OpenShot Library | libopenshot-audio  0.1.9
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 #if JUCE_UNIT_TESTS
165 
166 class MidiRPNDetectorTests : public UnitTest
167 {
168 public:
169  MidiRPNDetectorTests() : UnitTest ("MidiRPNDetector class", "MIDI/MPE") {}
170 
171  void runTest() override
172  {
173  beginTest ("7-bit RPN");
174  {
175  MidiRPNDetector detector;
176  MidiRPNMessage rpn;
177  expect (! detector.parseControllerMessage (2, 101, 0, rpn));
178  expect (! detector.parseControllerMessage (2, 100, 7, rpn));
179  expect (detector.parseControllerMessage (2, 6, 42, rpn));
180 
181  expectEquals (rpn.channel, 2);
182  expectEquals (rpn.parameterNumber, 7);
183  expectEquals (rpn.value, 42);
184  expect (! rpn.isNRPN);
185  expect (! rpn.is14BitValue);
186  }
187 
188  beginTest ("14-bit RPN");
189  {
190  MidiRPNDetector detector;
191  MidiRPNMessage rpn;
192  expect (! detector.parseControllerMessage (1, 100, 44, rpn));
193  expect (! detector.parseControllerMessage (1, 101, 2, rpn));
194  expect (! detector.parseControllerMessage (1, 38, 94, rpn));
195  expect (detector.parseControllerMessage (1, 6, 1, rpn));
196 
197  expectEquals (rpn.channel, 1);
198  expectEquals (rpn.parameterNumber, 300);
199  expectEquals (rpn.value, 222);
200  expect (! rpn.isNRPN);
201  expect (rpn.is14BitValue);
202  }
203 
204  beginTest ("RPNs on multiple channels simultaneously");
205  {
206  MidiRPNDetector detector;
207  MidiRPNMessage rpn;
208  expect (! detector.parseControllerMessage (1, 100, 44, rpn));
209  expect (! detector.parseControllerMessage (2, 101, 0, rpn));
210  expect (! detector.parseControllerMessage (1, 101, 2, rpn));
211  expect (! detector.parseControllerMessage (2, 100, 7, rpn));
212  expect (! detector.parseControllerMessage (1, 38, 94, rpn));
213  expect (detector.parseControllerMessage (2, 6, 42, rpn));
214 
215  expectEquals (rpn.channel, 2);
216  expectEquals (rpn.parameterNumber, 7);
217  expectEquals (rpn.value, 42);
218  expect (! rpn.isNRPN);
219  expect (! rpn.is14BitValue);
220 
221  expect (detector.parseControllerMessage (1, 6, 1, rpn));
222 
223  expectEquals (rpn.channel, 1);
224  expectEquals (rpn.parameterNumber, 300);
225  expectEquals (rpn.value, 222);
226  expect (! rpn.isNRPN);
227  expect (rpn.is14BitValue);
228  }
229 
230  beginTest ("14-bit RPN with value within 7-bit range");
231  {
232  MidiRPNDetector detector;
233  MidiRPNMessage rpn;
234  expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
235  expect (! detector.parseControllerMessage (16, 101, 0, rpn));
236  expect (! detector.parseControllerMessage (16, 38, 3, rpn));
237  expect (detector.parseControllerMessage (16, 6, 0, rpn));
238 
239  expectEquals (rpn.channel, 16);
240  expectEquals (rpn.parameterNumber, 0);
241  expectEquals (rpn.value, 3);
242  expect (! rpn.isNRPN);
243  expect (rpn.is14BitValue);
244  }
245 
246  beginTest ("invalid RPN (wrong order)");
247  {
248  MidiRPNDetector detector;
249  MidiRPNMessage rpn;
250  expect (! detector.parseControllerMessage (2, 6, 42, rpn));
251  expect (! detector.parseControllerMessage (2, 101, 0, rpn));
252  expect (! detector.parseControllerMessage (2, 100, 7, rpn));
253  }
254 
255  beginTest ("14-bit RPN interspersed with unrelated CC messages");
256  {
257  MidiRPNDetector detector;
258  MidiRPNMessage rpn;
259  expect (! detector.parseControllerMessage (16, 3, 80, rpn));
260  expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
261  expect (! detector.parseControllerMessage (16, 4, 81, rpn));
262  expect (! detector.parseControllerMessage (16, 101, 0, rpn));
263  expect (! detector.parseControllerMessage (16, 5, 82, rpn));
264  expect (! detector.parseControllerMessage (16, 5, 83, rpn));
265  expect (! detector.parseControllerMessage (16, 38, 3, rpn));
266  expect (! detector.parseControllerMessage (16, 4, 84, rpn));
267  expect (! detector.parseControllerMessage (16, 3, 85, rpn));
268  expect (detector.parseControllerMessage (16, 6, 0, rpn));
269 
270  expectEquals (rpn.channel, 16);
271  expectEquals (rpn.parameterNumber, 0);
272  expectEquals (rpn.value, 3);
273  expect (! rpn.isNRPN);
274  expect (rpn.is14BitValue);
275  }
276 
277  beginTest ("14-bit NRPN");
278  {
279  MidiRPNDetector detector;
280  MidiRPNMessage rpn;
281  expect (! detector.parseControllerMessage (1, 98, 44, rpn));
282  expect (! detector.parseControllerMessage (1, 99 , 2, rpn));
283  expect (! detector.parseControllerMessage (1, 38, 94, rpn));
284  expect (detector.parseControllerMessage (1, 6, 1, rpn));
285 
286  expectEquals (rpn.channel, 1);
287  expectEquals (rpn.parameterNumber, 300);
288  expectEquals (rpn.value, 222);
289  expect (rpn.isNRPN);
290  expect (rpn.is14BitValue);
291  }
292 
293  beginTest ("reset");
294  {
295  MidiRPNDetector detector;
296  MidiRPNMessage rpn;
297  expect (! detector.parseControllerMessage (2, 101, 0, rpn));
298  detector.reset();
299  expect (! detector.parseControllerMessage (2, 100, 7, rpn));
300  expect (! detector.parseControllerMessage (2, 6, 42, rpn));
301  }
302  }
303 };
304 
305 static MidiRPNDetectorTests MidiRPNDetectorUnitTests;
306 
307 //==============================================================================
308 class MidiRPNGeneratorTests : public UnitTest
309 {
310 public:
311  MidiRPNGeneratorTests() : UnitTest ("MidiRPNGenerator class", "MIDI/MPE") {}
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 
333 private:
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  MidiBuffer::Iterator iter (midiBuffer);
350  MidiMessage midiMessage;
351  MidiRPNMessage result = MidiRPNMessage();
352  MidiRPNDetector detector;
353  int samplePosition; // not actually used, so no need to initialise.
354 
355  while (iter.getNextEvent (midiMessage, samplePosition))
356  {
357  if (detector.parseControllerMessage (midiMessage.getChannel(),
358  midiMessage.getControllerNumber(),
359  midiMessage.getControllerValue(),
360  result))
361  break;
362  }
363 
364  expectEquals (result.channel, expected.channel);
365  expectEquals (result.parameterNumber, expected.parameterNumber);
366  expectEquals (result.value, expected.value);
367  expect (result.isNRPN == expected.isNRPN);
368  expect (result.is14BitValue == expected.is14BitValue);
369  }
370 };
371 
372 static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests;
373 
374 #endif // JUCE_UNIT_TESTS
375 
376 } // namespace juce
Encapsulates a MIDI message.
~MidiRPNDetector() noexcept
Destructor.
static MidiBuffer generate(MidiRPNMessage message)
Generates a MIDI sequence representing the given RPN or NRPN message.
bool getNextEvent(MidiMessage &result, int &samplePosition) noexcept
Retrieves a copy of the next event from the buffer.
MidiRPNDetector() noexcept
Constructor.
int channel
Midi channel of the message, in the range 1 to 16.
Definition: juce_MidiRPN.h:39
bool is14BitValue
True if the value uses 14-bit resolution (LSB + MSB); false if the value is 7-bit (MSB only)...
Definition: juce_MidiRPN.h:56
int value
The parameter value, in the range 0 to 16383 (0x3fff).
Definition: juce_MidiRPN.h:48
This is a base class for classes that perform a unit test.
Definition: juce_UnitTest.h:73
int getControllerNumber() const noexcept
Returns the controller number of a controller message.
int getChannel() const noexcept
Returns the midi channel associated with the message.
void reset() noexcept
Resets the RPN detector&#39;s internal state, so that it forgets about previously received MIDI CC messag...
Represents a MIDI RPN (registered parameter number) or NRPN (non-registered parameter number) message...
Definition: juce_MidiRPN.h:36
bool isNRPN
True if this message is an NRPN; false if it is an RPN.
Definition: juce_MidiRPN.h:51
int parameterNumber
The 14-bit parameter index, in the range 0 to 16383 (0x3fff).
Definition: juce_MidiRPN.h:42
Holds a sequence of time-stamped midi events.
int getControllerValue() const noexcept
Returns the controller value from a controller message.
void addEvent(const MidiMessage &midiMessage, int sampleNumber)
Adds an event to the buffer.
Used to iterate through the events in a MidiBuffer.
bool parseControllerMessage(int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage &result) noexcept
Takes the next in a stream of incoming MIDI CC messages and returns true if it forms the last of a se...
Parses a stream of MIDI data to assemble RPN and NRPN messages from their constituent MIDI CC message...
Definition: juce_MidiRPN.h:71