uxn

Varvara Ordinator, written in ANSI C(SDL2)
git clone https://git.eamoncaddigan.net/uxn.git
Log | Files | Refs | README | LICENSE

commit 87d67985930fc55f457454e8b0927c69b41ba333
parent c4ec4a6340ef2552eb38b14a103a5ff9c552e434
Author: neauoire <aliceffekt@gmail.com>
Date:   Fri, 20 Oct 2023 11:54:25 -0400

Formatting

Diffstat:
Msrc/devices/audio.c | 443+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/uxncli.c | 2+-
Msrc/uxnemu.c | 40++++++++++++++++++++--------------------
3 files changed, 249 insertions(+), 236 deletions(-)

diff --git a/src/devices/audio.c b/src/devices/audio.c @@ -19,42 +19,44 @@ WITH REGARD TO THIS SOFTWARE. #define INTERPOL_METHOD 1 typedef enum EnvStage { - ENV_ATTACK = (1 << 0), - ENV_DECAY = (1 << 1), - ENV_SUSTAIN = (1 << 2), - ENV_RELEASE = (1 << 3), + ENV_ATTACK = (1 << 0), + ENV_DECAY = (1 << 1), + ENV_SUSTAIN = (1 << 2), + ENV_RELEASE = (1 << 3), } EnvStage; typedef struct Envelope { - float a; - float d; - float s; - float r; - float vol; - EnvStage stage; + float a; + float d; + float s; + float r; + float vol; + EnvStage stage; } Envelope; typedef struct Sample { - Uint8 *data; - float len; - float pos; - float inc; - float loop; - Uint8 pitch; - Envelope env; + Uint8 *data; + float len; + float pos; + float inc; + float loop; + Uint8 pitch; + Envelope env; } Sample; typedef struct AudioChannel { - Sample sample; - Sample next_sample; - bool xfade; - float duration; - float vol_l; - float vol_r; + Sample sample; + Sample next_sample; + bool xfade; + float duration; + float vol_l; + float vol_r; } AudioChannel; AudioChannel channel[POLYPHONY]; +/* clang-format off */ + const float tuning[109] = { 0.00058853f, 0.00062352f, 0.00066060f, 0.00069988f, 0.00074150f, 0.00078559f, 0.00083230f, 0.00088179f, 0.00093423f, 0.00098978f, @@ -80,236 +82,247 @@ const float tuning[109] = { 0.25338348f, 0.26845044f, 0.28441334f, 0.30132544f, }; +/* clang-format on */ + void -env_on(Envelope *env) { - env->stage = ENV_ATTACK; - env->vol = 0.0f; - if (env->a > 0) { - env->a = (SOUND_TIMER / AUDIO_BUFSIZE) / env->a; - } else if (env->stage == ENV_ATTACK) { - env->stage = ENV_DECAY; - env->vol = 1.0f; - } - if (env->d < 10.0f) { - env->d = 10.0f; - } - env->d = (SOUND_TIMER / AUDIO_BUFSIZE) / env->d; - if (env->r < 10.0f) { - env->r = 10.0f; - } - env->r = (SOUND_TIMER / AUDIO_BUFSIZE) / env->r; +env_on(Envelope *env) +{ + env->stage = ENV_ATTACK; + env->vol = 0.0f; + if(env->a > 0) { + env->a = (SOUND_TIMER / AUDIO_BUFSIZE) / env->a; + } else if(env->stage == ENV_ATTACK) { + env->stage = ENV_DECAY; + env->vol = 1.0f; + } + if(env->d < 10.0f) { + env->d = 10.0f; + } + env->d = (SOUND_TIMER / AUDIO_BUFSIZE) / env->d; + if(env->r < 10.0f) { + env->r = 10.0f; + } + env->r = (SOUND_TIMER / AUDIO_BUFSIZE) / env->r; } void -env_off(Envelope *env) { - env->stage = ENV_RELEASE; +env_off(Envelope *env) +{ + env->stage = ENV_RELEASE; } void -note_on(AudioChannel *channel, Uint16 duration, Uint8 *data, Uint16 len, Uint8 vol, - Uint8 attack, Uint8 decay, Uint8 sustain, Uint8 release, Uint8 pitch, bool loop) { - channel->duration = duration > 0 ? duration : len / 44.1f; - channel->vol_l = (vol >> 4) / 15.0f; - channel->vol_r = (vol & 0xf) / 15.0f; - - Sample sample = {0}; - sample.data = data; - sample.len = len; - sample.pos = 0; - sample.env.a = attack * 64.0f; - sample.env.d = decay * 64.0f; - sample.env.s = sustain / 16.0f; - sample.env.r = release * 64.0f; - if (loop) { - sample.loop = len; - } else { - sample.loop = 0; - } - env_on(&sample.env); - if (pitch < 20) { - pitch = 20; - } - float sample_rate = 44100 / 261.60; - if (len <= 256) { - sample_rate = len; - } - const float *inc = &tuning[pitch - 20]; - sample.inc = *(inc) * sample_rate; - - channel->next_sample = sample; - channel->xfade = true; +note_on(AudioChannel *channel, Uint16 duration, Uint8 *data, Uint16 len, Uint8 vol, Uint8 attack, Uint8 decay, Uint8 sustain, Uint8 release, Uint8 pitch, bool loop) +{ + channel->duration = duration > 0 ? duration : len / 44.1f; + channel->vol_l = (vol >> 4) / 15.0f; + channel->vol_r = (vol & 0xf) / 15.0f; + + Sample sample = {0}; + sample.data = data; + sample.len = len; + sample.pos = 0; + sample.env.a = attack * 64.0f; + sample.env.d = decay * 64.0f; + sample.env.s = sustain / 16.0f; + sample.env.r = release * 64.0f; + if(loop) { + sample.loop = len; + } else { + sample.loop = 0; + } + env_on(&sample.env); + if(pitch < 20) { + pitch = 20; + } + float sample_rate = 44100 / 261.60; + if(len <= 256) { + sample_rate = len; + } + const float *inc = &tuning[pitch - 20]; + sample.inc = *(inc)*sample_rate; + + channel->next_sample = sample; + channel->xfade = true; } void -note_off(AudioChannel *channel, Uint16 duration) { - channel->duration = duration; - env_off(&channel->sample.env); +note_off(AudioChannel *channel, Uint16 duration) +{ + channel->duration = duration; + env_off(&channel->sample.env); } void -env_advance(Envelope *env) { - switch (env->stage) { - case ENV_ATTACK: { - env->vol += env->a; - if (env->vol >= 1.0f) { - env->stage = ENV_DECAY; - env->vol = 1.0f; - } - } break; - case ENV_DECAY: { - env->vol -= env->d; - if (env->vol <= env->s || env->d <= 0) { - env->stage = ENV_SUSTAIN; - env->vol = env->s; - } - } break; - case ENV_SUSTAIN: { - env->vol = env->s; - } break; - case ENV_RELEASE: { - if (env->vol <= 0 || env->r <= 0) { - env->vol = 0; - } else { - env->vol -= env->r; - } - } break; - } +env_advance(Envelope *env) +{ + switch(env->stage) { + case ENV_ATTACK: { + env->vol += env->a; + if(env->vol >= 1.0f) { + env->stage = ENV_DECAY; + env->vol = 1.0f; + } + } break; + case ENV_DECAY: { + env->vol -= env->d; + if(env->vol <= env->s || env->d <= 0) { + env->stage = ENV_SUSTAIN; + env->vol = env->s; + } + } break; + case ENV_SUSTAIN: { + env->vol = env->s; + } break; + case ENV_RELEASE: { + if(env->vol <= 0 || env->r <= 0) { + env->vol = 0; + } else { + env->vol -= env->r; + } + } break; + } } float -interpolate_sample(Uint8 *data, Uint16 len, float pos) { +interpolate_sample(Uint8 *data, Uint16 len, float pos) +{ #if INTERPOL_METHOD == 0 - return data[(int)pos]; + return data[(int)pos]; #elif INTERPOL_METHOD == 1 - float x = pos; - int x0 = (int)x; - int x1 = (x0 + 1); - float y0 = data[x0]; - float y1 = data[x1 % len]; - x = x - x0; - float y = y0 + x * (y1 - y0); - return y; + float x = pos; + int x0 = (int)x; + int x1 = (x0 + 1); + float y0 = data[x0]; + float y1 = data[x1 % len]; + x = x - x0; + float y = y0 + x * (y1 - y0); + return y; #elif INTERPOL_METHOD == 2 - float x = pos; - int x0 = x - 1; - int x1 = x; - int x2 = x + 1; - int x3 = x + 2; - float y0 = data[x0 % len]; - float y1 = data[x1]; - float y2 = data[x2 % len]; - float y3 = data[x3 % len]; - x = x - x1; - float c0 = y1; - float c1 = 0.5f * (y2 - y0); - float c2 = y0 - 2.5f * y1 + 2.f * y2 - 0.5f * y3; - float c3 = 1.5f * (y1 - y2) + 0.5f * (y3 - y0); - return ((c3 * x + c2) * x + c1) * x + c0; + float x = pos; + int x0 = x - 1; + int x1 = x; + int x2 = x + 1; + int x3 = x + 2; + float y0 = data[x0 % len]; + float y1 = data[x1]; + float y2 = data[x2 % len]; + float y3 = data[x3 % len]; + x = x - x1; + float c0 = y1; + float c1 = 0.5f * (y2 - y0); + float c2 = y0 - 2.5f * y1 + 2.f * y2 - 0.5f * y3; + float c3 = 1.5f * (y1 - y2) + 0.5f * (y3 - y0); + return ((c3 * x + c2) * x + c1) * x + c0; #endif } Sint16 -next_sample(Sample *sample) { - if (sample->pos >= sample->len) { - if (sample->loop == 0) { - sample->data = 0; - return 0; - } - while (sample->pos >= sample->len) { - sample->pos -= sample->loop; - } - } - - float val = interpolate_sample(sample->data, sample->len, sample->pos); - val *= sample->env.vol; - Sint8 next = (Sint8)0x80 ^ (Uint8)val; - - sample->pos += sample->inc; - env_advance(&sample->env); - return next; +next_sample(Sample *sample) +{ + if(sample->pos >= sample->len) { + if(sample->loop == 0) { + sample->data = 0; + return 0; + } + while(sample->pos >= sample->len) { + sample->pos -= sample->loop; + } + } + + float val = interpolate_sample(sample->data, sample->len, sample->pos); + val *= sample->env.vol; + Sint8 next = (Sint8)0x80 ^ (Uint8)val; + + sample->pos += sample->inc; + env_advance(&sample->env); + return next; } void -audio_handler(void *ctx, Uint8 *out_stream, int len) { - Sint16 *stream = (Sint16 *)out_stream; - memset(stream, 0x00, len); - - int n; - for (n = 0; n < POLYPHONY; n++) { - Uint8 device = (3 + n) << 4; - Uxn *u = (Uxn *)ctx; - Uint8 *addr = &u->dev[device]; - if (channel[n].duration <= 0 && PEEK2(addr)) { +audio_handler(void *ctx, Uint8 *out_stream, int len) +{ + Sint16 *stream = (Sint16 *)out_stream; + memset(stream, 0x00, len); + + int n; + for(n = 0; n < POLYPHONY; n++) { + Uint8 device = (3 + n) << 4; + Uxn *u = (Uxn *)ctx; + Uint8 *addr = &u->dev[device]; + if(channel[n].duration <= 0 && PEEK2(addr)) { uxn_eval(u, PEEK2(addr)); - } - channel[n].duration -= SOUND_TIMER; - - int x = 0; - if (channel[n].xfade) { - float delta = 1.0f / (XFADE_SAMPLES * 2); - while (x < XFADE_SAMPLES * 2) { - float alpha = x * delta; - float beta = 1.0f - alpha; - Sint16 next_a = next_sample(&channel[n].next_sample); - Sint16 next_b = 0; - if (channel[n].sample.data != 0) { - next_b = next_sample(&channel[n].sample); - } - Sint16 next = alpha * next_a + beta * next_b; - stream[x++] += next * channel[n].vol_l; - stream[x++] += next * channel[n].vol_r; - } - channel[n].sample = channel[n].next_sample; - channel[n].xfade = false; - } - Sample *sample = &channel[n].sample; - while (x < len / 2) { - if (sample->data == 0) { - break; - } - Sint16 next = next_sample(sample); - stream[x++] += next * channel[n].vol_l; - stream[x++] += next * channel[n].vol_r; - } - } - int i; - for (i = 0; i < len / 2; i++) { - stream[i] <<= 6; - } + } + channel[n].duration -= SOUND_TIMER; + + int x = 0; + if(channel[n].xfade) { + float delta = 1.0f / (XFADE_SAMPLES * 2); + while(x < XFADE_SAMPLES * 2) { + float alpha = x * delta; + float beta = 1.0f - alpha; + Sint16 next_a = next_sample(&channel[n].next_sample); + Sint16 next_b = 0; + if(channel[n].sample.data != 0) { + next_b = next_sample(&channel[n].sample); + } + Sint16 next = alpha * next_a + beta * next_b; + stream[x++] += next * channel[n].vol_l; + stream[x++] += next * channel[n].vol_r; + } + channel[n].sample = channel[n].next_sample; + channel[n].xfade = false; + } + Sample *sample = &channel[n].sample; + while(x < len / 2) { + if(sample->data == 0) { + break; + } + Sint16 next = next_sample(sample); + stream[x++] += next * channel[n].vol_l; + stream[x++] += next * channel[n].vol_r; + } + } + int i; + for(i = 0; i < len / 2; i++) { + stream[i] <<= 6; + } } void audio_start(int idx, Uint8 *d, Uxn *u) { - Uint16 duration = PEEK2(d + 0x5); - Uint8 off = d[0xf] == 0x00; - - if (!off) { - Uint16 addr = PEEK2(d + 0xc); - Uint8 *data = &u->ram[addr]; - Uint16 len = PEEK2(d + 0xa); - Uint8 volume = d[0xe]; - bool loop = !(d[0xf] & 0x80); - Uint8 pitch = d[0xf] & 0x7f; - Uint16 adsr = PEEK2(d + 0x8); - Uint8 attack = (adsr >> 12) & 0xF; - Uint8 decay = (adsr >> 8) & 0xF; - Uint8 sustain = (adsr >> 4) & 0xF; - Uint8 release = (adsr >> 0) & 0xF; - note_on(&channel[idx], duration, data, len, volume, attack, decay, sustain, release, pitch, loop); - } else { - note_off(&channel[idx], duration); - } + Uint16 duration = PEEK2(d + 0x5); + Uint8 off = d[0xf] == 0x00; + + if(!off) { + Uint16 addr = PEEK2(d + 0xc); + Uint8 *data = &u->ram[addr]; + Uint16 len = PEEK2(d + 0xa); + Uint8 volume = d[0xe]; + bool loop = !(d[0xf] & 0x80); + Uint8 pitch = d[0xf] & 0x7f; + Uint16 adsr = PEEK2(d + 0x8); + Uint8 attack = (adsr >> 12) & 0xF; + Uint8 decay = (adsr >> 8) & 0xF; + Uint8 sustain = (adsr >> 4) & 0xF; + Uint8 release = (adsr >> 0) & 0xF; + note_on(&channel[idx], duration, data, len, volume, attack, decay, sustain, release, pitch, loop); + } else { + note_off(&channel[idx], duration); + } } Uint8 -audio_get_vu(int instance) { - return channel[instance].sample.env.vol * 255.0f; +audio_get_vu(int instance) +{ + return channel[instance].sample.env.vol * 255.0f; } Uint16 -audio_get_position(int instance) { - return channel[instance].sample.pos; +audio_get_position(int instance) +{ + return channel[instance].sample.pos; } diff --git a/src/uxncli.c b/src/uxncli.c @@ -64,7 +64,7 @@ emu_end(Uxn *u) int main(int argc, char **argv) { - Uint8 dev[0x100] = {0}; + Uint8 dev[0x100] = {0}; Uxn u; u.dev = (Uint8 *)&dev; int i = 1; diff --git a/src/uxnemu.c b/src/uxnemu.c @@ -70,20 +70,20 @@ clamp(int v, int min, int max) static Uint8 audio_dei(int instance, Uint8 *d, Uint8 port) { - switch(port) { - case 0x2: - return audio_get_position(instance) >> 8; - case 0x3: - return audio_get_position(instance); - case 0x4: - return audio_get_vu(instance); - case 0x0: - case 0x8: - case 0xa: - case 0xc: return PEEK2(d + port); - default: return d[port]; - } - return d[port]; + switch(port) { + case 0x2: + return audio_get_position(instance) >> 8; + case 0x3: + return audio_get_position(instance); + case 0x4: + return audio_get_vu(instance); + case 0x0: + case 0x8: + case 0xa: + case 0xc: return PEEK2(d + port); + default: return d[port]; + } + return d[port]; } static void @@ -94,7 +94,7 @@ audio_deo(int instance, Uint8 *d, Uint8 port, Uxn *u) SDL_LockAudioDevice(audio_id); audio_start(instance, d, u); SDL_UnlockAudioDevice(audio_id); - SDL_PauseAudioDevice(audio_id, 0); + SDL_PauseAudioDevice(audio_id, 0); } } @@ -271,7 +271,7 @@ emu_init(Uxn *u) deadline_interval = ms_interval * TIMEOUT_MS; exec_deadline = SDL_GetPerformanceCounter() + deadline_interval; screen_resize(WIDTH, HEIGHT); - SDL_PauseAudioDevice(audio_id, 1); + SDL_PauseAudioDevice(audio_id, 1); return 1; } @@ -514,7 +514,7 @@ emu_run(Uxn *u, char *rom) static int emu_end(Uxn *u) { - SDL_CloseAudioDevice(audio_id); + SDL_CloseAudioDevice(audio_id); #ifdef _WIN32 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" TerminateThread((HANDLE)SDL_GetThreadID(stdin_thread), 0); @@ -529,7 +529,7 @@ emu_end(Uxn *u) int main(int argc, char **argv) { - Uint8 dev[0x100] = {0}; + Uint8 dev[0x100] = {0}; Uxn u = {0}; Uxn u_audio = {0}; u.dev = (Uint8 *)&dev; @@ -555,10 +555,10 @@ main(int argc, char **argv) char *rom = argv[i++]; if(!system_init(&u, ram, rom)) { return system_error("Init", "Failed to initialize uxn."); - } + } if(!system_init(&u_audio, ram, rom)) { return system_error("Init", "Failed to initialize uxn."); - } + } if(!emu_init(&u_audio)) return system_error("Init", "Failed to initialize varvara."); /* Game Loop */