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
This commit is contained in:
parent
b47524ded2
commit
9469d6b26a
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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 ();
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue
Block a user