Merge pull request #642 from paulfd/deprecate-internal-oversampling

Deprecate the internal oversampling factor
This commit is contained in:
Paul Ferrand 2021-04-14 11:11:55 +02:00 committed by GitHub
commit 887ff26220
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 87 additions and 450 deletions

View file

@ -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 <benchmark/benchmark.h>
#include <sndfile.hh>
#include "ghc/filesystem.hpp"
#include "Oversampler.h"
#include "AudioBuffer.h"
#include "absl/memory/memory.h"
#include <iostream>
constexpr std::array<double, 12> 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<double, 4> coeffsStage4x {
0.042448989488488006,
0.17072114107630679,
0.39329183835224008,
0.74569514831986694
};
constexpr std::array<double, 3> coeffsStage8x {
0.055748680811302048,
0.24305119574153092,
0.6466991311926823
};
#if defined(__x86_64__) || defined(__i386__)
#include "hiir/Upsampler2xSse.h"
using Upsampler2x = hiir::Upsampler2xSse<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xSse<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xSse<coeffsStage8x.size()>;
#elif defined(__arm__) || defined(__aarch64__)
#include "hiir/Upsampler2xNeon.h"
using Upsampler2x = hiir::Upsampler2xNeon<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xNeon<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xNeon<coeffsStage8x.size()>;
#else
#include "hiir/Upsampler2xFpu.h"
using Upsampler2x = hiir::Upsampler2xFpu<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xFpu<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xFpu<coeffsStage8x.size()>;
#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<size_t>(sndfile.frames());
output = absl::make_unique<sfz::AudioBuffer<float>>(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<sfz::AudioBuffer<float>> output;
SndfileHandle sndfile;
ghc::filesystem::path rootPath;
size_t numFrames { 0 };
};
BENCHMARK_DEFINE_F(FileFixture, NoResampling)(benchmark::State& state) {
for (auto _ : state)
{
sfz::Buffer<float> 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<float> buffer { numFrames * sndfile.channels() };
sfz::Buffer<float> 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<long>(numFrames));
upsampler4x.process_block(output->channelWriter(0), temp.data(), static_cast<long>(numFrames * 2));
upsampler2x.clear_buffers();
upsampler4x.clear_buffers();
upsampler2x.process_block(temp.data(), output->channelReader(1), static_cast<long>(numFrames));
upsampler4x.process_block(output->channelWriter(1), temp.data(), static_cast<long>(numFrames * 2));
}
}
BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) {
for (auto _ : state)
{
auto chunkSize = static_cast<size_t>(state.range(0));
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sfz::Buffer<float> leftInput { chunkSize };
sfz::Buffer<float> rightInput { chunkSize };
sfz::Buffer<float> 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<long>(thisChunkSize));
upsampler4xLeft.process_block(output->channelWriter(0) + outputFrameCounter, chunkSpan.data(), static_cast<long>(thisChunkSize * 2));
upsampler2xRight.process_block(chunkSpan.data(), rightSpan.data(), static_cast<long>(thisChunkSize));
upsampler4xRight.process_block(output->channelWriter(1) + outputFrameCounter, chunkSpan.data(), static_cast<long>(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();

View file

@ -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)

View file

@ -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<std::string>())
@ -74,7 +73,6 @@ int main(int argc, char** argv)
("wav", "Output wav file", cxxopts::value<std::string>())
("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<std::string>())
@ -126,23 +124,6 @@ int main(int argc, char** argv)
if (params.count("log") > 0)
synth.enableLogging(params["log"].as<std::string>());
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.");

View file

@ -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 ;

View file

@ -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.

View file

@ -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;

View file

@ -17,12 +17,6 @@
#include <cstdint>
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 };

View file

@ -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 <ThreadPool.h>
@ -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<int>(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<size_t>* filledFrames = nullptr)
void streamFromFile(sfz::AudioReader& reader, sfz::FileAudioBuffer& output, std::atomic<size_t>* filledFrames = nullptr)
{
const auto numFrames = static_cast<size_t>(reader.frames());
const auto numChannels = reader.channels();
const auto chunkSize = static_cast<size_t>(sfz::config::chunkSize);
output.reset();
output.addChannels(reader.channels());
output.resize(numFrames * static_cast<int>(factor));
output.resize(numFrames);
output.clear();
sfz::Oversampler oversampler { factor };
oversampler.stream(reader, output, filledFrames);
sfz::Buffer<float> 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<size_t>(
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<double>(oversamplingFactor);
fileInformation->sampleRate = factor * static_cast<double>(reader->sampleRate());
fileInformation->end = static_cast<uint32_t>(factor * fileInformation->end);
fileInformation->loopStart = static_cast<uint32_t>(factor * fileInformation->loopStart);
fileInformation->loopEnd = static_cast<uint32_t>(factor * fileInformation->loopEnd);
fileInformation->sampleRate = static_cast<double>(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<double>(oversamplingFactor);
fileInformation->sampleRate = factor * static_cast<double>(reader->sampleRate());
fileInformation->end = static_cast<uint32_t>(factor * fileInformation->end);
fileInformation->loopStart = static_cast<uint32_t>(factor * fileInformation->loopStart);
fileInformation->loopEnd = static_cast<uint32_t>(factor * fileInformation->loopEnd);
fileInformation->sampleRate = static_cast<double>(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<uint32_t>(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<float>(factor) / static_cast<float>(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<uint32_t>(samplerateChange * information.end);
information.loopStart = static_cast<uint32_t>(samplerateChange * information.loopStart);
information.loopEnd = static_cast<uint32_t>(samplerateChange * information.loopEnd);
if (preloadedFile.second.status == FileData::Status::Done) {
const auto realFrames =
preloadedFile.second.availableFrames.load() / static_cast<unsigned>(this->oversamplingFactor);
preloadedFile.second.fileData = readFromFile(*reader, realFrames, factor);
preloadedFile.second.availableFrames = realFrames * static_cast<unsigned>(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 {

View file

@ -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 };

View file

@ -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

View file

@ -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<int64_t> offsetDistribution { 0, offsetRandom };
uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator);
for (const auto& mod: offsetCC)
finalOffset += static_cast<uint64_t>(mod.data * midiState.getCCValue(mod.cc));
return Default::offset.bounds.clamp(finalOffset) * static_cast<uint64_t>(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<int64_t>(mod.data * midiState.getCCValue(mod.cc));
end = clamp(end, int64_t { 0 }, sampleEnd);
return static_cast<uint32_t>(end) * static_cast<uint32_t>(factor);
return static_cast<uint32_t>(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<int64_t>(mod.data * midiState.getCCValue(mod.cc));
start = clamp(start, int64_t { 0 }, sampleEnd);
return static_cast<uint32_t>(start) * static_cast<uint32_t>(factor);
return static_cast<uint32_t>(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<int64_t>(mod.data * midiState.getCCValue(mod.cc));
end = clamp(end, int64_t { 0 }, sampleEnd);
return static_cast<uint32_t>(end) * static_cast<uint32_t>(factor);
return static_cast<uint32_t>(end);
}
float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept

View file

@ -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

View file

@ -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_;

View file

@ -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

View file

@ -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<float> randNoteDistribution_ { 0, 1 };

View file

@ -466,7 +466,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
}
impl.updateLoopInformation();
impl.speedRatio_ = static_cast<float>(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<int>(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<int>(region_->loopStart(resources.midiState, factor));
loop_.end = max(static_cast<int>(region_->loopEnd(resources.midiState, factor)), loop_.start);
loop_.start = static_cast<int>(region_->loopStart(resources.midiState));
loop_.end = max(static_cast<int>(region_->loopEnd(resources.midiState)), loop_.start);
loop_.size = loop_.end + 1 - loop_.start;
loop_.xfSize = static_cast<int>(lroundPositive(region_->loopCrossfade * rate));
// Clamp the crossfade to the part available before the loop starts

View file

@ -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<int>(synth->synth.getOversamplingFactor());
return 1;
}
void sfz::Sfizz::setPreloadSize(uint32_t preloadSize) noexcept

View file

@ -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<sfizz_oversampling_factor_t>(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)

View file

@ -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<float> buffer { 2, static_cast<unsigned>(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;