waveform and mod dial for THiCC
parent
7af2ec5034
commit
24b7c72ef9
1
notes
1
notes
|
@ -33,6 +33,7 @@ parameters {
|
|||
TODO {
|
||||
immediate frontburner {
|
||||
reimplement sample import to IPC from standalone ffmpeg since QAudioDecoder is partially broken on arch and completely broken on macOS
|
||||
ffprobe -v quiet -show_streams -select_streams a -of json testOut.mp3
|
||||
|
||||
distortion effect
|
||||
single-selection sampler
|
||||
|
|
|
@ -20,6 +20,7 @@ using namespace Xybrid::UI;
|
|||
#include "util/strings.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRandomGenerator>
|
||||
|
@ -44,7 +45,7 @@ namespace {
|
|||
else return d;
|
||||
}
|
||||
}
|
||||
[[maybe_unused]] inline double lerp(double p, double a, double b) {
|
||||
[[maybe_unused]] inline double lerp(double a, double b, double p) {
|
||||
return b * p + a * (1.0 - p);
|
||||
}
|
||||
|
||||
|
@ -68,15 +69,49 @@ namespace {
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
[[gnu::optimize("O3")]] double oscSaw(double phase, double delta) {
|
||||
[[gnu::optimize("O3")]] double push(double in, double mod, double factor) {
|
||||
double s = in < 0 ? -1 : 1;
|
||||
in *= s;
|
||||
//if (mod < 0) mod = 1.0/-mod;
|
||||
//else mod += 1.0;
|
||||
mod = mod < 0 ? lerp(1.0, 1.0/factor, -mod) : lerp(1.0, factor, mod);
|
||||
return std::pow(in, mod)*s;
|
||||
}
|
||||
|
||||
[[gnu::optimize("O3")]] double oscSaw(double phase, double delta, double mod) {
|
||||
phase = std::fmod(phase + 0.5, 1.0);
|
||||
double d = phase;// * 0.2 + (std::floor(phase*7.0) / 7.0 + (0.5/7.0)) * 0.8;
|
||||
d = d * 2.0 - 1.0;
|
||||
d = push(d, -mod, 5);
|
||||
d -= polyblep(phase, delta);
|
||||
return d;
|
||||
}
|
||||
|
||||
[[gnu::optimize("O3")]] double oscSine(double phase, double, double mod) { return push(std::sin(phase*PI*2), -mod, 5); }
|
||||
|
||||
[[gnu::optimize("O3")]] double oscPulse(double phase, double delta, double mod) {
|
||||
double duty = (mod+1.0)/2.0;
|
||||
double d = 1.0;
|
||||
if (std::fmod(phase, 1.0) >= duty) d = -1.0;
|
||||
d += polyblep(std::fmod(phase, 1.0), delta);
|
||||
d -= polyblep(std::fmod(phase + (1.0 - duty), 1.0), delta);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// wave function list(s)
|
||||
const constexpr std::array waveFunc = {
|
||||
&oscSaw,
|
||||
&oscSine,
|
||||
&oscPulse,
|
||||
};
|
||||
const std::array waveName = {
|
||||
qs("saw"),
|
||||
qs("sine"),
|
||||
qs("pulse"),
|
||||
};
|
||||
}
|
||||
|
||||
Thicc::Thicc() { }
|
||||
|
@ -99,6 +134,8 @@ void Thicc::init() {
|
|||
core.processNote = [this](Note& note, AudioPort* p) {
|
||||
double freq;
|
||||
|
||||
auto osc = waveFunc[static_cast<size_t>(wave)];
|
||||
|
||||
int vc = voices;
|
||||
double vf = static_cast<double>(vc);
|
||||
double spr = std::pow(SEMI, detune/(vf/2.0));
|
||||
|
@ -122,11 +159,11 @@ void Thicc::init() {
|
|||
double dm = std::pow(spr, dc);
|
||||
double sg = i % 2 == 0 ? 1.0 : -1.0;
|
||||
//o += (std::fmod(pc + si * dm, 1.0) * 2.0 - 1.0) * sg;
|
||||
o += oscSaw(pc + si * dm, delta * dm) * sg;
|
||||
o += osc(pc + si * dm, delta * dm, mod) * sg;
|
||||
|
||||
}
|
||||
|
||||
o /= 1.0 + ((vf - 1.0)/3.0);
|
||||
o /= 1.0 + ((vf - 1.0)/5.0);
|
||||
|
||||
AudioFrame out = o;
|
||||
(*p)[i] += out.gainBalance(0, note.pan) * note.ampMult();
|
||||
|
@ -141,25 +178,31 @@ void Thicc::process() { core.process(this); }
|
|||
void Thicc::saveData(QCborMap& m) const {
|
||||
m[qs("adsr")] = adsr;
|
||||
|
||||
m[qs("wave")] = wave;
|
||||
m[qs("voices")] = voices;
|
||||
m[qs("mod")] = mod;
|
||||
m[qs("detune")] = detune;
|
||||
//m[qs("shift")] = shift;
|
||||
}
|
||||
|
||||
void Thicc::loadData(const QCborMap& m) {
|
||||
adsr = m.value("adsr");
|
||||
|
||||
wave = static_cast<int>(m.value("wave").toInteger(wave));
|
||||
voices = static_cast<int>(m.value("voices").toInteger(voices));
|
||||
mod = m.value("mod").toDouble(mod);
|
||||
detune = m.value("detune").toDouble(detune);
|
||||
//shift = m.value("shift").toDouble(shift);
|
||||
}
|
||||
|
||||
void Thicc::onGadgetCreated() {
|
||||
auto wn = [](size_t i) { if (i >= waveName.size()) return QString::number(i); return waveName[i]; };
|
||||
|
||||
auto l = new LayoutGadget(obj);
|
||||
|
||||
(new KnobGadget(l))->bind(wave)->setLabel(qs("Wave"))->setTextFunc(wn)->setRange(0, waveFunc.size()-1, 1, KnobGadget::BigStep);
|
||||
(new KnobGadget(l))->bind(mod)->setLabel(qs("W. Mod"))->setRange(-1.0, 1.0, 0.01);
|
||||
l->addSpacer();
|
||||
(new KnobGadget(l))->bind(voices)->setLabel(qs("Voices"))->setRange(1, 16, 1, KnobGadget::BigStep)->setDefault(1);
|
||||
(new KnobGadget(l))->bind(detune)->setLabel(qs("Detune"))->setRange(0.0, 1.0, 0.001);
|
||||
//(new KnobGadget(l))->bind(shift)->setLabel(qs("Shift"))->setRange(0.0, 25.0, 0.01);
|
||||
l->addSpacer();
|
||||
KnobGadget::autoCreate(l, adsr);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@ namespace Xybrid::Instruments {
|
|||
|
||||
NodeLib::ADSR adsr;
|
||||
|
||||
int wave = 0;
|
||||
int voices = 1;
|
||||
|
||||
double mod = 0.0;
|
||||
double detune = 0.0;
|
||||
//double shift = 0.0;
|
||||
|
||||
public:
|
||||
Thicc();
|
||||
|
|
Loading…
Reference in New Issue