#include "timecode/time.h"
#include "timecode/bbt_time.h"
+#include "ardour/amp.h"
#include "ardour/async_midi_port.h"
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "gtkmm2ext/gui_thread.h"
#include "gtkmm2ext/rgb_macros.h"
+#include "canvas/colors.h"
+
#include "canvas.h"
#include "gui.h"
#include "layout.h"
#include "pbd/i18n.h"
+#ifdef PLATFORM_WINDOWS
+#define random() rand()
+#endif
+
using namespace ARDOUR;
using namespace std;
using namespace PBD;
#define ABLETON 0x2982
#define PUSH2 0x1967
-__attribute__((constructor)) static void
-register_enums ()
-{
- EnumWriter& enum_writer (EnumWriter::instance());
- vector<int> i;
- vector<string> s;
-
- MusicalMode::Type mode;
-
-#define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
-#define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
-
- REGISTER_CLASS_ENUM (MusicalMode,Dorian);
- REGISTER_CLASS_ENUM (MusicalMode, IonianMajor);
- REGISTER_CLASS_ENUM (MusicalMode, Minor);
- REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor);
- REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending);
- REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending);
- REGISTER_CLASS_ENUM (MusicalMode, Phrygian);
- REGISTER_CLASS_ENUM (MusicalMode, Lydian);
- REGISTER_CLASS_ENUM (MusicalMode, Mixolydian);
- REGISTER_CLASS_ENUM (MusicalMode, Aeolian);
- REGISTER_CLASS_ENUM (MusicalMode, Locrian);
- REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor);
- REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor);
- REGISTER_CLASS_ENUM (MusicalMode, Chromatic);
- REGISTER_CLASS_ENUM (MusicalMode, BluesScale);
- REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor);
- REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor);
- REGISTER_CLASS_ENUM (MusicalMode, Oriental);
- REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic);
- REGISTER_CLASS_ENUM (MusicalMode, Enigmatic);
- REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi);
- REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor);
- REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor);
- REGISTER_CLASS_ENUM (MusicalMode, Kumoi);
- REGISTER_CLASS_ENUM (MusicalMode, Iwato);
- REGISTER_CLASS_ENUM (MusicalMode, Hindu);
- REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone);
- REGISTER_CLASS_ENUM (MusicalMode, Pelog);
- REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy);
- REGISTER_CLASS_ENUM (MusicalMode, Overtone);
- REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone);
- REGISTER_CLASS_ENUM (MusicalMode, Arabian);
- REGISTER_CLASS_ENUM (MusicalMode, Balinese);
- REGISTER_CLASS_ENUM (MusicalMode, Gypsy);
- REGISTER_CLASS_ENUM (MusicalMode, Mohammedan);
- REGISTER_CLASS_ENUM (MusicalMode, Javanese);
- REGISTER_CLASS_ENUM (MusicalMode, Persian);
- REGISTER_CLASS_ENUM (MusicalMode, Algerian);
- REGISTER (mode);
-}
-
Push2::Push2 (ARDOUR::Session& s)
: ControlProtocol (s, string (X_("Ableton Push 2")))
, AbstractUI<Push2Request> (name())
, handle (0)
+ , in_use (false)
, _modifier_state (None)
, splash_start (0)
, _current_layout (0)
, _pressure_mode (AfterTouch)
, selection_color (LED::Green)
, contrast_color (LED::Green)
+ , in_range_select (false)
{
+ /* we're going to need this */
+
+ libusb_init (NULL);
build_maps ();
build_color_map ();
/* master cannot be removed, so no need to connect to going-away signal */
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);
- /* catch current selection, if any */
- {
- StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
- stripable_selection_change (sp);
- }
+ /* allocate graphics layouts, even though we're not using them yet */
+
+ _canvas = new Push2Canvas (*this, 960, 160);
+ 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 */
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
/* 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);
- /* ports might already be there */
+ /* Push 2 ports might already be there */
port_registration_handler ();
}
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;
+ mix_layout = 0;
delete scale_layout;
+ scale_layout = 0;
+ delete splash_layout;
+ splash_layout = 0;
}
void
-Push2::port_registration_handler ()
+Push2::run_event_loop ()
{
- if (!_async_in && !_async_out) {
- /* ports not registered yet */
- return;
- }
+ DEBUG_TRACE (DEBUG::Push2, "start event loop\n");
+ BaseUI::run ();
+}
- if (_async_in->connected() && _async_out->connected()) {
- /* don't waste cycles here */
- return;
- }
+void
+Push2::stop_event_loop ()
+{
+ 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;
+int
+Push2::begin_using_device ()
+{
+ DEBUG_TRACE (DEBUG::Push2, "begin using device\n");
- 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);
+ /* 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.
+ */
- 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());
- }
+ 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::open ()
+Push2::stop_using_device ()
{
- int err;
+ DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
- if (handle) {
- /* already open */
+ if (!in_use) {
+ DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
return 0;
}
- if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
- return -1;
- }
+ init_buttons (false);
+ strip_buttons_off ();
- if ((err = libusb_claim_interface (handle, 0x00))) {
- return -1;
- }
+ vblank_connection.disconnect ();
+ session_connections.drop_connections ();
- try {
- _canvas = new Push2Canvas (*this, 960, 160);
- mix_layout = new MixLayout (*this, *session);
- scale_layout = new ScaleLayout (*this, *session);
- track_mix_layout = new TrackMixLayout (*this, *session);
- splash_layout = new SplashLayout (*this, *session);
- } catch (...) {
- error << _("Cannot construct Canvas for display") << endmsg;
- libusb_release_interface (handle, 0x00);
- libusb_close (handle);
- return -1;
- }
+ in_use = false;
+ return 0;
+}
+
+int
+Push2::ports_acquire ()
+{
+ DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
/* setup ports */
_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.
+ * really insist on that (and use JACK)
*/
_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
connect_to_parser ();
- return 0;
-}
+ /* Connect input port to event loop */
-list<boost::shared_ptr<ARDOUR::Bundle> >
-Push2::bundles ()
-{
- list<boost::shared_ptr<ARDOUR::Bundle> > b;
+ AsyncMIDIPort* asp;
- if (_output_bundle) {
- b.push_back (_output_bundle);
- }
+ 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 b;
+ return 0;
}
-int
-Push2::close ()
+void
+Push2::ports_release ()
{
- init_buttons (false);
+ DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
/* wait for button data to be flushed */
AsyncMIDIPort* asp;
_async_out.reset ((ARDOUR::Port*) 0);
_input_port = 0;
_output_port = 0;
+}
- periodic_connection.disconnect ();
- session_connections.drop_connections ();
+int
+Push2::device_acquire ()
+{
+ int err;
- if (_current_layout) {
- _canvas->root()->remove (_current_layout);
- _current_layout = 0;
+ DEBUG_TRACE (DEBUG::Push2, "acquiring device\n");
+
+ if (handle) {
+ DEBUG_TRACE (DEBUG::Push2, "open() called with handle already set\n");
+ /* already open */
+ return 0;
}
- delete mix_layout;
- mix_layout = 0;
- delete scale_layout;
- scale_layout = 0;
- delete splash_layout;
- splash_layout = 0;
+ if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
+ DEBUG_TRACE (DEBUG::Push2, "failed to open USB handle\n");
+ return -1;
+ }
+
+ 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) {
libusb_release_interface (handle, 0x00);
libusb_close (handle);
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
*/
ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
- Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
+ Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
};
write (b->state_msg());
}
- /* Strip buttons should all be off (black) by default. They will change
- * color to reflect various conditions
- */
-
- strip_buttons_off ();
-
if (startup) {
/* all other buttons are off (black) */
bool
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;
}
} else if (req->type == Quit) {
- stop ();
+ stop_using_device ();
}
}
-int
-Push2::stop ()
-{
- BaseUI::quit ();
- close ();
- return 0;
-}
-
-
void
Push2::splash ()
{
}
}
+ if (_current_layout) {
+ _current_layout->update_meters ();
+ _current_layout->update_clocks ();
+ }
+
_canvas->vblank();
return true;
if (yn) {
- /* start event loop */
-
- BaseUI::run ();
-
- if (open ()) {
- DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
- close ();
+ if (device_acquire ()) {
return -1;
}
- /* 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());
-
- 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 ();
+ if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
+ begin_using_device ();
+ } else {
+ /* begin_using_device () will get called once we're connected */
+ }
} else {
-
- stop ();
-
+ /* Control Protocol Manager never calls us with false, but
+ * insteads destroys us.
+ */
}
ControlProtocol::set_active (yn);
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);
if (asp) {
asp->clear ();
}
- //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
- framepos_t now = AudioEngine::instance()->sample_time();
- port->parse (now);
+ DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
+ if (in_use) {
+ framepos_t now = AudioEngine::instance()->sample_time();
+ port->parse (now);
+ }
}
return true;
}
-bool
-Push2::periodic ()
-{
- return true;
-}
-
void
Push2::connect_to_parser ()
{
Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
{
DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
+
+ if (sz < 8) {
+ return;
+ }
+
MidiByteArray msg (sz, raw_bytes);
- MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
- MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
-
- if (msg == aftertouch_mode_response) {
- _pressure_mode = AfterTouch;
- PressureModeChange (AfterTouch);
- cerr << "Pressure mod eis after\n";
- } else if (msg == polypress_mode_response) {
- _pressure_mode = PolyPressure;
- PressureModeChange (PolyPressure);
- cerr << "Pressure mod eis poly\n";
+ MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
+
+ if (!push2_sysex_header.compare_n (msg, 6)) {
+ return;
+ }
+
+ switch (msg[6]) {
+ case 0x1f: /* pressure mode */
+ if (msg[7] == 0x0) {
+ _pressure_mode = AfterTouch;
+ PressureModeChange (AfterTouch);
+ cerr << "Pressure mode is after\n";
+ } else {
+ _pressure_mode = PolyPressure;
+ PressureModeChange (PolyPressure);
+ cerr << "Pressure mode is poly\n";
+ }
+ break;
}
}
for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
Pad* pad = pi->second;
- if (pad->do_when_pressed == Pad::FlashOn) {
- pad->set_color (LED::White);
- pad->set_state (LED::OneShot24th);
- write (pad->state_msg());
- } else if (pad->do_when_pressed == Pad::FlashOff) {
- pad->set_color (contrast_color);
- pad->set_state (LED::OneShot24th);
- write (pad->state_msg());
- }
+ pad->set_color (contrast_color);
+ pad->set_state (LED::OneShot24th);
+ write (pad->state_msg());
}
}
void
Push2::other_vpot (int n, int delta)
{
+ boost::shared_ptr<Amp> click_gain;
switch (n) {
case 0:
+ /* tempo control */
break;
case 1:
+ /* metronome gain control */
+ click_gain = session->click_gain();
+ if (click_gain) {
+ boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
+ if (ac) {
+ ac->set_value (ac->interface_to_internal (
+ min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
+ PBD::Controllable::UseGroup);
+ }
+ }
break;
case 2:
/* master gain control */
if (master) {
boost::shared_ptr<AutomationControl> ac = master->gain_control();
if (ac) {
- ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
+ ac->set_value (ac->interface_to_internal (
+ min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
+ PBD::Controllable::UseGroup);
}
}
break;
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
Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
{
- DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
+ DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
if (!_input_port || !_output_port) {
return false;
}
connection_state &= ~OutputConnected;
}
} 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 */
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,
*/
g_usleep (100000);
- DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
- connected ();
+ 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 */
return true; /* connection status changed */
}
-void
-Push2::connected ()
-{
- request_pressure_mode ();
-}
-
boost::shared_ptr<Port>
Push2::output_port()
{
return 0;
}
+void
+Push2::update_selection_color ()
+{
+ boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
+
+ if (!current_midi_track) {
+ return;
+ }
+
+ selection_color = get_color_index (current_midi_track->presentation_info().color());
+ contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
+
+ reset_pad_colors ();
+}
+
void
Push2::reset_pad_colors ()
{
int drum_note = 36;
+ fn_pad_map.clear ();
+
for (int row = 0; row < 8; ++row) {
for (int col = 0; col < 4; ++col) {
/* disconnect from pad port, if appropriate */
if (current_midi_track && pad_port) {
+
+ /* XXX this could possibly leave dangling MIDI notes.
+ *
+ * A general libardour fix is required. It isn't obvious
+ * how note resolution can be done unless disconnecting
+ * becomes "slow" (i.e. deferred for as long as it takes
+ * to resolve notes).
+ */
current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
}
return i->second;
}
- int r, g, b, a;
- UINT_TO_RGBA (rgba, &r, &g, &b, &a);
- int w = 204; /* not sure where/when we should get this value */
+ double dr, dg, db, da;
+ int r, g, b;
+ ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
+ int w = 126; /* not sure where/when we should get this value */
+
+
+ r = (int) floor (255.0 * dr);
+ g = (int) floor (255.0 * dg);
+ b = (int) floor (255.0 * db);
/* get a free index */
color_map_free_list.pop();
}
- MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7);
- MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
-
+ MidiByteArray palette_msg (17,
+ 0xf0,
+ 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
+ 0x00, /* index = 7 */
+ 0x00, 0x00, /* r = 8 & 9 */
+ 0x00, 0x00, /* g = 10 & 11 */
+ 0x00, 0x00, /* b = 12 & 13 */
+ 0x00, 0x00, /* w (a?) = 14 & 15*/
+ 0xf7);
palette_msg[7] = index;
palette_msg[8] = r & 0x7f;
- palette_msg[9] = r & 0x1;
+ palette_msg[9] = (r & 0x80) >> 7;
palette_msg[10] = g & 0x7f;
- palette_msg[11] = g & 0x1;
+ palette_msg[11] = (g & 0x80) >> 7;
palette_msg[12] = b & 0x7f;
- palette_msg[13] = b & 0x1;
+ palette_msg[13] = (b & 0x80) >> 7;
palette_msg[14] = w & 0x7f;
- palette_msg[15] = w & 0x1;
+ palette_msg[15] = w & 0x80;
write (palette_msg);
+
+ MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
write (update_pallette_msg);
color_map[rgba] = index;
void
Push2::set_current_layout (Push2Layout* layout)
{
- if (_current_layout) {
- _current_layout->hide ();
- _canvas->root()->remove (_current_layout);
- _previous_layout = _current_layout;
- }
+ if (layout && layout == _current_layout) {
+ _current_layout->show ();
+ } else {
- /* turn off all strip buttons - let new layout set them if it
- * wants/needs to
- */
+ if (_current_layout) {
+ _current_layout->hide ();
+ _canvas->root()->remove (_current_layout);
+ _previous_layout = _current_layout;
+ }
- strip_buttons_off ();
+ _current_layout = layout;
- _current_layout = layout;
+ if (_current_layout) {
+ _canvas->root()->add (_current_layout);
+ _current_layout->show ();
+ }
- if (_current_layout) {
- _canvas->root()->add (_current_layout);
- _current_layout->show ();
- }
- _canvas->request_redraw ();
+ _canvas->request_redraw ();
+ }
}
void