44 jassert (midiChannel > 0 && midiChannel <= 16);
53 for (
auto& state : states)
55 state.parameterMSB = 0xff;
56 state.parameterLSB = 0xff;
69 case 0x62: parameterLSB =
uint8 (value); resetValue(); isNRPN =
true;
break;
70 case 0x63: parameterMSB =
uint8 (value); resetValue(); isNRPN =
true;
break;
72 case 0x64: parameterLSB =
uint8 (value); resetValue(); isNRPN =
false;
break;
73 case 0x65: parameterMSB =
uint8 (value); resetValue(); isNRPN =
false;
break;
75 case 0x06: valueMSB =
uint8 (value); valueLSB = 0xff;
return sendIfReady (channel);
76 case 0x26: valueLSB =
uint8 (value);
return sendIfReady (channel);
82void MidiRPNDetector::ChannelState::resetValue() noexcept
91 if (parameterMSB >= 0x80 || parameterLSB >= 0x80 || valueMSB >= 0x80)
94 MidiRPNMessage result{};
95 result.channel = channel;
96 result.parameterNumber = (parameterMSB << 7) + parameterLSB;
97 result.isNRPN = isNRPN;
101 result.
value = (valueMSB << 7) + valueLSB;
102 result.is14BitValue =
true;
106 result.value = valueMSB;
107 result.is14BitValue =
false;
129 jassert (midiChannel > 0 && midiChannel <= 16);
130 jassert (parameterNumber >= 0 && parameterNumber < 16384);
133 auto parameterLSB =
uint8 (parameterNumber & 0x0000007f);
134 auto parameterMSB =
uint8 (parameterNumber >> 7);
168 void runTest()
override
177 beginTest (
"Individual MSB is parsed as 7-bit");
180 expect (!
detector.tryParse (2, 101, 0));
181 expect (!
detector.tryParse (2, 100, 7));
184 expect (
parsed.has_value());
186 expectEquals (
parsed->channel, 2);
187 expectEquals (
parsed->parameterNumber, 7);
188 expectEquals (
parsed->value, 42);
189 expect (!
parsed->isNRPN);
190 expect (!
parsed->is14BitValue);
193 beginTest (
"LSB without preceding MSB is ignored");
196 expect (!
detector.tryParse (2, 101, 0));
197 expect (!
detector.tryParse (2, 100, 7));
198 expect (!
detector.tryParse (2, 38, 42));
201 beginTest (
"LSB following MSB is parsed as 14-bit");
204 expect (!
detector.tryParse (1, 101, 2));
205 expect (!
detector.tryParse (1, 100, 44));
207 expect (
detector.tryParse (1, 6, 1).has_value());
213 expectEquals (
lsbParsed->parameterNumber, 300);
219 beginTest (
"Multiple LSB following MSB re-use the MSB");
222 expect (!
detector.tryParse (1, 101, 2));
223 expect (!
detector.tryParse (1, 100, 43));
225 expect (
detector.tryParse (1, 6, 1).has_value());
227 expect (
detector.tryParse (1, 38, 94).has_value());
228 expect (
detector.tryParse (1, 38, 95).has_value());
229 expect (
detector.tryParse (1, 38, 96).has_value());
235 expectEquals (
lsbParsed->parameterNumber, 299);
241 beginTest (
"Sending a new MSB resets the LSB");
244 expect (!
detector.tryParse (1, 101, 3));
245 expect (!
detector.tryParse (1, 100, 43));
247 expect (
detector.tryParse (1, 6, 1).has_value());
248 expect (
detector.tryParse (1, 38, 94).has_value());
251 expect (
newMsb.has_value());
253 expectEquals (
newMsb->channel, 1);
254 expectEquals (
newMsb->parameterNumber, 427);
255 expectEquals (
newMsb->value, 2);
256 expect (!
newMsb->isNRPN);
257 expect (!
newMsb->is14BitValue);
260 beginTest (
"RPNs on multiple channels simultaneously");
263 expect (!
detector.tryParse (1, 100, 44));
264 expect (!
detector.tryParse (2, 101, 0));
265 expect (!
detector.tryParse (1, 101, 2));
266 expect (!
detector.tryParse (2, 100, 7));
267 expect (
detector.tryParse (1, 6, 1).has_value());
273 expectEquals (
channelTwo->parameterNumber, 7);
282 expectEquals (
channelOne->parameterNumber, 300);
288 beginTest (
"14-bit RPN with value within 7-bit range");
291 expect (!
detector.tryParse (16, 100, 0));
292 expect (!
detector.tryParse (16, 101, 0));
293 expect (
detector.tryParse (16, 6, 0).has_value());
296 expect (
parsed.has_value());
298 expectEquals (
parsed->channel, 16);
299 expectEquals (
parsed->parameterNumber, 0);
300 expectEquals (
parsed->value, 3);
301 expect (!
parsed->isNRPN);
302 expect (
parsed->is14BitValue);
305 beginTest (
"invalid RPN (wrong order)");
308 expect (!
detector.tryParse (2, 6, 42));
309 expect (!
detector.tryParse (2, 101, 0));
310 expect (!
detector.tryParse (2, 100, 7));
313 beginTest (
"14-bit RPN interspersed with unrelated CC messages");
316 expect (!
detector.tryParse (16, 3, 80));
317 expect (!
detector.tryParse (16, 100, 0));
318 expect (!
detector.tryParse (16, 4, 81));
319 expect (!
detector.tryParse (16, 101, 0));
320 expect (!
detector.tryParse (16, 5, 82));
321 expect (!
detector.tryParse (16, 5, 83));
322 expect (
detector.tryParse (16, 6, 0).has_value());
323 expect (!
detector.tryParse (16, 4, 84).has_value());
324 expect (!
detector.tryParse (16, 3, 85).has_value());
327 expect (
parsed.has_value());
329 expectEquals (
parsed->channel, 16);
330 expectEquals (
parsed->parameterNumber, 0);
331 expectEquals (
parsed->value, 3);
332 expect (!
parsed->isNRPN);
333 expect (
parsed->is14BitValue);
336 beginTest (
"14-bit NRPN");
339 expect (!
detector.tryParse (1, 98, 44));
340 expect (!
detector.tryParse (1, 99 , 2));
341 expect (
detector.tryParse (1, 6, 1).has_value());
344 expect (
parsed.has_value());
346 expectEquals (
parsed->channel, 1);
347 expectEquals (
parsed->parameterNumber, 300);
348 expectEquals (
parsed->value, 222);
350 expect (
parsed->is14BitValue);
356 expect (!
detector.tryParse (2, 101, 0));
358 expect (!
detector.tryParse (2, 100, 7));
359 expect (!
detector.tryParse (2, 6, 42));
374 void runTest()
override
376 beginTest (
"generating RPN/NRPN");
387 MidiRPNMessage message = { 16, 101, 34,
false,
false };
403 MidiRPNMessage
expected = { channel, parameterNumber, value, isNRPN, is14BitValue };
413 for (
const auto metadata : midiBuffer)
423 expectEquals (result->channel,
expected.channel);
424 expectEquals (result->parameterNumber,
expected.parameterNumber);
426 expect (result->isNRPN ==
expected.isNRPN);
427 expect (result->is14BitValue ==
expected.is14BitValue);
Holds a sequence of time-stamped midi events.
bool addEvent(const MidiMessage &midiMessage, int sampleNumber)
Adds an event to the buffer.
Encapsulates a MIDI message.
bool parseControllerMessage(int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage &result) noexcept
void reset() noexcept
Resets the RPN detector's internal state, so that it forgets about previously received MIDI CC messag...
std::optional< MidiRPNMessage > tryParse(int midiChannel, int controllerNumber, int controllerValue)
Takes the next in a stream of incoming MIDI CC messages and returns a MidiRPNMessage if the current m...
static MidiBuffer generate(MidiRPNMessage message)
Generates a MIDI sequence representing the given RPN or NRPN message.
This is a base class for classes that perform a unit test.
bool isNRPN
True if this message is an NRPN; false if it is an RPN.
bool is14BitValue
True if the value uses 14-bit resolution (LSB + MSB); false if the value is 7-bit (MSB only).
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
int channel
Midi channel of the message, in the range 1 to 16.
int parameterNumber
The 14-bit parameter index, in the range 0 to 16383 (0x3fff).
int value
The parameter value, in the range 0 to 16383 (0x3fff).
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
Represents a MIDI RPN (registered parameter number) or NRPN (non-registered parameter number) message...