You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.2 KiB
273 lines
8.2 KiB
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
#include "synth.h"
|
|
#include "config.h"
|
|
|
|
#include "tables.inc"
|
|
|
|
void synth_init(struct Synth *synth) {
|
|
synth_set_cutoff_freq(synth, 0);
|
|
synth_set_resonance(synth, 0);
|
|
srandom(1234);
|
|
synth->key_stack_size = 0;
|
|
synth->tuning = 440.0;
|
|
synth->volume = 1.0;
|
|
synth->pitch_bend = 0.0;
|
|
synth->lfo_depth = 0.0;
|
|
sine_osc_init(&synth->lfo_osc);
|
|
for(int i = 0; i < SYNTH_NUM_VOICES; i++) {
|
|
voice_init(&synth->voices[i], synth);
|
|
}
|
|
}
|
|
|
|
static float detune(float freq, float semitones) {
|
|
return freq * pow(2, semitones / 12.0);
|
|
}
|
|
|
|
static float midifreq(uint8_t note) {
|
|
return pow(2, (note - 69) / 12.0) * 440.0;
|
|
}
|
|
|
|
static void synth_set_pitch(struct Synth *synth) {
|
|
float lfo_detune = synth->lfo_depth * sine_osc_sample(&synth->lfo_osc);
|
|
for(int i = 0; i < SYNTH_NUM_VOICES; i++) {
|
|
struct Voice *voice = &synth->voices[i];
|
|
if(voice->osc_env.state != EnvNone)
|
|
oscillator_set_freq(&voice->osc, detune(midifreq(voice->note), voice->detune + synth->pitch_bend * synth->pitch_bend_range + lfo_detune));
|
|
}
|
|
}
|
|
|
|
static void synth_spread_unison(struct Synth *synth) {
|
|
synth->voices[0].detune = 0;
|
|
for(int i = 1; i <= 3; i++) {
|
|
synth->voices[i].detune = i * synth->unison_spread / 6.0;
|
|
}
|
|
for(int i = 4; i < 7; i++) {
|
|
synth->voices[i].detune = (i - 7) * synth->unison_spread / 6.0;
|
|
}
|
|
// for(int i = 0; i < 7; i++) {
|
|
// printf("voice %d detune %f\n", i, synth->voices[i].detune);
|
|
// }
|
|
}
|
|
|
|
static void synth_spread_stereo(struct Synth *synth) {
|
|
synth->voices[0].pan = 0;
|
|
for(int i = 1; i <= 7; i++) {
|
|
synth->voices[i].pan = ((i&1) * 2 - 1) * synth->stereo_spread * ((i + 1) / 2) / 3;
|
|
}
|
|
// for(int i = 0; i < 7; i++) {
|
|
// printf("voice %d pan %f\n", i, synth->voices[i].pan);
|
|
// }
|
|
}
|
|
|
|
static void synth_note_on_monophonic(struct Synth *synth, uint8_t note, uint8_t velocity) {
|
|
synth_spread_unison(synth);
|
|
synth_spread_stereo(synth);
|
|
|
|
for(int i = 0; i < 7; i++) {
|
|
struct Voice *voice = synth->voices + i;
|
|
voice->osc.phase = random();
|
|
|
|
envelope_set_attack_rate(&voice->osc_env, synth->osc_attack);
|
|
envelope_set_decay_rate(&voice->osc_env, synth->osc_decay);
|
|
envelope_set_sustain_level(&voice->osc_env, synth->osc_sustain);
|
|
envelope_set_release_rate(&voice->osc_env, synth->osc_release);
|
|
|
|
envelope_set_attack_rate(&voice->filter_env, synth->filter_attack);
|
|
envelope_set_decay_rate(&voice->filter_env, synth->filter_decay);
|
|
envelope_set_sustain_level(&voice->filter_env, synth->filter_sustain);
|
|
envelope_set_release_rate(&voice->filter_env, synth->filter_release);
|
|
|
|
voice_note_start(voice, note, velocity);
|
|
}
|
|
}
|
|
|
|
void synth_note_on(struct Synth *synth, uint8_t note, uint8_t velocity) {
|
|
if(synth->monophonic) {
|
|
synth->key_stack[synth->key_stack_size].note = note;
|
|
synth->key_stack[synth->key_stack_size].velocity = velocity;
|
|
synth->key_stack_size++;
|
|
if(synth->key_stack_size >= SYNTH_NUM_VOICES) {
|
|
memmove(synth->key_stack, synth->key_stack + 1, sizeof(synth->key_stack[0]) * (SYNTH_NUM_VOICES - 1));
|
|
synth->key_stack_size--;
|
|
}
|
|
synth_note_on_monophonic(synth, note, velocity);
|
|
} else {
|
|
struct Voice *found = 0;
|
|
uint32_t oldest_time = 0;
|
|
for(int i = 0; i < SYNTH_NUM_VOICES; i++) {
|
|
struct Voice *voice = &synth->voices[i];
|
|
if(voice->osc_env.state == EnvNone) {
|
|
found = voice;
|
|
break;
|
|
}
|
|
if(voice->time > oldest_time) {
|
|
oldest_time = voice->time;
|
|
found = voice;
|
|
}
|
|
}
|
|
|
|
envelope_set_attack_rate(&found->osc_env, synth->osc_attack);
|
|
envelope_set_decay_rate(&found->osc_env, synth->osc_decay);
|
|
envelope_set_sustain_level(&found->osc_env, synth->osc_sustain);
|
|
envelope_set_release_rate(&found->osc_env, synth->osc_release);
|
|
|
|
envelope_set_attack_rate(&found->filter_env, synth->filter_attack);
|
|
envelope_set_decay_rate(&found->filter_env, synth->filter_decay);
|
|
envelope_set_sustain_level(&found->filter_env, synth->filter_sustain);
|
|
envelope_set_release_rate(&found->filter_env, synth->filter_release);
|
|
|
|
voice_note_start(found, note, velocity);
|
|
}
|
|
}
|
|
|
|
static void synth_note_off_monophonic(struct Synth *synth) {
|
|
for(int i = 0; i < 7; i++) {
|
|
struct Voice *voice = synth->voices + i;
|
|
voice_stop(voice);
|
|
}
|
|
}
|
|
|
|
void synth_note_off(struct Synth *synth, uint8_t note, uint8_t velocity) {
|
|
if(synth->monophonic) {
|
|
if(synth->key_stack_size > 1) {
|
|
if(synth->key_stack[synth->key_stack_size - 1].note == note) {
|
|
synth_note_on_monophonic(synth, synth->key_stack[synth->key_stack_size - 2].note, synth->key_stack[synth->key_stack_size - 2].velocity);
|
|
} else {
|
|
for(int i = 0; i < synth->key_stack_size - 1; i++) {
|
|
if(synth->key_stack[i].note == note) {
|
|
memmove(synth->key_stack + i, synth->key_stack + i + 1, sizeof(synth->key_stack[0]) * (synth->key_stack_size - i - 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
synth_note_off_monophonic(synth);
|
|
}
|
|
synth->key_stack_size--;
|
|
} else {
|
|
for(int i = 0; i < SYNTH_NUM_VOICES; i++) {
|
|
struct Voice *voice = synth->voices + i;
|
|
if(voice->note == note && voice->osc_env.state != EnvNone && voice->osc_env.state != EnvRelease) {
|
|
voice_stop(&synth->voices[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void synth_render_sample(struct Synth *synth, float *out) {
|
|
float smpl[2] = { 0, 0 };
|
|
|
|
synth_set_pitch(synth);
|
|
|
|
for(int i = 0; i < SYNTH_NUM_VOICES; i++) {
|
|
struct Voice *v = &synth->voices[i];
|
|
|
|
float vsmpl[2];
|
|
voice_render_sample(v, vsmpl);
|
|
for(int j = 0; j < 2; j++) {
|
|
smpl[j] += vsmpl[j];
|
|
if(smpl[j] > 1) smpl[j] = 1;
|
|
else if(smpl[j] < -1) smpl[j] = -1;
|
|
}
|
|
}
|
|
|
|
out[0] = smpl[0];
|
|
out[1] = smpl[1];
|
|
synth->time++;
|
|
}
|
|
|
|
void synth_set_cutoff_freq(struct Synth *synth, uint8_t f) {
|
|
synth->cutoff = f / 127.0;
|
|
}
|
|
|
|
void synth_set_resonance(struct Synth *synth, uint8_t f) {
|
|
for(int i = 0; i < SYNTH_NUM_VOICES; i++) {
|
|
filter_set_resonance(&synth->voices[i].filter, f / 127.0);
|
|
}
|
|
}
|
|
|
|
void synth_set_unison_spread(struct Synth *synth, uint8_t w) {
|
|
synth->unison_spread = w / 127.0;
|
|
synth_spread_unison(synth);
|
|
}
|
|
|
|
void synth_set_stereo_spread(struct Synth *synth, uint8_t w) {
|
|
synth->stereo_spread = w / 127.0;
|
|
synth_spread_stereo(synth);
|
|
}
|
|
|
|
void synth_set_volume(struct Synth *s, uint8_t vol) {
|
|
s->volume = vol / 127.0;
|
|
}
|
|
|
|
void synth_pitch_bend(struct Synth *s, int16_t bend) {
|
|
s->pitch_bend = bend / 8191.0;
|
|
if(s->pitch_bend < -1.0) s->pitch_bend = -1.0;
|
|
if(s->pitch_bend > 1.0) s->pitch_bend = 1.0;
|
|
synth_set_pitch(s);
|
|
}
|
|
|
|
void synth_set_lfo_depth(struct Synth *s, uint8_t mod) {
|
|
s->lfo_depth = mod / 127.0;
|
|
}
|
|
|
|
void synth_load_patch(struct Synth *s, const char *filename) {
|
|
FILE *f = fopen(filename, "r");
|
|
if(!f) {
|
|
perror(filename);
|
|
return;
|
|
}
|
|
char buf[256];
|
|
while(!feof(f)) {
|
|
fgets(buf, sizeof(buf), f);
|
|
char *tok = strtok(buf, " \t");
|
|
if(tok) {
|
|
char *val = strtok(NULL, " \t");
|
|
if(val) {
|
|
if(!strcmp(tok, "lfo_freq")) {
|
|
sine_osc_set_freq(&s->lfo_osc, strtof(val, NULL));
|
|
} else if(!strcmp(tok, "osc_env.attack")) {
|
|
s->osc_attack = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "osc_env.decay")) {
|
|
s->osc_decay = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "osc_env.sustain")) {
|
|
s->osc_sustain = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "osc_env.release")) {
|
|
s->osc_release = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "filter_env.attack")) {
|
|
s->filter_attack = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "filter_env.decay")) {
|
|
s->filter_decay = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "filter_env.sustain")) {
|
|
s->filter_sustain = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "filter_env.release")) {
|
|
s->filter_release = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "filter_eg_intensity")) {
|
|
s->filter_eg_intensity = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "filter_kbd_track")) {
|
|
s->filter_kbd_track = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "pitch_bend_range")) {
|
|
s->pitch_bend_range = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "monophonic")) {
|
|
s->monophonic = atoi(val);
|
|
} else if(!strcmp(tok, "unison_spread")) {
|
|
s->unison_spread = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "stereo_spread")) {
|
|
s->stereo_spread = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "cutoff")) {
|
|
s->cutoff = strtof(val, NULL);
|
|
} else if(!strcmp(tok, "resonance")) {
|
|
for(int i = 0; i < SYNTH_NUM_VOICES; i++) {
|
|
filter_set_resonance(&s->voices[i].filter, strtof(val, NULL));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|