parent
f860ebfef1
commit
09df1b535f
42 changed files with 927 additions and 524 deletions
@ -1,54 +0,0 @@ |
||||
#ifndef __PART_H__ |
||||
#define __PART_H__ |
||||
|
||||
#include <unordered_map> |
||||
#include <set> |
||||
|
||||
#include "globals.h" |
||||
#include "voicemanager.h" |
||||
#include "voice.h" |
||||
#include "preset.h" |
||||
#include "dsp/frame.h" |
||||
|
||||
class Channel { |
||||
public: |
||||
VoiceManager::channel_t number; |
||||
VoiceManager* voiceManager; |
||||
Voice::Settings settings; |
||||
|
||||
Channel() : volume(1), pitchBend(0), modulation(0), lfoPhase(randPhase()) {} |
||||
|
||||
void loadPreset(const Preset* preset); |
||||
|
||||
void noteOn(int note, int vel); |
||||
void noteOff(int note); |
||||
void control(int cc, int val); |
||||
|
||||
frame tick() { |
||||
frame out{0, 0}; |
||||
float lfo = sin(lfoPhase); |
||||
Voice** voices = voiceManager->getChannelVoices(number); |
||||
for(Voice** voice = voices; *voice != NULL; ++voice) { |
||||
float voiceOut = (*voice)->tick(pitchBend + settings.lfoPitchMod * lfo, settings.lfoFltMod * lfo); |
||||
out.l += sinf((1 - (*voice)->pan) * (float) M_PI_2) * voiceOut; |
||||
out.r += sinf((*voice)->pan * (float) M_PI_2) * voiceOut; |
||||
} |
||||
out *= volume; |
||||
|
||||
lfoPhase += settings.lfoStep; |
||||
if(lfoPhase >= (float) PIx2) { |
||||
lfoPhase -= PIx2; |
||||
} |
||||
|
||||
return out; |
||||
} |
||||
|
||||
private: |
||||
float volume; |
||||
float pitchBend; |
||||
float modulation; |
||||
|
||||
float lfoPhase; |
||||
}; |
||||
|
||||
#endif |
@ -1,128 +0,0 @@ |
||||
#ifndef __SVF_H__ |
||||
#define __SVF_H__ |
||||
|
||||
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */ |
||||
|
||||
#include <cmath> |
||||
#include <cfloat> |
||||
#include <iostream> |
||||
#include <algorithm> |
||||
#include <functional> |
||||
|
||||
#include "../globals.h" |
||||
#include "util.h" |
||||
|
||||
#define FILTER_OVERSAMPLE 4 |
||||
#define FILTER_K_SCALE (2 * CV_FREQ_MIN / (FILTER_OVERSAMPLE * SAMPLE_RATE)) |
||||
#define FILTER_CV_MAX 9.25 // MIDI note "135" (19912.126958213178287 Hz)
|
||||
|
||||
inline float cvToK(float cv) { |
||||
return tanf((float) M_PI_2 * std::min(0.5f, std::min(1.f / FILTER_OVERSAMPLE, (float) FILTER_K_SCALE * exp2f(cv)))); |
||||
} |
||||
|
||||
class SVF12 { |
||||
public: |
||||
typedef struct { |
||||
float lp; // low-pass
|
||||
float bp; // band-pass
|
||||
float hp; // high-pass
|
||||
} Output; |
||||
|
||||
protected: |
||||
float as1, as2; |
||||
|
||||
public: |
||||
SVF12() : as1(0), as2(0) {} |
||||
|
||||
inline Output tick(float in, float kK, float kQ) { |
||||
Output out; |
||||
float kdiv = 1 + kK/kQ + kK*kK; |
||||
out.hp = (in - (1/kQ + kK) * as1 - as2) / kdiv; |
||||
float au = out.hp * kK; |
||||
out.bp = au + as1; |
||||
as1 = au + out.bp; |
||||
au = out.bp * kK; |
||||
out.lp = au + as2; |
||||
as2 = au + out.lp; |
||||
return out; |
||||
} |
||||
}; |
||||
|
||||
class Oversampler { |
||||
private: |
||||
float kK, kQ = (float) M_SQRT1_2; |
||||
SVF12 aa1, aa2; |
||||
|
||||
public: |
||||
Oversampler() : kK(tanf((float) M_PI_2 * 1.0f / FILTER_OVERSAMPLE)) {} |
||||
|
||||
inline float tick(float in, std::function<float(float)> process) { |
||||
if(FILTER_OVERSAMPLE < 2) { |
||||
return process(in); |
||||
} else { |
||||
// float out = aa2.tick(aa1.tick(times * process(in), kK, kQ).lp, kK, kQ).lp;
|
||||
// for(int i = 1; i < times; ++i) {
|
||||
// aa2.tick(aa1.tick(process(0), kK, kQ).lp, kK, kQ);
|
||||
// }
|
||||
float out = process(aa2.tick(aa1.tick(FILTER_OVERSAMPLE * in, kK, kQ).lp, kK, kQ).lp); |
||||
for(int i = 1; i < FILTER_OVERSAMPLE; ++i) { |
||||
process(aa2.tick(aa1.tick(0, kK, kQ).lp, kK, kQ).lp); |
||||
} |
||||
|
||||
return out; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// 12 and 24 dB/oct
|
||||
class Filter { |
||||
public: |
||||
enum Type { |
||||
TYPE_LP = 0, |
||||
TYPE_BP, |
||||
TYPE_HP |
||||
}; |
||||
|
||||
enum Slope { |
||||
SLOPE_24 = 0, |
||||
SLOPE_12, |
||||
}; |
||||
|
||||
typedef struct { |
||||
float freq; |
||||
float res; |
||||
Type type; |
||||
Slope slope; |
||||
} Settings; |
||||
|
||||
private: |
||||
Settings const * settings; |
||||
SVF12 fltA, fltB; |
||||
Oversampler os; |
||||
|
||||
public: |
||||
void assign(Settings const * settings); |
||||
|
||||
inline float tick(float in, float freqMod) { |
||||
float kK = cvToK(settings->freq + freqMod); |
||||
float kQ = (float) M_SQRT1_2 + settings->res; |
||||
|
||||
return os.tick(in, [this, kK, kQ](float oin) { |
||||
SVF12::Output outA = fltA.tick(oin, kK, kQ); |
||||
|
||||
switch(settings->type) { |
||||
case TYPE_LP: |
||||
return settings->slope == SLOPE_24 ? fltB.tick(outA.lp, kK, kQ).lp : outA.lp; |
||||
case TYPE_BP: |
||||
return settings->slope == SLOPE_24 ? fltB.tick(outA.bp, kK, kQ).bp : outA.bp;
|
||||
case TYPE_HP: |
||||
return settings->slope == SLOPE_24 ? fltB.tick(outA.hp, kK, kQ).hp : outA.hp; |
||||
default: |
||||
return oin; |
||||
} |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
|
||||
#endif |
@ -1,82 +0,0 @@ |
||||
#ifndef __OSCILLATOR_H__ |
||||
#define __OSCILLATOR_H__ |
||||
|
||||
/* PolyBLEP oscillator inspired by: https://www.metafunction.co.uk/post/all-about-digital-oscillators-part-2-blits-bleps
|
||||
* Further info about BLEP at: https://pbat.ch/sndkit/blep/
|
||||
*/ |
||||
|
||||
#include <cstdlib> |
||||
#include <math.h> |
||||
|
||||
#include "util.h" |
||||
|
||||
class Oscillator { |
||||
public: |
||||
enum Mode { MODE_SINE = 0, MODE_SAW, MODE_SQUARE }; |
||||
|
||||
Mode const * mode; |
||||
float phase; // current waveform phase angle (radians)
|
||||
float value; // current amplitude value
|
||||
float driftAmount; |
||||
float driftValue; |
||||
|
||||
Oscillator() : phase(randPhase()), value(0), driftAmount(0.001f), driftValue(0) {} |
||||
|
||||
void assign(Mode const * mode); |
||||
|
||||
// Generate next output sample and advance the phase angle
|
||||
inline float tick(float cv) { |
||||
const float phaseStep = (float) (2 * CV_FREQ_MIN) * exp2f(cv) * (float) SAMPLE_RATE_INV; |
||||
|
||||
const float t = phase * (float) PIx2_INV; // Define half phase
|
||||
const float dt = phaseStep * (float) PIx2_INV; |
||||
|
||||
if (*mode == MODE_SINE) { |
||||
value = sinf(phase); // No harmonics in sine so no aliasing!! No Poly BLEPs needed!
|
||||
} else if (*mode == MODE_SAW) { |
||||
value = (2.0f * t) - 1.0f; // Render naive waveshape
|
||||
value -= polyBlep(t, dt); // Layer output of Poly BLEP on top
|
||||
} else if (*mode == MODE_SQUARE) { |
||||
if (phase < (float) M_PI) {
|
||||
value = 1.0; // Flip
|
||||
} else {
|
||||
value = -1.0; // Flop
|
||||
} |
||||
value += polyBlep(t, dt); // Layer output of Poly BLEP on top (flip)
|
||||
value -= polyBlep(fmodf(t + 0.5f, 1.0f), dt); // Layer output of Poly BLEP on top (flop)
|
||||
} |
||||
|
||||
driftValue += 0.005f * whiteNoise() - 0.00001f * driftValue; |
||||
phase += phaseStep * (1.0f + driftAmount * driftValue); |
||||
|
||||
if(phase >= (float) PIx2) { // wrap if phase angle >=360º
|
||||
phase -= PIx2; |
||||
} |
||||
|
||||
return value; |
||||
} |
||||
|
||||
inline float polyBlep(float t, float dt) { |
||||
// t-t^2/2 +1/2
|
||||
// 0 < t <= 1
|
||||
// discontinuities between 0 & 1
|
||||
if (t < dt) { |
||||
t /= dt; |
||||
return t + t - t * t - 1.0f; |
||||
} |
||||
|
||||
// t^2/2 +t +1/2
|
||||
// -1 <= t <= 0
|
||||
// discontinuities between -1 & 0
|
||||
else if (t > 1.0f - dt) { |
||||
t = (t - 1.0f) / dt; |
||||
return t * t + t + t + 1.0f; |
||||
} |
||||
|
||||
// no discontinuities
|
||||
// 0 otherwise
|
||||
else return 0.0; |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -1,7 +0,0 @@ |
||||
#ifndef __GLOBALS_H__ |
||||
#define __GLOBALS_H__ |
||||
|
||||
#define SAMPLE_RATE 44100 |
||||
#define SAMPLE_RATE_INV (1.0 / SAMPLE_RATE) |
||||
|
||||
#endif |
@ -1,45 +0,0 @@ |
||||
#ifndef __SYNTH_H__ |
||||
#define __SYNTH_H__ |
||||
|
||||
#include "voicemanager.h" |
||||
#include "channel.h" |
||||
#include "dsp/frame.h" |
||||
#include "dsp/reverb.h" |
||||
|
||||
class Synth { |
||||
public: |
||||
Synth() { |
||||
for(VoiceManager::channel_t i = 0; i < 16; ++i) { |
||||
channels[i].number = i; |
||||
channels[i].voiceManager = &voiceManager; |
||||
channels[i].loadPreset(&DEFAULT_PRESET); |
||||
} |
||||
} |
||||
|
||||
void noteOn(int ch, int note, int vel); |
||||
void noteOff(int ch, int note); |
||||
void control(int ch, int cc, int val); |
||||
|
||||
inline frame tick() { |
||||
frame out{}; |
||||
frame reverbBus{}; |
||||
|
||||
for(auto& channel : channels) { |
||||
frame channelOut = channel.tick(); |
||||
out += channelOut; |
||||
reverbBus += channel.settings.reverb * channelOut; |
||||
} |
||||
|
||||
out += reverb.tick(reverbBus); |
||||
out *= 0.125f; |
||||
|
||||
return out; |
||||
} |
||||
|
||||
private: |
||||
VoiceManager voiceManager{}; |
||||
Channel channels[16]; |
||||
Reverb reverb; |
||||
}; |
||||
|
||||
#endif |
@ -1,74 +0,0 @@ |
||||
#ifndef __VOICE_H__ |
||||
#define __VOICE_H__ |
||||
|
||||
#include "globals.h" |
||||
#include "preset.h" |
||||
|
||||
#include "dsp/oscillator.h" |
||||
#include "dsp/filter.h" |
||||
#include "dsp/adsr.h" |
||||
|
||||
class Voice { |
||||
public: |
||||
typedef struct { |
||||
float unison; |
||||
|
||||
Oscillator::Mode osc1Mode; |
||||
Oscillator::Mode osc2Mode; |
||||
float oscMix; |
||||
float oscDetune; |
||||
float osc2Pitch; |
||||
|
||||
float noiseMix; |
||||
|
||||
Filter::Settings filter; |
||||
|
||||
ADSR::Envelope ampEnv; |
||||
ADSR::Envelope modEnv; |
||||
|
||||
float modEnvFltGain; |
||||
float keyTrack; |
||||
float lfoStep; |
||||
float lfoPitchMod; |
||||
float lfoFltMod; |
||||
|
||||
float reverb; |
||||
} Settings; |
||||
|
||||
private: |
||||
Settings const * settings; |
||||
|
||||
ADSR adsrAmp; |
||||
ADSR adsrMod; |
||||
Oscillator osc1; |
||||
Oscillator osc2; |
||||
Filter filter; |
||||
|
||||
public: |
||||
int note; |
||||
float gain; |
||||
float pan; |
||||
float detune; |
||||
|
||||
void reset(); |
||||
void assign(Settings const * settings); |
||||
void noteOn(int note, float detune = 0, float gain = 1.0, float pan = 0.5); |
||||
void noteOff(); |
||||
inline bool isIdle() { return adsrAmp.isIdle(); } |
||||
|
||||
inline float tick(float pitchMod, float fltMod) { |
||||
const float basePitch = note + pitchMod + detune; |
||||
const float osc1CV = noteToCV(basePitch - settings->oscDetune); |
||||
const float osc2CV = noteToCV(basePitch + settings->oscDetune + settings->osc2Pitch); |
||||
float out = 0; |
||||
out += (1 - settings->oscMix) * osc1.tick(osc1CV); |
||||
out += settings->oscMix * osc2.tick(osc2CV); |
||||
out += settings->noiseMix * ((1.f / 3.f) * (whiteNoise() + whiteNoise() + whiteNoise()) - out); |
||||
const float keyTrackVal = settings->keyTrack * (note - 72) / 12.f; |
||||
out = filter.tick(out, settings->modEnvFltGain * adsrMod.tick() + keyTrackVal + fltMod); |
||||
out *= gain * adsrAmp.tick(); |
||||
return out; |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,75 @@ |
||||
#ifndef __PART_H__ |
||||
#define __PART_H__ |
||||
|
||||
#include <unordered_map> |
||||
#include <set> |
||||
|
||||
#include "globals.h" |
||||
#include "voicemanager.h" |
||||
#include "voice.h" |
||||
#include "preset.h" |
||||
#include "dsp/frame.h" |
||||
#include "luts.h" |
||||
|
||||
class Channel { |
||||
public: |
||||
VoiceManager::channel_t number; |
||||
VoiceManager* voiceManager; |
||||
Voice::Settings settings; |
||||
|
||||
Channel() : volume(1), pitchBend(0), modulation(0), lfoPhase(randFrac()) {} |
||||
|
||||
void loadPreset(const Preset* preset); |
||||
|
||||
void noteOn(int note, int vel); |
||||
void noteOff(int note); |
||||
void control(int cc, int val); |
||||
|
||||
bool tick(float *out, const size_t bufferSize) { |
||||
Voice** voices = voiceManager->getChannelVoices(number); |
||||
if(*voices == NULL) { |
||||
return false; |
||||
} |
||||
|
||||
const size_t numFrames = bufferSize / 2; |
||||
|
||||
float lfo[numFrames]; |
||||
for(size_t i = 0; i < numFrames; ++i) { |
||||
lfo[i] = fastSin(lfoPhase); |
||||
|
||||
lfoPhase += settings.lfoStep; |
||||
lfoPhase -= floorf(lfoPhase); |
||||
} |
||||
|
||||
float pitchMod[numFrames]; |
||||
for(size_t i = 0; i < numFrames; ++i) { |
||||
pitchMod[i] = settings.lfoPitchMod * lfo[i]; |
||||
} |
||||
|
||||
float fltMod[numFrames]; |
||||
for(size_t i = 0; i < numFrames; ++i) { |
||||
fltMod[i] = settings.lfoFltMod * lfo[i]; |
||||
} |
||||
|
||||
std::fill(out, out + bufferSize, 0.f); |
||||
for(Voice** voice = voices; *voice != NULL; ++voice) { |
||||
float voiceOut[numFrames]; |
||||
(*voice)->tick(voiceOut, numFrames, pitchMod, fltMod); |
||||
|
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
out[i] += voiceOut[i]; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
private: |
||||
float volume; |
||||
float pitchBend; |
||||
float modulation; |
||||
|
||||
float lfoPhase; |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,74 @@ |
||||
#ifndef __DELAYLINE_H__ |
||||
#define __DELAYLINE_H__ |
||||
|
||||
#include <cstddef> |
||||
|
||||
template <size_t CAPACITY> |
||||
class DelayLine { |
||||
private: |
||||
float samples[CAPACITY] = {0}; |
||||
size_t cursor; |
||||
|
||||
public: |
||||
size_t size; |
||||
|
||||
DelayLine(size_t size = CAPACITY) : size(size) {} |
||||
|
||||
inline float get() { |
||||
while(cursor >= size) { |
||||
cursor -= size; |
||||
} |
||||
return samples[cursor]; |
||||
} |
||||
|
||||
inline float tick() { |
||||
++cursor; |
||||
while(cursor >= size) { |
||||
cursor -= size; |
||||
} |
||||
return samples[cursor]; |
||||
} |
||||
|
||||
inline float tick(float in) { |
||||
while(cursor >= size) { |
||||
cursor -= size; |
||||
} |
||||
samples[cursor++] = in; |
||||
if(cursor == size) { |
||||
cursor = 0; |
||||
} |
||||
return samples[cursor]; |
||||
} |
||||
|
||||
inline void inject(float in, size_t pos) { |
||||
size_t index = cursor + pos; |
||||
while(index >= size) { |
||||
index -= size; |
||||
} |
||||
samples[index] = in; |
||||
} |
||||
|
||||
inline void inject(float* in, size_t num, size_t pos = 0) { |
||||
size_t index = cursor + pos; |
||||
while(index >= size) { |
||||
index -= size; |
||||
} |
||||
for(size_t i = 0; i < num; ++i) { |
||||
samples[index++] = *(in++); |
||||
if(index == size) { |
||||
index = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
inline float tap(size_t n) { |
||||
size_t index = cursor + n; |
||||
if(index < size) { |
||||
return samples[index]; |
||||
} else { |
||||
return samples[index - size]; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,124 @@ |
||||
#ifndef __SVF_H__ |
||||
#define __SVF_H__ |
||||
|
||||
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */ |
||||
|
||||
#include <cmath> |
||||
#include <cfloat> |
||||
#include <iostream> |
||||
#include <algorithm> |
||||
#include <functional> |
||||
|
||||
#include "../globals.h" |
||||
#include "../util.h" |
||||
#include "../luts.h" |
||||
|
||||
#define FILTER_K_SCALE (2 * CV_FREQ_MIN / SAMPLE_RATE) |
||||
|
||||
inline float cvToK(float cv) { |
||||
return tanf((float) M_PI_2 * std::min(0.5f, (float) FILTER_K_SCALE * exp2f(cv))); |
||||
} |
||||
|
||||
class SVF12 { |
||||
protected: |
||||
float hp, bp, lp; |
||||
float as1, as2; |
||||
|
||||
public: |
||||
SVF12() : hp(0), bp(0), lp(0), as1(0), as2(0) {} |
||||
|
||||
inline void tick(float *in, float *hpo, float *bpo, float *lpo, size_t bufferSize, float *kK, float *kQ) { |
||||
float kQ_1[bufferSize]; |
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
kQ_1[i] = 1.f / kQ[i]; |
||||
} |
||||
float kMul[bufferSize]; |
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
kMul[i] = 1.f / (1.f + kK[i]*kQ_1[i] + kK[i]*kK[i]); |
||||
} |
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
hp = (in[i] - (kQ_1[i] + kK[i]) * as1 - as2) * kMul[i]; |
||||
float au = hp * kK[i]; |
||||
bp = au + as1; |
||||
as1 = au + bp; |
||||
au = bp * kK[i]; |
||||
lp = au + as2; |
||||
as2 = au + lp; |
||||
|
||||
hpo && (hpo[i] = hp); |
||||
bpo && (bpo[i] = bp); |
||||
lpo && (lpo[i] = lp); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// 12 and 24 dB/oct
|
||||
class Filter { |
||||
public: |
||||
enum Type { |
||||
TYPE_LP = 0, |
||||
TYPE_BP, |
||||
TYPE_HP |
||||
}; |
||||
|
||||
enum Slope { |
||||
SLOPE_12 = 0, |
||||
SLOPE_24, |
||||
}; |
||||
|
||||
typedef struct { |
||||
float freq; |
||||
float res; |
||||
Type type; |
||||
Slope slope; |
||||
} Settings; |
||||
|
||||
private: |
||||
Settings const * settings; |
||||
SVF12 fltA, fltB; |
||||
float freq, res; |
||||
|
||||
public: |
||||
void assign(Settings const * settings); |
||||
|
||||
inline void tick(float *in, float *out, size_t bufferSize, float *freqMod) { |
||||
float dt = 1.f / (float) bufferSize; |
||||
|
||||
float kK[bufferSize]; |
||||
float dFreq = dt * (settings->freq - freq); |
||||
for(size_t i = 0; i < bufferSize; i++) { |
||||
kK[i] = fastCVtoK(freq + freqMod[i]); |
||||
freq += dFreq; |
||||
} |
||||
float kQ[bufferSize]; |
||||
float dRes = dt * (settings->res - res); |
||||
for(size_t i = 0; i < bufferSize; i++) { |
||||
kQ[i] = (float) M_SQRT1_2 + res; |
||||
res += dRes; |
||||
} |
||||
|
||||
switch(settings->type) { |
||||
case TYPE_LP: |
||||
fltA.tick(in, NULL, NULL, out, bufferSize, kK, kQ); |
||||
if(settings->slope == SLOPE_24) { |
||||
fltB.tick(out, NULL, NULL, out, bufferSize, kK, kQ); |
||||
} |
||||
break; |
||||
case TYPE_BP: |
||||
fltA.tick(in, NULL, out, NULL, bufferSize, kK, kQ); |
||||
if(settings->slope == SLOPE_24) { |
||||
fltB.tick(out, NULL, out, NULL, bufferSize, kK, kQ); |
||||
} |
||||
break; |
||||
case TYPE_HP: |
||||
fltA.tick(in, out, NULL, NULL, bufferSize, kK, kQ); |
||||
if(settings->slope == SLOPE_24) { |
||||
fltB.tick(out, out, NULL, NULL, bufferSize, kK, kQ); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
|
||||
#endif |
@ -0,0 +1,107 @@ |
||||
#ifndef __FM_H__ |
||||
#define __FM_H__ |
||||
|
||||
#include <array> |
||||
|
||||
#include "../util.h" |
||||
|
||||
class FM { |
||||
private: |
||||
float phase1 = randPhase(), phase2 = randPhase(), phase3 = randPhase(), phase4 = randPhase(), phase5 = randPhase(), phase6 = randPhase(); |
||||
float fb1 = 0.f, fb2 = 0.f; |
||||
|
||||
public: |
||||
struct Settings { |
||||
struct { |
||||
float pitch; |
||||
float level; |
||||
} ops[6]; |
||||
}; |
||||
Settings settings; |
||||
|
||||
float tick(float pitch, float fmEnv) { |
||||
float step3 = cvToStep(pitch); |
||||
float step2 = cvToStep(pitch + 3.807354922057604f); // Frequency Coarse = 14
|
||||
float step1 = cvToStep(pitch + 0.007195501404204f); // Detune = +7
|
||||
float step6 = cvToStep(pitch); |
||||
float step5 = cvToStep(pitch - 0.007231569231076f); // Detune = -7
|
||||
float step4 = cvToStep(pitch); |
||||
|
||||
float osc3 = fmEnv * 0.333f * sinf(phase3); |
||||
float osc2 = fmEnv * 0.333f * sinf(phase2 + (float) M_PI * osc3); |
||||
float osc1 = sinf(phase1 + (float) M_PI * osc2); |
||||
|
||||
float osc6 = fmEnv * 1.0f * sinf(phase6 + 0.0f * (float) M_PI * (fb1 + fb2)); |
||||
float osc5 = fmEnv * 1.0f * sinf(phase5 + (float) M_PI * osc6); |
||||
float osc4 = sinf(phase4 + (float) M_PI * osc5); |
||||
fb2 = fb1; |
||||
fb1 = osc4; |
||||
|
||||
phase1 += step1; |
||||
if(phase1 >= (float) PIx2) { |
||||
phase1 -= PIx2; |
||||
} |
||||
phase2 += step2; |
||||
if(phase2 >= (float) PIx2) { |
||||
phase2 -= PIx2; |
||||
} |
||||
phase3 += step3; |
||||
if(phase3 >= (float) PIx2) { |
||||
phase3 -= PIx2; |
||||
} |
||||
phase4 += step4; |
||||
if(phase4 >= (float) PIx2) { |
||||
phase4 -= PIx2; |
||||
} |
||||
phase5 += step5; |
||||
if(phase5 >= (float) PIx2) { |
||||
phase5 -= PIx2; |
||||
} |
||||
phase6 += step6; |
||||
if(phase6 >= (float) PIx2) { |
||||
phase6 -= PIx2; |
||||
} |
||||
|
||||
return osc1 + osc4; |
||||
} |
||||
}; |
||||
|
||||
/*
|
||||
op6.detune 14 |
||||
op6.output_level 79 |
||||
op6.frequency_coarse 1 |
||||
|
||||
op5.detune 0 |
||||
op5.output_level 99 |
||||
op5.frequency_coarse 1 |
||||
|
||||
op4.detune 7 |
||||
op4.output_level 89 |
||||
op4.frequency_coarse 1 |
||||
|
||||
op3.detune 7 |
||||
op3.output_level 99 |
||||
op3.frequency_coarse 1 |
||||
|
||||
op2.detune 7 |
||||
op2.output_level 58 |
||||
op2.frequency_coarse 14 |
||||
|
||||
op1.detune 10 |
||||
op1.output_level 99 |
||||
op1.frequency_coarse 1 |
||||
|
||||
algorithm 4 |
||||
feedback 6 [0..7] |
||||
|
||||
RANGES |
||||
|
||||
opx.output_level [0, 99] |
||||
opX.frequency_coarse [0, 31] 0.50 - 31.00 X |
||||
op6.frequency_fine [0, 99] 1.00 - 1.99 X |
||||
opX.detune [0, 14] [-7, +7] |
||||
feedback [0, 7] |
||||
|
||||
*/ |
||||
|
||||
#endif |
@ -0,0 +1,101 @@ |
||||
#ifndef __OSCILLATOR_H__ |
||||
#define __OSCILLATOR_H__ |
||||
|
||||
/* PolyBLEP oscillator inspired by: https://www.metafunction.co.uk/post/all-about-digital-oscillators-part-2-blits-bleps
|
||||
* Further info about BLEP at: https://pbat.ch/sndkit/blep/
|
||||
*/ |
||||
|
||||
#include <cstdlib> |
||||
#include <math.h> |
||||
|
||||
#include "../util.h" |
||||
|
||||
#include "../luts.h" |
||||
|
||||
class Oscillator { |
||||
public: |
||||
enum Mode { MODE_SINE = 0, MODE_SAW, MODE_SQUARE }; |
||||
|
||||
Mode const * mode; |
||||
float phase; // current waveform phase angle (radians)
|
||||
float value; // current amplitude value
|
||||
float driftAmount; |
||||
float driftValue; |
||||
|
||||
Oscillator() : phase(randFrac()), value(0), driftAmount(0.001f), driftValue(0) {} |
||||
|
||||
void assign(Mode const * mode); |
||||
|
||||
// Generate next output sample and advance the phase angle
|
||||
inline void tick(float* out, size_t bufferSize, float *pitch, float* gain) { |
||||
size_t frameCount = bufferSize / 2; |
||||
|
||||
float driftBuf[frameCount]; |
||||
for(size_t i = 0; i < frameCount; ++i) { |
||||
driftValue += 0.005f * whiteNoise() - 0.00001f * driftValue; |
||||
driftBuf[i] = 1.0f + driftAmount * driftValue; |
||||
} |
||||
float phaseStepBuf[frameCount]; |
||||
for(size_t i = 0; i < frameCount; ++i) { |
||||
phaseStepBuf[i] = cvToStep(pitch[i]) * driftBuf[i]; |
||||
} |
||||
|
||||
float phaseBuf[frameCount]; |
||||
for(size_t i = 0; i < frameCount; ++i) { |
||||
phaseBuf[i] = phase; |
||||
phase += phaseStepBuf[i]; |
||||
phase -= floorf(phase); |
||||
} |
||||
|
||||
if(*mode == MODE_SINE) { |
||||
for(size_t i = 0, j = 0; i < frameCount; ++i) { |
||||
float value = fastSin(phaseBuf[i]); |
||||
out[j++] += gain[j & 1] * value; |
||||
out[j++] += gain[j & 1] * value; |
||||
} |
||||
} else if(*mode == MODE_SAW) { |
||||
for(size_t i = 0, j = 0; i < frameCount; ++i) { |
||||
float value = (2.0f * phaseBuf[i]) - 1.0f; // Render naive waveshape
|
||||
value -= polyBlep(phaseBuf[i], phaseStepBuf[i]); // Layer output of Poly BLEP on top
|
||||
out[j++] += gain[j & 1] * value; |
||||
out[j++] += gain[j & 1] * value; |
||||
} |
||||
} else if (*mode == MODE_SQUARE) { |
||||
for(size_t i = 0, j = 0; i < frameCount; ++i) { |
||||
float value = phaseBuf[i] < 0.5f ? 1.f : -1.f; // Render naive waveshape
|
||||
|
||||
value += polyBlep(phaseBuf[i], phaseStepBuf[i]); // Layer output of Poly BLEP on top (flip)
|
||||
value -= polyBlep(fmodf(phaseBuf[i] + 0.5f, 1.0f), phaseStepBuf[i]); // Layer output of Poly BLEP on top (flop)
|
||||
|
||||
out[j++] = gain[j & 1] * value; |
||||
out[j++] = gain[j & 1] * value; |
||||
} |
||||
} |
||||
} |
||||
|
||||
inline float polyBlep(float t, float dt) { |
||||
// t-t^2/2 +1/2
|
||||
// 0 < t <= 1
|
||||
// discontinuities between 0 & 1
|
||||
if(t < dt) { |
||||
t /= dt; |
||||
return t + t - t * t - 1.0f; |
||||
} |
||||
|
||||
// t^2/2 +t +1/2
|
||||
// -1 <= t <= 0
|
||||
// discontinuities between -1 & 0
|
||||
else if(t > 1.0f - dt) { |
||||
t = (t - 1.0f) / dt; |
||||
return t * t + t + t + 1.0f; |
||||
} |
||||
|
||||
// no discontinuities
|
||||
// 0 otherwise
|
||||
else { |
||||
return 0.0; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,34 @@ |
||||
#ifndef __PHYS_H__ |
||||
#define __PHYS_H__ |
||||
|
||||
#include "../util.h" |
||||
#include "delayline.h" |
||||
|
||||
class Physical { |
||||
private: |
||||
DelayLine<MAX_SAMPLE_PERIOD> dl; |
||||
float lp = 0, lp2 = 0; |
||||
|
||||
public: |
||||
inline float tick(float pitch) { |
||||
int period = roundf(cvToPeriod(pitch)); |
||||
dl.size = period; |
||||
float out = dl.get(); |
||||
lp *= 0.99f; |
||||
lp += 0.90f * (out - lp); |
||||
dl.inject(lp, 0); |
||||
dl.tick(); |
||||
lp2 += 0.01f * (out - lp2); |
||||
return lp2; |
||||
} |
||||
|
||||
inline void pluck() { |
||||
float tmp[512]; |
||||
for(int i = 0; i < 512; ++i) { |
||||
tmp[i] = 8.f * (whiteNoise() + whiteNoise() + whiteNoise() + whiteNoise()); |
||||
} |
||||
dl.inject(tmp, 512); |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -1,19 +1,28 @@ |
||||
#ifndef __DELAYLINE_H__ |
||||
#define __DELAYLINE_H__ |
||||
#ifndef __STEREODELAYLINE_H__ |
||||
#define __STEREODELAYLINE_H__ |
||||
|
||||
#include <cstddef> |
||||
|
||||
#include "frame.h" |
||||
|
||||
template <size_t CAPACITY> |
||||
class DelayLine { |
||||
class StereoDelayLine { |
||||
private: |
||||
const size_t size; |
||||
frame samples[CAPACITY] = {{0, 0}}; |
||||
size_t cursor; |
||||
|
||||
public: |
||||
DelayLine(size_t size = CAPACITY) : size(size) {} |
||||
size_t size; |
||||
|
||||
StereoDelayLine(size_t size = CAPACITY) : size(size) {} |
||||
|
||||
inline frame tick() { |
||||
++cursor; |
||||
if(cursor == size) { |
||||
cursor = 0; |
||||
} |
||||
return samples[cursor]; |
||||
} |
||||
|
||||
inline frame tick(frame in) { |
||||
samples[cursor++] = in; |
@ -0,0 +1,9 @@ |
||||
#ifndef __GLOBALS_H__ |
||||
#define __GLOBALS_H__ |
||||
|
||||
#define SAMPLE_RATE 96000 |
||||
#define SAMPLE_RATE_INV (1.0 / SAMPLE_RATE) |
||||
|
||||
#define FILTER_CV_MAX 9.25 // MIDI note "135" (19912.126958213178287 Hz)
|
||||
|
||||
#endif |
@ -0,0 +1,26 @@ |
||||
#ifndef __LUTS_H__ |
||||
#define __LUTS_H__ |
||||
|
||||
#include <algorithm> |
||||
|
||||
#include "globals.h" |
||||
#include "util.h" |
||||
|
||||
#define SIN_LUT_SIZE 256 |
||||
#define K_LUT_SIZE 256 |
||||
|
||||
extern float sinLUT[SIN_LUT_SIZE]; |
||||
extern float kLUT[K_LUT_SIZE]; |
||||
|
||||
void genLUTs(); |
||||
|
||||
inline float fastSin(float t) { |
||||
t += randFrac() * (1.f / SIN_LUT_SIZE); |
||||
return sinLUT[(size_t) (t * SIN_LUT_SIZE) & (SIN_LUT_SIZE - 1)]; |
||||
} |
||||
|
||||
inline float fastCVtoK(float cv) { |
||||
return kLUT[std::min(K_LUT_SIZE - (size_t) 1, (size_t) (cv * (K_LUT_SIZE - 1) * (1.f / (float) FILTER_CV_MAX)))]; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,65 @@ |
||||
#ifndef __SYNTH_H__ |
||||
#define __SYNTH_H__ |
||||
|
||||
#include "voicemanager.h" |
||||
#include "channel.h" |
||||
#include "dsp/frame.h" |
||||
#include "dsp/reverb.h" |
||||
#include "luts.h" |
||||
|
||||
class Synth { |
||||
public: |
||||
Synth() { |
||||
genLUTs(); |
||||
|
||||
for(VoiceManager::channel_t i = 0; i < 16; ++i) { |
||||
channels[i].number = i; |
||||
channels[i].voiceManager = &voiceManager; |
||||
channels[i].loadPreset(&DEFAULT_PRESET); |
||||
} |
||||
} |
||||
|
||||
void noteOn(int ch, int note, int vel); |
||||
void noteOff(int ch, int note); |
||||
void control(int ch, int cc, int val); |
||||
|
||||
inline void tick(float* out, const size_t bufferSize) { |
||||
std::fill(out, out + bufferSize, 0.f); |
||||
|
||||
float reverbBus[bufferSize]; |
||||
std::fill(reverbBus, reverbBus + bufferSize, 0.f); |
||||
|
||||
for(auto& channel : channels) { |
||||
float chOut[bufferSize]; |
||||
if(!channel.tick(chOut, bufferSize)) { |
||||
continue; |
||||
} |
||||
|
||||
for(size_t i = 0; i < bufferSize; ++i) {
|
||||
out[i] += chOut[i]; |
||||
} |
||||
|
||||
for(size_t i = 0; i < bufferSize; ++i) {
|
||||
reverbBus[i] += channel.settings.reverb * chOut[i]; |
||||
} |
||||
} |
||||
|
||||
for(size_t i = 0; i < bufferSize; i += 2) { |
||||
frame f = {reverbBus[i], reverbBus[i + 1]}; |
||||
f = reverb.tick(f); |
||||
out[i] += f.l; |
||||
out[i + 1] += f.r; |
||||
} |
||||
|
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
out[i] *= 0.125f; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
VoiceManager voiceManager{}; |
||||
Channel channels[16]; |
||||
Reverb reverb; |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,127 @@ |
||||
#ifndef __VOICE_H__ |
||||
#define __VOICE_H__ |
||||
|
||||
#include "globals.h" |
||||
#include "preset.h" |
||||
|
||||
#include "dsp/oscillator.h" |
||||
#include "dsp/filter.h" |
||||
#include "dsp/adsr.h" |
||||
|
||||
#define MAX_OSCS 8 |
||||
|
||||
class Voice { |
||||
public: |
||||
typedef struct { |
||||
size_t unison; |
||||
|
||||
Oscillator::Mode osc1Mode; |
||||
Oscillator::Mode osc2Mode; |
||||
float oscMix; |
||||
float oscDetune; |
||||
float osc2Pitch; |
||||
|
||||
float noiseMix; |
||||
|
||||
Filter::Settings filter; |
||||
|
||||
ADSR::Envelope ampEnv; |
||||
ADSR::Envelope modEnv; |
||||
|
||||
float modEnvFltGain; |
||||
float keyTrack; |
||||
float lfoStep; |
||||
float lfoPitchMod; |
||||
float lfoFltMod; |
||||
|
||||
float reverb; |
||||
} Settings; |
||||
|
||||
private: |
||||
Settings const * settings; |
||||
|
||||
ADSR adsrAmp; |
||||
ADSR adsrMod; |
||||
Oscillator osc[MAX_OSCS]; |
||||
Filter filter; |
||||
|
||||
public: |
||||
int note; |
||||
float velocity; |
||||
float gain; |
||||
|
||||
void reset(); |
||||
void assign(Settings const * settings); |
||||
void noteOn(int note, float velocity = 1, float gain = 1.0f); |
||||
void noteOff(); |
||||
inline bool isIdle() { return adsrAmp.isIdle(); } |
||||
|
||||
inline void tick(float *out, const size_t bufferSize, float *pitchMod, float *fltMod) { |
||||
const size_t frameCount = bufferSize / 2; |
||||
|
||||
float basePitchBuf[frameCount]; |
||||
for(size_t i = 0; i < frameCount; ++i) { |
||||
basePitchBuf[i] = note + pitchMod[i]; |
||||
} |
||||
|
||||
float oscCVBuf[MAX_OSCS][frameCount]; |
||||
float detune = -settings->oscDetune * 0.5f * (settings->unison - 1); |
||||
for(size_t i = 0; i < settings->unison; ++i) { |
||||
float transpose = (i & 1) ? settings->osc2Pitch : 0; |
||||
for(size_t j = 0; j < frameCount; ++j) { |
||||
oscCVBuf[i][j] = noteToCV(basePitchBuf[i] + detune + transpose); |
||||
} |
||||
detune += settings->oscDetune; |
||||
} |
||||
|
||||
float pan, panStep; |
||||
if(settings->unison > 1) { |
||||
pan = 0.f; |
||||
panStep = 1.f / (settings->unison - 1); |
||||
} else { |
||||
pan = 0.5f; |
||||
panStep = 0.f; |
||||
} |
||||
|
||||
float oscBuf[bufferSize]; |
||||
std::fill(oscBuf, oscBuf + bufferSize, 0.f); |
||||
for(size_t i = 0; i < settings->unison; ++i) { |
||||
float baseGain = (i & 1) ? settings->oscMix : (1 - settings->oscMix); |
||||
float gain[2]; |
||||
gain[0] = sinf((1.f - pan) * (float) M_PI_2); |
||||
gain[1] = sinf(pan * (float) M_PI_2); |
||||
pan += panStep; |
||||
osc[i].tick(oscBuf, bufferSize, oscCVBuf[i], gain); |
||||
} |
||||
|
||||
float noiseBuf[bufferSize]; |
||||
if(settings->unison > 1) { // Stereo noise
|
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
noiseBuf[i] = (1.f / 3.f) * (whiteNoise() + whiteNoise() + whiteNoise()); |
||||
} |
||||
} else { // Mono noise
|
||||
for(size_t i = 0; i < bufferSize; ) { |
||||
float noise = (1.f / 3.f) * (whiteNoise() + whiteNoise() + whiteNoise()); |
||||
noiseBuf[i++] = noise; |
||||
noiseBuf[i++] = noise; |
||||
} |
||||
} |
||||
|
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
oscBuf[i] += settings->noiseMix * (noiseBuf[i] - oscBuf[i]); |
||||
} |
||||
|
||||
const float keyTrackVal = settings->keyTrack * (note - 24) / 12.f; |
||||
|
||||
float fltModBuf[frameCount]; |
||||
for(size_t i = 0; i < frameCount; ++i) { |
||||
fltModBuf[i] = settings->modEnvFltGain * adsrMod.tick() + keyTrackVal + fltMod[i]; |
||||
} |
||||
filter.tick(oscBuf, out, bufferSize, fltModBuf); |
||||
for(size_t i = 0; i < bufferSize; ++i) { |
||||
out[i] *= gain * adsrAmp.tick(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -1,4 +1,4 @@ |
||||
#include "synth/dsp/adsr.h" |
||||
#include "dsp/adsr.h" |
||||
|
||||
void ADSR::reset() { |
||||
state = IDLE; |
@ -1,5 +1,7 @@ |
||||
#include "synth/dsp/filter.h" |
||||
#include "dsp/filter.h" |
||||
|
||||
void Filter::assign(Settings const * settings) { |
||||
this->settings = settings; |
||||
freq = settings->freq; |
||||
res = settings->res; |
||||
} |
@ -1,4 +1,4 @@ |
||||
#include "synth/dsp/oscillator.h" |
||||
#include "dsp/oscillator.h" |
||||
|
||||
void Oscillator::assign(Mode const * mode) { |
||||
this->mode = mode; |
@ -1,6 +1,6 @@ |
||||
#include <cmath> |
||||
|
||||
#include "synth/dsp/reverb.h" |
||||
#include "dsp/reverb.h" |
||||
|
||||
Reverb::Reverb() { |
||||
size_t interval = REVERB_FRAMES / REVERB_TAPS - 4096 / REVERB_TAPS; |
@ -0,0 +1,19 @@ |
||||
#include <cmath> |
||||
//#include <cstdio>
|
||||
|
||||
#include "luts.h" |
||||
|
||||
#include "dsp/filter.h" |
||||
|
||||
float sinLUT[SIN_LUT_SIZE]; |
||||
float kLUT[K_LUT_SIZE]; |
||||
|
||||
void genLUTs() { |
||||
for(size_t i = 0; i < SIN_LUT_SIZE; ++i) { |
||||
sinLUT[i] = sin((i * 2 * M_PI) / SIN_LUT_SIZE); |
||||
} |
||||
|
||||
for(size_t i = 0; i < K_LUT_SIZE; ++i) { |
||||
kLUT[i] = cvToK(i * FILTER_CV_MAX / (K_LUT_SIZE - 1)); |
||||
} |
||||
} |
@ -1,18 +1,18 @@ |
||||
#include <cstdio> |
||||
|
||||
#include "synth/synth.h" |
||||
#include "synth.h" |
||||
|
||||
void Synth::noteOn(int ch, int note, int vel) { |
||||
channels[ch].noteOn(note, vel); |
||||
printf("Note On: ch=%d note=%d vel=%d\n", ch, note, vel); |
||||
channels[ch].noteOn(note, vel); |
||||
} |
||||
|
||||
void Synth::noteOff(int ch, int note) { |
||||
channels[ch].noteOff(note); |
||||
printf("Note Off: ch=%d note=%d\n", ch, note); |
||||
channels[ch].noteOff(note); |
||||
} |
||||
|
||||
void Synth::control(int ch, int cc, int val) { |
||||
channels[ch].control(cc, val); |
||||
printf("Controller: ch=%d cc=%d val=%d\n", ch, cc, val); |
||||
channels[ch].control(cc, val); |
||||
} |
@ -1,7 +1,7 @@ |
||||
#include <cstdio> |
||||
#include <algorithm> |
||||
|
||||
#include "synth/voicemanager.h" |
||||
#include "voicemanager.h" |
||||
|
||||
#define VOICE_SCORE_MSB (8 * sizeof(score_t) - 1) |
||||
|
@ -1,18 +0,0 @@ |
||||
float centLUT[] = { |
||||
1.0000000, 1.0004549, 1.0009100, 1.0013654, 1.0018209, 1.0022767, 1.0027326, 1.0031888,
|
||||
1.0036452, 1.0041018, 1.0045586, 1.0050156, 1.0054728, 1.0059302, 1.0063878, 1.0068456,
|
||||
1.0073037, 1.0077620, 1.0082204, 1.0086790, 1.0091379, 1.0095969, 1.0100563, 1.0105158,
|
||||
1.0109755, 1.0114354, 1.0118955, 1.0123559, 1.0128164, 1.0132772, 1.0137382, 1.0141994,
|
||||
1.0146607, 1.0151223, 1.0155841, 1.0160462, 1.0165083, 1.0169708, 1.0174334, 1.0178963,
|
||||
1.0183593, 1.0188227, 1.0192862, 1.0197498, 1.0202137, 1.0206778, 1.0211421, 1.0216067,
|
||||
1.0220715, 1.0225364, 1.0230016, 1.0234669, 1.0239326, 1.0243984, 1.0248644, 1.0253307,
|
||||
1.0257971, 1.0262637, 1.0267307, 1.0271977, 1.0276650, 1.0281326, 1.0286002, 1.0290682,
|
||||
1.0295364, 1.0300047, 1.0304732, 1.0309421, 1.0314111, 1.0318803, 1.0323497, 1.0328194,
|
||||
1.0332892, 1.0337592, 1.0342295, 1.0347000, 1.0351708, 1.0356417, 1.0361128, 1.0365841,
|
||||
1.0370557, 1.0375276, 1.0379995, 1.0384717, 1.0389441, 1.0394168, 1.0398897, 1.0403627,
|
||||
1.0408360, 1.0413095, 1.0417832, 1.0422572, 1.0427313, 1.0432056, 1.0436803, 1.0441550,
|
||||
1.0446301, 1.0451053, 1.0455807, 1.0460564, 1.0465323, 1.0470084, 1.0474846, 1.0479612,
|
||||
1.0484380, 1.0489149, 1.0493921, 1.0498694, 1.0503471, 1.0508249, 1.0513029, 1.0517812,
|
||||
1.0522597, 1.0527384, 1.0532173, 1.0536964, 1.0541759, 1.0546554, 1.0551351, 1.0556152,
|
||||
1.0560954, 1.0565759, 1.0570565, 1.0575374, 1.0580184, 1.0584998, 1.0589813, 1.0594631 |
||||
}; |
@ -1,10 +0,0 @@ |
||||
float kLUT[] = { |
||||
0.0015708, 0.0017336, 0.0019134, 0.0021118, 0.0023307, 0.0025723, 0.0028390, 0.0031333,
|
||||
0.0034582, 0.0038167, 0.0042124, 0.0046491, 0.0051311, 0.0056631, 0.0062502, 0.0068982,
|
||||
0.0076134, 0.0084028, 0.0092740, 0.0102355, 0.0112968, 0.0124681, 0.0137608, 0.0151877,
|
||||
0.0167625, 0.0185007, 0.0204193, 0.0225369, 0.0248743, 0.0274544, 0.0303024, 0.0334462,
|
||||
0.0369167, 0.0407480, 0.0449779, 0.0496483, 0.0548053, 0.0605004, 0.0667905, 0.0737388,
|
||||
0.0814158, 0.0898997, 0.0992782, 0.1096492, 0.1211226, 0.1338222, 0.1478880, 0.1634790,
|
||||
0.1807770, 0.1999912, 0.2213640, 0.2451794, 0.2717728, 0.3015462, 0.3349871, 0.3726965,
|
||||
0.4154281, 0.4641461, 0.5201120, 0.5850177, 0.6611994, 0.7519926, 0.8623521, 1.0000000 |
||||
}; |
@ -1,18 +0,0 @@ |
||||
float noteLUT[] = { |
||||
8.1757994, 8.6619568, 9.1770239, 9.7227182, 10.3008614, 10.9133825, 11.5623255, 12.2498569,
|
||||
12.9782715, 13.7500000, 14.5676174, 15.4338531, 16.3515987, 17.3239136, 18.3540478, 19.4454365,
|
||||
20.6017227, 21.8267651, 23.1246510, 24.4997139, 25.9565430, 27.5000000, 29.1352348, 30.8677063,
|
||||
32.7031975, 34.6478271, 36.7080956, 38.8908730, 41.2034454, 43.6535301, 46.2493019, 48.9994278,
|
||||
51.9130859, 55.0000000, 58.2704697, 61.7354126, 65.4063950, 69.2956543, 73.4161911, 77.7817459,
|
||||
82.4068909, 87.3070602, 92.4986038, 97.9988556, 103.8261719, 110.0000000, 116.5409393, 123.4708252,
|
||||
130.8127899, 138.5913086, 146.8323822, 155.5634918, 164.8137817, 174.6141205, 184.9972076, 195.9977112,
|
||||
207.6523438, 220.0000000, 233.0818787, 246.9416504, 261.6255798, 277.1826172, 293.6647644, 311.1269836,
|
||||
329.6275635, 349.2282410, 369.9944153, 391.9954224, 415.3046875, 440.0000000, 466.1637573, 493.8833008,
|
||||
523.2511597, 554.3652344, 587.3295288, 622.2539673, 659.2551270, 698.4564819, 739.9888306, 783.9908447,
|
||||
830.6093750, 880.0000000, 932.3275146, 987.7666016, 1046.5023193, 1108.7304688, 1174.6590576, 1244.5079346,
|
||||
1318.5102539, 1396.9129639, 1479.9776611, 1567.9816895, 1661.2187500, 1760.0000000, 1864.6550293, 1975.5332031,
|
||||
2093.0046387, 2217.4609375, 2349.3181152, 2489.0158691, 2637.0205078, 2793.8259277, 2959.9553223, 3135.9633789,
|
||||
3322.4375000, 3520.0000000, 3729.3100586, 3951.0664062, 4186.0092773, 4434.9218750, 4698.6362305, 4978.0317383,
|
||||
5274.0410156, 5587.6518555, 5919.9106445, 6271.9267578, 6644.8750000, 7040.0000000, 7458.6201172, 7902.1328125,
|
||||
8372.0185547, 8869.8437500, 9397.2724609, 9956.0634766, 10548.0820312, 11175.3037109, 11839.8212891, 12543.8535156 |
||||
}; |
Loading…
Reference in new issue