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