From 2d4ffb346b634a9141c018f245658a927872e882 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 20 Jan 2010 07:49:38 +0000 Subject: [PATCH] Oops, lots of changes --- engine.c | 53 +++++++++++++++++++++++++++++++-- engine.h | 17 +++++++++++ filter.c | 52 ++++++++++++++++++++++++++++++++ filter.h | 18 +++++++++++- osc.c | 33 +++++++++++++-------- osc.h | 40 ++++++++++++++++++------- psyn.c | 55 ++++++++++++++++++++++++++++++---- psyn.ttl | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ voice.c | 77 ++++++++++++++++++++++++++++++++++++++++-------- voice.h | 3 +- 10 files changed, 391 insertions(+), 47 deletions(-) diff --git a/engine.c b/engine.c index a3c0a12..b99737e 100644 --- a/engine.c +++ b/engine.c @@ -6,11 +6,14 @@ #include "osc.h" #include "filter.h" #include "voice.h" +#include "control.h" #include "engine.h" #include "psyn.h" static double _freqs[128]; +struct engine_t _engine; + void engine_init() { uint32_t i; @@ -21,7 +24,14 @@ void engine_init() osc_init(); env_init(&_env, 0.0125, 0.025, 5.0, 0.0, 0.25); - env_init(&_env2, 0.0125, 0.025, 0.5, 0.0, 0.25); + env_init(&_env2, 0.0, 0.0, 0.5, 0.0, 0.25); + + osc_setfreq(&_engine.osc[0], 10.0); + osc_setfreq(&_engine.osc[1], 2.0); + osc_setfreq(&_engine.osc[2], 1.0); + _engine.osc[0].level = 1.0; + _engine.osc[1].level = 1.0; + _engine.osc[2].level = 1.0; } void engine_run(struct engine_t *engine, uint32_t samples, float *left, float *right) @@ -31,14 +41,16 @@ void engine_run(struct engine_t *engine, uint32_t samples, float *left, float *r uint32_t i; for (pos = 0; pos < samples; pos++) { + control_tick(&_engine.lowpass); + left[pos] = 0.0; right[pos] = 0.0; for (i = 0; i < NUM_LFO; i++) { - osc_tick(&engine->osc[i]); + osc_tick(&_engine.osc[i]); } - v = engine->voice; + v = _engine.voice; for (i = 0; i < NUM_POLYPHONY; i++, v++) { if (!v->playing) continue; @@ -52,6 +64,19 @@ void engine_startvoice(struct engine_t *engine, uint8_t note, uint8_t velocity) struct voice_t *v = engine->voice; uint32_t i; + if (engine->monosynth > 0) { + /* Constant voice parameters */ + v->playing = true; + if (v->released || engine->monosynth == 1) v->sample = 0; + v->released = 0; + v->note = note; + v->velocity = velocity / 127.0; + + /* Voice parameters which determine sound */ + voice_init(v, _freqs[note]); + return; + } + for (i = 0; i < NUM_POLYPHONY; i++, v++) { if (v->playing) continue; @@ -80,3 +105,25 @@ void engine_endvoice(struct engine_t *engine, uint8_t note, uint8_t velocity) v->released = v->sample; } } + +void engine_controlchange(struct engine_t *engine, uint8_t controller, uint8_t value) +{ + printf("Control change %u -> %u\n", controller, value); +} + +void engine_programchange(struct engine_t *engine, uint8_t value) +{ + printf("Program change %u\n", value); +} + +void engine_aftertouch(struct engine_t *engine, uint8_t value) +{ +// printf("Aftertouch %u\n", value); + engine->aftertouch.value = value / 127.0; +} + +void engine_pitchchange(struct engine_t *engine, int16_t value) +{ + printf("Pitch %d\n", value); + engine->pitchbend.value = value; +} diff --git a/engine.h b/engine.h index b9c1332..d070059 100644 --- a/engine.h +++ b/engine.h @@ -4,11 +4,28 @@ struct engine_t { + struct control_t lowpass; + + struct control_t modwheel; + struct control_t pitchbend; + struct control_t aftertouch; + + struct control_t osc_shape[4]; + struct control_t osc_level[4]; + + int monosynth; + struct osc_t osc[NUM_LFO]; struct voice_t voice[NUM_POLYPHONY]; }; +extern struct engine_t _engine; + void engine_init(); void engine_run(struct engine_t *engine, uint32_t samples, float *left, float *right); void engine_startvoice(struct engine_t *engine, uint8_t note, uint8_t velocity); void engine_endvoice(struct engine_t *engine, uint8_t note, uint8_t velocity); +void engine_controlchange(struct engine_t *engine, uint8_t controller, uint8_t value); +void engine_programchange(struct engine_t *engine, uint8_t value); +void engine_aftertouch(struct engine_t *engine, uint8_t value); +void engine_pitchchange(struct engine_t *engine, int16_t value); diff --git a/filter.c b/filter.c index bc655da..69744a3 100644 --- a/filter.c +++ b/filter.c @@ -1,15 +1,18 @@ +#include #include #include "filter.h" #include "psyn.h" void filter_init_rc(struct filter_t *filter, double R, double C) { + memset(filter, 0, sizeof *filter); filter->k = 1.0 / (R * C) / _sample_rate; filter->r = 1.0; } void filter_init_freq(struct filter_t *filter, double freq) { + memset(filter, 0, sizeof *filter); filter->k = (2.0 * M_PI * freq) / _sample_rate; filter->r = 1.0; } @@ -31,3 +34,52 @@ void filter_run_hp(struct filter_t *filter, double left, double right) filter->last_in_l = left; filter->last_in_r = right; } + +void bw_filter_init_lp(struct bw_filter_t *f, double freq, double resonance) +{ + double c = 1.0 / tan(M_PI * freq / _sample_rate); + f->a1 = 1.0 / (1.0 + resonance * c + c * c); + f->a2 = 2 * f->a1; + f->a3 = f->a1; + f->b1 = 2.0 * (1.0 - c * c) * f->a1; + f->b2 = (1.0 - resonance * c + c * c) * f->a1; +} + +void bw_filter_run(struct bw_filter_t *f, double in_l, double in_r, double *out_l, double *out_r) +{ + *out_l = f->a1 * in_l + f->a2 * f->in1_l + f->a3 * f->in2_l - f->b1 * f->out1_l - f->b2 * f->out2_l; + *out_r = f->a1 * in_r + f->a2 * f->in1_r + f->a3 * f->in2_r - f->b1 * f->out1_r - f->b2 * f->out2_r; + + f->in2_l = f->in1_l; + f->in1_l = in_l; + f->out2_l = f->out1_l; + f->out1_l = *out_l; + + f->in2_r = f->in1_r; + f->in1_r = in_r; + f->out2_r = f->out1_r; + f->out1_r = *out_r; +} + +/* + The filter algo: + out(n) = a1 * in + a2 * in(n-1) + a3 * in(n-2) - b1*out(n-1) - b2*out(n-2) + + Lowpass: + c = 1.0 / tan(pi * f / sample_rate); + + a1 = 1.0 / ( 1.0 + r * c + c * c); + a2 = 2* a1; + a3 = a1; + b1 = 2.0 * ( 1.0 - c*c) * a1; + b2 = ( 1.0 - r * c + c * c) * a1; + + Hipass: + c = tan(pi * f / sample_rate); + + a1 = 1.0 / ( 1.0 + r * c + c * c); + a2 = -2*a1; + a3 = a1; + b1 = 2.0 * ( c*c - 1.0) * a1; + b2 = ( 1.0 - r * c + c * c) * a1; +*/ diff --git a/filter.h b/filter.h index 34d2976..79bef4d 100644 --- a/filter.h +++ b/filter.h @@ -1,5 +1,6 @@ -struct filter_t { +struct filter_t +{ double k; double r; double v_l; @@ -10,7 +11,22 @@ struct filter_t { double last_out_r; }; +struct bw_filter_t +{ + double a1; + double a2; + double a3; + double b1; + double b2; + + double in1_l, in2_l, out1_l, out2_l; + double in1_r, in2_r, out1_r, out2_r; +}; + void filter_init_rc(struct filter_t *filter, double R, double C); void filter_init_freq(struct filter_t *filter, double freq); void filter_run_lp(struct filter_t *filter, double left, double right); void filter_run_hp(struct filter_t *filter, double left, double right); + +void bw_filter_init_lp(struct bw_filter_t *f, double freq, double resonance); +void bw_filter_run(struct bw_filter_t *f, double in_l, double in_r, double *out_l, double *out_r); diff --git a/osc.c b/osc.c index 92ca612..7cef3ba 100644 --- a/osc.c +++ b/osc.c @@ -3,24 +3,33 @@ #include "psyn.h" #include "osc.h" -double _sin_table[LOOKUP_SAMPLES + 1]; -double _saw_table[LOOKUP_SAMPLES + 1]; -double _tri_table[LOOKUP_SAMPLES + 1]; +double _lookup_table[6][LOOKUP_SAMPLES + 1]; void osc_init() { int i; for (i = 0; i <= LOOKUP_SAMPLES; i++) { - _sin_table[i] = sin(2 * M_PI * (double)i / LOOKUP_SAMPLES); - } - for (i = 0; i <= LOOKUP_SAMPLES; i++) { - _saw_table[i] = 1.0 - ((double)i / (LOOKUP_SAMPLES / 2)); - } - for (i = 0; i <= LOOKUP_SAMPLES; i++) { - _tri_table[i] = (double)i / (LOOKUP_SAMPLES / 4); - if (_tri_table[i] > 1.0) _tri_table[i] = 2.0 - _tri_table[i]; - if (_tri_table[i] < -1.0) _tri_table[i] = -2.0 - _tri_table[i]; + double ph = (double)i / LOOKUP_SAMPLES; + + _lookup_table[0][i] = sin(2.0 * M_PI * ph); + + _lookup_table[1][i] = -1.0 + ph * 2.0; + + if (ph <= 0.25) _lookup_table[2][i] = ph * 4.0; + else if (ph <= 0.75) _lookup_table[2][i] = 2.0 - ph * 4.0; + else _lookup_table[2][i] = ph * 4.0 - 4.0; + + //if (_tri_table[i] > 1.0) _tri_table[i] = 2.0 - _tri_table[i]; + //if (_tri_table[i] < -1.0) _tri_table[i] = -2.0 - _tri_table[i]; + + _lookup_table[3][i] = (ph >= 0.5) ? -1.0 : 1.0; + + if (ph < 0.5) _lookup_table[4][i] = -1.0 + ph * 4.0; + else _lookup_table[4][i] = 1.0 - 2.0 * ph; + + if (ph > 0.5) _lookup_table[5][i] = 1.0 - ph; + else _lookup_table[5][i] = -1.0 + 8.0 * ph * ph; } } diff --git a/osc.h b/osc.h index d25d03c..245aed0 100644 --- a/osc.h +++ b/osc.h @@ -1,32 +1,50 @@ -#define LOOKUP_SAMPLES 360 +#define LOOKUP_SAMPLES 3600 -extern double _sin_table[LOOKUP_SAMPLES + 1]; -extern double _saw_table[LOOKUP_SAMPLES + 1]; -extern double _tri_table[LOOKUP_SAMPLES + 1]; +extern double _lookup_table[6][LOOKUP_SAMPLES + 1]; struct osc_t { double freq; double step; double ramp; + double level; - double sin; - double saw; - double tri; + uint8_t shape; }; void osc_init(); void osc_setfreq(struct osc_t *osc, double freq); +static inline void osc_setphase(struct osc_t *osc, double phase) +{ + osc->ramp = LOOKUP_SAMPLES * phase; +} + static inline void osc_tick(struct osc_t *osc) { osc->ramp += osc->step; if (osc->ramp > LOOKUP_SAMPLES) osc->ramp -= LOOKUP_SAMPLES; - uint32_t pos = floor(osc->ramp); + //uint32_t pos = floor(osc->ramp); - osc->sin = _sin_table[pos]; - osc->saw = _saw_table[pos]; - osc->tri = _tri_table[pos]; + //osc->sin = _sin_table[pos]; + //osc->saw = _saw_table[pos]; + //osc->tri = _tri_table[pos]; +} + +static inline double osc_getsample(struct osc_t *osc) +{ + uint32_t pos = osc->ramp; + return _lookup_table[osc->shape][pos] * osc->level; +} + +static inline double osc_getsamplewithphase(struct osc_t *osc, float phase) +{ + double sample = osc->ramp + phase * LOOKUP_SAMPLES; + if (sample < 0.0) sample += LOOKUP_SAMPLES; + else if (sample > LOOKUP_SAMPLES) sample -= LOOKUP_SAMPLES; + + uint32_t pos = sample; + return _lookup_table[osc->shape][pos] * osc->level; } diff --git a/psyn.c b/psyn.c index bec9c99..ed04cd9 100644 --- a/psyn.c +++ b/psyn.c @@ -8,6 +8,7 @@ #include "osc.h" #include "filter.h" #include "voice.h" +#include "control.h" #include "engine.h" #include "psyn.h" @@ -20,8 +21,10 @@ struct psyn_t int midi_event_id; float *out_l; float *out_r; - - struct engine_t eng; + float *ctrlMono; + float *ctrlLP; + float *ctrlOscShape[4]; + float *ctrlOscLevel[4]; }; static void psyn_init(struct psyn_t *psyn, uint32_t sample_rate) @@ -78,6 +81,18 @@ static void connect_port(LV2_Handle lv2instance, uint32_t port, void *data) case 2: psyn->out_r = data; break; + case 3: + psyn->ctrlMono = data; + break; + case 4: + psyn->ctrlLP = data; + break; + case 5: case 6: case 7: case 8: + psyn->ctrlOscShape[port - 5] = data; + break; + case 9: case 10: case 11: case 12: + psyn->ctrlOscLevel[port - 9] = data; + break; } } @@ -89,6 +104,7 @@ static void cleanup(LV2_Handle lv2instance) static void run(LV2_Handle lv2instance, uint32_t sample_count) { + uint8_t i; struct psyn_t *psyn = (struct psyn_t *)lv2instance; uint32_t frame = 0; LV2_Event *ev = NULL; @@ -98,6 +114,13 @@ static void run(LV2_Handle lv2instance, uint32_t sample_count) if (lv2_event_is_valid(&iterator)) ev = lv2_event_get(&iterator, NULL); + control_setstep(&_engine.lowpass, *psyn->ctrlLP, sample_count); + _engine.monosynth = (int)*psyn->ctrlMono; + for (i = 0; i < 4; i++) { + _engine.osc_shape[i].value = *psyn->ctrlOscShape[i]; + _engine.osc_level[i].value = *psyn->ctrlOscLevel[i]; + } + while (frame < sample_count) { uint32_t to; @@ -107,22 +130,42 @@ static void run(LV2_Handle lv2instance, uint32_t sample_count) to = sample_count; } - engine_run(&psyn->eng, to - frame, psyn->out_l + frame, psyn->out_r + frame); + engine_run(&_engine, to - frame, psyn->out_l + frame, psyn->out_r + frame); frame = to; if (ev != NULL) { if (ev->type == 0) { psyn->event_ref->lv2_event_unref(psyn->event_ref->callback_data, ev); - } else if (ev->type == psyn->midi_event_id && ev->size == 3) { + } else if (ev->type == psyn->midi_event_id) { uint8_t *data = (uint8_t *)(ev + 1); switch (data[0] & 0xF0) { case 0x80: - engine_endvoice(&psyn->eng, data[1], data[2]); + engine_endvoice(&_engine, data[1], data[2]); break; case 0x90: - engine_startvoice(&psyn->eng, data[1], data[2]); + engine_startvoice(&_engine, data[1], data[2]); + break; + + case 0xA0: + engine_aftertouch(&_engine, data[2]); + break; + + case 0xC0: + engine_programchange(&_engine, data[1]); + break; + + case 0xB0: + engine_controlchange(&_engine, data[1], data[2]); + break; + + case 0xD0: + engine_aftertouch(&_engine, data[1]); + break; + + case 0xE0: + engine_pitchchange(&_engine, (data[1] | data[2] << 7) - 0x2000); break; } } diff --git a/psyn.ttl b/psyn.ttl index d2d0738..e27f31e 100644 --- a/psyn.ttl +++ b/psyn.ttl @@ -34,4 +34,94 @@ lv2:index 2; lv2:symbol "out R"; lv2:name "Audio Output R"; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 3; + lv2:symbol "mono"; + lv2:name "Mono mode"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 2; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 4; + lv2:symbol "lp_freq"; + lv2:name "Low Pass Filter Frequency"; + lv2:minimum 1.0; + lv2:default 2000.0; + lv2:maximum 16000.0; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 5; + lv2:symbol "oscshape0"; + lv2:name "Oscillator 0 shape"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 5; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 6; + lv2:symbol "oscshape1"; + lv2:name "Oscillator 1 shape"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 5; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 7; + lv2:symbol "oscshape2"; + lv2:name "Oscillator 2 shape"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 5; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 8; + lv2:symbol "oscshape3"; + lv2:name "Oscillator 3 shape"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 5; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 9; + lv2:symbol "osclevel0"; + lv2:name "Oscillator 0 level"; + lv2:minimum 0; + lv2:default 1; + lv2:maximum 1; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 10; + lv2:symbol "osclevel1"; + lv2:name "Oscillator 1 level"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 1; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 11; + lv2:symbol "osclevel2"; + lv2:name "Oscillator 2 level"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 1; + ], + [ + a lv2:InputPort, lv2:ControlPort; + lv2:index 12; + lv2:symbol "osclevel3"; + lv2:name "Oscillator 3 level"; + lv2:minimum 0; + lv2:default 0; + lv2:maximum 1; ]. diff --git a/voice.c b/voice.c index d230aac..7c94f06 100644 --- a/voice.c +++ b/voice.c @@ -6,19 +6,39 @@ #include "env.h" #include "filter.h" #include "voice.h" +#include "control.h" +#include "engine.h" #include "psyn.h" #include "rng.h" +static inline double max(double a, double b) +{ + return a > b ? a : b; +} + void voice_init(struct voice_t *voice, double freq) { + uint8_t i; // filter_init_rc(&voice->fil[0], 1000.0, 0.000000047); // filter_init_rc(&voice->fil[1], 47000.0, 0.000000047); - filter_init_freq(&voice->fil[0], 2000.0); - filter_init_freq(&voice->fil[1], 150.0); +// filter_init_freq(&voice->fil[0], freq); +// filter_init_freq(&voice->fil[1], 150.0); + +// bw_filter_init_lp(&voice->bw[0], _engine.lowpass.value, 1.0); + + for (i = 0; i < VOICE_OSCILLATORS; i++) { + voice->osc[i].shape = _engine.osc_shape[i].value; + voice->osc[i].level = _engine.osc_level[i].value; + } osc_setfreq(&voice->osc[0], freq); - osc_setfreq(&voice->osc[1], freq * 1.2); - osc_setfreq(&voice->osc[2], freq * 5); + osc_setphase(&voice->osc[0], 0.0); + osc_setfreq(&voice->osc[1], freq);// * 1.4983); + osc_setphase(&voice->osc[1], 0.25); + osc_setfreq(&voice->osc[2], freq * 2); + osc_setphase(&voice->osc[2], 0.0); + osc_setfreq(&voice->osc[3], 2500.0); + osc_setphase(&voice->osc[3], 0.0); } static inline void voice_tick(struct voice_t *voice) @@ -50,22 +70,53 @@ void voice_run(struct voice_t *voice, uint32_t samples, float *left, float *righ amplitude *= voice->velocity; - l = amplitude * voice->osc[0].sin * (1.0 + rng() * 0.25); - r = amplitude * voice->osc[0].sin * (1.0 + rng() * 0.25); + float s; + + s = osc_getsample(&voice->osc[0]); + l = amplitude * s;// * (1.0 + rng() * 0.25); + r = amplitude * s;// * (1.0 + rng() * 0.25); - amplitude = env_getamplitude(&_env2, voice->sample, voice->released) * voice->velocity * voice->velocity * 1.5; + s = osc_getsample(&voice->osc[1]); + l += amplitude * s; + r += amplitude * s; - if (amplitude > 0.0) { - l += amplitude * voice->osc[1].tri * (1.0 + voice->osc[2].sin * 0.25); - r += amplitude * voice->osc[1].tri * (1.0 + voice->osc[2].sin * 0.25); - } + s = osc_getsample(&voice->osc[2]); + l += amplitude * s; + r += amplitude * s; - *left += l; - *right += r; + s = osc_getsample(&_engine.osc[0]); + // Amplitude mod +// l *= 1.0 + s; +// r *= 1.0 + s; +// l = s * (1.0 + l); +// r = s * (1.0 + r); + // Ring mod +// l *= s; +// r *= s; + +// amplitude = env_getamplitude(&_env2, voice->sample, voice->released) * voice->velocity * voice->velocity * 1.5; + +// if (amplitude > 0.0) { +// float s1 = osc_getsample(&voice->osc[1]); +// float s2 = osc_getsample(&voice->osc[2]); +// l += amplitude * s1 * (1.0 + s2 * 0.25); +// r += amplitude * s1 * (1.0 + s2 * 0.25); +// } + +// *left += l; +// *right += r; + + double out_l, out_r; + + double cutoff = max(env_getamplitude(&_env2, voice->sample, 0), _engine.aftertouch.value) * 8000.0; + bw_filter_init_lp(&voice->bw[0], _engine.lowpass.value + cutoff, 1.0); + bw_filter_run(&voice->bw[0], l, r, &out_l, &out_r); // filter_run_lp(&voice->fil[0], l, r); // filter_run_hp(&voice->fil[1], voice->fil[0].last_out_l, voice->fil[0].last_out_r); + *left += out_l; + *right += out_r; // *left += voice->fil[0].last_out_l; // *right += voice->fil[0].last_out_r; // } diff --git a/voice.h b/voice.h index fe113d9..334f9c1 100644 --- a/voice.h +++ b/voice.h @@ -11,7 +11,8 @@ struct voice_t double velocity; struct osc_t osc[VOICE_OSCILLATORS]; - struct filter_t fil[VOICE_FILTERS]; + //struct filter_t fil[VOICE_FILTERS]; + struct bw_filter_t bw[VOICE_FILTERS]; }; void voice_init(struct voice_t *voice, double freq);