From 79986643c0c904f6574bb5323e2233a43a9e622e Mon Sep 17 00:00:00 2001 From: Hans Fugal Date: Fri, 4 Aug 2006 02:18:45 +0000 Subject: r269@gandalf: fugalh | 2006-08-03 20:18:05 -0600 Trunk merge conflicts resolved git-svn-id: svn://localhost/ardour2/branches/undo@756 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/surfaces/control_protocol/SConscript | 2 +- libs/surfaces/control_protocol/control_protocol.cc | 51 +-- .../control_protocol/control_protocol.h | 9 +- libs/surfaces/generic_midi/SConscript | 3 +- .../generic_midi/generic_midi_control_protocol.cc | 218 ++++++++++-- .../generic_midi/generic_midi_control_protocol.h | 44 ++- libs/surfaces/generic_midi/interface.cc | 10 +- libs/surfaces/generic_midi/midicontrollable.cc | 366 +++++++++++++++++++++ libs/surfaces/generic_midi/midicontrollable.h | 97 ++++++ libs/surfaces/tranzport/SConscript | 2 +- .../tranzport/tranzport_control_protocol.cc | 16 +- .../tranzport/tranzport_control_protocol.h | 3 + 12 files changed, 752 insertions(+), 69 deletions(-) create mode 100644 libs/surfaces/generic_midi/midicontrollable.cc create mode 100644 libs/surfaces/generic_midi/midicontrollable.h (limited to 'libs/surfaces') diff --git a/libs/surfaces/control_protocol/SConscript b/libs/surfaces/control_protocol/SConscript index f8a7d574f5..ce59b1c67c 100644 --- a/libs/surfaces/control_protocol/SConscript +++ b/libs/surfaces/control_protocol/SConscript @@ -34,7 +34,7 @@ cp.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") cp.Merge ([ libraries['ardour'], libraries['sigc2'], - libraries['pbd3'], + libraries['pbd'], libraries['midi++2'], libraries['xml'], libraries['usb'], diff --git a/libs/surfaces/control_protocol/control_protocol.cc b/libs/surfaces/control_protocol/control_protocol.cc index 10295bc698..dd9adc206e 100644 --- a/libs/surfaces/control_protocol/control_protocol.cc +++ b/libs/surfaces/control_protocol/control_protocol.cc @@ -49,7 +49,7 @@ void ControlProtocol::next_track (uint32_t initial_id) { uint32_t limit = session->nroutes(); - Route* cr = route_table[0]; + boost::shared_ptr cr = route_table[0]; uint32_t id; if (cr) { @@ -88,7 +88,7 @@ void ControlProtocol::prev_track (uint32_t initial_id) { uint32_t limit = session->nroutes() - 1; - Route* cr = route_table[0]; + boost::shared_ptr cr = route_table[0]; uint32_t id; if (cr) { @@ -128,29 +128,32 @@ void ControlProtocol::set_route_table_size (uint32_t size) { while (route_table.size() < size) { - route_table.push_back (0); + route_table.push_back (boost::shared_ptr ((Route*) 0)); } } void -ControlProtocol::set_route_table (uint32_t table_index, ARDOUR::Route*) +ControlProtocol::set_route_table (uint32_t table_index, boost::shared_ptr r) { + if (table_index >= route_table.size()) { + return; + } + + route_table[table_index] = r; + + // XXX SHAREDPTR need to handle r->GoingAway } bool ControlProtocol::set_route_table (uint32_t table_index, uint32_t remote_control_id) { - if (table_index >= route_table.size()) { - return false; - } - - Route* r = session->route_by_remote_id (remote_control_id); + boost::shared_ptr r = session->route_by_remote_id (remote_control_id); if (!r) { return false; } - - route_table[table_index] = r; + + set_route_table (table_index, r); return true; } @@ -162,9 +165,9 @@ ControlProtocol::route_set_rec_enable (uint32_t table_index, bool yn) return; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; - AudioTrack* at = dynamic_cast(r); + boost::shared_ptr at = boost::dynamic_pointer_cast(r); if (at) { at->set_record_enable (yn, this); @@ -178,9 +181,9 @@ ControlProtocol::route_get_rec_enable (uint32_t table_index) return false; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; - AudioTrack* at = dynamic_cast(r); + boost::shared_ptr at = boost::dynamic_pointer_cast(r); if (at) { return at->record_enabled (); @@ -197,7 +200,7 @@ ControlProtocol::route_get_gain (uint32_t table_index) return 0.0f; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r == 0) { return 0.0f; @@ -213,7 +216,7 @@ ControlProtocol::route_set_gain (uint32_t table_index, float gain) return; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r != 0) { r->set_gain (gain, this); @@ -227,7 +230,7 @@ ControlProtocol::route_get_effective_gain (uint32_t table_index) return 0.0f; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r == 0) { return 0.0f; @@ -244,7 +247,7 @@ ControlProtocol::route_get_peak_input_power (uint32_t table_index, uint32_t whic return 0.0f; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r == 0) { return 0.0f; @@ -261,7 +264,7 @@ ControlProtocol::route_get_muted (uint32_t table_index) return false; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r == 0) { return false; @@ -277,7 +280,7 @@ ControlProtocol::route_set_muted (uint32_t table_index, bool yn) return; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r != 0) { r->set_mute (yn, this); @@ -292,7 +295,7 @@ ControlProtocol::route_get_soloed (uint32_t table_index) return false; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r == 0) { return false; @@ -308,7 +311,7 @@ ControlProtocol::route_set_soloed (uint32_t table_index, bool yn) return; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r != 0) { r->set_solo (yn, this); @@ -322,7 +325,7 @@ ControlProtocol:: route_get_name (uint32_t table_index) return ""; } - Route* r = route_table[table_index]; + boost::shared_ptr r = route_table[table_index]; if (r == 0) { return ""; diff --git a/libs/surfaces/control_protocol/control_protocol/control_protocol.h b/libs/surfaces/control_protocol/control_protocol/control_protocol.h index 69135f2b4b..8be652b9df 100644 --- a/libs/surfaces/control_protocol/control_protocol/control_protocol.h +++ b/libs/surfaces/control_protocol/control_protocol/control_protocol.h @@ -25,8 +25,9 @@ #include #include #include +#include #include - +#include #include namespace ARDOUR { @@ -34,7 +35,7 @@ namespace ARDOUR { class Route; class Session; -class ControlProtocol : public sigc::trackable, public BasicUI { +class ControlProtocol : public sigc::trackable, public Stateful, public BasicUI { public: ControlProtocol (Session&, std::string name); virtual ~ControlProtocol(); @@ -73,7 +74,7 @@ class ControlProtocol : public sigc::trackable, public BasicUI { */ void set_route_table_size (uint32_t size); - void set_route_table (uint32_t table_index, ARDOUR::Route*); + void set_route_table (uint32_t table_index, boost::shared_ptr); bool set_route_table (uint32_t table_index, uint32_t remote_control_id); void route_set_rec_enable (uint32_t table_index, bool yn); @@ -94,7 +95,7 @@ class ControlProtocol : public sigc::trackable, public BasicUI { std::string route_get_name (uint32_t table_index); protected: - std::vector route_table; + std::vector > route_table; std::string _name; bool _active; diff --git a/libs/surfaces/generic_midi/SConscript b/libs/surfaces/generic_midi/SConscript index 1760eb24e9..f9c2de08f8 100644 --- a/libs/surfaces/generic_midi/SConscript +++ b/libs/surfaces/generic_midi/SConscript @@ -23,6 +23,7 @@ genericmidi.Append(POTFILE = domain + '.pot') genericmidi_files=Split(""" interface.cc generic_midi_control_protocol.cc +midicontrollable.cc """) genericmidi.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE") @@ -34,7 +35,7 @@ genericmidi.Merge ([ libraries['ardour'], libraries['ardour_cp'], libraries['midi++2'], - libraries['pbd3'], + libraries['pbd'], libraries['sigc2'], libraries['usb'], libraries['xml'], diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 95b9d22393..d905c0bc41 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -1,20 +1,65 @@ +/* + Copyright (C) 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 + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include + +#include +#include + #include +#include +#include #include #include #include "generic_midi_control_protocol.h" +#include "midicontrollable.h" using namespace ARDOUR; +using namespace PBD; #include "i18n.h" GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s) : ControlProtocol (s, _("GenericMIDI")) { - _port = s.midi_port(); - s.MIDI_PortChanged.connect (mem_fun (*this, &GenericMidiControlProtocol::port_change)); + MIDI::Manager* mm = MIDI::Manager::instance(); + + /* XXX it might be nice to run "control" through i18n, but thats a bit tricky because + the name is defined in ardour.rc which is likely not internationalized. + */ + _port = mm->port (X_("control")); + + if (_port == 0) { + error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg; + throw failed_constructor(); + } + + _feedback_interval = 10000; // microseconds + last_feedback_time = 0; + + Controllable::StartLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::start_learning)); + Controllable::StopLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::stop_learning)); + Session::SendFeedback.connect (mem_fun (*this, &GenericMidiControlProtocol::send_feedback)); } GenericMidiControlProtocol::~GenericMidiControlProtocol () @@ -24,42 +69,165 @@ GenericMidiControlProtocol::~GenericMidiControlProtocol () int GenericMidiControlProtocol::set_active (bool yn) { - /* start delivery/outbound thread */ + /* start/stop delivery/outbound thread */ return 0; } void -GenericMidiControlProtocol::port_change () +GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms) { - _port = session->midi_port (); + _feedback_interval = ms; } -void -GenericMidiControlProtocol::set_port (MIDI::Port* p) +void +GenericMidiControlProtocol::send_feedback () { - _port = p; + microseconds_t now = get_microseconds (); + + if (last_feedback_time != 0) { + if ((now - last_feedback_time) < _feedback_interval) { + return; + } + } + + _send_feedback (); + + last_feedback_time = now; } void -GenericMidiControlProtocol::send_route_feedback (list& routes) +GenericMidiControlProtocol::_send_feedback () { - if (_port != 0) { - - const int32_t bufsize = 16 * 1024; - MIDI::byte buf[bufsize]; - int32_t bsize = bufsize; - MIDI::byte* end = buf; - - for (list::iterator r = routes.begin(); r != routes.end(); ++r) { - end = (*r)->write_midi_feedback (end, bsize); + const int32_t bufsize = 16 * 1024; + 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; + } + + _port->write (buf, (int32_t) (end - buf)); +} + +bool +GenericMidiControlProtocol::start_learning (Controllable* c) +{ + if (c == 0) { + return false; + } + + MIDIControllable* mc = new MIDIControllable (*_port, *c); + + { + Glib::Mutex::Lock lm (pending_lock); + std::pair result; + result = pending_controllables.insert (mc); + if (result.second) { + c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc)); } - - if (end == buf) { - return; - } - - _port->write (buf, (int32_t) (end - buf)); - //cerr << "MIDI feedback: wrote " << (int32_t) (end - buf) << " to midi port\n"; } + + mc->learn_about_external_control (); + return true; } +void +GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc) +{ + Glib::Mutex::Lock lm (pending_lock); + Glib::Mutex::Lock lm2 (controllables_lock); + + MIDIControllables::iterator i = find (pending_controllables.begin(), pending_controllables.end(), mc); + + if (i != pending_controllables.end()) { + pending_controllables.erase (i); + } + + controllables.insert (mc); +} + +void +GenericMidiControlProtocol::stop_learning (Controllable* c) +{ + Glib::Mutex::Lock lm (pending_lock); + + /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the + relevant MIDIControllable and remove it from the pending list. + */ + + for (MIDIControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) { + if (&(*i)->get_controllable() == c) { + (*i)->stop_learning (); + delete (*i); + pending_controllables.erase (i); + break; + } + } +} + +XMLNode& +GenericMidiControlProtocol::get_state () +{ + XMLNode* node = new XMLNode (_name); /* node name must match protocol name */ + XMLNode* children = new XMLNode (X_("controls")); + + node->add_child_nocopy (*children); + + Glib::Mutex::Lock lm2 (controllables_lock); + for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) { + children->add_child_nocopy ((*i)->get_state()); + } + + return *node; +} + +int +GenericMidiControlProtocol::set_state (const XMLNode& node) +{ + XMLNodeList nlist; + XMLNodeConstIterator niter; + Controllable* c; + + { + Glib::Mutex::Lock lm (pending_lock); + pending_controllables.clear (); + } + + Glib::Mutex::Lock lm2 (controllables_lock); + + controllables.clear (); + + nlist = node.children(); + + if (nlist.empty()) { + return 0; + } + + nlist = nlist.front()->children (); + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + + XMLProperty* prop; + + if ((prop = (*niter)->property ("id")) != 0) { + + ID id = prop->value (); + + c = session->controllable_by_id (id); + + if (c) { + MIDIControllable* mc = new MIDIControllable (*_port, *c); + if (mc->set_state (**niter) == 0) { + controllables.insert (mc); + } + } + } + } + + return 0; +} diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.h b/libs/surfaces/generic_midi/generic_midi_control_protocol.h index 70cbd181c8..5f5a470b13 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.h +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.h @@ -1,34 +1,58 @@ #ifndef ardour_generic_midi_control_protocol_h #define ardour_generic_midi_control_protocol_h +#include +#include +#include + #include namespace MIDI { class Port; } +namespace PBD { + class Controllable; +} + namespace ARDOUR { + class Session; +} -class GenericMidiControlProtocol : public ControlProtocol { +class MIDIControllable; + +class GenericMidiControlProtocol : public ARDOUR::ControlProtocol { public: - GenericMidiControlProtocol (Session&); + GenericMidiControlProtocol (ARDOUR::Session&); virtual ~GenericMidiControlProtocol(); int set_active (bool yn); static bool probe() { return true; } - void set_port (MIDI::Port*); MIDI::Port* port () const { return _port; } + void set_feedback_interval (ARDOUR::microseconds_t); + + XMLNode& get_state (); + int set_state (const XMLNode&); - void send_route_feedback (std::list&); - private: - void route_feedback (ARDOUR::Route&, bool); MIDI::Port* _port; + ARDOUR::microseconds_t _feedback_interval; + ARDOUR::microseconds_t last_feedback_time; - void port_change (); -}; + void _send_feedback (); + void send_feedback (); -} + typedef std::set MIDIControllables; + MIDIControllables controllables; + MIDIControllables pending_controllables; + Glib::Mutex controllables_lock; + Glib::Mutex pending_lock; + + bool start_learning (PBD::Controllable*); + void stop_learning (PBD::Controllable*); + + void learning_stopped (MIDIControllable*); +}; -#endif // ardour_generic_midi_control_protocol_h +#endif /* ardour_generic_midi_control_protocol_h */ diff --git a/libs/surfaces/generic_midi/interface.cc b/libs/surfaces/generic_midi/interface.cc index c6c59c6589..230be694f2 100644 --- a/libs/surfaces/generic_midi/interface.cc +++ b/libs/surfaces/generic_midi/interface.cc @@ -1,3 +1,5 @@ +#include + #include #include "generic_midi_control_protocol.h" @@ -6,7 +8,13 @@ using namespace ARDOUR; ControlProtocol* new_generic_midi_protocol (ControlProtocolDescriptor* descriptor, Session* s) { - GenericMidiControlProtocol* gmcp = new GenericMidiControlProtocol (*s); + GenericMidiControlProtocol* gmcp; + + try { + gmcp = new GenericMidiControlProtocol (*s); + } catch (failed_constructor& err) { + return 0; + } if (gmcp->set_active (true)) { delete gmcp; diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc new file mode 100644 index 0000000000..d6135fd2a8 --- /dev/null +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -0,0 +1,366 @@ +/* + 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 + (at your option) any later version. + + This program 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 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 "midicontrollable.h" + +using namespace sigc; +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 ? + 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); +} + +MIDIControllable::~MIDIControllable () +{ + drop_external_control (); +} + +void +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 +MIDIControllable::midi_rebind (channel_t c) +{ + if (c >= 0) { + bind_midi (c, control_type, control_additional); + } else { + midi_forget (); + } +} + +void +MIDIControllable::learn_about_external_control () +{ + drop_external_control (); + midi_learn_connection = _port.input()->any.connect (mem_fun (*this, &MIDIControllable::midi_receiver)); +} + +void +MIDIControllable::stop_learning () +{ + midi_learn_connection.disconnect (); +} + +void +MIDIControllable::drop_external_control () +{ + if (connections > 0) { + midi_sense_connection[0].disconnect (); + } + if (connections > 1) { + midi_sense_connection[1].disconnect (); + } + + connections = 0; + midi_learn_connection.disconnect (); + + control_type = none; + control_additional = (byte) -1; +} + +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) +{ + midi_sense_note (p, tb, false); +} + +void +MIDIControllable::midi_sense_note (Parser &p, EventTwoBytes *msg, bool is_on) +{ + if (!bistate) { + controllable.set_value (msg->note_number/127.0); + } else { + + /* 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. + */ + + if (msg->note_number == control_additional) { + controllable.set_value (is_on ? 1 : 0); + } + } +} + +void +MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg) +{ + if (control_additional == msg->controller_number) { + if (!bistate) { + controllable.set_value (msg->value/127.0); + } else { + if (msg->value > 64.0) { + controllable.set_value (1); + } else { + controllable.set_value (0); + } + } + } +} + +void +MIDIControllable::midi_sense_program_change (Parser &p, byte msg) +{ + /* XXX program change messages make no sense for bistates */ + + if (!bistate) { + controllable.set_value (msg/127.0); + } +} + +void +MIDIControllable::midi_sense_pitchbend (Parser &p, 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)); +} + +void +MIDIControllable::midi_receiver (Parser &p, byte *msg, size_t len) +{ + /* we only respond to channel messages */ + + if ((msg[0] & 0xF0) < 0x80 || (msg[0] & 0xF0) > 0xE0) { + return; + } + + /* if the our port doesn't do input anymore, forget it ... */ + + if (!_port.input()) { + return; + } + + bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]); + + controllable.LearningFinished (); +} + +void +MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional) +{ + char buf[64]; + + drop_external_control (); + + control_type = ev; + control_channel = chn; + control_additional = additional; + + if (_port.input() == 0) { + return; + } + + Parser& p = *_port.input(); + + int chn_i = chn; + switch (ev) { + case MIDI::off: + midi_sense_connection[0] = p.channel_note_off[chn_i].connect + (mem_fun (*this, &MIDIControllable::midi_sense_note_off)); + + /* if this is a bistate, connect to noteOn as well, + and we'll toggle back and forth between the two. + */ + + if (bistate) { + midi_sense_connection[1] = p.channel_note_on[chn_i].connect + (mem_fun (*this, &MIDIControllable::midi_sense_note_on)); + connections = 2; + } else { + connections = 1; + } + _control_description = "MIDI control: NoteOff"; + break; + + case MIDI::on: + 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 + (mem_fun (*this, &MIDIControllable::midi_sense_note_off)); + connections = 2; + } else { + connections = 1; + } + _control_description = "MIDI control: NoteOn"; + break; + + case MIDI::controller: + 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); + _control_description = buf; + break; + + case MIDI::program: + if (!bistate) { + midi_sense_connection[0] = p.channel_program_change[chn_i].connect + (mem_fun (*this, + &MIDIControllable::midi_sense_program_change)); + connections = 1; + _control_description = "MIDI control: ProgramChange"; + } + break; + + case MIDI::pitchbend: + if (!bistate) { + midi_sense_connection[0] = p.channel_pitchbend[chn_i].connect + (mem_fun (*this, &MIDIControllable::midi_sense_pitchbend)); + connections = 1; + _control_description = "MIDI control: Pitchbend"; + } + break; + + default: + break; + } +} + +void +MIDIControllable::send_feedback () +{ + byte msg[3]; + + if (setting || !_send_feedback || control_type == none) { + return; + } + + msg[0] = (control_type & 0xF0) | (control_channel & 0xF); + msg[1] = control_additional; + msg[2] = (byte) (controllable.get_value() * 127.0f); + + _port.write (msg, 3); +} + +MIDI::byte* +MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool force) +{ + if (control_type != none &&_send_feedback && bufsize > 2) { + + MIDI::byte gm = (MIDI::byte) (controllable.get_value() * 127.0); + + if (gm != last_written) { + *buf++ = (0xF0 & control_type) | (0xF & control_channel); + *buf++ = control_additional; /* controller number */ + *buf++ = gm; + last_written = gm; + bufsize -= 3; + } + } + + return buf; +} + +int +MIDIControllable::set_state (const XMLNode& node) +{ + const XMLProperty* prop; + int xx; + + if ((prop = node.property ("event")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &xx); + control_type = (MIDI::eventType) xx; + } else { + return -1; + } + + if ((prop = node.property ("channel")) != 0) { + sscanf (prop->value().c_str(), "%d", &xx); + control_channel = (MIDI::channel_t) xx; + } else { + return -1; + } + + if ((prop = node.property ("additional")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &xx); + control_additional = (MIDI::byte) xx; + } else { + return -1; + } + + return 0; +} + +XMLNode& +MIDIControllable::get_state () +{ + char buf[32]; + XMLNode& node (controllable.get_state ()); + + snprintf (buf, sizeof(buf), "0x%x", (int) control_type); + node.add_property ("event", buf); + snprintf (buf, sizeof(buf), "%d", (int) control_channel); + node.add_property ("channel", buf); + snprintf (buf, sizeof(buf), "0x%x", (int) control_additional); + node.add_property ("additional", buf); + + return node; +} + diff --git a/libs/surfaces/generic_midi/midicontrollable.h b/libs/surfaces/generic_midi/midicontrollable.h new file mode 100644 index 0000000000..ab15f9f4ab --- /dev/null +++ b/libs/surfaces/generic_midi/midicontrollable.h @@ -0,0 +1,97 @@ +/* + 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 + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: controllable.h 4 2005-05-13 20:47:18Z taybin $ +*/ + +#ifndef __gm_midicontrollable_h__ +#define __gm_midicontrollable_h__ + +#include + +#include + +#include +#include +#include +#include + +namespace MIDI { + +class Channel; +class Port; +class Parser; + +} + +class MIDIControllable : public Stateful +{ + public: + MIDIControllable (MIDI::Port&, PBD::Controllable&, bool bistate = false); + virtual ~MIDIControllable (); + + void send_feedback (); + MIDI::byte* write_feedback (MIDI::byte* buf, int32_t& bufsize, bool force = false); + + void midi_rebind (MIDI::channel_t channel=-1); + void midi_forget (); + void learn_about_external_control (); + void stop_learning (); + void drop_external_control (); + + bool get_midi_feedback () { return feedback; } + void set_midi_feedback (bool val) { feedback = val; } + + MIDI::Port& get_port() const { return _port; } + PBD::Controllable& get_controllable() const { return controllable; } + + std::string control_description() const { return _control_description; } + + XMLNode& get_state (void); + int set_state (const XMLNode&); + + private: + PBD::Controllable& controllable; + MIDI::Port& _port; + bool setting; + MIDI::byte last_written; + bool bistate; + int midi_msg_id; /* controller ID or note number */ + sigc::connection midi_sense_connection[2]; + sigc::connection midi_learn_connection; + size_t connections; + MIDI::eventType control_type; + MIDI::byte control_additional; + MIDI::channel_t control_channel; + std::string _control_description; + bool feedback; + + static bool _send_feedback; + + void midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t); + void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on); + void midi_sense_note_on (MIDI::Parser &p, MIDI::EventTwoBytes *tb); + void midi_sense_note_off (MIDI::Parser &p, MIDI::EventTwoBytes *tb); + void midi_sense_controller (MIDI::Parser &, MIDI::EventTwoBytes *); + void midi_sense_program_change (MIDI::Parser &, MIDI::byte); + void midi_sense_pitchbend (MIDI::Parser &, MIDI::pitchbend_t); + + void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte); +}; + +#endif // __gm_midicontrollable_h__ + diff --git a/libs/surfaces/tranzport/SConscript b/libs/surfaces/tranzport/SConscript index 8f3568aa8f..3f9cc5cc15 100644 --- a/libs/surfaces/tranzport/SConscript +++ b/libs/surfaces/tranzport/SConscript @@ -34,7 +34,7 @@ tranzport.Merge ([ libraries['ardour'], libraries['ardour_cp'], libraries['sigc2'], - libraries['pbd3'], + libraries['pbd'], libraries['midi++2'], libraries['xml'], libraries['usb'], diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.cc b/libs/surfaces/tranzport/tranzport_control_protocol.cc index ee8fa95bc2..426c837b2f 100644 --- a/libs/surfaces/tranzport/tranzport_control_protocol.cc +++ b/libs/surfaces/tranzport/tranzport_control_protocol.cc @@ -579,7 +579,7 @@ TranzportControlProtocol::monitor_work () if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) { // do we care? not particularly. - info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg; + PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg; } pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0); @@ -693,7 +693,7 @@ TranzportControlProtocol::update_state () /* per track */ if (route_table[0]) { - AudioTrack* at = dynamic_cast (route_table[0]); + boost::shared_ptr at = boost::dynamic_pointer_cast (route_table[0]); if (at && at->record_enabled()) { pending_lights[LightTrackrec] = true; } else { @@ -1574,3 +1574,15 @@ TranzportControlProtocol::print (int row, int col, const char *text) } } +XMLNode& +TranzportControlProtocol::get_state () +{ + XMLNode* node = new XMLNode (_name); /* node name must match protocol name */ + return *node; +} + +int +TranzportControlProtocol::set_state (const XMLNode& node) +{ + return 0; +} diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.h b/libs/surfaces/tranzport/tranzport_control_protocol.h index 546cc2f2af..e6e1a83e46 100644 --- a/libs/surfaces/tranzport/tranzport_control_protocol.h +++ b/libs/surfaces/tranzport/tranzport_control_protocol.h @@ -23,6 +23,9 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol static bool probe (); + XMLNode& get_state (); + int set_state (const XMLNode&); + private: static const int VENDORID = 0x165b; static const int PRODUCTID = 0x8101; -- cgit v1.2.3