Returned SDL calls from apu.c to emulator.c
This commit is contained in:
parent
530ff4bef2
commit
59f5fbf4f1
117
src/apu.c
117
src/apu.c
|
@ -1,5 +1,3 @@
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (c) 2021 Devine Lu Linvega
|
Copyright (c) 2021 Devine Lu Linvega
|
||||||
Copyright (c) 2021 Andrew Alderwick
|
Copyright (c) 2021 Andrew Alderwick
|
||||||
|
@ -13,11 +11,9 @@ WITH REGARD TO THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "uxn.h"
|
#include "uxn.h"
|
||||||
|
#include "apu.h"
|
||||||
|
|
||||||
#define SAMPLE_FREQUENCY 48000
|
extern Device *devapu;
|
||||||
|
|
||||||
extern SDL_AudioDeviceID audio_id;
|
|
||||||
int error(char *msg, const char *err);
|
|
||||||
|
|
||||||
static Uint32 note_advances[12] = {
|
static Uint32 note_advances[12] = {
|
||||||
0x82d01286 / (SAMPLE_FREQUENCY / 30), /* C7 */
|
0x82d01286 / (SAMPLE_FREQUENCY / 30), /* C7 */
|
||||||
|
@ -34,55 +30,33 @@ static Uint32 note_advances[12] = {
|
||||||
0xf6f11003 / (SAMPLE_FREQUENCY / 30) /* B7 */
|
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
|
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;
|
int i;
|
||||||
Note *note = ¬es[note_i];
|
Note *note = &apu->notes[note_i];
|
||||||
while(n_samples--) {
|
while(n_samples--) {
|
||||||
Sint32 sample = 1;
|
Sint32 sample = 1;
|
||||||
for(i = 0; i < 2; ++i) {
|
for(i = 0; i < 2; ++i) {
|
||||||
WaveformGenerator *wv = ¬e->wv[i];
|
WaveformGenerator *wv = ¬e->wv[i];
|
||||||
q = &wv->queue;
|
apu->queue = &wv->queue;
|
||||||
wv->count += wv->advance;
|
wv->count += wv->advance;
|
||||||
while(wv->count > wv->period) {
|
while(wv->count > wv->period) {
|
||||||
wv->count -= wv->period;
|
wv->count -= wv->period;
|
||||||
wv->start_value = wv->end_value;
|
wv->start_value = wv->end_value;
|
||||||
if(q->i == q->n) {
|
if(apu->queue->i == apu->queue->n) {
|
||||||
q->i = q->n = 0;
|
apu->queue->i = apu->queue->n = 0;
|
||||||
if(!q->ends) {
|
if(!apu->queue->finishes) {
|
||||||
u->ram.dat[id_addr] = note_i;
|
u->ram.dat[devapu->addr + 0xa] = note_i;
|
||||||
evaluxn(u, wv->vector);
|
evaluxn(u, wv->vector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!q->n) {
|
if(!apu->queue->n) {
|
||||||
note->playing = 0;
|
note->playing = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wv->end_value = (Sint16)q->dat[q->i++];
|
wv->end_value = (Sint16)apu->queue->dat[apu->queue->i++];
|
||||||
wv->period = (30 << 4) * q->dat[q->i++];
|
wv->period = (30 << 4) * apu->queue->dat[apu->queue->i++];
|
||||||
}
|
}
|
||||||
if(wv->period >> 9)
|
if(wv->period >> 9)
|
||||||
sample *= wv->start_value + (Sint32)(wv->end_value - wv->start_value) * (Sint32)(wv->count >> 10) / (Sint32)(wv->period >> 10);
|
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
|
void
|
||||||
play_all_notes(void *u, Uint8 *stream, int len)
|
apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
SDL_memset(stream, 0, len);
|
for(i = 0; i < n_samples * 2; ++i)
|
||||||
for(i = 0; i < n_notes; ++i)
|
samples[i] = 0;
|
||||||
if(notes[i].playing) play_note(u, i, (Sint16 *)stream, len >> 2);
|
for(i = 0; i < apu->n_notes; ++i)
|
||||||
q = NULL;
|
if(apu->notes[i].playing) render_note(apu, u, i, samples, n_samples);
|
||||||
|
apu->queue = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Note *
|
void
|
||||||
get_note(Uint8 i)
|
apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch)
|
||||||
{
|
{
|
||||||
if(i >= n_notes) notes = SDL_realloc(notes, (i + 1) * sizeof(Note));
|
|
||||||
while(i >= n_notes) SDL_zero(notes[n_notes++]);
|
|
||||||
return ¬es[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8
|
|
||||||
audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
|
||||||
{
|
|
||||||
Uint8 *m = u->ram.dat + ptr;
|
|
||||||
int i;
|
int i;
|
||||||
if(b0 == 0xa) {
|
|
||||||
Note *note = get_note(b1);
|
|
||||||
note->playing = 1;
|
note->playing = 1;
|
||||||
for(i = 0; i < 2; ++i) {
|
for(i = 0; i < 2; ++i) {
|
||||||
note->volume[i] = 0xf & (m[0x8] >> 4 * (1 - i));
|
note->volume[i] = 0xf & (volume >> 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].count = note->wv[i].period = 0;
|
||||||
note->wv[i].end_value = 0;
|
note->wv[i].end_value = 0;
|
||||||
note->wv[i].queue.n = note->wv[i].queue.i = 0;
|
note->wv[i].queue.n = note->wv[i].queue.i = 0;
|
||||||
note->wv[i].queue.ends = 0;
|
note->wv[i].queue.finishes = 0;
|
||||||
}
|
}
|
||||||
note->wv[0].advance = note_advances[m[0x9] % 12] >> (8 - m[0x9] / 12);
|
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;
|
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;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
@ -15,15 +15,18 @@ WITH REGARD TO THIS SOFTWARE.
|
||||||
|
|
||||||
#include "uxn.h"
|
#include "uxn.h"
|
||||||
#include "ppu.h"
|
#include "ppu.h"
|
||||||
|
#include "apu.h"
|
||||||
|
|
||||||
int initapu(Uxn *u, Uint8 id);
|
int initapu(Uxn *u, Uint8 id);
|
||||||
|
|
||||||
SDL_AudioDeviceID audio_id;
|
static SDL_AudioDeviceID audio_id;
|
||||||
static SDL_Window *gWindow;
|
static SDL_Window *gWindow;
|
||||||
static SDL_Renderer *gRenderer;
|
static SDL_Renderer *gRenderer;
|
||||||
static SDL_Texture *gTexture;
|
static SDL_Texture *gTexture;
|
||||||
static Ppu ppu;
|
static Ppu ppu;
|
||||||
|
static Apu apu;
|
||||||
static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl;
|
static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl;
|
||||||
|
Device *devapu;
|
||||||
|
|
||||||
#pragma mark - Helpers
|
#pragma mark - Helpers
|
||||||
|
|
||||||
|
@ -41,6 +44,12 @@ error(char *msg, const char *err)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
audio_callback(void *u, Uint8 *stream, int len)
|
||||||
|
{
|
||||||
|
apu_render(&apu, (Uxn *)u, (Sint16 *)stream, len >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
redraw(Uint32 *dst, Uxn *u)
|
redraw(Uint32 *dst, Uxn *u)
|
||||||
{
|
{
|
||||||
|
@ -86,8 +95,9 @@ quit(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
init(void)
|
init(Uxn *u)
|
||||||
{
|
{
|
||||||
|
SDL_AudioSpec as;
|
||||||
if(!initppu(&ppu, 48, 32, 16))
|
if(!initppu(&ppu, 48, 32, 16))
|
||||||
return error("PPU", "Init failure");
|
return error("PPU", "Init failure");
|
||||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
||||||
|
@ -103,6 +113,17 @@ init(void)
|
||||||
return error("Texture", SDL_GetError());
|
return error("Texture", SDL_GetError());
|
||||||
SDL_StartTextInput();
|
SDL_StartTextInput();
|
||||||
SDL_ShowCursor(SDL_DISABLE);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +279,27 @@ file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
||||||
return 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
|
Uint8
|
||||||
midi_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
midi_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
||||||
{
|
{
|
||||||
|
@ -367,7 +409,7 @@ main(int argc, char **argv)
|
||||||
return error("Boot", "Failed");
|
return error("Boot", "Failed");
|
||||||
if(!loaduxn(&u, argv[1]))
|
if(!loaduxn(&u, argv[1]))
|
||||||
return error("Load", "Failed");
|
return error("Load", "Failed");
|
||||||
if(!init())
|
if(!init(&u))
|
||||||
return error("Init", "Failed");
|
return error("Init", "Failed");
|
||||||
|
|
||||||
devsystem = portuxn(&u, 0x00, "system", system_poke);
|
devsystem = portuxn(&u, 0x00, "system", system_poke);
|
||||||
|
@ -378,8 +420,7 @@ main(int argc, char **argv)
|
||||||
devkey = portuxn(&u, 0x05, "key", ppnil);
|
devkey = portuxn(&u, 0x05, "key", ppnil);
|
||||||
devmouse = portuxn(&u, 0x06, "mouse", ppnil);
|
devmouse = portuxn(&u, 0x06, "mouse", ppnil);
|
||||||
portuxn(&u, 0x07, "file", file_poke);
|
portuxn(&u, 0x07, "file", file_poke);
|
||||||
if(!initapu(&u, 0x08))
|
devapu = portuxn(&u, 0x08, "audio", audio_poke);
|
||||||
return 1;
|
|
||||||
portuxn(&u, 0x09, "midi", ppnil);
|
portuxn(&u, 0x09, "midi", ppnil);
|
||||||
portuxn(&u, 0x0a, "datetime", datetime_poke);
|
portuxn(&u, 0x0a, "datetime", datetime_poke);
|
||||||
portuxn(&u, 0x0b, "---", ppnil);
|
portuxn(&u, 0x0b, "---", ppnil);
|
||||||
|
|
Loading…
Reference in New Issue