diff --git a/include/engine/Engine.hpp b/include/engine/Engine.hpp index c6051836..f07762a0 100644 --- a/include/engine/Engine.hpp +++ b/include/engine/Engine.hpp @@ -47,6 +47,10 @@ struct Engine { /** Returns the number of audio samples since the Engine's first sample. */ int64_t getFrame(); + /** Returns the estimated timestamp corresponding to the current frame, based on the timestamp of when step() was last called. + Calculated by `stepTime + framesSinceStep / sampleRate`. + */ + int64_t getFrameTime(); /** Returns the frame when step() was last called. */ int64_t getStepFrame(); @@ -56,6 +60,10 @@ struct Engine { /** Returns the total number of frames in the current step() call. */ int getStepFrames(); + /** Returns the total time that step() is advancing, in nanoseconds. + Calculated by `stepFrames / sampleRate`. + */ + int64_t getStepDuration(); // Modules size_t getNumModules(); diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index e8a38f7a..e15eeb8c 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -210,7 +210,15 @@ struct Engine::Internal { int smoothParamId = 0; float smoothValue = 0.f; + /** Engine mutex + Writers lock when mutating the engine's Modules, Cables, etc. + Readers lock when using the engine's Modules, Cables, etc. + */ SharedMutex mutex; + /** Step mutex + step() locks to guarantee its exclusivity. + */ + std::mutex stepMutex; int threadCount = 0; std::vector workers; @@ -380,27 +388,29 @@ static void Cable_step(Cable* that) { } -static void Engine_stepModules(Engine* that) { +/** Steps a single frame +*/ +static void Engine_stepFrame(Engine* that) { Engine::Internal* internal = that->internal; // Param smoothing Module* smoothModule = internal->smoothModule; - int smoothParamId = internal->smoothParamId; - float smoothValue = internal->smoothValue; if (smoothModule) { - Param* param = &smoothModule->params[smoothParamId]; - float value = param->value; - // Decay rate is 1 graphics frame + int smoothParamId = internal->smoothParamId; + float smoothValue = internal->smoothValue; + Param* smoothParam = &smoothModule->params[smoothParamId]; + float value = smoothParam->value; + // Use decay rate of roughly 1 graphics frame const float smoothLambda = 60.f; float newValue = value + (smoothValue - value) * smoothLambda * internal->sampleTime; if (value == newValue) { // Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats) - param->setValue(smoothValue); + smoothParam->setValue(smoothValue); internal->smoothModule = NULL; internal->smoothParamId = 0; } else { - param->value = newValue; + smoothParam->setValue(newValue); } } @@ -543,6 +553,7 @@ void Engine::clear() { void Engine::step(int frames) { + std::lock_guard stepLock(internal->stepMutex); SharedLock lock(internal->mutex); // Configure thread initMXCSR(); @@ -573,9 +584,9 @@ void Engine::step(int frames) { // Launch workers Engine_relaunchWorkers(this, settings::threadCount); - // Step modules + // Step individual frames for (int i = 0; i < frames; i++) { - Engine_stepModules(this); + Engine_stepFrame(this); } yieldWorkers(); @@ -620,6 +631,12 @@ int64_t Engine::getFrame() { } +int64_t Engine::getFrameTime() { + double timeSinceStep = (internal->frame - internal->stepFrame) * internal->sampleTime; + return internal->stepTime + int64_t(timeSinceStep * 1e9); +} + + int64_t Engine::getStepFrame() { return internal->stepFrame; } @@ -635,6 +652,12 @@ int Engine::getStepFrames() { } +int64_t Engine::getStepDuration() { + double duration = internal->stepFrames * internal->sampleTime; + return int64_t(duration * 1e9); +} + + size_t Engine::getNumModules() { return internal->modules.size(); }