fix audio stream's packet larger than host buffer size
This commit is contained in:
parent
08691c24e1
commit
54421832e5
|
@ -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;
|
||||
};
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue