delay effect

portability/boost
zetaPRIME 2019-06-30 21:55:59 -04:00
parent 9056be08a3
commit c8b0108fb4
3 changed files with 151 additions and 0 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};
}