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:
parent
dc1e5d09a2
commit
91850f0eb4
|
@ -8,7 +8,6 @@
|
|||
#include "pbd/cpus.h"
|
||||
|
||||
#include "midi++/manager.h"
|
||||
#include "midi++/factory.h"
|
||||
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/dB.h"
|
||||
|
@ -209,7 +208,6 @@ private:
|
|||
|
||||
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 ("type", MIDI::PortFactory::default_port_type());
|
||||
node.add_property ("mode", smod);
|
||||
|
||||
if (MIDI::Manager::instance()->add_port (node) != 0) {
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "midi++/jack.h"
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "ardour/types.h"
|
||||
|
@ -29,6 +28,10 @@
|
|||
#ifndef TICKER_H_
|
||||
#define TICKER_H_
|
||||
|
||||
namespace MIDI {
|
||||
class Port;
|
||||
}
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#include "pbd/stacktrace.h"
|
||||
#include "pbd/unknown_type.h"
|
||||
|
||||
#include "midi++/jack.h"
|
||||
#include "midi++/port.h"
|
||||
#include "midi++/mmc.h"
|
||||
#include "midi++/manager.h"
|
||||
|
||||
|
@ -147,7 +147,7 @@ _thread_init_callback (void * /*arg*/)
|
|||
|
||||
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 ());
|
||||
}
|
||||
|
||||
|
@ -263,7 +263,7 @@ AudioEngine::stop (bool forever)
|
|||
} else {
|
||||
jack_deactivate (_priv_jack);
|
||||
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) {
|
||||
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) {
|
||||
_running = false;
|
||||
Stopped(); /* EMIT SIGNAL */
|
||||
MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */
|
||||
MIDI::Port::JackHalted (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include "pbd/pthread_utils.h"
|
||||
|
||||
#include "midi++/port.h"
|
||||
#include "midi++/jack.h"
|
||||
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/slave.h"
|
||||
|
|
|
@ -104,9 +104,7 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
|
|||
|
||||
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()));
|
||||
nframes64_t now = _session.engine().frame_time();
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
#include "ardour/utils.h"
|
||||
#include "ardour/graph.h"
|
||||
|
||||
#include "midi++/jack.h"
|
||||
#include "midi++/port.h"
|
||||
#include "midi++/mmc.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
@ -645,7 +645,7 @@ Session::hookup_io ()
|
|||
/* Tell all IO objects to connect themselves together */
|
||||
|
||||
IO::enable_connecting ();
|
||||
MIDI::JACK_MidiPort::MakeConnections ();
|
||||
MIDI::Port::MakeConnections ();
|
||||
|
||||
/* Now reset all panners */
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
$Id$
|
||||
*/
|
||||
|
||||
#include "midi++/port.h"
|
||||
#include "evoral/midi_events.h"
|
||||
#include "ardour/ticker.h"
|
||||
#include "ardour/session.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)
|
||||
return;
|
||||
|
||||
MIDI::JACK_MidiPort* jack_port = dynamic_cast<MIDI::JACK_MidiPort*>(_midi_port);
|
||||
assert(jack_port);
|
||||
|
||||
while (true) {
|
||||
double next_tick = _last_tick + one_ppqn_in_frames(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 << ":"
|
||||
<< ":Next tick time:" << next_tick << ":"
|
||||
<< "Offset:" << next_tick_offset << ":"
|
||||
<< "cycle length:" << jack_port->nframes_this_cycle()
|
||||
<< "cycle length:" << _midi_port->nframes_this_cycle()
|
||||
<< endl;
|
||||
#endif
|
||||
|
||||
if (next_tick_offset >= jack_port->nframes_this_cycle())
|
||||
if (next_tick_offset >= _midi_port->nframes_this_cycle())
|
||||
return;
|
||||
|
||||
send_midi_clock_event(next_tick_offset);
|
||||
|
@ -183,9 +182,7 @@ void MidiClockTicker::send_midi_clock_event(nframes_t offset)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef WITH_JACK_MIDI
|
||||
assert (MIDI::JACK_MidiPort::is_process_thread());
|
||||
#endif // WITH_JACK_MIDI
|
||||
assert (MIDI::Port::is_process_thread());
|
||||
#ifdef DEBUG_MIDI_CLOCK
|
||||
cerr << "Tick with offset " << offset << endl;
|
||||
#endif // DEBUG_MIDI_CLOCK
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -25,78 +25,13 @@
|
|||
|
||||
#include "midi++/types.h"
|
||||
#include "midi++/factory.h"
|
||||
#include "midi++/fifomidi.h"
|
||||
|
||||
#ifdef WITH_JACK_MIDI
|
||||
#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 MIDI;
|
||||
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
|
||||
PortFactory::ignore_duplicate_devices (Port::Type type)
|
||||
|
@ -104,23 +39,10 @@ PortFactory::ignore_duplicate_devices (Port::Type type)
|
|||
bool ret = false;
|
||||
|
||||
switch (type) {
|
||||
#ifdef WITH_ALSA
|
||||
case Port::ALSA_Sequencer:
|
||||
ret = true;
|
||||
break;
|
||||
#endif // WITH_ALSA
|
||||
|
||||
#ifdef WITH_JACK_MIDI
|
||||
case Port::JACK_Midi:
|
||||
ret = true;
|
||||
break;
|
||||
#endif // WITH_JACK_MIDI
|
||||
|
||||
#ifdef WITH_COREMIDI
|
||||
case Port::CoreMidi_MidiPort:
|
||||
ret = true;
|
||||
break;
|
||||
#endif // WITH_COREMIDI
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -129,65 +51,17 @@ PortFactory::ignore_duplicate_devices (Port::Type type)
|
|||
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
|
||||
PortFactory::default_port_type ()
|
||||
{
|
||||
#ifdef WITH_JACK_MIDI
|
||||
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
|
||||
PortFactory::string_to_type (const string& xtype)
|
||||
{
|
||||
if (0){
|
||||
#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)) {
|
||||
if (strings_equal_ignore_case (xtype, JACK_MidiPort::typestring)) {
|
||||
return Port::JACK_Midi;
|
||||
#endif
|
||||
}
|
||||
|
||||
return Port::Unknown;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 ();
|
||||
}
|
|
@ -25,8 +25,8 @@
|
|||
|
||||
#include "midi++/types.h"
|
||||
#include "midi++/manager.h"
|
||||
#include "midi++/factory.h"
|
||||
#include "midi++/channel.h"
|
||||
#include "midi++/port.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace MIDI;
|
||||
|
@ -60,7 +60,6 @@ Port *
|
|||
Manager::add_port (const XMLNode& node)
|
||||
{
|
||||
Port::Descriptor desc (node);
|
||||
PortFactory factory;
|
||||
Port *port;
|
||||
PortList::iterator p;
|
||||
|
||||
|
@ -68,29 +67,15 @@ Manager::add_port (const XMLNode& node)
|
|||
|
||||
if (desc.tag == (*p)->name()) {
|
||||
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()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
port = factory.create_port (node, api_data);
|
||||
port = new Port (node, (jack_client_t *) api_data);
|
||||
|
||||
if (port == 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 */
|
||||
void
|
||||
Manager::reestablish (void* a)
|
||||
|
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
||||
|
|
@ -81,8 +81,6 @@ class Manager {
|
|||
return theManager;
|
||||
}
|
||||
|
||||
int get_known_ports (std::vector<PortSet>&);
|
||||
|
||||
void reestablish (void *);
|
||||
void reconnect ();
|
||||
|
||||
|
|
|
@ -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__
|
|
@ -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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
|
@ -22,7 +22,15 @@
|
|||
#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"
|
||||
|
@ -34,29 +42,18 @@ class PortRequest;
|
|||
|
||||
class Port {
|
||||
public:
|
||||
enum Type {
|
||||
Unknown,
|
||||
JACK_Midi,
|
||||
ALSA_RawMidi,
|
||||
ALSA_Sequencer,
|
||||
CoreMidi_MidiPort,
|
||||
Null,
|
||||
FIFO
|
||||
};
|
||||
Port (const XMLNode&, jack_client_t *);
|
||||
~Port ();
|
||||
|
||||
|
||||
Port (const XMLNode&);
|
||||
virtual ~Port ();
|
||||
|
||||
virtual XMLNode& get_state () const;
|
||||
virtual void set_state (const XMLNode&);
|
||||
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(nframes_t nframes);
|
||||
void cycle_start(nframes_t nframes);
|
||||
/* Only for use by MidiManager. Don't ever call this. */
|
||||
virtual void cycle_end();
|
||||
void cycle_end();
|
||||
|
||||
/** Write a message to port.
|
||||
* @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)
|
||||
* @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.
|
||||
* @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;
|
||||
int read (byte *buf, size_t bufsize);
|
||||
|
||||
void parse (nframes_t timestamp);
|
||||
|
||||
|
@ -83,103 +80,90 @@ class Port {
|
|||
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);
|
||||
|
||||
/* 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 */
|
||||
|
||||
/** Get the file descriptor for port.
|
||||
* @return File descriptor, or -1 if not selectable.
|
||||
*/
|
||||
virtual int selectable() const = 0;
|
||||
virtual bool must_drain_selectable() const { return false; }
|
||||
int selectable () const {
|
||||
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) {
|
||||
return _channel[chn&0x7F];
|
||||
}
|
||||
|
||||
Parser *input() { return input_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(); }
|
||||
Type type () const { return _type; }
|
||||
int mode () const { return _mode; }
|
||||
bool ok () const { return _ok; }
|
||||
|
||||
struct Descriptor {
|
||||
std::string tag;
|
||||
std::string device;
|
||||
int mode;
|
||||
Port::Type type;
|
||||
|
||||
Descriptor (const XMLNode&);
|
||||
XMLNode& get_state();
|
||||
};
|
||||
|
||||
virtual void reestablish (void *) {}
|
||||
virtual void reconnect () {}
|
||||
nframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
|
||||
|
||||
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 _currently_in_cycle;
|
||||
nframes_t _nframes_this_cycle;
|
||||
Type _type;
|
||||
std::string _devname;
|
||||
std::string _tagname;
|
||||
int _mode;
|
||||
size_t _number;
|
||||
Channel *_channel[16];
|
||||
unsigned int bytes_written;
|
||||
unsigned int bytes_read;
|
||||
Parser *input_parser;
|
||||
Parser *output_parser;
|
||||
size_t slowdown;
|
||||
|
||||
virtual std::string get_typestring () const = 0;
|
||||
|
||||
private:
|
||||
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 {
|
||||
|
|
|
@ -22,24 +22,37 @@
|
|||
#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 "midi++/types.h"
|
||||
#include "midi++/port.h"
|
||||
#include "midi++/channel.h"
|
||||
#include "midi++/factory.h"
|
||||
|
||||
using namespace MIDI;
|
||||
using namespace std;
|
||||
using namespace PBD;
|
||||
|
||||
size_t Port::nports = 0;
|
||||
pthread_t Port::_process_thread;
|
||||
Signal0<void> Port::JackHalted;
|
||||
Signal0<void> Port::MakeConnections;
|
||||
|
||||
Port::Port (const XMLNode& node)
|
||||
: _currently_in_cycle(false)
|
||||
, _nframes_this_cycle(0)
|
||||
Port::Port (const XMLNode& node, jack_client_t* jack_client)
|
||||
: _currently_in_cycle (false)
|
||||
, _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);
|
||||
|
||||
|
@ -47,13 +60,9 @@ Port::Port (const XMLNode& node)
|
|||
succeeds.
|
||||
*/
|
||||
|
||||
bytes_written = 0;
|
||||
bytes_read = 0;
|
||||
input_parser = 0;
|
||||
output_parser = 0;
|
||||
slowdown = 0;
|
||||
|
||||
_devname = desc.device;
|
||||
_tagname = desc.tag;
|
||||
_mode = desc.mode;
|
||||
|
||||
|
@ -80,6 +89,15 @@ Port::Port (const XMLNode& node)
|
|||
_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++) {
|
||||
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
|
||||
|
@ -149,69 +181,54 @@ Port::cycle_start (nframes_t nframes)
|
|||
{
|
||||
_currently_in_cycle = true;
|
||||
_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
|
||||
Port::cycle_end ()
|
||||
{
|
||||
if (_jack_output_port != 0) {
|
||||
flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
|
||||
}
|
||||
|
||||
_currently_in_cycle = false;
|
||||
_nframes_this_cycle = 0;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
Port::get_state () const
|
||||
{
|
||||
XMLNode* node = new XMLNode ("MIDI-port");
|
||||
node->add_property ("tag", _tagname);
|
||||
node->add_property ("device", _devname);
|
||||
node->add_property ("mode", PortFactory::mode_to_string (_mode));
|
||||
node->add_property ("type", get_typestring());
|
||||
|
||||
#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 *node;
|
||||
}
|
||||
|
||||
void
|
||||
Port::set_state (const XMLNode& /*node*/)
|
||||
{
|
||||
// relax
|
||||
}
|
||||
|
||||
void
|
||||
Port::gtk_read_callback (void *ptr, int /*fd*/, int /*cond*/)
|
||||
{
|
||||
byte buf[64];
|
||||
((Port *)ptr)->read (buf, sizeof (buf));
|
||||
}
|
||||
|
||||
void
|
||||
Port::write_callback (byte *msg, unsigned int len, void *ptr)
|
||||
{
|
||||
((Port *)ptr)->write (msg, len, 0);
|
||||
}
|
||||
|
||||
std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port )
|
||||
{
|
||||
using namespace std;
|
||||
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();
|
||||
|
@ -224,8 +241,6 @@ Port::Descriptor::Descriptor (const XMLNode& node)
|
|||
{
|
||||
const XMLProperty *prop;
|
||||
bool have_tag = false;
|
||||
bool have_device = false;
|
||||
bool have_type = false;
|
||||
bool have_mode = false;
|
||||
|
||||
if ((prop = node.property ("tag")) != 0) {
|
||||
|
@ -233,23 +248,354 @@ Port::Descriptor::Descriptor (const XMLNode& node)
|
|||
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());
|
||||
|
||||
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_device || !have_type || !have_mode) {
|
||||
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&
|
||||
Port::get_state () const
|
||||
{
|
||||
XMLNode* root = new XMLNode ("MIDI-port");
|
||||
root->add_property ("tag", _tagname);
|
||||
|
||||
if (_mode == O_RDONLY) {
|
||||
root->add_property ("mode", "input");
|
||||
} else if (_mode == O_WRONLY) {
|
||||
root->add_property ("mode", "output");
|
||||
} else {
|
||||
root->add_property ("mode", "duplex");
|
||||
}
|
||||
|
||||
#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_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
|
||||
Port::set_state (const XMLNode& 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
|
||||
Port::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
|
||||
Port::set_process_thread (pthread_t thr)
|
||||
{
|
||||
_process_thread = thr;
|
||||
}
|
||||
|
||||
bool
|
||||
Port::is_process_thread()
|
||||
{
|
||||
return (pthread_self() == _process_thread);
|
||||
}
|
||||
|
||||
void
|
||||
Port::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
|
||||
Port::reconnect ()
|
||||
{
|
||||
make_connections ();
|
||||
}
|
||||
|
|
|
@ -45,11 +45,8 @@ def build(bld):
|
|||
# Library
|
||||
obj = bld.new_task_gen('cxx', 'shlib')
|
||||
obj.source = '''
|
||||
fd_midiport.cc
|
||||
fifomidi.cc
|
||||
midi.cc
|
||||
channel.cc
|
||||
factory.cc
|
||||
manager.cc
|
||||
parser.cc
|
||||
port.cc
|
||||
|
@ -59,21 +56,12 @@ def build(bld):
|
|||
version.cc
|
||||
'''
|
||||
# everybody loves JACK
|
||||
obj.source += ' jack_midiport.cc '
|
||||
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.includes = ['.', '../surfaces/control_protocol']
|
||||
obj.name = 'libmidipp'
|
||||
obj.target = 'midipp'
|
||||
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX COREAUDIO'
|
||||
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX'
|
||||
obj.uselib_local = 'libpbd libevoral'
|
||||
obj.vnum = LIBMIDIPP_LIB_VERSION
|
||||
obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')
|
||||
|
|
|
@ -563,20 +563,14 @@ MackieControlProtocol::connect_session_signals()
|
|||
void
|
||||
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);
|
||||
_ports.push_back (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->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
|
||||
}
|
||||
MackiePort * sport = new MackiePort (*this, midi_port, number);
|
||||
_ports.push_back (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->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -173,8 +173,6 @@ void SurfacePort::write_sysex( MIDI::byte msg )
|
|||
ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
|
||||
{
|
||||
os << "{ ";
|
||||
os << "device: " << port.port().device();
|
||||
os << "; ";
|
||||
os << "name: " << port.port().name();
|
||||
os << "; ";
|
||||
os << " }";
|
||||
|
|
Loading…
Reference in New Issue