Added logs for the processing time in voices

This commit is contained in:
Paul Fd 2020-02-17 00:01:15 +01:00
parent a1af7a0b67
commit cb88fb4bb7
7 changed files with 164 additions and 70 deletions

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

@ -66,7 +66,7 @@ sfz::Logger::~Logger()
<< "_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()); DBG("Logging file times to " << fileLogPath.filename());
std::ofstream loadLogFile { fileLogPath.string() }; std::ofstream loadLogFile { fileLogPath.native() };
loadLogFile << "WaitDuration,LoadDuration,FileSize,FileName" << '\n'; loadLogFile << "WaitDuration,LoadDuration,FileSize,FileName" << '\n';
for (auto& time: fileTimes) for (auto& time: fileTimes)
loadLogFile << time.waitDuration.count() << ',' loadLogFile << time.waitDuration.count() << ','
@ -82,22 +82,27 @@ sfz::Logger::~Logger()
<< "_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()); DBG("Logging callback times to " << callbackLogPath.filename());
std::ofstream callbackLogFile { callbackLogPath.string() }; std::ofstream callbackLogFile { callbackLogPath.native() };
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,14 @@ void sfz::Logger::disableLogging()
loggingEnabled = false; loggingEnabled = false;
clearFlag.clear(); clearFlag.clear();
} }
sfz::ScopedLogger::ScopedLogger(std::function<void(std::chrono::duration<double>)> callback)
: callback(std::move(callback))
{
}
sfz::ScopedLogger::~ScopedLogger()
{
callback(std::chrono::high_resolution_clock::now() - creationTime);
}

View file

@ -10,23 +10,46 @@
#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>;
struct ScopedLogger
{
using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
ScopedLogger() = delete;
ScopedLogger(std::function<void(Duration)> callback);
~ScopedLogger();
const std::function<void(Duration)> callback;
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;
}; };
@ -40,8 +63,8 @@ public:
void clear(); void clear();
void enableLogging(); void enableLogging();
void disableLogging(); void disableLogging();
void logCallbackTime(std::chrono::duration<double> duration, int numVoices, size_t numSamples); void logCallbackTime(CallbackBreakdown&& breakdown, int numVoices, size_t numSamples);
void logFileTime(std::chrono::duration<double> waitDuration, std::chrono::duration<double> loadDuration, uint32_t fileSize, absl::string_view filename); void logFileTime(Duration waitDuration, Duration loadDuration, uint32_t fileSize, absl::string_view filename);
private: private:
void moveEvents() noexcept; void moveEvents() noexcept;
bool loggingEnabled { config::loggingEnabled }; bool loggingEnabled { config::loggingEnabled };

View file

@ -411,6 +411,8 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
{ {
ScopedFTZ ftz; ScopedFTZ ftz;
const auto callbackStartTime = std::chrono::high_resolution_clock::now(); const auto callbackStartTime = std::chrono::high_resolution_clock::now();
CallbackBreakdown callbackBreakdown;
callbackBreakdown.dispatch = dispatchDuration;
buffer.fill(0.0f); buffer.fill(0.0f);
@ -430,13 +432,20 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
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.renderMethod = std::chrono::high_resolution_clock::now() - callbackStartTime;
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 +453,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);
ScopedLogger logger { [this](auto&& duration){ dispatchDuration += duration; } };
resources.midiState.noteOnEvent(noteNumber, velocity); resources.midiState.noteOnEvent(noteNumber, velocity);
AtomicGuard callbackGuard { inCallback }; AtomicGuard callbackGuard { inCallback };
@ -458,6 +468,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);
ScopedLogger logger { [this](auto&& duration){ dispatchDuration += duration; } };
resources.midiState.noteOffEvent(noteNumber, velocity); resources.midiState.noteOffEvent(noteNumber, velocity);
AtomicGuard callbackGuard { inCallback }; AtomicGuard callbackGuard { inCallback };
@ -513,6 +524,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);
ScopedLogger logger { [this](auto&& duration){ dispatchDuration += duration; } };
resources.midiState.ccEvent(ccNumber, ccValue); resources.midiState.ccEvent(ccNumber, ccValue);
AtomicGuard callbackGuard { inCallback }; AtomicGuard callbackGuard { inCallback };
@ -543,6 +556,8 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept
ASSERT(pitch <= 8192); ASSERT(pitch <= 8192);
ASSERT(pitch >= -8192); ASSERT(pitch >= -8192);
ScopedLogger logger { [this](auto&& duration){ dispatchDuration += duration; } };
resources.midiState.pitchBendEvent(pitch); resources.midiState.pitchBendEvent(pitch);
for (auto& region: regions) { for (auto& region: regions) {
@ -555,10 +570,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
{ {
ScopedLogger logger { [this](auto&& duration){ dispatchDuration += duration; } };
} }
void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept
{ {
ScopedLogger logger { [this](auto&& duration){ dispatchDuration += duration; } };
} }
int sfz::Synth::getNumRegions() const noexcept int sfz::Synth::getNumRegions() const noexcept

View file

@ -456,6 +456,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);
if (region->isGenerator()) { // Fill buffer with raw data
fillWithGenerator(delayed_buffer); ScopedLogger logger { [&](auto&& duration){ dataDuration = duration; } };
else if (region->isGenerator())
fillWithData(delayed_buffer); fillWithGenerator(delayed_buffer);
else
fillWithData(delayed_buffer);
}
if (region->isStereo) if (region->isStereo)
processStereo(buffer); processStereo(buffer);
@ -275,39 +278,50 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
auto modulationSpan = tempSpan1.first(numSamples); auto modulationSpan = tempSpan1.first(numSamples);
// Amplitude envelope { // Amplitude processing
amplitudeEnvelope.getBlock(modulationSpan); ScopedLogger logger { [&](auto&& duration){ amplitudeDuration = duration; } };
applyGain<float>(modulationSpan, leftBuffer);
// Crossfade envelope // Amplitude envelope
crossfadeEnvelope.getBlock(modulationSpan); amplitudeEnvelope.getBlock(modulationSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
// Volume envelope // Crossfade envelope
volumeEnvelope.getBlock(modulationSpan); crossfadeEnvelope.getBlock(modulationSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
// AmpEG envelope // Volume envelope
egEnvelope.getBlock(modulationSpan); volumeEnvelope.getBlock(modulationSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
// Filtering and EQ // AmpEG envelope
const float* inputChannel[1] { leftBuffer.data() }; egEnvelope.getBlock(modulationSpan);
float* outputChannel[1] { leftBuffer.data() }; applyGain<float>(modulationSpan, leftBuffer);
for (auto& filter: filters) {
filter->process(inputChannel, outputChannel, numSamples);
} }
for (auto& eq: equalizers) { { // Filtering and EQ
eq->process(inputChannel, outputChannel, numSamples); ScopedLogger logger { [&](auto&& duration){ filterDuration = duration; } };
const float* inputChannel[1] { leftBuffer.data() };
float* outputChannel[1] { leftBuffer.data() };
for (auto& filter: filters) {
filter->process(inputChannel, outputChannel, numSamples);
}
for (auto& eq: equalizers) {
eq->process(inputChannel, outputChannel, numSamples);
}
} }
// Prepare for stereo output { // Panning and stereo processing
copy<float>(leftBuffer, rightBuffer); ScopedLogger logger { [&](auto&& duration){ panningDuration = duration; } };
// Apply panning // Prepare for stereo output
panEnvelope.getBlock(modulationSpan); copy<float>(leftBuffer, rightBuffer);
pan<float>(modulationSpan, leftBuffer, rightBuffer);
// Apply panning
panEnvelope.getBlock(modulationSpan);
pan<float>(modulationSpan, leftBuffer, rightBuffer);
}
} }
void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
@ -317,38 +331,49 @@ 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 envelope { // Amplitude processing
amplitudeEnvelope.getBlock(modulationSpan); ScopedLogger logger { [&](auto&& duration){ amplitudeDuration = duration; } };
buffer.applyGain(modulationSpan);
// Crossfade envelope // Amplitude envelope
crossfadeEnvelope.getBlock(modulationSpan); amplitudeEnvelope.getBlock(modulationSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(modulationSpan);
// Volume envelope // Crossfade envelope
volumeEnvelope.getBlock(modulationSpan); crossfadeEnvelope.getBlock(modulationSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(modulationSpan);
// AmpEG envelope // Volume envelope
egEnvelope.getBlock(modulationSpan); volumeEnvelope.getBlock(modulationSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(modulationSpan);
// Apply the width/position process // AmpEG envelope
widthEnvelope.getBlock(modulationSpan); egEnvelope.getBlock(modulationSpan);
width<float>(modulationSpan, leftBuffer, rightBuffer); buffer.applyGain(modulationSpan);
positionEnvelope.getBlock(modulationSpan);
pan<float>(modulationSpan, leftBuffer, rightBuffer);
// Filtering and EQ
const float* inputChannels[2] { leftBuffer.data(), rightBuffer.data() };
float* outputChannels[2] { leftBuffer.data(), rightBuffer.data() };
for (auto& filter: filters) {
filter->process(inputChannels, outputChannels, numSamples);
} }
for (auto& eq: equalizers) { { // Panning and stereo processing
eq->process(inputChannels, outputChannels, numSamples); ScopedLogger logger { [&](auto&& duration){ panningDuration = duration; } };
// Apply the width/position process
widthEnvelope.getBlock(modulationSpan);
width<float>(modulationSpan, leftBuffer, rightBuffer);
positionEnvelope.getBlock(modulationSpan);
pan<float>(modulationSpan, leftBuffer, rightBuffer);
}
{ // Filtering and EQ
ScopedLogger logger { [&](auto&& duration){ filterDuration = duration; } };
const float* inputChannels[2] { leftBuffer.data(), rightBuffer.data() };
float* outputChannels[2] { leftBuffer.data(), rightBuffer.data() };
for (auto& filter: filters) {
filter->process(inputChannels, outputChannels, numSamples);
}
for (auto& eq: equalizers) {
eq->process(inputChannels, outputChannels, numSamples);
}
} }
} }

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