Line data Source code
1 : #pragma once 2 : 3 : #include <Def/MIDIAddress.hpp> 4 : #include <MIDI_Parsers/MIDI_MessageTypes.hpp> 5 : 6 : #include <Banks/Bank.hpp> // Bank<N>, BankSettingChangeCallback 7 : 8 : #include <AH/Containers/Updatable.hpp> 9 : #include <AH/STL/type_traits> 10 : 11 : BEGIN_CS_NAMESPACE 12 : 13 : // -------------------------------------------------------------------------- // 14 : 15 : /** 16 : * @brief A class for objects that listen for incoming MIDI events. 17 : * 18 : * They can either update some kind of display, or they can just save the state. 19 : */ 20 : template <MIDIMessageType Type> 21 : class MIDIInputElement : public AH::UpdatableCRTP<MIDIInputElement<Type>> { 22 : protected: 23 64 : MIDIInputElement() = default; 24 : 25 : public: 26 64 : virtual ~MIDIInputElement() = default; 27 : 28 : public: 29 : using MessageType = 30 : typename std::conditional<Type == MIDIMessageType::SysExStart, 31 : SysExMessage, ChannelMessage>::type; 32 : 33 : /// Initialize the input element. 34 : virtual void begin() {} // LCOV_EXCL_LINE 35 : 36 : /// Reset the input element to its initial state. 37 : virtual void reset() {} // LCOV_EXCL_LINE 38 : 39 : /// Update the value of the input element. Used for decaying VU meters etc. 40 : virtual void update() {} // LCOV_EXCL_LINE 41 : 42 : /// Receive a new MIDI message and update the internal state. 43 : virtual bool updateWith(MessageType midimsg) = 0; 44 : 45 : /// Update all 46 72 : static bool updateAllWith(MessageType midimsg) { 47 152 : for (auto &el : MIDIInputElement::updatables) { 48 75 : if (el.updateWith(midimsg)) { 49 67 : el.moveDown(); 50 67 : return true; 51 : } 52 : } 53 5 : return false; 54 : } 55 : 56 : /// Update all 57 1 : static void updateAll() { 58 1 : MIDIInputElement::applyToAll(&MIDIInputElement::update); 59 1 : } 60 : 61 : /// Begin all 62 6 : static void beginAll() { 63 6 : MIDIInputElement::applyToAll(&MIDIInputElement::begin); 64 6 : } 65 : 66 : /// Reset all 67 3 : static void resetAll() { 68 3 : MIDIInputElement::applyToAll(&MIDIInputElement::reset); 69 3 : } 70 : }; 71 : 72 : // -------------------------------------------------------------------------- // 73 : 74 : /// The @ref MIDIInputElement base class is very general: you give it a MIDI 75 : /// message, and it calls the `updateWith()` method with that message. Each 76 : /// instance must then determine whether the message is meant for them or not. 77 : /// This is often a very repetitive task, so that logic is best isolated in a 78 : /// so-called “Matcher”. The Matcher looks at the MIDI message, checks if it 79 : /// matches its MIDI address, for example, and if so, it extracts some data 80 : /// (such as the MIDI velocity value from the message). Then it returns whether 81 : /// it matched and the extra data as a “Matcher::Result” object. If the message 82 : /// matched, that Result object is passed to the 83 : /// @ref MatchingMIDIInputElement::handleUpdate() method, so it can be handled 84 : /// there. 85 : /// 86 : /// @todo Pass the MIDI message to the @ref handleUpdate() method. 87 : template <MIDIMessageType Type, class Matcher> 88 : class MatchingMIDIInputElement : public MIDIInputElement<Type> { 89 : protected: 90 51 : MatchingMIDIInputElement(const Matcher &matcher) : matcher(matcher) {} 91 : 92 : public: 93 : using MessageType = typename MIDIInputElement<Type>::MessageType; 94 : 95 176 : bool updateWith(MessageType midimsg) override { 96 176 : auto match = matcher(midimsg); 97 176 : if (match.match) 98 154 : handleUpdate(match); 99 176 : return match.match; 100 : } 101 : 102 : virtual void handleUpdate(typename Matcher::Result match) = 0; 103 : 104 : protected: 105 : Matcher matcher; 106 : }; 107 : 108 : // -------------------------------------------------------------------------- // 109 : 110 : /// Similar to @ref MatchingMIDIInputElement, but for Bankable MIDI Input 111 : /// Elements. 112 : template <MIDIMessageType Type, class Matcher> 113 : class BankableMatchingMIDIInputElement 114 : : public MatchingMIDIInputElement<Type, Matcher>, 115 : public BankSettingChangeCallback { 116 : friend class Bank<Matcher::getBankSize()>; 117 : 118 : protected: 119 : /// Create a new BankableMatchingMIDIInputElement object, and add it to the 120 : /// bank. 121 17 : BankableMatchingMIDIInputElement(const Matcher &matcher) 122 17 : : MatchingMIDIInputElement<Type, Matcher>(matcher) { 123 17 : this->matcher.getBank().add(this); 124 17 : } 125 : 126 272 : uint8_t getActiveBank() const { return this->matcher.getSelection(); } 127 : 128 : public: 129 : /// Destructor: remove element from the bank. 130 17 : virtual ~BankableMatchingMIDIInputElement() { 131 17 : this->matcher.getBank().remove(this); 132 34 : } 133 : }; 134 : 135 : // -------------------------------------------------------------------------- // 136 : 137 : /// MIDI Input Element that listens for MIDI Note On/Off messages. 138 : using MIDIInputElementNote = MIDIInputElement<MIDIMessageType::NoteOn>; 139 : /// MIDI Input Element that listens for MIDI Key Pressure messages. 140 : using MIDIInputElementKP = MIDIInputElement<MIDIMessageType::KeyPressure>; 141 : /// MIDI Input Element that listens for MIDI Control Change messages. 142 : using MIDIInputElementCC = MIDIInputElement<MIDIMessageType::ControlChange>; 143 : /// MIDI Input Element that listens for MIDI Program Change messages. 144 : using MIDIInputElementPC = MIDIInputElement<MIDIMessageType::ProgramChange>; 145 : /// MIDI Input Element that listens for MIDI Channel Pressure messages. 146 : using MIDIInputElementCP = MIDIInputElement<MIDIMessageType::ChannelPressure>; 147 : /// MIDI Input Element that listens for MIDI Pitch Bend messages. 148 : using MIDIInputElementPB = MIDIInputElement<MIDIMessageType::PitchBend>; 149 : /// MIDI Input Element that listens for MIDI System Exclusive messages. 150 : using MIDIInputElementSysEx = MIDIInputElement<MIDIMessageType::SysExStart>; 151 : 152 : END_CS_NAMESPACE