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/signals.h"
|
||||||
#include "pbd/undo.h"
|
#include "pbd/undo.h"
|
||||||
|
|
||||||
#include "midi++/mmc.h"
|
|
||||||
#include "midi++/types.h"
|
#include "midi++/types.h"
|
||||||
|
|
||||||
#include "ardour/ardour.h"
|
#include "ardour/ardour.h"
|
||||||
|
@ -64,6 +63,8 @@ class AEffect;
|
||||||
|
|
||||||
namespace MIDI {
|
namespace MIDI {
|
||||||
class Port;
|
class Port;
|
||||||
|
class MachineControl;
|
||||||
|
class Parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace PBD {
|
namespace PBD {
|
||||||
|
@ -640,16 +641,13 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
||||||
|
|
||||||
void midi_panic(void);
|
void midi_panic(void);
|
||||||
int set_mtc_port (std::string port_tag);
|
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_port (std::string port_tag);
|
||||||
int set_midi_clock_port (std::string port_tag);
|
int set_midi_clock_port (std::string port_tag);
|
||||||
MIDI::Port *mtc_port() const { return _mtc_port; }
|
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_port() const { return _midi_port; }
|
||||||
MIDI::Port *midi_clock_port() const { return _midi_clock_port; }
|
MIDI::Port *midi_clock_port() const { return _midi_clock_port; }
|
||||||
|
|
||||||
PBD::Signal0<void> MTC_PortChanged;
|
PBD::Signal0<void> MTC_PortChanged;
|
||||||
PBD::Signal0<void> MMC_PortChanged;
|
|
||||||
PBD::Signal0<void> MIDI_PortChanged;
|
PBD::Signal0<void> MIDI_PortChanged;
|
||||||
PBD::Signal0<void> MIDIClock_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_input(MIDI::Port *port = 0);
|
||||||
bool get_trace_midi_output(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 */
|
/* Scrubbing */
|
||||||
|
|
||||||
void start_scrub (nframes_t where);
|
void start_scrub (nframes_t where);
|
||||||
|
@ -951,15 +946,13 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
||||||
|
|
||||||
void check_declick_out ();
|
void check_declick_out ();
|
||||||
|
|
||||||
MIDI::MachineControl* mmc;
|
MIDI::MachineControl* _mmc;
|
||||||
MIDI::Port* _mmc_port;
|
|
||||||
MIDI::Port* _mtc_port;
|
MIDI::Port* _mtc_port;
|
||||||
MIDI::Port* _midi_port;
|
MIDI::Port* _midi_port;
|
||||||
MIDI::Port* _midi_clock_port;
|
MIDI::Port* _midi_clock_port;
|
||||||
std::string _path;
|
std::string _path;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
bool _is_new;
|
bool _is_new;
|
||||||
bool session_send_mmc;
|
|
||||||
bool session_send_mtc;
|
bool session_send_mtc;
|
||||||
bool session_midi_feedback;
|
bool session_midi_feedback;
|
||||||
bool play_loop;
|
bool play_loop;
|
||||||
|
@ -1092,8 +1085,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
||||||
|
|
||||||
/* MIDI Machine Control */
|
/* MIDI Machine Control */
|
||||||
|
|
||||||
void deliver_mmc (MIDI::MachineControl::Command, nframes_t);
|
|
||||||
|
|
||||||
void spp_start (MIDI::Parser&, nframes_t timestamp);
|
void spp_start (MIDI::Parser&, nframes_t timestamp);
|
||||||
void spp_continue (MIDI::Parser&, nframes_t timestamp);
|
void spp_continue (MIDI::Parser&, nframes_t timestamp);
|
||||||
void spp_stop (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;
|
MidiTimeoutList midi_timeouts;
|
||||||
bool mmc_step_timeout ();
|
bool mmc_step_timeout ();
|
||||||
|
|
||||||
MIDI::byte mmc_buffer[32];
|
|
||||||
MIDI::byte mtc_msg[16];
|
MIDI::byte mtc_msg[16];
|
||||||
MIDI::byte mtc_timecode_bits; /* encoding of SMTPE type for MTC */
|
MIDI::byte mtc_timecode_bits; /* encoding of SMTPE type for MTC */
|
||||||
MIDI::byte midi_msg[16];
|
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 add_session_range_location (nframes_t, nframes_t);
|
||||||
|
|
||||||
|
void setup_midi_machine_control ();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ARDOUR
|
} // namespace ARDOUR
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "pbd/unknown_type.h"
|
#include "pbd/unknown_type.h"
|
||||||
|
|
||||||
#include "midi++/jack.h"
|
#include "midi++/jack.h"
|
||||||
|
#include "midi++/mmc.h"
|
||||||
|
|
||||||
#include "ardour/amp.h"
|
#include "ardour/amp.h"
|
||||||
#include "ardour/audio_port.h"
|
#include "ardour/audio_port.h"
|
||||||
|
@ -146,6 +147,7 @@ _thread_init_callback (void * /*arg*/)
|
||||||
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
|
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
|
||||||
|
|
||||||
MIDI::JACK_MidiPort::set_process_thread (pthread_self());
|
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);
|
typedef void (*_JackInfoShutdownCallback)(jack_status_t code, const char* reason, void *arg);
|
||||||
|
|
|
@ -99,6 +99,7 @@
|
||||||
#include "ardour/graph.h"
|
#include "ardour/graph.h"
|
||||||
|
|
||||||
#include "midi++/jack.h"
|
#include "midi++/jack.h"
|
||||||
|
#include "midi++/mmc.h"
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
|
@ -135,8 +136,7 @@ Session::Session (AudioEngine &eng,
|
||||||
: _engine (eng),
|
: _engine (eng),
|
||||||
_target_transport_speed (0.0),
|
_target_transport_speed (0.0),
|
||||||
_requested_return_frame (-1),
|
_requested_return_frame (-1),
|
||||||
mmc (0),
|
_mmc (0),
|
||||||
_mmc_port (default_mmc_port),
|
|
||||||
_mtc_port (default_mtc_port),
|
_mtc_port (default_mtc_port),
|
||||||
_midi_port (default_midi_port),
|
_midi_port (default_midi_port),
|
||||||
_midi_clock_port (default_midi_clock_port),
|
_midi_clock_port (default_midi_clock_port),
|
||||||
|
@ -305,7 +305,7 @@ Session::destroy ()
|
||||||
|
|
||||||
Crossfade::set_buffer_size (0);
|
Crossfade::set_buffer_size (0);
|
||||||
|
|
||||||
delete mmc;
|
delete _mmc;
|
||||||
|
|
||||||
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
|
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
|
||||||
playlists.reset ();
|
playlists.reset ();
|
||||||
|
@ -961,7 +961,7 @@ Session::enable_record ()
|
||||||
if (g_atomic_int_get (&_record_status) != Recording) {
|
if (g_atomic_int_get (&_record_status) != Recording) {
|
||||||
g_atomic_int_set (&_record_status, Recording);
|
g_atomic_int_set (&_record_status, Recording);
|
||||||
_last_record_location = _transport_frame;
|
_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()) {
|
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) {
|
if ((!Config->get_latched_record_enable () && !play_loop) || force) {
|
||||||
g_atomic_int_set (&_record_status, Disabled);
|
g_atomic_int_set (&_record_status, Disabled);
|
||||||
|
_mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
|
||||||
} else {
|
} else {
|
||||||
if (rs == Recording) {
|
if (rs == Recording) {
|
||||||
g_atomic_int_set (&_record_status, Enabled);
|
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()) {
|
if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
|
||||||
|
|
||||||
boost::shared_ptr<RouteList> rl = routes.reader ();
|
boost::shared_ptr<RouteList> rl = routes.reader ();
|
||||||
|
@ -1053,7 +1047,7 @@ Session::maybe_enable_record ()
|
||||||
enable_record ();
|
enable_record ();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame);
|
_mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
|
||||||
RecordStateChanged (); /* EMIT SIGNAL */
|
RecordStateChanged (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,10 +56,6 @@ using namespace PBD;
|
||||||
using namespace MIDI;
|
using namespace MIDI;
|
||||||
using namespace Glib;
|
using namespace Glib;
|
||||||
|
|
||||||
MachineControl::CommandSignature MMC_CommandSignature;
|
|
||||||
MachineControl::ResponseSignature MMC_ResponseSignature;
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Session::midi_panic()
|
Session::midi_panic()
|
||||||
{
|
{
|
||||||
|
@ -80,12 +76,6 @@ Session::use_config_midi_ports ()
|
||||||
{
|
{
|
||||||
string port_name;
|
string port_name;
|
||||||
|
|
||||||
if (default_mmc_port) {
|
|
||||||
set_mmc_port (default_mmc_port->name());
|
|
||||||
} else {
|
|
||||||
set_mmc_port ("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (default_mtc_port) {
|
if (default_mtc_port) {
|
||||||
set_mtc_port (default_mtc_port->name());
|
set_mtc_port (default_mtc_port->name());
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,90 +143,6 @@ Session::set_mtc_port (string port_tag)
|
||||||
return 0;
|
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
|
int
|
||||||
Session::set_midi_port (string /*port_tag*/)
|
Session::set_midi_port (string /*port_tag*/)
|
||||||
{
|
{
|
||||||
|
@ -324,26 +230,26 @@ Session::set_trace_midi_input (bool yn, MIDI::Port* port)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (_mmc_port) {
|
if (_mmc->port()) {
|
||||||
if ((input_parser = _mmc_port->input()) != 0) {
|
if ((input_parser = _mmc->port()->input()) != 0) {
|
||||||
input_parser->trace (yn, &cout, "input: ");
|
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) {
|
if ((input_parser = _mtc_port->input()) != 0) {
|
||||||
input_parser->trace (yn, &cout, "input: ");
|
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) {
|
if ((input_parser = _midi_port->input()) != 0) {
|
||||||
input_parser->trace (yn, &cout, "input: ");
|
input_parser->trace (yn, &cout, "input: ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_midi_clock_port
|
if (_midi_clock_port
|
||||||
&& _midi_clock_port != _mmc_port
|
&& _midi_clock_port != _mmc->port()
|
||||||
&& _midi_clock_port != _mtc_port
|
&& _midi_clock_port != _mtc_port
|
||||||
&& _midi_clock_port != _midi_port) {
|
&& _midi_clock_port != _midi_port) {
|
||||||
if ((input_parser = _midi_clock_port->input()) != 0) {
|
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: ");
|
output_parser->trace (yn, &cout, "output: ");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_mmc_port) {
|
if (_mmc->port()) {
|
||||||
if ((output_parser = _mmc_port->output()) != 0) {
|
if ((output_parser = _mmc->port()->output()) != 0) {
|
||||||
output_parser->trace (yn, &cout, "output: ");
|
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) {
|
if ((output_parser = _mtc_port->output()) != 0) {
|
||||||
output_parser->trace (yn, &cout, "output: ");
|
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) {
|
if ((output_parser = _midi_port->output()) != 0) {
|
||||||
output_parser->trace (yn, &cout, "output: ");
|
output_parser->trace (yn, &cout, "output: ");
|
||||||
}
|
}
|
||||||
|
@ -398,8 +304,8 @@ Session::get_trace_midi_input(MIDI::Port *port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_mmc_port) {
|
if (_mmc->port()) {
|
||||||
if ((input_parser = _mmc_port->input()) != 0) {
|
if ((input_parser = _mmc->port()->input()) != 0) {
|
||||||
return input_parser->tracing();
|
return input_parser->tracing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,8 +336,8 @@ Session::get_trace_midi_output(MIDI::Port *port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_mmc_port) {
|
if (_mmc->port()) {
|
||||||
if ((output_parser = _mmc_port->output()) != 0) {
|
if ((output_parser = _mmc->port()->output()) != 0) {
|
||||||
return output_parser->tracing();
|
return output_parser->tracing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -459,13 +365,6 @@ Session::setup_midi_control ()
|
||||||
outbound_mtc_timecode_frame = 0;
|
outbound_mtc_timecode_frame = 0;
|
||||||
next_quarter_frame_to_send = 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 */
|
/* Set up the qtr frame message */
|
||||||
|
|
||||||
mtc_msg[0] = 0xf1;
|
mtc_msg[0] = 0xf1;
|
||||||
|
@ -896,69 +795,6 @@ Session::send_midi_time_code_for_cycle(nframes_t nframes)
|
||||||
OUTBOUND MMC STUFF
|
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
|
bool
|
||||||
Session::mmc_step_timeout ()
|
Session::mmc_step_timeout ()
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "ardour/port.h"
|
#include "ardour/port.h"
|
||||||
|
|
||||||
#include "midi++/manager.h"
|
#include "midi++/manager.h"
|
||||||
|
#include "midi++/mmc.h"
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
|
@ -67,6 +68,8 @@ Session::process (nframes_t nframes)
|
||||||
post_transport ();
|
post_transport ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_mmc->flush_pending ();
|
||||||
|
|
||||||
_engine.main_thread()->get_buffers ();
|
_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);
|
_state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
|
||||||
_was_seamless = Config->get_seamless_loop ();
|
_was_seamless = Config->get_seamless_loop ();
|
||||||
_slave = 0;
|
_slave = 0;
|
||||||
session_send_mmc = false;
|
|
||||||
session_send_mtc = false;
|
session_send_mtc = false;
|
||||||
g_atomic_int_set (&_playback_load, 100);
|
g_atomic_int_set (&_playback_load, 100);
|
||||||
g_atomic_int_set (&_capture_load, 100);
|
g_atomic_int_set (&_capture_load, 100);
|
||||||
|
@ -299,6 +298,8 @@ Session::second_stage_init ()
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setup_midi_machine_control ();
|
||||||
|
|
||||||
// set_state() will call setup_raid_path(), but if it's a new session we need
|
// set_state() will call setup_raid_path(), but if it's a new session we need
|
||||||
// to call setup_raid_path() here.
|
// 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);
|
_state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
|
||||||
|
|
||||||
|
|
||||||
_locations.changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
|
_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));
|
_locations.added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
|
||||||
setup_click_sounds (0);
|
setup_click_sounds (0);
|
||||||
|
@ -352,8 +352,8 @@ Session::second_stage_init ()
|
||||||
|
|
||||||
send_full_time_code (0);
|
send_full_time_code (0);
|
||||||
_engine.transport_locate (0);
|
_engine.transport_locate (0);
|
||||||
deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
|
_mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
|
||||||
deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
|
_mmc->send (MIDI::MachineControlCommand (Timecode::Time ()));
|
||||||
|
|
||||||
MidiClockTicker::instance().set_session (this);
|
MidiClockTicker::instance().set_session (this);
|
||||||
MIDI::Name::MidiPatchManager::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;
|
error << _("Session: XML state has no options section") << endmsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setup_midi_machine_control ();
|
||||||
|
|
||||||
if (use_config_midi_ports ()) {
|
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") {
|
} 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") {
|
} 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") {
|
} else if (p == "midi-control") {
|
||||||
|
|
||||||
|
@ -3254,16 +3252,7 @@ Session::config_changed (std::string p, bool ours)
|
||||||
|
|
||||||
} else if (p == "send-mmc") {
|
} else if (p == "send-mmc") {
|
||||||
|
|
||||||
/* only set the internal flag if we have
|
_mmc->enable_send (Config->get_send_mmc ());
|
||||||
a port.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (_mmc_port != 0) {
|
|
||||||
session_send_mmc = Config->get_send_mmc();
|
|
||||||
} else {
|
|
||||||
mmc = 0;
|
|
||||||
session_send_mmc = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (p == "midi-feedback") {
|
} else if (p == "midi-feedback") {
|
||||||
|
|
||||||
|
@ -3311,17 +3300,17 @@ Session::config_changed (std::string p, bool ours)
|
||||||
sync_order_keys ("session");
|
sync_order_keys ("session");
|
||||||
} else if (p == "initial-program-change") {
|
} 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];
|
MIDI::byte buf[2];
|
||||||
|
|
||||||
buf[0] = MIDI::program; // channel zero by default
|
buf[0] = MIDI::program; // channel zero by default
|
||||||
buf[1] = (Config->get_initial_program_change() & 0x7f);
|
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") {
|
} 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];
|
MIDI::byte* buf = new MIDI::byte[2];
|
||||||
|
|
||||||
buf[0] = MIDI::program; // channel zero by default
|
buf[0] = MIDI::program; // channel zero by default
|
||||||
|
@ -3374,3 +3363,31 @@ Session::load_diskstreams_2X (XMLNode const & node, int)
|
||||||
|
|
||||||
return 0;
|
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]
|
// FIXME: where should this really be? [DR]
|
||||||
//send_full_time_code();
|
//send_full_time_code();
|
||||||
deliver_mmc (MIDI::MachineControl::cmdStop, 0);
|
_mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
|
||||||
deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
|
Timecode::Time time;
|
||||||
|
timecode_time_subframes (_transport_frame, time);
|
||||||
|
_mmc->send (MIDI::MachineControlCommand (time));
|
||||||
|
|
||||||
if (_transport_speed < 0.0f) {
|
if (_transport_speed < 0.0f) {
|
||||||
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
|
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;
|
_send_timecode_update = true;
|
||||||
|
|
||||||
if (with_mmc) {
|
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 */
|
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 */
|
TransportStateChange (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,20 +20,22 @@
|
||||||
#ifndef __midipp_mmc_h_h__
|
#ifndef __midipp_mmc_h_h__
|
||||||
#define __midipp_mmc_h_h__
|
#define __midipp_mmc_h_h__
|
||||||
|
|
||||||
|
#include "control_protocol/timecode.h"
|
||||||
#include "pbd/signals.h"
|
#include "pbd/signals.h"
|
||||||
|
#include "pbd/ringbuffer.h"
|
||||||
#include "midi++/types.h"
|
#include "midi++/types.h"
|
||||||
|
|
||||||
namespace MIDI {
|
namespace MIDI {
|
||||||
|
|
||||||
class Port;
|
class Port;
|
||||||
class Parser;
|
class Parser;
|
||||||
|
class MachineControlCommand;
|
||||||
|
|
||||||
|
/** Class to handle incoming and outgoing MIDI machine control messages */
|
||||||
class MachineControl
|
class MachineControl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef PBD::Signal1<void,MachineControl&> MMCSignal;
|
typedef PBD::Signal1<void,MachineControl&> MMCSignal;
|
||||||
typedef byte CommandSignature[60];
|
|
||||||
typedef byte ResponseSignature[60];
|
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
cmdStop = 0x1,
|
cmdStop = 0x1,
|
||||||
|
@ -84,19 +86,21 @@ class MachineControl
|
||||||
cmdResume = 0x7F
|
cmdResume = 0x7F
|
||||||
};
|
};
|
||||||
|
|
||||||
MachineControl (Port &port,
|
MachineControl ();
|
||||||
float MMCVersion,
|
void set_port (Port* p);
|
||||||
CommandSignature &cs,
|
|
||||||
ResponseSignature &rs);
|
|
||||||
|
|
||||||
Port &port() { return _port; }
|
Port* port() { return _port; }
|
||||||
|
|
||||||
void set_receive_device_id (byte id);
|
void set_receive_device_id (byte id);
|
||||||
void set_send_device_id (byte id);
|
void set_send_device_id (byte id);
|
||||||
byte receive_device_id () const { return _receive_device_id; }
|
byte receive_device_id () const { return _receive_device_id; }
|
||||||
byte send_device_id () const { return _send_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 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"
|
/* Signals to connect to if you want to run "callbacks"
|
||||||
when certain MMC commands are received.
|
when certain MMC commands are received.
|
||||||
|
@ -170,102 +174,50 @@ class MachineControl
|
||||||
|
|
||||||
PBD::Signal2<void,MachineControl &, int> Step;
|
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:
|
private:
|
||||||
byte _receive_device_id;
|
byte _receive_device_id;
|
||||||
byte _send_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);
|
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_masked_write (byte *, size_t len);
|
||||||
int do_locate (byte *, size_t len);
|
int do_locate (byte *, size_t len);
|
||||||
int do_step (byte *, size_t len);
|
int do_step (byte *, size_t len);
|
||||||
int do_shuttle (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);
|
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
|
} // namespace MIDI
|
||||||
|
|
||||||
#endif /* __midipp_mmc_h_h__ */
|
#endif /* __midipp_mmc_h_h__ */
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "control_protocol/timecode.h"
|
||||||
#include "pbd/error.h"
|
#include "pbd/error.h"
|
||||||
#include "midi++/mmc.h"
|
#include "midi++/mmc.h"
|
||||||
#include "midi++/port.h"
|
#include "midi++/port.h"
|
||||||
|
@ -29,6 +30,8 @@ using namespace std;
|
||||||
using namespace MIDI;
|
using namespace MIDI;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
|
|
||||||
|
pthread_t MachineControl::_sending_thread;
|
||||||
|
|
||||||
static std::map<int,string> mmc_cmd_map;
|
static std::map<int,string> mmc_cmd_map;
|
||||||
static void build_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*/,
|
MachineControl::MachineControl ()
|
||||||
CommandSignature & /*csig*/,
|
: _port (0)
|
||||||
ResponseSignature & /*rsig*/)
|
, _pending (16)
|
||||||
|
|
||||||
: _port (p)
|
|
||||||
{
|
{
|
||||||
Parser *parser;
|
|
||||||
|
|
||||||
build_mmc_cmd_map ();
|
build_mmc_cmd_map ();
|
||||||
|
|
||||||
_receive_device_id = 0;
|
_receive_device_id = 0;
|
||||||
_send_device_id = 0x7f;
|
_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 {
|
} else {
|
||||||
warning << "MMC connected to a non-input port: useless!"
|
warning << "MMC connected to a non-input port: useless!" << endmsg;
|
||||||
<< endmsg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +233,6 @@ MachineControl::set_send_device_id (byte id)
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MachineControl::is_mmc (byte *sysex_buf, size_t len)
|
MachineControl::is_mmc (byte *sysex_buf, size_t len)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (len < 4 || len > 48) {
|
if (len < 4 || len > 48) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -247,7 +252,6 @@ MachineControl::is_mmc (byte *sysex_buf, size_t len)
|
||||||
|
|
||||||
void
|
void
|
||||||
MachineControl::process_mmc_message (Parser &, byte *msg, size_t len)
|
MachineControl::process_mmc_message (Parser &, byte *msg, size_t len)
|
||||||
|
|
||||||
{
|
{
|
||||||
size_t skiplen;
|
size_t skiplen;
|
||||||
byte *mmc_msg;
|
byte *mmc_msg;
|
||||||
|
@ -455,7 +459,6 @@ MachineControl::process_mmc_message (Parser &, byte *msg, size_t len)
|
||||||
|
|
||||||
int
|
int
|
||||||
MachineControl::do_masked_write (byte *msg, size_t len)
|
MachineControl::do_masked_write (byte *msg, size_t len)
|
||||||
|
|
||||||
{
|
{
|
||||||
/* return the number of bytes "consumed" */
|
/* return the number of bytes "consumed" */
|
||||||
|
|
||||||
|
@ -555,12 +558,10 @@ MachineControl::write_track_status (byte *msg, size_t /*len*/, byte reg)
|
||||||
|
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case 0x4f:
|
case 0x4f:
|
||||||
trackRecordStatus[base_track+n] = val;
|
|
||||||
TrackRecordStatusChange (*this, base_track+n, val);
|
TrackRecordStatusChange (*this, base_track+n, val);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x62:
|
case 0x62:
|
||||||
trackMute[base_track+n] = val;
|
|
||||||
TrackMuteChange (*this, base_track+n, val);
|
TrackMuteChange (*this, base_track+n, val);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -571,7 +572,6 @@ MachineControl::write_track_status (byte *msg, size_t /*len*/, byte reg)
|
||||||
|
|
||||||
int
|
int
|
||||||
MachineControl::do_locate (byte *msg, size_t /*msglen*/)
|
MachineControl::do_locate (byte *msg, size_t /*msglen*/)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (msg[2] == 0) {
|
if (msg[2] == 0) {
|
||||||
warning << "MIDI::MMC: locate [I/F] command not supported"
|
warning << "MIDI::MMC: locate [I/F] command not supported"
|
||||||
|
@ -600,7 +600,6 @@ MachineControl::do_step (byte *msg, size_t /*msglen*/)
|
||||||
|
|
||||||
int
|
int
|
||||||
MachineControl::do_shuttle (byte *msg, size_t /*msglen*/)
|
MachineControl::do_shuttle (byte *msg, size_t /*msglen*/)
|
||||||
|
|
||||||
{
|
{
|
||||||
size_t forward;
|
size_t forward;
|
||||||
byte sh = msg[2];
|
byte sh = msg[2];
|
||||||
|
@ -630,3 +629,97 @@ MachineControl::do_shuttle (byte *msg, size_t /*msglen*/)
|
||||||
return 0;
|
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.source += ' alsa_sequencer_midiport.cc '
|
||||||
obj.cxxflags += [ '-DWITH_ALSA' ]
|
obj.cxxflags += [ '-DWITH_ALSA' ]
|
||||||
obj.export_incdirs = ['.']
|
obj.export_incdirs = ['.']
|
||||||
obj.includes = ['.']
|
obj.includes = ['.', '../surfaces/control_protocol']
|
||||||
obj.name = 'libmidipp'
|
obj.name = 'libmidipp'
|
||||||
obj.target = 'midipp'
|
obj.target = 'midipp'
|
||||||
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX COREAUDIO'
|
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX COREAUDIO'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user