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
This commit is contained in:
Carl Hetherington 2012-03-06 15:08:17 +00:00
parent c6396ca7ce
commit fe86f97563
3 changed files with 64 additions and 68 deletions

View file

@ -259,17 +259,18 @@ GenericMidiControlProtocol::_send_feedback ()
const int32_t bufsize = 16 * 1024; /* XXX too big */ const int32_t bufsize = 16 * 1024; /* XXX too big */
MIDI::byte buf[bufsize]; MIDI::byte buf[bufsize];
int32_t bsize = 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 bool
@ -574,9 +575,6 @@ GenericMidiControlProtocol::get_feedback () const
return do_feedback; return do_feedback;
} }
int int
GenericMidiControlProtocol::load_bindings (const string& xmlpath) GenericMidiControlProtocol::load_bindings (const string& xmlpath)
{ {
@ -682,6 +680,8 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
ev = MIDI::on; ev = MIDI::on;
} else if ((prop = node.property (X_("pgm"))) != 0) { } else if ((prop = node.property (X_("pgm"))) != 0) {
ev = MIDI::program; ev = MIDI::program;
} else if ((prop = node.property (X_("pb"))) != 0) {
ev = MIDI::pitchbend;
} else { } else {
return 0; return 0;
} }

View file

@ -137,30 +137,26 @@ MIDIControllable::stop_learning ()
float float
MIDIControllable::control_to_midi (float val) MIDIControllable::control_to_midi (float val)
{ {
const float midi_range = 127.0f; // TODO: NRPN etc.
if (controllable->is_gain_like()) { 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_min = controllable->lower ();
float control_max = controllable->upper (); float control_max = controllable->upper ();
const float control_range = control_max - control_min; 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 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 /* 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 and can thus represent "middle" precisely as 0.5. this maps to
the range 0..+1.0 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()) { if (controllable->is_gain_like()) {
return slider_position_to_gain (val); return slider_position_to_gain (val);
@ -192,11 +188,9 @@ MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /*is_on*/)
return; return;
} }
if (!controllable->is_toggle()) { if (!controllable->is_toggle()) {
controllable->set_value (msg->note_number/127.0); controllable->set_value (midi_to_control (msg->note_number));
} else { } else {
if (control_additional == msg->note_number) { if (control_additional == msg->note_number) {
controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f); 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()) { if (!controllable->is_toggle()) {
controllable->set_value (msg/127.0); controllable->set_value (midi_to_control (msg));
} else { } else {
controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f); 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()) { if (!controllable->is_toggle()) {
/* XXX gack - get rid of assumption about typeof pitchbend_t */ controllable->set_value (midi_to_control (pb));
controllable->set_value ((pb/(float) SHRT_MAX));
} else { } else {
controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f); 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 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* MIDI::byte*
MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*force*/) MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*force*/)
{ {
if (controllable && control_type != none && feedback && bufsize > 2) { if (!controllable || control_type == none || !feedback || bufsize <= 2) {
return buf;
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;
}
} }
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; return buf;
} }
@ -470,3 +449,17 @@ MIDIControllable::get_state ()
return *node; 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;
}

View file

@ -54,7 +54,6 @@ class MIDIControllable : public PBD::Stateful
uint32_t rid() const { return _rid; } uint32_t rid() const { return _rid; }
std::string what() const { return _what; } std::string what() const { return _what; }
void send_feedback ();
MIDI::byte* write_feedback (MIDI::byte* buf, int32_t& bufsize, bool force = false); MIDI::byte* write_feedback (MIDI::byte* buf, int32_t& bufsize, bool force = false);
void midi_rebind (MIDI::channel_t channel=-1); 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; } MIDI::byte get_control_additional () { return control_additional; }
private: private:
int max_value_for_type () const;
PBD::Controllable* controllable; PBD::Controllable* controllable;
PBD::ControllableDescriptor* _descriptor; PBD::ControllableDescriptor* _descriptor;
std::string _current_uri; std::string _current_uri;
MIDI::Port& _port; MIDI::Port& _port;
bool setting; bool setting;
MIDI::byte last_value; int last_value;
float last_controllable_value; float last_controllable_value;
bool _momentary; bool _momentary;
bool _is_gain_controller; bool _is_gain_controller;
@ -102,6 +104,7 @@ class MIDIControllable : public PBD::Stateful
int midi_msg_id; /* controller ID or note number */ int midi_msg_id; /* controller ID or note number */
PBD::ScopedConnection midi_sense_connection[2]; PBD::ScopedConnection midi_sense_connection[2];
PBD::ScopedConnection midi_learn_connection; PBD::ScopedConnection midi_learn_connection;
/** the type of MIDI message that is used for this control */
MIDI::eventType control_type; MIDI::eventType control_type;
MIDI::byte control_additional; MIDI::byte control_additional;
MIDI::channel_t control_channel; MIDI::channel_t control_channel;