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