Faderport8 control surface support
This commit is contained in:
parent
d64ca9be08
commit
d43a23fe28
|
@ -13,7 +13,7 @@ export GTK2_RC_FILES=/nonexistent
|
|||
# can find all the components.
|
||||
#
|
||||
|
||||
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$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
|
||||
export ARDOUR_PANNER_PATH=$libs/panners
|
||||
export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
|
||||
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
||||
|
|
|
@ -80,6 +80,7 @@ namespace PBD {
|
|||
LIBARDOUR_API extern DebugBits BackendPorts;
|
||||
LIBARDOUR_API extern DebugBits VSTCallbacks;
|
||||
LIBARDOUR_API extern DebugBits FaderPort;
|
||||
LIBARDOUR_API extern DebugBits FaderPort8;
|
||||
LIBARDOUR_API extern DebugBits CC121;
|
||||
LIBARDOUR_API extern DebugBits VCA;
|
||||
LIBARDOUR_API extern DebugBits Push2;
|
||||
|
|
|
@ -77,6 +77,7 @@ PBD::DebugBits PBD::DEBUG::BackendThreads = PBD::new_debug_bit ("backendthreads"
|
|||
PBD::DebugBits PBD::DEBUG::BackendPorts = PBD::new_debug_bit ("backendports");
|
||||
PBD::DebugBits PBD::DEBUG::VSTCallbacks = PBD::new_debug_bit ("vstcallbacks");
|
||||
PBD::DebugBits PBD::DEBUG::FaderPort = PBD::new_debug_bit ("faderport");
|
||||
PBD::DebugBits PBD::DEBUG::FaderPort8 = PBD::new_debug_bit ("faderport8");
|
||||
PBD::DebugBits PBD::DEBUG::CC121 = PBD::new_debug_bit ("cc121");
|
||||
PBD::DebugBits PBD::DEBUG::VCA = PBD::new_debug_bit ("vca");
|
||||
PBD::DebugBits PBD::DEBUG::Push2 = PBD::new_debug_bit ("push2");
|
||||
|
|
|
@ -896,6 +896,7 @@ PortManager::port_is_control_only (std::string const& name)
|
|||
const char * const control_only_ports[] = {
|
||||
X_(".*Ableton Push.*"),
|
||||
X_(".*FaderPort .*"),
|
||||
X_(".*FaderPort8 .*"),
|
||||
};
|
||||
|
||||
pattern = "(";
|
||||
|
|
470
libs/surfaces/faderport8/actions.cc
Normal file
470
libs/surfaces/faderport8/actions.cc
Normal file
|
@ -0,0 +1,470 @@
|
|||
/* Faderport 8 Control Surface
|
||||
* This is the button "Controller" of the MVC surface inteface,
|
||||
* see callbacks.cc for the "View".
|
||||
*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ardour/dB.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/session_configuration.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
#include "gtkmm2ext/actions.h"
|
||||
|
||||
#include "faderport8.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
using namespace std;
|
||||
using namespace ArdourSurface::FP8Types;
|
||||
|
||||
#define BindMethod(ID, CB) \
|
||||
_ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this));
|
||||
|
||||
#define BindFunction(ID, ACT, CB, ...) \
|
||||
_ctrls.button (FP8Controls::ID). ACT .connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this, __VA_ARGS__));
|
||||
|
||||
#define BindAction(ID, GRP, ITEM) \
|
||||
_ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_action, this, GRP, ITEM));
|
||||
|
||||
#define BindUserAction(ID) \
|
||||
_ctrls.button (ID).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, true, ID)); \
|
||||
_ctrls.button (ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, false, ID));
|
||||
|
||||
void
|
||||
FaderPort8::setup_actions ()
|
||||
{
|
||||
BindMethod (BtnPlay, button_play);
|
||||
BindMethod (BtnStop, button_stop);
|
||||
BindMethod (BtnLoop, button_loop);
|
||||
BindMethod (BtnRecord, button_record);
|
||||
BindMethod (BtnClick, button_metronom);
|
||||
BindAction (BtnRedo, "Editor", "redo");
|
||||
|
||||
BindAction (BtnSave, "Common", "Save");
|
||||
BindAction (BtnUndo, "Editor", "undo");
|
||||
BindAction (BtnRedo, "Editor", "redo");
|
||||
|
||||
BindAction (BtnSoloClear, "Main", "cancel-solo");
|
||||
BindMethod (BtnMuteClear, button_mute_clear);
|
||||
|
||||
BindMethod (FP8Controls::BtnArmAll, button_arm_all);
|
||||
|
||||
BindFunction (BtnRewind, pressed, button_varispeed, false);
|
||||
BindFunction (BtnFastForward, pressed, button_varispeed, true);
|
||||
|
||||
BindFunction (BtnPrev, released, button_prev_next, false);
|
||||
BindFunction (BtnNext, released, button_prev_next, true);
|
||||
|
||||
BindFunction (BtnArm, pressed, button_arm, true);
|
||||
BindFunction (BtnArm, released, button_arm, false);
|
||||
|
||||
BindFunction (BtnAOff, released, button_automation, ARDOUR::Off);
|
||||
BindFunction (BtnATouch, released, button_automation, ARDOUR::Touch);
|
||||
BindFunction (BtnARead, released, button_automation, ARDOUR::Play);
|
||||
BindFunction (BtnAWrite, released, button_automation, ARDOUR::Write);
|
||||
|
||||
_ctrls.button (FP8Controls::BtnEncoder).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_encoder, this));
|
||||
_ctrls.button (FP8Controls::BtnParam).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_parameter, this));
|
||||
|
||||
|
||||
BindAction (BtnBypass, "Mixer", "ab-plugins");
|
||||
BindAction (BtnBypassAll, "Mixer", "ab-plugins"); // XXX
|
||||
|
||||
BindAction (BtnMacro, "Mixer", "show-editor");
|
||||
BindAction (BtnLink, "Window", "show-mixer");
|
||||
|
||||
BindAction (BtnOpen, "Common", "addExistingAudioFiles");
|
||||
BindAction (BtnLock, "Editor", "lock");
|
||||
|
||||
// user-specific
|
||||
for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
|
||||
i != _ctrls.user_buttons ().end (); ++i) {
|
||||
BindUserAction ((*i).first);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_play ()
|
||||
{
|
||||
if (session->transport_rolling ()) {
|
||||
if (session->transport_speed () != 1.0) {
|
||||
session->request_transport_speed (1.0);
|
||||
} else {
|
||||
transport_stop ();
|
||||
}
|
||||
} else {
|
||||
transport_play ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_stop ()
|
||||
{
|
||||
transport_stop ();
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_record ()
|
||||
{
|
||||
set_record_enable (!get_record_enabled ());
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_loop ()
|
||||
{
|
||||
loop_toggle ();
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_metronom ()
|
||||
{
|
||||
Config->set_clicking (!Config->get_clicking ());
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_automation (ARDOUR::AutoState as)
|
||||
{
|
||||
FaderMode fadermode = _ctrls.fader_mode ();
|
||||
switch (fadermode) {
|
||||
case ModePlugins:
|
||||
#if 0 // Plugin Control Automation Mode
|
||||
for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
|
||||
((*i).ac)->set_automation_state (as);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
case ModeSend:
|
||||
if (first_selected_stripable()) {
|
||||
#if 0 // Send Level Automation
|
||||
boost::shared_ptr<Stripable> s = first_selected_stripable();
|
||||
boost::shared_ptr<AutomationControl> send;
|
||||
uint32_t i = 0;
|
||||
while (0 != (send = s->send_level_controllable (i))) {
|
||||
send->set_automation_state (as);
|
||||
++i;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// apply to all selected tracks
|
||||
StripableList all;
|
||||
session->get_stripables (all);
|
||||
for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
|
||||
if ((*i)->is_master() || (*i)->is_monitor()) {
|
||||
continue;
|
||||
}
|
||||
if (!(*i)->is_selected()) {
|
||||
continue;
|
||||
}
|
||||
boost::shared_ptr<AutomationControl> ac;
|
||||
switch (fadermode) {
|
||||
case ModeTrack:
|
||||
ac = (*i)->gain_control ();
|
||||
break;
|
||||
case ModePan:
|
||||
ac = (*i)->pan_azimuth_control ();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ac) {
|
||||
ac->set_automation_state (as);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_varispeed (bool ffw)
|
||||
{
|
||||
/* pressing both rew + ffwd -> return to zero */
|
||||
FP8ButtonInterface& b_rew = _ctrls.button (FP8Controls::BtnRewind);
|
||||
FP8ButtonInterface& b_ffw = _ctrls.button (FP8Controls::BtnFastForward);
|
||||
if (b_rew.is_pressed () && b_ffw.is_pressed ()){
|
||||
// stop key-repeat
|
||||
dynamic_cast<FP8RepeatButton*>(&b_ffw)->stop_repeat();
|
||||
dynamic_cast<FP8RepeatButton*>(&b_rew)->stop_repeat();
|
||||
AccessAction ("Transport", "GotoStart");
|
||||
return;
|
||||
}
|
||||
|
||||
// switch play direction, if needed
|
||||
if (ffw) {
|
||||
if (session->transport_speed () <= 0) {
|
||||
session->request_transport_speed (1.0);
|
||||
return ;
|
||||
}
|
||||
} else {
|
||||
if (session->transport_speed () >= 0) {
|
||||
session->request_transport_speed (-1.0);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
// incremetally increase speed. double speed every 10 clicks
|
||||
// (keypress auto-repeat is 100ms)
|
||||
float maxspeed = Config->get_shuttle_max_speed();
|
||||
float speed = exp2f(0.1f) * session->transport_speed ();
|
||||
speed = std::max (-maxspeed, std::min (maxspeed, speed));
|
||||
session->request_transport_speed (speed, false);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_mute_clear ()
|
||||
{
|
||||
StripableList all;
|
||||
session->get_stripables (all);
|
||||
boost::shared_ptr<ControlList> cl (new ControlList);
|
||||
for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
|
||||
if ((*i)->is_master() || (*i)->is_monitor()) {
|
||||
continue;
|
||||
}
|
||||
boost::shared_ptr<AutomationControl> ac = (*i)->mute_control();
|
||||
if (ac) {
|
||||
cl->push_back (ac);
|
||||
}
|
||||
}
|
||||
session->set_controls (cl, 0.0, PBD::Controllable::UseGroup);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_arm (bool press)
|
||||
{
|
||||
FaderMode fadermode = _ctrls.fader_mode ();
|
||||
if (fadermode == ModeTrack || fadermode == ModePan) {
|
||||
_ctrls.button (FP8Controls::BtnArm).set_active (press);
|
||||
ARMButtonChange (press);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_arm_all ()
|
||||
{
|
||||
BasicUI::all_tracks_rec_in ();
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_action (const std::string& group, const std::string& item)
|
||||
{
|
||||
AccessAction (group, item);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::button_prev_next (bool next)
|
||||
{
|
||||
switch (_ctrls.nav_mode()) {
|
||||
case NavMaster:
|
||||
case NavChannel:
|
||||
case NavScroll:
|
||||
bank (!next, false);
|
||||
break;
|
||||
case NavBank:
|
||||
bank (!next, true);
|
||||
break;
|
||||
case NavZoom:
|
||||
if (next) {
|
||||
StepTracksDown ();
|
||||
} else {
|
||||
StepTracksUp ();
|
||||
}
|
||||
break;
|
||||
case NavSection:
|
||||
// TODO nudge
|
||||
break;
|
||||
case NavMarker:
|
||||
if (next) {
|
||||
next_marker ();
|
||||
} else {
|
||||
prev_marker ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle navigation encoder press */
|
||||
void
|
||||
FaderPort8::button_encoder ()
|
||||
{
|
||||
switch (_ctrls.nav_mode()) {
|
||||
case NavZoom:
|
||||
ZoomToSession (); // XXX undo zoom
|
||||
break;
|
||||
case NavScroll:
|
||||
ZoomToSession ();
|
||||
break;
|
||||
case NavChannel:
|
||||
case NavBank:
|
||||
move_selected_into_view ();
|
||||
break;
|
||||
case NavMaster:
|
||||
{
|
||||
/* master || monitor level -- reset to 0dB */
|
||||
boost::shared_ptr<AutomationControl> ac;
|
||||
if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
|
||||
ac = session->monitor_out()->gain_control ();
|
||||
} else if (session->master_out()) {
|
||||
ac = session->master_out()->gain_control ();
|
||||
}
|
||||
if (ac) {
|
||||
ac->set_value (ac->normal(), PBD::Controllable::NoGroup);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NavSection:
|
||||
break;
|
||||
case NavMarker:
|
||||
{
|
||||
string markername;
|
||||
/* Don't add another mark if one exists within 1/100th of a second of
|
||||
* the current position and we're not rolling.
|
||||
*/
|
||||
framepos_t where = session->audible_frame();
|
||||
if (session->transport_stopped() && session->locations()->mark_at (where, session->frame_rate() / 100.0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
session->locations()->next_available_name (markername,"mark");
|
||||
add_marker (markername);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle navigation encoder turn */
|
||||
void
|
||||
FaderPort8::encoder_navigate (bool neg, int steps)
|
||||
{
|
||||
/* special-case metronome level */
|
||||
if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
|
||||
// compare to ARDOUR_UI::click_button_scroll()
|
||||
gain_t gain = Config->get_click_gain();
|
||||
float gain_db = accurate_coefficient_to_dB (gain);
|
||||
gain_db += (neg ? -1.f : 1.f) * steps;
|
||||
gain_db = std::max (-60.f, gain_db);
|
||||
gain = dB_to_coefficient (gain_db);
|
||||
gain = std::min (gain, Config->get_max_gain());
|
||||
Config->set_click_gain (gain);
|
||||
_ctrls.button (FP8Controls::BtnClick).ignore_release();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_ctrls.nav_mode()) {
|
||||
case NavChannel:
|
||||
if (neg) {
|
||||
StepTracksUp ();
|
||||
} else {
|
||||
StepTracksDown ();
|
||||
}
|
||||
break;
|
||||
case NavZoom:
|
||||
if (neg) {
|
||||
ZoomOut ();
|
||||
} else {
|
||||
ZoomIn ();
|
||||
}
|
||||
break;
|
||||
case NavMarker:
|
||||
case NavScroll:
|
||||
ScrollTimeline ((neg ? -1.f : 1.f) * steps / (shift_mod() ? 1024.f : 256.f));
|
||||
break;
|
||||
case NavBank:
|
||||
bank (neg, false);
|
||||
break;
|
||||
case NavMaster:
|
||||
{
|
||||
/* master || monitor level */
|
||||
boost::shared_ptr<AutomationControl> ac;
|
||||
if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
|
||||
ac = session->monitor_out()->gain_control ();
|
||||
} else if (session->master_out()) {
|
||||
ac = session->master_out()->gain_control ();
|
||||
}
|
||||
if (ac) {
|
||||
double v = ac->internal_to_interface (ac->get_value());
|
||||
v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
|
||||
ac->set_value (ac->interface_to_internal(v), PBD::Controllable::NoGroup);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NavSection:
|
||||
// nudge event
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle pan/param encoder press */
|
||||
void
|
||||
FaderPort8::button_parameter ()
|
||||
{
|
||||
switch (_ctrls.fader_mode()) {
|
||||
case ModeTrack:
|
||||
case ModePan:
|
||||
// pan-width see FaderPort8::encoder_parameter()
|
||||
break;
|
||||
case ModePlugins:
|
||||
break;
|
||||
case ModeSend:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle pan/param encoder turn */
|
||||
void
|
||||
FaderPort8::encoder_parameter (bool neg, int steps)
|
||||
{
|
||||
switch (_ctrls.fader_mode()) {
|
||||
case ModeTrack:
|
||||
case ModePan:
|
||||
{
|
||||
boost::shared_ptr<Stripable> s = first_selected_stripable();
|
||||
if (s) {
|
||||
boost::shared_ptr<AutomationControl> ac;
|
||||
if (_ctrls.button (FP8Controls::BtnParam).is_pressed ()) {
|
||||
ac = s->pan_width_control ();
|
||||
} else {
|
||||
ac = s->pan_azimuth_control ();
|
||||
}
|
||||
if (ac) {
|
||||
double v = ac->internal_to_interface (ac->get_value());
|
||||
v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
|
||||
ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ModePlugins:
|
||||
case ModeSend:
|
||||
while (steps > 0) {
|
||||
bank_param (neg, false);
|
||||
--steps;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle user-specific actions */
|
||||
void
|
||||
FaderPort8::button_user (bool press, FP8Controls::ButtonId btn)
|
||||
{
|
||||
_user_action_map[btn].call (*this, press);
|
||||
}
|
206
libs/surfaces/faderport8/callbacks.cc
Normal file
206
libs/surfaces/faderport8/callbacks.cc
Normal file
|
@ -0,0 +1,206 @@
|
|||
/* Faderport 8 Control Surface
|
||||
* This is the button "View" of the MVC surface inteface,
|
||||
* see actions.cc for the "Controller"
|
||||
*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/session_configuration.h"
|
||||
|
||||
#include "gtkmm2ext/actions.h"
|
||||
|
||||
#include "faderport8.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourSurface::FP8Types;
|
||||
|
||||
void
|
||||
FaderPort8::connect_session_signals ()
|
||||
{
|
||||
session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_stripable_added_or_removed, this), this);
|
||||
PresentationInfo::Change.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_pi_property_changed, this, _1), this);
|
||||
|
||||
Config->ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_parameter_changed, this, _1), this);
|
||||
session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_parameter_changed, this, _1), this);
|
||||
|
||||
session->TransportStateChange.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_transport_state_changed, this), this);
|
||||
session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_loop_state_changed, this), this);
|
||||
session->RecordStateChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_record_state_changed, this), this);
|
||||
|
||||
session->DirtyChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_session_dirty_changed, this), this);
|
||||
session->SoloChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_solo_changed, this), this);
|
||||
session->MuteChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_mute_changed, this), this);
|
||||
session->history().Changed.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_history_changed, this), this);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::send_session_state ()
|
||||
{
|
||||
notify_transport_state_changed ();
|
||||
notify_record_state_changed ();
|
||||
notify_session_dirty_changed ();
|
||||
notify_history_changed ();
|
||||
notify_solo_changed ();
|
||||
notify_mute_changed ();
|
||||
notify_parameter_changed ("clicking");
|
||||
|
||||
notify_automation_mode_changed (); // XXX (stip specific, see below)
|
||||
}
|
||||
|
||||
// TODO: AutomationState display of plugin & send automation ?!
|
||||
void
|
||||
FaderPort8::notify_automation_mode_changed ()
|
||||
{
|
||||
boost::shared_ptr<Stripable> s = first_selected_stripable();
|
||||
boost::shared_ptr<AutomationControl> ac;
|
||||
if (s) {
|
||||
switch (_ctrls.fader_mode ()) {
|
||||
case ModeTrack:
|
||||
ac = s->gain_control();
|
||||
break;
|
||||
case ModePan:
|
||||
ac = s->pan_azimuth_control();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s || !ac) {
|
||||
_ctrls.button (FP8Controls::BtnALatch).set_active (false);
|
||||
_ctrls.button (FP8Controls::BtnATrim).set_active (false);
|
||||
_ctrls.button (FP8Controls::BtnAOff).set_active (false);
|
||||
_ctrls.button (FP8Controls::BtnATouch).set_active (false);
|
||||
_ctrls.button (FP8Controls::BtnARead).set_active (false);
|
||||
_ctrls.button (FP8Controls::BtnAWrite).set_active (false);
|
||||
return;
|
||||
}
|
||||
|
||||
ARDOUR::AutoState as = ac->automation_state();
|
||||
_ctrls.button (FP8Controls::BtnAOff).set_active (as == Off);
|
||||
_ctrls.button (FP8Controls::BtnATouch).set_active (as == Touch);
|
||||
_ctrls.button (FP8Controls::BtnARead).set_active (as == Play);
|
||||
_ctrls.button (FP8Controls::BtnAWrite).set_active (as == Write);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_parameter_changed (std::string param)
|
||||
{
|
||||
if (param == "clicking") {
|
||||
_ctrls.button (FP8Controls::BtnClick).set_active (Config->get_clicking ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_transport_state_changed ()
|
||||
{
|
||||
if (session->transport_rolling ()) {
|
||||
_ctrls.button (FP8Controls::BtnPlay).set_active (true);
|
||||
_ctrls.button (FP8Controls::BtnStop).set_active (false);
|
||||
} else {
|
||||
_ctrls.button (FP8Controls::BtnPlay).set_active (false);
|
||||
_ctrls.button (FP8Controls::BtnStop).set_active (true);
|
||||
}
|
||||
|
||||
/* set rewind/fastforward lights */
|
||||
const float ts = session->transport_speed ();
|
||||
FP8ButtonInterface& b_rew = _ctrls.button (FP8Controls::BtnRewind);
|
||||
FP8ButtonInterface& b_ffw = _ctrls.button (FP8Controls::BtnFastForward);
|
||||
|
||||
const bool rew = (ts < 0.f);
|
||||
const bool ffw = (ts > 0.f && ts != 1.f);
|
||||
if (b_rew.is_active() != rew) {
|
||||
b_rew.set_active (rew);
|
||||
}
|
||||
if (b_ffw.is_active() != ffw) {
|
||||
b_ffw.set_active (ffw);
|
||||
}
|
||||
|
||||
notify_loop_state_changed ();
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_record_state_changed ()
|
||||
{
|
||||
switch (session->record_status ()) {
|
||||
case Session::Disabled:
|
||||
_ctrls.button (FP8Controls::BtnRecord).set_active (0);
|
||||
_ctrls.button (FP8Controls::BtnRecord).set_blinking (false);
|
||||
break;
|
||||
case Session::Enabled:
|
||||
_ctrls.button (FP8Controls::BtnRecord).set_active (true);
|
||||
_ctrls.button (FP8Controls::BtnRecord).set_blinking (true);
|
||||
break;
|
||||
case Session::Recording:
|
||||
_ctrls.button (FP8Controls::BtnRecord).set_active (true);
|
||||
_ctrls.button (FP8Controls::BtnRecord).set_blinking (false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_loop_state_changed ()
|
||||
{
|
||||
bool looping = false;
|
||||
Location* looploc = session->locations ()->auto_loop_location ();
|
||||
if (looploc && session->get_play_loop ()) {
|
||||
looping = true;
|
||||
}
|
||||
_ctrls.button (FP8Controls::BtnLoop).set_active (looping);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_session_dirty_changed ()
|
||||
{
|
||||
const bool is_dirty = session->dirty ();
|
||||
_ctrls.button (FP8Controls::BtnSave).set_active (is_dirty);
|
||||
_ctrls.button (FP8Controls::BtnSave).set_color (is_dirty ? 0xff0000ff : 0x00ff00ff);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_history_changed ()
|
||||
{
|
||||
_ctrls.button (FP8Controls::BtnRedo).set_active (session->redo_depth() > 0);
|
||||
_ctrls.button (FP8Controls::BtnUndo).set_active (session->undo_depth() > 0);
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_solo_changed ()
|
||||
{
|
||||
_ctrls.button (FP8Controls::BtnSoloClear).set_active (session->soloing() || session->listening());
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::notify_mute_changed ()
|
||||
{
|
||||
bool muted = false;
|
||||
boost::shared_ptr<RouteList> rl = session->get_routes();
|
||||
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
|
||||
if ((*i)->is_master() || (*i)->is_monitor()) {
|
||||
continue;
|
||||
}
|
||||
boost::shared_ptr<MuteControl> mc = (*i)->mute_control();
|
||||
if (mc && mc->muted ()) {
|
||||
muted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ctrls.button (FP8Controls::BtnMuteClear).set_active (muted);
|
||||
}
|
1501
libs/surfaces/faderport8/faderport8.cc
Normal file
1501
libs/surfaces/faderport8/faderport8.cc
Normal file
File diff suppressed because it is too large
Load Diff
334
libs/surfaces/faderport8/faderport8.h
Normal file
334
libs/surfaces/faderport8/faderport8.h
Normal file
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
|
||||
* Copyright (C) 2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef ardour_surface_faderport8_h
|
||||
#define ardour_surface_faderport8_h
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#define ABSTRACT_UI_EXPORTS
|
||||
#include "pbd/abstract_ui.h"
|
||||
#include "pbd/properties.h"
|
||||
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/async_midi_port.h"
|
||||
#include "ardour/midi_port.h"
|
||||
|
||||
#include "control_protocol/control_protocol.h"
|
||||
|
||||
#include "fp8_base.h"
|
||||
#include "fp8_controls.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Parser;
|
||||
}
|
||||
|
||||
namespace ARDOUR {
|
||||
class Bundle;
|
||||
class Session;
|
||||
class Processor;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
struct FaderPort8Request : public BaseUI::BaseRequestObject
|
||||
{
|
||||
public:
|
||||
FaderPort8Request () {}
|
||||
~FaderPort8Request () {}
|
||||
};
|
||||
|
||||
class FaderPort8 : public FP8Base, public ARDOUR::ControlProtocol, public AbstractUI<FaderPort8Request>
|
||||
{
|
||||
public:
|
||||
FaderPort8 (ARDOUR::Session&);
|
||||
virtual ~FaderPort8();
|
||||
|
||||
int set_active (bool yn);
|
||||
|
||||
/* we probe for a device when our ports are connected. Before that,
|
||||
* there's no way to know if the device exists or not.
|
||||
*/
|
||||
static bool probe() { return true; }
|
||||
static void* request_factory (uint32_t);
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
||||
/* configuration GUI */
|
||||
bool has_editor () const { return true; }
|
||||
void* get_gui () const;
|
||||
void tear_down_gui ();
|
||||
PBD::Signal0<void> ConnectionChange;
|
||||
|
||||
void set_button_action (FP8Controls::ButtonId, bool, std::string const&);
|
||||
std::string get_button_action (FP8Controls::ButtonId, bool);
|
||||
FP8Controls const& control () const { return _ctrls; }
|
||||
|
||||
int stop ();
|
||||
void do_request (FaderPort8Request*);
|
||||
void thread_init ();
|
||||
|
||||
boost::shared_ptr<ARDOUR::Port> input_port() const { return _input_port; }
|
||||
boost::shared_ptr<ARDOUR::Port> output_port() const { return _output_port; }
|
||||
std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
|
||||
|
||||
size_t tx_midi (std::vector<uint8_t> const&) const;
|
||||
|
||||
private:
|
||||
void close ();
|
||||
|
||||
void start_midi_handling ();
|
||||
void stop_midi_handling ();
|
||||
|
||||
/* I/O Ports */
|
||||
PBD::ScopedConnection port_connection;
|
||||
boost::shared_ptr<ARDOUR::AsyncMIDIPort> _input_port;
|
||||
boost::shared_ptr<ARDOUR::AsyncMIDIPort> _output_port;
|
||||
boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
|
||||
boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
|
||||
|
||||
bool midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> port);
|
||||
|
||||
bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
|
||||
|
||||
enum ConnectionState {
|
||||
InputConnected = 0x1,
|
||||
OutputConnected = 0x2
|
||||
};
|
||||
|
||||
void connected ();
|
||||
void disconnected ();
|
||||
int _connection_state;
|
||||
bool _device_active;
|
||||
|
||||
/* MIDI input message handling */
|
||||
void sysex_handler (MIDI::Parser &p, MIDI::byte *, size_t);
|
||||
void polypressure_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb);
|
||||
void pitchbend_handler (MIDI::Parser &, uint8_t chan, MIDI::pitchbend_t pb);
|
||||
void controller_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb);
|
||||
void note_on_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb);
|
||||
void note_off_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb);
|
||||
PBD::ScopedConnectionList midi_connections;
|
||||
|
||||
/* ***************************************************************************
|
||||
* Control Elements
|
||||
*/
|
||||
FP8Controls _ctrls;
|
||||
void notify_stripable_added_or_removed ();
|
||||
void notify_fader_mode_changed ();
|
||||
void filter_stripables (ARDOUR::StripableList& strips) const;
|
||||
void assign_stripables ();
|
||||
void set_periodic_display_mode (FP8Strip::DisplayMode);
|
||||
|
||||
void assign_strips (bool reset_bank);
|
||||
void bank (bool down, bool page);
|
||||
void move_selected_into_view ();
|
||||
|
||||
void assign_sends ();
|
||||
void spill_plugins ();
|
||||
void assign_processor_ctrls ();
|
||||
void build_well_known_processor_ctrls (boost::shared_ptr<ARDOUR::Stripable>, bool);
|
||||
void select_plugin (int num);
|
||||
|
||||
void bank_param (bool down, bool page);
|
||||
/* bank offsets */
|
||||
int _channel_off;
|
||||
int _plugin_off;
|
||||
int _parameter_off;
|
||||
|
||||
/* plugin + send mode stripable
|
||||
*
|
||||
* This is used when parameters of one strip are assigned to
|
||||
* individual FP8Strip controls (Edit Send, Edit Plugins).
|
||||
*
|
||||
* When there's one stripable per FP8Strip, FP8Strip itself keeps
|
||||
* track of the object lifetime and these are NULL.
|
||||
*/
|
||||
PBD::ScopedConnectionList processor_connections;
|
||||
|
||||
PBD::ScopedConnectionList assigned_stripable_connections;
|
||||
typedef std::map<boost::shared_ptr<ARDOUR::Stripable>, uint8_t> StripAssignmentMap;
|
||||
StripAssignmentMap _assigned_strips;
|
||||
|
||||
void drop_ctrl_connections ();
|
||||
|
||||
void select_strip (boost::weak_ptr<ARDOUR::Stripable>);
|
||||
void notify_pi_property_changed (const PBD::PropertyChange&);
|
||||
void notify_stripable_property_changed (boost::weak_ptr<ARDOUR::Stripable>, const PBD::PropertyChange&);
|
||||
void gui_track_selection_changed ();
|
||||
|
||||
PBD::ScopedConnection selection_connection;
|
||||
PBD::ScopedConnectionList automation_state_connections;
|
||||
PBD::ScopedConnectionList modechange_connections;
|
||||
/* **************************************************************************/
|
||||
struct ProcessorCtrl {
|
||||
ProcessorCtrl (std::string const &n, boost::shared_ptr<ARDOUR::AutomationControl> c)
|
||||
: name (n)
|
||||
, ac (c)
|
||||
{}
|
||||
std::string name;
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> ac;
|
||||
};
|
||||
std::list <ProcessorCtrl> _proc_params;
|
||||
/* **************************************************************************/
|
||||
|
||||
/* periodic updates, parameter poll */
|
||||
sigc::connection _periodic_connection;
|
||||
bool periodic ();
|
||||
std::string _timecode;
|
||||
std::string const& timecode () const { return _timecode; }
|
||||
|
||||
/* sync button blink -- the FP's blink mode does not work */
|
||||
sigc::connection _blink_connection;
|
||||
bool _blink_onoff;
|
||||
bool blink_it ();
|
||||
|
||||
/* shift key */
|
||||
sigc::connection _shift_connection;
|
||||
bool _shift_lock;
|
||||
bool _shift_pressed;
|
||||
bool shift_timeout () { _shift_lock = true; return false; }
|
||||
bool shift_mod () const { return _shift_lock | _shift_pressed; }
|
||||
|
||||
/* GUI */
|
||||
void build_gui ();
|
||||
mutable void *gui;
|
||||
|
||||
/* setup callbacks & actions */
|
||||
void connect_session_signals ();
|
||||
void setup_actions ();
|
||||
void send_session_state ();
|
||||
|
||||
/* callbacks */
|
||||
PBD::ScopedConnectionList session_connections;
|
||||
void notify_parameter_changed (std::string);
|
||||
void notify_record_state_changed ();
|
||||
void notify_transport_state_changed ();
|
||||
void notify_loop_state_changed ();
|
||||
void notify_snap_change ();
|
||||
void notify_session_dirty_changed ();
|
||||
void notify_history_changed ();
|
||||
void notify_solo_changed ();
|
||||
void notify_mute_changed ();
|
||||
void notify_automation_mode_changed ();
|
||||
|
||||
/* actions */
|
||||
PBD::ScopedConnectionList button_connections;
|
||||
void button_play ();
|
||||
void button_stop ();
|
||||
void button_record ();
|
||||
void button_loop ();
|
||||
void button_metronom ();
|
||||
void button_varispeed (bool);
|
||||
void button_mute_clear ();
|
||||
void button_arm (bool);
|
||||
void button_arm_all ();
|
||||
void button_automation (ARDOUR::AutoState);
|
||||
void button_prev_next (bool);
|
||||
void button_action (const std::string& group, const std::string& item);
|
||||
|
||||
void button_encoder ();
|
||||
void button_parameter ();
|
||||
void encoder_navigate (bool, int);
|
||||
void encoder_parameter (bool, int);
|
||||
|
||||
/* user bound actions */
|
||||
void button_user (bool, FP8Controls::ButtonId);
|
||||
|
||||
enum ActionType {
|
||||
Unset,
|
||||
NamedAction,
|
||||
// InternalFunction, // unused
|
||||
};
|
||||
|
||||
struct UserAction {
|
||||
UserAction () : _type (Unset) {}
|
||||
|
||||
ActionType _type;
|
||||
std::string _action_name;
|
||||
//boost::function<void()> function; // unused
|
||||
|
||||
void clear ()
|
||||
{
|
||||
_type = Unset;
|
||||
_action_name.clear();
|
||||
}
|
||||
|
||||
void assign_action (std::string const& action_name)
|
||||
{
|
||||
if (action_name.empty ()) {
|
||||
_type = Unset;
|
||||
_action_name.clear();
|
||||
} else {
|
||||
_type = NamedAction;
|
||||
_action_name = action_name;
|
||||
}
|
||||
}
|
||||
|
||||
bool empty () const
|
||||
{
|
||||
return _type == Unset;
|
||||
}
|
||||
|
||||
void call (FaderPort8& _base) const
|
||||
{
|
||||
switch (_type) {
|
||||
case NamedAction:
|
||||
_base.access_action (_action_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ButtonAction {
|
||||
UserAction on_press;
|
||||
UserAction on_release;
|
||||
|
||||
UserAction& action (bool press)
|
||||
{
|
||||
return press ? on_press : on_release;
|
||||
}
|
||||
|
||||
UserAction const& action (bool press) const
|
||||
{
|
||||
return press ? on_press : on_release;
|
||||
}
|
||||
|
||||
void call (FaderPort8& _base, bool press) const
|
||||
{
|
||||
action (press).call (_base);
|
||||
}
|
||||
bool empty () const
|
||||
{
|
||||
return on_press.empty () && on_release.empty();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<FP8Controls::ButtonId, ButtonAction> UserActionMap;
|
||||
UserActionMap _user_action_map;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif /* ardour_surface_faderport8_h */
|
81
libs/surfaces/faderport8/faderport8_interface.cc
Normal file
81
libs/surfaces/faderport8/faderport8_interface.cc
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
|
||||
* Copyright (C) 2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <pbd/failed_constructor.h>
|
||||
|
||||
#include "control_protocol/control_protocol.h"
|
||||
#include "faderport8.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
|
||||
static ControlProtocol*
|
||||
new_faderport8_midi_protocol (ControlProtocolDescriptor* /*descriptor*/, Session* s)
|
||||
{
|
||||
FaderPort8* fp;
|
||||
|
||||
try {
|
||||
fp = new FaderPort8 (*s);
|
||||
} catch (failed_constructor& err) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fp->set_active (true)) {
|
||||
delete fp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
static void
|
||||
delete_faderport8_midi_protocol (ControlProtocolDescriptor* /*descriptor*/, ControlProtocol* cp)
|
||||
{
|
||||
delete cp;
|
||||
}
|
||||
|
||||
static bool
|
||||
probe_faderport8_midi_protocol (ControlProtocolDescriptor* /*descriptor*/)
|
||||
{
|
||||
return FaderPort8::probe ();
|
||||
}
|
||||
|
||||
static void*
|
||||
faderport8_request_buffer_factory (uint32_t num_requests)
|
||||
{
|
||||
return FaderPort8::request_factory (num_requests);
|
||||
}
|
||||
|
||||
static ControlProtocolDescriptor faderport8_midi_descriptor = {
|
||||
/*name : */ "PreSonus FaderPort8",
|
||||
/*id : */ "uri://ardour.org/surfaces/faderport8:0",
|
||||
/*ptr : */ 0,
|
||||
/*module : */ 0,
|
||||
/*mandatory : */ 0,
|
||||
/*supports_feedback : */ true,
|
||||
/*probe : */ probe_faderport8_midi_protocol,
|
||||
/*initialize : */ new_faderport8_midi_protocol,
|
||||
/*destroy : */ delete_faderport8_midi_protocol,
|
||||
/*request_buffer_factory */ faderport8_request_buffer_factory
|
||||
};
|
||||
|
||||
extern "C" ARDOURSURFACE_API
|
||||
ControlProtocolDescriptor* protocol_descriptor () {
|
||||
return &faderport8_midi_descriptor;
|
||||
}
|
151
libs/surfaces/faderport8/fp8_base.h
Normal file
151
libs/surfaces/faderport8/fp8_base.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ardour_surfaces_fp8base_h_
|
||||
#define _ardour_surfaces_fp8base_h_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
/* conveniece wrappers depending on "FP8Base& _base" */
|
||||
#define fp8_loop dynamic_cast<BaseUI*>(&_base)->main_loop
|
||||
#define fp8_context() dynamic_cast<BaseUI*>(&_base)
|
||||
#define fp8_protocol() dynamic_cast<ControlProtocol*>(&_base)
|
||||
|
||||
class FP8Base
|
||||
{
|
||||
public:
|
||||
virtual ~FP8Base() {}
|
||||
|
||||
virtual size_t tx_midi (std::vector<uint8_t> const&) const = 0;
|
||||
virtual std::string const& timecode () const = 0;
|
||||
|
||||
size_t tx_midi2 (uint8_t sb, uint8_t d1) const
|
||||
{
|
||||
std::vector<uint8_t> d;
|
||||
d.push_back (sb);
|
||||
d.push_back (d1);
|
||||
return tx_midi (d);
|
||||
}
|
||||
|
||||
size_t tx_midi3 (uint8_t sb, uint8_t d1, uint8_t d2) const
|
||||
{
|
||||
std::vector<uint8_t> d;
|
||||
d.push_back (sb);
|
||||
d.push_back (d1);
|
||||
d.push_back (d2);
|
||||
return tx_midi (d);
|
||||
}
|
||||
|
||||
size_t tx_sysex (size_t count, ...)
|
||||
{
|
||||
std::vector<uint8_t> d;
|
||||
sysexhdr (d);
|
||||
|
||||
va_list var_args;
|
||||
va_start (var_args, count);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
// uint8_t {aka unsigned char} is promoted to ‘int’ when passed through ‘...’
|
||||
uint8_t b = va_arg (var_args, int);
|
||||
d.push_back (b);
|
||||
}
|
||||
va_end (var_args);
|
||||
|
||||
d.push_back (0xf7);
|
||||
return tx_midi (d);
|
||||
}
|
||||
|
||||
size_t tx_text (uint8_t id, uint8_t line, uint8_t align, std::string const& txt)
|
||||
{
|
||||
std::vector<uint8_t> d;
|
||||
sysexhdr (d);
|
||||
d.push_back (0x12);
|
||||
d.push_back (id & 0x07);
|
||||
d.push_back (line & 0x03);
|
||||
d.push_back (align & 0x07);
|
||||
|
||||
for (size_t i = 0; i < txt.size(); ++i)
|
||||
{
|
||||
d.push_back (txt[i]);
|
||||
if (i >= 8) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.push_back (0xf7);
|
||||
return tx_midi (d);
|
||||
}
|
||||
|
||||
PBD::Signal1<void, bool> ShiftButtonChange;
|
||||
PBD::Signal1<void, bool> ARMButtonChange;
|
||||
|
||||
PBD::Signal1<void, bool> BlinkIt;
|
||||
PBD::Signal0<void> Periodic;
|
||||
|
||||
private:
|
||||
void sysexhdr (std::vector<uint8_t>& d)
|
||||
{
|
||||
/* faderport8 <SysExHdr> */
|
||||
d.push_back (0xf0);
|
||||
d.push_back (0x00);
|
||||
d.push_back (0x01);
|
||||
d.push_back (0x06);
|
||||
d.push_back (0x02);
|
||||
}
|
||||
};
|
||||
|
||||
namespace FP8Types {
|
||||
|
||||
enum FaderMode {
|
||||
ModeTrack,
|
||||
ModePlugins,
|
||||
ModeSend,
|
||||
ModePan
|
||||
};
|
||||
|
||||
enum NavigationMode {
|
||||
NavChannel,
|
||||
NavZoom,
|
||||
NavScroll,
|
||||
NavBank,
|
||||
NavMaster,
|
||||
NavSection,
|
||||
NavMarker
|
||||
};
|
||||
|
||||
enum MixMode {
|
||||
MixAudio,
|
||||
MixInstrument,
|
||||
MixBus,
|
||||
MixVCA,
|
||||
MixAll,
|
||||
MixInputs,
|
||||
MixMIDI,
|
||||
MixOutputs,
|
||||
MixFX,
|
||||
MixUser,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
#endif /* _ardour_surfaces_fp8base_h_ */
|
490
libs/surfaces/faderport8/fp8_button.h
Normal file
490
libs/surfaces/faderport8/fp8_button.h
Normal file
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ardour_surfaces_fp8button_h_
|
||||
#define _ardour_surfaces_fp8button_h_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pbd/base_ui.h"
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "fp8_base.h"
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class FP8ButtonInterface
|
||||
{
|
||||
public:
|
||||
FP8ButtonInterface () {}
|
||||
virtual ~FP8ButtonInterface () {}
|
||||
|
||||
/* user API */
|
||||
PBD::Signal0<void> pressed;
|
||||
PBD::Signal0<void> released;
|
||||
|
||||
virtual bool is_pressed () const { return false; }
|
||||
virtual bool is_active () const { return false; }
|
||||
|
||||
virtual void ignore_release () {}
|
||||
|
||||
/* internal API - called from midi thread,
|
||||
* user pressed/released button the device
|
||||
*/
|
||||
virtual bool midi_event (bool) = 0;
|
||||
|
||||
/* internal API - called from surface thread
|
||||
* set Light on the button
|
||||
*/
|
||||
virtual void set_active (bool a) = 0;
|
||||
virtual void set_color (uint32_t rgba) {}
|
||||
virtual void set_blinking (bool) {}
|
||||
|
||||
static bool force_change; // used during init
|
||||
};
|
||||
|
||||
class FP8DummyButton : public FP8ButtonInterface
|
||||
{
|
||||
public:
|
||||
virtual void set_active (bool a) {}
|
||||
virtual bool midi_event (bool) { return false; }
|
||||
};
|
||||
|
||||
|
||||
class FP8ButtonBase : public FP8ButtonInterface
|
||||
{
|
||||
public:
|
||||
FP8ButtonBase (FP8Base& b)
|
||||
: _base (b)
|
||||
, _pressed (false)
|
||||
, _active (false)
|
||||
, _ignore_release (false)
|
||||
, _rgba (0)
|
||||
, _blinking (false)
|
||||
{ }
|
||||
|
||||
bool is_pressed () const { return _pressed; }
|
||||
bool is_active () const { return _active; }
|
||||
|
||||
virtual bool midi_event (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 */
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ignore_release () {
|
||||
if (_pressed) {
|
||||
_ignore_release = true;
|
||||
}
|
||||
}
|
||||
|
||||
void set_blinking (bool yes) {
|
||||
if (yes && !_blinking) {
|
||||
_blinking = true;
|
||||
_base.BlinkIt.connect_same_thread (_blink_connection, boost::bind (&FP8ButtonBase::blink, this, _1));
|
||||
} else if (!yes && _blinking) {
|
||||
_blink_connection.disconnect ();
|
||||
blink (true);
|
||||
_blinking = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
FP8Base& _base;
|
||||
bool _pressed;
|
||||
bool _active;
|
||||
bool _ignore_release;
|
||||
uint32_t _rgba;
|
||||
virtual void blink (bool onoff) = 0;
|
||||
|
||||
private:
|
||||
PBD::ScopedConnection _blink_connection;
|
||||
bool _blinking;
|
||||
};
|
||||
|
||||
class FP8Button : public FP8ButtonBase
|
||||
{
|
||||
public:
|
||||
FP8Button (FP8Base& b, uint8_t id, bool color = false)
|
||||
: FP8ButtonBase (b)
|
||||
, _midi_id (id)
|
||||
, _has_color (color)
|
||||
{ }
|
||||
|
||||
virtual void set_active (bool a)
|
||||
{
|
||||
if (_active == a && !force_change) {
|
||||
return;
|
||||
}
|
||||
_active = a;
|
||||
_base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
|
||||
}
|
||||
|
||||
void set_color (uint32_t rgba)
|
||||
{
|
||||
if (!_has_color || _rgba == rgba) {
|
||||
return;
|
||||
}
|
||||
_rgba = rgba;
|
||||
_base.tx_midi3 (0x91, _midi_id, (_rgba >> 25) & 0x7f);
|
||||
_base.tx_midi3 (0x92, _midi_id, (_rgba >> 17) & 0x7f);
|
||||
_base.tx_midi3 (0x93, _midi_id, (_rgba >> 9) & 0x7f);
|
||||
}
|
||||
|
||||
protected:
|
||||
void blink (bool onoff)
|
||||
{
|
||||
if (!_active) { return; }
|
||||
_base.tx_midi3 (0x90, _midi_id, onoff ? 0x7f : 0x00);
|
||||
}
|
||||
|
||||
uint8_t _midi_id; // MIDI-note
|
||||
bool _has_color;
|
||||
};
|
||||
|
||||
class FP8ReadOnlyButton : public FP8Button
|
||||
{
|
||||
public:
|
||||
FP8ReadOnlyButton (FP8Base& b, uint8_t id, bool color = false)
|
||||
: FP8Button (b, id, color)
|
||||
{}
|
||||
|
||||
void set_active (bool) { }
|
||||
};
|
||||
|
||||
/* virtual button. used for shift toggle. */
|
||||
class ShadowButton : public FP8ButtonBase
|
||||
{
|
||||
public:
|
||||
ShadowButton (FP8Base& b)
|
||||
: FP8ButtonBase (b)
|
||||
{}
|
||||
|
||||
PBD::Signal1<void, bool> ActiveChanged;
|
||||
PBD::Signal0<void> ColourChanged;
|
||||
|
||||
uint32_t color () const { return _rgba; }
|
||||
|
||||
bool midi_event (bool a)
|
||||
{
|
||||
assert (0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_pressed (bool a)
|
||||
{
|
||||
return FP8ButtonBase::midi_event (a);
|
||||
}
|
||||
|
||||
void set_active (bool a)
|
||||
{
|
||||
if (_active == a && !force_change) {
|
||||
return;
|
||||
}
|
||||
_active = a;
|
||||
ActiveChanged (a); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void set_color (uint32_t rgba)
|
||||
{
|
||||
if (_rgba == rgba) {
|
||||
return;
|
||||
}
|
||||
_rgba = rgba;
|
||||
ColourChanged ();
|
||||
}
|
||||
|
||||
protected:
|
||||
void blink (bool onoff) {
|
||||
if (!_active) { return; }
|
||||
ActiveChanged (onoff);
|
||||
}
|
||||
};
|
||||
|
||||
/* Wraps 2 buttons with the same physical MIDI ID */
|
||||
class FP8DualButton : public FP8ButtonInterface
|
||||
{
|
||||
public:
|
||||
FP8DualButton (FP8Base& b, uint8_t id, bool color = false)
|
||||
: _base (b)
|
||||
, _b0 (b)
|
||||
, _b1 (b)
|
||||
, _midi_id (id)
|
||||
, _has_color (color)
|
||||
, _rgba (0)
|
||||
, _shift (false)
|
||||
{
|
||||
_b0.ActiveChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::active_changed, this, false, _1));
|
||||
_b1.ActiveChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::active_changed, this, true, _1));
|
||||
if (_has_color) {
|
||||
_b0.ColourChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::colour_changed, this, false));
|
||||
_b1.ColourChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::colour_changed, this, true));
|
||||
}
|
||||
}
|
||||
|
||||
bool midi_event (bool a) {
|
||||
return (_shift ? _b1 : _b0).set_pressed (a);
|
||||
}
|
||||
|
||||
void set_active (bool a) {
|
||||
/* This button is never directly used
|
||||
* by the libardour side API.
|
||||
*/
|
||||
assert (0);
|
||||
}
|
||||
|
||||
void active_changed (bool s, bool a) {
|
||||
if (s != _shift) {
|
||||
return;
|
||||
}
|
||||
_base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
|
||||
}
|
||||
|
||||
void colour_changed (bool s) {
|
||||
if (s != _shift || !_has_color) {
|
||||
return;
|
||||
}
|
||||
uint32_t rgba = (_shift ? _b1 : _b0).color ();
|
||||
if (rgba == _rgba) {
|
||||
return;
|
||||
}
|
||||
_rgba = rgba;
|
||||
_base.tx_midi3 (0x91, _midi_id, (rgba >> 25) & 0x7f);
|
||||
_base.tx_midi3 (0x92, _midi_id, (rgba >> 17) & 0x7f);
|
||||
_base.tx_midi3 (0x93, _midi_id, (rgba >> 9) & 0x7f);
|
||||
}
|
||||
|
||||
FP8ButtonInterface* button () { return &_b0; }
|
||||
FP8ButtonInterface* button_shift () { return &_b1; }
|
||||
|
||||
protected:
|
||||
FP8Base& _base;
|
||||
|
||||
virtual void connect_toggle () = 0;
|
||||
|
||||
void shift_changed (bool shift) {
|
||||
if (_shift == shift) {
|
||||
return;
|
||||
}
|
||||
(_shift ? _b1 : _b0).set_pressed (false);
|
||||
_shift = shift;
|
||||
active_changed (_shift, (_shift ? _b1 : _b0).is_active());
|
||||
colour_changed (_shift);
|
||||
}
|
||||
|
||||
private:
|
||||
ShadowButton _b0;
|
||||
ShadowButton _b1;
|
||||
uint8_t _midi_id; // MIDI-note
|
||||
bool _has_color;
|
||||
uint32_t _rgba;
|
||||
bool _shift;
|
||||
PBD::ScopedConnectionList _button_connections;
|
||||
};
|
||||
|
||||
class FP8ShiftSensitiveButton : public FP8DualButton
|
||||
{
|
||||
public:
|
||||
FP8ShiftSensitiveButton (FP8Base& b, uint8_t id, bool color = false)
|
||||
:FP8DualButton (b, id, color)
|
||||
{
|
||||
connect_toggle ();
|
||||
}
|
||||
|
||||
protected:
|
||||
void connect_toggle ()
|
||||
{
|
||||
_base.ShiftButtonChange.connect_same_thread (_shift_connection, boost::bind (&FP8ShiftSensitiveButton::shift_changed, this, _1));
|
||||
}
|
||||
|
||||
private:
|
||||
PBD::ScopedConnection _shift_connection;
|
||||
};
|
||||
|
||||
class FP8ARMSensitiveButton : public FP8DualButton
|
||||
{
|
||||
public:
|
||||
FP8ARMSensitiveButton (FP8Base& b, uint8_t id, bool color = false)
|
||||
:FP8DualButton (b, id, color)
|
||||
{
|
||||
connect_toggle ();
|
||||
}
|
||||
|
||||
protected:
|
||||
void connect_toggle ()
|
||||
{
|
||||
_base.ARMButtonChange.connect_same_thread (_arm_connection, boost::bind (&FP8ARMSensitiveButton::shift_changed, this, _1));
|
||||
}
|
||||
|
||||
private:
|
||||
PBD::ScopedConnection _arm_connection;
|
||||
};
|
||||
|
||||
|
||||
// short press: activate in press, deactivate on release,
|
||||
// long press + hold, activate on press, de-activate directly on release
|
||||
// e.g. mute/solo press + hold => changed()
|
||||
class FP8MomentaryButton : public FP8ButtonInterface
|
||||
{
|
||||
public:
|
||||
FP8MomentaryButton (FP8Base& b, uint8_t id)
|
||||
: _base (b)
|
||||
, _midi_id (id)
|
||||
, _pressed (false)
|
||||
, _active (false)
|
||||
{}
|
||||
|
||||
~FP8MomentaryButton () {
|
||||
_hold_connection.disconnect ();
|
||||
}
|
||||
|
||||
PBD::Signal1<void, bool> StateChange;
|
||||
|
||||
void set_active (bool a)
|
||||
{
|
||||
if (_active == a && !force_change) {
|
||||
return;
|
||||
}
|
||||
_active = a;
|
||||
_base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
|
||||
}
|
||||
|
||||
void reset ()
|
||||
{
|
||||
_was_active_on_press = false;
|
||||
_hold_connection.disconnect ();
|
||||
}
|
||||
|
||||
bool midi_event (bool a)
|
||||
{
|
||||
if (a == _pressed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_pressed = a;
|
||||
|
||||
if (a) {
|
||||
_was_active_on_press = _active;
|
||||
}
|
||||
|
||||
if (a && !_active) {
|
||||
_momentaty = false;
|
||||
StateChange (true); /* EMIT SIGNAL */
|
||||
Glib::RefPtr<Glib::TimeoutSource> hold_timer =
|
||||
Glib::TimeoutSource::create (500);
|
||||
hold_timer->attach (fp8_loop()->get_context());
|
||||
_hold_connection = hold_timer->connect (sigc::mem_fun (*this, &FP8MomentaryButton::hold_timeout));
|
||||
} else if (!a && _was_active_on_press) {
|
||||
_hold_connection.disconnect ();
|
||||
_momentaty = false;
|
||||
StateChange (false); /* EMIT SIGNAL */
|
||||
} else if (!a && _momentaty) {
|
||||
_hold_connection.disconnect ();
|
||||
_momentaty = false;
|
||||
StateChange (false); /* EMIT SIGNAL */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
FP8Base& _base;
|
||||
uint8_t _midi_id; // MIDI-note
|
||||
bool _pressed;
|
||||
bool _momentaty;
|
||||
bool _was_active_on_press;
|
||||
bool _active;
|
||||
|
||||
private:
|
||||
bool hold_timeout ()
|
||||
{
|
||||
_momentaty = true;
|
||||
return false;
|
||||
}
|
||||
sigc::connection _hold_connection;
|
||||
};
|
||||
|
||||
class FP8RepeatButton : public FP8Button
|
||||
{
|
||||
public:
|
||||
FP8RepeatButton (FP8Base& b, uint8_t id, bool color = false)
|
||||
: FP8Button (b, id, color)
|
||||
, _skip (0)
|
||||
{}
|
||||
|
||||
~FP8RepeatButton ()
|
||||
{
|
||||
stop_repeat ();
|
||||
}
|
||||
|
||||
bool midi_event (bool a)
|
||||
{
|
||||
bool rv = FP8Button::midi_event (a);
|
||||
if (rv && a) {
|
||||
start_repeat ();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void stop_repeat ()
|
||||
{
|
||||
_press_timeout_connection.disconnect ();
|
||||
}
|
||||
|
||||
private:
|
||||
void start_repeat ()
|
||||
{
|
||||
stop_repeat ();
|
||||
_skip = 5;
|
||||
Glib::RefPtr<Glib::TimeoutSource> press_timer =
|
||||
Glib::TimeoutSource::create (100);
|
||||
press_timer->attach (fp8_loop()->get_context());
|
||||
_press_timeout_connection = press_timer->connect (sigc::mem_fun (*this, &FP8RepeatButton::repeat_press));
|
||||
}
|
||||
|
||||
bool repeat_press ()
|
||||
{
|
||||
if (!_pressed) {
|
||||
return false;
|
||||
}
|
||||
if (_skip > 0) {
|
||||
--_skip;
|
||||
return true;
|
||||
}
|
||||
pressed ();
|
||||
return true;
|
||||
}
|
||||
|
||||
int _skip;
|
||||
sigc::connection _press_timeout_connection;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace */
|
||||
#endif /* _ardour_surfaces_fp8button_h_ */
|
413
libs/surfaces/faderport8/fp8_controls.cc
Normal file
413
libs/surfaces/faderport8/fp8_controls.cc
Normal file
|
@ -0,0 +1,413 @@
|
|||
/* Faderport 8 Control Surface
|
||||
* Abstraction of Surface Control Elements.
|
||||
*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "fp8_controls.h"
|
||||
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourSurface::FP8Types;
|
||||
|
||||
bool FP8ButtonInterface::force_change = false;
|
||||
|
||||
#define NEWBUTTON(midi_id, button_id, color) \
|
||||
do { \
|
||||
assert (_midimap.end() == _midimap.find (midi_id)); \
|
||||
assert (_ctrlmap.end() == _ctrlmap.find (button_id)); \
|
||||
FP8Button *t = new FP8Button (b, midi_id); \
|
||||
_midimap[midi_id] = t; \
|
||||
_ctrlmap[button_id] = t; \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define NEWTYPEBUTTON(TYPE, midi_id, button_id, color) \
|
||||
do { \
|
||||
assert (_midimap.end() == _midimap.find (midi_id)); \
|
||||
assert (_ctrlmap.end() == _ctrlmap.find (button_id)); \
|
||||
TYPE *t = new TYPE (b, midi_id); \
|
||||
_midimap[midi_id] = t; \
|
||||
_ctrlmap[button_id] = t; \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
#define NEWSHIFTBUTTON(midi_id, id1, id2, color) \
|
||||
do { \
|
||||
assert (_midimap.end() == _midimap.find (midi_id)); \
|
||||
assert (_ctrlmap.end() == _ctrlmap.find (id1)); \
|
||||
assert (_ctrlmap.end() == _ctrlmap.find (id2)); \
|
||||
FP8ShiftSensitiveButton *t = \
|
||||
new FP8ShiftSensitiveButton (b, midi_id, color); \
|
||||
_midimap[midi_id] = t; \
|
||||
_ctrlmap[id1] = t->button (); \
|
||||
_ctrlmap[id2] = t->button_shift (); \
|
||||
} while (0)
|
||||
|
||||
|
||||
FP8Controls::FP8Controls (FP8Base& b)
|
||||
: _fadermode (ModeTrack)
|
||||
, _navmode (NavMaster)
|
||||
, _mixmode (MixAll)
|
||||
, _display_timecode (false)
|
||||
{
|
||||
NEWBUTTON (0x56, BtnLoop, false);
|
||||
NEWTYPEBUTTON (FP8RepeatButton, 0x5b, BtnRewind, false);
|
||||
NEWTYPEBUTTON (FP8RepeatButton, 0x5c, BtnFastForward, false);
|
||||
NEWBUTTON (0x5d, BtnStop, false);
|
||||
NEWBUTTON (0x5e, BtnPlay, false);
|
||||
NEWBUTTON (0x5f, BtnRecord, false);
|
||||
|
||||
NEWSHIFTBUTTON (0x4a, BtnARead, BtnUser3, true);
|
||||
NEWSHIFTBUTTON (0x4b, BtnAWrite, BtnUser2, true);
|
||||
NEWSHIFTBUTTON (0x4c, BtnATrim, BtnRedo, true);
|
||||
NEWSHIFTBUTTON (0x4d, BtnATouch, BtnUser1, true);
|
||||
NEWSHIFTBUTTON (0x4e, BtnALatch, BtnSave, true);
|
||||
NEWSHIFTBUTTON (0x4f, BtnAOff, BtnUndo, true);
|
||||
|
||||
NEWBUTTON (0x2e, BtnPrev, false);
|
||||
NEWBUTTON (0x2f, BtnNext, false);
|
||||
|
||||
NEWSHIFTBUTTON (0x36, BtnChannel, BtnF1, false);
|
||||
NEWSHIFTBUTTON (0x37, BtnZoom, BtnF2, false);
|
||||
NEWSHIFTBUTTON (0x38, BtnScroll, BtnF3, false);
|
||||
NEWSHIFTBUTTON (0x39, BtnBank, BtnF4, false);
|
||||
NEWSHIFTBUTTON (0x3a, BtnMaster, BtnF5, false);
|
||||
NEWSHIFTBUTTON (0x3b, BtnClick, BtnF6, false);
|
||||
NEWSHIFTBUTTON (0x3c, BtnSection, BtnF7, false);
|
||||
NEWSHIFTBUTTON (0x3d, BtnMarker, BtnF8, false);
|
||||
|
||||
NEWSHIFTBUTTON (0x28, BtnTrack, BtnTimecode, false);
|
||||
NEWBUTTON (0x2b, BtnPlugins, false);
|
||||
NEWBUTTON (0x29, BtnSend, false);
|
||||
NEWBUTTON (0x2a, BtnPan, false);
|
||||
|
||||
NEWSHIFTBUTTON (0x00, BtnArm, BtnArmAll, false);
|
||||
NEWBUTTON (0x01, BtnSoloClear, false);
|
||||
NEWBUTTON (0x02, BtnMuteClear, false);
|
||||
|
||||
NEWSHIFTBUTTON (0x03, BtnBypass, BtnBypassAll, true);
|
||||
NEWSHIFTBUTTON (0x04, BtnMacro, BtnOpen, true);
|
||||
NEWSHIFTBUTTON (0x05, BtnLink, BtnLock, true);
|
||||
|
||||
NEWSHIFTBUTTON (0x3e, BtnMAudio, BtnMInputs, true);
|
||||
NEWSHIFTBUTTON (0x3f, BtnMVI, BtnMMIDI, true);
|
||||
NEWSHIFTBUTTON (0x40, BtnMBus, BtnMOutputs, true);
|
||||
NEWSHIFTBUTTON (0x41, BtnMVCA, BtnMFX, true);
|
||||
NEWSHIFTBUTTON (0x42, BtnMAll, BtnMUser, true);
|
||||
|
||||
NEWTYPEBUTTON (FP8ReadOnlyButton, 0x53, BtnEncoder, false);
|
||||
NEWTYPEBUTTON (FP8ReadOnlyButton, 0x20, BtnParam, false);
|
||||
NEWTYPEBUTTON (FP8ReadOnlyButton, 0x66, BtnFootswitch, false);
|
||||
|
||||
/* internal bindings */
|
||||
|
||||
#define BindMethod(ID, CB) \
|
||||
button (ID).released.connect_same_thread (button_connections, boost::bind (&FP8Controls:: CB, this));
|
||||
|
||||
BindMethod (FP8Controls::BtnTimecode, toggle_timecode);
|
||||
|
||||
#define BindNav(BTN, MODE)\
|
||||
button (BTN).released.connect_same_thread (button_connections, boost::bind (&FP8Controls::set_nav_mode, this, MODE))
|
||||
|
||||
BindNav (BtnChannel, NavChannel);
|
||||
BindNav (BtnZoom, NavZoom);
|
||||
BindNav (BtnScroll, NavScroll);
|
||||
BindNav (BtnBank, NavBank);
|
||||
BindNav (BtnMaster, NavMaster);
|
||||
BindNav (BtnSection, NavSection);
|
||||
BindNav (BtnMarker, NavMarker);
|
||||
|
||||
#define BindFader(BTN, MODE)\
|
||||
button (BTN).released.connect_same_thread (button_connections, boost::bind (&FP8Controls::set_fader_mode, this, MODE))
|
||||
|
||||
BindFader (BtnTrack, ModeTrack);
|
||||
BindFader (BtnPlugins, ModePlugins);
|
||||
BindFader (BtnSend, ModeSend);
|
||||
BindFader (BtnPan, ModePan);
|
||||
|
||||
|
||||
#define BindMix(BTN, MODE)\
|
||||
button (BTN).released.connect_same_thread (button_connections, boost::bind (&FP8Controls::set_mix_mode, this, MODE))
|
||||
|
||||
BindMix (BtnMAudio, MixAudio);
|
||||
BindMix (BtnMVI, MixInstrument);
|
||||
BindMix (BtnMBus, MixBus);
|
||||
BindMix (BtnMVCA, MixVCA);
|
||||
BindMix (BtnMAll, MixAll);
|
||||
BindMix (BtnMInputs, MixInputs);
|
||||
BindMix (BtnMMIDI, MixMIDI);
|
||||
BindMix (BtnMOutputs, MixOutputs);
|
||||
BindMix (BtnMFX, MixFX);
|
||||
BindMix (BtnMUser, MixUser);
|
||||
|
||||
/* create channelstrips */
|
||||
for (uint8_t id = 0; id < 8; ++id) {
|
||||
chanstrip[id] = new FP8Strip (b, id);
|
||||
_midimap_strip[0x08 + id] = &(chanstrip[id]->solo_button());
|
||||
_midimap_strip[0x10 + id] = &(chanstrip[id]->mute_button());
|
||||
_midimap_strip[0x18 + id] = &(chanstrip[id]->selrec_button());
|
||||
}
|
||||
|
||||
/* set User button names */
|
||||
|
||||
#define REGISTER_ENUM(ID, NAME) \
|
||||
_user_str_to_enum[#ID] = ID; \
|
||||
_user_enum_to_str[ID] = #ID; \
|
||||
_user_buttons[ID] = NAME;
|
||||
|
||||
REGISTER_ENUM (BtnFootswitch, "Footswitch");
|
||||
REGISTER_ENUM (BtnUser1 , "User 1");
|
||||
REGISTER_ENUM (BtnUser2 , "User 2");
|
||||
REGISTER_ENUM (BtnUser3 , "User 3");
|
||||
REGISTER_ENUM (BtnF1 , "F1");
|
||||
REGISTER_ENUM (BtnF2 , "F2");
|
||||
REGISTER_ENUM (BtnF3 , "F3");
|
||||
REGISTER_ENUM (BtnF4 , "F4");
|
||||
REGISTER_ENUM (BtnF5 , "F5");
|
||||
REGISTER_ENUM (BtnF6 , "F6");
|
||||
REGISTER_ENUM (BtnF7 , "F7");
|
||||
REGISTER_ENUM (BtnF8 , "F8");
|
||||
#undef REGISTER_ENUM
|
||||
}
|
||||
|
||||
FP8Controls::~FP8Controls ()
|
||||
{
|
||||
for (MidiButtonMap::const_iterator i = _midimap.begin (); i != _midimap.end (); ++i) {
|
||||
delete i->second;
|
||||
}
|
||||
for (uint8_t id = 0; id < 8; ++id) {
|
||||
delete chanstrip[id];
|
||||
}
|
||||
_midimap_strip.clear ();
|
||||
_ctrlmap.clear ();
|
||||
_midimap.clear ();
|
||||
}
|
||||
|
||||
bool
|
||||
FP8Controls::button_name_to_enum (std::string const& n, ButtonId& id) const
|
||||
{
|
||||
std::map<std::string, ButtonId>::const_iterator i = _user_str_to_enum.find (n);
|
||||
if (i == _user_str_to_enum.end()) {
|
||||
return false;
|
||||
}
|
||||
id = i->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FP8Controls::button_enum_to_name (ButtonId id, std::string& n) const
|
||||
{
|
||||
std::map<ButtonId, std::string>::const_iterator i = _user_enum_to_str.find (id);
|
||||
if (i == _user_enum_to_str.end()) {
|
||||
return false;
|
||||
}
|
||||
n = i->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FP8Controls::initialize ()
|
||||
{
|
||||
FP8ButtonInterface::force_change = true;
|
||||
/* set RGB colors */
|
||||
button (BtnUndo).set_color (0x00ff00ff);
|
||||
button (BtnRedo).set_color (0x00ff00ff);
|
||||
|
||||
button (BtnAOff).set_color (0xffffffff);
|
||||
button (BtnATrim).set_color (0x000030ff);
|
||||
button (BtnARead).set_color (0x00ff00ff);
|
||||
button (BtnAWrite).set_color (0xff0000ff);
|
||||
button (BtnATouch).set_color (0xff8800ff);
|
||||
|
||||
button (BtnUser1).set_color (0x0000ffff);
|
||||
button (BtnUser2).set_color (0x0000ffff);
|
||||
button (BtnUser3).set_color (0x0000ffff);
|
||||
|
||||
button (BtnALatch).set_color (0x0000ffff);
|
||||
|
||||
button (BtnBypass).set_color (0x888888ff);
|
||||
button (BtnBypassAll).set_color (0xffffffff);
|
||||
|
||||
button (BtnMacro).set_color (0x888888ff);
|
||||
button (BtnOpen).set_color (0xffffffff);
|
||||
|
||||
button (BtnLink).set_color (0x888888ff);
|
||||
button (BtnLock).set_color (0xffffffff);
|
||||
|
||||
button (BtnMAudio).set_color (0x0000ffff);
|
||||
button (BtnMVI).set_color (0x0000ffff);
|
||||
button (BtnMBus).set_color (0x0000ffff);
|
||||
button (BtnMVCA).set_color (0x0000ffff);
|
||||
button (BtnMAll).set_color (0x0000ffff);
|
||||
|
||||
button (BtnMInputs).set_color (0x0000ffff);
|
||||
button (BtnMMIDI).set_color (0x0000ffff);
|
||||
button (BtnMOutputs).set_color (0x0000ffff);
|
||||
button (BtnMFX).set_color (0x0000ffff);
|
||||
button (BtnMUser).set_color (0x0000ffff);
|
||||
|
||||
for (uint8_t id = 0; id < 8; ++id) {
|
||||
chanstrip[id]->initialize ();
|
||||
}
|
||||
|
||||
/* initally turn all lights off */
|
||||
for (CtrlButtonMap::const_iterator i = _ctrlmap.begin (); i != _ctrlmap.end (); ++i) {
|
||||
i->second->set_active (false);
|
||||
}
|
||||
|
||||
/* default modes */
|
||||
button (BtnMaster).set_active (true);
|
||||
button (BtnTrack).set_active (true);
|
||||
button (BtnMAll).set_active (true);
|
||||
button (BtnTimecode).set_active (_display_timecode);
|
||||
|
||||
FP8ButtonInterface::force_change = false;
|
||||
}
|
||||
|
||||
FP8ButtonInterface&
|
||||
FP8Controls::button (ButtonId id)
|
||||
{
|
||||
CtrlButtonMap::const_iterator i = _ctrlmap.find (id);
|
||||
if (i == _ctrlmap.end()) {
|
||||
assert (0);
|
||||
return _dummy_button;
|
||||
}
|
||||
return *(i->second);
|
||||
}
|
||||
|
||||
FP8Strip&
|
||||
FP8Controls::strip (uint8_t id)
|
||||
{
|
||||
assert (id < 8);
|
||||
return *chanstrip[id];
|
||||
}
|
||||
|
||||
/* *****************************************************************************
|
||||
* Delegate MIDI events
|
||||
*/
|
||||
|
||||
bool
|
||||
FP8Controls::midi_event (uint8_t id, uint8_t val)
|
||||
{
|
||||
MidiButtonMap::const_iterator i;
|
||||
|
||||
i = _midimap_strip.find (id);
|
||||
if (i != _midimap_strip.end()) {
|
||||
return i->second->midi_event (val > 0x40);
|
||||
}
|
||||
|
||||
i = _midimap.find (id);
|
||||
if (i != _midimap.end()) {
|
||||
return i->second->midi_event (val > 0x40);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
FP8Controls::midi_touch (uint8_t id, uint8_t val)
|
||||
{
|
||||
assert (id < 8);
|
||||
return chanstrip[id]->midi_touch (val > 0x40);
|
||||
}
|
||||
|
||||
bool
|
||||
FP8Controls::midi_fader (uint8_t id, unsigned short val)
|
||||
{
|
||||
assert (id < 8);
|
||||
return chanstrip[id]->midi_fader ((val >> 4) / 1023.f);
|
||||
}
|
||||
|
||||
/* *****************************************************************************
|
||||
* Internal Model + View for Modes
|
||||
*/
|
||||
|
||||
void
|
||||
FP8Controls::set_nav_mode (NavigationMode m)
|
||||
{
|
||||
if (_navmode == m) {
|
||||
return;
|
||||
}
|
||||
// TODO add special-cases:
|
||||
// - master/monitor (blink when button is held + monitor section present)
|
||||
// - "click" hold -> encoder sets click volume, encoder-press toggle rec-only-metro
|
||||
button (BtnChannel).set_active (m == NavChannel);
|
||||
button (BtnZoom).set_active (m == NavZoom);
|
||||
button (BtnScroll).set_active (m == NavScroll);
|
||||
button (BtnBank).set_active (m == NavBank);
|
||||
button (BtnMaster).set_active (m == NavMaster);
|
||||
button (BtnSection).set_active (m == NavSection);
|
||||
button (BtnMarker).set_active (m == NavMarker);
|
||||
_navmode = m;
|
||||
}
|
||||
|
||||
void
|
||||
FP8Controls::set_fader_mode (FaderMode m)
|
||||
{
|
||||
if (_fadermode == m) {
|
||||
if (m == ModePlugins || m == ModeSend) {
|
||||
/* "Edit Plugins" while editing Plugin-params, returns back
|
||||
* to plugin selection.
|
||||
* "Sends" button banks through sends.
|
||||
*/
|
||||
FaderModeChanged ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// set lights
|
||||
button (BtnTrack).set_active (m == ModeTrack);
|
||||
button (BtnPlugins).set_active (m == ModePlugins);
|
||||
button (BtnSend).set_active (m == ModeSend);
|
||||
button (BtnPan).set_active (m == ModePan);
|
||||
_fadermode = m;
|
||||
FaderModeChanged ();
|
||||
}
|
||||
|
||||
void
|
||||
FP8Controls::set_mix_mode (MixMode m)
|
||||
{
|
||||
if (_mixmode == m) {
|
||||
if (m == MixUser || m == MixInputs) {
|
||||
/* always re-assign:
|
||||
* - MixUser: depends on selection
|
||||
* - MixInputs: depends on rec-arm
|
||||
*/
|
||||
MixModeChanged ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
button (BtnMAudio).set_active (m == MixAudio);
|
||||
button (BtnMVI).set_active (m == MixInstrument);
|
||||
button (BtnMBus).set_active (m == MixBus);
|
||||
button (BtnMVCA).set_active (m == MixVCA);
|
||||
button (BtnMAll).set_active (m == MixAll);
|
||||
button (BtnMInputs).set_active (m == MixInputs);
|
||||
button (BtnMMIDI).set_active (m == MixMIDI);
|
||||
button (BtnMOutputs).set_active (m == MixOutputs);
|
||||
button (BtnMFX).set_active (m == MixFX);
|
||||
button (BtnMUser).set_active (m == MixUser);
|
||||
|
||||
_mixmode = m;
|
||||
MixModeChanged ();
|
||||
}
|
||||
|
||||
void
|
||||
FP8Controls::toggle_timecode ()
|
||||
{
|
||||
_display_timecode = !_display_timecode;
|
||||
button (BtnTimecode).set_active (_display_timecode);
|
||||
}
|
172
libs/surfaces/faderport8/fp8_controls.h
Normal file
172
libs/surfaces/faderport8/fp8_controls.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ardour_surfaces_fp8controls_h_
|
||||
#define _ardour_surfaces_fp8controls_h_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "fp8_base.h"
|
||||
#include "fp8_button.h"
|
||||
#include "fp8_strip.h"
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class FP8Controls
|
||||
{
|
||||
public:
|
||||
FP8Controls (FP8Base&);
|
||||
virtual ~FP8Controls ();
|
||||
|
||||
enum ButtonId {
|
||||
BtnPlay,
|
||||
BtnStop,
|
||||
BtnRecord,
|
||||
BtnLoop,
|
||||
BtnRewind,
|
||||
BtnFastForward,
|
||||
|
||||
BtnALatch,
|
||||
BtnATrim,
|
||||
BtnAOff,
|
||||
BtnATouch,
|
||||
BtnAWrite,
|
||||
BtnARead,
|
||||
|
||||
// Automation
|
||||
BtnSave,
|
||||
BtnRedo,
|
||||
BtnUndo,
|
||||
BtnUser1,
|
||||
BtnUser2,
|
||||
BtnUser3,
|
||||
|
||||
BtnFootswitch,
|
||||
|
||||
// Pan/Param encoder press
|
||||
BtnParam,
|
||||
|
||||
// Navigation
|
||||
BtnPrev,
|
||||
BtnNext,
|
||||
BtnEncoder,
|
||||
|
||||
BtnChannel,
|
||||
BtnZoom,
|
||||
BtnScroll,
|
||||
BtnBank,
|
||||
BtnMaster,
|
||||
BtnClick,
|
||||
BtnSection,
|
||||
BtnMarker,
|
||||
|
||||
BtnF1, BtnF2, BtnF3, BtnF4,
|
||||
BtnF5, BtnF6, BtnF7, BtnF8,
|
||||
|
||||
// FaderMode
|
||||
BtnTrack,
|
||||
BtnPlugins,
|
||||
BtnSend,
|
||||
BtnPan,
|
||||
|
||||
BtnTimecode,
|
||||
|
||||
// Mix Management
|
||||
BtnMAudio,
|
||||
BtnMVI,
|
||||
BtnMBus,
|
||||
BtnMVCA,
|
||||
BtnMAll,
|
||||
|
||||
BtnMInputs,
|
||||
BtnMMIDI,
|
||||
BtnMOutputs,
|
||||
BtnMFX,
|
||||
BtnMUser,
|
||||
|
||||
// General Controls
|
||||
BtnArm,
|
||||
BtnArmAll,
|
||||
BtnSoloClear,
|
||||
BtnMuteClear,
|
||||
|
||||
BtnBypass,
|
||||
BtnBypassAll,
|
||||
BtnMacro,
|
||||
BtnOpen,
|
||||
BtnLink,
|
||||
BtnLock,
|
||||
|
||||
};
|
||||
|
||||
typedef std::map <ButtonId, std::string> UserButtonMap;
|
||||
|
||||
UserButtonMap const& user_buttons () const {
|
||||
return _user_buttons;
|
||||
}
|
||||
|
||||
bool button_name_to_enum (std::string const&, ButtonId&) const;
|
||||
bool button_enum_to_name (ButtonId, std::string&) const;
|
||||
|
||||
PBD::Signal0<void> FaderModeChanged;
|
||||
PBD::Signal0<void> MixModeChanged;
|
||||
|
||||
FP8Types::FaderMode fader_mode () const { return _fadermode; }
|
||||
FP8Types::NavigationMode nav_mode () const { return _navmode; }
|
||||
FP8Types::MixMode mix_mode () const { return _mixmode; }
|
||||
bool display_timecode () const { return _display_timecode; }
|
||||
|
||||
FP8ButtonInterface& button (ButtonId id);
|
||||
FP8Strip& strip (uint8_t id);
|
||||
|
||||
bool midi_event (uint8_t id, uint8_t val);
|
||||
bool midi_touch (uint8_t id, uint8_t val);
|
||||
bool midi_fader (uint8_t id, unsigned short val);
|
||||
void initialize ();
|
||||
|
||||
void set_fader_mode (FP8Types::FaderMode);
|
||||
protected:
|
||||
typedef std::map <uint8_t, FP8ButtonInterface*> MidiButtonMap;
|
||||
typedef std::map <ButtonId, FP8ButtonInterface*> CtrlButtonMap;
|
||||
|
||||
void set_nav_mode (FP8Types::NavigationMode);
|
||||
void set_mix_mode (FP8Types::MixMode);
|
||||
void toggle_timecode ();
|
||||
|
||||
MidiButtonMap _midimap;
|
||||
CtrlButtonMap _ctrlmap;
|
||||
MidiButtonMap _midimap_strip;
|
||||
|
||||
FP8Strip* chanstrip[8];
|
||||
|
||||
FP8Types::FaderMode _fadermode;
|
||||
FP8Types::NavigationMode _navmode;
|
||||
FP8Types::MixMode _mixmode;
|
||||
bool _display_timecode;
|
||||
|
||||
UserButtonMap _user_buttons;
|
||||
FP8DummyButton _dummy_button;
|
||||
|
||||
std::map<std::string, ButtonId> _user_str_to_enum;
|
||||
std::map<ButtonId, std::string> _user_enum_to_str;
|
||||
|
||||
PBD::ScopedConnectionList button_connections;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
#endif /* _ardour_surfaces_fp8controls_h_ */
|
524
libs/surfaces/faderport8/fp8_strip.cc
Normal file
524
libs/surfaces/faderport8/fp8_strip.cc
Normal file
|
@ -0,0 +1,524 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ardour/automation_control.h"
|
||||
#include "ardour/gain_control.h"
|
||||
#include "ardour/meter.h"
|
||||
#include "ardour/mute_control.h"
|
||||
#include "ardour/plugin_insert.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/solo_control.h"
|
||||
#include "ardour/stripable.h"
|
||||
#include "ardour/track.h"
|
||||
#include "ardour/value_as_string.h"
|
||||
|
||||
#include "control_protocol/control_protocol.h"
|
||||
|
||||
#include "fp8_strip.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourSurface::FP8Types;
|
||||
|
||||
FP8Strip::FP8Strip (FP8Base& b, uint8_t id)
|
||||
: _base (b)
|
||||
, _id (id)
|
||||
, _solo (b, 0x08 + id)
|
||||
, _mute (b, 0x10 + id)
|
||||
, _selrec (b, 0x18 + id, true)
|
||||
, _touching (false)
|
||||
, _strip_mode (0)
|
||||
, _bar_mode (0)
|
||||
, _displaymode (Stripables)
|
||||
{
|
||||
assert (id < 8);
|
||||
|
||||
_last_fader = 65535;
|
||||
_last_meter = _last_redux = _last_panpos = 0xff;
|
||||
|
||||
_mute.StateChange.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_mute, this, _1));
|
||||
_solo.StateChange.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_solo, this, _1));
|
||||
select_button ().released.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_select, this));
|
||||
recarm_button ().released.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_recarm, this));
|
||||
b.Periodic.connect_same_thread (_base_connection, boost::bind (&FP8Strip::periodic, this));
|
||||
}
|
||||
|
||||
FP8Strip::~FP8Strip ()
|
||||
{
|
||||
_fader_connection.disconnect ();
|
||||
_mute_connection.disconnect ();
|
||||
_solo_connection.disconnect ();
|
||||
_rec_connection.disconnect ();
|
||||
_pan_connection.disconnect ();
|
||||
|
||||
_fader_ctrl.reset ();
|
||||
_mute_ctrl.reset ();
|
||||
_solo_ctrl.reset ();
|
||||
_rec_ctrl.reset ();
|
||||
_pan_ctrl.reset ();
|
||||
|
||||
_base_connection.disconnect ();
|
||||
_button_connections.drop_connections ();
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::initialize ()
|
||||
{
|
||||
/* this is called once midi transmission is possible,
|
||||
* ie from FaderPort8::connected()
|
||||
*/
|
||||
_solo.set_active (false);
|
||||
_mute.set_active (false);
|
||||
|
||||
/* reset momentary button state */
|
||||
_mute.reset ();
|
||||
_solo.reset ();
|
||||
|
||||
/* clear cached values */
|
||||
_last_fader = 65535;
|
||||
_last_meter = _last_redux = _last_panpos = 0xff;
|
||||
|
||||
select_button ().set_color (0xffffffff);
|
||||
select_button ().set_active (false);
|
||||
select_button ().set_blinking (false);
|
||||
|
||||
recarm_button ().set_active (false);
|
||||
recarm_button ().set_color (0xffffffff);
|
||||
|
||||
set_strip_mode (0, true);
|
||||
|
||||
// force unset txt
|
||||
_last_line[0].clear ();
|
||||
_last_line[1].clear ();
|
||||
_last_line[2].clear ();
|
||||
_last_line[3].clear ();
|
||||
_base.tx_sysex (4, 0x12, _id, 0x00, 0x00);
|
||||
_base.tx_sysex (4, 0x12, _id, 0x01, 0x00);
|
||||
_base.tx_sysex (4, 0x12, _id, 0x02, 0x00);
|
||||
_base.tx_sysex (4, 0x12, _id, 0x03, 0x00);
|
||||
|
||||
set_bar_mode (4); // off
|
||||
|
||||
_base.tx_midi2 (0xd0 + _id, 0); // reset meter
|
||||
_base.tx_midi2 (0xd8 + _id, 0); // reset redux
|
||||
|
||||
_base.tx_midi3 (0xe0 + _id, 0, 0); // fader
|
||||
}
|
||||
|
||||
|
||||
#define GENERATE_SET_CTRL_FUNCTION(NAME) \
|
||||
void \
|
||||
FP8Strip::set_ ##NAME##_controllable (boost::shared_ptr<AutomationControl> ac) \
|
||||
{ \
|
||||
if (_##NAME##_ctrl == ac) { \
|
||||
return; \
|
||||
} \
|
||||
_##NAME##_connection.disconnect(); \
|
||||
_##NAME##_ctrl = ac; \
|
||||
\
|
||||
if (ac) { \
|
||||
ac->Changed.connect (_##NAME##_connection, MISSING_INVALIDATOR, \
|
||||
boost::bind (&FP8Strip::notify_##NAME##_changed, this), fp8_context()); \
|
||||
} \
|
||||
notify_##NAME##_changed (); \
|
||||
}
|
||||
|
||||
|
||||
GENERATE_SET_CTRL_FUNCTION (fader)
|
||||
GENERATE_SET_CTRL_FUNCTION (mute)
|
||||
GENERATE_SET_CTRL_FUNCTION (solo)
|
||||
GENERATE_SET_CTRL_FUNCTION (rec)
|
||||
GENERATE_SET_CTRL_FUNCTION (pan)
|
||||
|
||||
#undef GENERATE_SET_CTRL_FUNCTION
|
||||
|
||||
void
|
||||
FP8Strip::unset_controllables (int which)
|
||||
{
|
||||
_peak_meter = boost::shared_ptr<ARDOUR::PeakMeter>();
|
||||
_redux_ctrl = boost::shared_ptr<ARDOUR::ReadOnlyControl>();
|
||||
|
||||
if (which & CTRL_FADER) {
|
||||
set_fader_controllable (boost::shared_ptr<AutomationControl>());
|
||||
}
|
||||
if (which & CTRL_MUTE) {
|
||||
set_mute_controllable (boost::shared_ptr<AutomationControl>());
|
||||
}
|
||||
if (which & CTRL_SOLO) {
|
||||
set_solo_controllable (boost::shared_ptr<AutomationControl>());
|
||||
}
|
||||
if (which & CTRL_REC) {
|
||||
set_rec_controllable (boost::shared_ptr<AutomationControl>());
|
||||
}
|
||||
if (which & CTRL_PAN) {
|
||||
set_bar_mode (4); // off
|
||||
set_pan_controllable (boost::shared_ptr<AutomationControl>());
|
||||
}
|
||||
if (which & CTRL_SELECT) {
|
||||
_select_plugin_functor.clear ();
|
||||
select_button ().set_color (0xffffffff);
|
||||
select_button ().set_active (false);
|
||||
select_button ().set_blinking (false);
|
||||
}
|
||||
if (which & CTRL_TEXT1) {
|
||||
set_text_line (0x00, "");
|
||||
}
|
||||
if (which & CTRL_TEXT2) {
|
||||
set_text_line (0x01, "");
|
||||
}
|
||||
if (which & CTRL_TEXT3) {
|
||||
set_text_line (0x02, "");
|
||||
}
|
||||
if (which & CTRL_TEXT4) {
|
||||
set_text_line (0x03, "");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::set_stripable (boost::shared_ptr<Stripable> s, bool panmode)
|
||||
{
|
||||
assert (s);
|
||||
|
||||
if (panmode) {
|
||||
set_fader_controllable (s->pan_azimuth_control ());
|
||||
} else {
|
||||
set_fader_controllable (s->gain_control ());
|
||||
}
|
||||
set_pan_controllable (s->pan_azimuth_control ());
|
||||
|
||||
if (s->is_monitor ()) {
|
||||
set_mute_controllable (boost::shared_ptr<AutomationControl>());
|
||||
} else {
|
||||
set_mute_controllable (s->mute_control ());
|
||||
}
|
||||
set_solo_controllable (s->solo_control ());
|
||||
|
||||
if (boost::dynamic_pointer_cast<Track> (s)) {
|
||||
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
|
||||
set_rec_controllable (t->rec_enable_control ());
|
||||
recarm_button ().set_color (0xff0000ff);
|
||||
} else {
|
||||
set_rec_controllable (boost::shared_ptr<AutomationControl>());
|
||||
recarm_button ().set_color (0xffffffff);
|
||||
recarm_button ().set_active (false);
|
||||
}
|
||||
_peak_meter = s->peak_meter ();
|
||||
_redux_ctrl = s->comp_redux_controllable ();
|
||||
|
||||
_select_plugin_functor.clear ();
|
||||
select_button ().set_active (s->is_selected ());
|
||||
select_button ().set_color (s->presentation_info ().color());
|
||||
//select_button ().set_blinking (false);
|
||||
|
||||
set_strip_mode (0x05);
|
||||
set_text_line (0x00, s->name ());
|
||||
set_text_line (0x01, _pan_ctrl ? _pan_ctrl->get_user_string () : "");
|
||||
set_text_line (0x02, "");
|
||||
set_text_line (0x03, "");
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::set_select_cb (boost::function<void ()>& functor)
|
||||
{
|
||||
_select_plugin_functor.clear ();
|
||||
_select_plugin_functor = functor;
|
||||
}
|
||||
|
||||
/* *****************************************************************************
|
||||
* Parse Strip Specifig MIDI Events
|
||||
*/
|
||||
|
||||
bool
|
||||
FP8Strip::midi_touch (bool t)
|
||||
{
|
||||
_touching = t;
|
||||
boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
|
||||
if (!ac) {
|
||||
return false;
|
||||
}
|
||||
if (t) {
|
||||
ac->start_touch (ac->session().transport_frame());
|
||||
} else {
|
||||
ac->stop_touch (true, ac->session().transport_frame());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FP8Strip::midi_fader (float val)
|
||||
{
|
||||
assert (val >= 0.f && val <= 1.f);
|
||||
if (!_touching) {
|
||||
return false;
|
||||
}
|
||||
boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
|
||||
if (!ac) {
|
||||
return false;
|
||||
}
|
||||
ac->set_value (ac->interface_to_internal (val), PBD::Controllable::UseGroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* *****************************************************************************
|
||||
* Actions from Controller, Update Model
|
||||
*/
|
||||
|
||||
void
|
||||
FP8Strip::set_mute (bool on)
|
||||
{
|
||||
if (_mute_ctrl) {
|
||||
if (!_mute_ctrl->touching ()) {
|
||||
_mute_ctrl->start_touch (_mute_ctrl->session().transport_frame());
|
||||
}
|
||||
_mute_ctrl->set_value (on ? 1.0 : 0.0, PBD::Controllable::UseGroup);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::set_solo (bool on)
|
||||
{
|
||||
if (_solo_ctrl) {
|
||||
if (!_solo_ctrl->touching ()) {
|
||||
_solo_ctrl->start_touch (_solo_ctrl->session().transport_frame());
|
||||
}
|
||||
_solo_ctrl->set_value (on ? 1.0 : 0.0, PBD::Controllable::UseGroup);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::set_recarm ()
|
||||
{
|
||||
if (_rec_ctrl) {
|
||||
const bool on = !recarm_button().is_active();
|
||||
_rec_ctrl->set_value (on ? 1.0 : 0.0, PBD::Controllable::UseGroup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FP8Strip::set_select ()
|
||||
{
|
||||
if (!_select_plugin_functor.empty ()) {
|
||||
_select_plugin_functor ();
|
||||
}
|
||||
}
|
||||
|
||||
/* *****************************************************************************
|
||||
* Callbacks from Stripable, Update View
|
||||
*/
|
||||
|
||||
void
|
||||
FP8Strip::notify_fader_changed ()
|
||||
{
|
||||
boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
|
||||
if (_touching) {
|
||||
return;
|
||||
}
|
||||
float val = 0;
|
||||
if (ac) {
|
||||
val = ac->internal_to_interface (ac->get_value()) * 16368.f; /* 16 * 1023 */
|
||||
}
|
||||
unsigned short mv = lrintf (val);
|
||||
if (mv == _last_fader) {
|
||||
return;
|
||||
}
|
||||
_last_fader = mv;
|
||||
_base.tx_midi3 (0xe0 + _id, (mv & 0x7f), (mv >> 7) & 0x7f);
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::notify_solo_changed ()
|
||||
{
|
||||
if (_solo_ctrl) {
|
||||
_solo.set_active (_solo_ctrl->get_value () > 0);
|
||||
} else {
|
||||
_solo.set_active (false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::notify_mute_changed ()
|
||||
{
|
||||
if (_mute_ctrl) {
|
||||
_mute.set_active (_mute_ctrl->get_value () > 0);
|
||||
} else {
|
||||
_mute.set_active (false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::notify_rec_changed ()
|
||||
{
|
||||
if (_rec_ctrl) {
|
||||
recarm_button ().set_active (_rec_ctrl->get_value() > 0.);
|
||||
} else {
|
||||
recarm_button ().set_active (false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::notify_pan_changed ()
|
||||
{
|
||||
}
|
||||
|
||||
/* *****************************************************************************
|
||||
* Periodic View Updates
|
||||
*/
|
||||
|
||||
void
|
||||
FP8Strip::periodic_update_fader ()
|
||||
{
|
||||
boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
|
||||
if (!ac || _touching) {
|
||||
return;
|
||||
}
|
||||
|
||||
ARDOUR::AutoState state = ac->automation_state();
|
||||
if (state == Touch || state == Play) {
|
||||
notify_fader_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::periodic_update_meter ()
|
||||
{
|
||||
bool have_meter = false;
|
||||
bool have_panner = false;
|
||||
|
||||
if (_peak_meter) {
|
||||
have_meter = true;
|
||||
float dB = _peak_meter->meter_level (0, MeterMCP);
|
||||
// TODO: deflect meter
|
||||
int val = std::min (127.f, std::max (0.f, 2.f * dB + 127.f));
|
||||
if (val != _last_meter || val > 0) {
|
||||
_base.tx_midi2 (0xd0 + _id, val & 0x7f); // falls off automatically
|
||||
_last_meter = val;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (0 != _last_meter) {
|
||||
_base.tx_midi2 (0xd0 + _id, 0);
|
||||
_last_meter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// show redux only if there's a meter, too (strip display mode 5)
|
||||
if (_peak_meter && _redux_ctrl) {
|
||||
float rx = (1.f - _redux_ctrl->get_parameter ()) * 127.f;
|
||||
// TODO: deflect redux
|
||||
int val = std::min (127.f, std::max (0.f, rx));
|
||||
if (val != _last_redux) {
|
||||
_base.tx_midi2 (0xd8 + _id, val & 0x7f);
|
||||
_last_redux = val;
|
||||
}
|
||||
} else {
|
||||
if (0 != _last_redux) {
|
||||
_base.tx_midi2 (0xd8 + _id, 0);
|
||||
_last_redux = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_displaymode == PluginParam) {
|
||||
set_bar_mode (4); // Off
|
||||
if (_fader_ctrl) {
|
||||
set_text_line (0x01, value_as_string(_fader_ctrl->desc(), _fader_ctrl->get_value()));
|
||||
} else {
|
||||
set_text_line (0x01, "");
|
||||
}
|
||||
} else if (_pan_ctrl) {
|
||||
have_panner = true;
|
||||
float panpos = _pan_ctrl->internal_to_interface (_pan_ctrl->get_value());
|
||||
int val = std::min (127.f, std::max (0.f, panpos * 128.f));
|
||||
set_bar_mode (1); // Bipolar
|
||||
if (val != _last_panpos) {
|
||||
_base.tx_midi3 (0xb0, 0x30 + _id, val & 0x7f);
|
||||
_last_panpos = val;
|
||||
}
|
||||
set_text_line (0x01, _pan_ctrl->get_user_string ());
|
||||
} else {
|
||||
set_bar_mode (4); // Off
|
||||
}
|
||||
|
||||
if (have_meter && have_panner) {
|
||||
set_strip_mode (5); // small meter mode
|
||||
}
|
||||
else if (have_meter) {
|
||||
set_strip_mode (4); // big meter mode
|
||||
}
|
||||
else if (have_panner) {
|
||||
set_strip_mode (0); // 3 lines of text + value
|
||||
} else {
|
||||
set_strip_mode (0); // 3 lines of text + value
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::set_strip_mode (uint8_t strip_mode, bool clear)
|
||||
{
|
||||
if (strip_mode == _strip_mode && !clear) {
|
||||
return;
|
||||
}
|
||||
_strip_mode = strip_mode;
|
||||
_base.tx_sysex (3, 0x13, _id, (_strip_mode & 0x07) | (clear ? 0x10 : 0));
|
||||
//_base.tx_midi3 (0xb0, 0x38 + _id, _bar_mode);
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::set_bar_mode (uint8_t bar_mode)
|
||||
{
|
||||
if (bar_mode == _bar_mode) {
|
||||
return;
|
||||
}
|
||||
_bar_mode = bar_mode;
|
||||
_base.tx_midi3 (0xb0, 0x38 + _id, bar_mode);
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::set_text_line (uint8_t line, std::string const& txt)
|
||||
{
|
||||
assert (line < 4);
|
||||
if (_last_line[line] == txt) {
|
||||
return;
|
||||
}
|
||||
_base.tx_text (_id, line, 0x00, txt);
|
||||
_last_line[line] = txt;
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::periodic_update_timecode ()
|
||||
{
|
||||
if (_id >= 2 && _id < 6) {
|
||||
std::string const& tc = _base.timecode();
|
||||
//" HH:MM:SS:FF"
|
||||
std::string t;
|
||||
if (tc.size () == 12) {
|
||||
t = tc.substr (1 + (_id - 2) * 3, 2);
|
||||
}
|
||||
set_text_line (0x02, t);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FP8Strip::periodic ()
|
||||
{
|
||||
periodic_update_fader ();
|
||||
periodic_update_meter ();
|
||||
if (_displaymode != PluginSelect) {
|
||||
periodic_update_timecode ();
|
||||
}
|
||||
}
|
162
libs/surfaces/faderport8/fp8_strip.h
Normal file
162
libs/surfaces/faderport8/fp8_strip.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ardour_surfaces_fp8strip_h_
|
||||
#define _ardour_surfaces_fp8strip_h_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "fp8_base.h"
|
||||
#include "fp8_button.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class Stripable;
|
||||
class AutomationControl;
|
||||
class PeakMeter;
|
||||
class ReadOnlyControl;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class FP8Strip
|
||||
{
|
||||
public:
|
||||
FP8Strip (FP8Base& b, uint8_t id);
|
||||
~FP8Strip ();
|
||||
|
||||
FP8ButtonInterface& solo_button () { return _solo; }
|
||||
FP8ButtonInterface& mute_button () { return _mute; }
|
||||
FP8ButtonInterface& selrec_button () { return _selrec; }
|
||||
FP8ButtonInterface& recarm_button () { return *_selrec.button_shift(); }
|
||||
FP8ButtonInterface& select_button () { return *_selrec.button(); }
|
||||
|
||||
bool midi_touch (bool t);
|
||||
bool midi_fader (float val);
|
||||
|
||||
void initialize (); // call only when connected, sends midi
|
||||
|
||||
void set_select_cb (boost::function<void ()>&);
|
||||
|
||||
enum DisplayMode {
|
||||
Stripables,
|
||||
PluginSelect, // no clock display
|
||||
PluginParam, // param value
|
||||
};
|
||||
|
||||
void set_periodic_display_mode (DisplayMode m) {
|
||||
_displaymode = m;
|
||||
}
|
||||
|
||||
// convenience function to call all set_XXX_controllable
|
||||
void set_stripable (boost::shared_ptr<ARDOUR::Stripable>, bool panmode);
|
||||
void set_text_line (uint8_t, std::string const&);
|
||||
|
||||
enum CtrlMask {
|
||||
CTRL_FADER = 0x001,
|
||||
CTRL_MUTE = 0x002,
|
||||
CTRL_SOLO = 0x004,
|
||||
CTRL_REC = 0x004,
|
||||
CTRL_PAN = 0x008,
|
||||
CTRL_SELECT = 0x010,
|
||||
CTRL_TEXT1 = 0x100,
|
||||
CTRL_TEXT2 = 0x200,
|
||||
CTRL_TEXT3 = 0x400,
|
||||
CTRL_TEXT4 = 0x800,
|
||||
|
||||
CTRL_TEXT = 0xf00,
|
||||
CTRL_ALL = 0xfff,
|
||||
};
|
||||
|
||||
void unset_controllables (int which = CTRL_ALL);
|
||||
|
||||
void set_fader_controllable (boost::shared_ptr<ARDOUR::AutomationControl>);
|
||||
void set_mute_controllable (boost::shared_ptr<ARDOUR::AutomationControl>);
|
||||
void set_solo_controllable (boost::shared_ptr<ARDOUR::AutomationControl>);
|
||||
void set_rec_controllable (boost::shared_ptr<ARDOUR::AutomationControl>);
|
||||
void set_pan_controllable (boost::shared_ptr<ARDOUR::AutomationControl>);
|
||||
|
||||
private:
|
||||
FP8Base& _base;
|
||||
uint8_t _id;
|
||||
FP8MomentaryButton _solo;
|
||||
FP8MomentaryButton _mute;
|
||||
FP8ARMSensitiveButton _selrec;
|
||||
|
||||
bool _touching;
|
||||
|
||||
PBD::ScopedConnection _base_connection; // periodic
|
||||
PBD::ScopedConnectionList _button_connections;
|
||||
|
||||
boost::shared_ptr<ARDOUR::Stripable> _stripable;
|
||||
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> _fader_ctrl;
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> _mute_ctrl;
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> _solo_ctrl;
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> _rec_ctrl;
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> _pan_ctrl;
|
||||
|
||||
PBD::ScopedConnection _fader_connection;
|
||||
PBD::ScopedConnection _mute_connection;
|
||||
PBD::ScopedConnection _solo_connection;
|
||||
PBD::ScopedConnection _rec_connection;
|
||||
PBD::ScopedConnection _pan_connection;
|
||||
|
||||
boost::shared_ptr<ARDOUR::PeakMeter> _peak_meter;
|
||||
boost::shared_ptr<ARDOUR::ReadOnlyControl> _redux_ctrl;
|
||||
boost::function<void ()> _select_plugin_functor;
|
||||
|
||||
/* notifications, update view */
|
||||
void notify_fader_changed ();
|
||||
void notify_solo_changed ();
|
||||
void notify_mute_changed ();
|
||||
void notify_rec_changed ();
|
||||
void notify_pan_changed ();
|
||||
|
||||
/* actions, update model */
|
||||
void set_mute (bool);
|
||||
void set_solo (bool);
|
||||
void set_select ();
|
||||
void set_recarm ();
|
||||
|
||||
/* periodic poll, update view */
|
||||
void periodic_update_fader ();
|
||||
void periodic_update_meter ();
|
||||
void periodic_update_timecode ();
|
||||
void periodic ();
|
||||
|
||||
/* cache */
|
||||
unsigned short _last_fader;
|
||||
uint8_t _last_meter;
|
||||
uint8_t _last_redux;
|
||||
uint8_t _last_panpos;
|
||||
|
||||
/* display */
|
||||
void set_strip_mode (uint8_t, bool clear = false);
|
||||
void set_bar_mode (uint8_t);
|
||||
|
||||
uint8_t _strip_mode;
|
||||
uint8_t _bar_mode;
|
||||
DisplayMode _displaymode;
|
||||
std::string _last_line[4];
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
#endif /* _ardour_surfaces_fp8strip_h_ */
|
424
libs/surfaces/faderport8/gui.cc
Normal file
424
libs/surfaces/faderport8/gui.cc
Normal file
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
|
||||
* Copyright (C) 2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gtkmm/alignment.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/liststore.h>
|
||||
|
||||
#include "pbd/unwind.h"
|
||||
#include "pbd/strsplit.h"
|
||||
#include "pbd/file_utils.h"
|
||||
|
||||
#include "gtkmm2ext/bindings.h"
|
||||
#include "gtkmm2ext/gtk_ui.h"
|
||||
#include "gtkmm2ext/gui_thread.h"
|
||||
#include "gtkmm2ext/utils.h"
|
||||
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/filesystem_paths.h"
|
||||
|
||||
#include "faderport8.h"
|
||||
#include "gui.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
using namespace std;
|
||||
using namespace Gtk;
|
||||
using namespace Gtkmm2ext;
|
||||
|
||||
void*
|
||||
FaderPort8::get_gui () const
|
||||
{
|
||||
if (!gui) {
|
||||
const_cast<FaderPort8*>(this)->build_gui ();
|
||||
}
|
||||
static_cast<Gtk::VBox*>(gui)->show_all();
|
||||
return gui;
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::tear_down_gui ()
|
||||
{
|
||||
if (gui) {
|
||||
Gtk::Widget *w = static_cast<Gtk::VBox*>(gui)->get_parent();
|
||||
if (w) {
|
||||
w->hide();
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
delete static_cast<FP8GUI*> (gui);
|
||||
gui = 0;
|
||||
}
|
||||
|
||||
void
|
||||
FaderPort8::build_gui ()
|
||||
{
|
||||
gui = (void*) new FP8GUI (*this);
|
||||
}
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
FP8GUI::FP8GUI (FaderPort8& p)
|
||||
: fp (p)
|
||||
, table (2, 3)
|
||||
, ignore_active_change (false)
|
||||
{
|
||||
set_border_width (12);
|
||||
|
||||
table.set_row_spacings (4);
|
||||
table.set_col_spacings (6);
|
||||
table.set_border_width (12);
|
||||
table.set_homogeneous (false);
|
||||
|
||||
Gtk::Label* l;
|
||||
int row = 0;
|
||||
|
||||
input_combo.pack_start (midi_port_columns.short_name);
|
||||
output_combo.pack_start (midi_port_columns.short_name);
|
||||
|
||||
input_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FP8GUI::active_port_changed), &input_combo, true));
|
||||
output_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FP8GUI::active_port_changed), &output_combo, false));
|
||||
|
||||
l = manage (new Gtk::Label);
|
||||
l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Incoming MIDI on:")));
|
||||
l->set_alignment (1.0, 0.5);
|
||||
table.attach (*l, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
|
||||
table.attach (input_combo, 2, 6, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
|
||||
row++;
|
||||
|
||||
l = manage (new Gtk::Label);
|
||||
l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Outgoing MIDI on:")));
|
||||
l->set_alignment (1.0, 0.5);
|
||||
table.attach (*l, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
|
||||
table.attach (output_combo, 2, 6, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
|
||||
row++;
|
||||
|
||||
pack_start (table);
|
||||
|
||||
/* actions */
|
||||
build_available_action_menu ();
|
||||
|
||||
int action_row = 0;
|
||||
int action_col = 0;
|
||||
Gtk::Alignment* align;
|
||||
|
||||
for (FP8Controls::UserButtonMap::const_iterator i = fp.control().user_buttons ().begin ();
|
||||
i != fp.control().user_buttons ().end (); ++i) {
|
||||
Gtk::ComboBox* user_combo = manage (new Gtk::ComboBox);
|
||||
build_action_combo (*user_combo, i->first);
|
||||
l = manage (new Gtk::Label);
|
||||
l->set_markup (string_compose ("<span weight=\"bold\">%1:</span>", i->second));
|
||||
l->set_alignment (1.0, 0.5);
|
||||
table.attach (*l, 2 * action_col, 2 * action_col + 1, row + action_row, row + action_row + 1, AttachOptions(FILL|EXPAND), AttachOptions (0));
|
||||
align = manage (new Alignment);
|
||||
align->set (0.0, 0.5);
|
||||
align->add (*user_combo);
|
||||
table.attach (*align, 2 * action_col + 1, 2 * action_col + 2, row + action_row, row + action_row + 1, AttachOptions(FILL|EXPAND), AttachOptions (0));
|
||||
|
||||
if (++action_row == 4) {
|
||||
action_row = 0;
|
||||
++action_col;
|
||||
}
|
||||
}
|
||||
|
||||
/* update the port connection combos */
|
||||
update_port_combos ();
|
||||
|
||||
/* catch future changes to connection state */
|
||||
fp.ConnectionChange.connect (connection_change_connection, invalidator (*this), boost::bind (&FP8GUI::connection_handler, this), gui_context());
|
||||
}
|
||||
|
||||
FP8GUI::~FP8GUI ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FP8GUI::connection_handler ()
|
||||
{
|
||||
PBD::Unwinder<bool> ici (ignore_active_change, true);
|
||||
update_port_combos ();
|
||||
}
|
||||
|
||||
void
|
||||
FP8GUI::update_port_combos ()
|
||||
{
|
||||
vector<string> midi_inputs;
|
||||
vector<string> midi_outputs;
|
||||
|
||||
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal), midi_inputs);
|
||||
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal), midi_outputs);
|
||||
|
||||
Glib::RefPtr<Gtk::ListStore> input = build_midi_port_list (midi_inputs, true);
|
||||
Glib::RefPtr<Gtk::ListStore> output = build_midi_port_list (midi_outputs, false);
|
||||
bool input_found = false;
|
||||
bool output_found = false;
|
||||
int n;
|
||||
|
||||
input_combo.set_model (input);
|
||||
output_combo.set_model (output);
|
||||
|
||||
Gtk::TreeModel::Children children = input->children();
|
||||
Gtk::TreeModel::Children::iterator i;
|
||||
i = children.begin();
|
||||
++i; /* skip "Disconnected" */
|
||||
|
||||
for (n = 1; i != children.end(); ++i, ++n) {
|
||||
string port_name = (*i)[midi_port_columns.full_name];
|
||||
if (fp.input_port()->connected_to (port_name)) {
|
||||
input_combo.set_active (n);
|
||||
input_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_found) {
|
||||
input_combo.set_active (0); /* disconnected */
|
||||
}
|
||||
|
||||
children = output->children();
|
||||
i = children.begin();
|
||||
++i; /* skip "Disconnected" */
|
||||
|
||||
for (n = 1; i != children.end(); ++i, ++n) {
|
||||
string port_name = (*i)[midi_port_columns.full_name];
|
||||
if (fp.output_port()->connected_to (port_name)) {
|
||||
output_combo.set_active (n);
|
||||
output_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_found) {
|
||||
output_combo.set_active (0); /* disconnected */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Glib::RefPtr<Gtk::ListStore>
|
||||
FP8GUI::build_midi_port_list (vector<string> const & ports, bool for_input)
|
||||
{
|
||||
Glib::RefPtr<Gtk::ListStore> store = ListStore::create (midi_port_columns);
|
||||
TreeModel::Row row;
|
||||
|
||||
row = *store->append ();
|
||||
row[midi_port_columns.full_name] = string();
|
||||
row[midi_port_columns.short_name] = _("Disconnected");
|
||||
|
||||
for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
|
||||
row = *store->append ();
|
||||
row[midi_port_columns.full_name] = *p;
|
||||
std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
|
||||
if (pn.empty ()) {
|
||||
pn = (*p).substr ((*p).find (':') + 1);
|
||||
}
|
||||
row[midi_port_columns.short_name] = pn;
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
void
|
||||
FP8GUI::active_port_changed (Gtk::ComboBox* combo, bool for_input)
|
||||
{
|
||||
if (ignore_active_change) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeModel::iterator active = combo->get_active ();
|
||||
string new_port = (*active)[midi_port_columns.full_name];
|
||||
|
||||
if (new_port.empty()) {
|
||||
if (for_input) {
|
||||
fp.input_port()->disconnect_all ();
|
||||
} else {
|
||||
fp.output_port()->disconnect_all ();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (for_input) {
|
||||
if (!fp.input_port()->connected_to (new_port)) {
|
||||
fp.input_port()->disconnect_all ();
|
||||
fp.input_port()->connect (new_port);
|
||||
}
|
||||
} else {
|
||||
if (!fp.output_port()->connected_to (new_port)) {
|
||||
fp.output_port()->disconnect_all ();
|
||||
fp.output_port()->connect (new_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
FP8GUI::build_available_action_menu ()
|
||||
{
|
||||
/* build a model of all available actions (needs to be tree structured
|
||||
* more)
|
||||
*/
|
||||
|
||||
available_action_model = TreeStore::create (action_columns);
|
||||
|
||||
vector<string> paths;
|
||||
vector<string> labels;
|
||||
vector<string> tooltips;
|
||||
vector<string> keys;
|
||||
vector<Glib::RefPtr<Gtk::Action> > actions;
|
||||
|
||||
Gtkmm2ext::ActionMap::get_all_actions (paths, labels, tooltips, keys, actions);
|
||||
|
||||
typedef std::map<string,TreeIter> NodeMap;
|
||||
NodeMap nodes;
|
||||
NodeMap::iterator r;
|
||||
|
||||
|
||||
vector<string>::iterator k;
|
||||
vector<string>::iterator p;
|
||||
vector<string>::iterator t;
|
||||
vector<string>::iterator l;
|
||||
|
||||
available_action_model->clear ();
|
||||
|
||||
TreeIter rowp;
|
||||
TreeModel::Row parent;
|
||||
|
||||
/* Disabled item (row 0) */
|
||||
|
||||
rowp = available_action_model->append();
|
||||
parent = *(rowp);
|
||||
parent[action_columns.name] = _("Disabled");
|
||||
|
||||
for (l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l) {
|
||||
|
||||
TreeModel::Row row;
|
||||
vector<string> parts;
|
||||
|
||||
parts.clear ();
|
||||
|
||||
split (*p, parts, '/');
|
||||
|
||||
if (parts.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//kinda kludgy way to avoid displaying menu items as mappable
|
||||
if ( parts[1] == _("Main_menu") )
|
||||
continue;
|
||||
if ( parts[1] == _("JACK") )
|
||||
continue;
|
||||
if ( parts[1] == _("redirectmenu") )
|
||||
continue;
|
||||
if ( parts[1] == _("Editor_menus") )
|
||||
continue;
|
||||
if ( parts[1] == _("RegionList") )
|
||||
continue;
|
||||
if ( parts[1] == _("ProcessorMenu") )
|
||||
continue;
|
||||
|
||||
if ((r = nodes.find (parts[1])) == nodes.end()) {
|
||||
|
||||
/* top level is missing */
|
||||
|
||||
TreeIter rowp;
|
||||
TreeModel::Row parent;
|
||||
rowp = available_action_model->append();
|
||||
nodes[parts[1]] = rowp;
|
||||
parent = *(rowp);
|
||||
parent[action_columns.name] = parts[1];
|
||||
|
||||
row = *(available_action_model->append (parent.children()));
|
||||
|
||||
} else {
|
||||
|
||||
row = *(available_action_model->append ((*r->second)->children()));
|
||||
|
||||
}
|
||||
|
||||
/* add this action */
|
||||
|
||||
if (l->empty ()) {
|
||||
row[action_columns.name] = *t;
|
||||
action_map[*t] = *p;
|
||||
} else {
|
||||
row[action_columns.name] = *l;
|
||||
action_map[*l] = *p;
|
||||
}
|
||||
|
||||
string path = (*p);
|
||||
/* ControlProtocol::access_action() is not interested in the
|
||||
legacy "<Actions>/" prefix part of a path.
|
||||
*/
|
||||
path = path.substr (strlen ("<Actions>/"));
|
||||
|
||||
row[action_columns.path] = path;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FP8GUI::find_action_in_model (const TreeModel::iterator& iter, std::string const& action_path, TreeModel::iterator* found)
|
||||
{
|
||||
TreeModel::Row row = *iter;
|
||||
string path = row[action_columns.path];
|
||||
|
||||
if (path == action_path) {
|
||||
*found = iter;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
FP8GUI::build_action_combo (Gtk::ComboBox& cb, FP8Controls::ButtonId id)
|
||||
{
|
||||
cb.set_model (available_action_model);
|
||||
cb.pack_start (action_columns.name);
|
||||
|
||||
/* set the active "row" to the right value for the current button binding */
|
||||
string current_action = fp.get_button_action (id, false); /* lookup release action */
|
||||
|
||||
if (current_action.empty()) {
|
||||
cb.set_active (0); /* "disabled" */
|
||||
} else {
|
||||
TreeModel::iterator iter = available_action_model->children().end();
|
||||
|
||||
available_action_model->foreach_iter (sigc::bind (sigc::mem_fun (*this, &FP8GUI::find_action_in_model), current_action, &iter));
|
||||
|
||||
if (iter != available_action_model->children().end()) {
|
||||
cb.set_active (iter);
|
||||
} else {
|
||||
cb.set_active (0);
|
||||
}
|
||||
}
|
||||
/* bind signal _after_ setting the current value */
|
||||
cb.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FP8GUI::action_changed), &cb, id));
|
||||
}
|
||||
|
||||
void
|
||||
FP8GUI::action_changed (Gtk::ComboBox* cb, FP8Controls::ButtonId id)
|
||||
{
|
||||
TreeModel::const_iterator row = cb->get_active ();
|
||||
string action_path = (*row)[action_columns.path];
|
||||
fp.set_button_action (id, false, action_path);
|
||||
}
|
98
libs/surfaces/faderport8/gui.h
Normal file
98
libs/surfaces/faderport8/gui.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
|
||||
* Copyright (C) 2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_faderport8_gui_h__
|
||||
#define __ardour_faderport8_gui_h__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/combobox.h>
|
||||
#include <gtkmm/image.h>
|
||||
#include <gtkmm/table.h>
|
||||
#include <gtkmm/treestore.h>
|
||||
|
||||
namespace Gtk {
|
||||
class CellRendererCombo;
|
||||
class ListStore;
|
||||
}
|
||||
|
||||
#include "faderport8.h"
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class FP8GUI : public Gtk::VBox
|
||||
{
|
||||
public:
|
||||
FP8GUI (FaderPort8&);
|
||||
~FP8GUI ();
|
||||
|
||||
private:
|
||||
FaderPort8& fp;
|
||||
Gtk::Table table;
|
||||
Gtk::Image image;
|
||||
|
||||
/* port connections */
|
||||
Gtk::ComboBox input_combo;
|
||||
Gtk::ComboBox output_combo;
|
||||
|
||||
void update_port_combos ();
|
||||
void connection_handler ();
|
||||
PBD::ScopedConnection connection_change_connection;
|
||||
|
||||
struct MidiPortColumns : public Gtk::TreeModel::ColumnRecord {
|
||||
MidiPortColumns() {
|
||||
add (short_name);
|
||||
add (full_name);
|
||||
}
|
||||
Gtk::TreeModelColumn<std::string> short_name;
|
||||
Gtk::TreeModelColumn<std::string> full_name;
|
||||
};
|
||||
|
||||
MidiPortColumns midi_port_columns;
|
||||
bool ignore_active_change;
|
||||
|
||||
Glib::RefPtr<Gtk::ListStore> build_midi_port_list (std::vector<std::string> const & ports, bool for_input);
|
||||
void active_port_changed (Gtk::ComboBox*,bool for_input);
|
||||
|
||||
/* user actions */
|
||||
void build_available_action_menu ();
|
||||
void build_action_combo (Gtk::ComboBox& cb, FP8Controls::ButtonId id);
|
||||
void action_changed (Gtk::ComboBox* cb, FP8Controls::ButtonId id);
|
||||
|
||||
struct ActionColumns : public Gtk::TreeModel::ColumnRecord {
|
||||
ActionColumns() {
|
||||
add (name);
|
||||
add (path);
|
||||
}
|
||||
Gtk::TreeModelColumn<std::string> name;
|
||||
Gtk::TreeModelColumn<std::string> path;
|
||||
};
|
||||
|
||||
ActionColumns action_columns;
|
||||
Glib::RefPtr<Gtk::TreeStore> available_action_model;
|
||||
std::map<std::string,std::string> action_map; // map from action names to paths
|
||||
|
||||
bool find_action_in_model (const Gtk::TreeModel::iterator& iter, std::string const & action_path, Gtk::TreeModel::iterator* found);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* __ardour_faderport8_gui_h__ */
|
37
libs/surfaces/faderport8/wscript
Normal file
37
libs/surfaces/faderport8/wscript
Normal file
|
@ -0,0 +1,37 @@
|
|||
#!/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):
|
||||
autowaf.configure(conf)
|
||||
|
||||
def build(bld):
|
||||
obj = bld(features = 'cxx cxxshlib')
|
||||
obj.source = [
|
||||
'faderport8.cc',
|
||||
'faderport8_interface.cc',
|
||||
'fp8_controls.cc',
|
||||
'fp8_strip.cc',
|
||||
'callbacks.cc',
|
||||
'actions.cc',
|
||||
'gui.cc'
|
||||
]
|
||||
obj.export_includes = ['.']
|
||||
obj.defines = [ 'PACKAGE="ardour_faderport8"' ]
|
||||
obj.defines += [ 'ARDOURSURFACE_DLL_EXPORTS' ]
|
||||
obj.includes = [ '.', './faderport8']
|
||||
obj.name = 'libardour_faderport8'
|
||||
obj.target = 'ardour_faderport8'
|
||||
obj.uselib = 'GTKMM GTK GDK XML'
|
||||
obj.use = 'libardour libardour_cp libgtkmm2ext libpbd'
|
||||
obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces')
|
||||
|
||||
def shutdown():
|
||||
autowaf.shutdown()
|
|
@ -22,6 +22,7 @@ out = 'build'
|
|||
children = [
|
||||
'control_protocol',
|
||||
'faderport',
|
||||
'faderport8',
|
||||
'cc121',
|
||||
'generic_midi',
|
||||
'mackie',
|
||||
|
@ -75,6 +76,7 @@ def build(bld):
|
|||
bld.recurse('control_protocol')
|
||||
bld.recurse('generic_midi')
|
||||
bld.recurse('faderport')
|
||||
bld.recurse('faderport8')
|
||||
bld.recurse('cc121')
|
||||
bld.recurse('mackie')
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user