The midi channels should now be 0-based everywhere

This commit is contained in:
Paul Ferrand 2019-12-15 23:48:21 +01:00
parent aba9c15e50
commit 194816ef58
12 changed files with 133 additions and 150 deletions

View file

@ -61,6 +61,7 @@ namespace Default
// Region logic: MIDI conditions
constexpr Range<uint8_t> channelRange { 1, 16 };
constexpr Range<uint8_t> midiChannelRange { 0, 15 };
constexpr Range<uint8_t> ccRange { 0, 127 };
constexpr uint8_t cc { 0 };
constexpr Range<int> bendRange { -8192, 8192 };

View file

@ -8,10 +8,10 @@ sfz::MidiState::MidiState()
void sfz::MidiState::noteOnEvent(int channel, int noteNumber, uint8_t velocity) noexcept
{
ASSERT(channel >= 1 && channel <= 16);
ASSERT(channel >= 0 && channel < 16);
ASSERT(noteNumber >= 0 && noteNumber <= 127);
ASSERT(velocity >= 0 && velocity <= 127);
channel = translateSfzChannelToMidi(channel);
if (noteNumber >= 0 && noteNumber < 128) {
lastNoteVelocities[channel][noteNumber] = velocity;
noteOnTimes[channel][noteNumber] = std::chrono::steady_clock::now();
@ -20,9 +20,9 @@ void sfz::MidiState::noteOnEvent(int channel, int noteNumber, uint8_t velocity)
float sfz::MidiState::getNoteDuration(int channel, int noteNumber) const
{
ASSERT(channel >= 1 && channel <= 16);
ASSERT(channel >= 0 && channel < 16);
ASSERT(noteNumber >= 0 && noteNumber <= 127);
channel = translateSfzChannelToMidi(channel);
if (noteNumber >= 0 && noteNumber < 128) {
const auto noteOffTime = std::chrono::steady_clock::now();
const auto duration = std::chrono::duration_cast<std::chrono::duration<float>>(noteOffTime - noteOnTimes[channel][noteNumber]);
@ -34,48 +34,48 @@ float sfz::MidiState::getNoteDuration(int channel, int noteNumber) const
uint8_t sfz::MidiState::getNoteVelocity(int channel, int noteNumber) const noexcept
{
ASSERT(channel >= 1 && channel <= 16);
ASSERT(channel >= 0 && channel < 16);
ASSERT(noteNumber >= 0 && noteNumber <= 127);
channel = translateSfzChannelToMidi(channel);
return lastNoteVelocities[channel][noteNumber];
}
void sfz::MidiState::pitchBendEvent(int channel, int pitchBendValue) noexcept
{
ASSERT(channel >= 1 && channel <= 16);
ASSERT(channel >= 0 && channel < 16);
ASSERT(pitchBendValue >= -8192 && pitchBendValue <= 8192);
channel = translateSfzChannelToMidi(channel);
pitchBends[channel] = pitchBendValue;
}
int sfz::MidiState::getPitchBend(int channel) const noexcept
{
ASSERT(channel >= 1 && channel <= 16);
channel = translateSfzChannelToMidi(channel);
ASSERT(channel >= 0 && channel < 16);
return pitchBends[channel];
}
void sfz::MidiState::ccEvent(int channel, int ccNumber, uint8_t ccValue) noexcept
{
ASSERT(channel >= 1 && channel <= 16);
ASSERT(channel >= 0 && channel < 16);
ASSERT(ccNumber >= 0 && ccNumber <= 127);
ASSERT(ccValue >= 0 && ccValue <= 127);
channel = translateSfzChannelToMidi(channel);
cc[channel][ccNumber] = ccValue;
}
uint8_t sfz::MidiState::getCCValue(int channel, int ccNumber) const noexcept
{
ASSERT(channel >= 1 && channel <= 16);
ASSERT(channel >= 0 && channel < 16);
ASSERT(ccNumber >= 0 && ccNumber <= 127);
channel = translateSfzChannelToMidi(channel);
return cc[channel][ccNumber];
}
const sfz::CCValueArray& sfz::MidiState::getCCArray(int channel) const noexcept
{
ASSERT(channel >= 1 && channel <= 16);
channel = translateSfzChannelToMidi(channel);
ASSERT(channel >= 0 && channel < 16);
return cc[channel];
}

View file

@ -18,7 +18,7 @@ public:
/**
* @brief Update the state after a note on event
*
* @param channel (1-based)
* @param channel (0-based)
* @param noteNumber
* @param velocity
*/
@ -27,7 +27,7 @@ public:
/**
* @brief Register a note off and get the note duration
*
* @param channel (1-based)
* @param channel (0-based)
* @param noteNumber
* @return float
*/
@ -36,7 +36,7 @@ public:
/**
* @brief Get the note on velocity for a given note
*
* @param channel (1-based)
* @param channel (0-based)
* @param noteNumber
* @return uint8_t
*/

View file

@ -134,10 +134,18 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
// Region logic: MIDI conditions
case hash("lochan"):
setRangeStartFromOpcode(opcode, channelRange, Default::channelRange);
{
const auto value = readOpcode(opcode.value, Default::channelRange);
if (value)
channelRange.setStart(*value - 1);
}
break;
case hash("hichan"):
setRangeEndFromOpcode(opcode, channelRange, Default::channelRange);
{
const auto value = readOpcode(opcode.value, Default::channelRange);
if (value)
channelRange.setEnd(*value - 1);
}
break;
case hash("lobend"):
setRangeStartFromOpcode(opcode, bendRange, Default::bendRange);

View file

@ -93,7 +93,7 @@ struct Region {
* @brief Register a new note on event. The region may be switched on or off using keys so
* this function updates the keyswitches state.
*
* @param channel MIDI channel (1-based)
* @param channel MIDI channel (0-based)
* @param noteNumber
* @param velocity
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
@ -106,7 +106,7 @@ struct Region {
* @brief Register a new note off event. The region may be switched on or off using keys so
* this function updates the keyswitches state.
*
* @param channel MIDI channel (1-based)
* @param channel MIDI channel (0-based)
* @param noteNumber
* @param velocity
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
@ -119,7 +119,7 @@ struct Region {
* @brief Register a new CC event. The region may be switched on or off using CCs so
* this function checks if it indeeds need to activate or not.
*
* @param channel MIDI channel (1-based)
* @param channel MIDI channel (0-based)
* @param ccNumber
* @param ccValue
* @return true if the region should trigger on this event
@ -129,14 +129,14 @@ struct Region {
/**
* @brief Register a new pitch wheel event.
*
* @param channel MIDI channel (1-based)
* @param channel MIDI channel (0-based)
* @param pitch
*/
void registerPitchWheel(int channel, int pitch) noexcept;
/**
* @brief Register a new aftertouch event.
*
* @param channel MIDI channel (1-based)
* @param channel MIDI channel (0-based)
* @param aftertouch
*/
void registerAftertouch(int channel, uint8_t aftertouch) noexcept;
@ -177,7 +177,7 @@ struct Region {
* @brief Get the base volume of the region depending on which note has been
* pressed to trigger the region.
*
* @param channel (1-based)
* @param channel (0-based)
* @param noteNumber
* @return float
*/
@ -247,7 +247,7 @@ struct Region {
Range<uint8_t> velocityRange { Default::velocityRange }; // hivel and lovel
// Region logic: MIDI conditions
Range<uint8_t> channelRange { Default::channelRange }; //lochan and hichan
Range<uint8_t> channelRange { Default::midiChannelRange }; //lochan and hichan
Range<int> bendRange { Default::bendRange }; // hibend and lobend
CCMap<Range<uint8_t>> ccConditions { Default::ccRange };
Range<uint8_t> keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey

View file

@ -35,28 +35,6 @@ using CCValueArray = std::array<uint8_t, 128>;
using CCValuePair = std::pair<uint8_t, float> ;
using CCNamePair = std::pair<uint8_t, std::string>;
/**
* @brief Translates the zero-based MIDI channel to the 1-based SFZ channel
*
* @param zeroBasedChannel
* @return constexpr int
*/
constexpr int translateMidiChannelToSfz(int zeroBasedChannel)
{
return zeroBasedChannel + 1;
}
/**
* @brief Translates the 1-based SFZ channel to the 0-based MIDI channel
*
* @param zeroBasedChannel
* @return constexpr int
*/
constexpr int translateSfzChannelToMidi(int oneBasedChannel)
{
return oneBasedChannel - 1;
}
/**
* @brief Converts cents to a pitch ratio
*

View file

@ -380,7 +380,6 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0);
channel = translateMidiChannelToSfz(channel);
midiState.noteOnEvent(channel, noteNumber, velocity);
AtomicGuard callbackGuard { inCallback };
@ -410,7 +409,6 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0);
channel = translateMidiChannelToSfz(channel);
AtomicGuard callbackGuard { inCallback };
if (!canEnterCallback)
return;
@ -440,7 +438,6 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc
ASSERT(ccNumber < 128);
ASSERT(ccNumber >= 0);
channel = translateMidiChannelToSfz(channel);
AtomicGuard callbackGuard { inCallback };
if (!canEnterCallback)
@ -466,15 +463,14 @@ void sfz::Synth::pitchWheel(int delay, int channel, int pitch) noexcept
{
ASSERT(pitch <= 8192);
ASSERT(pitch >= -8192);
channel = translateMidiChannelToSfz(channel);
midiState.pitchBendEvent(channel, pitch);
for (auto& voice: voices) {
voice->registerPitchWheel(delay, channel, pitch);
}
}
void sfz::Synth::aftertouch(int /* delay */, int channel, uint8_t /* aftertouch */) noexcept
void sfz::Synth::aftertouch(int /* delay */, int /*channel*/, uint8_t /* aftertouch */) noexcept
{
channel = translateMidiChannelToSfz(channel);
}
void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept
{

View file

@ -92,7 +92,7 @@ public:
*
* @param region
* @param delay
* @param channel
* @param channel (0-based)
* @param number
* @param value
* @param triggerType
@ -103,7 +103,7 @@ public:
* @brief Register a note-off event; this may trigger a release.
*
* @param delay
* @param channel
* @param channel (0-based)
* @param noteNumber
* @param velocity
*/
@ -114,7 +114,7 @@ public:
* parameter.
*
* @param delay
* @param channel
* @param channel (0-based)
* @param ccNumber
* @param ccValue
*/
@ -123,7 +123,7 @@ public:
* @brief Register a pitch wheel event; for now this does nothing
*
* @param delay
* @param channel
* @param channel (0-based)
* @param pitch
*/
void registerPitchWheel(int delay, int channel, int pitch) noexcept;
@ -131,7 +131,7 @@ public:
* @brief Register an aftertouch event; for now this does nothing
*
* @param delay
* @param channel
* @param channel (0-based)
* @param aftertouch
*/
void registerAftertouch(int delay, int channel, uint8_t aftertouch) noexcept;
@ -139,7 +139,7 @@ public:
* @brief Register a tempo event; for now this does nothing
*
* @param delay
* @param channel
* @param channel (0-based)
* @param pitch
*/
void registerTempo(int delay, float secondsPerQuarter) noexcept;
@ -243,7 +243,7 @@ private:
/**
* @brief Computes the values for the envelope depending on the note or CC number and the velocity/cc value
*
* @param channel
* @param channel (0-based)
* @param delay
* @param velocity
*/

View file

@ -50,7 +50,7 @@ TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
sfz::Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->channelRange == sfz::Range<uint8_t>(2, 14));
REQUIRE(synth.getRegionView(0)->channelRange == sfz::Range<uint8_t>(1, 13));
}
TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")

View file

@ -39,20 +39,20 @@ TEST_CASE("[MidiState] Initial values")
REQUIRE( cc == 0 );
for (auto& cc: state.getCCArray(6))
REQUIRE( cc == 0 );
for (int channel = 1; channel <= 16; ++channel)
for (int channel = 0; channel < 16; ++channel)
REQUIRE( state.getPitchBend(channel) == 0 );
}
TEST_CASE("[MidiState] Set and get CCs")
{
sfz::MidiState state;
const auto& cc1 = state.getCCArray(1);
const auto& cc0 = state.getCCArray(0);
const auto& cc6 = state.getCCArray(6);
const auto& cc12 = state.getCCArray(12);
state.ccEvent(1, 24, 23);
state.ccEvent(0, 24, 23);
state.ccEvent(6, 123, 124);
REQUIRE(state.getCCValue(1, 24) == 23);
REQUIRE(cc1[24] == 23);
REQUIRE(state.getCCValue(0, 24) == 23);
REQUIRE(cc0[24] == 23);
REQUIRE(state.getCCValue(6, 123) == 124);
REQUIRE(cc6[123] == 124);
REQUIRE(+state.getCCValue(12, 24) == 0);
@ -64,33 +64,33 @@ TEST_CASE("[MidiState] Set and get CCs")
TEST_CASE("[MidiState] Set and get pitch bends")
{
sfz::MidiState state;
state.pitchBendEvent(1, 894);
REQUIRE(state.getPitchBend(1) == 894);
state.pitchBendEvent(0, 894);
REQUIRE(state.getPitchBend(0) == 894);
REQUIRE(state.getPitchBend(6) == 0);
state.pitchBendEvent(1, 0);
REQUIRE(state.getPitchBend(1) == 0);
state.pitchBendEvent(0, 0);
REQUIRE(state.getPitchBend(0) == 0);
REQUIRE(state.getPitchBend(6) == 0);
}
TEST_CASE("[MidiState] Reset")
{
sfz::MidiState state;
state.pitchBendEvent(1, 894);
state.pitchBendEvent(0, 894);
state.noteOnEvent(6, 64, 24);
state.ccEvent(16, 123, 124);
state.ccEvent(15, 123, 124);
state.reset();
REQUIRE(state.getPitchBend(1) == 0);
REQUIRE(state.getPitchBend(0) == 0);
REQUIRE(state.getNoteVelocity(6, 64) == 0);
REQUIRE(state.getCCValue(16, 123) == 0);
REQUIRE(state.getCCValue(15, 123) == 0);
}
TEST_CASE("[MidiState] Set and get note velocities")
{
sfz::MidiState state;
state.noteOnEvent(1, 64, 24);
REQUIRE(+state.getNoteVelocity(1, 64) == 24);
REQUIRE(+state.getNoteVelocity(2, 64) == 0);
state.noteOnEvent(1, 64, 123);
REQUIRE(+state.getNoteVelocity(1, 64) == 123);
REQUIRE(+state.getNoteVelocity(16, 64) == 0);
state.noteOnEvent(0, 64, 24);
REQUIRE(+state.getNoteVelocity(0, 64) == 24);
REQUIRE(+state.getNoteVelocity(1, 64) == 0);
state.noteOnEvent(0, 64, 123);
REQUIRE(+state.getNoteVelocity(0, 64) == 123);
REQUIRE(+state.getNoteVelocity(15, 64) == 0);
}

View file

@ -238,19 +238,19 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("lochan, hichan")
{
REQUIRE(region.channelRange == sfz::Range<uint8_t>(1, 16));
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 15));
region.parseOpcode({ "lochan", "4" });
REQUIRE(region.channelRange == sfz::Range<uint8_t>(4, 16));
REQUIRE(region.channelRange == sfz::Range<uint8_t>(3, 15));
region.parseOpcode({ "lochan", "128" });
REQUIRE(region.channelRange == sfz::Range<uint8_t>(16, 16));
REQUIRE(region.channelRange == sfz::Range<uint8_t>(15, 15));
region.parseOpcode({ "lochan", "-3" });
REQUIRE(region.channelRange == sfz::Range<uint8_t>(1, 16));
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 15));
region.parseOpcode({ "hichan", "13" });
REQUIRE(region.channelRange == sfz::Range<uint8_t>(1, 13));
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 12));
region.parseOpcode({ "hichan", "-1" });
REQUIRE(region.channelRange == sfz::Range<uint8_t>(1, 1));
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 0));
region.parseOpcode({ "hichan", "128" });
REQUIRE(region.channelRange == sfz::Range<uint8_t>(1, 16));
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 15));
}
SECTION("lobend, hibend")

View file

@ -34,44 +34,44 @@ TEST_CASE("Basic triggers", "Region triggers")
SECTION("key")
{
region.parseOpcode({ "key", "40" });
REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE(!region.registerCC(1, 63, 64));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOff(0, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
REQUIRE(!region.registerCC(0, 63, 64));
}
SECTION("lokey and hikey")
{
region.parseOpcode({ "lokey", "40" });
region.parseOpcode({ "hikey", "42" });
REQUIRE(!region.registerNoteOn(1, 39, 64, 0.5f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE(region.registerNoteOn(1, 42, 64, 0.5f));
REQUIRE(!region.registerNoteOn(1, 43, 64, 0.5f));
REQUIRE(!region.registerNoteOff(1, 42, 64, 0.5f));
REQUIRE(!region.registerNoteOff(1, 42, 64, 0.5f));
REQUIRE(!region.registerCC(1, 63, 64));
REQUIRE(!region.registerNoteOn(0, 39, 64, 0.5f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOff(0, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(0, 41, 64, 0.5f));
REQUIRE(region.registerNoteOn(0, 42, 64, 0.5f));
REQUIRE(!region.registerNoteOn(0, 43, 64, 0.5f));
REQUIRE(!region.registerNoteOff(0, 42, 64, 0.5f));
REQUIRE(!region.registerNoteOff(0, 42, 64, 0.5f));
REQUIRE(!region.registerCC(0, 63, 64));
}
SECTION("key and release trigger")
{
region.parseOpcode({ "key", "40" });
region.parseOpcode({ "trigger", "release" });
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE(!region.registerNoteOff(1, 41, 64, 0.5f));
REQUIRE(!region.registerCC(1, 63, 64));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(region.registerNoteOff(0, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
REQUIRE(!region.registerNoteOff(0, 41, 64, 0.5f));
REQUIRE(!region.registerCC(0, 63, 64));
}
SECTION("key and release_key trigger")
{
region.parseOpcode({ "key", "40" });
region.parseOpcode({ "trigger", "release_key" });
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE(!region.registerNoteOff(1, 41, 64, 0.5f));
REQUIRE(!region.registerCC(1, 63, 64));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(region.registerNoteOff(0, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
REQUIRE(!region.registerNoteOff(0, 41, 64, 0.5f));
REQUIRE(!region.registerCC(0, 63, 64));
}
// TODO: first and legato triggers
SECTION("lovel and hivel")
@ -79,22 +79,22 @@ TEST_CASE("Basic triggers", "Region triggers")
region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lovel", "60" });
region.parseOpcode({ "hivel", "70" });
REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(1, 40, 60, 0.5f));
REQUIRE(region.registerNoteOn(1, 40, 70, 0.5f));
REQUIRE(!region.registerNoteOn(1, 41, 71, 0.5f));
REQUIRE(!region.registerNoteOn(1, 41, 59, 0.5f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(0, 40, 60, 0.5f));
REQUIRE(region.registerNoteOn(0, 40, 70, 0.5f));
REQUIRE(!region.registerNoteOn(0, 41, 71, 0.5f));
REQUIRE(!region.registerNoteOn(0, 41, 59, 0.5f));
}
SECTION("lochan and hichan")
{
region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lochan", "2" });
region.parseOpcode({ "hichan", "4" });
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(2, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(3, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(4, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(5, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(4, 40, 64, 0.5f));
}
SECTION("lorand and hirand")
@ -102,37 +102,37 @@ TEST_CASE("Basic triggers", "Region triggers")
region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lorand", "0.35" });
region.parseOpcode({ "hirand", "0.40" });
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.34f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.35f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.36f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.37f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.38f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.39f));
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.40f));
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.41f));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.34f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.35f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.36f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.37f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.38f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.39f));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.40f));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.41f));
}
SECTION("lorand and hirand on 1.0f")
{
region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lorand", "0.35" });
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.34f));
REQUIRE(region.registerNoteOn(1, 40, 64, 0.35f));
REQUIRE(region.registerNoteOn(1, 40, 64, 1.0f));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.34f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.35f));
REQUIRE(region.registerNoteOn(0, 40, 64, 1.0f));
}
SECTION("on_loccN, on_hiccN")
{
region.parseOpcode({ "on_locc47", "64" });
region.parseOpcode({ "on_hicc47", "68" });
REQUIRE(!region.registerCC(1, 47, 63));
REQUIRE(region.registerCC(1, 47, 64));
REQUIRE(region.registerCC(1, 47, 65));
REQUIRE(region.registerCC(1, 47, 66));
REQUIRE(region.registerCC(1, 47, 67));
REQUIRE(region.registerCC(1, 47, 68));
REQUIRE(!region.registerCC(1, 47, 69));
REQUIRE(!region.registerCC(1, 40, 64));
REQUIRE(!region.registerCC(0, 47, 63));
REQUIRE(region.registerCC(0, 47, 64));
REQUIRE(region.registerCC(0, 47, 65));
REQUIRE(region.registerCC(0, 47, 66));
REQUIRE(region.registerCC(0, 47, 67));
REQUIRE(region.registerCC(0, 47, 68));
REQUIRE(!region.registerCC(0, 47, 69));
REQUIRE(!region.registerCC(0, 40, 64));
}
}
@ -146,11 +146,11 @@ TEST_CASE("Legato triggers", "Region triggers")
region.parseOpcode({ "lokey", "40" });
region.parseOpcode({ "hikey", "50" });
region.parseOpcode({ "trigger", "first" });
REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
region.registerNoteOff(1, 40, 0, 0.5f);
region.registerNoteOff(1, 41, 0, 0.5f);
REQUIRE(region.registerNoteOn(1, 42, 64, 0.5f));
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
region.registerNoteOff(0, 40, 0, 0.5f);
region.registerNoteOff(0, 41, 0, 0.5f);
REQUIRE(region.registerNoteOn(0, 42, 64, 0.5f));
}
SECTION("Second note playing")
@ -158,10 +158,10 @@ TEST_CASE("Legato triggers", "Region triggers")
region.parseOpcode({ "lokey", "40" });
region.parseOpcode({ "hikey", "50" });
region.parseOpcode({ "trigger", "legato" });
REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(1, 41, 64, 0.5f));
region.registerNoteOff(1, 40, 0, 0.5f);
region.registerNoteOff(1, 41, 0, 0.5f);
REQUIRE(!region.registerNoteOn(1, 42, 64, 0.5f));
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
REQUIRE(region.registerNoteOn(0, 41, 64, 0.5f));
region.registerNoteOff(0, 40, 0, 0.5f);
region.registerNoteOff(0, 41, 0, 0.5f);
REQUIRE(!region.registerNoteOn(0, 42, 64, 0.5f));
}
}