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