add support for IP MIDI (multicast MIDI over IP UDP sockets) to ardour and use it...
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 24 Apr 2012 02:28:51 +0000 (02:28 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 24 Apr 2012 02:28:51 +0000 (02:28 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@12069 d708f5d6-7413-0410-9779-e7cbd77b26cf

29 files changed:
libs/ardour/audioengine.cc
libs/ardour/midi_ui.cc
libs/ardour/session.cc
libs/ardour/ticker.cc
libs/midi++2/channel.cc
libs/midi++2/ipmidi_port.cc [new file with mode: 0644]
libs/midi++2/jack_midi_port.cc [new file with mode: 0644]
libs/midi++2/manager.cc
libs/midi++2/midi++/channel.h
libs/midi++2/midi++/ipmidi_port.h [new file with mode: 0644]
libs/midi++2/midi++/jack_midi_port.h [new file with mode: 0644]
libs/midi++2/midi++/manager.h
libs/midi++2/midi++/parser.h
libs/midi++2/midi++/port.h
libs/midi++2/midi++/port_base.h [deleted file]
libs/midi++2/mmc.cc
libs/midi++2/parser.cc
libs/midi++2/port.cc
libs/midi++2/port_base.cc [deleted file]
libs/midi++2/wscript
libs/pbd/base_ui.cc
libs/pbd/crossthread.cc
libs/surfaces/mackie/device_info.cc
libs/surfaces/mackie/device_info.h
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/surface.cc
libs/surfaces/mackie/surface_port.cc
libs/surfaces/mackie/surface_port.h
mcp/nucleus.device

index 5d29f35301d4857b8d94ff8321a38e307eeb1ccd..372581f4e637f5276c8b9a413682439f45ea1e47 100644 (file)
@@ -34,6 +34,7 @@
 #include <jack/weakjack.h>
 
 #include "midi++/port.h"
+#include "midi++/jack_midi_port.h"
 #include "midi++/mmc.h"
 #include "midi++/manager.h"
 
@@ -133,7 +134,7 @@ _thread_init_callback (void * /*arg*/)
 
        SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
 
-       MIDI::Port::set_process_thread (pthread_self());
+       MIDI::JackMIDIPort::set_process_thread (pthread_self());
 }
 
 static void
@@ -233,7 +234,7 @@ AudioEngine::stop (bool forever)
                } else {
                        jack_deactivate (_priv_jack);
                        Stopped(); /* EMIT SIGNAL */
-                       MIDI::Port::JackHalted (); /* EMIT SIGNAL */
+                       MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
                }
        }
 
@@ -1106,7 +1107,7 @@ AudioEngine::halted (void *arg)
 
        if (was_running) {
                ae->Halted(""); /* EMIT SIGNAL */
-               MIDI::Port::JackHalted (); /* EMIT SIGNAL */
+               MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
        }
 }
 
@@ -1356,7 +1357,7 @@ AudioEngine::disconnect_from_jack ()
        if (_running) {
                _running = false;
                Stopped(); /* EMIT SIGNAL */
-               MIDI::Port::JackHalted (); /* EMIT SIGNAL */
+               MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
        }
 
        return 0;
index 770a3714575db47e53427c97d79a6a25dfca3e62..302dce86ba402a57582d38cea584eaa5fea8c7a5 100644 (file)
@@ -143,6 +143,8 @@ MidiControlUI::reset_ports ()
                if ((fd = (*i)->selectable ()) >= 0) {
                        Glib::RefPtr<IOSource> psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR);
 
+                       cerr << "MIDI UI listening to " << (*i)->name() << endl;
+
                        psrc->connect (sigc::bind (sigc::mem_fun (this, &MidiControlUI::midi_input_handler), *i));
                        psrc->attach (_main_loop->get_context());
 
index 5744b55c65dd62eb8278c4ee248a31668526ade2..80dbd989639dfd3d9580e86a2a4bcf97d745be88 100644 (file)
 #include "ardour/operations.h"
 
 #include "midi++/port.h"
+#include "midi++/jack_midi_port.h"
 #include "midi++/mmc.h"
 #include "midi++/manager.h"
 
@@ -837,7 +838,7 @@ Session::hookup_io ()
        /* Tell all IO objects to connect themselves together */
 
        IO::enable_connecting ();
-       MIDI::Port::MakeConnections ();
+       MIDI::JackMIDIPort::MakeConnections ();
 
        /* Now reset all panners */
 
index 734b4013563627e952bb5f7ffd5e2318a656f1aa..5d078952a1f01db16f0fa766a699022568717880 100644 (file)
@@ -20,6 +20,7 @@
 #include "pbd/stacktrace.h"
 
 #include "midi++/port.h"
+#include "midi++/jack_midi_port.h"
 #include "midi++/manager.h"
 
 #include "evoral/midi_events.h"
@@ -152,13 +153,13 @@ void MidiClockTicker::tick (const framepos_t& transport_frame)
                double next_tick = _last_tick + one_ppqn_in_frames (transport_frame);
                frameoffset_t next_tick_offset = llrint (next_tick) - transport_frame;
 
+               MIDI::JackMIDIPort* mp = dynamic_cast<MIDI::JackMIDIPort*> (_midi_port);
+               
                DEBUG_TRACE (PBD::DEBUG::MidiClock,
                             string_compose ("Transport: %1, last tick time: %2, next tick time: %3, offset: %4, cycle length: %5\n",
-                                            transport_frame, _last_tick, next_tick, next_tick_offset, _midi_port->nframes_this_cycle()
-                                    )
-                       );
+                                            transport_frame, _last_tick, next_tick, next_tick_offset, mp ? mp->nframes_this_cycle() : 0));
 
-               if (next_tick_offset >= _midi_port->nframes_this_cycle()) {
+               if (!mp || (next_tick_offset >= mp->nframes_this_cycle())) {
                        break;
                }
 
index 0c9fbe39d18d6bdebc737dafd51a6ec4455553a8..66ce5ed71ca6ad99f407d7f23f01d94b0ac8cca6 100644 (file)
@@ -25,7 +25,7 @@
 
 using namespace MIDI;
 
-Channel::Channel (byte channelnum, PortBase &p) 
+Channel::Channel (byte channelnum, Port &p) 
        : _port (p)
 {
        _channel_number = channelnum;
diff --git a/libs/midi++2/ipmidi_port.cc b/libs/midi++2/ipmidi_port.cc
new file mode 100644 (file)
index 0000000..0ef0a99
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+    Copyright (C) 2012 Paul Davie
+    
+    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.
+
+    $Id: port.cc 12065 2012-04-23 16:23:48Z paul $
+*/
+#include <iostream>
+#include <cstdio>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#if defined(WIN32)
+static WSADATA g_wsaData;
+typedef int socklen_t;
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+inline void closesocket(int s) { ::close(s); }
+#endif
+
+#include <jack/jack.h>
+#include <jack/midiport.h>
+
+#include "pbd/xml++.h"
+#include "pbd/error.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/convert.h"
+#include "pbd/compose.h"
+
+#include "midi++/types.h"
+#include "midi++/ipmidi_port.h"
+#include "midi++/channel.h"
+
+using namespace MIDI;
+using namespace std;
+using namespace PBD;
+
+IPMIDIPort::IPMIDIPort (int base_port, const string& iface)
+       : Port (string_compose ("IPmidi@%1", base_port), Port::Flags (Port::IsInput|Port::IsOutput))
+       , sockin (-1)
+       , sockout (-1)
+{
+       if (!open_sockets (base_port, iface)) {
+               throw (failed_constructor ());
+       }
+}
+
+IPMIDIPort::IPMIDIPort (const XMLNode& node)
+       : Port (node)
+{
+       /* base class does not class set_state() */
+       set_state (node);
+}
+
+IPMIDIPort::~IPMIDIPort ()
+{
+       close_sockets ();
+}
+
+int
+IPMIDIPort::selectable () const
+{ 
+       return sockin; 
+}
+
+XMLNode&
+IPMIDIPort::get_state () const
+{
+       return Port::get_state ();
+}
+
+void
+IPMIDIPort::set_state (const XMLNode& node)
+{
+       Port::set_state (node);
+}
+
+void
+IPMIDIPort::close_sockets ()
+{
+       if (sockin >= 0) {
+               ::closesocket (sockin);
+               sockin = -1;
+       }
+       
+       if (sockout >= 0) {
+               ::closesocket (sockout);
+               sockout = -1;
+       }
+}
+
+static bool 
+get_address (int sock, struct in_addr *inaddr, const string& ifname )
+{
+       // Get interface address from supplied name.
+
+#if !defined(WIN32)
+       struct ifreq ifr;
+       ::strncpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name));
+
+       if (::ioctl(sock, SIOCGIFFLAGS, (char *) &ifr)) {
+               ::perror("ioctl(SIOCGIFFLAGS)");
+               return false;
+       }
+
+       if ((ifr.ifr_flags & IFF_UP) == 0) {
+               error << string_compose ("interface %1 is down", ifname) << endmsg;
+               return false;
+       }
+
+       if (::ioctl(sock, SIOCGIFADDR, (char *) &ifr)) {
+               ::perror("ioctl(SIOCGIFADDR)");
+               return false;
+       }
+
+       struct sockaddr_in sa;
+       ::memcpy(&sa, &ifr.ifr_addr, sizeof(struct sockaddr_in));
+       inaddr->s_addr = sa.sin_addr.s_addr;
+
+       return true;
+
+#else
+
+       return false;
+
+#endif // !WIN32
+}
+
+bool
+IPMIDIPort::open_sockets (int base_port, const string& ifname)
+{
+       int protonum = 0;
+       struct protoent *proto = ::getprotobyname("IP");
+
+       if (proto) {
+               protonum = proto->p_proto;
+       }
+
+       sockin = ::socket (PF_INET, SOCK_DGRAM, protonum);
+       if (sockin < 0) {
+               ::perror("socket(in)");
+               return false;
+       }
+
+       struct sockaddr_in addrin;
+       ::memset(&addrin, 0, sizeof(addrin));
+       addrin.sin_family = AF_INET;
+       addrin.sin_addr.s_addr = htonl(INADDR_ANY);
+       addrin.sin_port = htons(base_port);
+       
+       if (::bind(sockin, (struct sockaddr *) (&addrin), sizeof(addrin)) < 0) {
+               ::perror("bind");
+               return false;
+       }
+       
+       // Will Hall, 2007
+       // INADDR_ANY will bind to default interface,
+       // specify alternate interface nameon which to bind...
+       struct in_addr if_addr_in;
+       if (!ifname.empty()) {
+               if (!get_address(sockin, &if_addr_in, ifname)) {
+                       error << string_compose ("socket(in): could not find interface address for %1", ifname) << endmsg;
+                       return false;
+               }
+               if (::setsockopt(sockin, IPPROTO_IP, IP_MULTICAST_IF,
+                                (char *) &if_addr_in, sizeof(if_addr_in))) {
+                       ::perror("setsockopt(IP_MULTICAST_IF)");
+                       return false;
+               }
+       } else {
+               if_addr_in.s_addr = htonl (INADDR_ANY);
+       }
+       
+       struct ip_mreq mreq;
+       mreq.imr_multiaddr.s_addr = ::inet_addr("225.0.0.37");
+       mreq.imr_interface.s_addr = if_addr_in.s_addr;
+       if(::setsockopt (sockin, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq)) < 0) {
+               ::perror("setsockopt(IP_ADD_MEMBERSHIP)");
+               fprintf(stderr, "socket(in): your kernel is probably missing multicast support.\n");
+               return false;
+       }
+
+       // Output socket...
+
+       sockout = ::socket (AF_INET, SOCK_DGRAM, protonum);
+
+       if (sockout < 0) {
+               ::perror("socket(out)");
+               return false;
+       }
+       
+       // Will Hall, Oct 2007
+       if (!ifname.empty()) {
+               struct in_addr if_addr_out;
+               if (!get_address(sockout, &if_addr_out, ifname)) {
+                       error << string_compose ("socket(out): could not find interface address for %1", ifname) << endmsg;
+                       return false;
+               }
+               if (::setsockopt(sockout, IPPROTO_IP, IP_MULTICAST_IF, (char *) &if_addr_out, sizeof(if_addr_out))) {
+                       ::perror("setsockopt(IP_MULTICAST_IF)");
+                       return false;
+               }
+       }
+       
+       ::memset(&addrout, 0, sizeof(struct sockaddr_in));
+       addrout.sin_family = AF_INET;
+       addrout.sin_addr.s_addr = ::inet_addr("225.0.0.37");
+       addrout.sin_port = htons (base_port);
+       
+       // Turn off loopback...
+       int loop = 0;
+       if (::setsockopt(sockout, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof (loop)) < 0) {
+               ::perror("setsockopt(IP_MULTICAST_LOOP)");
+               return false;
+       }
+
+       if (fcntl (sockin, F_SETFL, O_NONBLOCK)) {
+               error << "cannot set non-blocking mode for IP MIDI input socket (" << ::strerror (errno) << ')' << endmsg;
+               return false;
+       }
+
+       if (fcntl (sockout, F_SETFL, O_NONBLOCK)) {
+               error << "cannot set non-blocking mode for IP MIDI output socket (" << ::strerror (errno) << ')' << endmsg;
+               return false;
+       }
+       
+       return true;
+}
+
+int
+IPMIDIPort::write (byte* msg, size_t msglen, timestamp_t /* ignored */) {
+
+       if (sockout) {
+               if (::sendto (sockout, (char *) msg, msglen, 0, (struct sockaddr *) &addrout, sizeof(struct sockaddr_in)) < 0) {
+                       ::perror("sendto");
+                       return -1;
+               }
+               return msglen;
+       }
+       return 0;
+}
+
+int
+IPMIDIPort::read (byte* buf, size_t bufsize)
+{
+       /* nothing to do here - all handled by parse() */
+       return 0;
+}
+
+void
+IPMIDIPort::parse (framecnt_t timestamp)
+{
+       /* input was detected on the socket, so go get it and hand it to the
+        * parser. This will emit appropriate signals that will be handled
+        * by anyone who cares.
+        */
+       
+       unsigned char buf[1024];
+       struct sockaddr_in sender;
+       socklen_t slen = sizeof(sender);
+       int r = ::recvfrom (sockin, (char *) buf, sizeof(buf), 0, (struct sockaddr *) &sender, &slen);
+
+       if (r >= 0) {
+
+               _parser->set_timestamp (timestamp);
+               
+               for (int i = 0; i < r; ++i) {
+                       _parser->scanner (buf[i]);
+               }
+       } else {
+               ::perror ("failed to recv from socket");
+       }
+}
+
diff --git a/libs/midi++2/jack_midi_port.cc b/libs/midi++2/jack_midi_port.cc
new file mode 100644 (file)
index 0000000..b00aff8
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+    Copyright (C) 1998 Paul Barton-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.
+
+    $Id$
+*/
+#include <iostream>
+#include <cstdio>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <jack/jack.h>
+#include <jack/midiport.h>
+
+#include "pbd/xml++.h"
+#include "pbd/error.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/convert.h"
+#include "pbd/strsplit.h"
+#include "pbd/stacktrace.h"
+
+#include "midi++/types.h"
+#include "midi++/jack_midi_port.h"
+#include "midi++/channel.h"
+
+using namespace MIDI;
+using namespace std;
+using namespace PBD;
+
+pthread_t JackMIDIPort::_process_thread;
+Signal0<void> JackMIDIPort::JackHalted;
+Signal0<void> JackMIDIPort::MakeConnections;
+
+JackMIDIPort::JackMIDIPort (string const & name, Flags flags, jack_client_t* jack_client)
+       : Port (name, flags)
+       , _currently_in_cycle (false)
+       , _nframes_this_cycle (0)
+       , _jack_client (jack_client)
+       , _jack_port (0)
+       , output_fifo (512)
+       , input_fifo (1024)
+       , xthread (true)
+{
+       assert (jack_client);
+       init (name, flags);
+}
+
+JackMIDIPort::JackMIDIPort (const XMLNode& node, jack_client_t* jack_client)
+       : Port (node)
+       , _currently_in_cycle (false)
+       , _nframes_this_cycle (0)
+       , _jack_client (jack_client)
+       , _jack_port (0)
+       , output_fifo (512)
+       , input_fifo (1024)
+       , xthread (true)
+{
+       assert (jack_client);
+
+       Descriptor desc (node);
+       init (desc.tag, desc.flags);
+       set_state (node);
+}
+
+void
+JackMIDIPort::init (string const & name, Flags flags)
+{
+       if (!create_port ()) {
+               _ok = true;
+       }
+
+       MakeConnections.connect_same_thread (connect_connection, boost::bind (&JackMIDIPort::make_connections, this));
+       JackHalted.connect_same_thread (halt_connection, boost::bind (&JackMIDIPort::jack_halted, this));
+}
+
+
+JackMIDIPort::~JackMIDIPort ()
+{
+       for (int i = 0; i < 16; i++) {
+               delete _channel[i];
+       }
+
+       if (_jack_port) {
+               if (_jack_client) {
+                       jack_port_unregister (_jack_client, _jack_port);
+                       _jack_port = 0;
+               }
+       }
+}
+
+void
+JackMIDIPort::parse (framecnt_t timestamp)
+{
+       byte buf[512];
+
+       /* NOTE: parsing is done (if at all) by initiating a read from 
+          the port. Each port implementation calls on the parser
+          once it has data ready.
+       */
+       
+       _parser->set_timestamp (timestamp);
+
+       while (1) {
+               
+               // cerr << "+++ READ ON " << name() << endl;
+
+               int nread = read (buf, sizeof (buf));
+
+               // cerr << "-- READ (" << nread << " ON " << name() << endl;
+               
+               if (nread > 0) {
+                       if ((size_t) nread < sizeof (buf)) {
+                               break;
+                       } else {
+                               continue;
+                       }
+               } else if (nread == 0) {
+                       break;
+               } else if (errno == EAGAIN) {
+                       break;
+               } else {
+                       fatal << "Error reading from MIDI port " << name() << endmsg;
+                       /*NOTREACHED*/
+               }
+       }
+}
+
+void
+JackMIDIPort::cycle_start (pframes_t nframes)
+{
+       assert (_jack_port);
+       
+       _currently_in_cycle = true;
+       _nframes_this_cycle = nframes;
+
+       assert(_nframes_this_cycle == nframes);
+
+       if (sends_output()) {
+               void *buffer = jack_port_get_buffer (_jack_port, nframes);
+               jack_midi_clear_buffer (buffer);
+               flush (buffer); 
+       }
+       
+       if (receives_input()) {
+               void* jack_buffer = jack_port_get_buffer(_jack_port, nframes);
+               const pframes_t event_count = jack_midi_get_event_count(jack_buffer);
+
+               jack_midi_event_t ev;
+               timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
+
+               for (pframes_t i = 0; i < event_count; ++i) {
+                       jack_midi_event_get (&ev, jack_buffer, i);
+                       input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
+               }       
+               
+               if (event_count) {
+                       xthread.wakeup ();
+               }
+       }
+}
+
+void
+JackMIDIPort::cycle_end ()
+{
+       if (sends_output()) {
+               flush (jack_port_get_buffer (_jack_port, _nframes_this_cycle));
+       }
+
+       _currently_in_cycle = false;
+       _nframes_this_cycle = 0;
+}
+
+void
+JackMIDIPort::jack_halted ()
+{
+       _jack_client = 0;
+       _jack_port = 0;
+}
+
+void
+JackMIDIPort::drain (int check_interval_usecs)
+{
+       RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+
+       if (is_process_thread()) {
+               error << "Process thread called MIDI::JackMIDIPort::drain() - this cannot work" << endmsg;
+               return;
+       }
+
+       while (1) {
+               output_fifo.get_write_vector (&vec);
+               if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
+                       break;
+               }
+               usleep (check_interval_usecs);
+       }
+}
+
+int
+JackMIDIPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
+{
+       int ret = 0;
+
+       if (!sends_output()) {
+               return ret;
+       }
+       
+       if (!is_process_thread()) {
+
+               Glib::Mutex::Lock lm (output_fifo_lock);
+               RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+               
+               output_fifo.get_write_vector (&vec);
+
+               if (vec.len[0] + vec.len[1] < 1) {
+                       error << "no space in FIFO for non-process thread MIDI write" << endmsg;
+                       return 0;
+               }
+
+               if (vec.len[0]) {
+                        if (!vec.buf[0]->owns_buffer()) {
+                                vec.buf[0]->set_buffer (0, 0, true);
+                        }
+                       vec.buf[0]->set (msg, msglen, timestamp);
+               } else {
+                        if (!vec.buf[1]->owns_buffer()) {
+                                vec.buf[1]->set_buffer (0, 0, true);
+                        }
+                       vec.buf[1]->set (msg, msglen, timestamp);
+               }
+
+               output_fifo.increment_write_idx (1);
+               
+               ret = msglen;
+
+       } else {
+
+               // XXX This had to be temporarily commented out to make export work again
+               if (!(timestamp < _nframes_this_cycle)) {
+                       std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time "
+                                 << timestamp << " of " << _nframes_this_cycle
+                                 << " (this will not work - needs a code fix)"
+                                 << std::endl;
+               }
+
+               if (_currently_in_cycle) {
+                       if (timestamp == 0) {
+                               timestamp = _last_write_timestamp;
+                       } 
+
+                       if (jack_midi_event_write (jack_port_get_buffer (_jack_port, _nframes_this_cycle), 
+                                               timestamp, msg, msglen) == 0) {
+                               ret = msglen;
+                               _last_write_timestamp = timestamp;
+
+                       } else {
+                               ret = 0;
+                               cerr << "write of " << msglen << " failed, port holds "
+                                       << jack_midi_get_event_count (jack_port_get_buffer (_jack_port, _nframes_this_cycle))
+                                       << endl;
+                       }
+               } else {
+                       cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
+                       PBD::stacktrace (cerr, 20);
+               }
+       }
+
+       if (ret > 0 && _parser) {
+               // ardour doesn't care about this and neither should your app, probably
+               // output_parser->raw_preparse (*output_parser, msg, ret);
+               for (int i = 0; i < ret; i++) {
+                       _parser->scanner (msg[i]);
+               }
+               // ardour doesn't care about this and neither should your app, probably
+               // output_parser->raw_postparse (*output_parser, msg, ret);
+       }       
+
+       return ret;
+}
+
+void
+JackMIDIPort::flush (void* jack_port_buffer)
+{
+       RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
+       size_t written;
+
+       output_fifo.get_read_vector (&vec);
+
+       if (vec.len[0] + vec.len[1]) {
+               // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
+       }
+
+       if (vec.len[0]) {
+               Evoral::Event<double>* evp = vec.buf[0];
+               
+               for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
+                       jack_midi_event_write (jack_port_buffer,
+                                              (timestamp_t) evp->time(), evp->buffer(), evp->size());
+               }
+       }
+       
+       if (vec.len[1]) {
+               Evoral::Event<double>* evp = vec.buf[1];
+
+               for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
+                       jack_midi_event_write (jack_port_buffer,
+                                              (timestamp_t) evp->time(), evp->buffer(), evp->size());
+               }
+       }
+       
+       if ((written = vec.len[0] + vec.len[1]) != 0) {
+               output_fifo.increment_read_idx (written);
+       }
+}
+
+int
+JackMIDIPort::read (byte *, size_t)
+{
+       if (!receives_input()) {
+               return 0;
+       }
+       
+       timestamp_t time;
+       Evoral::EventType type;
+       uint32_t size;
+       byte buffer[input_fifo.capacity()];
+
+       while (input_fifo.read (&time, &type, &size, buffer)) {
+               _parser->set_timestamp (time);
+               for (uint32_t i = 0; i < size; ++i) {
+                       _parser->scanner (buffer[i]);
+               }
+       }
+
+       return 0;
+}
+
+int
+JackMIDIPort::create_port ()
+{
+       _jack_port = jack_port_register(_jack_client, _tagname.c_str(), JACK_DEFAULT_MIDI_TYPE, _flags, 0);
+       return _jack_port == 0 ? -1 : 0;
+}
+
+XMLNode& 
+JackMIDIPort::get_state () const
+{
+       XMLNode& root = Port::get_state ();
+       
+#if 0
+       byte device_inquiry[6];
+
+       device_inquiry[0] = 0xf0;
+       device_inquiry[0] = 0x7e;
+       device_inquiry[0] = 0x7f;
+       device_inquiry[0] = 0x06;
+       device_inquiry[0] = 0x02;
+       device_inquiry[0] = 0xf7;
+       
+       write (device_inquiry, sizeof (device_inquiry), 0);
+#endif
+
+       if (_jack_port) {
+               
+               const char** jc = jack_port_get_connections (_jack_port);
+               string connection_string;
+               if (jc) {
+                       for (int i = 0; jc[i]; ++i) {
+                               if (i > 0) {
+                                       connection_string += ',';
+                               }
+                               connection_string += jc[i];
+                       }
+                       free (jc);
+               }
+               
+               if (!connection_string.empty()) {
+                       root.add_property ("connections", connection_string);
+               }
+       } else {
+               if (!_connections.empty()) {
+                       root.add_property ("connections", _connections);
+               }
+       }
+
+       return root;
+}
+
+void
+JackMIDIPort::set_state (const XMLNode& node)
+{
+       const XMLProperty* prop;
+
+       if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) {
+               return;
+       }
+       
+       Port::set_state (node);
+
+       if ((prop = node.property ("connections")) != 0) {
+               _connections = prop->value ();
+       }
+}
+
+void
+JackMIDIPort::make_connections ()
+{
+       if (!_connections.empty()) {
+               vector<string> ports;
+               split (_connections, ports, ',');
+               for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
+                       if (_jack_client) {
+                               if (receives_input()) {
+                                       jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_port));
+                               } else {
+                                       jack_connect (_jack_client, jack_port_name (_jack_port), (*x).c_str());
+                               }
+                               /* ignore failures */
+                       }
+               }
+       }
+
+       connect_connection.disconnect ();
+}
+
+void
+JackMIDIPort::set_process_thread (pthread_t thr)
+{
+       _process_thread = thr;
+}
+
+bool
+JackMIDIPort::is_process_thread()
+{
+       return (pthread_self() == _process_thread);
+}
+
+void
+JackMIDIPort::reestablish (jack_client_t* jack)
+{
+       _jack_client = jack;
+       int const r = create_port ();
+
+       if (r) {
+               PBD::error << "could not reregister ports for " << name() << endmsg;
+       }
+}
+
+void
+JackMIDIPort::reconnect ()
+{
+       make_connections ();
+}
index 61d4c4c363ba31ac25c5f9fa8198af6058eb9ad4..822c74e1254ae1d5d48daebfe59345c78d3807d4 100644 (file)
@@ -27,6 +27,7 @@
 #include "midi++/manager.h"
 #include "midi++/channel.h"
 #include "midi++/port.h"
+#include "midi++/jack_midi_port.h"
 #include "midi++/mmc.h"
 
 using namespace std;
@@ -40,12 +41,12 @@ Manager::Manager (jack_client_t* jack)
 {
        _mmc = new MachineControl (this, jack);
        
-       _mtc_input_port = add_port (new MIDI::Port ("MTC in", Port::IsInput, jack));
-       _mtc_output_port = add_port (new MIDI::Port ("MTC out", Port::IsOutput, jack));
-       _midi_input_port = add_port (new MIDI::Port ("MIDI control in", Port::IsInput, jack));
-       _midi_output_port = add_port (new MIDI::Port ("MIDI control out", Port::IsOutput, jack));
-       _midi_clock_input_port = add_port (new MIDI::Port ("MIDI clock in", Port::IsInput, jack));
-       _midi_clock_output_port = add_port (new MIDI::Port ("MIDI clock out", Port::IsOutput, jack));
+       _mtc_input_port = add_port (new MIDI::JackMIDIPort ("MTC in", Port::IsInput, jack));
+       _mtc_output_port = add_port (new MIDI::JackMIDIPort ("MTC out", Port::IsOutput, jack));
+       _midi_input_port = add_port (new MIDI::JackMIDIPort ("MIDI control in", Port::IsInput, jack));
+       _midi_output_port = add_port (new MIDI::JackMIDIPort ("MIDI control out", Port::IsOutput, jack));
+       _midi_clock_input_port = add_port (new MIDI::JackMIDIPort ("MIDI clock in", Port::IsInput, jack));
+       _midi_clock_output_port = add_port (new MIDI::JackMIDIPort ("MIDI clock out", Port::IsOutput, jack));
 }
 
 Manager::~Manager ()
@@ -117,7 +118,10 @@ Manager::reestablish (jack_client_t* jack)
        boost::shared_ptr<PortList> pr = _ports.reader ();
 
        for (PortList::const_iterator p = pr->begin(); p != pr->end(); ++p) {
-               (*p)->reestablish (jack);
+               JackMIDIPort* pp = dynamic_cast<JackMIDIPort*> (*p);
+               if (pp) {
+                       pp->reestablish (jack);
+               }
        }
 }
 
@@ -128,7 +132,10 @@ Manager::reconnect ()
        boost::shared_ptr<PortList> pr = _ports.reader ();
 
        for (PortList::const_iterator p = pr->begin(); p != pr->end(); ++p) {
-               (*p)->reconnect ();
+               JackMIDIPort* pp = dynamic_cast<JackMIDIPort*> (*p);
+               if (pp) {
+                       pp->reconnect ();
+               }
        }
 }
 
index 370a5691569e5f62dac6a1ea006be57b031d5175..d00ce700c542997b183e7de2dfe3111902c552d3 100644 (file)
@@ -39,9 +39,9 @@ class Port;
 class Channel : public PBD::ScopedConnectionList {
 
   public:
-       Channel (byte channel_number, PortBase &);
+       Channel (byte channel_number, Port &);
 
-       PortBase &midi_port()           { return _port; }
+       Port &midi_port()           { return _port; }
        byte channel()                  { return _channel_number; }
        byte program()                  { return _program_number; }
        byte bank()                     { return _bank_number; }
@@ -111,11 +111,11 @@ class Channel : public PBD::ScopedConnectionList {
        }
 
   protected:
-       friend class PortBase;
+       friend class Port;
        void connect_signals ();
 
   private:
-       PortBase& _port;
+       Port& _port;
 
        /* Current channel values */
        byte               _channel_number;
diff --git a/libs/midi++2/midi++/ipmidi_port.h b/libs/midi++2/midi++/ipmidi_port.h
new file mode 100644 (file)
index 0000000..bf949bd
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+    Copyright (C) 1998-2010 Paul Barton-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  __libmidi_ipmidi_port_h__
+#define  __libmidi_ipmidi_port_h__
+
+#include <string>
+#include <iostream>
+#if defined(WIN32)
+#include <winsock.h>
+#else
+#include <arpa/inet.h>
+#include <net/if.h>
+#endif
+
+#include <jack/types.h>
+
+#include "pbd/xml++.h"
+#include "pbd/crossthread.h"
+#include "pbd/signals.h"
+#include "pbd/ringbuffer.h"
+
+#include "evoral/Event.hpp"
+#include "evoral/EventRingBuffer.hpp"
+
+#include "midi++/types.h"
+#include "midi++/parser.h"
+#include "midi++/port.h"
+
+namespace MIDI {
+
+class IPMIDIPort : public Port {
+  public:
+    IPMIDIPort (int base_port = lowest_ipmidi_port_default, const std::string& ifname = std::string());
+    IPMIDIPort (const XMLNode&);
+    ~IPMIDIPort ();
+    
+    XMLNode& get_state () const;
+    void set_state (const XMLNode&);
+    
+    int write (byte *msg, size_t msglen, timestamp_t timestamp);
+    int read (byte *buf, size_t bufsize);
+    void parse (framecnt_t timestamp);
+    int selectable () const;
+
+    static const int lowest_ipmidi_port_default = 21928;
+
+private:       
+    int sockin;
+    int sockout;
+    struct sockaddr_in addrout;
+    
+    bool open_sockets (int base_port, const std::string& ifname);
+    void close_sockets ();
+
+    void init (std::string const &, Flags);
+};
+
+} // namespace MIDI
+
+#endif // __libmidi_ipmidi_port_h__
diff --git a/libs/midi++2/midi++/jack_midi_port.h b/libs/midi++2/midi++/jack_midi_port.h
new file mode 100644 (file)
index 0000000..e381120
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+    Copyright (C) 1998-2010 Paul Barton-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  __libmidi_port_h__
+#define  __libmidi_port_h__
+
+#include <string>
+#include <iostream>
+
+#include <jack/types.h>
+
+#include "pbd/xml++.h"
+#include "pbd/crossthread.h"
+#include "pbd/signals.h"
+#include "pbd/ringbuffer.h"
+
+#include "evoral/Event.hpp"
+#include "evoral/EventRingBuffer.hpp"
+
+#include "midi++/types.h"
+#include "midi++/parser.h"
+#include "midi++/port.h"
+
+namespace MIDI {
+
+class Channel;
+class PortRequest;
+
+class JackMIDIPort : public Port {
+  public:
+       JackMIDIPort (std::string const &, Port::Flags, jack_client_t *);
+       JackMIDIPort (const XMLNode&, jack_client_t *);
+       ~JackMIDIPort ();
+
+       XMLNode& get_state () const;
+       void set_state (const XMLNode&);
+
+       void cycle_start (pframes_t nframes);
+       void cycle_end ();
+
+       void parse (framecnt_t timestamp);
+       int write (byte *msg, size_t msglen, timestamp_t timestamp);
+       int read (byte *buf, size_t bufsize);
+       void drain (int check_interval_usecs);
+       int selectable () const { return xthread.selectable(); }
+
+       pframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
+
+       void reestablish (jack_client_t *);
+       void reconnect ();
+
+       static void set_process_thread (pthread_t);
+       static pthread_t get_process_thread () { return _process_thread; }
+       static bool is_process_thread();
+
+       static PBD::Signal0<void> MakeConnections;
+       static PBD::Signal0<void> JackHalted;
+
+private:       
+       bool              _currently_in_cycle;
+       pframes_t         _nframes_this_cycle;
+       jack_client_t*    _jack_client;
+       jack_port_t*      _jack_port;
+       timestamp_t       _last_write_timestamp;
+       RingBuffer< Evoral::Event<double> > output_fifo;
+       Evoral::EventRingBuffer<timestamp_t> input_fifo;
+       Glib::Mutex output_fifo_lock;
+       CrossThreadChannel xthread;
+
+       int create_port ();
+
+       /** Channel used to signal to the MidiControlUI that input has arrived */
+       
+       std::string _connections;
+       PBD::ScopedConnection connect_connection;
+       PBD::ScopedConnection halt_connection;
+       void flush (void* jack_port_buffer);
+       void jack_halted ();
+       void make_connections ();
+       void init (std::string const &, Flags);
+
+       static pthread_t _process_thread;
+
+};
+
+} // namespace MIDI
+
+#endif // __libmidi_port_h__
index bab4a18dd22dc4635f74d36389d8d9bfbf5ce73f..c37ba392b4f14954ffc203f53cb79f981f1beff9 100644 (file)
@@ -88,12 +88,12 @@ class Manager {
        static Manager *theManager;
 
        MIDI::MachineControl*   _mmc;
-       MIDI::Port*             _mtc_input_port;
-       MIDI::Port*             _mtc_output_port;
-       MIDI::Port*             _midi_input_port;
-       MIDI::Port*             _midi_output_port;
-       MIDI::Port*             _midi_clock_input_port;
-       MIDI::Port*             _midi_clock_output_port;
+       MIDI::Port*         _mtc_input_port;
+       MIDI::Port*         _mtc_output_port;
+       MIDI::Port*         _midi_input_port;
+       MIDI::Port*         _midi_output_port;
+       MIDI::Port*         _midi_clock_input_port;
+       MIDI::Port*         _midi_clock_output_port;
 
        SerializedRCUManager<PortList> _ports;
 };
index ac452798cc60fbe72cba6566217edd7182d10bd3..f5f343e95217812f4143525c3ff229a8f7e9ffea 100644 (file)
@@ -29,7 +29,7 @@
 
 namespace MIDI {
 
-class PortBase;
+class Port;
 class Parser;
 
 typedef PBD::Signal1<void,Parser&>                   ZeroByteSignal;
@@ -41,7 +41,7 @@ typedef PBD::Signal3<void,Parser &, byte *, size_t>  Signal;
 
 class Parser {
  public:
-       Parser (PortBase &p);
+       Parser (Port &p);
        ~Parser ();
 
        /* sets the time that will be reported for any MTC or MIDI Clock
@@ -105,7 +105,7 @@ class Parser {
        const char *midi_event_type_name (MIDI::eventType);
        void trace (bool onoff, std::ostream *o, const std::string &prefix = "");
        bool tracing() { return trace_stream != 0; }
-       PortBase &port() { return _port; }
+       Port &port() { return _port; }
 
        void set_offline (bool);
        bool offline() const { return _offline; }
@@ -136,7 +136,7 @@ class Parser {
        void reset_mtc_state ();
        
   private:
-       PortBase&_port;
+       Port&_port;
        /* tracing */
        
        std::ostream *trace_stream;
index e7b532013ebfe04cb8d61808d5afffb9220ecc70..a2315f72844c7c1cc467b6cac9506227bbfcfa11 100644 (file)
@@ -16,8 +16,8 @@
 
 */
 
-#ifndef  __libmidi_port_h__
-#define  __libmidi_port_h__
+#ifndef  __libmidi_port_base_h__
+#define  __libmidi_port_base_h__
 
 #include <string>
 #include <iostream>
 
 #include "midi++/types.h"
 #include "midi++/parser.h"
-#include "midi++/port_base.h"
 
 namespace MIDI {
 
 class Channel;
 class PortRequest;
 
-class Port : public PortBase {
+class Port {
   public:
-       Port (std::string const &, PortBase::Flags, jack_client_t *);
-       Port (const XMLNode&, jack_client_t *);
-       ~Port ();
+       enum Flags {
+               IsInput = JackPortIsInput,
+               IsOutput = JackPortIsOutput,
+       };
+       
+       Port (std::string const &, Flags);
+       Port (const XMLNode&);
+       virtual ~Port ();
 
        XMLNode& get_state () const;
        void set_state (const XMLNode&);
 
-       void cycle_start (pframes_t nframes);
-       void cycle_end ();
+       // FIXME: make Manager a friend of port so these can be hidden?
+
+       /* Only for use by MidiManager.  Don't ever call this. */
+       virtual void cycle_start (pframes_t nframes) {}
+       /* Only for use by MidiManager.  Don't ever call this. */
+       virtual void cycle_end () {}
+
+       /** Write a message to port.
+        * @param msg Raw MIDI message to send
+        * @param msglen Size of @a msg
+        * @param timestamp Time stamp in frames of this message (relative to cycle start)
+        * @return number of bytes successfully written
+        */
+       virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0;
+
+       /** Read raw bytes from a port.
+        * @param buf memory to store read data in
+        * @param bufsize size of @a buf
+        * @return number of bytes successfully read, negative if error
+        */
+       virtual int read (byte *buf, size_t bufsize) = 0;
+
+       /** block until the output FIFO used by non-process threads
+        * is empty, checking every @a check_interval_usecs usecs
+        * for current status. Not to be called by a thread that
+        * executes any part of a JACK process callback (will 
+        * simply return immediately in that situation).
+        */
+       virtual void drain (int check_interval_usecs) {}
+
+       /** Write a message to port.
+        * @return true on success.
+        * FIXME: describe semantics here
+        */
+       int midimsg (byte *msg, size_t len, timestamp_t timestamp) {
+               return !(write (msg, len, timestamp) == (int) len);
+       } 
+
+       virtual void parse (framecnt_t timestamp) = 0;
+
+       bool clock (timestamp_t timestamp);
+
+       /* select(2)/poll(2)-based I/O */
+
+       /** Get the file descriptor for port.
+        * @return File descriptor, or -1 if not selectable. 
+        */
+       virtual int selectable () const = 0;
+
+       Channel *channel (channel_t chn) { 
+               return _channel[chn&0x7F];
+       }
+       
+       Parser* parser () {
+               return _parser;
+       }
+       
+       const char *name () const   { return _tagname.c_str(); }
+       bool   ok ()   const        { return _ok; }
 
-       void parse (framecnt_t timestamp);
-       int write (byte *msg, size_t msglen, timestamp_t timestamp);
-       int read (byte *buf, size_t bufsize);
-       void drain (int check_interval_usecs);
-       int selectable () const { return xthread.selectable(); }
+       virtual bool centrally_parsed() const;
+       void set_centrally_parsed (bool yn) { _centrally_parsed = yn; }
 
-       pframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
+       bool receives_input () const {
+               return _flags == IsInput;
+       }
 
-       void reestablish (jack_client_t *);
-       void reconnect ();
+       bool sends_output () const {
+               return _flags == IsOutput;
+       }
 
-       static void set_process_thread (pthread_t);
-       static pthread_t get_process_thread () { return _process_thread; }
-       static bool is_process_thread();
+       struct Descriptor {
+           std::string tag;
+           Flags flags;
 
-       static PBD::Signal0<void> MakeConnections;
-       static PBD::Signal0<void> JackHalted;
+           Descriptor (const XMLNode&);
+           XMLNode& get_state();
+       };
 
-private:       
-       bool              _currently_in_cycle;
-       pframes_t         _nframes_this_cycle;
-       jack_client_t*    _jack_client;
-       jack_port_t*      _jack_port;
-       timestamp_t       _last_write_timestamp;
-       RingBuffer< Evoral::Event<double> > output_fifo;
-       Evoral::EventRingBuffer<timestamp_t> input_fifo;
-       Glib::Mutex output_fifo_lock;
-       CrossThreadChannel xthread;
+       static std::string state_node_name;
 
-       int create_port ();
+  protected:
+       bool              _ok;
+       std::string       _tagname;
+       Channel*          _channel[16];
+       Parser*           _parser;
+       Flags             _flags;
+       bool              _centrally_parsed;
 
-       /** Channel used to signal to the MidiControlUI that input has arrived */
-       
-       std::string _connections;
-       PBD::ScopedConnection connect_connection;
-       PBD::ScopedConnection halt_connection;
-       void flush (void* jack_port_buffer);
-       void jack_halted ();
-       void make_connections ();
        void init (std::string const &, Flags);
+};
 
-       static pthread_t _process_thread;
-
+struct PortSet {
+    PortSet (std::string str) : owner (str) { }
+    
+    std::string owner;
+    std::list<XMLNode> ports;
 };
 
+std::ostream & operator << (std::ostream& os, const Port& port);
+
 } // namespace MIDI
 
-#endif // __libmidi_port_h__
+#endif // __libmidi_port_base_h__
diff --git a/libs/midi++2/midi++/port_base.h b/libs/midi++2/midi++/port_base.h
deleted file mode 100644 (file)
index f1649d8..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
-    Copyright (C) 1998-2010 Paul Barton-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  __libmidi_port_base_h__
-#define  __libmidi_port_base_h__
-
-#include <string>
-#include <iostream>
-
-#include <jack/types.h>
-
-#include "pbd/xml++.h"
-#include "pbd/crossthread.h"
-#include "pbd/signals.h"
-#include "pbd/ringbuffer.h"
-
-#include "evoral/Event.hpp"
-#include "evoral/EventRingBuffer.hpp"
-
-#include "midi++/types.h"
-#include "midi++/parser.h"
-
-namespace MIDI {
-
-class Channel;
-class PortRequest;
-
-class PortBase {
-  public:
-       enum Flags {
-               IsInput = JackPortIsInput,
-               IsOutput = JackPortIsOutput,
-       };
-       
-       PortBase (std::string const &, Flags);
-       PortBase (const XMLNode&);
-       virtual ~PortBase ();
-
-       XMLNode& get_state () const;
-       void set_state (const XMLNode&);
-
-       // FIXME: make Manager a friend of port so these can be hidden?
-
-       /* Only for use by MidiManager.  Don't ever call this. */
-       virtual void cycle_start (pframes_t nframes) {}
-       /* Only for use by MidiManager.  Don't ever call this. */
-       virtual void cycle_end () {}
-
-       /** Write a message to port.
-        * @param msg Raw MIDI message to send
-        * @param msglen Size of @a msg
-        * @param timestamp Time stamp in frames of this message (relative to cycle start)
-        * @return number of bytes successfully written
-        */
-       virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0;
-
-       /** Read raw bytes from a port.
-        * @param buf memory to store read data in
-        * @param bufsize size of @a buf
-        * @return number of bytes successfully read, negative if error
-        */
-       virtual int read (byte *buf, size_t bufsize) = 0;
-
-       /** block until the output FIFO used by non-process threads
-        * is empty, checking every @a check_interval_usecs usecs
-        * for current status. Not to be called by a thread that
-        * executes any part of a JACK process callback (will 
-        * simply return immediately in that situation).
-        */
-       virtual void drain (int check_interval_usecs) {}
-
-       /** Write a message to port.
-        * @return true on success.
-        * FIXME: describe semantics here
-        */
-       int midimsg (byte *msg, size_t len, timestamp_t timestamp) {
-               return !(write (msg, len, timestamp) == (int) len);
-       } 
-
-       bool clock (timestamp_t timestamp);
-
-       /* select(2)/poll(2)-based I/O */
-
-       /** Get the file descriptor for port.
-        * @return File descriptor, or -1 if not selectable. 
-        */
-       virtual int selectable () const = 0;
-
-       Channel *channel (channel_t chn) { 
-               return _channel[chn&0x7F];
-       }
-       
-       Parser* parser () {
-               return _parser;
-       }
-       
-       const char *name () const   { return _tagname.c_str(); }
-       bool   ok ()   const        { return _ok; }
-
-       virtual bool centrally_parsed() const;
-       void set_centrally_parsed (bool yn) { _centrally_parsed = yn; }
-
-       bool receives_input () const {
-               return _flags == IsInput;
-       }
-
-       bool sends_output () const {
-               return _flags == IsOutput;
-       }
-
-       struct Descriptor {
-           std::string tag;
-           Flags flags;
-
-           Descriptor (const XMLNode&);
-           XMLNode& get_state();
-       };
-
-       static std::string state_node_name;
-
-  protected:
-       bool              _ok;
-       std::string       _tagname;
-       Channel*          _channel[16];
-       Parser*           _parser;
-       Flags             _flags;
-       bool              _centrally_parsed;
-
-       void init (std::string const &, Flags);
-};
-
-struct PortSet {
-    PortSet (std::string str) : owner (str) { }
-    
-    std::string owner;
-    std::list<XMLNode> ports;
-};
-
-std::ostream & operator << (std::ostream& os, const PortBase& port);
-
-} // namespace MIDI
-
-#endif // __libmidi_port_base_h__
index e02598def2c48522e506d47c80841fdda05cdb3a..06eadb5b34adcead348030f8061b05d769cc5b02 100644 (file)
@@ -25,6 +25,7 @@
 #include "pbd/error.h"
 #include "midi++/mmc.h"
 #include "midi++/port.h"
+#include "midi++/jack_midi_port.h"
 #include "midi++/parser.h"
 #include "midi++/manager.h"
 
@@ -202,8 +203,8 @@ MachineControl::MachineControl (Manager* m, jack_client_t* jack)
        _receive_device_id = 0x7f;
        _send_device_id = 0x7f;
 
-       _input_port = m->add_port (new Port ("MMC in", Port::IsInput, jack));
-       _output_port = m->add_port (new Port ("MMC out", Port::IsOutput, jack));
+       _input_port = m->add_port (new JackMIDIPort ("MMC in", Port::IsInput, jack));
+       _output_port = m->add_port (new JackMIDIPort ("MMC out", Port::IsOutput, jack));
 
        _input_port->parser()->mmc.connect_same_thread (port_connections, boost::bind (&MachineControl::process_mmc_message, this, _1, _2, _3));
        _input_port->parser()->start.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_start, this));
index a56e3f82e4d964976d774220b0ff4258a7e7d5f1..8e3af64504407f8cdbf5e9377256bbdac1a2a3b6 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "midi++/types.h"
 #include "midi++/parser.h"
-#include "midi++/port_base.h"
+#include "midi++/port.h"
 #include "midi++/mmc.h"
 #include "pbd/transmitter.h"
 
@@ -104,7 +104,7 @@ Parser::midi_event_type_name (eventType t)
        }
 }
 
-Parser::Parser (PortBase &p) 
+Parser::Parser (Port &p) 
        : _port(p)
 {
        trace_stream = 0;
index ef1704857dc9d0e196f428b64992c54360f45e27..3e7896631a48f6ec348574c59c03ecd74a31e30c 100644 (file)
@@ -15,7 +15,7 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    $Id$
+    $Id: port.cc 11871 2012-04-10 16:27:01Z paul $
 */
 #include <iostream>
 #include <cstdio>
@@ -40,325 +40,117 @@ using namespace MIDI;
 using namespace std;
 using namespace PBD;
 
-pthread_t Port::_process_thread;
-Signal0<void> Port::JackHalted;
-Signal0<void> Port::MakeConnections;
-
-Port::Port (string const & name, Flags flags, jack_client_t* jack_client)
-       : PortBase (name, flags)
-       , _currently_in_cycle (false)
-       , _nframes_this_cycle (0)
-       , _jack_client (jack_client)
-       , _jack_port (0)
-       , output_fifo (512)
-       , input_fifo (1024)
-       , xthread (true)
+string Port::state_node_name = "MIDI-port";
+
+Port::Port (string const & name, Flags flags)
+       : _flags (flags)
+       , _centrally_parsed (true)
 {
-       assert (jack_client);
        init (name, flags);
 }
 
-Port::Port (const XMLNode& node, jack_client_t* jack_client)
-       : PortBase (node)
-       , _currently_in_cycle (false)
-       , _nframes_this_cycle (0)
-       , _jack_client (jack_client)
-       , _jack_port (0)
-       , output_fifo (512)
-       , input_fifo (1024)
-       , xthread (true)
+Port::Port (const XMLNode& node)
+       : _centrally_parsed (true)
 {
-       assert (jack_client);
-
        Descriptor desc (node);
+
        init (desc.tag, desc.flags);
-       set_state (node);
+
+       /* derived class must call ::set_state() */
 }
 
 void
 Port::init (string const & name, Flags flags)
 {
-       if (!create_port ()) {
-               _ok = true;
-       }
+       _ok = false;  /* derived class must set to true if constructor
+                        succeeds.
+                     */
 
-       MakeConnections.connect_same_thread (connect_connection, boost::bind (&Port::make_connections, this));
-       JackHalted.connect_same_thread (halt_connection, boost::bind (&Port::jack_halted, this));
-}
+       _parser = 0;
 
+       _tagname = name;
+       _flags = flags;
 
-Port::~Port ()
-{
-       for (int i = 0; i < 16; i++) {
-               delete _channel[i];
-       }
+       _parser = new Parser (*this);
 
-       if (_jack_port) {
-               if (_jack_client) {
-                       jack_port_unregister (_jack_client, _jack_port);
-                       _jack_port = 0;
-               }
+       for (int i = 0; i < 16; i++) {
+               _channel[i] = new Channel (i, *this);
+               _channel[i]->connect_signals ();
        }
 }
 
-void
-Port::parse (framecnt_t timestamp)
+Port::~Port ()
 {
-       byte buf[512];
-
-       /* NOTE: parsing is done (if at all) by initiating a read from 
-          the port. Each port implementation calls on the parser
-          once it has data ready.
-       */
-       
-       _parser->set_timestamp (timestamp);
-
-       while (1) {
-               
-               // cerr << "+++ READ ON " << name() << endl;
-
-               int nread = read (buf, sizeof (buf));
-
-               // cerr << "-- READ (" << nread << " ON " << name() << endl;
-               
-               if (nread > 0) {
-                       if ((size_t) nread < sizeof (buf)) {
-                               break;
-                       } else {
-                               continue;
-                       }
-               } else if (nread == 0) {
-                       break;
-               } else if (errno == EAGAIN) {
-                       break;
-               } else {
-                       fatal << "Error reading from MIDI port " << name() << endmsg;
-                       /*NOTREACHED*/
-               }
+       for (int i = 0; i < 16; i++) {
+               delete _channel[i];
        }
 }
 
-void
-Port::cycle_start (pframes_t nframes)
+/** Send a clock tick message.
+ * \return true on success.
+ */
+bool
+Port::clock (timestamp_t timestamp)
 {
-       assert (_jack_port);
+       static byte clockmsg = 0xf8;
        
-       _currently_in_cycle = true;
-       _nframes_this_cycle = nframes;
-
-       assert(_nframes_this_cycle == nframes);
-
        if (sends_output()) {
-               void *buffer = jack_port_get_buffer (_jack_port, nframes);
-               jack_midi_clear_buffer (buffer);
-               flush (buffer); 
+               return midimsg (&clockmsg, 1, timestamp);
        }
        
-       if (receives_input()) {
-               void* jack_buffer = jack_port_get_buffer(_jack_port, nframes);
-               const pframes_t event_count = jack_midi_get_event_count(jack_buffer);
-
-               jack_midi_event_t ev;
-               timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
-
-               for (pframes_t i = 0; i < event_count; ++i) {
-                       jack_midi_event_get (&ev, jack_buffer, i);
-                       input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
-               }       
-               
-               if (event_count) {
-                       xthread.wakeup ();
-               }
-       }
+       return false;
 }
 
-void
-Port::cycle_end ()
+std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port )
 {
-       if (sends_output()) {
-               flush (jack_port_get_buffer (_jack_port, _nframes_this_cycle));
-       }
-
-       _currently_in_cycle = false;
-       _nframes_this_cycle = 0;
-}
-
-void
-Port::jack_halted ()
-{
-       _jack_client = 0;
-       _jack_port = 0;
-}
-
-void
-Port::drain (int check_interval_usecs)
-{
-       RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
-
-       if (is_process_thread()) {
-               error << "Process thread called MIDI::Port::drain() - this cannot work" << endmsg;
-               return;
-       }
-
-       while (1) {
-               output_fifo.get_write_vector (&vec);
-               if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
-                       break;
-               }
-               usleep (check_interval_usecs);
-       }
+       using namespace std;
+       os << "MIDI::Port { ";
+       os << "name: " << port.name();
+       os << "; ";
+       os << "ok: " << port.ok();
+       os << "; ";
+       os << " }";
+       return os;
 }
 
-int
-Port::write(byte * msg, size_t msglen, timestamp_t timestamp)
+Port::Descriptor::Descriptor (const XMLNode& node)
 {
-       int ret = 0;
-
-       if (!sends_output()) {
-               return ret;
-       }
-       
-       if (!is_process_thread()) {
-
-               Glib::Mutex::Lock lm (output_fifo_lock);
-               RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
-               
-               output_fifo.get_write_vector (&vec);
-
-               if (vec.len[0] + vec.len[1] < 1) {
-                       error << "no space in FIFO for non-process thread MIDI write" << endmsg;
-                       return 0;
-               }
-
-               if (vec.len[0]) {
-                        if (!vec.buf[0]->owns_buffer()) {
-                                vec.buf[0]->set_buffer (0, 0, true);
-                        }
-                       vec.buf[0]->set (msg, msglen, timestamp);
-               } else {
-                        if (!vec.buf[1]->owns_buffer()) {
-                                vec.buf[1]->set_buffer (0, 0, true);
-                        }
-                       vec.buf[1]->set (msg, msglen, timestamp);
-               }
-
-               output_fifo.increment_write_idx (1);
-               
-               ret = msglen;
-
-       } else {
-
-               // XXX This had to be temporarily commented out to make export work again
-               if (!(timestamp < _nframes_this_cycle)) {
-                       std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time "
-                                 << timestamp << " of " << _nframes_this_cycle
-                                 << " (this will not work - needs a code fix)"
-                                 << std::endl;
-               }
+       const XMLProperty *prop;
+       bool have_tag = false;
+       bool have_mode = false;
 
-               if (_currently_in_cycle) {
-                       if (timestamp == 0) {
-                               timestamp = _last_write_timestamp;
-                       } 
-
-                       if (jack_midi_event_write (jack_port_get_buffer (_jack_port, _nframes_this_cycle), 
-                                               timestamp, msg, msglen) == 0) {
-                               ret = msglen;
-                               _last_write_timestamp = timestamp;
-
-                       } else {
-                               ret = 0;
-                               cerr << "write of " << msglen << " failed, port holds "
-                                       << jack_midi_get_event_count (jack_port_get_buffer (_jack_port, _nframes_this_cycle))
-                                       << endl;
-                       }
-               } else {
-                       cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
-                       PBD::stacktrace (cerr, 20);
-               }
+       if ((prop = node.property ("tag")) != 0) {
+               tag = prop->value();
+               have_tag = true;
        }
 
-       if (ret > 0 && _parser) {
-               // ardour doesn't care about this and neither should your app, probably
-               // output_parser->raw_preparse (*output_parser, msg, ret);
-               for (int i = 0; i < ret; i++) {
-                       _parser->scanner (msg[i]);
-               }
-               // ardour doesn't care about this and neither should your app, probably
-               // output_parser->raw_postparse (*output_parser, msg, ret);
-       }       
-
-       return ret;
-}
-
-void
-Port::flush (void* jack_port_buffer)
-{
-       RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
-       size_t written;
-
-       output_fifo.get_read_vector (&vec);
+       if ((prop = node.property ("mode")) != 0) {
 
-       if (vec.len[0] + vec.len[1]) {
-               // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
-       }
-
-       if (vec.len[0]) {
-               Evoral::Event<double>* evp = vec.buf[0];
-               
-               for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
-                       jack_midi_event_write (jack_port_buffer,
-                                              (timestamp_t) evp->time(), evp->buffer(), evp->size());
+               if (strings_equal_ignore_case (prop->value(), "output") || strings_equal_ignore_case (prop->value(), "out")) {
+                       flags = IsOutput;
+               } else if (strings_equal_ignore_case (prop->value(), "input") || strings_equal_ignore_case (prop->value(), "in")) {
+                       flags = IsInput;
                }
-       }
-       
-       if (vec.len[1]) {
-               Evoral::Event<double>* evp = vec.buf[1];
 
-               for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
-                       jack_midi_event_write (jack_port_buffer,
-                                              (timestamp_t) evp->time(), evp->buffer(), evp->size());
-               }
-       }
-       
-       if ((written = vec.len[0] + vec.len[1]) != 0) {
-               output_fifo.increment_read_idx (written);
+               have_mode = true;
        }
-}
 
-int
-Port::read (byte *, size_t)
-{
-       if (!receives_input()) {
-               return 0;
+       if (!have_tag || !have_mode) {
+               throw failed_constructor();
        }
-       
-       timestamp_t time;
-       Evoral::EventType type;
-       uint32_t size;
-       byte buffer[input_fifo.capacity()];
-
-       while (input_fifo.read (&time, &type, &size, buffer)) {
-               _parser->set_timestamp (time);
-               for (uint32_t i = 0; i < size; ++i) {
-                       _parser->scanner (buffer[i]);
-               }
-       }
-
-       return 0;
-}
-
-int
-Port::create_port ()
-{
-       _jack_port = jack_port_register(_jack_client, _tagname.c_str(), JACK_DEFAULT_MIDI_TYPE, _flags, 0);
-       return _jack_port == 0 ? -1 : 0;
 }
 
 XMLNode& 
 Port::get_state () const
 {
-       XMLNode& root = PortBase::get_state ();
+       XMLNode* root = new XMLNode (state_node_name);
+       root->add_property ("tag", _tagname);
+
+       if (_flags == IsInput) {
+               root->add_property ("mode", "input");
+       } else {
+               root->add_property ("mode", "output");
+       }
        
 #if 0
        byte device_inquiry[6];
@@ -373,30 +165,7 @@ Port::get_state () const
        write (device_inquiry, sizeof (device_inquiry), 0);
 #endif
 
-       if (_jack_port) {
-               
-               const char** jc = jack_port_get_connections (_jack_port);
-               string connection_string;
-               if (jc) {
-                       for (int i = 0; jc[i]; ++i) {
-                               if (i > 0) {
-                                       connection_string += ',';
-                               }
-                               connection_string += jc[i];
-                       }
-                       free (jc);
-               }
-               
-               if (!connection_string.empty()) {
-                       root.add_property ("connections", connection_string);
-               }
-       } else {
-               if (!_connections.empty()) {
-                       root.add_property ("connections", _connections);
-               }
-       }
-
-       return root;
+       return *root;
 }
 
 void
@@ -407,60 +176,10 @@ Port::set_state (const XMLNode& node)
        if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) {
                return;
        }
-       
-       PortBase::set_state (node);
-
-       if ((prop = node.property ("connections")) != 0) {
-               _connections = prop->value ();
-       }
-}
-
-void
-Port::make_connections ()
-{
-       if (!_connections.empty()) {
-               vector<string> ports;
-               split (_connections, ports, ',');
-               for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
-                       if (_jack_client) {
-                               if (receives_input()) {
-                                       jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_port));
-                               } else {
-                                       jack_connect (_jack_client, jack_port_name (_jack_port), (*x).c_str());
-                               }
-                               /* ignore failures */
-                       }
-               }
-       }
-
-       connect_connection.disconnect ();
-}
-
-void
-Port::set_process_thread (pthread_t thr)
-{
-       _process_thread = thr;
 }
 
 bool
-Port::is_process_thread()
-{
-       return (pthread_self() == _process_thread);
-}
-
-void
-Port::reestablish (jack_client_t* jack)
-{
-       _jack_client = jack;
-       int const r = create_port ();
-
-       if (r) {
-               PBD::error << "could not reregister ports for " << name() << endmsg;
-       }
-}
-
-void
-Port::reconnect ()
+Port::centrally_parsed() const
 {
-       make_connections ();
+       return _centrally_parsed;
 }
diff --git a/libs/midi++2/port_base.cc b/libs/midi++2/port_base.cc
deleted file mode 100644 (file)
index 2910ace..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
-    Copyright (C) 1998 Paul Barton-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.
-
-    $Id: port.cc 11871 2012-04-10 16:27:01Z paul $
-*/
-#include <iostream>
-#include <cstdio>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <jack/jack.h>
-#include <jack/midiport.h>
-
-#include "pbd/xml++.h"
-#include "pbd/error.h"
-#include "pbd/failed_constructor.h"
-#include "pbd/convert.h"
-#include "pbd/strsplit.h"
-#include "pbd/stacktrace.h"
-
-#include "midi++/types.h"
-#include "midi++/port_base.h"
-#include "midi++/channel.h"
-
-using namespace MIDI;
-using namespace std;
-using namespace PBD;
-
-string PortBase::state_node_name = "MIDI-port";
-
-PortBase::PortBase (string const & name, Flags flags)
-       : _flags (flags)
-       , _centrally_parsed (true)
-{
-       init (name, flags);
-}
-
-PortBase::PortBase (const XMLNode& node)
-       : _centrally_parsed (true)
-{
-       Descriptor desc (node);
-
-       init (desc.tag, desc.flags);
-
-       /* derived class must call ::set_state() */
-}
-
-void
-PortBase::init (string const & name, Flags flags)
-{
-       _ok = false;  /* derived class must set to true if constructor
-                        succeeds.
-                     */
-
-       _parser = 0;
-
-       _tagname = name;
-       _flags = flags;
-
-       _parser = new Parser (*this);
-
-       for (int i = 0; i < 16; i++) {
-               _channel[i] = new Channel (i, *this);
-               _channel[i]->connect_signals ();
-       }
-}
-
-PortBase::~PortBase ()
-{
-       for (int i = 0; i < 16; i++) {
-               delete _channel[i];
-       }
-}
-
-/** Send a clock tick message.
- * \return true on success.
- */
-bool
-PortBase::clock (timestamp_t timestamp)
-{
-       static byte clockmsg = 0xf8;
-       
-       if (sends_output()) {
-               return midimsg (&clockmsg, 1, timestamp);
-       }
-       
-       return false;
-}
-
-std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::PortBase & port )
-{
-       using namespace std;
-       os << "MIDI::Port { ";
-       os << "name: " << port.name();
-       os << "; ";
-       os << "ok: " << port.ok();
-       os << "; ";
-       os << " }";
-       return os;
-}
-
-PortBase::Descriptor::Descriptor (const XMLNode& node)
-{
-       const XMLProperty *prop;
-       bool have_tag = false;
-       bool have_mode = false;
-
-       if ((prop = node.property ("tag")) != 0) {
-               tag = prop->value();
-               have_tag = true;
-       }
-
-       if ((prop = node.property ("mode")) != 0) {
-
-               if (strings_equal_ignore_case (prop->value(), "output") || strings_equal_ignore_case (prop->value(), "out")) {
-                       flags = IsOutput;
-               } else if (strings_equal_ignore_case (prop->value(), "input") || strings_equal_ignore_case (prop->value(), "in")) {
-                       flags = IsInput;
-               }
-
-               have_mode = true;
-       }
-
-       if (!have_tag || !have_mode) {
-               throw failed_constructor();
-       }
-}
-
-XMLNode& 
-PortBase::get_state () const
-{
-       XMLNode* root = new XMLNode (state_node_name);
-       root->add_property ("tag", _tagname);
-
-       if (_flags == IsInput) {
-               root->add_property ("mode", "input");
-       } else {
-               root->add_property ("mode", "output");
-       }
-       
-#if 0
-       byte device_inquiry[6];
-
-       device_inquiry[0] = 0xf0;
-       device_inquiry[0] = 0x7e;
-       device_inquiry[0] = 0x7f;
-       device_inquiry[0] = 0x06;
-       device_inquiry[0] = 0x02;
-       device_inquiry[0] = 0xf7;
-       
-       write (device_inquiry, sizeof (device_inquiry), 0);
-#endif
-
-       return *root;
-}
-
-void
-PortBase::set_state (const XMLNode& node)
-{
-       const XMLProperty* prop;
-
-       if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) {
-               return;
-       }
-}
-
-bool
-PortBase::centrally_parsed() const
-{
-       return _centrally_parsed;
-}
index eccefe695d18d9c2003aa0d2771ad93cd8cbca32..d4f71124aa5be4c319ae9cf6538f7a48aa36904d 100644 (file)
@@ -47,9 +47,10 @@ def build(bld):
     obj.source = '''
             midi.cc
             channel.cc
+            ipmidi_port.cc
+            jack_midi_port.cc
             manager.cc
             parser.cc
-            port_base.cc
             port.cc
             midnam_patch.cc
             mmc.cc
index d56e4a31a4d4654c4368a6d683d052f1c2f9d15c..e3b34a6df2638356a414748671a9a629aeaac440 100644 (file)
@@ -74,7 +74,9 @@ BaseUI::main_thread ()
 {
        set_event_loop_for_thread (this);
        thread_init ();
+       std::cerr << pthread_self() << ' ' << _name << " running event loop\n";
        _main_loop->run ();
+       std::cerr << pthread_self() << ' ' << _name << " event loop finished\n";
 }
 
 void
@@ -104,7 +106,7 @@ BaseUI::quit ()
 bool
 BaseUI::request_handler (Glib::IOCondition ioc)
 {
-       /* check the transport request pipe */
+       /* check the request pipe */
 
        if (ioc & ~IO_IN) {
                _main_loop->quit ();
index 553c8d52f3369ee3df67d4723ac53ff041b5c2b1..07a732954b0b1f6f6103c1d598a0047fbda533cd 100644 (file)
@@ -81,6 +81,7 @@ RefPtr<IOSource>
 CrossThreadChannel::ios () 
 {
        if (!_ios) {
+               std::cerr << "New x-channel fd " << fds[0] << std::endl;
                _ios = new RefPtr<IOSource> (IOSource::create (fds[0], IOCondition(IO_IN|IO_PRI|IO_ERR|IO_HUP|IO_NVAL)));
        }
        return *_ios;
index 4df56d3a7aaa105599f49dbe08bbfd2687426c53..93800aa43b942f0a0d787ce7308ac6540448b73b 100644 (file)
@@ -50,6 +50,7 @@ DeviceInfo::DeviceInfo()
        , _has_jog_wheel (true)
        , _has_touch_sense_faders (true)
        , _uses_logic_control_buttons (false)
+       , _uses_ipmidi (false)
        , _name (X_("Mackie Control Universal Pro"))
 {
        mackie_control_buttons ();
@@ -267,6 +268,12 @@ DeviceInfo::set_state (const XMLNode& node, int /* version */)
                }
        }
 
+       if ((child = node.child ("UsesIPMIDI")) != 0) {
+               if ((prop = child->property ("value")) != 0) {
+                       _uses_ipmidi = string_is_affirmative (prop->value());
+               }
+       }
+
        if ((child = node.child ("LogicControlButtons")) != 0) {
                if ((prop = child->property ("value")) != 0) {
                        _uses_logic_control_buttons = string_is_affirmative (prop->value());
@@ -369,6 +376,12 @@ DeviceInfo::has_timecode_display () const
        return _has_timecode_display;
 }
 
+bool
+DeviceInfo::uses_ipmidi () const
+{
+       return _uses_ipmidi;
+}
+
 bool
 DeviceInfo::has_global_controls () const
 {
index b08616db45debcb4fe2b48ac6944b38ad5b64f01..4071fcffc72cadfde0a9c1c1dad73dea56bda511 100644 (file)
@@ -67,6 +67,7 @@ class DeviceInfo
        bool has_global_controls() const;
        bool has_jog_wheel () const;
        bool has_touch_sense_faders() const;
+       bool uses_ipmidi() const;
        const std::string& name() const;
 
        static std::map<std::string,DeviceInfo> device_info;
@@ -86,6 +87,7 @@ class DeviceInfo
     bool     _has_jog_wheel;
     bool     _has_touch_sense_faders;
     bool     _uses_logic_control_buttons;
+    bool     _uses_ipmidi;
     std::string _name;
 
     std::map<Button::ID,GlobalButtonInfo> _global_buttons;
index 2608ec48795dd88c139fadf59d354cfce31709d1..83c4399943a43ee06fce60d0ba86ee3f8356086f 100644 (file)
@@ -114,7 +114,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session)
 
        set_device (Config->get_mackie_device_name());
        set_profile (Config->get_mackie_device_profile());
-
+       
        TrackSelectionChanged.connect (gui_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::gui_track_selection_changed, this, _1), this);
 
        _instance = this;
@@ -128,6 +128,10 @@ MackieControlProtocol::~MackieControlProtocol()
 
        _active = false;
 
+       /* stop event loop */
+
+       BaseUI::quit ();
+
        try {
                close();
        }
@@ -584,21 +588,23 @@ MackieControlProtocol::create_surfaces ()
                }
                stype = ext;
 
-               _input_bundle->add_channel (
-                       surface->port().input_port().name(),
-                       ARDOUR::DataType::MIDI,
-                       session->engine().make_port_name_non_relative (surface->port().input_port().name())
-                       );
-               
-               _output_bundle->add_channel (
-                       surface->port().output_port().name(),
-                       ARDOUR::DataType::MIDI,
-                       session->engine().make_port_name_non_relative (surface->port().output_port().name())
-                       );
+               if (!_device_info.uses_ipmidi()) {
+                       _input_bundle->add_channel (
+                               surface->port().input_port().name(),
+                               ARDOUR::DataType::MIDI,
+                               session->engine().make_port_name_non_relative (surface->port().input_port().name())
+                               );
+                       
+                       _output_bundle->add_channel (
+                               surface->port().output_port().name(),
+                               ARDOUR::DataType::MIDI,
+                               session->engine().make_port_name_non_relative (surface->port().output_port().name())
+                               );
+               }
 
                int fd;
                MIDI::Port& input_port (surface->port().input_port());
-               
+
                if ((fd = input_port.selectable ()) >= 0) {
                        Glib::RefPtr<IOSource> psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR);
 
@@ -1083,8 +1089,6 @@ MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port)
 
        if (ioc & IO_IN) {
 
-               CrossThreadChannel::drain (port->selectable());
-
                DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
                framepos_t now = session->engine().frame_time();
                port->parse (now);
index 5c6eb6d559aee9ac49197ad2bc736f339917046c..e08ba24a9ee1970d6e69d71f85a7ffed00ca453d 100644 (file)
@@ -109,8 +109,7 @@ Surface::~Surface ()
        }
        
        delete _jog_wheel;
-
-       /* don't delete the port, because we want its output to remain queued */
+       delete _port;
 }
 
 const MidiByteArray& 
index be080fa44b6eb0e2bcd0eae6aa83657abf9bf03e..645da67b431867cb36d946e1823a94702e3e793e 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "midi++/types.h"
 #include "midi++/port.h"
+#include "midi++/jack_midi_port.h"
+#include "midi++/ipmidi_port.h"
 #include "midi++/manager.h"
 
 #include "ardour/debug.h"
@@ -51,38 +53,49 @@ using namespace PBD;
 SurfacePort::SurfacePort (Surface& s)
        : _surface (&s)
 {
-       jack_client_t* jack = MackieControlProtocol::instance()->get_session().engine().jack();
-
-       _input_port = new MIDI::Port (string_compose (_("%1 in"),  _surface->name()), MIDI::Port::IsInput, jack);
-       _output_port =new MIDI::Port (string_compose (_("%1 out"), _surface->name()), MIDI::Port::IsOutput, jack);
-
-       /* MackieControl has its own thread for handling input from the input
-        * port, and we don't want anything handling output from the output
-        * port. This stops the Generic MIDI UI event loop in ardour from
-        * attempting to handle these ports.
-        */
-
-       _input_port->set_centrally_parsed (false);
-       _output_port->set_centrally_parsed (false);
-       
-       MIDI::Manager * mm = MIDI::Manager::instance();
-
-       mm->add_port (_input_port);
-       mm->add_port (_output_port);
+       if (_surface->mcp().device_info().uses_ipmidi()) {
+               _input_port = new MIDI::IPMIDIPort (MIDI::IPMIDIPort::lowest_ipmidi_port_default+_surface->number());
+               _output_port = _input_port;
+       } else {
+               jack_client_t* jack = MackieControlProtocol::instance()->get_session().engine().jack();
+               
+               _input_port = new MIDI::JackMIDIPort (string_compose (_("%1 in"),  _surface->name()), MIDI::Port::IsInput, jack);
+               _output_port =new MIDI::JackMIDIPort (string_compose (_("%1 out"), _surface->name()), MIDI::Port::IsOutput, jack);
+               
+               /* MackieControl has its own thread for handling input from the input
+                * port, and we don't want anything handling output from the output
+                * port. This stops the Generic MIDI UI event loop in ardour from
+                * attempting to handle these ports.
+                */
+               
+               _input_port->set_centrally_parsed (false);
+               _output_port->set_centrally_parsed (false);
+               
+               MIDI::Manager * mm = MIDI::Manager::instance();
+               
+               mm->add_port (_input_port);
+               mm->add_port (_output_port);
+       }
 }
 
 SurfacePort::~SurfacePort()
 {
-       MIDI::Manager* mm = MIDI::Manager::instance ();
-       
-       if (_input_port) {
-               mm->remove_port (_input_port);
+       if (_surface->mcp().device_info().uses_ipmidi()) {
                delete _input_port;
-       }
+       } else {
 
-       if (_output_port) {
-               mm->remove_port (_output_port);
-               delete _output_port;
+               MIDI::Manager* mm = MIDI::Manager::instance ();
+               
+               if (_input_port) {
+                       mm->remove_port (_input_port);
+                       delete _input_port;
+               }
+               
+               if (_output_port) {
+                       _output_port->drain (10000);
+                       mm->remove_port (_output_port);
+                       delete _output_port;
+               }
        }
 }
 
index 1a65d8dbcf86c82e8c5d4baab6f41340989af9d5..70b1df4580d8153472f789be9d4c863211b4660c 100644 (file)
@@ -59,8 +59,8 @@ protected:
 
 private:
        Mackie::Surface* _surface;
-       MIDI::Port*      _input_port;
-       MIDI::Port*      _output_port;
+       MIDI::Port*  _input_port;
+       MIDI::Port*  _output_port;
 };     
 
 std::ostream& operator <<  (std::ostream& , const SurfacePort& port);
index ce9a546d6b1a4853e40fcc04e593d020163562e0..1456d4e8a776540591f16348fddb459e1d7656ea 100644 (file)
@@ -11,4 +11,5 @@
   <JogWheel value="yes"/>
   <TouchSenseFaders value="yes"/>
   <LogicControlButtons value="yes"/>
+  <UsesIPMIDI value="yes"/>
 </MackieProtocolDevice>