Compare commits
2 Commits
b70ab64dc1
...
2ea0409fe7
Author | SHA1 | Date |
---|---|---|
|
2ea0409fe7 | |
|
7b138536ef |
|
@ -138,6 +138,10 @@ void Assigner::noteOn(uint8_t note) {
|
||||||
|
|
||||||
// limit highest note to C7, one octave above the Solina's maximum range
|
// limit highest note to C7, one octave above the Solina's maximum range
|
||||||
while(note>96) note -= 12;
|
while(note>96) note -= 12;
|
||||||
|
// limit lowest note to C2, too
|
||||||
|
while(note<36) note += 12;
|
||||||
|
// I'm not sure these are correct; these string ensembles don't have MIDI
|
||||||
|
// so it's all a bit of a guess
|
||||||
|
|
||||||
voices[v].startNote(note);
|
voices[v].startNote(note);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#define _ASSIGNER_HPP
|
#define _ASSIGNER_HPP
|
||||||
|
|
||||||
#include "DistrhoPlugin.hpp"
|
#include "DistrhoPlugin.hpp"
|
||||||
|
|
||||||
#include "generator.hpp"
|
#include "generator.hpp"
|
||||||
|
|
||||||
class Assigner {
|
class Assigner {
|
||||||
|
@ -31,12 +30,12 @@ class Assigner {
|
||||||
private:
|
private:
|
||||||
void noteOn(uint8_t note); // incoming note on (or off, if velocity = 0)
|
void noteOn(uint8_t note); // incoming note on (or off, if velocity = 0)
|
||||||
void noteOff(uint8_t note); // incoming note off
|
void noteOff(uint8_t note); // incoming note off
|
||||||
|
void dumpTables();
|
||||||
|
|
||||||
uint8_t voiceTbl[NUM_VOICES]; // voices in order of use
|
uint8_t voiceTbl[NUM_VOICES]; // voices in order of use
|
||||||
uint8_t noteTbl[NUM_VOICES]; // note played by voice
|
uint8_t noteTbl[NUM_VOICES]; // note played by voice
|
||||||
|
Voice *voices; // used to gain access to generator voices
|
||||||
|
|
||||||
Voice *voices;
|
|
||||||
|
|
||||||
void dumpTables();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -23,12 +23,11 @@
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
Chorus::Chorus(uint32_t xbufferSize, double xsampleRate) { // no parameters, programs, or states
|
extern double sampleRate;
|
||||||
|
extern uint32_t bufferSize;
|
||||||
|
|
||||||
bufferSize = xbufferSize;
|
Chorus::Chorus() { // no parameters, programs, or states
|
||||||
sampleRate = xsampleRate;
|
|
||||||
|
|
||||||
lpfIn = new float[bufferSize];
|
|
||||||
lpfOut1 = new float[bufferSize];
|
lpfOut1 = new float[bufferSize];
|
||||||
lpfOut2 = new float[bufferSize];
|
lpfOut2 = new float[bufferSize];
|
||||||
ram = new float[DELAYSIZE]; // probably needs to be calculated based on sample rate
|
ram = new float[DELAYSIZE]; // probably needs to be calculated based on sample rate
|
||||||
|
@ -36,32 +35,29 @@ Chorus::Chorus(uint32_t xbufferSize, double xsampleRate) { // no parameters, pr
|
||||||
fastPhase = 0;
|
fastPhase = 0;
|
||||||
slowPhase = 0;
|
slowPhase = 0;
|
||||||
|
|
||||||
preFilter = new SVF();
|
postFilter1l = new SVF(POSTCUTOFF, .546);
|
||||||
postFilter1 = new SVF();
|
postFilter2l = new SVF(POSTCUTOFF, 1.324);
|
||||||
postFilter2 = new SVF();
|
postFilter1r = new SVF(POSTCUTOFF, .546);
|
||||||
|
postFilter2r = new SVF(POSTCUTOFF, 1.324);
|
||||||
|
|
||||||
// lfo values taken from a rough simulation
|
// lfo values taken from a rough simulation
|
||||||
fastOmega = 6.283 * 6.8 / sampleRate; // approximate, can be adjusted
|
fastOmega = 6.283 * 5.7 / sampleRate; // approximate, can be adjusted
|
||||||
slowOmega = 6.283 * 0.7 / sampleRate; // again approximate
|
slowOmega = 6.283 * 0.7 / sampleRate; // again approximate
|
||||||
|
|
||||||
// zero out the delay buffer
|
// zero out the delay buffer
|
||||||
memset(ram, 0, sizeof(float) * DELAYSIZE);
|
memset(ram, 0, sizeof(float) * DELAYSIZE);
|
||||||
memset(lpfIn, 0, sizeof(float) * bufferSize);
|
|
||||||
memset(lpfOut1, 0, sizeof(float) * bufferSize);
|
memset(lpfOut1, 0, sizeof(float) * bufferSize);
|
||||||
memset(lpfOut2, 0, sizeof(float) * bufferSize);
|
memset(lpfOut2, 0, sizeof(float) * bufferSize);
|
||||||
preFilter->setCutoff(12600, 1.3, sampleRate);
|
|
||||||
postFilter1->setCutoff(11653, 6.6, sampleRate);
|
|
||||||
postFilter2->setCutoff(5883, 1.1, sampleRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Chorus::~Chorus() {
|
Chorus::~Chorus() {
|
||||||
delete lpfIn;
|
|
||||||
delete lpfOut1;
|
delete lpfOut1;
|
||||||
delete lpfOut2;
|
delete lpfOut2;
|
||||||
delete ram;
|
delete ram;
|
||||||
delete preFilter;
|
delete postFilter1l;
|
||||||
delete postFilter1;
|
delete postFilter2l;
|
||||||
delete postFilter2;
|
delete postFilter1r;
|
||||||
|
delete postFilter2r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chorus::run(const float *input, float **outputs, uint32_t frames) {
|
void Chorus::run(const float *input, float **outputs, uint32_t frames) {
|
||||||
|
@ -72,9 +68,6 @@ void Chorus::run(const float *input, float **outputs, uint32_t frames) {
|
||||||
float lfoMod, dly1, frac;
|
float lfoMod, dly1, frac;
|
||||||
uint16_t tap, delay;
|
uint16_t tap, delay;
|
||||||
|
|
||||||
// filter the input
|
|
||||||
preFilter->runSVF(input, lpfIn, frames);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
for (uint32_t i = 0; i < frames; i++) {
|
||||||
// run a step of LFO
|
// run a step of LFO
|
||||||
fastPhase += fastOmega;
|
fastPhase += fastOmega;
|
||||||
|
@ -82,22 +75,13 @@ void Chorus::run(const float *input, float **outputs, uint32_t frames) {
|
||||||
slowPhase += slowOmega;
|
slowPhase += slowOmega;
|
||||||
if (slowPhase > 6.283) slowPhase -= 6.283;
|
if (slowPhase > 6.283) slowPhase -= 6.283;
|
||||||
|
|
||||||
ram[delayptr] = lpfIn[i];
|
ram[delayptr] = input[i];
|
||||||
|
|
||||||
// lowpass filter
|
#define BASE 0.05
|
||||||
|
#define AMT 0.00175
|
||||||
// now we need to calculate the delay
|
|
||||||
// I don't know how long the Solina's delay lines are so I'm guessing 2-4ms for now
|
|
||||||
// normalised mod depths, from a quick simulation of the LFO block:
|
|
||||||
// 0deg 0.203 slow 0.635 fast
|
|
||||||
// 120deg 0.248 slow 0.745 fast
|
|
||||||
// 240deg 0.252 slow 0.609 fast
|
|
||||||
|
|
||||||
#define BASE 0.005
|
|
||||||
#define AMT 0.0015
|
|
||||||
|
|
||||||
// 0 degree delay line
|
// 0 degree delay line
|
||||||
lfoMod = 0.203 * sin(fastPhase) + 0.635 * sin(slowPhase);
|
lfoMod = 0.203 * sin(fastPhase) + 0.835 * sin(slowPhase);
|
||||||
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
|
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
|
||||||
delay = (int)dly1;
|
delay = (int)dly1;
|
||||||
frac = dly1 - delay;
|
frac = dly1 - delay;
|
||||||
|
@ -119,7 +103,7 @@ void Chorus::run(const float *input, float **outputs, uint32_t frames) {
|
||||||
out120 = ((s1 - s0) * frac) + s0;
|
out120 = ((s1 - s0) * frac) + s0;
|
||||||
|
|
||||||
// 240 degree delay line
|
// 240 degree delay line
|
||||||
lfoMod = 0.252 * sin(fastPhase + 4.18) + 0.609 * sin(slowPhase + 4.18);
|
lfoMod = 0.252 * sin(fastPhase + 4.18) + 0.809 * sin(slowPhase + 4.18);
|
||||||
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
|
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
|
||||||
delay = (int)dly1;
|
delay = (int)dly1;
|
||||||
frac = dly1 - delay;
|
frac = dly1 - delay;
|
||||||
|
@ -129,13 +113,15 @@ void Chorus::run(const float *input, float **outputs, uint32_t frames) {
|
||||||
s0 = ram[tap & 0x3ff];
|
s0 = ram[tap & 0x3ff];
|
||||||
out240 = ((s1 - s0) * frac) + s0;
|
out240 = ((s1 - s0) * frac) + s0;
|
||||||
|
|
||||||
lpfOut1[i] = (out0 + out120 + out240) / 3;
|
lpfOut1[i] = (out0 + (out120 * 0.66) + (out240 * 0.33));
|
||||||
|
lpfOut2[i] = (out0 + (out120 * 0.33) + (out240 * 0.66));
|
||||||
|
|
||||||
delayptr++;
|
delayptr++;
|
||||||
delayptr &= 0x3ff;
|
delayptr &= 0x3ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
postFilter1->runSVF(lpfOut1, lpfOut2, frames);
|
postFilter1l->runSVF(lpfOut1, lpfOut1, frames);
|
||||||
postFilter2->runSVF(lpfOut2, outputs[0], frames);
|
postFilter2l->runSVF(lpfOut1, outputs[0], frames);
|
||||||
memcpy (outputs[1], outputs[0], frames * sizeof(float)); // only mono output for now
|
postFilter1r->runSVF(lpfOut2, lpfOut2, frames);
|
||||||
|
postFilter2r->runSVF(lpfOut2, outputs[1], frames);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,15 +24,15 @@
|
||||||
// total size of delay line buffer
|
// total size of delay line buffer
|
||||||
#define DELAYSIZE 1028
|
#define DELAYSIZE 1028
|
||||||
|
|
||||||
|
#define POSTCUTOFF 10000
|
||||||
|
|
||||||
class Chorus {
|
class Chorus {
|
||||||
public:
|
public:
|
||||||
Chorus(uint32_t xbufferSize, double xsampleRate);
|
Chorus();
|
||||||
~Chorus();
|
~Chorus();
|
||||||
void run(const float *input, float **outputs, uint32_t frames);
|
void run(const float *input, float **outputs, uint32_t frames);
|
||||||
double sampleRate;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t bufferSize;
|
|
||||||
double fastPhase, fastOmega;
|
double fastPhase, fastOmega;
|
||||||
double slowPhase, slowOmega;
|
double slowPhase, slowOmega;
|
||||||
double fastLfo, slowLfo;
|
double fastLfo, slowLfo;
|
||||||
|
@ -43,6 +43,6 @@ class Chorus {
|
||||||
float *lpfIn;
|
float *lpfIn;
|
||||||
float *lpfOut1, *lpfOut2;
|
float *lpfOut1, *lpfOut2;
|
||||||
|
|
||||||
SVF *preFilter, *postFilter1, *postFilter2;
|
SVF *postFilter1l, *postFilter2l, *postFilter1r, *postFilter2r;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,24 +23,29 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "DistrhoPluginInfo.h"
|
#include "DistrhoPluginInfo.h"
|
||||||
|
#include "svf.hpp"
|
||||||
|
|
||||||
// some unit-local globals
|
extern double sampleRate;
|
||||||
|
extern uint32_t bufferSize;
|
||||||
|
|
||||||
float sampleRate = 0;
|
// unit-local global
|
||||||
|
static float envTc[2];
|
||||||
|
|
||||||
float envTc[2];
|
Generator::Generator() {
|
||||||
|
|
||||||
|
|
||||||
Generator::Generator(uint32_t bufferSize, double xSampleRate) {
|
|
||||||
sampleRate = xSampleRate;
|
|
||||||
output = new float[bufferSize];
|
output = new float[bufferSize];
|
||||||
// create the phase increments for each semitone
|
// create the phase increments for each semitone
|
||||||
for (uint8_t i = 0; i < 12; i++) {
|
for (uint8_t i = 0; i < 12; i++) {
|
||||||
phase[i] = 0;
|
phase[i] = 0;
|
||||||
uint32_t f;
|
uint32_t f;
|
||||||
f = (1 << 31) * (65.406 * powf(2, 0.083334 * (i + 12)) / sampleRate);
|
f = (1 << 31) * (32.703 * powf(2, 0.083334 * i) / sampleRate);
|
||||||
omega[i] = f;
|
omega[i] = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// output filters
|
||||||
|
// trumpet 4200 2.5
|
||||||
|
// horn 1500 2.5
|
||||||
|
tr2 = SVF(9500.0f, 0.707f);
|
||||||
|
setEnvelope(0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
Generator::~Generator() {
|
Generator::~Generator() {
|
||||||
|
@ -50,7 +55,7 @@ Generator::~Generator() {
|
||||||
void Generator::runBlock(uint32_t frames) {
|
void Generator::runBlock(uint32_t frames) {
|
||||||
Voice *v;
|
Voice *v;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint8_t k, p, key, n1, n2, d;
|
uint8_t k, p, d;
|
||||||
float n;
|
float n;
|
||||||
|
|
||||||
memset(output, 0, frames * sizeof(float));
|
memset(output, 0, frames * sizeof(float));
|
||||||
|
@ -63,7 +68,7 @@ void Generator::runBlock(uint32_t frames) {
|
||||||
for (k = 0; k < NUM_VOICES; k++) {
|
for (k = 0; k < NUM_VOICES; k++) {
|
||||||
v = &voices[k];
|
v = &voices[k];
|
||||||
|
|
||||||
d = (phase[v->semi] & (0x40000000 >> v->oct)) != 0;
|
d = (phase[v->semi] & (0x20000000 >> v->oct)) != 0;
|
||||||
n = d ? 0.25 : -0.25;
|
n = d ? 0.25 : -0.25;
|
||||||
v->vc34 = ((n - v->vc34) * v->c34) + v->vc34;
|
v->vc34 = ((n - v->vc34) * v->c34) + v->vc34;
|
||||||
n -= v->vc34;
|
n -= v->vc34;
|
||||||
|
@ -71,7 +76,7 @@ void Generator::runBlock(uint32_t frames) {
|
||||||
v->vc78 = ((n - v->vc78) * v->c78) + v->vc78;
|
v->vc78 = ((n - v->vc78) * v->c78) + v->vc78;
|
||||||
v->vc107 = ((v->vc78 - v->vc107) * v->c107) + v->vc107;
|
v->vc107 = ((v->vc78 - v->vc107) * v->c107) + v->vc107;
|
||||||
|
|
||||||
d = (phase[v->semi] & (0x80000000 >> v->oct)) != 0;
|
d = (phase[v->semi] & (0x40000000 >> v->oct)) != 0;
|
||||||
n = d ? 0.25 : -0.25;
|
n = d ? 0.25 : -0.25;
|
||||||
v->vc33 = ((n - v->vc33) * v->c33) + v->vc33;
|
v->vc33 = ((n - v->vc33) * v->c33) + v->vc33;
|
||||||
n -= v->vc33;
|
n -= v->vc33;
|
||||||
|
@ -87,6 +92,7 @@ void Generator::runBlock(uint32_t frames) {
|
||||||
output[i] += 0.25 * (v4 + v8) * v->vca;
|
output[i] += 0.25 * (v4 + v8) * v->vca;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tr2.runSVF(output, output, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::startNote(uint8_t key) {
|
void Voice::startNote(uint8_t key) {
|
||||||
|
@ -117,6 +123,6 @@ void Voice::stopNote() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Generator::setEnvelope(float attack, float sustain) {
|
void Generator::setEnvelope(float attack, float sustain) {
|
||||||
envTc[0] = ((96* powf(100, -attack))/sampleRate);
|
envTc[0] = ((96 * powf(100, -attack)) / sampleRate);
|
||||||
envTc[1] = ((48* powf(100, -sustain))/sampleRate);
|
envTc[1] = ((48 * powf(100, -sustain)) / sampleRate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "DistrhoPluginInfo.h"
|
#include "DistrhoPluginInfo.h"
|
||||||
|
#include "svf.hpp"
|
||||||
|
|
||||||
class Generator;
|
class Generator;
|
||||||
|
|
||||||
|
@ -46,7 +47,9 @@ class Voice {
|
||||||
|
|
||||||
class Generator {
|
class Generator {
|
||||||
public:
|
public:
|
||||||
Generator(uint32_t bufferSize, double xSampleRate);
|
// Generator(uint32_t bufferSize, double xSampleRate);
|
||||||
|
Generator();
|
||||||
|
|
||||||
~Generator();
|
~Generator();
|
||||||
void setEnvelope(float attack, float sustain);
|
void setEnvelope(float attack, float sustain);
|
||||||
void runBlock(uint32_t frames);
|
void runBlock(uint32_t frames);
|
||||||
|
@ -56,6 +59,7 @@ class Generator {
|
||||||
private:
|
private:
|
||||||
uint32_t phase[12];
|
uint32_t phase[12];
|
||||||
uint32_t omega[12];
|
uint32_t omega[12];
|
||||||
|
SVF tr2;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -18,14 +18,20 @@
|
||||||
|
|
||||||
#include "sonnenlicht.hpp"
|
#include "sonnenlicht.hpp"
|
||||||
|
|
||||||
|
double sampleRate;
|
||||||
|
uint32_t bufferSize;
|
||||||
|
|
||||||
START_NAMESPACE_DISTRHO
|
START_NAMESPACE_DISTRHO
|
||||||
|
|
||||||
Sonnenlicht::Sonnenlicht() : Plugin(kParameterCount, 0, 0), fSampleRate(getSampleRate()) {
|
Sonnenlicht::Sonnenlicht() : Plugin(kParameterCount, 0, 0) {
|
||||||
genny = new Generator(getBufferSize(), fSampleRate);
|
// genny = new Generator(getBufferSize(), fSampleRate);
|
||||||
|
|
||||||
|
sampleRate = getSampleRate();
|
||||||
|
bufferSize = getBufferSize();
|
||||||
|
|
||||||
|
genny = new Generator();
|
||||||
assigner = new Assigner(genny->voices);
|
assigner = new Assigner(genny->voices);
|
||||||
|
chorus = new Chorus();
|
||||||
chorus = new Chorus(getBufferSize(), fSampleRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sonnenlicht::~Sonnenlicht() {
|
Sonnenlicht::~Sonnenlicht() {
|
||||||
|
@ -57,7 +63,6 @@ void Sonnenlicht::run(const float**, float** outputs, uint32_t frames,
|
||||||
genny->runBlock(frames);
|
genny->runBlock(frames);
|
||||||
|
|
||||||
if (prog.enableChorus) {
|
if (prog.enableChorus) {
|
||||||
|
|
||||||
chorus->run(genny->output, outputs, frames);
|
chorus->run(genny->output, outputs, frames);
|
||||||
} else {
|
} else {
|
||||||
memcpy(outputs[0], genny->output, frames * sizeof(float));
|
memcpy(outputs[0], genny->output, frames * sizeof(float));
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Sonnenlicht : public Plugin {
|
||||||
const MidiEvent *midiEvents, uint32_t midiEventCount) override;
|
const MidiEvent *midiEvents, uint32_t midiEventCount) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double fSampleRate;
|
//double fSampleRate;
|
||||||
|
|
||||||
Program prog;
|
Program prog;
|
||||||
Assigner *assigner;
|
Assigner *assigner;
|
||||||
|
|
|
@ -22,16 +22,14 @@
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
SVF::SVF() {
|
SVF::SVF(float cutoff, float Q) {
|
||||||
// zero out all values
|
// zero out all values
|
||||||
z1 = 0;
|
z1 = 0;
|
||||||
z2 = 0;
|
z2 = 0;
|
||||||
c1 = 0;
|
setCutoff(cutoff, Q);
|
||||||
c2 = 0;
|
|
||||||
d0 = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SVF::setCutoff(float cutoff, float Q, float sampleRate) {
|
void SVF::setCutoff(float cutoff, float Q) {
|
||||||
float F = cutoff / sampleRate;
|
float F = cutoff / sampleRate;
|
||||||
float w = 2 * tan(3.14159 * F);
|
float w = 2 * tan(3.14159 * F);
|
||||||
float a = w / Q;
|
float a = w / Q;
|
||||||
|
@ -45,6 +43,7 @@ void SVF::setCutoff(float cutoff, float Q, float sampleRate) {
|
||||||
|
|
||||||
void SVF::runSVF(const float *input, float *output, uint32_t frames) {
|
void SVF::runSVF(const float *input, float *output, uint32_t frames) {
|
||||||
float x;
|
float x;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
for (uint32_t i = 0; i < frames; i++) {
|
||||||
// lowpass filter
|
// lowpass filter
|
||||||
x = input[i] - z1 - z2;
|
x = input[i] - z1 - z2;
|
||||||
|
|
|
@ -21,10 +21,13 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern double sampleRate;
|
||||||
|
|
||||||
class SVF {
|
class SVF {
|
||||||
public:
|
public:
|
||||||
SVF();
|
SVF(float cutoff=1000, float Q=0.707);
|
||||||
void setCutoff(float cutoff, float Q, float sampleRate);
|
void setCutoff(float cutoff, float Q);
|
||||||
|
|
||||||
void runSVF(const float *input, float *output, uint32_t frames);
|
void runSVF(const float *input, float *output, uint32_t frames);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
Loading…
Reference in New Issue