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:
M | src/apu.c | | | 129 | ++++++++++++++++++++----------------------------------------------------------- |
A | src/apu.h | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/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 = ¬es[note_i];
+ Note *note = &apu->notes[note_i];
while(n_samples--) {
Sint32 sample = 1;
for(i = 0; i < 2; ++i) {
WaveformGenerator *wv = ¬e->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 ¬es[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);