diff --git a/libs/midi++2/channel.cc b/libs/midi++2/channel.cc index ae889058c4..0c9fbe39d1 100644 --- a/libs/midi++2/channel.cc +++ b/libs/midi++2/channel.cc @@ -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; diff --git a/libs/midi++2/midi++/channel.h b/libs/midi++2/midi++/channel.h index 162eea1e9a..370a569156 100644 --- a/libs/midi++2/midi++/channel.h +++ b/libs/midi++2/midi++/channel.h @@ -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; diff --git a/libs/midi++2/midi++/parser.h b/libs/midi++2/midi++/parser.h index e57323e72d..ac452798cc 100644 --- a/libs/midi++2/midi++/parser.h +++ b/libs/midi++2/midi++/parser.h @@ -29,7 +29,7 @@ namespace MIDI { -class Port; +class PortBase; class Parser; typedef PBD::Signal1 ZeroByteSignal; @@ -41,7 +41,7 @@ typedef PBD::Signal3 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); diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h index 6b4381d496..e7b532013e 100644 --- a/libs/midi++2/midi++/port.h +++ b/libs/midi++2/midi++/port.h @@ -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 MakeConnections; static PBD::Signal0 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 > output_fifo; Evoral::EventRingBuffer 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 ports; -}; - -std::ostream & operator << ( std::ostream & os, const Port & port ); - } // namespace MIDI #endif // __libmidi_port_h__ diff --git a/libs/midi++2/midi++/port_base.h b/libs/midi++2/midi++/port_base.h new file mode 100644 index 0000000000..ceb7834643 --- /dev/null +++ b/libs/midi++2/midi++/port_base.h @@ -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 +#include + +#include + +#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 ports; +}; + +std::ostream & operator << (std::ostream& os, const PortBase& port); + +} // namespace MIDI + +#endif // __libmidi_port_base_h__ diff --git a/libs/midi++2/parser.cc b/libs/midi++2/parser.cc index cdd23a5306..a56e3f82e4 100644 --- a/libs/midi++2/parser.cc +++ b/libs/midi++2/parser.cc @@ -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 = ""; diff --git a/libs/midi++2/port.cc b/libs/midi++2/port.cc index b5a16eef01..06db2e14b8 100644 --- a/libs/midi++2/port.cc +++ b/libs/midi++2/port.cc @@ -43,34 +43,30 @@ using namespace PBD; pthread_t Port::_process_thread; Signal0 Port::JackHalted; Signal0 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 >::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 (); diff --git a/libs/midi++2/port_base.cc b/libs/midi++2/port_base.cc new file mode 100644 index 0000000000..830dc4efe6 --- /dev/null +++ b/libs/midi++2/port_base.cc @@ -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 +#include +#include +#include + +#include +#include + +#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; +} diff --git a/libs/midi++2/wscript b/libs/midi++2/wscript index b810bdc802..eccefe695d 100644 --- a/libs/midi++2/wscript +++ b/libs/midi++2/wscript @@ -49,6 +49,7 @@ def build(bld): channel.cc manager.cc parser.cc + port_base.cc port.cc midnam_patch.cc mmc.cc