From: Jan Nieuwenhuizen Date: Sat, 12 Mar 2011 13:45:24 +0000 (+0100) Subject: Midi: Map voices to tracks in MIDI output. X-Git-Tag: release/2.13.54-1~6 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=bea1783a6b70e41c45f85136146cdd7fa773fb8e;p=lilypond.git Midi: Map voices to tracks in MIDI output. With voices mapped to tracks, midi2ly can neatly recreate voices without needing to do tricky guessing. Some extra trickery will be needed to re-assign the voices to the correct staves. Also, midi channels are optimally re-used: identical midi instruments accross different staves share the same channel. This can fix running out of channels in some cases, where similar instruments are notated on different staves. --- diff --git a/lily/audio-item.cc b/lily/audio-item.cc index 8459aa8c54..e33437f631 100644 --- a/lily/audio-item.cc +++ b/lily/audio-item.cc @@ -51,6 +51,7 @@ Audio_note::Audio_note (Pitch p, Moment m, bool tie_event, Pitch transposing) tied_ = 0; transposing_ = transposing; tie_event_ = tie_event; + volume_ = 0; } void diff --git a/lily/include/audio-item.hh b/lily/include/audio-item.hh index 525544dc1d..7ddd664aaf 100644 --- a/lily/include/audio-item.hh +++ b/lily/include/audio-item.hh @@ -89,6 +89,7 @@ public: Pitch pitch_; Moment length_mom_; Pitch transposing_; + Real volume_; Audio_note *tied_; bool tie_event_; diff --git a/lily/midi-chunk.cc b/lily/midi-chunk.cc index d50b72ea26..aaed0a27fe 100644 --- a/lily/midi-chunk.cc +++ b/lily/midi-chunk.cc @@ -56,7 +56,6 @@ Midi_track::Midi_track (int number) data_string += String_convert::hex2bin (data_str0); string port = "00" "ff" "21" "01" + String_convert::int2hex (number_, 2, '0'); - data_string += String_convert::hex2bin (port); char const *footer_str0 = "00" "ff2f" "00"; diff --git a/lily/midi-item.cc b/lily/midi-item.cc index ab67947d55..36234c4a9c 100644 --- a/lily/midi-item.cc +++ b/lily/midi-item.cc @@ -181,7 +181,7 @@ Midi_time_signature::to_string () const Midi_note::Midi_note (Audio_note *a) : Midi_channel_item (a) , audio_ (a) - , dynamic_byte_ (0x5a) + , dynamic_byte_ (a->volume_ > 0 ? Byte (a->volume_ * 0x7f) : Byte (0x5a)) { } diff --git a/lily/staff-performer.cc b/lily/staff-performer.cc index 3ae1482bec..5e124c8baf 100644 --- a/lily/staff-performer.cc +++ b/lily/staff-performer.cc @@ -19,12 +19,13 @@ #include -#include "warn.hh" #include "audio-column.hh" #include "audio-item.hh" #include "audio-staff.hh" -#include "performer-group.hh" #include "context.hh" +#include "international.hh" +#include "performer-group.hh" +#include "warn.hh" /* Perform a staff. Individual notes should have their instrument (staff-wide) set, so we override play_element () @@ -35,27 +36,38 @@ public: TRANSLATOR_DECLARATIONS (Staff_performer); ~Staff_performer (); - string new_instrument_string (); - string instrument_string_; - protected: virtual void acknowledge_audio_element (Audio_element_info info); virtual void finalize (); virtual void initialize (); void process_music (); void stop_translation_timestep (); - void set_instrument_name (); - void set_instrument (int channel); private: - Audio_staff *audio_staff_; + string new_instrument_string (); + void set_instrument_name (string voice); + void set_instrument (int channel, string voice); + int get_channel (string instrument); + Audio_staff* get_audio_staff (string voice); + Audio_staff* new_audio_staff (string voice); + Real get_dynamic (string voice); + + string instrument_string_; + int channel_; Audio_instrument *instrument_; Audio_text *instrument_name_; Audio_text *name_; Audio_tempo *tempo_; + map staff_map_; map channel_map_; + map dynamic_map_; + static map static_channel_map_; + static int channel_count_; }; +map Staff_performer::static_channel_map_; +int Staff_performer::channel_count_ = 0; + #include "translator.icc" ADD_TRANSLATOR (Staff_performer, @@ -72,12 +84,12 @@ ADD_TRANSLATOR (Staff_performer, ""); Staff_performer::Staff_performer () + : channel_ (0) + , instrument_ (0) + , instrument_name_ (0) + , name_ (0) + , tempo_ (0) { - audio_staff_ = 0; - instrument_ = 0; - instrument_name_ = 0; - name_ = 0; - tempo_ = 0; } Staff_performer::~Staff_performer () @@ -87,51 +99,75 @@ Staff_performer::~Staff_performer () void Staff_performer::initialize () { - audio_staff_ = new Audio_staff; - name_ = new Audio_text (Audio_text::TRACK_NAME, context ()->id_string ()); +} - audio_staff_->add_audio_item (name_); - - announce_element (Audio_element_info (audio_staff_, 0)); +Audio_staff* +Staff_performer::new_audio_staff (string voice) +{ + Audio_staff* audio_staff = new Audio_staff; + name_ = new Audio_text (Audio_text::TRACK_NAME, context ()->id_string () + + ":" + voice); + audio_staff->add_audio_item (name_); + announce_element (Audio_element_info (audio_staff, 0)); announce_element (Audio_element_info (name_, 0)); + staff_map_[voice] = audio_staff; + return audio_staff; } -void -Staff_performer::process_music () +Audio_staff* +Staff_performer::get_audio_staff (string voice) { - string str = new_instrument_string (); - if (str.length ()) + map::const_iterator i = staff_map_.find (voice); + if (i != staff_map_.end ()) + return i->second; + map::const_iterator e = staff_map_.find (""); + if (staff_map_.size () == 1 && e != staff_map_.end ()) { - set_instrument (0); - set_instrument_name (); + staff_map_[voice] = e->second; + return e->second; } + return new_audio_staff (voice); +} + +Real +Staff_performer::get_dynamic (string voice) +{ + map::const_iterator i = dynamic_map_.find (voice); + if (i != dynamic_map_.end ()) + return i->second; + return 0; +} + +void +Staff_performer::process_music () +{ } void -Staff_performer::set_instrument (int channel) +Staff_performer::set_instrument (int channel, string voice) { instrument_ = new Audio_instrument (instrument_string_); instrument_->channel_ = channel; announce_element (Audio_element_info (instrument_, 0)); - audio_staff_->add_audio_item (instrument_); + Audio_staff* audio_staff = get_audio_staff (voice); + audio_staff->add_audio_item (instrument_); + SCM proc = ly_lily_module_constant ("percussion?"); + SCM drums = scm_call_1 (proc, ly_symbol2scm (instrument_string_.c_str ())); + audio_staff->percussion_ = (drums == SCM_BOOL_T); } void -Staff_performer::set_instrument_name () +Staff_performer::set_instrument_name (string voice) { instrument_name_ = new Audio_text (Audio_text::INSTRUMENT_NAME, instrument_string_); announce_element (Audio_element_info (instrument_name_, 0)); - audio_staff_->add_audio_item (instrument_name_); + get_audio_staff (voice)->add_audio_item (instrument_name_); } void Staff_performer::stop_translation_timestep () { - SCM proc = ly_lily_module_constant ("percussion?"); - SCM drums = scm_call_1 (proc, ly_symbol2scm (instrument_string_.c_str ())); - audio_staff_->percussion_ = (drums == SCM_BOOL_T); - name_ = 0; tempo_ = 0; instrument_name_ = 0; @@ -141,7 +177,10 @@ Staff_performer::stop_translation_timestep () void Staff_performer::finalize () { - audio_staff_ = 0; + staff_map_.clear (); + channel_map_.clear (); + channel_count_ = 0; + static_channel_map_.clear (); } string @@ -159,32 +198,63 @@ Staff_performer::new_instrument_string () return instrument_string_; } +int +Staff_performer::get_channel (string instrument) +{ + SCM channel_per_staff = get_property ("midiChannelPerStaff"); + map& channel_map = (channel_per_staff == SCM_BOOL_T) + ? channel_map_ + : static_channel_map_; + + map::const_iterator i = channel_map.find (instrument); + if (i != channel_map.end ()) + return i->second; + + int channel = (channel_per_staff == SCM_BOOL_T) + ? channel_count_++ + :channel_map.size (); + + /* MIDI players tend to ignore instrument settings on channel + 10, the percussion channel. */ + if (channel % 16 == 9) + channel_map["percussion"] = channel++; + + if (channel > 15) + { + warning (_ ("MIDI channel wrapped around")); + warning (_ ("remapping modulo 16")); + channel = channel % 16; + } + + channel_map[instrument] = channel; + return channel; +} + void Staff_performer::acknowledge_audio_element (Audio_element_info inf) { if (Audio_item *ai = dynamic_cast (inf.elem_)) { - /* map each context (voice) to its own channel */ - Context *c = inf.origin_contexts (this)[0]; - string id = c->id_string (); - int channel = channel_map_.size (); - /* MIDI players tend to ignore instrument settings on channel - 10, the percussion channel. */ - if (channel % 16 == 9) - channel_map_[""] = channel++; - - map::const_iterator i = channel_map_.find (id); - if (i != channel_map_.end ()) - channel = i->second; - else + /* map each context (voice) to its own track */ + Context* c = inf.origin_contexts (this)[0]; + string voice; + if (c->is_alias (ly_symbol2scm ("Voice"))) + voice = c->id_string (); + string str = new_instrument_string (); + if (str.length ()) { - channel_map_[id] = channel; - if (channel) - set_instrument (channel); + channel_ = get_channel (str); + set_instrument (channel_, voice); + set_instrument_name (voice); } - - ai->channel_ = channel; - audio_staff_->add_audio_item (ai); + Audio_staff* audio_staff = get_audio_staff (voice); + ai->channel_ = channel_; + if (Audio_dynamic *d = dynamic_cast (inf.elem_)) + dynamic_map_[voice] = d->volume_; + if (Real d = get_dynamic (voice)) + if (Audio_note *n = dynamic_cast (inf.elem_)) + n->volume_ = d; + audio_staff->add_audio_item (ai); } } diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm index 3b3f369c87..046ac2e4f3 100644 --- a/scm/define-context-properties.scm +++ b/scm/define-context-properties.scm @@ -357,6 +357,8 @@ half staff-spaces. Usually determined by looking at @code{midiMinimumVolume}.") (midiMinimumVolume ,number? "Set the minimum loudness for MIDI. Ranges from 0 to@tie{}1.") + (midiChannelPerStaff ,boolean? "If set to @code{##t}, +each staff is assiged its own channel.") (minimumFret ,number? "The tablature auto string-selecting mechanism selects the highest string with a fret at least @code{minimumFret}.")