Hi,

Here are the Git-formatted patches for issue 3581
<http://code.google.com/p/lilypond/issues/detail?id=3581>, corresponding
to the latest patchset on Rietveld
<https://codereview.appspot.com/14434043>.

(The patches reached the "push" status on 16th October, but, if this
means that they have now also been approved for inclusion in the
codebase, they still need to be picked up by someone who can push them
to staging since I don't have the access rights to do this.)

Thanks to the development team for all the comments and help that I got
during the patch submission and review process.  I'm sorry for all the
extra work that I may have caused you especially in starting it - thanks
for the patience!

-- 
Heikki Tauriainen


>From 5cf487c93861c600c1cb9ea8fdc9345d7967db93 Mon Sep 17 00:00:00 2001
From: Heikki Tauriainen <g034...@welho.com>
Date: Sat, 19 Oct 2013 09:56:37 +0300
Subject: [PATCH 1/5] Issue 3581: New data types for adjusting MIDI control
 functions

---
 lily/audio-item.cc         | 16 ++++++++++
 lily/include/audio-item.hh | 32 ++++++++++++++++++++
 lily/include/lily-proto.hh |  2 ++
 lily/include/midi-item.hh  | 15 ++++++++++
 lily/midi-item.cc          | 73 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 138 insertions(+)

diff --git a/lily/audio-item.cc b/lily/audio-item.cc
index b7a5ef2..a41357b 100644
--- a/lily/audio-item.cc
+++ b/lily/audio-item.cc
@@ -204,3 +204,19 @@ 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 },
+  { "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 }
+};
diff --git a/lily/include/audio-item.hh b/lily/include/audio-item.hh
index 544dd83..43ed2e0 100644
--- a/lily/include/audio-item.hh
+++ b/lily/include/audio-item.hh
@@ -137,6 +137,38 @@ public:
   int one_beat_;
 };
 
+class Audio_control_function_value_change : public Audio_item
+{
+public:
+  // Supported control functions.
+  enum Control
+  {
+    BALANCE = 0, PAN_POSITION, 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_;
+};
+
 int moment_to_ticks (Moment);
 Real moment_to_real (Moment);
 Moment remap_grace_duration (Moment);
diff --git a/lily/include/lily-proto.hh b/lily/include/lily-proto.hh
index 53e863c..80240d5 100644
--- a/lily/include/lily-proto.hh
+++ b/lily/include/lily-proto.hh
@@ -24,6 +24,7 @@
 
 class All_font_metrics;
 class Audio_column;
+class Audio_control_function_value_change;
 class Audio_dynamic;
 class Audio_element;
 class Audio_instrument;
@@ -104,6 +105,7 @@ class Lyric_performer;
 class Lyric_phrasing_engraver;
 class Mensural_ligature_engraver;
 class Midi_chunk;
+class Midi_control_function_value_change;
 class Midi_duration;
 class Midi_dynamic;
 class Midi_event;
diff --git a/lily/include/midi-item.hh b/lily/include/midi-item.hh
index c5a9cc9..492e2e9 100644
--- a/lily/include/midi-item.hh
+++ b/lily/include/midi-item.hh
@@ -46,11 +46,26 @@ public:
 class Midi_channel_item : public Midi_item
 {
 public:
+  virtual ~Midi_channel_item ();
   int channel_;
   DECLARE_CLASSNAME (Midi_channel_item);
   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-item.cc b/lily/midi-item.cc
index 0d0edbb..c17c369 100644
--- a/lily/midi-item.cc
+++ b/lily/midi-item.cc
@@ -50,6 +50,9 @@ 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
     assert (0);
 
@@ -102,10 +105,24 @@ 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_item::~Midi_item ()
 {
 }
 
+Midi_channel_item::~Midi_channel_item ()
+{
+}
+
+Midi_control_function_value_change::~Midi_control_function_value_change ()
+{
+}
+
 string
 int2midi_varint_string (int i)
 {
@@ -342,6 +359,62 @@ Midi_text::to_string () const
   return str;
 }
 
+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
+      { 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);
+  int value = lround (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;
+}
+
 char const *
 Midi_item::name () const
 {
-- 
1.8.4.rc3

>From 5acadd20d3393c9c4c9140f168a31d82611bac2f Mon Sep 17 00:00:00 2001
From: Heikki Tauriainen <g034...@welho.com>
Date: Sat, 19 Oct 2013 10:01:06 +0300
Subject: [PATCH 2/5] Issue 3581: Allow overriding
 Translator::{connect_to,disconnect_from}_context

This is needed to make it easier to implement a custom translator that will
listen to SetProperty events.
---
 lily/include/translator.hh | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/lily/include/translator.hh b/lily/include/translator.hh
index 41e2d97..be6781e 100644
--- a/lily/include/translator.hh
+++ b/lily/include/translator.hh
@@ -136,9 +136,8 @@ public:
   virtual void initialize ();
   virtual void finalize ();
 
-  /* should maybe be virtual */
-  void connect_to_context (Context *c);
-  void disconnect_from_context (Context *c);
+  virtual void connect_to_context (Context *c);
+  virtual void disconnect_from_context (Context *c);
 
   void stop_translation_timestep ();
   void start_translation_timestep ();
-- 
1.8.4.rc3

>From 5c1722b5ab76b5c9319cdc19012e0ab6d0be2d2f Mon Sep 17 00:00:00 2001
From: Heikki Tauriainen <g034...@welho.com>
Date: Sat, 19 Oct 2013 10:10:52 +0300
Subject: [PATCH 3/5] Issue 3581: Add support for changing MIDI balance, pan
 position, reverb and chorus levels

---
 lily/midi-control-function-performer.cc | 133 ++++++++++++++++++++++++++++++++
 lily/staff-performer.cc                 |  26 +++++++
 ly/performer-init.ly                    |   4 +
 scm/define-context-properties.scm       |  16 ++++
 4 files changed, 179 insertions(+)
 create mode 100644 lily/midi-control-function-performer.cc

diff --git a/lily/midi-control-function-performer.cc b/lily/midi-control-function-performer.cc
new file mode 100644
index 0000000..ff0855d
--- /dev/null
+++ b/lily/midi-control-function-performer.cc
@@ -0,0 +1,133 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2013 by Heikki Tauriainen <g034...@welho.com>.
+  Adapted from performer implementations
+  Copyright (C) 1996--2012 Jan Nieuwenhuizen <jann...@gnu.org>,
+  Han-Wen Nienhyus <han...@xs4all.nl> and others.
+
+  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 "performer.hh"
+
+#include "audio-item.hh"
+#include "context.hh"
+#include "dispatcher.hh"
+#include "international.hh"
+#include "listener.hh"
+#include "stream-event.hh"
+
+#include "translator.icc"
+
+/**
+   MIDI control function performer.  Announces "set property" events on MIDI
+   context properties.
+*/
+class Midi_control_function_performer : public Performer
+{
+public:
+  TRANSLATOR_DECLARATIONS (Midi_control_function_performer);
+  DECLARE_LISTENER (announce_function_value_change);
+  ~Midi_control_function_performer ();
+
+  void connect_to_context (Context *c);
+  void disconnect_from_context (Context *c);
+};
+
+Midi_control_function_performer::Midi_control_function_performer ()
+{
+}
+
+Midi_control_function_performer::~Midi_control_function_performer ()
+{
+}
+
+void
+Midi_control_function_performer::connect_to_context (Context *c)
+{
+  c->events_below ()->
+    add_listener (GET_LISTENER (announce_function_value_change),
+                  ly_symbol2scm ("SetProperty"));
+}
+
+void
+Midi_control_function_performer::disconnect_from_context (Context *c)
+{
+  c->events_below ()->
+    remove_listener (GET_LISTENER (announce_function_value_change),
+                     ly_symbol2scm ("SetProperty"));
+}
+
+IMPLEMENT_LISTENER (Midi_control_function_performer,
+                    announce_function_value_change)
+void
+Midi_control_function_performer::announce_function_value_change (SCM sev)
+{
+  Stream_event *ev = unsmob_stream_event (sev);
+  SCM sym = ev->get_property ("symbol");
+  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;
+        }
+    }
+}
+
+ADD_TRANSLATOR (Midi_control_function_performer,
+                /* doc */
+                "",
+
+                /* create */
+                "",
+
+                /* read */
+                "midiBalance "
+                "midiPanPosition "
+                "midiReverbLevel "
+                "midiChorusLevel ",
+
+                /* write */
+                ""
+               );
diff --git a/lily/staff-performer.cc b/lily/staff-performer.cc
index c06ad9b..4daa2ca 100644
--- a/lily/staff-performer.cc
+++ b/lily/staff-performer.cc
@@ -128,6 +128,32 @@ 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_));
+        }
+    }
   return audio_staff;
 }
 
diff --git a/ly/performer-init.ly b/ly/performer-init.ly
index 816bb2f..0a1ac2d 100644
--- a/ly/performer-init.ly
+++ b/ly/performer-init.ly
@@ -30,6 +30,7 @@
 
   \consists "Staff_performer"
   \consists "Key_performer"
+  \consists "Midi_control_function_performer"
 }
 
 \context {
@@ -48,6 +49,7 @@
   \alias Staff
   \consists "Staff_performer"
   \consists "Key_performer"
+  \consists "Midi_control_function_performer"
 }
 
 \context {
@@ -59,6 +61,7 @@
   \defaultchild VaticanaVoice
   \consists "Staff_performer"
   \consists "Key_performer"
+  \consists "Midi_control_function_performer"
 }
 
 \context {
@@ -70,6 +73,7 @@
   \alias Staff
   \consists "Staff_performer"
   \consists "Key_performer"
+  \consists "Midi_control_function_performer"
 }
 
 \context {
diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm
index 1cbd9fb..0104802 100644
--- a/scm/define-context-properties.scm
+++ b/scm/define-context-properties.scm
@@ -433,6 +433,22 @@ event when notes with the same pitch, in the same MIDI-file track, overlap.")
      (midiMinimumVolume ,number? "Set the minimum loudness for MIDI.
 Ranges from 0 to@tie{}1.")
      (midiChannelMapping ,symbol? "How to map MIDI channels: per @code{instrument} (default), @code{staff} or @code{voice}.")
+     (midiBalance ,number? "Stereo balance for the MIDI channel
+associated with the current context.  Ranges from@tie{}@w{-1} to@tie{}1,
+where the values@tie{}@w{-1} (@code{#LEFT}),@tie{}0 (@code{#CENTER})
+and@tie{}1 (@code{#RIGHT}) correspond to leftmost emphasis, center
+balance, and rightmost emphasis, respectively.")
+     (midiPanPosition ,number? "Pan position for the MIDI channel
+associated with the current context.  Ranges from@tie{}@w{-1} to@tie{}1,
+where the values@tie{}@w{-1} (@code{#LEFT}),@tie{}0 (@code{#CENTER})
+and@tie{}1 (@code{#RIGHT}) correspond to hard left, center, and hard
+right, respectively.")
+     (midiReverbLevel ,number? "Reverb effect level for the MIDI channel
+associated with the current context.  Ranges from 0 to@tie{}1
+(0=off,@tie{}1=full effect).")
+     (midiChorusLevel ,number? "Chorus effect level for the MIDI channel
+associated with the current context.  Ranges from 0 to@tie{}1
+(0=off,@tie{}1=full effect).")
      (minimumFret ,number? "The tablature auto string-selecting
 mechanism selects the highest string with a fret at least
 @code{minimumFret}.")
-- 
1.8.4.rc3

>From 2c4f2f075d092fcde2396b5b4d62bd345f6af8e9 Mon Sep 17 00:00:00 2001
From: Heikki Tauriainen <g034...@welho.com>
Date: Sat, 19 Oct 2013 10:13:43 +0300
Subject: [PATCH 4/5] Fix the default value description for midiChannelMapping

The current default value is "staff", not "instrument" (noted during
the review of patches related to issue 3581).
---
 scm/define-context-properties.scm | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm
index 0104802..a0d3b98 100644
--- a/scm/define-context-properties.scm
+++ b/scm/define-context-properties.scm
@@ -432,7 +432,8 @@ event when notes with the same pitch, in the same MIDI-file track, overlap.")
 @code{midiMinimumVolume}.")
      (midiMinimumVolume ,number? "Set the minimum loudness for MIDI.
 Ranges from 0 to@tie{}1.")
-     (midiChannelMapping ,symbol? "How to map MIDI channels: per @code{instrument} (default), @code{staff} or @code{voice}.")
+     (midiChannelMapping ,symbol? "How to map MIDI channels: per
+@code{staff} (default), @code{instrument} or @code{voice}.")
      (midiBalance ,number? "Stereo balance for the MIDI channel
 associated with the current context.  Ranges from@tie{}@w{-1} to@tie{}1,
 where the values@tie{}@w{-1} (@code{#LEFT}),@tie{}0 (@code{#CENTER})
-- 
1.8.4.rc3

>From 87399700d48a0cd63d9e33db749dde862ddb8875 Mon Sep 17 00:00:00 2001
From: Heikki Tauriainen <g034...@welho.com>
Date: Sat, 19 Oct 2013 10:20:10 +0300
Subject: [PATCH 5/5] Remove definition of the "instrumentName" property from
 Score_performer

This setting has no effect; furthermore, according to the discussion on
issue 3581 and the related patch review comments, no default settings for
MIDI controls should ever be enforced if they are not set explicitly.
---
 ly/performer-init.ly | 1 -
 1 file changed, 1 deletion(-)

diff --git a/ly/performer-init.ly b/ly/performer-init.ly
index 0a1ac2d..2d0d4d4 100644
--- a/ly/performer-init.ly
+++ b/ly/performer-init.ly
@@ -197,7 +197,6 @@
   \name Score
 
   melismaBusyProperties = #default-melisma-properties
-  instrumentName = #"bright acoustic"
   midiChannelMapping = #'staff
 
   %% quarter = 60
-- 
1.8.4.rc3

_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to