diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 8506d90f..2b12a504 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -46,9 +46,10 @@ namespace Default constexpr float delay { 0.0 }; constexpr float delayRandom { 0.0 }; constexpr Range delayRange { 0.0, 100.0 }; - constexpr uint32_t offset { 0 }; - constexpr uint32_t offsetRandom { 0 }; - constexpr Range offsetRange { 0, std::numeric_limits::max() }; + constexpr int64_t offset { 0 }; + constexpr int64_t offsetRandom { 0 }; + constexpr Range offsetRange { 0, std::numeric_limits::max() }; + constexpr Range offsetCCRange = offsetRange; constexpr Range sampleEndRange { 0, std::numeric_limits::max() }; constexpr Range sampleCountRange { 0, std::numeric_limits::max() }; constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 3411e915..501550da 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -59,6 +59,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("offset_random"): setValueFromOpcode(opcode, offsetRandom, Default::offsetRange); break; + case hash("offset_cc&"): + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::offsetCCRange)) + offsetCC[opcode.parameters.back()] = *value; + break; case hash("end"): setValueFromOpcode(opcode, sampleEnd, Default::sampleEndRange); break; @@ -1134,10 +1140,13 @@ float sfz::Region::getPhase() const noexcept return phase; } -uint32_t sfz::Region::getOffset(Oversampling factor) const noexcept +uint64_t sfz::Region::getOffset(Oversampling factor) const noexcept { - std::uniform_int_distribution offsetDistribution { 0, offsetRandom }; - return (offset + offsetDistribution(Random::randomGenerator)) * static_cast(factor); + std::uniform_int_distribution offsetDistribution { 0, offsetRandom }; + uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator); + for (const auto& mod: offsetCC) + finalOffset += static_cast(mod.data * midiState.getCCValue(mod.cc)); + return Default::offsetCCRange.clamp(offset + offsetDistribution(Random::randomGenerator)) * static_cast(factor); } float sfz::Region::getDelay() const noexcept diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fa952675..9540b1cc 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -189,7 +189,7 @@ struct Region { * * @return uint32_t */ - uint32_t getOffset(Oversampling factor = Oversampling::x1) const noexcept; + uint64_t getOffset(Oversampling factor = Oversampling::x1) const noexcept; /** * @brief Get the region delay in seconds * @@ -230,8 +230,9 @@ struct Region { std::string sample {}; // Sample float delay { Default::delay }; // delay float delayRandom { Default::delayRandom }; // delay_random - uint32_t offset { Default::offset }; // offset - uint32_t offsetRandom { Default::offsetRandom }; // offset_random + int64_t offset { Default::offset }; // offset + int64_t offsetRandom { Default::offsetRandom }; // offset_random + CCMap offsetCC { Default::offset }; uint32_t sampleEnd { Default::sampleEndRange.getEnd() }; // end absl::optional sampleCount {}; // count absl::optional loopMode {}; // loopmode diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fd12b218..d52aa86c 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -374,7 +374,13 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) region->hasStereoSample = true; // TODO: adjust with LFO targets - const auto maxOffset = region->offset + region->offsetRandom; + const auto maxOffset = [region]() { + uint64_t sumOffsetCC = region->offset + region->offsetRandom; + for (const auto& offsets: region->offsetCC) + sumOffsetCC += offsets.data; + return Default::offsetCCRange.clamp(sumOffsetCC); + }(); + if (!resources.filePool.preloadFile(region->sample, maxOffset)) removeCurrentRegion(); } diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index f982c424..c90ebc78 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -53,6 +53,19 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "offset", "-1" }); REQUIRE(region.offset == 0); } + SECTION("offset_cc") + { + REQUIRE(region.offsetCC.empty()); + region.parseOpcode({ "offset_cc1", "1" }); + REQUIRE(region.offsetCC.contains(1)); + REQUIRE(region.offsetCC[1] == 1); + region.parseOpcode({ "offset_cc2", "15420" }); + REQUIRE(region.offsetCC.contains(2)); + REQUIRE(region.offsetCC[2] == 15420); + region.parseOpcode({ "offset_cc2", "-1" }); + REQUIRE(region.offsetCC[2] == 0); + } + SECTION("offset_random") {