OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
juce_MPEZoneLayout.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
23namespace juce
24{
25
27
29 : lowerZone (other.lowerZone),
30 upperZone (other.upperZone)
31{
32}
33
35{
36 lowerZone = other.lowerZone;
37 upperZone = other.upperZone;
38
39 sendLayoutChangeMessage();
40
41 return *this;
42}
43
44void MPEZoneLayout::sendLayoutChangeMessage()
45{
46 listeners.call ([this] (Listener& l) { l.zoneLayoutChanged (*this); });
47}
48
49//==============================================================================
50void MPEZoneLayout::setZone (bool isLower, int numMemberChannels, int perNotePitchbendRange, int masterPitchbendRange) noexcept
51{
52 checkAndLimitZoneParameters (0, 15, numMemberChannels);
53 checkAndLimitZoneParameters (0, 96, perNotePitchbendRange);
54 checkAndLimitZoneParameters (0, 96, masterPitchbendRange);
55
56 if (isLower)
57 lowerZone = { true, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
58 else
59 upperZone = { false, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
60
61 if (numMemberChannels > 0)
62 {
63 auto totalChannels = lowerZone.numMemberChannels + upperZone.numMemberChannels;
64
65 if (totalChannels >= 15)
66 {
67 if (isLower)
68 upperZone.numMemberChannels = 14 - numMemberChannels;
69 else
70 lowerZone.numMemberChannels = 14 - numMemberChannels;
71 }
72 }
73
74 sendLayoutChangeMessage();
75}
76
77void MPEZoneLayout::setLowerZone (int numMemberChannels, int perNotePitchbendRange, int masterPitchbendRange) noexcept
78{
79 setZone (true, numMemberChannels, perNotePitchbendRange, masterPitchbendRange);
80}
81
82void MPEZoneLayout::setUpperZone (int numMemberChannels, int perNotePitchbendRange, int masterPitchbendRange) noexcept
83{
84 setZone (false, numMemberChannels, perNotePitchbendRange, masterPitchbendRange);
85}
86
88{
89 lowerZone = { true, 0 };
90 upperZone = { false, 0 };
91
92 sendLayoutChangeMessage();
93}
94
95//==============================================================================
97{
98 if (! message.isController())
99 return;
100
102
103 if (rpnDetector.parseControllerMessage (message.getChannel(),
104 message.getControllerNumber(),
105 message.getControllerValue(),
106 rpn))
107 {
108 processRpnMessage (rpn);
109 }
110}
111
112void MPEZoneLayout::processRpnMessage (MidiRPNMessage rpn)
113{
114 if (rpn.parameterNumber == MPEMessages::zoneLayoutMessagesRpnNumber)
115 processZoneLayoutRpnMessage (rpn);
116 else if (rpn.parameterNumber == 0)
117 processPitchbendRangeRpnMessage (rpn);
118}
119
120void MPEZoneLayout::processZoneLayoutRpnMessage (MidiRPNMessage rpn)
121{
122 if (rpn.value < 16)
123 {
124 if (rpn.channel == 1)
125 setLowerZone (rpn.value);
126 else if (rpn.channel == 16)
127 setUpperZone (rpn.value);
128 }
129}
130
131void MPEZoneLayout::updateMasterPitchbend (Zone& zone, int value)
132{
133 if (zone.masterPitchbendRange != value)
134 {
135 checkAndLimitZoneParameters (0, 96, zone.masterPitchbendRange);
136 zone.masterPitchbendRange = value;
137 sendLayoutChangeMessage();
138 }
139}
140
141void MPEZoneLayout::updatePerNotePitchbendRange (Zone& zone, int value)
142{
143 if (zone.perNotePitchbendRange != value)
144 {
145 checkAndLimitZoneParameters (0, 96, zone.perNotePitchbendRange);
146 zone.perNotePitchbendRange = value;
147 sendLayoutChangeMessage();
148 }
149}
150
151void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn)
152{
153 if (rpn.channel == 1)
154 {
155 updateMasterPitchbend (lowerZone, rpn.value);
156 }
157 else if (rpn.channel == 16)
158 {
159 updateMasterPitchbend (upperZone, rpn.value);
160 }
161 else
162 {
163 if (lowerZone.isUsingChannelAsMemberChannel (rpn.channel))
164 updatePerNotePitchbendRange (lowerZone, rpn.value);
165 else if (upperZone.isUsingChannelAsMemberChannel (rpn.channel))
166 updatePerNotePitchbendRange (upperZone, rpn.value);
167 }
168}
169
171{
172 MidiBuffer::Iterator iter (buffer);
173 MidiMessage message;
174 int samplePosition; // not actually used, so no need to initialise.
175
176 while (iter.getNextEvent (message, samplePosition))
177 processNextMidiEvent (message);
178}
179
180//==============================================================================
182{
183 listeners.add (listenerToAdd);
184}
185
187{
188 listeners.remove (listenerToRemove);
189}
190
191//==============================================================================
192void MPEZoneLayout::checkAndLimitZoneParameters (int minValue, int maxValue,
193 int& valueToCheckAndLimit) noexcept
194{
196 {
197 // if you hit this, one of the parameters you supplied for this zone
198 // was not within the allowed range!
199 // we fit this back into the allowed range here to maintain a valid
200 // state for the zone, but probably the resulting zone is not what you
201 // wanted it to be!
202 jassertfalse;
203
204 valueToCheckAndLimit = jlimit (minValue, maxValue, valueToCheckAndLimit);
205 }
206}
207
208
209//==============================================================================
210//==============================================================================
211#if JUCE_UNIT_TESTS
212
213class MPEZoneLayoutTests : public UnitTest
214{
215public:
217 : UnitTest ("MPEZoneLayout class", UnitTestCategories::midi)
218 {}
219
220 void runTest() override
221 {
222 beginTest ("initialisation");
223 {
224 MPEZoneLayout layout;
225 expect (! layout.getLowerZone().isActive());
226 expect (! layout.getUpperZone().isActive());
227 }
228
229 beginTest ("adding zones");
230 {
231 MPEZoneLayout layout;
232
233 layout.setLowerZone (7);
234
235 expect (layout.getLowerZone().isActive());
236 expect (! layout.getUpperZone().isActive());
237 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
238 expectEquals (layout.getLowerZone().numMemberChannels, 7);
239
240 layout.setUpperZone (7);
241
242 expect (layout.getLowerZone().isActive());
243 expect (layout.getUpperZone().isActive());
244 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
245 expectEquals (layout.getLowerZone().numMemberChannels, 7);
246 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
247 expectEquals (layout.getUpperZone().numMemberChannels, 7);
248
249 layout.setLowerZone (3);
250
251 expect (layout.getLowerZone().isActive());
252 expect (layout.getUpperZone().isActive());
253 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
254 expectEquals (layout.getLowerZone().numMemberChannels, 3);
255 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
256 expectEquals (layout.getUpperZone().numMemberChannels, 7);
257
258 layout.setUpperZone (3);
259
260 expect (layout.getLowerZone().isActive());
261 expect (layout.getUpperZone().isActive());
262 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
263 expectEquals (layout.getLowerZone().numMemberChannels, 3);
264 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
265 expectEquals (layout.getUpperZone().numMemberChannels, 3);
266
267 layout.setLowerZone (15);
268
269 expect (layout.getLowerZone().isActive());
270 expect (! layout.getUpperZone().isActive());
271 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
272 expectEquals (layout.getLowerZone().numMemberChannels, 15);
273 }
274
275 beginTest ("clear all zones");
276 {
277 MPEZoneLayout layout;
278
279 expect (! layout.getLowerZone().isActive());
280 expect (! layout.getUpperZone().isActive());
281
282 layout.setLowerZone (7);
283 layout.setUpperZone (2);
284
285 expect (layout.getLowerZone().isActive());
286 expect (layout.getUpperZone().isActive());
287
288 layout.clearAllZones();
289
290 expect (! layout.getLowerZone().isActive());
291 expect (! layout.getUpperZone().isActive());
292 }
293
294 beginTest ("process MIDI buffers");
295 {
296 MPEZoneLayout layout;
297 MidiBuffer buffer;
298
299 buffer = MPEMessages::setLowerZone (7);
300 layout.processNextMidiBuffer (buffer);
301
302 expect (layout.getLowerZone().isActive());
303 expect (! layout.getUpperZone().isActive());
304 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
305 expectEquals (layout.getLowerZone().numMemberChannels, 7);
306
307 buffer = MPEMessages::setUpperZone (7);
308 layout.processNextMidiBuffer (buffer);
309
310 expect (layout.getLowerZone().isActive());
311 expect (layout.getUpperZone().isActive());
312 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
313 expectEquals (layout.getLowerZone().numMemberChannels, 7);
314 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
315 expectEquals (layout.getUpperZone().numMemberChannels, 7);
316
317 {
318 buffer = MPEMessages::setLowerZone (10);
319 layout.processNextMidiBuffer (buffer);
320
321 expect (layout.getLowerZone().isActive());
322 expect (layout.getUpperZone().isActive());
323 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
324 expectEquals (layout.getLowerZone().numMemberChannels, 10);
325 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
326 expectEquals (layout.getUpperZone().numMemberChannels, 4);
327
328
329 buffer = MPEMessages::setLowerZone (10, 33, 44);
330 layout.processNextMidiBuffer (buffer);
331
332 expectEquals (layout.getLowerZone().numMemberChannels, 10);
333 expectEquals (layout.getLowerZone().perNotePitchbendRange, 33);
334 expectEquals (layout.getLowerZone().masterPitchbendRange, 44);
335 }
336
337 {
338 buffer = MPEMessages::setUpperZone (10);
339 layout.processNextMidiBuffer (buffer);
340
341 expect (layout.getLowerZone().isActive());
342 expect (layout.getUpperZone().isActive());
343 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
344 expectEquals (layout.getLowerZone().numMemberChannels, 4);
345 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
346 expectEquals (layout.getUpperZone().numMemberChannels, 10);
347
348 buffer = MPEMessages::setUpperZone (10, 33, 44);
349
350 layout.processNextMidiBuffer (buffer);
351
352 expectEquals (layout.getUpperZone().numMemberChannels, 10);
353 expectEquals (layout.getUpperZone().perNotePitchbendRange, 33);
354 expectEquals (layout.getUpperZone().masterPitchbendRange, 44);
355 }
356
358 layout.processNextMidiBuffer (buffer);
359
360 expect (! layout.getLowerZone().isActive());
361 expect (! layout.getUpperZone().isActive());
362 }
363
364 beginTest ("process individual MIDI messages");
365 {
366 MPEZoneLayout layout;
367
368 layout.processNextMidiEvent ({ 0x80, 0x59, 0xd0 }); // unrelated note-off msg
369 layout.processNextMidiEvent ({ 0xb0, 0x64, 0x06 }); // RPN part 1
370 layout.processNextMidiEvent ({ 0xb0, 0x65, 0x00 }); // RPN part 2
371 layout.processNextMidiEvent ({ 0xb8, 0x0b, 0x66 }); // unrelated CC msg
372 layout.processNextMidiEvent ({ 0xb0, 0x06, 0x03 }); // RPN part 3
373 layout.processNextMidiEvent ({ 0x90, 0x60, 0x00 }); // unrelated note-on msg
374
375 expect (layout.getLowerZone().isActive());
376 expect (! layout.getUpperZone().isActive());
377 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
378 expectEquals (layout.getLowerZone().numMemberChannels, 3);
379 expectEquals (layout.getLowerZone().perNotePitchbendRange, 48);
380 expectEquals (layout.getLowerZone().masterPitchbendRange, 2);
381 }
382 }
383};
384
385static MPEZoneLayoutTests MPEZoneLayoutUnitTests;
386
387
388#endif
389
390} // namespace juce
void remove(int indexToRemove)
Definition juce_Array.h:767
Array()=default
void add(const ElementType &newElement)
Definition juce_Array.h:418
static MidiBuffer setUpperZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
static MidiBuffer clearAllZones()
static MidiBuffer setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
static const int zoneLayoutMessagesRpnNumber
void processNextMidiBuffer(const MidiBuffer &buffer)
void setUpperZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2) noexcept
void removeListener(Listener *const listenerToRemove) noexcept
void addListener(Listener *const listenerToAdd) noexcept
MPEZoneLayout & operator=(const MPEZoneLayout &other)
void setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2) noexcept
void processNextMidiEvent(const MidiMessage &message)
int getChannel() const noexcept
bool isController() const noexcept
int getControllerNumber() const noexcept
int getControllerValue() const noexcept
bool parseControllerMessage(int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage &result) noexcept