libmidi++: split apart "base-y" aspects of MIDI::Port into MIDI::PortBase and make MIDI::Port derive from it. This actually makes MIDI::Port effectively into MIDI::JackPort, but i'm not interested in the name changing at that level at this moment in time

git-svn-id: svn://localhost/ardour2/branches/3.0@12064 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2012-04-23 15:29:45 +00:00
parent 4bfdcc18bd
commit 1660f00ff3
9 changed files with 398 additions and 203 deletions

View file

@ -25,7 +25,8 @@
using namespace MIDI; using namespace MIDI;
Channel::Channel (byte channelnum, Port &p) : _port (p) Channel::Channel (byte channelnum, PortBase &p)
: _port (p)
{ {
_channel_number = channelnum; _channel_number = channelnum;

View file

@ -39,9 +39,9 @@ class Port;
class Channel : public PBD::ScopedConnectionList { class Channel : public PBD::ScopedConnectionList {
public: public:
Channel (byte channel_number, Port &); Channel (byte channel_number, PortBase &);
Port &midi_port() { return _port; } PortBase &midi_port() { return _port; }
byte channel() { return _channel_number; } byte channel() { return _channel_number; }
byte program() { return _program_number; } byte program() { return _program_number; }
byte bank() { return _bank_number; } byte bank() { return _bank_number; }
@ -111,11 +111,11 @@ class Channel : public PBD::ScopedConnectionList {
} }
protected: protected:
friend class Port; friend class PortBase;
void connect_signals (); void connect_signals ();
private: private:
Port & _port; PortBase& _port;
/* Current channel values */ /* Current channel values */
byte _channel_number; byte _channel_number;

View file

@ -29,7 +29,7 @@
namespace MIDI { namespace MIDI {
class Port; class PortBase;
class Parser; class Parser;
typedef PBD::Signal1<void,Parser&> ZeroByteSignal; typedef PBD::Signal1<void,Parser&> ZeroByteSignal;
@ -41,7 +41,7 @@ typedef PBD::Signal3<void,Parser &, byte *, size_t> Signal;
class Parser { class Parser {
public: public:
Parser (Port &p); Parser (PortBase &p);
~Parser (); ~Parser ();
/* sets the time that will be reported for any MTC or MIDI Clock /* sets the time that will be reported for any MTC or MIDI Clock
@ -105,7 +105,7 @@ class Parser {
const char *midi_event_type_name (MIDI::eventType); const char *midi_event_type_name (MIDI::eventType);
void trace (bool onoff, std::ostream *o, const std::string &prefix = ""); void trace (bool onoff, std::ostream *o, const std::string &prefix = "");
bool tracing() { return trace_stream != 0; } bool tracing() { return trace_stream != 0; }
Port &port() { return _port; } PortBase &port() { return _port; }
void set_offline (bool); void set_offline (bool);
bool offline() const { return _offline; } bool offline() const { return _offline; }
@ -136,9 +136,9 @@ class Parser {
void reset_mtc_state (); void reset_mtc_state ();
private: private:
Port &_port; PortBase&_port;
/* tracing */ /* tracing */
std::ostream *trace_stream; std::ostream *trace_stream;
std::string trace_prefix; std::string trace_prefix;
void trace_event (Parser &p, byte *msg, size_t len); void trace_event (Parser &p, byte *msg, size_t len);

View file

@ -34,98 +34,30 @@
#include "midi++/types.h" #include "midi++/types.h"
#include "midi++/parser.h" #include "midi++/parser.h"
#include "midi++/port_base.h"
namespace MIDI { namespace MIDI {
class Channel; class Channel;
class PortRequest; class PortRequest;
class Port { class Port : public PortBase {
public: public:
enum Flags { Port (std::string const &, PortBase::Flags, jack_client_t *);
IsInput = JackPortIsInput,
IsOutput = JackPortIsOutput,
};
Port (std::string const &, Flags, jack_client_t *);
Port (const XMLNode&, jack_client_t *); Port (const XMLNode&, jack_client_t *);
~Port (); ~Port ();
XMLNode& get_state () const; XMLNode& get_state () const;
void set_state (const XMLNode&); void set_state (const XMLNode&);
// FIXME: make Manager a friend of port so these can be hidden?
/* Only for use by MidiManager. Don't ever call this. */
void cycle_start (pframes_t nframes); void cycle_start (pframes_t nframes);
/* Only for use by MidiManager. Don't ever call this. */
void cycle_end (); void cycle_end ();
/** Write a message to port.
* @param msg Raw MIDI message to send
* @param msglen Size of @a msg
* @param timestamp Time stamp in frames of this message (relative to cycle start)
* @return number of bytes successfully written
*/
int write (byte *msg, size_t msglen, timestamp_t timestamp);
/** Read raw bytes from a port.
* @param buf memory to store read data in
* @param bufsize size of @a buf
* @return number of bytes successfully read, negative if error
*/
int read (byte *buf, size_t bufsize);
void parse (framecnt_t timestamp); void parse (framecnt_t timestamp);
int write (byte *msg, size_t msglen, timestamp_t timestamp);
/** Write a message to port. int read (byte *buf, size_t bufsize);
* @return true on success. void drain (int check_interval_usecs);
* FIXME: describe semantics here int selectable () const { return xthread.selectable(); }
*/
int midimsg (byte *msg, size_t len, timestamp_t timestamp) {
return !(write (msg, len, timestamp) == (int) len);
}
bool clock (timestamp_t timestamp);
/* select(2)/poll(2)-based I/O */
/** Get the file descriptor for port.
* @return File descriptor, or -1 if not selectable.
*/
int selectable () const {
return xthread.selectable();
}
Channel *channel (channel_t chn) {
return _channel[chn&0x7F];
}
Parser* parser () {
return _parser;
}
const char *name () const { return _tagname.c_str(); }
bool ok () const { return _ok; }
bool centrally_parsed() const { return _centrally_parsed; }
void set_centrally_parsed(bool yn) { _centrally_parsed = yn; }
bool receives_input () const {
return _flags == IsInput;
}
bool sends_output () const {
return _flags == IsOutput;
}
struct Descriptor {
std::string tag;
Flags flags;
Descriptor (const XMLNode&);
XMLNode& get_state();
};
pframes_t nframes_this_cycle() const { return _nframes_this_cycle; } pframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
@ -136,30 +68,19 @@ class Port {
static pthread_t get_process_thread () { return _process_thread; } static pthread_t get_process_thread () { return _process_thread; }
static bool is_process_thread(); static bool is_process_thread();
static std::string state_node_name;
static PBD::Signal0<void> MakeConnections; static PBD::Signal0<void> MakeConnections;
static PBD::Signal0<void> JackHalted; static PBD::Signal0<void> JackHalted;
private: private:
bool _ok;
bool _currently_in_cycle; bool _currently_in_cycle;
pframes_t _nframes_this_cycle; pframes_t _nframes_this_cycle;
std::string _tagname;
size_t _number;
Channel* _channel[16];
Parser* _parser;
jack_client_t* _jack_client; jack_client_t* _jack_client;
jack_port_t* _jack_port; jack_port_t* _jack_port;
framecnt_t _last_read_index;
timestamp_t _last_write_timestamp; timestamp_t _last_write_timestamp;
RingBuffer< Evoral::Event<double> > output_fifo; RingBuffer< Evoral::Event<double> > output_fifo;
Evoral::EventRingBuffer<timestamp_t> input_fifo; Evoral::EventRingBuffer<timestamp_t> input_fifo;
Glib::Mutex output_fifo_lock; Glib::Mutex output_fifo_lock;
CrossThreadChannel xthread; CrossThreadChannel xthread;
Flags _flags;
bool _centrally_parsed;
int create_port (); int create_port ();
@ -177,15 +98,6 @@ private:
}; };
struct PortSet {
PortSet (std::string str) : owner (str) { }
std::string owner;
std::list<XMLNode> ports;
};
std::ostream & operator << ( std::ostream & os, const Port & port );
} // namespace MIDI } // namespace MIDI
#endif // __libmidi_port_h__ #endif // __libmidi_port_h__

View file

@ -0,0 +1,158 @@
/*
Copyright (C) 1998-2010 Paul Barton-Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __libmidi_port_base_h__
#define __libmidi_port_base_h__
#include <string>
#include <iostream>
#include <jack/types.h>
#include "pbd/xml++.h"
#include "pbd/crossthread.h"
#include "pbd/signals.h"
#include "pbd/ringbuffer.h"
#include "evoral/Event.hpp"
#include "evoral/EventRingBuffer.hpp"
#include "midi++/types.h"
#include "midi++/parser.h"
namespace MIDI {
class Channel;
class PortRequest;
class PortBase {
public:
enum Flags {
IsInput = JackPortIsInput,
IsOutput = JackPortIsOutput,
};
PortBase (std::string const &, Flags);
PortBase (const XMLNode&);
virtual ~PortBase ();
XMLNode& get_state () const;
void set_state (const XMLNode&);
// FIXME: make Manager a friend of port so these can be hidden?
/* Only for use by MidiManager. Don't ever call this. */
virtual void cycle_start (pframes_t nframes) {}
/* Only for use by MidiManager. Don't ever call this. */
virtual void cycle_end () {}
/** Write a message to port.
* @param msg Raw MIDI message to send
* @param msglen Size of @a msg
* @param timestamp Time stamp in frames of this message (relative to cycle start)
* @return number of bytes successfully written
*/
virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0;
/** Read raw bytes from a port.
* @param buf memory to store read data in
* @param bufsize size of @a buf
* @return number of bytes successfully read, negative if error
*/
virtual int read (byte *buf, size_t bufsize) = 0;
/** block until the output FIFO used by non-process threads
* is empty, checking every @a check_interval_usecs usecs
* for current status. Not to be called by a thread that
* executes any part of a JACK process callback (will
* simply return immediately in that situation).
*/
virtual void drain (int check_interval_usecs) {}
/** Write a message to port.
* @return true on success.
* FIXME: describe semantics here
*/
int midimsg (byte *msg, size_t len, timestamp_t timestamp) {
return !(write (msg, len, timestamp) == (int) len);
}
bool clock (timestamp_t timestamp);
/* select(2)/poll(2)-based I/O */
/** Get the file descriptor for port.
* @return File descriptor, or -1 if not selectable.
*/
virtual int selectable () const = 0;
Channel *channel (channel_t chn) {
return _channel[chn&0x7F];
}
Parser* parser () {
return _parser;
}
const char *name () const { return _tagname.c_str(); }
bool ok () const { return _ok; }
virtual bool centrally_parsed() const;
void set_centrally_parsed (bool yn) { _centrally_parsed = yn; }
bool receives_input () const {
return _flags == IsInput;
}
bool sends_output () const {
return _flags == IsOutput;
}
struct Descriptor {
std::string tag;
Flags flags;
Descriptor (const XMLNode&);
XMLNode& get_state();
};
static std::string state_node_name;
protected:
bool _ok;
std::string _tagname;
Channel* _channel[16];
Parser* _parser;
Flags _flags;
bool _centrally_parsed;
virtual void init (std::string const &, Flags);
};
struct PortSet {
PortSet (std::string str) : owner (str) { }
std::string owner;
std::list<XMLNode> ports;
};
std::ostream & operator << (std::ostream& os, const PortBase& port);
} // namespace MIDI
#endif // __libmidi_port_base_h__

View file

@ -30,7 +30,7 @@
#include "midi++/types.h" #include "midi++/types.h"
#include "midi++/parser.h" #include "midi++/parser.h"
#include "midi++/port.h" #include "midi++/port_base.h"
#include "midi++/mmc.h" #include "midi++/mmc.h"
#include "pbd/transmitter.h" #include "pbd/transmitter.h"
@ -104,9 +104,8 @@ Parser::midi_event_type_name (eventType t)
} }
} }
Parser::Parser (Port &p) Parser::Parser (PortBase &p)
: _port (p) : _port(p)
{ {
trace_stream = 0; trace_stream = 0;
trace_prefix = ""; trace_prefix = "";

View file

@ -43,34 +43,30 @@ using namespace PBD;
pthread_t Port::_process_thread; pthread_t Port::_process_thread;
Signal0<void> Port::JackHalted; Signal0<void> Port::JackHalted;
Signal0<void> Port::MakeConnections; Signal0<void> Port::MakeConnections;
string Port::state_node_name = "MIDI-port";
Port::Port (string const & name, Flags flags, jack_client_t* jack_client) Port::Port (string const & name, Flags flags, jack_client_t* jack_client)
: _currently_in_cycle (false) : PortBase (name, flags)
, _currently_in_cycle (false)
, _nframes_this_cycle (0) , _nframes_this_cycle (0)
, _jack_client (jack_client) , _jack_client (jack_client)
, _jack_port (0) , _jack_port (0)
, _last_read_index (0)
, output_fifo (512) , output_fifo (512)
, input_fifo (1024) , input_fifo (1024)
, xthread (true) , xthread (true)
, _flags (flags)
, _centrally_parsed (true)
{ {
assert (jack_client); assert (jack_client);
init (name, flags); init (name, flags);
} }
Port::Port (const XMLNode& node, jack_client_t* jack_client) Port::Port (const XMLNode& node, jack_client_t* jack_client)
: _currently_in_cycle (false) : PortBase (node)
, _currently_in_cycle (false)
, _nframes_this_cycle (0) , _nframes_this_cycle (0)
, _jack_client (jack_client) , _jack_client (jack_client)
, _jack_port (0) , _jack_port (0)
, _last_read_index (0)
, output_fifo (512) , output_fifo (512)
, input_fifo (1024) , input_fifo (1024)
, xthread (true) , xthread (true)
, _centrally_parsed (true)
{ {
assert (jack_client); assert (jack_client);
@ -84,21 +80,7 @@ Port::Port (const XMLNode& node, jack_client_t* jack_client)
void void
Port::init (string const & name, Flags flags) Port::init (string const & name, Flags flags)
{ {
_ok = false; /* derived class must set to true if constructor PortBase::init (name, flags);
succeeds.
*/
_parser = 0;
_tagname = name;
_flags = flags;
_parser = new Parser (*this);
for (int i = 0; i < 16; i++) {
_channel[i] = new Channel (i, *this);
_channel[i]->connect_signals ();
}
if (!create_port ()) { if (!create_port ()) {
_ok = true; _ok = true;
@ -160,21 +142,6 @@ Port::parse (framecnt_t timestamp)
} }
} }
/** Send a clock tick message.
* \return true on success.
*/
bool
Port::clock (timestamp_t timestamp)
{
static byte clockmsg = 0xf8;
if (sends_output()) {
return midimsg (&clockmsg, 1, timestamp);
}
return false;
}
void void
Port::cycle_start (pframes_t nframes) Port::cycle_start (pframes_t nframes)
{ {
@ -184,8 +151,6 @@ Port::cycle_start (pframes_t nframes)
_nframes_this_cycle = nframes; _nframes_this_cycle = nframes;
assert(_nframes_this_cycle == nframes); assert(_nframes_this_cycle == nframes);
_last_read_index = 0;
_last_write_timestamp = 0;
if (sends_output()) { if (sends_output()) {
void *buffer = jack_port_get_buffer (_jack_port, nframes); void *buffer = jack_port_get_buffer (_jack_port, nframes);
@ -222,45 +187,6 @@ Port::cycle_end ()
_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 << "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) {
if (strings_equal_ignore_case (prop->value(), "output") || strings_equal_ignore_case (prop->value(), "out")) {
flags = IsOutput;
} else if (strings_equal_ignore_case (prop->value(), "input") || strings_equal_ignore_case (prop->value(), "in")) {
flags = IsInput;
}
have_mode = true;
}
if (!have_tag || !have_mode) {
throw failed_constructor();
}
}
void void
Port::jack_halted () Port::jack_halted ()
{ {
@ -268,6 +194,25 @@ Port::jack_halted ()
_jack_port = 0; _jack_port = 0;
} }
void
Port::drain (int check_interval_usecs)
{
RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
if (is_process_thread()) {
error << "Process thread called MIDI::Port::drain() - this cannot work" << endmsg;
return;
}
while (1) {
output_fifo.get_write_vector (&vec);
if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
break;
}
usleep (check_interval_usecs);
}
}
int int
Port::write(byte * msg, size_t msglen, timestamp_t timestamp) Port::write(byte * msg, size_t msglen, timestamp_t timestamp)
{ {
@ -305,6 +250,8 @@ Port::write(byte * msg, size_t msglen, timestamp_t timestamp)
ret = msglen; ret = msglen;
usleep (5000);
} else { } else {
// XXX This had to be temporarily commented out to make export work again // XXX This had to be temporarily commented out to make export work again
@ -359,7 +306,7 @@ Port::flush (void* jack_port_buffer)
output_fifo.get_read_vector (&vec); output_fifo.get_read_vector (&vec);
if (vec.len[0] + vec.len[1]) { if (vec.len[0] + vec.len[1]) {
// cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n"; cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
} }
if (vec.len[0]) { if (vec.len[0]) {
@ -417,14 +364,7 @@ Port::create_port ()
XMLNode& XMLNode&
Port::get_state () const Port::get_state () const
{ {
XMLNode* root = new XMLNode (state_node_name); XMLNode& root = PortBase::get_state ();
root->add_property ("tag", _tagname);
if (_flags == IsInput) {
root->add_property ("mode", "input");
} else {
root->add_property ("mode", "output");
}
#if 0 #if 0
byte device_inquiry[6]; byte device_inquiry[6];
@ -454,15 +394,15 @@ Port::get_state () const
} }
if (!connection_string.empty()) { if (!connection_string.empty()) {
root->add_property ("connections", connection_string); root.add_property ("connections", connection_string);
} }
} else { } else {
if (!_connections.empty()) { if (!_connections.empty()) {
root->add_property ("connections", _connections); root.add_property ("connections", _connections);
} }
} }
return *root; return root;
} }
void void
@ -470,9 +410,7 @@ Port::set_state (const XMLNode& node)
{ {
const XMLProperty* prop; const XMLProperty* prop;
if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) { PortBase::set_state (node);
return;
}
if ((prop = node.property ("connections")) != 0 && _jack_port) { if ((prop = node.property ("connections")) != 0 && _jack_port) {
_connections = prop->value (); _connections = prop->value ();

186
libs/midi++2/port_base.cc Normal file
View file

@ -0,0 +1,186 @@
/*
Copyright (C) 1998 Paul Barton-Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: port.cc 11871 2012-04-10 16:27:01Z paul $
*/
#include <iostream>
#include <cstdio>
#include <fcntl.h>
#include <errno.h>
#include <jack/jack.h>
#include <jack/midiport.h>
#include "pbd/xml++.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "pbd/convert.h"
#include "pbd/strsplit.h"
#include "pbd/stacktrace.h"
#include "midi++/types.h"
#include "midi++/port_base.h"
#include "midi++/channel.h"
using namespace MIDI;
using namespace std;
using namespace PBD;
string PortBase::state_node_name = "MIDI-port";
PortBase::PortBase (string const & name, Flags flags)
: _flags (flags)
, _centrally_parsed (true)
{
init (name, flags);
}
PortBase::PortBase (const XMLNode& node)
: _centrally_parsed (true)
{
Descriptor desc (node);
init (desc.tag, desc.flags);
set_state (node);
}
void
PortBase::init (string const & name, Flags flags)
{
_ok = false; /* derived class must set to true if constructor
succeeds.
*/
_parser = 0;
_tagname = name;
_flags = flags;
_parser = new Parser (*this);
for (int i = 0; i < 16; i++) {
_channel[i] = new Channel (i, *this);
_channel[i]->connect_signals ();
}
}
PortBase::~PortBase ()
{
for (int i = 0; i < 16; i++) {
delete _channel[i];
}
}
/** Send a clock tick message.
* \return true on success.
*/
bool
PortBase::clock (timestamp_t timestamp)
{
static byte clockmsg = 0xf8;
if (sends_output()) {
return midimsg (&clockmsg, 1, timestamp);
}
return false;
}
std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::PortBase & port )
{
using namespace std;
os << "MIDI::Port { ";
os << "name: " << port.name();
os << "; ";
os << "ok: " << port.ok();
os << "; ";
os << " }";
return os;
}
PortBase::Descriptor::Descriptor (const XMLNode& node)
{
const XMLProperty *prop;
bool have_tag = false;
bool have_mode = false;
if ((prop = node.property ("tag")) != 0) {
tag = prop->value();
have_tag = true;
}
if ((prop = node.property ("mode")) != 0) {
if (strings_equal_ignore_case (prop->value(), "output") || strings_equal_ignore_case (prop->value(), "out")) {
flags = IsOutput;
} else if (strings_equal_ignore_case (prop->value(), "input") || strings_equal_ignore_case (prop->value(), "in")) {
flags = IsInput;
}
have_mode = true;
}
if (!have_tag || !have_mode) {
throw failed_constructor();
}
}
XMLNode&
PortBase::get_state () const
{
XMLNode* root = new XMLNode (state_node_name);
root->add_property ("tag", _tagname);
if (_flags == IsInput) {
root->add_property ("mode", "input");
} else {
root->add_property ("mode", "output");
}
#if 0
byte device_inquiry[6];
device_inquiry[0] = 0xf0;
device_inquiry[0] = 0x7e;
device_inquiry[0] = 0x7f;
device_inquiry[0] = 0x06;
device_inquiry[0] = 0x02;
device_inquiry[0] = 0xf7;
write (device_inquiry, sizeof (device_inquiry), 0);
#endif
return *root;
}
void
PortBase::set_state (const XMLNode& node)
{
const XMLProperty* prop;
if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) {
return;
}
}
bool
PortBase::centrally_parsed() const
{
return _centrally_parsed;
}

View file

@ -49,6 +49,7 @@ def build(bld):
channel.cc channel.cc
manager.cc manager.cc
parser.cc parser.cc
port_base.cc
port.cc port.cc
midnam_patch.cc midnam_patch.cc
mmc.cc mmc.cc