]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-performer.cc
Add regtests for issue 1650
[lilypond.git] / lily / staff-performer.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2012 Jan Nieuwenhuizen <janneke@gnu.org>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <map>
21
22 #include "audio-column.hh"
23 #include "audio-item.hh"
24 #include "audio-staff.hh"
25 #include "context.hh"
26 #include "international.hh"
27 #include "performer-group.hh"
28 #include "warn.hh"
29
30 /* Perform a staff. Individual notes should have their instrument
31   (staff-wide) set, so we override play_element ()
32 */
33 class Staff_performer : public Performer
34 {
35 public:
36   TRANSLATOR_DECLARATIONS (Staff_performer);
37   ~Staff_performer ();
38
39 protected:
40   virtual void acknowledge_audio_element (Audio_element_info info);
41   virtual void finalize ();
42   virtual void initialize ();
43   void process_music ();
44   void stop_translation_timestep ();
45
46 private:
47   string new_instrument_string ();
48   void set_instrument_name (string voice);
49   void set_instrument (int channel, string voice);
50   int get_channel (string instrument);
51   Audio_staff *get_audio_staff (string voice);
52   Audio_staff *new_audio_staff (string voice);
53   Audio_dynamic *get_dynamic (string voice);
54
55   string instrument_string_;
56   int channel_;
57   Audio_instrument *instrument_;
58   Audio_text *instrument_name_;
59   Audio_text *name_;
60   Audio_tempo *tempo_;
61   map<string, Audio_staff *> staff_map_;
62   map<string, int> channel_map_;
63   map<string, Audio_dynamic *> dynamic_map_;
64   // Would prefer to have the following two items be
65   // members of the containing class Performance,
66   // so they can be reset for each new midi file output.
67   static map<string, int> static_channel_map_;
68   static int channel_count_;
69   // For now, ask the last Staff_performer clean up during its finalize method
70   static int staff_performer_count_;
71 };
72
73 map<string, int> Staff_performer::static_channel_map_;
74 int Staff_performer::channel_count_ = 0;
75 int Staff_performer::staff_performer_count_ = 0;
76
77 #include "translator.icc"
78
79 ADD_TRANSLATOR (Staff_performer,
80                 /* doc */
81                 "",
82
83                 /* create */
84                 "",
85
86                 /* read */
87                 "",
88
89                 /* write */
90                 "");
91
92 Staff_performer::Staff_performer ()
93   : channel_ (-1),
94     instrument_ (0),
95     instrument_name_ (0),
96     name_ (0),
97     tempo_ (0)
98 {
99 }
100
101 Staff_performer::~Staff_performer ()
102 {
103 }
104
105 void
106 Staff_performer::initialize ()
107 {
108   ++staff_performer_count_;
109 }
110
111 Audio_staff *
112 Staff_performer::new_audio_staff (string voice)
113 {
114   Audio_staff *audio_staff = new Audio_staff;
115   audio_staff->merge_unisons_
116     = to_boolean (get_property ("midiMergeUnisons"));
117   string track_name = context ()->id_string () + ":" + voice;
118   if (track_name != ":")
119     {
120       name_ = new Audio_text (Audio_text::TRACK_NAME, context ()->id_string ()
121                               + ":" + voice);
122       audio_staff->add_audio_item (name_);
123       announce_element (Audio_element_info (name_, 0));
124     }
125   announce_element (Audio_element_info (audio_staff, 0));
126   staff_map_[voice] = audio_staff;
127   if (!instrument_string_.empty ())
128     set_instrument (channel_, voice);
129   return audio_staff;
130 }
131
132 Audio_staff *
133 Staff_performer::get_audio_staff (string voice)
134 {
135   SCM channel_mapping = get_property ("midiChannelMapping");
136   if (channel_mapping != ly_symbol2scm ("instrument")
137       && staff_map_.size ())
138     return staff_map_.begin ()->second;
139
140   map<string, Audio_staff *>::const_iterator i = staff_map_.find (voice);
141   if (i != staff_map_.end ())
142     return i->second;
143   map<string, Audio_staff *>::const_iterator e = staff_map_.find ("");
144   if (staff_map_.size () == 1 && e != staff_map_.end ())
145     {
146       staff_map_[voice] = e->second;
147       return e->second;
148     }
149   return new_audio_staff (voice);
150 }
151
152 Audio_dynamic *
153 Staff_performer::get_dynamic (string voice)
154 {
155   map<string, Audio_dynamic *>::const_iterator i = dynamic_map_.find (voice);
156   if (i != dynamic_map_.end ())
157     return i->second;
158   return 0;
159 }
160
161 void
162 Staff_performer::process_music ()
163 {
164 }
165
166 void
167 Staff_performer::set_instrument (int channel, string voice)
168 {
169   instrument_ = new Audio_instrument (instrument_string_);
170   instrument_->channel_ = channel;
171   announce_element (Audio_element_info (instrument_, 0));
172   Audio_staff *audio_staff = get_audio_staff (voice);
173   audio_staff->add_audio_item (instrument_);
174   SCM proc = ly_lily_module_constant ("percussion?");
175   SCM drums = scm_call_1 (proc, ly_symbol2scm (instrument_string_.c_str ()));
176   audio_staff->percussion_ = (drums == SCM_BOOL_T);
177 }
178
179 void
180 Staff_performer::set_instrument_name (string voice)
181 {
182   instrument_name_ = new Audio_text (Audio_text::INSTRUMENT_NAME,
183                                      instrument_string_);
184   announce_element (Audio_element_info (instrument_name_, 0));
185   get_audio_staff (voice)->add_audio_item (instrument_name_);
186 }
187
188 void
189 Staff_performer::stop_translation_timestep ()
190 {
191   name_ = 0;
192   tempo_ = 0;
193   instrument_name_ = 0;
194   instrument_ = 0;
195 }
196
197 void
198 Staff_performer::finalize ()
199 {
200   staff_map_.clear ();
201   channel_map_.clear ();
202   if (staff_performer_count_)
203     --staff_performer_count_;
204   if (0 == staff_performer_count_)
205     {
206       static_channel_map_.clear ();
207       channel_count_ = 0;
208     }
209 }
210
211 string
212 Staff_performer::new_instrument_string ()
213 {
214   // mustn't ask Score for instrument: it will return piano!
215   SCM minstr = get_property ("midiInstrument");
216
217   if (!scm_is_string (minstr)
218       || ly_scm2string (minstr) == instrument_string_)
219     return "";
220
221   instrument_string_ = ly_scm2string (minstr);
222
223   return instrument_string_;
224 }
225
226 int
227 Staff_performer::get_channel (string instrument)
228 {
229   SCM channel_mapping = get_property ("midiChannelMapping");
230   map<string, int>& channel_map
231     = (channel_mapping != ly_symbol2scm ("instrument"))
232       ? channel_map_
233       : static_channel_map_;
234
235   if (channel_mapping == ly_symbol2scm ("staff")
236       && channel_ >= 0)
237     return channel_;
238
239   map<string, int>::const_iterator i = channel_map.find (instrument);
240   if (i != channel_map.end ())
241     return i->second;
242
243   int channel = (channel_mapping == ly_symbol2scm ("staff"))
244                 ? channel_count_++
245                 : channel_map.size ();
246
247   /* MIDI players tend to ignore instrument settings on channel
248      10, the percussion channel.  */
249   if (channel % 16 == 9)
250     {
251       channel_map["percussion"] = channel++;
252       channel_count_++;
253     }
254
255   if (channel > 15)
256     {
257       warning (_ ("MIDI channel wrapped around"));
258       warning (_ ("remapping modulo 16"));
259       channel = channel % 16;
260     }
261
262   channel_map[instrument] = channel;
263   return channel;
264 }
265
266 void
267 Staff_performer::acknowledge_audio_element (Audio_element_info inf)
268 {
269   if (Audio_item *ai = dynamic_cast<Audio_item *> (inf.elem_))
270     {
271       /* map each context (voice) to its own track */
272       Context *c = inf.origin_contexts (this)[0];
273       string voice;
274       if (c->is_alias (ly_symbol2scm ("Voice")))
275         voice = c->id_string ();
276       SCM channel_mapping = get_property ("midiChannelMapping");
277       string str = new_instrument_string ();
278       if (channel_mapping != ly_symbol2scm ("instrument"))
279         channel_ = get_channel (voice);
280       else if (channel_ < 0 && str.empty ())
281         channel_ = get_channel (str);
282       if (str.length ())
283         {
284           if (channel_mapping != ly_symbol2scm ("voice"))
285             channel_ = get_channel (str);
286           set_instrument (channel_, voice);
287           set_instrument_name (voice);
288         }
289       ai->channel_ = channel_;
290       bool encode_dynamics_as_velocity_ = true;
291       if (encode_dynamics_as_velocity_)
292         {
293           if (Audio_dynamic *d = dynamic_cast<Audio_dynamic *> (inf.elem_))
294             {
295               dynamic_map_[voice] = d;
296               // Output volume as velocity: must disable Midi_dynamic output
297               d->silent_ = true;
298             }
299           if (Audio_dynamic *d = get_dynamic (voice))
300             if (Audio_note *n = dynamic_cast<Audio_note *> (inf.elem_))
301               n->dynamic_ = d;
302         }
303       Audio_staff *audio_staff = get_audio_staff (voice);
304       audio_staff->add_audio_item (ai);
305     }
306 }
307