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;
Channel::Channel (byte channelnum, Port &p) : _port (p)
Channel::Channel (byte channelnum, PortBase &p)
: _port (p)
{
_channel_number = channelnum;

View File

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

View File

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

View File

@ -34,98 +34,30 @@
#include "midi++/types.h"
#include "midi++/parser.h"
#include "midi++/port_base.h"
namespace MIDI {
class Channel;
class PortRequest;
class Port {
class Port : public PortBase {
public:
enum Flags {
IsInput = JackPortIsInput,
IsOutput = JackPortIsOutput,
};
Port (std::string const &, Flags, jack_client_t *);
Port (std::string const &, PortBase::Flags, jack_client_t *);
Port (const XMLNode&, jack_client_t *);
~Port ();
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. */
void cycle_start (pframes_t nframes);
/* Only for use by MidiManager. Don't ever call this. */
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);
/** 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.
*/
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();
};
int write (byte *msg, size_t msglen, timestamp_t timestamp);
int read (byte *buf, size_t bufsize);
void drain (int check_interval_usecs);
int selectable () const { return xthread.selectable(); }
pframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
@ -136,30 +68,19 @@ class Port {
static pthread_t get_process_thread () { return _process_thread; }
static bool is_process_thread();
static std::string state_node_name;
static PBD::Signal0<void> MakeConnections;
static PBD::Signal0<void> JackHalted;
private:
bool _ok;
bool _currently_in_cycle;
pframes_t _nframes_this_cycle;
std::string _tagname;
size_t _number;
Channel* _channel[16];
Parser* _parser;
jack_client_t* _jack_client;
jack_port_t* _jack_port;
framecnt_t _last_read_index;
timestamp_t _last_write_timestamp;
RingBuffer< Evoral::Event<double> > output_fifo;
Evoral::EventRingBuffer<timestamp_t> input_fifo;
Glib::Mutex output_fifo_lock;
CrossThreadChannel xthread;
Flags _flags;
bool _centrally_parsed;
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
#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++/parser.h"
#include "midi++/port.h"
#include "midi++/port_base.h"
#include "midi++/mmc.h"
#include "pbd/transmitter.h"
@ -104,9 +104,8 @@ Parser::midi_event_type_name (eventType t)
}
}
Parser::Parser (Port &p)
: _port (p)
Parser::Parser (PortBase &p)
: _port(p)
{
trace_stream = 0;
trace_prefix = "";

View File

@ -43,34 +43,30 @@ using namespace PBD;
pthread_t Port::_process_thread;
Signal0<void> Port::JackHalted;
Signal0<void> Port::MakeConnections;
string Port::state_node_name = "MIDI-port";
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)
, _jack_client (jack_client)
, _jack_port (0)
, _last_read_index (0)
, output_fifo (512)
, input_fifo (1024)
, xthread (true)
, _flags (flags)
, _centrally_parsed (true)
{
assert (jack_client);
init (name, flags);
}
Port::Port (const XMLNode& node, jack_client_t* jack_client)
: _currently_in_cycle (false)
: PortBase (node)
, _currently_in_cycle (false)
, _nframes_this_cycle (0)
, _jack_client (jack_client)
, _jack_port (0)
, _last_read_index (0)
, output_fifo (512)
, input_fifo (1024)
, xthread (true)
, _centrally_parsed (true)
{
assert (jack_client);
@ -84,21 +80,7 @@ Port::Port (const XMLNode& node, jack_client_t* jack_client)
void
Port::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::init (name, flags);
if (!create_port ()) {
_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
Port::cycle_start (pframes_t nframes)
{
@ -184,8 +151,6 @@ Port::cycle_start (pframes_t nframes)
_nframes_this_cycle = nframes;
assert(_nframes_this_cycle == nframes);
_last_read_index = 0;
_last_write_timestamp = 0;
if (sends_output()) {
void *buffer = jack_port_get_buffer (_jack_port, nframes);
@ -222,45 +187,6 @@ Port::cycle_end ()
_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
Port::jack_halted ()
{
@ -268,6 +194,25 @@ Port::jack_halted ()
_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
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;
usleep (5000);
} else {
// 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);
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]) {
@ -417,14 +364,7 @@ Port::create_port ()
XMLNode&
Port::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");
}
XMLNode& root = PortBase::get_state ();
#if 0
byte device_inquiry[6];
@ -454,15 +394,15 @@ Port::get_state () const
}
if (!connection_string.empty()) {
root->add_property ("connections", connection_string);
root.add_property ("connections", connection_string);
}
} else {
if (!_connections.empty()) {
root->add_property ("connections", _connections);
root.add_property ("connections", _connections);
}
}
return *root;
return root;
}
void
@ -470,9 +410,7 @@ Port::set_state (const XMLNode& node)
{
const XMLProperty* prop;
if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) {
return;
}
PortBase::set_state (node);
if ((prop = node.property ("connections")) != 0 && _jack_port) {
_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
manager.cc
parser.cc
port_base.cc
port.cc
midnam_patch.cc
mmc.cc