1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3 
4   Copyright (C) 1997--2021 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 "audio-column.hh"
21 #include "audio-item.hh"
22 #include "audio-staff.hh"
23 #include "context.hh"
24 #include "international.hh"
25 #include "midi-cc-announcer.hh"
26 #include "performer-group.hh"
27 #include "warn.hh"
28 #include "lily-imports.hh"
29 
30 #include "translator.icc"
31 
32 #include <deque>
33 #include <map>
34 
35 using std::deque;
36 using std::map;
37 using std::string;
38 
39 /* Perform a staff. Individual notes should have their instrument
40   (staff-wide) set, so we override play_element ()
41 */
42 class Staff_performer : public Performer
43 {
44 public:
45   TRANSLATOR_DECLARATIONS (Staff_performer);
46   ~Staff_performer ();
47 
48 protected:
49   void acknowledge_audio_element (Audio_element_info info) override;
50   void finalize () override;
51   void initialize () override;
52   void process_music ();
53   void stop_translation_timestep ();
54 
55 private:
56   string new_instrument_string ();
57   void set_instrument_name (const string &voice);
58   void set_instrument (int channel, const string &voice);
59   int get_channel (const string &instrument);
60   Audio_staff *get_audio_staff (const string &voice);
61   Audio_staff *new_audio_staff (const string &voice);
62 
63   class Midi_control_initializer : public Midi_control_change_announcer
64   {
65   public:
66     Midi_control_initializer (Staff_performer *performer,
67                               Audio_staff *audio_staff,
68                               int channel);
69 
70     SCM get_property_value (const char *property_name) override;
71     void do_announce (Audio_control_change *item) override;
72 
73   private:
74     Staff_performer *performer_;
75     Audio_staff *audio_staff_;
76     int channel_;
77   };
78 
79   string instrument_string_;
80   int channel_;
81   Audio_instrument *instrument_;
82   Audio_text *instrument_name_;
83   Audio_text *name_;
84   Audio_tempo *tempo_;
85   map<string, Audio_staff *> staff_map_;
86   map<string, int> channel_map_;
87   // Would prefer to have the following two items be
88   // members of the containing class Performance,
89   // so they can be reset for each new midi file output.
90   static map<string, int> static_channel_map_;
91   static int channel_count_;
92   // For now, ask the last Staff_performer clean up during its finalize method
93   static int staff_performer_count_;
94 };
95 
96 map<string, int> Staff_performer::static_channel_map_;
97 int Staff_performer::channel_count_ = 0;
98 int Staff_performer::staff_performer_count_ = 0;
99 
100 void
boot()101 Staff_performer::boot ()
102 {
103 
104 }
105 
106 ADD_TRANSLATOR (Staff_performer,
107                 /* doc */
108                 "",
109 
110                 /* create */
111                 "",
112 
113                 /* read */
114                 "midiChannelMapping "
115                 "midiMergeUnisons "
116                 "midiSkipOffset ",
117 
118                 /* write */
119                 "");
120 
Staff_performer(Context * c)121 Staff_performer::Staff_performer (Context *c)
122   : Performer (c),
123     channel_ (-1),
124     instrument_ (0),
125     instrument_name_ (0),
126     name_ (0),
127     tempo_ (0)
128 {
129 }
130 
~Staff_performer()131 Staff_performer::~Staff_performer ()
132 {
133 }
134 
135 void
initialize()136 Staff_performer::initialize ()
137 {
138   ++staff_performer_count_;
139 }
140 
141 Audio_staff *
new_audio_staff(const string & voice)142 Staff_performer::new_audio_staff (const string &voice)
143 {
144   Audio_staff *audio_staff = new Audio_staff;
145   audio_staff->merge_unisons_
146     = from_scm<bool> (get_property (this, "midiMergeUnisons"));
147   string track_name = context ()->id_string () + ":" + voice;
148   if (track_name != ":")
149     {
150       name_ = new Audio_text (Audio_text::TRACK_NAME, context ()->id_string ()
151                               + ":" + voice);
152       audio_staff->add_audio_item (name_);
153       announce_element (Audio_element_info (name_, 0));
154     }
155   announce_element (Audio_element_info (audio_staff, 0));
156   staff_map_[voice] = audio_staff;
157   if (!instrument_string_.empty ())
158     set_instrument (channel_, voice);
159   // Set initial values (if any) for MIDI controls.
160   Midi_control_initializer i (this, audio_staff, channel_);
161   i.announce_control_changes ();
162   return audio_staff;
163 }
164 
165 Audio_staff *
get_audio_staff(const string & voice)166 Staff_performer::get_audio_staff (const string &voice)
167 {
168   SCM channel_mapping = get_property (this, "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 void
process_music()186 Staff_performer::process_music ()
187 {
188 }
189 
190 void
set_instrument(int channel,const string & voice)191 Staff_performer::set_instrument (int channel, const string &voice)
192 {
193   instrument_ = new Audio_instrument (instrument_string_);
194   instrument_->channel_ = channel;
195   announce_element (Audio_element_info (instrument_, 0));
196   Audio_staff *audio_staff = get_audio_staff (voice);
197   audio_staff->add_audio_item (instrument_);
198   SCM drums = Lily::percussion_p (ly_symbol2scm (instrument_string_.c_str ()));
199   audio_staff->percussion_ = from_scm<bool> (drums);
200 }
201 
202 void
set_instrument_name(const string & voice)203 Staff_performer::set_instrument_name (const string &voice)
204 {
205   instrument_name_ = new Audio_text (Audio_text::INSTRUMENT_NAME,
206                                      instrument_string_);
207   announce_element (Audio_element_info (instrument_name_, 0));
208   get_audio_staff (voice)->add_audio_item (instrument_name_);
209 }
210 
211 void
stop_translation_timestep()212 Staff_performer::stop_translation_timestep ()
213 {
214   name_ = 0;
215   tempo_ = 0;
216   instrument_name_ = 0;
217   instrument_ = 0;
218 }
219 
220 void
finalize()221 Staff_performer::finalize ()
222 {
223   Moment end_mom = now_mom ()
224     + from_scm (get_property (this, "midiSkipOffset"), Moment ());
225   for (map<string, Audio_staff *>::iterator i = staff_map_.begin ();
226        i != staff_map_.end (); ++i)
227     {
228       i->second->end_mom_ = end_mom;
229     }
230 
231   staff_map_.clear ();
232   channel_map_.clear ();
233   if (staff_performer_count_)
234     --staff_performer_count_;
235   if (0 == staff_performer_count_)
236     {
237       static_channel_map_.clear ();
238       channel_count_ = 0;
239     }
240 }
241 
242 string
new_instrument_string()243 Staff_performer::new_instrument_string ()
244 {
245   // mustn't ask Score for instrument: it will return piano!
246   SCM minstr = get_property (this, "midiInstrument");
247 
248   if (!scm_is_string (minstr)
249       || ly_scm2string (minstr) == instrument_string_)
250     return "";
251 
252   instrument_string_ = ly_scm2string (minstr);
253 
254   return instrument_string_;
255 }
256 
257 int
get_channel(const string & instrument)258 Staff_performer::get_channel (const string &instrument)
259 {
260   SCM channel_mapping = get_property (this, "midiChannelMapping");
261   map<string, int> &channel_map
262     = (!scm_is_eq (channel_mapping, ly_symbol2scm ("instrument")))
263       ? channel_map_
264       : static_channel_map_;
265 
266   if (scm_is_eq (channel_mapping, ly_symbol2scm ("staff"))
267       && channel_ >= 0)
268     return channel_;
269 
270   map<string, int>::const_iterator i = channel_map.find (instrument);
271   if (i != channel_map.end ())
272     return i->second;
273 
274   auto channel = (scm_is_eq (channel_mapping, ly_symbol2scm ("staff")))
275                  ? channel_count_++
276                  : static_cast<int> (channel_map.size ());
277 
278   /* MIDI players tend to ignore instrument settings on channel
279      10, the percussion channel.  */
280   if (channel % 16 == 9)
281     {
282       // TODO: Shouldn't this assign 9 rather than channel++?
283       //
284       // TODO: A hard-coded percussion entry ought to be created at the
285       // beginning, otherwise an early lookup of the key might cause it to be
286       // allocated an unexpected value.  Fixing this requires decoupling the
287       // next channel number from the map size.
288       //
289       // TODO: Should this entry really be created for any case of channel
290       // mapping, or perhaps only for the per-instrument case?
291       channel_map["percussion"] = channel++;
292       // TODO: Above, channel_count_ is incremented in the per-staff case only;
293       // should that be considered here as well?
294       channel_count_++;
295     }
296 
297   if (channel > 15) // TODO: warn the first time only, maybe
298     {
299       warning (_ ("MIDI channel wrapped around"));
300       warning (_ ("remapping modulo 16"));
301       channel = channel % 16;
302     }
303 
304   channel_map[instrument] = channel;
305   return channel;
306 }
307 
308 void
acknowledge_audio_element(Audio_element_info inf)309 Staff_performer::acknowledge_audio_element (Audio_element_info inf)
310 {
311   /* map each context (voice) to its own track */
312   Context *c = inf.origin_contexts (this)[0];
313   string voice;
314   if (c->is_alias (ly_symbol2scm ("Voice")))
315     voice = c->id_string ();
316   SCM channel_mapping = get_property (this, "midiChannelMapping");
317   string str = new_instrument_string ();
318   if (!scm_is_eq (channel_mapping, ly_symbol2scm ("instrument")))
319     channel_ = get_channel (voice);
320   else if (channel_ < 0 && str.empty ())
321     channel_ = get_channel (str);
322   if (str.length ())
323     {
324       if (!scm_is_eq (channel_mapping, ly_symbol2scm ("voice")))
325         channel_ = get_channel (str);
326       set_instrument (channel_, voice);
327       set_instrument_name (voice);
328     }
329   Audio_staff *audio_staff = get_audio_staff (voice);
330   if (Audio_item *ai = dynamic_cast<Audio_item *> (inf.elem_))
331     {
332       ai->channel_ = channel_;
333       audio_staff->add_audio_item (ai);
334     }
335 }
336 
Midi_control_initializer(Staff_performer * performer,Audio_staff * audio_staff,int channel)337 Staff_performer::Midi_control_initializer::Midi_control_initializer
338 (Staff_performer *performer, Audio_staff *audio_staff, int channel)
339   : performer_ (performer),
340     audio_staff_ (audio_staff),
341     channel_ (channel)
342 {
343 }
344 
get_property_value(const char * property_name)345 SCM Staff_performer::Midi_control_initializer::get_property_value
346 (const char *property_name)
347 {
348   return get_property (performer_, property_name);
349 }
350 
do_announce(Audio_control_change * item)351 void Staff_performer::Midi_control_initializer::do_announce
352 (Audio_control_change *item)
353 {
354   item->channel_ = channel_;
355   audio_staff_->add_audio_item (item);
356   performer_->announce_element (Audio_element_info (item, 0));
357 }
358