diff --git a/.gitmodules b/.gitmodules index 60ddf3d2..1968c884 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "ext/nanovg"] path = ext/nanovg url = https://github.com/memononen/nanovg.git +[submodule "ext/nanosvg"] + path = ext/nanosvg + url = https://github.com/memononen/nanosvg.git diff --git a/ext/nanosvg b/ext/nanosvg new file mode 160000 index 00000000..dc12d905 --- /dev/null +++ b/ext/nanosvg @@ -0,0 +1 @@ +Subproject commit dc12d90586a8ab99da0c575aafff999666aa5d55 diff --git a/include/rack.hpp b/include/rack.hpp index 781c0a88..6f5ab5ec 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -166,18 +166,18 @@ ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, f return param; } -inline +template InputPort *createInput(Vec pos, Module *module, int inputId) { - InputPort *port = new InputPort(); + InputPort *port = new TInputPort(); port->box.pos = pos; port->module = module; port->inputId = inputId; return port; } -inline +template OutputPort *createOutput(Vec pos, Module *module, int outputId) { - OutputPort *port = new OutputPort(); + OutputPort *port = new TOutputPort(); port->box.pos = pos; port->module = module; port->outputId = outputId; diff --git a/include/rackwidgets.hpp b/include/rackwidgets.hpp index c254338d..ab6ca229 100644 --- a/include/rackwidgets.hpp +++ b/include/rackwidgets.hpp @@ -94,7 +94,6 @@ struct RackWidget : OpaqueWidget { struct ModulePanel : TransparentWidget { NVGcolor backgroundColor; - NVGcolor highlightColor; std::string imageFilename; void draw(NVGcontext *vg); }; @@ -167,7 +166,7 @@ struct MomentarySwitch : virtual Switch { // ports //////////////////// -struct Port : OpaqueWidget, SpriteWidget { +struct Port : OpaqueWidget { Module *module = NULL; WireWidget *connectedWire = NULL; @@ -175,8 +174,6 @@ struct Port : OpaqueWidget, SpriteWidget { ~Port(); void disconnect(); - int type; - void drawGlow(NVGcontext *vg); void onMouseDown(int button); void onDragEnd(); }; @@ -184,7 +181,6 @@ struct Port : OpaqueWidget, SpriteWidget { struct InputPort : Port { int inputId; - void draw(NVGcontext *vg); void onDragStart(); void onDragDrop(Widget *origin); }; @@ -192,7 +188,6 @@ struct InputPort : Port { struct OutputPort : Port { int outputId; - void draw(NVGcontext *vg); void onDragStart(); void onDragDrop(Widget *origin); }; @@ -220,5 +215,41 @@ struct Scene : OpaqueWidget { void step(); }; +//////////////////// +// component library +//////////////////// + +struct PJ301M : SpriteWidget { + PJ301M() { + box.size = Vec(24, 24); + spriteOffset = Vec(-10, -10); + spriteSize = Vec(48, 48); + spriteFilename = "res/ComponentLibrary/PJ301M.png"; + } +}; +struct InputPortPJ301M : InputPort, PJ301M {}; +struct OutputPortPJ301M: OutputPort, PJ301M {}; + +struct PJ3410 : SpriteWidget { + PJ3410() { + box.size = Vec(31, 31); + spriteOffset = Vec(-9, -9); + spriteSize = Vec(54, 54); + spriteFilename = "res/ComponentLibrary/PJ3410.png"; + } +}; +struct InputPortPJ3410 : InputPort, PJ3410 {}; +struct OutputPortPJ3410: OutputPort, PJ3410 {}; + +struct CL1362 : SpriteWidget { + CL1362() { + box.size = Vec(33, 29); + spriteOffset = Vec(-10, -10); + spriteSize = Vec(57, 54); + spriteFilename = "res/ComponentLibrary/CL1362.png"; + } +}; +struct InputPortCL1362 : InputPort, CL1362 {}; +struct OutputPortCL1362 : OutputPort, CL1362 {}; } // namespace rack diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index d709ffe9..80bffdb4 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -45,10 +45,11 @@ struct AudioInterface : Module { SampleRateConverter<2> inputSrc; SampleRateConverter<2> outputSrc; - // in device's sample rate - DoubleRingBuffer, (1<<15)> inputBuffer; // in rack's sample rate + DoubleRingBuffer, 32> inputBuffer; DoubleRingBuffer, (1<<15)> outputBuffer; + // in device's sample rate + DoubleRingBuffer, (1<<15)> inputSrcBuffer; AudioInterface(); ~AudioInterface(); @@ -89,29 +90,35 @@ void AudioInterface::step() { // Get input and pass it through the sample rate converter if (numOutputs > 0) { - Frame<2> f; - f.samples[0] = getf(inputs[AUDIO1_INPUT]) / 5.0; - f.samples[1] = getf(inputs[AUDIO2_INPUT]) / 5.0; - - inputSrc.setRatio(sampleRate / gRack->sampleRate); - int inLen = 1; - int outLen = inputBuffer.capacity(); - inputSrc.process((const float*) &f, &inLen, (float*) inputBuffer.endData(), &outLen); - inputBuffer.endIncr(outLen); + if (!inputBuffer.full()) { + Frame<2> f; + f.samples[0] = getf(inputs[AUDIO1_INPUT]) / 5.0; + f.samples[1] = getf(inputs[AUDIO2_INPUT]) / 5.0; + inputBuffer.push(f); + } + + // Once full, sample rate convert the input + if (inputBuffer.full()) { + inputSrc.setRatio(sampleRate / gRack->sampleRate); + int inLen = inputBuffer.size(); + int outLen = inputSrcBuffer.capacity(); + inputSrc.process((const float*) inputBuffer.startData(), &inLen, (float*) inputSrcBuffer.endData(), &outLen); + inputBuffer.startIncr(inLen); + inputSrcBuffer.endIncr(outLen); + } } - // Read/write stream if we have enough input - bool streamReady = (numOutputs > 0) ? (inputBuffer.size() >= blockSize) : (outputBuffer.empty()); + // Read/write stream if we have enough input, OR the output buffer is empty if we have no input + bool streamReady = (numOutputs > 0) ? (inputSrcBuffer.size() >= blockSize) : (outputBuffer.empty()); if (streamReady) { + // printf("%p\t%d\t%d\n", this, inputSrcBuffer.size(), outputBuffer.size()); PaError err; // Read output from input stream // (for some reason, if you write the output stream before you read the input stream, PortAudio can segfault on Windows.) if (numInputs > 0) { Frame<2> *buf = new Frame<2>[blockSize]; - printf("read %d\n", blockSize); err = Pa_ReadStream(stream, (float*) buf, blockSize); - printf("read done\n"); if (err) { // Ignore buffer underflows if (err != paInputOverflowed) { @@ -131,11 +138,9 @@ void AudioInterface::step() { // Write input to output stream if (numOutputs > 0) { - assert(inputBuffer.size() >= blockSize); - printf("write %d\n", blockSize); - err = Pa_WriteStream(stream, (const float*) inputBuffer.startData(), blockSize); - printf("write done\n"); - inputBuffer.startIncr(blockSize); + assert(inputSrcBuffer.size() >= blockSize); + err = Pa_WriteStream(stream, (const float*) inputSrcBuffer.startData(), blockSize); + inputSrcBuffer.startIncr(blockSize); if (err) { // Ignore buffer underflows if (err != paOutputUnderflowed) { @@ -241,6 +246,7 @@ void AudioInterface::closeDevice() { // Clear buffers inputBuffer.clear(); outputBuffer.clear(); + inputSrcBuffer.clear(); inputSrc.reset(); outputSrc.reset(); } @@ -355,6 +361,14 @@ struct BlockSizeChoice : ChoiceButton { AudioInterfaceWidget::AudioInterfaceWidget() : ModuleWidget(new AudioInterface()) { box.size = Vec(15*8, 380); + { + ModulePanel *panel = new ModulePanel(); + panel->box.size = box.size; + panel->backgroundColor = nvgRGBf(0.90, 0.90, 0.90); + // panel->imageFilename = ""; + addChild(panel); + } + float margin = 5; float yPos = margin; @@ -420,8 +434,8 @@ AudioInterfaceWidget::AudioInterfaceWidget() : ModuleWidget(new AudioInterface() } yPos += 5; - addInput(createInput(Vec(25, yPos), module, AudioInterface::AUDIO1_INPUT)); - addInput(createInput(Vec(75, yPos), module, AudioInterface::AUDIO2_INPUT)); + addInput(createInput(Vec(20, yPos), module, AudioInterface::AUDIO1_INPUT)); + addInput(createInput(Vec(70, yPos), module, AudioInterface::AUDIO2_INPUT)); yPos += 35 + margin; { @@ -433,12 +447,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() : ModuleWidget(new AudioInterface() } yPos += 5; - addOutput(createOutput(Vec(25, yPos), module, AudioInterface::AUDIO1_OUTPUT)); - addOutput(createOutput(Vec(75, yPos), module, AudioInterface::AUDIO2_OUTPUT)); + addOutput(createOutput(Vec(20, yPos), module, AudioInterface::AUDIO1_OUTPUT)); + addOutput(createOutput(Vec(70, yPos), module, AudioInterface::AUDIO2_OUTPUT)); yPos += 35 + margin; } - -void AudioInterfaceWidget::draw(NVGcontext *vg) { - bndBackground(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); - ModuleWidget::draw(vg); -} diff --git a/src/core/MidiInterface.cpp b/src/core/MidiInterface.cpp index 0c72504d..ebbb3bd2 100644 --- a/src/core/MidiInterface.cpp +++ b/src/core/MidiInterface.cpp @@ -224,6 +224,14 @@ struct MidiChoice : ChoiceButton { MidiInterfaceWidget::MidiInterfaceWidget() : ModuleWidget(new MidiInterface()) { box.size = Vec(15*8, 380); + { + ModulePanel *panel = new ModulePanel(); + panel->box.size = box.size; + panel->backgroundColor = nvgRGBf(0.90, 0.90, 0.90); + // panel->imageFilename = ""; + addChild(panel); + } + float margin = 5; float yPos = margin; @@ -246,8 +254,8 @@ MidiInterfaceWidget::MidiInterfaceWidget() : ModuleWidget(new MidiInterface()) { } yPos += 5; - addOutput(createOutput(Vec(25, yPos), module, MidiInterface::PITCH_OUTPUT)); - addOutput(createOutput(Vec(75, yPos), module, MidiInterface::GATE_OUTPUT)); + addOutput(createOutput(Vec(20, yPos), module, MidiInterface::PITCH_OUTPUT)); + addOutput(createOutput(Vec(70, yPos), module, MidiInterface::GATE_OUTPUT)); yPos += 25 + margin; { @@ -264,8 +272,3 @@ MidiInterfaceWidget::MidiInterfaceWidget() : ModuleWidget(new MidiInterface()) { yPos += pitchLabel->box.size.y + margin; } } - -void MidiInterfaceWidget::draw(NVGcontext *vg) { - bndBackground(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); - ModuleWidget::draw(vg); -} diff --git a/src/core/core.hpp b/src/core/core.hpp index 179a15dd..434b7bc5 100644 --- a/src/core/core.hpp +++ b/src/core/core.hpp @@ -14,10 +14,8 @@ void midiInit(); struct AudioInterfaceWidget : ModuleWidget { AudioInterfaceWidget(); - void draw(NVGcontext *vg); }; struct MidiInterfaceWidget : ModuleWidget { MidiInterfaceWidget(); - void draw(NVGcontext *vg); }; diff --git a/src/widgets/InputPort.cpp b/src/widgets/InputPort.cpp index 04c7c931..f91a8900 100644 --- a/src/widgets/InputPort.cpp +++ b/src/widgets/InputPort.cpp @@ -3,13 +3,6 @@ namespace rack { -void InputPort::draw(NVGcontext *vg) { - SpriteWidget::draw(vg); - if (gRackWidget->activeWire && gRackWidget->activeWire->inputPort) { - Port::drawGlow(vg); - } -} - void InputPort::onDragStart() { if (connectedWire) { // Disconnect wire from this port, but set it as the active wire diff --git a/src/widgets/ModulePanel.cpp b/src/widgets/ModulePanel.cpp index 9b98a322..c5356c75 100644 --- a/src/widgets/ModulePanel.cpp +++ b/src/widgets/ModulePanel.cpp @@ -8,12 +8,8 @@ void ModulePanel::draw(NVGcontext *vg) { nvgRect(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); NVGpaint paint; - // Background gradient - Vec c = box.pos; - float length = box.size.norm(); - paint = nvgRadialGradient(vg, c.x, c.y, 0.0, length, highlightColor, backgroundColor); - nvgFillPaint(vg, paint); - // nvgFillColor(vg, backgroundColor); + // Background color + nvgFillColor(vg, backgroundColor); nvgFill(vg); // Background image diff --git a/src/widgets/OutputPort.cpp b/src/widgets/OutputPort.cpp index 52a69c75..64e8586f 100644 --- a/src/widgets/OutputPort.cpp +++ b/src/widgets/OutputPort.cpp @@ -3,13 +3,6 @@ namespace rack { -void OutputPort::draw(NVGcontext *vg) { - SpriteWidget::draw(vg); - if (gRackWidget->activeWire && gRackWidget->activeWire->outputPort) { - Port::drawGlow(vg); - } -} - void OutputPort::onDragStart() { if (connectedWire) { // Disconnect wire from this port, but set it as the active wire diff --git a/src/widgets/Port.cpp b/src/widgets/Port.cpp index 8d5608e5..2cf80cc1 100644 --- a/src/widgets/Port.cpp +++ b/src/widgets/Port.cpp @@ -5,11 +5,6 @@ namespace rack { Port::Port() { box.size = Vec(20, 20); - spriteOffset = Vec(-18, -18); - spriteSize = Vec(56, 56); - spriteFilename = "res/port.png"; - - index = randomu32() % 5; } Port::~Port() { @@ -24,17 +19,6 @@ void Port::disconnect() { } } -void Port::drawGlow(NVGcontext *vg) { - Vec c = box.getCenter(); - NVGcolor icol = nvgRGBAf(1, 1, 1, 0.5); - NVGcolor ocol = nvgRGBAf(1, 1, 1, 0); - NVGpaint paint = nvgRadialGradient(vg, c.x, c.y, 0, 20, icol, ocol); - nvgFillPaint(vg, paint); - nvgBeginPath(vg); - nvgRect(vg, box.pos.x - 10, box.pos.y - 10, box.size.x + 20, box.size.y + 20); - nvgFill(vg); -} - void Port::onMouseDown(int button) { if (button == 1) { disconnect(); diff --git a/src/widgets/WireWidget.cpp b/src/widgets/WireWidget.cpp index 503a65f2..1b240862 100644 --- a/src/widgets/WireWidget.cpp +++ b/src/widgets/WireWidget.cpp @@ -3,37 +3,79 @@ namespace rack { -void drawWire(NVGcontext *vg, Vec pos1, Vec pos2, float tension, NVGcolor color) { - float dist = pos1.minus(pos2).norm(); - Vec slump; - slump.y = (1.0 - tension) * (150.0 + 1.0*dist); - Vec pos3 = pos1.plus(pos2).div(2).plus(slump); +void drawWire(NVGcontext *vg, Vec pos1, Vec pos2, float tension, NVGcolor color, float opacity) { + NVGcolor colorShadow = nvgRGBAf(0, 0, 0, 0.08); + NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5); + + + // Wire + if (opacity > 0.0) { + nvgSave(vg); + nvgGlobalAlpha(vg, opacity); + + float dist = pos1.minus(pos2).norm(); + Vec slump; + slump.y = (1.0 - tension) * (150.0 + 1.0*dist); + Vec pos3 = pos1.plus(pos2).div(2).plus(slump); + + nvgLineJoin(vg, NVG_ROUND); + + // Shadow + Vec pos4 = pos3.plus(slump.mult(0.08)); + nvgBeginPath(vg); + nvgMoveTo(vg, pos1.x, pos1.y); + nvgQuadTo(vg, pos4.x, pos4.y, pos2.x, pos2.y); + nvgStrokeColor(vg, colorShadow); + nvgStrokeWidth(vg, 5); + nvgStroke(vg); + + // Wire outline + nvgBeginPath(vg); + nvgMoveTo(vg, pos1.x, pos1.y); + nvgQuadTo(vg, pos3.x, pos3.y, pos2.x, pos2.y); + nvgStrokeColor(vg, colorOutline); + nvgStrokeWidth(vg, 6); + nvgStroke(vg); + + // Wire solid + nvgStrokeColor(vg, color); + nvgStrokeWidth(vg, 4); + nvgStroke(vg); + + nvgRestore(vg); + } + + // First plug + nvgBeginPath(vg); + nvgCircle(vg, pos1.x, pos1.y, 21/2.0); + nvgFillColor(vg, colorOutline); + nvgFill(vg); - nvgLineJoin(vg, NVG_ROUND); + nvgBeginPath(vg); + nvgCircle(vg, pos1.x, pos1.y, 19/2.0); + nvgFillColor(vg, color); + nvgFill(vg); - // Shadow - Vec pos4 = pos3.plus(slump.mult(0.08)); - NVGcolor colorShadow = nvgRGBAf(0, 0, 0, 0.08); nvgBeginPath(vg); - nvgMoveTo(vg, pos1.x, pos1.y); - nvgQuadTo(vg, pos4.x, pos4.y, pos2.x, pos2.y); - nvgStrokeColor(vg, colorShadow); - nvgStrokeWidth(vg, 5); - nvgStroke(vg); - - // Wire outline - NVGcolor colorOutline = nvgRGBf(0, 0, 0); + nvgCircle(vg, pos1.x, pos1.y, 11/2.0); + nvgFillColor(vg, nvgRGBf(0.0, 0.0, 0.0)); + nvgFill(vg); + + // Second plug + nvgBeginPath(vg); + nvgCircle(vg, pos2.x, pos2.y, 21/2.0); + nvgFillColor(vg, colorOutline); + nvgFill(vg); + nvgBeginPath(vg); - nvgMoveTo(vg, pos1.x, pos1.y); - nvgQuadTo(vg, pos3.x, pos3.y, pos2.x, pos2.y); - nvgStrokeColor(vg, colorOutline); - nvgStrokeWidth(vg, 4); - nvgStroke(vg); - - // Wire solid - nvgStrokeColor(vg, color); - nvgStrokeWidth(vg, 2); - nvgStroke(vg); + nvgCircle(vg, pos2.x, pos2.y, 19/2.0); + nvgFillColor(vg, color); + nvgFill(vg); + + nvgBeginPath(vg); + nvgCircle(vg, pos2.x, pos2.y, 11/2.0); + nvgFillColor(vg, nvgRGBf(0.0, 0.0, 0.0)); + nvgFill(vg); } @@ -87,35 +129,29 @@ void WireWidget::updateWire() { void WireWidget::draw(NVGcontext *vg) { Vec outputPos, inputPos; Vec absolutePos = getAbsolutePos(); - float wireOpacity = gScene->toolbar->wireOpacitySlider->value / 100.0; + float opacity = gScene->toolbar->wireOpacitySlider->value / 100.0; + // Compute location of pos1 and pos2 if (outputPort) { outputPos = Rect(outputPort->getAbsolutePos(), outputPort->box.size).getCenter(); } else { outputPos = gMousePos; - wireOpacity = 1.0; + opacity = 1.0; } if (inputPort) { inputPos = Rect(inputPort->getAbsolutePos(), inputPort->box.size).getCenter(); } else { inputPos = gMousePos; - wireOpacity = 1.0; + opacity = 1.0; } outputPos = outputPos.minus(absolutePos); inputPos = inputPos.minus(absolutePos); - bndNodePort(vg, outputPos.x, outputPos.y, BND_DEFAULT, color); - bndNodePort(vg, inputPos.x, inputPos.y, BND_DEFAULT, color); - nvgSave(vg); - if (wireOpacity > 0.0) { - nvgGlobalAlpha(vg, wireOpacity); - float tension = gScene->toolbar->wireTensionSlider->value; - drawWire(vg, outputPos, inputPos, tension, color); - } - nvgRestore(vg); + float tension = gScene->toolbar->wireTensionSlider->value; + drawWire(vg, outputPos, inputPos, tension, color, opacity); }