10000 Add filter in Envtool by klknn · Pull Request #21 · klknn/kdr · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add filter in Envtool #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bin/synth2/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
28 changes: 26 additions & 2 deletions source/kdr/envtool/client.d
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -44,25 +45,48 @@ 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 (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;
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);
if (dst == Destination.volume) {
outputs[c][t] = e * inputs[c][t];
} else if (dst == Destination.cutoff) {
_filter[c].setParams(fkind, e * fcutoff, fres);
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];
}
}
}

private:
EnvToolGUI _gui;
double _sampleRate;
Filter[2] _filter;
}
128 changes: 103 additions & 25 deletions source/kdr/envtool/gui.d
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -264,14 +269,14 @@ unittest {
}

///
class EnvToolGUI : PBRSimpleGUI {
class EnvToolGUI : PBRSimpleGUI, IParameterListener {
public:
@nogc nothrow:
this(Parameter[] params) {
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"));
Expand All @@ -282,14 +287,31 @@ class EnvToolGUI : PBRSimpleGUI {
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);

_depthKnob = buildKnob(Params.depth);
_depthLabel = buildLabel("depth");

_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() {
Expand All @@ -301,27 +323,18 @@ class EnvToolGUI : PBRSimpleGUI {
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));

_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;
// Main. 70%
_envui.position = rectangle(0, 0, cast(int) (W * 0.7), cast(int) (H * 0.9));

// 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);
Expand All @@ -338,11 +351,74 @@ class EnvToolGUI : PBRSimpleGUI {
_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);
_destinationLabel.textSize = 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(
6DB6 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) {
if (EnumParameter rate = cast(EnumParameter) sender) {
_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]);
}
}
}

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;
Expand Down Expand Up @@ -371,8 +447,10 @@ class EnvToolGUI : PBRSimpleGUI {
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);
Expand Down
29 changes: 24 additions & 5 deletions source/kdr/envtool/params.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ import dplug.core;
import dplug.client;

import kdr.envelope;
import kdr.filter;

/// Envelope mod destination.
enum Destination {
volume,
pan,
cutoff,
}

/// String names of destinations.
static immutable destinationNames = [__traits(allMembers, Destination)];


/// Parameter for EnvToolClient.
enum Params {
Expand All @@ -13,11 +25,10 @@ enum Params {
rate,
depth,
stereoOffset,
// volumeMod,
// filterMod,
// filterMode,
// filterCutoff,
// filterRes,
destination,
filterKind,
filterCutoff,
filterRes,
}

/// Used by the "rate" param.
Expand Down Expand Up @@ -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!LogFloatParameter(n++, "filterCutoff", "", 0.01, 1, 1));
assert(n == Params.filterRes);
params.pushBack(mallocNew!LinearFloatParameter(n++, "filterRes", "", 0, 1, 0));

return params.releaseData();
}
Expand Down
5 changes: 5 additions & 0 deletions source/kdr/filter.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import mir.math : approxEqual, PI, SQRT2, fmax;

/// Kinds of filter implentations.
enum FilterKind {
none,
HP6,
HP12,
BP12,
Expand All @@ -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];
Expand Down Expand Up @@ -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"));
}
}
Expand Down
8 changes: 5 additions & 3 deletions source/kdr/synth2/client.d
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}

Expand Down
0