summaryrefslogtreecommitdiff
path: root/libs/surfaces/faderport/faderport.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/surfaces/faderport/faderport.cc')
-rw-r--r--libs/surfaces/faderport/faderport.cc512
1 files changed, 512 insertions, 0 deletions
diff --git a/libs/surfaces/faderport/faderport.cc b/libs/surfaces/faderport/faderport.cc
new file mode 100644
index 0000000000..4cc541bf7b
--- /dev/null
+++ b/libs/surfaces/faderport/faderport.cc
@@ -0,0 +1,512 @@
+/*
+ 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.
+
+*/
+
+#include <stdint.h>
+
+#include <sstream>
+#include <algorithm>
+
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+
+#include "pbd/controllable_descriptor.h"
+#include "pbd/error.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/file_utils.h"
+#include "pbd/xml++.h"
+#include "pbd/compose.h"
+
+#include "midi++/port.h"
+
+#include "ardour/audioengine.h"
+#include "ardour/filesystem_paths.h"
+#include "ardour/session.h"
+#include "ardour/route.h"
+#include "ardour/midi_ui.h"
+#include "ardour/midi_port.h"
+#include "ardour/rc_configuration.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/debug.h"
+#include "ardour/async_midi_port.h"
+
+#include "faderport.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace Glib;
+using namespace std;
+
+#include "i18n.h"
+
+#define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
+
+FaderPort::FaderPort (Session& s)
+ : ControlProtocol (s, _("Faderport"))
+ , _motorised (true)
+ , _threshold (10)
+ , gui (0)
+ , connection_state (ConnectionState (0))
+ , _device_active (false)
+ , fader_msb (0)
+ , fader_lsb (0)
+{
+ boost::shared_ptr<ARDOUR::Port> inp;
+ boost::shared_ptr<ARDOUR::Port> outp;
+
+ inp = AudioEngine::instance()->register_input_port (DataType::MIDI, "Faderport Recv", true);
+ outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "Faderport Send", true);
+
+ _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(inp);
+ _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(outp);
+
+ if (_input_port == 0 || _output_port == 0) {
+ throw failed_constructor();
+ }
+
+ do_feedback = false;
+ _feedback_interval = 10 * 1000; // microseconds
+ last_feedback_time = 0;
+ native_counter = 0;
+
+ _current_bank = 0;
+ _bank_size = 0;
+
+ /* handle device inquiry response */
+ _input_port->parser()->sysex.connect_same_thread (midi_connections, boost::bind (&FaderPort::sysex_handler, this, _1, _2, _3));
+ /* handle switches */
+ _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort::switch_handler, this, _1, _2));
+ /* handle encoder */
+ _input_port->parser()->pitchbend.connect_same_thread (midi_connections, boost::bind (&FaderPort::encoder_handler, this, _1, _2));
+ /* handle fader */
+ _input_port->parser()->controller.connect_same_thread (midi_connections, boost::bind (&FaderPort::fader_handler, this, _1, _2));
+
+ /* This connection means that whenever data is ready from the input
+ * port, the relevant thread will invoke our ::midi_input_handler()
+ * method, which will read the data, and invoke the parser.
+ */
+
+ _input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &FaderPort::midi_input_handler), _input_port));
+ _input_port->xthread().attach (midi_ui_context()->main_loop()->get_context());
+
+ Session::SendFeedback.connect_same_thread (*this, boost::bind (&FaderPort::send_feedback, this));
+ //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&FaderPort::send_feedback, this), midi_ui_context());;
+
+ /* this one is cross-thread */
+
+ Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&FaderPort::reset_controllables, this), midi_ui_context());
+
+ /* Catch port connections and disconnections */
+ ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort::connection_handler, this, _1, _2, _3, _4, _5), midi_ui_context());
+
+}
+
+FaderPort::~FaderPort ()
+{
+ if (_input_port) {
+ DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
+ AudioEngine::instance()->unregister_port (_input_port);
+ _input_port.reset ();
+ }
+
+ if (_output_port) {
+// _output_port->drain (10000); //ToDo: is this necessary? It hangs the shutdown, for me
+ DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
+ AudioEngine::instance()->unregister_port (_output_port);
+ _output_port.reset ();
+ }
+
+ tear_down_gui ();
+}
+
+void
+FaderPort::switch_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
+{
+ switch (tb->controller_number) {
+ case Mute:
+ cerr << "Mute\n";
+ break;
+ case Solo:
+ cerr << "Solo\n";
+ break;
+ case Rec:
+ cerr << "Rec\n";
+ break;
+ case Left:
+ cerr << "Left\n";
+ break;
+ case Bank:
+ cerr << "Bank\n";
+ break;
+ case Right:
+ cerr << "Right\n";
+ break;
+ case Output:
+ cerr << "Output\n";
+ break;
+ case Read:
+ cerr << "Read\n";
+ break;
+ case Write:
+ cerr << "Write\n";
+ break;
+ case Touch:
+ cerr << "Touch\n";
+ break;
+ case Off:
+ cerr << "Off\n";
+ break;
+ case Mix:
+ cerr << "Mix\n";
+ break;
+ case Proj:
+ cerr << "Proj\n";
+ break;
+ case Trns:
+ cerr << "Trns\n";
+ break;
+ case Undo:
+ cerr << "Undo\n";
+ break;
+ case Shift:
+ cerr << "Shift\n";
+ break;
+ case Punch:
+ cerr << "Punch\n";
+ break;
+ case User:
+ cerr << "User\n";
+ break;
+ case Loop:
+ cerr << "Loop\n";
+ break;
+ case Rewind:
+ cerr << "Rewind\n";
+ break;
+ case Ffwd:
+ cerr << "Ffwd\n";
+ break;
+ case Stop:
+ cerr << "Stop\n";
+ break;
+ case Play:
+ cerr << "Play\n";
+ break;
+ case RecEnable:
+ cerr << "RecEnable\n";
+ break;
+ case Fader:
+ cerr << "Fader touch\n";
+ break;
+ default:
+ cerr << "eh?\n";
+ }
+
+ /* send feedback to turn on the LED */
+
+ MIDI::byte buf[3];
+ buf[0] = 0xa0;
+ buf[1] = tb->controller_number;
+ buf[2] = tb->value;
+
+ _output_port->write (buf, 3, 0);
+}
+
+void
+FaderPort::encoder_handler (MIDI::Parser &, MIDI::pitchbend_t pb)
+{
+ if (pb < 8192) {
+ cerr << "Encoder right\n";
+ } else {
+ cerr << "Encoder left\n";
+ }
+}
+
+void
+FaderPort::fader_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
+{
+ bool was_fader = false;
+
+ if (tb->controller_number == 0x0) {
+ fader_msb = tb->value;
+ was_fader = true;
+ } else if (tb->controller_number == 0x20) {
+ fader_lsb = tb->value;
+ was_fader = true;
+ }
+
+ if (was_fader) {
+ cerr << "Fader now at " << ((fader_msb<<7)|fader_lsb) << endl;
+ }
+}
+
+void
+FaderPort::sysex_handler (MIDI::Parser &p, MIDI::byte *buf, size_t sz)
+{
+ if (sz < 17) {
+ return;
+ }
+
+ if (buf[2] == 0x7f &&
+ buf[3] == 0x06 &&
+ buf[4] == 0x02 &&
+ buf[5] == 0x0 &&
+ buf[6] == 0x1 &&
+ buf[7] == 0x06 &&
+ buf[8] == 0x02 &&
+ buf[9] == 0x0 &&
+ buf[10] == 0x01 &&
+ buf[11] == 0x0) {
+ _device_active = true;
+
+ cerr << "FaderPort identified\n";
+
+ /* put it into native mode */
+
+ MIDI::byte native[3];
+ native[0] = 0x91;
+ native[1] = 0x00;
+ native[2] = 0x64;
+
+ _output_port->write (native, 3, 0);
+ }
+}
+
+int
+FaderPort::set_active (bool /*yn*/)
+{
+ return 0;
+}
+
+void
+FaderPort::set_feedback_interval (microseconds_t ms)
+{
+ _feedback_interval = ms;
+}
+
+void
+FaderPort::send_feedback ()
+{
+ /* This is executed in RT "process" context", so no blocking calls
+ */
+
+ if (!do_feedback) {
+ return;
+ }
+
+ microseconds_t now = get_microseconds ();
+
+ if (last_feedback_time != 0) {
+ if ((now - last_feedback_time) < _feedback_interval) {
+ return;
+ }
+ }
+
+ last_feedback_time = now;
+}
+
+bool
+FaderPort::midi_input_handler (Glib::IOCondition ioc, boost::shared_ptr<ARDOUR::AsyncMIDIPort> port)
+{
+ DEBUG_TRACE (DEBUG::MidiIO, string_compose ("something happend on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
+
+ if (ioc & ~IO_IN) {
+ return false;
+ }
+
+ if (ioc & IO_IN) {
+
+ if (port) {
+ port->clear ();
+ }
+
+ DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
+ framepos_t now = session->engine().sample_time();
+ port->parse (now);
+ }
+
+ return true;
+}
+
+
+XMLNode&
+FaderPort::get_state ()
+{
+ XMLNode& node (ControlProtocol::get_state());
+
+ XMLNode* child;
+
+ child = new XMLNode (X_("Input"));
+ child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_input_port)->get_state());
+ node.add_child_nocopy (*child);
+
+
+ child = new XMLNode (X_("Output"));
+ child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
+ node.add_child_nocopy (*child);
+
+ return node;
+}
+
+int
+FaderPort::set_state (const XMLNode& node, int version)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode const* child;
+
+ if (ControlProtocol::set_state (node, version)) {
+ return -1;
+ }
+
+ if ((child = node.child (X_("Input"))) != 0) {
+ XMLNode* portnode = child->child (Port::state_node_name.c_str());
+ if (portnode) {
+ boost::shared_ptr<ARDOUR::Port>(_input_port)->set_state (*portnode, version);
+ }
+ }
+
+ if ((child = node.child (X_("Output"))) != 0) {
+ XMLNode* portnode = child->child (Port::state_node_name.c_str());
+ if (portnode) {
+ boost::shared_ptr<ARDOUR::Port>(_output_port)->set_state (*portnode, version);
+ }
+ }
+
+ return 0;
+}
+
+int
+FaderPort::set_feedback (bool yn)
+{
+ do_feedback = yn;
+ last_feedback_time = 0;
+ return 0;
+}
+
+bool
+FaderPort::get_feedback () const
+{
+ return do_feedback;
+}
+
+void
+FaderPort::set_current_bank (uint32_t b)
+{
+ _current_bank = b;
+// reset_controllables ();
+}
+
+void
+FaderPort::next_bank ()
+{
+ _current_bank++;
+// reset_controllables ();
+}
+
+void
+FaderPort::prev_bank()
+{
+ if (_current_bank) {
+ _current_bank--;
+// reset_controllables ();
+ }
+}
+
+void
+FaderPort::set_motorised (bool m)
+{
+ _motorised = m;
+}
+
+void
+FaderPort::set_threshold (int t)
+{
+ _threshold = t;
+}
+
+void
+FaderPort::reset_controllables ()
+{
+}
+
+bool
+FaderPort::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
+{
+ if (!_input_port || !_output_port) {
+ return false;
+ }
+
+ string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
+ string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
+
+ std::cerr << "Checking " << name1 << (yn ? " + " : " - " ) << name2 << " vs. " << ni << " & " << no << std::endl;
+
+ if (ni == name1 || ni == name2) {
+ if (yn) {
+ connection_state |= InputConnected;
+ } else {
+ connection_state &= ~InputConnected;
+ }
+ } else if (no == name1 || no == name2) {
+ if (yn) {
+ connection_state |= OutputConnected;
+ } else {
+ connection_state &= ~OutputConnected;
+ }
+ } else {
+ /* not our ports */
+ return false;
+ }
+
+ if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
+
+ /* XXX this is a horrible hack. Without a short sleep here,
+ something prevents the device wakeup messages from being
+ sent and/or the responses from being received.
+ */
+
+ g_usleep (100000);
+ connected ();
+
+ } else {
+ DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
+ _device_active = false;
+ }
+
+ return true; /* connection status changed */
+}
+
+void
+FaderPort::connected ()
+{
+ std::cerr << "faderport connected\n";
+
+ /* send device inquiry */
+
+ MIDI::byte buf[6];
+
+ buf[0] = 0xf0;
+ buf[1] = 0x7e;
+ buf[2] = 0x7f;
+ buf[3] = 0x06;
+ buf[4] = 0x01;
+ buf[5] = 0xf7;
+
+ _output_port->write (buf, 6, 0);
+}