fix audio stream's packet larger than host buffer size

This commit is contained in:
RadWolfie 2020-01-05 18:35:57 -06:00
parent 08691c24e1
commit 54421832e5
3 changed files with 136 additions and 57 deletions

View File

@ -168,8 +168,11 @@ class X_CMcpxStream
struct host_voice_packet {
XTL::XMEDIAPACKET xmp_data;
PVOID pBuffer_data;
DWORD rangeStart;
bool isWritten;
DWORD nextWriteOffset;
DWORD lastWritePos;
DWORD bufPlayed;
DWORD bufWrittenIndex;
DWORD avgBytesPerSec;
bool isPlayed;
};
@ -250,7 +253,6 @@ class X_CDirectSoundStream
DWORD X_MaxAttachedPackets;
std::vector<struct host_voice_packet> Host_BufferPacketArray;
DWORD Host_dwWriteOffsetNext;
DWORD Host_dwTriggerRange;
bool Host_isProcessing;
LPFNXMOCALLBACK Xb_lpfnCallback;
LPVOID Xb_lpvContext;
@ -262,6 +264,7 @@ class X_CDirectSoundStream
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
X_DSVOICEPROPS Xb_VoiceProperties;
DWORD Xb_Frequency;
DWORD Host_dwLastWritePos;
};
// ******************************************************************

View File

@ -936,6 +936,54 @@ static inline HRESULT DSoundBufferSynchPlaybackFlagAdd(
return DS_OK;
}
static inline void DSoundStreamPacketUploadPartial(
XTL::X_CDirectSoundStream* pThis,
vector_hvp_iterator &bufferCurrent
)
{
// Don't write beyond given buffer data, force do nothing.
if (bufferCurrent->bufPlayed >= bufferCurrent->xmp_data.dwMaxSize) {
EmuLog(LOG_LEVEL::DEBUG, "Attempted packet buffer overflow | pThis = %08X | bufferCurrent = %08X | bufferSize - %08X | dwBufferBytes = %08X",
pThis, bufferCurrent, bufferCurrent->xmp_data.dwMaxSize, pThis->EmuBufferDesc.dwBufferBytes);
return;
}
uint32_t avgBytesPerSec = bufferCurrent->avgBytesPerSec;
uint32_t bufferOffset = avgBytesPerSec * bufferCurrent->bufWrittenIndex;
if ((bufferOffset + avgBytesPerSec) > bufferCurrent->xmp_data.dwMaxSize) {
avgBytesPerSec = bufferCurrent->xmp_data.dwMaxSize - bufferOffset;
}
DSoundStreamWriteToBuffer(pThis->EmuDirectSoundBuffer8, bufferCurrent->nextWriteOffset, (uint8_t*)bufferCurrent->pBuffer_data + bufferOffset, avgBytesPerSec);
bufferCurrent->nextWriteOffset += avgBytesPerSec;
if (pThis->EmuBufferDesc.dwBufferBytes < bufferCurrent->nextWriteOffset) {
bufferCurrent->nextWriteOffset -= pThis->EmuBufferDesc.dwBufferBytes;
}
#if 0
// Debug area begin
EmuLog(LOG_LEVEL::DEBUG, "upload packet buffer process | pThis = %08X | bufferCurrent = %08X",
pThis, bufferCurrent._Ptr);
EmuLog(LOG_LEVEL::DEBUG, "nextWriteOffset = %08X | bufPlayed = %08X | bufWrittenIndex = %08X",
bufferCurrent->nextWriteOffset, bufferCurrent->bufPlayed, bufferCurrent->bufWrittenIndex);
EmuLog(LOG_LEVEL::DEBUG, "bufferSize - %08X | dwBufferBytes = %08X | dwLastWritePos = %08X | dwWriteOffsetNext = %08X\n",
bufferCurrent->xmp_data.dwMaxSize, pThis->EmuBufferDesc.dwBufferBytes, pThis->Host_dwLastWritePos, pThis->Host_dwWriteOffsetNext);
// Debug area end
#endif
if (pThis->Host_isProcessing == false) {
pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
pThis->Host_isProcessing = true;
}
bufferCurrent->bufWrittenIndex++;
}
static inline void DSoundStreamEndPacket(XTL::X_CDirectSoundStream* pThis) {
pThis->Host_isProcessing = false;
pThis->EmuDirectSoundBuffer8->Stop();
}
static inline bool DSoundStreamProcess(XTL::X_CDirectSoundStream* pThis) {
// If title want to pause, then don't process the packets.
@ -956,23 +1004,11 @@ static inline bool DSoundStreamProcess(XTL::X_CDirectSoundStream* pThis) {
DWORD dwAudioBytes;
HRESULT hRet = pThis->EmuDirectSoundBuffer8->GetStatus(&dwAudioBytes);
if (hRet == DS_OK) {
std::vector<XTL::host_voice_packet>::iterator buffer = pThis->Host_BufferPacketArray.begin();
if (buffer->isWritten == false) {
prepareNextBufferPacket:
DSoundStreamWriteToBuffer(pThis->EmuDirectSoundBuffer8, buffer->rangeStart, buffer->pBuffer_data, buffer->xmp_data.dwMaxSize);
// Debug area begin
//printf("DEBUG: next packet process | pThis = %08X | rangeStart = %08d | bufferSize - %08d | dwBufferBytes = %08d | dwWriteOffsetNext = %08d\n", pThis, buffer->rangeStart, buffer->xmp_data.dwMaxSize, pThis->EmuBufferDesc->dwBufferBytes, pThis->Host_dwWriteOffsetNext);
// Debug area end
if (pThis->Host_isProcessing == false) {
pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
pThis->Host_isProcessing = true;
}
buffer->isWritten = true;
vector_hvp_iterator bufferCurrent = pThis->Host_BufferPacketArray.begin();
vector_hvp_iterator bufferPrev = bufferCurrent;
if (bufferCurrent->bufWrittenIndex == 0) {
DSoundStreamPacketUploadPartial(pThis, bufferCurrent);
} else {
// NOTE: p1. Do not use play cursor, use write cursor to check ahead since by the time it gets there. The buffer is already played.
// p2. Plus play cursor is not reliable to check, write cursor is reliable as it is update more often.
@ -981,45 +1017,71 @@ static inline bool DSoundStreamProcess(XTL::X_CDirectSoundStream* pThis) {
hRet = pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &writePos);
if (hRet == DS_OK) {
int bufPlayed = writePos - buffer->rangeStart;
int bufPlayed = writePos - bufferCurrent->lastWritePos;
// Correct it if buffer was playing and is at beginning.
if (bufPlayed < 0 && (buffer->isPlayed || (writePos + pThis->EmuBufferDesc.dwBufferBytes - buffer->rangeStart) < buffer->xmp_data.dwMaxSize)) {
bufPlayed = pThis->EmuBufferDesc.dwBufferBytes - (bufPlayed * -1);
if (writePos < bufferCurrent->lastWritePos) {
bufPlayed = writePos + (pThis->EmuBufferDesc.dwBufferBytes - bufferCurrent->lastWritePos);
}
bufferCurrent->lastWritePos = writePos;
bufferCurrent->bufPlayed += bufPlayed;
if (bufPlayed >= 0) {
if (buffer->isPlayed == false) {
buffer->isPlayed = true;
}
if (bufPlayed >= (int)buffer->xmp_data.dwMaxSize) {
if (bufferCurrent->isPlayed == false) {
bufferCurrent->isPlayed = true;
}
#if 0 // Extend debug verification
if (pThis->Host_BufferPacketArray.size() == 1) {
EmuLog(LOG_LEVEL::DEBUG, "pThis: %08X; bufPlayed: %08X; bufdesc-bufferBytes: %08X; xmp-maxSize: %08X",
pThis,
bufPlayed,
pThis->EmuBufferDesc.dwBufferBytes,
bufferCurrent->xmp_data.dwMaxSize
);
}
#endif
if (bufferCurrent->bufPlayed >= bufferCurrent->xmp_data.dwMaxSize) {
bufPlayed = bufferCurrent->bufPlayed - bufferCurrent->xmp_data.dwMaxSize;
bufferCurrent->bufPlayed = bufferCurrent->xmp_data.dwMaxSize;
}
if (bufferCurrent->xmp_data.pdwCompletedSize != xbnullptr) {
(*bufferCurrent->xmp_data.pdwCompletedSize) = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, bufferCurrent->bufPlayed);
}
if (bufferCurrent->bufPlayed == bufferCurrent->xmp_data.dwMaxSize) {
DSoundStreamClearPacket(buffer, XMP_STATUS_SUCCESS, pThis->Xb_lpfnCallback, pThis->Xb_lpvContext, pThis);
DSoundStreamClearPacket(bufferCurrent, XMP_STATUS_SUCCESS, pThis->Xb_lpfnCallback, pThis->Xb_lpvContext, pThis);
if (pThis->Host_BufferPacketArray.size() == 0) {
goto endOfPacket;
}
if (buffer->isWritten == false) {
goto prepareNextBufferPacket;
}
if (pThis->Host_BufferPacketArray.size() == 0) {
DSoundStreamEndPacket(pThis);
return 0;
}
if (buffer->xmp_data.pdwCompletedSize != xbnullptr) {
(*buffer->xmp_data.pdwCompletedSize) = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, bufPlayed);
#if 0 // Extend debug verification
EmuLog(LOG_LEVEL::DEBUG, "nextBuffer: %08X; bufferCurrent->bufPlayed: %08X; bufPlayed: %08X;\n",
bufferCurrent._Ptr,
bufferCurrent->bufPlayed,
bufPlayed
);
#endif
#if 0 //TODO: How to send extra play process to next packet?
// Save what had been played in next packet.
//bufferCurrent->bufPlayed += bufPlayed;
#endif
}
if (bufferCurrent == bufferPrev) {
if ((bufferCurrent->bufWrittenIndex * bufferCurrent->avgBytesPerSec) <= bufferCurrent->bufPlayed) {
DSoundStreamPacketUploadPartial(pThis, bufferCurrent);
}
if (pThis->Host_BufferPacketArray.size() > 1) {
if ((buffer + 1)->isWritten == false) {
buffer++;
goto prepareNextBufferPacket;
}
}
if (pThis->Host_BufferPacketArray.size() > 1) {
if ((bufferCurrent->xmp_data.dwMaxSize - bufferCurrent->bufPlayed) <= pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec
&& (bufferCurrent + 1)->bufWrittenIndex == 0) {
bufferCurrent++;
DSoundStreamPacketUploadPartial(pThis, bufferCurrent);
}
}
}
// Out of packets, let's stop it.
if (pThis->Host_BufferPacketArray.size() == 0) {
endOfPacket:
pThis->Host_isProcessing = false;
pThis->EmuDirectSoundBuffer8->Stop();
DSoundStreamEndPacket(pThis);
return 0;
}
}

View File

@ -236,16 +236,17 @@ HRESULT WINAPI XTL::EMUPATCH(DirectSoundCreateStream)
GeneratePCMFormat(DSBufferDesc, pdssd->lpwfxFormat, (*ppStream)->EmuFlags, 0,
xbnullptr, (*ppStream)->X_BufferCacheSize, (*ppStream)->Xb_VoiceProperties, pdssd->lpMixBinsOutput);
// Test case: Star Wars: KotOR has one packet greater than 5 seconds worth. Increasing to 10 seconds works out fine, can increase more if need to.
// Allocate at least 10 second worth of bytes in PCM format.
DSBufferDesc.dwBufferBytes = DSBufferDesc.lpwfxFormat->nAvgBytesPerSec * 10;
// Test case: Star Wars: KotOR has one packet greater than 5 seconds worth. Increasing to 10 seconds allow stream to work until
// another test case below proven host's buffer size does not matter since packet's size can be greater than host's buffer size.
// Test case: GTA 3 / Vice City, and some other titles has packet's buffer size are bigger than 10 seconds worth of buffer size.
// Allocate at least 5 second worth of bytes in PCM format to allow partial upload packet's buffer.
DSBufferDesc.dwBufferBytes = DSBufferDesc.lpwfxFormat->nAvgBytesPerSec * 5;
(*ppStream)->EmuBufferDesc = DSBufferDesc;
(*ppStream)->Host_dwTriggerRange = (DSBufferDesc.lpwfxFormat->nSamplesPerSec / DSBufferDesc.lpwfxFormat->wBitsPerSample);
(*ppStream)->X_MaxAttachedPackets = pdssd->dwMaxAttachedPackets;
(*ppStream)->Host_BufferPacketArray.reserve(pdssd->dwMaxAttachedPackets);
(*ppStream)->Host_dwWriteOffsetNext = 0;
(*ppStream)->Host_dwLastWritePos = 0;
(*ppStream)->Host_isProcessing = false;
(*ppStream)->Xb_lpfnCallback = pdssd->lpfnCallback;
(*ppStream)->Xb_lpvContext = pdssd->lpvContext;
@ -613,23 +614,33 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Process)
// Add packets from title until it gets full.
if (pThis->Host_BufferPacketArray.size() != pThis->X_MaxAttachedPackets) {
host_voice_packet packet_input;
packet_input.pBuffer_data = nullptr;
packet_input.avgBytesPerSec = 0;
packet_input.xmp_data = *pInputBuffer;
packet_input.xmp_data.dwMaxSize = DSoundBufferGetPCMBufferSize(pThis->EmuFlags, pInputBuffer->dwMaxSize);
if (packet_input.xmp_data.dwMaxSize == 0) {
packet_input.pBuffer_data = nullptr;
} else {
if (packet_input.xmp_data.dwMaxSize != 0) {
packet_input.pBuffer_data = malloc(packet_input.xmp_data.dwMaxSize);
if (packet_input.xmp_data.dwMaxSize > pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec) {
packet_input.avgBytesPerSec = pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec;
}
else {
packet_input.avgBytesPerSec = packet_input.xmp_data.dwMaxSize;
}
DSoundSGEMemAlloc(packet_input.xmp_data.dwMaxSize);
}
packet_input.rangeStart = pThis->Host_dwWriteOffsetNext;
packet_input.nextWriteOffset = pThis->Host_dwWriteOffsetNext;
packet_input.lastWritePos = packet_input.nextWriteOffset;
pThis->Host_dwWriteOffsetNext += packet_input.xmp_data.dwMaxSize;
if (pThis->EmuBufferDesc.dwBufferBytes <= pThis->Host_dwWriteOffsetNext) {
// Packet size may be larger than host's pre-allocated buffer size, loop is a requirement until within range is known.
while (pThis->EmuBufferDesc.dwBufferBytes <= pThis->Host_dwWriteOffsetNext) {
pThis->Host_dwWriteOffsetNext -= pThis->EmuBufferDesc.dwBufferBytes;
}
packet_input.isWritten = false;
packet_input.bufWrittenIndex = 0;
packet_input.bufPlayed = 0;
packet_input.isPlayed = false;
DSoundBufferOutputXBtoHost(pThis->EmuFlags, pThis->EmuBufferDesc, pInputBuffer->pvBuffer, pInputBuffer->dwMaxSize, packet_input.pBuffer_data, packet_input.xmp_data.dwMaxSize);
pThis->Host_BufferPacketArray.push_back(packet_input);
if (pInputBuffer->pdwStatus != xbnullptr) {
@ -639,7 +650,7 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Process)
(*pInputBuffer->pdwCompletedSize) = 0;
}
if (pThis->Host_isProcessing == false && pThis->Host_BufferPacketArray.size() == 1) {
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(packet_input.rangeStart);
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(packet_input.nextWriteOffset);
}
// Once full it needs to change status to flushed when cannot hold any more packets.
} else {
@ -906,6 +917,9 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_SetFormat)
0, pThis->X_BufferCache, pThis->X_BufferCacheSize,
pThis->Xb_VoiceProperties, xbnullptr, pThis->Xb_Frequency);
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(0);
pThis->Host_dwLastWritePos = 0;
return hRet;
}