diff --git a/notes b/notes index d2d2286..0ac0307 100644 --- a/notes +++ b/notes @@ -42,7 +42,7 @@ TODO { revert-to-saved menu action - distortion effect + - distortion effect automation node { listens for one specific param diff --git a/xybrid/nodes/effect/distortion.cpp b/xybrid/nodes/effect/distortion.cpp new file mode 100644 index 0000000..7a553f9 --- /dev/null +++ b/xybrid/nodes/effect/distortion.cpp @@ -0,0 +1,99 @@ +#include "distortion.h" + +using Xybrid::Effects::Distortion; +using namespace Xybrid::Data; + +#include "util/strings.h" + +#include "nodelib/basics.h" +using namespace Xybrid::NodeLib; + +#include "data/audioframe.h" +#include "data/porttypes.h" + +#include "config/pluginregistry.h" +using namespace Xybrid::Config; + +#include "audio/audioengine.h" +using namespace Xybrid::Audio; + +#include "ui/patchboard/nodeobject.h" +#include "ui/gadgets/layoutgadget.h" +#include "ui/gadgets/togglegadget.h" +#include "ui/gadgets/knobgadget.h" +using namespace Xybrid::UI; + +#include + +#include + +// clazy:excludeall=non-pod-global-static +RegisterPlugin(Distortion, { + i->id = "fx:distortion"; + i->displayName = "Distortion"; + i->category = "Effect"; +}) + +Distortion::Distortion() = default; + +void Distortion::init() { + addPort(Port::Input, Port::Audio, 0); + addPort(Port::Output, Port::Audio, 0); +} + +void Distortion::reset() { + // +} + +namespace { + inline double sxp(double v, double e) { + double s = v < 0 ? -1.0 : 1.0; + return std::pow(std::abs(v), e) * s; + } + inline AudioFrame sxp(AudioFrame v, double e) { return {sxp(v.l, e), sxp(v.r, e)}; } +} + +void Distortion::process() { + auto in = std::static_pointer_cast(port(Port::Input, Port::Audio, 0)); + auto out = std::static_pointer_cast(port(Port::Output, Port::Audio, 0)); + in->pull(); + out->pull(); + + auto ts = audioEngine->curTickSize(); + + auto d = drive.start(), s = shape.start(), m = mix.start(), o = output.start(); + + for (size_t f = 0; f < ts; f++) { + AudioFrame inp = (*in)[f]; + auto g = inp.gainBalance(d.next()); + auto pv = s.next(); + auto exp = pv > 0.0 ? 1.0 / (1.0 + pv) : -pv + 1.0; + + + (*out)[f] = AudioFrame::lerp(inp, sxp(g.clamp(), exp), m.next()).gainBalance(o.next()); + } +} + +void Distortion::saveData(QCborMap& m) const { + drive.save(m); + shape.save(m); + mix.save(m); + output.save(m); +} + +void Distortion::loadData(const QCborMap& m) { + drive.load(m); + shape.load(m); + mix.load(m); + output.load(m); +} + +void Distortion::onGadgetCreated() { + if (!obj) return; + auto l = new LayoutGadget(obj); + + KnobGadget::autoGain(l, drive); + (new KnobGadget(l))->setRange(0, 0, 0.1)->bind(shape)->setTextFunc(KnobGadget::textOffset); + KnobGadget::autoPercent(l, mix); + KnobGadget::autoGain(l, output); +} diff --git a/xybrid/nodes/effect/distortion.h b/xybrid/nodes/effect/distortion.h new file mode 100644 index 0000000..79ab662 --- /dev/null +++ b/xybrid/nodes/effect/distortion.h @@ -0,0 +1,27 @@ +#pragma once + +#include "data/node.h" +#include "nodelib/param.h" + +namespace Xybrid::Effects { + class Distortion : public Data::Node { + NodeLib::Param drive = {"Drive", 0.0, 24.0, 0.0}; + NodeLib::Param shape = {"Shape", -5.0, 5.0, 0.0}; + NodeLib::Param mix = {"Mix", 0.0, 1.0, 1.0}; + NodeLib::Param output = {"Output", -12.0, 12.0, 0.0}; + + public: + Distortion(); + ~Distortion() override = default; + + void init() override; + void reset() override; + //void release() override; + void process() override; + + void saveData(QCborMap&) const override; + void loadData(const QCborMap&) override; + + void onGadgetCreated() override; + }; +}