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