Browse Source

Add convenience methods to Port. Draw blue plug lights for polyphonic ports.

tags/v1.0.0
Andrew Belt 5 years ago
parent
commit
6d86a8280c
24 changed files with 234 additions and 214 deletions
  1. +3
    -5
      include/app/common.hpp
  2. +0
    -17
      include/engine/Input.hpp
  3. +7
    -5
      include/engine/Light.hpp
  4. +1
    -2
      include/engine/Module.hpp
  5. +0
    -13
      include/engine/Output.hpp
  6. +3
    -1
      include/engine/Param.hpp
  7. +36
    -14
      include/engine/Port.hpp
  8. +3
    -0
      include/math.hpp
  9. +2
    -3
      include/rack.hpp
  10. +93
    -96
      src/Core/AudioInterface.cpp
  11. +8
    -8
      src/Core/CV_MIDI.cpp
  12. +1
    -1
      src/Core/MIDI_CC.cpp
  13. +5
    -5
      src/app/CableWidget.cpp
  14. +5
    -2
      src/app/ModuleWidget.cpp
  15. +3
    -5
      src/app/ParamQuantity.cpp
  16. +5
    -1
      src/app/PortWidget.cpp
  17. +7
    -5
      src/app/Scene.cpp
  18. +0
    -12
      src/app/common.cpp
  19. +2
    -2
      src/engine/Cable.cpp
  20. +33
    -7
      src/engine/Engine.cpp
  21. +1
    -0
      src/engine/Port.cpp
  22. +2
    -2
      src/main.cpp
  23. +5
    -5
      src/patch.cpp
  24. +9
    -3
      src/plugin.cpp

+ 3
- 5
include/app/common.hpp View File

@@ -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

+ 0
- 17
include/engine/Input.hpp View File

@@ -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

+ 7
- 5
include/engine/Light.hpp View File

@@ -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.
*/


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

@@ -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 <vector>
#include <jansson.h>


+ 0
- 13
include/engine/Output.hpp View File

@@ -1,13 +0,0 @@
#pragma once
#include "common.hpp"
#include "engine/Port.hpp"


namespace rack {


struct Output : Port {
};


} // namespace rack

+ 3
- 1
include/engine/Param.hpp View File

@@ -1,5 +1,6 @@
#pragma once
#include "common.hpp"
#include "math.hpp"
#include <jansson.h>


@@ -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();


+ 36
- 14
include/engine/Port.hpp View File

@@ -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

+ 3
- 0
include/math.hpp View File

@@ -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);


+ 2
- 3
include/rack.hpp View File

@@ -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"



+ 93
- 96
src/Core/AudioInterface.cpp View File

@@ -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<std::mutex> 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<AUDIO_INPUTS> 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<AUDIO_OUTPUTS> 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<std::mutex> 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<std::mutex> 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<AUDIO_INPUTS> 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<AUDIO_OUTPUTS> 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<std::mutex> 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);


+ 8
- 8
src/Core/CV_MIDI.cpp View File

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



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

@@ -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];


+ 5
- 5
src/app/CableWidget.cpp View File

@@ -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();


+ 5
- 2
src/app/ModuleWidget.cpp View File

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


+ 3
- 5
src/app/ParamQuantity.cpp View File

@@ -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() {


+ 5
- 1
src/app/PortWidget.cpp View File

@@ -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<float> values(2);
std::vector<float> 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);
}


+ 7
- 5
src/app/Scene.cpp View File

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



+ 0
- 12
src/app/common.cpp View File

@@ -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

+ 2
- 2
src/engine/Cable.cpp View File

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



+ 33
- 7
src/engine/Engine.cpp View File

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


+ 1
- 0
src/engine/Port.cpp View File

@@ -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);


+ 2
- 2
src/main.cpp View File

@@ -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());


+ 5
- 5
src/patch.cpp View File

@@ -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.


+ 9
- 3
src/plugin.cpp View File

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


Loading…
Cancel
Save