diff --git a/hw/xbox/mcpx/apu.c b/hw/xbox/mcpx/apu.c index c76708e9e0..efe32da9ed 100644 --- a/hw/xbox/mcpx/apu.c +++ b/hw/xbox/mcpx/apu.c @@ -110,9 +110,9 @@ static void se_frame(MCPXAPUState *d) g_dbg.gp_realtime = d->gp.realtime; g_dbg.ep_realtime = d->ep.realtime; - qemu_spin_lock(&d->vp.out_buf_lock); - int num_bytes_free = fifo8_num_free(&d->vp.out_buf); - qemu_spin_unlock(&d->vp.out_buf_lock); + qemu_spin_lock(&d->monitor.fifo_lock); + int num_bytes_free = fifo8_num_free(&d->monitor.fifo); + qemu_spin_unlock(&d->monitor.fifo_lock); /* A rudimentary calculation to determine approximately how taxed the APU * thread is, by measuring how much time we spend waiting for FIFO to drain @@ -120,7 +120,7 @@ static void se_frame(MCPXAPUState *d) * =1: thread is not sleeping and likely falling behind realtime * <1: thread is able to complete work on time */ - if (num_bytes_free < sizeof(d->apu_fifo_output)) { + if (num_bytes_free < sizeof(d->monitor.frame_buf)) { int64_t sleep_start = qemu_clock_get_us(QEMU_CLOCK_REALTIME); qemu_cond_wait(&d->cond, &d->lock); int64_t sleep_end = qemu_clock_get_us(QEMU_CLOCK_REALTIME); @@ -157,18 +157,18 @@ static void se_frame(MCPXAPUState *d) if (0 <= g_config.audio.volume_limit && g_config.audio.volume_limit < 1) { float f = pow(g_config.audio.volume_limit, M_E); for (int i = 0; i < 256; i++) { - d->apu_fifo_output[i][0] *= f; - d->apu_fifo_output[i][1] *= f; + d->monitor.frame_buf[i][0] *= f; + d->monitor.frame_buf[i][1] *= f; } } - qemu_spin_lock(&d->vp.out_buf_lock); - num_bytes_free = fifo8_num_free(&d->vp.out_buf); - assert(num_bytes_free >= sizeof(d->apu_fifo_output)); - fifo8_push_all(&d->vp.out_buf, (uint8_t *)d->apu_fifo_output, - sizeof(d->apu_fifo_output)); - qemu_spin_unlock(&d->vp.out_buf_lock); - memset(d->apu_fifo_output, 0, sizeof(d->apu_fifo_output)); + qemu_spin_lock(&d->monitor.fifo_lock); + num_bytes_free = fifo8_num_free(&d->monitor.fifo); + assert(num_bytes_free >= sizeof(d->monitor.frame_buf)); + fifo8_push_all(&d->monitor.fifo, (uint8_t *)d->monitor.frame_buf, + sizeof(d->monitor.frame_buf)); + qemu_spin_unlock(&d->monitor.fifo_lock); + memset(d->monitor.frame_buf, 0, sizeof(d->monitor.frame_buf)); } d->ep_frame_div++; @@ -189,7 +189,7 @@ static void sleep_ns(int64_t ns) #endif } -static void mcpx_vp_out_cb(void *opaque, uint8_t *stream, int free_b) +static void monitor_sink_cb(void *opaque, uint8_t *stream, int free_b) { MCPXAPUState *s = MCPX_APU_DEVICE(opaque); @@ -200,9 +200,9 @@ static void mcpx_vp_out_cb(void *opaque, uint8_t *stream, int free_b) int avail = 0; while (avail < free_b) { - qemu_spin_lock(&s->vp.out_buf_lock); - avail = fifo8_num_used(&s->vp.out_buf); - qemu_spin_unlock(&s->vp.out_buf_lock); + qemu_spin_lock(&s->monitor.fifo_lock); + avail = fifo8_num_used(&s->monitor.fifo); + qemu_spin_unlock(&s->monitor.fifo_lock); if (avail < free_b) { sleep_ns(1000000); qemu_cond_broadcast(&s->cond); @@ -212,10 +212,10 @@ static void mcpx_vp_out_cb(void *opaque, uint8_t *stream, int free_b) int to_copy = MIN(free_b, avail); while (to_copy > 0) { uint32_t chunk_len = 0; - qemu_spin_lock(&s->vp.out_buf_lock); - chunk_len = fifo8_pop_buf(&s->vp.out_buf, stream, to_copy); + qemu_spin_lock(&s->monitor.fifo_lock); + chunk_len = fifo8_pop_buf(&s->monitor.fifo, stream, to_copy); assert(chunk_len <= to_copy); - qemu_spin_unlock(&s->vp.out_buf_lock); + qemu_spin_unlock(&s->monitor.fifo_lock); stream += chunk_len; to_copy -= chunk_len; } @@ -223,6 +223,35 @@ static void mcpx_vp_out_cb(void *opaque, uint8_t *stream, int free_b) qemu_cond_broadcast(&s->cond); } +static void monitor_init(MCPXAPUState *d) +{ + qemu_spin_init(&d->monitor.fifo_lock); + fifo8_create(&d->monitor.fifo, 3 * (256 * 2 * 2)); + + struct SDL_AudioSpec sdl_audio_spec = { + .freq = 48000, + .format = AUDIO_S16LSB, + .channels = 2, + .samples = 512, + .callback = monitor_sink_cb, + .userdata = d, + }; + + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + fprintf(stderr, "Failed to initialize SDL audio subsystem: %s\n", SDL_GetError()); + exit(1); + } + + SDL_AudioDeviceID sdl_audio_dev; + sdl_audio_dev = SDL_OpenAudioDevice(NULL, 0, &sdl_audio_spec, NULL, 0); + if (sdl_audio_dev == 0) { + fprintf(stderr, "SDL_OpenAudioDevice failed: %s\n", SDL_GetError()); + assert(!"SDL_OpenAudioDevice failed"); + exit(1); + } + SDL_PauseAudioDevice(sdl_audio_dev, 0); +} + static void mcpx_apu_realize(PCIDevice *dev, Error **errp) { MCPXAPUState *d = MCPX_APU_DEVICE(dev); @@ -519,32 +548,6 @@ void mcpx_apu_init(PCIBus *bus, int devfn, MemoryRegion *ram) d->set_irq = false; d->exiting = false; - struct SDL_AudioSpec sdl_audio_spec = { - .freq = 48000, - .format = AUDIO_S16LSB, - .channels = 2, - .samples = 512, - .callback = mcpx_vp_out_cb, - .userdata = d, - }; - - if (SDL_Init(SDL_INIT_AUDIO) < 0) { - fprintf(stderr, "Failed to initialize SDL audio subsystem: %s\n", SDL_GetError()); - exit(1); - } - - SDL_AudioDeviceID sdl_audio_dev; - sdl_audio_dev = SDL_OpenAudioDevice(NULL, 0, &sdl_audio_spec, NULL, 0); - if (sdl_audio_dev == 0) { - fprintf(stderr, "SDL_OpenAudioDevice failed: %s\n", SDL_GetError()); - assert(!"SDL_OpenAudioDevice failed"); - exit(1); - } - SDL_PauseAudioDevice(sdl_audio_dev, 0); - - qemu_spin_init(&d->vp.out_buf_lock); - fifo8_create(&d->vp.out_buf, 3 * (256 * 2 * 2)); - qemu_mutex_init(&d->lock); qemu_cond_init(&d->cond); qemu_add_vm_change_state_handler(mcpx_apu_vm_state_change, d); @@ -552,4 +555,6 @@ void mcpx_apu_init(PCIBus *bus, int devfn, MemoryRegion *ram) mcpx_apu_vp_init(d); qemu_thread_create(&d->apu_thread, "mcpx.apu_thread", mcpx_apu_frame_thread, d, QEMU_THREAD_JOINABLE); + + monitor_init(d); } diff --git a/hw/xbox/mcpx/apu_debug.h b/hw/xbox/mcpx/apu_debug.h index ceedad7777..a45637adeb 100644 --- a/hw/xbox/mcpx/apu_debug.h +++ b/hw/xbox/mcpx/apu_debug.h @@ -23,13 +23,13 @@ #include #include -enum McpxApuDebugMon { +typedef enum McpxApuDebugMonitorPoint { MCPX_APU_DEBUG_MON_AC97, MCPX_APU_DEBUG_MON_VP, MCPX_APU_DEBUG_MON_GP, MCPX_APU_DEBUG_MON_EP, MCPX_APU_DEBUG_MON_GP_OR_EP -}; +} McpxApuDebugMonitorPoint; struct McpxApuDebugVoice { @@ -81,8 +81,8 @@ extern "C" { #endif const struct McpxApuDebug *mcpx_apu_get_debug_info(void); -int mcpx_apu_debug_get_monitor(void); -void mcpx_apu_debug_set_monitor(int mon); +McpxApuDebugMonitorPoint mcpx_apu_debug_get_monitor(void); +void mcpx_apu_debug_set_monitor(McpxApuDebugMonitorPoint monitor); void mcpx_apu_debug_isolate_voice(uint16_t v); void mcpx_apu_debug_clear_isolations(void); void mcpx_apu_debug_toggle_mute(uint16_t v); diff --git a/hw/xbox/mcpx/apu_int.h b/hw/xbox/mcpx/apu_int.h index eff951263b..a2297c7a08 100644 --- a/hw/xbox/mcpx/apu_int.h +++ b/hw/xbox/mcpx/apu_int.h @@ -93,12 +93,17 @@ typedef struct MCPXAPUState { uint32_t regs[0x20000]; - int mon; int ep_frame_div; int sleep_acc; int frame_count; int64_t frame_count_time; - int16_t apu_fifo_output[256][2]; // 1 EP frame (0x400 bytes), 8 buffered + + struct { + McpxApuDebugMonitorPoint point; + int16_t frame_buf[256][2]; // 1 EP frame (0x400 bytes), 8 buffered + QemuSpin fifo_lock; + Fifo8 fifo; + } monitor; } MCPXAPUState; extern MCPXAPUState *g_state; // Used via debug handlers diff --git a/hw/xbox/mcpx/debug.c b/hw/xbox/mcpx/debug.c index 40eb877c4c..04869d1d44 100644 --- a/hw/xbox/mcpx/debug.c +++ b/hw/xbox/mcpx/debug.c @@ -53,14 +53,14 @@ void mcpx_apu_debug_set_ep_realtime_enabled(bool run) g_state->ep.realtime = run; } -int mcpx_apu_debug_get_monitor(void) +McpxApuDebugMonitorPoint mcpx_apu_debug_get_monitor(void) { - return g_state->mon; + return g_state->monitor.point; } -void mcpx_apu_debug_set_monitor(int new_mon) +void mcpx_apu_debug_set_monitor(McpxApuDebugMonitorPoint monitor) { - g_state->mon = new_mon; + g_state->monitor.point = monitor; } void mcpx_apu_debug_isolate_voice(uint16_t v) diff --git a/hw/xbox/mcpx/dsp/gp_ep.c b/hw/xbox/mcpx/dsp/gp_ep.c index 897c1cb1b9..c2dab00f80 100644 --- a/hw/xbox/mcpx/dsp/gp_ep.c +++ b/hw/xbox/mcpx/dsp/gp_ep.c @@ -32,11 +32,11 @@ void mcpx_apu_update_dsp_preference(MCPXAPUState *d) } if (g_config.audio.use_dsp) { - d->mon = MCPX_APU_DEBUG_MON_GP_OR_EP; + d->monitor.point = MCPX_APU_DEBUG_MON_GP_OR_EP; d->gp.realtime = true; d->ep.realtime = true; } else { - d->mon = MCPX_APU_DEBUG_MON_VP; + d->monitor.point = MCPX_APU_DEBUG_MON_VP; d->gp.realtime = false; d->ep.realtime = false; } @@ -180,12 +180,12 @@ static void gp_fifo_rw(void *opaque, uint8_t *ptr, unsigned int index, static bool ep_sink_samples(MCPXAPUState *d, uint8_t *ptr, size_t len) { - if (d->mon == MCPX_APU_DEBUG_MON_AC97) { + if (d->monitor.point == MCPX_APU_DEBUG_MON_AC97) { return false; - } else if ((d->mon == MCPX_APU_DEBUG_MON_EP) || - (d->mon == MCPX_APU_DEBUG_MON_GP_OR_EP)) { - assert(len == sizeof(d->apu_fifo_output)); - memcpy(d->apu_fifo_output, ptr, len); + } else if ((d->monitor.point == MCPX_APU_DEBUG_MON_EP) || + (d->monitor.point == MCPX_APU_DEBUG_MON_GP_OR_EP)) { + assert(len == sizeof(d->monitor.frame_buf)); + memcpy(d->monitor.frame_buf, ptr, len); } return true; @@ -461,15 +461,15 @@ void mcpx_apu_dsp_frame(MCPXAPUState *d, float mixbins[NUM_MIXBINS][NUM_SAMPLES_ } while (!d->gp.dsp->core.is_idle && d->gp.realtime); g_dbg.gp.cycles = d->gp.dsp->core.cycle_count; - if ((d->mon == MCPX_APU_DEBUG_MON_GP) || - (d->mon == MCPX_APU_DEBUG_MON_GP_OR_EP && !ep_enabled)) { + if ((d->monitor.point == MCPX_APU_DEBUG_MON_GP) || + (d->monitor.point == MCPX_APU_DEBUG_MON_GP_OR_EP && !ep_enabled)) { int off = (d->ep_frame_div % 8) * NUM_SAMPLES_PER_FRAME; for (int i = 0; i < NUM_SAMPLES_PER_FRAME; i++) { uint32_t l = dsp_read_memory(d->gp.dsp, 'X', 0x1400 + i); - d->apu_fifo_output[off + i][0] = l >> 8; + d->monitor.frame_buf[off + i][0] = l >> 8; uint32_t r = dsp_read_memory(d->gp.dsp, 'X', 0x1400 + 1 * 0x20 + i); - d->apu_fifo_output[off + i][1] = r >> 8; + d->monitor.frame_buf[off + i][1] = r >> 8; } } } diff --git a/hw/xbox/mcpx/vp/vp.c b/hw/xbox/mcpx/vp/vp.c index 3f6e25ef31..20e182978a 100644 --- a/hw/xbox/mcpx/vp/vp.c +++ b/hw/xbox/mcpx/vp/vp.c @@ -1477,7 +1477,7 @@ static void voice_process(MCPXAPUState *d, } } - if (d->mon == MCPX_APU_DEBUG_MON_VP) { + if (d->monitor.point == MCPX_APU_DEBUG_MON_VP) { /* For VP mon, simply mix all voices together here, selecting the * maximal volume used for any given mixbin as the overall volume for * this voice. @@ -1599,7 +1599,7 @@ static void *voice_worker_thread(void *arg) // Process queued voices memset(self->mixbins, 0, sizeof(self->mixbins)); - if (d->mon == MCPX_APU_DEBUG_MON_VP) { + if (d->monitor.point == MCPX_APU_DEBUG_MON_VP) { memset(self->sample_buf, 0, sizeof(self->sample_buf)); } for (int i = 0; i < self->queue_len; i++) { @@ -1615,7 +1615,7 @@ static void *voice_worker_thread(void *arg) vwd->mixbins[b][s] += self->mixbins[b][s]; } } - if (d->mon == MCPX_APU_DEBUG_MON_VP) { + if (d->monitor.point == MCPX_APU_DEBUG_MON_VP) { for (int i = 0; i < NUM_SAMPLES_PER_FRAME; i++) { d->vp.sample_buf[i][0] += self->sample_buf[i][0]; d->vp.sample_buf[i][1] += self->sample_buf[i][1]; @@ -1827,15 +1827,15 @@ void mcpx_apu_vp_frame(MCPXAPUState *d, float mixbins[NUM_MIXBINS][NUM_SAMPLES_P } voice_work_dispatch(d, mixbins); - if (d->mon == MCPX_APU_DEBUG_MON_VP) { + if (d->monitor.point == MCPX_APU_DEBUG_MON_VP) { /* Mix all voices together to hear any audible voice */ int16_t isamp[NUM_SAMPLES_PER_FRAME * 2]; src_float_to_short_array((float *)d->vp.sample_buf, isamp, NUM_SAMPLES_PER_FRAME * 2); int off = (d->ep_frame_div % 8) * NUM_SAMPLES_PER_FRAME; for (int i = 0; i < NUM_SAMPLES_PER_FRAME; i++) { - d->apu_fifo_output[off + i][0] += isamp[2*i]; - d->apu_fifo_output[off + i][1] += isamp[2*i+1]; + d->monitor.frame_buf[off + i][0] += isamp[2*i]; + d->monitor.frame_buf[off + i][1] += isamp[2*i+1]; } memset(d->vp.sample_buf, 0, sizeof(d->vp.sample_buf)); diff --git a/hw/xbox/mcpx/vp/vp.h b/hw/xbox/mcpx/vp/vp.h index 6e185fe651..7797ade1b6 100644 --- a/hw/xbox/mcpx/vp/vp.h +++ b/hw/xbox/mcpx/vp/vp.h @@ -79,8 +79,6 @@ typedef struct { MemoryRegion mmio; VoiceWorkDispatch voice_work_dispatch; MCPXAPUVoiceFilter filters[MCPX_HW_MAX_VOICES]; - QemuSpin out_buf_lock; - Fifo8 out_buf; // FIXME: Where are these stored? int ssl_base_page; diff --git a/ui/xui/debug.cc b/ui/xui/debug.cc index ff48bba726..d670625113 100644 --- a/ui/xui/debug.cc +++ b/ui/xui/debug.cc @@ -201,9 +201,9 @@ void DebugApuWindow::Draw() ImGui::NextColumn(); static int mon = 0; - mon = mcpx_apu_debug_get_monitor(); + mon = (int)mcpx_apu_debug_get_monitor(); if (ImGui::Combo("Monitor", &mon, "AC97\0VP Only\0GP Only\0EP Only\0GP/EP if enabled\0")) { - mcpx_apu_debug_set_monitor(mon); + mcpx_apu_debug_set_monitor((McpxApuDebugMonitorPoint)mon); } static bool gp_realtime;