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 80 : 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
|