rough-in for faderport protocol backend
authorBen Loftis <ben@harrisonconsoles.com>
Tue, 24 Nov 2015 20:17:25 +0000 (14:17 -0600)
committerBen Loftis <ben@harrisonconsoles.com>
Tue, 24 Nov 2015 20:18:14 +0000 (14:18 -0600)
gtk2_ardour/ardev_common.sh.in
libs/surfaces/faderport/faderport_interface.cc [new file with mode: 0644]
libs/surfaces/faderport/faderport_midi_protocol.cc [new file with mode: 0644]
libs/surfaces/faderport/faderport_midi_protocol.h [new file with mode: 0644]
libs/surfaces/faderport/fmcp_gui.cc [new file with mode: 0644]
libs/surfaces/faderport/i18n.h [new file with mode: 0644]
libs/surfaces/faderport/wscript [new file with mode: 0644]
libs/surfaces/wscript

index cf372c0cca416fc46b309b83e43716ef68773fec..f4f4bf71ce2d36e57a5ed5686bd2660170570476 100644 (file)
@@ -11,7 +11,7 @@ libs=$TOP/@LIBS@
 #
 
 export ARDOUR_PATH=$TOP/gtk2_ardour/icons:$TOP/gtk2_ardour/pixmaps:$TOP/build/gtk2_ardour:$TOP/gtk2_ardour:.
-export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/wiimote
+export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/faderport:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/wiimote
 export ARDOUR_PANNER_PATH=$libs/panners
 export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
 export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
diff --git a/libs/surfaces/faderport/faderport_interface.cc b/libs/surfaces/faderport/faderport_interface.cc
new file mode 100644 (file)
index 0000000..c7c2815
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    Copyright (C) 2012 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 <pbd/failed_constructor.h>
+
+#include "control_protocol/control_protocol.h"
+#include "faderport_midi_protocol.h"
+
+using namespace ARDOUR;
+
+static ControlProtocol*
+new_faderport_midi_protocol (ControlProtocolDescriptor* /*descriptor*/, Session* s)
+{
+       FaderportMidiControlProtocol* fmcp;
+
+       try {
+               fmcp =  new FaderportMidiControlProtocol (*s);
+       } catch (failed_constructor& err) {
+               return 0;
+       }
+
+       if (fmcp->set_active (true)) {
+               delete fmcp;
+               return 0;
+       }
+
+       return fmcp;
+}
+
+static void
+delete_faderport_midi_protocol (ControlProtocolDescriptor* /*descriptor*/, ControlProtocol* cp)
+{
+       delete cp;
+}
+
+static bool
+probe_faderport_midi_protocol (ControlProtocolDescriptor* /*descriptor*/)
+{
+       return FaderportMidiControlProtocol::probe ();
+}
+
+static ControlProtocolDescriptor faderport_midi_descriptor = {
+       /*name :              */   "Faderport",
+       /*id :                */   "uri://ardour.org/surfaces/faderport:0",
+       /*ptr :               */   0,
+       /*module :            */   0,
+       /*mandatory :         */   0,
+       /*supports_feedback : */   true,
+       /*probe :             */   probe_faderport_midi_protocol,
+       /*initialize :        */   new_faderport_midi_protocol,
+       /*destroy :           */   delete_faderport_midi_protocol
+};
+
+extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { printf("HERE ********************************\n"); return &faderport_midi_descriptor; }
+
diff --git a/libs/surfaces/faderport/faderport_midi_protocol.cc b/libs/surfaces/faderport/faderport_midi_protocol.cc
new file mode 100644 (file)
index 0000000..23cfc2e
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+    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_midi_protocol.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 */
+
+FaderportMidiControlProtocol::FaderportMidiControlProtocol (Session& s)
+       : ControlProtocol (s, _("Faderport"))
+       , _motorised (true)
+       , _threshold (10)
+       , gui (0)
+{
+       _async_in  = AudioEngine::instance()->register_input_port (DataType::MIDI, "Faderport Recv", true);
+       _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, "Faderport Send", true);
+
+       if (_async_in == 0 || _async_out == 0) {
+               throw failed_constructor();
+       }
+
+       _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
+       _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
+               
+       do_feedback = false;
+       _feedback_interval = 10 * 1000; // microseconds
+       last_feedback_time = 0;
+       native_counter = 0;
+       
+       _current_bank = 0;
+       _bank_size = 0;
+
+//NOTE TO PAUL:
+// "midi_receiver" and "midi_input_handler"
+// were 2 different approaches to try to capture MIDI data;  neither seems to work as expected.
+
+
+//not sure if this should do anything
+       (*_input_port).parser()->any.connect_same_thread (midi_recv_connection, boost::bind (&FaderportMidiControlProtocol::midi_receiver, this, _1, _2, _3));
+
+//this is raw port acces (?)
+//     _input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &FaderportMidiControlProtocol::midi_input_handler), _input_port));
+
+       Session::SendFeedback.connect_same_thread (*this, boost::bind (&FaderportMidiControlProtocol::send_feedback, this));
+       //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&FaderportMidiControlProtocol::send_feedback, this), midi_ui_context());;
+
+       /* this one is cross-thread */
+
+       //Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&FaderportMidiControlProtocol::reset_controllables, this), midi_ui_context());
+
+}
+
+FaderportMidiControlProtocol::~FaderportMidiControlProtocol ()
+{
+       if (_input_port) {
+               DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("unregistering input port %1\n", _async_in->name()));
+               AudioEngine::instance()->unregister_port (_async_in);
+               _async_in.reset ((ARDOUR::Port*) 0);
+       }
+
+       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", _async_out->name()));
+               AudioEngine::instance()->unregister_port (_async_out);
+               _async_out.reset ((ARDOUR::Port*) 0);
+       }
+
+       tear_down_gui ();
+}
+
+void
+FaderportMidiControlProtocol::midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t)
+{
+//NOTE:  this never did anything
+//     printf("got some midi\n");
+}
+
+
+int
+FaderportMidiControlProtocol::set_active (bool /*yn*/)
+{
+       return 0;
+}
+
+void
+FaderportMidiControlProtocol::set_feedback_interval (microseconds_t ms)
+{
+       _feedback_interval = ms;
+}
+
+void
+FaderportMidiControlProtocol::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;
+               }
+       }
+
+       //occasionally tell the Faderport to go into "Native" mode
+       //ToDo:  trigger this on MIDI port connection ?
+       native_counter++;
+       if (native_counter > 10) {
+               native_counter = 0;
+               MIDI::byte midibuf[64];
+               MIDI::byte *buf = midibuf;
+               *buf++ = (0x91);
+               *buf++ = (0x00);
+               *buf++ = (0x64);
+               _output_port->write (buf, 3, 0);
+       }
+       
+       last_feedback_time = now;
+}
+
+bool
+FaderportMidiControlProtocol::midi_input_handler (Glib::IOCondition ioc, ARDOUR::AsyncMIDIPort* port)
+{
+       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("something happend on  %1\n", ((ARDOUR::Port*)port)->name()));
+
+       if (ioc & ~IO_IN) {
+               return false;
+       }
+
+       if (ioc & IO_IN) {
+
+               AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*> (port);
+               if (asp) {
+                       asp->clear ();
+               }
+
+               DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", ((ARDOUR::Port*)port)->name()));
+//             framepos_t now = _session.engine().sample_time();
+//             port->parse (now);
+       }
+
+       return true;
+}
+
+
+XMLNode&
+FaderportMidiControlProtocol::get_state ()
+{
+       XMLNode& node (ControlProtocol::get_state());
+       char buf[32];
+
+       return node;
+}
+
+int
+FaderportMidiControlProtocol::set_state (const XMLNode& node, int version)
+{
+       XMLNodeList nlist;
+       XMLNodeConstIterator niter;
+       const XMLProperty* prop;
+
+       if (ControlProtocol::set_state (node, version)) {
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+FaderportMidiControlProtocol::set_feedback (bool yn)
+{
+       do_feedback = yn;
+       last_feedback_time = 0;
+       return 0;
+}
+
+bool
+FaderportMidiControlProtocol::get_feedback () const
+{
+       return do_feedback;
+}
+
+void
+FaderportMidiControlProtocol::set_current_bank (uint32_t b)
+{
+       _current_bank = b;
+//     reset_controllables ();
+}
+
+void
+FaderportMidiControlProtocol::next_bank ()
+{
+       _current_bank++;
+//     reset_controllables ();
+}
+
+void
+FaderportMidiControlProtocol::prev_bank()
+{
+       if (_current_bank) {
+               _current_bank--;
+//             reset_controllables ();
+       }
+}
+
+void
+FaderportMidiControlProtocol::set_motorised (bool m)
+{
+       _motorised = m;
+}
+
+void
+FaderportMidiControlProtocol::set_threshold (int t)
+{
+       _threshold = t;
+}
diff --git a/libs/surfaces/faderport/faderport_midi_protocol.h b/libs/surfaces/faderport/faderport_midi_protocol.h
new file mode 100644 (file)
index 0000000..85dfb93
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+    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.
+
+*/
+
+#ifndef ardour_generic_midi_control_protocol_h
+#define ardour_generic_midi_control_protocol_h
+
+#include <list>
+#include <glibmm/threads.h>
+
+#include "ardour/types.h"
+
+#include "control_protocol/control_protocol.h"
+
+namespace PBD {
+       class Controllable;
+       class ControllableDescriptor;
+}
+
+#include <midi++/types.h>
+
+//#include "pbd/signals.h"
+
+
+//#include "midi_byte_array.h"
+#include "types.h"
+
+#include "glibmm/main.h"
+
+namespace MIDI {
+       class Parser;
+       class Port;
+}
+
+
+namespace ARDOUR {
+       class AsyncMIDIPort;
+       class Port;
+       class Session;
+       class MidiPort;
+}
+
+
+class MIDIControllable;
+class MIDIFunction;
+class MIDIAction;
+
+class FaderportMidiControlProtocol : public ARDOUR::ControlProtocol {
+  public:
+       FaderportMidiControlProtocol (ARDOUR::Session&);
+       virtual ~FaderportMidiControlProtocol();
+
+       int set_active (bool yn);
+       static bool probe() { return true; }  //do SysEx device check here?
+
+       void set_feedback_interval (ARDOUR::microseconds_t);
+
+       int set_feedback (bool yn);
+       bool get_feedback () const;
+
+       XMLNode& get_state ();
+       int set_state (const XMLNode&, int version);
+
+       bool has_editor () const { return true; }
+       void* get_gui () const;
+       void  tear_down_gui ();
+
+       void set_current_bank (uint32_t);
+       void next_bank ();
+       void prev_bank ();
+
+       void set_motorised (bool);
+
+       bool motorised () const {
+               return _motorised;
+       }
+
+       void set_threshold (int);
+
+       int threshold () const {
+               return _threshold;
+       }
+
+  private:
+       MIDI::Port* _input_port;
+       MIDI::Port* _output_port;
+       boost::shared_ptr<ARDOUR::Port> _async_in;
+       boost::shared_ptr<ARDOUR::Port> _async_out;
+
+       ARDOUR::microseconds_t _feedback_interval;
+       ARDOUR::microseconds_t last_feedback_time;
+       int native_counter;
+
+       bool  do_feedback;
+       void  send_feedback ();
+
+       PBD::ScopedConnection midi_recv_connection;
+       void midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t);
+
+       bool midi_input_handler (Glib::IOCondition ioc, ARDOUR::AsyncMIDIPort* port);
+
+       std::string _current_binding;
+       uint32_t _bank_size;
+       uint32_t _current_bank;
+       /** true if this surface is motorised.  If it is, we assume
+           that the surface's controls are never out of sync with
+           Ardour's state, so we don't have to take steps to avoid
+           values jumping around when things are not in sync.
+       */
+       bool _motorised;
+       int _threshold;
+
+       mutable void *gui;
+       void build_gui ();
+};
+
+#endif /* ardour_generic_midi_control_protocol_h */
diff --git a/libs/surfaces/faderport/fmcp_gui.cc b/libs/surfaces/faderport/fmcp_gui.cc
new file mode 100644 (file)
index 0000000..1569b88
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+    Copyright (C) 2009-2012 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 <iostream>
+#include <list>
+#include <string>
+
+#include <gtkmm/comboboxtext.h>
+#include <gtkmm/label.h>
+#include <gtkmm/box.h>
+#include <gtkmm/adjustment.h>
+#include <gtkmm/spinbutton.h>
+#include <gtkmm/table.h>
+
+#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/utils.h"
+
+#include "faderport_midi_protocol.h"
+
+#include "i18n.h"
+
+class GMCPGUI : public Gtk::VBox
+{
+public:
+       GMCPGUI (FaderportMidiControlProtocol&);
+       ~GMCPGUI ();
+
+private:
+       FaderportMidiControlProtocol& cp;
+       Gtk::ComboBoxText map_combo;
+       Gtk::Adjustment bank_adjustment;
+       Gtk::SpinButton bank_spinner;
+       Gtk::CheckButton motorised_button;
+       Gtk::Adjustment threshold_adjustment;
+       Gtk::SpinButton threshold_spinner;
+
+       void binding_changed ();
+       void bank_changed ();
+       void motorised_changed ();
+       void threshold_changed ();
+};
+
+using namespace PBD;
+using namespace ARDOUR;
+using namespace std;
+using namespace Gtk;
+using namespace Gtkmm2ext;
+
+void*
+FaderportMidiControlProtocol::get_gui () const
+{
+       if (!gui) {
+               const_cast<FaderportMidiControlProtocol*>(this)->build_gui ();
+       }
+       static_cast<Gtk::VBox*>(gui)->show_all();
+       return gui;
+}
+
+void
+FaderportMidiControlProtocol::tear_down_gui ()
+{
+       if (gui) {
+               Gtk::Widget *w = static_cast<Gtk::VBox*>(gui)->get_parent();
+               if (w) {
+                       w->hide();
+                       delete w;
+               }
+       }
+       delete (GMCPGUI*) gui;
+       gui = 0;
+}
+
+void
+FaderportMidiControlProtocol::build_gui ()
+{
+       gui = (void*) new GMCPGUI (*this);
+}
+
+/*--------------------*/
+
+GMCPGUI::GMCPGUI (FaderportMidiControlProtocol& p)
+       : cp (p)
+       , bank_adjustment (1, 1, 100, 1, 10)
+       , bank_spinner (bank_adjustment)
+       , motorised_button ("Motorised")
+       , threshold_adjustment (p.threshold(), 1, 127, 1, 10)
+       , threshold_spinner (threshold_adjustment)
+{
+}
+
+GMCPGUI::~GMCPGUI ()
+{
+}
+
+void
+GMCPGUI::bank_changed ()
+{
+//     int new_bank = bank_adjustment.get_value() - 1;
+//     cp.set_current_bank (new_bank);
+}
+
+void
+GMCPGUI::binding_changed ()
+{
+}
+
+void
+GMCPGUI::motorised_changed ()
+{
+//     cp.set_motorised (motorised_button.get_active ());
+}
+
+void
+GMCPGUI::threshold_changed ()
+{
+//     cp.set_threshold (threshold_adjustment.get_value());
+}
diff --git a/libs/surfaces/faderport/i18n.h b/libs/surfaces/faderport/i18n.h
new file mode 100644 (file)
index 0000000..dcbbfcf
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __i18n_h__
+#define __i18n_h__
+
+#include "pbd/compose.h"
+#include "pbd/convert.h"
+#include "gettext.h"
+
+#include <vector>
+#include <string>
+
+#define _(Text)  dgettext (PACKAGE,Text)
+#define N_(Text) gettext_noop (Text)
+#define X_(Text) Text
+#define I18N(Array) PBD::internationalize (PACKAGE, Array)
+
+#endif // __i18n_h__
diff --git a/libs/surfaces/faderport/wscript b/libs/surfaces/faderport/wscript
new file mode 100644 (file)
index 0000000..e3efc70
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+    autowaf.set_options(opt)
+
+def configure(conf):
+    autowaf.configure(conf)
+
+def build(bld):
+    obj = bld(features = 'cxx cxxshlib')
+    obj.source = '''
+            faderport_midi_protocol.cc
+            fmcp_gui.cc
+            faderport_interface.cc
+    '''
+    obj.export_includes = ['.']
+    obj.defines      = [ 'PACKAGE="ardour_faderport"' ]
+    obj.defines     += [ 'ARDOURSURFACE_DLL_EXPORTS' ]
+    obj.includes     = [ '.', './faderport']
+    obj.name         = 'libardour_faderport'
+    obj.target       = 'ardour_faderport'
+    obj.uselib       = 'GTKMM GTK GDK'
+    obj.use          = 'libardour libardour_cp libgtkmm2ext libpbd'
+    obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces')
+
+def shutdown():
+    autowaf.shutdown()
index 71bb35d6cc8f7d44b3978a5ac154ea2018ea39e2..5ab05cfb238a564dd279d8cef59221bdc94bd044 100644 (file)
@@ -21,6 +21,7 @@ out = 'build'
 
 children = [
         'control_protocol',
+        'faderport',
         'generic_midi',
         'mackie',
 ]
@@ -67,6 +68,7 @@ def configure(conf):
 def build(bld):
     bld.recurse('control_protocol')
     bld.recurse('generic_midi')
+    bld.recurse('faderport')
     bld.recurse('mackie')
 
     if bld.is_defined ('HAVE_LO'):