From 2ec5bed1c38a69ce5d8993c156ee584cb6944252 Mon Sep 17 00:00:00 2001 From: klknn <57452864+klknn@users.noreply.github.com> Date: Thu, 29 Dec 2022 15:44:24 +0900 Subject: [PATCH 1/5] Update rate label UI when parameter is changed. --- source/kdr/envtool/gui.d | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source/kdr/envtool/gui.d b/source/kdr/envtool/gui.d index f7f531a..5a681f4 100644 --- a/source/kdr/envtool/gui.d +++ b/source/kdr/envtool/gui.d @@ -267,7 +267,9 @@ unittest { } /// -class EnvToolGUI : PBRBackgroundGUI!(png1, png2, png3, png3, png3, "") { +class EnvToolGUI : + PBRBackgroundGUI!(png1, png2, png3, png3, png3, ""), + IParameterListener { public: @nogc nothrow: this(Parameter[] params) { @@ -287,6 +289,7 @@ class EnvToolGUI : PBRBackgroundGUI!(png1, png2, png3, png3, png3, "") { _rateKnob = buildKnob(Params.rate); _rateLabel = buildLabel("rate"); + _params[Params.rate].addListener(this); _depthKnob = buildKnob(Params.depth); _depthLabel = buildLabel("depth"); @@ -342,6 +345,24 @@ class EnvToolGUI : PBRBackgroundGUI!(png1, png2, png3, png3, png3, "") { hintSize, hintSize); } + override void onParameterChanged(Parameter sender) { + if (sender.index == Params.rate) { + logDebug("rate changed"); + if (EnumParameter rate = cast(EnumParameter) sender) { + logDebug("rate casted"); + _rateLabel.text(rateLabels[rate.value]); + } + } + } + + override void onBeginParameterEdit(Parameter sender) {} + + override void onEndParameterEdit(Parameter sender) {} + + override void onBeginParameterHover(Parameter sender) {} + + override void onEndParameterHover(Parameter sender) {} + private: UIKnob buildKnob(Params pid) { UIKnob knob; From 8d018645ebcb7363f2056491470a8433e2d3b902 Mon Sep 17 00:00:00 2001 From: klknn <57452864+klknn@users.noreply.github.com> Date: Thu, 29 Dec 2022 16:49:33 +0900 Subject: [PATCH 2/5] Add filter mod in envtool --- source/kdr/envtool/client.d | 22 ++++++++++++++++++++-- source/kdr/envtool/params.d | 29 ++++++++++++++++++++++++----- source/kdr/filter.d | 5 +++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/source/kdr/envtool/client.d b/source/kdr/envtool/client.d index e5f1b12..ba454c2 100644 --- a/source/kdr/envtool/client.d +++ b/source/kdr/envtool/client.d @@ -9,6 +9,7 @@ import dplug.core; import kdr.envelope : Envelope; import kdr.envtool.gui : EnvToolGUI; import kdr.envtool.params; +import kdr.filter; import kdr.logging : logInfo; /// Env tool client. @@ -44,20 +45,36 @@ class EnvToolClient : Client { override void reset( double sampleRate, int maxFrames, int numInputs, int numOutputs) { _sampleRate = sampleRate; + foreach (ref f; _filter) f.setSampleRate(sampleRate); } override void processAudio( const(float*)[] inputs, float*[] outputs, int frames, TimeInfo info) { + const Destination dst = readParam!Destination(Params.destination); + + // Setup rate. const Envelope env = buildEnvelope(params); const double beatScale = rateValues[readParam!int(Params.rate)] * 4; const float depth = readParam!float(Params.depth); const double beatPerSample = info.tempo / 60 / _sampleRate; + + // Setup filter. + const fkind = readParam!FilterKind(Params.filterKind); + const fcutoff = readParam!float(Params.filterCutoff); + const fres = readParam!float(Params.filterRes); + foreach (c; 0 .. inputs.length) { float offset = c == 0 ? 0 : readParam!float(Params.stereoOffset); foreach (t; 0 .. frames) { const double beats = (info.timeInSamples + t) * beatPerSample; - const float e = env.getY((beats / beatScale + offset) % 1.0); - outputs[c][t] = (depth * e + 1.0 - depth) * inputs[c][t]; + float e = env.getY((beats / beatScale + offset) % 1.0); + e = depth * e + 1.0 - depth; + if (dst == Destination.volume) { + outputs[c][t] = e * inputs[c][t]; + } else if (dst == Destination.filterCutoff) { + _filter[c].setParams(fkind, e * fcutoff, fres); + outputs[c][t] = _filter[c].apply(inputs[c][t]); + } } } } @@ -65,4 +82,5 @@ class EnvToolClient : Client { private: EnvToolGUI _gui; double _sampleRate; + Filter[2] _filter; } diff --git a/source/kdr/envtool/params.d b/source/kdr/envtool/params.d index 7c71b53..6720cbe 100644 --- a/source/kdr/envtool/params.d +++ b/source/kdr/envtool/params.d @@ -5,6 +5,18 @@ import dplug.core; import dplug.client; import kdr.envelope; +import kdr.filter; + +/// Envelope mod destination. +enum Destination { + volume, + pan, + filterCutoff, +} + +/// String names of destinations. +static immutable destinationNames = [__traits(allMembers, Destination)]; + /// Parameter for EnvToolClient. enum Params { @@ -13,11 +25,10 @@ enum Params { rate, depth, stereoOffset, - // volumeMod, - // filterMod, - // filterMode, - // filterCutoff, - // filterRes, + destination, + filterKind, + filterCutoff, + filterRes, } /// Used by the "rate" param. @@ -58,6 +69,14 @@ Parameter[] buildEnvelopeParameters() { params.pushBack(mallocNew!LinearFloatParameter(n++, "depth", "", 0.0, 1.0, 1.0)); assert(n == Params.stereoOffset); params.pushBack(mallocNew!LinearFloatParameter(n++, "stereoOffset", "", -1, 1, 0.0)); + assert(n == Params.destination); + params.pushBack(mallocNew!EnumParameter(n++, "destination", destinationNames, Destination.volume)); + assert(n == Params.filterKind); + params.pushBack(mallocNew!EnumParameter(n++, "filterKind", filterNames, FilterKind.none)); + assert(n == Params.filterCutoff); + params.pushBack(mallocNew!LinearFloatParameter(n++, "filterCutoff", "", 0, 1, 0.5)); + assert(n == Params.filterRes); + params.pushBack(mallocNew!LinearFloatParameter(n++, "filterRes", "", 0, 1, 0.5)); return params.releaseData(); } diff --git a/source/kdr/filter.d b/source/kdr/filter.d index a20b177..03174cb 100644 --- a/source/kdr/filter.d +++ b/source/kdr/filter.d @@ -16,6 +16,7 @@ import mir.math : approxEqual, PI, SQRT2, fmax; /// Kinds of filter implentations. enum FilterKind { + none, HP6, HP12, BP12, @@ -39,6 +40,8 @@ struct Filter { /// input = input wave frame. /// Returns: filtered wave frame. float apply(float input) { + if (kind == FilterKind.none) return input; + // TODO: use ring buffer foreach_reverse (i; 1 .. nFIR) { x[i] = x[i - 1]; @@ -98,6 +101,8 @@ struct Filter { assert(T != float.nan); assert(w0 != float.nan); final switch (kind) { + case FilterKind.none: + return; mixin(import("filter_coeff.d")); } } From b896f91fd564a9b6147a47337c240016550628f0 Mon Sep 17 00:00:00 2001 From: klknn Date: Fri, 30 Dec 2022 02:15:58 +0900 Subject: [PATCH 3/5] Fix synth2 test --- source/kdr/synth2/client.d | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/kdr/synth2/client.d b/source/kdr/synth2/client.d index 1ac7042..1d3bba3 100644 --- a/source/kdr/synth2/client.d +++ b/source/kdr/synth2/client.d @@ -732,9 +732,11 @@ unittest { scope (exit) destroyFree(host.client); foreach (fkind; EnumMembers!FilterKind) { host.setParam!(Params.filterKind)(fkind); - assert(host.paramChangeOutputs!(Params.filterCutoff)(0.5)); - if (fkind != FilterKind.HP6 && fkind != FilterKind.LP6) { - assert(host.paramChangeOutputs!(Params.filterQ)(0.5)); + if (fkind != FilterKind.none) { + assert(host.paramChangeOutputs!(Params.filterCutoff)(0.5)); + if (fkind != FilterKind.HP6 && fkind != FilterKind.LP6) { + assert(host.paramChangeOutputs!(Params.filterQ)(0.5)); + } } } From da13ef3e826aa1f033f19aa41ab8dda5aba8c963 Mon Sep 17 00:00:00 2001 From: klknn Date: Fri, 30 Dec 2022 04:44:28 +0900 Subject: [PATCH 4/5] Fix UI and filter --- bin/synth2/plugin.json | 4 +- source/kdr/envtool/client.d | 12 +++- source/kdr/envtool/gui.d | 110 +++++++++++++++++++++++++++--------- source/kdr/envtool/params.d | 6 +- 4 files changed, 98 insertions(+), 34 deletions(-) diff --git a/bin/synth2/plugin.json b/bin/synth2/plugin.json index 87943ee..1773324 100644 --- a/bin/synth2/plugin.json +++ b/bin/synth2/plugin.json @@ -3,9 +3,9 @@ "vendorName": "kdr", "vendorUniqueID": "KDR_", "vendorSupportEmail": "klknn.gh@gmail.com", - "pluginName": "synth2", + "pluginName": "kdr-synth2", "pluginHomepage": "https://github.com/klknn/kdr", - "pluginUniqueID": "syn2", + "pluginUniqueID": "ksy2", "publicVersion": "1.0.0", "CFBundleIdentifierPrefix": "com.kdr", "hasGUI": true, diff --git a/source/kdr/envtool/client.d b/source/kdr/envtool/client.d index ba454c2..9e67159 100644 --- a/source/kdr/envtool/client.d +++ b/source/kdr/envtool/client.d @@ -62,19 +62,25 @@ class EnvToolClient : Client { const fkind = readParam!FilterKind(Params.filterKind); const fcutoff = readParam!float(Params.filterCutoff); const fres = readParam!float(Params.filterRes); + foreach (ref f; _filter) f.setParams(fkind, fcutoff, fres); foreach (c; 0 .. inputs.length) { float offset = c == 0 ? 0 : readParam!float(Params.stereoOffset); foreach (t; 0 .. frames) { const double beats = (info.timeInSamples + t) * beatPerSample; float e = env.getY((beats / beatScale + offset) % 1.0); - e = depth * e + 1.0 - depth; if (dst == Destination.volume) { outputs[c][t] = e * inputs[c][t]; - } else if (dst == Destination.filterCutoff) { + } else if (dst == Destination.cutoff) { _filter[c].setParams(fkind, e * fcutoff, fres); - outputs[c][t] = _filter[c].apply(inputs[c][t]); + outputs[c][t] = inputs[c][t]; + } else if (dst == Destination.pan) { + float pan = c == 0 ? e : 1.0 - e; + outputs[c][t] = pan * inputs[c][t]; } + outputs[c][t] = _filter[c].apply(outputs[c][t]); + // mix dry and wet signals. + outputs[c][t] = depth * outputs[c][t] + (1.0 - depth) * inputs[c][t]; } } } diff --git a/source/kdr/envtool/gui.d b/source/kdr/envtool/gui.d index 0f1c7c4..6caab49 100644 --- a/source/kdr/envtool/gui.d +++ b/source/kdr/envtool/gui.d @@ -16,14 +16,15 @@ import dplug.pbrwidgets : UILabel, UIKnob; // : PBRBackgroundGUI; import kdr.envelope : Envelope; import kdr.envtool.params; +import kdr.filter : filterNames; import kdr.simplegui : PBRSimpleGUI; import kdr.logging : logDebug, logInfo; enum RGBA lineColor = RGBA(0, 255, 255, 96); -enum RGBA gradColor = RGBA(0, 32, 32, 96); +enum RGBA gradColor = RGBA(0, 64, 64, 96); enum RGBA gridColor = RGBA(100, 200, 200, 32); enum RGBA darkColor = RGBA(128, 128, 128, 128); -enum RGBA lightColor = RGBA(100, 200, 200, 200); +enum RGBA lightColor = RGBA(100, 200, 200, 100); enum RGBA textColor = RGBA(155, 255, 255, 0); enum RGBA knobColor = RGBA(96, 96, 96, 96); enum RGBA litColor = RGBA(155, 255, 255, 0); @@ -169,6 +170,10 @@ class EnvelopeUI : UIElement, IParameterListener { _canvas.initialize(cropped); _canvas.translate(-rect.min.x, -rect.min.y); + // Draw background. + _canvas.fillStyle = RGBA(0, 32, 32, 200); + _canvas.fillRect(0, 0, position.width, position.height); + // Draw grid. enum float gridWidth = 0.0015; int numGrid = 8; @@ -271,7 +276,7 @@ class EnvToolGUI : PBRSimpleGUI, IParameterListener { logDebug("Initialize %s", __FUNCTION__.ptr); static immutable float[] ratios = [1.0f, 1.25f, 1.5f, 1.75f, 2.0f]; - super(makeSizeConstraintsDiscrete(500, 300, ratios)); + super(makeSizeConstraintsDiscrete(600, 400, ratios)); _params = params; _font = mallocNew!Font(cast(ubyte[]) import("FORCED SQUARE.ttf")); @@ -282,6 +287,7 @@ class EnvToolGUI : PBRSimpleGUI, IParameterListener { addChild(_resizer = mallocNew!UIWindowResizer(context())); addChild(_envui = mallocNew!EnvelopeUI(context(), params)); + // General knobs and labels. _rateKnob = buildKnob(Params.rate); _rateLabel = buildLabel("rate"); _params[Params.rate].addListener(this); @@ -291,6 +297,21 @@ class EnvToolGUI : PBRSimpleGUI, IParameterListener { _stereoOffsetKnob = buildKnob(Params.stereoOffset); _stereoOffsetLabel = buildLabel("offset"); + + _destinationKnob = buildKnob(Params.destination); + _destinationLabel = buildLabel("dst"); + _params[Params.destination].addListener(this); + + // Filter knobs and labels. + _filterKindKnob = buildKnob(Params.filterKind); + _filterKindLabel = buildLabel("filter"); + _params[Params.filterKind].addListener(this); + + _filterCutoffKnob = buildKnob(Params.filterCutoff); + _filterCutoffLabel = buildLabel("cutoff"); + + _filterResKnob = buildKnob(Params.filterRes); + _filterResLabel = buildLabel("q"); } ~this() { @@ -302,27 +323,18 @@ class EnvToolGUI : PBRSimpleGUI, IParameterListener { const int W = position.width; const int H = position.height; - // Main. - _envui.position = rectangle(0, 0, cast(int) (W * 0.8), cast(int) (H * 0.9)); + // Main. 70% + _envui.position = rectangle(0, 0, cast(int) (W * 0.7), cast(int) (H * 0.9)); - _title.position = rectangle(0, _envui.position.max.y, _envui.position.width / 2, - cast(int) (H * 0.1)); - _title.textSize = _title.position.height; - - _date.position = rectangle(_title.position.max.x, - _title.position.min.y + _title.position.height / 2, - _envui.position.width - _title.position.width, - _title.position.height / 2); - _date.textSize = _title.textSize / 2; - - // Knobs. - int knobSize = cast(int) (W * 0.15); - int knobX = cast(int) (W * 0.825); - int labelSize = knobSize / 4; - int labelMargin = labelSize / 4; + // Knobs. 40% + float knobAndLabel = H / 4.0; + int knobSize = cast(int) (knobAndLabel * 0.8); + int knobX = cast(int) (W * 0.725); + int labelSize = cast(int) (knobAndLabel * 0.17); + int labelMargin = cast(int) (knobAndLabel * 0.03); _rateKnob.position = rectangle( - knobX, cast(int) (H * 0.025), knobSize, knobSize); + knobX, labelMargin, knobSize, knobSize); _rateLabel.textSize = labelSize; _rateLabel.position = rectangle( knobX, _rateKnob.position.max.y, knobSize, labelSize); @@ -339,18 +351,62 @@ class EnvToolGUI : PBRSimpleGUI, IParameterListener { _stereoOffsetLabel.position = rectangle( knobX, _stereoOffsetKnob.position.max.y, knobSize, labelSize); - int hintSize = 10; + _destinationKnob.position = rectangle( + knobX, _stereoOffsetLabel.position.max.y, knobSize, knobSize); + _destinationLabel.position = rectangle( + knobX, _destinationKnob.position.max.y, knobSize, labelSize); + + // filter. + knobX += knobSize; + _filterKindKnob.position = rectangle( + knobX, labelMargin, knobSize, knobSize); + _filterKindLabel.textSize = labelSize; + _filterKindLabel.position = rectangle( + knobX, _filterKindKnob.position.max.y, knobSize, labelSize); + + _filterCutoffKnob.position = rectangle( + knobX, _filterKindLabel.position.max.y, knobSize, knobSize); + _filterCutoffLabel.textSize = labelSize; + _filterCutoffLabel.position = rectangle( + knobX, _filterCutoffKnob.position.max.y, knobSize, labelSize); + + _filterResKnob.position = rectangle( + knobX, _filterCutoffLabel.position.max.y, knobSize, knobSize); + _filterResLabel.textSize = labelSize; + _filterResLabel.position = rectangle( + knobX, _filterResKnob.position.max.y, knobSize, labelSize); + + // etc. + _title.position = rectangle(0, _envui.position.max.y, + _envui.position.width * 2 / 3, + cast(int) (H * 0.1)); + _title.textSize = _title.position.height; + + int dateLabelSize = cast(int) _title.textSize / 3; + _date.position = rectangle(_title.position.max.x, + H - dateLabelSize, + _envui.position.width - _title.position.width, + dateLabelSize); + _date.textSize = dateLabelSize; + + int hintSize = H / 20; _resizer.position = rectangle(W - hintSize, H - hintSize, hintSize, hintSize); } override void onParameterChanged(Parameter sender) { if (sender.index == Params.rate) { - logDebug("rate changed"); if (EnumParameter rate = cast(EnumParameter) sender) { - logDebug("rate casted"); _rateLabel.text(rateLabels[rate.value]); } + } else if (sender.index == Params.destination) { + if (EnumParameter dest = cast(EnumParameter) sender) { + _destinationLabel.text(destinationNames[dest.value]); + } + } else if (sender.index == Params.filterKind) { + if (EnumParameter kind = cast(EnumParameter) sender) { + _filterKindLabel.text(filterNames[kind.value]); + } } } @@ -390,8 +446,10 @@ class EnvToolGUI : PBRSimpleGUI, IParameterListener { Parameter[] _params; UIWindowResizer _resizer; EnvelopeUI _envui; - UIKnob _rateKnob, _depthKnob, _stereoOffsetKnob; - UILabel _rateLabel, _depthLabel, _stereoOffsetLabel; + UIKnob _rateKnob, _depthKnob, _stereoOffsetKnob, _destinationKnob, + _filterKindKnob, _filterCutoffKnob, _filterResKnob; + UILabel _rateLabel, _depthLabel, _stereoOffsetLabel, _destinationLabel, + _filterKindLabel, _filterCutoffLabel, _filterResLabel; enum litTrailDiffuse = RGBA(151, 119, 255, 100); enum unlitTrailDiffuse = RGBA(81, 54, 108, 0); diff --git a/source/kdr/envtool/params.d b/source/kdr/envtool/params.d index 6720cbe..6642e8d 100644 --- a/source/kdr/envtool/params.d +++ b/source/kdr/envtool/params.d @@ -11,7 +11,7 @@ import kdr.filter; enum Destination { volume, pan, - filterCutoff, + cutoff, } /// String names of destinations. @@ -74,9 +74,9 @@ Parameter[] buildEnvelopeParameters() { assert(n == Params.filterKind); params.pushBack(mallocNew!EnumParameter(n++, "filterKind", filterNames, FilterKind.none)); assert(n == Params.filterCutoff); - params.pushBack(mallocNew!LinearFloatParameter(n++, "filterCutoff", "", 0, 1, 0.5)); + params.pushBack(mallocNew!LogFloatParameter(n++, "filterCutoff", "", 0.01, 1, 1)); assert(n == Params.filterRes); - params.pushBack(mallocNew!LinearFloatParameter(n++, "filterRes", "", 0, 1, 0.5)); + params.pushBack(mallocNew!LinearFloatParameter(n++, "filterRes", "", 0, 1, 0)); return params.releaseData(); } From 8309d092e8cd4db8a02313d2fe579f052f404e56 Mon Sep 17 00:00:00 2001 From: klknn Date: Fri, 30 Dec 2022 04:54:35 +0900 Subject: [PATCH 5/5] Fix label size of destination. --- source/kdr/envtool/gui.d | 1 + 1 file changed, 1 insertion(+) diff --git a/source/kdr/envtool/gui.d b/source/kdr/envtool/gui.d index 6caab49..db30078 100644 --- a/source/kdr/envtool/gui.d +++ b/source/kdr/envtool/gui.d @@ -355,6 +355,7 @@ class EnvToolGUI : PBRSimpleGUI, IParameterListener { knobX, _stereoOffsetLabel.position.max.y, knobSize, knobSize); _destinationLabel.position = rectangle( knobX, _destinationKnob.position.max.y, knobSize, labelSize); + _destinationLabel.textSize = labelSize; // filter. knobX += knobSize;