Deprecate the internal oversampling factor
- Removed it completely from the library - Kept the API for future work
This commit is contained in:
parent
752dd557e4
commit
2d6b99e1ab
19 changed files with 87 additions and 450 deletions
|
|
@ -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();
|
|
||||||
|
|
@ -64,9 +64,6 @@ target_link_libraries(bm_readChunk PRIVATE sfizz::sndfile)
|
||||||
sfizz_add_benchmark(bm_readChunkFlac BM_readChunkFlac.cpp)
|
sfizz_add_benchmark(bm_readChunkFlac BM_readChunkFlac.cpp)
|
||||||
target_link_libraries(bm_readChunkFlac PRIVATE sfizz::sndfile)
|
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_interpolators BM_interpolators.cpp)
|
||||||
|
|
||||||
sfizz_add_benchmark(bm_filterModulation BM_filterModulation.cpp ../src/sfizz/SfzFilter.cpp)
|
sfizz_add_benchmark(bm_filterModulation BM_filterModulation.cpp ../src/sfizz/SfzFilter.cpp)
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ int main(int argc, char** argv)
|
||||||
bool help { false };
|
bool help { false };
|
||||||
bool useEOT { false };
|
bool useEOT { false };
|
||||||
int quality { 2 };
|
int quality { 2 };
|
||||||
int oversampling { 1 };
|
|
||||||
|
|
||||||
options.add_options()
|
options.add_options()
|
||||||
("sfz", "SFZ file", cxxopts::value<std::string>())
|
("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>())
|
("wav", "Output wav file", cxxopts::value<std::string>())
|
||||||
("b,blocksize", "Block size for the sfizz callbacks", cxxopts::value(blockSize))
|
("b,blocksize", "Block size for the sfizz callbacks", cxxopts::value(blockSize))
|
||||||
("s,samplerate", "Output sample rate", cxxopts::value(sampleRate))
|
("s,samplerate", "Output sample rate", cxxopts::value(sampleRate))
|
||||||
("oversampling", "Internal oversampling factor", cxxopts::value(oversampling))
|
|
||||||
("q,quality", "Resampling quality", cxxopts::value(quality))
|
("q,quality", "Resampling quality", cxxopts::value(quality))
|
||||||
("v,verbose", "Verbose output", cxxopts::value(verbose))
|
("v,verbose", "Verbose output", cxxopts::value(verbose))
|
||||||
("log", "Produce logs", cxxopts::value<std::string>())
|
("log", "Produce logs", cxxopts::value<std::string>())
|
||||||
|
|
@ -126,23 +124,6 @@ int main(int argc, char** argv)
|
||||||
if (params.count("log") > 0)
|
if (params.count("log") > 0)
|
||||||
synth.enableLogging(params["log"].as<std::string>());
|
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.");
|
ERROR_IF(!synth.loadSfzFile(sfzPath), "There was an error loading the SFZ file.");
|
||||||
LOG_INFO(synth.getNumRegions() << " regions in the SFZ.");
|
LOG_INFO(synth.getNumRegions() << " regions in the SFZ.");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,9 +191,9 @@ midnam:update a lv2:Feature .
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 7 ;
|
lv2:index 7 ;
|
||||||
lv2:symbol "oversampling" ;
|
lv2:symbol "oversampling" ;
|
||||||
lv2:name "Internal oversampling factor",
|
lv2:name "Oversampling factor",
|
||||||
"Facteur de suréchantillonnage interne"@fr ,
|
"Facteur de suréchantillonnage"@fr ,
|
||||||
"Fattore Sovracampionamento Interno"@it ;
|
"Fattore Sovracampionamento"@it ;
|
||||||
pg:group <@LV2PLUGIN_URI@#config> ;
|
pg:group <@LV2PLUGIN_URI@#config> ;
|
||||||
lv2:portProperty pprop:notAutomatic ;
|
lv2:portProperty pprop:notAutomatic ;
|
||||||
lv2:portProperty pprop:expensive ;
|
lv2:portProperty pprop:expensive ;
|
||||||
|
|
|
||||||
16
src/sfizz.h
16
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.
|
* @brief Get the internal oversampling rate.
|
||||||
*
|
*
|
||||||
* This is the sampling rate of the engine, not the output or expected rate of
|
* As of 1.0, This is an inactive stub for future work on oversampling in the engine.
|
||||||
* the calling function. For the latter use the sfizz_get_sample_rate() function.
|
*
|
||||||
* @since 0.2.0
|
* @since 0.2.0
|
||||||
*
|
*
|
||||||
* @param synth The synth.
|
* @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.
|
* @brief Set the internal oversampling rate.
|
||||||
*
|
*
|
||||||
* This is the sampling rate of the engine, not the output or expected rate of
|
* As of 1.0, This is an inactive stub for future work on oversampling in the engine.
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @since 0.2.0
|
* @since 0.2.0
|
||||||
*
|
*
|
||||||
* @param synth The synth.
|
* @param synth The synth.
|
||||||
|
|
|
||||||
|
|
@ -723,18 +723,8 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Set the oversampling factor to a new value.
|
* @brief Set the oversampling factor to a new value.
|
||||||
*
|
*
|
||||||
* It will kill all the voices, and trigger a reloading of every file in
|
* As of 1.0, This is an inactive stub for future work on oversampling in the engine.
|
||||||
* the FilePool under the new oversampling.
|
|
||||||
*
|
*
|
||||||
* 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
|
* @since 0.2.0
|
||||||
*
|
*
|
||||||
|
|
@ -751,6 +741,9 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Return the current oversampling factor.
|
* @brief Return the current oversampling factor.
|
||||||
* @since 0.2.0
|
* @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;
|
int getOversamplingFactor() const noexcept;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,6 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
enum class Oversampling: int {
|
|
||||||
x1 = 1,
|
|
||||||
x2 = 2,
|
|
||||||
x4 = 4,
|
|
||||||
x8 = 8
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ExtendedCCs {
|
enum ExtendedCCs {
|
||||||
pitchBend = 128,
|
pitchBend = 128,
|
||||||
|
|
@ -69,7 +63,6 @@ namespace config {
|
||||||
constexpr float virtuallyZero { 0.001f };
|
constexpr float virtuallyZero { 0.001f };
|
||||||
constexpr float fastReleaseDuration { 0.01f };
|
constexpr float fastReleaseDuration { 0.01f };
|
||||||
constexpr char defineCharacter { '$' };
|
constexpr char defineCharacter { '$' };
|
||||||
constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 };
|
|
||||||
constexpr float A440 { 440.0 };
|
constexpr float A440 { 440.0 };
|
||||||
constexpr size_t powerHistoryLength { 16 };
|
constexpr size_t powerHistoryLength { 16 };
|
||||||
constexpr size_t powerFollowerStep { 512 };
|
constexpr size_t powerFollowerStep { 512 };
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@
|
||||||
#include "AudioBuffer.h"
|
#include "AudioBuffer.h"
|
||||||
#include "AudioSpan.h"
|
#include "AudioSpan.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Oversampler.h"
|
|
||||||
#include "utility/SwapAndPop.h"
|
#include "utility/SwapAndPop.h"
|
||||||
#include "utility/Debug.h"
|
#include "utility/Debug.h"
|
||||||
#include <ThreadPool.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;
|
sfz::FileAudioBuffer baseBuffer;
|
||||||
readBaseFile(reader, baseBuffer, numFrames);
|
readBaseFile(reader, baseBuffer, numFrames);
|
||||||
|
return baseBuffer;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.reset();
|
||||||
output.addChannels(reader.channels());
|
output.addChannels(reader.channels());
|
||||||
output.resize(numFrames * static_cast<int>(factor));
|
output.resize(numFrames);
|
||||||
output.clear();
|
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)
|
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 (existingFile != preloadedFiles.end()) {
|
||||||
if (framesToLoad > existingFile->second.preloadedData.getNumFrames()) {
|
if (framesToLoad > existingFile->second.preloadedData.getNumFrames()) {
|
||||||
preloadedFiles[fileId].information.maxOffset = maxOffset;
|
preloadedFiles[fileId].information.maxOffset = maxOffset;
|
||||||
preloadedFiles[fileId].preloadedData = readFromFile(*reader, framesToLoad, oversamplingFactor);
|
preloadedFiles[fileId].preloadedData = readFromFile(*reader, framesToLoad);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const auto factor = static_cast<double>(oversamplingFactor);
|
fileInformation->sampleRate = static_cast<double>(reader->sampleRate());
|
||||||
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);
|
|
||||||
auto insertedPair = preloadedFiles.insert_or_assign(fileId, {
|
auto insertedPair = preloadedFiles.insert_or_assign(fileId, {
|
||||||
readFromFile(*reader, framesToLoad, oversamplingFactor),
|
readFromFile(*reader, framesToLoad),
|
||||||
*fileInformation
|
*fileInformation
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -329,13 +349,9 @@ sfz::FileDataHolder sfz::FilePool::loadFile(const FileId& fileId) noexcept
|
||||||
if (existingFile != loadedFiles.end()) {
|
if (existingFile != loadedFiles.end()) {
|
||||||
return { &existingFile->second };
|
return { &existingFile->second };
|
||||||
} else {
|
} else {
|
||||||
const auto factor = static_cast<double>(oversamplingFactor);
|
fileInformation->sampleRate = static_cast<double>(reader->sampleRate());
|
||||||
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);
|
|
||||||
auto insertedPair = preloadedFiles.insert_or_assign(fileId, {
|
auto insertedPair = preloadedFiles.insert_or_assign(fileId, {
|
||||||
readFromFile(*reader, frames, oversamplingFactor),
|
readFromFile(*reader, frames),
|
||||||
*fileInformation
|
*fileInformation
|
||||||
});
|
});
|
||||||
insertedPair.first->second.status = FileData::Status::Preloaded;
|
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;
|
const auto maxOffset = preloadedFile.second.information.maxOffset;
|
||||||
fs::path file { rootDirectory / preloadedFile.first.filename() };
|
fs::path file { rootDirectory / preloadedFile.first.filename() };
|
||||||
AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse());
|
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;
|
return;
|
||||||
|
|
||||||
const auto frames = static_cast<uint32_t>(reader->frames());
|
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;
|
const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime;
|
||||||
logger.logFileTime(waitDuration, loadDuration, frames, id->filename());
|
logger.logFileTime(waitDuration, loadDuration, frames, id->filename());
|
||||||
|
|
||||||
|
|
@ -444,45 +460,6 @@ void sfz::FilePool::clear()
|
||||||
preloadedFiles.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
|
uint32_t sfz::FilePool::getPreloadSize() const noexcept
|
||||||
{
|
{
|
||||||
return preloadSize;
|
return preloadSize;
|
||||||
|
|
@ -578,8 +555,7 @@ void sfz::FilePool::setRamLoading(bool loadInRam) noexcept
|
||||||
AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse());
|
AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse());
|
||||||
preloadedFile.second.preloadedData = readFromFile(
|
preloadedFile.second.preloadedData = readFromFile(
|
||||||
*reader,
|
*reader,
|
||||||
preloadedFile.second.information.end,
|
preloadedFile.second.information.end
|
||||||
oversamplingFactor
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -278,19 +278,6 @@ public:
|
||||||
* @return uint32_t
|
* @return uint32_t
|
||||||
*/
|
*/
|
||||||
uint32_t getPreloadSize() const noexcept;
|
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
|
* @brief Empty the file loading queues without actually loading
|
||||||
* the files. All promises will be unfulfilled. Don't call this
|
* the files. All promises will be unfulfilled. Don't call this
|
||||||
|
|
@ -331,7 +318,6 @@ private:
|
||||||
|
|
||||||
bool loadInRam { config::loadInRam };
|
bool loadInRam { config::loadInRam };
|
||||||
uint32_t preloadSize { config::preloadSize };
|
uint32_t preloadSize { config::preloadSize };
|
||||||
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
volatile bool dispatchFlag { true };
|
volatile bool dispatchFlag { true };
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,13 @@
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
class AudioReader;
|
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
|
* @brief Wraps the internal oversampler in a single function that takes an
|
||||||
* AudioBuffer and oversamples it in another pre-allocated one. The
|
* AudioBuffer and oversamples it in another pre-allocated one. The
|
||||||
|
|
|
||||||
|
|
@ -1704,14 +1704,13 @@ float sfz::Region::getPhase() const noexcept
|
||||||
return phase;
|
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 };
|
std::uniform_int_distribution<int64_t> offsetDistribution { 0, offsetRandom };
|
||||||
uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator);
|
uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator);
|
||||||
for (const auto& mod: offsetCC)
|
for (const auto& mod: offsetCC)
|
||||||
finalOffset += static_cast<uint64_t>(mod.data * midiState.getCCValue(mod.cc));
|
finalOffset += static_cast<uint64_t>(mod.data * midiState.getCCValue(mod.cc));
|
||||||
|
return Default::offset.bounds.clamp(finalOffset);
|
||||||
return Default::offset.bounds.clamp(finalOffset) * static_cast<uint64_t>(factor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float sfz::Region::getDelay(const MidiState& midiState) const noexcept
|
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);
|
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;
|
int64_t end = sampleEnd;
|
||||||
for (const auto& mod: endCC)
|
for (const auto& mod: endCC)
|
||||||
end += static_cast<int64_t>(mod.data * midiState.getCCValue(mod.cc));
|
end += static_cast<int64_t>(mod.data * midiState.getCCValue(mod.cc));
|
||||||
|
|
||||||
end = clamp(end, int64_t { 0 }, sampleEnd);
|
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();
|
auto start = loopRange.getStart();
|
||||||
for (const auto& mod: loopStartCC)
|
for (const auto& mod: loopStartCC)
|
||||||
start += static_cast<int64_t>(mod.data * midiState.getCCValue(mod.cc));
|
start += static_cast<int64_t>(mod.data * midiState.getCCValue(mod.cc));
|
||||||
|
|
||||||
start = clamp(start, int64_t { 0 }, sampleEnd);
|
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();
|
auto end = loopRange.getEnd();
|
||||||
for (const auto& mod: loopEndCC)
|
for (const auto& mod: loopEndCC)
|
||||||
end += static_cast<int64_t>(mod.data * midiState.getCCValue(mod.cc));
|
end += static_cast<int64_t>(mod.data * midiState.getCCValue(mod.cc));
|
||||||
|
|
||||||
end = clamp(end, int64_t { 0 }, sampleEnd);
|
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
|
float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ struct Region {
|
||||||
* @param midiState
|
* @param midiState
|
||||||
* @return uint32_t
|
* @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
|
* @brief Get the region delay in seconds
|
||||||
*
|
*
|
||||||
|
|
@ -180,7 +180,7 @@ struct Region {
|
||||||
*
|
*
|
||||||
* @return uint32_t
|
* @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.
|
* @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.
|
* This must be called multiple times for each opcode applying to this region.
|
||||||
|
|
@ -260,8 +260,8 @@ struct Region {
|
||||||
|
|
||||||
void offsetAllKeys(int offset) noexcept;
|
void offsetAllKeys(int offset) noexcept;
|
||||||
|
|
||||||
uint32_t loopStart(MidiState& midiState, Oversampling factor = Oversampling::x1) const noexcept;
|
uint32_t loopStart(MidiState& midiState) const noexcept;
|
||||||
uint32_t loopEnd(MidiState& midiState, Oversampling factor = Oversampling::x1) const noexcept;
|
uint32_t loopEnd(MidiState& midiState) const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the gain this region contributes into the input of the Nth
|
* @brief Get the gain this region contributes into the input of the Nth
|
||||||
|
|
|
||||||
|
|
@ -1849,28 +1849,6 @@ void Synth::Impl::setupModMatrix()
|
||||||
mm.init();
|
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
|
void Synth::setPreloadSize(uint32_t preloadSize) noexcept
|
||||||
{
|
{
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
|
|
|
||||||
|
|
@ -545,26 +545,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void setNumVoices(int numVoices) noexcept;
|
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.
|
* @brief Set the preloaded file size.
|
||||||
* This function takes a lock and disables the callback; prefer calling
|
* This function takes a lock and disables the callback; prefer calling
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,6 @@ struct Synth::Impl final: public Parser::Listener {
|
||||||
float sampleRate_ { config::defaultSampleRate };
|
float sampleRate_ { config::defaultSampleRate };
|
||||||
float volume_ { Default::globalVolume };
|
float volume_ { Default::globalVolume };
|
||||||
int numVoices_ { config::numVoices };
|
int numVoices_ { config::numVoices };
|
||||||
Oversampling oversamplingFactor_ { config::defaultOversamplingFactor };
|
|
||||||
|
|
||||||
// Distribution used to generate random value for the *rand opcodes
|
// Distribution used to generate random value for the *rand opcodes
|
||||||
std::uniform_real_distribution<float> randNoteDistribution_ { 0, 1 };
|
std::uniform_real_distribution<float> randNoteDistribution_ { 0, 1 };
|
||||||
|
|
|
||||||
|
|
@ -466,7 +466,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
|
||||||
}
|
}
|
||||||
impl.updateLoopInformation();
|
impl.updateLoopInformation();
|
||||||
impl.speedRatio_ = static_cast<float>(impl.currentPromise_->information.sampleRate / impl.sampleRate_);
|
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
|
// 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.triggerDelay_ = delay;
|
||||||
impl.initialDelay_ = delay + static_cast<int>(region.getDelay(resources.midiState) * impl.sampleRate_);
|
impl.initialDelay_ = delay + static_cast<int>(region.getDelay(resources.midiState) * impl.sampleRate_);
|
||||||
impl.baseFrequency_ = resources.tuning.getFrequencyOfKey(impl.triggerEvent_.number);
|
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.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1;
|
||||||
impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_);
|
impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_);
|
||||||
impl.bendSmoother_.reset(region.getBendInCents(resources.midiState.getPitchBend()));
|
impl.bendSmoother_.reset(region.getBendInCents(resources.midiState.getPitchBend()));
|
||||||
|
|
@ -1676,11 +1676,10 @@ void Voice::Impl::updateLoopInformation() noexcept
|
||||||
|
|
||||||
Resources& resources = resources_;
|
Resources& resources = resources_;
|
||||||
const FileInformation& info = currentPromise_->information;
|
const FileInformation& info = currentPromise_->information;
|
||||||
const Oversampling factor = resources_.filePool.getOversamplingFactor();
|
|
||||||
const double rate = info.sampleRate;
|
const double rate = info.sampleRate;
|
||||||
|
|
||||||
loop_.start = static_cast<int>(region_->loopStart(resources.midiState, factor));
|
loop_.start = static_cast<int>(region_->loopStart(resources.midiState));
|
||||||
loop_.end = max(static_cast<int>(region_->loopEnd(resources.midiState, factor)), loop_.start);
|
loop_.end = max(static_cast<int>(region_->loopEnd(resources.midiState)), loop_.start);
|
||||||
loop_.size = loop_.end + 1 - loop_.start;
|
loop_.size = loop_.end + 1 - loop_.start;
|
||||||
loop_.xfSize = static_cast<int>(lroundPositive(region_->loopCrossfade * rate));
|
loop_.xfSize = static_cast<int>(lroundPositive(region_->loopCrossfade * rate));
|
||||||
// Clamp the crossfade to the part available before the loop starts
|
// Clamp the crossfade to the part available before the loop starts
|
||||||
|
|
|
||||||
|
|
@ -265,32 +265,15 @@ void sfz::Sfizz::setNumVoices(int numVoices) noexcept
|
||||||
synth->synth.setNumVoices(numVoices);
|
synth->synth.setNumVoices(numVoices);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sfz::Sfizz::setOversamplingFactor(int factor) noexcept
|
bool sfz::Sfizz::setOversamplingFactor(int) noexcept
|
||||||
{
|
{
|
||||||
using sfz::Oversampling;
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int sfz::Sfizz::getOversamplingFactor() const noexcept
|
int sfz::Sfizz::getOversamplingFactor() const noexcept
|
||||||
{
|
{
|
||||||
return static_cast<int>(synth->synth.getOversamplingFactor());
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Sfizz::setPreloadSize(uint32_t preloadSize) noexcept
|
void sfz::Sfizz::setPreloadSize(uint32_t preloadSize) noexcept
|
||||||
|
|
|
||||||
|
|
@ -200,31 +200,14 @@ void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned int preload_size)
|
||||||
synth->synth.setPreloadSize(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;
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sfizz_get_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode)
|
int sfizz_get_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode)
|
||||||
|
|
|
||||||
|
|
@ -111,22 +111,6 @@ TEST_CASE("[Synth] Check that we can change the size of the preload before and a
|
||||||
synth.renderBlock(buffer);
|
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")
|
TEST_CASE("[Synth] All notes offs/all sounds off")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue