streaming: Fix prefetch behaviour, read discards race, sfx and stream playback position race

This commit is contained in:
Stefanos Kornilios Mitsis Poiitidis
2025-03-29 16:23:44 +02:00
parent 99431370a2
commit 9eace08c7e
5 changed files with 97 additions and 35 deletions

View File

@@ -11,6 +11,8 @@
#include "crossplatform.h" #include "crossplatform.h"
#if !defined(AUDIO_OAL) && !defined(AUDIO_MSS) #if !defined(AUDIO_OAL) && !defined(AUDIO_MSS)
#define syncf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
#define streamf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
#define verbosef(...) // dbglog(DBG_CRITICAL, __VA_ARGS__) #define verbosef(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
#define debugf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__) #define debugf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
@@ -47,6 +49,8 @@
/* Quick access to the AICA channels */ /* Quick access to the AICA channels */
#define AICA_CHANNEL(x) (AICA_MEM_CHANNELS + (x) * sizeof(aica_channel_t)) #define AICA_CHANNEL(x) (AICA_MEM_CHANNELS + (x) * sizeof(aica_channel_t))
static uint32_t chn_version[64];
int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int pan, int loop, int freq) { int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int pan, int loop, int freq) {
// assert(size <= 65534); // assert(size <= 65534);
// We gotta fix this at some point // We gotta fix this at some point
@@ -70,6 +74,7 @@ int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int
chan->freq = freq; chan->freq = freq;
chan->vol = vol; chan->vol = vol;
chan->pan = pan; chan->pan = pan;
chan->version = ++chn_version[chn];
snd_sh4_to_aica(tmp, cmd->size); snd_sh4_to_aica(tmp, cmd->size);
return chn; return chn;
} }
@@ -363,6 +368,13 @@ cSampleManager::Initialise(void)
if (channels[i].ch != -1) { if (channels[i].ch != -1) {
assert(channels[i].nSfx != -1); assert(channels[i].nSfx != -1);
uint32_t channel_version = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, version));
if (chn_version[channels[i].ch] != channel_version) {
syncf("Stream version missmatch, skipping update. expected %d got %d\n", chn_version[channels[i].ch], channel_version);
continue;
}
uint16_t channel_pos = (g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, pos)) & 0xffff); uint16_t channel_pos = (g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, pos)) & 0xffff);
// verbosef("Channel %d pos: %d\n", i, channel_pos); // verbosef("Channel %d pos: %d\n", i, channel_pos);
if (!channels[i].loop) { if (!channels[i].loop) {
@@ -389,21 +401,28 @@ cSampleManager::Initialise(void)
{ {
std::lock_guard<std::mutex> lk(streams[i].mtx); std::lock_guard<std::mutex> lk(streams[i].mtx);
if (streams[i].playing) { if (streams[i].playing) {
uint32_t channel_version = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(streams[i].mapped_ch[0]) + offsetof(aica_channel_t, version));
if (chn_version[streams[i].mapped_ch[0]] != channel_version) {
syncf("Stream version missmatch, skipping update. expected %d got %d\n", chn_version[streams[i].mapped_ch[0]], channel_version);
continue;
}
// get channel pos // get channel pos
uint32_t channel_pos = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(streams[i].mapped_ch[0]) + offsetof(aica_channel_t, pos)) & 0xffff; uint32_t channel_pos = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(streams[i].mapped_ch[0]) + offsetof(aica_channel_t, pos)) & 0xffff;
uint32_t logical_pos = channel_pos; uint32_t logical_pos = channel_pos;
if (logical_pos > STREAM_CHANNEL_SAMPLE_COUNT/2) { if (logical_pos > STREAM_CHANNEL_SAMPLE_COUNT/2) {
logical_pos -= STREAM_CHANNEL_SAMPLE_COUNT/2; logical_pos -= STREAM_CHANNEL_SAMPLE_COUNT/2;
} }
verbosef("Stream %d pos: %d, log: %d, rem: %d\n", i, channel_pos, logical_pos, streams[i].played_samples); streamf("Stream %d pos: %d, log: %d, played: %d\n", i, channel_pos, logical_pos, streams[i].played_samples);
bool can_refill = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples; bool can_refill = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2 + (!streams[i].first_refill)*STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples;
bool can_fetch = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples; bool can_fetch = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2 + (!streams[i].first_refill)*STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples;
// copy over data if needed from staging // copy over data if needed from staging
if (channel_pos >= STREAM_CHANNEL_SAMPLE_COUNT/2 && !streams[i].next_is_upper_half) { if (channel_pos >= STREAM_CHANNEL_SAMPLE_COUNT/2 && !streams[i].next_is_upper_half) {
streams[i].next_is_upper_half = true; streams[i].next_is_upper_half = true;
if (can_refill) { // could we need a refill? if (can_refill) { // could we need a refill?
verbosef("Filling channel %d with lower half\n", i); streamf("Filling channel %d with lower half\n", i);
// fill lower half // fill lower half
spu_memload(streams[i].aica_buffers[0], streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2); spu_memload(streams[i].aica_buffers[0], streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2);
if (streams[i].stereo) { if (streams[i].stereo) {
@@ -419,7 +438,7 @@ cSampleManager::Initialise(void)
} else if (channel_pos < STREAM_CHANNEL_SAMPLE_COUNT/2 && streams[i].next_is_upper_half) { } else if (channel_pos < STREAM_CHANNEL_SAMPLE_COUNT/2 && streams[i].next_is_upper_half) {
streams[i].next_is_upper_half = false; streams[i].next_is_upper_half = false;
if (can_refill) { // could we need a refill? if (can_refill) { // could we need a refill?
verbosef("Filling channel %d with upper half\n", i); streamf("Filling channel %d with upper half\n", i);
// fill upper half // fill upper half
spu_memload(streams[i].aica_buffers[0] + STREAM_CHANNEL_BUFFER_SIZE/2, streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2); spu_memload(streams[i].aica_buffers[0] + STREAM_CHANNEL_BUFFER_SIZE/2, streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2);
if (streams[i].stereo) { if (streams[i].stereo) {
@@ -439,7 +458,7 @@ cSampleManager::Initialise(void)
// if end of file, stop // if end of file, stop
if ((streams[i].played_samples + logical_pos) > streams[i].total_samples) { if ((streams[i].played_samples + logical_pos) > streams[i].total_samples) {
// stop channel // stop channel
debugf("Auto stopping stream: %d -> {%d, %d}, %d total\n", i, streams[i].mapped_ch[0], streams[i].mapped_ch[1], streams[i].total_samples); streamf("Auto stopping stream: %d -> {%d, %d}, %d total\n", i, streams[i].mapped_ch[0], streams[i].mapped_ch[1], streams[i].total_samples);
aica_stop_chn(streams[i].mapped_ch[0]); aica_stop_chn(streams[i].mapped_ch[0]);
aica_stop_chn(streams[i].mapped_ch[1]); aica_stop_chn(streams[i].mapped_ch[1]);
streams[i].playing = false; streams[i].playing = false;
@@ -447,7 +466,7 @@ cSampleManager::Initialise(void)
} }
if (do_read) { if (do_read) {
debugf("Queueing stream read: %d, file: %d, buffer: %p, size: %d, tell: %d\n", i, streams[i].fd, streams[i].buffer, do_read, fs_tell(streams[i].fd)); streamf("Queueing stream read: %d, file: %d, buffer: %p, size: %d, file_offset: %d\n", i, streams[i].fd, streams[i].buffer, do_read, streams[i].file_offset);
CdStreamQueueAudioRead(streams[i].fd, streams[i].buffer, do_read, streams[i].file_offset); CdStreamQueueAudioRead(streams[i].fd, streams[i].buffer, do_read, streams[i].file_offset);
streams[i].file_offset += do_read; streams[i].file_offset += do_read;
} }
@@ -487,6 +506,7 @@ cSampleManager::Initialise(void)
void void
cSampleManager::Terminate(void) cSampleManager::Terminate(void)
{ {
CdStreamDiscardAudioRead(fdPedSfx);
fs_close(fdPedSfx); fs_close(fdPedSfx);
} }
@@ -737,9 +757,9 @@ cSampleManager::LoadPedComment(uint32 nComment)
assert(m_aSamples[nComment].nByteSize <= PED_BLOCKSIZE_ADPCM); assert(m_aSamples[nComment].nByteSize <= PED_BLOCKSIZE_ADPCM);
CdStreamQueueAudioRead(nComment, (void*)nPedSlotSfxAddr[nCurrentPedSlot], m_aSamples[nComment].nByteSize, m_aSamples[nComment].nFileOffset, [](AudioReadCmd* cmd) { CdStreamQueueAudioRead(fdPedSfx, (void*)nPedSlotSfxAddr[nCurrentPedSlot], m_aSamples[nComment].nByteSize, m_aSamples[nComment].nFileOffset, [](AudioReadCmd* cmd) {
debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize); debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize);
fs_seek(fdPedSfx, cmd->seek, SEEK_SET); assert(fs_seek(fdPedSfx, cmd->seek, SEEK_SET) == cmd->seek);
// TODO: When we can dma directly to AICA, we can use this instead // TODO: When we can dma directly to AICA, we can use this instead
@@ -1027,7 +1047,7 @@ cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream, uint32_t seek_by
{ {
ASSERT( nStream < MAX_STREAMS ); ASSERT( nStream < MAX_STREAMS );
file_t f = fs_open(DCStreamedNameTable[nFile], O_RDONLY); file_t f = fs_open(DCStreamedNameTable[nFile], O_RDONLY);
debugf("PreloadStreamedFile(%p, %d, %d) is %s\n", f, nFile, nStream, DCStreamedNameTable[nFile]); streamf("PreloadStreamedFile(%p, %d, %d) is %s\n", f, nFile, nStream, DCStreamedNameTable[nFile]);
assert(f >= 0 ); assert(f >= 0 );
WavHeader hdr; WavHeader hdr;
assert(fs_read(f, &hdr, sizeof(hdr)) == sizeof(hdr)); assert(fs_read(f, &hdr, sizeof(hdr)) == sizeof(hdr));
@@ -1050,13 +1070,13 @@ cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream, uint32_t seek_by
streams[nStream].next_is_upper_half = true; streams[nStream].next_is_upper_half = true;
streams[nStream].first_refill = true; streams[nStream].first_refill = true;
debugf("PreloadStreamedFile: %s: stream: %d, freq: %d, chans: %d, byte size: %d, played samples: %d\n", DCStreamedNameTable[nFile], nStream, hdr.samplesPerSec, hdr.numOfChan, hdr.dataSize, streams[nStream].played_samples); streamf("PreloadStreamedFile: %s: stream: %d, freq: %d, chans: %d, byte size: %d, played samples: %d\n", DCStreamedNameTable[nFile], nStream, hdr.samplesPerSec, hdr.numOfChan, hdr.dataSize, streams[nStream].played_samples);
// How to avoid the lock? // How to avoid the lock?
if (seek_bytes_aligned) { if (seek_bytes_aligned) {
streams[nStream].played_samples = seek_bytes_aligned * (streams[nStream].stereo ? 1 : 2); streams[nStream].played_samples = seek_bytes_aligned * (streams[nStream].stereo ? 1 : 2);
debugf("Seeking aligned to: %d, played_samples: %d\n", seek_bytes_aligned, streams[nStream].played_samples); streamf("Seeking aligned to: %d, played_samples: %d\n", seek_bytes_aligned, streams[nStream].played_samples);
fs_seek(streams[nStream].fd, 2048 + seek_bytes_aligned, SEEK_SET); fs_seek(streams[nStream].fd, 2048 + seek_bytes_aligned, SEEK_SET);
} else { } else {
fs_seek(f, 2048, SEEK_SET); fs_seek(f, 2048, SEEK_SET);
@@ -1085,7 +1105,7 @@ cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream, uint32_t seek_by
streams[nStream].file_offset = fs_tell(f); streams[nStream].file_offset = fs_tell(f);
} }
verbosef("PreloadStreamedFile: %p %d - %s, %d, %d, \n", f, nFile, DCStreamedNameTable[nFile], streams[nStream].rate, streams[nStream].stereo); streamf("PreloadStreamedFile: %p %d - %s, %d, %d, \n", f, nFile, DCStreamedNameTable[nFile], streams[nStream].rate, streams[nStream].stereo);
} }
// we can't really pause a stream, so we just make it go very slow with zero volume // we can't really pause a stream, so we just make it go very slow with zero volume

View File

@@ -479,6 +479,7 @@ RemoveFirstInQueue(Queue *queue)
} }
std::vector<AudioReadCmd> pendingAudioReads; std::vector<AudioReadCmd> pendingAudioReads;
volatile int pendingAudioRead_fd = -1;
#if !defined(DC_SH4) #if !defined(DC_SH4)
std::mutex pendingAudioReadsMutex; std::mutex pendingAudioReadsMutex;
#endif #endif
@@ -487,8 +488,9 @@ void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, st
AudioReadCmd cmd = { pBuffer, fd, bytes, seek}; AudioReadCmd cmd = { pBuffer, fd, bytes, seek};
if (!callback) { if (!callback) {
cmd.callback = [](AudioReadCmd* cmd){ cmd.callback = [](AudioReadCmd* cmd){
lseek(cmd->fd, cmd->seek, SEEK_SET); assert(pendingAudioRead_fd == cmd->fd);
read(cmd->fd, cmd->dest, cmd->size); assert(lseek(cmd->fd, cmd->seek, SEEK_SET) == cmd->seek);
assert(read(cmd->fd, cmd->dest, cmd->size) == cmd->size);
}; };
} else { } else {
cmd.callback = callback; cmd.callback = callback;
@@ -500,7 +502,7 @@ void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, st
auto mask = irq_disable(); auto mask = irq_disable();
#endif #endif
for (auto it = pendingAudioReads.rbegin(); it != pendingAudioReads.rend(); ++it) { for (auto it = pendingAudioReads.rbegin(); it != pendingAudioReads.rend(); ++it) {
if (it->fd == -1 || it->fd == fd) { if (it->fd == -1) {
*it = cmd; *it = cmd;
goto out; goto out;
} }
@@ -532,6 +534,12 @@ void CdStreamDiscardAudioRead(int fd) {
#if defined(DC_SH4) #if defined(DC_SH4)
irq_restore(mask); irq_restore(mask);
#endif #endif
while (pendingAudioRead_fd == fd) {
#if defined(DC_SH4)
thd_pass();
#endif
}
} }
AudioReadCmd CdStreamNextAudioRead() { AudioReadCmd CdStreamNextAudioRead() {
@@ -548,6 +556,8 @@ AudioReadCmd CdStreamNextAudioRead() {
break; break;
} }
} }
assert(pendingAudioRead_fd == -1);
pendingAudioRead_fd = cmd.fd;
#if defined(DC_SH4) #if defined(DC_SH4)
irq_restore(mask); irq_restore(mask);
#endif #endif
@@ -568,6 +578,7 @@ int read_loop(int fd, void* pBuffer, size_t bytes) {
auto cmd = CdStreamNextAudioRead(); auto cmd = CdStreamNextAudioRead();
while (cmd.fd != -1) { while (cmd.fd != -1) {
cmd.callback(&cmd); cmd.callback(&cmd);
pendingAudioRead_fd = -1;
cmd = CdStreamNextAudioRead(); cmd = CdStreamNextAudioRead();
} }
} }
@@ -584,6 +595,7 @@ void *CdStreamThread(void *param)
auto cmd = CdStreamNextAudioRead(); auto cmd = CdStreamNextAudioRead();
while (cmd.fd != -1) { while (cmd.fd != -1) {
cmd.callback(&cmd); cmd.callback(&cmd);
pendingAudioRead_fd = -1;
cmd = CdStreamNextAudioRead(); cmd = CdStreamNextAudioRead();
} }

View File

@@ -12,6 +12,8 @@
#include "thread/thread.h" #include "thread/thread.h"
#if !defined(AUDIO_OAL) && !defined(AUDIO_MSS) #if !defined(AUDIO_OAL) && !defined(AUDIO_MSS)
#define syncf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
#define streamf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
#define verbosef(...) // dbglog(DBG_CRITICAL, __VA_ARGS__) #define verbosef(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
#define debugf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__) #define debugf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__)
@@ -46,6 +48,8 @@
/* Quick access to the AICA channels */ /* Quick access to the AICA channels */
#define AICA_CHANNEL(x) (AICA_MEM_CHANNELS + (x) * sizeof(aica_channel_t)) #define AICA_CHANNEL(x) (AICA_MEM_CHANNELS + (x) * sizeof(aica_channel_t))
static uint32_t chn_version[64];
int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int pan, int loop, int freq) { int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int pan, int loop, int freq) {
// assert(size <= 65534); // assert(size <= 65534);
// We gotta fix this at some point // We gotta fix this at some point
@@ -69,6 +73,7 @@ int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int
chan->freq = freq; chan->freq = freq;
chan->vol = vol; chan->vol = vol;
chan->pan = pan; chan->pan = pan;
chan->version = ++chn_version[chn];
snd_sh4_to_aica(tmp, cmd->size); snd_sh4_to_aica(tmp, cmd->size);
return chn; return chn;
} }
@@ -370,6 +375,12 @@ cSampleManager::Initialise(void)
if (channels[i].ch != -1) { if (channels[i].ch != -1) {
assert(channels[i].nSfx != -1); assert(channels[i].nSfx != -1);
uint32_t channel_version = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, version));
if (chn_version[channels[i].ch] != channel_version) {
syncf("SFX version missmatch, skipping update. expected %d got %d\n", chn_version[channels[i].ch], channel_version);
continue;
}
uint16_t channel_pos = (g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, pos)) & 0xffff); uint16_t channel_pos = (g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, pos)) & 0xffff);
// verbosef("Channel %d pos: %d\n", i, channel_pos); // verbosef("Channel %d pos: %d\n", i, channel_pos);
if (!channels[i].loop) { if (!channels[i].loop) {
@@ -396,21 +407,27 @@ cSampleManager::Initialise(void)
{ {
std::lock_guard<std::mutex> lk(streams[i].mtx); std::lock_guard<std::mutex> lk(streams[i].mtx);
if (streams[i].playing) { if (streams[i].playing) {
uint32_t channel_version = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(streams[i].mapped_ch[0]) + offsetof(aica_channel_t, version));
if (chn_version[streams[i].mapped_ch[0]] != channel_version) {
syncf("Stream version missmatch, skipping update. expected %d got %d\n", chn_version[streams[i].mapped_ch[0]], channel_version);
continue;
}
// get channel pos // get channel pos
uint32_t channel_pos = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(streams[i].mapped_ch[0]) + offsetof(aica_channel_t, pos)) & 0xffff; uint32_t channel_pos = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(streams[i].mapped_ch[0]) + offsetof(aica_channel_t, pos)) & 0xffff;
uint32_t logical_pos = channel_pos; uint32_t logical_pos = channel_pos;
if (logical_pos > STREAM_CHANNEL_SAMPLE_COUNT/2) { if (logical_pos > STREAM_CHANNEL_SAMPLE_COUNT/2) {
logical_pos -= STREAM_CHANNEL_SAMPLE_COUNT/2; logical_pos -= STREAM_CHANNEL_SAMPLE_COUNT/2;
} }
verbosef("Stream %d pos: %d, log: %d, rem: %d\n", i, channel_pos, logical_pos, streams[i].played_samples); streamf("Stream %d pos: %d, log: %d, played: %d\n", i, channel_pos, logical_pos, streams[i].played_samples);
bool can_refill = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples; bool can_refill = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2 + (!streams[i].first_refill)*STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples;
bool can_fetch = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples; bool can_fetch = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2 + (!streams[i].first_refill)*STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples;
// copy over data if needed from staging // copy over data if needed from staging
if (channel_pos >= STREAM_CHANNEL_SAMPLE_COUNT/2 && !streams[i].next_is_upper_half) { if (channel_pos >= STREAM_CHANNEL_SAMPLE_COUNT/2 && !streams[i].next_is_upper_half) {
streams[i].next_is_upper_half = true; streams[i].next_is_upper_half = true;
if (can_refill) { // could we need a refill? if (can_refill) { // could we need a refill?
verbosef("Filling channel %d with lower half\n", i); streamf("Filling channel %d with lower half\n", i);
// fill lower half // fill lower half
spu_memload(streams[i].aica_buffers[0], streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2); spu_memload(streams[i].aica_buffers[0], streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2);
if (streams[i].stereo) { if (streams[i].stereo) {
@@ -426,7 +443,7 @@ cSampleManager::Initialise(void)
} else if (channel_pos < STREAM_CHANNEL_SAMPLE_COUNT/2 && streams[i].next_is_upper_half) { } else if (channel_pos < STREAM_CHANNEL_SAMPLE_COUNT/2 && streams[i].next_is_upper_half) {
streams[i].next_is_upper_half = false; streams[i].next_is_upper_half = false;
if (can_refill) { // could we need a refill? if (can_refill) { // could we need a refill?
verbosef("Filling channel %d with upper half\n", i); streamf("Filling channel %d with upper half\n", i);
// fill upper half // fill upper half
spu_memload(streams[i].aica_buffers[0] + STREAM_CHANNEL_BUFFER_SIZE/2, streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2); spu_memload(streams[i].aica_buffers[0] + STREAM_CHANNEL_BUFFER_SIZE/2, streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2);
if (streams[i].stereo) { if (streams[i].stereo) {
@@ -446,7 +463,7 @@ cSampleManager::Initialise(void)
// if end of file, stop // if end of file, stop
if ((streams[i].played_samples + logical_pos) > streams[i].total_samples) { if ((streams[i].played_samples + logical_pos) > streams[i].total_samples) {
// stop channel // stop channel
debugf("Auto stopping stream: %d -> {%d, %d}, %d total\n", i, streams[i].mapped_ch[0], streams[i].mapped_ch[1], streams[i].total_samples); streamf("Auto stopping stream: %d -> {%d, %d}, %d total\n", i, streams[i].mapped_ch[0], streams[i].mapped_ch[1], streams[i].total_samples);
aica_stop_chn(streams[i].mapped_ch[0]); aica_stop_chn(streams[i].mapped_ch[0]);
aica_stop_chn(streams[i].mapped_ch[1]); aica_stop_chn(streams[i].mapped_ch[1]);
streams[i].playing = false; streams[i].playing = false;
@@ -454,7 +471,7 @@ cSampleManager::Initialise(void)
} }
if (do_read) { if (do_read) {
debugf("Queueing stream read: %d, file: %d, buffer: %p, size: %d, tell: %d\n", i, streams[i].fd, streams[i].buffer, do_read, fs_tell(streams[i].fd)); streamf("Queueing stream read: %d, file: %d, buffer: %p, size: %d, file_offset: %d\n", i, streams[i].fd, streams[i].buffer, do_read, streams[i].file_offset);
CdStreamQueueAudioRead(streams[i].fd, streams[i].buffer, do_read, streams[i].file_offset); CdStreamQueueAudioRead(streams[i].fd, streams[i].buffer, do_read, streams[i].file_offset);
streams[i].file_offset += do_read; streams[i].file_offset += do_read;
} }
@@ -494,6 +511,7 @@ cSampleManager::Initialise(void)
void void
cSampleManager::Terminate(void) cSampleManager::Terminate(void)
{ {
CdStreamDiscardAudioRead(fdPedSfx);
fs_close(fdPedSfx); fs_close(fdPedSfx);
} }
@@ -696,8 +714,8 @@ cSampleManager::LoadMissionAudio(uint8 nSlot, uint32 nSample)
ASSERT(nSample < TOTAL_AUDIO_SAMPLES); ASSERT(nSample < TOTAL_AUDIO_SAMPLES);
debugf("Loading mission audio comment %d, offset: %d, size: %d\n", nSample, m_aSamples[nSample].nFileOffset, m_aSamples[nSample].nByteSize); debugf("Loading mission audio comment %d, offset: %d, size: %d\n", nSample, m_aSamples[nSample].nFileOffset, m_aSamples[nSample].nByteSize);
CdStreamQueueAudioRead(nSample, (void*)gPlayerTalkData, m_aSamples[nSample].nByteSize, m_aSamples[nSample].nFileOffset, [](AudioReadCmd* cmd) { CdStreamQueueAudioRead(fdPedSfx, (void*)gPlayerTalkData, m_aSamples[nSample].nByteSize, m_aSamples[nSample].nFileOffset, [](AudioReadCmd* cmd) {
fs_seek(fdPedSfx, cmd->seek, SEEK_SET); assert(fs_seek(fdPedSfx, cmd->seek, SEEK_SET) == cmd->seek);
// TODO: When we can dma directly to AICA, we can use this instead // TODO: When we can dma directly to AICA, we can use this instead
// fs_read(fdPedSfx, SPU_BASE_U8 + (uintptr_t)cmd->dest, cmd->size); // fs_read(fdPedSfx, SPU_BASE_U8 + (uintptr_t)cmd->dest, cmd->size);
@@ -793,8 +811,8 @@ cSampleManager::LoadPedComment(uint32 nComment)
assert(m_aSamples[nComment].nByteSize <= pedBlocksizeMax); assert(m_aSamples[nComment].nByteSize <= pedBlocksizeMax);
debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize); debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize);
CdStreamQueueAudioRead(nComment, (void*)nPedSlotSfxAddr[nCurrentPedSlot], m_aSamples[nComment].nByteSize, m_aSamples[nComment].nFileOffset, [](AudioReadCmd* cmd) { CdStreamQueueAudioRead(fdPedSfx, (void*)nPedSlotSfxAddr[nCurrentPedSlot], m_aSamples[nComment].nByteSize, m_aSamples[nComment].nFileOffset, [](AudioReadCmd* cmd) {
fs_seek(fdPedSfx, cmd->seek, SEEK_SET); assert(fs_seek(fdPedSfx, cmd->seek, SEEK_SET) == cmd->seek);
// TODO: When we can dma directly to AICA, we can use this instead // TODO: When we can dma directly to AICA, we can use this instead
@@ -1098,7 +1116,7 @@ cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream, uint32_t seek_b
ASSERT( nFile < TOTAL_STREAMED_SOUNDS ); ASSERT( nFile < TOTAL_STREAMED_SOUNDS );
file_t f = fs_open(DCStreamedNameTable[nFile], O_RDONLY); file_t f = fs_open(DCStreamedNameTable[nFile], O_RDONLY);
debugf("PreloadStreamedFile(%p, %d, %d) is %s\n", f, nFile, nStream, DCStreamedNameTable[nFile]); streamf("PreloadStreamedFile(%p, %d, %d) is %s\n", f, nFile, nStream, DCStreamedNameTable[nFile]);
assert(f >= 0 ); assert(f >= 0 );
WavHeader hdr; WavHeader hdr;
assert(fs_read(f, &hdr, sizeof(hdr)) == sizeof(hdr)); assert(fs_read(f, &hdr, sizeof(hdr)) == sizeof(hdr));
@@ -1130,13 +1148,13 @@ cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream, uint32_t seek_b
streams[nStream].next_is_upper_half = true; streams[nStream].next_is_upper_half = true;
streams[nStream].first_refill = true; streams[nStream].first_refill = true;
debugf("PreloadStreamedFile: %s: stream: %d, freq: %d, chans: %d, byte size: %d, played samples: %d\n", DCStreamedNameTable[nFile], nStream, hdr.samplesPerSec, hdr.numOfChan, hdr.dataSize, streams[nStream].played_samples); streamf("PreloadStreamedFile: %s: stream: %d, freq: %d, chans: %d, byte size: %d, played samples: %d\n", DCStreamedNameTable[nFile], nStream, hdr.samplesPerSec, hdr.numOfChan, hdr.dataSize, streams[nStream].played_samples);
// How to avoid the lock? // How to avoid the lock?
if (seek_bytes_aligned) { if (seek_bytes_aligned) {
streams[nStream].played_samples = seek_bytes_aligned * (streams[nStream].stereo ? 1 : 2); streams[nStream].played_samples = seek_bytes_aligned * (streams[nStream].stereo ? 1 : 2);
debugf("Seeking aligned to: %d, played_samples: %d\n", seek_bytes_aligned, streams[nStream].played_samples); streamf("Seeking aligned to: %d, played_samples: %d\n", seek_bytes_aligned, streams[nStream].played_samples);
fs_seek(streams[nStream].fd, 2048 + seek_bytes_aligned, SEEK_SET); fs_seek(streams[nStream].fd, 2048 + seek_bytes_aligned, SEEK_SET);
} else { } else {
fs_seek(f, 2048, SEEK_SET); fs_seek(f, 2048, SEEK_SET);
@@ -1165,7 +1183,7 @@ cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream, uint32_t seek_b
streams[nStream].file_offset = fs_tell(f); streams[nStream].file_offset = fs_tell(f);
} }
verbosef("PreloadStreamedFile: %p %d - %s, %d, %d, \n", f, nFile, DCStreamedNameTable[nFile], streams[nStream].rate, streams[nStream].stereo); streamf("PreloadStreamedFile: %p %d - %s, %d, %d, \n", f, nFile, DCStreamedNameTable[nFile], streams[nStream].rate, streams[nStream].stereo);
} }
// we can't really pause a stream, so we just make it go very slow with zero volume // we can't really pause a stream, so we just make it go very slow with zero volume

View File

@@ -479,6 +479,7 @@ RemoveFirstInQueue(Queue *queue)
} }
std::vector<AudioReadCmd> pendingAudioReads; std::vector<AudioReadCmd> pendingAudioReads;
volatile int pendingAudioRead_fd = -1;
#if !defined(DC_SH4) #if !defined(DC_SH4)
std::mutex pendingAudioReadsMutex; std::mutex pendingAudioReadsMutex;
#endif #endif
@@ -487,8 +488,9 @@ void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, st
AudioReadCmd cmd = { pBuffer, fd, bytes, seek}; AudioReadCmd cmd = { pBuffer, fd, bytes, seek};
if (!callback) { if (!callback) {
cmd.callback = [](AudioReadCmd* cmd){ cmd.callback = [](AudioReadCmd* cmd){
lseek(cmd->fd, cmd->seek, SEEK_SET); assert(pendingAudioRead_fd == cmd->fd);
read(cmd->fd, cmd->dest, cmd->size); assert(lseek(cmd->fd, cmd->seek, SEEK_SET) == cmd->seek);
assert(read(cmd->fd, cmd->dest, cmd->size) == cmd->size);
}; };
} else { } else {
cmd.callback = callback; cmd.callback = callback;
@@ -500,7 +502,7 @@ void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, st
auto mask = irq_disable(); auto mask = irq_disable();
#endif #endif
for (auto it = pendingAudioReads.rbegin(); it != pendingAudioReads.rend(); ++it) { for (auto it = pendingAudioReads.rbegin(); it != pendingAudioReads.rend(); ++it) {
if (it->fd == -1 || it->fd == fd) { if (it->fd == -1) {
*it = cmd; *it = cmd;
goto out; goto out;
} }
@@ -532,6 +534,12 @@ void CdStreamDiscardAudioRead(int fd) {
#if defined(DC_SH4) #if defined(DC_SH4)
irq_restore(mask); irq_restore(mask);
#endif #endif
while (pendingAudioRead_fd == fd) {
#if defined(DC_SH4)
thd_pass();
#endif
}
} }
AudioReadCmd CdStreamNextAudioRead() { AudioReadCmd CdStreamNextAudioRead() {
@@ -548,6 +556,8 @@ AudioReadCmd CdStreamNextAudioRead() {
break; break;
} }
} }
assert(pendingAudioRead_fd == -1);
pendingAudioRead_fd = cmd.fd;
#if defined(DC_SH4) #if defined(DC_SH4)
irq_restore(mask); irq_restore(mask);
#endif #endif
@@ -568,6 +578,7 @@ int read_loop(int fd, void* pBuffer, size_t bytes) {
auto cmd = CdStreamNextAudioRead(); auto cmd = CdStreamNextAudioRead();
while (cmd.fd != -1) { while (cmd.fd != -1) {
cmd.callback(&cmd); cmd.callback(&cmd);
pendingAudioRead_fd = -1;
cmd = CdStreamNextAudioRead(); cmd = CdStreamNextAudioRead();
} }
} }
@@ -584,6 +595,7 @@ void *CdStreamThread(void *param)
auto cmd = CdStreamNextAudioRead(); auto cmd = CdStreamNextAudioRead();
while (cmd.fd != -1) { while (cmd.fd != -1) {
cmd.callback(&cmd); cmd.callback(&cmd);
pendingAudioRead_fd = -1;
cmd = CdStreamNextAudioRead(); cmd = CdStreamNextAudioRead();
} }