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