1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

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.
This commit is contained in:
reuk 2026-01-06 13:09:26 +00:00
parent a35c8a97d2
commit f4ba4c1ad9
No known key found for this signature in database

View file

@ -169,20 +169,31 @@ void MPEZoneLayout::updatePerNotePitchbendRange (MPEZone& zone, int value)
void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn)
{
// When the range is specified using both MSB and LSB, then MSB corresponds to whole semitones
// and LSB corresponds to cents.
const auto range = rpn.is14BitValue
? std::div (rpn.value, 128)
: div_t { rpn.value, 0 };
// If this is hit, the requested pitchbend range is not a whole number of semitones.
// This isn't currently supported by JUCE - adding support would require
// public API updates.
jassert (range.rem == 0);
if (rpn.channel == 1)
{
updateMasterPitchbend (lowerZone, rpn.value);
updateMasterPitchbend (lowerZone, range.quot);
}
else if (rpn.channel == 16)
{
updateMasterPitchbend (upperZone, rpn.value);
updateMasterPitchbend (upperZone, range.quot);
}
else
{
if (lowerZone.isUsingChannelAsMemberChannel (rpn.channel))
updatePerNotePitchbendRange (lowerZone, rpn.value);
updatePerNotePitchbendRange (lowerZone, range.quot);
else if (upperZone.isUsingChannelAsMemberChannel (rpn.channel))
updatePerNotePitchbendRange (upperZone, rpn.value);
updatePerNotePitchbendRange (upperZone, range.quot);
}
}
@ -424,6 +435,33 @@ public:
expectEquals (layout.getLowerZone().masterPitchbendRange, newPitchBend);
}
beginTest ("process 14-bit pitch bend sensitivity");
{
MPEZoneLayout layout;
layout.setLowerZone (15);
expect (layout.getLowerZone().isActive());
constexpr auto masterPitchBendA = 0x60;
// LSB first
layout.processNextMidiEvent ({ 0xb0, 0x64, 0x00 }); // RPN part 1
layout.processNextMidiEvent ({ 0xb0, 0x65, 0x00 }); // PRN part 2
layout.processNextMidiEvent ({ 0xb0, 0x26, 0x00 }); // pitch bend cents
layout.processNextMidiEvent ({ 0xb0, 0x06, masterPitchBendA }); // pitch bend semis
expectEquals (layout.getLowerZone().masterPitchbendRange, masterPitchBendA);
constexpr auto masterPitchBendB = 0x50;
// MSB first
layout.processNextMidiEvent ({ 0xb0, 0x64, 0x00 }); // RPN part 1
layout.processNextMidiEvent ({ 0xb0, 0x65, 0x00 }); // PRN part 2
layout.processNextMidiEvent ({ 0xb0, 0x06, masterPitchBendB }); // pitch bend semis
layout.processNextMidiEvent ({ 0xb0, 0x26, 0x00 }); // pitch bend cents
expectEquals (layout.getLowerZone().masterPitchbendRange, masterPitchBendB);
}
}
};