From 6aae3548e64221b189cb650001edafa3f48d7a6b Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Sat, 18 Jan 2020 20:04:47 -0600 Subject: [PATCH] improve stream's packets process --- .../DirectSound/DSStream_PacketManager.cpp | 202 ++++++++++-------- .../hle/DSOUND/DirectSound/DirectSound.hpp | 3 +- .../DSOUND/DirectSound/DirectSoundStream.cpp | 9 +- 3 files changed, 117 insertions(+), 97 deletions(-) diff --git a/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp b/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp index 5a70a598c..7728fbe5c 100644 --- a/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp +++ b/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp @@ -114,45 +114,42 @@ void DSStream_Packet_Clear( static inline void DSStream_Packet_UploadPartial( XTL::X_CDirectSoundStream* pThis, - vector_hvp_iterator &bufferCurrent + vector_hvp_iterator &packetCurrent ) { // 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); + if (packetCurrent->bufWrittenBytes == packetCurrent->xmp_data.dwMaxSize) { return; } + LPWAVEFORMATEX pwfxFormat = pThis->EmuBufferDesc.lpwfxFormat; - uint32_t avgBytesPerSec = bufferCurrent->avgBytesPerSec; - uint32_t bufferOffset = avgBytesPerSec * bufferCurrent->bufWrittenIndex; + // Write one avg second worth of buffer ahead into host's audio buffer. + if (packetCurrent->bufWrittenBytes <= (packetCurrent->bufPlayed + pwfxFormat->nAvgBytesPerSec)) { + uint32_t bufWriteLength = pwfxFormat->nAvgBytesPerSec; + uint32_t bufferOffset = packetCurrent->bufWrittenBytes; - if ((bufferOffset + avgBytesPerSec) > bufferCurrent->xmp_data.dwMaxSize) { - avgBytesPerSec = bufferCurrent->xmp_data.dwMaxSize - bufferOffset; - } + if ((bufferOffset + bufWriteLength) > packetCurrent->xmp_data.dwMaxSize) { + bufWriteLength = packetCurrent->xmp_data.dwMaxSize - bufferOffset; + } + packetCurrent->bufWrittenBytes += bufWriteLength; - DSStream_Packet_UpdateHostBuffer(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; - } + DSStream_Packet_UpdateHostBuffer(pThis->EmuDirectSoundBuffer8, packetCurrent->nextWriteOffset, (uint8_t*)packetCurrent->pBuffer_data + bufferOffset, bufWriteLength); + packetCurrent->nextWriteOffset += bufWriteLength; + if (pThis->EmuBufferDesc.dwBufferBytes < packetCurrent->nextWriteOffset) { + packetCurrent->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 + // Debug area begin + EmuLog(LOG_LEVEL::DEBUG, "upload packet buffer process | pThis = %08X | packetCurrent = %08X", + pThis, packetCurrent._Ptr); + EmuLog(LOG_LEVEL::DEBUG, "nextWriteOffset = %08X | bufPlayed = %08X | bufWrittenIndex = %08X", + packetCurrent->nextWriteOffset, packetCurrent->bufPlayed, packetCurrent->bufWrittenIndex); + EmuLog(LOG_LEVEL::DEBUG, "bufferSize - %08X | dwBufferBytes = %08X | dwLastWritePos = %08X | dwWriteOffsetNext = %08X\n", + packetCurrent->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 DSStream_Packet_Starved( @@ -161,6 +158,32 @@ static inline void DSStream_Packet_Starved( { pThis->Host_isProcessing = false; pThis->EmuDirectSoundBuffer8->Stop(); + // We need to log this info since some titles may have stop/played audio often in middle of processing (relative to videos). + EmuLog(LOG_LEVEL::INFO, "Starved: pThis = %08X;", + pThis + ); +} + +// Prefill buffer with at least 1 second worth of buffer. See "nAvgBytesPerSec" below for inspection. +static void DSStream_Packet_Prefill( + XTL::X_CDirectSoundStream* pThis, + vector_hvp_iterator packetCurrent + ) +{ + unsigned int streamBufferSize = 0; + + while (packetCurrent != pThis->Host_BufferPacketArray.end()) { + // If buffer had been written, then ignore it. + if (packetCurrent->bufWrittenBytes == 0) { + DSStream_Packet_UploadPartial(pThis, packetCurrent); + } + // Get remaining size left in current packet. + streamBufferSize += (packetCurrent->xmp_data.dwMaxSize - packetCurrent->bufPlayed); + if (streamBufferSize > pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec) { + break; + } + packetCurrent++; + } } bool DSStream_Packet_Process( @@ -169,12 +192,12 @@ bool DSStream_Packet_Process( { // If title want to pause, then don't process the packets. - if ((pThis->EmuFlags & DSE_FLAG_PAUSE) > 0) { - return 0; - } - // If media object is being used as playback synch, then don't process the packets. - if ((pThis->EmuFlags & DSE_FLAG_SYNCHPLAYBACK_CONTROL) > 0) { + if ((pThis->EmuFlags & DSE_FLAG_PAUSE) > 0 || + (pThis->EmuFlags & DSE_FLAG_SYNCHPLAYBACK_CONTROL) > 0) { + + vector_hvp_iterator packetCurrent = pThis->Host_BufferPacketArray.begin(); + DSStream_Packet_Prefill(pThis, packetCurrent); return 0; } @@ -186,87 +209,92 @@ bool DSStream_Packet_Process( DWORD dwAudioBytes; HRESULT hRet = pThis->EmuDirectSoundBuffer8->GetStatus(&dwAudioBytes); if (hRet == DS_OK) { - vector_hvp_iterator bufferCurrent = pThis->Host_BufferPacketArray.begin(); - vector_hvp_iterator bufferPrev = bufferCurrent; - if (bufferCurrent->bufWrittenIndex == 0) { + vector_hvp_iterator packetCurrent = pThis->Host_BufferPacketArray.begin(); - DSStream_Packet_UploadPartial(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. - // Test case proof: Gauntlet Dark Legacy give 256 bytes of data to a per packet during intro FMV. - DWORD writePos = 0; - hRet = pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &writePos); - if (hRet == DS_OK) { - - int bufPlayed = writePos - bufferCurrent->lastWritePos; + // 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. + // Test case proof: Gauntlet Dark Legacy give 256 bytes of data to a per packet during intro FMV. + // TODO: Need a better solution to give actual playtime since beginning of audio do run ahead of time. + DWORD writePos = 0; + hRet = pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &writePos); + if (hRet == DS_OK) { + do { + int bufPlayed = writePos - packetCurrent->lastWritePos; // Correct it if buffer was playing and is at beginning. - if (writePos < bufferCurrent->lastWritePos) { - bufPlayed = writePos + (pThis->EmuBufferDesc.dwBufferBytes - bufferCurrent->lastWritePos); + if (writePos < packetCurrent->lastWritePos) { + bufPlayed = writePos + (pThis->EmuBufferDesc.dwBufferBytes - packetCurrent->lastWritePos); } - bufferCurrent->lastWritePos = writePos; - bufferCurrent->bufPlayed += bufPlayed; + packetCurrent->lastWritePos = writePos; + packetCurrent->bufPlayed += bufPlayed; - if (bufferCurrent->isPlayed == false) { - bufferCurrent->isPlayed = true; + if (packetCurrent->isPlayed == false) { + packetCurrent->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 + pThis, + bufPlayed, + pThis->EmuBufferDesc.dwBufferBytes, + packetCurrent->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 bufPlayed is over dwMaxSize, then re-correct it to return dwMaxSize only. + if (packetCurrent->bufPlayed >= packetCurrent->xmp_data.dwMaxSize) { + bufPlayed = packetCurrent->bufPlayed - packetCurrent->xmp_data.dwMaxSize; + packetCurrent->bufPlayed = packetCurrent->xmp_data.dwMaxSize; } - if (bufferCurrent->xmp_data.pdwCompletedSize != xbnullptr) { - (*bufferCurrent->xmp_data.pdwCompletedSize) = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, bufferCurrent->bufPlayed); + // Return current completed size to Xbox's application. + if (packetCurrent->xmp_data.pdwCompletedSize != xbnullptr) { + (*packetCurrent->xmp_data.pdwCompletedSize) = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, packetCurrent->bufPlayed); } - if (bufferCurrent->bufPlayed == bufferCurrent->xmp_data.dwMaxSize) { + // Once bufPlayed is equal to dwMaxSize, we know the packet is completed. + if (packetCurrent->bufPlayed == packetCurrent->xmp_data.dwMaxSize) { - DSStream_Packet_Clear(bufferCurrent, XMP_STATUS_SUCCESS, pThis->Xb_lpfnCallback, pThis->Xb_lpvContext, pThis); + DSStream_Packet_Clear(packetCurrent, XMP_STATUS_SUCCESS, pThis->Xb_lpfnCallback, pThis->Xb_lpvContext, pThis); if (pThis->Host_BufferPacketArray.size() == 0) { DSStream_Packet_Starved(pThis); return 0; } #if 0 // Extend debug verification - EmuLog(LOG_LEVEL::DEBUG, "nextBuffer: %08X; bufferCurrent->bufPlayed: %08X; bufPlayed: %08X;\n", - bufferCurrent._Ptr, - bufferCurrent->bufPlayed, + EmuLog(LOG_LEVEL::DEBUG, "nextBuffer: %08X; packetCurrent->bufPlayed: %08X; bufPlayed: %08X;\n", + packetCurrent._Ptr, + packetCurrent->bufPlayed, + bufPlayed + ); +#endif + // Is this important or already have it handled by using while statement? + if (packetCurrent->bufWrittenBytes == 0) { + EmuLog(LOG_LEVEL::INFO, "audio gap detected: %08X; packetCurrent->bufPlayed: %08X; bufPlayed: %08X;\n", + packetCurrent._Ptr, + packetCurrent->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) { - DSStream_Packet_UploadPartial(pThis, bufferCurrent); + ); } + // Since we have later packet ready to be process, let's process it in the loop. } - if (pThis->Host_BufferPacketArray.size() > 1) { - if ((bufferCurrent->xmp_data.dwMaxSize - bufferCurrent->bufPlayed) <= pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec - && (bufferCurrent + 1)->bufWrittenIndex == 0) { - bufferCurrent++; - DSStream_Packet_UploadPartial(pThis, bufferCurrent); - } + // Otherwise, continue upload partial of the packet's data to host if there are any left. + else { + DSStream_Packet_UploadPartial(pThis, packetCurrent); + break; // Leave loop since there is more buffer haven't been process in the current packet. } - } - // Out of packets, let's stop it. - if (pThis->Host_BufferPacketArray.size() == 0) { - DSStream_Packet_Starved(pThis); - return 0; - } + } while (true); + + DSStream_Packet_Prefill(pThis, packetCurrent); } + // Out of packets, let's stop stream's buffer. + if (pThis->Host_BufferPacketArray.size() == 0) { + DSStream_Packet_Starved(pThis); + return 0; + } + } + + if (pThis->Host_isProcessing == false) { + pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags); + pThis->Host_isProcessing = true; } return 1; } diff --git a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp index be5110bb7..907fec92e 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp @@ -171,8 +171,7 @@ struct host_voice_packet { DWORD nextWriteOffset; DWORD lastWritePos; DWORD bufPlayed; - DWORD bufWrittenIndex; - DWORD avgBytesPerSec; + DWORD bufWrittenBytes; bool isPlayed; }; diff --git a/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp b/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp index b51d79993..c8b0e66ff 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp @@ -616,17 +616,10 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Process) 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 = 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.nextWriteOffset = pThis->Host_dwWriteOffsetNext; @@ -636,7 +629,7 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Process) while (pThis->EmuBufferDesc.dwBufferBytes <= pThis->Host_dwWriteOffsetNext) { pThis->Host_dwWriteOffsetNext -= pThis->EmuBufferDesc.dwBufferBytes; } - packet_input.bufWrittenIndex = 0; + packet_input.bufWrittenBytes = 0; packet_input.bufPlayed = 0; packet_input.isPlayed = false;