beginning of Capaxitor, simple lead sampler
parent
82bb4e48e1
commit
26a2bf4e82
7
notes
7
notes
|
@ -32,6 +32,13 @@ parameters {
|
|||
|
||||
TODO {
|
||||
immediate frontburner {
|
||||
PLAYBACK BREAKS AFTER FIRST PLAY??? {
|
||||
doesn't happen in Lovely Storm; only where Capaxitor is used??
|
||||
resolves itself after project switch
|
||||
does NOT break preview mode??
|
||||
seems to be only with the Maybe sample??? idk
|
||||
}
|
||||
|
||||
|
||||
distortion effect
|
||||
single-selection sampler
|
||||
|
|
|
@ -1,10 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "data/audioframe.h"
|
||||
#include "data/sample.h"
|
||||
|
||||
namespace Xybrid::NodeLib {
|
||||
const constexpr size_t LUT_TAPS = 8;
|
||||
const constexpr size_t LUT_STEPS = 1024;
|
||||
extern const std::array<std::array<double, LUT_TAPS>, LUT_STEPS> resamplerLUT;
|
||||
|
||||
inline Data::AudioFrame resamp(Data::Sample* smp, double pos) {
|
||||
double ip = std::floor(pos);
|
||||
auto& pt = NodeLib::resamplerLUT[static_cast<size_t>(pos - ip*NodeLib::LUT_STEPS) % NodeLib::LUT_STEPS];
|
||||
|
||||
Data::AudioFrame out(0.0);
|
||||
|
||||
auto ii = static_cast<ptrdiff_t>(ip) - 3;
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
auto si = ii+static_cast<ptrdiff_t>(i);
|
||||
if (si >= 0 && si < static_cast<ptrdiff_t>(smp->length())) out += (*smp)[static_cast<size_t>(si)] * pt[i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ void BeatPad::process() { core.process(this); }
|
|||
|
||||
void BeatPad::saveData(QCborMap& m) const {
|
||||
QCborMap cm;
|
||||
for (auto c : cfg) {
|
||||
for (auto& c : cfg) {
|
||||
if (auto smp = c.second->smp.lock(); smp) {
|
||||
QCborMap e;
|
||||
e[qs("sample")] = QCborValue(smp->uuid);
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
#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/strings.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QCborMap>
|
||||
#include <QCborValue>
|
||||
#include <QCborArray>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QGraphicsProxyWidget>
|
||||
|
||||
#define qs QStringLiteral
|
||||
|
||||
namespace {
|
||||
bool _ = PluginRegistry::enqueueRegistration([] {
|
||||
auto i = std::make_shared<PluginInfo>();
|
||||
i->id = "plug:capaxitor";
|
||||
i->displayName = "Capaxitor";
|
||||
i->category = "Sampler";
|
||||
i->createInstance = []{ return std::make_shared<Capaxitor>(); };
|
||||
PluginRegistry::registerPlugin(i);
|
||||
//inf = i;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Capaxitor::Capaxitor() {
|
||||
|
||||
}
|
||||
|
||||
void Capaxitor::init() {
|
||||
addPort(Port::Input, Port::Command, 0);
|
||||
addPort(Port::Output, Port::Audio, 0);
|
||||
|
||||
core.onNoteOn = [](Note& note) {
|
||||
auto& data = *reinterpret_cast<NoteData*>(¬e.scratch);
|
||||
new (&data) NoteData(); // construct in-place
|
||||
|
||||
note.adsr = {0.0, 0.0, 1.0, shortStep}; // minimal tweening
|
||||
};
|
||||
|
||||
core.onDeleteNote = [](Note& note) {
|
||||
auto& data = *reinterpret_cast<NoteData*>(¬e.scratch);
|
||||
data.~NoteData(); // destroy
|
||||
};
|
||||
|
||||
/*core.globalParam['Q'] = [](const ParamReader& pr) {
|
||||
qDebug() << "global recieved" << pr.param() << pr.val();
|
||||
return true;
|
||||
};*/
|
||||
|
||||
core.processNote = [this](Note& note, AudioPort* p) {
|
||||
auto& data = *reinterpret_cast<NoteData*>(¬e.scratch);
|
||||
auto smp = this->smp.lock();
|
||||
if (!smp) return core.deleteNote(note);
|
||||
|
||||
double rate = static_cast<double>(smp->sampleRate) / static_cast<double>(audioEngine->curSampleRate());
|
||||
|
||||
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 - 12*5);
|
||||
|
||||
// actual sample pos
|
||||
double sp = data.sampleTime * rate;
|
||||
|
||||
auto out = NodeLib::resamp(smp.get(), sp);
|
||||
|
||||
(*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();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
//c->start = m.value("start").toInteger(-1);
|
||||
//c->end = m.value("end").toInteger(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void Capaxitor::onGadgetCreated() {
|
||||
if (!obj) return;
|
||||
|
||||
auto l = new LayoutGadget(obj);
|
||||
auto sampleSelector = new SampleSelectorGadget(project, l);
|
||||
sampleSelector->setSample(smp.lock(), false);
|
||||
QObject::connect(sampleSelector, &SampleSelectorGadget::sampleSelected, [=](auto smp) { this->smp = smp; });
|
||||
}
|
||||
|
||||
void Capaxitor::onDoubleClick() { emit project->socket->openNodeUI(this); }
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include "nodelib/instrumentcore.h"
|
||||
|
||||
namespace Xybrid::Data { class Sample; }
|
||||
namespace Xybrid::Instruments {
|
||||
class Capaxitor : public Data::Node {
|
||||
NodeLib::InstrumentCore core;
|
||||
|
||||
struct NoteData {
|
||||
double sampleTime = 0;
|
||||
|
||||
NoteData() = default;
|
||||
~NoteData() = default;
|
||||
};
|
||||
static_assert (sizeof(NoteData) <= sizeof(NodeLib::InstrumentCore::Note::scratch), "Note data overflows scratch space!");
|
||||
|
||||
std::weak_ptr<Data::Sample> smp = std::weak_ptr<Data::Sample>();
|
||||
|
||||
|
||||
|
||||
public:
|
||||
Capaxitor();
|
||||
~Capaxitor() 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;
|
||||
|
||||
void onDoubleClick() override;
|
||||
//void initUI(UI::NodeUIScene*) override;
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue