summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2012-04-24 02:28:51 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2012-04-24 02:28:51 +0000
commit33140f32671576a285d62f529447f941f96313fc (patch)
tree44faee5b21e3181077d0c22180895fb072432a53
parent10d37fecc1b54487a5fb0f3652bfb45a5224ef8b (diff)
add support for IP MIDI (multicast MIDI over IP UDP sockets) to ardour and use it if requested inside MCP code. required renaming the pre-existing MIDI::Port as MIDI:JackMIDIPort - MIDI::Port becomes the base type for both JackMIDIPort and IPMIDIPort
git-svn-id: svn://localhost/ardour2/branches/3.0@12069 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--libs/ardour/audioengine.cc9
-rw-r--r--libs/ardour/midi_ui.cc2
-rw-r--r--libs/ardour/session.cc3
-rw-r--r--libs/ardour/ticker.cc9
-rw-r--r--libs/midi++2/channel.cc2
-rw-r--r--libs/midi++2/ipmidi_port.cc290
-rw-r--r--libs/midi++2/jack_midi_port.cc466
-rw-r--r--libs/midi++2/manager.cc23
-rw-r--r--libs/midi++2/midi++/channel.h8
-rw-r--r--libs/midi++2/midi++/ipmidi_port.h76
-rw-r--r--libs/midi++2/midi++/jack_midi_port.h103
-rw-r--r--libs/midi++2/midi++/manager.h12
-rw-r--r--libs/midi++2/midi++/parser.h8
-rw-r--r--libs/midi++2/midi++/port.h145
-rw-r--r--libs/midi++2/midi++/port_base.h158
-rw-r--r--libs/midi++2/mmc.cc5
-rw-r--r--libs/midi++2/parser.cc4
-rw-r--r--libs/midi++2/port.cc415
-rw-r--r--libs/midi++2/port_base.cc185
-rw-r--r--libs/midi++2/wscript3
-rw-r--r--libs/pbd/base_ui.cc4
-rw-r--r--libs/pbd/crossthread.cc1
-rw-r--r--libs/surfaces/mackie/device_info.cc13
-rw-r--r--libs/surfaces/mackie/device_info.h2
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc34
-rw-r--r--libs/surfaces/mackie/surface.cc3
-rw-r--r--libs/surfaces/mackie/surface_port.cc65
-rw-r--r--libs/surfaces/mackie/surface_port.h4
-rw-r--r--mcp/nucleus.device1
29 files changed, 1235 insertions, 818 deletions
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 5d29f35301..372581f4e6 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -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;
diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc
index 770a371457..302dce86ba 100644
--- a/libs/ardour/midi_ui.cc
+++ b/libs/ardour/midi_ui.cc
@@ -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());
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 5744b55c65..80dbd98963 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -106,6 +106,7 @@
#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 */
diff --git a/libs/ardour/ticker.cc b/libs/ardour/ticker.cc
index 734b401356..5d078952a1 100644
--- a/libs/ardour/ticker.cc
+++ b/libs/ardour/ticker.cc
@@ -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;
}
diff --git a/libs/midi++2/channel.cc b/libs/midi++2/channel.cc
index 0c9fbe39d1..66ce5ed71c 100644
--- a/libs/midi++2/channel.cc
+++ b/libs/midi++2/channel.cc
@@ -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
index 0000000000..0ef0a9952a
--- /dev/null
+++ b/libs/midi++2/ipmidi_port.cc
@@ -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
index 0000000000..b00aff8d8d
--- /dev/null
+++ b/libs/midi++2/jack_midi_port.cc
@@ -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 ();
+}
diff --git a/libs/midi++2/manager.cc b/libs/midi++2/manager.cc
index 61d4c4c363..822c74e125 100644
--- a/libs/midi++2/manager.cc
+++ b/libs/midi++2/manager.cc
@@ -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 ();
+ }
}
}
diff --git a/libs/midi++2/midi++/channel.h b/libs/midi++2/midi++/channel.h
index 370a569156..d00ce700c5 100644
--- a/libs/midi++2/midi++/channel.h
+++ b/libs/midi++2/midi++/channel.h
@@ -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
index 0000000000..bf949bd601
--- /dev/null
+++ b/libs/midi++2/midi++/ipmidi_port.h
@@ -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
index 0000000000..e381120a99
--- /dev/null
+++ b/libs/midi++2/midi++/jack_midi_port.h
@@ -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__
diff --git a/libs/midi++2/midi++/manager.h b/libs/midi++2/midi++/manager.h
index bab4a18dd2..c37ba392b4 100644
--- a/libs/midi++2/midi++/manager.h
+++ b/libs/midi++2/midi++/manager.h
@@ -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;
};
diff --git a/libs/midi++2/midi++/parser.h b/libs/midi++2/midi++/parser.h
index ac452798cc..f5f343e952 100644
--- a/libs/midi++2/midi++/parser.h
+++ b/libs/midi++2/midi++/parser.h
@@ -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;
diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h
index e7b532013e..a2315f7284 100644
--- a/libs/midi++2/midi++/port.h
+++ b/libs/midi++2/midi++/port.h
@@ -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>
@@ -34,70 +34,127 @@
#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
index f1649d84ba..0000000000
--- a/libs/midi++2/midi++/port_base.h
+++ /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__
diff --git a/libs/midi++2/mmc.cc b/libs/midi++2/mmc.cc
index e02598def2..06eadb5b34 100644
--- a/libs/midi++2/mmc.cc
+++ b/libs/midi++2/mmc.cc
@@ -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));
diff --git a/libs/midi++2/parser.cc b/libs/midi++2/parser.cc
index a56e3f82e4..8e3af64504 100644
--- a/libs/midi++2/parser.cc
+++ b/libs/midi++2/parser.cc
@@ -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;
diff --git a/libs/midi++2/port.cc b/libs/midi++2/port.cc
index ef1704857d..3e7896631a 100644
--- a/libs/midi++2/port.cc
+++ b/libs/midi++2/port.cc
@@ -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
index 2910ace2a9..0000000000
--- a/libs/midi++2/port_base.cc
+++ /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;
-}
diff --git a/libs/midi++2/wscript b/libs/midi++2/wscript
index eccefe695d..d4f71124aa 100644
--- a/libs/midi++2/wscript
+++ b/libs/midi++2/wscript
@@ -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
diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc
index d56e4a31a4..e3b34a6df2 100644
--- a/libs/pbd/base_ui.cc
+++ b/libs/pbd/base_ui.cc
@@ -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 ();
diff --git a/libs/pbd/crossthread.cc b/libs/pbd/crossthread.cc
index 553c8d52f3..07a732954b 100644
--- a/libs/pbd/crossthread.cc
+++ b/libs/pbd/crossthread.cc
@@ -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;
diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc
index 4df56d3a7a..93800aa43b 100644
--- a/libs/surfaces/mackie/device_info.cc
+++ b/libs/surfaces/mackie/device_info.cc
@@ -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());
@@ -370,6 +377,12 @@ DeviceInfo::has_timecode_display () const
}
bool
+DeviceInfo::uses_ipmidi () const
+{
+ return _uses_ipmidi;
+}
+
+bool
DeviceInfo::has_global_controls () const
{
return _has_global_controls;
diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h
index b08616db45..4071fcffc7 100644
--- a/libs/surfaces/mackie/device_info.h
+++ b/libs/surfaces/mackie/device_info.h
@@ -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;
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
index 2608ec4879..83c4399943 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -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);
diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc
index 5c6eb6d559..e08ba24a9e 100644
--- a/libs/surfaces/mackie/surface.cc
+++ b/libs/surfaces/mackie/surface.cc
@@ -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&
diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc
index be080fa44b..645da67b43 100644
--- a/libs/surfaces/mackie/surface_port.cc
+++ b/libs/surfaces/mackie/surface_port.cc
@@ -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;
+ }
}
}
diff --git a/libs/surfaces/mackie/surface_port.h b/libs/surfaces/mackie/surface_port.h
index 1a65d8dbcf..70b1df4580 100644
--- a/libs/surfaces/mackie/surface_port.h
+++ b/libs/surfaces/mackie/surface_port.h
@@ -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);
diff --git a/mcp/nucleus.device b/mcp/nucleus.device
index ce9a546d6b..1456d4e8a7 100644
--- a/mcp/nucleus.device
+++ b/mcp/nucleus.device
@@ -11,4 +11,5 @@
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<LogicControlButtons value="yes"/>
+ <UsesIPMIDI value="yes"/>
</MackieProtocolDevice>