summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeikki Tauriainen <g034737@welho.com>2016-01-07 22:34:56 +0200
committerJames Lowe <pkx166h@gmail.com>2016-07-27 22:54:15 +0100
commit14de2f189e75424bd0b04f4577386b4f0dde1a6e (patch)
treea9b89f87ee3971e7d04a6a179ec2e78bdac37947
parent528d28e079419b3a45098b42b8b2b4eb9f1b15b1 (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.cc18
-rw-r--r--lily/include/audio-item.hh32
-rw-r--r--lily/include/lily-proto.hh3
-rw-r--r--lily/include/midi-cc-announcer.hh64
-rw-r--r--lily/include/midi-item.hh14
-rw-r--r--lily/midi-cc-announcer.cc110
-rw-r--r--lily/midi-control-function-performer.cc90
-rw-r--r--lily/midi-item.cc74
-rw-r--r--lily/staff-performer.cc67
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));
+}