summaryrefslogtreecommitdiff
path: root/libs
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 /libs
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
Diffstat (limited to 'libs')
-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
28 files changed, 1234 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);