mirror of
https://git.sr.ht/~rabbits/uxn
synced 2024-11-23 22:35:11 +00:00
Returned SDL calls from apu.c to emulator.c
This commit is contained in:
parent
530ff4bef2
commit
59f5fbf4f1
3 changed files with 124 additions and 102 deletions
129
src/apu.c
129
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;
|
||||
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 Note *
|
||||
get_note(Uint8 i)
|
||||
void
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
|
46
src/apu.h
Normal file
46
src/apu.h
Normal file
|
@ -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 "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);
|
||||
|
|
Loading…
Reference in a new issue