diff --git a/benchmarks/BM_resampleChunk.cpp b/benchmarks/BM_resampleChunk.cpp deleted file mode 100644 index 59ba3434..00000000 --- a/benchmarks/BM_resampleChunk.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause - -// This code is part of the sfizz library and is licensed under a BSD 2-clause -// license. You should have receive a LICENSE.md file along with the code. -// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz - -#include "Buffer.h" -#include "SIMDHelpers.h" -#include -#include -#include "ghc/filesystem.hpp" -#include "Oversampler.h" -#include "AudioBuffer.h" -#include "absl/memory/memory.h" -#include - - -constexpr std::array coeffsStage2x { - 0.036681502163648017, - 0.13654762463195771, - 0.27463175937945411, - 0.42313861743656667, - 0.56109869787919475, - 0.67754004997416162, - 0.76974183386322659, - 0.83988962484963803, - 0.89226081800387891, - 0.9315419599631839, - 0.96209454837808395, - 0.98781637073289708 -}; - -constexpr std::array coeffsStage4x { - 0.042448989488488006, - 0.17072114107630679, - 0.39329183835224008, - 0.74569514831986694 -}; - -constexpr std::array coeffsStage8x { - 0.055748680811302048, - 0.24305119574153092, - 0.6466991311926823 -}; - - -#if defined(__x86_64__) || defined(__i386__) -#include "hiir/Upsampler2xSse.h" -using Upsampler2x = hiir::Upsampler2xSse; -using Upsampler4x = hiir::Upsampler2xSse; -using Upsampler8x = hiir::Upsampler2xSse; -#elif defined(__arm__) || defined(__aarch64__) -#include "hiir/Upsampler2xNeon.h" -using Upsampler2x = hiir::Upsampler2xNeon; -using Upsampler4x = hiir::Upsampler2xNeon; -using Upsampler8x = hiir::Upsampler2xNeon; -#else -#include "hiir/Upsampler2xFpu.h" -using Upsampler2x = hiir::Upsampler2xFpu; -using Upsampler4x = hiir::Upsampler2xFpu; -using Upsampler8x = hiir::Upsampler2xFpu; -#endif - - -class FileFixture : public benchmark::Fixture { -public: - void SetUp(const ::benchmark::State& /* state */) { - rootPath = getPath() / "sample1.flac"; - if (!ghc::filesystem::exists(rootPath)) { - #ifndef NDEBUG - std::cerr << "Can't find path" << '\n'; - #endif - std::terminate(); - } - - sndfile = SndfileHandle(rootPath.c_str()); - numFrames = static_cast(sndfile.frames()); - output = absl::make_unique>(sndfile.channels(), numFrames * 4); - } - - void TearDown(const ::benchmark::State& /* state */) { - } - - ghc::filesystem::path getPath() - { - #ifdef __linux__ - char buf[PATH_MAX + 1]; - if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) == -1) - return {}; - std::string str { buf }; - return str.substr(0, str.rfind('/')); - #elif _WIN32 - return ghc::filesystem::current_path(); - #endif - } - - std::unique_ptr> output; - SndfileHandle sndfile; - ghc::filesystem::path rootPath; - size_t numFrames { 0 }; -}; - -BENCHMARK_DEFINE_F(FileFixture, NoResampling)(benchmark::State& state) { - for (auto _ : state) - { - sfz::Buffer buffer { numFrames * sndfile.channels() }; - sndfile.readf(buffer.data(), sndfile.frames()); - sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1)); - } -} - -BENCHMARK_DEFINE_F(FileFixture, ResampleAtOnce)(benchmark::State& state) { - for (auto _ : state) - { - sfz::Buffer buffer { numFrames * sndfile.channels() }; - sfz::Buffer temp { numFrames * 4 }; - - Upsampler2x upsampler2x; - Upsampler4x upsampler4x; - upsampler2x.set_coefs(coeffsStage2x.data()); - upsampler4x.set_coefs(coeffsStage4x.data()); - - sndfile.readf(buffer.data(), numFrames); - sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1)); - - upsampler2x.process_block(temp.data(), output->channelReader(0), static_cast(numFrames)); - upsampler4x.process_block(output->channelWriter(0), temp.data(), static_cast(numFrames * 2)); - - upsampler2x.clear_buffers(); - upsampler4x.clear_buffers(); - - upsampler2x.process_block(temp.data(), output->channelReader(1), static_cast(numFrames)); - upsampler4x.process_block(output->channelWriter(1), temp.data(), static_cast(numFrames * 2)); - } -} - -BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) { - for (auto _ : state) - { - auto chunkSize = static_cast(state.range(0)); - sfz::Buffer buffer { numFrames * sndfile.channels() }; - - sfz::Buffer leftInput { chunkSize }; - sfz::Buffer rightInput { chunkSize }; - sfz::Buffer chunk { chunkSize * 2 }; - - sndfile.readf(buffer.data(), numFrames); - - auto bufferSpan = absl::MakeSpan(buffer); - auto leftSpan = absl::MakeSpan(leftInput); - auto rightSpan = absl::MakeSpan(rightInput); - auto chunkSpan = absl::MakeSpan(chunk); - - Upsampler2x upsampler2xLeft; - Upsampler2x upsampler2xRight; - Upsampler4x upsampler4xLeft; - Upsampler4x upsampler4xRight; - upsampler2xLeft.set_coefs(coeffsStage2x.data()); - upsampler2xRight.set_coefs(coeffsStage2x.data()); - upsampler4xLeft.set_coefs(coeffsStage4x.data()); - upsampler4xRight.set_coefs(coeffsStage4x.data()); - - size_t inputFrameCounter { 0 }; - size_t outputFrameCounter { 0 }; - while(inputFrameCounter < numFrames) - { - // std::cout << "Input frames: " << inputFrameCounter << "/" << numFrames << '\n'; - const auto thisChunkSize = std::min(chunkSize, numFrames - inputFrameCounter); - const auto bufferChunk = bufferSpan.subspan( - inputFrameCounter * sndfile.channels(), - thisChunkSize * sndfile.channels() - ); - - sfz::readInterleaved(bufferChunk, leftSpan, rightSpan); - - upsampler2xLeft.process_block(chunkSpan.data(), leftSpan.data(), static_cast(thisChunkSize)); - upsampler4xLeft.process_block(output->channelWriter(0) + outputFrameCounter, chunkSpan.data(), static_cast(thisChunkSize * 2)); - - upsampler2xRight.process_block(chunkSpan.data(), rightSpan.data(), static_cast(thisChunkSize)); - upsampler4xRight.process_block(output->channelWriter(1) + outputFrameCounter, chunkSpan.data(), static_cast(thisChunkSize * 2)); - - inputFrameCounter += chunkSize; - outputFrameCounter += chunkSize * 4; - } - } -} - -BENCHMARK_REGISTER_F(FileFixture, NoResampling); -BENCHMARK_REGISTER_F(FileFixture, ResampleAtOnce); -BENCHMARK_REGISTER_F(FileFixture, ResampleInChunks)->RangeMultiplier(4)->Range((1 << 4), (1 << 16)); -BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 89d7c8e3..8f0aac20 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -64,9 +64,6 @@ target_link_libraries(bm_readChunk PRIVATE sfizz::sndfile) sfizz_add_benchmark(bm_readChunkFlac BM_readChunkFlac.cpp) target_link_libraries(bm_readChunkFlac PRIVATE sfizz::sndfile) -sfizz_add_benchmark(bm_resampleChunk BM_resampleChunk.cpp) -target_link_libraries(bm_resampleChunk PRIVATE sfizz::sndfile sfizz::hiir) - sfizz_add_benchmark(bm_interpolators BM_interpolators.cpp) sfizz_add_benchmark(bm_filterModulation BM_filterModulation.cpp ../src/sfizz/SfzFilter.cpp) diff --git a/clients/sfizz_render.cpp b/clients/sfizz_render.cpp index 6b646b4c..48b09bc1 100644 --- a/clients/sfizz_render.cpp +++ b/clients/sfizz_render.cpp @@ -66,7 +66,6 @@ int main(int argc, char** argv) bool help { false }; bool useEOT { false }; int quality { 2 }; - int oversampling { 1 }; options.add_options() ("sfz", "SFZ file", cxxopts::value()) @@ -74,7 +73,6 @@ int main(int argc, char** argv) ("wav", "Output wav file", cxxopts::value()) ("b,blocksize", "Block size for the sfizz callbacks", cxxopts::value(blockSize)) ("s,samplerate", "Output sample rate", cxxopts::value(sampleRate)) - ("oversampling", "Internal oversampling factor", cxxopts::value(oversampling)) ("q,quality", "Resampling quality", cxxopts::value(quality)) ("v,verbose", "Verbose output", cxxopts::value(verbose)) ("log", "Produce logs", cxxopts::value()) @@ -126,23 +124,6 @@ int main(int argc, char** argv) if (params.count("log") > 0) synth.enableLogging(params["log"].as()); - const auto osFactor = [oversampling] { - switch (oversampling){ - case 1: - return sfz::Oversampling::x1; - case 2: - return sfz::Oversampling::x2; - case 4: - return sfz::Oversampling::x4; - case 5: - return sfz::Oversampling::x8; - default: - LOG_ERROR("Bad oversampling factor: " << oversampling); - std::exit(-1); - } - }(); - synth.setOversamplingFactor(osFactor); - ERROR_IF(!synth.loadSfzFile(sfzPath), "There was an error loading the SFZ file."); LOG_INFO(synth.getNumRegions() << " regions in the SFZ."); diff --git a/plugins/lv2/sfizz.ttl.in b/plugins/lv2/sfizz.ttl.in index 040f4fd3..1ad9204d 100644 --- a/plugins/lv2/sfizz.ttl.in +++ b/plugins/lv2/sfizz.ttl.in @@ -191,9 +191,9 @@ midnam:update a lv2:Feature . a lv2:InputPort, lv2:ControlPort ; lv2:index 7 ; lv2:symbol "oversampling" ; - lv2:name "Internal oversampling factor", - "Facteur de suréchantillonnage interne"@fr , - "Fattore Sovracampionamento Interno"@it ; + lv2:name "Oversampling factor", + "Facteur de suréchantillonnage"@fr , + "Fattore Sovracampionamento"@it ; pg:group <@LV2PLUGIN_URI@#config> ; lv2:portProperty pprop:notAutomatic ; lv2:portProperty pprop:expensive ; diff --git a/src/sfizz.h b/src/sfizz.h index 7d32a414..933ca722 100644 --- a/src/sfizz.h +++ b/src/sfizz.h @@ -695,8 +695,8 @@ SFIZZ_EXPORTED_API void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned in /** * @brief Get the internal oversampling rate. * - * This is the sampling rate of the engine, not the output or expected rate of - * the calling function. For the latter use the sfizz_get_sample_rate() function. + * As of 1.0, This is an inactive stub for future work on oversampling in the engine. + * * @since 0.2.0 * * @param synth The synth. @@ -706,17 +706,7 @@ SFIZZ_EXPORTED_API sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfi /** * @brief Set the internal oversampling rate. * - * This is the sampling rate of the engine, not the output or expected rate of - * the calling function. For the latter use the sfizz_set_sample_rate() function. - * - * Increasing this value (up to x8 oversampling) improves the quality of the - * output at the expense of memory consumption and background loading speed. - * The main render path still uses the same linear interpolation algorithm and - * should not see its performance decrease, but the files are oversampled upon - * loading which increases the stress on the background loader and reduce - * the loading speed. You can tweak the size of the preloaded data to compensate - * for the memory increase, but the full loading will need to take place anyway. - * + * As of 1.0, This is an inactive stub for future work on oversampling in the engine. * @since 0.2.0 * * @param synth The synth. diff --git a/src/sfizz.hpp b/src/sfizz.hpp index 11d55e8e..d17ad902 100644 --- a/src/sfizz.hpp +++ b/src/sfizz.hpp @@ -723,18 +723,8 @@ public: /** * @brief Set the oversampling factor to a new value. * - * It will kill all the voices, and trigger a reloading of every file in - * the FilePool under the new oversampling. + * As of 1.0, This is an inactive stub for future work on oversampling in the engine. * - * Increasing this value (up to x8 oversampling) improves the - * quality of the output at the expense of memory consumption and - * background loading speed. The main render path still uses the - * same linear interpolation algorithm and should not see its - * performance decrease, but the files are oversampled upon loading - * which increases the stress on the background loader and reduce - * the loading speed. You can tweak the size of the preloaded data - * to compensate for the memory increase, but the full loading will - * need to take place anyway. * * @since 0.2.0 * @@ -751,6 +741,9 @@ public: /** * @brief Return the current oversampling factor. * @since 0.2.0 + * + * As of 1.0, This is an inactive stub for future work on oversampling in the engine. + * */ int getOversamplingFactor() const noexcept; diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 9a0bb82a..3631e6c7 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -17,12 +17,6 @@ #include namespace sfz { -enum class Oversampling: int { - x1 = 1, - x2 = 2, - x4 = 4, - x8 = 8 -}; enum ExtendedCCs { pitchBend = 128, @@ -69,7 +63,6 @@ namespace config { constexpr float virtuallyZero { 0.001f }; constexpr float fastReleaseDuration { 0.01f }; constexpr char defineCharacter { '$' }; - constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 }; constexpr float A440 { 440.0 }; constexpr size_t powerHistoryLength { 16 }; constexpr size_t powerFollowerStep { 512 }; diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index bea0f3b4..78783a0c 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -29,7 +29,6 @@ #include "AudioBuffer.h" #include "AudioSpan.h" #include "Config.h" -#include "Oversampler.h" #include "utility/SwapAndPop.h" #include "utility/Debug.h" #include @@ -91,29 +90,54 @@ void readBaseFile(sfz::AudioReader& reader, sfz::FileAudioBuffer& output, uint32 } } -sfz::FileAudioBuffer readFromFile(sfz::AudioReader& reader, uint32_t numFrames, sfz::Oversampling factor) +sfz::FileAudioBuffer readFromFile(sfz::AudioReader& reader, uint32_t numFrames) { sfz::FileAudioBuffer baseBuffer; readBaseFile(reader, baseBuffer, numFrames); - - if (factor == sfz::Oversampling::x1) - return baseBuffer; - - sfz::FileAudioBuffer outputBuffer { reader.channels(), numFrames * static_cast(factor) }; - outputBuffer.clear(); - sfz::Oversampler oversampler { factor }; - oversampler.stream(baseBuffer, outputBuffer); - return outputBuffer; + return baseBuffer; } -void streamFromFile(sfz::AudioReader& reader, uint32_t numFrames, sfz::Oversampling factor, sfz::FileAudioBuffer& output, std::atomic* filledFrames = nullptr) +void streamFromFile(sfz::AudioReader& reader, sfz::FileAudioBuffer& output, std::atomic* filledFrames = nullptr) { + const auto numFrames = static_cast(reader.frames()); + const auto numChannels = reader.channels(); + const auto chunkSize = static_cast(sfz::config::chunkSize); + output.reset(); output.addChannels(reader.channels()); - output.resize(numFrames * static_cast(factor)); + output.resize(numFrames); output.clear(); - sfz::Oversampler oversampler { factor }; - oversampler.stream(reader, output, filledFrames); + + sfz::Buffer fileBlock { chunkSize * numChannels }; + size_t inputFrameCounter { 0 }; + size_t outputFrameCounter { 0 }; + bool inputEof = false; + + while (!inputEof && inputFrameCounter < numFrames) + { + auto thisChunkSize = std::min(chunkSize, numFrames - inputFrameCounter); + const auto numFramesRead = static_cast( + reader.readNextBlock(fileBlock.data(), thisChunkSize)); + if (numFramesRead == 0) + break; + + if (numFramesRead < thisChunkSize) { + inputEof = true; + thisChunkSize = numFramesRead; + } + const auto outputChunkSize = thisChunkSize; + + for (size_t chanIdx = 0; chanIdx < numChannels; chanIdx++) { + const auto outputChunk = output.getSpan(chanIdx).subspan(outputFrameCounter, outputChunkSize); + for (size_t i = 0; i < thisChunkSize; ++i) + outputChunk[i] = fileBlock[i * numChannels + chanIdx]; + } + inputFrameCounter += thisChunkSize; + outputFrameCounter += outputChunkSize; + + if (filledFrames != nullptr) + filledFrames->fetch_add(outputChunkSize); + } } sfz::FilePool::FilePool(sfz::Logger& logger) @@ -294,16 +318,12 @@ bool sfz::FilePool::preloadFile(const FileId& fileId, uint32_t maxOffset) noexce if (existingFile != preloadedFiles.end()) { if (framesToLoad > existingFile->second.preloadedData.getNumFrames()) { preloadedFiles[fileId].information.maxOffset = maxOffset; - preloadedFiles[fileId].preloadedData = readFromFile(*reader, framesToLoad, oversamplingFactor); + preloadedFiles[fileId].preloadedData = readFromFile(*reader, framesToLoad); } } else { - const auto factor = static_cast(oversamplingFactor); - fileInformation->sampleRate = factor * static_cast(reader->sampleRate()); - fileInformation->end = static_cast(factor * fileInformation->end); - fileInformation->loopStart = static_cast(factor * fileInformation->loopStart); - fileInformation->loopEnd = static_cast(factor * fileInformation->loopEnd); + fileInformation->sampleRate = static_cast(reader->sampleRate()); auto insertedPair = preloadedFiles.insert_or_assign(fileId, { - readFromFile(*reader, framesToLoad, oversamplingFactor), + readFromFile(*reader, framesToLoad), *fileInformation }); @@ -329,13 +349,9 @@ sfz::FileDataHolder sfz::FilePool::loadFile(const FileId& fileId) noexcept if (existingFile != loadedFiles.end()) { return { &existingFile->second }; } else { - const auto factor = static_cast(oversamplingFactor); - fileInformation->sampleRate = factor * static_cast(reader->sampleRate()); - fileInformation->end = static_cast(factor * fileInformation->end); - fileInformation->loopStart = static_cast(factor * fileInformation->loopStart); - fileInformation->loopEnd = static_cast(factor * fileInformation->loopEnd); + fileInformation->sampleRate = static_cast(reader->sampleRate()); auto insertedPair = preloadedFiles.insert_or_assign(fileId, { - readFromFile(*reader, frames, oversamplingFactor), + readFromFile(*reader, frames), *fileInformation }); insertedPair.first->second.status = FileData::Status::Preloaded; @@ -375,7 +391,7 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept const auto maxOffset = preloadedFile.second.information.maxOffset; fs::path file { rootDirectory / preloadedFile.first.filename() }; AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse()); - preloadedFile.second.preloadedData = readFromFile(*reader, preloadSize + maxOffset, oversamplingFactor); + preloadedFile.second.preloadedData = readFromFile(*reader, preloadSize + maxOffset); } } @@ -424,7 +440,7 @@ void sfz::FilePool::loadingJob(const QueuedFileData& data) noexcept return; const auto frames = static_cast(reader->frames()); - streamFromFile(*reader, frames, oversamplingFactor, data.data->fileData, &data.data->availableFrames); + streamFromFile(*reader, data.data->fileData, &data.data->availableFrames); const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime; logger.logFileTime(waitDuration, loadDuration, frames, id->filename()); @@ -444,45 +460,6 @@ void sfz::FilePool::clear() preloadedFiles.clear(); } -void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept -{ - float samplerateChange { static_cast(factor) / static_cast(this->oversamplingFactor) }; - for (auto& preloadedFile : preloadedFiles) { - const auto framesToLoad = [&]() { - if (loadInRam) - return preloadedFile.second.information.end; - else - return min( - preloadedFile.second.information.end, - preloadedFile.second.information.maxOffset + preloadSize - ); - }(); - - fs::path file { rootDirectory / preloadedFile.first.filename() }; - AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse()); - preloadedFile.second.preloadedData = readFromFile(*reader, framesToLoad, factor); - FileInformation& information = preloadedFile.second.information; - information.sampleRate *= samplerateChange; - information.end = static_cast(samplerateChange * information.end); - information.loopStart = static_cast(samplerateChange * information.loopStart); - information.loopEnd = static_cast(samplerateChange * information.loopEnd); - - if (preloadedFile.second.status == FileData::Status::Done) { - const auto realFrames = - preloadedFile.second.availableFrames.load() / static_cast(this->oversamplingFactor); - preloadedFile.second.fileData = readFromFile(*reader, realFrames, factor); - preloadedFile.second.availableFrames = realFrames * static_cast(factor); - } - } - - this->oversamplingFactor = factor; -} - -sfz::Oversampling sfz::FilePool::getOversamplingFactor() const noexcept -{ - return oversamplingFactor; -} - uint32_t sfz::FilePool::getPreloadSize() const noexcept { return preloadSize; @@ -578,8 +555,7 @@ void sfz::FilePool::setRamLoading(bool loadInRam) noexcept AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse()); preloadedFile.second.preloadedData = readFromFile( *reader, - preloadedFile.second.information.end, - oversamplingFactor + preloadedFile.second.information.end ); } } else { diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index b30c5cf4..a219b46c 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -278,19 +278,6 @@ public: * @return uint32_t */ uint32_t getPreloadSize() const noexcept; - /** - * @brief Set the oversampling factor. This will trigger a full - * reload of all samples so don't call it on the audio thread. - * - * @param factor - */ - void setOversamplingFactor(Oversampling factor) noexcept; - /** - * @brief Get the current oversampling factor - * - * @return Oversampling - */ - Oversampling getOversamplingFactor() const noexcept; /** * @brief Empty the file loading queues without actually loading * the files. All promises will be unfulfilled. Don't call this @@ -331,7 +318,6 @@ private: bool loadInRam { config::loadInRam }; uint32_t preloadSize { config::preloadSize }; - Oversampling oversamplingFactor { config::defaultOversamplingFactor }; // Signals volatile bool dispatchFlag { true }; diff --git a/src/sfizz/Oversampler.h b/src/sfizz/Oversampler.h index 7f45f8c0..e109384c 100644 --- a/src/sfizz/Oversampler.h +++ b/src/sfizz/Oversampler.h @@ -17,6 +17,13 @@ namespace sfz { class AudioReader; +enum class Oversampling: int { + x1 = 1, + x2 = 2, + x4 = 4, + x8 = 8 +}; + /** * @brief Wraps the internal oversampler in a single function that takes an * AudioBuffer and oversamples it in another pre-allocated one. The diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 2649a369..05e73b67 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1704,14 +1704,13 @@ float sfz::Region::getPhase() const noexcept return phase; } -uint64_t sfz::Region::getOffset(const MidiState& midiState, Oversampling factor) const noexcept +uint64_t sfz::Region::getOffset(const MidiState& midiState) const noexcept { 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::offset.bounds.clamp(finalOffset) * static_cast(factor); + return Default::offset.bounds.clamp(finalOffset); } float sfz::Region::getDelay(const MidiState& midiState) const noexcept @@ -1725,34 +1724,34 @@ float sfz::Region::getDelay(const MidiState& midiState) const noexcept return Default::delay.bounds.clamp(finalDelay); } -uint32_t sfz::Region::getSampleEnd(MidiState& midiState, Oversampling factor) const noexcept +uint32_t sfz::Region::getSampleEnd(MidiState& midiState) const noexcept { int64_t end = sampleEnd; for (const auto& mod: endCC) end += static_cast(mod.data * midiState.getCCValue(mod.cc)); end = clamp(end, int64_t { 0 }, sampleEnd); - return static_cast(end) * static_cast(factor); + return static_cast(end); } -uint32_t sfz::Region::loopStart(MidiState& midiState, Oversampling factor) const noexcept +uint32_t sfz::Region::loopStart(MidiState& midiState) const noexcept { auto start = loopRange.getStart(); for (const auto& mod: loopStartCC) start += static_cast(mod.data * midiState.getCCValue(mod.cc)); start = clamp(start, int64_t { 0 }, sampleEnd); - return static_cast(start) * static_cast(factor); + return static_cast(start); } -uint32_t sfz::Region::loopEnd(MidiState& midiState, Oversampling factor) const noexcept +uint32_t sfz::Region::loopEnd(MidiState& midiState) const noexcept { auto end = loopRange.getEnd(); for (const auto& mod: loopEndCC) end += static_cast(mod.data * midiState.getCCValue(mod.cc)); end = clamp(end, int64_t { 0 }, sampleEnd); - return static_cast(end) * static_cast(factor); + return static_cast(end); } float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index c937c496..fe407065 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -166,7 +166,7 @@ struct Region { * @param midiState * @return uint32_t */ - uint64_t getOffset(const MidiState& midiState, Oversampling factor = Oversampling::x1) const noexcept; + uint64_t getOffset(const MidiState& midiState) const noexcept; /** * @brief Get the region delay in seconds * @@ -180,7 +180,7 @@ struct Region { * * @return uint32_t */ - uint32_t getSampleEnd(MidiState& midiState, Oversampling factor = Oversampling::x1) const noexcept; + uint32_t getSampleEnd(MidiState& midiState) const noexcept; /** * @brief Parse a new opcode into the region to fill in the proper parameters. * This must be called multiple times for each opcode applying to this region. @@ -260,8 +260,8 @@ struct Region { void offsetAllKeys(int offset) noexcept; - uint32_t loopStart(MidiState& midiState, Oversampling factor = Oversampling::x1) const noexcept; - uint32_t loopEnd(MidiState& midiState, Oversampling factor = Oversampling::x1) const noexcept; + uint32_t loopStart(MidiState& midiState) const noexcept; + uint32_t loopEnd(MidiState& midiState) const noexcept; /** * @brief Get the gain this region contributes into the input of the Nth diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 75bc24cb..2d7f2a92 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -1849,28 +1849,6 @@ void Synth::Impl::setupModMatrix() mm.init(); } -void Synth::setOversamplingFactor(Oversampling factor) noexcept -{ - Impl& impl = *impl_; - - // fast path - if (factor == impl.oversamplingFactor_) - return; - - for (auto& voice : impl.voiceManager_) - voice.reset(); - - impl.resources_.filePool.emptyFileLoadingQueues(); - impl.resources_.filePool.setOversamplingFactor(factor); - impl.oversamplingFactor_ = factor; -} - -Oversampling Synth::getOversamplingFactor() const noexcept -{ - Impl& impl = *impl_; - return impl.oversamplingFactor_; -} - void Synth::setPreloadSize(uint32_t preloadSize) noexcept { Impl& impl = *impl_; diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index fd89ffee..d44e3812 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -545,26 +545,6 @@ public: */ void setNumVoices(int numVoices) noexcept; - /** - * @brief Set the oversampling factor to a new value. - * It will kill all the voices, and trigger a reloading of every file in - * the FilePool under the new oversampling. - * This function takes a lock and disables the callback; prefer calling - * it out of the RT thread. It can also take a long time to return. - * If the new oversampling factor is the same as the current one, it will - * release the lock immediately and exit. - * - * @param factor - */ - void setOversamplingFactor(Oversampling factor) noexcept; - - /** - * @brief get the current oversampling factor - * - * @return Oversampling - */ - Oversampling getOversamplingFactor() const noexcept; - /** * @brief Set the preloaded file size. * This function takes a lock and disables the callback; prefer calling diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 73501323..1b8b85d3 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -287,7 +287,6 @@ struct Synth::Impl final: public Parser::Listener { float sampleRate_ { config::defaultSampleRate }; float volume_ { Default::globalVolume }; int numVoices_ { config::numVoices }; - Oversampling oversamplingFactor_ { config::defaultOversamplingFactor }; // Distribution used to generate random value for the *rand opcodes std::uniform_real_distribution randNoteDistribution_ { 0, 1 }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 26928526..64e6589a 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -466,7 +466,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.updateLoopInformation(); impl.speedRatio_ = static_cast(impl.currentPromise_->information.sampleRate / impl.sampleRate_); - impl.sourcePosition_ = region.getOffset(resources.midiState, resources.filePool.getOversamplingFactor()); + impl.sourcePosition_ = region.getOffset(resources.midiState); } // do Scala retuning and reconvert the frequency into a 12TET key number @@ -498,7 +498,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc impl.triggerDelay_ = delay; impl.initialDelay_ = delay + static_cast(region.getDelay(resources.midiState) * impl.sampleRate_); impl.baseFrequency_ = resources.tuning.getFrequencyOfKey(impl.triggerEvent_.number); - impl.sampleEnd_ = int(region.getSampleEnd(resources.midiState, resources.filePool.getOversamplingFactor())); + impl.sampleEnd_ = int(region.getSampleEnd(resources.midiState)); impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1; impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_); impl.bendSmoother_.reset(region.getBendInCents(resources.midiState.getPitchBend())); @@ -1676,11 +1676,10 @@ void Voice::Impl::updateLoopInformation() noexcept Resources& resources = resources_; const FileInformation& info = currentPromise_->information; - const Oversampling factor = resources_.filePool.getOversamplingFactor(); const double rate = info.sampleRate; - loop_.start = static_cast(region_->loopStart(resources.midiState, factor)); - loop_.end = max(static_cast(region_->loopEnd(resources.midiState, factor)), loop_.start); + loop_.start = static_cast(region_->loopStart(resources.midiState)); + loop_.end = max(static_cast(region_->loopEnd(resources.midiState)), loop_.start); loop_.size = loop_.end + 1 - loop_.start; loop_.xfSize = static_cast(lroundPositive(region_->loopCrossfade * rate)); // Clamp the crossfade to the part available before the loop starts diff --git a/src/sfizz/sfizz.cpp b/src/sfizz/sfizz.cpp index 7ab340ec..62f9bb3a 100644 --- a/src/sfizz/sfizz.cpp +++ b/src/sfizz/sfizz.cpp @@ -265,32 +265,15 @@ void sfz::Sfizz::setNumVoices(int numVoices) noexcept synth->synth.setNumVoices(numVoices); } -bool sfz::Sfizz::setOversamplingFactor(int factor) noexcept +bool sfz::Sfizz::setOversamplingFactor(int) noexcept { - using sfz::Oversampling; - switch(factor) - { - case 1: - synth->synth.setOversamplingFactor(sfz::Oversampling::x1); - return true; - case 2: - synth->synth.setOversamplingFactor(sfz::Oversampling::x2); - return true; - case 4: - synth->synth.setOversamplingFactor(sfz::Oversampling::x4); - return true; - case 8: - synth->synth.setOversamplingFactor(sfz::Oversampling::x8); - return true; - default: - return false; - } + return true; } int sfz::Sfizz::getOversamplingFactor() const noexcept { - return static_cast(synth->synth.getOversamplingFactor()); + return 1; } void sfz::Sfizz::setPreloadSize(uint32_t preloadSize) noexcept diff --git a/src/sfizz/sfizz_wrapper.cpp b/src/sfizz/sfizz_wrapper.cpp index 5aa676a4..c431dd5d 100644 --- a/src/sfizz/sfizz_wrapper.cpp +++ b/src/sfizz/sfizz_wrapper.cpp @@ -200,31 +200,14 @@ void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned int preload_size) synth->synth.setPreloadSize(preload_size); } -sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t* synth) +sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t*) { - return static_cast(synth->synth.getOversamplingFactor()); + return SFIZZ_OVERSAMPLING_X1; } -bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t oversampling) +bool sfizz_set_oversampling_factor(sfizz_synth_t*, sfizz_oversampling_factor_t) { - using sfz::Oversampling; - switch(oversampling) - { - case SFIZZ_OVERSAMPLING_X1: - synth->synth.setOversamplingFactor(sfz::Oversampling::x1); - return true; - case SFIZZ_OVERSAMPLING_X2: - synth->synth.setOversamplingFactor(sfz::Oversampling::x2); - return true; - case SFIZZ_OVERSAMPLING_X4: - synth->synth.setOversamplingFactor(sfz::Oversampling::x4); - return true; - case SFIZZ_OVERSAMPLING_X8: - synth->synth.setOversamplingFactor(sfz::Oversampling::x8); - return true; - default: - return false; - } + return true; } int sfizz_get_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode) diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index e077c173..abc9054a 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -111,22 +111,6 @@ TEST_CASE("[Synth] Check that we can change the size of the preload before and a synth.renderBlock(buffer); } -TEST_CASE("[Synth] Check that we can change the oversampling factor before and after loading") -{ - sfz::Synth synth; - synth.setOversamplingFactor(sfz::Oversampling::x2); - sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; - synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz"); - synth.setOversamplingFactor(sfz::Oversampling::x4); - - synth.noteOn(0, 36, 24); - synth.noteOn(0, 36, 89); - synth.renderBlock(buffer); - synth.setOversamplingFactor(sfz::Oversampling::x2); - synth.renderBlock(buffer); -} - - TEST_CASE("[Synth] All notes offs/all sounds off") { sfz::Synth synth;