| @@ -7,13 +7,13 @@ | |||||
| namespace rack { | namespace rack { | ||||
| struct Model; | |||||
| struct Module; | struct Module; | ||||
| struct Wire; | struct Wire; | ||||
| struct RackWidget; | struct RackWidget; | ||||
| struct ParamWidget; | struct ParamWidget; | ||||
| struct Port; | struct Port; | ||||
| struct Scene; | |||||
| //////////////////// | //////////////////// | ||||
| // module | // module | ||||
| @@ -23,7 +23,6 @@ struct Scene; | |||||
| #define RACK_GRID_WIDTH 15 | #define RACK_GRID_WIDTH 15 | ||||
| #define RACK_GRID_HEIGHT 380 | #define RACK_GRID_HEIGHT 380 | ||||
| struct Model; | |||||
| struct ModuleWidget : OpaqueWidget { | struct ModuleWidget : OpaqueWidget { | ||||
| Model *model = NULL; | Model *model = NULL; | ||||
| /** Owns the module pointer */ | /** Owns the module pointer */ | ||||
| @@ -168,12 +167,6 @@ struct CircularShadow : TransparentWidget { | |||||
| void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
| }; | }; | ||||
| struct LightWidget : TransparentWidget { | |||||
| NVGcolor bgColor = nvgRGBf(0, 0, 0); | |||||
| NVGcolor color = nvgRGBf(1, 1, 1); | |||||
| void draw(NVGcontext *vg) override; | |||||
| }; | |||||
| struct ParamWidget : OpaqueWidget, QuantityWidget { | struct ParamWidget : OpaqueWidget, QuantityWidget { | ||||
| Module *module = NULL; | Module *module = NULL; | ||||
| int paramId; | int paramId; | ||||
| @@ -309,6 +302,29 @@ struct SVGScrew : FramebufferWidget { | |||||
| SVGScrew(); | SVGScrew(); | ||||
| }; | }; | ||||
| //////////////////// | |||||
| // lights | |||||
| //////////////////// | |||||
| struct LightWidget : TransparentWidget { | |||||
| NVGcolor bgColor = nvgRGBf(0, 0, 0); | |||||
| NVGcolor color = nvgRGBf(1, 1, 1); | |||||
| void draw(NVGcontext *vg) override; | |||||
| }; | |||||
| /** A LightWidget that points to a module's Light or a range of lights */ | |||||
| struct ModuleLightWidget : LightWidget { | |||||
| Module *module = NULL; | |||||
| int lightId; | |||||
| }; | |||||
| /** Mixes colors based on the brightness of the module light at lightId, lightId + 1, etc */ | |||||
| struct ColorLightWidget : ModuleLightWidget { | |||||
| std::vector<NVGcolor> colors; | |||||
| void addColor(NVGcolor c); | |||||
| void step() override; | |||||
| }; | |||||
| //////////////////// | //////////////////// | ||||
| // scene | // scene | ||||
| //////////////////// | //////////////////// | ||||
| @@ -422,95 +422,55 @@ struct CL1362Port : SVGPort { | |||||
| // Lights | // Lights | ||||
| //////////////////// | //////////////////// | ||||
| struct ValueLight : LightWidget { | |||||
| float *value = NULL; | |||||
| virtual void setValue(float v) {} | |||||
| void step() override { | |||||
| if (value) | |||||
| setValue(*value); | |||||
| struct RedLight : ColorLightWidget { | |||||
| RedLight() { | |||||
| addColor(COLOR_RED); | |||||
| } | } | ||||
| }; | }; | ||||
| struct ColorValueLight : ValueLight { | |||||
| NVGcolor baseColor; | |||||
| void setValue(float v) override { | |||||
| v = sqrtBipolar(v); | |||||
| color = baseColor; | |||||
| color.a *= clampf(v, 0.0, 1.0); | |||||
| struct GreenLight : ColorLightWidget { | |||||
| GreenLight() { | |||||
| addColor(COLOR_GREEN); | |||||
| } | } | ||||
| }; | }; | ||||
| struct RedValueLight : ColorValueLight { | |||||
| RedValueLight() { | |||||
| baseColor = COLOR_RED; | |||||
| struct GreenRedLight : ColorLightWidget { | |||||
| GreenRedLight() { | |||||
| addColor(COLOR_GREEN); | |||||
| addColor(COLOR_RED); | |||||
| } | } | ||||
| }; | }; | ||||
| struct YellowValueLight : ColorValueLight { | |||||
| YellowValueLight() { | |||||
| baseColor = COLOR_YELLOW; | |||||
| } | |||||
| }; | |||||
| struct GreenValueLight : ColorValueLight { | |||||
| GreenValueLight() { | |||||
| baseColor = COLOR_GREEN; | |||||
| } | |||||
| }; | |||||
| struct PolarityLight : ValueLight { | |||||
| NVGcolor posColor; | |||||
| NVGcolor negColor; | |||||
| void setValue(float v) override { | |||||
| v = sqrtBipolar(v); | |||||
| color = (v >= 0.0) ? posColor : negColor; | |||||
| color.a *= clampf(fabsf(v), 0.0, 1.0); | |||||
| } | |||||
| }; | |||||
| struct GreenRedPolarityLight : PolarityLight { | |||||
| GreenRedPolarityLight() { | |||||
| posColor = COLOR_GREEN; | |||||
| negColor = COLOR_RED; | |||||
| } | |||||
| }; | |||||
| struct ModeValueLight : ValueLight { | |||||
| std::vector<NVGcolor> colors; | |||||
| void setValue(float v) override { | |||||
| int mode = clampi((int)roundf(v), 0, colors.size()); | |||||
| color = colors[mode]; | |||||
| } | |||||
| void addColor(NVGcolor color) { | |||||
| colors.push_back(color); | |||||
| } | |||||
| }; | |||||
| /** 5mm diameter */ | |||||
| template <typename BASE> | template <typename BASE> | ||||
| struct LargeLight : BASE { | struct LargeLight : BASE { | ||||
| LargeLight() { | LargeLight() { | ||||
| this->box.size = Vec(20, 20); | |||||
| this->box.size = Vec(15, 15); | |||||
| } | } | ||||
| }; | }; | ||||
| /** 3mm diameter */ | |||||
| template <typename BASE> | template <typename BASE> | ||||
| struct MediumLight : BASE { | struct MediumLight : BASE { | ||||
| MediumLight() { | MediumLight() { | ||||
| this->box.size = Vec(12, 12); | |||||
| this->box.size = Vec(9, 9); | |||||
| } | } | ||||
| }; | }; | ||||
| /** 2mm diameter */ | |||||
| template <typename BASE> | template <typename BASE> | ||||
| struct SmallLight : BASE { | struct SmallLight : BASE { | ||||
| SmallLight() { | SmallLight() { | ||||
| this->box.size = Vec(8, 8); | |||||
| this->box.size = Vec(6, 6); | |||||
| } | } | ||||
| }; | }; | ||||
| /** 1mm diameter */ | |||||
| template <typename BASE> | template <typename BASE> | ||||
| struct TinyLight : BASE { | struct TinyLight : BASE { | ||||
| TinyLight() { | TinyLight() { | ||||
| this->box.size = Vec(5, 5); | |||||
| this->box.size = Vec(3, 3); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -32,6 +32,7 @@ struct Output { | |||||
| struct Light { | struct Light { | ||||
| /** The square of the brightness value */ | /** The square of the brightness value */ | ||||
| float value = 0.0; | float value = 0.0; | ||||
| float getBrightness(); | |||||
| void setBrightness(float brightness) { | void setBrightness(float brightness) { | ||||
| value = brightness * brightness; | value = brightness * brightness; | ||||
| } | } | ||||
| @@ -34,9 +34,16 @@ Model *createModel(std::string manufacturerSlug, std::string manufacturerName, s | |||||
| return model; | return model; | ||||
| } | } | ||||
| template <class TParam> | |||||
| template <class TScrew> | |||||
| Widget *createScrew(Vec pos) { | |||||
| Widget *screw = new TScrew(); | |||||
| screw->box.pos = pos; | |||||
| return screw; | |||||
| } | |||||
| template <class TParamWidget> | |||||
| ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | ||||
| ParamWidget *param = new TParam(); | |||||
| ParamWidget *param = new TParamWidget(); | |||||
| param->box.pos = pos; | param->box.pos = pos; | ||||
| param->module = module; | param->module = module; | ||||
| param->paramId = paramId; | param->paramId = paramId; | ||||
| @@ -65,27 +72,12 @@ Port *createOutput(Vec pos, Module *module, int outputId) { | |||||
| return port; | return port; | ||||
| } | } | ||||
| template <class TScrew> | |||||
| Widget *createScrew(Vec pos) { | |||||
| Widget *screw = new TScrew(); | |||||
| screw->box.pos = pos; | |||||
| return screw; | |||||
| } | |||||
| template <class TLight> | |||||
| ValueLight *createValueLight(Vec pos, float *value) { | |||||
| ValueLight *light = new TLight(); | |||||
| light->box.pos = pos; | |||||
| light->value = value; | |||||
| return light; | |||||
| } | |||||
| /** Polyfill for future LightWidget */ | |||||
| template <class TLight> | |||||
| ValueLight *createValueLight(Vec pos, Module *module, int lightId) { | |||||
| ValueLight *light = new TLight(); | |||||
| template<class TModuleLightWidget> | |||||
| ModuleLightWidget *createLight(Vec pos, Module *module, int lightId) { | |||||
| ModuleLightWidget *light = new TModuleLightWidget(); | |||||
| light->box.pos = pos; | light->box.pos = pos; | ||||
| light->value = &module->lights[lightId].value; | |||||
| light->module = module; | |||||
| light->lightId = lightId; | |||||
| return light; | return light; | ||||
| } | } | ||||
| @@ -0,0 +1,24 @@ | |||||
| #include "app.hpp" | |||||
| #include "engine.hpp" | |||||
| namespace rack { | |||||
| void ColorLightWidget::addColor(NVGcolor c) { | |||||
| colors.push_back(c); | |||||
| } | |||||
| void ColorLightWidget::step() { | |||||
| color = nvgRGBf(0, 0, 0); | |||||
| for (int i = 0; i < (int)colors.size(); i++) { | |||||
| NVGcolor c = colors[i]; | |||||
| float brightness = module->lights[lightId + i].getBrightness(); | |||||
| color.r += c.r * brightness; | |||||
| color.g += c.g * brightness; | |||||
| color.b += c.b * brightness; | |||||
| } | |||||
| } | |||||
| } // namespace rack | |||||
| @@ -87,10 +87,10 @@ WireWidget::WireWidget() { | |||||
| lastWireColorId = (lastWireColorId + 1) % LENGTHOF(wireColors); | lastWireColorId = (lastWireColorId + 1) % LENGTHOF(wireColors); | ||||
| color = wireColors[lastWireColorId]; | color = wireColors[lastWireColorId]; | ||||
| inputLight = construct<PolarityLight>(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); | |||||
| outputLight = construct<PolarityLight>(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); | |||||
| addChild(inputLight); | |||||
| addChild(outputLight); | |||||
| // inputLight = construct<PolarityLight>(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); | |||||
| // outputLight = construct<PolarityLight>(&PolarityLight::posColor, COLOR_GREEN, &PolarityLight::negColor, COLOR_RED); | |||||
| // addChild(inputLight); | |||||
| // addChild(outputLight); | |||||
| } | } | ||||
| WireWidget::~WireWidget() { | WireWidget::~WireWidget() { | ||||
| @@ -166,6 +166,7 @@ void WireWidget::drawPlugs(NVGcontext *vg) { | |||||
| drawPlug(vg, inputPos, color); | drawPlug(vg, inputPos, color); | ||||
| // Draw plug light | // Draw plug light | ||||
| /* | |||||
| if (gToolbar->plugLightButton->value > 0.0) { | if (gToolbar->plugLightButton->value > 0.0) { | ||||
| if (wire) { | if (wire) { | ||||
| Output &output = wire->outputModule->outputs[wire->outputId]; | Output &output = wire->outputModule->outputs[wire->outputId]; | ||||
| @@ -188,6 +189,7 @@ void WireWidget::drawPlugs(NVGcontext *vg) { | |||||
| outputLight->visible = false; | outputLight->visible = false; | ||||
| inputLight->visible = false; | inputLight->visible = false; | ||||
| } | } | ||||
| */ | |||||
| Widget::draw(vg); | Widget::draw(vg); | ||||
| } | } | ||||
| @@ -18,6 +18,9 @@ struct MIDICCToCVInterface : MidiIO, Module { | |||||
| enum OutputIds { | enum OutputIds { | ||||
| NUM_OUTPUTS = 16 | NUM_OUTPUTS = 16 | ||||
| }; | }; | ||||
| enum LightIds { | |||||
| NUM_LIGHTS = 16 | |||||
| }; | |||||
| int cc[NUM_OUTPUTS]; | int cc[NUM_OUTPUTS]; | ||||
| int ccNum[NUM_OUTPUTS]; | int ccNum[NUM_OUTPUTS]; | ||||
| @@ -25,10 +28,9 @@ struct MIDICCToCVInterface : MidiIO, Module { | |||||
| bool ccSyncFirst[NUM_OUTPUTS]; | bool ccSyncFirst[NUM_OUTPUTS]; | ||||
| bool ccNumInited[NUM_OUTPUTS]; | bool ccNumInited[NUM_OUTPUTS]; | ||||
| bool onFocus[NUM_OUTPUTS]; | bool onFocus[NUM_OUTPUTS]; | ||||
| float lights[NUM_OUTPUTS]; | |||||
| MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | |||||
| MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||||
| for (int i = 0; i < NUM_OUTPUTS; i++) { | for (int i = 0; i < NUM_OUTPUTS; i++) { | ||||
| cc[i] = 0; | cc[i] = 0; | ||||
| ccNum[i] = i; | ccNum[i] = i; | ||||
| @@ -97,7 +99,7 @@ void MIDICCToCVInterface::step() { | |||||
| for (int i = 0; i < NUM_OUTPUTS; i++) { | for (int i = 0; i < NUM_OUTPUTS; i++) { | ||||
| lights[i] = ccSync[i] / 127.0; | |||||
| lights[i].setBrightness(ccSync[i] / 127.0); | |||||
| outputs[i].value = cc[i] / 127.0 * 10.0; | outputs[i].value = cc[i] / 127.0 * 10.0; | ||||
| } | } | ||||
| @@ -294,8 +296,7 @@ MIDICCToCVWidget::MIDICCToCVWidget() { | |||||
| yPos += labelHeight + margin; | yPos += labelHeight + margin; | ||||
| addOutput(createOutput<PJ3410Port>(Vec((i % 4) * (63) + 10, yPos + 5), module, i)); | addOutput(createOutput<PJ3410Port>(Vec((i % 4) * (63) + 10, yPos + 5), module, i)); | ||||
| addChild(createValueLight<SmallLight<RedValueLight>>(Vec((i % 4) * (63) + 32, yPos + 5), | |||||
| &module->lights[i])); | |||||
| addChild(createLight<SmallLight<RedLight>>(Vec((i % 4) * (63) + 32, yPos + 5), module, i)); | |||||
| if ((i + 1) % 4 == 0) { | if ((i + 1) % 4 == 0) { | ||||
| yPos += 47 + margin; | yPos += 47 + margin; | ||||
| @@ -26,6 +26,10 @@ struct MIDIToCVInterface : MidiIO, Module { | |||||
| CHANNEL_AFTERTOUCH_OUTPUT, | CHANNEL_AFTERTOUCH_OUTPUT, | ||||
| NUM_OUTPUTS | NUM_OUTPUTS | ||||
| }; | }; | ||||
| enum LightIds { | |||||
| RESET_LIGHT, | |||||
| NUM_LIGHTS | |||||
| }; | |||||
| std::list<int> notes; | std::list<int> notes; | ||||
| bool pedal = false; | bool pedal = false; | ||||
| @@ -38,9 +42,8 @@ struct MIDIToCVInterface : MidiIO, Module { | |||||
| bool retriggered = false; | bool retriggered = false; | ||||
| SchmittTrigger resetTrigger; | SchmittTrigger resetTrigger; | ||||
| float resetLight = 0.0; | |||||
| MIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | |||||
| MIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||||
| } | } | ||||
| @@ -78,14 +81,11 @@ void MIDIToCVInterface::resetMidi() { | |||||
| pitchWheel = 64; | pitchWheel = 64; | ||||
| afterTouch = 0; | afterTouch = 0; | ||||
| vel = 0; | vel = 0; | ||||
| resetLight = 1.0; | |||||
| outputs[GATE_OUTPUT].value = 0.0; | outputs[GATE_OUTPUT].value = 0.0; | ||||
| notes.clear(); | notes.clear(); | ||||
| } | } | ||||
| void MIDIToCVInterface::step() { | void MIDIToCVInterface::step() { | ||||
| static float sampleRate = engineGetSampleRate(); | |||||
| if (isPortOpen()) { | if (isPortOpen()) { | ||||
| std::vector<unsigned char> message; | std::vector<unsigned char> message; | ||||
| @@ -109,10 +109,7 @@ void MIDIToCVInterface::step() { | |||||
| return; | return; | ||||
| } | } | ||||
| if (resetLight > 0) { | |||||
| resetLight -= resetLight / 0.55 / sampleRate; // fade out light | |||||
| } | |||||
| lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light | |||||
| outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0; | outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0; | ||||
| outputs[MOD_OUTPUT].value = mod / 127.0 * 10.0; | outputs[MOD_OUTPUT].value = mod / 127.0 * 10.0; | ||||
| @@ -228,7 +225,7 @@ MidiToCVWidget::MidiToCVWidget() { | |||||
| } | } | ||||
| addParam(createParam<LEDButton>(Vec(7 * 15, labelHeight), module, MIDIToCVInterface::RESET_PARAM, 0.0, 1.0, 0.0)); | addParam(createParam<LEDButton>(Vec(7 * 15, labelHeight), module, MIDIToCVInterface::RESET_PARAM, 0.0, 1.0, 0.0)); | ||||
| addChild(createValueLight<SmallLight<RedValueLight>>(Vec(7 * 15 + 5, labelHeight + 5), &module->resetLight)); | |||||
| addChild(createLight<SmallLight<RedLight>>(Vec(7 * 15 + 5, labelHeight + 5), module, MIDIToCVInterface::RESET_LIGHT)); | |||||
| { | { | ||||
| Label *label = new Label(); | Label *label = new Label(); | ||||
| label->box.pos = Vec(margin, yPos); | label->box.pos = Vec(margin, yPos); | ||||
| @@ -28,6 +28,10 @@ struct QuadMIDIToCVInterface : MidiIO, Module { | |||||
| AT_OUTPUT = 12, | AT_OUTPUT = 12, | ||||
| NUM_OUTPUTS = 16 | NUM_OUTPUTS = 16 | ||||
| }; | }; | ||||
| enum LightIds { | |||||
| RESET_LIGHT, | |||||
| NUM_LIGHTS | |||||
| }; | |||||
| enum Modes { | enum Modes { | ||||
| ROTATE, | ROTATE, | ||||
| @@ -47,9 +51,8 @@ struct QuadMIDIToCVInterface : MidiIO, Module { | |||||
| std::list<int> open; | std::list<int> open; | ||||
| SchmittTrigger resetTrigger; | SchmittTrigger resetTrigger; | ||||
| float resetLight = 0.0; | |||||
| QuadMIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | |||||
| QuadMIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||||
| } | } | ||||
| @@ -90,11 +93,10 @@ void QuadMIDIToCVInterface::resetMidi() { | |||||
| open.clear(); | open.clear(); | ||||
| pedal = false; | pedal = false; | ||||
| resetLight = 1.0; | |||||
| lights[RESET_LIGHT].value = 1.0; | |||||
| } | } | ||||
| void QuadMIDIToCVInterface::step() { | void QuadMIDIToCVInterface::step() { | ||||
| static float sampleRate = engineGetSampleRate(); | |||||
| static int msgsProcessed = 0; | static int msgsProcessed = 0; | ||||
| if (isPortOpen()) { | if (isPortOpen()) { | ||||
| @@ -125,10 +127,7 @@ void QuadMIDIToCVInterface::step() { | |||||
| return; | return; | ||||
| } | } | ||||
| if (resetLight > 0) { | |||||
| resetLight -= resetLight / 0.55 / sampleRate; // fade out light | |||||
| } | |||||
| lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light | |||||
| } | } | ||||
| @@ -307,7 +306,7 @@ QuadMidiToCVWidget::QuadMidiToCVWidget() { | |||||
| addParam(createParam<LEDButton>(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0, | addParam(createParam<LEDButton>(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0, | ||||
| 0.0)); | 0.0)); | ||||
| addChild(createValueLight<SmallLight<RedValueLight>>(Vec(12 * 15 + 5, labelHeight + 5), &module->resetLight)); | |||||
| addChild(createLight<SmallLight<RedLight>>(Vec(12 * 15 + 5, labelHeight + 5), module, QuadMIDIToCVInterface::RESET_LIGHT)); | |||||
| { | { | ||||
| Label *label = new Label(); | Label *label = new Label(); | ||||
| label->box.pos = Vec(margin, yPos); | label->box.pos = Vec(margin, yPos); | ||||
| @@ -33,6 +33,10 @@ static int smoothParamId; | |||||
| static float smoothValue; | static float smoothValue; | ||||
| float Light::getBrightness() { | |||||
| return sqrtf(value); | |||||
| } | |||||
| void Light::setBrightnessSmooth(float brightness) { | void Light::setBrightnessSmooth(float brightness) { | ||||
| value += (brightness * brightness - value) / sampleRate * 60.0; | value += (brightness * brightness - value) / sampleRate * 60.0; | ||||
| } | } | ||||