From: Dimitris Panokostas Date: Sun, 14 Jun 2026 11:31:36 +0000 (+0200) Subject: Add UAESND capture ABI support X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=7b8207e706cc5cfa0ae687eca1dd319fb4f23414;p=francis%2Fwinuae.git Add UAESND capture ABI support --- diff --git a/sndboard.cpp b/sndboard.cpp index 922b4712..f5396a9b 100644 --- a/sndboard.cpp +++ b/sndboard.cpp @@ -35,8 +35,8 @@ static void snd_init(void); static void sndboard_rethink(void); static uae_u8 *sndboard_get_buffer(int *frames); static void sndboard_release_buffer(uae_u8 *buffer, int frames); -static void sndboard_free_capture(void); -static bool sndboard_init_capture(int freq); +static void sndboard_free_capture(int owner); +static bool sndboard_init_capture(int freq, int owner); static void uaesndboard_reset(int hardreset); static void sndboard_reset(int hardreset); @@ -47,6 +47,38 @@ static float base_event_clock; #define MAX_UAE_CHANNELS 8 #define MAX_UAE_STREAMS 8 +#define UAESND_CAP_24_32BIT 1 +#define UAESND_CAP_MONO_HPAN 2 +#define UAESND_CAP_DIAGNOSTICS 4 +#define UAESND_CAP_CAPTURE 8 +#define UAESND_CAP_CAPTURE_BLOCK 16 +#define UAESND_DIAG_VERSION 1 +#define UAESND_ERR_NONE 0 +#define UAESND_ERR_INVALID_SET 1 +#define UAESND_ERR_STREAM_ALLOC 2 +#define UAESND_ERR_CAPTURE_BLOCK_ADDRESS 3 +#define SNDBOARD_CAPTURE_OWNER_NONE 0 +#define SNDBOARD_CAPTURE_OWNER_TOCCATA 1 +#define SNDBOARD_CAPTURE_OWNER_UAESND 2 +#define UAESND_CAPTURE_REG_DATA 0xc0 +#define UAESND_CAPTURE_REG_OVERRUNS 0xc4 +#define UAESND_CAPTURE_REG_INTREQ 0xc8 +#define UAESND_CAPTURE_REG_THRESHOLD 0xcc +#define UAESND_CAPTURE_REG_CONTROL 0xd0 +#define UAESND_CAPTURE_REG_STATUS 0xd4 +#define UAESND_CAPTURE_REG_FREQUENCY 0xd8 +#define UAESND_CAPTURE_REG_AVAILABLE 0xdc +#define UAESND_CAPTURE_CONTROL_ENABLE 1 +#define UAESND_CAPTURE_CONTROL_IRQ_ENABLE 2 +#define UAESND_CAPTURE_BLOCK_REG_ADDRESS 0x900 +#define UAESND_CAPTURE_BLOCK_REG_FRAMES 0x904 +#define UAESND_CAPTURE_BLOCK_REG_DONE 0x908 +#define UAESND_CAPTURE_BLOCK_REG_COMMAND 0x90c +#define UAESND_CAPTURE_BLOCK_COMMAND_COPY 1 +#define UAESND_CAPTURE_STATUS_UNAVAILABLE 1 +#define UAESND_CAPTURE_STATUS_ACTIVE 2 +#define UAESND_CAPTURE_STATUS_OVERRUN 4 + struct uaesndboard_stream { int streamid; @@ -84,6 +116,27 @@ struct uaesndboard_stream int timer_event_time; int sample[MAX_UAE_CHANNELS]; }; + +struct uaesnd_capture_state +{ + uae_u32 control; + uae_u32 status; + uae_u32 frequency; + uae_u32 available; + uae_u32 overrun_count; + uae_u32 intreq; + uae_u32 threshold; + uae_u32 frame_count; + uae_u32 dropped_byte_count; + uae_u32 block_address; + uae_u32 block_frames; + uae_u32 block_done; + int buffer_size; + int read_index; + int write_index; + uae_u8 *buffer; +}; + struct uaesndboard_data { bool enabled; @@ -98,9 +151,310 @@ struct uaesndboard_data int volume[MAX_UAE_CHANNELS]; struct uaesndboard_stream stream[MAX_UAE_STREAMS]; uae_u8 info[256]; + uae_u32 invalid_set_count; + uae_u32 stream_alloc_failure_count; + uae_u32 stream_start_count; + uae_u32 stream_stop_count; + uae_u32 stream_irq_count; + uae_u32 timer_irq_count; + uae_u32 last_error_code; + struct uaesnd_capture_state capture; }; static struct uaesndboard_data uaesndboard[MAX_DUPLICATE_SOUND_BOARDS]; +static int capture_owner = SNDBOARD_CAPTURE_OWNER_NONE; + +static void uaesnd_update_info(struct uaesndboard_data *data) +{ + put_long_host(data->info + 24, UAESND_CAP_24_32BIT | UAESND_CAP_MONO_HPAN | UAESND_CAP_DIAGNOSTICS | UAESND_CAP_CAPTURE | UAESND_CAP_CAPTURE_BLOCK); + put_long_host(data->info + 28, UAESND_DIAG_VERSION); + put_long_host(data->info + 32, data->invalid_set_count); + put_long_host(data->info + 36, data->stream_alloc_failure_count); + put_long_host(data->info + 40, data->stream_start_count); + put_long_host(data->info + 44, data->stream_stop_count); + put_long_host(data->info + 48, data->stream_irq_count); + put_long_host(data->info + 52, data->timer_irq_count); + put_long_host(data->info + 56, data->last_error_code); + put_long_host(data->info + 60, data->capture.frame_count); + put_long_host(data->info + 64, data->capture.dropped_byte_count); +} + +static void uaesnd_set_error(struct uaesndboard_data *data, uae_u32 error_code) +{ + data->last_error_code = error_code; +} + +static bool uaesnd_capture_addr(uaecptr addr) +{ + addr &= 65535; + return addr >= UAESND_CAPTURE_REG_DATA && addr < 0xe0; +} + +static bool uaesnd_capture_block_addr(uaecptr addr) +{ + addr &= 65535; + return addr >= UAESND_CAPTURE_BLOCK_REG_ADDRESS && addr < UAESND_CAPTURE_BLOCK_REG_COMMAND + 4; +} + +static uae_u32 uaesnd_capture_available(struct uaesnd_capture_state *capture) +{ + if (!capture->buffer || capture->buffer_size <= 0) + return 0; + if (capture->write_index >= capture->read_index) + return capture->write_index - capture->read_index; + return capture->buffer_size - capture->read_index + capture->write_index; +} + +static void uaesnd_capture_reset_buffer(struct uaesnd_capture_state *capture) +{ + capture->read_index = 0; + capture->write_index = 0; + capture->available = 0; +} + +static void uaesnd_capture_start(struct uaesndboard_data *data) +{ + if (data->capture.status & UAESND_CAPTURE_STATUS_ACTIVE) + return; + if (data->capture.frequency == 0) + data->capture.frequency = currprefs.sound_freq; + data->capture.buffer_size = data->capture.frequency * 2 * 2; + if (data->capture.buffer_size < 4096) + data->capture.buffer_size = 4096; + data->capture.buffer = xcalloc(uae_u8, data->capture.buffer_size); + uaesnd_capture_reset_buffer(&data->capture); + data->capture.control |= UAESND_CAPTURE_CONTROL_ENABLE; + if (!sndboard_init_capture(data->capture.frequency, SNDBOARD_CAPTURE_OWNER_UAESND)) { + xfree(data->capture.buffer); + data->capture.buffer = NULL; + data->capture.buffer_size = 0; + data->capture.status = UAESND_CAPTURE_STATUS_UNAVAILABLE; + return; + } + data->capture.status = UAESND_CAPTURE_STATUS_ACTIVE; +} + +static void uaesnd_capture_stop(struct uaesndboard_data *data) +{ + if (data->capture.status & UAESND_CAPTURE_STATUS_ACTIVE) + sndboard_free_capture(SNDBOARD_CAPTURE_OWNER_UAESND); + xfree(data->capture.buffer); + data->capture.buffer = NULL; + data->capture.buffer_size = 0; + data->capture.control &= ~UAESND_CAPTURE_CONTROL_ENABLE; + data->capture.status = 0; + data->capture.intreq = 0; + uaesnd_capture_reset_buffer(&data->capture); +} + +static void uaesnd_capture_write_byte(struct uaesnd_capture_state *capture, uae_u8 value) +{ + if (!capture->buffer || capture->buffer_size <= 1) + return; + int next = (capture->write_index + 1) % capture->buffer_size; + if (next == capture->read_index) { + capture->read_index = (capture->read_index + 1) % capture->buffer_size; + capture->overrun_count++; + capture->dropped_byte_count++; + capture->status |= UAESND_CAPTURE_STATUS_OVERRUN; + } + capture->buffer[capture->write_index] = value; + capture->write_index = next; + capture->available = uaesnd_capture_available(capture); +} + +static void uaesnd_capture_write_s16be(struct uaesnd_capture_state *capture, int sample) +{ + uaesnd_capture_write_byte(capture, sample >> 8); + uaesnd_capture_write_byte(capture, sample); +} + +static void uaesnd_capture_process(struct uaesndboard_data *data) +{ + if (!(data->capture.status & UAESND_CAPTURE_STATUS_ACTIVE)) + return; + int frames = 0; + uae_u8 *buffer = sndboard_get_buffer(&frames); + if (buffer && frames > 0) { + data->capture.frame_count += frames; + int samples = frames * 2; + for (int i = 0; i < samples; i++) { + int sample = (uae_s16)(buffer[i * 2 + 0] | (buffer[i * 2 + 1] << 8)); + uaesnd_capture_write_s16be(&data->capture, sample); + } + if ((data->capture.control & UAESND_CAPTURE_CONTROL_IRQ_ENABLE) && data->capture.threshold > 0 && data->capture.available >= data->capture.threshold) + data->capture.intreq = 1; + } + sndboard_release_buffer(buffer, frames); +} + +static bool uaesnd_capture_rethink(struct uaesndboard_data *data) +{ + uaesnd_capture_process(data); + return (data->capture.control & UAESND_CAPTURE_CONTROL_IRQ_ENABLE) && data->capture.intreq != 0; +} + +static void uaesnd_capture_refresh_if_empty(struct uaesndboard_data *data) +{ + struct uaesnd_capture_state *capture = &data->capture; + if (!capture->buffer || capture->read_index == capture->write_index) + uaesnd_capture_process(data); +} + +static uae_u8 uaesnd_capture_read_byte(struct uaesndboard_data *data) +{ + struct uaesnd_capture_state *capture = &data->capture; + uaesnd_capture_refresh_if_empty(data); + if (!capture->buffer || capture->read_index == capture->write_index) { + capture->available = 0; + return 0; + } + uae_u8 value = capture->buffer[capture->read_index]; + capture->read_index = (capture->read_index + 1) % capture->buffer_size; + capture->available = uaesnd_capture_available(capture); + return value; +} + +static void uaesnd_capture_block_copy(struct uaesndboard_data *data) +{ + uaesnd_capture_process(data); + data->capture.block_done = 0; + uae_u32 frames = data->capture.block_frames; + uae_u32 available_frames = data->capture.available / 4; + if (frames > available_frames) + frames = available_frames; + uae_u32 bytes = frames * 4; + if (bytes == 0) + return; + if (!valid_address(data->capture.block_address, bytes)) { + uaesnd_set_error(data, UAESND_ERR_CAPTURE_BLOCK_ADDRESS); + return; + } + for (uae_u32 i = 0; i < bytes; i++) { + put_byte(data->capture.block_address + i, uaesnd_capture_read_byte(data)); + } + data->capture.block_done = frames; +} + +static void uaesnd_capture_fill_regs(struct uaesndboard_data *data, uae_u8 *regs) +{ + uaesnd_capture_process(data); + put_long_host(regs + (UAESND_CAPTURE_REG_OVERRUNS - UAESND_CAPTURE_REG_DATA), data->capture.overrun_count); + put_long_host(regs + (UAESND_CAPTURE_REG_INTREQ - UAESND_CAPTURE_REG_DATA), data->capture.intreq); + put_long_host(regs + (UAESND_CAPTURE_REG_THRESHOLD - UAESND_CAPTURE_REG_DATA), data->capture.threshold); + put_long_host(regs + (UAESND_CAPTURE_REG_CONTROL - UAESND_CAPTURE_REG_DATA), data->capture.control); + put_long_host(regs + (UAESND_CAPTURE_REG_STATUS - UAESND_CAPTURE_REG_DATA), data->capture.status); + put_long_host(regs + (UAESND_CAPTURE_REG_FREQUENCY - UAESND_CAPTURE_REG_DATA), data->capture.frequency); + put_long_host(regs + (UAESND_CAPTURE_REG_AVAILABLE - UAESND_CAPTURE_REG_DATA), data->capture.available); +} + +static uae_u8 uaesnd_capture_bget(struct uaesndboard_data *data, uaecptr addr) +{ + if ((addr & ~3) == UAESND_CAPTURE_REG_DATA) + return uaesnd_capture_read_byte(data); + uae_u8 regs[32] = {}; + uaesnd_capture_fill_regs(data, regs); + return regs[(addr - UAESND_CAPTURE_REG_DATA) & 31]; +} + +static uae_u16 uaesnd_capture_wget(struct uaesndboard_data *data, uaecptr addr) +{ + return (uaesnd_capture_bget(data, addr) << 8) | uaesnd_capture_bget(data, addr + 1); +} + +static uae_u32 uaesnd_capture_lget(struct uaesndboard_data *data, uaecptr addr) +{ + return (uaesnd_capture_wget(data, addr) << 16) | uaesnd_capture_wget(data, addr + 2); +} + +static void uaesnd_capture_apply_reg(struct uaesndboard_data *data, int reg, uae_u32 value) +{ + if (reg == UAESND_CAPTURE_REG_CONTROL) { + data->capture.control = value; + if (value & UAESND_CAPTURE_CONTROL_ENABLE) + uaesnd_capture_start(data); + else + uaesnd_capture_stop(data); + } else if (reg == UAESND_CAPTURE_REG_FREQUENCY) { + data->capture.frequency = value; + } else if (reg == UAESND_CAPTURE_REG_INTREQ) { + data->capture.intreq &= value; + } else if (reg == UAESND_CAPTURE_REG_THRESHOLD) { + data->capture.threshold = value; + } else if (reg == UAESND_CAPTURE_REG_STATUS) { + data->capture.status &= value; + } +} + +static void uaesnd_capture_put(struct uaesndboard_data *data, uaecptr addr, uae_u32 value, int size) +{ + uae_u8 regs[32] = {}; + int offset = (addr - UAESND_CAPTURE_REG_DATA) & 31; + int reg = (addr & ~3) & 65535; + uaesnd_capture_fill_regs(data, regs); + if (size == 4) { + put_long_host(regs + offset, value); + } else if (size == 2) { + put_word_host(regs + offset, value); + } else { + regs[offset] = value; + } + uaesnd_capture_apply_reg(data, reg, get_long_host(regs + (reg - UAESND_CAPTURE_REG_DATA))); +} + +static void uaesnd_capture_block_fill_regs(struct uaesndboard_data *data, uae_u8 *regs) +{ + put_long_host(regs + (UAESND_CAPTURE_BLOCK_REG_ADDRESS - UAESND_CAPTURE_BLOCK_REG_ADDRESS), data->capture.block_address); + put_long_host(regs + (UAESND_CAPTURE_BLOCK_REG_FRAMES - UAESND_CAPTURE_BLOCK_REG_ADDRESS), data->capture.block_frames); + put_long_host(regs + (UAESND_CAPTURE_BLOCK_REG_DONE - UAESND_CAPTURE_BLOCK_REG_ADDRESS), data->capture.block_done); + put_long_host(regs + (UAESND_CAPTURE_BLOCK_REG_COMMAND - UAESND_CAPTURE_BLOCK_REG_ADDRESS), 0); +} + +static uae_u8 uaesnd_capture_block_bget(struct uaesndboard_data *data, uaecptr addr) +{ + uae_u8 regs[16] = {}; + uaesnd_capture_block_fill_regs(data, regs); + return regs[(addr - UAESND_CAPTURE_BLOCK_REG_ADDRESS) & 15]; +} + +static uae_u16 uaesnd_capture_block_wget(struct uaesndboard_data *data, uaecptr addr) +{ + return (uaesnd_capture_block_bget(data, addr) << 8) | uaesnd_capture_block_bget(data, addr + 1); +} + +static uae_u32 uaesnd_capture_block_lget(struct uaesndboard_data *data, uaecptr addr) +{ + return (uaesnd_capture_block_wget(data, addr) << 16) | uaesnd_capture_block_wget(data, addr + 2); +} + +static void uaesnd_capture_block_apply_reg(struct uaesndboard_data *data, int reg, uae_u32 value) +{ + if (reg == UAESND_CAPTURE_BLOCK_REG_ADDRESS) { + data->capture.block_address = value; + } else if (reg == UAESND_CAPTURE_BLOCK_REG_FRAMES) { + data->capture.block_frames = value; + } else if (reg == UAESND_CAPTURE_BLOCK_REG_DONE) { + data->capture.block_done = value; + } else if (reg == UAESND_CAPTURE_BLOCK_REG_COMMAND && (value & UAESND_CAPTURE_BLOCK_COMMAND_COPY)) { + uaesnd_capture_block_copy(data); + } +} + +static void uaesnd_capture_block_put(struct uaesndboard_data *data, uaecptr addr, uae_u32 value, int size) +{ + uae_u8 regs[16] = {}; + int offset = (addr - UAESND_CAPTURE_BLOCK_REG_ADDRESS) & 15; + int reg = (addr & ~3) & 65535; + uaesnd_capture_block_fill_regs(data, regs); + if (size == 4) { + put_long_host(regs + offset, value); + } else if (size == 2) { + put_word_host(regs + offset, value); + } else { + regs[offset] = value; + } + uaesnd_capture_block_apply_reg(data, reg, get_long_host(regs + (reg - UAESND_CAPTURE_BLOCK_REG_ADDRESS))); +} /* autoconfig data: @@ -267,26 +621,28 @@ MEMORY_FUNCTIONS(uaesndboard_ram); static bool uaesnd_rethink(void) { - bool irq = false; + bool any_irq = false; for (int j = 0; j < MAX_DUPLICATE_SOUND_BOARDS; j++) { struct uaesndboard_data *data = &uaesndboard[j]; if (data->enabled) { + bool capture_irq = uaesnd_capture_rethink(data); + bool stream_irq = false; for (int i = 0; i < MAX_UAE_STREAMS; i++) { if (data->streamintenamask & (1 << i)) { struct uaesndboard_stream *s = &uaesndboard[j].stream[i]; if (s->intreqmask & 0x80) { data->streamintreqmask |= 1 << i; - irq = true; - break; + stream_irq = true; + continue; } } - if (!irq) { - data->streamintreqmask &= ~(1 << i); - } + data->streamintreqmask &= ~(1 << i); } + if (capture_irq || stream_irq) + any_irq = true; } } - return irq; + return any_irq; } static void uaesndboard_stop(struct uaesndboard_data *data, struct uaesndboard_stream *s) @@ -298,6 +654,7 @@ static void uaesndboard_stop(struct uaesndboard_data *data, struct uaesndboard_s write_log("UAESND %d: STOP\n", s - data->stream); #endif + data->stream_stop_count++; s->play = 0; s->next = 0; data->streammask &= ~(1 << (s - data->stream)); @@ -324,6 +681,8 @@ static void uaesndboard_maybe_alloc_stream(struct uaesndboard_data *data, struct #endif if (!s->streamid) { + data->stream_alloc_failure_count++; + uaesnd_set_error(data, UAESND_ERR_STREAM_ALLOC); uaesndboard_stop(data, s); } } @@ -402,6 +761,7 @@ static void uaesndboard_start(struct uaesndboard_data *data, struct uaesndboard_ #endif s->play = 1; + data->stream_start_count++; for (int i = 0; i < MAX_UAE_CHANNELS; i++) { s->sample[i] = 0; } @@ -591,13 +951,20 @@ static bool uaesnd_directload(struct uaesndboard_data *data, struct uaesndboard_ s->panx = get_word_host(s->io + 36); s->pany = get_word_host(s->io + 38); } - return uaesnd_validate(data, s); + if (!uaesnd_validate(data, s)) { + data->invalid_set_count++; + uaesnd_set_error(data, UAESND_ERR_INVALID_SET); + return false; + } + return true; } static bool uaesnd_next(struct uaesndboard_data *data, struct uaesndboard_stream *s, uaecptr addr) { if ((addr & 3) || !valid_address(addr, STREAM_STRUCT_SIZE) || addr < 0x100) { write_log(_T("UAESND: invalid sample set pointer %08x\n"), addr); + data->invalid_set_count++; + uaesnd_set_error(data, UAESND_ERR_INVALID_SET); return false; } @@ -606,7 +973,12 @@ static bool uaesnd_next(struct uaesndboard_data *data, struct uaesndboard_stream s->first = 10; s->repeating = false; - return uaesnd_validate(data, s); + if (!uaesnd_validate(data, s)) { + data->invalid_set_count++; + uaesnd_set_error(data, UAESND_ERR_INVALID_SET); + return false; + } + return true; } static void uaesnd_stream_start(struct uaesndboard_data *data, struct uaesndboard_stream *s, bool always) @@ -629,8 +1001,12 @@ static void uaesnd_stream_start(struct uaesndboard_data *data, struct uaesndboar static void uaesnd_irq(struct uaesndboard_stream *s, uae_u8 mask) { + struct uaesndboard_data *data = &uaesndboard[0]; uae_u8 enablemask = s->masterintenamask; uae_u8 intenamask = s->intenamask | 0x10 | 0x20 | 0x40; + data->stream_irq_count++; + if (mask & 0x10) + data->timer_irq_count++; s->intreqmask |= mask; if ((intenamask & mask) && (enablemask & mask)) { s->intreqmask |= 0x80; @@ -991,8 +1367,13 @@ static uae_u32 REGPARAM2 uaesndboard_bget(uaecptr addr) s->intreqmask = 0; s->io[0x93] = (s->play ? 1 : 0) | (s->streamid > 0 ? 2 : 0) | (s->repeating ? 4 : 0); v = get_byte_host(s->io + reg); + } else if (uaesnd_capture_addr(addr)) { + v = uaesnd_capture_bget(data, addr); } else if (addr >= 0x80 && addr < 0xe0) { + uaesnd_update_info(data); v = get_byte_host(data->info + (addr & 0x7f)); + } else if (uaesnd_capture_block_addr(addr)) { + v = uaesnd_capture_block_bget(data, addr); } else if (addr >= 0xe0 && addr <= 0xe3) { v = data->streamallocmask >> (8 * (3 - (addr - 0xe0))); } else if (addr >= 0xf0 && addr <= 0xf3) { @@ -1033,8 +1414,13 @@ static uae_u32 REGPARAM2 uaesndboard_wget(uaecptr addr) s->intreqmask = 0; } v = get_word_host(s->io + reg); + } else if (uaesnd_capture_addr(addr)) { + v = uaesnd_capture_wget(data, addr); } else if (addr >= 0x80 && addr < 0xe0 - 1) { + uaesnd_update_info(data); v = get_word_host(data->info + (addr & 0x7f)); + } else if (uaesnd_capture_block_addr(addr)) { + v = uaesnd_capture_block_wget(data, addr); } else if (addr == 0xe0) { v = data->streamallocmask >> 16; } else if (addr == 0xe2) { @@ -1074,8 +1460,13 @@ static uae_u32 REGPARAM2 uaesndboard_lget(uaecptr addr) if (reg == 0x84) s->intreqmask = 0; v = get_long_host(s->io + reg); + } else if (uaesnd_capture_addr(addr)) { + v = uaesnd_capture_lget(data, addr); } else if (addr >= 0x80 && addr < 0xe0 - 3) { + uaesnd_update_info(data); v = get_long_host(data->info + (addr & 0x7f)); + } else if (uaesnd_capture_block_addr(addr)) { + v = uaesnd_capture_block_lget(data, addr); } else if (addr == 0xe0) { v = data->streamallocmask; } else if (addr == 0xf0) { @@ -1125,6 +1516,10 @@ static void REGPARAM2 uaesndboard_bput(uaecptr addr, uae_u32 b) int reg = addr & 255; put_byte_host(s->io + reg, b); uaesnd_put(data, s, reg); + } else if (uaesnd_capture_addr(addr)) { + uaesnd_capture_put(data, addr, b, 1); + } else if (uaesnd_capture_block_addr(addr)) { + uaesnd_capture_block_put(data, addr, b, 1); } else if (addr >= 0xe0 && addr <= 0xe3) { uae_u32 v = data->streamallocmask; int shift = 8 * (3 - (addr - 0xe0)); @@ -1173,6 +1568,10 @@ static void REGPARAM2 uaesndboard_wput(uaecptr addr, uae_u32 b) int reg = addr & 255; put_word_host(s->io + reg, b); uaesnd_put(data, s, reg); + } else if (uaesnd_capture_addr(addr)) { + uaesnd_capture_put(data, addr, b, 2); + } else if (uaesnd_capture_block_addr(addr)) { + uaesnd_capture_block_put(data, addr, b, 2); } else if (addr == 0xe4 + 2) { uaesnd_latch_mask(data, b); } else if (addr == 0xe8 + 2) { @@ -1199,6 +1598,10 @@ static void REGPARAM2 uaesndboard_lput(uaecptr addr, uae_u32 b) int reg = addr & 255; put_long_host(s->io + reg, b); uaesnd_put(data, s, reg); + } else if (uaesnd_capture_addr(addr)) { + uaesnd_capture_put(data, addr, b, 4); + } else if (uaesnd_capture_block_addr(addr)) { + uaesnd_capture_block_put(data, addr, b, 4); } else if (addr == 0xe0) { uaesnd_streammask(data, data->streammask & b); data->streamallocmask = b; @@ -1245,6 +1648,14 @@ bool uaesndboard_init (struct autoconfig_info *aci, int z) if (!ert) return false; data->streammask = 0; + data->invalid_set_count = 0; + data->stream_alloc_failure_count = 0; + data->stream_start_count = 0; + data->stream_stop_count = 0; + data->stream_irq_count = 0; + data->timer_irq_count = 0; + data->last_error_code = UAESND_ERR_NONE; + memset(&data->capture, 0, sizeof data->capture); for (int i = 0; i < MAX_UAE_CHANNELS; i++) { data->volume[i] = 32768; } @@ -1256,6 +1667,7 @@ bool uaesndboard_init (struct autoconfig_info *aci, int z) put_byte_host(data->info + 14, MAX_UAE_STREAMS); put_long_host(data->info + 16, data->z3 ? 8 * 1024 * 1024 : 0x8000); put_long_host(data->info + 20, data->z3 ? 8 * 1024 * 1024 : 0x8000); + uaesnd_update_info(data); for (int i = 0; i < 16; i++) { uae_u8 b = ert->autoconfig[i]; ew(data->acmemory, i * 4, b); @@ -1279,6 +1691,7 @@ static void uaesndboard_free(void) { for (int j = 0; j < MAX_DUPLICATE_SOUND_BOARDS; j++) { struct uaesndboard_data *data = &uaesndboard[j]; + uaesnd_capture_stop(data); data->enabled = false; } mapped_free(&uaesndboard_ram_bank); @@ -1298,6 +1711,7 @@ static void uaesndboard_reset(int hardreset) } } data->streammask = 0; + uaesnd_capture_stop(data); } mapped_free(&uaesndboard_ram_bank); sndboard_rethink(); @@ -1718,7 +2132,7 @@ static void codec_start(struct snddev_data *data) if (data->snddev_active & STATUS_FIFO_RECORD) { data->capture_buffer_size = 48000 * 2 * 2; // 1s at 48000/stereo/16bit data->capture_buffer = xcalloc(uae_u8, data->capture_buffer_size); - if (!sndboard_init_capture(data->freq_adjusted)) { + if (!sndboard_init_capture(data->freq_adjusted, SNDBOARD_CAPTURE_OWNER_TOCCATA)) { write_log(_T("SNDDEV capture unavailable, recording disabled\n")); data->snddev_active &= ~STATUS_FIFO_RECORD; xfree(data->capture_buffer); @@ -1733,7 +2147,7 @@ static void codec_stop(struct snddev_data *data) return; write_log(_T("CODEC stop\n")); data->snddev_active = 0; - sndboard_free_capture(); + sndboard_free_capture(SNDBOARD_CAPTURE_OWNER_TOCCATA); int streamid = data->streamid; data->streamid = 0; audio_enable_stream(false, streamid, 0, NULL, NULL); @@ -3145,18 +3559,21 @@ static void sndboard_release_buffer(uae_u8 *buffer, int frames) } } -static void sndboard_free_capture(void) +static void sndboard_free_capture(int owner) { + if (capture_owner != owner) + return; if (capture_started) pAudioClient->Stop(); capture_started = false; + capture_owner = SNDBOARD_CAPTURE_OWNER_NONE; SAFE_RELEASE(pEnumerator) SAFE_RELEASE(pDevice) SAFE_RELEASE(pAudioClient) SAFE_RELEASE(pCaptureClient) } -static bool sndboard_init_capture(int freq) +static bool sndboard_init_capture(int freq, int owner) { HRESULT hr; WAVEFORMATEX wavfmtsrc; @@ -3164,6 +3581,9 @@ static bool sndboard_init_capture(int freq) WAVEFORMATEX *wavfmt; bool init = false; + if (capture_started) + return capture_owner == owner; + wavfmt2 = NULL; hr = CoCreateInstance( @@ -3204,10 +3624,12 @@ static bool sndboard_init_capture(int freq) if (hr == S_FALSE && wavfmt2) { wavfmt = wavfmt2; - hr = pAudioClient->Initialize(exc, 0, time, 0, wavfmt, NULL); - if (SUCCEEDED(hr)) { - init = true; - break; + if (wavfmt->wFormatTag == WAVE_FORMAT_PCM && wavfmt->nChannels == 2 && wavfmt->wBitsPerSample == 16) { + hr = pAudioClient->Initialize(exc, 0, time, 0, wavfmt, NULL); + if (SUCCEEDED(hr)) { + init = true; + break; + } } } } @@ -3224,6 +3646,7 @@ static bool sndboard_init_capture(int freq) hr = pAudioClient->Start(); EXIT_ON_ERROR(hr) capture_started = true; + capture_owner = owner; CoTaskMemFree(wavfmt2); @@ -3233,7 +3656,7 @@ static bool sndboard_init_capture(int freq) Exit:; CoTaskMemFree(wavfmt2); write_log(_T("sndboard capture init failed %08x\n"), hr); - sndboard_free_capture(); + sndboard_free_capture(owner); return false; } @@ -3249,14 +3672,22 @@ static void sndboard_release_buffer(uae_u8 *buffer, int frames) unix_sndboard_release_buffer(buffer, frames); } -static void sndboard_free_capture(void) +static void sndboard_free_capture(int owner) { + if (capture_owner != owner) + return; unix_sndboard_free_capture(); + capture_owner = SNDBOARD_CAPTURE_OWNER_NONE; } -static bool sndboard_init_capture(int freq) +static bool sndboard_init_capture(int freq, int owner) { - return unix_sndboard_init_capture(freq); + if (capture_owner != SNDBOARD_CAPTURE_OWNER_NONE) + return capture_owner == owner; + if (!unix_sndboard_init_capture(freq)) + return false; + capture_owner = owner; + return true; } #endif