Control Surface new-input
MIDI Control Surface library for Arduino
MIDI_Callbacks.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <AH/Containers/CRTP.hpp>
5 
7 
8 class MIDI_Interface;
9 
10 // LCOV_EXCL_START
11 
16  public:
26 
28  virtual ~MIDI_Callbacks() = default;
29 };
30 
31 // LCOV_EXCL_STOP
32 
33 template <class Derived>
35  protected:
39  // clang-format off
40  void onNoteOff(Channel channel, uint8_t note, uint8_t velocity, Cable cable);
41  void onNoteOn(Channel channel, uint8_t note, uint8_t velocity, Cable cable);
42  void onKeyPressure(Channel channel, uint8_t note, uint8_t pressure, Cable cable);
43  void onControlChange(Channel channel, uint8_t controller, uint8_t value, Cable cable);
44  void onProgramChange(Channel channel, uint8_t program, Cable cable);
45  void onChannelPressure(Channel channel, uint8_t pressure, Cable cable);
46  void onPitchBend(Channel channel, uint16_t bend, Cable cable);
47  void onSystemExclusive(SysExMessage message);
48  void onTimeCodeQuarterFrame(uint8_t data, Cable cable);
49  void onSongPosition(uint16_t beats, Cable cable);
50  void onSongSelect(uint8_t songnumber, Cable cable);
51  void onTuneRequest(Cable cable);
52  void onClock(Cable cable);
53  void onStart(Cable cable);
54  void onContinue(Cable cable);
55  void onStop(Cable cable);
56  void onActiveSensing(Cable cable);
57  void onSystemReset(Cable cable);
58  // clang-format on
60 
62  using MMT = MIDIMessageType;
63  switch (msg.getMessageType()) {
64  case MMT::NONE: break;
65  case MMT::NOTE_OFF:
66  CRTP(Derived).onNoteOff(msg.getChannel(), msg.getData1(),
67  msg.getData2(), msg.getCable());
68  break;
69  case MMT::NOTE_ON:
70  CRTP(Derived).onNoteOn(msg.getChannel(), msg.getData1(),
71  msg.getData2(), msg.getCable());
72  break;
73  case MMT::KEY_PRESSURE:
74  CRTP(Derived).onKeyPressure(msg.getChannel(), msg.getData1(),
75  msg.getData2(), msg.getCable());
76  break;
77  case MMT::CONTROL_CHANGE:
78  CRTP(Derived).onControlChange(msg.getChannel(), msg.getData1(),
79  msg.getData2(), msg.getCable());
80  break;
81  case MMT::PROGRAM_CHANGE:
82  CRTP(Derived).onProgramChange(msg.getChannel(), msg.getData1(),
83  msg.getCable());
84  break;
85  case MMT::CHANNEL_PRESSURE:
86  CRTP(Derived).onChannelPressure(msg.getChannel(),
87  msg.getData1(), msg.getCable());
88  break;
89  case MMT::PITCH_BEND:
90  CRTP(Derived).onPitchBend(msg.getChannel(), msg.getData14bit(),
91  msg.getCable());
92  break;
93  case MMT::SYSEX_START:
94  case MMT::MTC_QUARTER_FRAME:
95  case MMT::SONG_POSITION_POINTER:
96  case MMT::SONG_SELECT:
97  case MMT::UNDEFINED_SYSCOMMON_1:
98  case MMT::UNDEFINED_SYSCOMMON_2:
99  case MMT::TUNE_REQUEST:
100  case MMT::SYSEX_END:
101  case MMT::TIMING_CLOCK:
102  case MMT::UNDEFINED_REALTIME_1:
103  case MMT::START:
104  case MMT::CONTINUE:
105  case MMT::STOP:
106  case MMT::UNDEFINED_REALTIME_2:
107  case MMT::ACTIVE_SENSING:
108  case MMT::SYSTEM_RESET:
109  default: break;
110  }
111  }
112 
114  CRTP(Derived).onSystemExclusive(msg);
115  }
116 
118  using MMT = MIDIMessageType;
119  switch (msg.getMessageType()) {
120  case MMT::NONE: break;
121  case MMT::NOTE_OFF:
122  case MMT::NOTE_ON:
123  case MMT::KEY_PRESSURE:
124  case MMT::CONTROL_CHANGE:
125  case MMT::PROGRAM_CHANGE:
126  case MMT::CHANNEL_PRESSURE:
127  case MMT::PITCH_BEND:
128  case MMT::SYSEX_START: break;
129  case MMT::MTC_QUARTER_FRAME:
130  CRTP(Derived).onTimeCodeQuarterFrame(msg.getData1(),
131  msg.getCable());
132  break;
133  case MMT::SONG_POSITION_POINTER:
134  CRTP(Derived).onSongPosition(msg.getData14bit(),
135  msg.getCable());
136  break;
137  case MMT::SONG_SELECT:
138  CRTP(Derived).onSongSelect(msg.getData1(), msg.getCable());
139  break;
140  case MMT::UNDEFINED_SYSCOMMON_1: break;
141  case MMT::UNDEFINED_SYSCOMMON_2: break;
142  case MMT::TUNE_REQUEST:
143  CRTP(Derived).onTuneRequest(msg.getCable());
144  break;
145  case MMT::SYSEX_END:
146  case MMT::TIMING_CLOCK:
147  case MMT::UNDEFINED_REALTIME_1:
148  case MMT::START:
149  case MMT::CONTINUE:
150  case MMT::STOP:
151  case MMT::UNDEFINED_REALTIME_2:
152  case MMT::ACTIVE_SENSING:
153  case MMT::SYSTEM_RESET:
154  default: break;
155  }
156  }
157 
159  using MMT = MIDIMessageType;
160  switch (msg.getMessageType()) {
161  case MMT::NONE: break;
162  case MMT::NOTE_OFF:
163  case MMT::NOTE_ON:
164  case MMT::KEY_PRESSURE:
165  case MMT::CONTROL_CHANGE:
166  case MMT::PROGRAM_CHANGE:
167  case MMT::CHANNEL_PRESSURE:
168  case MMT::PITCH_BEND:
169  case MMT::SYSEX_START:
170  case MMT::MTC_QUARTER_FRAME:
171  case MMT::SONG_POSITION_POINTER:
172  case MMT::SONG_SELECT:
173  case MMT::UNDEFINED_SYSCOMMON_1:
174  case MMT::UNDEFINED_SYSCOMMON_2:
175  case MMT::TUNE_REQUEST:
176  case MMT::SYSEX_END: break;
177  case MMT::TIMING_CLOCK:
178  CRTP(Derived).onClock(msg.getCable());
179  break;
180  case MMT::UNDEFINED_REALTIME_1: break;
181  case MMT::START: CRTP(Derived).onStart(msg.getCable()); break;
182  case MMT::CONTINUE: CRTP(Derived).onContinue(msg.getCable()); break;
183  case MMT::STOP: CRTP(Derived).onStop(msg.getCable()); break;
184  case MMT::UNDEFINED_REALTIME_2: break;
185  case MMT::ACTIVE_SENSING:
186  CRTP(Derived).onActiveSensing(msg.getCable());
187  break;
188  case MMT::SYSTEM_RESET: CRTP(Derived).onSystemReset(msg.getCable()); break;
189  default: break;
190  }
191  }
192 
194 
195  template <class...>
196  struct Dummy {};
197 
198  template <class T1, class R1, class... Args1, //
199  class T2, class R2, class... Args2>
200  static constexpr bool same_return_type_and_arguments(R1 (T1::*)(Args1...),
201  R2 (T2::*)(Args2...)) {
202  return std::is_same<Dummy<R1, Args1...>, Dummy<R2, Args2...>>::value;
203  }
204 
205  public:
207  // clang-format off
208  static_assert(std::is_base_of<FineGrainedMIDI_Callbacks, Derived>::value, "Invalid CRTP");
209  static_assert(same_return_type_and_arguments(&Derived::onNoteOff, &FineGrainedMIDI_Callbacks::onNoteOff), "Incorrect signature for onNoteOff");
210  static_assert(same_return_type_and_arguments(&Derived::onNoteOn, &FineGrainedMIDI_Callbacks::onNoteOn), "Incorrect signature for onNoteOn");
211  static_assert(same_return_type_and_arguments(&Derived::onKeyPressure, &FineGrainedMIDI_Callbacks::onKeyPressure), "Incorrect signature for onKeyPressure");
212  static_assert(same_return_type_and_arguments(&Derived::onControlChange, &FineGrainedMIDI_Callbacks::onControlChange), "Incorrect signature for onControlChange");
213  static_assert(same_return_type_and_arguments(&Derived::onProgramChange, &FineGrainedMIDI_Callbacks::onProgramChange), "Incorrect signature for onProgramChange");
214  static_assert(same_return_type_and_arguments(&Derived::onChannelPressure, &FineGrainedMIDI_Callbacks::onChannelPressure), "Incorrect signature for onChannelPressure");
215  static_assert(same_return_type_and_arguments(&Derived::onPitchBend, &FineGrainedMIDI_Callbacks::onPitchBend), "Incorrect signature for onPitchBend");
216  static_assert(same_return_type_and_arguments(&Derived::onSystemExclusive, &FineGrainedMIDI_Callbacks::onSystemExclusive), "Incorrect signature for onSystemExclusive");
217  static_assert(same_return_type_and_arguments(&Derived::onTimeCodeQuarterFrame, &FineGrainedMIDI_Callbacks::onTimeCodeQuarterFrame), "Incorrect signature for onTimeCodeQuarterFrame");
218  static_assert(same_return_type_and_arguments(&Derived::onSongPosition, &FineGrainedMIDI_Callbacks::onSongPosition), "Incorrect signature for onSongPosition");
219  static_assert(same_return_type_and_arguments(&Derived::onSongSelect, &FineGrainedMIDI_Callbacks::onSongSelect), "Incorrect signature for onSongSelect");
220  static_assert(same_return_type_and_arguments(&Derived::onTuneRequest, &FineGrainedMIDI_Callbacks::onTuneRequest), "Incorrect signature for onTuneRequest");
221  static_assert(same_return_type_and_arguments(&Derived::onClock, &FineGrainedMIDI_Callbacks::onClock), "Incorrect signature for onClock");
222  static_assert(same_return_type_and_arguments(&Derived::onStart, &FineGrainedMIDI_Callbacks::onStart), "Incorrect signature for onStart");
223  static_assert(same_return_type_and_arguments(&Derived::onContinue, &FineGrainedMIDI_Callbacks::onContinue), "Incorrect signature for onContinue");
224  static_assert(same_return_type_and_arguments(&Derived::onStop, &FineGrainedMIDI_Callbacks::onStop), "Incorrect signature for onStop");
225  static_assert(same_return_type_and_arguments(&Derived::onActiveSensing, &FineGrainedMIDI_Callbacks::onActiveSensing), "Incorrect signature for onActiveSensing");
226  static_assert(same_return_type_and_arguments(&Derived::onSystemReset, &FineGrainedMIDI_Callbacks::onSystemReset), "Incorrect signature for onSystemReset");
227  // clang-format on
228  }
229 
231 };
232 
233 // clang-format off
234 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onNoteOff(Channel, uint8_t, uint8_t, Cable) {}
235 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onNoteOn(Channel, uint8_t, uint8_t, Cable) {}
236 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onKeyPressure(Channel, uint8_t, uint8_t, Cable) {}
237 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onControlChange(Channel, uint8_t, uint8_t, Cable) {}
238 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onProgramChange(Channel, uint8_t, Cable) {}
239 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onChannelPressure(Channel, uint8_t, Cable) {}
240 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onPitchBend(Channel, uint16_t, Cable) {}
242 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onTimeCodeQuarterFrame(uint8_t,Cable) {}
243 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onSongPosition(uint16_t,Cable) {}
244 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onSongSelect(uint8_t,Cable) {}
245 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onTuneRequest(Cable) {}
246 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onClock(Cable) {}
247 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onStart(Cable) {}
248 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onContinue(Cable) {}
249 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onStop(Cable) {}
250 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onActiveSensing(Cable) {}
251 template <class Derived> inline void FineGrainedMIDI_Callbacks<Derived>::onSystemReset(Cable) {}
252 // clang-format on
253 
#define CRTP(Derived)
Helper for the Curiously Recurring Template Pattern.
Definition: CRTP.hpp:4
MIDIMessageType
All possible MIDI status byte values (without channel).
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
A type-safe class for MIDI USB Cable numbers.
Definition: Cable.hpp:13
A type-safe class for MIDI channels.
Definition: Channel.hpp:13
void onChannelMessage(MIDI_Interface &, ChannelMessage msg) override
Callback for incoming MIDI Channel Messages (notes, control change, pitch bend, etc....
void onTimeCodeQuarterFrame(uint8_t data, Cable cable)
void onPitchBend(Channel channel, uint16_t bend, Cable cable)
void onSysCommonMessage(MIDI_Interface &, SysCommonMessage msg) override
Callback for incoming MIDI System Common Messages.
void onSystemReset(Cable cable)
void onContinue(Cable cable)
void onSystemExclusive(SysExMessage message)
void onActiveSensing(Cable cable)
void onProgramChange(Channel channel, uint8_t program, Cable cable)
void onNoteOff(Channel channel, uint8_t note, uint8_t velocity, Cable cable)
void onRealTimeMessage(MIDI_Interface &, RealTimeMessage msg) override
Callback for incoming MIDI Real-Time Messages.
void onNoteOn(Channel channel, uint8_t note, uint8_t velocity, Cable cable)
void onControlChange(Channel channel, uint8_t controller, uint8_t value, Cable cable)
void onSongPosition(uint16_t beats, Cable cable)
void onTuneRequest(Cable cable)
void onSongSelect(uint8_t songnumber, Cable cable)
void onKeyPressure(Channel channel, uint8_t note, uint8_t pressure, Cable cable)
void onSysExMessage(MIDI_Interface &, SysExMessage msg) override
Callback for incoming MIDI System Exclusive Messages.
void onChannelPressure(Channel channel, uint8_t pressure, Cable cable)
A class for callbacks from MIDI input.
virtual void onChannelMessage(MIDI_Interface &, ChannelMessage)
Callback for incoming MIDI Channel Messages (notes, control change, pitch bend, etc....
virtual ~MIDI_Callbacks()=default
Destructor.
virtual void onSysExMessage(MIDI_Interface &, SysExMessage)
Callback for incoming MIDI System Exclusive Messages.
virtual void onRealTimeMessage(MIDI_Interface &, RealTimeMessage)
Callback for incoming MIDI Real-Time Messages.
virtual void onSysCommonMessage(MIDI_Interface &, SysCommonMessage)
Callback for incoming MIDI System Common Messages.
An abstract class for MIDI interfaces.
constexpr int8_t note(Note note, int8_t numOctave)
Get the MIDI note in the given octave.
Definition: Notes.hpp:56
MIDIMessageType getMessageType() const
Get the MIDI message type.
Channel getChannel() const
Get the MIDI channel of the message.
Cable getCable() const
Get the MIDI USB cable number of the message.
uint8_t getData1() const
Get the first data byte.
uint16_t getData14bit() const
If Data 1 and Data 2 represent a single 14-bit number, you can use this method to retrieve that numbe...
uint8_t getData2() const
Get the second data byte.
Cable getCable() const
Get the MIDI USB cable number of the message.
MIDIMessageType getMessageType() const
Get the MIDI message type.
MIDIMessageType getMessageType() const
Get the MIDI message type.