X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fsurfaces%2Fgeneric_midi%2Fmidicontrollable.cc;h=92000a6dee459b3e07ab4c28913cac92e56052d8;hb=52b4b464f9657860e07b224564245ce22ea03df8;hp=1cf32f11f60d6a6fce5c1c16fc715902987393a2;hpb=ffdf5ada616d285fafb58f45c2e3d37b212a328a;p=ardour.git diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc index 1cf32f11f6..92000a6dee 100644 --- a/libs/surfaces/generic_midi/midicontrollable.cc +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -1,6 +1,6 @@ /* Copyright (C) 1998-2006 Paul Davis - + This program 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 2 of the License, or @@ -15,15 +15,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: midicontrollable.cc 629 2006-06-21 23:01:03Z paul $ */ #include /* for sprintf, sigh */ #include -#include -#include -#include -#include +#include "pbd/error.h" +#include "pbd/xml++.h" +#include "midi++/port.h" +#include "midi++/channel.h" +#include "ardour/automation_control.h" #include "midicontrollable.h" @@ -32,19 +32,17 @@ using namespace MIDI; using namespace PBD; using namespace ARDOUR; -bool MIDIControllable::_send_feedback = false; - MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool is_bistate) : controllable (c), _port (p), bistate (is_bistate) { setting = false; - last_written = 0; // got a better idea ? + last_value = 0; // got a better idea ? control_type = none; _control_description = "MIDI Control: none"; control_additional = (byte) -1; connections = 0; feedback = true; // for now - + /* use channel 0 ("1") as the initial channel */ midi_rebind (0); @@ -61,18 +59,18 @@ MIDIControllable::midi_forget () /* stop listening for incoming messages, but retain our existing event + type information. */ - + if (connections > 0) { midi_sense_connection[0].disconnect (); - } - + } + if (connections > 1) { midi_sense_connection[1].disconnect (); } - + connections = 0; midi_learn_connection.disconnect (); - + } void @@ -103,7 +101,7 @@ MIDIControllable::drop_external_control () { if (connections > 0) { midi_sense_connection[0].disconnect (); - } + } if (connections > 1) { midi_sense_connection[1].disconnect (); } @@ -115,20 +113,54 @@ MIDIControllable::drop_external_control () control_additional = (byte) -1; } -void -MIDIControllable::midi_sense_note_on (Parser &p, EventTwoBytes *tb) +float +MIDIControllable::control_to_midi(float val) +{ + float control_min = 0.0f; + float control_max = 1.0f; + ARDOUR::AutomationControl* ac = dynamic_cast(&controllable); + if (ac) { + control_min = ac->parameter().min(); + control_max = ac->parameter().max(); + } + + const float control_range = control_max - control_min; + const float midi_range = 127.0f; // TODO: NRPN etc. + + return (val - control_min) / control_range * midi_range; +} + +float +MIDIControllable::midi_to_control(float val) +{ + float control_min = 0.0f; + float control_max = 1.0f; + ARDOUR::AutomationControl* ac = dynamic_cast(&controllable); + if (ac) { + control_min = ac->parameter().min(); + control_max = ac->parameter().max(); + } + + const float control_range = control_max - control_min; + const float midi_range = 127.0f; // TODO: NRPN etc. + + return val / midi_range * control_range + control_min; +} + +void +MIDIControllable::midi_sense_note_on (Parser &p, EventTwoBytes *tb) { midi_sense_note (p, tb, true); } -void -MIDIControllable::midi_sense_note_off (Parser &p, EventTwoBytes *tb) +void +MIDIControllable::midi_sense_note_off (Parser &p, EventTwoBytes *tb) { midi_sense_note (p, tb, false); } void -MIDIControllable::midi_sense_note (Parser &p, EventTwoBytes *msg, bool is_on) +MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool is_on) { if (!bistate) { controllable.set_value (msg->note_number/127.0); @@ -136,21 +168,27 @@ MIDIControllable::midi_sense_note (Parser &p, EventTwoBytes *msg, bool is_on) /* Note: parser handles the use of zero velocity to mean note off. if we get called with is_on=true, then we - got a *real* note on. + got a *real* note on. */ if (msg->note_number == control_additional) { controllable.set_value (is_on ? 1 : 0); } } + + last_value = (MIDI::byte) (controllable.get_value() * 127.0); // to prevent feedback fights } void MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg) { + if (controllable.touching()) { + return; // to prevent feedback fights when e.g. dragging a UI slider + } + if (control_additional == msg->controller_number) { if (!bistate) { - controllable.set_value (msg->value/127.0); + controllable.set_value (midi_to_control(msg->value)); } else { if (msg->value > 64.0) { controllable.set_value (1); @@ -158,31 +196,35 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg) controllable.set_value (0); } } + + last_value = (MIDI::byte) (control_to_midi(controllable.get_value())); // to prevent feedback fights } } void -MIDIControllable::midi_sense_program_change (Parser &p, byte msg) +MIDIControllable::midi_sense_program_change (Parser &, byte msg) { /* XXX program change messages make no sense for bistates */ if (!bistate) { controllable.set_value (msg/127.0); - } + last_value = (MIDI::byte) (controllable.get_value() * 127.0); // to prevent feedback fights + } } void -MIDIControllable::midi_sense_pitchbend (Parser &p, pitchbend_t pb) +MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb) { /* pitchbend messages make no sense for bistates */ /* XXX gack - get rid of assumption about typeof pitchbend_t */ controllable.set_value ((pb/(float) SHRT_MAX)); -} + last_value = (MIDI::byte) (controllable.get_value() * 127.0); // to prevent feedback fights +} void -MIDIControllable::midi_receiver (Parser &p, byte *msg, size_t len) +MIDIControllable::midi_receiver (Parser &, byte *msg, size_t /*len*/) { /* we only respond to channel messages */ @@ -215,7 +257,7 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional) if (_port.input() == 0) { return; } - + Parser& p = *_port.input(); int chn_i = chn; @@ -242,7 +284,7 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional) midi_sense_connection[0] = p.channel_note_on[chn_i].connect (mem_fun (*this, &MIDIControllable::midi_sense_note_on)); if (bistate) { - midi_sense_connection[1] = p.channel_note_off[chn_i].connect + midi_sense_connection[1] = p.channel_note_off[chn_i].connect (mem_fun (*this, &MIDIControllable::midi_sense_note_off)); connections = 2; } else { @@ -252,7 +294,7 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional) break; case MIDI::controller: - midi_sense_connection[0] = p.channel_controller[chn_i].connect + midi_sense_connection[0] = p.channel_controller[chn_i].connect (mem_fun (*this, &MIDIControllable::midi_sense_controller)); connections = 1; snprintf (buf, sizeof (buf), "MIDI control: Controller %d", control_additional); @@ -262,7 +304,7 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional) case MIDI::program: if (!bistate) { midi_sense_connection[0] = p.channel_program_change[chn_i].connect - (mem_fun (*this, + (mem_fun (*this, &MIDIControllable::midi_sense_program_change)); connections = 1; _control_description = "MIDI control: ProgramChange"; @@ -288,38 +330,38 @@ MIDIControllable::send_feedback () { byte msg[3]; - if (setting || !_send_feedback || control_type == none) { + if (setting || !feedback || control_type == none) { return; } - - msg[0] = (control_type & 0xF0) | (control_channel & 0xF); + + msg[0] = (control_type & 0xF0) | (control_channel & 0xF); msg[1] = control_additional; - msg[2] = (byte) (controllable.get_value() * 127.0f); + msg[2] = (byte) (control_to_midi(controllable.get_value())); - _port.write (msg, 3); + _port.write (msg, 3, 0); } 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 (control_type != none &&_send_feedback && bufsize > 2) { + if (control_type != none && feedback && bufsize > 2) { - MIDI::byte gm = (MIDI::byte) (controllable.get_value() * 127.0); - - if (gm != last_written) { + MIDI::byte gm = (MIDI::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_written = gm; + last_value = gm; bufsize -= 3; } } - + return buf; } -int -MIDIControllable::set_state (const XMLNode& node) +int +MIDIControllable::set_state (const XMLNode& node, int /*version*/) { const XMLProperty* prop; int xx; @@ -345,8 +387,14 @@ MIDIControllable::set_state (const XMLNode& node) return -1; } + if ((prop = node.property ("feedback")) != 0) { + feedback = (prop->value() == "yes"); + } else { + feedback = true; // default + } + bind_midi (control_channel, control_type, control_additional); - + return 0; } @@ -362,6 +410,7 @@ MIDIControllable::get_state () node.add_property ("channel", buf); snprintf (buf, sizeof(buf), "0x%x", (int) control_additional); node.add_property ("additional", buf); + node.add_property ("feedback", (feedback ? "yes" : "no")); return node; }