1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21 */
22
23 namespace juce
24 {
25
MidiRPNDetector()26 MidiRPNDetector::MidiRPNDetector() noexcept
27 {
28 }
29
~MidiRPNDetector()30 MidiRPNDetector::~MidiRPNDetector() noexcept
31 {
32 }
33
parseControllerMessage(int midiChannel,int controllerNumber,int controllerValue,MidiRPNMessage & result)34 bool MidiRPNDetector::parseControllerMessage (int midiChannel,
35 int controllerNumber,
36 int controllerValue,
37 MidiRPNMessage& result) noexcept
38 {
39 jassert (midiChannel > 0 && midiChannel <= 16);
40 jassert (controllerNumber >= 0 && controllerNumber < 128);
41 jassert (controllerValue >= 0 && controllerValue < 128);
42
43 return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue, result);
44 }
45
reset()46 void MidiRPNDetector::reset() noexcept
47 {
48 for (int i = 0; i < 16; ++i)
49 {
50 states[i].parameterMSB = 0xff;
51 states[i].parameterLSB = 0xff;
52 states[i].resetValue();
53 states[i].isNRPN = false;
54 }
55 }
56
57 //==============================================================================
ChannelState()58 MidiRPNDetector::ChannelState::ChannelState() noexcept
59 : parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false)
60 {
61 }
62
handleController(int channel,int controllerNumber,int value,MidiRPNMessage & result)63 bool MidiRPNDetector::ChannelState::handleController (int channel,
64 int controllerNumber,
65 int value,
66 MidiRPNMessage& result) noexcept
67 {
68 switch (controllerNumber)
69 {
70 case 0x62: parameterLSB = uint8 (value); resetValue(); isNRPN = true; break;
71 case 0x63: parameterMSB = uint8 (value); resetValue(); isNRPN = true; break;
72
73 case 0x64: parameterLSB = uint8 (value); resetValue(); isNRPN = false; break;
74 case 0x65: parameterMSB = uint8 (value); resetValue(); isNRPN = false; break;
75
76 case 0x06: valueMSB = uint8 (value); return sendIfReady (channel, result);
77 case 0x26: valueLSB = uint8 (value); break;
78
79 default: break;
80 }
81
82 return false;
83 }
84
resetValue()85 void MidiRPNDetector::ChannelState::resetValue() noexcept
86 {
87 valueMSB = 0xff;
88 valueLSB = 0xff;
89 }
90
91 //==============================================================================
sendIfReady(int channel,MidiRPNMessage & result)92 bool MidiRPNDetector::ChannelState::sendIfReady (int channel, MidiRPNMessage& result) noexcept
93 {
94 if (parameterMSB < 0x80 && parameterLSB < 0x80)
95 {
96 if (valueMSB < 0x80)
97 {
98 result.channel = channel;
99 result.parameterNumber = (parameterMSB << 7) + parameterLSB;
100 result.isNRPN = isNRPN;
101
102 if (valueLSB < 0x80)
103 {
104 result.value = (valueMSB << 7) + valueLSB;
105 result.is14BitValue = true;
106 }
107 else
108 {
109 result.value = valueMSB;
110 result.is14BitValue = false;
111 }
112
113 return true;
114 }
115 }
116
117 return false;
118 }
119
120 //==============================================================================
generate(MidiRPNMessage message)121 MidiBuffer MidiRPNGenerator::generate (MidiRPNMessage message)
122 {
123 return generate (message.channel,
124 message.parameterNumber,
125 message.value,
126 message.isNRPN,
127 message.is14BitValue);
128 }
129
generate(int midiChannel,int parameterNumber,int value,bool isNRPN,bool use14BitValue)130 MidiBuffer MidiRPNGenerator::generate (int midiChannel,
131 int parameterNumber,
132 int value,
133 bool isNRPN,
134 bool use14BitValue)
135 {
136 jassert (midiChannel > 0 && midiChannel <= 16);
137 jassert (parameterNumber >= 0 && parameterNumber < 16384);
138 jassert (value >= 0 && value < (use14BitValue ? 16384 : 128));
139
140 uint8 parameterLSB = uint8 (parameterNumber & 0x0000007f);
141 uint8 parameterMSB = uint8 (parameterNumber >> 7);
142
143 uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00;
144 uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value);
145
146 uint8 channelByte = uint8 (0xb0 + midiChannel - 1);
147
148 MidiBuffer buffer;
149
150 buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x62 : 0x64, parameterLSB), 0);
151 buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x63 : 0x65, parameterMSB), 0);
152
153 // sending the value LSB is optional, but must come before sending the value MSB:
154 if (use14BitValue)
155 buffer.addEvent (MidiMessage (channelByte, 0x26, valueLSB), 0);
156
157 buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0);
158
159 return buffer;
160 }
161
162
163 //==============================================================================
164 //==============================================================================
165 #if JUCE_UNIT_TESTS
166
167 class MidiRPNDetectorTests : public UnitTest
168 {
169 public:
MidiRPNDetectorTests()170 MidiRPNDetectorTests()
171 : UnitTest ("MidiRPNDetector class", UnitTestCategories::midi)
172 {}
173
runTest()174 void runTest() override
175 {
176 beginTest ("7-bit RPN");
177 {
178 MidiRPNDetector detector;
179 MidiRPNMessage rpn;
180 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
181 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
182 expect (detector.parseControllerMessage (2, 6, 42, rpn));
183
184 expectEquals (rpn.channel, 2);
185 expectEquals (rpn.parameterNumber, 7);
186 expectEquals (rpn.value, 42);
187 expect (! rpn.isNRPN);
188 expect (! rpn.is14BitValue);
189 }
190
191 beginTest ("14-bit RPN");
192 {
193 MidiRPNDetector detector;
194 MidiRPNMessage rpn;
195 expect (! detector.parseControllerMessage (1, 100, 44, rpn));
196 expect (! detector.parseControllerMessage (1, 101, 2, rpn));
197 expect (! detector.parseControllerMessage (1, 38, 94, rpn));
198 expect (detector.parseControllerMessage (1, 6, 1, rpn));
199
200 expectEquals (rpn.channel, 1);
201 expectEquals (rpn.parameterNumber, 300);
202 expectEquals (rpn.value, 222);
203 expect (! rpn.isNRPN);
204 expect (rpn.is14BitValue);
205 }
206
207 beginTest ("RPNs on multiple channels simultaneously");
208 {
209 MidiRPNDetector detector;
210 MidiRPNMessage rpn;
211 expect (! detector.parseControllerMessage (1, 100, 44, rpn));
212 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
213 expect (! detector.parseControllerMessage (1, 101, 2, rpn));
214 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
215 expect (! detector.parseControllerMessage (1, 38, 94, rpn));
216 expect (detector.parseControllerMessage (2, 6, 42, rpn));
217
218 expectEquals (rpn.channel, 2);
219 expectEquals (rpn.parameterNumber, 7);
220 expectEquals (rpn.value, 42);
221 expect (! rpn.isNRPN);
222 expect (! rpn.is14BitValue);
223
224 expect (detector.parseControllerMessage (1, 6, 1, rpn));
225
226 expectEquals (rpn.channel, 1);
227 expectEquals (rpn.parameterNumber, 300);
228 expectEquals (rpn.value, 222);
229 expect (! rpn.isNRPN);
230 expect (rpn.is14BitValue);
231 }
232
233 beginTest ("14-bit RPN with value within 7-bit range");
234 {
235 MidiRPNDetector detector;
236 MidiRPNMessage rpn;
237 expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
238 expect (! detector.parseControllerMessage (16, 101, 0, rpn));
239 expect (! detector.parseControllerMessage (16, 38, 3, rpn));
240 expect (detector.parseControllerMessage (16, 6, 0, rpn));
241
242 expectEquals (rpn.channel, 16);
243 expectEquals (rpn.parameterNumber, 0);
244 expectEquals (rpn.value, 3);
245 expect (! rpn.isNRPN);
246 expect (rpn.is14BitValue);
247 }
248
249 beginTest ("invalid RPN (wrong order)");
250 {
251 MidiRPNDetector detector;
252 MidiRPNMessage rpn;
253 expect (! detector.parseControllerMessage (2, 6, 42, rpn));
254 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
255 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
256 }
257
258 beginTest ("14-bit RPN interspersed with unrelated CC messages");
259 {
260 MidiRPNDetector detector;
261 MidiRPNMessage rpn;
262 expect (! detector.parseControllerMessage (16, 3, 80, rpn));
263 expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
264 expect (! detector.parseControllerMessage (16, 4, 81, rpn));
265 expect (! detector.parseControllerMessage (16, 101, 0, rpn));
266 expect (! detector.parseControllerMessage (16, 5, 82, rpn));
267 expect (! detector.parseControllerMessage (16, 5, 83, rpn));
268 expect (! detector.parseControllerMessage (16, 38, 3, rpn));
269 expect (! detector.parseControllerMessage (16, 4, 84, rpn));
270 expect (! detector.parseControllerMessage (16, 3, 85, rpn));
271 expect (detector.parseControllerMessage (16, 6, 0, rpn));
272
273 expectEquals (rpn.channel, 16);
274 expectEquals (rpn.parameterNumber, 0);
275 expectEquals (rpn.value, 3);
276 expect (! rpn.isNRPN);
277 expect (rpn.is14BitValue);
278 }
279
280 beginTest ("14-bit NRPN");
281 {
282 MidiRPNDetector detector;
283 MidiRPNMessage rpn;
284 expect (! detector.parseControllerMessage (1, 98, 44, rpn));
285 expect (! detector.parseControllerMessage (1, 99 , 2, rpn));
286 expect (! detector.parseControllerMessage (1, 38, 94, rpn));
287 expect (detector.parseControllerMessage (1, 6, 1, rpn));
288
289 expectEquals (rpn.channel, 1);
290 expectEquals (rpn.parameterNumber, 300);
291 expectEquals (rpn.value, 222);
292 expect (rpn.isNRPN);
293 expect (rpn.is14BitValue);
294 }
295
296 beginTest ("reset");
297 {
298 MidiRPNDetector detector;
299 MidiRPNMessage rpn;
300 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
301 detector.reset();
302 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
303 expect (! detector.parseControllerMessage (2, 6, 42, rpn));
304 }
305 }
306 };
307
308 static MidiRPNDetectorTests MidiRPNDetectorUnitTests;
309
310 //==============================================================================
311 class MidiRPNGeneratorTests : public UnitTest
312 {
313 public:
MidiRPNGeneratorTests()314 MidiRPNGeneratorTests()
315 : UnitTest ("MidiRPNGenerator class", UnitTestCategories::midi)
316 {}
317
runTest()318 void runTest() override
319 {
320 beginTest ("generating RPN/NRPN");
321 {
322 {
323 MidiBuffer buffer = MidiRPNGenerator::generate (1, 23, 1337, true, true);
324 expectContainsRPN (buffer, 1, 23, 1337, true, true);
325 }
326 {
327 MidiBuffer buffer = MidiRPNGenerator::generate (16, 101, 34, false, false);
328 expectContainsRPN (buffer, 16, 101, 34, false, false);
329 }
330 {
331 MidiRPNMessage message = { 16, 101, 34, false, false };
332 MidiBuffer buffer = MidiRPNGenerator::generate (message);
333 expectContainsRPN (buffer, message);
334 }
335 }
336 }
337
338 private:
339 //==============================================================================
expectContainsRPN(const MidiBuffer & midiBuffer,int channel,int parameterNumber,int value,bool isNRPN,bool is14BitValue)340 void expectContainsRPN (const MidiBuffer& midiBuffer,
341 int channel,
342 int parameterNumber,
343 int value,
344 bool isNRPN,
345 bool is14BitValue)
346 {
347 MidiRPNMessage expected = { channel, parameterNumber, value, isNRPN, is14BitValue };
348 expectContainsRPN (midiBuffer, expected);
349 }
350
351 //==============================================================================
expectContainsRPN(const MidiBuffer & midiBuffer,MidiRPNMessage expected)352 void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected)
353 {
354 MidiRPNMessage result = MidiRPNMessage();
355 MidiRPNDetector detector;
356
357 for (const auto metadata : midiBuffer)
358 {
359 const auto midiMessage = metadata.getMessage();
360
361 if (detector.parseControllerMessage (midiMessage.getChannel(),
362 midiMessage.getControllerNumber(),
363 midiMessage.getControllerValue(),
364 result))
365 break;
366 }
367
368 expectEquals (result.channel, expected.channel);
369 expectEquals (result.parameterNumber, expected.parameterNumber);
370 expectEquals (result.value, expected.value);
371 expect (result.isNRPN == expected.isNRPN);
372 expect (result.is14BitValue == expected.is14BitValue);
373 }
374 };
375
376 static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests;
377
378 #endif
379
380 } // namespace juce
381