diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in index cf372c0cca..f4f4bf71ce 100644 --- a/gtk2_ardour/ardev_common.sh.in +++ b/gtk2_ardour/ardev_common.sh.in @@ -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 index 0000000000..c7c2815a15 --- /dev/null +++ b/libs/surfaces/faderport/faderport_interface.cc @@ -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 + +#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 index 0000000000..23cfc2e430 --- /dev/null +++ b/libs/surfaces/faderport/faderport_midi_protocol.cc @@ -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 + +#include +#include + +#include +#include + +#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(_async_in).get(); + _output_port = boost::dynamic_pointer_cast(_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 (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 index 0000000000..85dfb9342c --- /dev/null +++ b/libs/surfaces/faderport/faderport_midi_protocol.h @@ -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 +#include + +#include "ardour/types.h" + +#include "control_protocol/control_protocol.h" + +namespace PBD { + class Controllable; + class ControllableDescriptor; +} + +#include + +//#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 _async_in; + boost::shared_ptr _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 index 0000000000..1569b88d01 --- /dev/null +++ b/libs/surfaces/faderport/fmcp_gui.cc @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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(this)->build_gui (); + } + static_cast(gui)->show_all(); + return gui; +} + +void +FaderportMidiControlProtocol::tear_down_gui () +{ + if (gui) { + Gtk::Widget *w = static_cast(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 index 0000000000..dcbbfcf52e --- /dev/null +++ b/libs/surfaces/faderport/i18n.h @@ -0,0 +1,16 @@ +#ifndef __i18n_h__ +#define __i18n_h__ + +#include "pbd/compose.h" +#include "pbd/convert.h" +#include "gettext.h" + +#include +#include + +#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 index 0000000000..e3efc70ed5 --- /dev/null +++ b/libs/surfaces/faderport/wscript @@ -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() diff --git a/libs/surfaces/wscript b/libs/surfaces/wscript index 71bb35d6cc..5ab05cfb23 100644 --- a/libs/surfaces/wscript +++ b/libs/surfaces/wscript @@ -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'):