From 6d86a8280cacc1fdcf351dade396ac805a4d0670 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 25 Jan 2019 10:38:48 -0500 Subject: [PATCH] Add convenience methods to Port. Draw blue plug lights for polyphonic ports. --- include/app/common.hpp | 8 +- include/engine/Input.hpp | 17 ---- include/engine/Light.hpp | 12 ++- include/engine/Module.hpp | 3 +- include/engine/Output.hpp | 13 --- include/engine/Param.hpp | 4 +- include/engine/Port.hpp | 50 +++++++--- include/math.hpp | 3 + include/rack.hpp | 5 +- src/Core/AudioInterface.cpp | 189 ++++++++++++++++++------------------ src/Core/CV_MIDI.cpp | 16 +-- src/Core/MIDI_CC.cpp | 2 +- src/app/CableWidget.cpp | 10 +- src/app/ModuleWidget.cpp | 7 +- src/app/ParamQuantity.cpp | 8 +- src/app/PortWidget.cpp | 6 +- src/app/Scene.cpp | 12 ++- src/app/common.cpp | 12 --- src/engine/Cable.cpp | 4 +- src/engine/Engine.cpp | 40 ++++++-- src/engine/Port.cpp | 1 + src/main.cpp | 4 +- src/patch.cpp | 10 +- src/plugin.cpp | 12 ++- 24 files changed, 234 insertions(+), 214 deletions(-) delete mode 100644 include/engine/Input.hpp delete mode 100644 include/engine/Output.hpp delete mode 100644 src/app/common.cpp diff --git a/include/app/common.hpp b/include/app/common.hpp index fbcc743b..2f880d3a 100644 --- a/include/app/common.hpp +++ b/include/app/common.hpp @@ -7,9 +7,9 @@ namespace rack { -extern const std::string APP_NAME; -extern const std::string APP_VERSION; -extern const std::string API_HOST; +static const char APP_NAME[] = "VCV Rack"; +static const char APP_VERSION[] = TOSTRING(VERSION); +static const char API_HOST[] = "https://api.vcvrack.com"; static const float APP_SVG_DPI = 75.0; static const float MM_PER_IN = 25.4; @@ -39,7 +39,5 @@ static const float RACK_GRID_WIDTH = 15; static const float RACK_GRID_HEIGHT = 380; static const math::Vec RACK_GRID_SIZE = math::Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); -static const std::string PRESET_FILTERS = "VCV Rack module preset (.vcvm):vcvm"; - } // namespace rack diff --git a/include/engine/Input.hpp b/include/engine/Input.hpp deleted file mode 100644 index 4c6e75b3..00000000 --- a/include/engine/Input.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "common.hpp" -#include "engine/Port.hpp" - - -namespace rack { - - -struct Input : Port { - /** Returns the value if a cable is plugged in, otherwise returns the given default value */ - float normalize(float normalVoltage, int channel = 0) { - return isActive() ? getVoltage(channel) : normalVoltage; - } -}; - - -} // namespace rack diff --git a/include/engine/Light.hpp b/include/engine/Light.hpp index 177f1ddd..31a4bf4a 100644 --- a/include/engine/Light.hpp +++ b/include/engine/Light.hpp @@ -6,17 +6,19 @@ namespace rack { struct Light { - /** The mean-square of the brightness */ + /** The mean-square of the brightness + Unstable API. Use set/getBrightness(). + */ float value = 0.f; - float getBrightness() { - return std::sqrt(value); - } - void setBrightness(float brightness) { value = (brightness > 0.f) ? std::pow(brightness, 2) : 0.f; } + float getBrightness() { + return std::sqrt(value); + } + /** Emulates slow fall (but immediate rise) of LED brightness. `frames` rescales the timestep. For example, if your module calls this method every 16 frames, use 16.f. */ diff --git a/include/engine/Module.hpp b/include/engine/Module.hpp index 4f639b9b..9db6a0ad 100644 --- a/include/engine/Module.hpp +++ b/include/engine/Module.hpp @@ -2,8 +2,7 @@ #include "common.hpp" #include "string.hpp" #include "engine/Param.hpp" -#include "engine/Input.hpp" -#include "engine/Output.hpp" +#include "engine/Port.hpp" #include "engine/Light.hpp" #include #include diff --git a/include/engine/Output.hpp b/include/engine/Output.hpp deleted file mode 100644 index db938435..00000000 --- a/include/engine/Output.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "common.hpp" -#include "engine/Port.hpp" - - -namespace rack { - - -struct Output : Port { -}; - - -} // namespace rack diff --git a/include/engine/Param.hpp b/include/engine/Param.hpp index 99692e50..3158490a 100644 --- a/include/engine/Param.hpp +++ b/include/engine/Param.hpp @@ -1,5 +1,6 @@ #pragma once #include "common.hpp" +#include "math.hpp" #include @@ -16,6 +17,7 @@ struct ParamQuantityFactory { struct Param { + /** Unstable API. Use set/getValue() instead. */ float value = 0.f; float minValue = 0.f; @@ -72,7 +74,7 @@ struct Param { } void setValue(float value) { - this->value = value; + this->value = math::clamp(value, minValue, maxValue); } bool isBounded(); diff --git a/include/engine/Port.hpp b/include/engine/Port.hpp index 7df47de1..740b2af3 100644 --- a/include/engine/Port.hpp +++ b/include/engine/Port.hpp @@ -12,35 +12,49 @@ static const int PORT_MAX_CHANNELS = 16; struct Port { /** Voltage of the port */ union { - float values[PORT_MAX_CHANNELS] = {}; - /** DEPRECATED. Use getVoltage() and setVoltage() instead. */ + /** Unstable API. Use set/getVoltage() instead. */ + float voltages[PORT_MAX_CHANNELS] = {}; + /** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */ float value; }; /** Number of polyphonic channels + Unstable API. Use set/getChannels() instead. May be 0 to PORT_MAX_CHANNELS. */ - union { - uint8_t channels = 1; - /** DEPRECATED. Use isActive() instead. */ - bool active; - }; + uint8_t channels = 1; + /** Unstable API. Use isConnected() instead. */ + bool active; /** For rendering plug lights on cables Green for positive, red for negative, and blue for polyphonic */ Light plugLights[3]; + void setVoltage(float voltage, int channel = 0) { + voltages[channel] = voltage; + } + float getVoltage(int channel = 0) { - return values[channel]; + return voltages[channel]; } - void setVoltage(float voltage, int channel = 0) { - values[channel] = voltage; + /** Returns the voltage if a cable is plugged in, otherwise returns the given normal voltage */ + float getNormalVoltage(float normalVoltage, int channel = 0) { + return isConnected() ? getVoltage(channel) : normalVoltage; + } + + /** Returns the voltage if there are enough channels, otherwise returns the first voltage (channel 0) */ + float getPolyVoltage(int channel) { + return (channel < channels) ? getVoltage(channel) : getVoltage(0); + } + + float getNormalPolyVoltage(float normalVoltage, int channel) { + return isConnected() ? getPolyVoltage(channel) : normalVoltage; } void setChannels(int channels) { - // Set higher channel values to 0 + // Set higher channel voltages to 0 for (int c = channels; c < this->channels; c++) { - values[c] = 0.f; + voltages[c] = 0.f; } this->channels = channels; } @@ -49,12 +63,20 @@ struct Port { return channels; } - bool isActive() { - return channels; + bool isConnected() { + return active; } void step(); + + DEPRECATED float normalize(float normalVoltage) { + return getNormalVoltage(normalVoltage); + } }; +struct Output : Port {}; +struct Input : Port {}; + + } // namespace rack diff --git a/include/math.hpp b/include/math.hpp index dd6cc96f..4ce69954 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -199,6 +199,9 @@ struct Vec { float norm() const { return std::hypotf(x, y); } + float square() const { + return x * x + y * y; + } /** Rotates counterclockwise in radians */ Vec rotate(float angle) { float sin = std::sin(angle); diff --git a/include/rack.hpp b/include/rack.hpp index f381c566..585880ca 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -67,10 +67,9 @@ #include "app/CableWidget.hpp" #include "engine/Engine.hpp" -#include "engine/Input.hpp" -#include "engine/Light.hpp" +#include "engine/Param.hpp" +#include "engine/Port.hpp" #include "engine/Module.hpp" -#include "engine/Output.hpp" #include "engine/Param.hpp" #include "engine/Cable.hpp" diff --git a/src/Core/AudioInterface.cpp b/src/Core/AudioInterface.cpp index 181dba57..aba0c073 100644 --- a/src/Core/AudioInterface.cpp +++ b/src/Core/AudioInterface.cpp @@ -122,7 +122,99 @@ struct AudioInterface : Module { onSampleRateChange(); } - void step() override; + void step() override { + // Update SRC states + int sampleRate = (int) app()->engine->getSampleRate(); + inputSrc.setRates(audioIO.sampleRate, sampleRate); + outputSrc.setRates(sampleRate, audioIO.sampleRate); + + inputSrc.setChannels(audioIO.numInputs); + outputSrc.setChannels(audioIO.numOutputs); + + // Inputs: audio engine -> rack engine + if (audioIO.active && audioIO.numInputs > 0) { + // Wait until inputs are present + // Give up after a timeout in case the audio device is being unresponsive. + std::unique_lock lock(audioIO.engineMutex); + auto cond = [&] { + return (!audioIO.inputBuffer.empty()); + }; + auto timeout = std::chrono::milliseconds(200); + if (audioIO.engineCv.wait_for(lock, timeout, cond)) { + // Convert inputs + int inLen = audioIO.inputBuffer.size(); + int outLen = inputBuffer.capacity(); + inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen); + audioIO.inputBuffer.startIncr(inLen); + inputBuffer.endIncr(outLen); + } + else { + // Give up on pulling input + audioIO.active = false; + // DEBUG("Audio Interface underflow"); + } + } + + // Take input from buffer + dsp::Frame inputFrame; + if (!inputBuffer.empty()) { + inputFrame = inputBuffer.shift(); + } + else { + std::memset(&inputFrame, 0, sizeof(inputFrame)); + } + for (int i = 0; i < audioIO.numInputs; i++) { + outputs[AUDIO_OUTPUT + i].setVoltage(10.f * inputFrame.samples[i]); + } + for (int i = audioIO.numInputs; i < AUDIO_INPUTS; i++) { + outputs[AUDIO_OUTPUT + i].setVoltage(0.f); + } + + // Outputs: rack engine -> audio engine + if (audioIO.active && audioIO.numOutputs > 0) { + // Get and push output SRC frame + if (!outputBuffer.full()) { + dsp::Frame outputFrame; + for (int i = 0; i < AUDIO_OUTPUTS; i++) { + outputFrame.samples[i] = inputs[AUDIO_INPUT + i].getVoltage() / 10.f; + } + outputBuffer.push(outputFrame); + } + + if (outputBuffer.full()) { + // Wait until enough outputs are consumed + // Give up after a timeout in case the audio device is being unresponsive. + std::unique_lock lock(audioIO.engineMutex); + auto cond = [&] { + return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize); + }; + auto timeout = std::chrono::milliseconds(200); + if (audioIO.engineCv.wait_for(lock, timeout, cond)) { + // Push converted output + int inLen = outputBuffer.size(); + int outLen = audioIO.outputBuffer.capacity(); + outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen); + outputBuffer.startIncr(inLen); + audioIO.outputBuffer.endIncr(outLen); + } + else { + // Give up on pushing output + audioIO.active = false; + outputBuffer.clear(); + // DEBUG("Audio Interface underflow"); + } + } + + // Notify audio thread that an output is potentially ready + audioIO.audioCv.notify_one(); + } + + // Turn on light if at least one port is enabled in the nearby pair + for (int i = 0; i < AUDIO_INPUTS / 2; i++) + lights[INPUT_LIGHT + i].setBrightness(audioIO.active && audioIO.numOutputs >= 2*i+1); + for (int i = 0; i < AUDIO_OUTPUTS / 2; i++) + lights[OUTPUT_LIGHT + i].setBrightness(audioIO.active && audioIO.numInputs >= 2*i+1); + } json_t *dataToJson() override { json_t *rootJ = json_object(); @@ -141,101 +233,6 @@ struct AudioInterface : Module { }; -void AudioInterface::step() { - // Update SRC states - int sampleRate = (int) app()->engine->getSampleRate(); - inputSrc.setRates(audioIO.sampleRate, sampleRate); - outputSrc.setRates(sampleRate, audioIO.sampleRate); - - inputSrc.setChannels(audioIO.numInputs); - outputSrc.setChannels(audioIO.numOutputs); - - // Inputs: audio engine -> rack engine - if (audioIO.active && audioIO.numInputs > 0) { - // Wait until inputs are present - // Give up after a timeout in case the audio device is being unresponsive. - std::unique_lock lock(audioIO.engineMutex); - auto cond = [&] { - return (!audioIO.inputBuffer.empty()); - }; - auto timeout = std::chrono::milliseconds(200); - if (audioIO.engineCv.wait_for(lock, timeout, cond)) { - // Convert inputs - int inLen = audioIO.inputBuffer.size(); - int outLen = inputBuffer.capacity(); - inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen); - audioIO.inputBuffer.startIncr(inLen); - inputBuffer.endIncr(outLen); - } - else { - // Give up on pulling input - audioIO.active = false; - // DEBUG("Audio Interface underflow"); - } - } - - // Take input from buffer - dsp::Frame inputFrame; - if (!inputBuffer.empty()) { - inputFrame = inputBuffer.shift(); - } - else { - std::memset(&inputFrame, 0, sizeof(inputFrame)); - } - for (int i = 0; i < audioIO.numInputs; i++) { - outputs[AUDIO_OUTPUT + i].setVoltage(10.f * inputFrame.samples[i]); - } - for (int i = audioIO.numInputs; i < AUDIO_INPUTS; i++) { - outputs[AUDIO_OUTPUT + i].setVoltage(0.f); - } - - // Outputs: rack engine -> audio engine - if (audioIO.active && audioIO.numOutputs > 0) { - // Get and push output SRC frame - if (!outputBuffer.full()) { - dsp::Frame outputFrame; - for (int i = 0; i < AUDIO_OUTPUTS; i++) { - outputFrame.samples[i] = inputs[AUDIO_INPUT + i].getVoltage() / 10.f; - } - outputBuffer.push(outputFrame); - } - - if (outputBuffer.full()) { - // Wait until enough outputs are consumed - // Give up after a timeout in case the audio device is being unresponsive. - std::unique_lock lock(audioIO.engineMutex); - auto cond = [&] { - return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize); - }; - auto timeout = std::chrono::milliseconds(200); - if (audioIO.engineCv.wait_for(lock, timeout, cond)) { - // Push converted output - int inLen = outputBuffer.size(); - int outLen = audioIO.outputBuffer.capacity(); - outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen); - outputBuffer.startIncr(inLen); - audioIO.outputBuffer.endIncr(outLen); - } - else { - // Give up on pushing output - audioIO.active = false; - outputBuffer.clear(); - // DEBUG("Audio Interface underflow"); - } - } - - // Notify audio thread that an output is potentially ready - audioIO.audioCv.notify_one(); - } - - // Turn on light if at least one port is enabled in the nearby pair - for (int i = 0; i < AUDIO_INPUTS / 2; i++) - lights[INPUT_LIGHT + i].setBrightness(audioIO.active && audioIO.numOutputs >= 2*i+1); - for (int i = 0; i < AUDIO_OUTPUTS / 2; i++) - lights[OUTPUT_LIGHT + i].setBrightness(audioIO.active && audioIO.numInputs >= 2*i+1); -} - - struct AudioInterfaceWidget : ModuleWidget { AudioInterfaceWidget(AudioInterface *module) { setModule(module); diff --git a/src/Core/CV_MIDI.cpp b/src/Core/CV_MIDI.cpp index 15b59271..42cdca90 100644 --- a/src/Core/CV_MIDI.cpp +++ b/src/Core/CV_MIDI.cpp @@ -252,7 +252,7 @@ struct CV_MIDI : Module { } for (int c = 0; c < inputs[PITCH_INPUT].getChannels(); c++) { - int vel = (int) std::round(inputs[VEL_INPUT].normalize(10.f * 100 / 127, c) / 10.f * 127); + int vel = (int) std::round(inputs[VEL_INPUT].getNormalPolyVoltage(10.f * 100 / 127, c) / 10.f * 127); vel = clamp(vel, 0, 127); midiOutput.setVelocity(vel, c); @@ -260,12 +260,12 @@ struct CV_MIDI : Module { note = clamp(note, 0, 127); midiOutput.setNote(note, c); - bool gate = inputs[GATE_INPUT].getVoltage(c) >= 1.f; + bool gate = inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f; midiOutput.setGate(gate, c); midiOutput.stepChannel(c); - int aft = (int) std::round(inputs[AFT_INPUT].getVoltage(c) / 10.f * 127); + int aft = (int) std::round(inputs[AFT_INPUT].getPolyVoltage(c) / 10.f * 127); aft = clamp(aft, 0, 127); midiOutput.setAftertouch(aft, c); } @@ -278,7 +278,7 @@ struct CV_MIDI : Module { mw = clamp(mw, 0, 127); midiOutput.setModWheel(mw); - int vol = (int) std::round(inputs[VOL_INPUT].normalize(10.f) / 10.f * 127); + int vol = (int) std::round(inputs[VOL_INPUT].getNormalVoltage(10.f) / 10.f * 127); vol = clamp(vol, 0, 127); midiOutput.setVolume(vol); @@ -286,16 +286,16 @@ struct CV_MIDI : Module { pan = clamp(pan, 0, 127); midiOutput.setPan(pan); - bool clk = inputs[CLK_INPUT].value >= 1.f; + bool clk = inputs[CLK_INPUT].getVoltage() >= 1.f; midiOutput.setClock(clk); - bool start = inputs[START_INPUT].value >= 1.f; + bool start = inputs[START_INPUT].getVoltage() >= 1.f; midiOutput.setStart(start); - bool stop = inputs[STOP_INPUT].value >= 1.f; + bool stop = inputs[STOP_INPUT].getVoltage() >= 1.f; midiOutput.setStop(stop); - bool cont = inputs[CONTINUE_INPUT].value >= 1.f; + bool cont = inputs[CONTINUE_INPUT].getVoltage() >= 1.f; midiOutput.setContinue(cont); } diff --git a/src/Core/MIDI_CC.cpp b/src/Core/MIDI_CC.cpp index 31e3d43f..db4cb8ce 100644 --- a/src/Core/MIDI_CC.cpp +++ b/src/Core/MIDI_CC.cpp @@ -47,7 +47,7 @@ struct MIDI_CC : Module { float lambda = app()->engine->getSampleTime() * 100.f; for (int i = 0; i < 16; i++) { - if (!outputs[CC_OUTPUT + i].isActive()) + if (!outputs[CC_OUTPUT + i].isConnected()) continue; int cc = learnedCcs[i]; diff --git a/src/app/CableWidget.cpp b/src/app/CableWidget.cpp index 36b4108e..3b1f3808 100644 --- a/src/app/CableWidget.cpp +++ b/src/app/CableWidget.cpp @@ -221,16 +221,16 @@ void CableWidget::draw(NVGcontext *vg) { } float thickness = 5; - if (cable && cable->outputModule) { + if (cable->outputModule) { Output *output = &cable->outputModule->outputs[cable->outputId]; if (output->channels > 1) { // Increase thickness if output port is polyphonic thickness = 7; } - else if (output->channels == 0) { - // Draw translucent cable if not active (i.e. 0 channels) - nvgGlobalAlpha(vg, 0.5); - } + // else if (output->channels == 0) { + // // Draw translucent cable if not active (i.e. 0 channels) + // opacity *= 0.5; + // } } math::Vec outputPos = getOutputPos(); diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 360fd63f..f64ffbc4 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -15,6 +15,9 @@ namespace rack { +static const char PRESET_FILTERS[] = "VCV Rack module preset (.vcvm):vcvm"; + + struct ModuleDisconnectItem : MenuItem { ModuleWidget *moduleWidget; ModuleDisconnectItem() { @@ -499,7 +502,7 @@ void ModuleWidget::loadDialog() { std::string dir = asset::user("presets"); system::createDirectory(dir); - osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); + osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS); DEFER({ osdialog_filters_free(filters); }); @@ -520,7 +523,7 @@ void ModuleWidget::saveDialog() { std::string dir = asset::user("presets"); system::createDirectory(dir); - osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); + osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS); DEFER({ osdialog_filters_free(filters); }); diff --git a/src/app/ParamQuantity.cpp b/src/app/ParamQuantity.cpp index 29b17e79..8f5ac3e0 100644 --- a/src/app/ParamQuantity.cpp +++ b/src/app/ParamQuantity.cpp @@ -25,16 +25,14 @@ float ParamQuantity::getSmoothValue() { void ParamQuantity::setValue(float value) { if (!module) return; - value = math::clamp(value, getMinValue(), getMaxValue()); - // TODO Smooth - // TODO Snap - getParam()->value = value; + // This setter clamps the value + getParam()->setValue(value); } float ParamQuantity::getValue() { if (!module) return 0.f; - return getParam()->value; + return getParam()->getValue(); } float ParamQuantity::getMinValue() { diff --git a/src/app/PortWidget.cpp b/src/app/PortWidget.cpp index 663e3e16..75c54da4 100644 --- a/src/app/PortWidget.cpp +++ b/src/app/PortWidget.cpp @@ -13,6 +13,7 @@ struct PlugLight : MultiLightWidget { PlugLight() { addBaseColor(color::GREEN); addBaseColor(color::RED); + addBaseColor(color::BLUE); box.size = math::Vec(8, 8); bgColor = color::BLACK_TRANSPARENT; } @@ -35,14 +36,17 @@ void PortWidget::step() { if (!module) return; - std::vector values(2); + std::vector values(3); + // Input and Outputs are not virtual, so we can't cast to Port to make this less redundant. if (type == OUTPUT) { values[0] = module->outputs[portId].plugLights[0].getBrightness(); values[1] = module->outputs[portId].plugLights[1].getBrightness(); + values[2] = module->outputs[portId].plugLights[2].getBrightness(); } else { values[0] = module->inputs[portId].plugLights[0].getBrightness(); values[1] = module->inputs[portId].plugLights[1].getBrightness(); + values[2] = module->inputs[portId].plugLights[2].getBrightness(); } plugLight->setValues(values); } diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index 4138988d..cc4041a8 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -75,7 +75,7 @@ void Scene::step() { // Version popup message if (!latestVersion.empty()) { - std::string versionMessage = string::f("Rack %s is available.\n\nYou have Rack %s.\n\nClose Rack and download new version on the website?", latestVersion.c_str(), APP_VERSION.c_str()); + std::string versionMessage = string::f("Rack %s is available.\n\nYou have Rack %s.\n\nClose Rack and download new version on the website?", latestVersion.c_str(), APP_VERSION); if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, versionMessage.c_str())) { std::thread t(system::openBrowser, "https://vcvrack.com/"); t.detach(); @@ -164,17 +164,19 @@ void Scene::onPathDrop(const event::PathDrop &e) { } void Scene::runCheckVersion() { - json_t *resJ = network::requestJson(network::METHOD_GET, API_HOST + "/version", NULL); + std::string versionUrl = API_HOST; + versionUrl += "/version"; + json_t *versionResJ = network::requestJson(network::METHOD_GET, versionUrl, NULL); - if (resJ) { - json_t *versionJ = json_object_get(resJ, "version"); + if (versionResJ) { + json_t *versionJ = json_object_get(versionResJ, "version"); if (versionJ) { std::string version = json_string_value(versionJ); if (version != APP_VERSION) { latestVersion = version; } } - json_decref(resJ); + json_decref(versionResJ); } } diff --git a/src/app/common.cpp b/src/app/common.cpp deleted file mode 100644 index d1630c3a..00000000 --- a/src/app/common.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "app/common.hpp" - - -namespace rack { - - -const std::string APP_NAME = "VCV Rack"; -const std::string APP_VERSION = TOSTRING(VERSION); -const std::string API_HOST = "https://api.vcvrack.com"; - - -} // namespace rack diff --git a/src/engine/Cable.cpp b/src/engine/Cable.cpp index 9660526e..80d70b49 100644 --- a/src/engine/Cable.cpp +++ b/src/engine/Cable.cpp @@ -9,9 +9,9 @@ void Cable::step() { Input *input = &inputModule->inputs[inputId]; // Match number of polyphonic channels to output port input->channels = output->channels; - // Copy values from output to input + // Copy voltages from output to input for (int i = 0; i < output->channels; i++) { - input->values[i] = output->values[i]; + input->voltages[i] = output->voltages[i]; } } diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 9bddf19a..321f6b5d 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -100,12 +100,11 @@ static void Engine_step(Engine *engine) { Param *param = &smoothModule->params[smoothParamId]; float value = param->value; // decay rate is 1 graphics frame - const float lambda = 60.f; - float delta = smoothValue - value; - float newValue = value + delta * lambda * engine->internal->sampleTime; - if (value == newValue) { - // Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats) - param->value = smoothValue; + const float smoothLambda = 60.f; + float newValue = value + (smoothValue - value) * smoothLambda * engine->internal->sampleTime; + if (value == newValue || !(param->minValue <= newValue && newValue <= param->maxValue)) { + // Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats), or if newValue is out of bounds + param->setValue(smoothValue); engine->internal->smoothModule = NULL; } else { @@ -121,11 +120,19 @@ static void Engine_step(Engine *engine) { if (module->bypass) { // Bypass module for (Output &output : module->outputs) { + // This also zeros all voltages output.setChannels(0); } - module->cpuTime = 0.f; + if (settings::powerMeter) { + module->cpuTime = 0.f; + } } else { + // Set all outputs to 1 channel so that modules are forced to specify 0 or >2 channels every frame + for (Output &output : module->outputs) { + // Don't use Port::setChannels() so we maintain all previous voltages + output.channels = 1; + } // Step module if (settings::powerMeter) { auto startTime = std::chrono::high_resolution_clock::now(); @@ -266,6 +273,23 @@ void Engine::randomizeModule(Module *module) { module->randomize(); } +static void Engine_updateConnected(Engine *engine) { + // Set everything to unconnected + for (Module *module : engine->modules) { + for (Input &input : module->inputs) { + input.active = false; + } + for (Output &output : module->outputs) { + output.active = false; + } + } + // Set inputs/outputs to active + for (Cable *cable : engine->cables) { + cable->outputModule->outputs[cable->outputId].active = true; + cable->inputModule->inputs[cable->inputId].active = true; + } +} + void Engine::addCable(Cable *cable) { assert(cable); VIPLock vipLock(internal->vipMutex); @@ -293,6 +317,7 @@ void Engine::addCable(Cable *cable) { } // Add the cable cables.push_back(cable); + Engine_updateConnected(this); } void Engine::removeCable(Cable *cable) { @@ -307,6 +332,7 @@ void Engine::removeCable(Cable *cable) { input.setChannels(0); // Remove the cable cables.erase(it); + Engine_updateConnected(this); // Remove ID cable->id = 0; } diff --git a/src/engine/Port.cpp b/src/engine/Port.cpp index 90a869b8..606f622b 100644 --- a/src/engine/Port.cpp +++ b/src/engine/Port.cpp @@ -5,6 +5,7 @@ namespace rack { void Port::step() { + // Set plug lights if (channels == 0) { plugLights[0].setBrightness(0.f); plugLights[1].setBrightness(0.f); diff --git a/src/main.cpp b/src/main.cpp index 7fb96886..1251171a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { #ifdef ARCH_WIN // Windows global mutex to prevent multiple instances // Handle will be closed by Windows when the process ends - HANDLE instanceMutex = CreateMutex(NULL, true, APP_NAME.c_str()); + HANDLE instanceMutex = CreateMutex(NULL, true, APP_NAME); if (GetLastError() == ERROR_ALREADY_EXISTS) { osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Rack is already running. Multiple Rack instances are not supported."); exit(1); @@ -65,7 +65,7 @@ int main(int argc, char *argv[]) { logger::init(devMode); // Log environment - INFO("%s %s", APP_NAME.c_str(), APP_VERSION.c_str()); + INFO("%s %s", APP_NAME, APP_VERSION); if (devMode) INFO("Development mode"); INFO("System directory: %s", asset::systemDir.c_str()); diff --git a/src/patch.cpp b/src/patch.cpp index 02d72a5a..9d813940 100644 --- a/src/patch.cpp +++ b/src/patch.cpp @@ -11,7 +11,7 @@ namespace rack { -static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv"; +static const char PATCH_FILTERS[] = "VCV Rack patch (.vcv):vcv"; void PatchManager::reset() { @@ -72,7 +72,7 @@ void PatchManager::saveAsDialog() { filename = string::filename(path); } - osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); + osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS); DEFER({ osdialog_filters_free(filters); }); @@ -140,7 +140,7 @@ void PatchManager::loadDialog() { dir = string::directory(path); } - osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); + osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS); DEFER({ osdialog_filters_free(filters); }); @@ -179,7 +179,7 @@ json_t *PatchManager::toJson() { json_t *rootJ = json_object(); // version - json_t *versionJ = json_string(APP_VERSION.c_str()); + json_t *versionJ = json_string(APP_VERSION); json_object_set_new(rootJ, "version", versionJ); // Merge with RackWidget JSON @@ -200,7 +200,7 @@ void PatchManager::fromJson(json_t *rootJ) { if (versionJ) version = json_string_value(versionJ); if (version != APP_VERSION) { - INFO("Patch made with Rack version %s, current Rack version is %s", version.c_str(), APP_VERSION.c_str()); + INFO("Patch made with Rack version %s, current Rack version is %s", version.c_str(), APP_VERSION); } // Detect old patches with ModuleWidget::params/inputs/outputs indices. diff --git a/src/plugin.cpp b/src/plugin.cpp index 8866159e..76a005bd 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -378,7 +378,9 @@ void logIn(std::string email, std::string password) { json_t *reqJ = json_object(); json_object_set(reqJ, "email", json_string(email.c_str())); json_object_set(reqJ, "password", json_string(password.c_str())); - json_t *resJ = network::requestJson(network::METHOD_POST, API_HOST + "/token", reqJ); + std::string tokenUrl = API_HOST; + tokenUrl += "/token"; + json_t *resJ = network::requestJson(network::METHOD_POST, tokenUrl, reqJ); json_decref(reqJ); if (resJ) { @@ -421,7 +423,9 @@ bool sync(bool dryRun) { // Get user's plugins list json_t *pluginsReqJ = json_object(); json_object_set(pluginsReqJ, "token", json_string(token.c_str())); - json_t *pluginsResJ = network::requestJson(network::METHOD_GET, API_HOST + "/plugins", pluginsReqJ); + std::string pluginsUrl = API_HOST; + pluginsUrl += "/plugins"; + json_t *pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, pluginsReqJ); json_decref(pluginsReqJ); if (!pluginsResJ) { WARN("Request for user's plugins failed"); @@ -438,7 +442,9 @@ bool sync(bool dryRun) { } // Get community manifests - json_t *manifestsResJ = network::requestJson(network::METHOD_GET, API_HOST + "/community/manifests", NULL); + std::string manifestsUrl = API_HOST; + manifestsUrl += "/community/manifests"; + json_t *manifestsResJ = network::requestJson(network::METHOD_GET, manifestsUrl, NULL); if (!manifestsResJ) { WARN("Request for community manifests failed"); return false;