make audio output thread safe(r?)

This commit is contained in:
RSDuck 2020-11-11 17:54:27 +01:00
parent 1b0a24a9bd
commit 05b94eff66
3 changed files with 91 additions and 38 deletions

View File

@ -999,6 +999,7 @@ u32 RunFrame()
ARM7Timestamp-SysTimestamp, ARM7Timestamp-SysTimestamp,
GPU3D::Timestamp-SysTimestamp); GPU3D::Timestamp-SysTimestamp);
#endif #endif
SPU::TransferOutput();
NDSCart::FlushSRAMFile(); NDSCart::FlushSRAMFile();

View File

@ -18,6 +18,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "Platform.h"
#include "NDS.h" #include "NDS.h"
#include "DSi.h" #include "DSi.h"
#include "SPU.h" #include "SPU.h"
@ -61,11 +62,15 @@ const s16 PSGTable[8][8] =
{-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF} {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF}
}; };
const u32 OutputBufferSize = 2*1024; const u32 OutputBufferSize = 2*2048;
s16 OutputBuffer[2 * OutputBufferSize]; s16 OutputBackbuffer[2 * OutputBufferSize];
volatile u32 OutputReadOffset; u32 OutputBackbufferWritePosition;
volatile u32 OutputWriteOffset;
s16 OutputFrontBuffer[2 * OutputBufferSize];
u32 OutputFrontBufferWritePosition;
u32 OutputFrontBufferReadPosition;
Platform::Mutex* AudioLock;
u16 Cnt; u16 Cnt;
u8 MasterVolume; u8 MasterVolume;
@ -83,6 +88,8 @@ bool Init()
Capture[0] = new CaptureUnit(0); Capture[0] = new CaptureUnit(0);
Capture[1] = new CaptureUnit(1); Capture[1] = new CaptureUnit(1);
AudioLock = Platform::Mutex_Create();
return true; return true;
} }
@ -93,6 +100,8 @@ void DeInit()
delete Capture[0]; delete Capture[0];
delete Capture[1]; delete Capture[1];
Platform::Mutex_Free(AudioLock);
} }
void Reset() void Reset()
@ -114,10 +123,13 @@ void Reset()
void Stop() void Stop()
{ {
memset(OutputBuffer, 0, 2*OutputBufferSize*2); Platform::Mutex_Lock(AudioLock);
memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2);
OutputReadOffset = 0; OutputBackbufferWritePosition = 0;
OutputWriteOffset = 0; OutputFrontBufferReadPosition = 0;
OutputFrontBufferWritePosition = 0;
Platform::Mutex_Unlock(AudioLock);
} }
void DoSavestate(Savestate* file) void DoSavestate(Savestate* file)
@ -704,59 +716,88 @@ void Mix(u32 dummy)
if (rightoutput < -0x8000) rightoutput = -0x8000; if (rightoutput < -0x8000) rightoutput = -0x8000;
else if (rightoutput > 0x7FFF) rightoutput = 0x7FFF; else if (rightoutput > 0x7FFF) rightoutput = 0x7FFF;
OutputBuffer[OutputWriteOffset ] = leftoutput >> 1; // OutputBufferFrame can never get full because it's
OutputBuffer[OutputWriteOffset + 1] = rightoutput >> 1; // transfered to OutputBuffer at the end of the frame
OutputWriteOffset += 2; OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1;
OutputWriteOffset &= ((2*OutputBufferSize)-1); OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1;
if (OutputWriteOffset == OutputReadOffset) OutputBackbufferWritePosition += 2;
{
//printf("!! SOUND FIFO OVERFLOW %d\n", OutputWriteOffset>>1);
// advance the read position too, to avoid losing the entire FIFO
OutputReadOffset += 2;
OutputReadOffset &= ((2*OutputBufferSize)-1);
}
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0); NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0);
} }
void TransferOutput()
{
Platform::Mutex_Lock(AudioLock);
for (u32 i = 0; i < OutputBackbufferWritePosition; i += 2)
{
OutputFrontBuffer[OutputFrontBufferWritePosition ] = OutputBackbuffer[i ];
OutputFrontBuffer[OutputFrontBufferWritePosition + 1] = OutputBackbuffer[i + 1];
OutputFrontBufferWritePosition += 2;
OutputFrontBufferWritePosition &= OutputBufferSize*2-1;
if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition)
{
// advance the read position too, to avoid losing the entire FIFO
OutputFrontBufferReadPosition += 2;
OutputFrontBufferReadPosition &= OutputBufferSize*2-1;
}
}
OutputBackbufferWritePosition = 0;
Platform::Mutex_Unlock(AudioLock);
}
void TrimOutput() void TrimOutput()
{ {
Platform::Mutex_Lock(AudioLock);
const int halflimit = (OutputBufferSize / 2); const int halflimit = (OutputBufferSize / 2);
int readpos = OutputWriteOffset - (halflimit*2); int readpos = OutputFrontBufferWritePosition - (halflimit*2);
if (readpos < 0) readpos += (OutputBufferSize*2); if (readpos < 0) readpos += (OutputBufferSize*2);
OutputReadOffset = readpos; OutputFrontBufferReadPosition = readpos;
Platform::Mutex_Unlock(AudioLock);
} }
void DrainOutput() void DrainOutput()
{ {
OutputReadOffset = 0; Platform::Mutex_Lock(AudioLock);
OutputWriteOffset = 0; OutputFrontBufferWritePosition = 0;
OutputFrontBufferReadPosition = 0;
Platform::Mutex_Unlock(AudioLock);
} }
void InitOutput() void InitOutput()
{ {
memset(OutputBuffer, 0, 2*OutputBufferSize*2); Platform::Mutex_Lock(AudioLock);
OutputReadOffset = 0; memset(OutputBackbuffer, 0, 2*OutputBufferSize*2);
OutputWriteOffset = OutputBufferSize; memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2);
OutputFrontBufferReadPosition = 0;
OutputFrontBufferWritePosition = 0;
Platform::Mutex_Unlock(AudioLock);
} }
int GetOutputSize() int GetOutputSize()
{ {
Platform::Mutex_Lock(AudioLock);
int ret; int ret;
if (OutputWriteOffset >= OutputReadOffset) if (OutputFrontBufferWritePosition >= OutputFrontBufferReadPosition)
ret = OutputWriteOffset - OutputReadOffset; ret = OutputFrontBufferWritePosition - OutputFrontBufferReadPosition;
else else
ret = (OutputBufferSize*2) - OutputReadOffset + OutputWriteOffset; ret = (OutputBufferSize*2) - OutputFrontBufferReadPosition + OutputFrontBufferWritePosition;
ret >>= 1; ret >>= 1;
Platform::Mutex_Unlock(AudioLock);
return ret; return ret;
} }
void Sync(bool wait) void Sync(bool wait)
{ {
// this function is currently not used anywhere
// depending on the usage context the thread safety measures could be made
// a lot faster
// sync to audio output in case the core is running too fast // sync to audio output in case the core is running too fast
// * wait=true: wait until enough audio data has been played // * wait=true: wait until enough audio data has been played
// * wait=false: merely skip some audio data to avoid a FIFO overflow // * wait=false: merely skip some audio data to avoid a FIFO overflow
@ -770,32 +811,42 @@ void Sync(bool wait)
} }
else if (GetOutputSize() > halflimit) else if (GetOutputSize() > halflimit)
{ {
int readpos = OutputWriteOffset - (halflimit*2); Platform::Mutex_Lock(AudioLock);
int readpos = OutputFrontBufferWritePosition - (halflimit*2);
if (readpos < 0) readpos += (OutputBufferSize*2); if (readpos < 0) readpos += (OutputBufferSize*2);
OutputReadOffset = readpos; OutputFrontBufferReadPosition = readpos;
Platform::Mutex_Unlock(AudioLock);
} }
} }
int ReadOutput(s16* data, int samples) int ReadOutput(s16* data, int samples)
{ {
if (OutputReadOffset == OutputWriteOffset) Platform::Mutex_Lock(AudioLock);
if (OutputFrontBufferReadPosition == OutputFrontBufferWritePosition)
{
Platform::Mutex_Unlock(AudioLock);
return 0; return 0;
}
for (int i = 0; i < samples; i++) for (int i = 0; i < samples; i++)
{ {
*data++ = OutputBuffer[OutputReadOffset]; *data++ = OutputFrontBuffer[OutputFrontBufferReadPosition];
*data++ = OutputBuffer[OutputReadOffset + 1]; *data++ = OutputFrontBuffer[OutputFrontBufferReadPosition + 1];
//if (OutputReadOffset != OutputWriteOffset) OutputFrontBufferReadPosition += 2;
OutputFrontBufferReadPosition &= ((2*OutputBufferSize)-1);
if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition)
{ {
OutputReadOffset += 2; Platform::Mutex_Unlock(AudioLock);
OutputReadOffset &= ((2*OutputBufferSize)-1);
}
if (OutputReadOffset == OutputWriteOffset)
return i+1; return i+1;
}
} }
Platform::Mutex_Unlock(AudioLock);
return samples; return samples;
} }

View File

@ -41,6 +41,7 @@ void InitOutput();
int GetOutputSize(); int GetOutputSize();
void Sync(bool wait); void Sync(bool wait);
int ReadOutput(s16* data, int samples); int ReadOutput(s16* data, int samples);
void TransferOutput();
u8 Read8(u32 addr); u8 Read8(u32 addr);
u16 Read16(u32 addr); u16 Read16(u32 addr);