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; pressureDimension.value = &MPENote::pressure;
timbreDimension.value = &MPENote::timbre; timbreDimension.value = &MPENote::timbre;
omniMode.isEnabled = false; legacyMode.isEnabled = false;
omniMode.pitchbendRange = 2; legacyMode.pitchbendRange = 2;
legacyMode.channelRange = Range<int> (1, 17);
} }
MPEInstrument::~MPEInstrument() MPEInstrument::~MPEInstrument()
@ -58,24 +59,53 @@ void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout)
releaseAllNotes(); releaseAllNotes();
const ScopedLock sl (lock); const ScopedLock sl (lock);
omniMode.isEnabled = false; legacyMode.isEnabled = false;
zoneLayout = newLayout; zoneLayout = newLayout;
} }
//============================================================================== //==============================================================================
void MPEInstrument::enableOmniMode (int pitchbendRange) void MPEInstrument::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
{ {
releaseAllNotes(); releaseAllNotes();
const ScopedLock sl (lock); const ScopedLock sl (lock);
omniMode.isEnabled = true; legacyMode.isEnabled = true;
omniMode.pitchbendRange = pitchbendRange; legacyMode.pitchbendRange = pitchbendRange;
legacyMode.channelRange = channelRange;
zoneLayout.clearAllZones(); 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) 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;) for (int i = notes.size(); --i >= 0;)
{ {
@ -267,7 +300,7 @@ void MPEInstrument::noteOn (int midiChannel,
int midiNoteNumber, int midiNoteNumber,
MPEValue midiNoteOnVelocity) MPEValue midiNoteOnVelocity)
{ {
if (! isNoteChannel (midiChannel) && ! omniMode.isEnabled) if (! isNoteChannel (midiChannel))
return; return;
MPENote newNote (midiChannel, MPENote newNote (midiChannel,
@ -299,7 +332,7 @@ void MPEInstrument::noteOff (int midiChannel,
int midiNoteNumber, int midiNoteNumber,
MPEValue midiNoteOffVelocity) MPEValue midiNoteOffVelocity)
{ {
if (notes.empty() || (! isNoteChannel (midiChannel) && ! omniMode.isEnabled)) if (notes.empty() || ! isNoteChannel (midiChannel))
return; return;
const ScopedLock sl (lock); const ScopedLock sl (lock);
@ -357,7 +390,7 @@ void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, M
{ {
updateDimensionMaster (*zone, dimension, value); updateDimensionMaster (*zone, dimension, value);
} }
else if (isNoteChannel (midiChannel) || omniMode.isEnabled) else if (isNoteChannel (midiChannel))
{ {
if (dimension.trackingMode == allNotesOnChannel) if (dimension.trackingMode == allNotesOnChannel)
{ {
@ -429,9 +462,9 @@ void MPEInstrument::callListenersDimensionChanged (MPENote& note, MPEDimension&
//============================================================================== //==============================================================================
void MPEInstrument::updateNoteTotalPitchbend (MPENote& note) 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 else
{ {
@ -465,18 +498,19 @@ void MPEInstrument::sostenutoPedal (int midiChannel, bool isDown)
//============================================================================== //==============================================================================
void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto) 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); MPEZone* affectedZone = zoneLayout.getZoneByMasterChannel (midiChannel);
if (affectedZone == nullptr && ! omniMode.isEnabled) if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (affectedZone == nullptr))
return; return;
for (int i = notes.size(); --i >= 0;) for (int i = notes.size(); --i >= 0;)
{ {
MPENote& note = notes.getReference (i); MPENote& note = notes.getReference (i);
if ((omniMode.isEnabled if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : affectedZone->isUsingChannel (note.midiChannel))
|| note.midiChannel == midiChannel)
|| affectedZone->isUsingChannel (note.midiChannel))
{ {
if (note.keyState == MPENote::keyDown && isDown) if (note.keyState == MPENote::keyDown && isDown)
note.keyState = MPENote::keyDownAndSustained; note.keyState = MPENote::keyDownAndSustained;
@ -499,7 +533,7 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool
if (! isSostenuto) if (! isSostenuto)
{ {
if (omniMode.isEnabled) if (legacyMode.isEnabled)
isNoteChannelSustained[midiChannel - 1] = isDown; isNoteChannelSustained[midiChannel - 1] = isDown;
else else
for (int i = affectedZone->getFirstNoteChannel(); i <= affectedZone->getLastNoteChannel(); ++i) 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 bool MPEInstrument::isNoteChannel (int midiChannel) const noexcept
{ {
if (legacyMode.isEnabled)
return legacyMode.channelRange.contains (midiChannel);
return zoneLayout.getZoneByNoteChannel (midiChannel) != nullptr; return zoneLayout.getZoneByNoteChannel (midiChannel) != nullptr;
} }
bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept
{ {
if (legacyMode.isEnabled)
return false;
return zoneLayout.getZoneByMasterChannel (midiChannel) != nullptr; return zoneLayout.getZoneByMasterChannel (midiChannel) != nullptr;
} }
@ -1672,10 +1712,10 @@ public:
expectEquals (test.getNumPlayingNotes(), 0); expectEquals (test.getNumPlayingNotes(), 0);
} }
beginTest ("MIDI all notes off (omni mode)"); beginTest ("MIDI all notes off (legacy mode)");
{ {
UnitTestInstrument test; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.noteOn (3, 60, MPEValue::from7BitInt (100)); test.noteOn (3, 60, MPEValue::from7BitInt (100));
test.noteOn (4, 61, MPEValue::from7BitInt (100)); test.noteOn (4, 61, MPEValue::from7BitInt (100));
test.noteOn (15, 62, 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); expectNote (test.getMostRecentNote (3), 100, 33, 4444, 55, MPENote::keyDown);
} }
beginTest ("Omni mode"); beginTest ("Legacy mode");
{ {
{ {
// basic check // basic check
MPEInstrument test; MPEInstrument test;
expect (! test.isOmniModeEnabled()); expect (! test.isLegacyModeEnabled());
test.setZoneLayout (testLayout); test.setZoneLayout (testLayout);
expect (! test.isOmniModeEnabled()); expect (! test.isLegacyModeEnabled());
test.enableOmniMode(); test.enableLegacyMode();
expect (test.isOmniModeEnabled()); expect (test.isLegacyModeEnabled());
test.setZoneLayout (testLayout); 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 // note on should trigger notes on all 16 channels
UnitTestInstrument test; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.noteOn (1, 60, MPEValue::from7BitInt (100)); test.noteOn (1, 60, MPEValue::from7BitInt (100));
test.noteOn (2, 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 (15, 60), 100, 100, 8192, 77, MPENote::keyDown);
expectNote (test.getNote (16, 60), 100, 100, 8192, 64, 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 (15, 60, MPEValue::from7BitInt (0));
test.noteOff (1, 60, MPEValue::from7BitInt (0)); test.noteOff (1, 60, MPEValue::from7BitInt (0));
@ -1767,10 +1828,31 @@ public:
expectEquals (test.getNumPlayingNotes(), 0); 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; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel); test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100)); test.noteOn (1, 60, MPEValue::from7BitInt (100));
@ -1783,7 +1865,7 @@ public:
} }
{ {
UnitTestInstrument test; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel); test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100)); test.noteOn (1, 60, MPEValue::from7BitInt (100));
@ -1796,7 +1878,7 @@ public:
} }
{ {
UnitTestInstrument test; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel); test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100)); test.noteOn (1, 60, MPEValue::from7BitInt (100));
@ -1809,7 +1891,7 @@ public:
} }
{ {
UnitTestInstrument test; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel); test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel);
test.noteOn (1, 60, MPEValue::from7BitInt (100)); 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; UnitTestInstrument test;
test.enableOmniMode (11); test.enableLegacyMode (11);
test.pitchbend (1, MPEValue::from14BitInt (4096)); test.pitchbend (1, MPEValue::from14BitInt (4096));
test.noteOn (1, 60, MPEValue::from7BitInt (100)); test.noteOn (1, 60, MPEValue::from7BitInt (100));
expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01); 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; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.sustainPedal (1, true); test.sustainPedal (1, true);
test.noteOn (2, 61, MPEValue::from7BitInt (100)); test.noteOn (2, 61, MPEValue::from7BitInt (100));
@ -1846,11 +1928,17 @@ public:
test.sustainPedal (1, false); test.sustainPedal (1, false);
expectEquals (test.getNumPlayingNotes(), 0); 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; UnitTestInstrument test;
test.enableOmniMode(); test.enableLegacyMode();
test.noteOn (1, 60, MPEValue::from7BitInt (100)); test.noteOn (1, 60, MPEValue::from7BitInt (100));
test.sostenutoPedal (1, true); test.sostenutoPedal (1, true);
@ -1863,6 +1951,11 @@ public:
test.sostenutoPedal (1, false); test.sostenutoPedal (1, false);
expectEquals (test.getNumPlayingNotes(), 0); 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 // all notes released when switching layout
@ -1871,7 +1964,7 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100)); test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 1); expectEquals (test.getNumPlayingNotes(), 1);
test.enableOmniMode(); test.enableLegacyMode();
expectEquals (test.getNumPlayingNotes(), 0); expectEquals (test.getNumPlayingNotes(), 0);
test.noteOn (3, 60, MPEValue::from7BitInt (100)); test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 1); expectEquals (test.getNumPlayingNotes(), 1);

View file

@ -61,7 +61,7 @@ public:
This will construct an MPE instrument with initially no MPE zones. This will construct an MPE instrument with initially no MPE zones.
In order to process incoming MIDI, call setZoneLayout, define the layout In order to process incoming MIDI, call setZoneLayout, define the layout
via MIDI RPN messages, or set the instrument to omni mode. via MIDI RPN messages, or set the instrument to legacy mode.
*/ */
MPEInstrument() noexcept; MPEInstrument() noexcept;
@ -72,7 +72,7 @@ public:
/** Returns the current zone layout of the instrument. /** Returns the current zone layout of the instrument.
This happens by value, to enforce thread-safety and class invariants. This happens by value, to enforce thread-safety and class invariants.
Note: If the instrument is in Omni mode, the return value of this Note: If the instrument is in legacy mode, the return value of this
method is unspecified. method is unspecified.
*/ */
MPEZoneLayout getZoneLayout() const noexcept; MPEZoneLayout getZoneLayout() const noexcept;
@ -81,38 +81,23 @@ public:
As a side effect, this will discard all currently playing notes, As a side effect, this will discard all currently playing notes,
and call noteReleased for all of them. and call noteReleased for all of them.
This will also disable Omni Mode in case it was enabled previously. This will also disable legacy mode in case it was enabled previously.
*/ */
void setZoneLayout (MPEZoneLayout newLayout); void setZoneLayout (MPEZoneLayout newLayout);
/** Returns true if the given MIDI channel (1-16) is a note channel in any /** Returns true if the given MIDI channel (1-16) is a note channel in any
of the MPEInstrument's MPE zones; false otherwise. of the MPEInstrument's MPE zones; false otherwise.
When in legacy mode, this will return true if the given channel is
contained in the current legacy mode channel range; false otherwise.
*/ */
bool isNoteChannel (int midiChannel) const noexcept; bool isNoteChannel (int midiChannel) const noexcept;
/** Returns true if the given MIDI channel (1-16) is a master channel in any /** Returns true if the given MIDI channel (1-16) is a master channel in any
of the MPEInstrument's MPE zones; false otherwise. of the MPEInstrument's MPE zones; false otherwise.
When in legacy mode, this will always return false.
*/ */
bool isMasterChannel (int midiChannel) const noexcept; bool isMasterChannel (int midiChannel) const noexcept;
/** Sets the instrument to Omni Mode.
As a side effect, this will discard all currently playing notes,
and call noteReleased for all of them.
This special zone layout mode is for backwards compatibility with
non-MPE MIDI devices. In this mode, the instrument will ignore the
current zone layout. It will instead treat all 16 MIDI channels as note
channels, with no master channel.
@param pitchbendRange The pitchbend range in semitones that should be
used while the instrument is in Omni mode. Must
be between 0 and 96, otherwise behaviour is undefined.
*/
void enableOmniMode (int pitchbendRange = 2);
/** Returns true if the instrument is in Omni mode, false otherwise. */
bool isOmniModeEnabled() const noexcept;
//========================================================================== //==========================================================================
/** The MPE note tracking mode. In case there is more than one note playing /** The MPE note tracking mode. In case there is more than one note playing
simultaneously on the same MIDI channel, this determines which of these simultaneously on the same MIDI channel, this determines which of these
@ -300,6 +285,44 @@ public:
/** Removes a listener. */ /** Removes a listener. */
void removeListener (Listener* const listenerToRemove) noexcept; void removeListener (Listener* const listenerToRemove) noexcept;
//==========================================================================
/** Puts the instrument into legacy mode.
As a side effect, this will discard all currently playing notes,
and call noteReleased for all of them.
This special zone layout mode is for backwards compatibility with
non-MPE MIDI devices. In this mode, the instrument will ignore the
current MPE zone layout. It will instead take a range of MIDI channels
(default: all channels 1-16) and treat them as note channels, with no
master channel. MIDI channels outside of this range will be ignored.
@param pitchbendRange The note pitchbend range in semitones to use when in legacy mode.
Must be between 0 and 96, otherwise behaviour is undefined.
The default pitchbend range in legacy mode is +/- 2 semitones.
@param channelRange The range of MIDI channels to use for notes when in legacy mode.
The default is to use all MIDI channels (1-16).
To get out of legacy mode, set a new MPE zone layout using setZoneLayout.
*/
void enableLegacyMode (int pitchbendRange = 2,
Range<int> channelRange = Range<int> (1, 17));
/** Returns true if the instrument is in legacy mode, false otherwise. */
bool isLegacyModeEnabled() const noexcept;
/** Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
Range<int> getLegacyModeChannelRange() const noexcept;
/** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
void setLegacyModeChannelRange (Range<int> channelRange);
/** Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
int getLegacyModePitchbendRange() const noexcept;
/** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
void setLegacyModePitchbendRange (int pitchbendRange);
protected: protected:
//========================================================================== //==========================================================================
/** This method defines what initial pitchbend value should be used for newly /** This method defines what initial pitchbend value should be used for newly
@ -341,9 +364,10 @@ private:
uint8 lastTimbreLowerBitReceivedOnChannel[16]; uint8 lastTimbreLowerBitReceivedOnChannel[16];
bool isNoteChannelSustained[16]; bool isNoteChannelSustained[16];
struct OmniMode struct LegacyMode
{ {
bool isEnabled; bool isEnabled;
Range<int> channelRange;
int pitchbendRange; int pitchbendRange;
}; };
@ -356,7 +380,7 @@ private:
MPEValue& getValue (MPENote& note) noexcept { return note.*(value); } MPEValue& getValue (MPENote& note) noexcept { return note.*(value); }
}; };
OmniMode omniMode; LegacyMode legacyMode;
MPEDimension pitchbendDimension, pressureDimension, timbreDimension; MPEDimension pitchbendDimension, pressureDimension, timbreDimension;
void updateDimension (int midiChannel, MPEDimension&, MPEValue); void updateDimension (int midiChannel, MPEDimension&, MPEValue);

View file

@ -50,14 +50,35 @@ void MPESynthesiserBase::setZoneLayout (MPEZoneLayout newLayout)
instrument->setZoneLayout (newLayout); instrument->setZoneLayout (newLayout);
} }
void MPESynthesiserBase::enableOmniMode (int pitchbendRange) //==============================================================================
void MPESynthesiserBase::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
{ {
instrument->enableOmniMode (pitchbendRange); instrument->enableLegacyMode (pitchbendRange, channelRange);
} }
bool MPESynthesiserBase::isOmniModeEnabled() const noexcept bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept
{ {
return instrument->isOmniModeEnabled(); return instrument->isLegacyModeEnabled();
}
Range<int> MPESynthesiserBase::getLegacyModeChannelRange() const noexcept
{
return instrument->getLegacyModeChannelRange();
}
void MPESynthesiserBase::setLegacyModeChannelRange (Range<int> channelRange)
{
instrument->setLegacyModeChannelRange (channelRange);
}
int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept
{
return instrument->getLegacyModePitchbendRange();
}
void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange)
{
instrument->setLegacyModePitchbendRange (pitchbendRange);
} }
//============================================================================== //==============================================================================

View file

@ -69,16 +69,10 @@ public:
/** Re-sets the synthesiser's internal MPE zone layout to the one passed in. /** Re-sets the synthesiser's internal MPE zone layout to the one passed in.
As a side effect, this will discard all currently playing notes, As a side effect, this will discard all currently playing notes,
call noteReleased for all of them, and disable Omni mode (if previously enabled). call noteReleased for all of them, and disable legacy mode (if previously enabled).
*/ */
void setZoneLayout (MPEZoneLayout newLayout); void setZoneLayout (MPEZoneLayout newLayout);
/** Sets the synthesiser to Omni mode. */
void enableOmniMode (int pitchbendRange = 2);
/** Returns true if the synthesiser is currently in Omni mode. */
bool isOmniModeEnabled() const noexcept;
//========================================================================== //==========================================================================
/** Tells the synthesiser what the sample rate is for the audio it's being /** Tells the synthesiser what the sample rate is for the audio it's being
used to render. used to render.
@ -136,6 +130,35 @@ public:
*/ */
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; void setMinimumRenderingSubdivisionSize (int numSamples) noexcept;
//==========================================================================
/** Puts the synthesiser into legacy mode.
@param pitchbendRange The note pitchbend range in semitones to use when in legacy mode.
Must be between 0 and 96, otherwise behaviour is undefined.
The default pitchbend range in legacy mode is +/- 2 semitones.
@param channelRange The range of MIDI channels to use for notes when in legacy mode.
The default is to use all MIDI channels (1-16).
To get out of legacy mode, set a new MPE zone layout using setZoneLayout.
*/
void enableLegacyMode (int pitchbendRange = 2,
Range<int> channelRange = Range<int> (1, 17));
/** Returns true if the instrument is in legacy mode, false otherwise. */
bool isLegacyModeEnabled() const noexcept;
/** Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
Range<int> getLegacyModeChannelRange() const noexcept;
/** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
void setLegacyModeChannelRange (Range<int> channelRange);
/** Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
int getLegacyModePitchbendRange() const noexcept;
/** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
void setLegacyModePitchbendRange (int pitchbendRange);
protected: protected:
//========================================================================== //==========================================================================
/** Implement this method to render your audio inside. /** Implement this method to render your audio inside.