From 2d339c73e7be9eca4ae14f1e84daee38d03c3ba1 Mon Sep 17 00:00:00 2001 From: nopjne Date: Mon, 30 Sep 2024 08:43:52 -0700 Subject: [PATCH] Switch wav64 and opus over to a file descriptor (#606) --- include/wav64.h | 6 +-- src/asset_internal.h | 1 + src/audio/wav64.c | 73 ++++++++++++++++++--------------- src/audio/wav64_opus.c | 46 +++++++++------------ src/audio/wav64_opus_internal.h | 1 + src/audio/wav64internal.h | 15 +++++-- src/audio/xm64.c | 2 +- 7 files changed, 78 insertions(+), 66 deletions(-) diff --git a/include/wav64.h b/include/wav64.h index f05c34532d..ae242971ad 100644 --- a/include/wav64.h +++ b/include/wav64.h @@ -32,9 +32,9 @@ typedef struct { */ waveform_t wave; - /** @brief Absolute ROM address of WAV64 */ - uint32_t rom_addr; - + /** @brief File descriptor to read WAV64 */ + int current_fd; ///< File descriptor for the wav64 file + int base_offset; ///< Start of Wav64 data. int format; ///< Internal format of the file void *ext; ///< Pointer to extended data (internal use) } wav64_t; diff --git a/src/asset_internal.h b/src/asset_internal.h index f18ced624b..f93562a0e3 100644 --- a/src/asset_internal.h +++ b/src/asset_internal.h @@ -100,5 +100,6 @@ typedef struct { FILE *must_fopen(const char *fn); +int must_open(const char *fn); #endif diff --git a/src/audio/wav64.c b/src/audio/wav64.c index 8c2e81f499..c9202e70fb 100644 --- a/src/audio/wav64.c +++ b/src/audio/wav64.c @@ -16,6 +16,7 @@ #include "debug.h" #include "utils.h" #include "rspq.h" +#include "asset_internal.h" #include #include #include @@ -23,6 +24,8 @@ #include #include #include +#include +#include /** @brief Set to 1 to use the reference C decode for VADPCM */ #define VADPCM_REFERENCE_DECODER 0 @@ -148,7 +151,16 @@ static inline void rsp_vadpcm_decompress(void *input, int16_t *output, bool ster #endif /* VADPCM_REFERENCE_DECODER */ -void raw_waveform_read(samplebuffer_t *sbuf, int base_rom_addr, int wpos, int wlen, int bps) { +void raw_waveform_read(samplebuffer_t *sbuf, int current_fd, int wpos, int wlen, int bps) { + uint8_t* ram_addr = (uint8_t*)samplebuffer_append(sbuf, wlen); + int bytes = wlen << bps; + + uint32_t t0 = TICKS_READ(); + read(current_fd, ram_addr, bytes); + __wav64_profile_dma += TICKS_READ() - t0; +} + +void raw_waveform_read_address(samplebuffer_t *sbuf, int base_rom_addr, int wpos, int wlen, int bps) { uint32_t rom_addr = base_rom_addr + (wpos << bps); uint8_t* ram_addr = (uint8_t*)samplebuffer_append(sbuf, wlen); int bytes = wlen << bps; @@ -165,7 +177,10 @@ void raw_waveform_read(samplebuffer_t *sbuf, int base_rom_addr, int wpos, int wl static void waveform_read(void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) { wav64_t *wav = (wav64_t*)ctx; int bps = (wav->wave.bits == 8 ? 0 : 1) + (wav->wave.channels == 2 ? 1 : 0); - raw_waveform_read(sbuf, wav->rom_addr, wpos, wlen, bps); + if (seeking) { + lseek(wav->current_fd, wav->base_offset + (wpos << bps), SEEK_SET); + } + raw_waveform_read(sbuf, wav->current_fd, wpos, wlen, bps); } static void waveform_vadpcm_read(void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) { @@ -175,12 +190,12 @@ static void waveform_vadpcm_read(void *ctx, samplebuffer_t *sbuf, int wpos, int if (seeking) { if (wpos == 0) { memset(&vhead->state, 0, sizeof(vhead->state)); - vhead->current_rom_addr = wav->rom_addr; + lseek(wav->current_fd, wav->base_offset, SEEK_SET); } else { assertf(wpos == wav->wave.len - wav->wave.loop_len, "wav64: seeking to %x not supported (%x %x)\n", wpos, wav->wave.len, wav->wave.loop_len); memcpy(&vhead->state, &vhead->loop_state, sizeof(vhead->state)); - vhead->current_rom_addr = wav->rom_addr + (wav->wave.len - wav->wave.loop_len) / 16 * 9; + lseek(wav->current_fd, (wav->wave.len - wav->wave.loop_len) / 16 * 9, SEEK_CUR); } } @@ -208,8 +223,7 @@ static void waveform_vadpcm_read(void *ctx, samplebuffer_t *sbuf, int wpos, int void *src = (void*)dest + ((nframes*16) << SAMPLES_BPS_SHIFT(sbuf)) - src_bytes; // Fetch compressed data - dma_read(src, vhead->current_rom_addr, src_bytes); - vhead->current_rom_addr += src_bytes; + read(wav->current_fd, src, src_bytes); #if VADPCM_REFERENCE_DECODER if (wav->wave.channels == 1) { @@ -252,38 +266,30 @@ static void waveform_vadpcm_read(void *ctx, samplebuffer_t *sbuf, int wpos, int rspq_highpri_end(); } -void wav64_open(wav64_t *wav, const char *fn) { +void wav64_open(wav64_t *wav, const char *file_name) { memset(wav, 0, sizeof(*wav)); - // Currently, we only support streaming WAVs from DFS (ROMs). Any other - // filesystem is unsupported. - // For backward compatibility, we also silently accept a non-prefixed path. - if (strstr(fn, ":/")) { - assertf(strncmp(fn, "rom:/", 5) == 0, "Cannot open %s: wav64 only supports files in ROM (rom:/)", fn); - fn += 5; - } - - int fh = dfs_open(fn); - assertf(fh >= 0, "error opening file %s: %s (%d)\n", fn, dfs_strerror(fh), fh); - + // Open the input file. + int file_handle = must_open(file_name); wav64_header_t head = {0}; - dfs_read(&head, 1, sizeof(head), fh); + read(file_handle, &head, sizeof(head)); if (memcmp(head.id, WAV64_ID, 4) != 0) { assertf(memcmp(head.id, WAV_RIFF_ID, 4) != 0 && memcmp(head.id, WAV_RIFX_ID, 4) != 0, - "wav64 %s: use audioconv64 to convert to wav64 format", fn); + "wav64 %s: use audioconv64 to convert to wav64 format", file_name); assertf(0, "wav64 %s: invalid ID: %02x%02x%02x%02x\n", - fn, head.id[0], head.id[1], head.id[2], head.id[3]); + file_name, head.id[0], head.id[1], head.id[2], head.id[3]); } assertf(head.version == WAV64_FILE_VERSION, "wav64 %s: invalid version: %02x\n", - fn, head.version); + file_name, head.version); - wav->wave.name = fn; + wav->wave.name = file_name; wav->wave.channels = head.channels; wav->wave.bits = head.nbits; wav->wave.frequency = head.freq; wav->wave.len = head.len; wav->wave.loop_len = head.loop_len; - wav->rom_addr = dfs_rom_addr(fn) + head.start_offset; + wav->current_fd = file_handle; + wav->base_offset = head.start_offset; wav->format = head.format; switch (head.format) { @@ -294,29 +300,28 @@ void wav64_open(wav64_t *wav, const char *fn) { case WAV64_FORMAT_VADPCM: { wav64_header_vadpcm_t vhead = {0}; - dfs_read(&vhead, 1, sizeof(vhead), fh); - + read(file_handle, &vhead, sizeof(vhead)); int codebook_size = vhead.npredictors * vhead.order * head.channels * sizeof(wav64_vadpcm_vector_t); void *ext = malloc_uncached(sizeof(vhead) + codebook_size); memcpy(ext, &vhead, sizeof(vhead)); - dfs_read(ext + sizeof(vhead), 1, codebook_size, fh); + read(file_handle, ext + sizeof(vhead), codebook_size); wav->ext = ext; wav->wave.read = waveform_vadpcm_read; wav->wave.ctx = wav; assertf(head.loop_len == 0 || head.loop_len % 16 == 0, - "wav64 %s: invalid loop length: %ld\n", fn, head.loop_len); + "wav64 %s: invalid loop length: %ld\n", file_name, head.loop_len); } break; case WAV64_FORMAT_OPUS: { - wav64_opus_init(wav, fh); + wav64_opus_init(wav, file_handle); } break; default: - assertf(0, "wav64 %s: invalid format: %02x\n", fn, head.format); + assertf(0, "wav64 %s: invalid format: %02x\n", file_name, head.format); } - dfs_close(fh); + lseek(wav->current_fd, wav->base_offset, SEEK_SET); } void wav64_play(wav64_t *wav, int ch) @@ -324,7 +329,7 @@ void wav64_play(wav64_t *wav, int ch) // Update the context pointer, so that we try to catch cases where the // wav64_t instance was moved. wav->wave.ctx = wav; - mixer_ch_play(ch, &wav->wave); + mixer_ch_play(ch, &wav->wave); } void wav64_set_loop(wav64_t *wav, bool loop) { @@ -362,5 +367,9 @@ void wav64_close(wav64_t *wav) break; } wav->ext = NULL; + if (wav->current_fd >= 0) { + close(wav->current_fd); + wav->current_fd = -1; + } } } \ No newline at end of file diff --git a/src/audio/wav64_opus.c b/src/audio/wav64_opus.c index 92016efe64..9479ba3f16 100644 --- a/src/audio/wav64_opus.c +++ b/src/audio/wav64_opus.c @@ -41,6 +41,7 @@ #include "n64sys.h" #include "rspq.h" #include "utils.h" +#include #include "libopus_internal.h" @@ -54,35 +55,28 @@ typedef struct { /// @brief Wav64 Opus state typedef struct { wav64_opus_header_ext xhead; ///< Opus header extension - uint32_t current_rom_addr; ///< Current ROM reading address OpusCustomMode *mode; ///< Opus custom mode for this file OpusCustomDecoder *dec; ///< Opus decoder for this file } wav64_opus_state; -static uint16_t io_read16(uint32_t addr) { - uint32_t value = io_read(addr & ~3); - if (!(addr & 2)) - value >>= 16; - return value; -} - - static void waveform_opus_read(void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) { + static int callcount = 0; + callcount += 1; wav64_t *wav = (wav64_t*)ctx; wav64_opus_state *st = (wav64_opus_state*)wav->ext; if (seeking) { if (wpos == 0) { - st->current_rom_addr = wav->rom_addr; - opus_custom_decoder_ctl(st->dec, OPUS_RESET_STATE); + lseek(wav->current_fd, wav->base_offset, SEEK_SET); + opus_custom_decoder_ctl(st->dec, OPUS_RESET_STATE); } else { - assertf(0, "seeking not support in wav64 with opus compression"); + assertf(0, "seeking not support in wav64 with opus compression"); } } // Allocate stack buffer for reading compressed data. Align it to cacheline // to avoid any false sharing. - uint8_t alignas(16) buf[st->xhead.max_cmp_frame_size]; + uint8_t alignas(16) buf[st->xhead.max_cmp_frame_size + 1]; int nframes = DIVIDE_CEIL(wlen, st->xhead.frame_size); // Make space for the decoded samples. Call samplebuffer_append once as we @@ -97,17 +91,18 @@ static void waveform_opus_read(void *ctx, samplebuffer_t *sbuf, int wpos, int wl memset(out, 0, st->xhead.frame_size * wav->wave.channels * sizeof(int16_t)); } else { // Read frame size - int nb = io_read16(st->current_rom_addr); - assertf(nb <= st->xhead.max_cmp_frame_size, "opus frame size too large: %d (%ld)", nb, st->xhead.max_cmp_frame_size); + uint16_t nb = 0; + read(wav->current_fd, &nb, 2); + assertf(nb <= st->xhead.max_cmp_frame_size, "opus frame size too large: %08X (%ld) %d", nb, st->xhead.max_cmp_frame_size, callcount); - // Read frame - data_cache_hit_writeback_invalidate(buf, nb); - dma_read(buf, st->current_rom_addr + 2, nb); + unsigned long aligned_frame_size = nb; + if (aligned_frame_size & 1) { + aligned_frame_size += 1; + } - // Update ROM pointer after current frame - st->current_rom_addr += nb + 2; - if (st->current_rom_addr & 1) - st->current_rom_addr += 1; + // Read frame + data_cache_hit_writeback_invalidate(buf, aligned_frame_size); + read(wav->current_fd, buf, aligned_frame_size); // Decode frame int err = opus_custom_decode(st->dec, buf, nb, out, st->xhead.frame_size); @@ -127,8 +122,8 @@ static void waveform_opus_read(void *ctx, samplebuffer_t *sbuf, int wpos, int wl } void wav64_opus_init(wav64_t *wav, int fh) { - wav64_opus_header_ext xhead; - dfs_read(&xhead, sizeof(xhead), 1, fh); + wav64_opus_header_ext xhead; + read(fh, &xhead, sizeof(xhead)); debugf("opus header: frame_size=%ld, max_cmp_frame_size=%ld, bitrate_bps=%ld\n", xhead.frame_size, xhead.max_cmp_frame_size, xhead.bitrate_bps); debugf("frequency: %f\n", wav->wave.frequency); @@ -136,13 +131,12 @@ void wav64_opus_init(wav64_t *wav, int fh) { int err = OPUS_OK; OpusCustomMode *custom_mode = opus_custom_mode_create(wav->wave.frequency, xhead.frame_size, &err); - assert(err == OPUS_OK); + assertf(err == OPUS_OK, "%i", err); OpusCustomDecoder *dec = opus_custom_decoder_create(custom_mode, wav->wave.channels, &err); assert(err == OPUS_OK); // FIXME: try to avoid one allocation by allocating the decoder in the same malloc wav64_opus_state *state = malloc(sizeof(wav64_opus_state)); - state->current_rom_addr = 0; state->mode = custom_mode; state->dec = dec; state->xhead = xhead; diff --git a/src/audio/wav64_opus_internal.h b/src/audio/wav64_opus_internal.h index 936005bf14..9622165265 100644 --- a/src/audio/wav64_opus_internal.h +++ b/src/audio/wav64_opus_internal.h @@ -8,6 +8,7 @@ #define LIBDRAGON_AUDIO_WAV64_OPUS_INTERNAL_H #include +#include #include "wav64.h" /** @brief Initialize opus decompression on a wav64 file */ diff --git a/src/audio/wav64internal.h b/src/audio/wav64internal.h index 1de85ef21d..546b2db602 100644 --- a/src/audio/wav64internal.h +++ b/src/audio/wav64internal.h @@ -31,8 +31,8 @@ typedef struct __attribute__((aligned(8))) { typedef struct __attribute__((packed, aligned(8))) { int8_t npredictors; ///< Number of predictors int8_t order; ///< Order of the predictors - int16_t padding; ///< Padding - uint32_t current_rom_addr; ///< Current address in ROM + uint16_t padding; ///< padding + uint32_t padding1; ///< padding1 wav64_vadpcm_vector_t loop_state[2];///< State at the loop point wav64_vadpcm_vector_t state[2]; ///< Current decompression state wav64_vadpcm_vector_t codebook[]; ///< Codebook of the predictors @@ -43,8 +43,15 @@ typedef struct samplebuffer_s samplebuffer_t; /** * @brief Utility function to help implementing #WaveformRead for uncompressed (raw) samples. * - * This function uses PI DMA to load samples from ROM into the sample buffer. + * This function uses a file descriptor to load samples from ROM into the sample buffer. */ -void raw_waveform_read(samplebuffer_t *sbuf, int base_rom_addr, int wpos, int wlen, int bps); +void raw_waveform_read(samplebuffer_t *sbuf, int fd, int wpos, int wlen, int bps); +/** + * @brief Utility function to help implementing #WaveformRead for uncompressed (raw) samples. + * + * This function uses PI DMA to load samples from ROM into the sample buffer. + * Note: Tempory function should be removed when XM64 moves to using FILE*. + */ +void raw_waveform_read_address(samplebuffer_t *sbuf, int rom_addr, int wpos, int wlen, int bps); #endif diff --git a/src/audio/xm64.c b/src/audio/xm64.c index a0756bcfd8..e7f9ff3b4e 100644 --- a/src/audio/xm64.c +++ b/src/audio/xm64.c @@ -13,7 +13,7 @@ static void wave_read(void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) { xm_sample_t *samp = (xm_sample_t*)ctx; - raw_waveform_read(sbuf, samp->data8_offset, wpos, wlen, samp->bits >> 4); + raw_waveform_read_address(sbuf, samp->data8_offset, wpos, wlen, samp->bits >> 4); } static int tick(void *arg) {