audio: Make the last byte of the audio buffer a trap address

at that point, there should be 370 contiguous, sensible samples
available, so let the front end grab them.

Sadly this adds a test to the "store byte in RAM" fast path, but it seems
to be unavoidable.

In the unix frontend, vsync is now presented based on the SDL audio
callback when enabled, instead of gettimeofday.
This commit is contained in:
Jeff Epler 2025-03-19 13:31:38 -05:00
parent c5ac98dffb
commit 0be2290d4a
5 changed files with 89 additions and 22 deletions

View file

@ -27,6 +27,7 @@
DEBUG ?= 0
MEMSIZE ?= 128
ENABLE_AUDIO ?= 1
SOURCES = $(wildcard src/*.c)
@ -54,7 +55,7 @@ endif
# Basic support for changing screen res (with the MacPlusV3 ROM)
DISP_WIDTH ?= 512
DISP_HEIGHT ?= 342
CFLAGS_CFG = -DDISP_WIDTH=$(DISP_WIDTH) -DDISP_HEIGHT=$(DISP_HEIGHT)
CFLAGS_CFG = -DDISP_WIDTH=$(DISP_WIDTH) -DDISP_HEIGHT=$(DISP_HEIGHT) -DENABLE_AUDIO=$(ENABLE_AUDIO)
all: main

View file

@ -81,6 +81,8 @@ extern int overlay;
* But, that should never happen post-boot.
*/
#define CLAMP_RAM_ADDR(x) ((x) >= RAM_SIZE ? (x) % RAM_SIZE : (x))
/* Is the address (previously clamped with CLAMP_RAM_ADDR) the audio trap address? (last byte of audio data) */
#define IS_RAM_AUDIO_TRAP(x) (x == umac_get_audio_offset() + 2 * 369)
#define IS_VIA(x) ((ADR24(x) & 0xe80000) == 0xe80000)
#define IS_IWM(x) ((ADR24(x) >= 0xdfe1ff) && (ADR24(x) < (0xdfe1ff + 0x2000)))

View file

@ -53,9 +53,12 @@ static inline unsigned int umac_get_fb_offset(void)
return RAM_SIZE - ((DISP_WIDTH * DISP_HEIGHT / 8) + 0x380);
}
#if ENABLE_AUDIO
#define umac_get_audio_offset() (RAM_SIZE - 768)
#define umac_get_audio_offset_end() (RAM_SIZE - 768 + 2 * 370)
extern int umac_volume;
void umac_audio_trap();
void umac_audio_cfg(int volume, int sndres);
#endif
#endif

View file

@ -71,7 +71,9 @@ static unsigned int g_int_controller_highest_int = 0; /* Highest pending interr
uint8_t *_ram_base;
uint8_t *_rom_base;
int umac_volume = 0;
#if ENABLE_AUDIO
static int umac_volume, umac_sndres;
#endif
int overlay = 1;
static uint64_t global_time_us = 0;
static int sim_done = 0;
@ -156,9 +158,14 @@ static void via_ra_changed(uint8_t val)
MDBG("OVERLAY CHANGING\n");
update_overlay_layout();
}
umac_volume = val & 7;
#if ENABLE_AUDIO
uint8_t vol = val & 7;
if (vol != umac_volume) {
umac_volume = val & 7;
umac_audio_cfg(umac_volume, umac_sndres);
}
oldval = val;
#endif
}
static void via_rb_changed(uint8_t val)
@ -169,7 +176,13 @@ static void via_rb_changed(uint8_t val)
// 4 = mouse4 (in, mouse X2)
// 3 = mouse7 (in, 0 = button pressed)
// [2:0] = RTC controls
(void)val;
#if ENABLE_AUDIO
uint8_t sndres = val >> 7;
if(sndres != umac_sndres) {
umac_sndres = sndres;
umac_audio_cfg(umac_volume, umac_sndres);
}
#endif
}
static uint8_t via_ra_in(void)
@ -440,7 +453,13 @@ unsigned int cpu_read_long_dasm(unsigned int address)
void FAST_FUNC(cpu_write_byte)(unsigned int address, unsigned int value)
{
if (IS_RAM(address)) {
RAM_WR8(CLAMP_RAM_ADDR(address), value);
address = CLAMP_RAM_ADDR(address);
RAM_WR8(address, value);
#if ENABLE_AUDIO
if(IS_RAM_AUDIO_TRAP(address)) {
umac_audio_trap();
}
#endif
return;
}

View file

@ -27,6 +27,7 @@
* SOFTWARE.
*/
#include <assert.h>
#include <stdio.h>
#include <fcntl.h>
#include <getopt.h>
@ -35,6 +36,9 @@
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#if ENABLE_AUDIO
#include <stdatomic.h>
#endif
#include "SDL.h"
#include "rom.h"
@ -78,19 +82,42 @@ static void copy_fb(uint32_t *fb_out, uint8_t *fb_in)
}
}
static void audio_callback(void *userdata, Uint8 *stream_in, int len_bytes) {
int16_t *stream = (int16_t*) stream_in;
int len = len_bytes / 2;
uint16_t *audiodata = userdata;
int32_t scale = 65536 * umac_volume / 7;
int32_t offset = 128;
while (len--) {
int32_t a = (*audiodata++ & 0xff) - offset;
a = (a * scale) >> 8;
*stream++ = a;
}
#if ENABLE_AUDIO
// audio_callback is called from a thread, so pending_v_retrace can't be a regular variable
atomic_int pending_v_retrace;
static int volscale;
uint8_t *audio_base;
int16_t audio[370];
void umac_audio_cfg(int umac_volume, int umac_sndres) {
volscale = umac_sndres ? 0 : 65536 * umac_volume / 7;
}
void umac_audio_trap() {
int32_t offset = 128;
uint16_t *audiodata = (uint16_t*)audio_base;
int scale = volscale;
if (!scale) {
memset(audio, 0, sizeof(audio));
return;
}
int16_t *stream = audio;
for(int i=0; i<370; i++) {
int32_t a = (*audiodata++ & 0xff) - offset;
a = (a * scale) >> 8;
*stream++ = a;
}
}
static void audio_callback(void *userdata, Uint8 *stream_in, int len_bytes) {
(void) userdata;
assert(len_bytes == sizeof(audio));
atomic_store(&pending_v_retrace, 1);
memcpy(stream_in, audio, sizeof(audio));
}
#endif
/**********************************************************************/
/* The emulator core expects to be given ROM and RAM pointers,
@ -245,7 +272,11 @@ int main(int argc, char *argv[])
SDL_Renderer *renderer;
SDL_Texture *texture;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
SDL_Init(SDL_INIT_VIDEO
#if ENABLE_AUDIO
| SDL_INIT_AUDIO
#endif
);
SDL_Window *window = SDL_CreateWindow("umac",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
@ -277,8 +308,11 @@ int main(int argc, char *argv[])
return 1;
}
#if ENABLE_AUDIO
SDL_AudioSpec desired, obtained;
audio_base = (uint8_t*)ram_base + umac_get_audio_offset();
SDL_zero(desired);
desired.freq = 22256; // wat
desired.channels = 1;
@ -294,6 +328,7 @@ int main(int argc, char *argv[])
printf("SDL audio_deviceSDL_GetError() -> %s\n", buf);
return 1;
}
#endif
////////////////////////////////////////////////////////////////////////
// Emulator init
@ -301,15 +336,19 @@ int main(int argc, char *argv[])
umac_init(ram_base, rom_base, discs);
umac_opt_disassemble(opt_disassemble);
#if ENABLE_AUDIO
// Default state is paused, this unpauses it
SDL_PauseAudioDevice(audio_device, 0);
#endif
////////////////////////////////////////////////////////////////////////
// Main loop
int done = 0;
int mouse_button = 0;
#if !ENABLE_AUDIO
uint64_t last_vsync = 0;
#endif
uint64_t last_1hz = 0;
do {
struct timeval tv_now;
@ -355,11 +394,14 @@ int main(int argc, char *argv[])
uint64_t now_usec = (tv_now.tv_sec * 1000000) + tv_now.tv_usec;
/* Passage of time: */
if ((now_usec - last_vsync) >= 16667) {
#if ENABLE_AUDIO
int do_v_retrace = atomic_exchange(&pending_v_retrace, 0);
#else
int do_v_retrace = (now_usec - last_vsync) >= 16667;
#endif
if (do_v_retrace) {
umac_vsync_event();
last_vsync = now_usec;
/* Cheapo framerate limiting: */
copy_fb(framebuffer, ram_get_base() + umac_get_fb_offset());
SDL_UpdateTexture(texture, NULL, framebuffer,
DISP_WIDTH * sizeof (Uint32));