]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-performer.cc
Issue 3531: replaced function argument 'string' by 'const string&' where it makes...
[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 #include <deque>
22
23 #include "audio-column.hh"
24 #include "audio-item.hh"
25 #include "audio-staff.hh"
26 #include "context.hh"
27 #include "international.hh"
28 #include "performer-group.hh"
29 #include "warn.hh"
30
31 /* Perform a staff. Individual notes should have their instrument
32   (staff-wide) set, so we override play_element ()
33 */
34 class Staff_performer : public Performer
35 {
36 public:
37   TRANSLATOR_DECLARATIONS (Staff_performer);
38   ~Staff_performer ();
39
40 protected:
41   virtual void acknowledge_audio_element (Audio_element_info info);
42   virtual void finalize ();
43   virtual void initialize ();
44   void process_music ();
45   void stop_translation_timestep ();
46
47 private:
48   string new_instrument_string ();
49   void set_instrument_name (const string &voice);
50   void set_instrument (int channel, const string &voice);
51   int get_channel (const string &instrument);
52   Audio_staff *get_audio_staff (const string &voice);
53   Audio_staff *new_audio_staff (const string &voice);
54   Audio_dynamic *get_dynamic (const string &voice);
55
56   string instrument_string_;
57   int channel_;
58   Audio_instrument *instrument_;
59   Audio_text *instrument_name_;
60   Audio_text *name_;
61   Audio_tempo *tempo_;
62   map<string, deque<Audio_note *> > note_map_;
63   map<string, Audio_staff *> staff_map_;
64   map<string, int> channel_map_;
65   map<string, Audio_dynamic *> dynamic_map_;
66   // Would prefer to have the following two items be
67   // members of the containing class Performance,
68   // so they can be reset for each new midi file output.
69   static map<string, int> static_channel_map_;
70   static int channel_count_;
71   // For now, ask the last Staff_performer clean up during its finalize method
72   static int staff_performer_count_;
73 };
74
75 map<string, int> Staff_performer::static_channel_map_;
76 int Staff_performer::channel_count_ = 0;
77 int Staff_performer::staff_performer_count_ = 0;
78
79 #include "translator.icc"
80
81 ADD_TRANSLATOR (Staff_performer,
82                 /* doc */
83                 "",
84
85                 /* create */
86                 "",
87
88                 /* read */
89                 "",
90
91                 /* write */
92                 "");
93
94 Staff_performer::Staff_performer ()
95   : channel_ (-1),
96     instrument_ (0),
97     instrument_name_ (0),
98     name_ (0),
99     tempo_ (0)
100 {
101 }
102
103 Staff_performer::~Staff_performer ()
104 {
105 }
106
107 void
108 Staff_performer::initialize ()
109 {
110   ++staff_performer_count_;
111 }
112
113 Audio_staff *
114 Staff_performer::new_audio_staff (const string &voice)
115 {
116   Audio_staff *audio_staff = new Audio_staff;
117   audio_staff->merge_unisons_
118     = to_boolean (get_property ("midiMergeUnisons"));
119   string track_name = context ()->id_string () + ":" + voice;
120   if (track_name != ":")
121     {
122       name_ = new Audio_text (Audio_text::TRACK_NAME, context ()->id_string ()
123                               + ":" + voice);
124       audio_staff->add_audio_item (name_);
125       announce_element (Audio_element_info (name_, 0));
126     }
127   announce_element (Audio_element_info (audio_staff, 0));
128   staff_map_[voice] = audio_staff;
129   if (!instrument_string_.empty ())
130     set_instrument (channel_, voice);
131   return audio_staff;
132 }
133
134 Audio_staff *
135 Staff_performer::get_audio_staff (const string &voice)
136 {
137   SCM channel_mapping = get_property ("midiChannelMapping");
138   if (channel_mapping != ly_symbol2scm ("instrument")
139       && staff_map_.size ())
140     return staff_map_.begin ()->second;
141
142   map<string, Audio_staff *>::const_iterator i = staff_map_.find (voice);
143   if (i != staff_map_.end ())
144     return i->second;
145   map<string, Audio_staff *>::const_iterator e = staff_map_.find ("");
146   if (staff_map_.size () == 1 && e != staff_map_.end ())
147     {
148       staff_map_[voice] = e->second;
149       return e->second;
150     }
151   return new_audio_staff (voice);
152 }
153
154 Audio_dynamic *
155 Staff_performer::get_dynamic (const string &voice)
156 {
157   map<string, Audio_dynamic *>::const_iterator i = dynamic_map_.find (voice);
158   if (i != dynamic_map_.end ())
159     return i->second;
160   return 0;
161 }
162
163 void
164 Staff_performer::process_music ()
165 {
166 }
167
168 void
169 Staff_performer::set_instrument (int channel, const string &voice)
170 {
171   instrument_ = new Audio_instrument (instrument_string_);
172   instrument_->channel_ = channel;
173   announce_element (Audio_element_info (instrument_, 0));
174   Audio_staff *audio_staff = get_audio_staff (voice);
175   audio_staff->add_audio_item (instrument_);
176   SCM proc = ly_lily_module_constant ("percussion?");
177   SCM drums = scm_call_1 (proc, ly_symbol2scm (instrument_string_.c_str ()));
178   audio_staff->percussion_ = (drums == SCM_BOOL_T);
179 }
180
181 void
182 Staff_performer::set_instrument_name (const string &voice)
183 {
184   instrument_name_ = new Audio_text (Audio_text::INSTRUMENT_NAME,
185                                      instrument_string_);
186   announce_element (Audio_element_info (instrument_name_, 0));
187   get_audio_staff (voice)->add_audio_item (instrument_name_);
188 }
189
190 void
191 Staff_performer::stop_translation_timestep ()
192 {
193   name_ = 0;
194   tempo_ = 0;
195   instrument_name_ = 0;
196   instrument_ = 0;
197   // For each voice with a note played in the current translation time step,
198   // check if the voice has an Audio_dynamic registered: if yes, apply this
199   // dynamic to every note played in the voice in the current translation time
200   // step.
201   for (map<string, deque<Audio_note *> >::iterator vi = note_map_.begin ();
202        vi != note_map_.end (); ++vi)
203     {
204       Audio_dynamic *d = get_dynamic (vi->first);
205       if (d)
206         {
207           for (deque<Audio_note *>::iterator ni = vi->second.begin ();
208                ni != vi->second.end (); ++ni)
209             (*ni)->dynamic_ = d;
210         }
211     }
212   note_map_.clear ();
213 }
214
215 void
216 Staff_performer::finalize ()
217 {
218   staff_map_.clear ();
219   channel_map_.clear ();
220   if (staff_performer_count_)
221     --staff_performer_count_;
222   if (0 == staff_performer_count_)
223     {
224       static_channel_map_.clear ();
225       channel_count_ = 0;
226     }
227 }
228
229 string
230 Staff_performer::new_instrument_string ()
231 {
232   // mustn't ask Score for instrument: it will return piano!
233   SCM minstr = get_property ("midiInstrument");
234
235   if (!scm_is_string (minstr)
236       || ly_scm2string (minstr) == instrument_string_)
237     return "";
238
239   instrument_string_ = ly_scm2string (minstr);
240
241   return instrument_string_;
242 }
243
244 int
245 Staff_performer::get_channel (const string &instrument)
246 {
247   SCM channel_mapping = get_property ("midiChannelMapping");
248   map<string, int> &channel_map
249     = (channel_mapping != ly_symbol2scm ("instrument"))
250       ? channel_map_
251       : static_channel_map_;
252
253   if (channel_mapping == ly_symbol2scm ("staff")
254       && channel_ >= 0)
255     return channel_;
256
257   map<string, int>::const_iterator i = channel_map.find (instrument);
258   if (i != channel_map.end ())
259     return i->second;
260
261   int channel = (channel_mapping == ly_symbol2scm ("staff"))
262                 ? channel_count_++
263                 : channel_map.size ();
264
265   /* MIDI players tend to ignore instrument settings on channel
266      10, the percussion channel.  */
267   if (channel % 16 == 9)
268     {
269       channel_map["percussion"] = channel++;
270       channel_count_++;
271     }
272
273   if (channel > 15)
274     {
275       warning (_ ("MIDI channel wrapped around"));
276       warning (_ ("remapping modulo 16"));
277       channel = channel % 16;
278     }
279
280   channel_map[instrument] = channel;
281   return channel;
282 }
283
284 void
285 Staff_performer::acknowledge_audio_element (Audio_element_info inf)
286 {
287   if (Audio_item *ai = dynamic_cast<Audio_item *> (inf.elem_))
288     {
289       /* map each context (voice) to its own track */
290       Context *c = inf.origin_contexts (this)[0];
291       string voice;
292       if (c->is_alias (ly_symbol2scm ("Voice")))
293         voice = c->id_string ();
294       SCM channel_mapping = get_property ("midiChannelMapping");
295       string str = new_instrument_string ();
296       if (channel_mapping != ly_symbol2scm ("instrument"))
297         channel_ = get_channel (voice);
298       else if (channel_ < 0 && str.empty ())
299         channel_ = get_channel (str);
300       if (str.length ())
301         {
302           if (channel_mapping != ly_symbol2scm ("voice"))
303             channel_ = get_channel (str);
304           set_instrument (channel_, voice);
305           set_instrument_name (voice);
306         }
307       ai->channel_ = channel_;
308       bool encode_dynamics_as_velocity_ = true;
309       if (encode_dynamics_as_velocity_)
310         {
311           if (Audio_note *n = dynamic_cast<Audio_note *> (inf.elem_))
312             {
313               // Keep track of the notes played in the current voice in this
314               // translation time step (for adjusting their dynamics later in
315               // stop_translation_timestep).
316               note_map_[voice].push_back (n);
317             }
318           else if (Audio_dynamic *d = dynamic_cast<Audio_dynamic *> (inf.elem_))
319             {
320               dynamic_map_[voice] = d;
321               // Output volume as velocity: must disable Midi_dynamic output
322               d->silent_ = true;
323             }
324         }
325       Audio_staff *audio_staff = get_audio_staff (voice);
326       audio_staff->add_audio_item (ai);
327     }
328 }
329