Merge pull request #2326 from NZJenkins/exp/dsound-buffer-streaming

DirectSoundBuffer streaming
This commit is contained in:
ergo720 2022-03-29 14:03:54 +02:00 committed by GitHub
commit dc1f93b120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 302 additions and 111 deletions

View File

@ -25,6 +25,7 @@ void ImGuiAudio::DrawMenu()
{
if (ImGui::BeginMenu("Audio")) {
ImGui::MenuItem("Debug General Cache Stats", NULL, &m_windows.cache_stats_general);
ImGui::MenuItem("SoundBuffer Visualization", NULL, &m_windows.cache_visualization);
ImGui::EndMenu();
}
}
@ -32,6 +33,9 @@ void ImGuiAudio::DrawMenu()
void ImGuiAudio::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
{
//TODO: In need of make interface class to return generic info in some way.
extern void DSound_PrintStats(bool, ImGuiWindowFlags, bool m_show_audio_stats);
extern void DSound_PrintStats(bool, ImGuiWindowFlags, bool m_show_audio_stats);
DSound_PrintStats(is_focus, input_handler, m_windows.cache_stats_general);
extern void DSound_DrawBufferVisualization(bool, bool *p_show, ImGuiWindowFlags);
DSound_DrawBufferVisualization(is_focus, &m_windows.cache_visualization, input_handler);
}

View File

@ -44,6 +44,7 @@ typedef struct {
typedef struct {
bool cache_stats_general;
bool cache_visualization;
bool Reserved[3];
} imgui_audio_windows;

View File

@ -365,22 +365,138 @@ xbox::void_xt WINAPI xbox::EMUPATCH(DirectSoundDoWork)()
return;
}
// For Async process purpose only
void StreamBufferAudio(xbox::XbHybridDSBuffer* pHybridBuffer, float msToCopy) {
auto pThis = pHybridBuffer->emuDSBuffer;
auto dsb = pThis->EmuDirectSoundBuffer8;
bool isAdpcm = pThis->EmuFlags & DSE_FLAG_XADPCM;
DWORD xBufferRangeStart;
DWORD xBufferRangeSize;
DSoundBufferRegionCurrentLocation(pHybridBuffer, pThis->EmuPlayFlags, xBufferRangeStart, xBufferRangeSize);
DWORD hostBufferSize = pThis->EmuBufferDesc.dwBufferBytes;
DWORD playCursor;
DWORD writeCursor;
dsb->GetCurrentPosition(&playCursor, &writeCursor);
DWORD cursorGap = writeCursor >= playCursor
? (writeCursor - playCursor)
: hostBufferSize - playCursor + writeCursor;
// Determine where to copy data from.
// Note: The DirectSound write cursor can sit quite far ahead of the play cursor,
// but copying closer to the play cursor can introduce weird looping or
// latency issues
// Test case: NBA Live 2005 (writes to a very small buffer expecting low latency, can crackle at > 1ms stream interval)
// Test case: Halo (intro video delay when writing from play cursor)
DWORD writeOffset = writeCursor + cursorGap * g_dsBufferStreaming.tweakCopyOffset;
DWORD writeSize = std::min(
(DWORD)(pThis->EmuBufferDesc.lpwfxFormat->nAvgBytesPerSec * msToCopy / 1000),
hostBufferSize
);
DWORD blockSize = isAdpcm
? XBOX_ADPCM_DSTSIZE * pThis->EmuBufferDesc.lpwfxFormat->nChannels
: pThis->EmuBufferDesc.lpwfxFormat->nBlockAlign;
// ADPCM block alignment
writeOffset = ((writeOffset + blockSize / 2) / blockSize) * blockSize;
writeSize = ((writeSize + blockSize / 2) / blockSize) * blockSize;
writeOffset %= hostBufferSize;
DWORD xWriteOffset = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, writeOffset);
assert(xBufferRangeStart + xBufferRangeSize > xWriteOffset);
if (isAdpcm) {
assert(writeOffset % XBOX_ADPCM_DSTSIZE == 0);
assert(xWriteOffset % XBOX_ADPCM_SRCSIZE == 0);
}
LPVOID lplpvAudioPtr1, lplpvAudioPtr2;
DWORD lplpvAudioBytes1, lplpvAudioBytes2;
HRESULT hRet = pThis->EmuDirectSoundBuffer8->Lock(writeOffset, writeSize,
&lplpvAudioPtr1, &lplpvAudioBytes1,
&lplpvAudioPtr2, &lplpvAudioBytes2,
0);
if (hRet != 0) {
CxbxrKrnlAbort("DirectSoundBuffer Lock Failed!");
}
if (lplpvAudioPtr1 && pThis->X_BufferCache != nullptr) {
DSoundBufferOutputXBtoHost(
pThis->EmuFlags,
pThis->EmuBufferDesc,
((PBYTE)pThis->X_BufferCache + xBufferRangeStart + xWriteOffset),
DSoundBufferGetXboxBufferSize(pThis->EmuFlags, lplpvAudioBytes1),
lplpvAudioPtr1,
lplpvAudioBytes1
);
if (lplpvAudioPtr2) {
DSoundBufferOutputXBtoHost(
pThis->EmuFlags,
pThis->EmuBufferDesc,
((PBYTE)pThis->X_BufferCache + xBufferRangeStart + 0),
DSoundBufferGetXboxBufferSize(pThis->EmuFlags, lplpvAudioBytes2),
lplpvAudioPtr2,
lplpvAudioBytes2
);
}
HRESULT hRet = dsb->Unlock(lplpvAudioPtr1, lplpvAudioBytes1, lplpvAudioPtr2, lplpvAudioBytes2);
if (hRet != DS_OK) {
CxbxrKrnlAbort("DirectSoundBuffer Unlock Failed!");
}
}
}
static void dsound_thread_worker(LPVOID nullPtr)
{
g_AffinityPolicy->SetAffinityOther();
const int dsStreamInterval = 300;
int waitCounter = 0;
while (true) {
// FIXME time this loop more accurately
// and account for variation in the length of Sleep calls
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
Sleep(300);
Sleep(g_dsBufferStreaming.streamInterval);
waitCounter += g_dsBufferStreaming.streamInterval;
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
{
DSoundMutexGuardLock;
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
DirectSoundDoWork_Stream(getTime);
if (waitCounter > dsStreamInterval) {
waitCounter = 0;
// For Async process purpose only
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
DirectSoundDoWork_Stream(getTime);
}
// Stream sound buffer audio
// because the title may change the content of sound buffers at any time
for (auto& pBuffer : g_pDSoundBufferCache) {
// Avoid expensive calls to DirectSound on buffers unless they've been played at least once
// Since some titles create a large amount of buffers, but only use a few
if (pBuffer->emuDSBuffer->EmuStreamingInfo.playRequested) {
DWORD status;
HRESULT hRet = pBuffer->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&status);
if (hRet == 0 && status & DSBSTATUS_PLAYING) {
auto streamMs = g_dsBufferStreaming.streamInterval + g_dsBufferStreaming.streamAhead;
StreamBufferAudio(pBuffer, streamMs);
}
}
}
}
}
}
@ -945,6 +1061,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(CDirectSound_SynchPlayback)
DSoundBufferSynchPlaybackFlagRemove(pDSBuffer->EmuFlags);
EmuLog(LOG_LEVEL::DEBUG, "SynchPlayback - pDSBuffer: %08X; EmuPlayFlags: %08X", *ppDSBuffer, pDSBuffer->EmuPlayFlags);
pDSBuffer->EmuDirectSoundBuffer8->Play(0, 0, pDSBuffer->EmuPlayFlags);
pDSBuffer->EmuStreamingInfo.playRequested = true;
}
}

View File

@ -110,6 +110,10 @@ struct EmuDirectSoundBuffer
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
X_DSVOICEPROPS Xb_VoiceProperties;
DWORD Xb_Flags;
struct {
// True if the buffer has been played, and should be considered for streaming
bool playRequested = false;
} EmuStreamingInfo;
};
struct XbHybridDSBuffer : DSBUFFER_S::DSBUFFER_I {

View File

@ -80,6 +80,7 @@ void DirectSoundDoWork_Buffer(xbox::LARGE_INTEGER &time)
pThis->Xb_rtPauseEx = 0LL;
pThis->EmuFlags &= ~DSE_FLAG_PAUSE;
pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
pThis->EmuStreamingInfo.playRequested = true;
}
if (pThis->Xb_rtStopEx != 0LL && pThis->Xb_rtStopEx <= time.QuadPart) {
@ -437,55 +438,26 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Lock)
LOG_FUNC_END;
EmuDirectSoundBuffer* pThis = pHybridThis->emuDSBuffer;
HRESULT hRet = D3D_OK;
DWORD pcmSize = DSoundBufferGetPCMBufferSize(pThis->EmuFlags, dwBytes);
DWORD pcmOffset = DSoundBufferGetPCMBufferSize(pThis->EmuFlags, dwOffset);
DSoundGenericUnlock(pThis->EmuFlags,
pThis->EmuDirectSoundBuffer8,
pThis->EmuBufferDesc,
pThis->Host_lock,
pThis->X_BufferCache,
pThis->X_lock.dwLockOffset,
pThis->X_lock.dwLockBytes1,
pThis->X_lock.dwLockBytes2);
// Xbox directsound doesn't require locking buffers
// This Xbox api only exists to match PC
if (ppvAudioPtr2 == xbox::zeroptr) {
hRet = pThis->EmuDirectSoundBuffer8->Lock(pcmOffset, pcmSize, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
nullptr, 0, dwFlags);
pThis->Host_lock.pLockPtr2 = nullptr;
} else {
hRet = pThis->EmuDirectSoundBuffer8->Lock(pcmOffset, pcmSize, &pThis->Host_lock.pLockPtr1, &pThis->Host_lock.dwLockBytes1,
&pThis->Host_lock.pLockPtr2, &pThis->Host_lock.dwLockBytes2, dwFlags);
}
if (hRet != DS_OK) {
CxbxrKrnlAbort("IDirectSoundBuffer_Lock Failed!");
}
// Host lock position
pThis->Host_lock.dwLockOffset = pcmOffset;
pThis->Host_lock.dwLockFlags = dwFlags;
pThis->X_lock.dwLockFlags = dwFlags;
// Emulate to xbox's lock position
pThis->X_lock.dwLockOffset = dwOffset;
*ppvAudioPtr1 = pThis->X_lock.pLockPtr1 = ((LPBYTE)pThis->X_BufferCache + dwOffset);
*pdwAudioBytes1 = pThis->X_lock.dwLockBytes1 = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, pThis->Host_lock.dwLockBytes1);
if (pThis->Host_lock.pLockPtr2 != nullptr) {
*ppvAudioPtr2 = pThis->X_lock.pLockPtr2 = pThis->X_BufferCache;
*pdwAudioBytes2 = pThis->X_lock.dwLockBytes2 = DSoundBufferGetXboxBufferSize(pThis->EmuFlags, pThis->Host_lock.dwLockBytes2);
} else {
// If secondary pointers are not used, then set them as zero.
// There are applications bug didn't check for audio pointer that is null pointer which should not use invalid audio bytes.
// Since internal functions do set them zero. We'll set them here as well.
if (ppvAudioPtr2 != xbox::zeroptr) {
*ppvAudioPtr2 = xbox::zeroptr;
}
if (pdwAudioBytes2 != xbox::zeroptr) {
*pdwAudioBytes2 = 0;
}
}
if (dwOffset + dwBytes <= pThis->X_BufferCacheSize) {
*pdwAudioBytes1 = dwBytes;
*ppvAudioPtr1 = (PBYTE)pThis->X_BufferCache + dwOffset;
if (ppvAudioPtr2 != nullptr) {
*ppvAudioPtr2 = nullptr;
*pdwAudioBytes2 = 0;
}
}
else {
*pdwAudioBytes1 = pThis->X_BufferCacheSize - dwOffset;
*ppvAudioPtr1 = (PBYTE)pThis->X_BufferCache + dwOffset;
if (ppvAudioPtr2 != nullptr) {
*pdwAudioBytes2 = dwBytes - *pdwAudioBytes1;
*ppvAudioPtr2 = (PBYTE)pThis->X_BufferCache;
}
}
LOG_FUNC_BEGIN_ARG_RESULT
LOG_FUNC_ARG_RESULT(ppvAudioPtr1)
@ -494,7 +466,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Lock)
LOG_FUNC_ARG_RESULT(pdwAudioBytes2)
LOG_FUNC_END_ARG_RESULT;
RETURN_RESULT_CHECK(hRet);
RETURN(DS_OK);
}
// ******************************************************************
@ -509,7 +481,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Unlock)
dword_xt pdwAudioBytes2
)
{
DSoundMutexGuardLock;
// DSoundMutexGuardLock;
LOG_FUNC_BEGIN
LOG_FUNC_ARG(pHybridThis)
@ -519,28 +491,8 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Unlock)
LOG_FUNC_ARG(pdwAudioBytes2)
LOG_FUNC_END;
EmuDirectSoundBuffer* pThis = pHybridThis->emuDSBuffer;
// TODO: Find out why pThis->EmuLockPtr1 is nullptr... (workaround atm is to check if it is not a nullptr.)
if (pThis->X_BufferCache != xbox::zeroptr && pThis->Host_lock.pLockPtr1 != nullptr) {
memcpy_s((PBYTE)pThis->X_BufferCache + pThis->X_lock.dwLockOffset,
pThis->X_BufferCacheSize - pThis->X_lock.dwLockOffset,
pThis->X_lock.pLockPtr1,
pThis->X_lock.dwLockBytes1);
if (pThis->Host_lock.pLockPtr2 != nullptr) {
memcpy_s(pThis->X_BufferCache, pThis->X_BufferCacheSize, pThis->X_lock.pLockPtr2, pThis->X_lock.dwLockBytes2);
}
}
DSoundGenericUnlock(pThis->EmuFlags,
pThis->EmuDirectSoundBuffer8,
pThis->EmuBufferDesc,
pThis->Host_lock,
pThis->X_BufferCache,
pThis->X_lock.dwLockOffset,
pThis->X_lock.dwLockBytes1,
pThis->X_lock.dwLockBytes2);
// Xbox directsound doesn't require locking buffers
// This Xbox api only exists to match PC
return DS_OK;
}
@ -660,6 +612,7 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_Play)
}
if ((pThis->EmuFlags & DSE_FLAG_SYNCHPLAYBACK_CONTROL) == 0) {
hRet = pThis->EmuDirectSoundBuffer8->Play(0, 0, pThis->EmuPlayFlags);
pThis->EmuStreamingInfo.playRequested = true;
}
}
@ -1594,52 +1547,54 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_StopEx)
hRet = pThis->EmuDirectSoundBuffer8->Stop();
pThis->Xb_rtStopEx = 0LL;
}
else {
bool isLooping;
if ((pThis->EmuPlayFlags & X_DSBPLAY_LOOPING) > 0) {
isLooping = true;
}
else {
isLooping = false;
}
else if(dwFlags & X_DSBSTOPEX_ENVELOPE) {
bool isLooping = pThis->EmuPlayFlags & X_DSBPLAY_LOOPING;
if ((dwFlags & X_DSBSTOPEX_ENVELOPE) > 0) {
if (rtTimeStamp == 0LL) {
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
pThis->Xb_rtStopEx = getTime.QuadPart;
}
else {
pThis->Xb_rtStopEx = rtTimeStamp;
}
pThis->Xb_rtStopEx += (pThis->Xb_EnvolopeDesc.dwRelease * 512) / 48000;
double releaseSamples = pThis->Xb_EnvolopeDesc.dwRelease * 512.0;
if (rtTimeStamp == 0LL) {
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
pThis->Xb_rtStopEx = getTime.QuadPart;
}
else {
pThis->Xb_rtStopEx = rtTimeStamp;
}
const double samplesToTicks = 10000000 / 48000.0;
xbox::REFERENCE_TIME releaseTicks = static_cast<xbox::REFERENCE_TIME>(releaseSamples * samplesToTicks);
pThis->Xb_rtStopEx += releaseTicks;
if ((dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) > 0) {
// Release from loop region.
pThis->EmuPlayFlags &= ~X_DSBPLAY_LOOPING;
}
DWORD dwValue, dwStatus;
DWORD currentPos, dwStatus;
pThis->EmuDirectSoundBuffer8->GetStatus(&dwStatus);
if (pThis->EmuBufferToggle != X_DSB_TOGGLE_DEFAULT) {
pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &dwValue);
pThis->EmuDirectSoundBuffer8->GetCurrentPosition(nullptr, &currentPos);
hRet = pThis->EmuDirectSoundBuffer8->Stop();
DSoundBufferResizeUpdate(pHybridThis, pThis->EmuPlayFlags, hRet, 0, pThis->X_BufferCacheSize);
// Determine the range of bytes we need to play
// Test case: Outrun 2006 - converting large buffers tanks the FPS
// Is set within DSoundBufferRegionCurrentLocation function
DWORD bufferRangeStart;
DWORD bufferRangeSize;
DSoundBufferRegionCurrentLocation(pHybridThis, pThis->EmuPlayFlags, bufferRangeStart, bufferRangeSize);
dwValue += pThis->EmuRegionPlayStartOffset;
if (isLooping) {
dwValue += pThis->EmuRegionLoopStartOffset;
if (pThis->EmuBufferToggle == X_DSB_TOGGLE_LOOP) {
// if we are to release from loop region, then we need change the size to end of actual buffer cache.
if (dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) {
bufferRangeSize = pThis->X_BufferCacheSize - bufferRangeStart;
}
}
DSoundBufferResizeUpdate(pHybridThis, pThis->EmuPlayFlags, hRet, bufferRangeStart, bufferRangeSize);
pThis->EmuBufferToggle = X_DSB_TOGGLE_DEFAULT;
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(dwValue);
pThis->EmuDirectSoundBuffer8->SetCurrentPosition(currentPos);
}
if (dwFlags & X_DSBSTOPEX_RELEASEWAVEFORM) {
// Release from loop region.
pThis->EmuPlayFlags &= ~X_DSBPLAY_LOOPING;
}
if (dwStatus & DSBSTATUS_PLAYING && rtTimeStamp != 0LL) {
@ -1650,6 +1605,9 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSoundBuffer_StopEx)
pThis->Xb_rtStopEx = 0LL;
}
}
else {
LOG_TEST_CASE("Expected X_DSBSTOPEX_ENVELOPE");
}
}
return hRet;

View File

@ -27,11 +27,13 @@
#define LOG_PREFIX CXBXR_MODULE::DSOUND
#include <imgui.h>
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h" // For ImVec
#include "core/common/imgui/ui.hpp"
#include <core\kernel\exports\xboxkrnl.h>
#include <dsound.h>
#include "DirectSoundGlobal.hpp"
#include "DirectSoundInline.hpp" // For GetCurrentPosition, RegionCurrentLocation
Settings::s_audio g_XBAudio = { 0 };
std::recursive_mutex g_DSoundMutex;
@ -54,6 +56,105 @@ DWORD g_dwXbMemAllocated = 0;
DWORD g_dwFree2DBuffers = 0;
DWORD g_dwFree3DBuffers = 0;
DsBufferStreaming g_dsBufferStreaming;
void DrawAudioProgress(xbox::XbHybridDSBuffer* pHybrid, float scaleWidth, ImDrawList* drawList) {
const auto& pBuffer = pHybrid->emuDSBuffer;
auto cursor = ImGui::GetCursorScreenPos();
auto width = ImGui::GetContentRegionAvail().x;
DWORD rawCursor;
HybridDirectSoundBuffer_GetCurrentPosition(pBuffer->EmuDirectSoundBuffer8, &rawCursor, nullptr, pBuffer->EmuFlags);
float scale = (width / pBuffer->X_BufferCacheSize) / scaleWidth;
float playCursor = rawCursor * scale;
bool isLooping = pBuffer->EmuPlayFlags & X_DSBPLAY_LOOPING;
auto colSpan = ImColor(0.8f, 0.1f, 0.1f, 0.3f);
auto colRegion = ImColor(0.1f, 0.8f, 0.1, 0.3f);
auto colRegionLoop = ImColor(0.1f, 0.2f, 0.8, 0.3f);
auto colPlay = ImColor(0.8f, 0.8f, 0.1f, 0.6);
float height = 8;
float sBuf = height * 0.4;
float sReg = height * 1;
float sPlay = height * 0.4;
// Buffer
auto start = cursor + ImVec2(0, (height - sBuf) / 2);
drawList->AddRectFilled(start, start + ImVec2(pBuffer->X_BufferCacheSize * scale, sBuf), colSpan, 0);
DWORD bufferRangeStart;
DWORD bufferRangeSize;
DSoundBufferRegionCurrentLocation(pHybrid, pBuffer->EmuPlayFlags, bufferRangeStart, bufferRangeSize);
bufferRangeStart *= scale;
bufferRangeSize *= scale;
// Region
start = cursor + ImVec2(bufferRangeStart, (height - sReg) / 2);
drawList->AddRectFilled(start, start + ImVec2(bufferRangeSize, sReg), isLooping ? colRegionLoop : colRegion);
// Play area
start = cursor + ImVec2(bufferRangeStart, (height - sPlay) / 2);
drawList->AddRectFilled(start, start + ImVec2(playCursor, sPlay), colPlay);
// Play cursor
start = cursor + ImVec2(bufferRangeStart + playCursor, 0);
drawList->AddLine(start, start + ImVec2(0, height), colPlay);
ImGui::Dummy(ImVec2(pBuffer->X_BufferCacheSize * scale, height));
}
void DSound_DrawBufferVisualization(bool is_focus, bool* p_show, ImGuiWindowFlags input_handler) {
if (!*p_show) return;
DSoundMutexGuardLock;
ImGui::SetNextWindowPos(ImVec2(IMGUI_MIN_DIST_SIDE, IMGUI_MIN_DIST_TOP), ImGuiCond_FirstUseEver, ImVec2(0.0f, 0.0f));
ImGui::SetNextWindowSize(ImVec2(200, 275), ImGuiCond_FirstUseEver);
if (ImGui::Begin("DSBuffer Visualization", p_show, input_handler)) {
static bool showPlayingOnly = true;
ImGui::Checkbox("Show playing only", &showPlayingOnly);
static float bufferScale = 1;
ImGui::PushItemWidth(100);
ImGui::DragFloat("Audio Scale", &bufferScale, 1 / 1000.f, 1 / 24000.f, 1.f, "%.7f", ImGuiSliderFlags_Logarithmic);
if (ImGui::CollapsingHeader("Buffering Controls")) {
ImGui::SliderInt("Stream interval (ms)", (int*)&g_dsBufferStreaming.streamInterval, 0, 50);
ImGui::SliderInt("Stream ahead (ms)", (int*)&g_dsBufferStreaming.streamAhead, 0, 1000);
ImGui::SliderFloat("Tweak copy offset", &g_dsBufferStreaming.tweakCopyOffset, -1, 1);
}
if (ImGui::BeginChild("DSBuffer Graph", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) {
auto drawList = ImGui::GetWindowDrawList();
int index = 0;
for (const auto& i : g_pDSoundBufferCache) {
if (showPlayingOnly) {
DWORD dwStatus;
auto hRet = i->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&dwStatus);
if (hRet != DS_OK || !(dwStatus & DSBSTATUS_PLAYING)) {
continue;
}
}
// Required to add controls inside the loop
ImGui::PushID(index++);
DrawAudioProgress(i, bufferScale, drawList);
ImGui::PopID();
}
}
ImGui::EndChild();
}
ImGui::End();
}
void DSound_PrintStats(bool is_focus, ImGuiWindowFlags input_handler, bool m_show_audio_stats)
{
DSoundMutexGuardLock;
@ -149,7 +250,7 @@ void DSound_PrintStats(bool is_focus, ImGuiWindowFlags input_handler, bool m_sho
ImGui::Text("Total active DSStream = %u", isActive);
}
}
ImGui::End();
}
ImGui::End();
}
}

View File

@ -61,6 +61,13 @@ extern DWORD g_dwXbMemAllocated;
extern DWORD g_dwFree2DBuffers;
extern DWORD g_dwFree3DBuffers;
struct DsBufferStreaming {
DWORD streamInterval = 1;
DWORD streamAhead = 50;
float tweakCopyOffset = 0;
};
extern DsBufferStreaming g_dsBufferStreaming;
// size of DirectSound cache max size
#define X_DIRECTSOUND_CACHE_MAX 0x800

View File

@ -569,7 +569,6 @@ static inline void DSoundBufferResizeUpdate(
static inline void DSoundBufferRegionCurrentLocation(
xbox::XbHybridDSBuffer* pHybridThis,
DWORD dwPlayFlags,
HRESULT &hRet,
DWORD &Xb_dwStartOffset,
DWORD &Xb_dwByteLength)
{
@ -608,7 +607,7 @@ static inline void DSoundBufferUpdate(
DWORD Xb_dwByteLength;
DWORD Xb_dwStartOffset;
DSoundBufferRegionCurrentLocation(pHybridThis, dwPlayFlags, hRet, Xb_dwStartOffset, Xb_dwByteLength);
DSoundBufferRegionCurrentLocation(pHybridThis, dwPlayFlags, Xb_dwStartOffset, Xb_dwByteLength);
DSoundBufferResizeUpdate(pHybridThis, dwPlayFlags, hRet, Xb_dwStartOffset, Xb_dwByteLength);
}