Merge pull request #65 from paulfd/processing-logs

Processing logs
This commit is contained in:
Paul Ferrand 2020-02-27 12:59:29 +01:00 committed by GitHub
commit 9cac220de1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 414 additions and 83 deletions

51
benchmarks/BM_clock.cpp Normal file
View file

@ -0,0 +1,51 @@
// 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 <benchmark/benchmark.h>
#include <chrono>
class Clock : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
}
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
}
std::chrono::time_point<std::chrono::high_resolution_clock> hires;
std::chrono::time_point<std::chrono::steady_clock> steady;
};
BENCHMARK_DEFINE_F(Clock, HighRes)(benchmark::State& state) {
for (auto _ : state)
{
hires = std::chrono::high_resolution_clock::now();
}
}
BENCHMARK_DEFINE_F(Clock, Steady)(benchmark::State& state) {
for (auto _ : state)
{
steady = std::chrono::steady_clock::now();
}
}
// Just checking that stuff happens..
BENCHMARK_DEFINE_F(Clock, Both)(benchmark::State& state) {
for (auto _ : state)
{
hires = std::chrono::high_resolution_clock::now();
steady = std::chrono::steady_clock::now();
}
}
BENCHMARK_REGISTER_F(Clock, HighRes);
BENCHMARK_REGISTER_F(Clock, Steady);
BENCHMARK_REGISTER_F(Clock, Both);
BENCHMARK_MAIN();

48
benchmarks/BM_logger.cpp Normal file
View file

@ -0,0 +1,48 @@
// 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 <benchmark/benchmark.h>
#include "Logger.h"
#include <chrono>
class Logger : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
}
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
}
};
BENCHMARK_DEFINE_F(Logger, Baseline)(benchmark::State& state) {
for (auto _ : state)
{
sfz::Logger logger {};
benchmark::DoNotOptimize(logger);
}
}
BENCHMARK_DEFINE_F(Logger, ProcessingTime)(benchmark::State& state) {
for (auto _ : state)
{
sfz::Logger logger {};
sfz::CallbackBreakdown breakdown;
breakdown.data = sfz::Duration(1);
breakdown.renderMethod = sfz::Duration(1);
breakdown.dispatch = sfz::Duration(1);
breakdown.panning = sfz::Duration(1);
breakdown.filters = sfz::Duration(1);
breakdown.amplitude = sfz::Duration(1);
logger.logCallbackTime(std::move(breakdown), 16, 16);
}
}
BENCHMARK_REGISTER_F(Logger, Baseline);
BENCHMARK_REGISTER_F(Logger, ProcessingTime);
BENCHMARK_MAIN();

View file

@ -32,6 +32,7 @@ macro(sfizz_add_benchmark TARGET)
endmacro() endmacro()
sfizz_add_benchmark(bm_opf_high_vs_low BM_OPF_high_vs_low.cpp) sfizz_add_benchmark(bm_opf_high_vs_low BM_OPF_high_vs_low.cpp)
sfizz_add_benchmark(bm_clock BM_clock.cpp)
sfizz_add_benchmark(bm_write BM_writeInterleaved.cpp) sfizz_add_benchmark(bm_write BM_writeInterleaved.cpp)
sfizz_add_benchmark(bm_read BM_readInterleaved.cpp) sfizz_add_benchmark(bm_read BM_readInterleaved.cpp)
sfizz_add_benchmark(bm_fill BM_fill.cpp) sfizz_add_benchmark(bm_fill BM_fill.cpp)
@ -57,6 +58,9 @@ sfizz_add_benchmark(bm_widthPos BM_widthPos.cpp)
sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp) sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp)
sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp) sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp)
sfizz_add_benchmark(bm_logger BM_logger.cpp)
target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
if (TARGET sfizz-samplerate) if (TARGET sfizz-samplerate)
sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES}) sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_resample PRIVATE sfizz-samplerate sfizz-sndfile) target_link_libraries(bm_resample PRIVATE sfizz-samplerate sfizz-sndfile)
@ -86,6 +90,7 @@ add_custom_target(sfizz_benchmarks)
add_dependencies(sfizz_benchmarks add_dependencies(sfizz_benchmarks
bm_opf_high_vs_low bm_opf_high_vs_low
bm_write bm_write
bm_clock
bm_pointerIterationOrOffsets bm_pointerIterationOrOffsets
bm_read bm_read
bm_mean bm_mean
@ -102,6 +107,7 @@ add_dependencies(sfizz_benchmarks
bm_ramp bm_ramp
bm_ADSR bm_ADSR
bm_add bm_add
bm_logger
bm_pan bm_pan
bm_subtract bm_subtract
bm_multiplyAdd bm_multiplyAdd

View file

@ -361,6 +361,21 @@ SFIZZ_EXPORTED_API char* sfizz_get_unknown_opcodes(sfizz_synth_t* synth);
*/ */
SFIZZ_EXPORTED_API bool sfizz_should_reload_file(sfizz_synth_t* synth); SFIZZ_EXPORTED_API bool sfizz_should_reload_file(sfizz_synth_t* synth);
/**
* @brief Enable logging of timings to sidecar CSV files. This can produce
* many outputs so use with caution.
*
* @param synth
*/
SFIZZ_EXPORTED_API void sfizz_enable_logging(sfizz_synth_t* synth);
/**
* @brief Disable logging
*
* @param synth
*/
SFIZZ_EXPORTED_API void sfizz_disable_logging(sfizz_synth_t* synth);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -248,6 +248,17 @@ public:
* @return false * @return false
*/ */
bool shouldReloadFile(); bool shouldReloadFile();
/**
* @brief Enable logging of timings to sidecar CSV files. This can produce
* many outputs so use with caution.
*
*/
void enableLogging() noexcept;
/**
* @brief Disable logging;
*
*/
void disableLogging() noexcept;
private: private:
std::unique_ptr<sfz::Synth> synth; std::unique_ptr<sfz::Synth> synth;
}; };

View file

@ -30,6 +30,7 @@ namespace config {
constexpr int maxBlockSize { 8192 }; constexpr int maxBlockSize { 8192 };
constexpr int preloadSize { 8192 }; constexpr int preloadSize { 8192 };
constexpr int loggerQueueSize { 16 }; constexpr int loggerQueueSize { 16 };
constexpr int voiceLoggerQueueSize { 256 };
constexpr bool loggingEnabled { false }; constexpr bool loggingEnabled { false };
constexpr size_t numChannels { 2 }; constexpr size_t numChannels { 2 };
constexpr int numBackgroundThreads { 4 }; constexpr int numBackgroundThreads { 4 };

View file

@ -324,8 +324,6 @@ void sfz::FilePool::loadingThread() noexcept
threadsLoading--; threadsLoading--;
while (!filledPromiseQueue.try_push(promise)) { while (!filledPromiseQueue.try_push(promise)) {
DBG("[sfizz] Error enqueuing the promise for " << promise->filename << " in the filledPromiseQueue"); DBG("[sfizz] Error enqueuing the promise for " << promise->filename << " in the filledPromiseQueue");
std::this_thread::sleep_for(1ms); std::this_thread::sleep_for(1ms);

View file

@ -65,7 +65,7 @@ sfz::Logger::~Logger()
<< prefix << prefix
<< "_file_log.csv"; << "_file_log.csv";
fs::path fileLogPath{ fs::current_path() / fileLogFilename.str() }; fs::path fileLogPath{ fs::current_path() / fileLogFilename.str() };
DBG("Logging file times to " << fileLogPath.filename()); std::cout << "Logging " << fileTimes.size() << " file times to " << fileLogPath.filename() << '\n';
std::ofstream loadLogFile { fileLogPath.string() }; std::ofstream loadLogFile { fileLogPath.string() };
loadLogFile << "WaitDuration,LoadDuration,FileSize,FileName" << '\n'; loadLogFile << "WaitDuration,LoadDuration,FileSize,FileName" << '\n';
for (auto& time: fileTimes) for (auto& time: fileTimes)
@ -81,23 +81,28 @@ sfz::Logger::~Logger()
<< prefix << prefix
<< "_callback_log.csv"; << "_callback_log.csv";
fs::path callbackLogPath{ fs::current_path() / callbackLogFilename.str() }; fs::path callbackLogPath{ fs::current_path() / callbackLogFilename.str() };
DBG("Logging callback times to " << callbackLogPath.filename()); std::cout << "Logging " << callbackTimes.size() << " callback times to " << callbackLogPath.filename() << '\n';
std::ofstream callbackLogFile { callbackLogPath.string() }; std::ofstream callbackLogFile { callbackLogPath.string() };
callbackLogFile << "Duration,NumVoices,NumSamples" << '\n'; callbackLogFile << "Dispatch,RenderMethod,Data,Amplitude,Filters,Panning,NumVoices,NumSamples" << '\n';
for (auto& time: callbackTimes) for (auto& time: callbackTimes)
callbackLogFile << time.duration.count() << ',' callbackLogFile << time.breakdown.dispatch.count() << ','
<< time.breakdown.renderMethod.count() << ','
<< time.breakdown.data.count() << ','
<< time.breakdown.amplitude.count() << ','
<< time.breakdown.filters.count() << ','
<< time.breakdown.panning.count() << ','
<< time.numVoices << ',' << time.numVoices << ','
<< time.numSamples << '\n'; << time.numSamples << '\n';
} }
} }
void sfz::Logger::logCallbackTime(std::chrono::duration<double> duration, int numVoices, size_t numSamples) void sfz::Logger::logCallbackTime(CallbackBreakdown&& breakdown, int numVoices, size_t numSamples)
{ {
if (!loggingEnabled) if (!loggingEnabled)
return; return;
callbackTimeQueue.try_push<CallbackTime>({ duration, numVoices, numSamples }); callbackTimeQueue.try_push<CallbackTime>({ std::move(breakdown), numVoices, numSamples });
} }
void sfz::Logger::logFileTime(std::chrono::duration<double> waitDuration, std::chrono::duration<double> loadDuration, uint32_t fileSize, absl::string_view filename) void sfz::Logger::logFileTime(std::chrono::duration<double> waitDuration, std::chrono::duration<double> loadDuration, uint32_t fileSize, absl::string_view filename)
@ -126,7 +131,7 @@ void sfz::Logger::moveEvents() noexcept
while (callbackTimeQueue.try_pop(callbackTime)) while (callbackTimeQueue.try_pop(callbackTime))
callbackTimes.push_back(callbackTime); callbackTimes.push_back(callbackTime);
sfz::FileTime fileTime; FileTime fileTime;
while (fileTimeQueue.try_pop(fileTime)) while (fileTimeQueue.try_pop(fileTime))
fileTimes.push_back(fileTime); fileTimes.push_back(fileTime);
@ -135,7 +140,7 @@ void sfz::Logger::moveEvents() noexcept
callbackTimes.clear(); callbackTimes.clear();
} }
std::this_thread::sleep_for(50ms); std::this_thread::sleep_for(10ms);
} }
} }
@ -149,3 +154,22 @@ void sfz::Logger::disableLogging()
loggingEnabled = false; loggingEnabled = false;
clearFlag.clear(); clearFlag.clear();
} }
sfz::ScopedTiming::ScopedTiming(Duration& targetDuration, Operation operation)
: targetDuration(targetDuration), operation(operation)
{
}
sfz::ScopedTiming::~ScopedTiming()
{
switch(operation)
{
case(Operation::replaceDuration):
targetDuration = std::chrono::high_resolution_clock::now() - creationTime;
break;
case(Operation::addToDuration):
targetDuration += std::chrono::high_resolution_clock::now() - creationTime;
break;
}
}

View file

@ -10,23 +10,62 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <chrono> #include <chrono>
#include <functional>
#include <thread> #include <thread>
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
namespace sfz namespace sfz
{ {
using Duration = std::chrono::duration<double>;
/**
* @brief Creates an RAII logger which fills or adds to a duration on destruction
*
*/
struct ScopedTiming
{
using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
enum class Operation
{
addToDuration,
replaceDuration
};
ScopedTiming() = delete;
/**
* @brief Construct a new Scoped Logger object
*
* @param targetDuration
* @param op
*/
ScopedTiming(Duration& targetDuration, Operation op = Operation::replaceDuration);
~ScopedTiming();
Duration& targetDuration;
const Operation operation;
const TimePoint creationTime { std::chrono::high_resolution_clock::now() };
};
struct FileTime struct FileTime
{ {
std::chrono::duration<double> waitDuration; Duration waitDuration;
std::chrono::duration<double> loadDuration; Duration loadDuration;
uint32_t fileSize; uint32_t fileSize;
absl::string_view filename; absl::string_view filename;
}; };
struct CallbackBreakdown
{
Duration dispatch { 0 };
Duration renderMethod { 0 };
Duration data { 0 };
Duration amplitude { 0 };
Duration filters { 0 };
Duration panning { 0 };
};
struct CallbackTime struct CallbackTime
{ {
std::chrono::duration<double> duration; CallbackBreakdown breakdown;
int numVoices; int numVoices;
size_t numSamples; size_t numSamples;
}; };
@ -36,13 +75,53 @@ class Logger
public: public:
Logger(); Logger();
~Logger(); ~Logger();
/**
* @brief Set the prefix for the output log files
*
* @param prefix
*/
void setPrefix(const std::string& prefix); void setPrefix(const std::string& prefix);
/**
* @brief Removes all logged data
*
*/
void clear(); void clear();
/**
* @brief Enables logging and writing to log files on destruction
*
*/
void enableLogging(); void enableLogging();
/**
* @brief Disables logging and writing to log files on destruction
*
*/
void disableLogging(); void disableLogging();
void logCallbackTime(std::chrono::duration<double> duration, int numVoices, size_t numSamples);
void logFileTime(std::chrono::duration<double> waitDuration, std::chrono::duration<double> loadDuration, uint32_t fileSize, absl::string_view filename); /**
* @brief Logs the callback duration, with breakdown per operations
*
* @param breakdown The different timings for the callback
* @param numVoices The number of active voices
* @param numSamples The number of samples in the callback
*/
void logCallbackTime(CallbackBreakdown&& breakdown, int numVoices, size_t numSamples);
/**
* @brief Log a file loading and waiting duration
*
* @param waitDuration The time spent waiting before loading the file
* @param loadDuration The time it took to load the file
* @param fileSize The file size
* @param filename The file name
*/
void logFileTime(Duration waitDuration, Duration loadDuration, uint32_t fileSize, absl::string_view filename);
private: private:
/**
* @brief Move all events from the real time queues to the non-realtime vectors
*/
void moveEvents() noexcept; void moveEvents() noexcept;
bool loggingEnabled { config::loggingEnabled }; bool loggingEnabled { config::loggingEnabled };
std::string prefix { "" }; std::string prefix { "" };

View file

@ -410,11 +410,7 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
{ {
ScopedFTZ ftz; ScopedFTZ ftz;
const auto callbackStartTime = std::chrono::high_resolution_clock::now();
buffer.fill(0.0f);
resources.filePool.cleanupPromises();
if (freeWheeling) if (freeWheeling)
resources.filePool.waitForBackgroundLoading(); resources.filePool.waitForBackgroundLoading();
@ -423,20 +419,35 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
if (!canEnterCallback) if (!canEnterCallback)
return; return;
auto tempSpan = AudioSpan<float>(tempBuffer).first(buffer.getNumFrames()); CallbackBreakdown callbackBreakdown;
int numActiveVoices { 0 }; int numActiveVoices { 0 };
{ // Main render block
ScopedTiming logger { callbackBreakdown.renderMethod };
buffer.fill(0.0f);
resources.filePool.cleanupPromises();
auto tempSpan = AudioSpan<float>(tempBuffer).first(buffer.getNumFrames());
for (auto& voice : voices) { for (auto& voice : voices) {
if (!voice->isFree()) { if (!voice->isFree()) {
numActiveVoices++; numActiveVoices++;
voice->renderBlock(tempSpan); voice->renderBlock(tempSpan);
buffer.add(tempSpan); buffer.add(tempSpan);
callbackBreakdown.data += voice->getLastDataDuration();
callbackBreakdown.amplitude += voice->getLastAmplitudeDuration();
callbackBreakdown.filters += voice->getLastFilterDuration();
callbackBreakdown.panning += voice->getLastPanningDuration();
} }
} }
buffer.applyGain(db2mag(volume)); buffer.applyGain(db2mag(volume));
}
const auto callbackDuration = std::chrono::high_resolution_clock::now() - callbackStartTime; callbackBreakdown.dispatch = dispatchDuration;
resources.logger.logCallbackTime(callbackDuration, numActiveVoices, buffer.getNumFrames()); resources.logger.logCallbackTime(std::move(callbackBreakdown), numActiveVoices, buffer.getNumFrames());
// Reset the dispatch counter
dispatchDuration = Duration(0);
} }
void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
@ -444,6 +455,7 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
ASSERT(noteNumber < 128); ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0); ASSERT(noteNumber >= 0);
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
resources.midiState.noteOnEvent(noteNumber, velocity); resources.midiState.noteOnEvent(noteNumber, velocity);
AtomicGuard callbackGuard { inCallback }; AtomicGuard callbackGuard { inCallback };
@ -458,6 +470,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity [[maybe_unu
ASSERT(noteNumber < 128); ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0); ASSERT(noteNumber >= 0);
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
resources.midiState.noteOffEvent(noteNumber, velocity); resources.midiState.noteOffEvent(noteNumber, velocity);
AtomicGuard callbackGuard { inCallback }; AtomicGuard callbackGuard { inCallback };
@ -513,6 +526,8 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept
ASSERT(ccNumber < config::numCCs); ASSERT(ccNumber < config::numCCs);
ASSERT(ccNumber >= 0); ASSERT(ccNumber >= 0);
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
resources.midiState.ccEvent(ccNumber, ccValue); resources.midiState.ccEvent(ccNumber, ccValue);
AtomicGuard callbackGuard { inCallback }; AtomicGuard callbackGuard { inCallback };
@ -543,6 +558,8 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept
ASSERT(pitch <= 8192); ASSERT(pitch <= 8192);
ASSERT(pitch >= -8192); ASSERT(pitch >= -8192);
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
resources.midiState.pitchBendEvent(pitch); resources.midiState.pitchBendEvent(pitch);
for (auto& region: regions) { for (auto& region: regions) {
@ -555,10 +572,11 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept
} }
void sfz::Synth::aftertouch(int /* delay */, uint8_t /* aftertouch */) noexcept void sfz::Synth::aftertouch(int /* delay */, uint8_t /* aftertouch */) noexcept
{ {
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
} }
void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept
{ {
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
} }
int sfz::Synth::getNumRegions() const noexcept int sfz::Synth::getNumRegions() const noexcept
@ -806,3 +824,13 @@ bool sfz::Synth::shouldReloadFile()
{ {
return (checkModificationTime() > modificationTime); return (checkModificationTime() > modificationTime);
} }
void sfz::Synth::enableLogging() noexcept
{
resources.logger.enableLogging();
}
void sfz::Synth::disableLogging() noexcept
{
resources.logger.disableLogging();
}

View file

@ -340,6 +340,17 @@ public:
* @return false * @return false
*/ */
bool shouldReloadFile(); bool shouldReloadFile();
/**
* @brief Enable logging of timings to sidecar CSV files. This can produce
* many outputs so use with caution.
*
*/
void enableLogging() noexcept;
/**
* @brief Disable logging;
*
*/
void disableLogging() noexcept;
protected: protected:
/** /**
* @brief The parser callback; this is called by the parent object each time * @brief The parser callback; this is called by the parent object each time
@ -456,6 +467,8 @@ private:
int noteOffset { 0 }; int noteOffset { 0 };
int octaveOffset { 0 }; int octaveOffset { 0 };
Duration dispatchDuration { 0 };
fs::file_time_type modificationTime { }; fs::file_time_type modificationTime { };
LEAK_DETECTOR(Synth); LEAK_DETECTOR(Synth);

View file

@ -250,10 +250,13 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
auto delayed_buffer = buffer.subspan(delay); auto delayed_buffer = buffer.subspan(delay);
initialDelay -= static_cast<int>(delay); initialDelay -= static_cast<int>(delay);
{ // Fill buffer with raw data
ScopedTiming logger { dataDuration };
if (region->isGenerator()) if (region->isGenerator())
fillWithGenerator(delayed_buffer); fillWithGenerator(delayed_buffer);
else else
fillWithData(delayed_buffer); fillWithData(delayed_buffer);
}
if (region->isStereo) if (region->isStereo)
processStereo(buffer); processStereo(buffer);
@ -275,6 +278,9 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
auto modulationSpan = tempSpan1.first(numSamples); auto modulationSpan = tempSpan1.first(numSamples);
{ // Amplitude processing
ScopedTiming logger { amplitudeDuration };
// Amplitude envelope // Amplitude envelope
amplitudeEnvelope.getBlock(modulationSpan); amplitudeEnvelope.getBlock(modulationSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
@ -290,8 +296,11 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
// AmpEG envelope // AmpEG envelope
egEnvelope.getBlock(modulationSpan); egEnvelope.getBlock(modulationSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
}
{ // Filtering and EQ
ScopedTiming logger { filterDuration };
// Filtering and EQ
const float* inputChannel[1] { leftBuffer.data() }; const float* inputChannel[1] { leftBuffer.data() };
float* outputChannel[1] { leftBuffer.data() }; float* outputChannel[1] { leftBuffer.data() };
for (auto& filter: filters) { for (auto& filter: filters) {
@ -301,6 +310,10 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
for (auto& eq: equalizers) { for (auto& eq: equalizers) {
eq->process(inputChannel, outputChannel, numSamples); eq->process(inputChannel, outputChannel, numSamples);
} }
}
{ // Panning and stereo processing
ScopedTiming logger { panningDuration };
// Prepare for stereo output // Prepare for stereo output
copy<float>(leftBuffer, rightBuffer); copy<float>(leftBuffer, rightBuffer);
@ -309,6 +322,7 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
panEnvelope.getBlock(modulationSpan); panEnvelope.getBlock(modulationSpan);
pan<float>(modulationSpan, leftBuffer, rightBuffer); pan<float>(modulationSpan, leftBuffer, rightBuffer);
} }
}
void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
{ {
@ -317,6 +331,9 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
auto leftBuffer = buffer.getSpan(0); auto leftBuffer = buffer.getSpan(0);
auto rightBuffer = buffer.getSpan(1); auto rightBuffer = buffer.getSpan(1);
{ // Amplitude processing
ScopedTiming logger { amplitudeDuration };
// Amplitude envelope // Amplitude envelope
amplitudeEnvelope.getBlock(modulationSpan); amplitudeEnvelope.getBlock(modulationSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(modulationSpan);
@ -332,14 +349,21 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
// AmpEG envelope // AmpEG envelope
egEnvelope.getBlock(modulationSpan); egEnvelope.getBlock(modulationSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(modulationSpan);
}
{ // Panning and stereo processing
ScopedTiming logger { panningDuration };
// Apply the width/position process // Apply the width/position process
widthEnvelope.getBlock(modulationSpan); widthEnvelope.getBlock(modulationSpan);
width<float>(modulationSpan, leftBuffer, rightBuffer); width<float>(modulationSpan, leftBuffer, rightBuffer);
positionEnvelope.getBlock(modulationSpan); positionEnvelope.getBlock(modulationSpan);
pan<float>(modulationSpan, leftBuffer, rightBuffer); pan<float>(modulationSpan, leftBuffer, rightBuffer);
}
{ // Filtering and EQ
ScopedTiming logger { filterDuration };
// Filtering and EQ
const float* inputChannels[2] { leftBuffer.data(), rightBuffer.data() }; const float* inputChannels[2] { leftBuffer.data(), rightBuffer.data() };
float* outputChannels[2] { leftBuffer.data(), rightBuffer.data() }; float* outputChannels[2] { leftBuffer.data(), rightBuffer.data() };
@ -351,6 +375,7 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
eq->process(inputChannels, outputChannels, numSamples); eq->process(inputChannels, outputChannels, numSamples);
} }
} }
}
void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
{ {

View file

@ -209,6 +209,12 @@ public:
* @param numFilters * @param numFilters
*/ */
void setMaxEQsPerVoice(size_t numEQs); void setMaxEQsPerVoice(size_t numEQs);
Duration getLastDataDuration() const noexcept { return dataDuration; }
Duration getLastAmplitudeDuration() const noexcept { return amplitudeDuration; }
Duration getLastFilterDuration() const noexcept { return filterDuration; }
Duration getLastPanningDuration() const noexcept { return panningDuration; }
private: private:
/** /**
* @brief Fill a span with data from a file source. This is the first step * @brief Fill a span with data from a file source. This is the first step
@ -301,6 +307,11 @@ private:
MultiplicativeEnvelope<float> volumeEnvelope; MultiplicativeEnvelope<float> volumeEnvelope;
float bendStepFactor { centsFactor(1) }; float bendStepFactor { centsFactor(1) };
Duration dataDuration;
Duration amplitudeDuration;
Duration panningDuration;
Duration filterDuration;
std::normal_distribution<float> noiseDist { 0, config::noiseVariance }; std::normal_distribution<float> noiseDist { 0, config::noiseVariance };
HistoricalBuffer<float> powerHistory { config::powerHistoryLength }; HistoricalBuffer<float> powerHistory { config::powerHistoryLength };

View file

@ -184,3 +184,13 @@ bool sfz::Sfizz::shouldReloadFile()
{ {
return synth->shouldReloadFile(); return synth->shouldReloadFile();
} }
void sfz::Sfizz::enableLogging() noexcept
{
synth->enableLogging();
}
void sfz::Sfizz::disableLogging() noexcept
{
synth->disableLogging();
}

View file

@ -232,6 +232,17 @@ bool sfizz_should_reload_file(sfizz_synth_t* synth)
return self->shouldReloadFile(); return self->shouldReloadFile();
} }
void sfizz_enable_logging(sfizz_synth_t* synth)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);
return self->enableLogging();
}
void sfizz_disable_logging(sfizz_synth_t* synth)
{
auto self = reinterpret_cast<sfz::Synth*>(synth);
return self->disableLogging();
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif