uxn

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

commit 59f5fbf4f1042aa70d6d7794eebb1cf2a921785b
parent 530ff4bef2728843517636626decb165e4ea5ef7
Author: Andrew Alderwick <andrew@alderwick.co.uk>
Date:   Thu,  8 Apr 2021 21:14:40 +0100

Returned SDL calls from apu.c to emulator.c

Diffstat:
Msrc/apu.c | 129++++++++++++++++++++-----------------------------------------------------------
Asrc/apu.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/emulator.c | 51++++++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 124 insertions(+), 102 deletions(-)

diff --git a/src/apu.c b/src/apu.c @@ -1,5 +1,3 @@ -#include <SDL2/SDL.h> - /* Copyright (c) 2021 Devine Lu Linvega Copyright (c) 2021 Andrew Alderwick @@ -13,11 +11,9 @@ WITH REGARD TO THIS SOFTWARE. */ #include "uxn.h" +#include "apu.h" -#define SAMPLE_FREQUENCY 48000 - -extern SDL_AudioDeviceID audio_id; -int error(char *msg, const char *err); +extern Device *devapu; static Uint32 note_advances[12] = { 0x82d01286 / (SAMPLE_FREQUENCY / 30), /* C7 */ @@ -34,55 +30,33 @@ static Uint32 note_advances[12] = { 0xf6f11003 / (SAMPLE_FREQUENCY / 30) /* B7 */ }; -typedef struct { - Uint16 *dat; - Uint8 i, n, sz, ends; -} Queue; - -typedef struct { - Uint32 count, advance, period; - Uint16 vector; - Sint16 start_value, end_value; - Queue queue; -} WaveformGenerator; - -typedef struct { - WaveformGenerator wv[2]; - Sint8 volume[2], playing; -} Note; - -static Note *notes = NULL; -static int n_notes = 0; -static Queue *q; -static Uint16 id_addr; - static void -play_note(Uxn *u, int note_i, Sint16 *samples, int n_samples) +render_note(Apu *apu, Uxn *u, int note_i, Sint16 *samples, int n_samples) { int i; - Note *note = &notes[note_i]; + Note *note = &apu->notes[note_i]; while(n_samples--) { Sint32 sample = 1; for(i = 0; i < 2; ++i) { WaveformGenerator *wv = &note->wv[i]; - q = &wv->queue; + apu->queue = &wv->queue; wv->count += wv->advance; while(wv->count > wv->period) { wv->count -= wv->period; wv->start_value = wv->end_value; - if(q->i == q->n) { - q->i = q->n = 0; - if(!q->ends) { - u->ram.dat[id_addr] = note_i; + if(apu->queue->i == apu->queue->n) { + apu->queue->i = apu->queue->n = 0; + if(!apu->queue->finishes) { + u->ram.dat[devapu->addr + 0xa] = note_i; evaluxn(u, wv->vector); } } - if(!q->n) { + if(!apu->queue->n) { note->playing = 0; return; } - wv->end_value = (Sint16)q->dat[q->i++]; - wv->period = (30 << 4) * q->dat[q->i++]; + wv->end_value = (Sint16)apu->queue->dat[apu->queue->i++]; + wv->period = (30 << 4) * apu->queue->dat[apu->queue->i++]; } if(wv->period >> 9) sample *= wv->start_value + (Sint32)(wv->end_value - wv->start_value) * (Sint32)(wv->count >> 10) / (Sint32)(wv->period >> 10); @@ -94,70 +68,31 @@ play_note(Uxn *u, int note_i, Sint16 *samples, int n_samples) } } -static void -play_all_notes(void *u, Uint8 *stream, int len) +void +apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples) { int i; - SDL_memset(stream, 0, len); - for(i = 0; i < n_notes; ++i) - if(notes[i].playing) play_note(u, i, (Sint16 *)stream, len >> 2); - q = NULL; -} - -static Note * -get_note(Uint8 i) -{ - if(i >= n_notes) notes = SDL_realloc(notes, (i + 1) * sizeof(Note)); - while(i >= n_notes) SDL_zero(notes[n_notes++]); - return &notes[i]; + for(i = 0; i < n_samples * 2; ++i) + samples[i] = 0; + for(i = 0; i < apu->n_notes; ++i) + if(apu->notes[i].playing) render_note(apu, u, i, samples, n_samples); + apu->queue = NULL; } -static Uint8 -audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) +void +apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch) { - Uint8 *m = u->ram.dat + ptr; int i; - if(b0 == 0xa) { - Note *note = get_note(b1); - note->playing = 1; - for(i = 0; i < 2; ++i) { - note->volume[i] = 0xf & (m[0x8] >> 4 * (1 - i)); - note->wv[i].vector = (m[0x0 + i * 2] << 8) + m[0x1 + i * 2]; - note->wv[i].count = note->wv[i].period = 0; - note->wv[i].end_value = 0; - note->wv[i].queue.n = note->wv[i].queue.i = 0; - note->wv[i].queue.ends = 0; - } - note->wv[0].advance = note_advances[m[0x9] % 12] >> (8 - m[0x9] / 12); - note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY; - } else if(b0 == 0xe && q != NULL) { - if(q->n == q->sz) { - q->sz = q->sz < 4 ? 4 : q->sz * 2; - q->dat = SDL_realloc(q->dat, q->sz * sizeof(*q->dat)); - } - q->dat[q->n++] = (m[0xb] << 8) + m[0xc]; - q->dat[q->n++] = (m[0xd] << 8) + b1; - } else if(b0 == 0xf && q != NULL) { - q->ends = 1; + note->playing = 1; + for(i = 0; i < 2; ++i) { + note->volume[i] = 0xf & (volume >> 4 * (1 - i)); + note->wv[i].count = note->wv[i].period = 0; + note->wv[i].end_value = 0; + note->wv[i].queue.n = note->wv[i].queue.i = 0; + note->wv[i].queue.finishes = 0; } - return b1; -} - -int -initapu(Uxn *u, Uint8 id) -{ - SDL_AudioSpec as; - SDL_zero(as); - as.freq = SAMPLE_FREQUENCY; - as.format = AUDIO_S16; - as.channels = 2; - as.callback = play_all_notes; - as.samples = 2048; - as.userdata = u; - audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); - if(!audio_id) - return error("Audio", SDL_GetError()); - id_addr = portuxn(u, id, "audio", audio_poke)->addr + 0xa; - SDL_PauseAudioDevice(audio_id, 0); - return 1; + note->wv[0].vector = wave_vector; + note->wv[0].advance = note_advances[pitch % 12] >> (8 - pitch / 12); + note->wv[1].vector = envelope_vector; + note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY; } diff --git a/src/apu.h b/src/apu.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 2021 Devine Lu Linvega +Copyright (c) 2021 Andrew Alderwick + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE. +*/ + +typedef unsigned char Uint8; +typedef signed char Sint8; +typedef unsigned short Uint16; +typedef signed short Sint16; +typedef unsigned int Uint32; +typedef signed int Sint32; + +#define SAMPLE_FREQUENCY 48000 + +typedef struct { + Uint16 *dat; + Uint8 i, n, sz, finishes; +} Queue; + +typedef struct { + Uint32 count, advance, period; + Uint16 vector; + Sint16 start_value, end_value; + Queue queue; +} WaveformGenerator; + +typedef struct { + WaveformGenerator wv[2]; + Sint8 volume[2], playing; +} Note; + +typedef struct { + Queue *queue; + Note *notes; + int n_notes; +} Apu; + +void apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples); +void apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch); diff --git a/src/emulator.c b/src/emulator.c @@ -15,15 +15,18 @@ WITH REGARD TO THIS SOFTWARE. #include "uxn.h" #include "ppu.h" +#include "apu.h" int initapu(Uxn *u, Uint8 id); -SDL_AudioDeviceID audio_id; +static SDL_AudioDeviceID audio_id; static SDL_Window *gWindow; static SDL_Renderer *gRenderer; static SDL_Texture *gTexture; static Ppu ppu; +static Apu apu; static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl; +Device *devapu; #pragma mark - Helpers @@ -41,6 +44,12 @@ error(char *msg, const char *err) return 0; } +static void +audio_callback(void *u, Uint8 *stream, int len) +{ + apu_render(&apu, (Uxn *)u, (Sint16 *)stream, len >> 2); +} + void redraw(Uint32 *dst, Uxn *u) { @@ -86,8 +95,9 @@ quit(void) } int -init(void) +init(Uxn *u) { + SDL_AudioSpec as; if(!initppu(&ppu, 48, 32, 16)) return error("PPU", "Init failure"); if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) @@ -103,6 +113,17 @@ init(void) return error("Texture", SDL_GetError()); SDL_StartTextInput(); SDL_ShowCursor(SDL_DISABLE); + SDL_zero(as); + as.freq = SAMPLE_FREQUENCY; + as.format = AUDIO_S16; + as.channels = 2; + as.callback = audio_callback; + as.samples = 2048; + as.userdata = u; + audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); + if(!audio_id) + return error("Audio", SDL_GetError()); + SDL_PauseAudioDevice(audio_id, 0); return 1; } @@ -258,6 +279,27 @@ file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) return b1; } +static Uint8 +audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) +{ + Uint8 *m = u->ram.dat + ptr; + if(b0 == 0xa) { + if(b1 >= apu.n_notes) apu.notes = SDL_realloc(apu.notes, (b1 + 1) * sizeof(Note)); + while(b1 >= apu.n_notes) SDL_zero(apu.notes[apu.n_notes++]); + apu_play_note(&apu.notes[b1], (m[0x0] << 8) + m[0x1], (m[0x2] << 8) + m[0x3], m[0x8], m[0x9]); + } else if(b0 == 0xe && apu.queue != NULL) { + if(apu.queue->n == apu.queue->sz) { + apu.queue->sz = apu.queue->sz < 4 ? 4 : apu.queue->sz * 2; + apu.queue->dat = SDL_realloc(apu.queue->dat, apu.queue->sz * sizeof(*apu.queue->dat)); + } + apu.queue->dat[apu.queue->n++] = (m[0xb] << 8) + m[0xc]; + apu.queue->dat[apu.queue->n++] = (m[0xd] << 8) + b1; + } else if(b0 == 0xf && apu.queue != NULL) { + apu.queue->finishes = 1; + } + return b1; +} + Uint8 midi_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) { @@ -367,7 +409,7 @@ main(int argc, char **argv) return error("Boot", "Failed"); if(!loaduxn(&u, argv[1])) return error("Load", "Failed"); - if(!init()) + if(!init(&u)) return error("Init", "Failed"); devsystem = portuxn(&u, 0x00, "system", system_poke); @@ -378,8 +420,7 @@ main(int argc, char **argv) devkey = portuxn(&u, 0x05, "key", ppnil); devmouse = portuxn(&u, 0x06, "mouse", ppnil); portuxn(&u, 0x07, "file", file_poke); - if(!initapu(&u, 0x08)) - return 1; + devapu = portuxn(&u, 0x08, "audio", audio_poke); portuxn(&u, 0x09, "midi", ppnil); portuxn(&u, 0x0a, "datetime", datetime_poke); portuxn(&u, 0x0b, "---", ppnil);