generates a bandlimited sawtooth, nothing else

This commit is contained in:
Gordon JC Pearce 2023-06-19 23:00:40 +01:00
parent e14f0ded1a
commit f0bc568030
4 changed files with 78 additions and 38 deletions

View File

@ -7,7 +7,7 @@
# --------------------------------------------------------------
# Project name, used for binaries
NAME = Nekobi
NAME = Nekobi-v8
# --------------------------------------------------------------
# Files to build

View File

@ -83,8 +83,7 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice,
voice->delay4 = 0.0f;
voice->c5 = 0.0f;
voice->osc_index = 0;
voice->osc1.last_waveform = -1;
voice->osc1.pos = 0.0f;
voice->osc.phase = 0.0f;
}
voice->vca_eg_phase = 0;

View File

@ -34,23 +34,6 @@
/* maximum size of a rendering burst */
#define XSYNTH_NUGGET_SIZE 64
/* minBLEP constants */
/* minBLEP table oversampling factor (must be a power of two): */
#define MINBLEP_PHASES 64
/* MINBLEP_PHASES minus one: */
#define MINBLEP_PHASE_MASK 63
/* length in samples of (truncated) step discontinuity delta: */
#define STEP_DD_PULSE_LENGTH 72
/* length in samples of (truncated) slope discontinuity delta: */
#define SLOPE_DD_PULSE_LENGTH 71
/* the longer of the two above: */
#define LONGEST_DD_PULSE_LENGTH STEP_DD_PULSE_LENGTH
/* MINBLEP_BUFFER_LENGTH must be at least XSYNTH_NUGGET_SIZE plus
* LONGEST_DD_PULSE_LENGTH, and not less than twice LONGEST_DD_PULSE_LENGTH: */
#define MINBLEP_BUFFER_LENGTH 512
/* delay between start of DD pulse and the discontinuity, in samples: */
#define DD_SAMPLE_DELAY 4
struct _nekobee_patch_t {
float tuning;
unsigned char waveform;
@ -71,12 +54,9 @@ enum nekobee_voice_status {
of envelopes */
};
struct blosc {
int last_waveform, /* persistent */
waveform, /* comes from LADSPA port each cycle */
bp_high; /* persistent */
float pos, /* persistent */
pw; /* comes from LADSPA port each cycle */
struct blosc_t {
float phase; // 0 - 1, with 1 being 2pi radians of phase rotation
float delay; // one sample delay for blep
};
/*
@ -95,11 +75,11 @@ struct _nekobee_voice_t {
/* persistent voice state */
float prev_pitch, target_pitch, lfo_pos;
struct blosc osc1;
struct blosc_t osc;
float vca_eg, vcf_eg, accent_slug, delay1, delay2, delay3, delay4, c5;
unsigned char vca_eg_phase, vcf_eg_phase;
int osc_index; /* shared index into osc_audio */
float osc_audio[MINBLEP_BUFFER_LENGTH];
float osc_audio[XSYNTH_NUGGET_SIZE];
float freqcut_buf[XSYNTH_NUGGET_SIZE];
float vca_buf[XSYNTH_NUGGET_SIZE];
};
@ -112,13 +92,6 @@ struct _nekobee_voice_t {
extern float nekobee_pitch[128];
typedef struct {
float value, delta;
} float_value_delta;
extern float_value_delta step_dd_table[];
extern float slope_dd_table[];
/* nekobee_voice.c */
nekobee_voice_t *nekobee_voice_new();
void nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice,
@ -147,7 +120,7 @@ void nekobee_voice_render(nekobee_synth_t *synth, float *out,
static inline void nekobee_voice_off(nekobee_voice_t *voice) {
voice->status = XSYNTH_VOICE_OFF;
/* silence the oscillator buffer for the next use */
memset(voice->osc_audio, 0, MINBLEP_BUFFER_LENGTH * sizeof(float));
memset(voice->osc_audio, 0, XSYNTH_NUGGET_SIZE * sizeof(float));
/* -FIX- decrement active voice count? */
}

View File

@ -20,25 +20,93 @@
// complete rewrite of the voice engine
#include <stdint.h>
#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;
}