mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-09 00:04:56 +01:00
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
28 changed files with 507 additions and 2174 deletions
|
|
@ -8,7 +8,6 @@
|
||||||
#include "pbd/cpus.h"
|
#include "pbd/cpus.h"
|
||||||
|
|
||||||
#include "midi++/manager.h"
|
#include "midi++/manager.h"
|
||||||
#include "midi++/factory.h"
|
|
||||||
|
|
||||||
#include "ardour/audioengine.h"
|
#include "ardour/audioengine.h"
|
||||||
#include "ardour/dB.h"
|
#include "ardour/dB.h"
|
||||||
|
|
@ -209,7 +208,6 @@ private:
|
||||||
|
|
||||||
node.add_property ("tag", dialog.port_name.get_text());
|
node.add_property ("tag", dialog.port_name.get_text());
|
||||||
node.add_property ("device", X_("ardour")); // XXX this can't be right for all types
|
node.add_property ("device", X_("ardour")); // XXX this can't be right for all types
|
||||||
node.add_property ("type", MIDI::PortFactory::default_port_type());
|
|
||||||
node.add_property ("mode", smod);
|
node.add_property ("mode", smod);
|
||||||
|
|
||||||
if (MIDI::Manager::instance()->add_port (node) != 0) {
|
if (MIDI::Manager::instance()->add_port (node) != 0) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "midi++/jack.h"
|
|
||||||
#include "pbd/signals.h"
|
#include "pbd/signals.h"
|
||||||
|
|
||||||
#include "ardour/types.h"
|
#include "ardour/types.h"
|
||||||
|
|
@ -29,6 +28,10 @@
|
||||||
#ifndef TICKER_H_
|
#ifndef TICKER_H_
|
||||||
#define TICKER_H_
|
#define TICKER_H_
|
||||||
|
|
||||||
|
namespace MIDI {
|
||||||
|
class Port;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ARDOUR
|
namespace ARDOUR
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
#include "pbd/stacktrace.h"
|
#include "pbd/stacktrace.h"
|
||||||
#include "pbd/unknown_type.h"
|
#include "pbd/unknown_type.h"
|
||||||
|
|
||||||
#include "midi++/jack.h"
|
#include "midi++/port.h"
|
||||||
#include "midi++/mmc.h"
|
#include "midi++/mmc.h"
|
||||||
#include "midi++/manager.h"
|
#include "midi++/manager.h"
|
||||||
|
|
||||||
|
|
@ -147,7 +147,7 @@ _thread_init_callback (void * /*arg*/)
|
||||||
|
|
||||||
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
|
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
|
||||||
|
|
||||||
MIDI::JACK_MidiPort::set_process_thread (pthread_self());
|
MIDI::Port::set_process_thread (pthread_self());
|
||||||
MIDI::MachineControl::set_sending_thread (pthread_self ());
|
MIDI::MachineControl::set_sending_thread (pthread_self ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,7 +263,7 @@ AudioEngine::stop (bool forever)
|
||||||
} else {
|
} else {
|
||||||
jack_deactivate (_priv_jack);
|
jack_deactivate (_priv_jack);
|
||||||
Stopped(); /* EMIT SIGNAL */
|
Stopped(); /* EMIT SIGNAL */
|
||||||
MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */
|
MIDI::Port::JackHalted (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1074,7 +1074,7 @@ AudioEngine::halted (void *arg)
|
||||||
|
|
||||||
if (was_running) {
|
if (was_running) {
|
||||||
ae->Halted(""); /* EMIT SIGNAL */
|
ae->Halted(""); /* EMIT SIGNAL */
|
||||||
MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */
|
MIDI::Port::JackHalted (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1358,7 +1358,7 @@ AudioEngine::disconnect_from_jack ()
|
||||||
if (_running) {
|
if (_running) {
|
||||||
_running = false;
|
_running = false;
|
||||||
Stopped(); /* EMIT SIGNAL */
|
Stopped(); /* EMIT SIGNAL */
|
||||||
MIDI::JACK_MidiPort::JackHalted (); /* EMIT SIGNAL */
|
MIDI::Port::JackHalted (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
#include "pbd/pthread_utils.h"
|
#include "pbd/pthread_utils.h"
|
||||||
|
|
||||||
#include "midi++/port.h"
|
#include "midi++/port.h"
|
||||||
#include "midi++/jack.h"
|
|
||||||
|
|
||||||
#include "ardour/debug.h"
|
#include "ardour/debug.h"
|
||||||
#include "ardour/slave.h"
|
#include "ardour/slave.h"
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,7 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
|
||||||
|
|
||||||
if (ioc & IO_IN) {
|
if (ioc & IO_IN) {
|
||||||
|
|
||||||
if (port->must_drain_selectable()) {
|
|
||||||
CrossThreadChannel::drain (port->selectable());
|
CrossThreadChannel::drain (port->selectable());
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
|
DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
|
||||||
nframes64_t now = _session.engine().frame_time();
|
nframes64_t now = _session.engine().frame_time();
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@
|
||||||
#include "ardour/utils.h"
|
#include "ardour/utils.h"
|
||||||
#include "ardour/graph.h"
|
#include "ardour/graph.h"
|
||||||
|
|
||||||
#include "midi++/jack.h"
|
#include "midi++/port.h"
|
||||||
#include "midi++/mmc.h"
|
#include "midi++/mmc.h"
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
@ -645,7 +645,7 @@ Session::hookup_io ()
|
||||||
/* Tell all IO objects to connect themselves together */
|
/* Tell all IO objects to connect themselves together */
|
||||||
|
|
||||||
IO::enable_connecting ();
|
IO::enable_connecting ();
|
||||||
MIDI::JACK_MidiPort::MakeConnections ();
|
MIDI::Port::MakeConnections ();
|
||||||
|
|
||||||
/* Now reset all panners */
|
/* Now reset all panners */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
$Id$
|
$Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "midi++/port.h"
|
||||||
|
#include "evoral/midi_events.h"
|
||||||
#include "ardour/ticker.h"
|
#include "ardour/ticker.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
#include "ardour/tempo.h"
|
#include "ardour/tempo.h"
|
||||||
|
|
@ -138,9 +140,6 @@ void MidiClockTicker::tick(const nframes_t& transport_frames, const BBT_Time& /*
|
||||||
if (!Config->get_send_midi_clock() || _session == 0 || _session->transport_speed() != 1.0f || _midi_port == 0)
|
if (!Config->get_send_midi_clock() || _session == 0 || _session->transport_speed() != 1.0f || _midi_port == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MIDI::JACK_MidiPort* jack_port = dynamic_cast<MIDI::JACK_MidiPort*>(_midi_port);
|
|
||||||
assert(jack_port);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
double next_tick = _last_tick + one_ppqn_in_frames(transport_frames);
|
double next_tick = _last_tick + one_ppqn_in_frames(transport_frames);
|
||||||
nframes_t next_tick_offset = nframes_t(next_tick) - transport_frames;
|
nframes_t next_tick_offset = nframes_t(next_tick) - transport_frames;
|
||||||
|
|
@ -150,11 +149,11 @@ void MidiClockTicker::tick(const nframes_t& transport_frames, const BBT_Time& /*
|
||||||
<< ":Last tick time:" << _last_tick << ":"
|
<< ":Last tick time:" << _last_tick << ":"
|
||||||
<< ":Next tick time:" << next_tick << ":"
|
<< ":Next tick time:" << next_tick << ":"
|
||||||
<< "Offset:" << next_tick_offset << ":"
|
<< "Offset:" << next_tick_offset << ":"
|
||||||
<< "cycle length:" << jack_port->nframes_this_cycle()
|
<< "cycle length:" << _midi_port->nframes_this_cycle()
|
||||||
<< endl;
|
<< endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (next_tick_offset >= jack_port->nframes_this_cycle())
|
if (next_tick_offset >= _midi_port->nframes_this_cycle())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
send_midi_clock_event(next_tick_offset);
|
send_midi_clock_event(next_tick_offset);
|
||||||
|
|
@ -183,9 +182,7 @@ void MidiClockTicker::send_midi_clock_event(nframes_t offset)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_JACK_MIDI
|
assert (MIDI::Port::is_process_thread());
|
||||||
assert (MIDI::JACK_MidiPort::is_process_thread());
|
|
||||||
#endif // WITH_JACK_MIDI
|
|
||||||
#ifdef DEBUG_MIDI_CLOCK
|
#ifdef DEBUG_MIDI_CLOCK
|
||||||
cerr << "Tick with offset " << offset << endl;
|
cerr << "Tick with offset " << offset << endl;
|
||||||
#endif // DEBUG_MIDI_CLOCK
|
#endif // DEBUG_MIDI_CLOCK
|
||||||
|
|
|
||||||
|
|
@ -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++/types.h"
|
||||||
#include "midi++/factory.h"
|
#include "midi++/factory.h"
|
||||||
#include "midi++/fifomidi.h"
|
|
||||||
|
|
||||||
#ifdef WITH_JACK_MIDI
|
|
||||||
#include "midi++/jack.h"
|
#include "midi++/jack.h"
|
||||||
|
|
||||||
std::string MIDI::JACK_MidiPort::typestring = "jack";
|
|
||||||
#endif // WITH_JACK_MIDI
|
|
||||||
|
|
||||||
std::string MIDI::FIFO_MidiPort::typestring = "fifo";
|
|
||||||
|
|
||||||
#ifdef WITH_ALSA
|
|
||||||
#include "midi++/alsa_sequencer.h"
|
|
||||||
#include "midi++/alsa_rawmidi.h"
|
|
||||||
|
|
||||||
std::string MIDI::ALSA_SequencerMidiPort::typestring = "alsa/sequencer";
|
|
||||||
std::string MIDI::ALSA_RawMidiPort::typestring = "alsa/raw";
|
|
||||||
|
|
||||||
#endif // WITH_ALSA
|
|
||||||
|
|
||||||
#ifdef WITH_COREMIDI
|
|
||||||
#include "midi++/coremidi_midiport.h"
|
|
||||||
|
|
||||||
std::string MIDI::CoreMidi_MidiPort::typestring = "coremidi";
|
|
||||||
|
|
||||||
#endif // WITH_COREMIDI
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace MIDI;
|
using namespace MIDI;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
|
|
||||||
// FIXME: void* data pointer, filthy
|
|
||||||
Port *
|
|
||||||
PortFactory::create_port (const XMLNode& node, void* data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Port::Descriptor desc (node);
|
|
||||||
Port *port;
|
|
||||||
|
|
||||||
switch (desc.type) {
|
|
||||||
#ifdef WITH_JACK_MIDI
|
|
||||||
case Port::JACK_Midi:
|
|
||||||
assert(data != NULL);
|
|
||||||
port = new JACK_MidiPort (node, (jack_client_t*) data);
|
|
||||||
break;
|
|
||||||
#endif // WITH_JACK_MIDI
|
|
||||||
|
|
||||||
#ifdef WITH_ALSA
|
|
||||||
case Port::ALSA_RawMidi:
|
|
||||||
port = new ALSA_RawMidiPort (node);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Port::ALSA_Sequencer:
|
|
||||||
port = new ALSA_SequencerMidiPort (node);
|
|
||||||
break;
|
|
||||||
#endif // WITH_ALSA
|
|
||||||
|
|
||||||
#ifdef WITH_COREMIDI
|
|
||||||
case Port::CoreMidi_MidiPort:
|
|
||||||
port = new CoreMidi_MidiPort (node);
|
|
||||||
break;
|
|
||||||
#endif // WITH_COREMIDI
|
|
||||||
|
|
||||||
case Port::FIFO:
|
|
||||||
port = new FIFO_MidiPort (node);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PortFactory::ignore_duplicate_devices (Port::Type type)
|
PortFactory::ignore_duplicate_devices (Port::Type type)
|
||||||
|
|
@ -104,23 +39,10 @@ PortFactory::ignore_duplicate_devices (Port::Type type)
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
#ifdef WITH_ALSA
|
|
||||||
case Port::ALSA_Sequencer:
|
|
||||||
ret = true;
|
|
||||||
break;
|
|
||||||
#endif // WITH_ALSA
|
|
||||||
|
|
||||||
#ifdef WITH_JACK_MIDI
|
|
||||||
case Port::JACK_Midi:
|
case Port::JACK_Midi:
|
||||||
ret = true;
|
ret = true;
|
||||||
break;
|
break;
|
||||||
#endif // WITH_JACK_MIDI
|
|
||||||
|
|
||||||
#ifdef WITH_COREMIDI
|
|
||||||
case Port::CoreMidi_MidiPort:
|
|
||||||
ret = true;
|
|
||||||
break;
|
|
||||||
#endif // WITH_COREMIDI
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -129,65 +51,17 @@ PortFactory::ignore_duplicate_devices (Port::Type type)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
#if defined (WITH_ALSA) || defined (WITH_COREMIDI)
|
|
||||||
PortFactory::get_known_ports (vector<PortSet>& ports)
|
|
||||||
#else
|
|
||||||
PortFactory::get_known_ports (vector<PortSet>&)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
int n = 0;
|
|
||||||
#ifdef WITH_ALSA
|
|
||||||
n += ALSA_SequencerMidiPort::discover (ports);
|
|
||||||
#endif // WITH_ALSA
|
|
||||||
|
|
||||||
#ifdef WITH_COREMIDI
|
|
||||||
n += CoreMidi_MidiPort::discover (ports);
|
|
||||||
#endif // WITH_COREMIDI
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
PortFactory::default_port_type ()
|
PortFactory::default_port_type ()
|
||||||
{
|
{
|
||||||
#ifdef WITH_JACK_MIDI
|
|
||||||
return "jack";
|
return "jack";
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_ALSA
|
|
||||||
return "alsa/sequencer";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_COREMIDI
|
|
||||||
return "coremidi";
|
|
||||||
#endif // WITH_COREMIDI
|
|
||||||
|
|
||||||
PBD::fatal << "programming error: no default port type defined in midifactory.cc" << endmsg;
|
|
||||||
/*NOTREACHED*/
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Port::Type
|
Port::Type
|
||||||
PortFactory::string_to_type (const string& xtype)
|
PortFactory::string_to_type (const string& xtype)
|
||||||
{
|
{
|
||||||
if (0){
|
if (strings_equal_ignore_case (xtype, JACK_MidiPort::typestring)) {
|
||||||
#ifdef WITH_ALSA
|
|
||||||
} else if (strings_equal_ignore_case (xtype, ALSA_RawMidiPort::typestring)) {
|
|
||||||
return Port::ALSA_RawMidi;
|
|
||||||
} else if (strings_equal_ignore_case (xtype, ALSA_SequencerMidiPort::typestring)) {
|
|
||||||
return Port::ALSA_Sequencer;
|
|
||||||
#endif
|
|
||||||
#ifdef WITH_COREMIDI
|
|
||||||
} else if (strings_equal_ignore_case (xtype, CoreMidi_MidiPort::typestring)) {
|
|
||||||
return Port::CoreMidi_MidiPort;
|
|
||||||
#endif
|
|
||||||
} else if (strings_equal_ignore_case (xtype, FIFO_MidiPort::typestring)) {
|
|
||||||
return Port::FIFO;
|
|
||||||
#ifdef WITH_JACK_MIDI
|
|
||||||
} else if (strings_equal_ignore_case (xtype, JACK_MidiPort::typestring)) {
|
|
||||||
return Port::JACK_Midi;
|
return Port::JACK_Midi;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Port::Unknown;
|
return Port::Unknown;
|
||||||
|
|
|
||||||
|
|
@ -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++/types.h"
|
||||||
#include "midi++/manager.h"
|
#include "midi++/manager.h"
|
||||||
#include "midi++/factory.h"
|
|
||||||
#include "midi++/channel.h"
|
#include "midi++/channel.h"
|
||||||
|
#include "midi++/port.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace MIDI;
|
using namespace MIDI;
|
||||||
|
|
@ -60,7 +60,6 @@ Port *
|
||||||
Manager::add_port (const XMLNode& node)
|
Manager::add_port (const XMLNode& node)
|
||||||
{
|
{
|
||||||
Port::Descriptor desc (node);
|
Port::Descriptor desc (node);
|
||||||
PortFactory factory;
|
|
||||||
Port *port;
|
Port *port;
|
||||||
PortList::iterator p;
|
PortList::iterator p;
|
||||||
|
|
||||||
|
|
@ -70,27 +69,13 @@ Manager::add_port (const XMLNode& node)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PortFactory::ignore_duplicate_devices (desc.type)) {
|
|
||||||
if (desc.device == (*p)->device()) {
|
|
||||||
/* If the existing is duplex, and this request
|
|
||||||
is not, then fail, because most drivers won't
|
|
||||||
allow opening twice with duplex and non-duplex
|
|
||||||
operation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ((desc.mode == O_RDWR && (*p)->mode() != O_RDWR) ||
|
|
||||||
(desc.mode != O_RDWR && (*p)->mode() == O_RDWR)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p != _ports.end()) {
|
if (p != _ports.end()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
port = factory.create_port (node, api_data);
|
port = new Port (node, (jack_client_t *) api_data);
|
||||||
|
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -228,13 +213,6 @@ Manager::cycle_end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
Manager::get_known_ports (vector<PortSet>& ports)
|
|
||||||
{
|
|
||||||
return PortFactory::get_known_ports (ports);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Re-register ports that disappear on JACK shutdown */
|
/** Re-register ports that disappear on JACK shutdown */
|
||||||
void
|
void
|
||||||
Manager::reestablish (void* a)
|
Manager::reestablish (void* a)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
return theManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_known_ports (std::vector<PortSet>&);
|
|
||||||
|
|
||||||
void reestablish (void *);
|
void reestablish (void *);
|
||||||
void reconnect ();
|
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
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
|
@ -22,7 +22,15 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <jack/types.h>
|
||||||
|
|
||||||
#include "pbd/xml++.h"
|
#include "pbd/xml++.h"
|
||||||
|
#include "pbd/crossthread.h"
|
||||||
|
#include "pbd/signals.h"
|
||||||
|
#include "pbd/ringbuffer.h"
|
||||||
|
|
||||||
|
#include "evoral/Event.hpp"
|
||||||
|
#include "evoral/EventRingBuffer.hpp"
|
||||||
|
|
||||||
#include "midi++/types.h"
|
#include "midi++/types.h"
|
||||||
#include "midi++/parser.h"
|
#include "midi++/parser.h"
|
||||||
|
|
@ -34,29 +42,18 @@ class PortRequest;
|
||||||
|
|
||||||
class Port {
|
class Port {
|
||||||
public:
|
public:
|
||||||
enum Type {
|
Port (const XMLNode&, jack_client_t *);
|
||||||
Unknown,
|
~Port ();
|
||||||
JACK_Midi,
|
|
||||||
ALSA_RawMidi,
|
|
||||||
ALSA_Sequencer,
|
|
||||||
CoreMidi_MidiPort,
|
|
||||||
Null,
|
|
||||||
FIFO
|
|
||||||
};
|
|
||||||
|
|
||||||
|
XMLNode& get_state () const;
|
||||||
Port (const XMLNode&);
|
void set_state (const XMLNode&);
|
||||||
virtual ~Port ();
|
|
||||||
|
|
||||||
virtual XMLNode& get_state () const;
|
|
||||||
virtual void set_state (const XMLNode&);
|
|
||||||
|
|
||||||
// FIXME: make Manager a friend of port so these can be hidden?
|
// FIXME: make Manager a friend of port so these can be hidden?
|
||||||
|
|
||||||
/* Only for use by MidiManager. Don't ever call this. */
|
/* Only for use by MidiManager. Don't ever call this. */
|
||||||
virtual void cycle_start(nframes_t nframes);
|
void cycle_start(nframes_t nframes);
|
||||||
/* Only for use by MidiManager. Don't ever call this. */
|
/* Only for use by MidiManager. Don't ever call this. */
|
||||||
virtual void cycle_end();
|
void cycle_end();
|
||||||
|
|
||||||
/** Write a message to port.
|
/** Write a message to port.
|
||||||
* @param msg Raw MIDI message to send
|
* @param msg Raw MIDI message to send
|
||||||
|
|
@ -64,14 +61,14 @@ class Port {
|
||||||
* @param timestamp Time stamp in frames of this message (relative to cycle start)
|
* @param timestamp Time stamp in frames of this message (relative to cycle start)
|
||||||
* @return number of bytes successfully written
|
* @return number of bytes successfully written
|
||||||
*/
|
*/
|
||||||
virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0;
|
int write (byte *msg, size_t msglen, timestamp_t timestamp);
|
||||||
|
|
||||||
/** Read raw bytes from a port.
|
/** Read raw bytes from a port.
|
||||||
* @param buf memory to store read data in
|
* @param buf memory to store read data in
|
||||||
* @param bufsize size of @a buf
|
* @param bufsize size of @a buf
|
||||||
* @return number of bytes successfully read, negative if error
|
* @return number of bytes successfully read, negative if error
|
||||||
*/
|
*/
|
||||||
virtual int read (byte *buf, size_t bufsize) = 0;
|
int read (byte *buf, size_t bufsize);
|
||||||
|
|
||||||
void parse (nframes_t timestamp);
|
void parse (nframes_t timestamp);
|
||||||
|
|
||||||
|
|
@ -83,38 +80,16 @@ class Port {
|
||||||
return !(write (msg, len, timestamp) == (int) len);
|
return !(write (msg, len, timestamp) == (int) len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int three_byte_msg (byte a, byte b, byte c, timestamp_t timestamp) {
|
|
||||||
byte msg[3];
|
|
||||||
|
|
||||||
msg[0] = a;
|
|
||||||
msg[1] = b;
|
|
||||||
msg[2] = c;
|
|
||||||
|
|
||||||
return !(write (msg, 3, timestamp) == 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool clock (timestamp_t timestamp);
|
bool clock (timestamp_t timestamp);
|
||||||
|
|
||||||
/* slowdown i/o to a loop of single byte emissions
|
|
||||||
interspersed with a busy loop of 10000 * this value.
|
|
||||||
|
|
||||||
This may be ignored by a particular instance
|
|
||||||
of this virtual class. See FD_MidiPort for an
|
|
||||||
example of where it used.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void set_slowdown (size_t n) { slowdown = n; }
|
|
||||||
|
|
||||||
/* select(2)/poll(2)-based I/O */
|
/* select(2)/poll(2)-based I/O */
|
||||||
|
|
||||||
/** Get the file descriptor for port.
|
/** Get the file descriptor for port.
|
||||||
* @return File descriptor, or -1 if not selectable.
|
* @return File descriptor, or -1 if not selectable.
|
||||||
*/
|
*/
|
||||||
virtual int selectable() const = 0;
|
int selectable () const {
|
||||||
virtual bool must_drain_selectable() const { return false; }
|
return xthread.selectable();
|
||||||
|
}
|
||||||
static void gtk_read_callback (void *ptr, int fd, int cond);
|
|
||||||
static void write_callback (byte *msg, unsigned int len, void *);
|
|
||||||
|
|
||||||
Channel *channel (channel_t chn) {
|
Channel *channel (channel_t chn) {
|
||||||
return _channel[chn&0x7F];
|
return _channel[chn&0x7F];
|
||||||
|
|
@ -123,63 +98,72 @@ class Port {
|
||||||
Parser *input() { return input_parser; }
|
Parser *input() { return input_parser; }
|
||||||
Parser *output() { return output_parser; }
|
Parser *output() { return output_parser; }
|
||||||
|
|
||||||
void iostat (int *written, int *read,
|
|
||||||
const size_t **in_counts,
|
|
||||||
const size_t **out_counts) {
|
|
||||||
|
|
||||||
*written = bytes_written;
|
|
||||||
*read = bytes_read;
|
|
||||||
if (input_parser) {
|
|
||||||
*in_counts = input_parser->message_counts();
|
|
||||||
} else {
|
|
||||||
*in_counts = 0;
|
|
||||||
}
|
|
||||||
if (output_parser) {
|
|
||||||
*out_counts = output_parser->message_counts();
|
|
||||||
} else {
|
|
||||||
*out_counts = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *device () const { return _devname.c_str(); }
|
|
||||||
const char *name () const { return _tagname.c_str(); }
|
const char *name () const { return _tagname.c_str(); }
|
||||||
Type type () const { return _type; }
|
|
||||||
int mode () const { return _mode; }
|
int mode () const { return _mode; }
|
||||||
bool ok () const { return _ok; }
|
bool ok () const { return _ok; }
|
||||||
|
|
||||||
struct Descriptor {
|
struct Descriptor {
|
||||||
std::string tag;
|
std::string tag;
|
||||||
std::string device;
|
|
||||||
int mode;
|
int mode;
|
||||||
Port::Type type;
|
|
||||||
|
|
||||||
Descriptor (const XMLNode&);
|
Descriptor (const XMLNode&);
|
||||||
XMLNode& get_state();
|
XMLNode& get_state();
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void reestablish (void *) {}
|
nframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
|
||||||
virtual void reconnect () {}
|
|
||||||
|
|
||||||
protected:
|
void reestablish (void *);
|
||||||
|
void reconnect ();
|
||||||
|
|
||||||
|
static void set_process_thread (pthread_t);
|
||||||
|
static pthread_t get_process_thread () { return _process_thread; }
|
||||||
|
static bool is_process_thread();
|
||||||
|
|
||||||
|
static PBD::Signal0<void> MakeConnections;
|
||||||
|
static PBD::Signal0<void> JackHalted;
|
||||||
|
|
||||||
|
private:
|
||||||
bool _ok;
|
bool _ok;
|
||||||
bool _currently_in_cycle;
|
bool _currently_in_cycle;
|
||||||
nframes_t _nframes_this_cycle;
|
nframes_t _nframes_this_cycle;
|
||||||
Type _type;
|
|
||||||
std::string _devname;
|
|
||||||
std::string _tagname;
|
std::string _tagname;
|
||||||
int _mode;
|
int _mode;
|
||||||
size_t _number;
|
size_t _number;
|
||||||
Channel *_channel[16];
|
Channel *_channel[16];
|
||||||
unsigned int bytes_written;
|
|
||||||
unsigned int bytes_read;
|
|
||||||
Parser *input_parser;
|
Parser *input_parser;
|
||||||
Parser *output_parser;
|
Parser *output_parser;
|
||||||
size_t slowdown;
|
|
||||||
|
|
||||||
virtual std::string get_typestring () const = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static size_t nports;
|
static size_t nports;
|
||||||
|
|
||||||
|
int create_ports(const XMLNode&);
|
||||||
|
int create_ports ();
|
||||||
|
|
||||||
|
jack_client_t* _jack_client;
|
||||||
|
std::string _jack_input_port_name; /// input port name, or empty if there isn't one
|
||||||
|
jack_port_t* _jack_input_port;
|
||||||
|
std::string _jack_output_port_name; /// output port name, or empty if there isn't one
|
||||||
|
jack_port_t* _jack_output_port;
|
||||||
|
nframes_t _last_read_index;
|
||||||
|
timestamp_t _last_write_timestamp;
|
||||||
|
|
||||||
|
/** Channel used to signal to the MidiControlUI that input has arrived */
|
||||||
|
CrossThreadChannel xthread;
|
||||||
|
|
||||||
|
std::string _inbound_connections;
|
||||||
|
std::string _outbound_connections;
|
||||||
|
PBD::ScopedConnection connect_connection;
|
||||||
|
PBD::ScopedConnection halt_connection;
|
||||||
|
void flush (void* jack_port_buffer);
|
||||||
|
void jack_halted ();
|
||||||
|
void make_connections();
|
||||||
|
|
||||||
|
static pthread_t _process_thread;
|
||||||
|
|
||||||
|
RingBuffer< Evoral::Event<double> > output_fifo;
|
||||||
|
Evoral::EventRingBuffer<timestamp_t> input_fifo;
|
||||||
|
|
||||||
|
Glib::Mutex output_fifo_lock;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PortSet {
|
struct PortSet {
|
||||||
|
|
|
||||||
|
|
@ -22,24 +22,37 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <jack/jack.h>
|
||||||
|
#include <jack/midiport.h>
|
||||||
|
|
||||||
#include "pbd/xml++.h"
|
#include "pbd/xml++.h"
|
||||||
#include "pbd/error.h"
|
#include "pbd/error.h"
|
||||||
#include "pbd/failed_constructor.h"
|
#include "pbd/failed_constructor.h"
|
||||||
|
#include "pbd/convert.h"
|
||||||
|
#include "pbd/strsplit.h"
|
||||||
|
|
||||||
#include "midi++/types.h"
|
#include "midi++/types.h"
|
||||||
#include "midi++/port.h"
|
#include "midi++/port.h"
|
||||||
#include "midi++/channel.h"
|
#include "midi++/channel.h"
|
||||||
#include "midi++/factory.h"
|
|
||||||
|
|
||||||
using namespace MIDI;
|
using namespace MIDI;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
|
|
||||||
size_t Port::nports = 0;
|
size_t Port::nports = 0;
|
||||||
|
pthread_t Port::_process_thread;
|
||||||
|
Signal0<void> Port::JackHalted;
|
||||||
|
Signal0<void> Port::MakeConnections;
|
||||||
|
|
||||||
Port::Port (const XMLNode& node)
|
Port::Port (const XMLNode& node, jack_client_t* jack_client)
|
||||||
: _currently_in_cycle(false)
|
: _currently_in_cycle (false)
|
||||||
, _nframes_this_cycle(0)
|
, _nframes_this_cycle (0)
|
||||||
|
, _jack_client (jack_client)
|
||||||
|
, _jack_input_port (0)
|
||||||
|
, _jack_output_port (0)
|
||||||
|
, _last_read_index (0)
|
||||||
|
, output_fifo (512)
|
||||||
|
, input_fifo (1024)
|
||||||
{
|
{
|
||||||
Descriptor desc (node);
|
Descriptor desc (node);
|
||||||
|
|
||||||
|
|
@ -47,13 +60,9 @@ Port::Port (const XMLNode& node)
|
||||||
succeeds.
|
succeeds.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bytes_written = 0;
|
|
||||||
bytes_read = 0;
|
|
||||||
input_parser = 0;
|
input_parser = 0;
|
||||||
output_parser = 0;
|
output_parser = 0;
|
||||||
slowdown = 0;
|
|
||||||
|
|
||||||
_devname = desc.device;
|
|
||||||
_tagname = desc.tag;
|
_tagname = desc.tag;
|
||||||
_mode = desc.mode;
|
_mode = desc.mode;
|
||||||
|
|
||||||
|
|
@ -80,6 +89,15 @@ Port::Port (const XMLNode& node)
|
||||||
_channel[i]->connect_output_signals ();
|
_channel[i]->connect_output_signals ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!create_ports (node)) {
|
||||||
|
_ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MakeConnections.connect_same_thread (connect_connection, boost::bind (&Port::make_connections, this));
|
||||||
|
JackHalted.connect_same_thread (halt_connection, boost::bind (&Port::jack_halted, this));
|
||||||
|
|
||||||
|
set_state (node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -88,6 +106,20 @@ Port::~Port ()
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
delete _channel[i];
|
delete _channel[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_jack_input_port) {
|
||||||
|
if (_jack_client) {
|
||||||
|
jack_port_unregister (_jack_client, _jack_input_port);
|
||||||
|
}
|
||||||
|
_jack_input_port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_jack_output_port) {
|
||||||
|
if (_jack_client) {
|
||||||
|
jack_port_unregister (_jack_client, _jack_input_port);
|
||||||
|
}
|
||||||
|
_jack_input_port = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -149,23 +181,292 @@ Port::cycle_start (nframes_t nframes)
|
||||||
{
|
{
|
||||||
_currently_in_cycle = true;
|
_currently_in_cycle = true;
|
||||||
_nframes_this_cycle = nframes;
|
_nframes_this_cycle = nframes;
|
||||||
|
|
||||||
|
assert(_nframes_this_cycle == nframes);
|
||||||
|
_last_read_index = 0;
|
||||||
|
_last_write_timestamp = 0;
|
||||||
|
|
||||||
|
if (_jack_output_port != 0) {
|
||||||
|
// output
|
||||||
|
void *buffer = jack_port_get_buffer (_jack_output_port, nframes);
|
||||||
|
jack_midi_clear_buffer (buffer);
|
||||||
|
flush (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_jack_input_port != 0) {
|
||||||
|
// input
|
||||||
|
void* jack_buffer = jack_port_get_buffer(_jack_input_port, nframes);
|
||||||
|
const nframes_t event_count = jack_midi_get_event_count(jack_buffer);
|
||||||
|
|
||||||
|
jack_midi_event_t ev;
|
||||||
|
timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
|
||||||
|
|
||||||
|
for (nframes_t i = 0; i < event_count; ++i) {
|
||||||
|
jack_midi_event_get (&ev, jack_buffer, i);
|
||||||
|
input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_count) {
|
||||||
|
xthread.wakeup ();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Port::cycle_end ()
|
Port::cycle_end ()
|
||||||
{
|
{
|
||||||
|
if (_jack_output_port != 0) {
|
||||||
|
flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
|
||||||
|
}
|
||||||
|
|
||||||
_currently_in_cycle = false;
|
_currently_in_cycle = false;
|
||||||
_nframes_this_cycle = 0;
|
_nframes_this_cycle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port )
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
os << "MIDI::Port { ";
|
||||||
|
os << "name: " << port.name();
|
||||||
|
os << "; ";
|
||||||
|
os << "mode: " << port.mode();
|
||||||
|
os << "; ";
|
||||||
|
os << "ok: " << port.ok();
|
||||||
|
os << "; ";
|
||||||
|
os << " }";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
Port::Descriptor::Descriptor (const XMLNode& node)
|
||||||
|
{
|
||||||
|
const XMLProperty *prop;
|
||||||
|
bool have_tag = false;
|
||||||
|
bool have_mode = false;
|
||||||
|
|
||||||
|
if ((prop = node.property ("tag")) != 0) {
|
||||||
|
tag = prop->value();
|
||||||
|
have_tag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((prop = node.property ("mode")) != 0) {
|
||||||
|
|
||||||
|
mode = O_RDWR;
|
||||||
|
|
||||||
|
if (strings_equal_ignore_case (prop->value(), "output") || strings_equal_ignore_case (prop->value(), "out")) {
|
||||||
|
mode = O_WRONLY;
|
||||||
|
} else if (strings_equal_ignore_case (prop->value(), "input") || strings_equal_ignore_case (prop->value(), "in")) {
|
||||||
|
mode = O_RDONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
have_mode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_tag || !have_mode) {
|
||||||
|
throw failed_constructor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Port::jack_halted ()
|
||||||
|
{
|
||||||
|
_jack_client = 0;
|
||||||
|
_jack_input_port = 0;
|
||||||
|
_jack_output_port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Port::write(byte * msg, size_t msglen, timestamp_t timestamp)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!_jack_output_port) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_process_thread()) {
|
||||||
|
|
||||||
|
Glib::Mutex::Lock lm (output_fifo_lock);
|
||||||
|
RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
|
||||||
|
|
||||||
|
output_fifo.get_write_vector (&vec);
|
||||||
|
|
||||||
|
if (vec.len[0] + vec.len[1] < 1) {
|
||||||
|
error << "no space in FIFO for non-process thread MIDI write" << endmsg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec.len[0]) {
|
||||||
|
if (!vec.buf[0]->owns_buffer()) {
|
||||||
|
vec.buf[0]->set_buffer (0, 0, true);
|
||||||
|
}
|
||||||
|
vec.buf[0]->set (msg, msglen, timestamp);
|
||||||
|
} else {
|
||||||
|
if (!vec.buf[1]->owns_buffer()) {
|
||||||
|
vec.buf[1]->set_buffer (0, 0, true);
|
||||||
|
}
|
||||||
|
vec.buf[1]->set (msg, msglen, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_fifo.increment_write_idx (1);
|
||||||
|
|
||||||
|
ret = msglen;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// XXX This had to be temporarily commented out to make export work again
|
||||||
|
if (!(timestamp < _nframes_this_cycle)) {
|
||||||
|
std::cerr << "assertion timestamp < _nframes_this_cycle failed!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currently_in_cycle) {
|
||||||
|
if (timestamp == 0) {
|
||||||
|
timestamp = _last_write_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jack_midi_event_write (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle),
|
||||||
|
timestamp, msg, msglen) == 0) {
|
||||||
|
ret = msglen;
|
||||||
|
_last_write_timestamp = timestamp;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
cerr << "write of " << msglen << " failed, port holds "
|
||||||
|
<< jack_midi_get_event_count (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle))
|
||||||
|
<< endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret > 0 && output_parser) {
|
||||||
|
// ardour doesn't care about this and neither should your app, probably
|
||||||
|
// output_parser->raw_preparse (*output_parser, msg, ret);
|
||||||
|
for (int i = 0; i < ret; i++) {
|
||||||
|
output_parser->scanner (msg[i]);
|
||||||
|
}
|
||||||
|
// ardour doesn't care about this and neither should your app, probably
|
||||||
|
// output_parser->raw_postparse (*output_parser, msg, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Port::flush (void* jack_port_buffer)
|
||||||
|
{
|
||||||
|
RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
|
||||||
|
size_t written;
|
||||||
|
|
||||||
|
output_fifo.get_read_vector (&vec);
|
||||||
|
|
||||||
|
if (vec.len[0] + vec.len[1]) {
|
||||||
|
// cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec.len[0]) {
|
||||||
|
Evoral::Event<double>* evp = vec.buf[0];
|
||||||
|
|
||||||
|
for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
|
||||||
|
jack_midi_event_write (jack_port_buffer,
|
||||||
|
(timestamp_t) evp->time(), evp->buffer(), evp->size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec.len[1]) {
|
||||||
|
Evoral::Event<double>* evp = vec.buf[1];
|
||||||
|
|
||||||
|
for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
|
||||||
|
jack_midi_event_write (jack_port_buffer,
|
||||||
|
(timestamp_t) evp->time(), evp->buffer(), evp->size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((written = vec.len[0] + vec.len[1]) != 0) {
|
||||||
|
output_fifo.increment_read_idx (written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Port::read (byte *, size_t)
|
||||||
|
{
|
||||||
|
timestamp_t time;
|
||||||
|
Evoral::EventType type;
|
||||||
|
uint32_t size;
|
||||||
|
byte buffer[input_fifo.capacity()];
|
||||||
|
|
||||||
|
while (input_fifo.read (&time, &type, &size, buffer)) {
|
||||||
|
if (input_parser) {
|
||||||
|
input_parser->set_timestamp (time);
|
||||||
|
for (uint32_t i = 0; i < size; ++i) {
|
||||||
|
input_parser->scanner (buffer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Port::create_ports(const XMLNode& node)
|
||||||
|
{
|
||||||
|
Descriptor desc (node);
|
||||||
|
|
||||||
|
assert(!_jack_input_port);
|
||||||
|
assert(!_jack_output_port);
|
||||||
|
|
||||||
|
if (desc.mode == O_RDWR || desc.mode == O_WRONLY) {
|
||||||
|
_jack_output_port_name = string(desc.tag).append ("_out");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.mode == O_RDWR || desc.mode == O_RDONLY) {
|
||||||
|
_jack_input_port_name = string(desc.tag).append ("_in");
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ports ();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Port::create_ports ()
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
jack_nframes_t nframes = jack_get_buffer_size(_jack_client);
|
||||||
|
|
||||||
|
if (!_jack_output_port_name.empty()) {
|
||||||
|
_jack_output_port = jack_port_register(_jack_client, _jack_output_port_name.c_str(),
|
||||||
|
JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
|
||||||
|
if (_jack_output_port) {
|
||||||
|
jack_midi_clear_buffer(jack_port_get_buffer(_jack_output_port, nframes));
|
||||||
|
}
|
||||||
|
ret = ret && (_jack_output_port != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_jack_input_port_name.empty()) {
|
||||||
|
_jack_input_port = jack_port_register(_jack_client, _jack_input_port_name.c_str(),
|
||||||
|
JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
|
||||||
|
if (_jack_input_port) {
|
||||||
|
jack_midi_clear_buffer(jack_port_get_buffer(_jack_input_port, nframes));
|
||||||
|
}
|
||||||
|
ret = ret && (_jack_input_port != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
XMLNode&
|
XMLNode&
|
||||||
Port::get_state () const
|
Port::get_state () const
|
||||||
{
|
{
|
||||||
XMLNode* node = new XMLNode ("MIDI-port");
|
XMLNode* root = new XMLNode ("MIDI-port");
|
||||||
node->add_property ("tag", _tagname);
|
root->add_property ("tag", _tagname);
|
||||||
node->add_property ("device", _devname);
|
|
||||||
node->add_property ("mode", PortFactory::mode_to_string (_mode));
|
if (_mode == O_RDONLY) {
|
||||||
node->add_property ("type", get_typestring());
|
root->add_property ("mode", "input");
|
||||||
|
} else if (_mode == O_WRONLY) {
|
||||||
|
root->add_property ("mode", "output");
|
||||||
|
} else {
|
||||||
|
root->add_property ("mode", "duplex");
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
byte device_inquiry[6];
|
byte device_inquiry[6];
|
||||||
|
|
@ -180,76 +481,121 @@ Port::get_state () const
|
||||||
write (device_inquiry, sizeof (device_inquiry), 0);
|
write (device_inquiry, sizeof (device_inquiry), 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return *node;
|
if (_jack_output_port) {
|
||||||
|
|
||||||
|
const char** jc = jack_port_get_connections (_jack_output_port);
|
||||||
|
string connection_string;
|
||||||
|
if (jc) {
|
||||||
|
for (int i = 0; jc[i]; ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
connection_string += ',';
|
||||||
|
}
|
||||||
|
connection_string += jc[i];
|
||||||
|
}
|
||||||
|
free (jc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connection_string.empty()) {
|
||||||
|
root->add_property ("outbound", connection_string);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_outbound_connections.empty()) {
|
||||||
|
root->add_property ("outbound", _outbound_connections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_jack_input_port) {
|
||||||
|
|
||||||
|
const char** jc = jack_port_get_connections (_jack_input_port);
|
||||||
|
string connection_string;
|
||||||
|
if (jc) {
|
||||||
|
for (int i = 0; jc[i]; ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
connection_string += ',';
|
||||||
|
}
|
||||||
|
connection_string += jc[i];
|
||||||
|
}
|
||||||
|
free (jc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connection_string.empty()) {
|
||||||
|
root->add_property ("inbound", connection_string);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_inbound_connections.empty()) {
|
||||||
|
root->add_property ("inbound", _inbound_connections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *root;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Port::set_state (const XMLNode& /*node*/)
|
Port::set_state (const XMLNode& node)
|
||||||
{
|
{
|
||||||
// relax
|
const XMLProperty* prop;
|
||||||
|
|
||||||
|
if ((prop = node.property ("inbound")) != 0 && _jack_input_port) {
|
||||||
|
_inbound_connections = prop->value ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((prop = node.property ("outbound")) != 0 && _jack_output_port) {
|
||||||
|
_outbound_connections = prop->value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Port::gtk_read_callback (void *ptr, int /*fd*/, int /*cond*/)
|
Port::make_connections ()
|
||||||
{
|
{
|
||||||
byte buf[64];
|
if (!_inbound_connections.empty()) {
|
||||||
((Port *)ptr)->read (buf, sizeof (buf));
|
vector<string> ports;
|
||||||
|
split (_inbound_connections, ports, ',');
|
||||||
|
for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
|
||||||
|
if (_jack_client) {
|
||||||
|
jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_input_port));
|
||||||
|
/* ignore failures */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_outbound_connections.empty()) {
|
||||||
|
vector<string> ports;
|
||||||
|
split (_outbound_connections, ports, ',');
|
||||||
|
for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
|
||||||
|
if (_jack_client) {
|
||||||
|
jack_connect (_jack_client, jack_port_name (_jack_output_port), (*x).c_str());
|
||||||
|
/* ignore failures */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connect_connection.disconnect ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Port::write_callback (byte *msg, unsigned int len, void *ptr)
|
Port::set_process_thread (pthread_t thr)
|
||||||
{
|
{
|
||||||
((Port *)ptr)->write (msg, len, 0);
|
_process_thread = thr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port )
|
bool
|
||||||
|
Port::is_process_thread()
|
||||||
{
|
{
|
||||||
using namespace std;
|
return (pthread_self() == _process_thread);
|
||||||
os << "MIDI::Port { ";
|
|
||||||
os << "device: " << port.device();
|
|
||||||
os << "; ";
|
|
||||||
os << "name: " << port.name();
|
|
||||||
os << "; ";
|
|
||||||
os << "type: " << port.type();
|
|
||||||
os << "; ";
|
|
||||||
os << "mode: " << port.mode();
|
|
||||||
os << "; ";
|
|
||||||
os << "ok: " << port.ok();
|
|
||||||
os << "; ";
|
|
||||||
os << " }";
|
|
||||||
return os;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Port::Descriptor::Descriptor (const XMLNode& node)
|
void
|
||||||
|
Port::reestablish (void* jack)
|
||||||
{
|
{
|
||||||
const XMLProperty *prop;
|
_jack_client = static_cast<jack_client_t*> (jack);
|
||||||
bool have_tag = false;
|
int const r = create_ports ();
|
||||||
bool have_device = false;
|
|
||||||
bool have_type = false;
|
|
||||||
bool have_mode = false;
|
|
||||||
|
|
||||||
if ((prop = node.property ("tag")) != 0) {
|
if (r) {
|
||||||
tag = prop->value();
|
PBD::error << "could not reregister ports for " << name() << endmsg;
|
||||||
have_tag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((prop = node.property ("device")) != 0) {
|
|
||||||
device = prop->value();
|
|
||||||
have_device = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((prop = node.property ("type")) != 0) {
|
|
||||||
type = PortFactory::string_to_type (prop->value());
|
|
||||||
have_type = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((prop = node.property ("mode")) != 0) {
|
|
||||||
mode = PortFactory::string_to_mode (prop->value());
|
|
||||||
have_mode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!have_tag || !have_device || !have_type || !have_mode) {
|
|
||||||
throw failed_constructor();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Port::reconnect ()
|
||||||
|
{
|
||||||
|
make_connections ();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,8 @@ def build(bld):
|
||||||
# Library
|
# Library
|
||||||
obj = bld.new_task_gen('cxx', 'shlib')
|
obj = bld.new_task_gen('cxx', 'shlib')
|
||||||
obj.source = '''
|
obj.source = '''
|
||||||
fd_midiport.cc
|
|
||||||
fifomidi.cc
|
|
||||||
midi.cc
|
midi.cc
|
||||||
channel.cc
|
channel.cc
|
||||||
factory.cc
|
|
||||||
manager.cc
|
manager.cc
|
||||||
parser.cc
|
parser.cc
|
||||||
port.cc
|
port.cc
|
||||||
|
|
@ -59,21 +56,12 @@ def build(bld):
|
||||||
version.cc
|
version.cc
|
||||||
'''
|
'''
|
||||||
# everybody loves JACK
|
# everybody loves JACK
|
||||||
obj.source += ' jack_midiport.cc '
|
|
||||||
obj.cxxflags = [ '-DWITH_JACK_MIDI' ]
|
obj.cxxflags = [ '-DWITH_JACK_MIDI' ]
|
||||||
if bld.env['HAVE_COREAUDIO'] and bld.env['COREAUDIO']:
|
|
||||||
# OS X
|
|
||||||
obj.source += ' coremidi_midiport.cc '
|
|
||||||
obj.cxxflags += [ '-DWITH_COREMIDI' ]
|
|
||||||
elif sys.platform == 'linux':
|
|
||||||
# linux
|
|
||||||
obj.source += ' alsa_sequencer_midiport.cc '
|
|
||||||
obj.cxxflags += [ '-DWITH_ALSA' ]
|
|
||||||
obj.export_incdirs = ['.']
|
obj.export_incdirs = ['.']
|
||||||
obj.includes = ['.', '../surfaces/control_protocol']
|
obj.includes = ['.', '../surfaces/control_protocol']
|
||||||
obj.name = 'libmidipp'
|
obj.name = 'libmidipp'
|
||||||
obj.target = 'midipp'
|
obj.target = 'midipp'
|
||||||
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX COREAUDIO'
|
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX'
|
||||||
obj.uselib_local = 'libpbd libevoral'
|
obj.uselib_local = 'libpbd libevoral'
|
||||||
obj.vnum = LIBMIDIPP_LIB_VERSION
|
obj.vnum = LIBMIDIPP_LIB_VERSION
|
||||||
obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')
|
obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')
|
||||||
|
|
|
||||||
|
|
@ -563,20 +563,14 @@ MackieControlProtocol::connect_session_signals()
|
||||||
void
|
void
|
||||||
MackieControlProtocol::add_port (MIDI::Port & midi_port, int number)
|
MackieControlProtocol::add_port (MIDI::Port & midi_port, int number)
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1,%2,%3\n", midi_port.name(), midi_port.device(), midi_port.type()));
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1\n", midi_port.name()));
|
||||||
|
|
||||||
if (string (midi_port.device()) == string ("ardour") && midi_port.type() == MIDI::Port::ALSA_Sequencer) {
|
|
||||||
throw MackieControlException ("The Mackie MCU driver will not use a port with device=ardour");
|
|
||||||
} else if (midi_port.type() == MIDI::Port::ALSA_Sequencer) {
|
|
||||||
throw MackieControlException ("alsa/sequencer ports don't work with the Mackie MCU driver right now");
|
|
||||||
} else {
|
|
||||||
MackiePort * sport = new MackiePort (*this, midi_port, number);
|
MackiePort * sport = new MackiePort (*this, midi_port, number);
|
||||||
_ports.push_back (sport);
|
_ports.push_back (sport);
|
||||||
|
|
||||||
sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
|
sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
|
||||||
sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
|
sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
|
||||||
sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
|
sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -173,8 +173,6 @@ void SurfacePort::write_sysex( MIDI::byte msg )
|
||||||
ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
|
ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
|
||||||
{
|
{
|
||||||
os << "{ ";
|
os << "{ ";
|
||||||
os << "device: " << port.port().device();
|
|
||||||
os << "; ";
|
|
||||||
os << "name: " << port.port().name();
|
os << "name: " << port.port().name();
|
||||||
os << "; ";
|
os << "; ";
|
||||||
os << " }";
|
os << " }";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue