]> git.donarmstrong.com Git - lilypond.git/blob - lily/staff-performer.cc
4daa2ca9a2d0a8c17b1688363d7470ba14f90800
[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   // Set initial values (if any) for control functions.
132   for (const Audio_control_function_value_change::Context_property *p
133          = Audio_control_function_value_change::context_properties_;
134        p->name_; ++p)
135     {
136       SCM value = get_property (p->name_);
137       if (scm_is_number (value))
138         {
139           Real val = scm_to_double (value);
140           if (val >= p->range_min_ && val <= p->range_max_)
141             {
142               // Normalize the value to the 0.0 to 1.0 range.
143               val = ((val - p->range_min_)
144                      / (p->range_max_ - p->range_min_));
145               Audio_control_function_value_change *item
146                 = new Audio_control_function_value_change (p->control_, val);
147               item->channel_ = channel_;
148               audio_staff->add_audio_item (item);
149               announce_element (Audio_element_info (item, 0));
150             }
151           else
152             warning (_f ("ignoring out-of-range value change for MIDI "
153                          "property `%s'",
154                          p->name_));
155         }
156     }
157   return audio_staff;
158 }
159
160 Audio_staff *
161 Staff_performer::get_audio_staff (const string &voice)
162 {
163   SCM channel_mapping = get_property ("midiChannelMapping");
164   if (channel_mapping != ly_symbol2scm ("instrument")
165       && staff_map_.size ())
166     return staff_map_.begin ()->second;
167
168   map<string, Audio_staff *>::const_iterator i = staff_map_.find (voice);
169   if (i != staff_map_.end ())
170     return i->second;
171   map<string, Audio_staff *>::const_iterator e = staff_map_.find ("");
172   if (staff_map_.size () == 1 && e != staff_map_.end ())
173     {
174       staff_map_[voice] = e->second;
175       return e->second;
176     }
177   return new_audio_staff (voice);
178 }
179
180 Audio_dynamic *
181 Staff_performer::get_dynamic (const string &voice)
182 {
183   map<string, Audio_dynamic *>::const_iterator i = dynamic_map_.find (voice);
184   if (i != dynamic_map_.end ())
185     return i->second;
186   return 0;
187 }
188
189 void
190 Staff_performer::process_music ()
191 {
192 }
193
194 void
195 Staff_performer::set_instrument (int channel, const string &voice)
196 {
197   instrument_ = new Audio_instrument (instrument_string_);
198   instrument_->channel_ = channel;
199   announce_element (Audio_element_info (instrument_, 0));
200   Audio_staff *audio_staff = get_audio_staff (voice);
201   audio_staff->add_audio_item (instrument_);
202   SCM proc = ly_lily_module_constant ("percussion?");
203   SCM drums = scm_call_1 (proc, ly_symbol2scm (instrument_string_.c_str ()));
204   audio_staff->percussion_ = (drums == SCM_BOOL_T);
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     = (channel_mapping != ly_symbol2scm ("instrument"))
276       ? channel_map_
277       : static_channel_map_;
278
279   if (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 = (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 (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 (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