diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index e158da91..498e4feb 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -2,6 +2,7 @@ #include #include #include "SfzHelpers.h" +#include "Debug.h" namespace sfz { @@ -11,33 +12,43 @@ namespace sfz * pressed notes. * */ -struct MidiState +class MidiState { +public: /** * @brief Update the state after a note on event * + * @param channel (1-based) * @param noteNumber * @param velocity */ - inline void noteOn(int noteNumber, uint8_t velocity) + void noteOnEvent(int channel, int noteNumber, uint8_t velocity) { + ASSERT(channel >= 1 && channel <= 16); + ASSERT(noteNumber >= 0 && noteNumber <= 127); + ASSERT(velocity >= 0 && velocity <= 127); + channel = translateSfzChannelToMidi(channel); if (noteNumber >= 0 && noteNumber < 128) { - lastNoteVelocities[noteNumber] = velocity; - noteOnTimes[noteNumber] = std::chrono::steady_clock::now(); + lastNoteVelocities[channel][noteNumber] = velocity; + noteOnTimes[channel][noteNumber] = std::chrono::steady_clock::now(); } } /** * @brief Register a note off and get the note duration * + * @param channel (1-based) * @param noteNumber * @return float */ - inline float getNoteDuration(int noteNumber) const + float getNoteDuration(int channel, int noteNumber) const { + ASSERT(channel >= 1 && 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>(noteOffTime - noteOnTimes[noteNumber]); + const auto duration = std::chrono::duration_cast>(noteOffTime - noteOnTimes[channel][noteNumber]); return duration.count(); } @@ -47,41 +58,96 @@ struct MidiState /** * @brief Get the note on velocity for a given note * + * @param channel (1-based) * @param noteNumber * @return uint8_t */ - inline uint8_t getNoteVelocity(int noteNumber) const + uint8_t getNoteVelocity(int channel, int noteNumber) const { - if (noteNumber >= 0 && noteNumber < 128) - return lastNoteVelocities[noteNumber]; - - return 0; + ASSERT(channel >= 1 && channel <= 16); + ASSERT(noteNumber >= 0 && noteNumber <= 127); + channel = translateSfzChannelToMidi(channel); + return lastNoteVelocities[channel][noteNumber]; } - inline void pitchBendEvent(int pitchBendValue) + void pitchBendEvent(int channel, int pitchBendValue) noexcept { - pitchBend = pitchBendValue; + ASSERT(channel >= 1 && channel <= 16); + ASSERT(pitchBendValue >= 8192 && pitchBendValue <= 8192); + channel = translateSfzChannelToMidi(channel); + pitchBends[channel] = pitchBendValue; } + int getPitchBend(int channel) const noexcept + { + ASSERT(channel >= 1 && channel <= 16); + channel = translateSfzChannelToMidi(channel); + return pitchBends[channel]; + } + + void ccEvent(int channel, int ccNumber, uint8_t ccValue) noexcept + { + ASSERT(channel >= 1 && channel <= 16); + ASSERT(ccNumber >= 0 && ccNumber <= 127); + ASSERT(ccValue >= 0 && ccValue <= 127); + channel = translateSfzChannelToMidi(channel); + cc[channel][ccNumber] = ccValue; + } + + uint8_t getCCValue(int channel, int ccNumber) const noexcept + { + ASSERT(channel >= 1 && channel <= 16); + ASSERT(ccNumber >= 0 && ccNumber <= 127); + channel = translateSfzChannelToMidi(channel); + return cc[channel][ccNumber]; + } + + const CCValueArray& getCCArray(int channel) const noexcept + { + ASSERT(channel >= 1 && channel <= 16); + channel = translateSfzChannelToMidi(channel); + return cc[channel]; + } + + void reset() noexcept + { + for (auto& channelArray: lastNoteVelocities) + for (auto& velocity: channelArray) + velocity = 0; + + for (auto& channelArray: cc) + for (auto& ccValue: channelArray) + ccValue = 0; + + for (auto& bendValue: pitchBends) + bendValue = 0; + } + +private: + template + using ChannelArray = std::array; + template + using MidiArray = std::array; + using NoteOnTime = std::chrono::steady_clock::time_point; /** * @brief Stores the note on times. * */ - std::array noteOnTimes { }; + ChannelArray> noteOnTimes { }; /** * @brief Stores the velocity of the note ons for currently * depressed notes. * */ - std::array lastNoteVelocities { }; + ChannelArray> lastNoteVelocities { }; /** * @brief Current known values for the CCs. * */ - CCValueArray cc; + ChannelArray cc; /** * Pitch bend status */ - int pitchBend { 0 }; + ChannelArray pitchBends { 0 }; }; } diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index e7f9f273..2d232429 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -640,11 +640,11 @@ float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) noexc return centsFactor(pitchVariationInCents); } -float sfz::Region::getBaseVolumedB(int noteNumber) noexcept +float sfz::Region::getBaseVolumedB(int channel, int noteNumber) noexcept { auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator); if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key) - baseVolumedB -= rtDecay * midiState.getNoteDuration(noteNumber); + baseVolumedB -= rtDecay * midiState.getNoteDuration(channel, noteNumber); return baseVolumedB; } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 4c1fbad1..f42b2213 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -173,10 +173,11 @@ 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 noteNumber * @return float */ - float getBaseVolumedB(int noteNumber) noexcept; + float getBaseVolumedB(int channel, int noteNumber) noexcept; /** * @brief Get the base gain of the region. * diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index c180201b..96f3266f 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -41,11 +41,22 @@ using CCNamePair = std::pair; * @param zeroBasedChannel * @return constexpr int */ -inline constexpr int translateMidiChannelToSfz(int zeroBasedChannel) +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 * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 3fb00c90..fa4418f6 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -145,8 +145,7 @@ void sfz::Synth::clear() numCurves = 0; fileTicket = -1; defaultSwitch = absl::nullopt; - for (auto& state : midiState.cc) - state = 0; + midiState.reset(); ccNames.clear(); globalOpcodes.clear(); masterOpcodes.clear(); @@ -175,8 +174,10 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) case hash("Set_cc"): [[fallthrough]]; case hash("set_cc"): - if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter)) - setValueFromOpcode(member, midiState.cc[*member.parameter], Default::ccRange); + if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter)){ + for (int channel=1; channel <=16; channel++) + midiState.ccEvent(channel, *member.parameter, readOpcode(member.value, Default::ccRange).value_or(0)); + } break; case hash("Label_cc"): [[fallthrough]]; @@ -271,8 +272,10 @@ bool sfz::Synth::loadSfzFile(const fs::path& filename) } // Defaults - for (int ccIndex = 1; ccIndex < 128; ccIndex++) - region->registerCC(region->channelRange.getStart(), ccIndex, midiState.cc[ccIndex]); + for (int ccIndex = 1; ccIndex < 128; ccIndex++) { + const auto channel = region->channelRange.getStart(); + region->registerCC(channel, ccIndex, midiState.getCCValue(channel, ccIndex)); + } if (defaultSwitch) { region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0); @@ -388,7 +391,7 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity ASSERT(noteNumber >= 0); channel = translateMidiChannelToSfz(channel); - midiState.noteOn(noteNumber, velocity); + midiState.noteOnEvent(channel, noteNumber, velocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -417,8 +420,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; @@ -426,8 +427,10 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // way in sfz to specify that a release trigger should NOT use the note-on velocity? // auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity); - auto replacedVelocity = midiState.getNoteVelocity(noteNumber); + auto replacedVelocity = midiState.getNoteVelocity(channel, noteNumber); auto randValue = randNoteDistribution(Random::randomGenerator); + + channel = translateMidiChannelToSfz(channel); for (auto& voice : voices) voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity); @@ -453,10 +456,10 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc if (!canEnterCallback) return; + midiState.ccEvent(channel, ccNumber, ccValue); for (auto& voice : voices) voice->registerCC(delay, channel, ccNumber, ccValue); - midiState.cc[ccNumber] = ccValue; for (auto& region : ccActivationLists[ccNumber]) { if (region->registerCC(channel, ccNumber, ccValue)) { @@ -474,7 +477,7 @@ void sfz::Synth::pitchWheel(int delay, int channel, int pitch) noexcept ASSERT(pitch <= 8192); ASSERT(pitch >= -8192); channel = translateMidiChannelToSfz(channel); - midiState.pitchBendEvent(pitch); + midiState.pitchBendEvent(channel, pitch); for (auto& voice: voices) { voice->registerPitchWheel(delay, channel, pitch); } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 19958469..93521467 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -60,39 +60,39 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, } pitchRatio = region->getBasePitchVariation(number, value); - baseVolumedB = region->getBaseVolumedB(number); + baseVolumedB = region->getBaseVolumedB(channel, number); auto volumedB { baseVolumedB }; if (region->volumeCC) - volumedB += normalizeCC(midiState.cc[region->volumeCC->first]) * region->volumeCC->second; + volumedB += normalizeCC(midiState.getCCValue(channel, region->volumeCC->first)) * region->volumeCC->second; volumeEnvelope.reset(db2mag(volumedB)); baseGain = region->getBaseGain(); - baseGain *= region->getCrossfadeGain(midiState.cc); + baseGain *= region->getCrossfadeGain(midiState.getCCArray(channel)); if (triggerType != TriggerType::CC) baseGain *= region->getNoteGain(number, value); float gain { baseGain }; if (region->amplitudeCC) - gain *= normalizeCC(midiState.cc[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second); + gain *= normalizeCC(midiState.getCCValue(channel, region->amplitudeCC->first)) * normalizePercents(region->amplitudeCC->second); amplitudeEnvelope.reset(gain); basePan = normalizeNegativePercents(region->pan); auto pan { basePan }; if (region->panCC) - pan += normalizeCC(midiState.cc[region->panCC->first]) * normalizeNegativePercents(region->panCC->second); + pan += normalizeCC(midiState.getCCValue(channel, region->panCC->first)) * normalizeNegativePercents(region->panCC->second); panEnvelope.reset(pan); basePosition = normalizeNegativePercents(region->position); auto position { basePosition }; if (region->positionCC) - position += normalizeCC(midiState.cc[region->positionCC->first]) * normalizeNegativePercents(region->positionCC->second); + position += normalizeCC(midiState.getCCValue(channel, region->positionCC->first)) * normalizeNegativePercents(region->positionCC->second); positionEnvelope.reset(position); baseWidth = normalizeNegativePercents(region->width); auto width { baseWidth }; if (region->widthCC) - width += normalizeCC(midiState.cc[region->widthCC->first]) * normalizeNegativePercents(region->widthCC->second); + width += normalizeCC(midiState.getCCValue(channel, region->widthCC->first)) * normalizeNegativePercents(region->widthCC->second); widthEnvelope.reset(width); pitchBendEnvelope.setFunction([region](float pitchValue){ @@ -100,29 +100,29 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, const auto bendInCents = normalizedBend > 0 ? normalizedBend * region->bendUp : -normalizedBend * region->bendDown; return centsFactor(bendInCents); }); - pitchBendEnvelope.reset(midiState.pitchBend); + pitchBendEnvelope.reset(midiState.getPitchBend(channel)); sourcePosition = region->getOffset(); initialDelay = delay + static_cast(region->getDelay() * sampleRate); baseFrequency = midiNoteFrequency(number); bendStepFactor = centsFactor(region->bendStep); - prepareEGEnvelope(initialDelay, value); + prepareEGEnvelope(channel, initialDelay, value); } -void sfz::Voice::prepareEGEnvelope(int delay, uint8_t velocity) noexcept +void sfz::Voice::prepareEGEnvelope(int channel, int delay, uint8_t velocity) noexcept { auto secondsToSamples = [this](auto timeInSeconds) { return static_cast(timeInSeconds * sampleRate); }; - + const auto& ccArray = midiState.getCCArray(channel); egEnvelope.reset( - secondsToSamples(region->amplitudeEG.getAttack(midiState.cc, velocity)), - secondsToSamples(region->amplitudeEG.getRelease(midiState.cc, velocity)), - normalizePercents(region->amplitudeEG.getSustain(midiState.cc, velocity)), - delay + secondsToSamples(region->amplitudeEG.getDelay(midiState.cc, velocity)), - secondsToSamples(region->amplitudeEG.getDecay(midiState.cc, velocity)), - secondsToSamples(region->amplitudeEG.getHold(midiState.cc, velocity)), - normalizePercents(region->amplitudeEG.getStart(midiState.cc, velocity))); + secondsToSamples(region->amplitudeEG.getAttack(ccArray, velocity)), + secondsToSamples(region->amplitudeEG.getRelease(ccArray, velocity)), + normalizePercents(region->amplitudeEG.getSustain(ccArray, velocity)), + delay + secondsToSamples(region->amplitudeEG.getDelay(ccArray, velocity)), + secondsToSamples(region->amplitudeEG.getDecay(ccArray, velocity)), + secondsToSamples(region->amplitudeEG.getHold(ccArray, velocity)), + normalizePercents(region->amplitudeEG.getStart(ccArray, velocity))); } bool sfz::Voice::isFree() const noexcept @@ -152,7 +152,7 @@ void sfz::Voice::registerNoteOff(int delay, int channel, int noteNumber, uint8_t if (region->loopMode == SfzLoopMode::one_shot) return; - if (!region->checkSustain || midiState.cc[config::sustainCC] < config::halfCCThreshold) + if (!region->checkSustain || midiState.getCCValue(channel, config::sustainCC) < config::halfCCThreshold) release(delay); } } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 7f073d7f..619ab771 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -243,10 +243,11 @@ private: /** * @brief Computes the values for the envelope depending on the note or CC number and the velocity/cc value * + * @param channel * @param delay * @param velocity */ - void prepareEGEnvelope(int delay, uint8_t velocity) noexcept; + void prepareEGEnvelope(int channel, int delay, uint8_t velocity) noexcept; /** * @brief The function processing a mono sample source * diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index 022f9341..50ef6c86 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -183,13 +183,13 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); - midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); - midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); - midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.70711_a ); - midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.86603_a ); - midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); - midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); + midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); + midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a ); + midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.70711_a ); + midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.86603_a ); + midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); + midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -201,13 +201,13 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); - midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); - midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.25_a ); - midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); - midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.75_a ); - midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); - midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); + midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); + midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.25_a ); + midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a ); + midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.75_a ); + midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); + midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on CC") { @@ -217,13 +217,13 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); - midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); - midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.86603_a ); - midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.70711_a ); - midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); - midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); - midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); + midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); + midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.86603_a ); + midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.70711_a ); + midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a ); + midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); + midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -235,13 +235,13 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); - midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); - midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.75_a ); - midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); - midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.25_a ); - midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); - midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); + midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a ); + midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.75_a ); + midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a ); + midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.25_a ); + midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); + midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a ); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") @@ -282,17 +282,17 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "trigger", "release" }); region.parseOpcode({ "rt_decay", "10" }); - midiState.noteOn(64, 64); + midiState.noteOnEvent(1, 64, 64); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) ); + REQUIRE( region.getBaseVolumedB(1, 64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) ); region.parseOpcode({ "rt_decay", "20" }); - midiState.noteOn(64, 64); + midiState.noteOnEvent(1, 64, 64); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) ); + REQUIRE( region.getBaseVolumedB(1, 64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) ); region.parseOpcode({ "trigger", "attack" }); - midiState.noteOn(64, 64); + midiState.noteOnEvent(1, 64, 64); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) ); + REQUIRE( region.getBaseVolumedB(1, 64) == Approx(sfz::Default::volume).margin(0.1) ); } TEST_CASE("[Region] Base delay")