diff options
author | Heikki Tauriainen <g034737@welho.com> | 2016-01-07 22:34:56 +0200 |
---|---|---|
committer | James Lowe <pkx166h@gmail.com> | 2016-07-27 22:54:15 +0100 |
commit | 14de2f189e75424bd0b04f4577386b4f0dde1a6e (patch) | |
tree | a9b89f87ee3971e7d04a6a179ec2e78bdac37947 | |
parent | 528d28e079419b3a45098b42b8b2b4eb9f1b15b1 (diff) |
Issue 4938 (2/3) Refactor handling of MIDI control changes
Handle the MIDI control value initialization from context properties
(Staff_performer::new_audio_staff), control value changes
(Midi_control_function_performer::announce_function_value_change), and
value conversion for output
(Midi_control_function_value_change::to_string) in the new
Midi_control_change_announcer class.
All MIDI control changes are now encoded using
{Audio,Midi}_control_change items. This change makes the old
{Audio,Midi}_control_function_value_change classes obsolete.
-rw-r--r-- | lily/audio-item.cc | 18 | ||||
-rw-r--r-- | lily/include/audio-item.hh | 32 | ||||
-rw-r--r-- | lily/include/lily-proto.hh | 3 | ||||
-rw-r--r-- | lily/include/midi-cc-announcer.hh | 64 | ||||
-rw-r--r-- | lily/include/midi-item.hh | 14 | ||||
-rw-r--r-- | lily/midi-cc-announcer.cc | 110 | ||||
-rw-r--r-- | lily/midi-control-function-performer.cc | 90 | ||||
-rw-r--r-- | lily/midi-item.cc | 74 | ||||
-rw-r--r-- | lily/staff-performer.cc | 67 |
9 files changed, 267 insertions, 205 deletions
diff --git a/lily/audio-item.cc b/lily/audio-item.cc index 5c713c43ed..a8a78199c7 100644 --- a/lily/audio-item.cc +++ b/lily/audio-item.cc @@ -205,24 +205,6 @@ Audio_text::Audio_text (Audio_text::Type type, const string &text_string) type_ = type; } -Audio_control_function_value_change -::Audio_control_function_value_change (Control control, Real value) - : control_ (control), value_ (value) -{ -} - -const Audio_control_function_value_change::Context_property -Audio_control_function_value_change::context_properties_[] = { - // property name, enum constant, lower bound for range, upper bound for range - { "midiBalance", BALANCE, -1.0, 1.0 }, - { "midiPanPosition", PAN_POSITION, -1.0, 1.0 }, - { "midiExpression", EXPRESSION, 0.0, 1.0 }, - { "midiReverbLevel", REVERB_LEVEL, 0.0, 1.0 }, - { "midiChorusLevel", CHORUS_LEVEL, 0.0, 1.0 }, - // extra element to signify the end of the mapping, must be kept last - { 0, NUM_CONTROLS, 0.0, 0.0 } -}; - Audio_control_change::Audio_control_change (int control, int value) : control_ (control), value_ (value) diff --git a/lily/include/audio-item.hh b/lily/include/audio-item.hh index 551d43f1c5..f3a97a2949 100644 --- a/lily/include/audio-item.hh +++ b/lily/include/audio-item.hh @@ -144,38 +144,6 @@ public: int one_beat_; }; -class Audio_control_function_value_change : public Audio_item -{ -public: - // Supported control functions. - enum Control - { - BALANCE = 0, PAN_POSITION, EXPRESSION, REVERB_LEVEL, CHORUS_LEVEL, - // pseudo value for representing the size of the enum; must be kept last - NUM_CONTROLS - }; - - Audio_control_function_value_change (Control control, Real value); - - // Information about a context property corresponding to a control function - // (name, the corresponding enumeration value, and the allowed range for the - // value of the context property). - struct Context_property - { - const char *name_; - Control control_; - Real range_min_; - Real range_max_; - }; - - // Mapping from supported control functions to the corresponding context - // properties. - static const Context_property context_properties_[]; - - Control control_; - Real value_; -}; - class Audio_control_change : public Audio_item { public: diff --git a/lily/include/lily-proto.hh b/lily/include/lily-proto.hh index f41c8f5a3f..6de69e16b9 100644 --- a/lily/include/lily-proto.hh +++ b/lily/include/lily-proto.hh @@ -25,7 +25,6 @@ class All_font_metrics; class Audio_column; class Audio_control_change; -class Audio_control_function_value_change; class Audio_dynamic; class Audio_element; class Audio_instrument; @@ -92,7 +91,7 @@ class Lyric_performer; class Mensural_ligature_engraver; class Midi_chunk; class Midi_control_change; -class Midi_control_function_value_change; +class Midi_control_change_announcer; class Midi_duration; class Midi_dynamic; class Midi_event; diff --git a/lily/include/midi-cc-announcer.hh b/lily/include/midi-cc-announcer.hh new file mode 100644 index 0000000000..38cb93f15a --- /dev/null +++ b/lily/include/midi-cc-announcer.hh @@ -0,0 +1,64 @@ +/* + This file is part of LilyPond, the GNU music typesetter. + + Copyright (C) 2016 by Heikki Tauriainen <g034737@welho.com>. + + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef MIDI_CC_ANNOUNCER_HH +#define MIDI_CC_ANNOUNCER_HH + +#include "input.hh" +#include "performer.hh" +#include "audio-item.hh" + +/* Base class for announcing MIDI control changes. */ +class Midi_control_change_announcer +{ +public: + /* Constructor. The optional parameter can be used to specify an Input + to use for relativizing warning messages about out-of-range values. */ + Midi_control_change_announcer (Input *origin = 0); + virtual ~Midi_control_change_announcer (); + + void announce_from_context_properties (); + + /* Announces MIDI CC changes by creating new Audio_control_change events + from them, and calling 'do_announce' on each event. Control change + events will be created from every supported MIDI context property for + which the 'get_property_value' function returns a value that is + compatible with the expected type of the context property's value. */ + void announce_control_changes (); + +private: + virtual SCM get_property_value (const char *property_name) = 0; + virtual void do_announce (Audio_control_change *item) = 0; + void warn (const string &message); + + Input *origin_; + + struct Control_spec + { + const char *const context_property_name_; + const Real range_min_; + const Real range_max_; + const int msb_control_number_; + const int lsb_control_number_; + }; + + static const Control_spec controls_[]; +}; + +#endif // MIDI_CC_ANNOUNCER_HH diff --git a/lily/include/midi-item.hh b/lily/include/midi-item.hh index b979bca4ba..57bb2d4da2 100644 --- a/lily/include/midi-item.hh +++ b/lily/include/midi-item.hh @@ -52,20 +52,6 @@ public: Midi_channel_item (Audio_item *ai); }; -/** - Midi control function value changes. -*/ -class Midi_control_function_value_change : public Midi_channel_item -{ -public: - DECLARE_CLASSNAME (Midi_control_function_value_change); - Midi_control_function_value_change (Audio_control_function_value_change *ai); - virtual ~Midi_control_function_value_change (); - virtual string to_string () const; - Audio_control_function_value_change::Control control_; - Real value_; -}; - class Midi_duration : public Midi_item { public: diff --git a/lily/midi-cc-announcer.cc b/lily/midi-cc-announcer.cc new file mode 100644 index 0000000000..7fb4ed6c27 --- /dev/null +++ b/lily/midi-cc-announcer.cc @@ -0,0 +1,110 @@ +/* + This file is part of LilyPond, the GNU music typesetter. + + Copyright (C) 2016 by Heikki Tauriainen <g034737@welho.com>. + + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audio-item.hh" +#include "input.hh" +#include "international.hh" +#include "libc-extension.hh" +#include "midi-cc-announcer.hh" + +/* + Context properties for setting MIDI controls. Each MIDI control + specification has the following components: + 1. The name of the LilyPond context property used to change the value of + the MIDI control. + 2. The lower bound for the numeric range of the LilyPond context property. + 3. The upper bound for the numeric range of the LilyPond context property. + 4. The MIDI control number for setting the most significant 7 bits of the + control value. + 5. The MIDI control number for setting the least significant 7 bits of the + control value, if the control supports 14-bit ("fine") resolution. If + the control supports only 7-bit ("coarse") resolution, the LSB control + number should be negative. +*/ +const Midi_control_change_announcer::Control_spec +Midi_control_change_announcer::controls_[] += +{ + { "midiBalance", -1.0, 1.0, 8, 40 }, + { "midiPanPosition", -1.0, 1.0, 10, 42 }, + { "midiExpression", 0.0, 1.0, 11, 43 }, + { "midiReverbLevel", 0.0, 1.0, 91, -1 }, + { "midiChorusLevel", 0.0, 1.0, 93, -1 }, + // This element should be kept last in the array. + { 0, 0.0, 0.0, 0, 0 } +}; + +Midi_control_change_announcer::Midi_control_change_announcer (Input *origin) + : origin_ (origin) +{ +} + +Midi_control_change_announcer::~Midi_control_change_announcer () +{ +} + +void Midi_control_change_announcer::announce_control_changes () +{ + for (const Control_spec *spec = controls_; spec->context_property_name_; + ++spec) + { + SCM value = get_property_value (spec->context_property_name_); + if (!scm_is_number (value)) + continue; + Real val = scm_to_double (value); + if (val >= spec->range_min_ && val <= spec->range_max_) + { + // Normalize the value to the 0.0 to 1.0 range. + val = ((val - spec->range_min_) + / (spec->range_max_ - spec->range_min_)); + // Transform the normalized context property value into a 14-bit or + // a 7-bit (non-negative) integer depending on the MIDI control's + // resolution. For directional value changes, #CENTER will + // correspond to 0.5 exactly, and my_round rounds upwards when in + // case of doubt. That means that center position will round to + // 0x40 or 0x2000 by a hair's breadth. + const Real full_fine_scale = 0x3FFF; + const Real full_coarse_scale = 0x7F; + const bool fine_resolution = (spec->lsb_control_number_ >= 0); + const int v = (int) (my_round (val * (fine_resolution + ? full_fine_scale + : full_coarse_scale))); + // Announce a control change for the most significant 7 bits of the + // control value (and, if the control supports fine resolution, for + // the least significant 7 bits as well). + do_announce (new Audio_control_change (spec->msb_control_number_, + fine_resolution + ? (v >> 7) : v)); + if (fine_resolution) + do_announce (new Audio_control_change (spec->lsb_control_number_, + v & 0x7F)); + } + else + warn (_f ("ignoring out-of-range value change for MIDI property `%s'", + spec->context_property_name_)); + } +} + +void Midi_control_change_announcer::warn (const string &message) +{ + if (origin_) + origin_->warning (message); + else + warning (message); +} diff --git a/lily/midi-control-function-performer.cc b/lily/midi-control-function-performer.cc index a112e6bcef..48037e9f97 100644 --- a/lily/midi-control-function-performer.cc +++ b/lily/midi-control-function-performer.cc @@ -1,7 +1,7 @@ /* This file is part of LilyPond, the GNU music typesetter. - Copyright (C) 2013--2015 by Heikki Tauriainen <g034737@welho.com>. + Copyright (C) 2013--2016 by Heikki Tauriainen <g034737@welho.com>. Adapted from performer implementations Copyright (C) 1996--2015 Jan Nieuwenhuizen <janneke@gnu.org>, Han-Wen Nienhyus <hanwen@xs4all.nl> and others. @@ -27,6 +27,7 @@ #include "dispatcher.hh" #include "international.hh" #include "listener.hh" +#include "midi-cc-announcer.hh" #include "stream-event.hh" #include "translator.icc" @@ -44,6 +45,22 @@ public: void connect_to_context (Context *c); void disconnect_from_context (Context *c); + +private: + class Control_change_announcer : public Midi_control_change_announcer + { + public: + Control_change_announcer (Midi_control_function_performer *p, + Stream_event *ev, const string &s); + + SCM get_property_value (const char *property_name); + void do_announce (Audio_control_change *item); + + private: + Midi_control_function_performer *performer_; + Stream_event *event_; + string symbol_; + }; }; Midi_control_function_performer::Midi_control_function_performer () @@ -58,16 +75,18 @@ void Midi_control_function_performer::connect_to_context (Context *c) { c->events_below ()-> - add_listener (GET_LISTENER (Midi_control_function_performer, announce_function_value_change), - ly_symbol2scm ("SetProperty")); + add_listener (GET_LISTENER (Midi_control_function_performer, + announce_function_value_change), + ly_symbol2scm ("SetProperty")); } void Midi_control_function_performer::disconnect_from_context (Context *c) { c->events_below ()-> - remove_listener (GET_LISTENER (Midi_control_function_performer, announce_function_value_change), - ly_symbol2scm ("SetProperty")); + remove_listener (GET_LISTENER (Midi_control_function_performer, + announce_function_value_change), + ly_symbol2scm ("SetProperty")); } void @@ -78,39 +97,30 @@ Midi_control_function_performer::announce_function_value_change (SCM sev) if (!scm_is_symbol (sym)) return; - // Search for a matching context property; if found, check that the value - // of the property is within the allowed range, and announce a possible - // change in the value of the corresponding control function. - string symbol = ly_symbol2string (sym); - for (const Audio_control_function_value_change::Context_property *p - = Audio_control_function_value_change::context_properties_; - p->name_; ++p) - { - if (symbol == p->name_) - { - SCM value = ev->get_property ("value"); - if (scm_is_number (value)) - { - Real val = scm_to_double (value); - if (val >= p->range_min_ && val <= p->range_max_) - { - // Normalize the value to the 0.0 to 1.0 range. - val = ((val - p->range_min_) - / (p->range_max_ - p->range_min_)); - Audio_control_function_value_change *item - = new Audio_control_function_value_change (p->control_, - val); - announce_element (Audio_element_info (item, 0)); - } - else - ev->origin ()-> - warning (_f ("ignoring out-of-range value change for MIDI " - "property `%s'", - p->name_)); - } - break; - } - } + Control_change_announcer a (this, ev, ly_symbol2string (sym)); + a.announce_control_changes (); +} + +Midi_control_function_performer::Control_change_announcer::Control_change_announcer +(Midi_control_function_performer *p, Stream_event *ev, const string &s) + : Midi_control_change_announcer (ev->origin ()), + performer_ (p), + event_ (ev), + symbol_ (s) +{ +} + +SCM +Midi_control_function_performer::Control_change_announcer::get_property_value +(const char *property_name) +{ + return symbol_ == property_name ? event_->get_property ("value") : SCM_EOL; +} + +void Midi_control_function_performer::Control_change_announcer::do_announce +(Audio_control_change *item) +{ + performer_->announce_element (Audio_element_info (item, 0)); } void @@ -121,7 +131,9 @@ Midi_control_function_performer::boot () ADD_TRANSLATOR (Midi_control_function_performer, /* doc */ - "", + "This performer listens to SetProperty events on context " + "properties for generating MIDI control changes and " + "prepares them for MIDI output.", /* create */ "", diff --git a/lily/midi-item.cc b/lily/midi-item.cc index 47ccd787c3..ba29ba0ba5 100644 --- a/lily/midi-item.cc +++ b/lily/midi-item.cc @@ -51,9 +51,6 @@ Midi_item::get_midi (Audio_item *a) return new Midi_time_signature (i); else if (Audio_text *i = dynamic_cast<Audio_text *> (a)) return new Midi_text (i); - else if (Audio_control_function_value_change *i - = dynamic_cast<Audio_control_function_value_change *> (a)) - return new Midi_control_function_value_change (i); else if (Audio_control_change *i = dynamic_cast<Audio_control_change *> (a)) return new Midi_control_change (i); else @@ -107,12 +104,6 @@ Midi_channel_item::Midi_channel_item (Audio_item *ai) { } -Midi_control_function_value_change -::Midi_control_function_value_change (Audio_control_function_value_change *ai) - : Midi_channel_item (ai), control_ (ai->control_), value_ (ai->value_) -{ -} - Midi_control_change::Midi_control_change (Audio_control_change *ai) : Midi_channel_item (ai), audio_ (ai) @@ -127,10 +118,6 @@ Midi_channel_item::~Midi_channel_item () { } -Midi_control_function_value_change::~Midi_control_function_value_change () -{ -} - Midi_control_change::~Midi_control_change () { } @@ -340,67 +327,6 @@ Midi_text::to_string () const } string -Midi_control_function_value_change::to_string () const -{ - // MIDI control function information. A MIDI control function may have one - // or two assigned control numbers depending on whether it supports coarse - // (7-bit) or fine (14-bit) resolution. If the control function supports - // fine resolution, the first (respectively, second) member of the structure - // represents the control number for setting the most (least) significant 7 - // bits of the control function's value. - struct Control_function - { - int msb_control_number_; - int lsb_control_number_; - }; - - // Mapping from supported control functions (enumeration values defined in - // Audio_controller_value_change::Control) to the corresponding MIDI control - // numbers. - static const Control_function control_functions[] = - { - // When adding support for new control functions, please note the - // following: - // - The order of the control number definitions should be kept - // consistent with the order of the enumeration values defined in - // Audio_control_function_value_change::Control. - // - If the control function has only coarse resolution, the function's - // control number should be stored in the MSB member of the array - // element, and the LSB member should be set to a negative value. - - { 8, 40 }, // balance - { 10, 42 }, // pan position - { 11, 43 }, // expression - { 91, -1 }, // reverb level (only coarse resolution available) - { 93, -1 } // chorus level (only coarse resolution available) - }; - - string str; - const Control_function *control_function = &control_functions[control_]; - static const Real full_fine_scale = 0x3FFF; - static const Real full_coarse_scale = 0x7F; - bool fine_resolution = (control_function->lsb_control_number_ >= 0); - // value_ is in range [0.0 .. 1.0]. For directional value ranges, - // #CENTER will correspond to 0.5 exactly, and my_round rounds - // upwards when in case of doubt. That means that center position - // will round to 0x40 or 0x2000 by a hair's breadth. - int value = (int) my_round (value_ * (fine_resolution ? - full_fine_scale : full_coarse_scale)); - Byte status_byte = (char) (0xB0 + channel_); - str += ::to_string ((char)status_byte); - str += ::to_string ((char)(control_function->msb_control_number_)); - str += ::to_string ((char)(fine_resolution ? (value >> 7) : value)); - if (fine_resolution) - { - str += ::to_string ((char)0x00); - str += ::to_string ((char)status_byte); - str += ::to_string ((char)(control_function->lsb_control_number_)); - str += ::to_string ((char)(value & 0x7F)); - } - return str; -} - -string Midi_control_change::to_string () const { Byte status_byte = (char) (0xB0 + channel_); diff --git a/lily/staff-performer.cc b/lily/staff-performer.cc index 505da3adba..d413a46835 100644 --- a/lily/staff-performer.cc +++ b/lily/staff-performer.cc @@ -25,6 +25,7 @@ #include "audio-staff.hh" #include "context.hh" #include "international.hh" +#include "midi-cc-announcer.hh" #include "performer-group.hh" #include "warn.hh" #include "lily-imports.hh" @@ -56,6 +57,22 @@ private: Audio_staff *new_audio_staff (const string &voice); Audio_span_dynamic *get_dynamic (const string &voice); + class Midi_control_initializer : public Midi_control_change_announcer + { + public: + Midi_control_initializer (Staff_performer *performer, + Audio_staff *audio_staff, + int channel); + + SCM get_property_value (const char *property_name); + void do_announce (Audio_control_change *item); + + private: + Staff_performer *performer_; + Audio_staff *audio_staff_; + int channel_; + }; + string instrument_string_; int channel_; Audio_instrument *instrument_; @@ -135,32 +152,9 @@ Staff_performer::new_audio_staff (const string &voice) staff_map_[voice] = audio_staff; if (!instrument_string_.empty ()) set_instrument (channel_, voice); - // Set initial values (if any) for control functions. - for (const Audio_control_function_value_change::Context_property *p - = Audio_control_function_value_change::context_properties_; - p->name_; ++p) - { - SCM value = get_property (p->name_); - if (scm_is_number (value)) - { - Real val = scm_to_double (value); - if (val >= p->range_min_ && val <= p->range_max_) - { - // Normalize the value to the 0.0 to 1.0 range. - val = ((val - p->range_min_) - / (p->range_max_ - p->range_min_)); - Audio_control_function_value_change *item - = new Audio_control_function_value_change (p->control_, val); - item->channel_ = channel_; - audio_staff->add_audio_item (item); - announce_element (Audio_element_info (item, 0)); - } - else - warning (_f ("ignoring out-of-range value change for MIDI " - "property `%s'", - p->name_)); - } - } + // Set initial values (if any) for MIDI controls. + Midi_control_initializer i (this, audio_staff, channel_); + i.announce_control_changes (); return audio_staff; } @@ -352,3 +346,24 @@ Staff_performer::acknowledge_audio_element (Audio_element_info inf) } } +Staff_performer::Midi_control_initializer::Midi_control_initializer +(Staff_performer *performer, Audio_staff *audio_staff, int channel) + : performer_ (performer), + audio_staff_ (audio_staff), + channel_ (channel) +{ +} + +SCM Staff_performer::Midi_control_initializer::get_property_value +(const char *property_name) +{ + return performer_->get_property (property_name); +} + +void Staff_performer::Midi_control_initializer::do_announce +(Audio_control_change *item) +{ + item->channel_ = channel_; + audio_staff_->add_audio_item (item); + performer_->announce_element (Audio_element_info (item, 0)); +} |