Quick and dirty sound for DOOM. Samplerate seems off, but it's just a demo.

This commit is contained in:
Toby Jaffey 2025-12-14 23:32:34 +00:00
parent e524ea4815
commit 39e7dddc5a
4 changed files with 83 additions and 10 deletions

View file

@ -198,6 +198,9 @@ fn submain() !void {
while(true) { while(true) {
pd.doom_update(); pd.doom_update();
const doomSndBuf: [*]i16 = pd.doom_get_sound_buffer();
uvm.renderAudio(doomSndBuf, 2048);
const fb: [*]const u8 = pd.doom_get_framebuffer(4); const fb: [*]const u8 = pd.doom_get_framebuffer(4);
uvm.render(fb, WIDTH * HEIGHT * 4); uvm.render(fb, WIDTH * HEIGHT * 4);

View file

@ -24,6 +24,10 @@ pub inline fn syscall(id: u32, param1: u32, param2: u32) u32 {
return val; return val;
} }
pub inline fn renderAudio(audbuf: [*]const i16, len:u32) void {
_ = syscall(uvm32.UVM32_SYSCALL_RENDERAUDIO, @intFromPtr(audbuf), len);
}
pub inline fn render(fb: [*]const u8, len:u32) void { pub inline fn render(fb: [*]const u8, len:u32) void {
_ = syscall(uvm32.UVM32_SYSCALL_RENDER, @intFromPtr(fb), len); _ = syscall(uvm32.UVM32_SYSCALL_RENDER, @intFromPtr(fb), len);
} }

View file

@ -2,14 +2,15 @@
// These are not required when building a custom host target code with uvm32 // These are not required when building a custom host target code with uvm32
// syscalls for exposed host functions, start at 0 // syscalls for exposed host functions, start at 0
#define UVM32_SYSCALL_PUTC 0x00000000 #define UVM32_SYSCALL_PUTC 0x00000000
#define UVM32_SYSCALL_GETC 0x00000001 #define UVM32_SYSCALL_GETC 0x00000001
#define UVM32_SYSCALL_PRINT 0x00000002 #define UVM32_SYSCALL_PRINT 0x00000002
#define UVM32_SYSCALL_PRINTLN 0x00000003 #define UVM32_SYSCALL_PRINTLN 0x00000003
#define UVM32_SYSCALL_PRINTDEC 0x00000004 #define UVM32_SYSCALL_PRINTDEC 0x00000004
#define UVM32_SYSCALL_PRINTHEX 0x00000005 #define UVM32_SYSCALL_PRINTHEX 0x00000005
#define UVM32_SYSCALL_MILLIS 0x00000006 #define UVM32_SYSCALL_MILLIS 0x00000006
#define UVM32_SYSCALL_PRINTBUF 0x00000007 #define UVM32_SYSCALL_PRINTBUF 0x00000007
#define UVM32_SYSCALL_RENDER 0x00000008 #define UVM32_SYSCALL_RENDER 0x00000008
#define UVM32_SYSCALL_GETKEY 0x00000009 #define UVM32_SYSCALL_GETKEY 0x00000009
#define UVM32_SYSCALL_RENDERAUDIO 0x0000000A

View file

@ -33,6 +33,12 @@ static keyevent_t keyBuffer[KEYBUFFER_LEN];
static int keyBufferWr = 0; static int keyBufferWr = 0;
static int keyBufferRd = 0; static int keyBufferRd = 0;
// circular buffer of audio samples, so vm code can read them as it's ready
#define AUDIOBUFFER_LEN 4096
static int16_t audioBuffer[AUDIOBUFFER_LEN];
static int audioBufferWr = 0;
static int audioBufferRd = 0;
void key_enq(uint16_t scancode, bool down) { void key_enq(uint16_t scancode, bool down) {
keyBuffer[keyBufferWr].scancode = scancode; keyBuffer[keyBufferWr].scancode = scancode;
keyBuffer[keyBufferWr].down = down; keyBuffer[keyBufferWr].down = down;
@ -50,6 +56,22 @@ bool key_deq(keyevent_t *ke) {
} }
} }
void audio_enq(int16_t sample) {
audioBuffer[audioBufferWr] = sample;
audioBufferWr = (audioBufferWr + 1) % AUDIOBUFFER_LEN;
}
bool audio_deq(int16_t *sample) {
if (audioBufferWr != audioBufferRd) {
*sample = audioBuffer[audioBufferRd];
audioBufferRd = (audioBufferRd + 1) % AUDIOBUFFER_LEN;
return true;
} else {
return false;
}
}
static uint8_t *read_file(const char* filename, int *len) { static uint8_t *read_file(const char* filename, int *len) {
FILE* f = fopen(filename, "rb"); FILE* f = fopen(filename, "rb");
uint8_t *buf = NULL; uint8_t *buf = NULL;
@ -107,6 +129,27 @@ void usage(const char *name) {
exit(1); exit(1);
} }
void audiocb(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) {
if (audioBufferWr == audioBufferRd) { // empty
return;
}
while(total_amount > 0) {
int16_t sample[2];
// always expect audio in pairs
if (audio_deq(&sample[0])) {
if (audio_deq(&sample[1])) {
if (!SDL_PutAudioStreamData(stream, sample, 4)) {
printf("audio write failed\r\n");
}
}
} else {
return;
}
total_amount -= 4;
}
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -199,6 +242,19 @@ int main(int argc, char *argv[]) {
render_target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); render_target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
SDL_SetTextureScaleMode(render_target, SDL_SCALEMODE_NEAREST); SDL_SetTextureScaleMode(render_target, SDL_SCALEMODE_NEAREST);
SDL_AudioSpec srcspec = {
.format = SDL_AUDIO_S16,
.channels = 2,
.freq = 44100,
};
SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &srcspec, audiocb, NULL);
if (NULL == stream) {
printf("Could not setup audio!\n");
return 1;
}
SDL_ResumeAudioStreamDevice(stream);
while (isrunning) { while (isrunning) {
SDL_PollEvent(&event); SDL_PollEvent(&event);
@ -277,6 +333,15 @@ int main(int argc, char *argv[]) {
case UVM32_SYSCALL_GETC: { case UVM32_SYSCALL_GETC: {
uvm32_arg_setval(vmst, &evt, RET, 0xFFFFFFFF); uvm32_arg_setval(vmst, &evt, RET, 0xFFFFFFFF);
} break; } break;
case UVM32_SYSCALL_RENDERAUDIO: {
uvm32_slice_t buf = uvm32_arg_getslice(vmst, &evt, ARG0, ARG1);
printf("Got audio buf len=%d\n", buf.len);
int16_t *samples = (int16_t *)buf.ptr;
for (int i=0;i<buf.len/2;i++) {
audio_enq(samples[i]);
}
} break;
case UVM32_SYSCALL_RENDER: { case UVM32_SYSCALL_RENDER: {
uvm32_slice_t buf = uvm32_arg_getslice(vmst, &evt, ARG0, ARG1); uvm32_slice_t buf = uvm32_arg_getslice(vmst, &evt, ARG0, ARG1);