reorganize push 2 code and logic to better handle device arrival after program startup
Note: we do not handle device departure correctly yet, mostly because the shadow (pad) port has a retained reference somewhere
This commit is contained in:
parent
8cff66c7e0
commit
1b830f2604
@ -71,7 +71,6 @@ Push2Canvas::vblank ()
|
|||||||
/* re-render dirty areas, if any */
|
/* re-render dirty areas, if any */
|
||||||
|
|
||||||
if (expose ()) {
|
if (expose ()) {
|
||||||
DEBUG_TRACE (DEBUG::Push2, "re-blit to device frame buffer\n");
|
|
||||||
/* something rendered, update device_frame_buffer */
|
/* something rendered, update device_frame_buffer */
|
||||||
blit_to_device_frame_buffer ();
|
blit_to_device_frame_buffer ();
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ Push2::Push2 (ARDOUR::Session& s)
|
|||||||
: ControlProtocol (s, string (X_("Ableton Push 2")))
|
: ControlProtocol (s, string (X_("Ableton Push 2")))
|
||||||
, AbstractUI<Push2Request> (name())
|
, AbstractUI<Push2Request> (name())
|
||||||
, handle (0)
|
, handle (0)
|
||||||
|
, in_use (false)
|
||||||
, _modifier_state (None)
|
, _modifier_state (None)
|
||||||
, splash_start (0)
|
, splash_start (0)
|
||||||
, _current_layout (0)
|
, _current_layout (0)
|
||||||
@ -93,6 +94,9 @@ Push2::Push2 (ARDOUR::Session& s)
|
|||||||
, contrast_color (LED::Green)
|
, contrast_color (LED::Green)
|
||||||
, in_range_select (false)
|
, in_range_select (false)
|
||||||
{
|
{
|
||||||
|
/* we're going to need this */
|
||||||
|
|
||||||
|
libusb_init (NULL);
|
||||||
|
|
||||||
build_maps ();
|
build_maps ();
|
||||||
build_color_map ();
|
build_color_map ();
|
||||||
@ -101,17 +105,21 @@ Push2::Push2 (ARDOUR::Session& s)
|
|||||||
/* master cannot be removed, so no need to connect to going-away signal */
|
/* master cannot be removed, so no need to connect to going-away signal */
|
||||||
master = session->master_out ();
|
master = session->master_out ();
|
||||||
|
|
||||||
if (open ()) {
|
|
||||||
throw failed_constructor ();
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
|
ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
|
||||||
|
|
||||||
/* catch current selection, if any */
|
/* allocate graphics layouts, even though we're not using them yet */
|
||||||
{
|
|
||||||
StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
|
_canvas = new Push2Canvas (*this, 960, 160);
|
||||||
stripable_selection_change (sp);
|
mix_layout = new MixLayout (*this, *session, "globalmix");
|
||||||
}
|
scale_layout = new ScaleLayout (*this, *session, "scale");
|
||||||
|
track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
|
||||||
|
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 */
|
/* catch arrival and departure of Push2 itself */
|
||||||
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
|
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
|
||||||
@ -119,83 +127,103 @@ Push2::Push2 (ARDOUR::Session& s)
|
|||||||
/* Catch port connections and disconnections */
|
/* Catch port connections and disconnections */
|
||||||
ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
|
ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
|
||||||
|
|
||||||
/* ports might already be there */
|
/* Push 2 ports might already be there */
|
||||||
port_registration_handler ();
|
port_registration_handler ();
|
||||||
}
|
}
|
||||||
|
|
||||||
Push2::~Push2 ()
|
Push2::~Push2 ()
|
||||||
{
|
{
|
||||||
stop ();
|
selection_connection.disconnect ();
|
||||||
|
|
||||||
|
stop_event_loop (); /* this will call stop_using_device () in Quit request handler */
|
||||||
|
device_release ();
|
||||||
|
ports_release ();
|
||||||
|
|
||||||
|
if (_current_layout) {
|
||||||
|
_canvas->root()->remove (_current_layout);
|
||||||
|
_current_layout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
delete track_mix_layout;
|
|
||||||
delete mix_layout;
|
delete mix_layout;
|
||||||
|
mix_layout = 0;
|
||||||
delete scale_layout;
|
delete scale_layout;
|
||||||
|
scale_layout = 0;
|
||||||
|
delete splash_layout;
|
||||||
|
splash_layout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Push2::port_registration_handler ()
|
Push2::run_event_loop ()
|
||||||
{
|
{
|
||||||
if (!_async_in && !_async_out) {
|
DEBUG_TRACE (DEBUG::Push2, "start event loop\n");
|
||||||
/* ports not registered yet */
|
BaseUI::run ();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (_async_in->connected() && _async_out->connected()) {
|
void
|
||||||
/* don't waste cycles here */
|
Push2::stop_event_loop ()
|
||||||
return;
|
{
|
||||||
}
|
DEBUG_TRACE (DEBUG::Push2, "stop event loop\n");
|
||||||
|
BaseUI::quit ();
|
||||||
string input_port_name = X_("Ableton Push 2 MIDI 1 in");
|
|
||||||
string output_port_name = X_("Ableton Push 2 MIDI 1 out");
|
|
||||||
vector<string> in;
|
|
||||||
vector<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()) {
|
|
||||||
cerr << "Push2: both ports found\n";
|
|
||||||
cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
|
|
||||||
if (!_async_in->connected()) {
|
|
||||||
AudioEngine::instance()->connect (_async_in->name(), in.front());
|
|
||||||
}
|
|
||||||
if (!_async_out->connected()) {
|
|
||||||
AudioEngine::instance()->connect (_async_out->name(), out.front());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Push2::open ()
|
Push2::begin_using_device ()
|
||||||
{
|
{
|
||||||
int err;
|
DEBUG_TRACE (DEBUG::Push2, "begin using device\n");
|
||||||
|
|
||||||
if (handle) {
|
/* set up periodic task used to push a frame buffer to the
|
||||||
/* already open */
|
* device (25fps). The device can handle 60fps, but we don't
|
||||||
|
* need that frame rate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
|
||||||
|
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 ();
|
||||||
|
set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
|
||||||
|
splash ();
|
||||||
|
|
||||||
|
/* catch current selection, if any so that we can wire up the pads if appropriate */
|
||||||
|
{
|
||||||
|
StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
|
||||||
|
stripable_selection_change (sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
request_pressure_mode ();
|
||||||
|
|
||||||
|
in_use = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Push2::stop_using_device ()
|
||||||
|
{
|
||||||
|
DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
|
||||||
|
|
||||||
|
if (!in_use) {
|
||||||
|
DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
|
init_buttons (false);
|
||||||
return -1;
|
strip_buttons_off ();
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = libusb_claim_interface (handle, 0x00))) {
|
vblank_connection.disconnect ();
|
||||||
return -1;
|
session_connections.drop_connections ();
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
in_use = false;
|
||||||
_canvas = new Push2Canvas (*this, 960, 160);
|
return 0;
|
||||||
mix_layout = new MixLayout (*this, *session, "globalmix");
|
}
|
||||||
scale_layout = new ScaleLayout (*this, *session, "scale");
|
|
||||||
track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
|
int
|
||||||
splash_layout = new SplashLayout (*this, *session, "splash");
|
Push2::ports_acquire ()
|
||||||
} catch (...) {
|
{
|
||||||
error << _("Cannot construct Canvas for display") << endmsg;
|
DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
|
||||||
libusb_release_interface (handle, 0x00);
|
|
||||||
libusb_close (handle);
|
|
||||||
handle = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup ports */
|
/* setup ports */
|
||||||
|
|
||||||
@ -203,12 +231,13 @@ Push2::open ()
|
|||||||
_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
|
_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
|
||||||
|
|
||||||
if (_async_in == 0 || _async_out == 0) {
|
if (_async_in == 0 || _async_out == 0) {
|
||||||
|
DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We do not add our ports to the input/output bundles because we don't
|
/* 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
|
* want users wiring them by hand. They could use JACK tools if they
|
||||||
* really insist on that.
|
* really insist on that (and use JACK)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
|
_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
|
||||||
@ -237,26 +266,21 @@ Push2::open ()
|
|||||||
|
|
||||||
connect_to_parser ();
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
list<boost::shared_ptr<ARDOUR::Bundle> >
|
void
|
||||||
Push2::bundles ()
|
Push2::ports_release ()
|
||||||
{
|
{
|
||||||
list<boost::shared_ptr<ARDOUR::Bundle> > b;
|
DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
|
||||||
|
|
||||||
if (_output_bundle) {
|
|
||||||
b.push_back (_output_bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
Push2::close ()
|
|
||||||
{
|
|
||||||
init_buttons (false);
|
|
||||||
strip_buttons_off ();
|
|
||||||
|
|
||||||
/* wait for button data to be flushed */
|
/* wait for button data to be flushed */
|
||||||
AsyncMIDIPort* asp;
|
AsyncMIDIPort* asp;
|
||||||
@ -270,29 +294,57 @@ Push2::close ()
|
|||||||
_async_out.reset ((ARDOUR::Port*) 0);
|
_async_out.reset ((ARDOUR::Port*) 0);
|
||||||
_input_port = 0;
|
_input_port = 0;
|
||||||
_output_port = 0;
|
_output_port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
periodic_connection.disconnect ();
|
int
|
||||||
session_connections.drop_connections ();
|
Push2::device_acquire ()
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
if (_current_layout) {
|
DEBUG_TRACE (DEBUG::Push2, "acquiring device\n");
|
||||||
_canvas->root()->remove (_current_layout);
|
|
||||||
_current_layout = 0;
|
if (handle) {
|
||||||
|
DEBUG_TRACE (DEBUG::Push2, "open() called with handle already set\n");
|
||||||
|
/* already open */
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete mix_layout;
|
if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
|
||||||
mix_layout = 0;
|
DEBUG_TRACE (DEBUG::Push2, "failed to open USB handle\n");
|
||||||
delete scale_layout;
|
return -1;
|
||||||
scale_layout = 0;
|
}
|
||||||
delete splash_layout;
|
|
||||||
splash_layout = 0;
|
|
||||||
|
|
||||||
|
if ((err = libusb_claim_interface (handle, 0x00))) {
|
||||||
|
DEBUG_TRACE (DEBUG::Push2, "failed to claim USB device\n");
|
||||||
|
libusb_close (handle);
|
||||||
|
handle = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Push2::device_release ()
|
||||||
|
{
|
||||||
|
DEBUG_TRACE (DEBUG::Push2, "releasing device\n");
|
||||||
if (handle) {
|
if (handle) {
|
||||||
libusb_release_interface (handle, 0x00);
|
libusb_release_interface (handle, 0x00);
|
||||||
libusb_close (handle);
|
libusb_close (handle);
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
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
|
void
|
||||||
@ -366,16 +418,6 @@ Push2::init_buttons (bool startup)
|
|||||||
bool
|
bool
|
||||||
Push2::probe ()
|
Push2::probe ()
|
||||||
{
|
{
|
||||||
libusb_device_handle *h;
|
|
||||||
libusb_init (NULL);
|
|
||||||
|
|
||||||
if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
|
|
||||||
DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_close (h);
|
|
||||||
DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,19 +441,10 @@ Push2::do_request (Push2Request * req)
|
|||||||
|
|
||||||
} else if (req->type == Quit) {
|
} else if (req->type == Quit) {
|
||||||
|
|
||||||
stop ();
|
stop_using_device ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
Push2::stop ()
|
|
||||||
{
|
|
||||||
BaseUI::quit ();
|
|
||||||
close ();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Push2::splash ()
|
Push2::splash ()
|
||||||
{
|
{
|
||||||
@ -454,49 +487,20 @@ Push2::set_active (bool yn)
|
|||||||
|
|
||||||
if (yn) {
|
if (yn) {
|
||||||
|
|
||||||
/* start event loop */
|
if (device_acquire ()) {
|
||||||
|
|
||||||
BaseUI::run ();
|
|
||||||
|
|
||||||
if (open ()) {
|
|
||||||
DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
|
|
||||||
close ();
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Connect input port to event loop */
|
if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
|
||||||
|
begin_using_device ();
|
||||||
AsyncMIDIPort* asp;
|
} else {
|
||||||
|
/* begin_using_device () will get called once we're connected */
|
||||||
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());
|
|
||||||
|
|
||||||
connect_session_signals ();
|
|
||||||
|
|
||||||
/* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
|
|
||||||
vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
|
|
||||||
vblank_timeout->attach (main_loop()->get_context());
|
|
||||||
|
|
||||||
|
|
||||||
Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
|
|
||||||
periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
|
|
||||||
periodic_timeout->attach (main_loop()->get_context());
|
|
||||||
|
|
||||||
init_buttons (true);
|
|
||||||
init_touch_strip ();
|
|
||||||
set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
|
|
||||||
splash ();
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
/* Control Protocol Manager never calls us with false, but
|
||||||
stop ();
|
* insteads destroys us.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlProtocol::set_active (yn);
|
ControlProtocol::set_active (yn);
|
||||||
@ -537,27 +541,23 @@ Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
|
|||||||
|
|
||||||
if (ioc & IO_IN) {
|
if (ioc & IO_IN) {
|
||||||
|
|
||||||
// DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
|
DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
|
||||||
|
|
||||||
AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
|
AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
|
||||||
if (asp) {
|
if (asp) {
|
||||||
asp->clear ();
|
asp->clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
//DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
|
DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
|
||||||
|
if (in_use) {
|
||||||
framepos_t now = AudioEngine::instance()->sample_time();
|
framepos_t now = AudioEngine::instance()->sample_time();
|
||||||
port->parse (now);
|
port->parse (now);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
Push2::periodic ()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Push2::connect_to_parser ()
|
Push2::connect_to_parser ()
|
||||||
{
|
{
|
||||||
@ -1166,6 +1166,47 @@ Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
|
|||||||
return matched;
|
return matched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Push2::port_registration_handler ()
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
string input_port_name = X_("system:midi_capture_1319078870");
|
||||||
|
string output_port_name = X_("system:midi_playback_3409210341");
|
||||||
|
#else
|
||||||
|
string input_port_name = X_("Ableton Push 2 MIDI 1 in");
|
||||||
|
string output_port_name = X_("Ableton Push 2 MIDI 1 out");
|
||||||
|
#endif
|
||||||
|
vector<string> in;
|
||||||
|
vector<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()) {
|
||||||
|
cerr << "Push2: both ports found\n";
|
||||||
|
cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
|
||||||
|
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
|
bool
|
||||||
Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
|
Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
|
||||||
{
|
{
|
||||||
@ -1190,11 +1231,14 @@ Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boo
|
|||||||
connection_state &= ~OutputConnected;
|
connection_state &= ~OutputConnected;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
|
DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
|
||||||
/* not our ports */
|
/* not our ports */
|
||||||
return false;
|
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)) {
|
if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
|
||||||
|
|
||||||
/* XXX this is a horrible hack. Without a short sleep here,
|
/* XXX this is a horrible hack. Without a short sleep here,
|
||||||
@ -1203,11 +1247,19 @@ Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boo
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
g_usleep (100000);
|
g_usleep (100000);
|
||||||
DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
|
DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
|
||||||
connected ();
|
|
||||||
|
/* 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 {
|
} else {
|
||||||
DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
|
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 */
|
ConnectionChange (); /* emit signal for our GUI */
|
||||||
@ -1217,12 +1269,6 @@ Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boo
|
|||||||
return true; /* connection status changed */
|
return true; /* connection status changed */
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Push2::connected ()
|
|
||||||
{
|
|
||||||
request_pressure_mode ();
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::shared_ptr<Port>
|
boost::shared_ptr<Port>
|
||||||
Push2::output_port()
|
Push2::output_port()
|
||||||
{
|
{
|
||||||
|
@ -370,12 +370,19 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
libusb_device_handle *handle;
|
libusb_device_handle *handle;
|
||||||
|
bool in_use;
|
||||||
ModifierState _modifier_state;
|
ModifierState _modifier_state;
|
||||||
|
|
||||||
void do_request (Push2Request*);
|
void do_request (Push2Request*);
|
||||||
int stop ();
|
|
||||||
int open ();
|
int begin_using_device ();
|
||||||
int close ();
|
int stop_using_device ();
|
||||||
|
int device_acquire ();
|
||||||
|
void device_release ();
|
||||||
|
int ports_acquire ();
|
||||||
|
void ports_release ();
|
||||||
|
void run_event_loop ();
|
||||||
|
void stop_event_loop ();
|
||||||
|
|
||||||
void relax () {}
|
void relax () {}
|
||||||
|
|
||||||
@ -431,9 +438,6 @@ class Push2 : public ARDOUR::ControlProtocol
|
|||||||
|
|
||||||
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
|
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
|
||||||
|
|
||||||
sigc::connection periodic_connection;
|
|
||||||
bool periodic ();
|
|
||||||
|
|
||||||
void thread_init ();
|
void thread_init ();
|
||||||
|
|
||||||
PBD::ScopedConnectionList session_connections;
|
PBD::ScopedConnectionList session_connections;
|
||||||
|
Loading…
Reference in New Issue
Block a user