xybrid/xybrid/nodes/sampler/capaxitor.cpp

135 lines
3.6 KiB
C++

#include "capaxitor.h"
using Xybrid::Instruments::Capaxitor;
using namespace Xybrid::NodeLib;
using Note = InstrumentCore::Note;
using namespace Xybrid::Data;
#include "nodelib/commandreader.h"
#include "nodelib/resampler.h"
#include "data/project.h"
#include "data/sample.h"
#include "data/porttypes.h"
#include "config/pluginregistry.h"
using namespace Xybrid::Config;
#include "audio/audioengine.h"
using namespace Xybrid::Audio;
#include "ui/waveformpreviewwidget.h"
#include "ui/patchboard/nodeobject.h"
#include "ui/gadgets/layoutgadget.h"
#include "ui/gadgets/knobgadget.h"
#include "ui/gadgets/selectorgadget.h"
#include "ui/gadgets/sampleselectorgadget.h"
using namespace Xybrid::UI;
#include "ui/patchboard/nodeuiscene.h"
#include "uisocket.h"
#include "util/ext.h"
#include "util/strings.h"
#include <cmath>
#include <QDebug>
#include <QCborMap>
#include <QCborValue>
#include <QCborArray>
#include <QMenu>
#include <QGraphicsProxyWidget>
// clazy:excludeall=non-pod-global-static
RegisterPlugin(Capaxitor, {
i->id = "plug:capaxitor";
i->displayName = "CapaXitor";
i->category = "Sampler";
})
Capaxitor::Capaxitor() {
}
void Capaxitor::init() {
addPort(Port::Input, Port::Command, 0);
addPort(Port::Output, Port::Audio, 0);
core.onNoteOn = [this](Note& note) {
auto& data = *hard_cast<NoteData*>(&note.scratch);
new (&data) NoteData(); // construct in-place
note.adsr = adsr;
};
core.onDeleteNote = [](Note& note) {
auto& data = *hard_cast<NoteData*>(&note.scratch);
data.~NoteData(); // destroy
};
core.processNote = [this](Note& note, AudioPort* p) {
auto& data = *hard_cast<NoteData*>(&note.scratch);
auto smp = this->smp.lock();
if (!smp) return core.deleteNote(note);
double rate = static_cast<double>(smp->sampleRate) / static_cast<double>(audioEngine->curSampleRate());
double baseNote = smp->getNote();
bool loop = smp->loopStart >= 0;
auto len = static_cast<double>(smp->length());
size_t ts = p->size;
for (size_t i = 0; i < ts; i++) {
core.advanceNote(note);
double n = note.effectiveNote();
double fr = std::pow(SEMI, n - baseNote);
// actual sample pos
double sp = data.sampleTime * rate;
if (!loop && sp >= len) return core.deleteNote(note);
auto out = NodeLib::resamp(smp.get(), sp, rate*fr);
(*p)[i] += out.gainBalance(0, note.pan) * note.ampMult();
data.sampleTime += fr;
}
};
}
void Capaxitor::reset() { core.reset(); }
void Capaxitor::release() { core.release(); }
void Capaxitor::process() { core.process(this); }
void Capaxitor::saveData(QCborMap& m) const {
if (auto smp = this->smp.lock(); smp) {
m[qs("sample")] = QCborValue(smp->uuid);
smp->markForExport();
}
m[qs("adsr")] = adsr;
}
void Capaxitor::loadData(const QCborMap& m) {
auto id = m.value("sample").toUuid();
if (auto f = project->samples.find(id); f != project->samples.end()) {
smp = f.value();
}
adsr = m.value("adsr");
}
void Capaxitor::onGadgetCreated() {
if (!obj) return;
auto l = new LayoutGadget(obj);
auto sampleSelector = new SampleSelectorGadget(project, l);
sampleSelector->setSize(128, 48);
sampleSelector->setSample(smp.lock(), false);
KnobGadget::autoCreate(l, adsr);
QObject::connect(sampleSelector, &SampleSelectorGadget::sampleSelected, sampleSelector, [=](auto smp) { this->smp = smp; });
}
void Capaxitor::onDoubleClick() { emit project->socket->openNodeUI(this); }