113 lines
3.4 KiB
C
113 lines
3.4 KiB
C
/* nekobee DSSI software synthesizer plugin
|
|
*
|
|
* Copyright (C) 2023 Gordonjcp, with attributions inline
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the Free
|
|
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307, USA.
|
|
*/
|
|
|
|
// complete rewrite of the voice engine
|
|
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
|
|
#include "nekobee_synth.h"
|
|
|
|
// centre oscillator around Middle C
|
|
// conveniently the middle of the 303's range
|
|
#define REF_NOTE 60
|
|
|
|
float nekobee_pitch[128];
|
|
float logpot[128];
|
|
|
|
void nekobee_init_tables(void) {
|
|
// create tables used by Nekobee to save on expensive calculations
|
|
// mostly involving exponentiation!
|
|
// tables are scaled to 128 values for ease of calculation with MIDI
|
|
|
|
// it's worth noting that a real 303 only responds over four octaves
|
|
// although in theory its DAC could do five
|
|
|
|
// it's a bit of a waste defining 128 MIDI notes in the expo scale
|
|
|
|
uint8_t i;
|
|
float x;
|
|
|
|
for (i = 0; i < 128; i++) {
|
|
// expo pitch scale (MIDI note number to VCO control current)
|
|
nekobee_pitch[i] = powf(2, (i - REF_NOTE) / 12.0f);
|
|
// log pot scale used for volume, decay, cutoff, and env mod
|
|
// for a range of "0 to 1" scaled to 0-127, gives a log response
|
|
// with 50% of "pot rotation" giving 15% output
|
|
x = i / 128.0f; // pot input from 0 to 1
|
|
logpot[i] = 0.0323 * powf(32, x) - 0.0323;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void vco(nekobee_synth_t *synth, uint32_t count) {
|
|
// generate a bandlimited oscillator
|
|
// uses polyblep for bandlimiting
|
|
// massive and endless thanks to Mystran
|
|
// https://www.kvraudio.com/forum/viewtopic.php?t=398553
|
|
|
|
nekobee_voice_t *voice = synth->voice;
|
|
blosc_t *osc = &voice->osc;
|
|
|
|
uint32_t i;
|
|
|
|
float phase = osc->phase; // current running phase 0..1
|
|
float delay = osc->delay; // delay sample for polyblep
|
|
float out, t; // output sample, temporary value for blep
|
|
|
|
// calculate omega for phase shift
|
|
float w = nekobee_pitch[voice->key] * 261.63 * synth->deltat;
|
|
|
|
// FIXME this only does saws
|
|
|
|
for (i = 0; i < count; i++) {
|
|
phase += w;
|
|
out = delay;
|
|
delay = 0;
|
|
|
|
if (phase > 1.0f) {
|
|
t = (phase - 1) / w;
|
|
out -= 0.5 * t * t; // other polynomials are available
|
|
t = 1 - t;
|
|
delay += 0.5 * t * t;
|
|
phase -= 1.0f;
|
|
}
|
|
delay += phase; // save value for next time
|
|
voice->osc_audio[i] = 0.5 - out; // save output in buffer, remove DC offset
|
|
}
|
|
osc->phase = phase;
|
|
osc->delay = delay;
|
|
}
|
|
|
|
void nekobee_voice_render(nekobee_synth_t *synth, float *out, uint32_t count) {
|
|
// generate "count" samples into the buffer at out
|
|
|
|
vco(synth, count);
|
|
|
|
for(uint32_t i=0; i<count; i++) {
|
|
out[i] = synth->voice->osc_audio[i];
|
|
}
|
|
|
|
return;
|
|
(void)synth;
|
|
(void)out;
|
|
(void)count;
|
|
}
|