surfaces; convert push2 to inherit from MIDISurface
This commit is contained in:
parent
4fbf3028aa
commit
99712e7585
|
@ -87,6 +87,7 @@ namespace PBD {
|
|||
LIBARDOUR_API extern DebugBits ProcessThreads;
|
||||
LIBARDOUR_API extern DebugBits Processors;
|
||||
LIBARDOUR_API extern DebugBits Push2;
|
||||
LIBARDOUR_API extern DebugBits MIDISurface;
|
||||
LIBARDOUR_API extern DebugBits Selection;
|
||||
LIBARDOUR_API extern DebugBits SessionEvents;
|
||||
LIBARDOUR_API extern DebugBits Slave;
|
||||
|
|
|
@ -82,6 +82,7 @@ PBD::DebugBits PBD::DEBUG::Ports = PBD::new_debug_bit ("Ports");
|
|||
PBD::DebugBits PBD::DEBUG::ProcessThreads = PBD::new_debug_bit ("processthreads");
|
||||
PBD::DebugBits PBD::DEBUG::Processors = PBD::new_debug_bit ("processors");
|
||||
PBD::DebugBits PBD::DEBUG::Push2 = PBD::new_debug_bit ("push2");
|
||||
PBD::DebugBits PBD::DEBUG::MIDISurface = PBD::new_debug_bit ("midisurface");
|
||||
PBD::DebugBits PBD::DEBUG::Selection = PBD::new_debug_bit ("selection");
|
||||
PBD::DebugBits PBD::DEBUG::SessionEvents = PBD::new_debug_bit ("sessionevents");
|
||||
PBD::DebugBits PBD::DEBUG::Slave = PBD::new_debug_bit ("slave");
|
||||
|
|
|
@ -33,11 +33,13 @@ using namespace ARDOUR;
|
|||
using namespace Glib;
|
||||
using namespace PBD;
|
||||
|
||||
MIDISurface::MIDISurface (ARDOUR::Session& s, std::string const & namestr, bool use_pad_filter)
|
||||
MIDISurface::MIDISurface (ARDOUR::Session& s, std::string const & namestr, std::string const & port_prefix, bool use_pad_filter)
|
||||
: ControlProtocol (s, namestr)
|
||||
, AbstractUI<MidiSurfaceRequest> (namestr)
|
||||
, with_pad_filter (use_pad_filter)
|
||||
, _in_use (false)
|
||||
, port_name_prefix (port_prefix)
|
||||
, _connection_state (ConnectionState (0))
|
||||
{
|
||||
|
||||
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::port_registration_handler, this), this);
|
||||
|
@ -52,8 +54,8 @@ MIDISurface::ports_acquire ()
|
|||
|
||||
/* setup ports */
|
||||
|
||||
_async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
|
||||
_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
|
||||
_async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, string_compose (X_("%1 in"), port_name_prefix), true);
|
||||
_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, string_compose (X_("%1 out"), port_name_prefix), true);
|
||||
|
||||
if (_async_in == 0 || _async_out == 0) {
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "cannot register ports\n");
|
||||
|
@ -74,7 +76,7 @@ MIDISurface::ports_acquire ()
|
|||
*/
|
||||
|
||||
if (with_pad_filter) {
|
||||
boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->add_shadow_port (string_compose (_("%1 Pads"), X_("Push 2")), boost::bind (&MIDISurface::pad_filter, this, _1, _2));
|
||||
boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->add_shadow_port (string_compose (_("%1 Pads"), port_name_prefix), boost::bind (&MIDISurface::pad_filter, this, _1, _2));
|
||||
boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
|
||||
|
||||
if (shadow_port) {
|
||||
|
@ -378,12 +380,25 @@ int
|
|||
MIDISurface::begin_using_device ()
|
||||
{
|
||||
_in_use = true;
|
||||
connect_session_signals ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MIDISurface::stop_using_device ()
|
||||
{
|
||||
session_connections.drop_connections ();
|
||||
_in_use = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::drop ()
|
||||
{
|
||||
/* do this before stopping the event loop, so that we don't get any notifications */
|
||||
port_connections.drop_connections ();
|
||||
|
||||
stop_using_device ();
|
||||
device_release ();
|
||||
ports_release ();
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class MIDISurface : public ARDOUR::ControlProtocol
|
|||
, public AbstractUI<MidiSurfaceRequest>
|
||||
{
|
||||
public:
|
||||
MIDISurface (ARDOUR::Session&, std::string const & name, bool use_pad_filter);
|
||||
MIDISurface (ARDOUR::Session&, std::string const & name, std::string const & port_name_prefix, bool use_pad_filter);
|
||||
~MIDISurface ();
|
||||
|
||||
boost::shared_ptr<ARDOUR::Port> input_port();
|
||||
|
@ -71,9 +71,10 @@ class MIDISurface : public ARDOUR::ControlProtocol
|
|||
|
||||
CONTROL_PROTOCOL_THREADS_NEED_TEMPO_MAP_DECL();
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool with_pad_filter;
|
||||
bool _in_use;
|
||||
std::string port_name_prefix;
|
||||
MIDI::Port* _input_port;
|
||||
MIDI::Port* _output_port;
|
||||
|
||||
|
@ -122,4 +123,6 @@ class MIDISurface : public ARDOUR::ControlProtocol
|
|||
virtual int stop_using_device ();
|
||||
virtual int device_acquire () = 0;
|
||||
virtual void device_release () = 0;
|
||||
|
||||
void drop ();
|
||||
};
|
||||
|
|
|
@ -97,15 +97,12 @@ row_interval_semitones (const Push2::RowInterval row_interval, const bool inkey)
|
|||
}
|
||||
|
||||
Push2::Push2 (ARDOUR::Session& s)
|
||||
: ControlProtocol (s, std::string (X_("Ableton Push 2")))
|
||||
, AbstractUI<Push2Request> (name())
|
||||
: MIDISurface (s, X_("Ableton Push 2"), X_("Push 2"), true)
|
||||
, _handle (0)
|
||||
, _in_use (false)
|
||||
, _modifier_state (None)
|
||||
, _splash_start (0)
|
||||
, _current_layout (0)
|
||||
, _previous_layout (0)
|
||||
, _connection_state (ConnectionState (0))
|
||||
, _gui (0)
|
||||
, _mode (MusicalMode::IonianMajor)
|
||||
, _row_interval (Fourth)
|
||||
|
@ -141,31 +138,13 @@ Push2::Push2 (ARDOUR::Session& s)
|
|||
_splash_layout = new SplashLayout (*this, *session, "splash");
|
||||
|
||||
run_event_loop ();
|
||||
|
||||
/* Ports exist for the life of this instance */
|
||||
|
||||
ports_acquire ();
|
||||
|
||||
/* catch arrival and departure of Push2 itself */
|
||||
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
|
||||
|
||||
/* Catch port connections and disconnections */
|
||||
ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
|
||||
|
||||
/* Push 2 ports might already be there */
|
||||
port_registration_handler ();
|
||||
}
|
||||
|
||||
Push2::~Push2 ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Push2, "push2 control surface object being destroyed\n");
|
||||
|
||||
/* do this before stopping the event loop, so that we don't get any notifications */
|
||||
port_connections.drop_connections ();
|
||||
|
||||
stop_using_device ();
|
||||
device_release ();
|
||||
ports_release ();
|
||||
MIDISurface::drop ();
|
||||
|
||||
if (_current_layout) {
|
||||
_canvas->root()->remove (_current_layout);
|
||||
|
@ -206,6 +185,7 @@ Push2::begin_using_device ()
|
|||
{
|
||||
DEBUG_TRACE (DEBUG::Push2, "begin using device\n");
|
||||
|
||||
|
||||
/* set up periodic task used to push a frame buffer to the
|
||||
* device (25fps). The device can handle 60fps, but we don't
|
||||
* need that frame rate.
|
||||
|
@ -215,8 +195,6 @@ Push2::begin_using_device ()
|
|||
_vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
|
||||
vblank_timeout->attach (main_loop()->get_context());
|
||||
|
||||
connect_session_signals ();
|
||||
|
||||
init_buttons (true);
|
||||
init_touch_strip ();
|
||||
reset_pad_colors ();
|
||||
|
@ -227,9 +205,7 @@ Push2::begin_using_device ()
|
|||
|
||||
request_pressure_mode ();
|
||||
|
||||
_in_use = true;
|
||||
|
||||
return 0;
|
||||
return MIDISurface::begin_using_device ();
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -252,89 +228,8 @@ Push2::stop_using_device ()
|
|||
}
|
||||
|
||||
_vblank_connection.disconnect ();
|
||||
session_connections.drop_connections ();
|
||||
|
||||
_in_use = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Push2::ports_acquire ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
|
||||
|
||||
/* setup ports */
|
||||
|
||||
_async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
|
||||
_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
|
||||
|
||||
if (_async_in == 0 || _async_out == 0) {
|
||||
DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We do not add our ports to the input/output bundles because we don't
|
||||
* want users wiring them by hand. They could use JACK tools if they
|
||||
* really insist on that (and use JACK)
|
||||
*/
|
||||
|
||||
_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
|
||||
_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
|
||||
|
||||
/* Create a shadow port where, depending on the state of the surface,
|
||||
* we will make pad note on/off events appear. The surface code will
|
||||
* automatically this port to the first selected MIDI track.
|
||||
*/
|
||||
|
||||
boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->add_shadow_port (string_compose (_("%1 Pads"), X_("Push 2")), boost::bind (&Push2::pad_filter, this, _1, _2));
|
||||
boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
|
||||
|
||||
if (shadow_port) {
|
||||
|
||||
_output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
|
||||
|
||||
_output_bundle->add_channel (
|
||||
shadow_port->name(),
|
||||
ARDOUR::DataType::MIDI,
|
||||
session->engine().make_port_name_non_relative (shadow_port->name())
|
||||
);
|
||||
}
|
||||
|
||||
session->BundleAddedOrRemoved ();
|
||||
|
||||
connect_to_parser ();
|
||||
|
||||
/* Connect input port to event loop */
|
||||
|
||||
AsyncMIDIPort* asp;
|
||||
|
||||
asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
|
||||
asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
|
||||
asp->xthread().attach (main_loop()->get_context());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
Push2::ports_release ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
|
||||
|
||||
/* wait for button data to be flushed */
|
||||
AsyncMIDIPort* asp;
|
||||
asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
|
||||
asp->drain (10000, 500000);
|
||||
|
||||
{
|
||||
Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
|
||||
AudioEngine::instance()->unregister_port (_async_in);
|
||||
AudioEngine::instance()->unregister_port (_async_out);
|
||||
}
|
||||
|
||||
_async_in.reset ((ARDOUR::Port*) 0);
|
||||
_async_out.reset ((ARDOUR::Port*) 0);
|
||||
_input_port = 0;
|
||||
_output_port = 0;
|
||||
return MIDISurface::stop_using_device ();
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -369,6 +264,7 @@ void
|
|||
Push2::device_release ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Push2, "releasing device\n");
|
||||
|
||||
if (_handle) {
|
||||
libusb_release_interface (_handle, 0x00);
|
||||
libusb_close (_handle);
|
||||
|
@ -376,18 +272,6 @@ Push2::device_release ()
|
|||
}
|
||||
}
|
||||
|
||||
list<boost::shared_ptr<ARDOUR::Bundle> >
|
||||
Push2::bundles ()
|
||||
{
|
||||
list<boost::shared_ptr<ARDOUR::Bundle> > b;
|
||||
|
||||
if (_output_bundle) {
|
||||
b.push_back (_output_bundle);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void
|
||||
Push2::strip_buttons_off ()
|
||||
{
|
||||
|
@ -474,19 +358,6 @@ Push2::request_factory (uint32_t num_requests)
|
|||
return request_buffer_factory (num_requests);
|
||||
}
|
||||
|
||||
void
|
||||
Push2::do_request (Push2Request * req)
|
||||
{
|
||||
if (req->type == CallSlot) {
|
||||
|
||||
call_slot (MISSING_INVALIDATOR, req->the_slot);
|
||||
|
||||
} else if (req->type == Quit) {
|
||||
|
||||
stop_using_device ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Push2::splash ()
|
||||
{
|
||||
|
@ -566,59 +437,6 @@ Push2::init_touch_strip ()
|
|||
write (msg);
|
||||
}
|
||||
|
||||
void
|
||||
Push2::write (const MidiByteArray& data)
|
||||
{
|
||||
/* immediate delivery */
|
||||
_output_port->write (&data[0], data.size(), 0);
|
||||
}
|
||||
|
||||
bool
|
||||
Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
|
||||
{
|
||||
if (ioc & ~IO_IN) {
|
||||
DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioc & IO_IN) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Push2, string_compose ("something happened on %1\n", port->name()));
|
||||
|
||||
AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
|
||||
if (asp) {
|
||||
asp->clear ();
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
|
||||
if (_in_use) {
|
||||
samplepos_t now = AudioEngine::instance()->sample_time();
|
||||
port->parse (now);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Push2::connect_to_parser ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
|
||||
|
||||
MIDI::Parser* p = _input_port->parser();
|
||||
|
||||
/* Incoming sysex */
|
||||
p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
|
||||
/* V-Pot messages are Controller */
|
||||
p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
|
||||
/* Button messages are NoteOn */
|
||||
p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
|
||||
/* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
|
||||
p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
|
||||
/* Fader messages are Pitchbend */
|
||||
p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
|
||||
}
|
||||
|
||||
void
|
||||
Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
|
||||
{
|
||||
|
@ -884,37 +702,6 @@ Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
Push2::thread_init ()
|
||||
{
|
||||
pthread_set_name (event_loop_name().c_str());
|
||||
|
||||
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
|
||||
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
|
||||
|
||||
set_thread_priority ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2::connect_session_signals()
|
||||
{
|
||||
// receive routes added
|
||||
//session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
|
||||
// receive VCAs added
|
||||
//session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
|
||||
|
||||
// receive record state toggled
|
||||
session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
|
||||
// receive transport state changed
|
||||
session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
|
||||
session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
|
||||
// receive punch-in and punch-out
|
||||
Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
|
||||
session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
|
||||
// receive rude solo changed
|
||||
session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
|
||||
}
|
||||
|
||||
void
|
||||
Push2::notify_record_state_changed ()
|
||||
{
|
||||
|
@ -1014,15 +801,7 @@ Push2::notify_solo_active_changed (bool yn)
|
|||
XMLNode&
|
||||
Push2::get_state() const
|
||||
{
|
||||
XMLNode& node (ControlProtocol::get_state());
|
||||
XMLNode* child;
|
||||
|
||||
child = new XMLNode (X_("Input"));
|
||||
child->add_child_nocopy (_async_in->get_state());
|
||||
node.add_child_nocopy (*child);
|
||||
child = new XMLNode (X_("Output"));
|
||||
child->add_child_nocopy (_async_out->get_state());
|
||||
node.add_child_nocopy (*child);
|
||||
XMLNode& node (MIDISurface::get_state());
|
||||
|
||||
node.set_property (X_("root"), _scale_root);
|
||||
node.set_property (X_("root-octave"), _root_octave);
|
||||
|
@ -1039,28 +818,10 @@ Push2::set_state (const XMLNode & node, int version)
|
|||
|
||||
int retval = 0;
|
||||
|
||||
if (ControlProtocol::set_state (node, version)) {
|
||||
if (MIDISurface::set_state (node, version)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
XMLNode* child;
|
||||
|
||||
if ((child = node.child (X_("Input"))) != 0) {
|
||||
XMLNode* portnode = child->child (Port::state_node_name.c_str());
|
||||
if (portnode) {
|
||||
portnode->remove_property ("name");
|
||||
_async_in->set_state (*portnode, version);
|
||||
}
|
||||
}
|
||||
|
||||
if ((child = node.child (X_("Output"))) != 0) {
|
||||
XMLNode* portnode = child->child (Port::state_node_name.c_str());
|
||||
if (portnode) {
|
||||
portnode->remove_property ("name");
|
||||
_async_out->set_state (*portnode, version);
|
||||
}
|
||||
}
|
||||
|
||||
node.get_property (X_("root"), _scale_root);
|
||||
node.get_property (X_("root-octave"), _root_octave);
|
||||
node.get_property (X_("in-key"), _in_key);
|
||||
|
@ -1199,117 +960,30 @@ Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
|
|||
return matched;
|
||||
}
|
||||
|
||||
void
|
||||
Push2::port_registration_handler ()
|
||||
std::string
|
||||
Push2::input_port_name () const
|
||||
{
|
||||
if (!_async_in || !_async_out) {
|
||||
/* ports not registered yet */
|
||||
return;
|
||||
}
|
||||
|
||||
if (_async_in->connected() && _async_out->connected()) {
|
||||
/* don't waste cycles here */
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* the origin of the numeric magic identifiers is known only to Ableton
|
||||
and may change in time. This is part of how CoreMIDI works.
|
||||
*/
|
||||
std::string input_port_name = X_("system:midi_capture_1319078870");
|
||||
std::string output_port_name = X_("system:midi_playback_3409210341");
|
||||
return X_("system:midi_capture_1319078870");
|
||||
#else
|
||||
std::string input_port_name = X_("Ableton Push 2 MIDI 1 in");
|
||||
std::string output_port_name = X_("Ableton Push 2 MIDI 1 out");
|
||||
return X_("Ableton Push 2 MIDI 1 in");
|
||||
#endif
|
||||
std::vector<std::string> in;
|
||||
std::vector<std::string> out;
|
||||
|
||||
AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
|
||||
AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
|
||||
|
||||
if (!in.empty() && !out.empty()) {
|
||||
if (!_async_in->connected()) {
|
||||
AudioEngine::instance()->connect (_async_in->name(), in.front());
|
||||
}
|
||||
if (!_async_out->connected()) {
|
||||
AudioEngine::instance()->connect (_async_out->name(), out.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
|
||||
std::string
|
||||
Push2::output_port_name () const
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
|
||||
if (!_input_port || !_output_port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
|
||||
std::string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
|
||||
|
||||
if (ni == name1 || ni == name2) {
|
||||
if (yn) {
|
||||
_connection_state |= InputConnected;
|
||||
} else {
|
||||
_connection_state &= ~InputConnected;
|
||||
}
|
||||
} else if (no == name1 || no == name2) {
|
||||
if (yn) {
|
||||
_connection_state |= OutputConnected;
|
||||
} else {
|
||||
_connection_state &= ~OutputConnected;
|
||||
}
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
|
||||
/* not our ports */
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Push2, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
|
||||
name1, name2, yn));
|
||||
|
||||
if ((_connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
|
||||
|
||||
/* XXX this is a horrible hack. Without a short sleep here,
|
||||
something prevents the device wakeup messages from being
|
||||
sent and/or the responses from being received.
|
||||
*/
|
||||
|
||||
g_usleep (100000);
|
||||
DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
|
||||
|
||||
/* may not have the device open if it was just plugged
|
||||
in. Really need USB device detection rather than MIDI port
|
||||
detection for this to work well.
|
||||
*/
|
||||
|
||||
device_acquire ();
|
||||
begin_using_device ();
|
||||
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
|
||||
stop_using_device ();
|
||||
}
|
||||
|
||||
ConnectionChange (); /* emit signal for our GUI */
|
||||
|
||||
DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
|
||||
|
||||
return true; /* connection status changed */
|
||||
}
|
||||
|
||||
boost::shared_ptr<Port>
|
||||
Push2::output_port()
|
||||
{
|
||||
return _async_out;
|
||||
}
|
||||
|
||||
boost::shared_ptr<Port>
|
||||
Push2::input_port()
|
||||
{
|
||||
return _async_in;
|
||||
#ifdef __APPLE__
|
||||
/* the origin of the numeric magic identifiers is known only to Ableton
|
||||
and may change in time. This is part of how CoreMIDI works.
|
||||
*/
|
||||
return X_("system:midi_playback_3409210341");
|
||||
#else
|
||||
return X_("Ableton Push 2 MIDI 1 out");
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
|
||||
#include "gtkmm2ext/colors.h"
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
#include "midi_surface/midi_byte_array.h"
|
||||
#include "midi_surface/midi_surface.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Parser;
|
||||
|
@ -55,18 +56,11 @@ namespace ARDOUR {
|
|||
|
||||
namespace ArdourSurface {
|
||||
|
||||
struct Push2Request : public BaseUI::BaseRequestObject {
|
||||
public:
|
||||
Push2Request () {}
|
||||
~Push2Request () {}
|
||||
};
|
||||
|
||||
class P2GUI;
|
||||
class Push2Layout;
|
||||
class Push2Canvas;
|
||||
|
||||
class Push2 : public ARDOUR::ControlProtocol
|
||||
, public AbstractUI<Push2Request>
|
||||
class Push2 : public MIDISurface
|
||||
{
|
||||
public:
|
||||
enum ButtonID {
|
||||
|
@ -305,7 +299,8 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||
static bool probe ();
|
||||
static void* request_factory (uint32_t);
|
||||
|
||||
std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
|
||||
std::string input_port_name () const;
|
||||
std::string output_port_name () const;
|
||||
|
||||
bool has_editor () const { return true; }
|
||||
void* get_gui () const;
|
||||
|
@ -315,11 +310,6 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||
XMLNode& get_state() const;
|
||||
int set_state (const XMLNode & node, int version);
|
||||
|
||||
PBD::Signal0<void> ConnectionChange;
|
||||
|
||||
boost::shared_ptr<ARDOUR::Port> input_port();
|
||||
boost::shared_ptr<ARDOUR::Port> output_port();
|
||||
|
||||
int pad_note (int row, int col) const;
|
||||
PBD::Signal0<void> PadChange;
|
||||
|
||||
|
@ -438,8 +428,6 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||
|
||||
void strip_buttons_off ();
|
||||
|
||||
void write (const MidiByteArray&);
|
||||
|
||||
uint8_t get_color_index (Gtkmm2ext::Color rgba);
|
||||
Gtkmm2ext::Color get_color (ColorName);
|
||||
|
||||
|
@ -449,8 +437,6 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||
|
||||
libusb_device_handle* usb_handle() const { return _handle; }
|
||||
|
||||
ARDOUR::Session & get_session() { return *session; }
|
||||
|
||||
bool stop_down () const { return _stop_down; }
|
||||
|
||||
typedef std::map<int,boost::shared_ptr<Pad> > PadMap;
|
||||
|
@ -459,21 +445,14 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||
boost::shared_ptr<Pad> pad_by_xy (int x, int y);
|
||||
boost::shared_ptr<Button> lower_button_by_column (uint32_t col);
|
||||
|
||||
CONTROL_PROTOCOL_THREADS_NEED_TEMPO_MAP_DECL();
|
||||
|
||||
private:
|
||||
libusb_device_handle* _handle;
|
||||
bool _in_use;
|
||||
ModifierState _modifier_state;
|
||||
|
||||
void do_request (Push2Request*);
|
||||
|
||||
int begin_using_device ();
|
||||
int stop_using_device ();
|
||||
int device_acquire ();
|
||||
void device_release ();
|
||||
int ports_acquire ();
|
||||
void ports_release ();
|
||||
void run_event_loop ();
|
||||
void stop_event_loop ();
|
||||
|
||||
|
@ -515,29 +494,12 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||
|
||||
void build_maps ();
|
||||
|
||||
// Bundle to represent our input ports
|
||||
boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
|
||||
// Bundle to represent our output ports
|
||||
boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
|
||||
|
||||
MIDI::Port* _input_port;
|
||||
MIDI::Port* _output_port;
|
||||
boost::shared_ptr<ARDOUR::Port> _async_in;
|
||||
boost::shared_ptr<ARDOUR::Port> _async_out;
|
||||
|
||||
void connect_to_parser ();
|
||||
void handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t);
|
||||
void handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes*);
|
||||
void handle_midi_note_on_message (MIDI::Parser&, MIDI::EventTwoBytes*);
|
||||
void handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes*);
|
||||
void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
|
||||
|
||||
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
|
||||
|
||||
void thread_init ();
|
||||
|
||||
PBD::ScopedConnectionList session_connections;
|
||||
void connect_session_signals ();
|
||||
void notify_record_state_changed ();
|
||||
void notify_transport_state_changed ();
|
||||
void notify_loop_state_changed ();
|
||||
|
@ -649,19 +611,6 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||
|
||||
boost::weak_ptr<ARDOUR::MidiTrack> _current_pad_target;
|
||||
|
||||
void port_registration_handler ();
|
||||
|
||||
enum ConnectionState {
|
||||
InputConnected = 0x1,
|
||||
OutputConnected = 0x2
|
||||
};
|
||||
|
||||
int _connection_state;
|
||||
|
||||
bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
|
||||
PBD::ScopedConnectionList port_connections;
|
||||
void connected ();
|
||||
|
||||
/* GUI */
|
||||
|
||||
mutable P2GUI* _gui;
|
||||
|
|
|
@ -38,11 +38,11 @@ def build(bld):
|
|||
obj.defines = [ 'PACKAGE="ardour_push2"' ]
|
||||
obj.defines += [ 'ARDOURSURFACE_DLL_EXPORTS' ]
|
||||
obj.defines += [ 'VERSIONSTRING="' + bld.env['VERSION'] + '"' ]
|
||||
obj.includes = ['.', './push2']
|
||||
obj.includes = ['.', '..', './push2']
|
||||
obj.name = 'libardour_push2'
|
||||
obj.target = 'ardour_push2'
|
||||
obj.uselib = 'CAIROMM PANGOMM USB GTKMM SIGCPP XML OSX'
|
||||
obj.use = 'libardour libardour_cp libgtkmm2ext libpbd libevoral libcanvas libtemporal'
|
||||
obj.use = 'libardour libardour_cp lib_midisurface libgtkmm2ext libpbd libevoral libcanvas libtemporal'
|
||||
obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces')
|
||||
|
||||
def shutdown():
|
||||
|
|
Loading…
Reference in New Issue