Browse Source

Rename "step" terminology of Engine to "block". Tweak ModuleWidget meter appearance.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
f0753792b8
10 changed files with 104 additions and 71 deletions
  1. +15
    -12
      include/engine/Engine.hpp
  2. +1
    -1
      include/engine/Module.hpp
  3. +24
    -14
      src/app/ModuleWidget.cpp
  4. +1
    -1
      src/core/AudioInterface.cpp
  5. +1
    -1
      src/core/MIDI_CC.cpp
  6. +5
    -1
      src/core/MIDI_CV.cpp
  7. +1
    -1
      src/core/MIDI_Gate.cpp
  8. +1
    -1
      src/core/MIDI_Map.cpp
  9. +37
    -29
      src/engine/Engine.cpp
  10. +18
    -10
      src/engine/Module.cpp

+ 15
- 12
include/engine/Engine.hpp View File

@@ -30,7 +30,7 @@ struct Engine {
/** Advances the engine by `frames` frames. /** Advances the engine by `frames` frames.
Only call this method from the primary module. Only call this method from the primary module.
*/ */
void step(int frames);
void stepBlock(int frames);
void setPrimaryModule(Module* module); void setPrimaryModule(Module* module);
Module* getPrimaryModule(); Module* getPrimaryModule();


@@ -41,29 +41,32 @@ struct Engine {
*/ */
float getSampleTime(); float getSampleTime();
/** Causes worker threads to block on a mutex instead of spinlock. /** Causes worker threads to block on a mutex instead of spinlock.
Call this in your Module::step() method to hint that the operation will take more than ~0.1 ms.
Call this in your Module::stepBlock() method to hint that the operation will take more than ~0.1 ms.
*/ */
void yieldWorkers(); void yieldWorkers();
/** Returns the number of audio samples since the Engine's first sample.
/** Returns the number of stepBlock() calls since the Engine was created.
*/
int64_t getBlock();
/** Returns the number of audio samples since the Engine was created.
*/ */
int64_t getFrame(); int64_t getFrame();
/** Returns the estimated timestamp corresponding to the current frame, based on the timestamp of when step() was last called.
/** Returns the estimated time corresponding to the current frame, based on the time of when stepBlock() was last called.
Calculated by `stepTime + framesSinceStep / sampleRate`. Calculated by `stepTime + framesSinceStep / sampleRate`.
*/ */
double getFrameTime(); double getFrameTime();
/** Returns the frame when step() was last called.
/** Returns the frame when stepBlock() was last called.
*/ */
int64_t getStepFrame();
/** Returns the timestamp in seconds when step() was last called.
int64_t getBlockFrame();
/** Returns the time in seconds when stepBlock() was last called.
*/ */
double getStepTime();
/** Returns the total number of frames in the current step() call.
double getBlockTime();
/** Returns the total number of frames in the current stepBlock() call.
*/ */
int getStepFrames();
/** Returns the total time that step() is advancing, in seconds.
int getBlockFrames();
/** Returns the total time that stepBlock() is advancing, in seconds.
Calculated by `stepFrames / sampleRate`. Calculated by `stepFrames / sampleRate`.
*/ */
double getStepDuration();
double getBlockDuration();


// Modules // Modules
size_t getNumModules(); size_t getNumModules();


+ 1
- 1
include/engine/Module.hpp View File

@@ -348,7 +348,7 @@ struct Module {
PRIVATE const float* meterBuffer(); PRIVATE const float* meterBuffer();
PRIVATE int meterLength(); PRIVATE int meterLength();
PRIVATE int meterIndex(); PRIVATE int meterIndex();
PRIVATE void step(const ProcessArgs& args);
PRIVATE void doProcess(const ProcessArgs& args);
}; };






+ 24
- 14
src/app/ModuleWidget.cpp View File

@@ -401,40 +401,50 @@ void ModuleWidget::draw(const DrawArgs& args) {
int meterLength = module->meterLength(); int meterLength = module->meterLength();
int meterIndex = module->meterIndex(); int meterIndex = module->meterIndex();


float meterMax = 0.f;
float meterAvg = 0.f; float meterAvg = 0.f;
for (int i = 0; i < meterLength; i++) { for (int i = 0; i < meterLength; i++) {
meterAvg += meterBuffer[i];
float m = meterBuffer[i];
meterAvg += m;
meterMax = std::max(meterMax, m);
} }
meterAvg /= meterLength; meterAvg /= meterLength;
float percentMax = meterMax * sampleRate;
float mult = (percentMax <= 0.1f) ? 10.f : 1.f;


// Text background
nvgBeginPath(args.vg);
nvgRect(args.vg, 0, box.size.y - 20, box.size.x, 20);
nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
nvgFill(args.vg);

// Text
float percent = meterAvg * sampleRate * 100.f;
float microseconds = meterAvg * 1e6f;
std::string meterText = string::f("%.1f%% %.2f ÎĽs", percent, microseconds);
bndLabel(args.vg, 0.0, box.size.y - 20.0, INFINITY, INFINITY, -1, meterText.c_str());
// // Text background
// nvgBeginPath(args.vg);
// nvgRect(args.vg, 0.0, box.size.y - infoHeight, box.size.x, infoHeight);
// nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
// nvgFill(args.vg);


// Draw time plot // Draw time plot
nvgBeginPath(args.vg); nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x, box.size.y); nvgMoveTo(args.vg, box.size.x, box.size.y);
for (int i = 0; i < meterLength; i++) { for (int i = 0; i < meterLength; i++) {
int index = (meterIndex - i + meterLength) % meterLength; int index = (meterIndex - i + meterLength) % meterLength;
float percent = math::clamp(meterBuffer[index] * mult * sampleRate, 0.f, 1.f);
math::Vec p; math::Vec p;
p.x = (1.f - (float) i / (meterLength - 1)) * box.size.x; p.x = (1.f - (float) i / (meterLength - 1)) * box.size.x;
p.y = (1.f - meterBuffer[index] * sampleRate * 1.f) * (box.size.y - 20);
p.y = (1.f - percent) * (box.size.y);
nvgLineTo(args.vg, p.x, p.y); nvgLineTo(args.vg, p.x, p.y);
} }
NVGcolor color = nvgRGBAf(0.6, 0, 0, 0.6);
NVGcolor color;
if (mult == 1.f)
color = nvgRGBAf(0.5, 0, 0, 0.85);
else if (mult == 10.f)
color = nvgRGBAf(0.85, 0, 0, 0.85);
nvgLineTo(args.vg, 0.0, box.size.y); nvgLineTo(args.vg, 0.0, box.size.y);
nvgClosePath(args.vg); nvgClosePath(args.vg);
nvgFillColor(args.vg, color); nvgFillColor(args.vg, color);
nvgFill(args.vg); nvgFill(args.vg);


// Text
float percent = meterAvg * sampleRate * 100.f;
float microseconds = meterAvg * 1e6f;
std::string meterText = string::f("%.0fx\n%.2f ÎĽs\n%.1f%%", mult, microseconds, percent);
bndLabel(args.vg, 0.0, box.size.y - 60, INFINITY, INFINITY, -1, meterText.c_str());

// Draw border // Draw border
nvgStrokeColor(args.vg, color); nvgStrokeColor(args.vg, color);
nvgBeginPath(args.vg); nvgBeginPath(args.vg);


+ 1
- 1
src/core/AudioInterface.cpp View File

@@ -245,7 +245,7 @@ struct AudioInterface : Module, audio::Port {
bool isPrimary = (APP->engine->getPrimaryModule() == this); bool isPrimary = (APP->engine->getPrimaryModule() == this);
// Step engine // Step engine
if (isPrimary && requestedEngineFrames > 0) { if (isPrimary && requestedEngineFrames > 0) {
APP->engine->step(requestedEngineFrames);
APP->engine->stepBlock(requestedEngineFrames);
} }
} }




+ 1
- 1
src/core/MIDI_CC.cpp View File

@@ -73,7 +73,7 @@ struct MIDI_CC : Module {
while (!midiInput.queue.empty()) { while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front(); midi::Message& msg = midiInput.queue.front();
// Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block. // Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block.
if (msg.timestamp + APP->engine->getStepDuration() > APP->engine->getFrameTime())
if (msg.timestamp + APP->engine->getBlockDuration() > APP->engine->getFrameTime())
break; break;
processMessage(msg); processMessage(msg);
midiInput.queue.pop(); midiInput.queue.pop();


+ 5
- 1
src/core/MIDI_CV.cpp View File

@@ -124,7 +124,7 @@ struct MIDI_CV : Module {
while (!midiInput.queue.empty()) { while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front(); midi::Message& msg = midiInput.queue.front();
// Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block. // Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block.
if (msg.timestamp + APP->engine->getStepDuration() > APP->engine->getFrameTime())
if (msg.timestamp + APP->engine->getBlockDuration() > APP->engine->getFrameTime())
break; break;
processMessage(msg); processMessage(msg);
midiInput.queue.pop(); midiInput.queue.pop();
@@ -639,6 +639,10 @@ struct MIDI_CVWidget : ModuleWidget {
panicItem->text = "Panic"; panicItem->text = "Panic";
panicItem->module = module; panicItem->module = module;
menu->addChild(panicItem); menu->addChild(panicItem);

// Example of using appendMidiMenu()
// menu->addChild(new MenuSeparator);
// appendMidiMenu(menu, &module->midiInput);
} }
}; };




+ 1
- 1
src/core/MIDI_Gate.cpp View File

@@ -67,7 +67,7 @@ struct MIDI_Gate : Module {
while (!midiInput.queue.empty()) { while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front(); midi::Message& msg = midiInput.queue.front();
// Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block. // Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block.
if (msg.timestamp + APP->engine->getStepDuration() > APP->engine->getFrameTime())
if (msg.timestamp + APP->engine->getBlockDuration() > APP->engine->getFrameTime())
break; break;
processMessage(msg); processMessage(msg);
midiInput.queue.pop(); midiInput.queue.pop();


+ 1
- 1
src/core/MIDI_Map.cpp View File

@@ -85,7 +85,7 @@ struct MIDI_Map : Module {
while (!midiInput.queue.empty()) { while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front(); midi::Message& msg = midiInput.queue.front();
// Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block. // Don't process MIDI message until its timestamp corresponds with the audio frame time when played back in the next block.
if (msg.timestamp + APP->engine->getStepDuration() > APP->engine->getFrameTime())
if (msg.timestamp + APP->engine->getBlockDuration() > APP->engine->getFrameTime())
break; break;
processMessage(msg); processMessage(msg);
midiInput.queue.pop(); midiInput.queue.pop();


+ 37
- 29
src/engine/Engine.cpp View File

@@ -196,10 +196,11 @@ struct Engine::Internal {


float sampleRate = 0.f; float sampleRate = 0.f;
float sampleTime = 0.f; float sampleTime = 0.f;
int64_t block = 0;
int64_t frame = 0; int64_t frame = 0;
int64_t stepFrame = 0;
double stepTime = 0.0;
int stepFrames = 0;
int64_t blockFrame = 0;
double blockTime = 0.0;
int blockFrames = 0;
Module* primaryModule = NULL; Module* primaryModule = NULL;


// Parameter smoothing // Parameter smoothing
@@ -207,15 +208,15 @@ struct Engine::Internal {
int smoothParamId = 0; int smoothParamId = 0;
float smoothValue = 0.f; 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.
/** Mutex that guards the Engine state, such as settings, Modules, and Cables.
Writers lock when mutating the engine's state.
Readers lock when using the engine's state.
*/ */
SharedMutex mutex; SharedMutex mutex;
/** Step mutex
step() locks to guarantee its exclusivity.
/** Mutex that guards the block.
stepBlock() locks to guarantee its exclusivity.
*/ */
std::mutex stepMutex;
std::mutex blockMutex;


int threadCount = 0; int threadCount = 0;
std::vector<EngineWorker> workers; std::vector<EngineWorker> workers;
@@ -310,7 +311,7 @@ static void Engine_stepWorker(Engine* that, int threadId) {
break; break;


Module* module = internal->modules[i]; Module* module = internal->modules[i];
module->step(processArgs);
module->doProcess(processArgs);
} }
} }


@@ -497,16 +498,16 @@ void Engine::clear() {
} }




void Engine::step(int frames) {
std::lock_guard<std::mutex> stepLock(internal->stepMutex);
void Engine::stepBlock(int frames) {
std::lock_guard<std::mutex> stepLock(internal->blockMutex);
SharedLock lock(internal->mutex); SharedLock lock(internal->mutex);
// Configure thread // Configure thread
initMXCSR(); initMXCSR();
random::init(); random::init();


internal->stepFrame = internal->frame;
internal->stepTime = system::getTime();
internal->stepFrames = frames;
internal->blockFrame = internal->frame;
internal->blockTime = system::getTime();
internal->blockFrames = frames;


// Set sample rate // Set sample rate
if (internal->sampleRate != settings::sampleRate) { if (internal->sampleRate != settings::sampleRate) {
@@ -538,9 +539,11 @@ void Engine::step(int frames) {
yieldWorkers(); yieldWorkers();


double endTime = system::getTime(); double endTime = system::getTime();
float duration = endTime - internal->stepTime;
float stepDuration = internal->stepFrames * internal->sampleTime;
// DEBUG("%d %f / %f = %f%%", internal->stepFrames, duration, stepDuration, duration / stepDuration * 100.f);
float duration = endTime - internal->blockTime;
float blockDuration = internal->blockFrames * internal->sampleTime;
// DEBUG("%d %f / %f = %f%%", internal->blockFrames, duration, blockDuration, duration / blockDuration * 100.f);

internal->block++;
} }




@@ -577,34 +580,39 @@ void Engine::yieldWorkers() {
} }




int64_t Engine::getBlock() {
return internal->block;
}


int64_t Engine::getFrame() { int64_t Engine::getFrame() {
return internal->frame; return internal->frame;
} }




double Engine::getFrameTime() { double Engine::getFrameTime() {
double timeSinceStep = (internal->frame - internal->stepFrame) * internal->sampleTime;
return internal->stepTime + timeSinceStep;
double timeSinceBlock = (internal->frame - internal->blockFrame) * internal->sampleTime;
return internal->blockTime + timeSinceBlock;
} }




int64_t Engine::getStepFrame() {
return internal->stepFrame;
int64_t Engine::getBlockFrame() {
return internal->blockFrame;
} }




double Engine::getStepTime() {
return internal->stepTime;
double Engine::getBlockTime() {
return internal->blockTime;
} }




int Engine::getStepFrames() {
return internal->stepFrames;
int Engine::getBlockFrames() {
return internal->blockFrames;
} }




double Engine::getStepDuration() {
return internal->stepFrames * internal->sampleTime;
double Engine::getBlockDuration() {
return internal->blockFrames * internal->sampleTime;
} }




@@ -1031,7 +1039,7 @@ json_t* Engine::toJson() {


void Engine::fromJson(json_t* rootJ) { void Engine::fromJson(json_t* rootJ) {
// We can't lock here because addModule() and addCable() are called inside. // We can't lock here because addModule() and addCable() are called inside.
// Also, AudioInterface::fromJson() can open the audio device, which can call Engine::step() before this method exits.
// Also, AudioInterface::fromJson() can open the audio device, which can call Engine::stepBlock() before this method exits.
// ExclusiveSharedLock lock(internal->mutex); // ExclusiveSharedLock lock(internal->mutex);
clear(); clear();
// modules // modules


+ 18
- 10
src/engine/Module.cpp View File

@@ -1,8 +1,10 @@
#include <engine/Module.hpp> #include <engine/Module.hpp>
#include <engine/Engine.hpp>
#include <plugin.hpp> #include <plugin.hpp>
#include <system.hpp> #include <system.hpp>
#include <settings.hpp> #include <settings.hpp>
#include <asset.hpp> #include <asset.hpp>
#include <context.hpp>




namespace rack { namespace rack {
@@ -11,19 +13,19 @@ namespace engine {




// Arbitrary prime number so it doesn't over- or under-estimate time of buffered processors. // Arbitrary prime number so it doesn't over- or under-estimate time of buffered processors.
static const int meterDivider = 23;
static const int samplesCount = 64;
static const int meterDivider = 1;
static const int meterBufferLength = 128; static const int meterBufferLength = 128;





struct Module::Internal { struct Module::Internal {
bool bypass = false; bool bypass = false;


float meterTimeTotal = 0.f;
int64_t meterLastBlock = 0;
int meterSamples = 0; int meterSamples = 0;
int meterIndex = 0;
float meterTimeTotal = 0.f;

float meterBuffer[meterBufferLength] = {}; float meterBuffer[meterBufferLength] = {};
int meterIndex = 0;
}; };




@@ -326,7 +328,7 @@ static void Port_step(Port* that, float deltaTime) {
} }




void Module::step(const ProcessArgs& args) {
void Module::doProcess(const ProcessArgs& args) {
// This global setting can change while the function is running, so use a local variable. // This global setting can change while the function is running, so use a local variable.
bool meterEnabled = settings::cpuMeter && (args.frame % meterDivider == 0); bool meterEnabled = settings::cpuMeter && (args.frame % meterDivider == 0);


@@ -347,15 +349,21 @@ void Module::step(const ProcessArgs& args) {
double endTime = system::getTime(); double endTime = system::getTime();
float duration = endTime - startTime; float duration = endTime - startTime;


internal->meterTimeTotal += duration;
if (++internal->meterSamples >= samplesCount) {
int64_t block = APP->engine->getBlock();
if (block > internal->meterLastBlock) {
// Push time to buffer // Push time to buffer
internal->meterBuffer[internal->meterIndex++] = internal->meterTimeTotal / samplesCount;
internal->meterIndex %= meterBufferLength;
if (internal->meterSamples > 0) {
internal->meterBuffer[internal->meterIndex++] = internal->meterTimeTotal / internal->meterSamples;
internal->meterIndex %= meterBufferLength;
}
// Reset total // Reset total
internal->meterSamples = 0; internal->meterSamples = 0;
internal->meterTimeTotal = 0.f; internal->meterTimeTotal = 0.f;
} }

internal->meterLastBlock = block;
internal->meterSamples++;
internal->meterTimeTotal += duration;
} }


// Iterate ports to step plug lights // Iterate ports to step plug lights


Loading…
Cancel
Save