mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-09-10 12:45:09 -04:00
Reopen the OPUS decoder when a note moves to another sample (#7137)
The decoder cached on a note was keyed on nothing, so a note reused for a different streamed sample carried on decoding the previous track. Audible as the wrong custom music: the sequence and soundfont the audio editor reports are correct, only the samples reaching the mixer are not. Reproduced on the game-start cutscene chain with a streamed music pack in 4 of 6 runs; 0 of 6 after. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: serprex <159546+serprex@users.noreply.github.com>
This commit is contained in:
@@ -468,7 +468,7 @@ typedef struct {
|
||||
/* 0x00F0 */ s16 dummyResampleState[0x10];
|
||||
} NoteSynthesisBuffers; // size = 0x110
|
||||
|
||||
struct OggOpusFile;
|
||||
struct OpusDecState;
|
||||
|
||||
typedef struct {
|
||||
/* 0x00 */ u8 restart;
|
||||
@@ -488,7 +488,7 @@ typedef struct {
|
||||
/* 0x1A */ u8 unk_1A;
|
||||
/* 0x1C */ u16 unk_1C;
|
||||
/* 0x1E */ u16 unk_1E;
|
||||
struct OggOpusFile* opusFile; // Only for streamed opus audio
|
||||
struct OpusDecState* opusFile; // Only for streamed opus audio
|
||||
} NoteSynthesisState; // size = 0x20
|
||||
|
||||
typedef struct {
|
||||
|
||||
+33
-8
@@ -2,6 +2,7 @@
|
||||
//! when unoptimized and clang does not allow optimizing a single function.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mixer.h"
|
||||
@@ -108,28 +109,52 @@ void aLoadBufferImpl(const void* source_addr, uint16_t dest_addr, uint16_t nbyte
|
||||
|
||||
#include <opusfile.h>
|
||||
|
||||
void aOPUSdecImpl(void* source_addr, uint16_t dest_addr, uint16_t nbytes, struct OggOpusFile** decState, int32_t pos,
|
||||
// The decoder is cached on the note, so remember which buffer it was opened for.
|
||||
struct OpusDecState {
|
||||
OggOpusFile* file;
|
||||
const void* source;
|
||||
};
|
||||
|
||||
void aOPUSdecImpl(void* source_addr, uint16_t dest_addr, uint16_t nbytes, struct OpusDecState** decState, int32_t pos,
|
||||
uint32_t size) {
|
||||
int readSamples = 0;
|
||||
if (*decState == NULL) {
|
||||
*decState = op_open_memory(source_addr, size, NULL);
|
||||
struct OpusDecState* dec = *decState;
|
||||
|
||||
// Note reused for another streamed sample may have previous decoder opened,
|
||||
// which would keep playing the previous track under new note.
|
||||
if (dec != NULL && dec->source != source_addr) {
|
||||
aOPUSFree(dec);
|
||||
dec = NULL;
|
||||
*decState = NULL;
|
||||
}
|
||||
op_pcm_seek(*decState, pos);
|
||||
int ret = op_read(*decState, BUF_S16(dest_addr), nbytes / 2, NULL);
|
||||
if (dec == NULL) {
|
||||
OggOpusFile* file = op_open_memory(source_addr, size, NULL);
|
||||
if (file == NULL) {
|
||||
return;
|
||||
}
|
||||
dec = malloc(sizeof(struct OpusDecState));
|
||||
dec->file = file;
|
||||
dec->source = source_addr;
|
||||
*decState = dec;
|
||||
}
|
||||
|
||||
op_pcm_seek(dec->file, pos);
|
||||
int ret = op_read(dec->file, BUF_S16(dest_addr), nbytes / 2, NULL);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
readSamples += ret;
|
||||
while (readSamples < nbytes / 2) {
|
||||
ret = op_read(*decState, BUF_S16(dest_addr + readSamples * 2), (nbytes - readSamples * 2) / 2, NULL);
|
||||
ret = op_read(dec->file, BUF_S16(dest_addr + readSamples * 2), (nbytes - readSamples * 2) / 2, NULL);
|
||||
if (ret == 0)
|
||||
break;
|
||||
readSamples += ret;
|
||||
}
|
||||
}
|
||||
|
||||
void aOPUSFree(struct OggOpusFile* opusFile) {
|
||||
op_free(opusFile);
|
||||
void aOPUSFree(struct OpusDecState* dec) {
|
||||
op_free(dec->file);
|
||||
free(dec);
|
||||
}
|
||||
|
||||
void aSaveBufferImpl(uint16_t source_addr, int16_t* dest_addr, uint16_t nbytes) {
|
||||
|
||||
+3
-2
@@ -57,10 +57,11 @@ void aHiLoGainImpl(uint8_t g, uint16_t count, uint16_t addr);
|
||||
void aUnkCmd3Impl(uint16_t a, uint16_t b, uint16_t c);
|
||||
void aUnkCmd19Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_addr);
|
||||
|
||||
struct OggOpusFile;
|
||||
struct OpusDecState;
|
||||
|
||||
void aOPUSdecImpl(void* source_addr, uint16_t dest_addr, uint16_t nbytes, struct OggOpusFile** decState, int32_t pos,
|
||||
void aOPUSdecImpl(void* source_addr, uint16_t dest_addr, uint16_t nbytes, struct OpusDecState** decState, int32_t pos,
|
||||
uint32_t size);
|
||||
void aOPUSFree(struct OpusDecState* dec);
|
||||
|
||||
#define aSegment(pkt, s, b) \
|
||||
do { \
|
||||
|
||||
@@ -150,7 +150,7 @@ void Audio_NoteInit(Note* note) {
|
||||
note->noteSubEu = gDefaultNoteSub;
|
||||
}
|
||||
|
||||
extern void aOPUSFree(struct OggOpusFile* opusFile);
|
||||
extern void aOPUSFree(struct OpusDecState* dec);
|
||||
void Audio_NoteDisable(Note* note) {
|
||||
if (note->noteSubEu.bitField0.needsInit == true) {
|
||||
note->noteSubEu.bitField0.needsInit = false;
|
||||
|
||||
Reference in New Issue
Block a user