>From 5c1722b5ab76b5c9319cdc19012e0ab6d0be2d2f Mon Sep 17 00:00:00 2001 From: Heikki Tauriainen 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 . + Adapted from performer implementations + Copyright (C) 1996--2012 Jan Nieuwenhuizen , + Han-Wen Nienhyus 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 . +*/ + +#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 address@hidden") (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 address@hidden@w{-1} address@hidden, +where the address@hidden@w{-1} (@code{#LEFT}),@tie{}0 (@code{#CENTER}) address@hidden (@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 address@hidden@w{-1} address@hidden, +where the address@hidden@w{-1} (@code{#LEFT}),@tie{}0 (@code{#CENTER}) address@hidden (@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 address@hidden +(0=off,@tie{}1=full effect).") + (midiChorusLevel ,number? "Chorus effect level for the MIDI channel +associated with the current context. Ranges from 0 address@hidden +(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