Merge pull request #1835 from RadWolfie/fix-wfx-format-sanity
Fix Sanity and Organize wfxformat into Functions
This commit is contained in:
commit
951520ece1
|
@ -141,6 +141,7 @@ file (GLOB CXBXR_HEADER_EMU
|
||||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbState.h"
|
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbState.h"
|
||||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbVertexBuffer.h"
|
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbVertexBuffer.h"
|
||||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbVertexShader.h"
|
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbVertexShader.h"
|
||||||
|
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/windows/WFXformat.hpp"
|
||||||
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/DirectSound/DirectSound.hpp"
|
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/DirectSound/DirectSound.hpp"
|
||||||
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/DirectSound/DirectSoundGlobal.hpp"
|
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/DirectSound/DirectSoundGlobal.hpp"
|
||||||
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/DirectSound/DirectSoundInline.hpp"
|
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/DirectSound/DirectSoundInline.hpp"
|
||||||
|
|
|
@ -109,6 +109,7 @@ struct X_CDirectSoundBuffer
|
||||||
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
|
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
|
||||||
X_DSVOICEPROPS Xb_VoiceProperties;
|
X_DSVOICEPROPS Xb_VoiceProperties;
|
||||||
DWORD Xb_Frequency;
|
DWORD Xb_Frequency;
|
||||||
|
DWORD Xb_Flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
//Custom flags (4 bytes support up to 31 shifts,starting from 0)
|
//Custom flags (4 bytes support up to 31 shifts,starting from 0)
|
||||||
|
@ -263,7 +264,8 @@ class X_CDirectSoundStream
|
||||||
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
|
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
|
||||||
X_DSVOICEPROPS Xb_VoiceProperties;
|
X_DSVOICEPROPS Xb_VoiceProperties;
|
||||||
DWORD Xb_Frequency;
|
DWORD Xb_Frequency;
|
||||||
DWORD Host_dwLastWritePos;
|
DWORD Host_dwLastWritePos;
|
||||||
|
DWORD Xb_Flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ******************************************************************
|
// ******************************************************************
|
||||||
|
|
|
@ -205,14 +205,14 @@ HRESULT WINAPI XTL::EMUPATCH(DirectSoundCreateBuffer)
|
||||||
// TODO: Garbage Collection
|
// TODO: Garbage Collection
|
||||||
*ppBuffer = new X_CDirectSoundBuffer();
|
*ppBuffer = new X_CDirectSoundBuffer();
|
||||||
|
|
||||||
DSoundBufferSetDefault((*ppBuffer), 0);
|
DSoundBufferSetDefault((*ppBuffer), 0, pdsbd->dwFlags);
|
||||||
(*ppBuffer)->Host_lock = { 0 };
|
(*ppBuffer)->Host_lock = { 0 };
|
||||||
(*ppBuffer)->Xb_rtStopEx = 0LL;
|
(*ppBuffer)->Xb_rtStopEx = 0LL;
|
||||||
|
|
||||||
DSoundBufferRegionSetDefault(*ppBuffer);
|
DSoundBufferRegionSetDefault(*ppBuffer);
|
||||||
|
|
||||||
// We have to set DSBufferDesc last due to EmuFlags must be either 0 or previously written value to preserve other flags.
|
// We have to set DSBufferDesc last due to EmuFlags must be either 0 or previously written value to preserve other flags.
|
||||||
GeneratePCMFormat(DSBufferDesc, pdsbd->lpwfxFormat, (*ppBuffer)->EmuFlags, pdsbd->dwBufferBytes,
|
GeneratePCMFormat(DSBufferDesc, pdsbd->lpwfxFormat, pdsbd->dwFlags, (*ppBuffer)->EmuFlags, pdsbd->dwBufferBytes,
|
||||||
&(*ppBuffer)->X_BufferCache, (*ppBuffer)->X_BufferCacheSize, (*ppBuffer)->Xb_VoiceProperties, pdsbd->lpMixBinsOutput);
|
&(*ppBuffer)->X_BufferCache, (*ppBuffer)->X_BufferCacheSize, (*ppBuffer)->Xb_VoiceProperties, pdsbd->lpMixBinsOutput);
|
||||||
(*ppBuffer)->EmuBufferDesc = DSBufferDesc;
|
(*ppBuffer)->EmuBufferDesc = DSBufferDesc;
|
||||||
|
|
||||||
|
@ -929,8 +929,8 @@ HRESULT WINAPI XTL::EMUPATCH(IDirectSoundBuffer_SetFormat)
|
||||||
LOG_FUNC_ARG(pwfxFormat)
|
LOG_FUNC_ARG(pwfxFormat)
|
||||||
LOG_FUNC_END;
|
LOG_FUNC_END;
|
||||||
|
|
||||||
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat,
|
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat, pThis->Xb_Flags,
|
||||||
pThis->EmuBufferDesc, pThis->EmuFlags,
|
pThis->EmuBufferDesc, pThis->EmuFlags,
|
||||||
pThis->EmuPlayFlags, pThis->EmuDirectSound3DBuffer8,
|
pThis->EmuPlayFlags, pThis->EmuDirectSound3DBuffer8,
|
||||||
0, pThis->X_BufferCache, pThis->X_BufferCacheSize,
|
0, pThis->X_BufferCache, pThis->X_BufferCacheSize,
|
||||||
pThis->Xb_VoiceProperties, xbnullptr, pThis->Xb_Frequency);
|
pThis->Xb_VoiceProperties, xbnullptr, pThis->Xb_Frequency);
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "common/XADPCM.h"
|
#include "common/XADPCM.h"
|
||||||
#include "core/hle/DSOUND/XbDSoundTypes.h"
|
#include "core/hle/DSOUND/XbDSoundTypes.h"
|
||||||
|
#include "core/hle/DSOUND/common/windows/WFXformat.hpp"
|
||||||
|
|
||||||
#include <mmreg.h>
|
#include <mmreg.h>
|
||||||
|
|
||||||
|
@ -85,75 +86,6 @@ static void DSoundBufferOutputXBtoHost(DWORD emuFlags, DSBUFFERDESC &DSBufferDes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert XADPCM to PCM format helper function
|
|
||||||
static inline void XADPCM2PCMFormat(LPWAVEFORMATEX lpwfxFormat)
|
|
||||||
{
|
|
||||||
|
|
||||||
#if 0 //For testing purpose if XADPCM to PCM is not accurate.
|
|
||||||
EmuLog(LOG_LEVEL::DEBUG,
|
|
||||||
"EmuDSound: XADPCM WAVEFORMATEX\n"
|
|
||||||
"{\n"
|
|
||||||
" wFormatTag : 0x%.04hX\n"
|
|
||||||
" nChannels : 0x%.02hd\n"
|
|
||||||
" nSamplesPerSec : 0x%.08X\n"
|
|
||||||
" nAvgBytesPerSec : 0x%.08X\n"
|
|
||||||
" nBlockAlign : 0x%.02hd\n"
|
|
||||||
" wBitsPerSample : 0x%.04hX\n"
|
|
||||||
" cbSize : 0x%.04hX\n"
|
|
||||||
"}\n",
|
|
||||||
lpwfxFormat->wFormatTag,
|
|
||||||
lpwfxFormat->nChannels,
|
|
||||||
lpwfxFormat->nSamplesPerSec,
|
|
||||||
lpwfxFormat->nAvgBytesPerSec,
|
|
||||||
lpwfxFormat->nBlockAlign,
|
|
||||||
lpwfxFormat->wBitsPerSample,
|
|
||||||
lpwfxFormat->cbSize);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
lpwfxFormat->wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
|
|
||||||
//lpwfxFormat.wFormatTag; /* format type */
|
|
||||||
//lpwfxFormat.nChannels; /* number of channels (i.e. mono, stereo...) */ NO CHANGE
|
|
||||||
//lpwfxFormat.nSamplesPerSec; /* sample rate */ NO CHANGE
|
|
||||||
//lpwfxFormat.nAvgBytesPerSec; /* for buffer estimation */
|
|
||||||
//lpwfxFormat.nBlockAlign; /* block size of data */
|
|
||||||
//lpwfxFormat.wBitsPerSample; /* number of bits per sample of mono data */
|
|
||||||
//lpwfxFormat.cbSize; /* the count in bytes of the size of extra information (after cbSize) */
|
|
||||||
|
|
||||||
lpwfxFormat->wBitsPerSample = 16;
|
|
||||||
lpwfxFormat->nBlockAlign = 2 * lpwfxFormat->nChannels;
|
|
||||||
lpwfxFormat->nAvgBytesPerSec = lpwfxFormat->nSamplesPerSec * lpwfxFormat->nBlockAlign;
|
|
||||||
lpwfxFormat->cbSize = 0;
|
|
||||||
//Enable this only if you have Xbox ADPCM Codec installed on your PC, or else it will fail every time.
|
|
||||||
//This is just to verify format conversion is correct or not.
|
|
||||||
#if 0
|
|
||||||
if (waveOutOpen(nullptr, WAVE_MAPPER, lpwfxFormat, NULL, NULL, WAVE_FORMAT_QUERY) != MMSYSERR_NOERROR) {
|
|
||||||
return DSERR_BADFORMAT;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0 //For testing purpose if XADPCM to PCM is not accurate.
|
|
||||||
EmuLog(LOG_LEVEL::DEBUG,
|
|
||||||
"EmuDSound: Converted to PCM WAVEFORMATEX\n"
|
|
||||||
"{\n"
|
|
||||||
" wFormatTag : 0x%.04hX\n"
|
|
||||||
" nChannels : 0x%.02hd\n"
|
|
||||||
" nSamplesPerSec : 0x%.08X\n"
|
|
||||||
" nAvgBytesPerSec : 0x%.08X\n"
|
|
||||||
" nBlockAlign : 0x%.02hd\n"
|
|
||||||
" wBitsPerSample : 0x%.04hX\n"
|
|
||||||
" cbSize : 0x%.04hX\n"
|
|
||||||
"}\n",
|
|
||||||
lpwfxFormat->wFormatTag,
|
|
||||||
lpwfxFormat->nChannels,
|
|
||||||
lpwfxFormat->nSamplesPerSec,
|
|
||||||
lpwfxFormat->nAvgBytesPerSec,
|
|
||||||
lpwfxFormat->nBlockAlign,
|
|
||||||
lpwfxFormat->wBitsPerSample,
|
|
||||||
lpwfxFormat->cbSize);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void GenerateXboxBufferCache(
|
static inline void GenerateXboxBufferCache(
|
||||||
DSBUFFERDESC &DSBufferDesc,
|
DSBUFFERDESC &DSBufferDesc,
|
||||||
DWORD &dwEmuFlags,
|
DWORD &dwEmuFlags,
|
||||||
|
@ -312,7 +244,8 @@ static inline void GenerateMixBinDefault(
|
||||||
|
|
||||||
static inline void GeneratePCMFormat(
|
static inline void GeneratePCMFormat(
|
||||||
DSBUFFERDESC &DSBufferDesc,
|
DSBUFFERDESC &DSBufferDesc,
|
||||||
LPCWAVEFORMATEX lpwfxFormat,
|
LPCWAVEFORMATEX Xb_lpwfxFormat,
|
||||||
|
DWORD &Xb_flags,
|
||||||
DWORD &dwEmuFlags,
|
DWORD &dwEmuFlags,
|
||||||
DWORD X_BufferSizeRequest,
|
DWORD X_BufferSizeRequest,
|
||||||
LPVOID* X_BufferCache,
|
LPVOID* X_BufferCache,
|
||||||
|
@ -321,186 +254,61 @@ static inline void GeneratePCMFormat(
|
||||||
XTL::X_LPDSMIXBINS mixbins_output)
|
XTL::X_LPDSMIXBINS mixbins_output)
|
||||||
{
|
{
|
||||||
bool bIsSpecial = false;
|
bool bIsSpecial = false;
|
||||||
DWORD checkAvgBps;
|
|
||||||
|
|
||||||
GenerateMixBinDefault(Xb_VoiceProperties, lpwfxFormat, mixbins_output, ((DSBufferDesc.dwFlags & DSBCAPS_CTRL3D) > 0));
|
GenerateMixBinDefault(Xb_VoiceProperties, Xb_lpwfxFormat, mixbins_output, ((DSBufferDesc.dwFlags & DSBCAPS_CTRL3D) > 0));
|
||||||
|
|
||||||
// convert from Xbox to PC DSound
|
// convert from Xbox to PC DSound
|
||||||
{
|
{
|
||||||
DSBufferDesc.dwReserved = 0;
|
DSBufferDesc.dwReserved = 0;
|
||||||
|
|
||||||
if (lpwfxFormat != xbnullptr) {
|
// Allocate only once, does not need to re-allocate.
|
||||||
|
if (DSBufferDesc.lpwfxFormat == nullptr) {
|
||||||
|
// Only allocate extra value for setting extra values later on. WAVEFORMATEXTENSIBLE is the highest size I had seen.
|
||||||
|
DSBufferDesc.lpwfxFormat = (WAVEFORMATEX*)calloc(1, sizeof(WAVEFORMATEXTENSIBLE));
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: RadWolfie - Need implement support for WAVEFORMATEXTENSIBLE as stated in CDirectSoundStream_SetFormat function note below
|
if (DSBufferDesc.lpwfxFormat == nullptr) {
|
||||||
// Do we need to convert it? Or just do the WAVEFORMATEX only?
|
CxbxKrnlCleanup("Unable to allocate DSBufferDesc.Xb_lpwfxFormat");
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: pwfxFormat is not always a WAVEFORMATEX structure, it can
|
if (Xb_lpwfxFormat != xbnullptr) {
|
||||||
// be WAVEFORMATEXTENSIBLE if that's what the programmer(s) wanted
|
|
||||||
// in the first place, FYI.
|
|
||||||
|
|
||||||
// Allocate only once, does not need to re-allocate.
|
PWAVEFORMATEXTENSIBLE lpwfxFormatHost = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(DSBufferDesc.lpwfxFormat);
|
||||||
if (DSBufferDesc.lpwfxFormat == nullptr) {
|
|
||||||
// Only allocate extra value for setting extra values later on. WAVEFORMATEXTENSIBLE is the highest size I had seen.
|
|
||||||
DSBufferDesc.lpwfxFormat = (WAVEFORMATEX*)calloc(1, sizeof(WAVEFORMATEXTENSIBLE));
|
|
||||||
}
|
|
||||||
WAVEFORMATEX* lpwfxFormatHost = DSBufferDesc.lpwfxFormat;
|
|
||||||
|
|
||||||
if (lpwfxFormat->wFormatTag == WAVE_FORMAT_PCM) {
|
if (Xb_lpwfxFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||||
// Test case: Hulk crash due to cbSize is not a valid size.
|
memcpy(lpwfxFormatHost, Xb_lpwfxFormat, sizeof(WAVEFORMATEX) + Xb_lpwfxFormat->cbSize);
|
||||||
memcpy(lpwfxFormatHost, lpwfxFormat, sizeof(WAVEFORMATEX));
|
|
||||||
lpwfxFormatHost->cbSize = 0; // Let's enforce this value to prevent any other exception later on.
|
|
||||||
}
|
|
||||||
else if (lpwfxFormat->wFormatTag == 0 && (DSBufferDesc.dwFlags & DSBCAPS_LOCDEFER) > 0) {
|
|
||||||
// NOTE: This is currently a hack for ability to create buffer class with DSBCAPS_LOCDEFER flag.
|
|
||||||
lpwfxFormatHost->wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
lpwfxFormatHost->nChannels = 2;
|
|
||||||
lpwfxFormatHost->nSamplesPerSec = 44100;
|
|
||||||
lpwfxFormatHost->wBitsPerSample = 8;
|
|
||||||
lpwfxFormatHost->nBlockAlign = lpwfxFormatHost->nChannels * static_cast<uint32_t>(lpwfxFormatHost->wBitsPerSample) / 8;
|
|
||||||
lpwfxFormatHost->nAvgBytesPerSec = lpwfxFormatHost->nSamplesPerSec * lpwfxFormatHost->nBlockAlign;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
memcpy(lpwfxFormatHost, lpwfxFormat, sizeof(WAVEFORMATEX) + lpwfxFormat->cbSize);
|
// Other formats will enforce use WAVEFORMATEX size, we cannot allow corruption sneak into WAVEFORMATEXTENSIBLE's structure.
|
||||||
}
|
memcpy(lpwfxFormatHost, Xb_lpwfxFormat, sizeof(WAVEFORMATEX));
|
||||||
|
lpwfxFormatHost->Format.cbSize = 0;
|
||||||
// NOTE: Currently a workaround hack fix until custom management for buffer/stream can allow unallocated buffer.
|
|
||||||
// Without this fix, some titles wouldn't progress further. Plus no matter what values are set in Xbox's wfxFormat
|
|
||||||
// are approved. It does need further investigation which require LLE APU stubbed and a HLE verbose plugin.
|
|
||||||
if (X_BufferSizeRequest == 0 &&
|
|
||||||
(lpwfxFormatHost->nSamplesPerSec == 0 || lpwfxFormatHost->nAvgBytesPerSec == 0)) {
|
|
||||||
// NOTE: When X_BufferSizeRequest is 0, creation is allow to be performed until allocated size is given from different API.
|
|
||||||
// Set dwBufferBytes is not needed as it is handled later on.
|
|
||||||
// Test case: Hobbit did not input nSamplesPerSec & nAvgBytesPerSec variable, yet isn't a requirement.
|
|
||||||
if (lpwfxFormatHost->nChannels == 0) {
|
|
||||||
lpwfxFormatHost->nChannels = 2;
|
|
||||||
}
|
|
||||||
if (lpwfxFormatHost->wBitsPerSample == 0) {
|
|
||||||
lpwfxFormatHost->wBitsPerSample = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lpwfxFormatHost->nBlockAlign == 0) {
|
|
||||||
lpwfxFormatHost->nBlockAlign = lpwfxFormatHost->nChannels * static_cast<uint32_t>(lpwfxFormatHost->wBitsPerSample) / 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lpwfxFormatHost->nSamplesPerSec == 0) {
|
|
||||||
lpwfxFormatHost->nSamplesPerSec = 44100;
|
|
||||||
lpwfxFormatHost->nAvgBytesPerSec = lpwfxFormatHost->nSamplesPerSec * lpwfxFormatHost->nBlockAlign;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: I don't think we need WAVEFORMATEXTENSIBLE stub for this fix.
|
|
||||||
if (lpwfxFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
||||||
EmuLog(LOG_LEVEL::WARNING, "Is WAVE_FORMAT_EXTENSIBLE having problem with creation?");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dwEmuFlags = dwEmuFlags & ~DSE_FLAG_AUDIO_CODECS;
|
dwEmuFlags = dwEmuFlags & ~DSE_FLAG_AUDIO_CODECS;
|
||||||
|
|
||||||
switch (DSBufferDesc.lpwfxFormat->wFormatTag) {
|
CODEC_FORMAT cf_audio = WFXformat_SyncHostFormat(DSBufferDesc.lpwfxFormat, Xb_lpwfxFormat, X_BufferSizeRequest, Xb_flags);
|
||||||
case WAVE_FORMAT_PCM:
|
if (cf_audio == CF_PCM) {
|
||||||
dwEmuFlags |= DSE_FLAG_PCM;
|
dwEmuFlags |= DSE_FLAG_PCM;
|
||||||
|
|
||||||
//TODO: Phantasy Star Online Episode I & II made an attempt to use avg byte/second below sample/second requirement.
|
|
||||||
//In other word, this is a workaround to fix title mistake...
|
|
||||||
checkAvgBps = lpwfxFormat->nSamplesPerSec * lpwfxFormat->nBlockAlign;
|
|
||||||
if (lpwfxFormat->nAvgBytesPerSec < checkAvgBps) {
|
|
||||||
DSBufferDesc.lpwfxFormat->nAvgBytesPerSec = checkAvgBps;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WAVE_FORMAT_XBOX_ADPCM:
|
|
||||||
dwEmuFlags |= DSE_FLAG_XADPCM;
|
|
||||||
XADPCM2PCMFormat(DSBufferDesc.lpwfxFormat);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dwEmuFlags |= DSE_FLAG_PCM_UNKNOWN;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (cf_audio == CF_XADPCM) {
|
||||||
|
dwEmuFlags |= DSE_FLAG_XADPCM;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dwEmuFlags |= DSE_FLAG_PCM_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
bIsSpecial = true;
|
|
||||||
dwEmuFlags |= DSE_FLAG_RECIEVEDATA;
|
dwEmuFlags |= DSE_FLAG_RECIEVEDATA;
|
||||||
|
|
||||||
EmuLog(LOG_LEVEL::WARNING, "Creating dummy WAVEFORMATEX (pdsbd->lpwfxFormat = xbnullptr)...");
|
(void)WFXformat_SyncHostFormat(DSBufferDesc.lpwfxFormat, Xb_lpwfxFormat, X_BufferSizeRequest, Xb_flags);
|
||||||
|
|
||||||
// HACK: This is a special sound buffer, create dummy WAVEFORMATEX data.
|
|
||||||
// It's supposed to recieve data rather than generate it. Buffers created
|
|
||||||
// with flags DSBCAPS_MIXIN, DSBCAPS_FXIN, and DSBCAPS_FXIN2 will have no
|
|
||||||
// WAVEFORMATEX structure by default.
|
|
||||||
|
|
||||||
// TODO: A better response to this scenario if possible.
|
|
||||||
|
|
||||||
DSBufferDesc.lpwfxFormat = (WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEXTENSIBLE));
|
|
||||||
|
|
||||||
//memset(pDSBufferDescSpecial->lpwfxFormat, 0, sizeof(WAVEFORMATEX));
|
|
||||||
//memset(pDSBufferDescSpecial, 0, sizeof(DSBUFFERDESC));
|
|
||||||
|
|
||||||
DSBufferDesc.lpwfxFormat->wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
DSBufferDesc.lpwfxFormat->nChannels = 2;
|
|
||||||
DSBufferDesc.lpwfxFormat->nSamplesPerSec = 22050;
|
|
||||||
DSBufferDesc.lpwfxFormat->nBlockAlign = 4;
|
|
||||||
DSBufferDesc.lpwfxFormat->nAvgBytesPerSec = DSBufferDesc.lpwfxFormat->nSamplesPerSec * DSBufferDesc.lpwfxFormat->nBlockAlign;
|
|
||||||
DSBufferDesc.lpwfxFormat->wBitsPerSample = 16;
|
|
||||||
DSBufferDesc.lpwfxFormat->cbSize = 0;
|
|
||||||
|
|
||||||
DSBufferDesc.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
|
DSBufferDesc.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
|
||||||
DSBufferDesc.dwBufferBytes = 3 * DSBufferDesc.lpwfxFormat->nAvgBytesPerSec;
|
DSBufferDesc.dwBufferBytes = 5 * DSBufferDesc.lpwfxFormat->nAvgBytesPerSec;
|
||||||
|
|
||||||
// DSBufferDesc.lpwfxFormat = (WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX)/*+pdsbd->lpwfxFormat->cbSize*/);
|
|
||||||
|
|
||||||
//// DSBufferDesc.lpwfxFormat->cbSize = sizeof( WAVEFORMATEX );
|
|
||||||
// DSBufferDesc.lpwfxFormat->nChannels = 1;
|
|
||||||
// DSBufferDesc.lpwfxFormat->wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
// DSBufferDesc.lpwfxFormat->nSamplesPerSec = 22050;
|
|
||||||
// DSBufferDesc.lpwfxFormat->nBlockAlign = 4;
|
|
||||||
// DSBufferDesc.lpwfxFormat->nAvgBytesPerSec = 4 * 22050;
|
|
||||||
// DSBufferDesc.lpwfxFormat->wBitsPerSample = 16;
|
|
||||||
|
|
||||||
// Give this buffer 3 seconds of data if needed
|
|
||||||
/*if(pdsbd->dwBufferBytes == 0) {
|
|
||||||
DSBufferDesc.dwBufferBytes = 3 * DSBufferDesc.lpwfxFormat->nAvgBytesPerSec;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DSBufferDesc.guid3DAlgorithm = DS3DALG_DEFAULT;
|
DSBufferDesc.guid3DAlgorithm = DS3DALG_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Still a requirement? Need to retest it again. Can't remember which title cause problem or had been resolved.
|
|
||||||
// sanity check
|
|
||||||
if (!bIsSpecial) {
|
|
||||||
if (DSBufferDesc.lpwfxFormat->nBlockAlign != DSBufferDesc.lpwfxFormat->nChannels* static_cast<uint32_t>(DSBufferDesc.lpwfxFormat->wBitsPerSample) / 8) {
|
|
||||||
DSBufferDesc.lpwfxFormat->nBlockAlign = (2 * DSBufferDesc.lpwfxFormat->wBitsPerSample) / 8;
|
|
||||||
DSBufferDesc.lpwfxFormat->nAvgBytesPerSec = DSBufferDesc.lpwfxFormat->nSamplesPerSec * DSBufferDesc.lpwfxFormat->nBlockAlign;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DSBufferDesc.lpwfxFormat != nullptr) {
|
|
||||||
|
|
||||||
// NOTE: This process check for 2+ channels whenever XADPCM or PCM format from xbox titles input in nChannels.
|
|
||||||
// Since the host do not support 2+ channels on PCM only format. We must convert/update allocated wfxFormat to EXTENSIBLE
|
|
||||||
// format which had been allocated enough to update.
|
|
||||||
if (DSBufferDesc.lpwfxFormat->nChannels > 2 && DSBufferDesc.lpwfxFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
|
|
||||||
PWAVEFORMATEXTENSIBLE lpwfxFormatExtensible = (PWAVEFORMATEXTENSIBLE)DSBufferDesc.lpwfxFormat;
|
|
||||||
lpwfxFormatExtensible->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
||||||
lpwfxFormatExtensible->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
|
||||||
lpwfxFormatExtensible->Samples.wValidBitsPerSample = lpwfxFormatExtensible->Format.wBitsPerSample;
|
|
||||||
lpwfxFormatExtensible->Samples.wSamplesPerBlock = 0;
|
|
||||||
lpwfxFormatExtensible->Samples.wReserved = 0;
|
|
||||||
lpwfxFormatExtensible->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
|
||||||
|
|
||||||
// Add rear speakers
|
|
||||||
if (lpwfxFormat->nChannels >= 4) {
|
|
||||||
lpwfxFormatExtensible->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
|
||||||
}
|
|
||||||
// Add center speaker
|
|
||||||
if (lpwfxFormat->nChannels >= 5) {
|
|
||||||
lpwfxFormatExtensible->dwChannelMask |= SPEAKER_FRONT_CENTER;
|
|
||||||
}
|
|
||||||
// Add subwoofer mask (pretty sure 3 channels is 2.1, not 3.0 for xbox purpose)
|
|
||||||
if (lpwfxFormat->nChannels == 6 || lpwfxFormat->nChannels == 3) {
|
|
||||||
lpwfxFormatExtensible->dwChannelMask |= SPEAKER_LOW_FREQUENCY;
|
|
||||||
}
|
|
||||||
lpwfxFormatExtensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (X_BufferSizeRequest < DSBSIZE_MIN) {
|
if (X_BufferSizeRequest < DSBSIZE_MIN) {
|
||||||
X_BufferSizeRequest = DSBSIZE_MIN;
|
X_BufferSizeRequest = DSBSIZE_MIN;
|
||||||
} else if (X_BufferSizeRequest > DSBSIZE_MAX) {
|
} else if (X_BufferSizeRequest > DSBSIZE_MAX) {
|
||||||
|
@ -572,7 +380,7 @@ static inline void DSound3DBufferCreate(LPDIRECTSOUNDBUFFER8 pDSBuffer, LPDIRECT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DSoundBufferSetDefault(pThis, dwEmuPlayFlags) \
|
#define DSoundBufferSetDefault(pThis, dwEmuPlayFlags, Xb_dwFlags) \
|
||||||
pThis->EmuDirectSoundBuffer8 = nullptr; \
|
pThis->EmuDirectSoundBuffer8 = nullptr; \
|
||||||
pThis->EmuDirectSound3DBuffer8 = nullptr; \
|
pThis->EmuDirectSound3DBuffer8 = nullptr; \
|
||||||
pThis->X_BufferCache = xbnullptr; \
|
pThis->X_BufferCache = xbnullptr; \
|
||||||
|
@ -585,7 +393,8 @@ static inline void DSound3DBufferCreate(LPDIRECTSOUNDBUFFER8 pDSBuffer, LPDIRECT
|
||||||
pThis->Xb_dwHeadroom = 600; /* default for 2D voice */ \
|
pThis->Xb_dwHeadroom = 600; /* default for 2D voice */ \
|
||||||
pThis->Xb_EnvolopeDesc = { 0 }; \
|
pThis->Xb_EnvolopeDesc = { 0 }; \
|
||||||
InitVoiceProperties(pThis->Xb_VoiceProperties); /* The rest will initialize in GeneratePCMFormat to GenerateMixBinDefault. */ \
|
InitVoiceProperties(pThis->Xb_VoiceProperties); /* The rest will initialize in GeneratePCMFormat to GenerateMixBinDefault. */ \
|
||||||
pThis->Xb_Frequency = XTL_DSXFREQUENCY_ORIGINAL;
|
pThis->Xb_Frequency = XTL_DSXFREQUENCY_ORIGINAL; \
|
||||||
|
pThis->Xb_Flags = Xb_dwFlags;
|
||||||
//pThis->EmuBufferDesc = { 0 }; // Enable this when become necessary.
|
//pThis->EmuBufferDesc = { 0 }; // Enable this when become necessary.
|
||||||
/*
|
/*
|
||||||
pThis->EmuLockPtr1 = xbnullptr; \
|
pThis->EmuLockPtr1 = xbnullptr; \
|
||||||
|
@ -1259,7 +1068,8 @@ static inline HRESULT HybridDirectSoundBuffer_SetFilter(
|
||||||
//IDirectSoundBuffer
|
//IDirectSoundBuffer
|
||||||
static inline HRESULT HybridDirectSoundBuffer_SetFormat(
|
static inline HRESULT HybridDirectSoundBuffer_SetFormat(
|
||||||
LPDIRECTSOUNDBUFFER8 &pDSBuffer,
|
LPDIRECTSOUNDBUFFER8 &pDSBuffer,
|
||||||
LPCWAVEFORMATEX pwfxFormat,
|
LPCWAVEFORMATEX Xb_pwfxFormat,
|
||||||
|
DWORD Xb_flags,
|
||||||
DSBUFFERDESC &BufferDesc,
|
DSBUFFERDESC &BufferDesc,
|
||||||
DWORD &dwEmuFlags,
|
DWORD &dwEmuFlags,
|
||||||
DWORD &dwPlayFlags,
|
DWORD &dwPlayFlags,
|
||||||
|
@ -1274,10 +1084,10 @@ static inline HRESULT HybridDirectSoundBuffer_SetFormat(
|
||||||
pDSBuffer->Stop();
|
pDSBuffer->Stop();
|
||||||
|
|
||||||
if (X_BufferAllocate) {
|
if (X_BufferAllocate) {
|
||||||
GeneratePCMFormat(BufferDesc, pwfxFormat, dwEmuFlags, X_BufferCacheSize, xbnullptr, X_BufferCacheSize, Xb_VoiceProperties, mixbins_output);
|
GeneratePCMFormat(BufferDesc, Xb_pwfxFormat, Xb_flags, dwEmuFlags, X_BufferCacheSize, xbnullptr, X_BufferCacheSize, Xb_VoiceProperties, mixbins_output);
|
||||||
// Don't allocate for DS Stream class, it is using straight from the source.
|
// Don't allocate for DS Stream class, it is using straight from the source.
|
||||||
} else {
|
} else {
|
||||||
GeneratePCMFormat(BufferDesc, pwfxFormat, dwEmuFlags, 0, xbnullptr, X_BufferCacheSize, Xb_VoiceProperties, mixbins_output);
|
GeneratePCMFormat(BufferDesc, Xb_pwfxFormat, Xb_flags, dwEmuFlags, 0, xbnullptr, X_BufferCacheSize, Xb_VoiceProperties, mixbins_output);
|
||||||
}
|
}
|
||||||
HRESULT hRet = DS_OK;
|
HRESULT hRet = DS_OK;
|
||||||
if (g_pDSoundPrimaryBuffer == pDSBuffer) {
|
if (g_pDSoundPrimaryBuffer == pDSBuffer) {
|
||||||
|
|
|
@ -230,11 +230,11 @@ HRESULT WINAPI XTL::EMUPATCH(DirectSoundCreateStream)
|
||||||
DSBufferDesc.dwFlags |= DSBCAPS_CTRLPAN;
|
DSBufferDesc.dwFlags |= DSBCAPS_CTRLPAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
DSoundBufferSetDefault((*ppStream), DSBPLAY_LOOPING);
|
DSoundBufferSetDefault((*ppStream), DSBPLAY_LOOPING, pdssd->dwFlags);
|
||||||
(*ppStream)->Xb_rtFlushEx = 0LL;
|
(*ppStream)->Xb_rtFlushEx = 0LL;
|
||||||
|
|
||||||
// We have to set DSBufferDesc last due to EmuFlags must be either 0 or previously written value to preserve other flags.
|
// We have to set DSBufferDesc last due to EmuFlags must be either 0 or previously written value to preserve other flags.
|
||||||
GeneratePCMFormat(DSBufferDesc, pdssd->lpwfxFormat, (*ppStream)->EmuFlags, 0,
|
GeneratePCMFormat(DSBufferDesc, pdssd->lpwfxFormat, pdssd->dwFlags, (*ppStream)->EmuFlags, 0,
|
||||||
xbnullptr, (*ppStream)->X_BufferCacheSize, (*ppStream)->Xb_VoiceProperties, pdssd->lpMixBinsOutput);
|
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 allow stream to work until
|
// Test case: Star Wars: KotOR has one packet greater than 5 seconds worth. Increasing to 10 seconds allow stream to work until
|
||||||
|
@ -896,10 +896,11 @@ HRESULT WINAPI XTL::EMUPATCH(CDirectSoundStream_SetFormat)
|
||||||
|
|
||||||
while (DSStream_Packet_Flush(pThis));
|
while (DSStream_Packet_Flush(pThis));
|
||||||
|
|
||||||
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat, pThis->EmuBufferDesc,
|
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat, pThis->Xb_Flags,
|
||||||
pThis->EmuFlags, pThis->EmuPlayFlags, pThis->EmuDirectSound3DBuffer8,
|
pThis->EmuBufferDesc, pThis->EmuFlags, pThis->EmuPlayFlags,
|
||||||
0, pThis->X_BufferCache, pThis->X_BufferCacheSize,
|
pThis->EmuDirectSound3DBuffer8, 0, pThis->X_BufferCache,
|
||||||
pThis->Xb_VoiceProperties, xbnullptr, pThis->Xb_Frequency);
|
pThis->X_BufferCacheSize, pThis->Xb_VoiceProperties,
|
||||||
|
xbnullptr, pThis->Xb_Frequency);
|
||||||
|
|
||||||
return hRet;
|
return hRet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
// ******************************************************************
|
||||||
|
// *
|
||||||
|
// * This file is part of the Cxbx project.
|
||||||
|
// *
|
||||||
|
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||||
|
// * and/or modify them under the terms of the GNU General Public
|
||||||
|
// * License as published by the Free Software Foundation; either
|
||||||
|
// * version 2 of the license, or (at your option) any later version.
|
||||||
|
// *
|
||||||
|
// * This program is distributed in the hope that it will be useful,
|
||||||
|
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// * GNU General Public License for more details.
|
||||||
|
// *
|
||||||
|
// * You should have recieved a copy of the GNU General Public License
|
||||||
|
// * along with this program; see the file COPYING.
|
||||||
|
// * If not, write to the Free Software Foundation, Inc.,
|
||||||
|
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||||
|
// *
|
||||||
|
// * (c) 2017-2020 RadWolfie
|
||||||
|
// *
|
||||||
|
// * All rights reserved
|
||||||
|
// *
|
||||||
|
// ******************************************************************
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <mmreg.h>
|
||||||
|
#include "core/kernel/init/CxbxKrnl.h"
|
||||||
|
#include "core/hle/DSOUND/XbDSoundTypes.h"
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
|
#include "core/hle/DSOUND/DirectSound/DirectSoundLogging.hpp"
|
||||||
|
#include "core/hle/DSOUND/XbDSoundLogging.hpp"
|
||||||
|
|
||||||
|
// Only use as last resort instead of alternative duplicates (an error occur between keyboard and chair)
|
||||||
|
static void WFXformat_GeneratePCMFormat(
|
||||||
|
uint16_t nChannels,
|
||||||
|
uint32_t nSamplesPerSec,
|
||||||
|
uint16_t wBitPerSample,
|
||||||
|
PWAVEFORMATEXTENSIBLE Host_wfxFormat)
|
||||||
|
{
|
||||||
|
Host_wfxFormat->Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
Host_wfxFormat->Format.nChannels = nChannels;
|
||||||
|
Host_wfxFormat->Format.nSamplesPerSec = nSamplesPerSec;
|
||||||
|
Host_wfxFormat->Format.wBitsPerSample = wBitPerSample;
|
||||||
|
Host_wfxFormat->Format.nBlockAlign = wBitPerSample / 8 * nChannels;
|
||||||
|
Host_wfxFormat->Format.nAvgBytesPerSec = nSamplesPerSec * Host_wfxFormat->Format.nBlockAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WFXformat_GenerateXADPCMFormat(
|
||||||
|
uint16_t nChannels,
|
||||||
|
uint32_t nSamplesPerSec,
|
||||||
|
PWAVEFORMATEXTENSIBLE Host_pwfxFormat)
|
||||||
|
{
|
||||||
|
Host_pwfxFormat->Format.nChannels = nChannels;
|
||||||
|
Host_pwfxFormat->Format.nSamplesPerSec = nSamplesPerSec;
|
||||||
|
Host_pwfxFormat->Format.wBitsPerSample = 4; // Always 4 (even if they're mono or stereo)
|
||||||
|
Host_pwfxFormat->Format.nBlockAlign = 36 * nChannels;
|
||||||
|
// nAvgBytesPerSec's equation is confirmed.
|
||||||
|
Host_pwfxFormat->Format.nAvgBytesPerSec = (nSamplesPerSec / 64/*Always 64 samples per block*/) * Host_pwfxFormat->Format.nBlockAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert XADPCM to PCM format helper function
|
||||||
|
static void XADPCM2PCMFormat(PWAVEFORMATEXTENSIBLE Host_pwfxFormat)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if 0 //For testing purpose if XADPCM to PCM is not accurate.
|
||||||
|
EmuLog(LOG_LEVEL::DEBUG,
|
||||||
|
"EmuDSound: XADPCM WAVEFORMATEX\n"
|
||||||
|
"{\n"
|
||||||
|
" wFormatTag : 0x%.04hX\n"
|
||||||
|
" nChannels : 0x%.02hd\n"
|
||||||
|
" nSamplesPerSec : 0x%.08X\n"
|
||||||
|
" nAvgBytesPerSec : 0x%.08X\n"
|
||||||
|
" nBlockAlign : 0x%.02hd\n"
|
||||||
|
" wBitsPerSample : 0x%.04hX\n"
|
||||||
|
" cbSize : 0x%.04hX\n"
|
||||||
|
"}\n",
|
||||||
|
Host_pwfxFormat->Format.wFormatTag,
|
||||||
|
Host_pwfxFormat->Format.nChannels,
|
||||||
|
Host_pwfxFormat->Format.nSamplesPerSec,
|
||||||
|
Host_pwfxFormat->Format.nAvgBytesPerSec,
|
||||||
|
Host_pwfxFormat->Format.nBlockAlign,
|
||||||
|
Host_pwfxFormat->Format.wBitsPerSample,
|
||||||
|
Host_pwfxFormat->Format.cbSize);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Xb_lpwfxFormat.wFormatTag; /* format type */
|
||||||
|
//Xb_lpwfxFormat.nChannels; /* number of channels (i.e. mono, stereo...) */ NO CHANGE
|
||||||
|
//Xb_lpwfxFormat.nSamplesPerSec; /* sample rate */ NO CHANGE
|
||||||
|
//Xb_lpwfxFormat.nAvgBytesPerSec; /* for buffer estimation */
|
||||||
|
//Xb_lpwfxFormat.nBlockAlign; /* block size of data */
|
||||||
|
//Xb_lpwfxFormat.wBitsPerSample; /* number of bits per sample of mono data */
|
||||||
|
//Xb_lpwfxFormat.cbSize; /* the count in bytes of the size of extra information (after cbSize) */
|
||||||
|
WFXformat_GeneratePCMFormat(Host_pwfxFormat->Format.nChannels,
|
||||||
|
Host_pwfxFormat->Format.nSamplesPerSec,
|
||||||
|
16,
|
||||||
|
Host_pwfxFormat);
|
||||||
|
//Enable this only if you have Xbox ADPCM Codec installed on your PC, or else it will fail every time.
|
||||||
|
//This is just to verify format conversion is correct or not.
|
||||||
|
#if 0
|
||||||
|
if (waveOutOpen(nullptr, WAVE_MAPPER, Xb_lpwfxFormat, NULL, NULL, WAVE_FORMAT_QUERY) != MMSYSERR_NOERROR) {
|
||||||
|
return DSERR_BADFORMAT;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0 //For testing purpose if XADPCM to PCM is not accurate.
|
||||||
|
EmuLog(LOG_LEVEL::DEBUG,
|
||||||
|
"EmuDSound: Converted to PCM WAVEFORMATEX\n"
|
||||||
|
"{\n"
|
||||||
|
" wFormatTag : 0x%.04hX\n"
|
||||||
|
" nChannels : 0x%.02hd\n"
|
||||||
|
" nSamplesPerSec : 0x%.08X\n"
|
||||||
|
" nAvgBytesPerSec : 0x%.08X\n"
|
||||||
|
" nBlockAlign : 0x%.02hd\n"
|
||||||
|
" wBitsPerSample : 0x%.04hX\n"
|
||||||
|
" cbSize : 0x%.04hX\n"
|
||||||
|
"}\n",
|
||||||
|
Host_pwfxFormat->Format.wFormatTag,
|
||||||
|
Host_pwfxFormat->Format.nChannels,
|
||||||
|
Host_pwfxFormat->Format.nSamplesPerSec,
|
||||||
|
Host_pwfxFormat->Format.nAvgBytesPerSec,
|
||||||
|
Host_pwfxFormat->Format.nBlockAlign,
|
||||||
|
Host_pwfxFormat->Format.wBitsPerSample,
|
||||||
|
Host_pwfxFormat->Format.cbSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WFXformat_SyncPCMFormat(
|
||||||
|
PWAVEFORMATEXTENSIBLE Host_pwfxFormat)
|
||||||
|
{
|
||||||
|
PWAVEFORMATEX ex_pwfxFormat = &Host_pwfxFormat->Format;
|
||||||
|
// NOTE: This process check for 2+ channels whenever XADPCM or PCM format from xbox titles input in nChannels.
|
||||||
|
// Since the host do not support 2+ channels on PCM only format. We must convert/update allocated wfxFormat to EXTENSIBLE
|
||||||
|
// format which had been allocated enough to update.
|
||||||
|
if (ex_pwfxFormat->nChannels > 2) {
|
||||||
|
Host_pwfxFormat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
|
Host_pwfxFormat->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||||
|
Host_pwfxFormat->Samples.wValidBitsPerSample = ex_pwfxFormat->wBitsPerSample;
|
||||||
|
Host_pwfxFormat->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
|
Host_pwfxFormat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
|
|
||||||
|
if (ex_pwfxFormat->nChannels >= 4) {
|
||||||
|
Host_pwfxFormat->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||||
|
}
|
||||||
|
if (ex_pwfxFormat->nChannels == 6) {
|
||||||
|
Host_pwfxFormat->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Host_pwfxFormat->Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
// Test case: Hulk crash due to cbSize is not a valid size.
|
||||||
|
// If Xbox applications didn't enfore cbSize, we'll have to enforce from host side.
|
||||||
|
// Also, we're enforcing it anyway.
|
||||||
|
Host_pwfxFormat->Format.cbSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WFXformat_SyncXADPCMFormat(
|
||||||
|
PWAVEFORMATEXTENSIBLE Host_wfxFormat)
|
||||||
|
{
|
||||||
|
XADPCM2PCMFormat(Host_wfxFormat);
|
||||||
|
WFXformat_SyncPCMFormat(Host_wfxFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum _CODEC_FORMAT {
|
||||||
|
CF_PCM=0,
|
||||||
|
CF_XADPCM,
|
||||||
|
CF_UNKNOWN
|
||||||
|
} CODEC_FORMAT;
|
||||||
|
|
||||||
|
static void WFXformat_SanityFix(
|
||||||
|
PWAVEFORMATEXTENSIBLE Host_pwfxFormat,
|
||||||
|
CODEC_FORMAT codec_format,
|
||||||
|
uint32_t Xb_buffer_request_size,
|
||||||
|
uint32_t Xb_flags)
|
||||||
|
{
|
||||||
|
WAVEFORMATEXTENSIBLE before_wfxFormat = *Host_pwfxFormat;
|
||||||
|
bool isNotSanity = false;
|
||||||
|
uint32_t nSamplesPerSec = Host_pwfxFormat->Format.nSamplesPerSec;
|
||||||
|
|
||||||
|
// Generic enforcement
|
||||||
|
// If Xbox applications supply invalid total channels, enforce to use either mono or stereo channel
|
||||||
|
if (Host_pwfxFormat->Format.nChannels == 0 || Host_pwfxFormat->Format.nChannels > 6) {
|
||||||
|
Host_pwfxFormat->Format.nChannels = 2;
|
||||||
|
isNotSanity = true;
|
||||||
|
}
|
||||||
|
// If nSamplesPerSec is zero'd then use 44.1kHz by default
|
||||||
|
if (nSamplesPerSec == 0) {
|
||||||
|
Host_pwfxFormat->Format.nSamplesPerSec = 44100;
|
||||||
|
isNotSanity = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Host_pwfxFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||||
|
if (Host_pwfxFormat->Format.cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) {
|
||||||
|
Host_pwfxFormat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
|
//isNotSanity = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Test case: Hulk crash due to cbSize is not a valid size.
|
||||||
|
// If Xbox applications didn't enfore cbSize, we'll have to enforce from host side.
|
||||||
|
/* NOTE: cbSize is already handled in WFXformat_SyncPCMFormat function as enforcement.
|
||||||
|
if (Host_pwfxFormat->Format.cbSize != 0) {
|
||||||
|
Host_pwfxFormat->Format.cbSize = 0;
|
||||||
|
//isNotSanity = true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic handler
|
||||||
|
if (codec_format == CF_PCM || codec_format == CF_UNKNOWN) {
|
||||||
|
// If wBitsPerSample is zero'd then use 16 by default
|
||||||
|
if (Host_pwfxFormat->Format.wBitsPerSample == 0) {
|
||||||
|
isNotSanity = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// testcase: Phantasy Star Online Episode I & II made an attempt to use avg byte/second below sample/second requirement.
|
||||||
|
// Defender is another title made an attempt to use avg byte/second way above the sample/second requirement.
|
||||||
|
// In other word, this is a workaround to fix titles' mistake...
|
||||||
|
uint32_t checkAvgBps = Host_pwfxFormat->Format.nSamplesPerSec * Host_pwfxFormat->Format.nBlockAlign;
|
||||||
|
|
||||||
|
if (isNotSanity) {
|
||||||
|
WFXformat_GeneratePCMFormat(Host_pwfxFormat->Format.nChannels, Host_pwfxFormat->Format.nSamplesPerSec, 16, Host_pwfxFormat);
|
||||||
|
}
|
||||||
|
else if (Host_pwfxFormat->Format.nAvgBytesPerSec != checkAvgBps) {
|
||||||
|
Host_pwfxFormat->Format.nAvgBytesPerSec = checkAvgBps;
|
||||||
|
isNotSanity = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CF_XADPCM
|
||||||
|
else if (codec_format == CF_XADPCM) {
|
||||||
|
// If wBitsPerSample is not equal to 4, then wfx format is not sane.
|
||||||
|
if (Host_pwfxFormat->Format.wBitsPerSample != 4) {
|
||||||
|
//Host_pwfxFormat->Format.wBitsPerSample = 4;
|
||||||
|
isNotSanity = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotSanity) {
|
||||||
|
WFXformat_GenerateXADPCMFormat(Host_pwfxFormat->Format.nChannels, Host_pwfxFormat->Format.nSamplesPerSec, Host_pwfxFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotSanity) {
|
||||||
|
std::stringstream before, after;
|
||||||
|
before << before_wfxFormat;
|
||||||
|
after << Host_pwfxFormat;
|
||||||
|
EmuLog(LOG_LEVEL::WARNING, "One or more values were not set properly before: %s\nvs after: %s", before.str().c_str(), after.str().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CODEC_FORMAT WFXformat_SyncHostFormat(
|
||||||
|
void* Host_wfx_ptr,
|
||||||
|
const void* Xb_wfx_ptr,
|
||||||
|
uint32_t Xb_buffer_request_size,
|
||||||
|
uint32_t Xb_flags)
|
||||||
|
{
|
||||||
|
PWAVEFORMATEXTENSIBLE Xb_wfxFormat = (PWAVEFORMATEXTENSIBLE)Xb_wfx_ptr;
|
||||||
|
PWAVEFORMATEXTENSIBLE Host_wfxFormat = (PWAVEFORMATEXTENSIBLE)Host_wfx_ptr;
|
||||||
|
CODEC_FORMAT codec_format_ret = CF_PCM;
|
||||||
|
bool require_validate = true;
|
||||||
|
|
||||||
|
// If no format is provided, then use default.
|
||||||
|
if (Xb_wfx_ptr == xbnullptr) {
|
||||||
|
WFXformat_GeneratePCMFormat(2, 44100, 16, Host_wfxFormat);
|
||||||
|
require_validate = false;
|
||||||
|
}
|
||||||
|
// HACK: This is a special sound buffer, create dummy WAVEFORMATEX data.
|
||||||
|
// It's supposed to recieve data rather than generate it. Buffers created
|
||||||
|
// with flags DSBCAPS_MIXIN, DSBCAPS_FXIN, and DSBCAPS_FXIN2 will have no
|
||||||
|
// WAVEFORMATEX structure by default.
|
||||||
|
else if ((Xb_flags & (XTL_DSBCAPS_MIXIN | XTL_DSBCAPS_FXIN | XTL_DSBCAPS_FXIN2)) > 0) {
|
||||||
|
EmuLog(LOG_LEVEL::WARNING, "Creating dummy WAVEFORMATEX (pdsbd->Xb_lpwfxFormat = xbnullptr)...");
|
||||||
|
WFXformat_GeneratePCMFormat(2, 44100, 16, Host_wfxFormat);
|
||||||
|
require_validate = false;
|
||||||
|
}
|
||||||
|
// Otherwise, let's process given format.
|
||||||
|
else {
|
||||||
|
switch (Xb_wfxFormat->Format.wFormatTag) {
|
||||||
|
case WAVE_FORMAT_PCM:
|
||||||
|
// No further steps require here.
|
||||||
|
break;
|
||||||
|
case WAVE_FORMAT_XBOX_ADPCM:
|
||||||
|
codec_format_ret = CF_XADPCM;
|
||||||
|
break;
|
||||||
|
case WAVE_FORMAT_EXTENSIBLE:
|
||||||
|
if (Xb_wfxFormat->SubFormat.Data1 = WAVE_FORMAT_PCM) {
|
||||||
|
// No further steps require here.
|
||||||
|
}
|
||||||
|
else if (Xb_wfxFormat->SubFormat.Data1 == WAVE_FORMAT_XBOX_ADPCM) {
|
||||||
|
codec_format_ret = CF_XADPCM;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
codec_format_ret = CF_UNKNOWN;
|
||||||
|
require_validate = false;
|
||||||
|
std::stringstream oss;
|
||||||
|
oss << Xb_wfxFormat->SubFormat;
|
||||||
|
std::string msg = "Please report unknown extensible format : " + oss.str();
|
||||||
|
LOG_TEST_CASE(msg.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// Both 0 and default will use static structure until given a valid one.
|
||||||
|
case 0:
|
||||||
|
// NOTE: This is currently a hack for ability to create buffer class with DSBCAPS_LOCDEFER flag.
|
||||||
|
WFXformat_GeneratePCMFormat(2, 44100, 16, Host_wfxFormat);
|
||||||
|
require_validate = false;
|
||||||
|
LOG_TEST_CASE("WAVE_FORMAT_(0) found");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
codec_format_ret = CF_UNKNOWN;
|
||||||
|
require_validate = false;
|
||||||
|
EmuLog(LOG_LEVEL::WARNING, "Unknown Xbox format: %08X", Xb_wfxFormat->Format.wFormatTag);
|
||||||
|
LOG_TEST_CASE("Unknown Xbox format, check your log output");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require_validate) {
|
||||||
|
// First, perform sanity fix
|
||||||
|
WFXformat_SanityFix(Host_wfxFormat, codec_format_ret, Xb_buffer_request_size, Xb_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then re-convert to Host's PCM format.
|
||||||
|
if (codec_format_ret == CF_PCM) {
|
||||||
|
WFXformat_SyncPCMFormat(Host_wfxFormat);
|
||||||
|
}
|
||||||
|
else if (codec_format_ret == CF_XADPCM) {
|
||||||
|
WFXformat_SyncXADPCMFormat(Host_wfxFormat);
|
||||||
|
}
|
||||||
|
// Any unknown formats will be using default PCM format.
|
||||||
|
else {
|
||||||
|
WFXformat_GeneratePCMFormat(2, 44100, 16, Host_wfxFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codec_format_ret;
|
||||||
|
}
|
Loading…
Reference in New Issue