1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

MPE: replaced omniMode (all MIDI channels) by legacyMode which allows to define a custom MIDI channel range. Fixed a few minor MPE bugs.

This commit is contained in:
Timur Doumler 2015-12-16 17:40:59 +00:00
parent 1fe45f3d41
commit e0bd51f26c
4 changed files with 236 additions and 75 deletions

View file

@ -39,8 +39,9 @@ MPEInstrument::MPEInstrument() noexcept
pressureDimension.value = &MPENote::pressure;
timbreDimension.value = &MPENote::timbre;
omniMode.isEnabled = false;
omniMode.pitchbendRange = 2;
legacyMode.isEnabled = false;
legacyMode.pitchbendRange = 2;
legacyMode.channelRange = Range<int> (1, 17);
}
MPEInstrument::~MPEInstrument()
@ -58,24 +59,53 @@ void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout)
releaseAllNotes();
const ScopedLock sl (lock);
omniMode.isEnabled = false;
legacyMode.isEnabled = false;
zoneLayout = newLayout;
}
//==============================================================================
void MPEInstrument::enableOmniMode (int pitchbendRange)
void MPEInstrument::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
{
releaseAllNotes();
const ScopedLock sl (lock);
omniMode.isEnabled = true;
omniMode.pitchbendRange = pitchbendRange;
legacyMode.isEnabled = true;
legacyMode.pitchbendRange = pitchbendRange;
legacyMode.channelRange = channelRange;
zoneLayout.clearAllZones();
}
bool MPEInstrument::isOmniModeEnabled() const noexcept
bool MPEInstrument::isLegacyModeEnabled() const noexcept
{
return omniMode.isEnabled;
return legacyMode.isEnabled;
}
Range<int> MPEInstrument::getLegacyModeChannelRange() const noexcept
{
return legacyMode.channelRange;
}
void MPEInstrument::setLegacyModeChannelRange (Range<int> channelRange)
{
jassert (Range<int>(1, 17).contains (channelRange));
releaseAllNotes();
const ScopedLock sl (lock);
legacyMode.channelRange = channelRange;
}
int MPEInstrument::getLegacyModePitchbendRange() const noexcept
{
return legacyMode.pitchbendRange;
}
void MPEInstrument::setLegacyModePitchbendRange (int pitchbendRange)
{
jassert (pitchbendRange >= 0 && pitchbendRange <= 96);
releaseAllNotes();
const ScopedLock sl (lock);
legacyMode.pitchbendRange = pitchbendRange;
}
//==============================================================================
@ -187,7 +217,10 @@ void MPEInstrument::processMidiControllerMessage (const MidiMessage& message)
//==============================================================================
void MPEInstrument::processMidiAllNotesOffMessage (const MidiMessage& message)
{
if (omniMode.isEnabled)
// in MPE mode, "all notes off" is per-zone and expected on the master channel;
// in legacy mode, "all notes off" is per MIDI channel (within the channel range used).
if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel()))
{
for (int i = notes.size(); --i >= 0;)
{
@ -267,7 +300,7 @@ void MPEInstrument::noteOn (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOnVelocity)
{
if (! isNoteChannel (midiChannel) && ! omniMode.isEnabled)
if (! isNoteChannel (midiChannel))
return;
MPENote newNote (midiChannel,
@ -299,7 +332,7 @@ void MPEInstrument::noteOff (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOffVelocity)
{
if (notes.empty() || (! isNoteChannel (midiChannel) && ! omniMode.isEnabled))
if (notes.empty() || ! isNoteChannel (midiChannel))
return;
const ScopedLock sl (lock);
@ -357,7 +390,7 @@ void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, M
{
updateDimensionMaster (*zone, dimension, value);
}
else if (isNoteChannel (midiChannel) || omniMode.isEnabled)
else if (isNoteChannel (midiChannel))
{
if (dimension.trackingMode == allNotesOnChannel)
{
@ -429,9 +462,9 @@ void MPEInstrument::callListenersDimensionChanged (MPENote& note, MPEDimension&
//==============================================================================
void MPEInstrument::updateNoteTotalPitchbend (MPENote& note)
{
if (omniMode.isEnabled)
if (legacyMode.isEnabled)
{
note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * omniMode.pitchbendRange;
note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * legacyMode.pitchbendRange;
}
else
{
@ -465,18 +498,19 @@ void MPEInstrument::sostenutoPedal (int midiChannel, bool isDown)
//==============================================================================
void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto)
{
// in MPE mode, sustain/sostenuto is per-zone and expected on the master channel;
// in legacy mode, sustain/sostenuto is per MIDI channel (within the channel range used).
MPEZone* affectedZone = zoneLayout.getZoneByMasterChannel (midiChannel);
if (affectedZone == nullptr && ! omniMode.isEnabled)
if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (affectedZone == nullptr))
return;
for (int i = notes.size(); --i >= 0;)
{
MPENote& note = notes.getReference (i);
if ((omniMode.isEnabled
|| note.midiChannel == midiChannel)
|| affectedZone->isUsingChannel (note.midiChannel))
if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : affectedZone->isUsingChannel (note.midiChannel))
{
if (note.keyState == MPENote::keyDown && isDown)
note.keyState = MPENote::keyDownAndSustained;
@ -499,7 +533,7 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool
if (! isSostenuto)
{
if (omniMode.isEnabled)
if (legacyMode.isEnabled)
isNoteChannelSustained[midiChannel - 1] = isDown;
else
for (int i = affectedZone->getFirstNoteChannel(); i <= affectedZone->getLastNoteChannel(); ++i)
@ -510,11 +544,17 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool
//==============================================================================
bool MPEInstrument::isNoteChannel (int midiChannel) const noexcept
{
if (legacyMode.isEnabled)
return legacyMode.channelRange.contains (midiChannel);
return zoneLayout.getZoneByNoteChannel (midiChannel) != nullptr;
}
bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept
{
if (legacyMode.isEnabled)
return false;
return zoneLayout.getZoneByMasterChannel (midiChannel) != nullptr;
}
@ -1672,10 +1712,10 @@ public:
expectEquals (test.getNumPlayingNotes(), 0);
}
beginTest ("MIDI all notes off (omni mode)");
beginTest ("MIDI all notes off (legacy mode)");
{
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.noteOn (3, 60, MPEValue::from7BitInt (100));
test.noteOn (4, 61, MPEValue::from7BitInt (100));
test.noteOn (15, 62, MPEValue::from7BitInt (100));
@ -1719,27 +1759,48 @@ public:
expectNote (test.getMostRecentNote (3), 100, 33, 4444, 55, MPENote::keyDown);
}
beginTest ("Omni mode");
beginTest ("Legacy mode");
{
{
// basic check
MPEInstrument test;
expect (! test.isOmniModeEnabled());
expect (! test.isLegacyModeEnabled());
test.setZoneLayout (testLayout);
expect (! test.isOmniModeEnabled());
expect (! test.isLegacyModeEnabled());
test.enableOmniMode();
expect (test.isOmniModeEnabled());
test.enableLegacyMode();
expect (test.isLegacyModeEnabled());
test.setZoneLayout (testLayout);
expect (! test.isOmniModeEnabled());
expect (! test.isLegacyModeEnabled());
}
{
// constructor w/o default arguments
MPEInstrument test;
test.enableLegacyMode (0, Range<int> (1, 11));
expectEquals (test.getLegacyModePitchbendRange(), 0);
expect (test.getLegacyModeChannelRange() == Range<int> (1, 11));
}
{
// getters and setters
MPEInstrument test;
test.enableLegacyMode();
expectEquals (test.getLegacyModePitchbendRange(), 2);
expect (test.getLegacyModeChannelRange() == Range<int> (1, 17));
test.setLegacyModePitchbendRange (96);
expectEquals (test.getLegacyModePitchbendRange(), 96);
test.setLegacyModeChannelRange (Range<int> (10, 12));
expect (test.getLegacyModeChannelRange() == Range<int> (10, 12));
}
{
// note on should trigger notes on all 16 channels
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.noteOn (1, 60, MPEValue::from7BitInt (100));
test.noteOn (2, 60, MPEValue::from7BitInt (100));
@ -1758,7 +1819,7 @@ public:
expectNote (test.getNote (15, 60), 100, 100, 8192, 77, MPENote::keyDown);
expectNote (test.getNote (16, 60), 100, 100, 8192, 64, MPENote::keyDown);
// note off should work in omni mode
// note off should work in legacy mode
test.noteOff (15, 60, MPEValue::from7BitInt (0));
test.noteOff (1, 60, MPEValue::from7BitInt (0));
@ -1767,10 +1828,31 @@ public:
expectEquals (test.getNumPlayingNotes(), 0);
}
{
// tracking mode in omni mode
// legacy mode w/ custom channel range: note on should trigger notes only within range
UnitTestInstrument test;
test.enableLegacyMode (2, Range<int> (3, 8)); // channels 3-7
test.noteOn (1, 60, MPEValue::from7BitInt (100));
test.noteOn (2, 60, MPEValue::from7BitInt (100));
test.noteOn (3, 60, MPEValue::from7BitInt (100)); // should trigger
test.noteOn (4, 60, MPEValue::from7BitInt (100)); // should trigger
test.noteOn (6, 60, MPEValue::from7BitInt (100)); // should trigger
test.noteOn (7, 60, MPEValue::from7BitInt (100)); // should trigger
test.noteOn (8, 60, MPEValue::from7BitInt (100));
test.noteOn (16, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 4);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (6, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (7, 60), 100, 100, 8192, 64, MPENote::keyDown);
}
{
// tracking mode in legacy mode
{
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100));
@ -1783,7 +1865,7 @@ public:
}
{
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100));
@ -1796,7 +1878,7 @@ public:
}
{
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100));
@ -1809,7 +1891,7 @@ public:
}
{
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100));
@ -1822,18 +1904,18 @@ public:
}
}
{
// custom pitchbend range in omni mode.
// custom pitchbend range in legacy mode.
UnitTestInstrument test;
test.enableOmniMode (11);
test.enableLegacyMode (11);
test.pitchbend (1, MPEValue::from14BitInt (4096));
test.noteOn (1, 60, MPEValue::from7BitInt (100));
expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01);
}
{
// sustain pedal should be per channel in omni mode.
// sustain pedal should be per channel in legacy mode.
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.sustainPedal (1, true);
test.noteOn (2, 61, MPEValue::from7BitInt (100));
@ -1846,11 +1928,17 @@ public:
test.sustainPedal (1, false);
expectEquals (test.getNumPlayingNotes(), 0);
test.noteOn (2, 61, MPEValue::from7BitInt (100));
test.sustainPedal (1, true);
test.noteOff (2, 61, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 0);
}
{
// sostenuto pedal should be per channel in omni mode.
// sostenuto pedal should be per channel in legacy mode.
UnitTestInstrument test;
test.enableOmniMode();
test.enableLegacyMode();
test.noteOn (1, 60, MPEValue::from7BitInt (100));
test.sostenutoPedal (1, true);
@ -1863,6 +1951,11 @@ public:
test.sostenutoPedal (1, false);
expectEquals (test.getNumPlayingNotes(), 0);
test.noteOn (2, 61, MPEValue::from7BitInt (100));
test.sostenutoPedal (1, true);
test.noteOff (2, 61, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 0);
}
{
// all notes released when switching layout
@ -1871,7 +1964,7 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 1);
test.enableOmniMode();
test.enableLegacyMode();
expectEquals (test.getNumPlayingNotes(), 0);
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 1);