declick things, now just needs parameter tuning
This commit is contained in:
parent
132b684db7
commit
9d64247598
|
|
@ -29,6 +29,8 @@ Chorus::Chorus() {
|
||||||
lfoPhase = 1;
|
lfoPhase = 1;
|
||||||
lfoSpeed = 6.283 * 10.7 / sampleRate; // plainly silly value to show if it hasn't been set
|
lfoSpeed = 6.283 * 10.7 / sampleRate; // plainly silly value to show if it hasn't been set
|
||||||
|
|
||||||
|
gainTC = 1 - exp(-6.283 * 10 / sampleRate);
|
||||||
|
|
||||||
// not quite Butterworth but you'd never hear the difference
|
// not quite Butterworth but you'd never hear the difference
|
||||||
// these are calculated from the real-world component values
|
// these are calculated from the real-world component values
|
||||||
postFilter1l = new SVF(9688, .549);
|
postFilter1l = new SVF(9688, .549);
|
||||||
|
|
@ -108,8 +110,10 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
|
||||||
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
for (uint32_t i = 0; i < frames; i++) {
|
||||||
float y = input[i];
|
float y = input[i];
|
||||||
outputs[0][i] = y + (gain * lpfOut1[i]);
|
gainRC = (gain - gainRC) * gainTC + gainRC;
|
||||||
outputs[1][i] = y + (gain * lpfOut2[i]);
|
|
||||||
|
outputs[0][i] = y + (gainRC * lpfOut1[i]);
|
||||||
|
outputs[1][i] = y + (gainRC * lpfOut2[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,12 @@ class Chorus {
|
||||||
private:
|
private:
|
||||||
double lfoPhase = 0, lfoSpeed = 0;
|
double lfoPhase = 0, lfoSpeed = 0;
|
||||||
uint8_t lfoState=0;
|
uint8_t lfoState=0;
|
||||||
|
|
||||||
float gain = 1.2;
|
float gain = 1.2;
|
||||||
|
|
||||||
|
float gainRC = 0;
|
||||||
|
float gainTC = 0;
|
||||||
|
|
||||||
|
|
||||||
uint16_t delayptr = 0;
|
uint16_t delayptr = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,29 @@
|
||||||
|
|
||||||
#include "module.hpp"
|
#include "module.hpp"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "tables.hpp"
|
#include "tables.hpp"
|
||||||
|
|
||||||
Module::Module() {
|
Module::Module() {
|
||||||
|
// cutoff frequencies for various RC networks
|
||||||
|
vcaTC = 1 - exp(-6.283 * 159 / sampleRate); // VCA and VCF 10k/0.1u time constant
|
||||||
|
subTC = 1 - exp(-6.283 * 15 / sampleRate); // Main VCA and Sub Level 1k + 10u time constant
|
||||||
|
pwmTC = 1 - exp(-6.283 * 40 / sampleRate); // integrator with 100k/0.047u time constant
|
||||||
|
|
||||||
|
vcaBuf = new float[bufferSize];
|
||||||
|
subBuf = new float[bufferSize];
|
||||||
|
pwmBuf = new float[bufferSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::run(Voice* voice) {
|
Module::~Module() {
|
||||||
|
printf("module destructor\n");
|
||||||
|
delete vcaBuf;
|
||||||
|
delete subBuf;
|
||||||
|
delete pwmBuf;
|
||||||
|
}
|
||||||
|
void Module::run(Voice* voices, uint32_t blockSize) {
|
||||||
// run updates for module board
|
// run updates for module board
|
||||||
|
|
||||||
// FIXME break these out to the patch setter
|
// FIXME break these out to the patch setter
|
||||||
|
|
@ -42,6 +57,7 @@ void Module::run(Voice* voice) {
|
||||||
res = patchRam.vcfReso / 127.0 * 5;
|
res = patchRam.vcfReso / 127.0 * 5;
|
||||||
noise = patchRam.noise / 127.0;
|
noise = patchRam.noise / 127.0;
|
||||||
|
|
||||||
|
// FIXME the exp in these is expensive, don't call it all the time
|
||||||
chorus->setChorus(patchRam.switch1 & 0x60);
|
chorus->setChorus(patchRam.switch1 & 0x60);
|
||||||
chorus->setHpf(patchRam.switch2 & 0x18);
|
chorus->setHpf(patchRam.switch2 & 0x18);
|
||||||
|
|
||||||
|
|
@ -50,9 +66,26 @@ void Module::run(Voice* voice) {
|
||||||
else
|
else
|
||||||
lfo = (lfoPhase & 0x3fff) - 0x1fff;
|
lfo = (lfoPhase & 0x3fff) - 0x1fff;
|
||||||
|
|
||||||
|
// FIXME represent PW as int until we calculate the block?
|
||||||
pw = 0.5 - ((0x2000 + lfo) * patchRam.pwmLfo) / (32768.0f * 128);
|
pw = 0.5 - ((0x2000 + lfo) * patchRam.pwmLfo) / (32768.0f * 128);
|
||||||
pw = (patchRam.switch2 & 0x01) ? 0.5 - (patchRam.pwmLfo / 256.0f) : pw;
|
pw = (patchRam.switch2 & 0x01) ? 0.5 - (patchRam.pwmLfo / 256.0f) : pw;
|
||||||
|
|
||||||
|
float master = powf(2, (patchRam.vca / 31.75 - 4.0f));
|
||||||
|
float sub = patchRam.sub/ 127.0f;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < blockSize; i++) {
|
||||||
|
vcaRC = (master - vcaRC) * subTC + vcaRC;
|
||||||
|
pwmRC = (pw - pwmRC) * pwmTC + pwmRC;
|
||||||
|
subRC = (sub - subRC) * vcaTC + subRC;
|
||||||
|
|
||||||
|
vcaBuf[i] = vcaRC;
|
||||||
|
pwmBuf[i] = pwmRC;
|
||||||
|
subBuf[i] = subRC;
|
||||||
|
|
||||||
|
|
||||||
|
if (bufPtr < bufferSize) bufPtr++;
|
||||||
|
}
|
||||||
|
|
||||||
int16_t vcf = (patchRam.vcfEnv << 7) * ((patchRam.switch2 & 0x02) ? -1 : 1);
|
int16_t vcf = (patchRam.vcfEnv << 7) * ((patchRam.switch2 & 0x02) ? -1 : 1);
|
||||||
|
|
||||||
int16_t pitchBase = 0x1818;
|
int16_t pitchBase = 0x1818;
|
||||||
|
|
@ -60,7 +93,7 @@ void Module::run(Voice* voice) {
|
||||||
|
|
||||||
for (uint32_t i = 0; i < NUM_VOICES; i++) {
|
for (uint32_t i = 0; i < NUM_VOICES; i++) {
|
||||||
// maybe move all this into voice.cpp FIXME
|
// maybe move all this into voice.cpp FIXME
|
||||||
Voice* v = &voice[i];
|
Voice* v = &voices[i];
|
||||||
switch (v->envPhase) {
|
switch (v->envPhase) {
|
||||||
case 0: // release phase FIXME use an enum I guess
|
case 0: // release phase FIXME use an enum I guess
|
||||||
v->env = (v->env * r) >> 16; // "RC" decay to zero
|
v->env = (v->env * r) >> 16; // "RC" decay to zero
|
||||||
|
|
@ -78,6 +111,7 @@ void Module::run(Voice* voice) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pitch
|
// pitch
|
||||||
|
// FIXME clean this all up a bit
|
||||||
int16_t pitch = pitchBase + (v->note << 8);
|
int16_t pitch = pitchBase + (v->note << 8);
|
||||||
int16_t semi = pitch >> 8;
|
int16_t semi = pitch >> 8;
|
||||||
float frac = (pitch & 0xff) / 256.0;
|
float frac = (pitch & 0xff) / 256.0;
|
||||||
|
|
@ -90,11 +124,12 @@ void Module::run(Voice* voice) {
|
||||||
v->omega = px / (sampleRate * 4.0f); // fixme use proper scaler
|
v->omega = px / (sampleRate * 4.0f); // fixme use proper scaler
|
||||||
|
|
||||||
// per voice we need to calculate the key follow amount and envelope amount
|
// per voice we need to calculate the key follow amount and envelope amount
|
||||||
v->vcfCut = (patchRam.vcfFreq << 7) + ((vcf * v->env) >> 16);
|
v->vcfCut = (patchRam.vcfFreq << 7) + ((vcf * v->env) >> 14);
|
||||||
v->vcfCut += (int)(v->note * (patchRam.vcfKey << 1) * 0.375);
|
v->vcfCut += (int)(v->note * (patchRam.vcfKey << 1) * 0.375);
|
||||||
|
|
||||||
if (v->vcfCut > 0x3fff) v->vcfCut = 0x3fff;
|
if (v->vcfCut > 0x3fff) v->vcfCut = 0x3fff;
|
||||||
if (v->vcfCut < 0) v->vcfCut = 0;
|
if (v->vcfCut < 0) v->vcfCut = 0;
|
||||||
|
|
||||||
v->vcaEnv = (patchRam.switch2 & 0x04) ? (v->envPhase ? 0x3fff : 0) : v->env;
|
v->vcaEnv = (patchRam.switch2 & 0x04) ? (v->envPhase ? 0x3fff : 0) : v->env;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,9 @@ class Voice;
|
||||||
class Module {
|
class Module {
|
||||||
public:
|
public:
|
||||||
Module();
|
Module();
|
||||||
|
~Module();
|
||||||
|
|
||||||
void run(Voice* voice);
|
void run(Voice* voices, uint32_t blockLeft);
|
||||||
|
|
||||||
float res = 0;
|
float res = 0;
|
||||||
// precomputed values for all voices
|
// precomputed values for all voices
|
||||||
|
|
@ -45,6 +46,7 @@ class Module {
|
||||||
|
|
||||||
float saw = 0, square = 0, sub = 0, noise = 0;
|
float saw = 0, square = 0, sub = 0, noise = 0;
|
||||||
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t lfoRate = 0x18;
|
uint8_t lfoRate = 0x18;
|
||||||
uint8_t lfoDelay = 0x00;
|
uint8_t lfoDelay = 0x00;
|
||||||
|
|
@ -67,7 +69,18 @@ class Module {
|
||||||
} patchRam;
|
} patchRam;
|
||||||
Chorus* chorus;
|
Chorus* chorus;
|
||||||
|
|
||||||
|
float vcaTC;
|
||||||
|
uint32_t bufPtr = 0;
|
||||||
|
|
||||||
|
float* vcaBuf;
|
||||||
|
float* subBuf;
|
||||||
|
float* pwmBuf;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// precalculated coefficients for RC networks
|
||||||
|
float pwmTC = 0, subTC = 0, mVcaTC = 0;
|
||||||
|
float pwmRC = 0, subRC = 0, vcaRC = 0;
|
||||||
|
|
||||||
// controls
|
// controls
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -81,9 +94,6 @@ class Voice {
|
||||||
void run(Module* m, float* buffer, uint32_t samples);
|
void run(Module* m, float* buffer, uint32_t samples);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// control
|
|
||||||
float vcaRC = 0, vcfRC = 0;
|
|
||||||
|
|
||||||
float omega = 0, theta = 0; // phase increment and angle FIXME better names
|
float omega = 0, theta = 0; // phase increment and angle FIXME better names
|
||||||
float delay = 0, lastpw = 0; // delay slots for antialiasing
|
float delay = 0, lastpw = 0; // delay slots for antialiasing
|
||||||
uint8_t pulseStage = 1; // pulse wave phase
|
uint8_t pulseStage = 1; // pulse wave phase
|
||||||
|
|
@ -93,7 +103,8 @@ class Voice {
|
||||||
int16_t env = 0; // output amplitude
|
int16_t env = 0; // output amplitude
|
||||||
int16_t vcfCut;
|
int16_t vcfCut;
|
||||||
int16_t vcaEnv;
|
int16_t vcaEnv;
|
||||||
float vcaEnvRC = 0;
|
float vcaRC = 0, vcfRC = 0;
|
||||||
|
|
||||||
uint8_t note = 0;
|
uint8_t note = 0;
|
||||||
|
|
||||||
// filter
|
// filter
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,6 @@ Peacock::Peacock() : Plugin(parameterCount, 0, 0) {
|
||||||
ic1 = new Assigner;
|
ic1 = new Assigner;
|
||||||
ic1->voice = voice;
|
ic1->voice = voice;
|
||||||
m->chorus = chorus;
|
m->chorus = chorus;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Peacock::~Peacock() {
|
Peacock::~Peacock() {
|
||||||
|
|
@ -73,6 +71,7 @@ void Peacock::run(const float**, float** outputs, uint32_t frames, const MidiEve
|
||||||
|
|
||||||
// if there were any events that happen between now and the end of this block, process them
|
// if there were any events that happen between now and the end of this block, process them
|
||||||
lastEvent = 0;
|
lastEvent = 0;
|
||||||
|
m->bufPtr = 0;
|
||||||
runMidi(midiEvents, midiEventCount, blockLeft);
|
runMidi(midiEvents, midiEventCount, blockLeft);
|
||||||
|
|
||||||
while (framePos < frames) {
|
while (framePos < frames) {
|
||||||
|
|
@ -81,11 +80,12 @@ void Peacock::run(const float**, float** outputs, uint32_t frames, const MidiEve
|
||||||
blockLeft = sampleRate / 238; // update rate in Hz
|
blockLeft = sampleRate / 238; // update rate in Hz
|
||||||
runMidi(midiEvents, midiEventCount, framePos + blockLeft);
|
runMidi(midiEvents, midiEventCount, framePos + blockLeft);
|
||||||
|
|
||||||
m->run(voice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// how many frames to do? Are we about to run off an update block
|
// how many frames to do? Are we about to run off an update block
|
||||||
sizeThisTime = (framesLeft < blockLeft) ? framesLeft : blockLeft;
|
sizeThisTime = (framesLeft < blockLeft) ? framesLeft : blockLeft;
|
||||||
|
m->run(voice, sizeThisTime);
|
||||||
|
|
||||||
|
|
||||||
// now run all the voices for this chunk of samples
|
// now run all the voices for this chunk of samples
|
||||||
for (uint32_t i = 0; i < NUM_VOICES; i++) {
|
for (uint32_t i = 0; i < NUM_VOICES; i++) {
|
||||||
|
|
@ -98,8 +98,8 @@ void Peacock::run(const float**, float** outputs, uint32_t frames, const MidiEve
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we've assembled a full chunk of audio
|
// now we've assembled a full chunk of audio
|
||||||
|
//memcpy(outputs[0], m->vcaBuf, sizeof(float)* frames);
|
||||||
chorus->run(outputs[0], outputs, frames);
|
chorus->run(outputs[0], outputs, frames);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin* createPlugin() { return new Peacock(); }
|
Plugin* createPlugin() { return new Peacock(); }
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,10 @@ Voice::Voice() {
|
||||||
|
|
||||||
void Voice::on(uint8_t midiNote) {
|
void Voice::on(uint8_t midiNote) {
|
||||||
// omega = 261.63 * powf(2, (note - 60) / 12.0f) / 48000.0f;
|
// omega = 261.63 * powf(2, (note - 60) / 12.0f) / 48000.0f;
|
||||||
if (midiNote>24) note = midiNote-24;
|
if (midiNote > 24)
|
||||||
else note = 24;
|
note = midiNote - 24;
|
||||||
|
else
|
||||||
|
note = 24;
|
||||||
envPhase = 1;
|
envPhase = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,8 +56,7 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) {
|
||||||
// carry out per-voice calculations for each block of samples
|
// carry out per-voice calculations for each block of samples
|
||||||
float out, t, fb;
|
float out, t, fb;
|
||||||
|
|
||||||
//float cut = 0.00513 + 0.0000075*env;
|
// FIXME incorrect
|
||||||
|
|
||||||
// calculate cutoff frequency
|
// calculate cutoff frequency
|
||||||
float cut = 248.0f * (powf(2, (vcfCut - 0x1880) / 1143.0f));
|
float cut = 248.0f * (powf(2, (vcfCut - 0x1880) / 1143.0f));
|
||||||
cut = 0.25 * 6.2832 * cut / 48000.0f; // FIXME hardcoded values
|
cut = 0.25 * 6.2832 * cut / 48000.0f; // FIXME hardcoded values
|
||||||
|
|
@ -71,8 +72,8 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) {
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (pulseStage == 0) {
|
if (pulseStage == 0) {
|
||||||
if (theta < m->pw) break;
|
if (theta < m->pwmBuf[i]) break;
|
||||||
t = (theta - m->pw) / (lastpw - m->pw + omega);
|
t = (theta - m->pwmBuf[i]) / (lastpw - m->pwmBuf[i] + omega);
|
||||||
out -= poly3blep0(t) * m->square;
|
out -= poly3blep0(t) * m->square;
|
||||||
delay -= poly3blep1(t) * m->square;
|
delay -= poly3blep1(t) * m->square;
|
||||||
pulseStage = 1;
|
pulseStage = 1;
|
||||||
|
|
@ -84,8 +85,8 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) {
|
||||||
out += poly3blep0(t) * (m->saw + m->square);
|
out += poly3blep0(t) * (m->saw + m->square);
|
||||||
delay += poly3blep1(t) * (m->saw + m->square);
|
delay += poly3blep1(t) * (m->saw + m->square);
|
||||||
|
|
||||||
out -= poly3blep0(t) * (m->sub * subosc);
|
out -= poly3blep0(t) * (m->subBuf[i] * subosc);
|
||||||
delay -= poly3blep1(t) * (m->sub * subosc);
|
delay -= poly3blep1(t) * (m->subBuf[i] * subosc);
|
||||||
pulseStage = 0;
|
pulseStage = 0;
|
||||||
subosc = -subosc;
|
subosc = -subosc;
|
||||||
|
|
||||||
|
|
@ -93,14 +94,17 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME DC offset removal
|
||||||
delay += m->saw * (1 - (2 * theta));
|
delay += m->saw * (1 - (2 * theta));
|
||||||
delay += m->square * (pulseStage ? -1.f : 1.f);
|
delay += m->square * ((pulseStage ? -1.f : 1.f) - m->pwmBuf[i] + 0.5);
|
||||||
delay += m->sub * subosc;
|
delay += m->subBuf[i] * subosc ;
|
||||||
// delay += (1-(m->noisegen/(float)(1<<30))) * m->noise; FIXME figure out what to do about noise
|
|
||||||
|
|
||||||
out += m->noise * (0.8 - 1.6 * (rand() & 0xffff) / 65536.0);
|
out += m->noise * (0.8 - 1.6 * (rand() & 0xffff) / 65536.0);
|
||||||
out *= 0.5;
|
out *= 0.5;
|
||||||
|
|
||||||
|
// same time constant for both VCF and VCF RC circuits
|
||||||
|
vcfRC = (cut - vcfRC) * m->vcaTC + vcfRC;
|
||||||
|
|
||||||
for (uint8_t ovs = 0; ovs < 4; ovs++) {
|
for (uint8_t ovs = 0; ovs < 4; ovs++) {
|
||||||
fb = b4;
|
fb = b4;
|
||||||
// hard clip
|
// hard clip
|
||||||
|
|
@ -110,16 +114,16 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) {
|
||||||
// fb = 1.5 * fb - 0.5 * fb * fb * fb;
|
// fb = 1.5 * fb - 0.5 * fb * fb * fb;
|
||||||
//
|
//
|
||||||
|
|
||||||
b1 = ((out + fb - b1) * cut) + b1;
|
b1 = ((out + fb - b1) * vcfRC) + b1;
|
||||||
b2 = ((b1 - b2) * cut) + b2;
|
b2 = ((b1 - b2) * vcfRC) + b2;
|
||||||
b3 = ((b2 - b3) * cut) + b3;
|
b3 = ((b2 - b3) * vcfRC) + b3;
|
||||||
b4 = ((b3 - b4) * cut) + b4;
|
b4 = ((b3 - b4) * vcfRC) + b4;
|
||||||
}
|
}
|
||||||
|
|
||||||
vcaEnvRC = (amp - vcaEnvRC) * 0.0203 + vcaEnvRC;
|
vcaRC = (amp - vcaRC) * m->vcaTC + vcaRC;
|
||||||
|
buffer[i] += 0.09367 * m->vcaBuf[i] * vcaRC * b4;
|
||||||
|
|
||||||
buffer[i] += 0.0367 * vcaEnvRC * b4;
|
lastpw = m->pwmBuf[i];
|
||||||
lastpw = m->pw;
|
|
||||||
}
|
}
|
||||||
// buffer[0] += 1;
|
// buffer[0] += 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue