From fe86f975634f243d2e4088dab633f43182c2b0af Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 6 Mar 2012 15:08:17 +0000 Subject: [PATCH] Work around problems with some JACK<->ALSA midi bridges which don't transfer multiple MIDI messages when they are written in one jack_midi_event_write. Support pitch bend messages in the generic midi control surface code. git-svn-id: svn://localhost/ardour2/branches/3.0@11601 d708f5d6-7413-0410-9779-e7cbd77b26cf --- .../generic_midi_control_protocol.cc | 26 ++--- .../surfaces/generic_midi/midicontrollable.cc | 99 +++++++++---------- libs/surfaces/generic_midi/midicontrollable.h | 7 +- 3 files changed, 64 insertions(+), 68 deletions(-) diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 05a3ef0505..f5f75ce50d 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -259,17 +259,18 @@ GenericMidiControlProtocol::_send_feedback () const int32_t bufsize = 16 * 1024; /* XXX too big */ MIDI::byte buf[bufsize]; int32_t bsize = bufsize; - MIDI::byte* end = buf; - - for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) { - end = (*r)->write_feedback (end, bsize); - } - - if (end == buf) { - return; - } - _output_port->write (buf, (int32_t) (end - buf), 0); + /* XXX: due to bugs in some ALSA / JACK MIDI bridges, we have to do separate + writes for each controllable here; if we send more than one MIDI message + in a single jack_midi_event_write then some bridges will only pass the + first on to ALSA. + */ + for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) { + MIDI::byte* end = (*r)->write_feedback (buf, bsize); + if (end != buf) { + _output_port->write (buf, (int32_t) (end - buf), 0); + } + } } bool @@ -574,9 +575,6 @@ GenericMidiControlProtocol::get_feedback () const return do_feedback; } - - - int GenericMidiControlProtocol::load_bindings (const string& xmlpath) { @@ -682,6 +680,8 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node) ev = MIDI::on; } else if ((prop = node.property (X_("pgm"))) != 0) { ev = MIDI::program; + } else if ((prop = node.property (X_("pb"))) != 0) { + ev = MIDI::pitchbend; } else { return 0; } diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc index 226502f0ae..dd94dfe063 100644 --- a/libs/surfaces/generic_midi/midicontrollable.cc +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -137,30 +137,26 @@ MIDIControllable::stop_learning () float MIDIControllable::control_to_midi (float val) { - const float midi_range = 127.0f; // TODO: NRPN etc. - if (controllable->is_gain_like()) { - return gain_to_slider_position (val/midi_range); + return gain_to_slider_position (val) * max_value_for_type (); } float control_min = controllable->lower (); float control_max = controllable->upper (); const float control_range = control_max - control_min; - return (val - control_min) / control_range * midi_range; + return (val - control_min) / control_range * max_value_for_type (); } float -MIDIControllable::midi_to_control(float val) +MIDIControllable::midi_to_control (float val) { /* fiddle with MIDI value so that we get an odd number of integer steps and can thus represent "middle" precisely as 0.5. this maps to the range 0..+1.0 - - TODO: 14bit values */ - val = (val == 0.0f ? 0.0f : (val-1.0f) / 126.0f); + val = (val == 0.0f ? 0.0f : (val-1.0f) / (max_value_for_type() - 1)); if (controllable->is_gain_like()) { return slider_position_to_gain (val); @@ -192,11 +188,9 @@ MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /*is_on*/) return; } - if (!controllable->is_toggle()) { - controllable->set_value (msg->note_number/127.0); + controllable->set_value (midi_to_control (msg->note_number)); } else { - if (control_additional == msg->note_number) { controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f); } @@ -253,7 +247,7 @@ MIDIControllable::midi_sense_program_change (Parser &, byte msg) } if (!controllable->is_toggle()) { - controllable->set_value (msg/127.0); + controllable->set_value (midi_to_control (msg)); } else { controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f); } @@ -269,13 +263,12 @@ MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb) } if (!controllable->is_toggle()) { - /* XXX gack - get rid of assumption about typeof pitchbend_t */ - controllable->set_value ((pb/(float) SHRT_MAX)); + controllable->set_value (midi_to_control (pb)); } else { controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f); } - last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights + last_value = control_to_midi (controllable->get_value ()); } void @@ -360,48 +353,34 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional) } } -void -MIDIControllable::send_feedback () -{ - byte msg[3]; - - if (!_learned || setting || !feedback || control_type == none || !controllable) { - return; - } - - msg[0] = (control_type & 0xF0) | (control_channel & 0xF); - msg[1] = control_additional; - - if (controllable->is_gain_like()) { - msg[2] = (byte) lrintf (gain_to_slider_position (controllable->get_value()) * 127.0f); - } else { - msg[2] = (byte) (control_to_midi(controllable->get_value())); - } - - _port.write (msg, 3, 0); -} - MIDI::byte* MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*force*/) { - if (controllable && control_type != none && feedback && bufsize > 2) { - - MIDI::byte gm; - - if (controllable->is_gain_like()) { - gm = (byte) lrintf (gain_to_slider_position (controllable->get_value()) * 127.0f); - } else { - gm = (byte) (control_to_midi(controllable->get_value())); - } - - if (gm != last_value) { - *buf++ = (0xF0 & control_type) | (0xF & control_channel); - *buf++ = control_additional; /* controller number */ - *buf++ = gm; - last_value = gm; - bufsize -= 3; - } + if (!controllable || control_type == none || !feedback || bufsize <= 2) { + return buf; } + + float const gm = control_to_midi (controllable->get_value()); + + if (gm == last_value) { + return buf; + } + + *buf++ = (0xF0 & control_type) | (0xF & control_channel); + + switch (control_type) { + case MIDI::pitchbend: + *buf++ = int (gm) & 127; + *buf++ = (int (gm) >> 7) & 127; + break; + default: + *buf++ = control_additional; /* controller number */ + *buf++ = gm; + break; + } + + last_value = gm; + bufsize -= 3; return buf; } @@ -470,3 +449,17 @@ MIDIControllable::get_state () return *node; } +/** @return the maximum value for a control value transmitted + * using a given MIDI::eventType. + */ +int +MIDIControllable::max_value_for_type () const +{ + /* XXX: this is not complete */ + + if (control_type == MIDI::pitchbend) { + return 16383; + } + + return 127; +} diff --git a/libs/surfaces/generic_midi/midicontrollable.h b/libs/surfaces/generic_midi/midicontrollable.h index 5f188d1688..1c2579069e 100644 --- a/libs/surfaces/generic_midi/midicontrollable.h +++ b/libs/surfaces/generic_midi/midicontrollable.h @@ -54,7 +54,6 @@ class MIDIControllable : public PBD::Stateful uint32_t rid() const { return _rid; } std::string what() const { return _what; } - void send_feedback (); MIDI::byte* write_feedback (MIDI::byte* buf, int32_t& bufsize, bool force = false); void midi_rebind (MIDI::channel_t channel=-1); @@ -89,12 +88,15 @@ class MIDIControllable : public PBD::Stateful MIDI::byte get_control_additional () { return control_additional; } private: + + int max_value_for_type () const; + PBD::Controllable* controllable; PBD::ControllableDescriptor* _descriptor; std::string _current_uri; MIDI::Port& _port; bool setting; - MIDI::byte last_value; + int last_value; float last_controllable_value; bool _momentary; bool _is_gain_controller; @@ -102,6 +104,7 @@ class MIDIControllable : public PBD::Stateful int midi_msg_id; /* controller ID or note number */ PBD::ScopedConnection midi_sense_connection[2]; PBD::ScopedConnection midi_learn_connection; + /** the type of MIDI message that is used for this control */ MIDI::eventType control_type; MIDI::byte control_additional; MIDI::channel_t control_channel;