delay effect
parent
9056be08a3
commit
c8b0108fb4
|
@ -112,6 +112,7 @@ namespace Xybrid::Audio {
|
|||
inline size_t curTickId() const { return tickId; }
|
||||
inline size_t curTickSize() const { return buffer[0].size(); }
|
||||
inline int curSampleRate() const { return sampleRate; }
|
||||
inline double curTempo() const { return tempo; }
|
||||
|
||||
// QIODevice functions
|
||||
qint64 readData(char* data, qint64 maxlen) override;
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
#include "delay.h"
|
||||
using Xybrid::Effects::Delay;
|
||||
using namespace Xybrid::Data;
|
||||
|
||||
#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>
|
||||
|
||||
#define qs QStringLiteral
|
||||
|
||||
namespace {
|
||||
bool _ = PluginRegistry::enqueueRegistration([] {
|
||||
auto i = std::make_shared<PluginInfo>();
|
||||
i->id = "gadget:delay";
|
||||
i->displayName = "Delay";
|
||||
i->category = "Effect";
|
||||
i->createInstance = []{ return std::make_shared<Delay>(); };
|
||||
PluginRegistry::registerPlugin(i);
|
||||
});
|
||||
}
|
||||
|
||||
Delay::Delay() { }
|
||||
|
||||
void Delay::init() {
|
||||
addPort(Port::Input, Port::Audio, 0);
|
||||
addPort(Port::Output, Port::Audio, 0);
|
||||
}
|
||||
|
||||
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()) * (timeInBeats ? (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
|
||||
buf.normalizeIndexes(); // and make sure every frame is reachable
|
||||
|
||||
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));
|
||||
in->pull();
|
||||
out->pull();
|
||||
|
||||
size_t ts = audioEngine->curTickSize();
|
||||
for (size_t f = 0; f < ts; f++) {
|
||||
AudioFrame fCurrent = (*in)[f];
|
||||
auto fOut = buf.takeFirst();
|
||||
int i = frames + buf.firstIndex();
|
||||
buf[i] += (fCurrent * delayMult) + (fOut * fbMult);
|
||||
|
||||
(*out)[f] = fCurrent + fOut;
|
||||
}
|
||||
}
|
||||
|
||||
void Delay::saveData(QCborMap& m) const {
|
||||
m[qs("time")] = QCborValue(delayTime);
|
||||
m[qs("inBeats")] = QCborValue(timeInBeats);
|
||||
m[qs("amount")] = QCborValue(amount);
|
||||
m[qs("feedback")] = QCborValue(feedback);
|
||||
}
|
||||
|
||||
void Delay::loadData(const QCborMap& m) {
|
||||
delayTime = m.value("time").toDouble(delayTime);
|
||||
timeInBeats = m.value("inBeats").toBool(timeInBeats);
|
||||
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("Time")->setRange(0.0, 5.0)->setDefault(0.5);
|
||||
(new ToggleGadget(l))->bind(timeInBeats)->setColor({191, 127, 255})->setToolTip("BPM-relative");
|
||||
l->addSpacer();
|
||||
(new KnobGadget(l))->bind(amount)->setLabel("Amount")->setDefault(0.5);
|
||||
l->addSpacer();
|
||||
(new KnobGadget(l))->bind(feedback)->setLabel("Feedback")->setDefault(0.0);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <QContiguousCache>
|
||||
|
||||
#include "data/node.h"
|
||||
#include "data/audioframe.h"
|
||||
|
||||
namespace Xybrid::Effects {
|
||||
class Delay : public Data::Node {
|
||||
QContiguousCache<Data::AudioFrame> buf;
|
||||
|
||||
double delayTime = 0.5;
|
||||
bool timeInBeats = false;
|
||||
|
||||
double amount = 0.5;
|
||||
double feedback = 0.0;
|
||||
|
||||
public:
|
||||
Delay();
|
||||
~Delay() override = default;
|
||||
|
||||
void init() override;
|
||||
void reset() override;
|
||||
void release() override;
|
||||
void process() override;
|
||||
|
||||
//void onRename() override;
|
||||
|
||||
void saveData(QCborMap&) const override;
|
||||
void loadData(const QCborMap&) override;
|
||||
|
||||
//void onUnparent(std::shared_ptr<Data::Graph>) override;
|
||||
//void onParent(std::shared_ptr<Data::Graph>) override;
|
||||
|
||||
void onGadgetCreated() override;
|
||||
|
||||
//void drawCustomChrome(QPainter*, const QStyleOptionGraphicsItem*) override;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue