OpenShot Audio Library | OpenShotAudio  0.3.0
juce_MidiRPN.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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 
27 {
28 }
29 
31 {
32 }
33 
35  int controllerNumber,
36  int controllerValue,
37  MidiRPNMessage& result) noexcept
38 {
39  jassert (midiChannel >= 1 && 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 
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 //==============================================================================
58 MidiRPNDetector::ChannelState::ChannelState() noexcept
59  : parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false)
60 {
61 }
62 
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 
85 void MidiRPNDetector::ChannelState::resetValue() noexcept
86 {
87  valueMSB = 0xff;
88  valueLSB = 0xff;
89 }
90 
91 //==============================================================================
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 //==============================================================================
122 {
123  return generate (message.channel,
124  message.parameterNumber,
125  message.value,
126  message.isNRPN,
127  message.is14BitValue);
128 }
129 
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:
170  MidiRPNDetectorTests()
171  : UnitTest ("MidiRPNDetector class", UnitTestCategories::midi)
172  {}
173 
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:
314  MidiRPNGeneratorTests()
315  : UnitTest ("MidiRPNGenerator class", UnitTestCategories::midi)
316  {}
317 
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  //==============================================================================
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  //==============================================================================
352  void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected)
353  {
354  MidiBuffer::Iterator iter (midiBuffer);
355  MidiMessage midiMessage;
356  MidiRPNMessage result = MidiRPNMessage();
357  MidiRPNDetector detector;
358  int samplePosition; // not actually used, so no need to initialise.
359 
360  while (iter.getNextEvent (midiMessage, samplePosition))
361  {
362  if (detector.parseControllerMessage (midiMessage.getChannel(),
363  midiMessage.getControllerNumber(),
364  midiMessage.getControllerValue(),
365  result))
366  break;
367  }
368 
369  expectEquals (result.channel, expected.channel);
370  expectEquals (result.parameterNumber, expected.parameterNumber);
371  expectEquals (result.value, expected.value);
372  expect (result.isNRPN == expected.isNRPN);
373  expect (result.is14BitValue == expected.is14BitValue);
374  }
375 };
376 
377 static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests;
378 
379 #endif
380 
381 } // namespace juce
~MidiRPNDetector() noexcept
static MidiBuffer generate(MidiRPNMessage message)
bool getNextEvent(MidiMessage &result, int &samplePosition) noexcept
MidiRPNDetector() noexcept
int getControllerNumber() const noexcept
int getChannel() const noexcept
void reset() noexcept
int getControllerValue() const noexcept
void addEvent(const MidiMessage &midiMessage, int sampleNumber)
bool parseControllerMessage(int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage &result) noexcept