LMMS
Loading...
Searching...
No Matches
juce_MidiRPN.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - 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
23namespace juce
24{
25
29
33
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
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//==============================================================================
59 int controllerNumber,
60 int value,
61 MidiRPNMessage& result) noexcept
62{
63 switch (controllerNumber)
64 {
65 case 0x62: parameterLSB = uint8 (value); resetValue(); isNRPN = true; break;
66 case 0x63: parameterMSB = uint8 (value); resetValue(); isNRPN = true; break;
67
68 case 0x64: parameterLSB = uint8 (value); resetValue(); isNRPN = false; break;
69 case 0x65: parameterMSB = uint8 (value); resetValue(); isNRPN = false; break;
70
71 case 0x06: valueMSB = uint8 (value); return sendIfReady (channel, result);
72 case 0x26: valueLSB = uint8 (value); break;
73
74 default: break;
75 }
76
77 return false;
78}
79
85
86//==============================================================================
88{
89 if (parameterMSB < 0x80 && parameterLSB < 0x80)
90 {
91 if (valueMSB < 0x80)
92 {
93 result.channel = channel;
94 result.parameterNumber = (parameterMSB << 7) + parameterLSB;
95 result.isNRPN = isNRPN;
96
97 if (valueLSB < 0x80)
98 {
99 result.value = (valueMSB << 7) + valueLSB;
100 result.is14BitValue = true;
101 }
102 else
103 {
104 result.value = valueMSB;
105 result.is14BitValue = false;
106 }
107
108 return true;
109 }
110 }
111
112 return false;
113}
114
115//==============================================================================
117{
118 return generate (message.channel,
119 message.parameterNumber,
120 message.value,
121 message.isNRPN,
122 message.is14BitValue);
123}
124
126 int parameterNumber,
127 int value,
128 bool isNRPN,
129 bool use14BitValue)
130{
131 jassert (midiChannel > 0 && midiChannel <= 16);
132 jassert (parameterNumber >= 0 && parameterNumber < 16384);
133 jassert (value >= 0 && value < (use14BitValue ? 16384 : 128));
134
135 uint8 parameterLSB = uint8 (parameterNumber & 0x0000007f);
136 uint8 parameterMSB = uint8 (parameterNumber >> 7);
137
138 uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00;
139 uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value);
140
141 uint8 channelByte = uint8 (0xb0 + midiChannel - 1);
142
143 MidiBuffer buffer;
144
145 buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x62 : 0x64, parameterLSB), 0);
146 buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x63 : 0x65, parameterMSB), 0);
147
148 // sending the value LSB is optional, but must come before sending the value MSB:
149 if (use14BitValue)
150 buffer.addEvent (MidiMessage (channelByte, 0x26, valueLSB), 0);
151
152 buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0);
153
154 return buffer;
155}
156
157
158//==============================================================================
159//==============================================================================
160#if JUCE_UNIT_TESTS
161
162class MidiRPNDetectorTests : public UnitTest
163{
164public:
165 MidiRPNDetectorTests()
166 : UnitTest ("MidiRPNDetector class", UnitTestCategories::midi)
167 {}
168
169 void runTest() override
170 {
171 beginTest ("7-bit RPN");
172 {
173 MidiRPNDetector detector;
174 MidiRPNMessage rpn;
175 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
176 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
177 expect (detector.parseControllerMessage (2, 6, 42, rpn));
178
179 expectEquals (rpn.channel, 2);
180 expectEquals (rpn.parameterNumber, 7);
181 expectEquals (rpn.value, 42);
182 expect (! rpn.isNRPN);
183 expect (! rpn.is14BitValue);
184 }
185
186 beginTest ("14-bit RPN");
187 {
188 MidiRPNDetector detector;
189 MidiRPNMessage rpn;
190 expect (! detector.parseControllerMessage (1, 100, 44, rpn));
191 expect (! detector.parseControllerMessage (1, 101, 2, rpn));
192 expect (! detector.parseControllerMessage (1, 38, 94, rpn));
193 expect (detector.parseControllerMessage (1, 6, 1, rpn));
194
195 expectEquals (rpn.channel, 1);
196 expectEquals (rpn.parameterNumber, 300);
197 expectEquals (rpn.value, 222);
198 expect (! rpn.isNRPN);
199 expect (rpn.is14BitValue);
200 }
201
202 beginTest ("RPNs on multiple channels simultaneously");
203 {
204 MidiRPNDetector detector;
205 MidiRPNMessage rpn;
206 expect (! detector.parseControllerMessage (1, 100, 44, rpn));
207 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
208 expect (! detector.parseControllerMessage (1, 101, 2, rpn));
209 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
210 expect (! detector.parseControllerMessage (1, 38, 94, rpn));
211 expect (detector.parseControllerMessage (2, 6, 42, rpn));
212
213 expectEquals (rpn.channel, 2);
214 expectEquals (rpn.parameterNumber, 7);
215 expectEquals (rpn.value, 42);
216 expect (! rpn.isNRPN);
217 expect (! rpn.is14BitValue);
218
219 expect (detector.parseControllerMessage (1, 6, 1, rpn));
220
221 expectEquals (rpn.channel, 1);
222 expectEquals (rpn.parameterNumber, 300);
223 expectEquals (rpn.value, 222);
224 expect (! rpn.isNRPN);
225 expect (rpn.is14BitValue);
226 }
227
228 beginTest ("14-bit RPN with value within 7-bit range");
229 {
230 MidiRPNDetector detector;
231 MidiRPNMessage rpn;
232 expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
233 expect (! detector.parseControllerMessage (16, 101, 0, rpn));
234 expect (! detector.parseControllerMessage (16, 38, 3, rpn));
235 expect (detector.parseControllerMessage (16, 6, 0, rpn));
236
237 expectEquals (rpn.channel, 16);
238 expectEquals (rpn.parameterNumber, 0);
239 expectEquals (rpn.value, 3);
240 expect (! rpn.isNRPN);
241 expect (rpn.is14BitValue);
242 }
243
244 beginTest ("invalid RPN (wrong order)");
245 {
246 MidiRPNDetector detector;
247 MidiRPNMessage rpn;
248 expect (! detector.parseControllerMessage (2, 6, 42, rpn));
249 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
250 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
251 }
252
253 beginTest ("14-bit RPN interspersed with unrelated CC messages");
254 {
255 MidiRPNDetector detector;
256 MidiRPNMessage rpn;
257 expect (! detector.parseControllerMessage (16, 3, 80, rpn));
258 expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
259 expect (! detector.parseControllerMessage (16, 4, 81, rpn));
260 expect (! detector.parseControllerMessage (16, 101, 0, rpn));
261 expect (! detector.parseControllerMessage (16, 5, 82, rpn));
262 expect (! detector.parseControllerMessage (16, 5, 83, rpn));
263 expect (! detector.parseControllerMessage (16, 38, 3, rpn));
264 expect (! detector.parseControllerMessage (16, 4, 84, rpn));
265 expect (! detector.parseControllerMessage (16, 3, 85, rpn));
266 expect (detector.parseControllerMessage (16, 6, 0, rpn));
267
268 expectEquals (rpn.channel, 16);
269 expectEquals (rpn.parameterNumber, 0);
270 expectEquals (rpn.value, 3);
271 expect (! rpn.isNRPN);
272 expect (rpn.is14BitValue);
273 }
274
275 beginTest ("14-bit NRPN");
276 {
277 MidiRPNDetector detector;
278 MidiRPNMessage rpn;
279 expect (! detector.parseControllerMessage (1, 98, 44, rpn));
280 expect (! detector.parseControllerMessage (1, 99 , 2, rpn));
281 expect (! detector.parseControllerMessage (1, 38, 94, rpn));
282 expect (detector.parseControllerMessage (1, 6, 1, rpn));
283
284 expectEquals (rpn.channel, 1);
285 expectEquals (rpn.parameterNumber, 300);
286 expectEquals (rpn.value, 222);
287 expect (rpn.isNRPN);
288 expect (rpn.is14BitValue);
289 }
290
291 beginTest ("reset");
292 {
293 MidiRPNDetector detector;
294 MidiRPNMessage rpn;
295 expect (! detector.parseControllerMessage (2, 101, 0, rpn));
296 detector.reset();
297 expect (! detector.parseControllerMessage (2, 100, 7, rpn));
298 expect (! detector.parseControllerMessage (2, 6, 42, rpn));
299 }
300 }
301};
302
303static MidiRPNDetectorTests MidiRPNDetectorUnitTests;
304
305//==============================================================================
306class MidiRPNGeneratorTests : public UnitTest
307{
308public:
309 MidiRPNGeneratorTests()
310 : UnitTest ("MidiRPNGenerator class", UnitTestCategories::midi)
311 {}
312
313 void runTest() override
314 {
315 beginTest ("generating RPN/NRPN");
316 {
317 {
318 MidiBuffer buffer = MidiRPNGenerator::generate (1, 23, 1337, true, true);
319 expectContainsRPN (buffer, 1, 23, 1337, true, true);
320 }
321 {
322 MidiBuffer buffer = MidiRPNGenerator::generate (16, 101, 34, false, false);
323 expectContainsRPN (buffer, 16, 101, 34, false, false);
324 }
325 {
326 MidiRPNMessage message = { 16, 101, 34, false, false };
327 MidiBuffer buffer = MidiRPNGenerator::generate (message);
328 expectContainsRPN (buffer, message);
329 }
330 }
331 }
332
333private:
334 //==============================================================================
335 void expectContainsRPN (const MidiBuffer& midiBuffer,
336 int channel,
337 int parameterNumber,
338 int value,
339 bool isNRPN,
340 bool is14BitValue)
341 {
342 MidiRPNMessage expected = { channel, parameterNumber, value, isNRPN, is14BitValue };
343 expectContainsRPN (midiBuffer, expected);
344 }
345
346 //==============================================================================
347 void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected)
348 {
349 MidiRPNMessage result = MidiRPNMessage();
350 MidiRPNDetector detector;
351
352 for (const auto metadata : midiBuffer)
353 {
354 const auto midiMessage = metadata.getMessage();
355
356 if (detector.parseControllerMessage (midiMessage.getChannel(),
357 midiMessage.getControllerNumber(),
358 midiMessage.getControllerValue(),
359 result))
360 break;
361 }
362
363 expectEquals (result.channel, expected.channel);
364 expectEquals (result.parameterNumber, expected.parameterNumber);
365 expectEquals (result.value, expected.value);
366 expect (result.isNRPN == expected.isNRPN);
367 expect (result.is14BitValue == expected.is14BitValue);
368 }
369};
370
371static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests;
372
373#endif
374
375} // namespace juce
#define noexcept
Definition DistrhoDefines.h:72
static void message(int level, const char *fmt,...)
Definition adplugdb.cpp:120
uint8_t uint8
Definition basics.h:86
Definition juce_MidiBuffer.h:145
Definition juce_MidiMessage.h:35
ChannelState states[16]
Definition juce_MidiRPN.h:108
MidiRPNDetector() noexcept
Definition juce_MidiRPN.cpp:26
bool parseControllerMessage(int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage &result) noexcept
Definition juce_MidiRPN.cpp:34
void reset() noexcept
Definition juce_MidiRPN.cpp:46
~MidiRPNDetector() noexcept
Definition juce_MidiRPN.cpp:30
static MidiBuffer generate(MidiRPNMessage message)
Definition juce_MidiRPN.cpp:116
Definition juce_UnitTest.h:70
register unsigned i
Definition inflate.c:1575
static PuglViewHint int value
Definition pugl.h:1708
#define jassert(expression)
static int generate(SordWorld *world, SordModel *sord, size_t n_quads, SordNode *graph)
Definition sord_test.c:72
Definition juce_UnitTestCategories.h:27
JOCTET * buffer
Definition juce_JPEGLoader.cpp:302
Definition carla_juce.cpp:31
unsigned char uint8
Definition juce_MathsFunctions.h:37
bool handleController(int channel, int controllerNumber, int value, MidiRPNMessage &) noexcept
Definition juce_MidiRPN.cpp:58
bool isNRPN
Definition juce_MidiRPN.h:104
bool sendIfReady(int channel, MidiRPNMessage &) noexcept
Definition juce_MidiRPN.cpp:87
uint8 valueMSB
Definition juce_MidiRPN.h:103
void resetValue() noexcept
Definition juce_MidiRPN.cpp:80
uint8 parameterLSB
Definition juce_MidiRPN.h:103
uint8 valueLSB
Definition juce_MidiRPN.h:103
uint8 parameterMSB
Definition juce_MidiRPN.h:103
Definition juce_MidiRPN.h:33
int result
Definition process.c:1455