Remove non-JACK midi++ ports.

git-svn-id: svn://localhost/ardour2/branches/3.0@7377 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2010-07-06 00:16:36 +00:00
parent dc1e5d09a2
commit 91850f0eb4
28 changed files with 507 additions and 2174 deletions

View file

@ -8,7 +8,6 @@
#include "pbd/cpus.h" #include "pbd/cpus.h"
#include "midi++/manager.h" #include "midi++/manager.h"
#include "midi++/factory.h"
#include "ardour/audioengine.h" #include "ardour/audioengine.h"
#include "ardour/dB.h" #include "ardour/dB.h"
@ -209,7 +208,6 @@ private:
node.add_property ("tag", dialog.port_name.get_text()); node.add_property ("tag", dialog.port_name.get_text());
node.add_property ("device", X_("ardour")); // XXX this can't be right for all types node.add_property ("device", X_("ardour")); // XXX this can't be right for all types
node.add_property ("type", MIDI::PortFactory::default_port_type());
node.add_property ("mode", smod); node.add_property ("mode", smod);
if (MIDI::Manager::instance()->add_port (node) != 0) { if (MIDI::Manager::instance()->add_port (node) != 0) {

View file

@ -19,7 +19,6 @@
*/ */
#include "midi++/jack.h"
#include "pbd/signals.h" #include "pbd/signals.h"
#include "ardour/types.h" #include "ardour/types.h"
@ -29,6 +28,10 @@
#ifndef TICKER_H_ #ifndef TICKER_H_
#define TICKER_H_ #define TICKER_H_
namespace MIDI {
class Port;
}
namespace ARDOUR namespace ARDOUR
{ {

View file

@ -32,7 +32,7 @@
#include "pbd/stacktrace.h" #include "pbd/stacktrace.h"
#include "pbd/unknown_type.h" #include "pbd/unknown_type.h"
#include "midi++/jack.h" #include "midi++/port.h"
#include "midi++/mmc.h" #include "midi++/mmc.h"
#include "midi++/manager.h" #include "midi++/manager.h"
@ -147,7 +147,7 @@ _thread_init_callback (void * /*arg*/)
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512); SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
MIDI::JACK_MidiPort::set_process_thread (pthread_self()); MIDI::Port::set_process_thread (pthread_self());
MIDI::MachineControl::set_sending_thread (pthread_self ()); MIDI::MachineControl::set_sending_thread (pthread_self ());
} }
@ -263,7 +263,7 @@ AudioEngine::stop (bool forever)
} else { } else {
jack_deactivate (_priv_jack); jack_deactivate (_priv_jack);
Stopped(); /* EMIT SIGNAL */ Stopped(); /* EMIT SIGNAL */
MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */ MIDI::Port::JackHalted (); /* EMIT SIGNAL */
} }
} }
@ -1074,7 +1074,7 @@ AudioEngine::halted (void *arg)
if (was_running) { if (was_running) {
ae->Halted(""); /* EMIT SIGNAL */ ae->Halted(""); /* EMIT SIGNAL */
MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */ MIDI::Port::JackHalted (); /* EMIT SIGNAL */
} }
} }
@ -1358,7 +1358,7 @@ AudioEngine::disconnect_from_jack ()
if (_running) { if (_running) {
_running = false; _running = false;
Stopped(); /* EMIT SIGNAL */ Stopped(); /* EMIT SIGNAL */
MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */ MIDI::Port::JackHalted (); /* EMIT SIGNAL */
} }
return 0; return 0;

View file

@ -28,7 +28,6 @@
#include "pbd/pthread_utils.h" #include "pbd/pthread_utils.h"
#include "midi++/port.h" #include "midi++/port.h"
#include "midi++/jack.h"
#include "ardour/debug.h" #include "ardour/debug.h"
#include "ardour/slave.h" #include "ardour/slave.h"

View file

@ -104,9 +104,7 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
if (ioc & IO_IN) { if (ioc & IO_IN) {
if (port->must_drain_selectable()) {
CrossThreadChannel::drain (port->selectable()); CrossThreadChannel::drain (port->selectable());
}
DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name())); DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
nframes64_t now = _session.engine().frame_time(); nframes64_t now = _session.engine().frame_time();

View file

@ -98,7 +98,7 @@
#include "ardour/utils.h" #include "ardour/utils.h"
#include "ardour/graph.h" #include "ardour/graph.h"
#include "midi++/jack.h" #include "midi++/port.h"
#include "midi++/mmc.h" #include "midi++/mmc.h"
#include "i18n.h" #include "i18n.h"
@ -645,7 +645,7 @@ Session::hookup_io ()
/* Tell all IO objects to connect themselves together */ /* Tell all IO objects to connect themselves together */
IO::enable_connecting (); IO::enable_connecting ();
MIDI::JACK_MidiPort::MakeConnections (); MIDI::Port::MakeConnections ();
/* Now reset all panners */ /* Now reset all panners */

View file

@ -18,6 +18,8 @@
$Id$ $Id$
*/ */
#include "midi++/port.h"
#include "evoral/midi_events.h"
#include "ardour/ticker.h" #include "ardour/ticker.h"
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/tempo.h" #include "ardour/tempo.h"
@ -138,9 +140,6 @@ void MidiClockTicker::tick(const nframes_t& transport_frames, const BBT_Time& /*
if (!Config->get_send_midi_clock() || _session == 0 || _session->transport_speed() != 1.0f || _midi_port == 0) if (!Config->get_send_midi_clock() || _session == 0 || _session->transport_speed() != 1.0f || _midi_port == 0)
return; return;
MIDI::JACK_MidiPort* jack_port = dynamic_cast<MIDI::JACK_MidiPort*>(_midi_port);
assert(jack_port);
while (true) { while (true) {
double next_tick = _last_tick + one_ppqn_in_frames(transport_frames); double next_tick = _last_tick + one_ppqn_in_frames(transport_frames);
nframes_t next_tick_offset = nframes_t(next_tick) - transport_frames; nframes_t next_tick_offset = nframes_t(next_tick) - transport_frames;
@ -150,11 +149,11 @@ void MidiClockTicker::tick(const nframes_t& transport_frames, const BBT_Time& /*
<< ":Last tick time:" << _last_tick << ":" << ":Last tick time:" << _last_tick << ":"
<< ":Next tick time:" << next_tick << ":" << ":Next tick time:" << next_tick << ":"
<< "Offset:" << next_tick_offset << ":" << "Offset:" << next_tick_offset << ":"
<< "cycle length:" << jack_port->nframes_this_cycle() << "cycle length:" << _midi_port->nframes_this_cycle()
<< endl; << endl;
#endif #endif
if (next_tick_offset >= jack_port->nframes_this_cycle()) if (next_tick_offset >= _midi_port->nframes_this_cycle())
return; return;
send_midi_clock_event(next_tick_offset); send_midi_clock_event(next_tick_offset);
@ -183,9 +182,7 @@ void MidiClockTicker::send_midi_clock_event(nframes_t offset)
return; return;
} }
#ifdef WITH_JACK_MIDI assert (MIDI::Port::is_process_thread());
assert (MIDI::JACK_MidiPort::is_process_thread());
#endif // WITH_JACK_MIDI
#ifdef DEBUG_MIDI_CLOCK #ifdef DEBUG_MIDI_CLOCK
cerr << "Tick with offset " << offset << endl; cerr << "Tick with offset " << offset << endl;
#endif // DEBUG_MIDI_CLOCK #endif // DEBUG_MIDI_CLOCK

View file

@ -1,429 +0,0 @@
/*
Copyright (C) 2004 Paul 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 <fcntl.h>
#include <cerrno>
#include "pbd/failed_constructor.h"
#include "pbd/error.h"
#include "pbd/xml++.h"
#include "midi++/types.h"
#include "midi++/alsa_sequencer.h"
#include "i18n.h"
//#define DOTRACE 1
#ifdef DOTRACE
#define TR_FN() (cerr << __FUNCTION__ << endl)
#define TR_VAL(v) (cerr << __FILE__ " " << __LINE__ << " " #v "=" << v << endl)
#else
#define TR_FN()
#define TR_VAL(v)
#endif
using namespace std;
using namespace MIDI;
using namespace PBD;
snd_seq_t* ALSA_SequencerMidiPort::seq = 0;
ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (const XMLNode& node)
: Port (node)
, decoder (0)
, encoder (0)
, port_id (-1)
{
TR_FN();
int err;
Descriptor desc (node);
if (!seq && init_client (desc.device) < 0) {
_ok = false;
} else {
if (0 <= (err = create_ports (desc)) &&
0 <= (err = snd_midi_event_new (1024, &decoder)) && // Length taken from ARDOUR::Session::midi_read ()
0 <= (err = snd_midi_event_new (64, &encoder))) { // Length taken from ARDOUR::Session::mmc_buffer
snd_midi_event_init (decoder);
snd_midi_event_init (encoder);
_ok = true;
}
}
set_state (node);
}
ALSA_SequencerMidiPort::~ALSA_SequencerMidiPort ()
{
if (decoder) {
snd_midi_event_free (decoder);
}
if (encoder) {
snd_midi_event_free (encoder);
}
if (port_id >= 0) {
snd_seq_delete_port (seq, port_id);
}
}
int
ALSA_SequencerMidiPort::selectable () const
{
struct pollfd pfd[1];
if (0 <= snd_seq_poll_descriptors (seq, pfd, 1, POLLIN | POLLOUT)) {
return pfd[0].fd;
}
return -1;
}
int
ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t ignored)
{
TR_FN ();
int R;
int totwritten = 0;
snd_midi_event_reset_encode (encoder);
int nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
TR_VAL (nwritten);
while (0 < nwritten) {
if (0 <= (R = snd_seq_event_output (seq, &SEv)) &&
0 <= (R = snd_seq_drain_output (seq))) {
bytes_written += nwritten;
totwritten += nwritten;
if (output_parser) {
output_parser->raw_preparse (*output_parser, msg, nwritten);
for (int i = 0; i < nwritten; i++) {
output_parser->scanner (msg[i]);
}
output_parser->raw_postparse (*output_parser, msg, nwritten);
}
} else {
TR_VAL(R);
return R;
}
msglen -= nwritten;
msg += nwritten;
if (msglen > 0) {
nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
TR_VAL(nwritten);
}
else {
break;
}
}
return totwritten;
}
int
ALSA_SequencerMidiPort::read (byte *buf, size_t max)
{
TR_FN();
int err;
snd_seq_event_t *ev;
if (0 <= (err = snd_seq_event_input (seq, &ev))) {
TR_VAL(err);
err = snd_midi_event_decode (decoder, buf, max, ev);
}
if (err > 0) {
bytes_read += err;
if (input_parser) {
input_parser->raw_preparse (*input_parser, buf, err);
for (int i = 0; i < err; i++) {
input_parser->scanner (buf[i]);
}
input_parser->raw_postparse (*input_parser, buf, err);
}
}
return -ENOENT == err ? 0 : err;
}
int
ALSA_SequencerMidiPort::create_ports (const Port::Descriptor& desc)
{
int err;
unsigned int caps = 0;
if (desc.mode == O_WRONLY || desc.mode == O_RDWR)
caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
if (desc.mode == O_RDONLY || desc.mode == O_RDWR)
caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
if (0 <= (err = snd_seq_create_simple_port (seq, desc.tag.c_str(), caps,
(SND_SEQ_PORT_TYPE_MIDI_GENERIC|
SND_SEQ_PORT_TYPE_SOFTWARE|
SND_SEQ_PORT_TYPE_APPLICATION)))) {
port_id = err;
snd_seq_ev_clear (&SEv);
snd_seq_ev_set_source (&SEv, port_id);
snd_seq_ev_set_subs (&SEv);
snd_seq_ev_set_direct (&SEv);
return 0;
}
return err;
}
int
ALSA_SequencerMidiPort::init_client (std::string name)
{
static bool called = false;
if (called) {
return -1;
}
called = true;
if (snd_seq_open (&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) >= 0) {
snd_seq_set_client_name (seq, name.c_str());
return 0;
} else {
warning << "The ALSA MIDI system is not available. No ports based on it will be created"
<< endmsg;
return -1;
}
}
int
ALSA_SequencerMidiPort::discover (vector<PortSet>& ports)
{
int n = 0;
snd_seq_client_info_t *client_info;
snd_seq_port_info_t *port_info;
snd_seq_client_info_alloca (&client_info);
snd_seq_port_info_alloca (&port_info);
snd_seq_client_info_set_client (client_info, -1);
while (snd_seq_query_next_client(seq, client_info) >= 0) {
int alsa_client;
if ((alsa_client = snd_seq_client_info_get_client(client_info)) <= 0) {
break;
}
snd_seq_port_info_set_client(port_info, alsa_client);
snd_seq_port_info_set_port(port_info, -1);
char client[256];
snprintf (client, sizeof (client), "%d:%s", alsa_client, snd_seq_client_info_get_name(client_info));
ports.push_back (PortSet (client));
while (snd_seq_query_next_port(seq, port_info) >= 0) {
#if 0
int type = snd_seq_port_info_get_type(pinfo);
if (!(type & SND_SEQ_PORT_TYPE_PORT)) {
continue;
}
#endif
unsigned int port_capability = snd_seq_port_info_get_capability(port_info);
if ((port_capability & SND_SEQ_PORT_CAP_NO_EXPORT) == 0) {
int alsa_port = snd_seq_port_info_get_port(port_info);
char port[256];
snprintf (port, sizeof (port), "%d:%s", alsa_port, snd_seq_port_info_get_name(port_info));
std::string mode;
if (port_capability & SND_SEQ_PORT_CAP_READ) {
if (port_capability & SND_SEQ_PORT_CAP_WRITE) {
mode = "duplex";
} else {
mode = "output";
}
} else if (port_capability & SND_SEQ_PORT_CAP_WRITE) {
if (port_capability & SND_SEQ_PORT_CAP_READ) {
mode = "duplex";
} else {
mode = "input";
}
}
XMLNode node (X_("MIDI-port"));
node.add_property ("device", client);
node.add_property ("tag", port);
node.add_property ("mode", mode);
node.add_property ("type", "alsa/sequencer");
ports.back().ports.push_back (node);
++n;
}
}
}
return n;
}
void
ALSA_SequencerMidiPort::get_connections (vector<SequencerPortAddress>& connections, int dir) const
{
snd_seq_query_subscribe_t *subs;
snd_seq_addr_t seq_addr;
snd_seq_query_subscribe_alloca (&subs);
// Get port connections...
if (dir) {
snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_WRITE);
} else {
snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_READ);
}
snd_seq_query_subscribe_set_index(subs, 0);
seq_addr.client = snd_seq_client_id (seq);
seq_addr.port = port_id;
snd_seq_query_subscribe_set_root(subs, &seq_addr);
while (snd_seq_query_port_subscribers(seq, subs) >= 0) {
if (snd_seq_query_subscribe_get_time_real (subs)) {
/* interesting connection */
seq_addr = *snd_seq_query_subscribe_get_addr (subs);
connections.push_back (SequencerPortAddress (seq_addr.client,
seq_addr.port));
}
snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1);
}
}
XMLNode&
ALSA_SequencerMidiPort::get_state () const
{
XMLNode& root (Port::get_state ());
vector<SequencerPortAddress> connections;
XMLNode* sub = 0;
char buf[256];
get_connections (connections, 1);
if (!connections.empty()) {
if (!sub) {
sub = new XMLNode (X_("connections"));
}
for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) {
XMLNode* cnode = new XMLNode (X_("read"));
snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second);
cnode->add_property ("dest", buf);
sub->add_child_nocopy (*cnode);
}
}
connections.clear ();
get_connections (connections, 0);
if (!connections.empty()) {
if (!sub) {
sub = new XMLNode (X_("connections"));
}
for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) {
XMLNode* cnode = new XMLNode (X_("write"));
snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second);
cnode->add_property ("dest", buf);
sub->add_child_nocopy (*cnode);
}
}
if (sub) {
root.add_child_nocopy (*sub);
}
return root;
}
void
ALSA_SequencerMidiPort::set_state (const XMLNode& node)
{
Port::set_state (node);
XMLNodeList children (node.children());
XMLNodeIterator iter;
for (iter = children.begin(); iter != children.end(); ++iter) {
if ((*iter)->name() == X_("connections")) {
XMLNodeList gchildren ((*iter)->children());
XMLNodeIterator gciter;
for (gciter = gchildren.begin(); gciter != gchildren.end(); ++gciter) {
XMLProperty* prop;
if ((prop = (*gciter)->property ("dest")) != 0) {
int client;
int port;
if (sscanf (prop->value().c_str(), "%d:%d", &client, &port) == 2) {
snd_seq_port_subscribe_t *sub;
snd_seq_addr_t seq_addr;
snd_seq_port_subscribe_alloca(&sub);
if ((*gciter)->name() == X_("write")) {
seq_addr.client = snd_seq_client_id (seq);
seq_addr.port = port_id;
snd_seq_port_subscribe_set_sender(sub, &seq_addr);
seq_addr.client = client;
seq_addr.port = port;
snd_seq_port_subscribe_set_dest(sub, &seq_addr);
} else {
seq_addr.client = snd_seq_client_id (seq);
seq_addr.port = port_id;
snd_seq_port_subscribe_set_dest(sub, &seq_addr);
seq_addr.client = client;
seq_addr.port = port;
snd_seq_port_subscribe_set_sender(sub, &seq_addr);
}
snd_seq_subscribe_port (seq, sub);
}
}
}
break;
}
}
}

View file

@ -1,157 +0,0 @@
/*
Copyright (C) 2004 Paul Davis
Copyright (C) 2004 Grame
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.
*/
#include <fcntl.h>
#include <cerrno>
#include "midi++/coremidi_midiport.h"
#include "midi++/types.h"
#include <mach/mach_time.h>
#include "pbd/pthread_utils.h"
using namespace std;
using namespace MIDI;
MIDITimeStamp CoreMidi_MidiPort::MIDIGetCurrentHostTime()
{
return mach_absolute_time();
}
CoreMidi_MidiPort::CoreMidi_MidiPort (const XMLNode& node) : Port (node)
{
Descriptor desc (node);
firstrecv = true;
int err;
if (0 == (err = Open(desc))) {
_ok = true;
}
}
CoreMidi_MidiPort::~CoreMidi_MidiPort () {Close();}
void CoreMidi_MidiPort::Close ()
{
if (midi_destination) MIDIEndpointDispose(midi_destination);
if (midi_source) MIDIEndpointDispose(midi_source);
if (midi_client) MIDIClientDispose(midi_client);
}
int CoreMidi_MidiPort::write (byte *msg, size_t msglen, timestamp_t ignored)
{
OSStatus err;
MIDIPacketList* pktlist = (MIDIPacketList*)midi_buffer;
MIDIPacket* packet = MIDIPacketListInit(pktlist);
packet = MIDIPacketListAdd(pktlist,sizeof(midi_buffer),packet,MIDIGetCurrentHostTime(),msglen,msg);
if (packet) {
err = MIDIReceived(midi_source,pktlist);
if (err != noErr) {
//error << "MIDIReceived error" << err << endmsg.
}
bytes_written += msglen;
return msglen;
}else{
return 0;
}
}
int CoreMidi_MidiPort::Open (const Descriptor& desc)
{
OSStatus err;
CFStringRef coutputStr;
string str;
coutputStr = CFStringCreateWithCString(0, desc.device.c_str(), CFStringGetSystemEncoding());
err = MIDIClientCreate(coutputStr, 0, 0, &midi_client);
CFRelease(coutputStr);
if (!midi_client) {
//error << "Cannot open CoreMidi client : " << err << endmsg.
goto error;
}
str = desc.tag + string("_in");
coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding());
err = MIDIDestinationCreate(midi_client, coutputStr, read_proc, this, &midi_destination);
CFRelease(coutputStr);
if (!midi_destination) {
//error << "Cannot create CoreMidi destination : " << err << endmsg.
goto error;
}
str = desc.tag + string("_out");
coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding());
err = MIDISourceCreate(midi_client, coutputStr, &midi_source);
CFRelease(coutputStr);
if (!midi_source) {
//error << "Cannot create CoreMidi source : " << err << endmsg.
goto error;
}
return err;
error:
Close();
return err;
}
void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
{
CoreMidi_MidiPort* driver = (CoreMidi_MidiPort*)refCon;
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
if (driver->firstrecv) {
driver->firstrecv = false;
PBD::notify_gui_about_thread_creation ("gui", pthread_self(), "COREMIDI", 256);
}
for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
driver->bytes_read += packet->length;
if (driver->input_parser) {
//driver->input_parser->raw_preparse (*driver->input_parser, packet->data, packet->length);
/* XXX This is technically the wrong timebase, since it is based on
host time.
*/
driver->input_parser->set_timestamp (packet->timestamp);
for (int i = 0; i < packet->length; i++) {
driver->input_parser->scanner (packet->data[i]);
}
//driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
}
packet = MIDIPacketNext(packet);
}
}
int
CoreMidi_MidiPort::discover (vector<PortSet>& ports)
{
/* XXX do dynamic port discovery here */
return 0;
}

View file

@ -25,78 +25,13 @@
#include "midi++/types.h" #include "midi++/types.h"
#include "midi++/factory.h" #include "midi++/factory.h"
#include "midi++/fifomidi.h"
#ifdef WITH_JACK_MIDI
#include "midi++/jack.h" #include "midi++/jack.h"
std::string MIDI::JACK_MidiPort::typestring = "jack";
#endif // WITH_JACK_MIDI
std::string MIDI::FIFO_MidiPort::typestring = "fifo";
#ifdef WITH_ALSA
#include "midi++/alsa_sequencer.h"
#include "midi++/alsa_rawmidi.h"
std::string MIDI::ALSA_SequencerMidiPort::typestring = "alsa/sequencer";
std::string MIDI::ALSA_RawMidiPort::typestring = "alsa/raw";
#endif // WITH_ALSA
#ifdef WITH_COREMIDI
#include "midi++/coremidi_midiport.h"
std::string MIDI::CoreMidi_MidiPort::typestring = "coremidi";
#endif // WITH_COREMIDI
using namespace std; using namespace std;
using namespace MIDI; using namespace MIDI;
using namespace PBD; using namespace PBD;
// FIXME: void* data pointer, filthy
Port *
PortFactory::create_port (const XMLNode& node, void* data)
{
Port::Descriptor desc (node);
Port *port;
switch (desc.type) {
#ifdef WITH_JACK_MIDI
case Port::JACK_Midi:
assert(data != NULL);
port = new JACK_MidiPort (node, (jack_client_t*) data);
break;
#endif // WITH_JACK_MIDI
#ifdef WITH_ALSA
case Port::ALSA_RawMidi:
port = new ALSA_RawMidiPort (node);
break;
case Port::ALSA_Sequencer:
port = new ALSA_SequencerMidiPort (node);
break;
#endif // WITH_ALSA
#ifdef WITH_COREMIDI
case Port::CoreMidi_MidiPort:
port = new CoreMidi_MidiPort (node);
break;
#endif // WITH_COREMIDI
case Port::FIFO:
port = new FIFO_MidiPort (node);
break;
default:
return 0;
}
return port;
}
bool bool
PortFactory::ignore_duplicate_devices (Port::Type type) PortFactory::ignore_duplicate_devices (Port::Type type)
@ -104,23 +39,10 @@ PortFactory::ignore_duplicate_devices (Port::Type type)
bool ret = false; bool ret = false;
switch (type) { switch (type) {
#ifdef WITH_ALSA
case Port::ALSA_Sequencer:
ret = true;
break;
#endif // WITH_ALSA
#ifdef WITH_JACK_MIDI
case Port::JACK_Midi: case Port::JACK_Midi:
ret = true; ret = true;
break; break;
#endif // WITH_JACK_MIDI
#ifdef WITH_COREMIDI
case Port::CoreMidi_MidiPort:
ret = true;
break;
#endif // WITH_COREMIDI
default: default:
break; break;
@ -129,65 +51,17 @@ PortFactory::ignore_duplicate_devices (Port::Type type)
return ret; return ret;
} }
int
#if defined (WITH_ALSA) || defined (WITH_COREMIDI)
PortFactory::get_known_ports (vector<PortSet>& ports)
#else
PortFactory::get_known_ports (vector<PortSet>&)
#endif
{
int n = 0;
#ifdef WITH_ALSA
n += ALSA_SequencerMidiPort::discover (ports);
#endif // WITH_ALSA
#ifdef WITH_COREMIDI
n += CoreMidi_MidiPort::discover (ports);
#endif // WITH_COREMIDI
return n;
}
std::string std::string
PortFactory::default_port_type () PortFactory::default_port_type ()
{ {
#ifdef WITH_JACK_MIDI
return "jack"; return "jack";
#endif
#ifdef WITH_ALSA
return "alsa/sequencer";
#endif
#ifdef WITH_COREMIDI
return "coremidi";
#endif // WITH_COREMIDI
PBD::fatal << "programming error: no default port type defined in midifactory.cc" << endmsg;
/*NOTREACHED*/
return "";
} }
Port::Type Port::Type
PortFactory::string_to_type (const string& xtype) PortFactory::string_to_type (const string& xtype)
{ {
if (0){ if (strings_equal_ignore_case (xtype, JACK_MidiPort::typestring)) {
#ifdef WITH_ALSA
} else if (strings_equal_ignore_case (xtype, ALSA_RawMidiPort::typestring)) {
return Port::ALSA_RawMidi;
} else if (strings_equal_ignore_case (xtype, ALSA_SequencerMidiPort::typestring)) {
return Port::ALSA_Sequencer;
#endif
#ifdef WITH_COREMIDI
} else if (strings_equal_ignore_case (xtype, CoreMidi_MidiPort::typestring)) {
return Port::CoreMidi_MidiPort;
#endif
} else if (strings_equal_ignore_case (xtype, FIFO_MidiPort::typestring)) {
return Port::FIFO;
#ifdef WITH_JACK_MIDI
} else if (strings_equal_ignore_case (xtype, JACK_MidiPort::typestring)) {
return Port::JACK_Midi; return Port::JACK_Midi;
#endif
} }
return Port::Unknown; return Port::Unknown;

View file

@ -1,181 +0,0 @@
/*
Copyright (C) 1999 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 <fcntl.h>
#include <cerrno>
#include <cstring>
#include "pbd/error.h"
#include "pbd/pathscanner.h"
#include "midi++/types.h"
#include "midi++/fd_midiport.h"
using namespace std;
using namespace MIDI;
using namespace PBD;
string *FD_MidiPort::midi_dirpath = 0;
string *FD_MidiPort::midi_filename_pattern = 0;
FD_MidiPort::FD_MidiPort (const XMLNode& node,
const string &dirpath,
const string &pattern)
: Port (node)
{
Descriptor desc (node);
open (desc);
if (_fd < 0) {
switch (errno) {
case EBUSY:
error << "MIDI: port device in use" << endmsg;
break;
case ENOENT:
error << "MIDI: no such port device" << endmsg;
break;
case EACCES:
error << "MIDI: access to port denied" << endmsg;
break;
default:
break;
}
} else {
_ok = true;
if (midi_dirpath == 0) {
midi_dirpath = new string (dirpath);
midi_filename_pattern = new string (pattern);
}
if ((desc.mode & O_NONBLOCK) == 0) {
/* we unconditionally set O_NONBLOCK during
open, but the request didn't ask for it,
so remove it.
*/
int flags = fcntl (_fd, F_GETFL, 0);
fcntl (_fd, F_SETFL, flags & ~(O_NONBLOCK));
}
}
}
void
FD_MidiPort::open (const Descriptor& desc)
{
int mode = desc.mode | O_NONBLOCK;
_fd = ::open (desc.device.c_str(), mode);
}
vector<string *> *
FD_MidiPort::list_devices ()
{
PathScanner scanner;
return scanner (*midi_dirpath, *midi_filename_pattern, false, true, false);
}
int
FD_MidiPort::selectable () const
{
long flags;
/* turn on non-blocking mode, since we plan to use select/poll
to tell us when there is data to read.
*/
flags = fcntl (_fd, F_GETFL);
flags |= O_NONBLOCK;
if (fcntl (_fd, F_SETFL, flags)) {
error << "FD_MidiPort: could not turn on non-blocking mode"
<< " (" << strerror (errno)
<< ')'
<< endmsg;
return -1;
}
return _fd;
}
int
FD_MidiPort::do_slow_write (byte *msg, unsigned int msglen)
{
size_t n;
size_t i;
for (n = 0; n < msglen; n++) {
if (::write (_fd, &msg[n], 1) != 1) {
break;
}
bytes_written++;
for (i = 0; i < slowdown * 10000; i++) {}
}
if (n && output_parser) {
output_parser->raw_preparse (*output_parser, msg, n);
for (unsigned int i = 0; i < n; i++) {
output_parser->scanner (msg[i]);
}
output_parser->raw_postparse (*output_parser, msg, n);
}
return n;
}
int
FD_MidiPort::read (byte* buf, size_t max)
{
int nread;
if ((_mode & O_ACCMODE) == O_WRONLY) {
return -EACCES;
}
// cerr << "MIDI: read up to " << max << " from " << _fd << " (" << name() << ')' << endl;
if ((nread = ::read (_fd, buf, max)) > 0) {
bytes_read += nread;
// cerr << " read " << nread << endl;
if (input_parser) {
input_parser->raw_preparse
(*input_parser, buf, nread);
for (int i = 0; i < nread; i++) {
input_parser->scanner (buf[i]);
}
input_parser->raw_postparse
(*input_parser, buf, nread);
}
}
return nread;
}

View file

@ -1,44 +0,0 @@
/*
Copyright (C) 1998-99 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 <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "midi++/types.h"
#include "midi++/fifomidi.h"
using namespace MIDI;
FIFO_MidiPort::FIFO_MidiPort (const XMLNode& node)
: FD_MidiPort (node, ".", "midi")
{
}
void
FIFO_MidiPort::open (const Port::Descriptor& desc)
{
/* This is a placeholder for the fun-and-games I think we will
need to do with FIFO's.
*/
_fd = ::open (desc.device.c_str(), desc.mode|O_NDELAY);
}

View file

@ -1,433 +0,0 @@
/*
Copyright (C) 2006 Paul Davis
Written by Dave Robillard
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.
*/
#include <fcntl.h>
#include <cerrno>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include "pbd/error.h"
#include "pbd/compose.h"
#include "pbd/strsplit.h"
#include "midi++/types.h"
#include "midi++/jack.h"
using namespace std;
using namespace MIDI;
using namespace PBD;
pthread_t JACK_MidiPort::_process_thread;
Signal0<void> JACK_MidiPort::JackHalted;
Signal0<void> JACK_MidiPort::MakeConnections;
JACK_MidiPort::JACK_MidiPort(const XMLNode& node, jack_client_t* jack_client)
: Port(node)
, _jack_client(jack_client)
, _jack_input_port(NULL)
, _jack_output_port(NULL)
, _last_read_index(0)
, output_fifo (512)
, input_fifo (1024)
{
if (!create_ports (node)) {
_ok = true;
}
MakeConnections.connect_same_thread (connect_connection, boost::bind (&JACK_MidiPort::make_connections, this));
JackHalted.connect_same_thread (halt_connection, boost::bind (&JACK_MidiPort::jack_halted, this));
set_state (node);
}
JACK_MidiPort::~JACK_MidiPort()
{
if (_jack_input_port) {
if (_jack_client) {
jack_port_unregister (_jack_client, _jack_input_port);
}
_jack_input_port = 0;
}
if (_jack_output_port) {
if (_jack_client) {
jack_port_unregister (_jack_client, _jack_input_port);
}
_jack_input_port = 0;
}
}
void
JACK_MidiPort::jack_halted ()
{
_jack_client = 0;
_jack_input_port = 0;
_jack_output_port = 0;
}
void
JACK_MidiPort::cycle_start (nframes_t nframes)
{
Port::cycle_start(nframes);
assert(_nframes_this_cycle == nframes);
_last_read_index = 0;
_last_write_timestamp = 0;
if (_jack_output_port != 0) {
// output
void *buffer = jack_port_get_buffer (_jack_output_port, nframes);
jack_midi_clear_buffer (buffer);
flush (buffer);
}
if (_jack_input_port != 0) {
// input
void* jack_buffer = jack_port_get_buffer(_jack_input_port, nframes);
const nframes_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 (nframes_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
JACK_MidiPort::cycle_end ()
{
if (_jack_output_port != 0) {
flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
}
Port::cycle_end();
}
int
JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
{
int ret = 0;
if (!_jack_output_port) {
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 << "assertion timestamp < _nframes_this_cycle failed!" << std::endl;
}
if (_currently_in_cycle) {
if (timestamp == 0) {
timestamp = _last_write_timestamp;
}
if (jack_midi_event_write (jack_port_get_buffer (_jack_output_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_output_port, _nframes_this_cycle))
<< endl;
}
} else {
cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
}
}
if (ret > 0 && output_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++) {
output_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
JACK_MidiPort::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
JACK_MidiPort::read (byte *, size_t)
{
timestamp_t time;
Evoral::EventType type;
uint32_t size;
byte buffer[input_fifo.capacity()];
while (input_fifo.read (&time, &type, &size, buffer)) {
if (input_parser) {
input_parser->set_timestamp (time);
for (uint32_t i = 0; i < size; ++i) {
input_parser->scanner (buffer[i]);
}
}
}
return 0;
}
int
JACK_MidiPort::create_ports(const XMLNode& node)
{
Descriptor desc (node);
assert(!_jack_input_port);
assert(!_jack_output_port);
if (desc.mode == O_RDWR || desc.mode == O_WRONLY) {
_jack_output_port_name = string(desc.tag).append ("_out");
}
if (desc.mode == O_RDWR || desc.mode == O_RDONLY) {
_jack_input_port_name = string(desc.tag).append ("_in");
}
return create_ports ();
}
int
JACK_MidiPort::create_ports ()
{
bool ret = true;
jack_nframes_t nframes = jack_get_buffer_size(_jack_client);
if (!_jack_output_port_name.empty()) {
_jack_output_port = jack_port_register(_jack_client, _jack_output_port_name.c_str(),
JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
if (_jack_output_port) {
jack_midi_clear_buffer(jack_port_get_buffer(_jack_output_port, nframes));
}
ret = ret && (_jack_output_port != NULL);
}
if (!_jack_input_port_name.empty()) {
_jack_input_port = jack_port_register(_jack_client, _jack_input_port_name.c_str(),
JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
if (_jack_input_port) {
jack_midi_clear_buffer(jack_port_get_buffer(_jack_input_port, nframes));
}
ret = ret && (_jack_input_port != NULL);
}
return ret ? 0 : -1;
}
XMLNode&
JACK_MidiPort::get_state () const
{
XMLNode& root (Port::get_state ());
if (_jack_output_port) {
const char** jc = jack_port_get_connections (_jack_output_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 ("outbound", connection_string);
}
} else {
if (!_outbound_connections.empty()) {
root.add_property ("outbound", _outbound_connections);
}
}
if (_jack_input_port) {
const char** jc = jack_port_get_connections (_jack_input_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 ("inbound", connection_string);
}
} else {
if (!_inbound_connections.empty()) {
root.add_property ("inbound", _inbound_connections);
}
}
return root;
}
void
JACK_MidiPort::set_state (const XMLNode& node)
{
Port::set_state (node);
const XMLProperty* prop;
if ((prop = node.property ("inbound")) != 0 && _jack_input_port) {
_inbound_connections = prop->value ();
}
if ((prop = node.property ("outbound")) != 0 && _jack_output_port) {
_outbound_connections = prop->value();
}
}
void
JACK_MidiPort::make_connections ()
{
if (!_inbound_connections.empty()) {
vector<string> ports;
split (_inbound_connections, ports, ',');
for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
if (_jack_client) {
jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_input_port));
/* ignore failures */
}
}
}
if (!_outbound_connections.empty()) {
vector<string> ports;
split (_outbound_connections, ports, ',');
for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
if (_jack_client) {
jack_connect (_jack_client, jack_port_name (_jack_output_port), (*x).c_str());
/* ignore failures */
}
}
}
connect_connection.disconnect ();
}
void
JACK_MidiPort::set_process_thread (pthread_t thr)
{
_process_thread = thr;
}
bool
JACK_MidiPort::is_process_thread()
{
return (pthread_self() == _process_thread);
}
void
JACK_MidiPort::reestablish (void* jack)
{
_jack_client = static_cast<jack_client_t*> (jack);
int const r = create_ports ();
if (r) {
PBD::error << "could not reregister ports for " << name() << endmsg;
}
}
void
JACK_MidiPort::reconnect ()
{
make_connections ();
}

View file

@ -25,8 +25,8 @@
#include "midi++/types.h" #include "midi++/types.h"
#include "midi++/manager.h" #include "midi++/manager.h"
#include "midi++/factory.h"
#include "midi++/channel.h" #include "midi++/channel.h"
#include "midi++/port.h"
using namespace std; using namespace std;
using namespace MIDI; using namespace MIDI;
@ -60,7 +60,6 @@ Port *
Manager::add_port (const XMLNode& node) Manager::add_port (const XMLNode& node)
{ {
Port::Descriptor desc (node); Port::Descriptor desc (node);
PortFactory factory;
Port *port; Port *port;
PortList::iterator p; PortList::iterator p;
@ -70,27 +69,13 @@ Manager::add_port (const XMLNode& node)
break; break;
} }
if (!PortFactory::ignore_duplicate_devices (desc.type)) {
if (desc.device == (*p)->device()) {
/* If the existing is duplex, and this request
is not, then fail, because most drivers won't
allow opening twice with duplex and non-duplex
operation.
*/
if ((desc.mode == O_RDWR && (*p)->mode() != O_RDWR) ||
(desc.mode != O_RDWR && (*p)->mode() == O_RDWR)) {
break;
}
}
}
} }
if (p != _ports.end()) { if (p != _ports.end()) {
return 0; return 0;
} }
port = factory.create_port (node, api_data); port = new Port (node, (jack_client_t *) api_data);
if (port == 0) { if (port == 0) {
return 0; return 0;
@ -228,13 +213,6 @@ Manager::cycle_end()
} }
} }
int
Manager::get_known_ports (vector<PortSet>& ports)
{
return PortFactory::get_known_ports (ports);
}
/** Re-register ports that disappear on JACK shutdown */ /** Re-register ports that disappear on JACK shutdown */
void void
Manager::reestablish (void* a) Manager::reestablish (void* a)

View file

@ -1,52 +0,0 @@
/*
Copyright (C) 1998-99 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 __alsa_rawmidi_h__
#define __alsa_rawmidi_h__
#include <vector>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include "midi++/port.h"
#include "midi++/fd_midiport.h"
namespace MIDI {
class ALSA_RawMidiPort : public MIDI::FD_MidiPort
{
public:
ALSA_RawMidiPort (const XMLNode& node)
: FD_MidiPort (node, "/dev/snd", "midi") {}
virtual ~ALSA_RawMidiPort () {}
static std::string typestring;
protected:
std::string get_typestring () const {
return typestring;
}
};
}
#endif // __alsa_rawmidi_h__

View file

@ -1,76 +0,0 @@
/*
Copyright (C) 2004 Paul 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 __alsa_sequencer_midiport_h__
#define __alsa_sequencer_midiport_h__
#include <vector>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#include "midi++/port.h"
namespace MIDI {
class ALSA_SequencerMidiPort : public Port
{
public:
ALSA_SequencerMidiPort (const XMLNode&);
virtual ~ALSA_SequencerMidiPort ();
int write (byte *msg, size_t msglen, timestamp_t timestamp);
int read (byte *buf, size_t max);
/* select(2)/poll(2)-based I/O */
virtual int selectable() const;
static int discover (std::vector<PortSet>&);
static std::string typestring;
XMLNode& get_state() const;
void set_state (const XMLNode&);
protected:
std::string get_typestring () const {
return typestring;
}
private:
snd_midi_event_t *decoder, *encoder;
int port_id;
snd_seq_event_t SEv;
int create_ports (const Port::Descriptor&);
static int init_client (std::string name);
static snd_seq_t* seq;
typedef std::pair<int,int> SequencerPortAddress;
void get_connections (std::vector<SequencerPortAddress>&, int dir) const;
};
}; /* namespace MIDI */
#endif // __alsa_sequencer_midiport_h__

View file

@ -1,79 +0,0 @@
/*
Copyright (C) 2004 Paul Davis
Copyright (C) 2004 Grame
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 __coremidi_midiport_h__
#define __coremidi_midiport_h__
#include <list>
#include <string>
#include <vector>
#include <fcntl.h>
#include <unistd.h>
#include "midi++/port.h"
#include <CoreMIDI/CoreMIDI.h>
namespace MIDI {
class CoreMidi_MidiPort:public Port {
public:
CoreMidi_MidiPort(const XMLNode& node);
virtual ~ CoreMidi_MidiPort();
int write (byte * msg, size_t msglen, timestamp_t timestamp);
int read (byte * buf, size_t max) {
return 0;
}
virtual int selectable() const {
return -1;
}
static int discover (std::vector<PortSet>&);
static std::string typestring;
protected:
/* CoreMidi callback */
static void read_proc(const MIDIPacketList * pktlist,
void *refCon, void *connRefCon);
std::string get_typestring () const {
return typestring;
}
private:
byte midi_buffer[1024];
MIDIClientRef midi_client;
MIDIEndpointRef midi_destination;
MIDIEndpointRef midi_source;
int Open(const Port::Descriptor&);
void Close();
static MIDITimeStamp MIDIGetCurrentHostTime();
bool firstrecv;
};
} // namespace MIDI
#endif // __coremidi_midiport_h__

View file

@ -1,43 +0,0 @@
/*
Copyright (C) 1998-99 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 __midi_factory_h__
#define __midi_factory_h__
#include <vector>
#include <string>
#include "midi++/port.h"
namespace MIDI {
class PortFactory {
public:
Port *create_port (const XMLNode&, void* data);
static bool ignore_duplicate_devices (Port::Type);
static int get_known_ports (std::vector<PortSet>&);
static std::string default_port_type ();
static Port::Type string_to_type (const std::string&);
static std::string mode_to_string (int);
static int string_to_mode (const std::string&);
};
} // namespace MIDI
#endif // __midi_factory_h__

View file

@ -1,91 +0,0 @@
/*
Copyright (C) 1999 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 __fd_midiport_h__
#define __fd_midiport_h__
#include <vector>
#include <string>
#include <cerrno>
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#include "midi++/port.h"
namespace MIDI {
class FD_MidiPort : public Port
{
public:
FD_MidiPort (const XMLNode& node,
const std::string &dirpath,
const std::string &pattern);
virtual ~FD_MidiPort () {
::close (_fd);
}
virtual int selectable() const;
static std::vector<std::string *> *list_devices ();
protected:
int _fd;
virtual void open (const Port::Descriptor&);
virtual int write (byte *msg, size_t msglen, timestamp_t) {
int nwritten;
if ((_mode & O_ACCMODE) == O_RDONLY) {
return -EACCES;
}
if (slowdown) {
return do_slow_write (msg, msglen);
}
if ((nwritten = ::write (_fd, msg, msglen)) > 0) {
bytes_written += nwritten;
if (output_parser) {
output_parser->raw_preparse (*output_parser, msg, nwritten);
for (int i = 0; i < nwritten; i++) {
output_parser->scanner (msg[i]);
}
output_parser->raw_postparse (*output_parser, msg, nwritten);
}
}
return nwritten;
}
int read (byte *buf, size_t max);
private:
static std::string *midi_dirpath;
static std::string *midi_filename_pattern;
int do_slow_write (byte *msg, unsigned int msglen);
};
} // namespace MIDI
#endif // __fd_midiport_h__

View file

@ -1,52 +0,0 @@
/*
Copyright (C) 1998-99 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 __fifomidi_h__
#define __fifomidi_h__
#include <fcntl.h>
#include <vector>
#include <string>
#include <unistd.h>
#include "midi++/port.h"
#include "midi++/fd_midiport.h"
namespace MIDI {
class FIFO_MidiPort : public MIDI::FD_MidiPort
{
public:
FIFO_MidiPort (const XMLNode&);
~FIFO_MidiPort () {};
static std::string typestring;
protected:
std::string get_typestring () const {
return typestring;
}
private:
void open (const Port::Descriptor&);
};
} // namespace MIDI
#endif // __fifomidi_h__

View file

@ -1,117 +0,0 @@
/*
Copyright (C) 2006 Paul Davis
Written by Dave Robillard
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: jack.h 4 2005-05-13 20:47:18Z taybin $
*/
#ifndef __jack_midiport_h__
#define __jack_midiport_h__
#include <vector>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <glibmm/thread.h>
#include <jack/weakjack.h>
#include <jack/jack.h>
#include <jack/midiport.h>
#include "pbd/ringbuffer.h"
#include "pbd/signals.h"
#include "pbd/crossthread.h"
#include "evoral/EventRingBuffer.hpp"
#include "midi++/port.h"
#include "midi++/event.h"
namespace MIDI
{
class JACK_MidiPort : public Port
{
public:
JACK_MidiPort (const XMLNode& node, jack_client_t* jack_client);
virtual ~JACK_MidiPort ();
int write(byte *msg, size_t msglen, timestamp_t timestamp);
int read(byte *buf, size_t max);
int selectable() const { return xthread.selectable(); }
bool must_drain_selectable() const { return true; }
void cycle_start(nframes_t nframes);
void cycle_end();
static std::string typestring;
XMLNode& get_state () const;
void set_state (const XMLNode&);
static void set_process_thread (pthread_t);
static pthread_t get_process_thread () { return _process_thread; }
static bool is_process_thread();
nframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
void reestablish (void *);
void reconnect ();
static PBD::Signal0<void> MakeConnections;
static PBD::Signal0<void> JackHalted;
protected:
std::string get_typestring () const {
return typestring;
}
private:
int create_ports(const XMLNode&);
int create_ports ();
jack_client_t* _jack_client;
std::string _jack_input_port_name; /// input port name, or empty if there isn't one
jack_port_t* _jack_input_port;
std::string _jack_output_port_name; /// output port name, or empty if there isn't one
jack_port_t* _jack_output_port;
nframes_t _last_read_index;
timestamp_t _last_write_timestamp;
CrossThreadChannel xthread;
std::string _inbound_connections;
std::string _outbound_connections;
PBD::ScopedConnection connect_connection;
PBD::ScopedConnection halt_connection;
void flush (void* jack_port_buffer);
void jack_halted ();
void make_connections();
static pthread_t _process_thread;
RingBuffer< Evoral::Event<double> > output_fifo;
Evoral::EventRingBuffer<timestamp_t> input_fifo;
Glib::Mutex output_fifo_lock;
};
} /* namespace MIDI */
#endif // __jack_midiport_h__

View file

@ -81,8 +81,6 @@ class Manager {
return theManager; return theManager;
} }
int get_known_ports (std::vector<PortSet>&);
void reestablish (void *); void reestablish (void *);
void reconnect (); void reconnect ();

View file

@ -1,68 +0,0 @@
/*
Copyright (C) 1998-99 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 __nullmidi_h__
#define __nullmidi_h__
#include <fcntl.h>
#include <vector>
#include <string>
#include "midi++/port.h"
namespace MIDI {
class Null_MidiPort : public Port
{
public:
Null_MidiPort (PortRequest &req)
: Port (req) {
/* reset devname and tagname */
_devname = "nullmidi";
_tagname = "null";
_type = Port::Null;
_ok = true;
}
virtual ~Null_MidiPort () {};
/* Direct I/O */
int write (byte *msg, size_t msglen, timestamp_t timestamp) {
return msglen;
}
int read (byte *buf, size_t max) {
return 0;
}
virtual int selectable() const { return -1; }
static std::string typestring;
protected:
std::string get_typestring () const {
return typestring;
}
};
} // namespace MIDI
#endif // __nullmidi_h__

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 1998-99 Paul Barton-Davis Copyright (C) 1998-2010 Paul Barton-Davis
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
@ -22,7 +22,15 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <jack/types.h>
#include "pbd/xml++.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++/types.h"
#include "midi++/parser.h" #include "midi++/parser.h"
@ -34,29 +42,18 @@ class PortRequest;
class Port { class Port {
public: public:
enum Type { Port (const XMLNode&, jack_client_t *);
Unknown, ~Port ();
JACK_Midi,
ALSA_RawMidi,
ALSA_Sequencer,
CoreMidi_MidiPort,
Null,
FIFO
};
XMLNode& get_state () const;
Port (const XMLNode&); void set_state (const XMLNode&);
virtual ~Port ();
virtual XMLNode& get_state () const;
virtual void set_state (const XMLNode&);
// FIXME: make Manager a friend of port so these can be hidden? // FIXME: make Manager a friend of port so these can be hidden?
/* Only for use by MidiManager. Don't ever call this. */ /* Only for use by MidiManager. Don't ever call this. */
virtual void cycle_start(nframes_t nframes); void cycle_start(nframes_t nframes);
/* Only for use by MidiManager. Don't ever call this. */ /* Only for use by MidiManager. Don't ever call this. */
virtual void cycle_end(); void cycle_end();
/** Write a message to port. /** Write a message to port.
* @param msg Raw MIDI message to send * @param msg Raw MIDI message to send
@ -64,14 +61,14 @@ class Port {
* @param timestamp Time stamp in frames of this message (relative to cycle start) * @param timestamp Time stamp in frames of this message (relative to cycle start)
* @return number of bytes successfully written * @return number of bytes successfully written
*/ */
virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0; int write (byte *msg, size_t msglen, timestamp_t timestamp);
/** Read raw bytes from a port. /** Read raw bytes from a port.
* @param buf memory to store read data in * @param buf memory to store read data in
* @param bufsize size of @a buf * @param bufsize size of @a buf
* @return number of bytes successfully read, negative if error * @return number of bytes successfully read, negative if error
*/ */
virtual int read (byte *buf, size_t bufsize) = 0; int read (byte *buf, size_t bufsize);
void parse (nframes_t timestamp); void parse (nframes_t timestamp);
@ -83,38 +80,16 @@ class Port {
return !(write (msg, len, timestamp) == (int) len); return !(write (msg, len, timestamp) == (int) len);
} }
int three_byte_msg (byte a, byte b, byte c, timestamp_t timestamp) {
byte msg[3];
msg[0] = a;
msg[1] = b;
msg[2] = c;
return !(write (msg, 3, timestamp) == 3);
}
bool clock (timestamp_t timestamp); bool clock (timestamp_t timestamp);
/* slowdown i/o to a loop of single byte emissions
interspersed with a busy loop of 10000 * this value.
This may be ignored by a particular instance
of this virtual class. See FD_MidiPort for an
example of where it used.
*/
void set_slowdown (size_t n) { slowdown = n; }
/* select(2)/poll(2)-based I/O */ /* select(2)/poll(2)-based I/O */
/** Get the file descriptor for port. /** Get the file descriptor for port.
* @return File descriptor, or -1 if not selectable. * @return File descriptor, or -1 if not selectable.
*/ */
virtual int selectable() const = 0; int selectable () const {
virtual bool must_drain_selectable() const { return false; } return xthread.selectable();
}
static void gtk_read_callback (void *ptr, int fd, int cond);
static void write_callback (byte *msg, unsigned int len, void *);
Channel *channel (channel_t chn) { Channel *channel (channel_t chn) {
return _channel[chn&0x7F]; return _channel[chn&0x7F];
@ -123,63 +98,72 @@ class Port {
Parser *input() { return input_parser; } Parser *input() { return input_parser; }
Parser *output() { return output_parser; } Parser *output() { return output_parser; }
void iostat (int *written, int *read,
const size_t **in_counts,
const size_t **out_counts) {
*written = bytes_written;
*read = bytes_read;
if (input_parser) {
*in_counts = input_parser->message_counts();
} else {
*in_counts = 0;
}
if (output_parser) {
*out_counts = output_parser->message_counts();
} else {
*out_counts = 0;
}
}
const char *device () const { return _devname.c_str(); }
const char *name () const { return _tagname.c_str(); } const char *name () const { return _tagname.c_str(); }
Type type () const { return _type; }
int mode () const { return _mode; } int mode () const { return _mode; }
bool ok () const { return _ok; } bool ok () const { return _ok; }
struct Descriptor { struct Descriptor {
std::string tag; std::string tag;
std::string device;
int mode; int mode;
Port::Type type;
Descriptor (const XMLNode&); Descriptor (const XMLNode&);
XMLNode& get_state(); XMLNode& get_state();
}; };
virtual void reestablish (void *) {} nframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
virtual void reconnect () {}
protected: void reestablish (void *);
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 _ok; bool _ok;
bool _currently_in_cycle; bool _currently_in_cycle;
nframes_t _nframes_this_cycle; nframes_t _nframes_this_cycle;
Type _type;
std::string _devname;
std::string _tagname; std::string _tagname;
int _mode; int _mode;
size_t _number; size_t _number;
Channel *_channel[16]; Channel *_channel[16];
unsigned int bytes_written;
unsigned int bytes_read;
Parser *input_parser; Parser *input_parser;
Parser *output_parser; Parser *output_parser;
size_t slowdown;
virtual std::string get_typestring () const = 0;
private:
static size_t nports; static size_t nports;
int create_ports(const XMLNode&);
int create_ports ();
jack_client_t* _jack_client;
std::string _jack_input_port_name; /// input port name, or empty if there isn't one
jack_port_t* _jack_input_port;
std::string _jack_output_port_name; /// output port name, or empty if there isn't one
jack_port_t* _jack_output_port;
nframes_t _last_read_index;
timestamp_t _last_write_timestamp;
/** Channel used to signal to the MidiControlUI that input has arrived */
CrossThreadChannel xthread;
std::string _inbound_connections;
std::string _outbound_connections;
PBD::ScopedConnection connect_connection;
PBD::ScopedConnection halt_connection;
void flush (void* jack_port_buffer);
void jack_halted ();
void make_connections();
static pthread_t _process_thread;
RingBuffer< Evoral::Event<double> > output_fifo;
Evoral::EventRingBuffer<timestamp_t> input_fifo;
Glib::Mutex output_fifo_lock;
}; };
struct PortSet { struct PortSet {

View file

@ -22,24 +22,37 @@
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <jack/jack.h>
#include <jack/midiport.h>
#include "pbd/xml++.h" #include "pbd/xml++.h"
#include "pbd/error.h" #include "pbd/error.h"
#include "pbd/failed_constructor.h" #include "pbd/failed_constructor.h"
#include "pbd/convert.h"
#include "pbd/strsplit.h"
#include "midi++/types.h" #include "midi++/types.h"
#include "midi++/port.h" #include "midi++/port.h"
#include "midi++/channel.h" #include "midi++/channel.h"
#include "midi++/factory.h"
using namespace MIDI; using namespace MIDI;
using namespace std; using namespace std;
using namespace PBD; using namespace PBD;
size_t Port::nports = 0; size_t Port::nports = 0;
pthread_t Port::_process_thread;
Signal0<void> Port::JackHalted;
Signal0<void> Port::MakeConnections;
Port::Port (const XMLNode& node) Port::Port (const XMLNode& node, jack_client_t* jack_client)
: _currently_in_cycle(false) : _currently_in_cycle (false)
, _nframes_this_cycle(0) , _nframes_this_cycle (0)
, _jack_client (jack_client)
, _jack_input_port (0)
, _jack_output_port (0)
, _last_read_index (0)
, output_fifo (512)
, input_fifo (1024)
{ {
Descriptor desc (node); Descriptor desc (node);
@ -47,13 +60,9 @@ Port::Port (const XMLNode& node)
succeeds. succeeds.
*/ */
bytes_written = 0;
bytes_read = 0;
input_parser = 0; input_parser = 0;
output_parser = 0; output_parser = 0;
slowdown = 0;
_devname = desc.device;
_tagname = desc.tag; _tagname = desc.tag;
_mode = desc.mode; _mode = desc.mode;
@ -80,6 +89,15 @@ Port::Port (const XMLNode& node)
_channel[i]->connect_output_signals (); _channel[i]->connect_output_signals ();
} }
} }
if (!create_ports (node)) {
_ok = true;
}
MakeConnections.connect_same_thread (connect_connection, boost::bind (&Port::make_connections, this));
JackHalted.connect_same_thread (halt_connection, boost::bind (&Port::jack_halted, this));
set_state (node);
} }
@ -88,6 +106,20 @@ Port::~Port ()
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
delete _channel[i]; delete _channel[i];
} }
if (_jack_input_port) {
if (_jack_client) {
jack_port_unregister (_jack_client, _jack_input_port);
}
_jack_input_port = 0;
}
if (_jack_output_port) {
if (_jack_client) {
jack_port_unregister (_jack_client, _jack_input_port);
}
_jack_input_port = 0;
}
} }
void void
@ -149,23 +181,292 @@ Port::cycle_start (nframes_t nframes)
{ {
_currently_in_cycle = true; _currently_in_cycle = true;
_nframes_this_cycle = nframes; _nframes_this_cycle = nframes;
assert(_nframes_this_cycle == nframes);
_last_read_index = 0;
_last_write_timestamp = 0;
if (_jack_output_port != 0) {
// output
void *buffer = jack_port_get_buffer (_jack_output_port, nframes);
jack_midi_clear_buffer (buffer);
flush (buffer);
}
if (_jack_input_port != 0) {
// input
void* jack_buffer = jack_port_get_buffer(_jack_input_port, nframes);
const nframes_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 (nframes_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 void
Port::cycle_end () Port::cycle_end ()
{ {
if (_jack_output_port != 0) {
flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
}
_currently_in_cycle = false; _currently_in_cycle = false;
_nframes_this_cycle = 0; _nframes_this_cycle = 0;
} }
std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port )
{
using namespace std;
os << "MIDI::Port { ";
os << "name: " << port.name();
os << "; ";
os << "mode: " << port.mode();
os << "; ";
os << "ok: " << port.ok();
os << "; ";
os << " }";
return os;
}
Port::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) {
mode = O_RDWR;
if (strings_equal_ignore_case (prop->value(), "output") || strings_equal_ignore_case (prop->value(), "out")) {
mode = O_WRONLY;
} else if (strings_equal_ignore_case (prop->value(), "input") || strings_equal_ignore_case (prop->value(), "in")) {
mode = O_RDONLY;
}
have_mode = true;
}
if (!have_tag || !have_mode) {
throw failed_constructor();
}
}
void
Port::jack_halted ()
{
_jack_client = 0;
_jack_input_port = 0;
_jack_output_port = 0;
}
int
Port::write(byte * msg, size_t msglen, timestamp_t timestamp)
{
int ret = 0;
if (!_jack_output_port) {
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 << "assertion timestamp < _nframes_this_cycle failed!" << std::endl;
}
if (_currently_in_cycle) {
if (timestamp == 0) {
timestamp = _last_write_timestamp;
}
if (jack_midi_event_write (jack_port_get_buffer (_jack_output_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_output_port, _nframes_this_cycle))
<< endl;
}
} else {
cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
}
}
if (ret > 0 && output_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++) {
output_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 (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
Port::read (byte *, size_t)
{
timestamp_t time;
Evoral::EventType type;
uint32_t size;
byte buffer[input_fifo.capacity()];
while (input_fifo.read (&time, &type, &size, buffer)) {
if (input_parser) {
input_parser->set_timestamp (time);
for (uint32_t i = 0; i < size; ++i) {
input_parser->scanner (buffer[i]);
}
}
}
return 0;
}
int
Port::create_ports(const XMLNode& node)
{
Descriptor desc (node);
assert(!_jack_input_port);
assert(!_jack_output_port);
if (desc.mode == O_RDWR || desc.mode == O_WRONLY) {
_jack_output_port_name = string(desc.tag).append ("_out");
}
if (desc.mode == O_RDWR || desc.mode == O_RDONLY) {
_jack_input_port_name = string(desc.tag).append ("_in");
}
return create_ports ();
}
int
Port::create_ports ()
{
bool ret = true;
jack_nframes_t nframes = jack_get_buffer_size(_jack_client);
if (!_jack_output_port_name.empty()) {
_jack_output_port = jack_port_register(_jack_client, _jack_output_port_name.c_str(),
JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
if (_jack_output_port) {
jack_midi_clear_buffer(jack_port_get_buffer(_jack_output_port, nframes));
}
ret = ret && (_jack_output_port != NULL);
}
if (!_jack_input_port_name.empty()) {
_jack_input_port = jack_port_register(_jack_client, _jack_input_port_name.c_str(),
JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
if (_jack_input_port) {
jack_midi_clear_buffer(jack_port_get_buffer(_jack_input_port, nframes));
}
ret = ret && (_jack_input_port != NULL);
}
return ret ? 0 : -1;
}
XMLNode& XMLNode&
Port::get_state () const Port::get_state () const
{ {
XMLNode* node = new XMLNode ("MIDI-port"); XMLNode* root = new XMLNode ("MIDI-port");
node->add_property ("tag", _tagname); root->add_property ("tag", _tagname);
node->add_property ("device", _devname);
node->add_property ("mode", PortFactory::mode_to_string (_mode)); if (_mode == O_RDONLY) {
node->add_property ("type", get_typestring()); root->add_property ("mode", "input");
} else if (_mode == O_WRONLY) {
root->add_property ("mode", "output");
} else {
root->add_property ("mode", "duplex");
}
#if 0 #if 0
byte device_inquiry[6]; byte device_inquiry[6];
@ -180,76 +481,121 @@ Port::get_state () const
write (device_inquiry, sizeof (device_inquiry), 0); write (device_inquiry, sizeof (device_inquiry), 0);
#endif #endif
return *node; if (_jack_output_port) {
const char** jc = jack_port_get_connections (_jack_output_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 ("outbound", connection_string);
}
} else {
if (!_outbound_connections.empty()) {
root->add_property ("outbound", _outbound_connections);
}
}
if (_jack_input_port) {
const char** jc = jack_port_get_connections (_jack_input_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 ("inbound", connection_string);
}
} else {
if (!_inbound_connections.empty()) {
root->add_property ("inbound", _inbound_connections);
}
}
return *root;
} }
void void
Port::set_state (const XMLNode& /*node*/) Port::set_state (const XMLNode& node)
{ {
// relax const XMLProperty* prop;
if ((prop = node.property ("inbound")) != 0 && _jack_input_port) {
_inbound_connections = prop->value ();
}
if ((prop = node.property ("outbound")) != 0 && _jack_output_port) {
_outbound_connections = prop->value();
}
} }
void void
Port::gtk_read_callback (void *ptr, int /*fd*/, int /*cond*/) Port::make_connections ()
{ {
byte buf[64]; if (!_inbound_connections.empty()) {
((Port *)ptr)->read (buf, sizeof (buf)); vector<string> ports;
split (_inbound_connections, ports, ',');
for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
if (_jack_client) {
jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_input_port));
/* ignore failures */
}
}
}
if (!_outbound_connections.empty()) {
vector<string> ports;
split (_outbound_connections, ports, ',');
for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
if (_jack_client) {
jack_connect (_jack_client, jack_port_name (_jack_output_port), (*x).c_str());
/* ignore failures */
}
}
}
connect_connection.disconnect ();
} }
void void
Port::write_callback (byte *msg, unsigned int len, void *ptr) Port::set_process_thread (pthread_t thr)
{ {
((Port *)ptr)->write (msg, len, 0); _process_thread = thr;
} }
std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port ) bool
Port::is_process_thread()
{ {
using namespace std; return (pthread_self() == _process_thread);
os << "MIDI::Port { ";
os << "device: " << port.device();
os << "; ";
os << "name: " << port.name();
os << "; ";
os << "type: " << port.type();
os << "; ";
os << "mode: " << port.mode();
os << "; ";
os << "ok: " << port.ok();
os << "; ";
os << " }";
return os;
} }
Port::Descriptor::Descriptor (const XMLNode& node) void
Port::reestablish (void* jack)
{ {
const XMLProperty *prop; _jack_client = static_cast<jack_client_t*> (jack);
bool have_tag = false; int const r = create_ports ();
bool have_device = false;
bool have_type = false;
bool have_mode = false;
if ((prop = node.property ("tag")) != 0) { if (r) {
tag = prop->value(); PBD::error << "could not reregister ports for " << name() << endmsg;
have_tag = true;
}
if ((prop = node.property ("device")) != 0) {
device = prop->value();
have_device = true;
}
if ((prop = node.property ("type")) != 0) {
type = PortFactory::string_to_type (prop->value());
have_type = true;
}
if ((prop = node.property ("mode")) != 0) {
mode = PortFactory::string_to_mode (prop->value());
have_mode = true;
}
if (!have_tag || !have_device || !have_type || !have_mode) {
throw failed_constructor();
} }
} }
void
Port::reconnect ()
{
make_connections ();
}

View file

@ -45,11 +45,8 @@ def build(bld):
# Library # Library
obj = bld.new_task_gen('cxx', 'shlib') obj = bld.new_task_gen('cxx', 'shlib')
obj.source = ''' obj.source = '''
fd_midiport.cc
fifomidi.cc
midi.cc midi.cc
channel.cc channel.cc
factory.cc
manager.cc manager.cc
parser.cc parser.cc
port.cc port.cc
@ -59,21 +56,12 @@ def build(bld):
version.cc version.cc
''' '''
# everybody loves JACK # everybody loves JACK
obj.source += ' jack_midiport.cc '
obj.cxxflags = [ '-DWITH_JACK_MIDI' ] obj.cxxflags = [ '-DWITH_JACK_MIDI' ]
if bld.env['HAVE_COREAUDIO'] and bld.env['COREAUDIO']:
# OS X
obj.source += ' coremidi_midiport.cc '
obj.cxxflags += [ '-DWITH_COREMIDI' ]
elif sys.platform == 'linux':
# linux
obj.source += ' alsa_sequencer_midiport.cc '
obj.cxxflags += [ '-DWITH_ALSA' ]
obj.export_incdirs = ['.'] obj.export_incdirs = ['.']
obj.includes = ['.', '../surfaces/control_protocol'] obj.includes = ['.', '../surfaces/control_protocol']
obj.name = 'libmidipp' obj.name = 'libmidipp'
obj.target = 'midipp' obj.target = 'midipp'
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX COREAUDIO' obj.uselib = 'GLIBMM SIGCPP XML JACK OSX'
obj.uselib_local = 'libpbd libevoral' obj.uselib_local = 'libpbd libevoral'
obj.vnum = LIBMIDIPP_LIB_VERSION obj.vnum = LIBMIDIPP_LIB_VERSION
obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')

View file

@ -563,20 +563,14 @@ MackieControlProtocol::connect_session_signals()
void void
MackieControlProtocol::add_port (MIDI::Port & midi_port, int number) MackieControlProtocol::add_port (MIDI::Port & midi_port, int number)
{ {
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1,%2,%3\n", midi_port.name(), midi_port.device(), midi_port.type())); DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1\n", midi_port.name()));
if (string (midi_port.device()) == string ("ardour") && midi_port.type() == MIDI::Port::ALSA_Sequencer) {
throw MackieControlException ("The Mackie MCU driver will not use a port with device=ardour");
} else if (midi_port.type() == MIDI::Port::ALSA_Sequencer) {
throw MackieControlException ("alsa/sequencer ports don't work with the Mackie MCU driver right now");
} else {
MackiePort * sport = new MackiePort (*this, midi_port, number); MackiePort * sport = new MackiePort (*this, midi_port, number);
_ports.push_back (sport); _ports.push_back (sport);
sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport)); sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport)); sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport)); sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
}
} }
void void

View file

@ -173,8 +173,6 @@ void SurfacePort::write_sysex( MIDI::byte msg )
ostream & Mackie::operator << ( ostream & os, const SurfacePort & port ) ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
{ {
os << "{ "; os << "{ ";
os << "device: " << port.port().device();
os << "; ";
os << "name: " << port.port().name(); os << "name: " << port.port().name();
os << "; "; os << "; ";
os << " }"; os << " }";