diff --git a/gtk2_ardour/icons/push2-small.png b/gtk2_ardour/icons/push2-small.png new file mode 100644 index 0000000000..eaa9a34c90 Binary files /dev/null and b/gtk2_ardour/icons/push2-small.png differ diff --git a/libs/surfaces/push2/push2.cc b/libs/surfaces/push2/push2.cc index f1d37b246c..ce9723ac8f 100644 --- a/libs/surfaces/push2/push2.cc +++ b/libs/surfaces/push2/push2.cc @@ -69,6 +69,8 @@ Push2::Push2 (ARDOUR::Session& s) , modifier_state (None) , splash_start (0) , bank_start (0) + , connection_state (ConnectionState (0)) + , gui (0) { context = Cairo::Context::create (frame_buffer); tc_clock_layout = Pango::Layout::create (context); @@ -100,6 +102,14 @@ Push2::Push2 (ARDOUR::Session& s) throw failed_constructor (); } + ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this); + + /* Catch port connections and disconnections */ + ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this); + + /* ports might already be there */ + + port_registration_handler (); } Push2::~Push2 () @@ -107,6 +117,34 @@ Push2::~Push2 () stop (); } +void +Push2::port_registration_handler () +{ + if (_async_in->connected() && _async_out->connected()) { + /* don't waste cycles here */ + return; + } + + string input_port_name = X_("Ableton Push 2 MIDI 1 in"); + string output_port_name = X_("Ableton Push 2 MIDI 1 out"); + vector in; + vector out; + + AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in); + AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out); + + if (!in.empty() && !out.empty()) { + cerr << "Push2: both ports found\n"; + cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl; + if (!_async_in->connected()) { + AudioEngine::instance()->connect (_async_in->name(), in.front()); + } + if (!_async_out->connected()) { + AudioEngine::instance()->connect (_async_out->name(), out.front()); + } + } +} + int Push2::open () { @@ -1478,3 +1516,67 @@ Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const return matched; } + +bool +Push2::connection_handler (boost::weak_ptr, std::string name1, boost::weak_ptr, std::string name2, bool yn) +{ + DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n"); + if (!_input_port || !_output_port) { + return false; + } + + string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr(_async_in)->name()); + string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr(_async_out)->name()); + + 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 { + DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2)); + /* 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); + DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n"); + // connected (); + + } else { + DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n"); + } + + ConnectionChange (); /* emit signal for our GUI */ + + DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n"); + + return true; /* connection status changed */ +} + +boost::shared_ptr +Push2::output_port() +{ + return _async_out; +} + +boost::shared_ptr +Push2::input_port() +{ + return _async_in; +} + diff --git a/libs/surfaces/push2/push2.h b/libs/surfaces/push2/push2.h index dab96260c6..7e480495ad 100644 --- a/libs/surfaces/push2/push2.h +++ b/libs/surfaces/push2/push2.h @@ -74,10 +74,19 @@ class Push2 : public ARDOUR::ControlProtocol static bool probe (); static void* request_factory (uint32_t); + bool has_editor () const { return true; } + void* get_gui () const; + void tear_down_gui (); + int set_active (bool yn); XMLNode& get_state(); int set_state (const XMLNode & node, int version); + PBD::Signal0 ConnectionChange; + + boost::shared_ptr input_port(); + boost::shared_ptr output_port(); + private: libusb_device_handle *handle; uint8_t frame_header[16]; @@ -452,6 +461,24 @@ class Push2 : public ARDOUR::ControlProtocol bool pad_filter (ARDOUR::MidiBuffer& in, ARDOUR::MidiBuffer& out) const; boost::weak_ptr first_selected_stripable; + + PBD::ScopedConnection port_reg_connection; + void port_registration_handler (); + + enum ConnectionState { + InputConnected = 0x1, + OutputConnected = 0x2 + }; + + int connection_state; + bool connection_handler (boost::weak_ptr, std::string name1, boost::weak_ptr, std::string name2, bool yn); + PBD::ScopedConnection port_connection; + + /* GUI */ + + mutable void *gui; + void build_gui (); + }; diff --git a/libs/surfaces/push2/wscript b/libs/surfaces/push2/wscript index 36e9a644c4..2a76b10648 100644 --- a/libs/surfaces/push2/wscript +++ b/libs/surfaces/push2/wscript @@ -25,6 +25,7 @@ def build(bld): interface.cc midi_byte_array.cc leds.cc + gui.cc ''' obj.export_includes = ['.'] obj.defines = [ 'PACKAGE="ardour_push2"' ] @@ -33,8 +34,8 @@ def build(bld): obj.includes = [ '.', './push2'] obj.name = 'libardour_push2' obj.target = 'ardour_push2' - obj.uselib = 'CAIROMM PANGOMM USB' - obj.use = 'libardour libardour_cp libpbd libtimecode' + obj.uselib = 'CAIROMM PANGOMM USB GTKMM' + obj.use = 'libardour libardour_cp libgtkmm2ext libpbd libtimecode' obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') def shutdown():