/* * Copyright (C) 2017-2018 Robin Gareus * * 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::FP_NAMESPACE; using namespace ArdourSurface::FP_NAMESPACE::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) #ifdef FADERPORT2 , _navmode (NavScroll) #else , _navmode (NavMaster) #endif , _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); #ifdef FADERPORT2 NEWSHIFTBUTTON (0x4a, BtnARead, BtnAOff, true); NEWSHIFTBUTTON (0x4b, BtnAWrite, BtnATrim, true); NEWSHIFTBUTTON (0x4d, BtnATouch, BtnALatch, true); NEWSHIFTBUTTON (0x2e, BtnPrev, BtnUndo, false); NEWSHIFTBUTTON (0x2f, BtnNext, BtnRedo, false); NEWSHIFTBUTTON (0x2a, BtnPan, BtnFlip, true); //TODO: Flip Pan knob to fader ...? NEWSHIFTBUTTON (0x36, BtnChannel, BtnChanLock, true); NEWSHIFTBUTTON (0x38, BtnScroll, BtnZoom, true); NEWSHIFTBUTTON (0x3a, BtnMaster, BtnF1, false); NEWSHIFTBUTTON (0x3b, BtnClick, BtnF2, false); NEWSHIFTBUTTON (0x3c, BtnSection, BtnF3, false); NEWSHIFTBUTTON (0x3d, BtnMarker, BtnF4, false); //these buttons do not exist in FP2, but they need to exist in the ctrlmap: NEWBUTTON (0x71, BtnBank, false); NEWBUTTON (0x72, BtnF5, false); NEWBUTTON (0x73, BtnF6, false); NEWBUTTON (0x74, BtnF7, false); NEWBUTTON (0x75, BtnF8, false); NEWBUTTON (0x76, BtnUser1, false); NEWBUTTON (0x77, BtnUser2, false); NEWBUTTON (0x78, BtnUser3, false); NEWBUTTON (0x79, BtnSave, false); #else 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); NEWBUTTON (0x2a, BtnPan, false); #endif NEWSHIFTBUTTON (0x28, BtnTrack, BtnTimecode, false); NEWBUTTON (0x2b, BtnPlugins, false); NEWBUTTON (0x29, BtnSend, 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, std::bind (&FP8Controls:: CB, this)); BindMethod (FP8Controls::BtnTimecode, toggle_timecode); #define BindNav(BTN, MODE)\ button (BTN).released.connect_same_thread (button_connections, std::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); #ifdef FADERPORT2 BindNav (BtnPan, NavPan); #endif #define BindFader(BTN, MODE)\ button (BTN).released.connect_same_thread (button_connections, std::bind (&FP8Controls::set_fader_mode, this, MODE)) BindFader (BtnTrack, ModeTrack); BindFader (BtnPlugins, ModePlugins); BindFader (BtnSend, ModeSend); #ifndef FADERPORT2 BindFader (BtnPan, ModePan); #endif #define BindMix(BTN, MODE)\ button (BTN).released.connect_same_thread (button_connections, std::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 < N_STRIPS; ++id) { chanstrip[id] = new FP8Strip (b, id); _midimap_strip[FP8Strip::midi_ctrl_id (FP8Strip::BtnSolo, id)] = &(chanstrip[id]->solo_button()); _midimap_strip[FP8Strip::midi_ctrl_id (FP8Strip::BtnMute, id)] = &(chanstrip[id]->mute_button()); _midimap_strip[FP8Strip::midi_ctrl_id (FP8Strip::BtnSelect, 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; #ifdef FADERPORT2 REGISTER_ENUM (BtnF1 , "F1"); REGISTER_ENUM (BtnF2 , "F2"); REGISTER_ENUM (BtnF3 , "F3"); REGISTER_ENUM (BtnF4 , "F4"); REGISTER_ENUM (BtnFootswitch, "Footswitch"); #else 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"); #endif #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 < N_STRIPS; ++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::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::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 (BtnALatch).set_color (0xffff00ff); button (BtnUser1).set_color (0x0000ffff); button (BtnUser2).set_color (0x0000ffff); button (BtnUser3).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); #ifdef FADERPORT2 /* encoder mode-switches are orange, to match the Master switch physical color */ button (BtnLink).set_color (0x000000ff); button (BtnChannel).set_color (0x0000ffff); button (BtnScroll).set_color (0x0000ffff); button (BtnPan).set_color (0xffffffff); #endif for (uint8_t id = 0; id < N_STRIPS; ++id) { chanstrip[id]->initialize (); } /* initally turn all lights off */ all_lights_off (); /* default modes */ #ifdef FADERPORT2 button (BtnScroll).set_active (true); #else button (BtnMaster).set_active (true); #endif button (BtnTrack).set_active (true); button (BtnMAll).set_active (true); button (BtnTimecode).set_active (_display_timecode); FP8ButtonInterface::force_change = false; } void FP8Controls::all_lights_off () const { for (CtrlButtonMap::const_iterator i = _ctrlmap.begin (); i != _ctrlmap.end (); ++i) { i->second->set_active (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 < N_STRIPS); 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 < N_STRIPS); return chanstrip[id]->midi_touch (val > 0x40); } bool FP8Controls::midi_fader (uint8_t id, unsigned short val) { assert (id < N_STRIPS); 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); #ifdef FADERPORT2 button (BtnPan).set_active (m == NavPan); #endif _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); }