27 : lowerZone (lower), upperZone (upper)
32 : lowerZone (zone.isLowerZone() ? zone :
MPEZone()),
33 upperZone (! zone.isLowerZone() ? zone :
MPEZone())
39 : lowerZone (other.lowerZone),
40 upperZone (other.upperZone)
44MPEZoneLayout& MPEZoneLayout::operator= (
const MPEZoneLayout& other)
46 lowerZone = other.lowerZone;
47 upperZone = other.upperZone;
49 sendLayoutChangeMessage();
54void MPEZoneLayout::sendLayoutChangeMessage()
56 listeners.call ([
this] (Listener& l) { l.zoneLayoutChanged (*
this); });
60void MPEZoneLayout::setZone (
bool isLower,
int numMemberChannels,
int perNotePitchbendRange,
int masterPitchbendRange)
noexcept
62 checkAndLimitZoneParameters (0, 15, numMemberChannels);
63 checkAndLimitZoneParameters (0, 96, perNotePitchbendRange);
64 checkAndLimitZoneParameters (0, 96, masterPitchbendRange);
67 lowerZone = { MPEZone::Type::lower, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
69 upperZone = { MPEZone::Type::upper, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
71 if (numMemberChannels > 0)
73 auto totalChannels = lowerZone.numMemberChannels + upperZone.numMemberChannels;
75 if (totalChannels >= 15)
78 upperZone.numMemberChannels = 14 - numMemberChannels;
80 lowerZone.numMemberChannels = 14 - numMemberChannels;
84 sendLayoutChangeMessage();
89 setZone (
true, numMemberChannels, perNotePitchbendRange, masterPitchbendRange);
94 setZone (
false, numMemberChannels, perNotePitchbendRange, masterPitchbendRange);
99 lowerZone = { MPEZone::Type::lower, 0 };
100 upperZone = { MPEZone::Type::upper, 0 };
102 sendLayoutChangeMessage();
115 processRpnMessage (*parsed);
122 processZoneLayoutRpnMessage (rpn);
124 processPitchbendRangeRpnMessage (rpn);
127void MPEZoneLayout::processZoneLayoutRpnMessage (MidiRPNMessage rpn)
131 if (rpn.channel == 1)
133 else if (rpn.channel == 16)
138void MPEZoneLayout::updateMasterPitchbend (MPEZone& zone,
int value)
140 if (zone.masterPitchbendRange != value)
142 checkAndLimitZoneParameters (0, 96, zone.masterPitchbendRange);
143 zone.masterPitchbendRange = value;
144 sendLayoutChangeMessage();
148void MPEZoneLayout::updatePerNotePitchbendRange (MPEZone& zone,
int value)
150 if (zone.perNotePitchbendRange != value)
152 checkAndLimitZoneParameters (0, 96, zone.perNotePitchbendRange);
153 zone.perNotePitchbendRange = value;
154 sendLayoutChangeMessage();
158void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn)
160 if (rpn.channel == 1)
162 updateMasterPitchbend (lowerZone, rpn.value);
164 else if (rpn.channel == 16)
166 updateMasterPitchbend (upperZone, rpn.value);
170 if (lowerZone.isUsingChannelAsMemberChannel (rpn.channel))
171 updatePerNotePitchbendRange (lowerZone, rpn.value);
172 else if (upperZone.isUsingChannelAsMemberChannel (rpn.channel))
173 updatePerNotePitchbendRange (upperZone, rpn.value);
179 for (
const auto metadata : buffer)
186 listeners.add (listenerToAdd);
191 listeners.remove (listenerToRemove);
195void MPEZoneLayout::checkAndLimitZoneParameters (
int minValue,
int maxValue,
196 int& valueToCheckAndLimit)
noexcept
198 if (valueToCheckAndLimit < minValue || valueToCheckAndLimit > maxValue)
207 valueToCheckAndLimit = jlimit (minValue, maxValue, valueToCheckAndLimit);
216class MPEZoneLayoutTests final :
public UnitTest
220 : UnitTest (
"MPEZoneLayout class", UnitTestCategories::midi)
223 void runTest()
override
225 beginTest (
"initialisation");
227 MPEZoneLayout layout;
228 expect (! layout.getLowerZone().isActive());
229 expect (! layout.getUpperZone().isActive());
232 beginTest (
"adding zones");
234 MPEZoneLayout layout;
236 layout.setLowerZone (7);
238 expect (layout.getLowerZone().isActive());
239 expect (! layout.getUpperZone().isActive());
240 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
241 expectEquals (layout.getLowerZone().numMemberChannels, 7);
243 layout.setUpperZone (7);
245 expect (layout.getLowerZone().isActive());
246 expect (layout.getUpperZone().isActive());
247 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
248 expectEquals (layout.getLowerZone().numMemberChannels, 7);
249 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
250 expectEquals (layout.getUpperZone().numMemberChannels, 7);
252 layout.setLowerZone (3);
254 expect (layout.getLowerZone().isActive());
255 expect (layout.getUpperZone().isActive());
256 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
257 expectEquals (layout.getLowerZone().numMemberChannels, 3);
258 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
259 expectEquals (layout.getUpperZone().numMemberChannels, 7);
261 layout.setUpperZone (3);
263 expect (layout.getLowerZone().isActive());
264 expect (layout.getUpperZone().isActive());
265 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
266 expectEquals (layout.getLowerZone().numMemberChannels, 3);
267 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
268 expectEquals (layout.getUpperZone().numMemberChannels, 3);
270 layout.setLowerZone (15);
272 expect (layout.getLowerZone().isActive());
273 expect (! layout.getUpperZone().isActive());
274 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
275 expectEquals (layout.getLowerZone().numMemberChannels, 15);
278 beginTest (
"clear all zones");
280 MPEZoneLayout layout;
282 expect (! layout.getLowerZone().isActive());
283 expect (! layout.getUpperZone().isActive());
285 layout.setLowerZone (7);
286 layout.setUpperZone (2);
288 expect (layout.getLowerZone().isActive());
289 expect (layout.getUpperZone().isActive());
291 layout.clearAllZones();
293 expect (! layout.getLowerZone().isActive());
294 expect (! layout.getUpperZone().isActive());
297 beginTest (
"process MIDI buffers");
299 MPEZoneLayout layout;
302 buffer = MPEMessages::setLowerZone (7);
303 layout.processNextMidiBuffer (buffer);
305 expect (layout.getLowerZone().isActive());
306 expect (! layout.getUpperZone().isActive());
307 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
308 expectEquals (layout.getLowerZone().numMemberChannels, 7);
310 buffer = MPEMessages::setUpperZone (7);
311 layout.processNextMidiBuffer (buffer);
313 expect (layout.getLowerZone().isActive());
314 expect (layout.getUpperZone().isActive());
315 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
316 expectEquals (layout.getLowerZone().numMemberChannels, 7);
317 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
318 expectEquals (layout.getUpperZone().numMemberChannels, 7);
321 buffer = MPEMessages::setLowerZone (10);
322 layout.processNextMidiBuffer (buffer);
324 expect (layout.getLowerZone().isActive());
325 expect (layout.getUpperZone().isActive());
326 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
327 expectEquals (layout.getLowerZone().numMemberChannels, 10);
328 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
329 expectEquals (layout.getUpperZone().numMemberChannels, 4);
332 buffer = MPEMessages::setLowerZone (10, 33, 44);
333 layout.processNextMidiBuffer (buffer);
335 expectEquals (layout.getLowerZone().numMemberChannels, 10);
336 expectEquals (layout.getLowerZone().perNotePitchbendRange, 33);
337 expectEquals (layout.getLowerZone().masterPitchbendRange, 44);
341 buffer = MPEMessages::setUpperZone (10);
342 layout.processNextMidiBuffer (buffer);
344 expect (layout.getLowerZone().isActive());
345 expect (layout.getUpperZone().isActive());
346 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
347 expectEquals (layout.getLowerZone().numMemberChannels, 4);
348 expectEquals (layout.getUpperZone().getMasterChannel(), 16);
349 expectEquals (layout.getUpperZone().numMemberChannels, 10);
351 buffer = MPEMessages::setUpperZone (10, 33, 44);
353 layout.processNextMidiBuffer (buffer);
355 expectEquals (layout.getUpperZone().numMemberChannels, 10);
356 expectEquals (layout.getUpperZone().perNotePitchbendRange, 33);
357 expectEquals (layout.getUpperZone().masterPitchbendRange, 44);
360 buffer = MPEMessages::clearAllZones();
361 layout.processNextMidiBuffer (buffer);
363 expect (! layout.getLowerZone().isActive());
364 expect (! layout.getUpperZone().isActive());
367 beginTest (
"process individual MIDI messages");
369 MPEZoneLayout layout;
371 layout.processNextMidiEvent ({ 0x80, 0x59, 0xd0 });
372 layout.processNextMidiEvent ({ 0xb0, 0x64, 0x06 });
373 layout.processNextMidiEvent ({ 0xb0, 0x65, 0x00 });
374 layout.processNextMidiEvent ({ 0xb8, 0x0b, 0x66 });
375 layout.processNextMidiEvent ({ 0xb0, 0x06, 0x03 });
376 layout.processNextMidiEvent ({ 0x90, 0x60, 0x00 });
378 expect (layout.getLowerZone().isActive());
379 expect (! layout.getUpperZone().isActive());
380 expectEquals (layout.getLowerZone().getMasterChannel(), 1);
381 expectEquals (layout.getLowerZone().numMemberChannels, 3);
382 expectEquals (layout.getLowerZone().perNotePitchbendRange, 48);
383 expectEquals (layout.getLowerZone().masterPitchbendRange, 2);
385 const auto masterPitchBend = 0x0c;
386 layout.processNextMidiEvent ({ 0xb0, 0x64, 0x00 });
387 layout.processNextMidiEvent ({ 0xb0, 0x06, masterPitchBend });
389 expectEquals (layout.getLowerZone().masterPitchbendRange, masterPitchBend);
391 const auto newPitchBend = 0x0d;
392 layout.processNextMidiEvent ({ 0xb0, 0x06, newPitchBend });
394 expectEquals (layout.getLowerZone().masterPitchbendRange, newPitchBend);
399static MPEZoneLayoutTests MPEZoneLayoutUnitTests;
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
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
std::optional< MidiRPNMessage > tryParse(int midiChannel, int controllerNumber, int controllerValue)