#pragma once #include #include #include // Bank, BankSettingChangeCallback #include #include BEGIN_CS_NAMESPACE // -------------------------------------------------------------------------- // /** * @brief A class for objects that listen for incoming MIDI events. * * They can either update some kind of display, or they can just save the state. */ template class MIDIInputElement : public AH::UpdatableCRTP> { protected: MIDIInputElement() = default; public: virtual ~MIDIInputElement() = default; public: using MessageType = typename std::conditional::type; /// Initialize the input element. virtual void begin() {} // LCOV_EXCL_LINE /// Reset the input element to its initial state. virtual void reset() {} // LCOV_EXCL_LINE /// Update the value of the input element. Used for decaying VU meters etc. virtual void update() {} // LCOV_EXCL_LINE /// Receive a new MIDI message and update the internal state. virtual bool updateWith(MessageType midimsg) = 0; /// Update all static bool updateAllWith(MessageType midimsg) { for (auto &el : MIDIInputElement::updatables) { if (el.updateWith(midimsg)) { el.moveDown(); return true; } } return false; } /// Update all static void updateAll() { MIDIInputElement::applyToAll(&MIDIInputElement::update); } /// Begin all static void beginAll() { MIDIInputElement::applyToAll(&MIDIInputElement::begin); } /// Reset all static void resetAll() { MIDIInputElement::applyToAll(&MIDIInputElement::reset); } }; // -------------------------------------------------------------------------- // /// The @ref MIDIInputElement base class is very general: you give it a MIDI /// message, and it calls the `updateWith()` method with that message. Each /// instance must then determine whether the message is meant for them or not. /// This is often a very repetitive task, so that logic is best isolated in a /// so-called “Matcher”. The Matcher looks at the MIDI message, checks if it /// matches its MIDI address, for example, and if so, it extracts some data /// (such as the MIDI velocity value from the message). Then it returns whether /// it matched and the extra data as a “Matcher::Result” object. If the message /// matched, that Result object is passed to the /// @ref MatchingMIDIInputElement::handleUpdate() method, so it can be handled /// there. /// /// @todo Pass the MIDI message to the @ref handleUpdate() method. template class MatchingMIDIInputElement : public MIDIInputElement { protected: MatchingMIDIInputElement(const Matcher &matcher) : matcher(matcher) {} public: using MessageType = typename MIDIInputElement::MessageType; bool updateWith(MessageType midimsg) override { auto match = matcher(midimsg); if (match.match) handleUpdate(match); return match.match; } virtual void handleUpdate(typename Matcher::Result match) = 0; protected: Matcher matcher; }; // -------------------------------------------------------------------------- // /// Similar to @ref MatchingMIDIInputElement, but for Bankable MIDI Input /// Elements. template class BankableMatchingMIDIInputElement : public MatchingMIDIInputElement, public BankSettingChangeCallback { friend class Bank; protected: /// Create a new BankableMatchingMIDIInputElement object, and add it to the /// bank. BankableMatchingMIDIInputElement(const Matcher &matcher) : MatchingMIDIInputElement(matcher) { this->matcher.getBank().add(this); } uint8_t getActiveBank() const { return this->matcher.getSelection(); } public: /// Destructor: remove element from the bank. virtual ~BankableMatchingMIDIInputElement() { this->matcher.getBank().remove(this); } }; // -------------------------------------------------------------------------- // /// MIDI Input Element that listens for MIDI Note On/Off messages. using MIDIInputElementNote = MIDIInputElement; /// MIDI Input Element that listens for MIDI Key Pressure messages. using MIDIInputElementKP = MIDIInputElement; /// MIDI Input Element that listens for MIDI Control Change messages. using MIDIInputElementCC = MIDIInputElement; /// MIDI Input Element that listens for MIDI Program Change messages. using MIDIInputElementPC = MIDIInputElement; /// MIDI Input Element that listens for MIDI Channel Pressure messages. using MIDIInputElementCP = MIDIInputElement; /// MIDI Input Element that listens for MIDI Pitch Bend messages. using MIDIInputElementPB = MIDIInputElement; /// MIDI Input Element that listens for MIDI System Exclusive messages. using MIDIInputElementSysEx = MIDIInputElement; END_CS_NAMESPACE