/* * Copyright (C) 2016-2018 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_push2_h__ #define __ardour_push2_h__ #include #include #include #include #include #include #define ABSTRACT_UI_EXPORTS #include "pbd/abstract_ui.h" #include "midi++/types.h" #include "ardour/mode.h" #include "ardour/types.h" #include "control_protocol/control_protocol.h" #include "control_protocol/types.h" #include "gtkmm2ext/colors.h" #include "midi_surface/midi_byte_array.h" #include "midi_surface/midi_surface.h" namespace MIDI { class Parser; class Port; } namespace ARDOUR { class Port; class MidiBuffer; class MidiTrack; } namespace ArdourSurface { class P2GUI; class Push2Layout; class Push2Canvas; class Push2 : public MIDISurface { public: enum ButtonID { TapTempo, Metronome, Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8, Setup, User, Delete, AddDevice, Device, Mix, Undo, AddTrack, Browse, Clip, Mute, Solo, Stop, Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, Master, Convert, DoubleLoop, Quantize, Duplicate, New, FixedLength, Automate, RecordEnable, Play, Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr, Up, Right, Down, Left, Repeat, Accent, Scale, Layout, Note, Session, OctaveUp, PageRight, OctaveDown, PageLeft, Shift, Select }; struct LED { enum State { NoTransition, OneShot24th, OneShot16th, OneShot8th, OneShot4th, OneShot2th, Pulsing24th, Pulsing16th, Pulsing8th, Pulsing4th, Pulsing2th, Blinking24th, Blinking16th, Blinking8th, Blinking4th, Blinking2th }; enum Colors { Black = 0, Red = 127, Green = 126, Blue = 125, DarkGray = 124, LightGray = 123, White = 122 }; LED (uint8_t e) : _extra (e), _color_index (Black), _state (NoTransition) {} virtual ~LED() {} uint8_t extra () const { return _extra; } uint8_t color_index () const { return _color_index; } State state () const { return _state; } void set_color (uint8_t color_index); void set_state (State state); virtual MidiByteArray state_msg() const = 0; protected: uint8_t _extra; uint8_t _color_index; State _state; }; struct Pad : public LED { enum WhenPressed { Nothing, FlashOn, FlashOff, }; Pad (int xx, int yy, uint8_t ex) : LED (ex) , x (xx) , y (yy) , do_when_pressed (FlashOn) , filtered (ex) , perma_color (LED::Black) {} MidiByteArray state_msg () const { return MidiByteArray (3, 0x90|_state, _extra, _color_index); } int coord () const { return (y * 8) + x; } int note_number() const { return extra(); } int x; int y; int do_when_pressed; int filtered; int perma_color; }; struct Button : public LED { Button (ButtonID bb, uint8_t ex) : LED (ex) , id (bb) , press_method (&Push2::relax) , release_method (&Push2::relax) , long_press_method (&Push2::relax) {} Button (ButtonID bb, uint8_t ex, void (Push2::*press)()) : LED (ex) , id (bb) , press_method (press) , release_method (&Push2::relax) , long_press_method (&Push2::relax) {} Button (ButtonID bb, uint8_t ex, void (Push2::*press)(), void (Push2::*release)()) : LED (ex) , id (bb) , press_method (press) , release_method (release) , long_press_method (&Push2::relax) {} Button (ButtonID bb, uint8_t ex, void (Push2::*press)(), void (Push2::*release)(), void (Push2::*long_press)()) : LED (ex) , id (bb) , press_method (press) , release_method (release) , long_press_method (long_press) {} MidiByteArray state_msg () const { return MidiByteArray (3, 0xb0|_state, _extra, _color_index); } int controller_number() const { return extra(); } ButtonID id; void (Push2::*press_method)(); void (Push2::*release_method)(); void (Push2::*long_press_method)(); sigc::connection timeout_connection; }; struct ColorButton : public Button { ColorButton (ButtonID bb, uint8_t ex) : Button (bb, ex) {} ColorButton (ButtonID bb, uint8_t ex, void (Push2::*press)()) : Button (bb, ex, press) {} ColorButton (ButtonID bb, uint8_t ex, void (Push2::*press)(), void (Push2::*release)()) : Button (bb, ex, press, release) {} ColorButton (ButtonID bb, uint8_t ex, void (Push2::*press)(), void (Push2::*release)(), void (Push2::*long_press)()) : Button (bb, ex, press, release, long_press) {} }; struct WhiteButton : public Button { WhiteButton (ButtonID bb, uint8_t ex) : Button (bb, ex) {} WhiteButton (ButtonID bb, uint8_t ex, void (Push2::*press)()) : Button (bb, ex, press) {} WhiteButton (ButtonID bb, uint8_t ex, void (Push2::*press)(), void (Push2::*release)()) : Button (bb, ex, press, release) {} WhiteButton (ButtonID bb, uint8_t ex, void (Push2::*press)(), void (Push2::*release)(), void (Push2::*long_press)()) : Button (bb, ex, press, release, long_press) {} }; enum ColorName { DarkBackground, LightBackground, ParameterName, StripableName, ClockText, KnobArcBackground, KnobArcStart, KnobArcEnd, KnobLine, KnobLineShadow, KnobForeground, KnobBackground, KnobShadow, KnobBorder, }; enum PressureMode { AfterTouch, PolyPressure, }; public: Push2 (ARDOUR::Session&); ~Push2 (); static bool available (); static bool match_usb (uint16_t, uint16_t); static bool probe (std::string&, std::string&); std::string input_port_name () const; std::string output_port_name () const; bool has_editor () const { return true; } void* get_gui () const; void tear_down_gui (); int set_active (bool yn); XMLNode& get_state() const; int set_state (const XMLNode & node, int version); int pad_note (int row, int col) const; PBD::Signal0 PadChange; void update_selection_color (); /** The "origin" or "root" of the note grid. * * This controls whether the grid is "fixed" in terms of the notes that it * plays (so changing the scale is effectively just an overlay), or * "rooted" so the root note of the scale is in the bottom left. */ enum NoteGridOrigin { Fixed, ///< Bottom left pad is always C, or as close as possible Rooted, ///< Bottom left pad is the scale root }; /** Interval between vertically adjacent note pads ("layout"). * * The comments describe the ideal interval that is used in chromatic mode. * For in-scale mode, they may be slightly adjusted, hence the more general * enumerator names. */ enum RowInterval { Third, /// Major third or 4 semitones Fourth, /// Perfect fourth or 5 semitones Fifth, /// Perfect fifth or 7 semitones Sequential, /// Sequential from the last row, or 8 semitones }; /// "Kind" of pad that plays a note enum PadNoteKind { RootNote, InScaleNote, OutOfScaleNote }; /// Set up a pad to represent a "kind" of note void set_pad_note_kind(Pad& pad, PadNoteKind kind); /** Set an "in-key" scale on the pads. * * "In-key" mode shows only notes which are in the scale, so every pad is * in the scale and there are no "spaces". This provides access to a wide * range of notes in the scale, but no access to notes outside the scale at * all. * * * @param root The root note in the lowest octave (at most 11). * * @param octave The octave number of the "actual" root (at most 10). * * @param mode The active musical mode (scale). * * @param ideal_vertical_semitones The ideal interval between rows in * semitones. This is an "ideal" because it may not be possible to use * exactly this interval for every row depending on the scale. It may be * bumped up to the next note in the scale if necessary, so with this mode, * rows are not guaranteed to all have the same vertical interval. */ void set_pad_scale_in_key (int root, int octave, MusicalMode::Type mode, NoteGridOrigin origin, int ideal_vertical_semitones); /** Set a "chromatic" scale on the pads. * * "Chromatic" mode is chromatic from left to right, and "tuned" to some * interval from bottom up, like a stringed instrument. The default * vertical interval is 5 semitones, or a perfect 4th, like strings on a * bass guitar. * * @param root The root note in the lowest octave (at most 11). * * @param octave The octave number of the "actual" root (at most 10). * * @param mode The active musical mode (scale). * * @param vertical_semitones The interval between rows in semitones. This * mode guarantees that the vertical interval for all rows is always * exactly this. */ void set_pad_scale_chromatic (int root, int octave, MusicalMode::Type mode, NoteGridOrigin origin, int vertical_semitones); void set_pad_scale (int root, int octave, MusicalMode::Type mode, NoteGridOrigin origin, RowInterval row_interval, bool inkey); PBD::Signal0 ScaleChange; MusicalMode::Type mode() const { return _mode; } NoteGridOrigin note_grid_origin() { return _note_grid_origin; } RowInterval row_interval() const { return _row_interval; } int scale_root() const { return _scale_root; } int root_octave() const { return _root_octave; } bool in_key() const { return _in_key; } Push2Layout* current_layout() const; void use_previous_layout (); Push2Canvas* canvas() const { return _canvas; } enum ModifierState { None = 0, ModShift = 0x1, ModSelect = 0x2, }; ModifierState modifier_state() const { return _modifier_state; } std::shared_ptr