summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <carl@carlh.net>2010-06-29 13:47:53 +0000
committerCarl Hetherington <carl@carlh.net>2010-06-29 13:47:53 +0000
commit9469d6b26acb0a6c79cd96058760081cc9bfa7fb (patch)
treef1939b23b9c279350279ae240e4a8d5b765f7dc7
parentb47524ded2af50bda96dc9d2dcf5f56cd7e94092 (diff)
Clean up MMC transmission a bit, and make sure that it is all done from one thread.
git-svn-id: svn://localhost/ardour2/branches/3.0@7324 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--libs/ardour/ardour/session.h18
-rw-r--r--libs/ardour/audioengine.cc2
-rw-r--r--libs/ardour/session.cc18
-rw-r--r--libs/ardour/session_midi.cc190
-rw-r--r--libs/ardour/session_process.cc3
-rw-r--r--libs/ardour/session_state.cc63
-rw-r--r--libs/ardour/session_transport.cc14
-rw-r--r--libs/midi++2/midi++/mmc.h130
-rw-r--r--libs/midi++2/mmc.cc131
-rw-r--r--libs/midi++2/wscript2
10 files changed, 232 insertions, 339 deletions
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index c9ef5fc774..79bdd4d966 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -41,7 +41,6 @@
#include "pbd/signals.h"
#include "pbd/undo.h"
-#include "midi++/mmc.h"
#include "midi++/types.h"
#include "ardour/ardour.h"
@@ -64,6 +63,8 @@ class AEffect;
namespace MIDI {
class Port;
+ class MachineControl;
+ class Parser;
}
namespace PBD {
@@ -640,16 +641,13 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void midi_panic(void);
int set_mtc_port (std::string port_tag);
- int set_mmc_port (std::string port_tag);
int set_midi_port (std::string port_tag);
int set_midi_clock_port (std::string port_tag);
MIDI::Port *mtc_port() const { return _mtc_port; }
- MIDI::Port *mmc_port() const { return _mmc_port; }
MIDI::Port *midi_port() const { return _midi_port; }
MIDI::Port *midi_clock_port() const { return _midi_clock_port; }
PBD::Signal0<void> MTC_PortChanged;
- PBD::Signal0<void> MMC_PortChanged;
PBD::Signal0<void> MIDI_PortChanged;
PBD::Signal0<void> MIDIClock_PortChanged;
@@ -659,9 +657,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
bool get_trace_midi_input(MIDI::Port *port = 0);
bool get_trace_midi_output(MIDI::Port *port = 0);
- void set_mmc_receive_device_id (uint32_t id);
- void set_mmc_send_device_id (uint32_t id);
-
/* Scrubbing */
void start_scrub (nframes_t where);
@@ -951,15 +946,13 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void check_declick_out ();
- MIDI::MachineControl* mmc;
- MIDI::Port* _mmc_port;
+ MIDI::MachineControl* _mmc;
MIDI::Port* _mtc_port;
MIDI::Port* _midi_port;
MIDI::Port* _midi_clock_port;
std::string _path;
std::string _name;
bool _is_new;
- bool session_send_mmc;
bool session_send_mtc;
bool session_midi_feedback;
bool play_loop;
@@ -1092,8 +1085,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
/* MIDI Machine Control */
- void deliver_mmc (MIDI::MachineControl::Command, nframes_t);
-
void spp_start (MIDI::Parser&, nframes_t timestamp);
void spp_continue (MIDI::Parser&, nframes_t timestamp);
void spp_stop (MIDI::Parser&, nframes_t timestamp);
@@ -1121,7 +1112,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
MidiTimeoutList midi_timeouts;
bool mmc_step_timeout ();
- MIDI::byte mmc_buffer[32];
MIDI::byte mtc_msg[16];
MIDI::byte mtc_timecode_bits; /* encoding of SMTPE type for MTC */
MIDI::byte midi_msg[16];
@@ -1444,7 +1434,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void add_session_range_location (nframes_t, nframes_t);
-
+ void setup_midi_machine_control ();
};
} // namespace ARDOUR
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 79aa5d9ff8..0058acc7e2 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -33,6 +33,7 @@
#include "pbd/unknown_type.h"
#include "midi++/jack.h"
+#include "midi++/mmc.h"
#include "ardour/amp.h"
#include "ardour/audio_port.h"
@@ -146,6 +147,7 @@ _thread_init_callback (void * /*arg*/)
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
MIDI::JACK_MidiPort::set_process_thread (pthread_self());
+ MIDI::MachineControl::set_sending_thread (pthread_self ());
}
typedef void (*_JackInfoShutdownCallback)(jack_status_t code, const char* reason, void *arg);
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 7a4a4c4e9f..423cc8cb9a 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -99,6 +99,7 @@
#include "ardour/graph.h"
#include "midi++/jack.h"
+#include "midi++/mmc.h"
#include "i18n.h"
@@ -135,8 +136,7 @@ Session::Session (AudioEngine &eng,
: _engine (eng),
_target_transport_speed (0.0),
_requested_return_frame (-1),
- mmc (0),
- _mmc_port (default_mmc_port),
+ _mmc (0),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
_midi_clock_port (default_midi_clock_port),
@@ -305,7 +305,7 @@ Session::destroy ()
Crossfade::set_buffer_size (0);
- delete mmc;
+ delete _mmc;
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
playlists.reset ();
@@ -961,7 +961,7 @@ Session::enable_record ()
if (g_atomic_int_get (&_record_status) != Recording) {
g_atomic_int_set (&_record_status, Recording);
_last_record_location = _transport_frame;
- deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location);
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
@@ -987,19 +987,13 @@ Session::disable_record (bool rt_context, bool force)
if ((!Config->get_latched_record_enable () && !play_loop) || force) {
g_atomic_int_set (&_record_status, Disabled);
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
} else {
if (rs == Recording) {
g_atomic_int_set (&_record_status, Enabled);
}
}
- // FIXME: timestamp correct? [DR]
- // FIXME FIXME FIXME: rt_context? this must be called in the process thread.
- // does this /need/ to be sent in all cases?
- if (rt_context) {
- deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame);
- }
-
if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
boost::shared_ptr<RouteList> rl = routes.reader ();
@@ -1053,7 +1047,7 @@ Session::maybe_enable_record ()
enable_record ();
}
} else {
- deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame);
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
RecordStateChanged (); /* EMIT SIGNAL */
}
diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc
index 0a6b490b5a..201c913588 100644
--- a/libs/ardour/session_midi.cc
+++ b/libs/ardour/session_midi.cc
@@ -56,10 +56,6 @@ using namespace PBD;
using namespace MIDI;
using namespace Glib;
-MachineControl::CommandSignature MMC_CommandSignature;
-MachineControl::ResponseSignature MMC_ResponseSignature;
-
-
void
Session::midi_panic()
{
@@ -80,12 +76,6 @@ Session::use_config_midi_ports ()
{
string port_name;
- if (default_mmc_port) {
- set_mmc_port (default_mmc_port->name());
- } else {
- set_mmc_port ("");
- }
-
if (default_mtc_port) {
set_mtc_port (default_mtc_port->name());
} else {
@@ -153,90 +143,6 @@ Session::set_mtc_port (string port_tag)
return 0;
}
-void
-Session::set_mmc_receive_device_id (uint32_t device_id)
-{
- if (mmc) {
- mmc->set_receive_device_id (device_id);
- }
-}
-
-void
-Session::set_mmc_send_device_id (uint32_t device_id)
-{
- if (mmc) {
- mmc->set_send_device_id (device_id);
- }
-}
-
-int
-Session::set_mmc_port (string port_tag)
-{
- MIDI::byte old_recv_device_id = 0;
- MIDI::byte old_send_device_id = 0;
- bool reset_id = false;
-
- if (port_tag.length() == 0) {
- if (_mmc_port == 0) {
- return 0;
- }
- _mmc_port = 0;
- goto out;
- }
-
- MIDI::Port* port;
-
- if ((port = MIDI::Manager::instance()->port (port_tag)) == 0) {
- return -1;
- }
-
- _mmc_port = port;
-
- if (mmc) {
- old_recv_device_id = mmc->receive_device_id();
- old_recv_device_id = mmc->send_device_id();
- reset_id = true;
- delete mmc;
- }
-
- mmc = new MIDI::MachineControl (*_mmc_port, 1.0,
- MMC_CommandSignature,
- MMC_ResponseSignature);
-
- if (reset_id) {
- mmc->set_receive_device_id (old_recv_device_id);
- mmc->set_send_device_id (old_send_device_id);
- }
-
- mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
- mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
- mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
- mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
- mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
- mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
- mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
- mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
- mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
- mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
- mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
-
-
- /* also handle MIDI SPP because its so common */
-
- _mmc_port->input()->start.connect_same_thread (*this, boost::bind (&Session::spp_start, this, _1, _2));
- _mmc_port->input()->contineu.connect_same_thread (*this, boost::bind (&Session::spp_continue, this, _1, _2));
- _mmc_port->input()->stop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this, _1, _2));
-
- Config->set_mmc_port_name (port_tag);
-
- out:
- MMC_PortChanged(); /* EMIT SIGNAL */
- set_dirty();
- return 0;
-}
-
int
Session::set_midi_port (string /*port_tag*/)
{
@@ -324,26 +230,26 @@ Session::set_trace_midi_input (bool yn, MIDI::Port* port)
}
} else {
- if (_mmc_port) {
- if ((input_parser = _mmc_port->input()) != 0) {
+ if (_mmc->port()) {
+ if ((input_parser = _mmc->port()->input()) != 0) {
input_parser->trace (yn, &cout, "input: ");
}
}
- if (_mtc_port && _mtc_port != _mmc_port) {
+ if (_mtc_port && _mtc_port != _mmc->port()) {
if ((input_parser = _mtc_port->input()) != 0) {
input_parser->trace (yn, &cout, "input: ");
}
}
- if (_midi_port && _midi_port != _mmc_port && _midi_port != _mtc_port ) {
+ if (_midi_port && _midi_port != _mmc->port() && _midi_port != _mtc_port ) {
if ((input_parser = _midi_port->input()) != 0) {
input_parser->trace (yn, &cout, "input: ");
}
}
if (_midi_clock_port
- && _midi_clock_port != _mmc_port
+ && _midi_clock_port != _mmc->port()
&& _midi_clock_port != _mtc_port
&& _midi_clock_port != _midi_port) {
if ((input_parser = _midi_clock_port->input()) != 0) {
@@ -365,19 +271,19 @@ Session::set_trace_midi_output (bool yn, MIDI::Port* port)
output_parser->trace (yn, &cout, "output: ");
}
} else {
- if (_mmc_port) {
- if ((output_parser = _mmc_port->output()) != 0) {
+ if (_mmc->port()) {
+ if ((output_parser = _mmc->port()->output()) != 0) {
output_parser->trace (yn, &cout, "output: ");
}
}
- if (_mtc_port && _mtc_port != _mmc_port) {
+ if (_mtc_port && _mtc_port != _mmc->port()) {
if ((output_parser = _mtc_port->output()) != 0) {
output_parser->trace (yn, &cout, "output: ");
}
}
- if (_midi_port && _midi_port != _mmc_port && _midi_port != _mtc_port ) {
+ if (_midi_port && _midi_port != _mmc->port() && _midi_port != _mtc_port ) {
if ((output_parser = _midi_port->output()) != 0) {
output_parser->trace (yn, &cout, "output: ");
}
@@ -398,8 +304,8 @@ Session::get_trace_midi_input(MIDI::Port *port)
}
}
else {
- if (_mmc_port) {
- if ((input_parser = _mmc_port->input()) != 0) {
+ if (_mmc->port()) {
+ if ((input_parser = _mmc->port()->input()) != 0) {
return input_parser->tracing();
}
}
@@ -430,8 +336,8 @@ Session::get_trace_midi_output(MIDI::Port *port)
}
}
else {
- if (_mmc_port) {
- if ((output_parser = _mmc_port->output()) != 0) {
+ if (_mmc->port()) {
+ if ((output_parser = _mmc->port()->output()) != 0) {
return output_parser->tracing();
}
}
@@ -459,13 +365,6 @@ Session::setup_midi_control ()
outbound_mtc_timecode_frame = 0;
next_quarter_frame_to_send = 0;
- /* setup the MMC buffer */
-
- mmc_buffer[0] = 0xf0; // SysEx
- mmc_buffer[1] = 0x7f; // Real Time SysEx ID for MMC
- mmc_buffer[2] = (mmc ? mmc->send_device_id() : 0x7f);
- mmc_buffer[3] = 0x6; // MCC
-
/* Set up the qtr frame message */
mtc_msg[0] = 0xf1;
@@ -896,69 +795,6 @@ Session::send_midi_time_code_for_cycle(nframes_t nframes)
OUTBOUND MMC STUFF
**********************************************************************/
-void
-Session::deliver_mmc (MIDI::MachineControl::Command cmd, nframes_t where)
-{
- using namespace MIDI;
- int nbytes = 4;
- Timecode::Time timecode;
-
- if (_mmc_port == 0 || !session_send_mmc) {
- // cerr << "Not delivering MMC " << _mmc_port << " - " << session_send_mmc << endl;
- return;
- }
-
- mmc_buffer[nbytes++] = cmd;
-
- // cerr << "delivering MMC, cmd = " << hex << (int) cmd << dec << endl;
-
- switch (cmd) {
- case MachineControl::cmdLocate:
- timecode_time_subframes (where, timecode);
-
- mmc_buffer[nbytes++] = 0x6; // byte count
- mmc_buffer[nbytes++] = 0x1; // "TARGET" subcommand
- mmc_buffer[nbytes++] = timecode.hours;
- mmc_buffer[nbytes++] = timecode.minutes;
- mmc_buffer[nbytes++] = timecode.seconds;
- mmc_buffer[nbytes++] = timecode.frames;
- mmc_buffer[nbytes++] = timecode.subframes;
- break;
-
- case MachineControl::cmdStop:
- break;
-
- case MachineControl::cmdPlay:
- /* always convert Play into Deferred Play */
- /* Why? [DR] */
- mmc_buffer[4] = MachineControl::cmdDeferredPlay;
- break;
-
- case MachineControl::cmdDeferredPlay:
- break;
-
- case MachineControl::cmdRecordStrobe:
- break;
-
- case MachineControl::cmdRecordExit:
- break;
-
- case MachineControl::cmdRecordPause:
- break;
-
- default:
- nbytes = 0;
- };
-
- if (nbytes) {
-
- mmc_buffer[nbytes++] = 0xf7; // terminate SysEx/MMC message
-
- if (_mmc_port->midimsg (mmc_buffer, nbytes, 0)) {
- error << string_compose(_("MMC: cannot send command %1%2%3"), &hex, cmd, &dec) << endmsg;
- }
- }
-}
bool
Session::mmc_step_timeout ()
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index ba5db152cb..e555350088 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -40,6 +40,7 @@
#include "ardour/port.h"
#include "midi++/manager.h"
+#include "midi++/mmc.h"
#include "i18n.h"
@@ -67,6 +68,8 @@ Session::process (nframes_t nframes)
post_transport ();
}
}
+
+ _mmc->flush_pending ();
_engine.main_thread()->get_buffers ();
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 2842357db8..85d6a1689b 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -204,7 +204,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
_state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
_was_seamless = Config->get_seamless_loop ();
_slave = 0;
- session_send_mmc = false;
session_send_mtc = false;
g_atomic_int_set (&_playback_load, 100);
g_atomic_int_set (&_capture_load, 100);
@@ -299,6 +298,8 @@ Session::second_stage_init ()
return -1;
}
+ setup_midi_machine_control ();
+
// set_state() will call setup_raid_path(), but if it's a new session we need
// to call setup_raid_path() here.
@@ -322,7 +323,6 @@ Session::second_stage_init ()
_state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
-
_locations.changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
_locations.added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
setup_click_sounds (0);
@@ -352,8 +352,8 @@ Session::second_stage_init ()
send_full_time_code (0);
_engine.transport_locate (0);
- deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
- deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
+ _mmc->send (MIDI::MachineControlCommand (Timecode::Time ()));
MidiClockTicker::instance().set_session (this);
MIDI::Name::MidiPatchManager::instance().set_session (this);
@@ -1242,6 +1242,8 @@ Session::set_state (const XMLNode& node, int version)
error << _("Session: XML state has no options section") << endmsg;
}
+ setup_midi_machine_control ();
+
if (use_config_midi_ports ()) {
}
@@ -3180,15 +3182,11 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "mmc-device-id" || p == "mmc-receive-id") {
- if (mmc) {
- mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
- }
+ _mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
} else if (p == "mmc-send-id") {
- if (mmc) {
- mmc->set_send_device_id (Config->get_mmc_send_device_id());
- }
+ _mmc->set_send_device_id (Config->get_mmc_send_device_id());
} else if (p == "midi-control") {
@@ -3254,16 +3252,7 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "send-mmc") {
- /* only set the internal flag if we have
- a port.
- */
-
- if (_mmc_port != 0) {
- session_send_mmc = Config->get_send_mmc();
- } else {
- mmc = 0;
- session_send_mmc = false;
- }
+ _mmc->enable_send (Config->get_send_mmc ());
} else if (p == "midi-feedback") {
@@ -3311,17 +3300,17 @@ Session::config_changed (std::string p, bool ours)
sync_order_keys ("session");
} else if (p == "initial-program-change") {
- if (_mmc_port && Config->get_initial_program_change() >= 0) {
+ if (_mmc->port() && Config->get_initial_program_change() >= 0) {
MIDI::byte buf[2];
buf[0] = MIDI::program; // channel zero by default
buf[1] = (Config->get_initial_program_change() & 0x7f);
- _mmc_port->midimsg (buf, sizeof (buf), 0);
+ _mmc->port()->midimsg (buf, sizeof (buf), 0);
}
} else if (p == "initial-program-change") {
- if (_mmc_port && Config->get_initial_program_change() >= 0) {
+ if (_mmc->port() && Config->get_initial_program_change() >= 0) {
MIDI::byte* buf = new MIDI::byte[2];
buf[0] = MIDI::program; // channel zero by default
@@ -3374,3 +3363,31 @@ Session::load_diskstreams_2X (XMLNode const & node, int)
return 0;
}
+
+/** Create our MachineControl object and connect things to it */
+void
+Session::setup_midi_machine_control ()
+{
+ _mmc = new MIDI::MachineControl;
+ _mmc->set_port (default_mmc_port);
+
+ _mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
+ _mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
+ _mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
+ _mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
+ _mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
+ _mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
+ _mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
+ _mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
+ _mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
+ _mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
+ _mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
+
+ /* also handle MIDI SPP because its so common */
+
+ _mmc->port()->input()->start.connect_same_thread (*this, boost::bind (&Session::spp_start, this, _1, _2));
+ _mmc->port()->input()->contineu.connect_same_thread (*this, boost::bind (&Session::spp_continue, this, _1, _2));
+ _mmc->port()->input()->stop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this, _1, _2));
+}
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index 2ea53c6a5d..74eafa0f15 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -183,8 +183,10 @@ Session::realtime_stop (bool abort, bool clear_state)
// FIXME: where should this really be? [DR]
//send_full_time_code();
- deliver_mmc (MIDI::MachineControl::cmdStop, 0);
- deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+ Timecode::Time time;
+ timecode_time_subframes (_transport_frame, time);
+ _mmc->send (MIDI::MachineControlCommand (time));
if (_transport_speed < 0.0f) {
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
@@ -892,7 +894,9 @@ Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool
_send_timecode_update = true;
if (with_mmc) {
- deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
+ Timecode::Time time;
+ timecode_time_subframes (_transport_frame, time);
+ _mmc->send (MIDI::MachineControlCommand (time));
}
Located (); /* EMIT SIGNAL */
@@ -1129,7 +1133,9 @@ Session::start_transport ()
}
}
- deliver_mmc(MIDI::MachineControl::cmdDeferredPlay, _transport_frame);
+ Timecode::Time time;
+ timecode_time_subframes (_transport_frame, time);
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
TransportStateChange (); /* EMIT SIGNAL */
}
diff --git a/libs/midi++2/midi++/mmc.h b/libs/midi++2/midi++/mmc.h
index 687d0e14d8..5b3bc663e6 100644
--- a/libs/midi++2/midi++/mmc.h
+++ b/libs/midi++2/midi++/mmc.h
@@ -20,20 +20,22 @@
#ifndef __midipp_mmc_h_h__
#define __midipp_mmc_h_h__
+#include "control_protocol/timecode.h"
#include "pbd/signals.h"
+#include "pbd/ringbuffer.h"
#include "midi++/types.h"
namespace MIDI {
class Port;
class Parser;
+class MachineControlCommand;
+/** Class to handle incoming and outgoing MIDI machine control messages */
class MachineControl
{
public:
typedef PBD::Signal1<void,MachineControl&> MMCSignal;
- typedef byte CommandSignature[60];
- typedef byte ResponseSignature[60];
enum Command {
cmdStop = 0x1,
@@ -84,19 +86,21 @@ class MachineControl
cmdResume = 0x7F
};
- MachineControl (Port &port,
- float MMCVersion,
- CommandSignature &cs,
- ResponseSignature &rs);
+ MachineControl ();
+ void set_port (Port* p);
- Port &port() { return _port; }
+ Port* port() { return _port; }
void set_receive_device_id (byte id);
void set_send_device_id (byte id);
byte receive_device_id () const { return _receive_device_id; }
byte send_device_id () const { return _send_device_id; }
+ void enable_send (bool);
+ void send (MachineControlCommand const &);
+ void flush_pending ();
static bool is_mmc (byte *sysex_buf, size_t len);
+ static void set_sending_thread (pthread_t);
/* Signals to connect to if you want to run "callbacks"
when certain MMC commands are received.
@@ -170,102 +174,50 @@ class MachineControl
PBD::Signal2<void,MachineControl &, int> Step;
- protected:
-
-#define MMC_NTRACKS 48
-
- /* MMC Information fields (think "registers") */
-
- CommandSignature commandSignature;
- ResponseSignature responseSignature;
-
- byte updateRate;
- byte responseError;
- byte commandError;
- byte commandErrorLevel;
-
- byte motionControlTally;
- byte velocityTally;
- byte stopMode;
- byte fastMode;
- byte recordMode;
- byte recordStatus;
- bool trackRecordStatus[MMC_NTRACKS];
- bool trackRecordReady[MMC_NTRACKS];
- byte globalMonitor;
- byte recordMonitor;
- byte trackSyncMonitor;
- byte trackInputMonitor;
- byte stepLength;
- byte playSpeedReference;
- byte fixedSpeed;
- byte lifterDefeat;
- byte controlDisable;
- byte trackMute[MMC_NTRACKS];
- byte failure;
- byte selectedTimeCode;
- byte shortSelectedTimeCode;
- byte timeStandard;
- byte selectedTimeCodeSource;
- byte selectedTimeCodeUserbits;
- byte selectedMasterCode;
- byte requestedOffset;
- byte actualOffset;
- byte lockDeviation;
- byte shortSelectedMasterCode;
- byte shortRequestedOffset;
- byte shortActualOffset;
- byte shortLockDeviation;
- byte resolvedPlayMode;
- byte chaseMode;
- byte generatorTimeCode;
- byte shortGeneratorTimeCode;
- byte generatorCommandTally;
- byte generatorSetUp;
- byte generatorUserbits;
- byte vitcInsertEnable;
- byte midiTimeCodeInput;
- byte shortMidiTimeCodeInput;
- byte midiTimeCodeCommandTally;
- byte midiTimeCodeSetUp;
- byte gp0;
- byte gp1;
- byte gp2;
- byte gp3;
- byte gp4;
- byte gp5;
- byte gp6;
- byte gp7;
- byte shortGp0;
- byte shortGp1;
- byte shortGp2;
- byte shortGp3;
- byte shortGp4;
- byte shortGp5;
- byte shortGp6;
- byte shortGp7;
- byte procedureResponse;
- byte eventResponse;
- byte responseSegment;
- byte wait;
- byte resume;
-
private:
byte _receive_device_id;
byte _send_device_id;
- MIDI::Port &_port;
+ Port* _port;
+ bool _enable_send; ///< true if MMC sending is enabled
+
+ /** A ringbuffer of MMC commands that were `sent' from the wrong thread, which
+ are queued up and sent when flush_pending() is called.
+ */
+ RingBuffer<MachineControlCommand> _pending;
+
+ /** The thread to use for sending MMC commands */
+ static pthread_t _sending_thread;
void process_mmc_message (Parser &p, byte *, size_t len);
- PBD::ScopedConnection mmc_connection;
+ PBD::ScopedConnection mmc_connection; ///< connection to our parser for incoming data
int do_masked_write (byte *, size_t len);
int do_locate (byte *, size_t len);
int do_step (byte *, size_t len);
int do_shuttle (byte *, size_t len);
+ void send_immediately (MachineControlCommand const &);
void write_track_status (byte *, size_t len, byte reg);
};
+/** Class to describe a MIDI machine control command to be sent.
+ * In an ideal world we might use a class hierarchy for this, but objects of this type
+ * have to be allocated off the stack for thread safety.
+ */
+class MachineControlCommand
+{
+public:
+ MachineControlCommand () : _command (MachineControl::Command (0)) {}
+ MachineControlCommand (MachineControl::Command);
+ MachineControlCommand (Timecode::Time);
+
+ MIDI::byte* fill_buffer (MachineControl *mmc, MIDI::byte *) const;
+
+private:
+ MachineControl::Command _command;
+ Timecode::Time _time;
+};
+
} // namespace MIDI
#endif /* __midipp_mmc_h_h__ */
diff --git a/libs/midi++2/mmc.cc b/libs/midi++2/mmc.cc
index 6030230108..b8aa253f80 100644
--- a/libs/midi++2/mmc.cc
+++ b/libs/midi++2/mmc.cc
@@ -20,6 +20,7 @@
#include <map>
+#include "control_protocol/timecode.h"
#include "pbd/error.h"
#include "midi++/mmc.h"
#include "midi++/port.h"
@@ -29,6 +30,8 @@ using namespace std;
using namespace MIDI;
using namespace PBD;
+pthread_t MachineControl::_sending_thread;
+
static std::map<int,string> mmc_cmd_map;
static void build_mmc_cmd_map ()
{
@@ -192,24 +195,27 @@ static void build_mmc_cmd_map ()
}
-MachineControl::MachineControl (Port &p, float /*version*/,
- CommandSignature & /*csig*/,
- ResponseSignature & /*rsig*/)
-
- : _port (p)
+MachineControl::MachineControl ()
+ : _port (0)
+ , _pending (16)
{
- Parser *parser;
-
build_mmc_cmd_map ();
_receive_device_id = 0;
_send_device_id = 0x7f;
-
- if ((parser = _port.input()) != 0) {
- parser->mmc.connect_same_thread (mmc_connection, boost::bind (&MachineControl::process_mmc_message, this, _1, _2, _3));
+}
+
+void
+MachineControl::set_port (Port* p)
+{
+ _port = p;
+
+ mmc_connection.disconnect ();
+
+ if (_port->input()) {
+ _port->input()->mmc.connect_same_thread (mmc_connection, boost::bind (&MachineControl::process_mmc_message, this, _1, _2, _3));
} else {
- warning << "MMC connected to a non-input port: useless!"
- << endmsg;
+ warning << "MMC connected to a non-input port: useless!" << endmsg;
}
}
@@ -227,7 +233,6 @@ MachineControl::set_send_device_id (byte id)
bool
MachineControl::is_mmc (byte *sysex_buf, size_t len)
-
{
if (len < 4 || len > 48) {
return false;
@@ -247,7 +252,6 @@ MachineControl::is_mmc (byte *sysex_buf, size_t len)
void
MachineControl::process_mmc_message (Parser &, byte *msg, size_t len)
-
{
size_t skiplen;
byte *mmc_msg;
@@ -455,7 +459,6 @@ MachineControl::process_mmc_message (Parser &, byte *msg, size_t len)
int
MachineControl::do_masked_write (byte *msg, size_t len)
-
{
/* return the number of bytes "consumed" */
@@ -555,12 +558,10 @@ MachineControl::write_track_status (byte *msg, size_t /*len*/, byte reg)
switch (reg) {
case 0x4f:
- trackRecordStatus[base_track+n] = val;
TrackRecordStatusChange (*this, base_track+n, val);
break;
case 0x62:
- trackMute[base_track+n] = val;
TrackMuteChange (*this, base_track+n, val);
break;
}
@@ -571,7 +572,6 @@ MachineControl::write_track_status (byte *msg, size_t /*len*/, byte reg)
int
MachineControl::do_locate (byte *msg, size_t /*msglen*/)
-
{
if (msg[2] == 0) {
warning << "MIDI::MMC: locate [I/F] command not supported"
@@ -600,7 +600,6 @@ MachineControl::do_step (byte *msg, size_t /*msglen*/)
int
MachineControl::do_shuttle (byte *msg, size_t /*msglen*/)
-
{
size_t forward;
byte sh = msg[2];
@@ -630,3 +629,97 @@ MachineControl::do_shuttle (byte *msg, size_t /*msglen*/)
return 0;
}
+void
+MachineControl::enable_send (bool yn)
+{
+ _enable_send = yn;
+}
+
+/** Send a MMC command. It will be sent immediately if the call is made in _sending_thread,
+ * otherwise it will be queued and sent next time flush_pending()
+ * is called.
+ * @param c command, which this method takes ownership of.
+ */
+void
+MachineControl::send (MachineControlCommand const & c)
+{
+ if (pthread_self() == _sending_thread) {
+ send_immediately (c);
+ } else {
+ _pending.write (&c, 1);
+ }
+}
+
+/** Send any pending MMC commands immediately. Must be called from _sending_thread */
+void
+MachineControl::flush_pending ()
+{
+ MachineControlCommand c;
+ while (_pending.read (&c, 1) == 1) {
+ send_immediately (c);
+ }
+}
+
+/** Send a MMC immediately. Must be called from _sending_thread.
+ * @param c command, which this method takes ownership of.
+ */
+void
+MachineControl::send_immediately (MachineControlCommand const & c)
+{
+ if (_port == 0 || !_enable_send) {
+ // cerr << "Not delivering MMC " << _mmc->port() << " - " << session_send_mmc << endl;
+ return;
+ }
+
+ MIDI::byte buffer[32];
+ MIDI::byte* b = c.fill_buffer (this, buffer);
+
+ if (_port->midimsg (buffer, b - buffer, 0)) {
+ error << "MMC: cannot send command" << endmsg;
+ }
+}
+
+/** Set the thread that we should send MMC in */
+void
+MachineControl::set_sending_thread (pthread_t t)
+{
+ _sending_thread = t;
+}
+
+MachineControlCommand::MachineControlCommand (MachineControl::Command c)
+ : _command (c)
+{
+
+}
+
+MachineControlCommand::MachineControlCommand (Timecode::Time t)
+ : _command (MachineControl::cmdLocate)
+ , _time (t)
+{
+
+}
+
+MIDI::byte *
+MachineControlCommand::fill_buffer (MachineControl* mmc, MIDI::byte* b) const
+{
+ *b++ = 0xf0; // SysEx
+ *b++ = 0x7f; // Real-time SysEx ID for MMC
+ *b++ = mmc->send_device_id();
+ *b++ = 0x6; // MMC command
+
+ *b++ = _command;
+
+ if (_command == MachineControl::cmdLocate) {
+ *b++ = 0x6; // byte count
+ *b++ = 0x1; // "TARGET" subcommand
+ *b++ = _time.hours;
+ *b++ = _time.minutes;
+ *b++ = _time.seconds;
+ *b++ = _time.frames;
+ *b++ = _time.subframes;
+ }
+
+ *b++ = 0xf7;
+
+ return b;
+}
diff --git a/libs/midi++2/wscript b/libs/midi++2/wscript
index 6eaa606776..3be03ca2d4 100644
--- a/libs/midi++2/wscript
+++ b/libs/midi++2/wscript
@@ -70,7 +70,7 @@ def build(bld):
obj.source += ' alsa_sequencer_midiport.cc '
obj.cxxflags += [ '-DWITH_ALSA' ]
obj.export_incdirs = ['.']
- obj.includes = ['.']
+ obj.includes = ['.', '../surfaces/control_protocol']
obj.name = 'libmidipp'
obj.target = 'midipp'
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX COREAUDIO'