Skip to content

Commit f4ba4c1

Browse files
committed
MPEZoneLayout: Correctly handle 14-bit pitch-bend ranges
Previously, the MPEZoneLayout could only handle pitch-bend range adjustments that ended with the MSB. If the final controller message was the LSB, this resulted in the range being set as a 14-bit value, with a value 128 times higher than intended.
1 parent a35c8a9 commit f4ba4c1

File tree

1 file changed

+42
-4
lines changed

1 file changed

+42
-4
lines changed

modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,20 +169,31 @@ void MPEZoneLayout::updatePerNotePitchbendRange (MPEZone& zone, int value)
169169

170170
void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn)
171171
{
172+
// When the range is specified using both MSB and LSB, then MSB corresponds to whole semitones
173+
// and LSB corresponds to cents.
174+
const auto range = rpn.is14BitValue
175+
? std::div (rpn.value, 128)
176+
: div_t { rpn.value, 0 };
177+
178+
// If this is hit, the requested pitchbend range is not a whole number of semitones.
179+
// This isn't currently supported by JUCE - adding support would require
180+
// public API updates.
181+
jassert (range.rem == 0);
182+
172183
if (rpn.channel == 1)
173184
{
174-
updateMasterPitchbend (lowerZone, rpn.value);
185+
updateMasterPitchbend (lowerZone, range.quot);
175186
}
176187
else if (rpn.channel == 16)
177188
{
178-
updateMasterPitchbend (upperZone, rpn.value);
189+
updateMasterPitchbend (upperZone, range.quot);
179190
}
180191
else
181192
{
182193
if (lowerZone.isUsingChannelAsMemberChannel (rpn.channel))
183-
updatePerNotePitchbendRange (lowerZone, rpn.value);
194+
updatePerNotePitchbendRange (lowerZone, range.quot);
184195
else if (upperZone.isUsingChannelAsMemberChannel (rpn.channel))
185-
updatePerNotePitchbendRange (upperZone, rpn.value);
196+
updatePerNotePitchbendRange (upperZone, range.quot);
186197
}
187198
}
188199

@@ -424,6 +435,33 @@ class MPEZoneLayoutTests final : public UnitTest
424435

425436
expectEquals (layout.getLowerZone().masterPitchbendRange, newPitchBend);
426437
}
438+
439+
beginTest ("process 14-bit pitch bend sensitivity");
440+
{
441+
MPEZoneLayout layout;
442+
layout.setLowerZone (15);
443+
expect (layout.getLowerZone().isActive());
444+
445+
constexpr auto masterPitchBendA = 0x60;
446+
447+
// LSB first
448+
layout.processNextMidiEvent ({ 0xb0, 0x64, 0x00 }); // RPN part 1
449+
layout.processNextMidiEvent ({ 0xb0, 0x65, 0x00 }); // PRN part 2
450+
layout.processNextMidiEvent ({ 0xb0, 0x26, 0x00 }); // pitch bend cents
451+
layout.processNextMidiEvent ({ 0xb0, 0x06, masterPitchBendA }); // pitch bend semis
452+
453+
expectEquals (layout.getLowerZone().masterPitchbendRange, masterPitchBendA);
454+
455+
constexpr auto masterPitchBendB = 0x50;
456+
457+
// MSB first
458+
layout.processNextMidiEvent ({ 0xb0, 0x64, 0x00 }); // RPN part 1
459+
layout.processNextMidiEvent ({ 0xb0, 0x65, 0x00 }); // PRN part 2
460+
layout.processNextMidiEvent ({ 0xb0, 0x06, masterPitchBendB }); // pitch bend semis
461+
layout.processNextMidiEvent ({ 0xb0, 0x26, 0x00 }); // pitch bend cents
462+
463+
expectEquals (layout.getLowerZone().masterPitchbendRange, masterPitchBendB);
464+
}
427465
}
428466
};
429467

0 commit comments

Comments
 (0)