@@ -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 |
@@ -0,0 +1 @@ | |||
Subproject commit dc12d90586a8ab99da0c575aafff999666aa5d55 |
@@ -166,18 +166,18 @@ ParamWidget *createParam(Vec pos, Module *module, int paramId, float minValue, f | |||
return param; | |||
} | |||
inline | |||
template <class TInputPort> | |||
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 <class TOutputPort> | |||
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; | |||
@@ -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 |
@@ -45,10 +45,11 @@ struct AudioInterface : Module { | |||
SampleRateConverter<2> inputSrc; | |||
SampleRateConverter<2> outputSrc; | |||
// in device's sample rate | |||
DoubleRingBuffer<Frame<2>, (1<<15)> inputBuffer; | |||
// in rack's sample rate | |||
DoubleRingBuffer<Frame<2>, 32> inputBuffer; | |||
DoubleRingBuffer<Frame<2>, (1<<15)> outputBuffer; | |||
// in device's sample rate | |||
DoubleRingBuffer<Frame<2>, (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<InputPortPJ3410>(Vec(20, yPos), module, AudioInterface::AUDIO1_INPUT)); | |||
addInput(createInput<InputPortPJ3410>(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<OutputPortPJ3410>(Vec(20, yPos), module, AudioInterface::AUDIO1_OUTPUT)); | |||
addOutput(createOutput<OutputPortPJ3410>(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); | |||
} |
@@ -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<OutputPortPJ3410>(Vec(20, yPos), module, MidiInterface::PITCH_OUTPUT)); | |||
addOutput(createOutput<OutputPortPJ3410>(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); | |||
} |
@@ -14,10 +14,8 @@ void midiInit(); | |||
struct AudioInterfaceWidget : ModuleWidget { | |||
AudioInterfaceWidget(); | |||
void draw(NVGcontext *vg); | |||
}; | |||
struct MidiInterfaceWidget : ModuleWidget { | |||
MidiInterfaceWidget(); | |||
void draw(NVGcontext *vg); | |||
}; |
@@ -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 | |||
@@ -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 | |||
@@ -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 | |||
@@ -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(); | |||
@@ -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); | |||
} | |||