diff --git a/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp b/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp index 92ba741b9..0c0cd65de 100644 --- a/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp +++ b/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.cpp @@ -152,12 +152,37 @@ static inline void DSStream_Packet_UploadPartial( } } -static inline void DSStream_Packet_Starved( +static inline void DSStream_Packet_Stop_Internal( XTL::X_CDirectSoundStream* pThis ) { pThis->Host_isProcessing = false; pThis->EmuDirectSoundBuffer8->Stop(); +} + +static inline void DSStream_Packet_Stop( + XTL::X_CDirectSoundStream* pThis + ) +{ + DSStream_Packet_Stop_Internal(pThis); + + if (pThis->Host_BufferPacketArray.size() == 0) { + if ((pThis->EmuFlags & DSE_FLAG_ENVELOPE2) > 0) { + pThis->Xb_Status |= X_DSSSTATUS_ENVELOPECOMPLETE; + } + } + // 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, "Stopped: pThis = %08X; Remaining packet(s): %d", + pThis, pThis->Host_BufferPacketArray.size() + ); +} + +static inline void DSStream_Packet_Starved( + XTL::X_CDirectSoundStream* pThis + ) +{ + DSStream_Packet_Stop_Internal(pThis); + pThis->Xb_Status |= X_DSSSTATUS_STARVED | X_DSSSTATUS_PAUSED; // 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 @@ -191,6 +216,11 @@ bool DSStream_Packet_Process( ) { + // Do not allow to process if there is no packets. + if (pThis->Host_BufferPacketArray.size() == 0) { + return 0; + } + // If title want to pause, then don't process the packets. // If media object is being used as playback synch, then don't process the packets. if ((pThis->EmuFlags & DSE_FLAG_PAUSE) > 0 || @@ -201,9 +231,14 @@ bool DSStream_Packet_Process( return 0; } - // Do not allow to process if there is no packets. - if (pThis->Host_BufferPacketArray.size() == 0) { - return 0; + if ((pThis->Xb_Status & X_DSSSTATUS_PAUSED) > 0) { + pThis->Xb_Status &= ~X_DSSSTATUS_PAUSED; + } + + if (pThis->Host_isProcessing == false) { + if (!(pThis->EmuFlags & DSE_FLAG_IS_ACTIVATED)) { + pThis->EmuFlags |= DSE_FLAG_IS_ACTIVATED; + } } DWORD dwAudioBytes; @@ -218,7 +253,8 @@ bool DSStream_Packet_Process( DWORD writePos = 0; hRet = pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &writePos); if (hRet == DS_OK) { - do { + // Disabled do/while loop since xbox thread will be processing as well. + //do { int bufPlayed = writePos - packetCurrent->lastWritePos; // Correct it if buffer was playing and is at beginning. @@ -253,10 +289,16 @@ bool DSStream_Packet_Process( // Once bufPlayed is equal to dwMaxSize, we know the packet is completed. if (packetCurrent->bufPlayed == packetCurrent->xmp_data.dwMaxSize) { + bool isStreamEnd = packetCurrent->isStreamEnd; DSStream_Packet_Clear(packetCurrent, XMP_STATUS_SUCCESS, pThis->Xb_lpfnCallback, pThis->Xb_lpvContext, pThis); if (pThis->Host_BufferPacketArray.size() == 0) { - DSStream_Packet_Starved(pThis); + if (isStreamEnd) { + DSStream_Packet_Stop(pThis); + } + else { + DSStream_Packet_Starved(pThis); + } return 0; } #if 0 // Extend debug verification @@ -279,9 +321,9 @@ bool DSStream_Packet_Process( // 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. + //break; // Leave loop since there is more buffer haven't been process in the current packet. } - } while (true); + //} while (true); DSStream_Packet_Prefill(pThis, packetCurrent); } @@ -292,17 +334,32 @@ bool DSStream_Packet_Process( } } + // The only place when Host_isProcessing is set to true to start play Host's audio buffer. if (pThis->Host_isProcessing == false) { pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags); pThis->Host_isProcessing = true; + // Add debug log for verify if Host_isProcessing has set to true. + EmuLog(LOG_LEVEL::DEBUG, "pStream = %08X:Host_isProcessing is set to true.", pThis); } return 1; } +void DSStream_Packet_FlushEx_Reset( + XTL::X_CDirectSoundStream* pThis + ) +{ + // Remove flags only (This is the only place it will remove other than FlushEx perform set/remove the flags.) + pThis->EmuFlags &= ~(DSE_FLAG_FLUSH_ASYNC | DSE_FLAG_ENVELOPE | DSE_FLAG_ENVELOPE2 | DSE_FLAG_PAUSE); + pThis->Xb_rtFlushEx = 0LL; + pThis->Xb_Status = 0; +} + bool DSStream_Packet_Flush( XTL::X_CDirectSoundStream* pThis ) { + DSStream_Packet_FlushEx_Reset(pThis); + // If host's audio is still playing then return busy-state until buffer has stop playing. DWORD dwStatus; pThis->EmuDirectSoundBuffer8->GetStatus(&dwStatus); diff --git a/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.hpp b/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.hpp index 9f8cfd7d7..821e218e1 100644 --- a/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.hpp +++ b/src/core/hle/DSOUND/DirectSound/DSStream_PacketManager.hpp @@ -39,4 +39,6 @@ extern void DSStream_Packet_Clear( extern bool DSStream_Packet_Process(XTL::X_CDirectSoundStream* pThis); +extern void DSStream_Packet_FlushEx_Reset(XTL::X_CDirectSoundStream* pThis); + extern bool DSStream_Packet_Flush(XTL::X_CDirectSoundStream* pThis); diff --git a/src/core/hle/DSOUND/DirectSound/DirectSound.cpp b/src/core/hle/DSOUND/DirectSound/DirectSound.cpp index d6acc3c99..8fb08fb1c 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSound.cpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSound.cpp @@ -390,15 +390,9 @@ static void dsound_thread_worker(LPVOID nullPtr) { DSoundMutexGuardLock; - vector_ds_stream::iterator ppDSStream = g_pDSoundStreamCache.begin(); - for (; ppDSStream != g_pDSoundStreamCache.end(); ppDSStream++) { - if ((*ppDSStream)->Host_BufferPacketArray.size() == 0) { - continue; - } - if (((*ppDSStream)->EmuFlags & DSE_FLAG_FLUSH_ASYNC) > 0 && (*ppDSStream)->Xb_rtFlushEx == 0LL) { - DSStream_Packet_Process((*ppDSStream)); - } - } + xboxkrnl::LARGE_INTEGER getTime; + xboxkrnl::KeQuerySystemTime(&getTime); + DirectSoundDoWork_Stream(getTime); } } } @@ -895,7 +889,7 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSound_SynchPlayback) if (((*ppDSBuffer)->EmuFlags & DSE_FLAG_SYNCHPLAYBACK_CONTROL) > 0) { DSoundBufferSynchPlaybackFlagRemove((*ppDSBuffer)->EmuFlags); - EmuLog(LOG_LEVEL::DEBUG, "SynchPlayback - EmuPlayFlags: %08X", (*ppDSBuffer)->EmuPlayFlags); + EmuLog(LOG_LEVEL::DEBUG, "SynchPlayback - pDSBuffer: %08X; EmuPlayFlags: %08X", *ppDSBuffer, (*ppDSBuffer)->EmuPlayFlags); (*ppDSBuffer)->EmuDirectSoundBuffer8->Play(0, 0, (*ppDSBuffer)->EmuPlayFlags); } } @@ -907,6 +901,7 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSound_SynchPlayback) } if (((*ppDSStream)->EmuFlags & DSE_FLAG_SYNCHPLAYBACK_CONTROL) > 0) { DSoundBufferSynchPlaybackFlagRemove((*ppDSStream)->EmuFlags); + EmuLog(LOG_LEVEL::DEBUG, "SynchPlayback - pDSStream: %08X; EmuPlayFlags: %08X", *ppDSStream, (*ppDSStream)->EmuPlayFlags); DSStream_Packet_Process((*ppDSStream)); } } diff --git a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp index 70b3bc298..85a7fca67 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp @@ -122,6 +122,7 @@ struct X_CDirectSoundBuffer #define DSE_FLAG_ENVELOPE (1 << 13) #define DSE_FLAG_ENVELOPE2 (1 << 14) // NOTE: This flag is a requirement for GetStatus to return X_DSSSTATUS_ENVELOPECOMPLETE value. #define DSE_FLAG_RECIEVEDATA (1 << 20) +#define DSE_FLAG_IS_ACTIVATED (1 << 21) // Only used for DirectSoundStream class, to acknowledge pause's no activate flag. #define DSE_FLAG_DEBUG_MUTE (1 << 30) // Cxbx-R debugging usage only #define DSE_FLAG_BUFFER_EXTERNAL (1 << 31) #define DSE_FLAG_AUDIO_CODECS (DSE_FLAG_PCM | DSE_FLAG_XADPCM | DSE_FLAG_PCM_UNKNOWN) @@ -174,6 +175,7 @@ struct host_voice_packet { DWORD bufPlayed; DWORD bufWrittenBytes; bool isPlayed; + bool isStreamEnd; }; // ****************************************************************** @@ -266,6 +268,7 @@ class X_CDirectSoundStream DWORD Xb_Frequency; DWORD Host_dwLastWritePos; DWORD Xb_Flags; + DWORD Xb_Status; }; // ****************************************************************** diff --git a/src/core/hle/DSOUND/DirectSound/DirectSoundBuffer.cpp b/src/core/hle/DSOUND/DirectSound/DirectSoundBuffer.cpp index 12cfd4d98..94d49add4 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSoundBuffer.cpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSoundBuffer.cpp @@ -81,7 +81,7 @@ void DirectSoundDoWork_Buffer(xboxkrnl::LARGE_INTEGER &time) // TODO: Do we need this in async thread loop? if (pThis->Xb_rtPauseEx != 0LL && pThis->Xb_rtPauseEx <= time.QuadPart) { pThis->Xb_rtPauseEx = 0LL; - pThis->EmuFlags ^= DSE_FLAG_PAUSE; + pThis->EmuFlags &= ~DSE_FLAG_PAUSE; pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags); } diff --git a/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp b/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp index 6b999300f..d5d60aee6 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSoundStream.cpp @@ -102,17 +102,17 @@ void DirectSoundDoWork_Stream(xboxkrnl::LARGE_INTEGER& time) // TODO: Do we need this in async thread loop? if (pThis->Xb_rtPauseEx != 0LL && pThis->Xb_rtPauseEx <= time.QuadPart) { pThis->Xb_rtPauseEx = 0LL; - pThis->EmuFlags ^= DSE_FLAG_PAUSE; + pThis->EmuFlags &= ~DSE_FLAG_PAUSE; // Don't call play here, let DSStream_Packet_Process deal with it. } - if ((pThis->EmuFlags & DSE_FLAG_FLUSH_ASYNC) == 0) { - DSStream_Packet_Process(pThis); - } else { - // Confirmed flush packet must be done in DirectSoundDoWork only when title is ready. - if (pThis->Xb_rtFlushEx != 0LL && pThis->Xb_rtFlushEx <= time.QuadPart) { - pThis->Xb_rtFlushEx = 0LL; - DSStream_Packet_Flush(pThis); + // If has flush async requested then verify time has expired to perform flush process. + if ((pThis->EmuFlags & DSE_FLAG_FLUSH_ASYNC) > 0 && pThis->Xb_rtFlushEx <= time.QuadPart) { + if (pThis->Xb_rtFlushEx == 0LL) { + EmuLog(LOG_LEVEL::WARNING, "Attempted to flush without Xb_rtFlushEx set to non-zero"); } + while(DSStream_Packet_Flush(pThis)); + } else { + DSStream_Packet_Process(pThis); } } } @@ -251,6 +251,7 @@ HRESULT WINAPI XTL::EMUPATCH(DirectSoundCreateStream) (*ppStream)->Host_isProcessing = false; (*ppStream)->Xb_lpfnCallback = pdssd->lpfnCallback; (*ppStream)->Xb_lpvContext = pdssd->lpvContext; + (*ppStream)->Xb_Status = 0; //TODO: Implement mixbin variable support. Or just merge pdssd struct into DS Stream class. EmuLog(LOG_LEVEL::DEBUG, "DirectSoundCreateStream, *ppStream := 0x%.08X", *ppStream); @@ -337,11 +338,9 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Discontinuity) // default ret = DSERR_GENERIC - // Perform check if has pending data. if so, clear pending packets. - if (pThis->Host_BufferPacketArray.size() > 1) { - for (auto buffer = pThis->Host_BufferPacketArray.begin() + 1; buffer != pThis->Host_BufferPacketArray.end();) { - DSStream_Packet_Clear(buffer, XMP_STATUS_FLUSHED, pThis->Xb_lpfnCallback, pThis->Xb_lpvContext, pThis); - } + // Perform check if packets exist, then mark the last submited packet as end of stream. + if (!pThis->Host_BufferPacketArray.empty()) { + pThis->Host_BufferPacketArray.back().isStreamEnd = true; } return DS_OK; @@ -360,10 +359,6 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Flush) DSoundBufferSynchPlaybackFlagRemove(pThis->EmuFlags); - // Remove flags only (This is the only place it will remove other than FlushEx perform set/remove the flags.) - pThis->EmuFlags &= ~(DSE_FLAG_FLUSH_ASYNC | DSE_FLAG_ENVELOPE | DSE_FLAG_ENVELOPE2); - pThis->Xb_rtFlushEx = 0LL; - while (DSStream_Packet_Flush(pThis)); return DS_OK; @@ -387,6 +382,8 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_FlushEx) LOG_FUNC_END; HRESULT hRet = DSERR_INVALIDPARAM; + // Reset flags here to reprocess dwFlags request. + DSStream_Packet_FlushEx_Reset(pThis); // Cannot use rtTimeStamp here, it must be flush. if (dwFlags == X_DSSFLUSHEX_IMMEDIATE) { @@ -464,32 +461,14 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_GetStatus) LOG_FUNC_ARG_OUT(pdwStatus) LOG_FUNC_END; - DWORD dwStatusXbox = 0, dwStatusHost; + DWORD dwStatusXbox = pThis->Xb_Status, dwStatusHost; HRESULT hRet = pThis->EmuDirectSoundBuffer8->GetStatus(&dwStatusHost); // Convert host to xbox status flag. if (hRet == DS_OK) { - DWORD testSize = pThis->Host_BufferPacketArray.size(); - if ((dwStatusHost & DSBSTATUS_PLAYING) > 0) { + if (pThis->Host_isProcessing && !(dwStatusXbox & X_DSSSTATUS_PAUSED)) { dwStatusXbox |= X_DSSSTATUS_PLAYING; - } else { - - if ((pThis->EmuFlags & DSE_FLAG_PAUSE) > 0) { - dwStatusXbox |= X_DSSSTATUS_PAUSED; - - // Set to paused when has packet(s) queued and is not processing. - } else if (pThis->Host_BufferPacketArray.size() != 0 && pThis->Host_isProcessing == false) { - dwStatusXbox |= X_DSSSTATUS_PAUSED; - } - - if (pThis->Host_BufferPacketArray.size() == 0) { - dwStatusXbox |= X_DSSSTATUS_STARVED; - - if ((pThis->EmuFlags & DSE_FLAG_ENVELOPE2) > 0) { - dwStatusXbox |= X_DSSSTATUS_ENVELOPECOMPLETE; - } - } } if (pThis->Host_BufferPacketArray.size() != pThis->X_MaxAttachedPackets) { dwStatusXbox |= X_DSSSTATUS_READY; @@ -554,6 +533,24 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Pause) HRESULT hRet = HybridDirectSoundBuffer_Pause(pThis->EmuDirectSoundBuffer8, dwPause, pThis->EmuFlags, pThis->EmuPlayFlags, pThis->Host_isProcessing, 0LL, pThis->Xb_rtPauseEx); + if (dwPause == X_DSSPAUSE_PAUSENOACTIVATE) { + if (pThis->Host_BufferPacketArray.size() == 0 && !(pThis->EmuFlags & DSE_FLAG_IS_ACTIVATED)) { + pThis->EmuFlags |= DSE_FLAG_PAUSE; + } + } + + if ((pThis->EmuFlags & DSE_FLAG_PAUSE) > 0) { + pThis->Host_isProcessing = false; + if ((pThis->EmuFlags & DSE_FLAG_IS_ACTIVATED) > 0) { + if (pThis->Host_BufferPacketArray.size() != 0) { + pThis->Xb_Status |= X_DSSSTATUS_PAUSED; + } + } + } + else if (!pThis->Host_isProcessing) { + DSStream_Packet_Process(pThis); + } + return hRet; } @@ -628,6 +625,7 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Process) packet_input.bufWrittenBytes = 0; packet_input.bufPlayed = 0; packet_input.isPlayed = false; + packet_input.isStreamEnd = false; DSoundBufferOutputXBtoHost(pThis->EmuFlags, pThis->EmuBufferDesc, pInputBuffer->pvBuffer, pInputBuffer->dwMaxSize, packet_input.pBuffer_data, packet_input.xmp_data.dwMaxSize); @@ -642,10 +640,18 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_Process) if (pThis->Host_isProcessing == false && pThis->Host_BufferPacketArray.size() == 1) { pThis->EmuDirectSoundBuffer8->SetCurrentPosition(packet_input.nextWriteOffset); } + + if ((pThis->Xb_Status & X_DSSSTATUS_STARVED) > 0) { + pThis->Xb_Status &= ~X_DSSSTATUS_STARVED; + } + if ((pThis->EmuFlags & DSE_FLAG_IS_ACTIVATED) > 0 && (pThis->EmuFlags & DSE_FLAG_PAUSE) > 0) { + pThis->Xb_Status |= X_DSSSTATUS_PAUSED; + } + DSStream_Packet_Process(pThis); // Once full it needs to change status to flushed when cannot hold any more packets. } else { if (pInputBuffer->pdwStatus != xbnullptr) { - (*pInputBuffer->pdwStatus) = XMP_STATUS_FLUSHED; + (*pInputBuffer->pdwStatus) = XMP_STATUS_FAILURE; } } }