31 auto parsed =
tryParse (midiChannel, controllerNumber, controllerValue);
33 if (! parsed.has_value())
44 jassert (midiChannel > 0 && midiChannel <= 16);
45 jassert (controllerNumber >= 0 && controllerNumber < 128);
46 jassert (controllerValue >= 0 && controllerValue < 128);
48 return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue);
53 for (
auto& state : states)
55 state.parameterMSB = 0xff;
56 state.parameterLSB = 0xff;
63std::optional<MidiRPNMessage> MidiRPNDetector::ChannelState::handleController (
int channel,
67 switch (controllerNumber)
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
89std::optional<MidiRPNMessage> MidiRPNDetector::ChannelState::sendIfReady (
int channel)
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);
131 jassert (value >= 0 && value < (use14BitValue ? 16384 : 128));
133 auto parameterLSB = uint8 (parameterNumber & 0x0000007f);
134 auto parameterMSB = uint8 (parameterNumber >> 7);
136 uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00;
137 uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value);
139 auto channelByte = uint8 (0xb0 + midiChannel - 1);
161class MidiRPNDetectorTests final :
public UnitTest
164 MidiRPNDetectorTests()
165 :
UnitTest (
"MidiRPNDetector class", UnitTestCategories::midi)
168 void runTest()
override
177 beginTest (
"Individual MSB is parsed as 7-bit");
179 MidiRPNDetector detector;
180 expect (! detector.tryParse (2, 101, 0));
181 expect (! detector.tryParse (2, 100, 7));
183 auto parsed = detector.tryParse (2, 6, 42);
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");
195 MidiRPNDetector detector;
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");
203 MidiRPNDetector detector;
204 expect (! detector.tryParse (1, 101, 2));
205 expect (! detector.tryParse (1, 100, 44));
207 expect (detector.tryParse (1, 6, 1).has_value());
209 auto lsbParsed = detector.tryParse (1, 38, 94);
210 expect (lsbParsed.has_value());
212 expectEquals (lsbParsed->channel, 1);
213 expectEquals (lsbParsed->parameterNumber, 300);
214 expectEquals (lsbParsed->value, 222);
215 expect (! lsbParsed->isNRPN);
216 expect (lsbParsed->is14BitValue);
219 beginTest (
"Multiple LSB following MSB re-use the MSB");
221 MidiRPNDetector detector;
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());
231 auto lsbParsed = detector.tryParse (1, 38, 97);
232 expect (lsbParsed.has_value());
234 expectEquals (lsbParsed->channel, 1);
235 expectEquals (lsbParsed->parameterNumber, 299);
236 expectEquals (lsbParsed->value, 225);
237 expect (! lsbParsed->isNRPN);
238 expect (lsbParsed->is14BitValue);
241 beginTest (
"Sending a new MSB resets the LSB");
243 MidiRPNDetector detector;
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());
250 auto newMsb = detector.tryParse (1, 6, 2);
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");
262 MidiRPNDetector detector;
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());
269 auto channelTwo = detector.tryParse (2, 6, 42);
270 expect (channelTwo.has_value());
272 expectEquals (channelTwo->channel, 2);
273 expectEquals (channelTwo->parameterNumber, 7);
274 expectEquals (channelTwo->value, 42);
275 expect (! channelTwo->isNRPN);
276 expect (! channelTwo->is14BitValue);
278 auto channelOne = detector.tryParse (1, 38, 94);
279 expect (channelOne.has_value());
281 expectEquals (channelOne->channel, 1);
282 expectEquals (channelOne->parameterNumber, 300);
283 expectEquals (channelOne->value, 222);
284 expect (! channelOne->isNRPN);
285 expect (channelOne->is14BitValue);
288 beginTest (
"14-bit RPN with value within 7-bit range");
290 MidiRPNDetector detector;
291 expect (! detector.tryParse (16, 100, 0));
292 expect (! detector.tryParse (16, 101, 0));
293 expect (detector.tryParse (16, 6, 0).has_value());
295 auto parsed = detector.tryParse (16, 38, 3);
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)");
307 MidiRPNDetector detector;
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");
315 MidiRPNDetector detector;
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());
326 auto parsed = detector.tryParse (16, 38, 3);
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");
338 MidiRPNDetector detector;
339 expect (! detector.tryParse (1, 98, 44));
340 expect (! detector.tryParse (1, 99 , 2));
341 expect (detector.tryParse (1, 6, 1).has_value());
343 auto parsed = detector.tryParse (1, 38, 94);
344 expect (parsed.has_value());
346 expectEquals (parsed->channel, 1);
347 expectEquals (parsed->parameterNumber, 300);
348 expectEquals (parsed->value, 222);
349 expect (parsed->isNRPN);
350 expect (parsed->is14BitValue);
355 MidiRPNDetector detector;
356 expect (! detector.tryParse (2, 101, 0));
358 expect (! detector.tryParse (2, 100, 7));
359 expect (! detector.tryParse (2, 6, 42));
364static MidiRPNDetectorTests MidiRPNDetectorUnitTests;
367class MidiRPNGeneratorTests final :
public UnitTest
370 MidiRPNGeneratorTests()
371 : UnitTest (
"MidiRPNGenerator class", UnitTestCategories::midi)
374 void runTest()
override
376 beginTest (
"generating RPN/NRPN");
379 MidiBuffer buffer = MidiRPNGenerator::generate (1, 23, 1337,
true,
true);
380 expectContainsRPN (buffer, 1, 23, 1337,
true,
true);
383 MidiBuffer buffer = MidiRPNGenerator::generate (16, 101, 34,
false,
false);
384 expectContainsRPN (buffer, 16, 101, 34,
false,
false);
387 MidiRPNMessage message = { 16, 101, 34,
false,
false };
388 MidiBuffer buffer = MidiRPNGenerator::generate (message);
389 expectContainsRPN (buffer, message);
396 void expectContainsRPN (
const MidiBuffer& midiBuffer,
403 MidiRPNMessage expected = { channel, parameterNumber, value, isNRPN, is14BitValue };
404 expectContainsRPN (midiBuffer, expected);
408 void expectContainsRPN (
const MidiBuffer& midiBuffer, MidiRPNMessage expected)
410 std::optional<MidiRPNMessage> result;
411 MidiRPNDetector detector;
413 for (
const auto metadata : midiBuffer)
415 const auto midiMessage = metadata.getMessage();
417 result = detector.tryParse (midiMessage.getChannel(),
418 midiMessage.getControllerNumber(),
419 midiMessage.getControllerValue());
422 expect (result.has_value());
423 expectEquals (result->channel, expected.channel);
424 expectEquals (result->parameterNumber, expected.parameterNumber);
425 expectEquals (result->value, expected.value);
426 expect (result->isNRPN == expected.isNRPN);
427 expect (result->is14BitValue == expected.is14BitValue);
431static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests;
bool addEvent(const MidiMessage &midiMessage, int sampleNumber)
bool parseControllerMessage(int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage &result) noexcept
std::optional< MidiRPNMessage > tryParse(int midiChannel, int controllerNumber, int controllerValue)
static MidiBuffer generate(MidiRPNMessage message)