| @@ -54,13 +54,13 @@ struct Braids : Module { | |||
| configInput(COLOR_INPUT, "Color"); | |||
| configOutput(OUT_OUTPUT, "Audio"); | |||
| memset(&osc, 0, sizeof(osc)); | |||
| std::memset(&osc, 0, sizeof(osc)); | |||
| osc.Init(); | |||
| memset(&jitter_source, 0, sizeof(jitter_source)); | |||
| std::memset(&jitter_source, 0, sizeof(jitter_source)); | |||
| jitter_source.Init(); | |||
| memset(&ws, 0, sizeof(ws)); | |||
| std::memset(&ws, 0, sizeof(ws)); | |||
| ws.Init(0x0000); | |||
| memset(&settings, 0, sizeof(settings)); | |||
| std::memset(&settings, 0, sizeof(settings)); | |||
| // List of supported settings | |||
| settings.meta_modulation = 0; | |||
| @@ -81,9 +81,9 @@ struct Braids : Module { | |||
| float fm = params[FM_PARAM].getValue() * inputs[FM_INPUT].getVoltage(); | |||
| // Set shape | |||
| int shape = roundf(params[SHAPE_PARAM].getValue() * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); | |||
| int shape = std::round(params[SHAPE_PARAM].getValue() * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); | |||
| if (settings.meta_modulation) { | |||
| shape += roundf(fm / 10.0 * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); | |||
| shape += std::round(fm / 10.0 * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); | |||
| } | |||
| settings.shape = clamp(shape, 0, braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); | |||
| @@ -102,7 +102,7 @@ struct Braids : Module { | |||
| if (!settings.meta_modulation) | |||
| pitchV += fm; | |||
| if (lowCpu) | |||
| pitchV += log2f(96000.f * args.sampleTime); | |||
| pitchV += std::log2(96000.f * args.sampleTime); | |||
| int32_t pitch = (pitchV * 12.0 + 60) * 128; | |||
| pitch += jitter_source.Render(settings.vco_drift); | |||
| pitch = clamp(pitch, 0, 16383); | |||
| @@ -184,62 +184,73 @@ struct Braids : Module { | |||
| lowCpu = json_boolean_value(lowCpuJ); | |||
| } | |||
| } | |||
| int getShapeParam() { | |||
| return std::round(params[SHAPE_PARAM].getValue() * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); | |||
| } | |||
| void setShapeParam(int shape) { | |||
| params[SHAPE_PARAM].setValue(shape / (float) braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); | |||
| } | |||
| }; | |||
| struct ShapeInfo { | |||
| std::string code; | |||
| std::string label; | |||
| }; | |||
| static const char* algo_values[] = { | |||
| "CSAW", | |||
| "/\\-_", | |||
| "//-_", | |||
| "FOLD", | |||
| "uuuu", | |||
| "SUB-", | |||
| "SUB/", | |||
| "SYN-", | |||
| "SYN/", | |||
| "//x3", | |||
| "-_x3", | |||
| "/\\x3", | |||
| "SIx3", | |||
| "RING", | |||
| "////", | |||
| "//uu", | |||
| "TOY*", | |||
| "ZLPF", | |||
| "ZPKF", | |||
| "ZBPF", | |||
| "ZHPF", | |||
| "VOSM", | |||
| "VOWL", | |||
| "VFOF", | |||
| "HARM", | |||
| "FM ", | |||
| "FBFM", | |||
| "WTFM", | |||
| "PLUK", | |||
| "BOWD", | |||
| "BLOW", | |||
| "FLUT", | |||
| "BELL", | |||
| "DRUM", | |||
| "KICK", | |||
| "CYMB", | |||
| "SNAR", | |||
| "WTBL", | |||
| "WMAP", | |||
| "WLIN", | |||
| "WTx4", | |||
| "NOIS", | |||
| "TWNQ", | |||
| "CLKN", | |||
| "CLOU", | |||
| "PRTC", | |||
| "QPSK", | |||
| " ", | |||
| static const std::vector<ShapeInfo> SHAPE_INFOS = { | |||
| {"CSAW", "Quirky sawtooth"}, | |||
| {"/\\-_", "Triangle to saw"}, | |||
| {"//-_", "Sawtooth wave with dephasing"}, | |||
| {"FOLD", "Wavefolded sine/triangle"}, | |||
| {"uuuu", "Buzz"}, | |||
| {"SUB-", "Square sub"}, | |||
| {"SUB/", "Saw sub"}, | |||
| {"SYN-", "Square sync"}, | |||
| {"SYN/", "Saw sync"}, | |||
| {"//x3", "Triple saw"}, | |||
| {"-_x3", "Triple square"}, | |||
| {"/\\x3", "Triple triangle"}, | |||
| {"SIx3", "Triple sine"}, | |||
| {"RING", "Triple ring mod"}, | |||
| {"////", "Saw swarm"}, | |||
| {"//uu", "Saw comb"}, | |||
| {"TOY*", "Circuit-bent toy"}, | |||
| {"ZLPF", "Low-pass filtered waveform"}, | |||
| {"ZPKF", "Peak filtered waveform"}, | |||
| {"ZBPF", "Band-pass filtered waveform"}, | |||
| {"ZHPF", "High-pass filtered waveform"}, | |||
| {"VOSM", "VOSIM formant"}, | |||
| {"VOWL", "Speech synthesis"}, | |||
| {"VFOF", "FOF speech synthesis"}, | |||
| {"HARM", "12 sine harmonics"}, | |||
| {"FM ", "2-operator phase-modulation"}, | |||
| {"FBFM", "2-operator phase-modulation with feedback"}, | |||
| {"WTFM", "2-operator phase-modulation with chaotic feedback"}, | |||
| {"PLUK", "Plucked string"}, | |||
| {"BOWD", "Bowed string"}, | |||
| {"BLOW", "Blown reed"}, | |||
| {"FLUT", "Flute"}, | |||
| {"BELL", "Bell"}, | |||
| {"DRUM", "Drum"}, | |||
| {"KICK", "Kick drum circuit simulation"}, | |||
| {"CYMB", "Cymbal"}, | |||
| {"SNAR", "Snare"}, | |||
| {"WTBL", "Wavetable"}, | |||
| {"WMAP", "2D wavetable"}, | |||
| {"WLIN", "1D wavetable"}, | |||
| {"WTx4", "4-voice paraphonic 1D wavetable"}, | |||
| {"NOIS", "Filtered noise"}, | |||
| {"TWNQ", "Twin peaks noise"}, | |||
| {"CLKN", "Clocked noise"}, | |||
| {"CLOU", "Granular cloud"}, | |||
| {"PRTC", "Particle noise"}, | |||
| {"QPSK", "Digital modulation"}, | |||
| }; | |||
| struct BraidsDisplay : TransparentWidget { | |||
| Braids* module; | |||
| std::shared_ptr<Font> font; | |||
| @@ -271,35 +282,10 @@ struct BraidsDisplay : TransparentWidget { | |||
| Vec textPos = Vec(11, 47); | |||
| NVGcolor textColor = nvgRGB(0xaf, 0xd2, 0x2c); | |||
| nvgFillColor(args.vg, nvgTransRGBA(textColor, 16)); | |||
| // Background of all segments | |||
| nvgText(args.vg, textPos.x, textPos.y, "~~~~", NULL); | |||
| nvgFillColor(args.vg, textColor); | |||
| nvgText(args.vg, textPos.x, textPos.y, algo_values[shape], NULL); | |||
| } | |||
| }; | |||
| struct BraidsSettingItem : MenuItem { | |||
| uint8_t* setting = NULL; | |||
| uint8_t offValue = 0; | |||
| uint8_t onValue = 1; | |||
| void onAction(const event::Action& e) override { | |||
| // Toggle setting | |||
| *setting = (*setting == onValue) ? offValue : onValue; | |||
| } | |||
| void step() override { | |||
| rightText = (*setting == onValue) ? "✔" : ""; | |||
| MenuItem::step(); | |||
| } | |||
| }; | |||
| struct BraidsLowCpuItem : MenuItem { | |||
| Braids* braids; | |||
| void onAction(const event::Action& e) override { | |||
| braids->lowCpu = !braids->lowCpu; | |||
| } | |||
| void step() override { | |||
| rightText = (braids->lowCpu) ? "✔" : ""; | |||
| MenuItem::step(); | |||
| nvgText(args.vg, textPos.x, textPos.y, SHAPE_INFOS[shape].code.c_str(), NULL); | |||
| } | |||
| }; | |||
| @@ -309,14 +295,6 @@ struct BraidsWidget : ModuleWidget { | |||
| setModule(module); | |||
| setPanel(Svg::load(asset::plugin(pluginInstance, "res/Braids.svg"))); | |||
| { | |||
| BraidsDisplay* display = new BraidsDisplay(); | |||
| display->box.pos = Vec(14, 53); | |||
| display->box.size = Vec(148, 56); | |||
| display->module = module; | |||
| addChild(display); | |||
| } | |||
| addChild(createWidget<ScrewSilver>(Vec(15, 0))); | |||
| addChild(createWidget<ScrewSilver>(Vec(210, 0))); | |||
| addChild(createWidget<ScrewSilver>(Vec(15, 365))); | |||
| @@ -338,6 +316,12 @@ struct BraidsWidget : ModuleWidget { | |||
| addInput(createInput<PJ301MPort>(Vec(122, 316), module, Braids::TIMBRE_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(160, 316), module, Braids::COLOR_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(205, 316), module, Braids::OUT_OUTPUT)); | |||
| BraidsDisplay* display = new BraidsDisplay(); | |||
| display->box.pos = Vec(14, 53); | |||
| display->box.size = Vec(148, 56); | |||
| display->module = module; | |||
| addChild(display); | |||
| } | |||
| void appendContextMenu(Menu* menu) override { | |||
| @@ -345,6 +329,15 @@ struct BraidsWidget : ModuleWidget { | |||
| menu->addChild(new MenuSeparator); | |||
| std::vector<std::string> shapeLabels; | |||
| for (const ShapeInfo& s : SHAPE_INFOS) { | |||
| shapeLabels.push_back(s.label + " (" + s.code + ")"); | |||
| } | |||
| menu->addChild(createIndexSubmenuItem("Model", shapeLabels, | |||
| [=]() {return module->getShapeParam();}, | |||
| [=](int i) {module->setShapeParam(i);} | |||
| )); | |||
| menu->addChild(createBoolPtrMenuItem("FM CV selects model (META)", &module->settings.meta_modulation)); | |||
| menu->addChild(createBoolMenuItem("Pitch drift (DRFT)", | |||