mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 14:54:56 +01:00
Skeleton for NI Maschine2 Surface
This commit is contained in:
parent
0c2e25a506
commit
0a6d1ab06e
25 changed files with 3338 additions and 1 deletions
|
|
@ -13,7 +13,7 @@ export GTK2_RC_FILES=/nonexistent
|
||||||
# can find all the components.
|
# can find all the components.
|
||||||
#
|
#
|
||||||
|
|
||||||
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/faderport8:$libs/surfaces/faderport:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/wiimote:$libs/surfaces/push2
|
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/faderport8:$libs/surfaces/faderport:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/wiimote:$libs/surfaces/push2:$libs/surfaces/maschine2
|
||||||
export ARDOUR_PANNER_PATH=$libs/panners
|
export ARDOUR_PANNER_PATH=$libs/panners
|
||||||
export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
|
export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
|
||||||
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
||||||
|
|
|
||||||
460
libs/surfaces/maschine2/callbacks.cc
Normal file
460
libs/surfaces/maschine2/callbacks.cc
Normal file
|
|
@ -0,0 +1,460 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ardour/session.h"
|
||||||
|
#include "gtkmm2ext/colors.h"
|
||||||
|
#include "gtkmm2ext/actions.h"
|
||||||
|
#include "gtkmm2ext/gui_thread.h"
|
||||||
|
#include "pbd/i18n.h"
|
||||||
|
|
||||||
|
#include "maschine2.h"
|
||||||
|
#include "m2controls.h"
|
||||||
|
|
||||||
|
#include "midi++/port.h"
|
||||||
|
|
||||||
|
#define COLOR_WHITE 0xffffffff
|
||||||
|
#define COLOR_GRAY 0x606060ff
|
||||||
|
#define COLOR_BLACK 0x000000ff
|
||||||
|
|
||||||
|
using namespace ARDOUR;
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::connect_signals ()
|
||||||
|
{
|
||||||
|
// TODO: use some convenience macros here
|
||||||
|
|
||||||
|
/* Signals */
|
||||||
|
session->TransportStateChange.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_transport_state_changed, this), this);
|
||||||
|
session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_loop_state_changed, this), this);
|
||||||
|
session->RecordStateChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_record_state_changed, this), this);
|
||||||
|
Config->ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_parameter_changed, this, _1), this);
|
||||||
|
session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_parameter_changed, this, _1), this);
|
||||||
|
session->DirtyChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_session_dirty_changed, this), this);
|
||||||
|
session->history().Changed.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_history_changed, this), this);
|
||||||
|
|
||||||
|
/* Actions */
|
||||||
|
Glib::RefPtr<Gtk::Action> act;
|
||||||
|
#if 0
|
||||||
|
act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
|
||||||
|
tact->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_grid_change));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
act = ActionManager::get_action (X_("Editor"), X_("snap-off"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
|
||||||
|
}
|
||||||
|
act = ActionManager::get_action (X_("Editor"), X_("snap-magnetic"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
|
||||||
|
}
|
||||||
|
act = ActionManager::get_action (X_("Editor"), X_("snap-normal"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Surface events */
|
||||||
|
_ctrl->button (M2Contols::Play)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_play, this));
|
||||||
|
_ctrl->button (M2Contols::Rec)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_record, this));
|
||||||
|
_ctrl->button (M2Contols::Loop)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_loop, this));
|
||||||
|
_ctrl->button (M2Contols::Metronom)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_metronom, this));
|
||||||
|
_ctrl->button (M2Contols::GotoStart)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_rewind, this));
|
||||||
|
_ctrl->button (M2Contols::FastRewind)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Transport", "RewindSlow"));
|
||||||
|
_ctrl->button (M2Contols::FastForward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Transport", "ForwardSlow"));
|
||||||
|
_ctrl->button (M2Contols::Panic)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "MIDI", "panic"));
|
||||||
|
_ctrl->button (M2Contols::JumpForward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "jump-forward-to-mark"));
|
||||||
|
_ctrl->button (M2Contols::JumpBackward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "jump-backward-to-mark"));
|
||||||
|
|
||||||
|
_ctrl->button (M2Contols::Grid)->pressed.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_pressed, this), gui_context());
|
||||||
|
_ctrl->button (M2Contols::Grid)->released.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_released, this), gui_context());
|
||||||
|
_ctrl->button (M2Contols::Grid)->changed.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_changed, this, _1), gui_context());
|
||||||
|
|
||||||
|
_ctrl->button (M2Contols::Save)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Common", "Save"));
|
||||||
|
_ctrl->button (M2Contols::Undo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "undo"));
|
||||||
|
_ctrl->button (M2Contols::Redo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "redo"));
|
||||||
|
|
||||||
|
_ctrl->button (M2Contols::MasterVolume)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::handle_master_change, this, MST_VOLUME));
|
||||||
|
_ctrl->button (M2Contols::MasterTempo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::handle_master_change, this, MST_TEMPO));
|
||||||
|
|
||||||
|
_ctrl->button (M2Contols::EncoderWheel)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_encoder, this));
|
||||||
|
_ctrl->encoder (0)->changed.connect_same_thread (button_connections, boost::bind (&Maschine2::encoder_master, this, _1));
|
||||||
|
|
||||||
|
for (unsigned int pad = 0; pad < 16; ++pad) {
|
||||||
|
_ctrl->pad (pad)->event.connect_same_thread (button_connections, boost::bind (&Maschine2::pad_event, this, pad, _1, _2));
|
||||||
|
_ctrl->pad (pad)->changed.connect_same_thread (button_connections, boost::bind (&Maschine2::pad_change, this, pad, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set initial values */
|
||||||
|
notify_record_state_changed ();
|
||||||
|
notify_transport_state_changed ();
|
||||||
|
notify_loop_state_changed ();
|
||||||
|
notify_parameter_changed ("clicking");
|
||||||
|
notify_snap_change ();
|
||||||
|
notify_session_dirty_changed ();
|
||||||
|
notify_history_changed ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_record_state_changed ()
|
||||||
|
{
|
||||||
|
switch (session->record_status ()) {
|
||||||
|
case Session::Disabled:
|
||||||
|
_ctrl->button (M2Contols::Rec)->set_color (0);
|
||||||
|
_ctrl->button (M2Contols::Rec)->set_blinking (false);
|
||||||
|
break;
|
||||||
|
case Session::Enabled:
|
||||||
|
_ctrl->button (M2Contols::Rec)->set_color (COLOR_WHITE);
|
||||||
|
_ctrl->button (M2Contols::Rec)->set_blinking (true);
|
||||||
|
break;
|
||||||
|
case Session::Recording:
|
||||||
|
_ctrl->button (M2Contols::Rec)->set_color (COLOR_WHITE);
|
||||||
|
_ctrl->button (M2Contols::Rec)->set_blinking (false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_transport_state_changed ()
|
||||||
|
{
|
||||||
|
if (session->transport_rolling ()) {
|
||||||
|
_ctrl->button (M2Contols::Play)->set_color (COLOR_WHITE);
|
||||||
|
} else {
|
||||||
|
_ctrl->button (M2Contols::Play)->set_color (0);
|
||||||
|
}
|
||||||
|
notify_loop_state_changed ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_loop_state_changed ()
|
||||||
|
{
|
||||||
|
bool looping = false;
|
||||||
|
Location* looploc = session->locations ()->auto_loop_location ();
|
||||||
|
if (looploc && session->get_play_loop ()) {
|
||||||
|
looping = true;
|
||||||
|
}
|
||||||
|
_ctrl->button (M2Contols::Loop)->set_color (looping ? COLOR_GRAY : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_parameter_changed (std::string param)
|
||||||
|
{
|
||||||
|
if (param == "clicking") {
|
||||||
|
_ctrl->button (M2Contols::Metronom)->set_color (Config->get_clicking () ? COLOR_GRAY : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void
|
||||||
|
Maschine2::notify_grid_change ()
|
||||||
|
{
|
||||||
|
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
|
||||||
|
_ctrl->button (M2Contols::Grid)->set_color (tact->get_active () ? COLOR_WHITE : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_snap_change ()
|
||||||
|
{
|
||||||
|
uint32_t rgba = 0;
|
||||||
|
if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("snap-magnetic"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
if (ract->get_active ()) { rgba = COLOR_GRAY; }
|
||||||
|
}
|
||||||
|
act = ActionManager::get_action (X_("Editor"), X_("snap-normal"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
if (ract->get_active ()) { rgba = COLOR_WHITE; }
|
||||||
|
}
|
||||||
|
|
||||||
|
_ctrl->button (M2Contols::Grid)->set_color (rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_session_dirty_changed ()
|
||||||
|
{
|
||||||
|
bool is_dirty = session->dirty ();
|
||||||
|
_ctrl->button (M2Contols::Save)->set_color (is_dirty ? COLOR_WHITE : COLOR_BLACK);
|
||||||
|
_ctrl->button (M2Contols::Save)->set_blinking (is_dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_history_changed ()
|
||||||
|
{
|
||||||
|
_ctrl->button (M2Contols::Redo)->set_color (session->redo_depth() > 0 ? COLOR_WHITE : COLOR_BLACK);
|
||||||
|
_ctrl->button (M2Contols::Undo)->set_color (session->undo_depth() > 0 ? COLOR_WHITE : COLOR_BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_play ()
|
||||||
|
{
|
||||||
|
if (session->transport_rolling ()) {
|
||||||
|
transport_stop ();
|
||||||
|
} else {
|
||||||
|
transport_play ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_record ()
|
||||||
|
{
|
||||||
|
set_record_enable (!get_record_enabled ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_loop ()
|
||||||
|
{
|
||||||
|
loop_toggle ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_metronom ()
|
||||||
|
{
|
||||||
|
Config->set_clicking (!Config->get_clicking ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_rewind ()
|
||||||
|
{
|
||||||
|
goto_start (session->transport_rolling ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_action (const std::string& group, const std::string& item)
|
||||||
|
{
|
||||||
|
AccessAction (group, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void
|
||||||
|
Maschine2::button_grid ()
|
||||||
|
{
|
||||||
|
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
|
||||||
|
tact->set_active (!tact->get_active ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_snap_pressed ()
|
||||||
|
{
|
||||||
|
_ctrl->button (M2Contols::Grid)->set_color (COLOR_WHITE);
|
||||||
|
_ctrl->button (M2Contols::Grid)->set_blinking (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_snap_changed (bool pressed)
|
||||||
|
{
|
||||||
|
if (!pressed) {
|
||||||
|
_ctrl->button (M2Contols::Grid)->set_blinking (false);
|
||||||
|
notify_snap_change ();
|
||||||
|
}
|
||||||
|
notify_master_change ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_snap_released ()
|
||||||
|
{
|
||||||
|
_ctrl->button (M2Contols::Grid)->set_blinking (false);
|
||||||
|
|
||||||
|
const char* action = 0;
|
||||||
|
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("snap-off"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
if (ract->get_active ()) { action = "snap-normal"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
act = ActionManager::get_action (X_("Editor"), X_("snap-normal"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
if (ract->get_active ()) { action = "snap-magnetic"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
act = ActionManager::get_action (X_("Editor"), X_("snap-magnetic"));
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
if (ract->get_active ()) { action = "snap-off"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!action) {
|
||||||
|
assert (0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
act = ActionManager::get_action (X_("Editor"), action);
|
||||||
|
if (act) {
|
||||||
|
Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
|
||||||
|
ract->set_active (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Master mode + state -- main encoder fn */
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::handle_master_change (enum MasterMode id)
|
||||||
|
{
|
||||||
|
switch (id) {
|
||||||
|
case MST_VOLUME:
|
||||||
|
if (_master_state == MST_VOLUME) { _master_state = MST_NONE; } else { _master_state = MST_VOLUME; }
|
||||||
|
break;
|
||||||
|
case MST_TEMPO:
|
||||||
|
if (_master_state == MST_TEMPO) { _master_state = MST_NONE; } else { _master_state = MST_TEMPO; }
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
notify_master_change ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::notify_master_change ()
|
||||||
|
{
|
||||||
|
if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
|
||||||
|
_ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
|
||||||
|
_ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (_master_state) {
|
||||||
|
case MST_NONE:
|
||||||
|
_ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
|
||||||
|
_ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
|
||||||
|
break;
|
||||||
|
case MST_VOLUME:
|
||||||
|
_ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_WHITE);
|
||||||
|
_ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
|
||||||
|
break;
|
||||||
|
case MST_TEMPO:
|
||||||
|
_ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
|
||||||
|
_ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_WHITE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_ac_delta (boost::shared_ptr<AutomationControl> ac, double d) {
|
||||||
|
if (!ac) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ac->set_value (ac->interface_to_internal (min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + d))),
|
||||||
|
PBD::Controllable::UseGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::encoder_master (int delta)
|
||||||
|
{
|
||||||
|
if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
|
||||||
|
_ctrl->button (M2Contols::Grid)->ignore_release ();
|
||||||
|
if (delta > 0) {
|
||||||
|
AccessAction ("Editor", "next-snap-choice");
|
||||||
|
} else {
|
||||||
|
AccessAction ("Editor", "prev-snap-choice");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (_master_state) {
|
||||||
|
case MST_NONE:
|
||||||
|
if (_ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active ()) {
|
||||||
|
if (delta > 0) {
|
||||||
|
AccessAction ("Editor", "temporal-zoom-in");
|
||||||
|
} else {
|
||||||
|
AccessAction ("Editor", "temporal-zoom-out");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (delta > 0) {
|
||||||
|
AccessAction ("Editor", "playhead-forward-to-grid");
|
||||||
|
} else {
|
||||||
|
AccessAction ("Editor", "playhead-backward-to-grid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MST_VOLUME:
|
||||||
|
{
|
||||||
|
boost::shared_ptr<Route> master = session->master_out ();
|
||||||
|
if (master) {
|
||||||
|
// TODO consider _ctrl->button (M2Contols::EncoderWheel)->is_pressed() for fine grained
|
||||||
|
const double factor = _ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? 256. : 32.;
|
||||||
|
apply_ac_delta (master->gain_control(), delta / factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MST_TEMPO:
|
||||||
|
// set new tempo.. apply with "enter"
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::button_encoder ()
|
||||||
|
{
|
||||||
|
switch (_master_state) {
|
||||||
|
case MST_NONE:
|
||||||
|
// OR: add marker ??
|
||||||
|
if (_ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active ()) {
|
||||||
|
AccessAction ("Editor", "zoom-to-session");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MST_VOLUME:
|
||||||
|
// ignore -> fine gained?
|
||||||
|
break;
|
||||||
|
case MST_TEMPO:
|
||||||
|
// add new tempo.. ?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::pad_change (unsigned int pad, float v)
|
||||||
|
{
|
||||||
|
float lvl = v; // _ctrl->pad (pad)->value () / 4095.f;
|
||||||
|
Gtkmm2ext::Color c = Gtkmm2ext::hsva_to_color (270 - 270.f * lvl, 1.0, lvl * lvl, 1.0);
|
||||||
|
_ctrl->pad (pad)->set_color (c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::pad_event (unsigned int pad, float v, bool ev)
|
||||||
|
{
|
||||||
|
if (ev) {
|
||||||
|
uint8_t msg[3];
|
||||||
|
msg[0] = v > 0 ? 0x90 : 0x80;
|
||||||
|
msg[1] = 36 + pad; // TODO map note to scale
|
||||||
|
msg[2] = ((uint8_t)floor (v * 127)) & 0x7f;
|
||||||
|
_output_port->write (msg, 3, 0);
|
||||||
|
} else {
|
||||||
|
uint8_t msg[3];
|
||||||
|
msg[0] = 0xa0;
|
||||||
|
msg[1] = 36 + pad; // TODO map note to scale
|
||||||
|
msg[2] = ((uint8_t)floor (v * 127)) & 0x7f;
|
||||||
|
_output_port->write (msg, 3, 0);
|
||||||
|
}
|
||||||
|
//printf ("[%2d] %s %.1f\n", pad, ev ? "On/Off" : "Aftertouch" , v * 127);
|
||||||
|
}
|
||||||
151
libs/surfaces/maschine2/canvas.cc
Normal file
151
libs/surfaces/maschine2/canvas.cc
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Paul Davis
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cairomm/region.h>
|
||||||
|
#include <cairomm/surface.h>
|
||||||
|
#include <cairomm/context.h>
|
||||||
|
|
||||||
|
#include "pbd/compose.h"
|
||||||
|
#include "pbd/error.h"
|
||||||
|
#include "pbd/i18n.h"
|
||||||
|
|
||||||
|
#include "canvas.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
#include "maschine2.h"
|
||||||
|
#include "m2device.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define Rect ArdourCanvas::Rect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace ArdourCanvas;
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
using namespace PBD;
|
||||||
|
|
||||||
|
Maschine2Canvas::Maschine2Canvas (Maschine2&m, M2Device* hw)
|
||||||
|
: m2 (m)
|
||||||
|
{
|
||||||
|
context = Cairo::Context::create (hw->surface ());
|
||||||
|
expose_region = Cairo::Region::create ();
|
||||||
|
_width = hw->surface ()->get_width ();
|
||||||
|
_height = hw->surface ()->get_height ();
|
||||||
|
|
||||||
|
hw->vblank.connect_same_thread (vblank_connections, boost::bind (&Maschine2Canvas::expose, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
Maschine2Canvas::~Maschine2Canvas ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Canvas::request_redraw ()
|
||||||
|
{
|
||||||
|
request_redraw (Rect (0, 0, _width, _height));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Canvas::request_redraw (Rect const & r)
|
||||||
|
{
|
||||||
|
Cairo::RectangleInt cr;
|
||||||
|
|
||||||
|
cr.x = r.x0;
|
||||||
|
cr.y = r.y0;
|
||||||
|
cr.width = r.width();
|
||||||
|
cr.height = r.height();
|
||||||
|
|
||||||
|
expose_region->do_union (cr);
|
||||||
|
|
||||||
|
/* next vblank will redraw */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Maschine2Canvas::expose ()
|
||||||
|
{
|
||||||
|
if (expose_region->empty()) {
|
||||||
|
return false; /* nothing drawn */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set up clipping */
|
||||||
|
|
||||||
|
const int nrects = expose_region->get_num_rectangles ();
|
||||||
|
|
||||||
|
for (int n = 0; n < nrects; ++n) {
|
||||||
|
Cairo::RectangleInt r = expose_region->get_rectangle (n);
|
||||||
|
context->rectangle (r.x, r.y, r.width, r.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
context->clip ();
|
||||||
|
|
||||||
|
Maschine2Layout* layout = m2.current_layout();
|
||||||
|
|
||||||
|
if (layout) {
|
||||||
|
/* all layouts cover (at least) the full size of the video
|
||||||
|
display, so we do not need to check if the layout intersects
|
||||||
|
the bounding box of the full expose region.
|
||||||
|
*/
|
||||||
|
Cairo::RectangleInt r = expose_region->get_extents();
|
||||||
|
Rect rr (r.x, r.y, r.x + r.width, r.y + r.height);
|
||||||
|
layout->render (Rect (r.x, r.y, r.x + r.width, r.y + r.height), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
context->reset_clip ();
|
||||||
|
|
||||||
|
/* why is there no "reset()" method for Cairo::Region? */
|
||||||
|
expose_region = Cairo::Region::create ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Canvas::request_size (Duple)
|
||||||
|
{
|
||||||
|
/* fixed size canvas */
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect
|
||||||
|
Maschine2Canvas::visible_area () const
|
||||||
|
{
|
||||||
|
/* may need to get more sophisticated once we do scrolling */
|
||||||
|
return Rect (0, 0, _width, _height);
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Pango::Context>
|
||||||
|
Maschine2Canvas::get_pango_context ()
|
||||||
|
{
|
||||||
|
if (!pango_context) {
|
||||||
|
PangoFontMap* map = pango_cairo_font_map_get_default ();
|
||||||
|
if (!map) {
|
||||||
|
error << _("Default Cairo font map is null!") << endmsg;
|
||||||
|
return Glib::RefPtr<Pango::Context> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
PangoContext* context = pango_font_map_create_context (map);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
error << _("cannot create new PangoContext from cairo font map") << endmsg;
|
||||||
|
return Glib::RefPtr<Pango::Context> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
pango_context = Glib::wrap (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pango_context;
|
||||||
|
}
|
||||||
87
libs/surfaces/maschine2/canvas.h
Normal file
87
libs/surfaces/maschine2/canvas.h
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Paul Davis
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_maschine2_canvas_h_
|
||||||
|
#define _ardour_maschine2_canvas_h_
|
||||||
|
|
||||||
|
#include <cairomm/refptr.h>
|
||||||
|
#include <glibmm/threads.h>
|
||||||
|
|
||||||
|
#include "canvas/canvas.h"
|
||||||
|
|
||||||
|
namespace Cairo {
|
||||||
|
class ImageSurface;
|
||||||
|
class Context;
|
||||||
|
class Region;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2Device;
|
||||||
|
class Maschine2;
|
||||||
|
|
||||||
|
/* A canvas which renders to the Push2 display */
|
||||||
|
|
||||||
|
class Maschine2Canvas : public ArdourCanvas::Canvas
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Maschine2Canvas (Maschine2&, M2Device*);
|
||||||
|
~Maschine2Canvas();
|
||||||
|
|
||||||
|
void request_redraw ();
|
||||||
|
void request_redraw (ArdourCanvas::Rect const &);
|
||||||
|
bool vblank ();
|
||||||
|
|
||||||
|
Cairo::RefPtr<Cairo::Context> image_context() { return context; }
|
||||||
|
|
||||||
|
ArdourCanvas::Coord width() const { return _width; }
|
||||||
|
ArdourCanvas::Coord height() const { return _height; }
|
||||||
|
|
||||||
|
void request_size (ArdourCanvas::Duple);
|
||||||
|
ArdourCanvas::Rect visible_area () const;
|
||||||
|
|
||||||
|
/* API that does nothing since we have no input events */
|
||||||
|
void ungrab () {}
|
||||||
|
void grab (ArdourCanvas::Item*) {}
|
||||||
|
void focus (ArdourCanvas::Item*) {}
|
||||||
|
void unfocus (ArdourCanvas::Item*) {}
|
||||||
|
void re_enter() {}
|
||||||
|
void pick_current_item (int) {}
|
||||||
|
void pick_current_item (ArdourCanvas::Duple const &, int) {}
|
||||||
|
bool get_mouse_position (ArdourCanvas::Duple&) const { return false; }
|
||||||
|
|
||||||
|
Glib::RefPtr<Pango::Context> get_pango_context ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _width;
|
||||||
|
int _height;
|
||||||
|
|
||||||
|
Cairo::RefPtr<Cairo::Context> context;
|
||||||
|
Cairo::RefPtr<Cairo::Region> expose_region;
|
||||||
|
Glib::RefPtr<Pango::Context> pango_context;
|
||||||
|
|
||||||
|
Maschine2& m2;
|
||||||
|
PBD::ScopedConnection vblank_connections;
|
||||||
|
|
||||||
|
bool expose ();
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace ArdourSurface */
|
||||||
|
|
||||||
|
#endif
|
||||||
85
libs/surfaces/maschine2/images.h
Normal file
85
libs/surfaces/maschine2/images.h
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
static const uint8_t maschine_png[] = {
|
||||||
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40,
|
||||||
|
0x01, 0x03, 0x00, 0x00, 0x00, 0x56, 0x71, 0x5d, 0xfc, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xa5,
|
||||||
|
0xd9, 0x9f, 0xdd, 0x00, 0x00, 0x02, 0xa0, 0x49, 0x44, 0x41, 0x54, 0x58,
|
||||||
|
0xc3, 0xed, 0xd7, 0x41, 0x8b, 0x13, 0x31, 0x14, 0x07, 0xf0, 0x0c, 0x11,
|
||||||
|
0x72, 0x33, 0x4a, 0x2f, 0x1e, 0xca, 0xe6, 0xe6, 0x5d, 0xf6, 0xa0, 0xa0,
|
||||||
|
0x34, 0xfa, 0x4d, 0x04, 0xbf, 0x80, 0xde, 0x2a, 0x5b, 0x3a, 0xb3, 0xec,
|
||||||
|
0x61, 0x2b, 0x48, 0x8b, 0xc7, 0x45, 0xd0, 0x4f, 0x22, 0x56, 0xe6, 0x50,
|
||||||
|
0x85, 0x05, 0xcf, 0x7b, 0x50, 0xa7, 0x54, 0xe8, 0x65, 0xc1, 0x94, 0x3d,
|
||||||
|
0x98, 0x65, 0xd3, 0x79, 0x66, 0x32, 0xc9, 0xb4, 0x6c, 0xd3, 0xce, 0x38,
|
||||||
|
0xe2, 0x2e, 0xe8, 0xce, 0xa1, 0x9d, 0xc3, 0xcc, 0xaf, 0x79, 0x2f, 0xf9,
|
||||||
|
0x67, 0xa6, 0x08, 0x5d, 0x1d, 0xff, 0xc5, 0x11, 0x82, 0x39, 0xa2, 0xbf,
|
||||||
|
0x00, 0x3c, 0xbe, 0x74, 0x40, 0x54, 0x03, 0xda, 0xfd, 0x3b, 0x2f, 0x9f,
|
||||||
|
0xdc, 0x1a, 0xad, 0x02, 0x81, 0xbc, 0x20, 0x60, 0x6d, 0x09, 0x58, 0x5d,
|
||||||
|
0x10, 0xd0, 0xee, 0x67, 0xfd, 0x26, 0xab, 0x00, 0x49, 0x2f, 0x1b, 0xa0,
|
||||||
|
0x50, 0x9c, 0x76, 0x37, 0xf6, 0x20, 0x5d, 0x03, 0xb0, 0xa2, 0x2f, 0x18,
|
||||||
|
0x86, 0x75, 0x46, 0xb0, 0x00, 0xe8, 0x54, 0xd4, 0x01, 0x78, 0xf1, 0xbb,
|
||||||
|
0x5b, 0xa8, 0x8d, 0x78, 0x44, 0x13, 0x2c, 0x10, 0x97, 0x88, 0x09, 0x92,
|
||||||
|
0xd0, 0x38, 0x91, 0x81, 0x00, 0xa8, 0x08, 0xdc, 0x43, 0x4d, 0x03, 0x10,
|
||||||
|
0x89, 0x20, 0x45, 0x5c, 0x19, 0x40, 0x39, 0x60, 0xed, 0x3a, 0x08, 0x21,
|
||||||
|
0xb1, 0x67, 0x4f, 0xbb, 0xd7, 0x11, 0x1f, 0x32, 0x0d, 0x04, 0xfa, 0x1e,
|
||||||
|
0x48, 0x0d, 0x90, 0x96, 0x02, 0x00, 0xae, 0xf2, 0x31, 0xec, 0x1b, 0x80,
|
||||||
|
0x2a, 0x0c, 0xb0, 0x0b, 0xf0, 0xdb, 0x40, 0x0c, 0x31, 0xe2, 0x09, 0xd7,
|
||||||
|
0x00, 0x91, 0x7c, 0x0f, 0x60, 0x3f, 0x03, 0xa0, 0xe8, 0xc1, 0x60, 0xe7,
|
||||||
|
0xeb, 0xcf, 0x77, 0x3f, 0x56, 0x00, 0x3d, 0x5a, 0x9b, 0xa6, 0x60, 0x04,
|
||||||
|
0x93, 0xc8, 0x02, 0x09, 0x8b, 0x25, 0xcb, 0x81, 0x5d, 0x21, 0x37, 0xa6,
|
||||||
|
0x71, 0x09, 0x18, 0xa0, 0x99, 0x01, 0x98, 0xae, 0x1e, 0xe9, 0xf1, 0x8f,
|
||||||
|
0x0c, 0xf0, 0xc1, 0x02, 0x21, 0xf8, 0x47, 0xa0, 0xcb, 0xb5, 0x69, 0xc2,
|
||||||
|
0x7d, 0xf4, 0x6c, 0xc8, 0x45, 0x98, 0x01, 0x12, 0x91, 0x21, 0x39, 0x0f,
|
||||||
|
0xf8, 0x7b, 0xb0, 0x04, 0xbc, 0x41, 0xcf, 0x0d, 0xc0, 0x39, 0x81, 0x84,
|
||||||
|
0x0c, 0x11, 0x01, 0xd0, 0xc0, 0xb8, 0x04, 0xd0, 0x57, 0xa5, 0xcb, 0x80,
|
||||||
|
0x04, 0x0d, 0x60, 0x50, 0xd4, 0x01, 0x33, 0x91, 0x5f, 0xd0, 0x9e, 0xd2,
|
||||||
|
0xe6, 0x8d, 0x47, 0xab, 0x0b, 0x89, 0x02, 0xd8, 0x34, 0x61, 0xbd, 0x12,
|
||||||
|
0x0d, 0xd0, 0x62, 0xba, 0x31, 0x34, 0xca, 0x81, 0xb3, 0x53, 0x07, 0xc4,
|
||||||
|
0x95, 0x80, 0xd6, 0xfc, 0x2c, 0xe9, 0xb2, 0x28, 0x84, 0xc1, 0x79, 0x20,
|
||||||
|
0x8c, 0xa9, 0xf2, 0x01, 0xac, 0xa8, 0xcb, 0x94, 0x60, 0x01, 0x0a, 0x9f,
|
||||||
|
0x6d, 0x09, 0xa7, 0xf3, 0xa2, 0x04, 0x68, 0x56, 0x01, 0xc2, 0x93, 0x04,
|
||||||
|
0x74, 0x3a, 0xf8, 0xc8, 0xce, 0x82, 0x04, 0xb7, 0x0e, 0xc0, 0x0f, 0x70,
|
||||||
|
0x00, 0x9b, 0x26, 0x33, 0x8d, 0x0e, 0xa0, 0x05, 0x10, 0x96, 0xcc, 0xc2,
|
||||||
|
0x02, 0x30, 0x0b, 0xa9, 0xc5, 0x27, 0x06, 0xc8, 0xee, 0xce, 0x01, 0xee,
|
||||||
|
0x46, 0xd0, 0x1b, 0x78, 0x47, 0x10, 0x7e, 0x03, 0x1b, 0x47, 0xb3, 0x94,
|
||||||
|
0x35, 0x30, 0x06, 0x98, 0x0a, 0xea, 0x96, 0x72, 0x29, 0x30, 0xff, 0x52,
|
||||||
|
0xa4, 0x29, 0x0b, 0x53, 0x0e, 0x4c, 0x14, 0x8b, 0x6d, 0x98, 0x24, 0x5b,
|
||||||
|
0xa4, 0x51, 0x95, 0x00, 0x59, 0x9c, 0x5b, 0x2c, 0x9e, 0x08, 0x7c, 0xb2,
|
||||||
|
0x88, 0xb3, 0xa4, 0x0b, 0xc0, 0x37, 0x82, 0x40, 0x1d, 0xbd, 0x75, 0x69,
|
||||||
|
0xca, 0x36, 0x94, 0x1c, 0x98, 0xe9, 0x66, 0xd9, 0x0d, 0x45, 0x12, 0x17,
|
||||||
|
0x67, 0x7f, 0x09, 0xcb, 0x40, 0xb6, 0xa5, 0xb5, 0xe8, 0xde, 0x24, 0x09,
|
||||||
|
0x44, 0x08, 0x28, 0x54, 0x2b, 0x80, 0x6f, 0x16, 0xf0, 0xf1, 0xd1, 0x6b,
|
||||||
|
0x97, 0xa6, 0x6c, 0x53, 0xd5, 0xc0, 0x34, 0x09, 0x24, 0x53, 0xc5, 0xa6,
|
||||||
|
0x8a, 0x37, 0x97, 0x40, 0x0e, 0x35, 0x90, 0x56, 0xda, 0xd6, 0xfd, 0x25,
|
||||||
|
0x90, 0xc3, 0x4f, 0x07, 0x45, 0x1c, 0x37, 0x3f, 0x58, 0x7a, 0x03, 0xdf,
|
||||||
|
0x2c, 0xd0, 0x0c, 0x70, 0x0f, 0x37, 0x28, 0x79, 0xbc, 0x37, 0xbd, 0xc0,
|
||||||
|
0x8b, 0x83, 0xf7, 0x50, 0xf1, 0xfd, 0xc0, 0x07, 0x30, 0x03, 0x44, 0xf5,
|
||||||
|
0x5f, 0x30, 0xb6, 0x2a, 0x03, 0x6b, 0x9a, 0xf8, 0x40, 0x03, 0xf7, 0x37,
|
||||||
|
0x36, 0xaf, 0x04, 0xe8, 0x68, 0x60, 0xbb, 0x78, 0xb8, 0x95, 0x94, 0xe0,
|
||||||
|
0x9b, 0x85, 0xce, 0xc7, 0xde, 0xab, 0x46, 0x25, 0x60, 0xcd, 0x08, 0xd4,
|
||||||
|
0x10, 0xdd, 0x6c, 0x70, 0x51, 0x1f, 0x38, 0x7e, 0x88, 0x82, 0x6a, 0x80,
|
||||||
|
0x7f, 0x16, 0x82, 0xef, 0xfa, 0xa3, 0x71, 0x57, 0xfe, 0x19, 0x10, 0x6c,
|
||||||
|
0xd7, 0x07, 0xb0, 0x99, 0x40, 0xdc, 0xa9, 0x0d, 0x5c, 0xcb, 0xbf, 0x6e,
|
||||||
|
0x5f, 0xfd, 0x21, 0xfb, 0x37, 0x8e, 0x5f, 0xd7, 0x55, 0xc2, 0x86, 0x4a,
|
||||||
|
0xcd, 0xa5, 0x35, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
|
||||||
|
0x42, 0x60, 0x82
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t mikro_png[] = {
|
||||||
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40,
|
||||||
|
0x01, 0x03, 0x00, 0x00, 0x00, 0xe8, 0x18, 0xed, 0x3c, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xa5,
|
||||||
|
0xd9, 0x9f, 0xdd, 0x00, 0x00, 0x00, 0x77, 0x49, 0x44, 0x41, 0x54, 0x38,
|
||||||
|
0xcb, 0x63, 0x60, 0x18, 0x05, 0xe4, 0x01, 0xf6, 0x07, 0x68, 0x02, 0xf6,
|
||||||
|
0x7f, 0xd0, 0x04, 0xfe, 0xd5, 0xa3, 0xf2, 0x19, 0x1f, 0xb0, 0x37, 0xa0,
|
||||||
|
0x08, 0x30, 0x37, 0x30, 0x1e, 0x40, 0x35, 0x93, 0xfd, 0x01, 0xaa, 0xa9,
|
||||||
|
0x7c, 0xf2, 0x3f, 0x0a, 0x50, 0x04, 0x78, 0xec, 0xff, 0x18, 0xa0, 0x08,
|
||||||
|
0xc8, 0xd4, 0xff, 0xb3, 0x40, 0x11, 0x90, 0xf8, 0xc0, 0x2f, 0x81, 0x22,
|
||||||
|
0x60, 0xf1, 0x80, 0x5d, 0x06, 0x45, 0xc0, 0xf2, 0x01, 0xfb, 0x1c, 0xfc,
|
||||||
|
0x02, 0x86, 0x0f, 0xd8, 0x7b, 0xf0, 0x0b, 0x10, 0x36, 0x03, 0xc3, 0x5a,
|
||||||
|
0x0c, 0x87, 0x61, 0x38, 0x1d, 0xc3, 0x73, 0x18, 0xde, 0xc7, 0x08, 0x20,
|
||||||
|
0x8c, 0x20, 0xc4, 0x08, 0x64, 0x8c, 0x68, 0xc0, 0x8c, 0x28, 0xfe, 0x0f,
|
||||||
|
0xa3, 0xc9, 0x99, 0x4c, 0x00, 0x00, 0x2c, 0x35, 0x29, 0x11, 0x00, 0x07,
|
||||||
|
0x1a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42,
|
||||||
|
0x60, 0x82
|
||||||
|
};
|
||||||
79
libs/surfaces/maschine2/interface.cc
Normal file
79
libs/surfaces/maschine2/interface.cc
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "pbd/error.h"
|
||||||
|
#include "ardour/rc_configuration.h"
|
||||||
|
#include "control_protocol/control_protocol.h"
|
||||||
|
#include "maschine2.h"
|
||||||
|
|
||||||
|
using namespace ARDOUR;
|
||||||
|
using namespace PBD;
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
|
||||||
|
static ControlProtocol*
|
||||||
|
new_maschine2 (ControlProtocolDescriptor*, Session* s)
|
||||||
|
{
|
||||||
|
Maschine2* m2 = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
m2 = new Maschine2 (*s);
|
||||||
|
}
|
||||||
|
catch (std::exception & e) {
|
||||||
|
PBD::error << "Failed to instantiate Maschine2: " << e.what() << endmsg;
|
||||||
|
delete m2;
|
||||||
|
m2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m2->set_active (true);
|
||||||
|
return m2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
delete_maschine2 (ControlProtocolDescriptor*, ControlProtocol* cp)
|
||||||
|
{
|
||||||
|
delete cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
probe_maschine2 (ControlProtocolDescriptor*)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void*
|
||||||
|
maschine2_request_buffer_factory (uint32_t num_requests)
|
||||||
|
{
|
||||||
|
return Maschine2::request_factory (num_requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ControlProtocolDescriptor maschine2_descriptor = {
|
||||||
|
/*name : */ "NI Maschine2",
|
||||||
|
/*id : */ "uri://ardour.org/surfaces/maschine2:0",
|
||||||
|
/*ptr : */ 0,
|
||||||
|
/*module : */ 0,
|
||||||
|
/*mandatory : */ 0,
|
||||||
|
/*supports_feedback : */ false,
|
||||||
|
/*probe : */ probe_maschine2,
|
||||||
|
/*initialize : */ new_maschine2,
|
||||||
|
/*destroy : */ delete_maschine2,
|
||||||
|
/*request_buffer_factory */ maschine2_request_buffer_factory
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { return &maschine2_descriptor; }
|
||||||
63
libs/surfaces/maschine2/layout.cc
Normal file
63
libs/surfaces/maschine2/layout.cc
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2016 Paul Davis
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "maschine2.h"
|
||||||
|
#include "canvas.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define Rect ArdourCanvas::Rect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace ARDOUR;
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
using namespace ArdourCanvas;
|
||||||
|
|
||||||
|
Maschine2Layout::Maschine2Layout (Maschine2& m2, Session& s, const std::string& name)
|
||||||
|
: Container (m2.canvas())
|
||||||
|
, _m2 (m2)
|
||||||
|
, _session (s)
|
||||||
|
, _name (name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Maschine2Layout::~Maschine2Layout ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Layout::compute_bounding_box () const
|
||||||
|
{
|
||||||
|
/* all layouts occupy at least the full screen, even if their combined
|
||||||
|
* child boxes do not.
|
||||||
|
*/
|
||||||
|
_bounding_box = Rect (0, 0, display_width(), display_height());
|
||||||
|
_bounding_box_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Maschine2Layout::display_height() const
|
||||||
|
{
|
||||||
|
return _m2.canvas()->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Maschine2Layout::display_width() const
|
||||||
|
{
|
||||||
|
return _m2.canvas()->width();
|
||||||
|
}
|
||||||
55
libs/surfaces/maschine2/layout.h
Normal file
55
libs/surfaces/maschine2/layout.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Paul Davis
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_maschine2_layout_h_
|
||||||
|
#define _ardour_maschine2_layout_h_
|
||||||
|
|
||||||
|
#include <sigc++/trackable.h>
|
||||||
|
#include <cairomm/refptr.h>
|
||||||
|
#include "canvas/container.h"
|
||||||
|
|
||||||
|
namespace ARDOUR {
|
||||||
|
class Session;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class Maschine2;
|
||||||
|
|
||||||
|
class Maschine2Layout : public sigc::trackable, public ArdourCanvas::Container
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Maschine2Layout (Maschine2& m2, ARDOUR::Session& s, std::string const & name);
|
||||||
|
virtual ~Maschine2Layout ();
|
||||||
|
|
||||||
|
std::string name() const { return _name; }
|
||||||
|
int display_width () const;
|
||||||
|
int display_height () const;
|
||||||
|
|
||||||
|
void compute_bounding_box () const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Maschine2& _m2;
|
||||||
|
ARDOUR::Session& _session;
|
||||||
|
std::string _name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
|
||||||
|
#endif /* _ardour_maschine2_layout_h_ */
|
||||||
220
libs/surfaces/maschine2/m2_button.h
Normal file
220
libs/surfaces/maschine2/m2_button.h
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2button_h_
|
||||||
|
#define _ardour_surfaces_m2button_h_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "gtkmm2ext/colors.h"
|
||||||
|
#include "pbd/signals.h"
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2ButtonInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2ButtonInterface () {}
|
||||||
|
virtual ~M2ButtonInterface () {}
|
||||||
|
|
||||||
|
/* user API */
|
||||||
|
PBD::Signal1<void, bool> changed;
|
||||||
|
PBD::Signal0<void> pressed;
|
||||||
|
PBD::Signal0<void> released;
|
||||||
|
|
||||||
|
virtual void set_blinking (bool) {}
|
||||||
|
virtual void set_color (uint32_t rgba) {}
|
||||||
|
|
||||||
|
virtual bool is_pressed () const { return false; }
|
||||||
|
virtual bool active () const { return is_pressed (); }
|
||||||
|
|
||||||
|
virtual void ignore_release () {}
|
||||||
|
|
||||||
|
// TODO allow to suspend *next* release signal
|
||||||
|
// e.g. press + hold "grid", move encoder -> release "grid" -> noop
|
||||||
|
|
||||||
|
/* internal API - called from device thread */
|
||||||
|
virtual bool set_active (bool a) { return false; }
|
||||||
|
virtual uint8_t lightness (float) const { return 0; }
|
||||||
|
virtual uint32_t color (float) const { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class M2Button : public M2ButtonInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2Button ()
|
||||||
|
: M2ButtonInterface ()
|
||||||
|
, _pressed (false)
|
||||||
|
, _blink (false)
|
||||||
|
, _ignore_release (false)
|
||||||
|
, _lightness (0)
|
||||||
|
, _rgba (0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/* user API */
|
||||||
|
void set_blinking (bool en) {
|
||||||
|
_blink = en;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set_color (uint32_t rgba) {
|
||||||
|
_rgba = rgba;
|
||||||
|
/* 7 bit color */
|
||||||
|
const uint8_t r = ((rgba >> 24) & 0xff) >> 1;
|
||||||
|
const uint8_t g = ((rgba >> 16) & 0xff) >> 1;
|
||||||
|
const uint8_t b = ((rgba >> 8) & 0xff) >> 1;
|
||||||
|
_lightness = std::max (r, std::max (g, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_pressed () const { return _pressed; }
|
||||||
|
|
||||||
|
void ignore_release () {
|
||||||
|
if (_pressed) {
|
||||||
|
_ignore_release = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* internal API - called from device thread */
|
||||||
|
virtual bool set_active (bool a) {
|
||||||
|
if (a == _pressed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_pressed = a;
|
||||||
|
|
||||||
|
if (a) {
|
||||||
|
pressed (); /* EMIT SIGNAL */
|
||||||
|
} else {
|
||||||
|
if (_ignore_release) {
|
||||||
|
_ignore_release = false;
|
||||||
|
} else {
|
||||||
|
released (); /* EMIT SIGNAL */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changed (a); /* EMIT SIGNAL */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t lightness (float blink) const {
|
||||||
|
if (_blink && blink >= 0.f && blink <= 1.f) {
|
||||||
|
return (uint8_t) floorf(blink * _lightness);
|
||||||
|
}
|
||||||
|
return _lightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t color (float blink) const {
|
||||||
|
if (_blink && blink >= 0.f && blink <= 1.f) {
|
||||||
|
Gtkmm2ext::HSV hsv (_rgba);
|
||||||
|
Gtkmm2ext::HSV s (hsv.shade (blink));
|
||||||
|
return s.color();
|
||||||
|
}
|
||||||
|
return _rgba;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool _pressed;
|
||||||
|
bool _blink;
|
||||||
|
bool _ignore_release;
|
||||||
|
uint8_t _lightness;
|
||||||
|
uint32_t _rgba;
|
||||||
|
};
|
||||||
|
|
||||||
|
class M2StatelessButton : public M2Button
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2StatelessButton () : M2Button () {}
|
||||||
|
|
||||||
|
bool set_active (bool a) {
|
||||||
|
if (a == _pressed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (a) {
|
||||||
|
set_color (0xffffffff);
|
||||||
|
} else {
|
||||||
|
set_color (0x000000ff);
|
||||||
|
}
|
||||||
|
return M2Button::set_active (a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class M2ToggleButton : public M2Button
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2ToggleButton ()
|
||||||
|
: M2Button ()
|
||||||
|
, _active (false)
|
||||||
|
{
|
||||||
|
changed.connect_same_thread (changed_connection, boost::bind (&M2ToggleButton::change_event, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
PBD::Signal1<void, bool> toggled;
|
||||||
|
bool active () const { return _active; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void change_event (bool down) {
|
||||||
|
if (down) { return; }
|
||||||
|
_active = !_active;
|
||||||
|
set_color (_active ? 0xffffffff : 0x000000ff);
|
||||||
|
toggled (_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
PBD::ScopedConnection changed_connection;
|
||||||
|
bool _active;
|
||||||
|
};
|
||||||
|
|
||||||
|
class M2ToggleHoldButton : public M2Button
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2ToggleHoldButton ()
|
||||||
|
: M2Button ()
|
||||||
|
, _active (false)
|
||||||
|
, _active_on_release (false)
|
||||||
|
{
|
||||||
|
changed.connect_same_thread (changed_connection, boost::bind (&M2ToggleHoldButton::change_event, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
PBD::Signal1<void, bool> toggled;
|
||||||
|
bool active () const { return _active; }
|
||||||
|
void unset_active_on_release () { if (is_pressed ()) { _active_on_release = false; } }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void change_event (bool down) {
|
||||||
|
if (down) {
|
||||||
|
if (_active) {
|
||||||
|
_active_on_release = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_active = true;
|
||||||
|
_active_on_release = true;
|
||||||
|
} else {
|
||||||
|
if (_active == _active_on_release) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_active = _active_on_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_color (_active ? 0xffffffff : 0x000000ff);
|
||||||
|
toggled (_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
PBD::ScopedConnection changed_connection;
|
||||||
|
bool _active;
|
||||||
|
bool _active_on_release;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif /* _ardour_surfaces_m2button_h_ */
|
||||||
|
|
||||||
284
libs/surfaces/maschine2/m2_dev_mikro.cc
Normal file
284
libs/surfaces/maschine2/m2_dev_mikro.cc
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "pbd/compose.h"
|
||||||
|
|
||||||
|
#include "maschine2.h"
|
||||||
|
#include "m2controls.h"
|
||||||
|
#include "m2_dev_mikro.h"
|
||||||
|
|
||||||
|
#include <pangomm/fontdescription.h>
|
||||||
|
|
||||||
|
#include "images.h"
|
||||||
|
|
||||||
|
static size_t mikro_png_readoff = 0;
|
||||||
|
|
||||||
|
static Cairo::ErrorStatus maschine_png_read (unsigned char* d, unsigned int s) {
|
||||||
|
if (s + mikro_png_readoff > sizeof (mikro_png)) {
|
||||||
|
return CAIRO_STATUS_READ_ERROR;
|
||||||
|
}
|
||||||
|
memcpy (d, &mikro_png[mikro_png_readoff], s);
|
||||||
|
mikro_png_readoff += s;
|
||||||
|
return CAIRO_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
|
||||||
|
Maschine2Mikro::Maschine2Mikro () : M2Device ()
|
||||||
|
{
|
||||||
|
_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, 128, 64);
|
||||||
|
clear (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mikro::clear (bool splash)
|
||||||
|
{
|
||||||
|
M2Device::clear (splash);
|
||||||
|
|
||||||
|
memset (&ctrl_in, 0, sizeof (ctrl_in));
|
||||||
|
memset (pad, 0, sizeof (pad));
|
||||||
|
|
||||||
|
_lights[0] = 0xff;
|
||||||
|
|
||||||
|
for (int l = 0; l < 4; ++l) {
|
||||||
|
_img[l][0] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (_surface);
|
||||||
|
if (!splash) {
|
||||||
|
mikro_png_readoff = 0;
|
||||||
|
Cairo::RefPtr<Cairo::ImageSurface> sf = Cairo::ImageSurface::create_from_png_stream (sigc::ptr_fun (maschine_png_read));
|
||||||
|
cr->set_source(sf, 0, 0);
|
||||||
|
cr->paint ();
|
||||||
|
} else {
|
||||||
|
cr->set_operator (Cairo::OPERATOR_CLEAR);
|
||||||
|
cr->paint ();
|
||||||
|
cr->set_operator (Cairo::OPERATOR_OVER);
|
||||||
|
|
||||||
|
Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (cr);
|
||||||
|
Pango::FontDescription fd ("Sans Bold 18px");
|
||||||
|
layout->set_font_description (fd);
|
||||||
|
layout->set_alignment (Pango::ALIGN_CENTER);
|
||||||
|
|
||||||
|
layout->set_text (string_compose ("%1\n%2", PROGRAM_NAME, VERSIONSTRING));
|
||||||
|
int tw, th;
|
||||||
|
layout->get_pixel_size (tw, th);
|
||||||
|
cr->move_to (128 - tw * 0.5, 32 - th * 0.5);
|
||||||
|
cr->set_source_rgb (1, 1, 1);
|
||||||
|
layout->show_in_cairo_context(cr);
|
||||||
|
}
|
||||||
|
//_surface->write_to_png ("/tmp/amaschine.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mikro::read (hid_device* handle, M2Contols* ctrl)
|
||||||
|
{
|
||||||
|
assert (ctrl);
|
||||||
|
while (true) {
|
||||||
|
uint8_t buf[256];
|
||||||
|
int res = hid_read (handle, buf, 256);
|
||||||
|
if (res < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO parse incrementally if chunked at 64
|
||||||
|
|
||||||
|
if (res > 4 && buf[0] == 0x01) {
|
||||||
|
memcpy (&ctrl_in, &buf[1], sizeof (ctrl_in));
|
||||||
|
assign_controls (ctrl);
|
||||||
|
}
|
||||||
|
else if (res > 32 && buf[0] == 0x20) {
|
||||||
|
for (unsigned int i = 0; i < 16; ++i) {
|
||||||
|
uint8_t v0 = buf[1 + 2 * i];
|
||||||
|
uint8_t v1 = buf[2 + 2 * i];
|
||||||
|
uint8_t p = (v1 & 0xf0) >> 4;
|
||||||
|
pad[p] = ((v1 & 0xf) << 8) | v0;
|
||||||
|
unsigned int pid = 15 - ((i & 0xc) + (3 - (i & 0x3)));
|
||||||
|
ctrl->pad (pid)->set_value (pad[p]);
|
||||||
|
}
|
||||||
|
// TODO read complete 65 byte msg, expect buf[33] == 0x00
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mikro::write (hid_device* handle, M2Contols* ctrl)
|
||||||
|
{
|
||||||
|
bump_blink ();
|
||||||
|
uint8_t buf[265];
|
||||||
|
|
||||||
|
//TODO double-buffer, send changes only if needed
|
||||||
|
|
||||||
|
/* 30 control buttons, 8-bit brightness,
|
||||||
|
* + 16 RGB pads
|
||||||
|
*/
|
||||||
|
buf[0] = 0x80;
|
||||||
|
set_lights (ctrl, &buf[1]);
|
||||||
|
set_pads (ctrl, &buf[31]);
|
||||||
|
if (memcmp (_lights, buf, 79)) {
|
||||||
|
hid_write (handle, buf, 79);
|
||||||
|
memcpy (_lights, buf, 79);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_splashcnt < _splashtime ) {
|
||||||
|
++_splashcnt;
|
||||||
|
}
|
||||||
|
else if (! vblank () /* EMIT SIGNAL*/) {
|
||||||
|
/* check clear/initial draw */
|
||||||
|
if (_img[0][0] != 0xff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display */
|
||||||
|
_surface->flush ();
|
||||||
|
const unsigned char* img = _surface->get_data ();
|
||||||
|
const int stride = _surface->get_stride ();
|
||||||
|
memset (buf, 0, 9);
|
||||||
|
buf[0] = 0xe0;
|
||||||
|
for (int l = 0; l < 4; ++l) {
|
||||||
|
buf[1] = 32 * l;
|
||||||
|
buf[5] = 0x20;
|
||||||
|
buf[7] = 0x08;
|
||||||
|
|
||||||
|
int y0 = l * 16;
|
||||||
|
for (int p = 0; p < 256; ++p) {
|
||||||
|
uint8_t v = 0;
|
||||||
|
const int y = y0 + p / 16;
|
||||||
|
for (int b = 0; b < 8; ++b) {
|
||||||
|
const int x = (p % 16) * 8 + b;
|
||||||
|
int off = y * stride + x * 4 /* ARGB32 */;
|
||||||
|
/* off + 0 == blue
|
||||||
|
* off + 1 == green
|
||||||
|
* off + 2 == red
|
||||||
|
* off + 3 == alpha
|
||||||
|
*/
|
||||||
|
/* calculate lightness */
|
||||||
|
uint8_t l = std::max (img[off + 0], std::max (img[off + 1], img[off + 2]));
|
||||||
|
if (l > 0x7e) { // TODO: take alpha channel into account?!
|
||||||
|
v |= 1 << (7 - b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[9 + p] = v;
|
||||||
|
}
|
||||||
|
if (memcmp (_img[l], buf, 265)) {
|
||||||
|
hid_write (handle, buf, 265);
|
||||||
|
memcpy (_img[l], buf, 265);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mikro::assign_controls (M2Contols* ctrl) const
|
||||||
|
{
|
||||||
|
ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->set_active (ctrl_in.trs_shift ? true : false);
|
||||||
|
M2Contols::Modifier mod = ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? M2Contols::ModShift : M2Contols::ModNone;
|
||||||
|
|
||||||
|
bool change = false;
|
||||||
|
#define ASSIGN(BTN, VAR) \
|
||||||
|
change |= ctrl->button (M2Contols:: BTN, mod)->set_active (ctrl_in. VAR ? true : false)
|
||||||
|
|
||||||
|
ASSIGN (BtnRestart, trs_restart);
|
||||||
|
ASSIGN (BtnStepLeft, trs_left);
|
||||||
|
ASSIGN (BtnStepRight, trs_right);
|
||||||
|
ASSIGN (BtnGrid, trs_grid);
|
||||||
|
ASSIGN (BtnPlay, trs_play);
|
||||||
|
ASSIGN (BtnRec, trs_rec);
|
||||||
|
ASSIGN (BtnErase, trs_erase);
|
||||||
|
|
||||||
|
ASSIGN (BtnGroupA, group);
|
||||||
|
ASSIGN (BtnBrowse, browse);
|
||||||
|
ASSIGN (BtnSampling, sampling);
|
||||||
|
ASSIGN (BtnNoteRepeat, note_repeat);
|
||||||
|
ASSIGN (BtnWheel, mst_wheel);
|
||||||
|
|
||||||
|
ASSIGN (BtnTop0, f1);
|
||||||
|
ASSIGN (BtnTop1, f1);
|
||||||
|
ASSIGN (BtnTop2, f3);
|
||||||
|
|
||||||
|
ASSIGN (BtnControl, control);
|
||||||
|
ASSIGN (BtnNavigate, navigate); // XXX
|
||||||
|
ASSIGN (BtnNavLeft, nav_left);
|
||||||
|
ASSIGN (BtnNavRight, nav_right);
|
||||||
|
ASSIGN (BtnEnter, main);
|
||||||
|
|
||||||
|
ASSIGN (BtnScene, pads_scene);
|
||||||
|
ASSIGN (BtnPattern, pads_pattern);
|
||||||
|
ASSIGN (BtnPadMode, pads_mode);
|
||||||
|
ASSIGN (BtnNavigate, pads_navigate);
|
||||||
|
ASSIGN (BtnDuplicate, pads_duplicate);
|
||||||
|
ASSIGN (BtnSelect, pads_select);
|
||||||
|
ASSIGN (BtnSolo, pads_solo);
|
||||||
|
ASSIGN (BtnMute, pads_mute);
|
||||||
|
#undef ASSIGN
|
||||||
|
|
||||||
|
change |= ctrl->encoder (0)->set_value (ctrl_in.mst_wheel_pos);
|
||||||
|
|
||||||
|
if (change && mod == M2Contols::ModShift) {
|
||||||
|
M2ToggleHoldButton* btn = dynamic_cast<M2ToggleHoldButton*> (ctrl->button (M2Contols::BtnShift, M2Contols::ModNone));
|
||||||
|
if (btn) {
|
||||||
|
btn->unset_active_on_release ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIGHT(BIT, BTN) \
|
||||||
|
b[BIT] = ctrl->button (M2Contols:: BTN, mod)->lightness (_blink_shade)
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mikro::set_pads (M2Contols* ctrl, uint8_t* b) const
|
||||||
|
{
|
||||||
|
if (!ctrl) {
|
||||||
|
memset (b, 0, 48);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < 16; ++i) {
|
||||||
|
unsigned int pid = 15 - ((i & 0xc) + (3 - (i & 0x3)));
|
||||||
|
ctrl->pad (pid)->color (b[i * 3], b[1 + i * 3], b[2 + i * 3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mikro::set_lights (M2Contols* ctrl, uint8_t* b) const
|
||||||
|
{
|
||||||
|
if (!ctrl) {
|
||||||
|
memset (b, 0, 29);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
M2Contols::Modifier mod = ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? M2Contols::ModShift : M2Contols::ModNone;
|
||||||
|
|
||||||
|
LIGHT ( 0, BtnTop0); // F1
|
||||||
|
LIGHT ( 1, BtnTop1); // F2
|
||||||
|
LIGHT ( 2, BtnTop2); // F3
|
||||||
|
LIGHT ( 3, BtnControl);
|
||||||
|
LIGHT ( 4, BtnNavigate); // XXX
|
||||||
|
LIGHT ( 5, BtnNavLeft);
|
||||||
|
LIGHT ( 6, BtnNavRight);
|
||||||
|
LIGHT ( 7, BtnEnter); // Main
|
||||||
|
|
||||||
|
const uint32_t rgb = ctrl->button (M2Contols::BtnGroupA, mod)->color (_blink_shade);
|
||||||
|
b[8] = (rgb >> 0) & 0xff;
|
||||||
|
b[9] = (rgb >> 8) & 0xff;
|
||||||
|
b[10] = (rgb >> 16) & 0xff;
|
||||||
|
|
||||||
|
LIGHT (11, BtnBrowse);
|
||||||
|
LIGHT (12, BtnSampling);
|
||||||
|
LIGHT (13, BtnNoteRepeat);
|
||||||
|
|
||||||
|
LIGHT (14, BtnRestart);
|
||||||
|
LIGHT (15, BtnStepLeft);
|
||||||
|
LIGHT (16, BtnStepRight);
|
||||||
|
LIGHT (17, BtnGrid);
|
||||||
|
LIGHT (18, BtnPlay);
|
||||||
|
LIGHT (19, BtnRec);
|
||||||
|
LIGHT (20, BtnErase);
|
||||||
|
LIGHT (21, BtnShift);
|
||||||
|
|
||||||
|
LIGHT (22, BtnScene);
|
||||||
|
LIGHT (23, BtnPattern);
|
||||||
|
LIGHT (24, BtnPadMode);
|
||||||
|
LIGHT (25, BtnNavigate);
|
||||||
|
LIGHT (26, BtnDuplicate);
|
||||||
|
LIGHT (27, BtnSelect);
|
||||||
|
LIGHT (28, BtnSolo);
|
||||||
|
LIGHT (29, BtnMute);
|
||||||
|
}
|
||||||
99
libs/surfaces/maschine2/m2_dev_mikro.h
Normal file
99
libs/surfaces/maschine2/m2_dev_mikro.h
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2mikro_h_
|
||||||
|
#define _ardour_surfaces_m2mikro_h_
|
||||||
|
|
||||||
|
#include "m2device.h"
|
||||||
|
|
||||||
|
#include <cairomm/context.h>
|
||||||
|
#include <pangomm/layout.h>
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class Maschine2Mikro : public M2Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Maschine2Mikro ();
|
||||||
|
void clear (bool splash = false);
|
||||||
|
void read (hid_device*, M2Contols*);
|
||||||
|
void write (hid_device*, M2Contols*);
|
||||||
|
Cairo::RefPtr<Cairo::ImageSurface> surface () { return _surface; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define ATTRIBUTE_PACKED __attribute__((__packed__))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_PACKED
|
||||||
|
#pragma pack(1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct machine_mk2_input {
|
||||||
|
unsigned int trs_restart : 1; // 0
|
||||||
|
unsigned int trs_left : 1;
|
||||||
|
unsigned int trs_right : 1;
|
||||||
|
unsigned int trs_grid : 1;
|
||||||
|
unsigned int trs_play : 1;
|
||||||
|
unsigned int trs_rec : 1;
|
||||||
|
unsigned int trs_erase : 1;
|
||||||
|
unsigned int trs_shift : 1;
|
||||||
|
unsigned int group : 1; // 8
|
||||||
|
unsigned int browse : 1;
|
||||||
|
unsigned int sampling : 1;
|
||||||
|
unsigned int note_repeat : 1;
|
||||||
|
unsigned int mst_wheel : 1;
|
||||||
|
unsigned int reserved : 3;
|
||||||
|
unsigned int f1 : 1; // 16
|
||||||
|
unsigned int f2 : 1;
|
||||||
|
unsigned int f3 : 1;
|
||||||
|
unsigned int control : 1;
|
||||||
|
unsigned int navigate : 1;
|
||||||
|
unsigned int nav_left : 1;
|
||||||
|
unsigned int nav_right : 1;
|
||||||
|
unsigned int main : 1;
|
||||||
|
unsigned int pads_scene : 1; // 24
|
||||||
|
unsigned int pads_pattern : 1;
|
||||||
|
unsigned int pads_mode : 1;
|
||||||
|
unsigned int pads_navigate : 1;
|
||||||
|
unsigned int pads_duplicate : 1;
|
||||||
|
unsigned int pads_select : 1;
|
||||||
|
unsigned int pads_solo : 1;
|
||||||
|
unsigned int pads_mute : 1; // 31
|
||||||
|
unsigned int mst_wheel_pos : 8; // 32..40 // range: 0..15
|
||||||
|
} ATTRIBUTE_PACKED ctrl_in;
|
||||||
|
|
||||||
|
#if (!defined __GNUC__)
|
||||||
|
#pragma pack()
|
||||||
|
#endif
|
||||||
|
uint16_t pad[16];
|
||||||
|
|
||||||
|
Cairo::RefPtr<Cairo::ImageSurface> _surface;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void assign_controls (M2Contols*) const;
|
||||||
|
|
||||||
|
void set_lights (M2Contols*, uint8_t*) const;
|
||||||
|
void set_pads (M2Contols*, uint8_t*) const;
|
||||||
|
|
||||||
|
uint8_t _lights[79];
|
||||||
|
uint8_t _img[4][265];
|
||||||
|
};
|
||||||
|
} /* namespace */
|
||||||
|
|
||||||
|
#endif
|
||||||
380
libs/surfaces/maschine2/m2_dev_mk2.cc
Normal file
380
libs/surfaces/maschine2/m2_dev_mk2.cc
Normal file
|
|
@ -0,0 +1,380 @@
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "pbd/compose.h"
|
||||||
|
|
||||||
|
#include "maschine2.h"
|
||||||
|
#include "m2controls.h"
|
||||||
|
#include "m2_dev_mk2.h"
|
||||||
|
|
||||||
|
#include <pangomm/fontdescription.h>
|
||||||
|
|
||||||
|
#include "images.h"
|
||||||
|
|
||||||
|
static size_t maschine_png_readoff = 0;
|
||||||
|
|
||||||
|
static Cairo::ErrorStatus maschine_png_read (unsigned char* d, unsigned int s) {
|
||||||
|
if (s + maschine_png_readoff > sizeof (maschine_png)) {
|
||||||
|
return CAIRO_STATUS_READ_ERROR;
|
||||||
|
}
|
||||||
|
memcpy (d, &maschine_png[maschine_png_readoff], s);
|
||||||
|
maschine_png_readoff += s;
|
||||||
|
return CAIRO_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
|
||||||
|
Maschine2Mk2::Maschine2Mk2 () : M2Device ()
|
||||||
|
{
|
||||||
|
_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, 512, 64);
|
||||||
|
clear (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mk2::clear (bool splash)
|
||||||
|
{
|
||||||
|
M2Device::clear (splash);
|
||||||
|
|
||||||
|
memset (&ctrl_in, 0, sizeof (ctrl_in));
|
||||||
|
memset (pad, 0, sizeof (pad));
|
||||||
|
|
||||||
|
ctrl80[0] = 0xff;
|
||||||
|
ctrl81[0] = 0xff;
|
||||||
|
ctrl82[0] = 0xff;
|
||||||
|
|
||||||
|
for (int d = 0; d < 2; ++d) {
|
||||||
|
for (int l = 0; l < 8; ++l) {
|
||||||
|
_img[d][l][0] = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
Cairo::RefPtr<Cairo::Context> c = Cairo::Context::create (_surface);
|
||||||
|
c->set_operator (Cairo::OPERATOR_CLEAR);
|
||||||
|
c->paint ();
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
maschine_png_readoff = 0;
|
||||||
|
Cairo::RefPtr<Cairo::ImageSurface> sf = Cairo::ImageSurface::create_from_png_stream (sigc::ptr_fun (maschine_png_read));
|
||||||
|
Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (_surface);
|
||||||
|
cr->set_source(sf, 0, 0);
|
||||||
|
cr->paint ();
|
||||||
|
|
||||||
|
Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (cr);
|
||||||
|
Pango::FontDescription fd ("Sans Bold 18px");
|
||||||
|
layout->set_font_description (fd);
|
||||||
|
layout->set_alignment (Pango::ALIGN_CENTER);
|
||||||
|
|
||||||
|
int cx;
|
||||||
|
if (splash) {
|
||||||
|
layout->set_text (string_compose ("%1\n%2", PROGRAM_NAME, VERSIONSTRING));
|
||||||
|
cx = 384;
|
||||||
|
} else {
|
||||||
|
cr->rectangle (326, 0, 186, 64);
|
||||||
|
cr->set_source_rgb (0, 0, 0);
|
||||||
|
cr->fill ();
|
||||||
|
layout->set_text ("Keep Groovin'");
|
||||||
|
cx = 421;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tw, th;
|
||||||
|
layout->get_pixel_size (tw, th);
|
||||||
|
cr->move_to (cx - tw * 0.5, 32 - th * 0.5);
|
||||||
|
cr->set_source_rgb (1, 1, 1);
|
||||||
|
layout->show_in_cairo_context(cr);
|
||||||
|
//_surface->write_to_png ("/tmp/amaschine.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mk2::read (hid_device* handle, M2Contols* ctrl)
|
||||||
|
{
|
||||||
|
assert (ctrl);
|
||||||
|
while (true) {
|
||||||
|
uint8_t buf[256];
|
||||||
|
int res = hid_read (handle, buf, 256);
|
||||||
|
if (res < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO parse incrementally if chunked at 64
|
||||||
|
|
||||||
|
if (res > 24 && buf[0] == 0x01) {
|
||||||
|
memcpy (&ctrl_in, &buf[1], sizeof (ctrl_in));
|
||||||
|
assign_controls (ctrl);
|
||||||
|
}
|
||||||
|
else if (res > 32 && buf[0] == 0x20) {
|
||||||
|
for (unsigned int i = 0; i < 16; ++i) {
|
||||||
|
uint8_t v0 = buf[1 + 2 * i];
|
||||||
|
uint8_t v1 = buf[2 + 2 * i];
|
||||||
|
uint8_t p = (v1 & 0xf0) >> 4;
|
||||||
|
pad[p] = ((v1 & 0xf) << 8) | v0;
|
||||||
|
unsigned int pid = 15 - ((i & 0xc) + (3 - (i & 0x3)));
|
||||||
|
ctrl->pad (pid)->set_value (pad[p]);
|
||||||
|
}
|
||||||
|
// TODO read complete 65 byte msg, expect buf[33] == 0x00
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mk2::write (hid_device* handle, M2Contols* ctrl)
|
||||||
|
{
|
||||||
|
bump_blink ();
|
||||||
|
uint8_t buf[265];
|
||||||
|
|
||||||
|
//TODO double-buffer, send changes only if needed
|
||||||
|
|
||||||
|
/* 31 control buttons: 8 mst + 8 top + 8 pads + 7 mst
|
||||||
|
* 8-bit brightness
|
||||||
|
*/
|
||||||
|
buf[0] = 0x82;
|
||||||
|
set_colors82 (ctrl, &buf[1]);
|
||||||
|
if (memcmp (ctrl82, buf, 32)) {
|
||||||
|
hid_write (handle, buf, 32);
|
||||||
|
memcpy (ctrl82, buf, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8 group rgb|rgb + 8 on/off transport buttons */
|
||||||
|
buf[0] = 0x81;
|
||||||
|
set_colors81 (ctrl, &buf[1]);
|
||||||
|
if (memcmp (ctrl81, buf, 57)) {
|
||||||
|
hid_write (handle, buf, 57);
|
||||||
|
memcpy (ctrl81, buf, 57);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 16 RGB grid pads */
|
||||||
|
buf[0] = 0x80;
|
||||||
|
set_colors80 (ctrl, &buf[1]);
|
||||||
|
if (memcmp (ctrl80, buf, 49)) {
|
||||||
|
hid_write (handle, buf, 49);
|
||||||
|
memcpy (ctrl80, buf, 49);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_splashcnt < _splashtime) {
|
||||||
|
++_splashcnt;
|
||||||
|
}
|
||||||
|
else if (! vblank () /* EMIT SIGNAL*/) {
|
||||||
|
/* check clear/initial draw */
|
||||||
|
if (_img[0][0][0] != 0xff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display */
|
||||||
|
_surface->flush ();
|
||||||
|
const unsigned char* img = _surface->get_data ();
|
||||||
|
const int stride = _surface->get_stride ();
|
||||||
|
for (int d = 0; d < 2; ++d) {
|
||||||
|
memset (buf, 0, 9);
|
||||||
|
buf[0] = 0xe0 | d;
|
||||||
|
for (int l = 0; l < 8; ++l) {
|
||||||
|
buf[3] = 8 * l;
|
||||||
|
buf[5] = 0x20;
|
||||||
|
buf[7] = 0x08;
|
||||||
|
|
||||||
|
int y0 = l * 8;
|
||||||
|
int x0 = d * 256;
|
||||||
|
|
||||||
|
for (int p = 0; p < 256; ++p) {
|
||||||
|
uint8_t v = 0;
|
||||||
|
const int y = y0 + p / 32;
|
||||||
|
for (int b = 0; b < 8; ++b) {
|
||||||
|
const int x = x0 + (p % 32) * 8 + b;
|
||||||
|
int off = y * stride + x * 4 /* ARGB32 */;
|
||||||
|
/* off + 0 == blue
|
||||||
|
* off + 1 == green
|
||||||
|
* off + 2 == red
|
||||||
|
* off + 3 == alpha
|
||||||
|
*/
|
||||||
|
/* calculate lightness */
|
||||||
|
uint8_t l = std::max (img[off + 0], std::max (img[off + 1], img[off + 2]));
|
||||||
|
if (l > 0x7e) { // TODO: take alpha channel into account?!
|
||||||
|
v |= 1 << (7 - b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[9 + p] = v;
|
||||||
|
}
|
||||||
|
if (memcmp (_img[d][l], buf, 265)) {
|
||||||
|
hid_write (handle, buf, 265);
|
||||||
|
memcpy (_img[d][l], buf, 265);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mk2::assign_controls (M2Contols* ctrl) const
|
||||||
|
{
|
||||||
|
ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->set_active (ctrl_in.trs_shift ? true : false);
|
||||||
|
M2Contols::Modifier mod = ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? M2Contols::ModShift : M2Contols::ModNone;
|
||||||
|
|
||||||
|
bool change = false;
|
||||||
|
#define ASSIGN(BTN, VAR) \
|
||||||
|
change |= ctrl->button (M2Contols:: BTN, mod)->set_active (ctrl_in. VAR ? true : false)
|
||||||
|
|
||||||
|
ASSIGN (BtnRestart, trs_restart);
|
||||||
|
ASSIGN (BtnStepLeft, trs_left);
|
||||||
|
ASSIGN (BtnStepRight, trs_right);
|
||||||
|
ASSIGN (BtnGrid, trs_grid);
|
||||||
|
ASSIGN (BtnPlay, trs_play);
|
||||||
|
ASSIGN (BtnRec, trs_rec);
|
||||||
|
ASSIGN (BtnErase, trs_erase);
|
||||||
|
|
||||||
|
ASSIGN (BtnScene, pads_scene);
|
||||||
|
ASSIGN (BtnPattern, pads_pattern);
|
||||||
|
ASSIGN (BtnPadMode, pads_mode);
|
||||||
|
ASSIGN (BtnNavigate, pads_navigate);
|
||||||
|
ASSIGN (BtnDuplicate, pads_duplicate);
|
||||||
|
ASSIGN (BtnSelect, pads_select);
|
||||||
|
ASSIGN (BtnSolo, pads_solo);
|
||||||
|
ASSIGN (BtnMute, pads_mute);
|
||||||
|
|
||||||
|
ASSIGN (BtnControl, top_control);
|
||||||
|
ASSIGN (BtnStep, top_step);
|
||||||
|
ASSIGN (BtnBrowse, top_browse);
|
||||||
|
ASSIGN (BtnSampling, top_sampling);
|
||||||
|
ASSIGN (BtnSelLeft, top_left);
|
||||||
|
ASSIGN (BtnSelRight, top_right);
|
||||||
|
ASSIGN (BtnAll, top_all);
|
||||||
|
ASSIGN (BtnAuto, top_auto);
|
||||||
|
|
||||||
|
ASSIGN (BtnVolume, mst_volume);
|
||||||
|
ASSIGN (BtnSwing, mst_swing);
|
||||||
|
ASSIGN (BtnTempo, mst_tempo);
|
||||||
|
ASSIGN (BtnNavLeft, mst_left);
|
||||||
|
ASSIGN (BtnNavRight, mst_right);
|
||||||
|
ASSIGN (BtnEnter, mst_enter);
|
||||||
|
ASSIGN (BtnNoteRepeat, mst_note_repeat);
|
||||||
|
ASSIGN (BtnWheel, mst_wheel);
|
||||||
|
|
||||||
|
ASSIGN (BtnGroupA, groups_a);
|
||||||
|
ASSIGN (BtnGroupB, groups_b);
|
||||||
|
ASSIGN (BtnGroupC, groups_c);
|
||||||
|
ASSIGN (BtnGroupD, groups_d);
|
||||||
|
ASSIGN (BtnGroupE, groups_e);
|
||||||
|
ASSIGN (BtnGroupF, groups_f);
|
||||||
|
ASSIGN (BtnGroupG, groups_g);
|
||||||
|
ASSIGN (BtnGroupH, groups_h);
|
||||||
|
|
||||||
|
ASSIGN (BtnTop0, top_0);
|
||||||
|
ASSIGN (BtnTop1, top_1);
|
||||||
|
ASSIGN (BtnTop2, top_2);
|
||||||
|
ASSIGN (BtnTop3, top_3);
|
||||||
|
ASSIGN (BtnTop4, top_4);
|
||||||
|
ASSIGN (BtnTop5, top_5);
|
||||||
|
ASSIGN (BtnTop6, top_6);
|
||||||
|
ASSIGN (BtnTop7, top_7);
|
||||||
|
#undef ASSIGN
|
||||||
|
|
||||||
|
change |= ctrl->encoder (0)->set_value (ctrl_in.mst_wheel_pos);
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
change |= ctrl->encoder (1 + i)->set_value (ctrl_in.top_knobs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change && mod == M2Contols::ModShift) {
|
||||||
|
M2ToggleHoldButton* btn = dynamic_cast<M2ToggleHoldButton*> (ctrl->button (M2Contols::BtnShift, M2Contols::ModNone));
|
||||||
|
if (btn) {
|
||||||
|
btn->unset_active_on_release ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIGHT(BIT, BTN) \
|
||||||
|
b[BIT] = ctrl->button (M2Contols:: BTN, mod)->lightness (_blink_shade)
|
||||||
|
|
||||||
|
#define COLOR(BIT, BTN) \
|
||||||
|
{ \
|
||||||
|
const uint32_t rgb = ctrl->button (M2Contols:: BTN, mod)->color (_blink_shade); \
|
||||||
|
b[0 + BIT ] = (rgb >> 0) & 0xff; \
|
||||||
|
b[1 + BIT ] = (rgb >> 8) & 0xff; \
|
||||||
|
b[2 + BIT ] = (rgb >> 16) & 0xff; \
|
||||||
|
b[3 + BIT ] = (rgb >> 0) & 0xff; \
|
||||||
|
b[4 + BIT ] = (rgb >> 8) & 0xff; \
|
||||||
|
b[5 + BIT ] = (rgb >> 16) & 0xff; \
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mk2::set_colors80 (M2Contols* ctrl, uint8_t* b) const
|
||||||
|
{
|
||||||
|
if (!ctrl) {
|
||||||
|
memset (b, 0, 48);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < 16; ++i) {
|
||||||
|
unsigned int pid = 15 - ((i & 0xc) + (3 - (i & 0x3)));
|
||||||
|
ctrl->pad (pid)->color (b[i * 3], b[1 + i * 3], b[2 + i * 3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mk2::set_colors81 (M2Contols* ctrl, uint8_t* b) const
|
||||||
|
{
|
||||||
|
if (!ctrl) {
|
||||||
|
memset (b, 0, 56);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
M2Contols::Modifier mod = ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? M2Contols::ModShift : M2Contols::ModNone;
|
||||||
|
|
||||||
|
COLOR ( 0, BtnGroupA);
|
||||||
|
COLOR ( 6, BtnGroupB);
|
||||||
|
COLOR (12, BtnGroupC);
|
||||||
|
COLOR (18, BtnGroupD);
|
||||||
|
COLOR (24, BtnGroupE);
|
||||||
|
COLOR (30, BtnGroupF);
|
||||||
|
COLOR (36, BtnGroupG);
|
||||||
|
COLOR (42, BtnGroupH);
|
||||||
|
|
||||||
|
LIGHT (48, BtnRestart);
|
||||||
|
LIGHT (49, BtnStepLeft);
|
||||||
|
LIGHT (50, BtnStepRight);
|
||||||
|
LIGHT (51, BtnGrid);
|
||||||
|
LIGHT (52, BtnPlay);
|
||||||
|
LIGHT (53, BtnRec);
|
||||||
|
LIGHT (54, BtnErase);
|
||||||
|
LIGHT (55, BtnShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2Mk2::set_colors82 (M2Contols* ctrl, uint8_t* b) const
|
||||||
|
{
|
||||||
|
if (!ctrl) {
|
||||||
|
memset (b, 0, 31);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
M2Contols::Modifier mod = ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? M2Contols::ModShift : M2Contols::ModNone;
|
||||||
|
|
||||||
|
LIGHT ( 0, BtnControl);
|
||||||
|
LIGHT ( 1, BtnStep);
|
||||||
|
LIGHT ( 2, BtnBrowse);
|
||||||
|
LIGHT ( 3, BtnSampling);
|
||||||
|
LIGHT ( 4, BtnSelLeft);
|
||||||
|
LIGHT ( 5, BtnSelRight);
|
||||||
|
LIGHT ( 6, BtnAll);
|
||||||
|
LIGHT ( 7, BtnAuto);
|
||||||
|
|
||||||
|
LIGHT ( 8, BtnTop0);
|
||||||
|
LIGHT ( 9, BtnTop1);
|
||||||
|
LIGHT (10, BtnTop2);
|
||||||
|
LIGHT (11, BtnTop3);
|
||||||
|
LIGHT (12, BtnTop4);
|
||||||
|
LIGHT (13, BtnTop5);
|
||||||
|
LIGHT (14, BtnTop6);
|
||||||
|
LIGHT (15, BtnTop7);
|
||||||
|
|
||||||
|
LIGHT (16, BtnScene);
|
||||||
|
LIGHT (17, BtnPattern);
|
||||||
|
LIGHT (18, BtnPadMode);
|
||||||
|
LIGHT (19, BtnNavigate);
|
||||||
|
LIGHT (20, BtnDuplicate);
|
||||||
|
LIGHT (21, BtnSelect);
|
||||||
|
LIGHT (22, BtnSolo);
|
||||||
|
LIGHT (23, BtnMute);
|
||||||
|
|
||||||
|
LIGHT (24, BtnVolume);
|
||||||
|
LIGHT (25, BtnSwing);
|
||||||
|
LIGHT (26, BtnTempo);
|
||||||
|
LIGHT (27, BtnNavLeft);
|
||||||
|
LIGHT (28, BtnNavRight);
|
||||||
|
LIGHT (29, BtnEnter);
|
||||||
|
LIGHT (30, BtnNoteRepeat);
|
||||||
|
}
|
||||||
122
libs/surfaces/maschine2/m2_dev_mk2.h
Normal file
122
libs/surfaces/maschine2/m2_dev_mk2.h
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2mk2_h_
|
||||||
|
#define _ardour_surfaces_m2mk2_h_
|
||||||
|
|
||||||
|
#include "m2device.h"
|
||||||
|
|
||||||
|
#include <cairomm/context.h>
|
||||||
|
#include <pangomm/layout.h>
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class Maschine2Mk2 : public M2Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Maschine2Mk2 ();
|
||||||
|
void clear (bool splash = false);
|
||||||
|
void read (hid_device*, M2Contols*);
|
||||||
|
void write (hid_device*, M2Contols*);
|
||||||
|
Cairo::RefPtr<Cairo::ImageSurface> surface () { return _surface; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define ATTRIBUTE_PACKED __attribute__((__packed__))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_PACKED
|
||||||
|
#pragma pack(1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct machine_mk2_input {
|
||||||
|
unsigned int top_0 : 1; // 0
|
||||||
|
unsigned int top_1 : 1;
|
||||||
|
unsigned int top_2 : 1;
|
||||||
|
unsigned int top_3 : 1;
|
||||||
|
unsigned int top_4 : 1;
|
||||||
|
unsigned int top_5 : 1;
|
||||||
|
unsigned int top_6 : 1;
|
||||||
|
unsigned int top_7 : 1;
|
||||||
|
unsigned int top_control : 1; // 8
|
||||||
|
unsigned int top_step : 1;
|
||||||
|
unsigned int top_browse : 1;
|
||||||
|
unsigned int top_sampling : 1;
|
||||||
|
unsigned int top_left : 1;
|
||||||
|
unsigned int top_right : 1;
|
||||||
|
unsigned int top_all : 1;
|
||||||
|
unsigned int top_auto : 1;
|
||||||
|
unsigned int mst_volume : 1; // 16
|
||||||
|
unsigned int mst_swing : 1;
|
||||||
|
unsigned int mst_tempo : 1;
|
||||||
|
unsigned int mst_left : 1;
|
||||||
|
unsigned int mst_right : 1;
|
||||||
|
unsigned int mst_enter : 1;
|
||||||
|
unsigned int mst_note_repeat : 1;
|
||||||
|
unsigned int mst_wheel : 1;
|
||||||
|
unsigned int groups_a : 1; // 24
|
||||||
|
unsigned int groups_b : 1;
|
||||||
|
unsigned int groups_c : 1;
|
||||||
|
unsigned int groups_d : 1;
|
||||||
|
unsigned int groups_e : 1;
|
||||||
|
unsigned int groups_f : 1;
|
||||||
|
unsigned int groups_g : 1;
|
||||||
|
unsigned int groups_h : 1;
|
||||||
|
unsigned int trs_restart : 1; // 32
|
||||||
|
unsigned int trs_left : 1;
|
||||||
|
unsigned int trs_right : 1;
|
||||||
|
unsigned int trs_grid : 1;
|
||||||
|
unsigned int trs_play : 1;
|
||||||
|
unsigned int trs_rec : 1;
|
||||||
|
unsigned int trs_erase : 1;
|
||||||
|
unsigned int trs_shift : 1;
|
||||||
|
unsigned int pads_scene : 1; // 40
|
||||||
|
unsigned int pads_pattern : 1;
|
||||||
|
unsigned int pads_mode : 1;
|
||||||
|
unsigned int pads_navigate : 1;
|
||||||
|
unsigned int pads_duplicate : 1;
|
||||||
|
unsigned int pads_select : 1;
|
||||||
|
unsigned int pads_solo : 1;
|
||||||
|
unsigned int pads_mute : 1;
|
||||||
|
unsigned int reserved : 8; // 48
|
||||||
|
unsigned int mst_wheel_pos : 8; // 56 // range: 0..15
|
||||||
|
uint16_t top_knobs[8]; // 64 ... 191 // range 0..999
|
||||||
|
} ATTRIBUTE_PACKED ctrl_in;
|
||||||
|
|
||||||
|
#if (!defined __GNUC__)
|
||||||
|
#pragma pack()
|
||||||
|
#endif
|
||||||
|
uint16_t pad[16];
|
||||||
|
|
||||||
|
Cairo::RefPtr<Cairo::ImageSurface> _surface;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void assign_controls (M2Contols*) const;
|
||||||
|
|
||||||
|
void set_colors80 (M2Contols*, uint8_t*) const;
|
||||||
|
void set_colors81 (M2Contols*, uint8_t*) const;
|
||||||
|
void set_colors82 (M2Contols*, uint8_t*) const;
|
||||||
|
|
||||||
|
uint8_t ctrl82[32];
|
||||||
|
uint8_t ctrl81[57];
|
||||||
|
uint8_t ctrl80[49];
|
||||||
|
uint8_t _img[2][8][265];
|
||||||
|
};
|
||||||
|
} /* namespace */
|
||||||
|
|
||||||
|
#endif
|
||||||
97
libs/surfaces/maschine2/m2_encoder.h
Normal file
97
libs/surfaces/maschine2/m2_encoder.h
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2encoder_h_
|
||||||
|
#define _ardour_surfaces_m2encoder_h_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "pbd/signals.h"
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2EncoderInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2EncoderInterface () {}
|
||||||
|
virtual ~M2EncoderInterface () {}
|
||||||
|
|
||||||
|
/* user API */
|
||||||
|
PBD::Signal1<void, int> changed;
|
||||||
|
virtual float value () const { return 0.f; }
|
||||||
|
virtual float range () const { return 0.f; }
|
||||||
|
|
||||||
|
/* internal API - called from device thread */
|
||||||
|
virtual bool set_value (unsigned int v) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class M2Encoder : public M2EncoderInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2Encoder (unsigned int upper = 1000)
|
||||||
|
: M2EncoderInterface ()
|
||||||
|
, _upper (upper /* limit, exclusive. eg [0..15]: 16 */)
|
||||||
|
, _value (0)
|
||||||
|
, _initialized (false)
|
||||||
|
{
|
||||||
|
assert (_upper > 7);
|
||||||
|
_wrapcnt = std::max (3U, upper / 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
float value () const { return _value / (_upper - 1.f); }
|
||||||
|
float range () const { return (_upper - 1.f); }
|
||||||
|
|
||||||
|
bool set_value (unsigned int v) {
|
||||||
|
if (!_initialized) {
|
||||||
|
_initialized = true;
|
||||||
|
_value = v;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == _value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int delta;
|
||||||
|
if (v < _wrapcnt && _value > _upper - _wrapcnt) {
|
||||||
|
// wrap around max -> min
|
||||||
|
delta = v + _upper - _value;
|
||||||
|
}
|
||||||
|
else if (_value < _wrapcnt && v > _upper - _wrapcnt) {
|
||||||
|
// wrap around min -> max
|
||||||
|
delta = v - _upper - _value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delta = v - _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_value = v;
|
||||||
|
changed (delta);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned int _upper;
|
||||||
|
unsigned int _value;
|
||||||
|
unsigned int _wrapcnt;
|
||||||
|
bool _initialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif /* _ardour_surfaces_m2encoder_h_ */
|
||||||
|
|
||||||
|
|
||||||
57
libs/surfaces/maschine2/m2_map_mikro.cc
Normal file
57
libs/surfaces/maschine2/m2_map_mikro.cc
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m2_map_mikro.h"
|
||||||
|
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
|
||||||
|
M2MapMikro::M2MapMikro ()
|
||||||
|
: M2Contols ()
|
||||||
|
, enc_master (16)
|
||||||
|
{}
|
||||||
|
|
||||||
|
M2ButtonInterface*
|
||||||
|
M2MapMikro::button (PhysicalButtonId id, Modifier m)
|
||||||
|
{
|
||||||
|
return M2Contols::button (id, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
M2ButtonInterface*
|
||||||
|
M2MapMikro::button (SemanticButtonId id)
|
||||||
|
{
|
||||||
|
return M2Contols::button (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
M2EncoderInterface*
|
||||||
|
M2MapMikro::encoder (unsigned int id)
|
||||||
|
{
|
||||||
|
if (id == 0) {
|
||||||
|
return &enc_master;
|
||||||
|
}
|
||||||
|
// TODO map "nav" (select) and Left/Right to encoder(s) delta.
|
||||||
|
return M2Contols::encoder (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
M2PadInterface*
|
||||||
|
M2MapMikro::pad (unsigned int id)
|
||||||
|
{
|
||||||
|
if (id < 16) {
|
||||||
|
return &pads[id];
|
||||||
|
}
|
||||||
|
return M2Contols::pad (id);
|
||||||
|
}
|
||||||
42
libs/surfaces/maschine2/m2_map_mikro.h
Normal file
42
libs/surfaces/maschine2/m2_map_mikro.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2map_mikro_h_
|
||||||
|
#define _ardour_surfaces_m2map_mikro_h_
|
||||||
|
|
||||||
|
#include "m2controls.h"
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2MapMikro : public M2Contols
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2MapMikro ();
|
||||||
|
|
||||||
|
M2ButtonInterface* button (PhysicalButtonId id, Modifier m);
|
||||||
|
M2ButtonInterface* button (SemanticButtonId id);
|
||||||
|
M2EncoderInterface* encoder (unsigned int id);
|
||||||
|
M2PadInterface* pad (unsigned int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
M2Encoder enc_master;
|
||||||
|
M2Pad pads[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif
|
||||||
108
libs/surfaces/maschine2/m2_map_mk2.cc
Normal file
108
libs/surfaces/maschine2/m2_map_mk2.cc
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m2_map_mk2.h"
|
||||||
|
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
M2MapMk2::M2MapMk2 ()
|
||||||
|
: M2Contols ()
|
||||||
|
, enc_master (16)
|
||||||
|
{
|
||||||
|
#define PSMAP(MOD, PHYS, SEM, BTN) \
|
||||||
|
pmap[MOD].insert (make_pair (PHYS, BTN)); \
|
||||||
|
smap.insert (make_pair (SEM, BTN));
|
||||||
|
|
||||||
|
#define PSMAPALL(PHYS, SEM, BTN) \
|
||||||
|
pmap[ModNone].insert (make_pair (PHYS, BTN)); \
|
||||||
|
pmap[ModShift].insert (make_pair (PHYS, BTN)); \
|
||||||
|
smap.insert (make_pair (SEM, BTN)); \
|
||||||
|
|
||||||
|
PSMAP(ModNone, BtnPlay, Play, &tr[0]);
|
||||||
|
PSMAP(ModShift, BtnPlay, Metronom, &tr[1]);
|
||||||
|
PSMAP(ModNone, BtnRec, Rec, &tr[2]);
|
||||||
|
PSMAP(ModNone, BtnGrid, Grid, &tr[3]);
|
||||||
|
PSMAP(ModNone, BtnRestart, GotoStart, &ts[0]);
|
||||||
|
PSMAP(ModShift, BtnRestart, Loop, &tr[4]);
|
||||||
|
|
||||||
|
PSMAP(ModNone, BtnStepLeft, FastRewind, &ts[1]);
|
||||||
|
PSMAP(ModNone, BtnStepRight, FastForward, &ts[2]);
|
||||||
|
PSMAP(ModShift, BtnStepLeft, JumpBackward, &ts[3]);
|
||||||
|
PSMAP(ModShift, BtnStepRight, JumpForward, &ts[4]);
|
||||||
|
|
||||||
|
PSMAPALL(BtnWheel, EncoderWheel, &mst[0]);
|
||||||
|
PSMAPALL(BtnVolume, MasterVolume, &mst[1]);
|
||||||
|
//PSMAPALL(BtnSwing, Master?????, &mst[2]);
|
||||||
|
PSMAPALL(BtnTempo, MasterTempo, &mst[3]);
|
||||||
|
|
||||||
|
PSMAP(ModShift, BtnAll, Save, &save);
|
||||||
|
|
||||||
|
PSMAP(ModShift, BtnNavLeft, Undo, &undoredo[0]);
|
||||||
|
PSMAP(ModShift, BtnNavRight, Redo, &undoredo[1]);
|
||||||
|
|
||||||
|
PSMAP(ModNone, BtnMute, Mute, &sm[0]);
|
||||||
|
PSMAP(ModShift, BtnMute, Panic, &panic);
|
||||||
|
PSMAPALL(BtnSolo, Solo, &sm[1]);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
pmap[ModNone].insert (make_pair (BtnErase, &ts[5]));
|
||||||
|
pmap[ModShift].insert (make_pair (BtnErase, &ts[5]));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
M2ButtonInterface*
|
||||||
|
M2MapMk2::button (PhysicalButtonId id, Modifier m)
|
||||||
|
{
|
||||||
|
PhysicalMap::const_iterator i = pmap[m].find (id);
|
||||||
|
if (i != pmap[m].end()) {
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
return M2Contols::button (id, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
M2ButtonInterface*
|
||||||
|
M2MapMk2::button (SemanticButtonId id)
|
||||||
|
{
|
||||||
|
SematicMap::const_iterator i = smap.find (id);
|
||||||
|
if (i != smap.end()) {
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
return M2Contols::button (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
M2EncoderInterface*
|
||||||
|
M2MapMk2::encoder (unsigned int id)
|
||||||
|
{
|
||||||
|
if (id == 0) {
|
||||||
|
return &enc_master;
|
||||||
|
}
|
||||||
|
else if (id < 9) {
|
||||||
|
return &enc_top[id - 1];
|
||||||
|
}
|
||||||
|
return M2Contols::encoder (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
M2PadInterface*
|
||||||
|
M2MapMk2::pad (unsigned int id)
|
||||||
|
{
|
||||||
|
if (id < 16) {
|
||||||
|
return &pads[id];
|
||||||
|
}
|
||||||
|
return M2Contols::pad (id);
|
||||||
|
}
|
||||||
58
libs/surfaces/maschine2/m2_map_mk2.h
Normal file
58
libs/surfaces/maschine2/m2_map_mk2.h
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2map_mk2_h_
|
||||||
|
#define _ardour_surfaces_m2map_mk2_h_
|
||||||
|
|
||||||
|
#include "m2controls.h"
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2MapMk2 : public M2Contols
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2MapMk2 ();
|
||||||
|
|
||||||
|
M2ButtonInterface* button (PhysicalButtonId id, Modifier m);
|
||||||
|
M2ButtonInterface* button (SemanticButtonId id);
|
||||||
|
M2EncoderInterface* encoder (unsigned int id);
|
||||||
|
M2PadInterface* pad (unsigned int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PhysicalMap pmap[2]; // 2: Modifiers
|
||||||
|
SematicMap smap;
|
||||||
|
|
||||||
|
M2Button tr[5]; // transport controlables
|
||||||
|
M2StatelessButton ts[6]; // transport pushbuttons
|
||||||
|
|
||||||
|
M2Button mst[4]; // master "volume", "swing", "tempo", "encoder-push"
|
||||||
|
|
||||||
|
M2Button save;
|
||||||
|
|
||||||
|
M2Button undoredo[2];
|
||||||
|
M2Button sm[2]; // solo, mute
|
||||||
|
M2StatelessButton panic;
|
||||||
|
|
||||||
|
M2Encoder enc_master;
|
||||||
|
M2Encoder enc_top[8];
|
||||||
|
|
||||||
|
M2Pad pads[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif
|
||||||
148
libs/surfaces/maschine2/m2_pad.h
Normal file
148
libs/surfaces/maschine2/m2_pad.h
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2pad_h_
|
||||||
|
#define _ardour_surfaces_m2pad_h_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "pbd/signals.h"
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2PadInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2PadInterface () {}
|
||||||
|
virtual ~M2PadInterface () {}
|
||||||
|
|
||||||
|
/* user API */
|
||||||
|
PBD::Signal1<void, float> pressed;
|
||||||
|
PBD::Signal0<void> released;
|
||||||
|
PBD::Signal1<void, float> aftertouch;
|
||||||
|
PBD::Signal2<void, float, bool> event;
|
||||||
|
PBD::Signal1<void, float> changed;
|
||||||
|
|
||||||
|
virtual uint16_t value () const { return 0; }
|
||||||
|
virtual float pressure () const { return 0.f; }
|
||||||
|
virtual void set_color (uint32_t rgba) {}
|
||||||
|
|
||||||
|
/* internal API - called from device thread */
|
||||||
|
virtual void set_value (uint16_t v) {}
|
||||||
|
|
||||||
|
virtual void color (uint8_t& r, uint8_t& g, uint8_t& b) const {
|
||||||
|
r = g = b = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class M2Pad : public M2PadInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2Pad ()
|
||||||
|
: M2PadInterface ()
|
||||||
|
, _pressed (false)
|
||||||
|
, _pressure (0)
|
||||||
|
, _last (0)
|
||||||
|
, _cnt (0)
|
||||||
|
, _rgba (0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
hist[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t value () const { return _raw; }
|
||||||
|
float pressure () const { return _pressure; }
|
||||||
|
|
||||||
|
void set_color (uint32_t rgba) { _rgba = rgba; }
|
||||||
|
|
||||||
|
void color (uint8_t& r, uint8_t& g, uint8_t& b) const
|
||||||
|
{
|
||||||
|
r = ((_rgba >> 24) & 0xff) >> 1;
|
||||||
|
g = ((_rgba >> 16) & 0xff) >> 1;
|
||||||
|
b = ((_rgba >> 8) & 0xff) >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_value (uint16_t v)
|
||||||
|
{
|
||||||
|
// bleed to neighboring pads...
|
||||||
|
static const uint16_t high = 159;
|
||||||
|
static const float low = 159 / 4095.f;
|
||||||
|
static const float mindelta = 32.f / 4096.f;
|
||||||
|
|
||||||
|
if (_raw != v) {
|
||||||
|
changed (v / 4095.f);
|
||||||
|
_raw = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some pads never return to "0", and there's
|
||||||
|
// TODO map pressure from a min..max range,
|
||||||
|
// even hard hits rarely exceed 3400 or thereabouts.
|
||||||
|
// -> "pad sensitivity" config or "calibrate pads"
|
||||||
|
|
||||||
|
hist[_cnt] = v;
|
||||||
|
_cnt = (_cnt + 1) & 3;
|
||||||
|
|
||||||
|
if (_pressed) {
|
||||||
|
const float p = v / 4095.f;
|
||||||
|
_pressure += .1 * (p - _pressure);
|
||||||
|
if (_pressure < low) {
|
||||||
|
_pressure = 0;
|
||||||
|
_pressed = false;
|
||||||
|
released (); /* EMIT SIGNAL */
|
||||||
|
event (_pressure, true); /* EMIT SIGNAL */
|
||||||
|
} else {
|
||||||
|
if (fabsf (_last - _pressure) > mindelta) {
|
||||||
|
_last = _pressure;
|
||||||
|
aftertouch (_pressure); /* EMIT SIGNAL */
|
||||||
|
event (_pressure, false); /* EMIT SIGNAL */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool above_thresh = true;
|
||||||
|
uint16_t max = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (hist[i] < high) {
|
||||||
|
above_thresh = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
max = std::max (max, hist[i]);
|
||||||
|
}
|
||||||
|
if (above_thresh) {
|
||||||
|
_pressed = true;
|
||||||
|
_last = _pressure = max / 4095.f;
|
||||||
|
pressed (_pressure);
|
||||||
|
event (_pressure, true); /* EMIT SIGNAL */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool _pressed;
|
||||||
|
float _pressure;
|
||||||
|
uint16_t _raw;
|
||||||
|
float _last;
|
||||||
|
uint16_t hist[4];
|
||||||
|
unsigned int _cnt;
|
||||||
|
uint32_t _rgba;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif /* _ardour_surfaces_m2pad_h_ */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
169
libs/surfaces/maschine2/m2controls.h
Normal file
169
libs/surfaces/maschine2/m2controls.h
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_m2controls_h_
|
||||||
|
#define _ardour_surfaces_m2controls_h_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "m2_button.h"
|
||||||
|
#include "m2_encoder.h"
|
||||||
|
#include "m2_pad.h"
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
/** Abstraction for various variants:
|
||||||
|
* - NI Maschine Mikro
|
||||||
|
* - NI Maschine
|
||||||
|
* - NI Maschine Studio
|
||||||
|
*/
|
||||||
|
|
||||||
|
class M2Contols
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2Contols () {}
|
||||||
|
virtual ~M2Contols () {}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ModNone = 0,
|
||||||
|
ModShift,
|
||||||
|
} Modifier;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* Transport */
|
||||||
|
BtnRestart,
|
||||||
|
BtnStepLeft,
|
||||||
|
BtnStepRight,
|
||||||
|
BtnGrid,
|
||||||
|
BtnPlay,
|
||||||
|
BtnRec,
|
||||||
|
BtnErase,
|
||||||
|
BtnShift,
|
||||||
|
|
||||||
|
/* modes */
|
||||||
|
BtnScene,
|
||||||
|
BtnPattern,
|
||||||
|
BtnPadMode,
|
||||||
|
BtnNavigate, // aka. "view" on Mikro
|
||||||
|
BtnDuplicate,
|
||||||
|
BtnSelect,
|
||||||
|
BtnSolo,
|
||||||
|
BtnMute,
|
||||||
|
|
||||||
|
/* global */
|
||||||
|
#if 0
|
||||||
|
BtnArrange, // Studio only
|
||||||
|
BtnMix, // Studio only
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BtnControl, // Studio: "Channel"
|
||||||
|
BtnStep, // Studio: "Plug-In"
|
||||||
|
BtnBrowse,
|
||||||
|
BtnSampling,
|
||||||
|
BtnSelLeft,
|
||||||
|
BtnSelRight,
|
||||||
|
BtnAll,
|
||||||
|
BtnAuto,
|
||||||
|
|
||||||
|
/* master */
|
||||||
|
BtnVolume,
|
||||||
|
BtnSwing,
|
||||||
|
BtnTempo,
|
||||||
|
BtnNavLeft,
|
||||||
|
BtnNavRight,
|
||||||
|
BtnEnter,
|
||||||
|
BtnNoteRepeat, // Tap
|
||||||
|
BtnWheel, // Encoder Push
|
||||||
|
|
||||||
|
/* Selectors above display */
|
||||||
|
BtnTop0, BtnTop1, BtnTop2, BtnTop3, // Mikro F1, F2, F3
|
||||||
|
BtnTop4, BtnTop5, BtnTop6, BtnTop7,
|
||||||
|
|
||||||
|
/* Maschine & Studio "Groups" */
|
||||||
|
BtnGroupA, BtnGroupB, BtnGroupC, BtnGroupD,
|
||||||
|
BtnGroupE, BtnGroupF, BtnGroupG, BtnGroupH,
|
||||||
|
|
||||||
|
#if 1 // Studio only -- Edit
|
||||||
|
BtnCopy,
|
||||||
|
BtnPaste,
|
||||||
|
BtnNote,
|
||||||
|
BtnNudge,
|
||||||
|
BtnUndo,
|
||||||
|
BtnRedo,
|
||||||
|
BtnQuantize,
|
||||||
|
BtnClear,
|
||||||
|
|
||||||
|
BtnIn1, BtnIn2, BtnIn3, BtnIn4,
|
||||||
|
BtnMst, BtnGrp, BtnSnd, BtnCue,
|
||||||
|
#endif
|
||||||
|
} PhysicalButtonId;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
Play,
|
||||||
|
Rec,
|
||||||
|
Loop,
|
||||||
|
Metronom,
|
||||||
|
GotoStart,
|
||||||
|
GotoEnd,
|
||||||
|
JumpBackward,
|
||||||
|
JumpForward,
|
||||||
|
FastRewind,
|
||||||
|
FastForward,
|
||||||
|
Grid,
|
||||||
|
Delete,
|
||||||
|
Undo, Redo,
|
||||||
|
Save,
|
||||||
|
EncoderWheel, // multi-purpose
|
||||||
|
MasterVolume,
|
||||||
|
MasterTempo,
|
||||||
|
Solo, Mute,
|
||||||
|
Panic
|
||||||
|
} SemanticButtonId;
|
||||||
|
|
||||||
|
typedef std::map <PhysicalButtonId, M2ButtonInterface*> PhysicalMap;
|
||||||
|
typedef std::map <SemanticButtonId, M2ButtonInterface*> SematicMap;
|
||||||
|
|
||||||
|
virtual M2ButtonInterface* button (PhysicalButtonId id, Modifier m) {
|
||||||
|
if (id == BtnShift) {
|
||||||
|
return &_shift;
|
||||||
|
}
|
||||||
|
return &_dummy_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual M2ButtonInterface* button (SemanticButtonId id) {
|
||||||
|
return &_dummy_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual M2EncoderInterface* encoder (unsigned int id) {
|
||||||
|
return &_dummy_encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual M2PadInterface* pad (unsigned int id) {
|
||||||
|
return &_dummy_pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
M2ButtonInterface _dummy_button;
|
||||||
|
M2EncoderInterface _dummy_encoder;
|
||||||
|
M2PadInterface _dummy_pad;
|
||||||
|
|
||||||
|
M2ToggleHoldButton _shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif /* _ardour_surfaces_m2controls_h_*/
|
||||||
76
libs/surfaces/maschine2/m2device.h
Normal file
76
libs/surfaces/maschine2/m2device.h
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_maschine2hardware_h_
|
||||||
|
#define _ardour_surfaces_maschine2hardware_h_
|
||||||
|
|
||||||
|
#include <hidapi.h>
|
||||||
|
#include <cairomm/refptr.h>
|
||||||
|
#include <cairomm/surface.h>
|
||||||
|
#include "pbd/signals.h"
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2Contols;
|
||||||
|
|
||||||
|
/** Abstraction for various variants:
|
||||||
|
* - NI Maschine Mikro
|
||||||
|
* - NI Maschine
|
||||||
|
* - NI Maschine Studio
|
||||||
|
*/
|
||||||
|
|
||||||
|
class M2Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
M2Device ()
|
||||||
|
: _splashcnt (0)
|
||||||
|
, _blink_counter (0)
|
||||||
|
, _blink_shade (0.f)
|
||||||
|
{}
|
||||||
|
virtual ~M2Device () {}
|
||||||
|
|
||||||
|
virtual void clear (bool splash = false) {
|
||||||
|
if (splash) {
|
||||||
|
_splashcnt = 0;
|
||||||
|
} else {
|
||||||
|
_splashcnt = _splashtime;
|
||||||
|
}
|
||||||
|
_blink_counter = 0;
|
||||||
|
_blink_shade = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void read (hid_device*, M2Contols*) = 0;
|
||||||
|
virtual void write (hid_device*, M2Contols*) = 0;
|
||||||
|
virtual Cairo::RefPtr<Cairo::ImageSurface> surface () = 0;
|
||||||
|
|
||||||
|
PBD::Signal0<bool> vblank;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void bump_blink () {
|
||||||
|
_blink_counter = (_blink_counter + 1) % 12;
|
||||||
|
_blink_shade = fabsf (1.f - _blink_counter / 6.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t _splashcnt;
|
||||||
|
static const uint32_t _splashtime = 25 * 3;
|
||||||
|
unsigned int _blink_counter;
|
||||||
|
float _blink_shade;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif /* _ardour_surfaces_maschine2_h_*/
|
||||||
288
libs/surfaces/maschine2/maschine2.cc
Normal file
288
libs/surfaces/maschine2/maschine2.cc
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "pbd/compose.h"
|
||||||
|
#include "pbd/error.h"
|
||||||
|
#include "pbd/i18n.h"
|
||||||
|
#include "pbd/abstract_ui.cc" // instantiate template
|
||||||
|
|
||||||
|
#include "ardour/async_midi_port.h"
|
||||||
|
#include "ardour/audioengine.h"
|
||||||
|
#include "ardour/session.h"
|
||||||
|
|
||||||
|
#include "midi++/port.h"
|
||||||
|
|
||||||
|
#include "maschine2.h"
|
||||||
|
|
||||||
|
#include "m2_dev_mk2.h"
|
||||||
|
#include "m2_map_mk2.h"
|
||||||
|
|
||||||
|
#include "m2_dev_mikro.h"
|
||||||
|
#include "m2_map_mikro.h"
|
||||||
|
|
||||||
|
#include "canvas.h"
|
||||||
|
|
||||||
|
using namespace ARDOUR;
|
||||||
|
using namespace PBD;
|
||||||
|
using namespace ArdourSurface;
|
||||||
|
|
||||||
|
Maschine2::Maschine2 (ARDOUR::Session& s)
|
||||||
|
: ControlProtocol (s, string (X_("NI Maschine2")))
|
||||||
|
, AbstractUI<Maschine2Request> (name())
|
||||||
|
, _handle (0)
|
||||||
|
, _hw (0)
|
||||||
|
, _ctrl (0)
|
||||||
|
, _canvas (0)
|
||||||
|
, _maschine_type (Maschine)
|
||||||
|
, _master_state (MST_NONE)
|
||||||
|
{
|
||||||
|
if (hid_init()) {
|
||||||
|
throw Maschine2Exception ("HIDAPI initialization failed");
|
||||||
|
}
|
||||||
|
run_event_loop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Maschine2::~Maschine2 ()
|
||||||
|
{
|
||||||
|
stop ();
|
||||||
|
hid_exit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
Maschine2::request_factory (uint32_t num_requests)
|
||||||
|
{
|
||||||
|
return request_buffer_factory (num_requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::do_request (Maschine2Request* req)
|
||||||
|
{
|
||||||
|
if (req->type == CallSlot) {
|
||||||
|
call_slot (MISSING_INVALIDATOR, req->the_slot);
|
||||||
|
} else if (req->type == Quit) {
|
||||||
|
stop ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Maschine2::set_active (bool yn)
|
||||||
|
{
|
||||||
|
if (yn == active()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yn) {
|
||||||
|
if (start ()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (stop ()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlProtocol::set_active (yn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLNode&
|
||||||
|
Maschine2::get_state()
|
||||||
|
{
|
||||||
|
XMLNode& node (ControlProtocol::get_state());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Maschine2::set_state (const XMLNode & node, int version)
|
||||||
|
{
|
||||||
|
if (ControlProtocol::set_state (node, version)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Maschine2::start ()
|
||||||
|
{
|
||||||
|
_maschine_type = Maschine;
|
||||||
|
_handle = hid_open (0x17cc, 0x1140, NULL); // Maschine
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (!_handle) {
|
||||||
|
if ((_handle = hid_open (0x17cc, 0x1300, NULL))) {
|
||||||
|
_maschine_type = Studio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!_handle) {
|
||||||
|
if ((_handle = hid_open (0x17cc, 0x1110, NULL))) {
|
||||||
|
_maschine_type = Mikro;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_handle) {
|
||||||
|
if ((_handle = hid_open (0x17cc, 0x1200, NULL))) {
|
||||||
|
_maschine_type = Mikro;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_handle) {
|
||||||
|
error << _("Cannot find or connect to Maschine2\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_set_nonblocking (_handle, 1);
|
||||||
|
|
||||||
|
_midi_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Maschine2 out"), true);
|
||||||
|
if (!_midi_out) {
|
||||||
|
error << _("Cannot create Maschine2 PAD MIDI Port");
|
||||||
|
stop ();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_out)->set_flush_at_cycle_start (true);
|
||||||
|
_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_out).get();
|
||||||
|
|
||||||
|
switch (_maschine_type) {
|
||||||
|
case Mikro:
|
||||||
|
_hw = new Maschine2Mikro ();
|
||||||
|
_ctrl = new M2MapMikro ();
|
||||||
|
info << _("Maschine2 Mikro control surface intialized");
|
||||||
|
break;
|
||||||
|
case Maschine:
|
||||||
|
_hw = new Maschine2Mk2 ();
|
||||||
|
_ctrl = new M2MapMk2 ();
|
||||||
|
info << _("Maschine2 control surface intialized");
|
||||||
|
break;
|
||||||
|
case Studio:
|
||||||
|
error << _("Maschine2 Studio is not yet supported");
|
||||||
|
stop ();
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_canvas = new Maschine2Canvas (*this, _hw);
|
||||||
|
connect_signals ();
|
||||||
|
|
||||||
|
Glib::RefPtr<Glib::TimeoutSource> write_timeout = Glib::TimeoutSource::create (40);
|
||||||
|
write_connection = write_timeout->connect (sigc::mem_fun (*this, &Maschine2::dev_write));
|
||||||
|
write_timeout->attach (main_loop()->get_context());
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
Glib::RefPtr<Glib::TimeoutSource> read_timeout = Glib::TimeoutSource::create (20);
|
||||||
|
#else
|
||||||
|
Glib::RefPtr<Glib::TimeoutSource> read_timeout = Glib::TimeoutSource::create (1);
|
||||||
|
#endif
|
||||||
|
read_connection = read_timeout->connect (sigc::mem_fun (*this, &Maschine2::dev_read));
|
||||||
|
read_timeout->attach (main_loop ()->get_context());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Maschine2::stop ()
|
||||||
|
{
|
||||||
|
read_connection.disconnect ();
|
||||||
|
write_connection.disconnect ();
|
||||||
|
|
||||||
|
session_connections.drop_connections ();
|
||||||
|
button_connections.drop_connections ();
|
||||||
|
|
||||||
|
if (_handle && _hw) {
|
||||||
|
_hw->clear ();
|
||||||
|
_hw->write (_handle, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_close (_handle);
|
||||||
|
_handle = 0;
|
||||||
|
|
||||||
|
stop_event_loop ();
|
||||||
|
|
||||||
|
if (_midi_out) {
|
||||||
|
AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
|
||||||
|
asp->drain (10000, 500000);
|
||||||
|
|
||||||
|
AudioEngine::instance()->unregister_port (_midi_out);
|
||||||
|
_midi_out.reset ((ARDOUR::Port*) 0);
|
||||||
|
_output_port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete _canvas;
|
||||||
|
delete _hw;
|
||||||
|
delete _ctrl;
|
||||||
|
|
||||||
|
_canvas = 0;
|
||||||
|
_hw = 0;
|
||||||
|
_ctrl = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::thread_init ()
|
||||||
|
{
|
||||||
|
pthread_set_name (event_loop_name().c_str());
|
||||||
|
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 1024);
|
||||||
|
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 1024);
|
||||||
|
|
||||||
|
struct sched_param rtparam;
|
||||||
|
memset (&rtparam, 0, sizeof (rtparam));
|
||||||
|
rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
|
||||||
|
if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
|
||||||
|
// do we care? not particularly.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::run_event_loop ()
|
||||||
|
{
|
||||||
|
BaseUI::run ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Maschine2::stop_event_loop ()
|
||||||
|
{
|
||||||
|
BaseUI::quit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Maschine2::dev_read ()
|
||||||
|
{
|
||||||
|
_hw->read (_handle, _ctrl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Maschine2::dev_write ()
|
||||||
|
{
|
||||||
|
_hw->write (_handle, _ctrl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move to callbacks.c || M2Contols implementation
|
||||||
|
Maschine2Layout*
|
||||||
|
Maschine2::current_layout() const
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
161
libs/surfaces/maschine2/maschine2.h
Normal file
161
libs/surfaces/maschine2/maschine2.h
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_surfaces_maschine2_h_
|
||||||
|
#define _ardour_surfaces_maschine2_h_
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <hidapi.h>
|
||||||
|
|
||||||
|
#define ABSTRACT_UI_EXPORTS
|
||||||
|
#include "pbd/abstract_ui.h"
|
||||||
|
#include "ardour/types.h"
|
||||||
|
#include "ardour/port.h"
|
||||||
|
#include "control_protocol/control_protocol.h"
|
||||||
|
|
||||||
|
namespace MIDI {
|
||||||
|
class Port;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ArdourSurface {
|
||||||
|
|
||||||
|
class M2Contols;
|
||||||
|
class M2Device;
|
||||||
|
class Maschine2Canvas;
|
||||||
|
class Maschine2Layout;
|
||||||
|
|
||||||
|
class Maschine2Exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Maschine2Exception (const std::string& msg) : _msg (msg) { }
|
||||||
|
virtual ~Maschine2Exception () throw () {}
|
||||||
|
const char* what () const throw () { return _msg.c_str (); }
|
||||||
|
private:
|
||||||
|
std::string _msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Maschine2Request : public BaseUI::BaseRequestObject {
|
||||||
|
public:
|
||||||
|
Maschine2Request () {}
|
||||||
|
~Maschine2Request () {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Maschine2: public ARDOUR::ControlProtocol, public AbstractUI<Maschine2Request>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Maschine2 (ARDOUR::Session&);
|
||||||
|
~Maschine2 ();
|
||||||
|
|
||||||
|
static void* request_factory (uint32_t);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool has_editor () const { return false; }
|
||||||
|
void* get_gui () const;
|
||||||
|
void tear_down_gui ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int set_active (bool yn);
|
||||||
|
XMLNode& get_state ();
|
||||||
|
int set_state (const XMLNode & node, int version);
|
||||||
|
|
||||||
|
Maschine2Canvas* canvas () const { return _canvas; }
|
||||||
|
Maschine2Layout* current_layout() const;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
Mikro,
|
||||||
|
Maschine,
|
||||||
|
Studio
|
||||||
|
} Maschine2Type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void do_request (Maschine2Request*);
|
||||||
|
|
||||||
|
int start ();
|
||||||
|
int stop ();
|
||||||
|
|
||||||
|
void thread_init ();
|
||||||
|
void run_event_loop ();
|
||||||
|
void stop_event_loop ();
|
||||||
|
|
||||||
|
sigc::connection read_connection;
|
||||||
|
sigc::connection write_connection;
|
||||||
|
|
||||||
|
bool dev_write ();
|
||||||
|
bool dev_read ();
|
||||||
|
|
||||||
|
hid_device* _handle;
|
||||||
|
M2Device* _hw;
|
||||||
|
M2Contols* _ctrl;
|
||||||
|
Maschine2Canvas* _canvas;
|
||||||
|
|
||||||
|
Maschine2Type _maschine_type;
|
||||||
|
|
||||||
|
PBD::ScopedConnectionList session_connections;
|
||||||
|
PBD::ScopedConnectionList button_connections;
|
||||||
|
|
||||||
|
void connect_signals ();
|
||||||
|
void stripable_selection_changed () {}
|
||||||
|
|
||||||
|
|
||||||
|
/* Master Mode */
|
||||||
|
enum MasterMode {
|
||||||
|
MST_NONE,
|
||||||
|
MST_VOLUME,
|
||||||
|
MST_TEMPO
|
||||||
|
} _master_state;
|
||||||
|
|
||||||
|
void handle_master_change (enum MasterMode);
|
||||||
|
void notify_master_change ();
|
||||||
|
|
||||||
|
/* PAD Port */
|
||||||
|
boost::shared_ptr<ARDOUR::Port> _midi_out;
|
||||||
|
MIDI::Port* _output_port;
|
||||||
|
|
||||||
|
/* callbacks */
|
||||||
|
void notify_record_state_changed ();
|
||||||
|
void notify_transport_state_changed ();
|
||||||
|
void notify_loop_state_changed ();
|
||||||
|
void notify_parameter_changed (std::string);
|
||||||
|
void notify_snap_change ();
|
||||||
|
void notify_session_dirty_changed ();
|
||||||
|
void notify_history_changed ();
|
||||||
|
|
||||||
|
void button_play ();
|
||||||
|
void button_record ();
|
||||||
|
void button_loop ();
|
||||||
|
void button_metronom ();
|
||||||
|
void button_rewind ();
|
||||||
|
|
||||||
|
void button_action (const std::string&, const std::string&);
|
||||||
|
|
||||||
|
void button_snap_released ();
|
||||||
|
void button_snap_pressed ();
|
||||||
|
void button_snap_changed (bool);
|
||||||
|
|
||||||
|
void encoder_master (int);
|
||||||
|
void button_encoder ();
|
||||||
|
|
||||||
|
void pad_event (unsigned int, float, bool);
|
||||||
|
void pad_change (unsigned int, float);
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
#endif /* _ardour_surfaces_maschine2_h_*/
|
||||||
43
libs/surfaces/maschine2/wscript
Normal file
43
libs/surfaces/maschine2/wscript
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from waflib.extras import autowaf as autowaf
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Mandatory variables
|
||||||
|
top = '.'
|
||||||
|
out = 'build'
|
||||||
|
|
||||||
|
def options(opt):
|
||||||
|
autowaf.set_options(opt)
|
||||||
|
|
||||||
|
def configure(conf):
|
||||||
|
conf.load ('compiler_cxx')
|
||||||
|
autowaf.configure(conf)
|
||||||
|
autowaf.check_pkg(conf, 'pangomm-1.4', uselib_store='PANGOMM', atleast_version='1.4', mandatory=True)
|
||||||
|
autowaf.check_pkg(conf, 'cairomm-1.0', uselib_store='CAIROMM', atleast_version='1.8.4', mandatory=True)
|
||||||
|
|
||||||
|
def build(bld):
|
||||||
|
obj = bld(features = 'cxx cxxshlib')
|
||||||
|
obj.source = '''
|
||||||
|
maschine2.cc
|
||||||
|
callbacks.cc
|
||||||
|
canvas.cc
|
||||||
|
interface.cc
|
||||||
|
layout.cc
|
||||||
|
m2_dev_mk2.cc
|
||||||
|
m2_map_mk2.cc
|
||||||
|
m2_dev_mikro.cc
|
||||||
|
m2_map_mikro.cc
|
||||||
|
'''
|
||||||
|
obj.export_includes = ['.']
|
||||||
|
obj.defines = [ 'PACKAGE="ardour_maschine2"' ]
|
||||||
|
obj.defines += [ 'ARDOURSURFACE_DLL_EXPORTS' ]
|
||||||
|
obj.defines += [ 'VERSIONSTRING="' + bld.env['VERSION'] + '"' ]
|
||||||
|
obj.includes = [ '.', './maschine2']
|
||||||
|
obj.name = 'libardour_maschine2'
|
||||||
|
obj.target = 'ardour_maschine2'
|
||||||
|
obj.uselib = 'CAIROMM PANGOMM'
|
||||||
|
obj.use = 'libardour libardour_cp libpbd libcanvas hidapi libgtkmm2ext'
|
||||||
|
obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces')
|
||||||
|
|
||||||
|
def shutdown():
|
||||||
|
autowaf.shutdown()
|
||||||
|
|
@ -50,6 +50,9 @@ def configure(conf):
|
||||||
else:
|
else:
|
||||||
print ('You are missing the libusb-1.0 development package needed to compile Push2 support')
|
print ('You are missing the libusb-1.0 development package needed to compile Push2 support')
|
||||||
|
|
||||||
|
if conf.is_defined('HAVE_HIDAPI'):
|
||||||
|
children += [ 'maschine2' ]
|
||||||
|
|
||||||
if autowaf.check_pkg (conf, 'liblo', mandatory=False, uselib_store="LO", atleast_version="0.24"):
|
if autowaf.check_pkg (conf, 'liblo', mandatory=False, uselib_store="LO", atleast_version="0.24"):
|
||||||
children += [ 'osc' ]
|
children += [ 'osc' ]
|
||||||
|
|
||||||
|
|
@ -88,6 +91,8 @@ def build(bld):
|
||||||
bld.recurse('tranzport')
|
bld.recurse('tranzport')
|
||||||
if bld.is_defined('HAVE_USB'):
|
if bld.is_defined('HAVE_USB'):
|
||||||
bld.recurse('push2')
|
bld.recurse('push2')
|
||||||
|
if bld.is_defined('HAVE_HIDAPI'):
|
||||||
|
bld.recurse('maschine2')
|
||||||
|
|
||||||
def shutdown():
|
def shutdown():
|
||||||
autowaf.shutdown()
|
autowaf.shutdown()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue