Merge pull request #1835 from RadWolfie/fix-wfx-format-sanity

Fix Sanity and Organize wfxformat into Functions
This commit is contained in:
Luke Usher 2020-02-26 06:49:59 +00:00 committed by GitHub
commit 951520ece1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 388 additions and 238 deletions

View File

@ -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/XbVertexBuffer.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/DirectSoundGlobal.hpp"
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/DirectSound/DirectSoundInline.hpp"

View File

@ -109,6 +109,7 @@ struct X_CDirectSoundBuffer
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
X_DSVOICEPROPS Xb_VoiceProperties;
DWORD Xb_Frequency;
DWORD Xb_Flags;
};
//Custom flags (4 bytes support up to 31 shifts,starting from 0)
@ -263,7 +264,8 @@ class X_CDirectSoundStream
X_DSENVOLOPEDESC Xb_EnvolopeDesc;
X_DSVOICEPROPS Xb_VoiceProperties;
DWORD Xb_Frequency;
DWORD Host_dwLastWritePos;
DWORD Host_dwLastWritePos;
DWORD Xb_Flags;
};
// ******************************************************************

View File

@ -205,14 +205,14 @@ HRESULT WINAPI XTL::EMUPATCH(DirectSoundCreateBuffer)
// TODO: Garbage Collection
*ppBuffer = new X_CDirectSoundBuffer();
DSoundBufferSetDefault((*ppBuffer), 0);
DSoundBufferSetDefault((*ppBuffer), 0, pdsbd->dwFlags);
(*ppBuffer)->Host_lock = { 0 };
(*ppBuffer)->Xb_rtStopEx = 0LL;
DSoundBufferRegionSetDefault(*ppBuffer);
// 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)->EmuBufferDesc = DSBufferDesc;
@ -929,8 +929,8 @@ HRESULT WINAPI XTL::EMUPATCH(IDirectSoundBuffer_SetFormat)
LOG_FUNC_ARG(pwfxFormat)
LOG_FUNC_END;
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat,
pThis->EmuBufferDesc, pThis->EmuFlags,
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat, pThis->Xb_Flags,
pThis->EmuBufferDesc, pThis->EmuFlags,
pThis->EmuPlayFlags, pThis->EmuDirectSound3DBuffer8,
0, pThis->X_BufferCache, pThis->X_BufferCacheSize,
pThis->Xb_VoiceProperties, xbnullptr, pThis->Xb_Frequency);

View File

@ -30,6 +30,7 @@
#include "common/XADPCM.h"
#include "core/hle/DSOUND/XbDSoundTypes.h"
#include "core/hle/DSOUND/common/windows/WFXformat.hpp"
#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(
DSBUFFERDESC &DSBufferDesc,
DWORD &dwEmuFlags,
@ -312,7 +244,8 @@ static inline void GenerateMixBinDefault(
static inline void GeneratePCMFormat(
DSBUFFERDESC &DSBufferDesc,
LPCWAVEFORMATEX lpwfxFormat,
LPCWAVEFORMATEX Xb_lpwfxFormat,
DWORD &Xb_flags,
DWORD &dwEmuFlags,
DWORD X_BufferSizeRequest,
LPVOID* X_BufferCache,
@ -321,186 +254,61 @@ static inline void GeneratePCMFormat(
XTL::X_LPDSMIXBINS mixbins_output)
{
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
{
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
// Do we need to convert it? Or just do the WAVEFORMATEX only?
if (DSBufferDesc.lpwfxFormat == nullptr) {
CxbxKrnlCleanup("Unable to allocate DSBufferDesc.Xb_lpwfxFormat");
}
// NOTE: pwfxFormat is not always a WAVEFORMATEX structure, it can
// be WAVEFORMATEXTENSIBLE if that's what the programmer(s) wanted
// in the first place, FYI.
if (Xb_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));
}
WAVEFORMATEX* lpwfxFormatHost = DSBufferDesc.lpwfxFormat;
PWAVEFORMATEXTENSIBLE lpwfxFormatHost = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(DSBufferDesc.lpwfxFormat);
if (lpwfxFormat->wFormatTag == WAVE_FORMAT_PCM) {
// Test case: Hulk crash due to cbSize is not a valid size.
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;
if (Xb_lpwfxFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
memcpy(lpwfxFormatHost, Xb_lpwfxFormat, sizeof(WAVEFORMATEX) + Xb_lpwfxFormat->cbSize);
}
else {
memcpy(lpwfxFormatHost, lpwfxFormat, sizeof(WAVEFORMATEX) + lpwfxFormat->cbSize);
}
// 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?");
}
// 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;
}
dwEmuFlags = dwEmuFlags & ~DSE_FLAG_AUDIO_CODECS;
switch (DSBufferDesc.lpwfxFormat->wFormatTag) {
case WAVE_FORMAT_PCM:
CODEC_FORMAT cf_audio = WFXformat_SyncHostFormat(DSBufferDesc.lpwfxFormat, Xb_lpwfxFormat, X_BufferSizeRequest, Xb_flags);
if (cf_audio == CF_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 {
bIsSpecial = true;
dwEmuFlags |= DSE_FLAG_RECIEVEDATA;
EmuLog(LOG_LEVEL::WARNING, "Creating dummy WAVEFORMATEX (pdsbd->lpwfxFormat = xbnullptr)...");
// 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;
(void)WFXformat_SyncHostFormat(DSBufferDesc.lpwfxFormat, Xb_lpwfxFormat, X_BufferSizeRequest, Xb_flags);
DSBufferDesc.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
DSBufferDesc.dwBufferBytes = 3 * 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.dwBufferBytes = 5 * DSBufferDesc.lpwfxFormat->nAvgBytesPerSec;
}
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) {
X_BufferSizeRequest = DSBSIZE_MIN;
} 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->EmuDirectSound3DBuffer8 = nullptr; \
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_EnvolopeDesc = { 0 }; \
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->EmuLockPtr1 = xbnullptr; \
@ -1259,7 +1068,8 @@ static inline HRESULT HybridDirectSoundBuffer_SetFilter(
//IDirectSoundBuffer
static inline HRESULT HybridDirectSoundBuffer_SetFormat(
LPDIRECTSOUNDBUFFER8 &pDSBuffer,
LPCWAVEFORMATEX pwfxFormat,
LPCWAVEFORMATEX Xb_pwfxFormat,
DWORD Xb_flags,
DSBUFFERDESC &BufferDesc,
DWORD &dwEmuFlags,
DWORD &dwPlayFlags,
@ -1274,10 +1084,10 @@ static inline HRESULT HybridDirectSoundBuffer_SetFormat(
pDSBuffer->Stop();
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.
} 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;
if (g_pDSoundPrimaryBuffer == pDSBuffer) {

View File

@ -230,11 +230,11 @@ HRESULT WINAPI XTL::EMUPATCH(DirectSoundCreateStream)
DSBufferDesc.dwFlags |= DSBCAPS_CTRLPAN;
}
DSoundBufferSetDefault((*ppStream), DSBPLAY_LOOPING);
DSoundBufferSetDefault((*ppStream), DSBPLAY_LOOPING, pdssd->dwFlags);
(*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.
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);
// 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));
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat, pThis->EmuBufferDesc,
pThis->EmuFlags, pThis->EmuPlayFlags, pThis->EmuDirectSound3DBuffer8,
0, pThis->X_BufferCache, pThis->X_BufferCacheSize,
pThis->Xb_VoiceProperties, xbnullptr, pThis->Xb_Frequency);
HRESULT hRet = HybridDirectSoundBuffer_SetFormat(pThis->EmuDirectSoundBuffer8, pwfxFormat, pThis->Xb_Flags,
pThis->EmuBufferDesc, pThis->EmuFlags, pThis->EmuPlayFlags,
pThis->EmuDirectSound3DBuffer8, 0, pThis->X_BufferCache,
pThis->X_BufferCacheSize, pThis->Xb_VoiceProperties,
xbnullptr, pThis->Xb_Frequency);
return hRet;
}

View File

@ -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;
}