push2: basic GUI dialog
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 7 Jul 2016 02:34:10 +0000 (22:34 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 27 Sep 2016 19:59:30 +0000 (14:59 -0500)
gtk2_ardour/icons/push2-small.png [new file with mode: 0644]
libs/surfaces/push2/push2.cc
libs/surfaces/push2/push2.h
libs/surfaces/push2/wscript

diff --git a/gtk2_ardour/icons/push2-small.png b/gtk2_ardour/icons/push2-small.png
new file mode 100644 (file)
index 0000000..eaa9a34
Binary files /dev/null and b/gtk2_ardour/icons/push2-small.png differ
index f1d37b246cde6d7d76862893e8bab3a4bf95a39f..ce9723ac8f6744df2755fa69bece41f2515f0513 100644 (file)
@@ -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<string> in;
+       vector<string> 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<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, 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<ARDOUR::Port>(_async_in)->name());
+       string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_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<Port>
+Push2::output_port()
+{
+       return _async_out;
+}
+
+boost::shared_ptr<Port>
+Push2::input_port()
+{
+       return _async_in;
+}
+
index dab96260c62bbbb55ed04c9e054efc64e8507b0d..7e480495ad6e5bba6c90fcb8ff30679f177d8b11 100644 (file)
@@ -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<void> ConnectionChange;
+
+       boost::shared_ptr<ARDOUR::Port> input_port();
+       boost::shared_ptr<ARDOUR::Port> 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<ARDOUR::Stripable> 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<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
+       PBD::ScopedConnection port_connection;
+
+       /* GUI */
+
+       mutable void *gui;
+       void build_gui ();
+
 };
 
 
index 36e9a644c4bb66bfae9625e97b25694a1388169e..2a76b10648b14f0efa1180284e6a8d5739d3261f 100644 (file)
@@ -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():