120 lines
3.9 KiB
C++
120 lines
3.9 KiB
C++
#include "delay.h"
|
|
using Xybrid::Effects::Delay;
|
|
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 <cmath>
|
|
|
|
#include <QCborMap>
|
|
|
|
// clazy:excludeall=non-pod-global-static
|
|
RegisterPlugin(Delay, {
|
|
i->id = "fx:delay";
|
|
i->oldIds = {"gadget:delay"};
|
|
i->displayName = "Delay";
|
|
i->category = "Effect";
|
|
})
|
|
|
|
Delay::Delay() { }
|
|
|
|
void Delay::init() {
|
|
addPort(Port::Input, Port::Audio, 0);
|
|
addPort(Port::Output, Port::Audio, 0);
|
|
addPort(Port::Output, Port::Audio, 1)->name = "wet only";
|
|
}
|
|
|
|
void Delay::reset() {
|
|
release();
|
|
auto sr = audioEngine->curSampleRate();
|
|
buf.setCapacity(sr * 5); // five seconds of buffer
|
|
}
|
|
|
|
void Delay::release() {
|
|
buf.clear();
|
|
buf.setCapacity(0);
|
|
}
|
|
|
|
void Delay::process() {
|
|
// precalculate actual multipliers from volume knobs
|
|
auto delayMult = std::pow(amount, 2);
|
|
auto fbMult = std::pow(feedback, 2);
|
|
|
|
// calculate number of frames ahead to write to the buffer
|
|
int frames = static_cast<int>(delayTime * static_cast<double>(audioEngine->curSampleRate()) * (bpmRelative ? (60.0 / audioEngine->curTempo()) : 1.0));
|
|
|
|
// enlarge buffer if too small
|
|
if (auto mc = frames+1; buf.capacity() < mc) buf.setCapacity(mc);
|
|
while (buf.size() <= frames) buf.append({0.0, 0.0}); // pack to fit
|
|
|
|
auto in = std::static_pointer_cast<AudioPort>(port(Port::Input, Port::Audio, 0));
|
|
auto out = std::static_pointer_cast<AudioPort>(port(Port::Output, Port::Audio, 0));
|
|
auto wout = std::static_pointer_cast<AudioPort>(port(Port::Output, Port::Audio, 1));
|
|
in->pull();
|
|
bool oc = out->isConnected();
|
|
bool wc = wout->isConnected();
|
|
if (oc) out->pull();
|
|
if (wc) wout->pull();
|
|
|
|
size_t ts = audioEngine->curTickSize();
|
|
for (size_t f = 0; f < ts; f++) {
|
|
AudioFrame fCurrent = (*in)[f];
|
|
auto fOut = buf.takeFirst();
|
|
if (!buf.areIndexesValid()) buf.normalizeIndexes(); // make sure we can actually reach the point we need
|
|
int i = frames + buf.firstIndex();
|
|
buf[i] += (fCurrent * delayMult) + (fOut * fbMult);
|
|
if (pingPong) buf[i] = buf[i].flip();
|
|
|
|
if (oc) (*out)[f] = fCurrent + fOut;
|
|
if (wc) (*wout)[f] = fOut;
|
|
}
|
|
}
|
|
|
|
void Delay::saveData(QCborMap& m) const {
|
|
m[qs("time")] = delayTime;
|
|
m[qs("bpmRelative")] = bpmRelative;
|
|
m[qs("pingPong")] = pingPong;
|
|
m[qs("amount")] = amount;
|
|
m[qs("feedback")] = feedback;
|
|
}
|
|
|
|
void Delay::loadData(const QCborMap& m) {
|
|
delayTime = m.value("time").toDouble(delayTime);
|
|
bpmRelative = m.value("bpmRelative").toBool(m.value("inBeats").toBool(bpmRelative));
|
|
pingPong = m.value("pingPong").toBool(pingPong);
|
|
amount = m.value("amount").toDouble(amount);
|
|
feedback = m.value("feedback").toDouble(feedback);
|
|
}
|
|
|
|
void Delay::onGadgetCreated() {
|
|
if (!obj) return;
|
|
auto l = new LayoutGadget(obj);
|
|
|
|
(new KnobGadget(l))->bind(delayTime)->setLabel(qs("Time"))->setRange(0.0, 5.0, 0.001, -1, 0.01)->setDefault(0.5);
|
|
auto l2 = (new LayoutGadget(l, true))->setMetrics(0, 4);
|
|
(new ToggleGadget(l2))->bind(bpmRelative)->setColor({191, 127, 255})->setToolTip(qs("BPM-relative"));
|
|
(new ToggleGadget(l2))->bind(pingPong)->setColor({127, 191, 255})->setToolTip(qs("Stereo ping-pong"));
|
|
//l->addSpacer();
|
|
(new KnobGadget(l))->bind(amount)->setLabel(qs("Level"))->setTextFunc(KnobGadget::textPercent)->setDefault(0.5);
|
|
//l->addSpacer();
|
|
(new KnobGadget(l))->bind(feedback)->setLabel(qs("Feedback"))->setTextFunc(KnobGadget::textPercent)->setDefault(0.0);
|
|
}
|