MuckyFoot-UrbanChaos/fallen/DDLibrary/Source/DCLowLevel.cpp
2017-05-20 11:14:17 +10:00

2711 lines
54 KiB
C++

// ========================================================
//
// THIS IS RIPPED FROM THE DC EXAMPLE CODE
//
// ========================================================
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Copyright (c) 1996, 1997 Microsoft Corporation
Module Name:
DSUtil.cpp
Abstract:
Contains routines for handling sounds from resources
-------------------------------------------------------------------*/
#include <tchar.h>
#include <windows.h>
#include "MFStdLib.h"
#include <dsound.h>
#include <math.h>
#ifdef TARGET_DC
#include <floatmathlib.h>
// On DC, it's called "pows" for some reason ("s" = "single"?)
#define powf pows
#endif
// ++++ Global Variables ++++++++++++++++++++++++++++++++++++++++++++
LPDIRECTSOUND g_pds = NULL; // The DirectSound object
LPDIRECTSOUND3DLISTENER g_pds3dl = NULL;
LPDIRECTSOUNDBUFFER g_pdsbPrimary = NULL;
HRESULT g_errLast;
#define CheckError(anything) (FALSE)
#ifdef TARGET_DC
// Stream Yamaha ADPCM, not normal PCM.
#define STREAMING_BUFFER_IS_ADPCM defined
// Pants hack for the ADPCM buffer.
//#define CRAPPY_STREAMING_ADPCM_HACK defined
#endif
//extern HWND g_hwndApp;
extern volatile HWND hDDLibWindow;
bool m_bSoundHasActuallyStartedPlaying = FALSE;
#define DCLL_OUTPUT_STEREO_WAVS
#ifdef DCLL_OUTPUT_STEREO_WAVS
FILE *DCLL_handle;
#endif
#ifdef DEBUG
#define SHARON TRACE
//#define SHARON sizeof
#else
#define SHARON sizeof
#endif
inline DWORD ReadNonalignedDword ( void *addr )
{
DWORD res;
unsigned char *myaddr = (unsigned char *)addr;
res = ( ( *myaddr++ ) & 0xff );
res |= ( ( *myaddr++ ) & 0xff ) << 8;
res |= ( ( *myaddr++ ) & 0xff ) << 16;
res |= ( ( *myaddr++ ) & 0xff ) << 24;
return ( res );
}
inline WORD ReadNonalignedWord ( void *addr )
{
WORD res;
char *myaddr = (char *)addr;
res = ( ( *myaddr++ ) & 0xff );
res |= ( ( *myaddr++ ) & 0xff ) << 8;
return ( res );
}
void *DwordMemcpy ( void *pvDst, const void *pvSrc, size_t dwSize )
{
//TRACE ( "DwordMemcpy arrived with 0x%x, 0x%x, 0x%x\n", pvDst, pvSrc, dwSize );
if ( ( (DWORD)pvSrc & 0x3 ) == 0 )
{
// Source is DWORD-aligned.
const DWORD *pdwSrc = (DWORD *)pvSrc;
DWORD *pdwDst = (DWORD *)pvDst;
ASSERT ( ( (DWORD)pdwSrc & 0x3 ) == 0 );
ASSERT ( ( (DWORD)pdwDst & 0x3 ) == 0 );
ASSERT ( ( dwSize & 0x3 ) == 0 );
dwSize >>= 2;
while ( dwSize > 0 )
{
*pdwDst++ = *pdwSrc++;
dwSize--;
}
}
else
{
#ifdef DEBUG
// Check endianness
DWORD dwTest = 0x12345678;
ASSERT ( *(((char*)&dwTest)+0) == 0x78 );
ASSERT ( *(((char*)&dwTest)+1) == 0x56 );
ASSERT ( *(((char*)&dwTest)+2) == 0x34 );
ASSERT ( *(((char*)&dwTest)+3) == 0x12 );
#endif
// Source is not dword aligned!
const unsigned char *pdwSrc = (unsigned char *)pvSrc;
DWORD *pdwDst = (DWORD *)pvDst;
//ASSERT ( ( (DWORD)pdwSrc & 0x3 ) == 0 );
ASSERT ( ( (DWORD)pdwDst & 0x3 ) == 0 );
ASSERT ( ( dwSize & 0x3 ) == 0 );
dwSize >>= 2;
while ( dwSize > 0 )
{
DWORD dwTemp = (DWORD)*pdwSrc++;
dwTemp |= ( (DWORD)(*pdwSrc++) ) << 8;
dwTemp |= ( (DWORD)(*pdwSrc++) ) << 16;
dwTemp |= ( (DWORD)(*pdwSrc++) ) << 24;
*pdwDst++ = dwTemp;
dwSize--;
}
}
return pvDst;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:
InitDirectSound
Description:
Initialize the DirectSound object
Arguments:
None
Return Value:
TRUE on success, FALSE on failure.
-------------------------------------------------------------------*/
BOOL
InitDirectSound()
{
// Create the DirectSound object
g_errLast = DirectSoundCreate(NULL, &g_pds, NULL);
if (CheckError(TEXT("DirectSoundCreate")))
return FALSE;
// Set the DirectSound cooperative level
if (g_pds->SetCooperativeLevel(hDDLibWindow, DSSCL_NORMAL) != DS_OK)
{
ASSERT(0);
}
return TRUE;
}
BOOL
InitDirectSound3D()
{
DSBUFFERDESC dsbd;
// Create the primary sound buffer (needed for the listener interface)
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
g_errLast = g_pds->CreateSoundBuffer(&dsbd, &g_pdsbPrimary, NULL);
if (CheckError(TEXT("Create SoundBuffer")))
return FALSE;
// Get a pointer to the IDirectSound3DListener interface
g_errLast = g_pdsbPrimary->QueryInterface(IID_IDirectSound3DListener, (void **)&g_pds3dl);
if (CheckError(TEXT("QueryInterface for IDirectSound3DListener interface")))
return FALSE;
//
// How many metres in an UC block? 256 => 2 metres?
//
g_pds3dl->SetDistanceFactor(0.25F / 256.0F, DS3D_IMMEDIATE);
// We no longer need the primary buffer, just the Listener interface
// g_pdsbPrimary->Release();
// Set the doppler factor to the maximum, so we can more easily notice it
// g_errLast = g_pds3dl->SetDopplerFactor(DS3D_MAXDOPPLERFACTOR, DS3D_IMMEDIATE);
//
// Set the primary buffer playing all the time to avoid nasty clicks...
//
g_pdsbPrimary->Play(0,0,0);
return TRUE;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:
ParseWaveFile
Description:
Get the Wave File header, size, and data pointer...
Arguments:
void *pvWaveFile - Pointer to the wav file to parse
WAVEFORMATEX **ppWaveHeader - Fill this with pointer to wave header
BYTE **ppbWaveData - Fill this with pointer to wave data
DWORD **pcbWaveSize - Fill this with wave data size.
Return Value:
TRUE on success, FALSE on failure.
-------------------------------------------------------------------*/
BOOL
ParseWaveFile(void *pvWaveFile, WAVEFORMATEX **ppWaveHeader, BYTE **ppbWaveData, DWORD *pcbWaveSize)
{
DWORD *pdw;
DWORD *pdwEnd;
DWORD dwRiff;
DWORD dwType;
DWORD dwLength;
if (ppWaveHeader)
*ppWaveHeader = NULL;
if (ppbWaveData)
*ppbWaveData = NULL;
if (pcbWaveSize)
*pcbWaveSize = 0;
pdw = (DWORD *)pvWaveFile;
dwRiff = *pdw++;
dwLength = *pdw++;
dwType = *pdw++;
// Check if it's a WAV format file
if (dwType != mmioFOURCC('W', 'A', 'V', 'E'))
{
ASSERT ( FALSE );
return FALSE;
}
// Check if it's a RIFF format file
if (dwRiff != mmioFOURCC('R', 'I', 'F', 'F'))
{
ASSERT ( FALSE );
return FALSE;
}
pdwEnd = (DWORD *)((BYTE *)pdw + dwLength-4);
while (pdw < pdwEnd)
{
#if 0
dwType = *pdw++;
dwLength = *pdw++;
#else
dwType = ReadNonalignedDword ( pdw );
pdw++;
dwLength = ReadNonalignedDword ( pdw );
pdw++;
#endif
switch (dwType)
{
case mmioFOURCC('f', 'm', 't', ' '):
if (ppWaveHeader && !*ppWaveHeader)
{
if (dwLength < sizeof(WAVEFORMAT))
return FALSE;
*ppWaveHeader = (WAVEFORMATEX *)pdw;
if ((!ppbWaveData || *ppbWaveData) && (!pcbWaveSize || *pcbWaveSize))
return TRUE;
}
break;
case mmioFOURCC('d', 'a', 't', 'a'):
if ((ppbWaveData && !*ppbWaveData) || (pcbWaveSize && !*pcbWaveSize))
{
if (ppbWaveData)
*ppbWaveData = (LPBYTE)pdw;
if (pcbWaveSize)
*pcbWaveSize = dwLength;
if (!ppWaveHeader || *ppWaveHeader)
return TRUE;
}
break;
default:
ASSERT ( FALSE );
return FALSE;
break;
}
pdw = (DWORD *)((BYTE *)pdw + ((dwLength+1)&~1));
}
return FALSE;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:
GetWaveResource
Description:
Load a WAV file from the executable's Resource file or the specified file.
Arguments:
LPCTSTR tszName - Name of the WAV file to load
WAVEFORMATEX **ppWaveHeader - Fill this with pointer to wave header
BYTE **ppbWaveData - Fill this with pointer to wave data
DWORD **pcbWaveSize - Fill this with wave data size.
Return Value:
TRUE on success, FALSE on failure.
NOTE! Return values of **ppWaveHeader, **ppbWaveData, **pcbWaveSize
are owned by this rout. Do not do anything permanent to them, coz
the memory blocks will be frazzed next time this is called.
-------------------------------------------------------------------*/
// For some bizarre reason, they keep failing every now and then.
#define USE_ALIGNED_LOADS 0
static DWORD dwSizeOfGWRBlock = 0;
static void *pvGWRBlock = NULL;
BOOL
GetWaveResource(char *szName, WAVEFORMATEX **ppWaveHeader,
BYTE **ppbWaveData, DWORD *pcbWaveSize)
{
//HRSRC hResInfo;
//HGLOBAL hResData;
//void *pvRes;
MFFileHandle hFile;
//static BYTE *rgbyFileTemp = NULL;
unsigned long cbRead;
DWORD dwSize;
/*
// Find the specifed WAV resource
hResInfo = FindResource(g_hinst, tszName, TEXT("WAV"));
if (hResInfo == NULL)
goto TryFile;
// Load the Resource
hResData = LoadResource(g_hinst, hResInfo);
if (hResData == NULL)
goto TryFile;
// Lock the Resource
pvRes = LockResource(hResData);
if (pvRes == NULL)
goto TryFile;
// Read and parse the Resource
if (ParseWaveFile(pvRes, ppWaveHeader, ppbWaveData, pcbWaveSize) != NULL)
return TRUE;
*/
//TryFile:
#ifdef TARGET_DC
// GDWorkshop converts any non-alphanumerics to "_"
char chTemp[128];
char *pchSrc = szName;
char *pchDst = chTemp;
while ( TRUE )
{
if ( ( ( *pchSrc >= 'a' ) && ( *pchSrc <= 'z' ) ) ||
( ( *pchSrc >= 'A' ) && ( *pchSrc <= 'Z' ) ) ||
( ( *pchSrc >= '0' ) && ( *pchSrc <= '9' ) ) ||
( *pchSrc == '.' ) ||
( *pchSrc == '\\' )
)
{
*pchDst++ = *pchSrc++;
}
else if ( *pchSrc == '\0' )
{
*pchDst++ = '\0';
break;
}
else
{
*pchDst++ = '_';
pchSrc++;
}
}
szName = chTemp;
#endif
#if 0
#ifdef DEBUG
if ( 0 == stricmp ( szName, "data\\sfx\\1622dc\\heli.wav" ) )
{
// Replace it for the mo.
szName = "data\\sfx\\1622dc\\helifucked64.wav";
}
else if ( 0 == stricmp ( szName, "data\\sfx\\1622dc\\sfx080799_\\search2.wav" ) )
{
// Replace it for the mo.
szName = "data\\sfx\\1622dc\\sfx080799_\\search2fucked32.wav";
}
#endif
#endif
hFile = FileOpen ( szName );
if ( hFile == FILE_OPEN_ERROR )
{
TRACE ( "Failed to load sound %s\n", szName );
return FALSE;
}
dwSize = FileSize(hFile);
#if !USE_ALIGNED_LOADS
// Free memory used in previous call.
if ( pvGWRBlock != NULL )
{
MemFree ( pvGWRBlock );
pvGWRBlock = NULL;
}
pvGWRBlock = (BYTE*) MemAlloc ( dwSize );
if ( !pvGWRBlock )
{
ASSERT ( FALSE );
return FALSE;
}
#else
if ( dwSizeOfGWRBlock < dwSize )
{
if ( pvGWRBlock != NULL )
{
//MemFree ( pvGWRBlock );
VirtualFree ( pvGWRBlock, NULL, MEM_RELEASE );
}
// Grow slightly more than needed to prevent hammering.
dwSizeOfGWRBlock = ( dwSize * 5 / 4 + 10240 );
// Ensure it's 4k-aligned.
dwSizeOfGWRBlock = ( ( dwSizeOfGWRBlock + 4095 ) & ~4095 );
//pvGWRBlock = MemAlloc ( dwSizeOfGWRBlock );
pvGWRBlock = VirtualAlloc ( NULL, dwSizeOfGWRBlock, MEM_COMMIT, PAGE_READWRITE );
ASSERT ( pvGWRBlock != NULL );
}
//rgbyFileTemp = (BYTE *)pvGWRBlock;
#endif
#if !USE_ALIGNED_LOADS
// Load in one go.
cbRead = FileRead ( hFile, pvGWRBlock, dwSize );
#else
// Use DMA load, then finish the rest.
int iAlignedFileSize = dwSize & ( ~4095 );
// DMA read
if ( iAlignedFileSize > 0 )
{
cbRead = FileRead ( hFile, pvGWRBlock, iAlignedFileSize );
}
else
{
cbRead = 0;
}
// Finish off with PIO or whatever.
if ( dwSize - iAlignedFileSize > 0 )
{
cbRead += FileRead ( hFile, (void *)( (char *)pvGWRBlock + iAlignedFileSize ), dwSize - iAlignedFileSize );
}
#endif
FileClose ( hFile );
#if 0
hFile = CreateFile(tszName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
dwSize = GetFileSize(hFile, NULL);
pvGWRBlock = (BYTE*) MemAlloc(dwSize);
if (!pvGWRBlock)
return FALSE;
ReadFile (hFile, pvGWRBlock, dwSize, &cbRead, NULL);
CloseHandle(hFile);
#endif
if (cbRead != dwSize)
{
ASSERT ( FALSE );
#if !USE_ALIGNED_LOADS
//MemFree ( pvGWRBlock );
#endif
return FALSE;
}
if (ParseWaveFile(pvGWRBlock, ppWaveHeader, ppbWaveData, pcbWaveSize) == NULL)
{
#if !USE_ALIGNED_LOADS
//MemFree ( pvGWRBlock );
#endif
return FALSE;
}
// This is NOT freed until next call, since it holds all the data that the pointer all point into.
#if !USE_ALIGNED_LOADS
//MemFree ( pvGWRBlock );
#endif
return TRUE;
}
// Call this when a bunch of sounds have finished loading.
// Can be called whenever you like really.
void DCLL_ProbablyDoneMostOfMySoundLoadingForAWhile ( void )
{
#if !USE_ALIGNED_LOADS
if ( pvGWRBlock != NULL )
{
MemFree ( pvGWRBlock );
pvGWRBlock = NULL;
}
#else
if ( pvGWRBlock != NULL )
{
//MemFree ( pvGWRBlock );
VirtualFree ( pvGWRBlock, NULL, MEM_RELEASE );
pvGWRBlock = NULL;
}
#endif
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:
FillSoundBuffer
Description:
Copies the Sound data to the specified DirecSoundBuffer's data file
Arguments:
LPCTSTR tszName - Name of the WAV file to load
WAVEFORMATEX **ppWaveHeader - Fill this with pointer to wave header
BYTE **ppbWaveData - Fill this with pointer to wave data
DWORD **pcbWaveSize - Fill this with wave data size.
Return Value:
TRUE on success, FALSE on failure.
-------------------------------------------------------------------*/
BOOL
FillSoundBuffer(IDirectSoundBuffer *pdsb, BYTE *pbWaveData, DWORD dwWaveSize)
{
LPVOID pMem1, pMem2;
DWORD dwSize1, dwSize2;
if (!pdsb || !pbWaveData || !dwWaveSize)
return FALSE;
g_errLast = pdsb->Lock(0, dwWaveSize, &pMem1, &dwSize1, &pMem2, &dwSize2, 0);
switch(g_errLast)
{
case DS_OK:
break;
case DSERR_BUFFERLOST: OutputDebugString("DSERR_BUFFERLOST: \n"); break;
case DSERR_INVALIDCALL: OutputDebugString("DSERR_INVALIDCALL: \n"); break;
case DSERR_INVALIDPARAM: OutputDebugString("DSERR_INVALIDPARAM: \n"); break;
case DSERR_PRIOLEVELNEEDED: OutputDebugString("DSERR_PRIOLEVELNEEDED: \n"); break;
default:
ASSERT(0);
break;
}
if (CheckError(TEXT("Lock SoundBuffer")))
return FALSE;
// Have you learned nothing of what I have taught you, young Adami?
#if 0
memcpy(pMem1, pbWaveData, dwSize1);
if (dwSize2 != 0)
memcpy(pMem2, pbWaveData+dwSize1, dwSize2);
#else
//TRACE ( "DwordMemcpy called with 0x%x, 0x%x, 0x%x\n", pMem1, pbWaveData, dwSize1 );
DwordMemcpy(pMem1, pbWaveData, dwSize1);
if (dwSize2 != 0)
{
//TRACE ( "DwordMemcpy called with 0x%x, 0x%x, 0x%x\n", pMem2, pbWaveData+dwSize1, dwSize2 );
DwordMemcpy(pMem2, pbWaveData+dwSize1, dwSize2);
}
#endif
pdsb->Unlock(pMem1, dwSize1, pMem2, dwSize2);
return TRUE;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:
LoadSoundBuffer
Description:
Creates a DirectSoundBuffer and loads the specified file into it.
Arguments:
LPCTSTR tszName - Name of the WAV file to load
Return Value:
TRUE on success, FALSE on failure.
-------------------------------------------------------------------*/
SLONG DCLL_bytes_of_sound_memory_used = 0;
#ifdef DEBUG
DWORD dwTotalSampleSize = 0;
#endif
IDirectSoundBuffer *
LoadSoundBuffer(char *szName, SLONG is_3d)
{
IDirectSoundBuffer *pdsb = NULL;
DSBUFFERDESC dsbd = {0};
BYTE *pbWaveData;
DCLL_bytes_of_sound_memory_used = 0;
#if 0
// Just testing.
return NULL;
#else
if (GetWaveResource(szName, &dsbd.lpwfxFormat, &pbWaveData, &dsbd.dwBufferBytes))
{
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2;
#ifdef DCLL_OUTPUT_STEREO_WAVS
if (dsbd.lpwfxFormat->nChannels == 2)
{
//
// This is a stereo sample.
//
fprintf(DCLL_handle, szName);
fprintf(DCLL_handle, "\r\n");
fflush(DCLL_handle);
}
#endif
if (is_3d)
{
//dsbd.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE;
}
#ifdef TARGET_DC
// Do a compact, just in case we got fragmentation, though it's unlikely.
HRESULT hres = g_pds->Compact();
ASSERT ( SUCCEEDED ( hres ) );
#endif
pdsb = NULL;
#ifdef DEBUG
// See if we're out of memory or not.
DSCAPS dscaps;
ZeroMemory ( &dscaps, sizeof(dscaps) );
dscaps.dwSize = sizeof(dscaps);
g_pds->GetCaps ( &dscaps );
if ( dscaps.dwMaxContigFreeHwMemBytes < 0x8000 )
{
SHARON ( "Running very low on sound memory - 0x%x bytes left.\n", dscaps.dwMaxContigFreeHwMemBytes );
// And don't bother to load it.
goto yuk_didnt_like_that_sound;
}
if ( dsbd.dwBufferBytes > 0x8000 )
{
SHARON ( "Tried to load <%s> more than 32k in length - bad person!\n", szName );
goto yuk_didnt_like_that_sound;
}
#endif
if (SUCCEEDED(g_pds->CreateSoundBuffer(&dsbd, &pdsb, NULL)))
{
if (!FillSoundBuffer(pdsb, pbWaveData, dsbd.dwBufferBytes))
{
pdsb->Release();
pdsb = NULL;
}
DCLL_bytes_of_sound_memory_used = dsbd.dwBufferBytes;
#ifdef TARGET_DC
DSBCAPS dsbcaps;
ZeroMemory ( (void *)&dsbcaps, sizeof ( dsbcaps ) );
dsbcaps.dwSize = sizeof ( dsbcaps );
pdsb->GetCaps ( &dsbcaps );
if ( dsbcaps.dwFlags & DSBCAPS_LOCHARDWARE )
{
//SHARON ( "Hardware buffer 0x%x bytes, %i CPU overhead\n", dsbcaps.dwBufferBytes, dsbcaps.dwPlayCpuOverhead );
// Make sure this is the right length.
if ( ( dsbd.dwBufferBytes & 31 ) != 0 )
{
TRACE ( "Hardware buffer wasn't a multiple of 32 bytes in length - don't loop it! %i \n", (dsbd.dwBufferBytes & 31) );
}
if ( dsbd.dwBufferBytes > 16383 )
{
TRACE ( "Hardware buffer more than 32k samples - don't loop it! %i \n", (dsbd.dwBufferBytes) );
}
}
else
{
SHARON ( "Software buffer <%s>, 0x%x bytes, %iHz\n", szName, dsbcaps.dwBufferBytes, dsbd.lpwfxFormat->nSamplesPerSec );
SHARON ( "Binning it for now.\n" );
ASSERT ( FALSE );
pdsb->Release();
pdsb = NULL;
}
#if 0
#ifdef DEBUG
CBYTE *words_for_11khz[] =
{
"Suspension",
"Scrape",
"Balrog",
"Footstep",
"PAIN",
"TAUNT",
"darci\\",
"roper\\"
"bthug1\\",
"wthug1\\",
"wthug2\\",
"cop\\COP",
"misc\\",
"Barrel",
"Ambience",
"CarX",
"!"
};
// Shall we chop these down even more?
bool bChopIt = FALSE;
for (int j = 0; words_for_11khz[j][0] != '!'; j++)
{
if (strstr(szName, words_for_11khz[j]))
{
bChopIt = TRUE;
break;
}
}
if ( bChopIt )
{
// Chop it down to 8kHz.
dwTotalSampleSize += ( dsbcaps.dwBufferBytes * 8000 / dsbd.lpwfxFormat->nSamplesPerSec );
}
else if ( dsbd.lpwfxFormat->nSamplesPerSec > 11100 )
{
// Pretend you halved the sample rate.
dwTotalSampleSize += dsbcaps.dwBufferBytes / 2;
}
else
{
dwTotalSampleSize += dsbcaps.dwBufferBytes;
}
SHARON ( "Total desired sample memory 0x%x\n", dwTotalSampleSize );
#endif
#endif
#endif
}
else
{
pdsb = NULL;
}
yuk_didnt_like_that_sound:;
}
#endif
return pdsb;
}
IDirectSoundBuffer *CreateStreamingSoundBuffer(int nSamplesPerSec, WORD wBitsPerSample, DWORD dwBufferSize)
{
IDirectSoundBuffer *pdsb = NULL;
DSBUFFERDESC dsbd = {0};
WAVEFORMATEX waveformatex = {0};
#ifdef STREAMING_BUFFER_IS_ADPCM
// Set up the Wave format description for Yamaha ADPCM
waveformatex.wFormatTag = 0x0020;
waveformatex.nChannels = 1;
waveformatex.nSamplesPerSec = 22050;
waveformatex.wBitsPerSample = 4;
waveformatex.nBlockAlign = (waveformatex.nChannels * waveformatex.wBitsPerSample) / 8;
waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign;
waveformatex.cbSize = 0;
dsbd.dwSize = sizeof(dsbd);
dsbd.dwBufferBytes = dwBufferSize;
dsbd.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY;// | DSBCAPS_LOCSOFTWARE;
dsbd.lpwfxFormat = &waveformatex;
#else
// Set up the Wave format description
waveformatex.wFormatTag = WAVE_FORMAT_PCM;
waveformatex.nChannels = 1;
waveformatex.nSamplesPerSec = nSamplesPerSec;
waveformatex.wBitsPerSample = wBitsPerSample;
waveformatex.nBlockAlign = (waveformatex.nChannels * waveformatex.wBitsPerSample) / 8;
waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign;
waveformatex.cbSize = 0;
dsbd.dwSize = sizeof(dsbd);
dsbd.dwBufferBytes = dwBufferSize;
dsbd.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY;// | DSBCAPS_LOCSOFTWARE;
dsbd.lpwfxFormat = &waveformatex;
#endif
g_errLast = g_pds->CreateSoundBuffer(&dsbd, &pdsb, NULL);
if (CheckError(TEXT("Create DirectSound Buffer")))
return NULL;
return pdsb;
}
// ========================================================
//
// FROM HERE ONWARDS IT'S MY CODE!
//
// ========================================================
#include "dclowlevel.h"
typedef struct dcll_sound
{
SLONG used;
IDirectSoundBuffer *dsb;
IDirectSound3DBuffer *dsb_3d;
} DCLL_Sound;
#define DCLL_MAX_SOUNDS 1024
DCLL_Sound DCLL_sound[DCLL_MAX_SOUNDS];
//
// Nasty streaming stuff...
//
// How many bytes to do at a time.
#define DCLL_GRANULARITY 256
#ifdef STREAMING_BUFFER_IS_ADPCM
// We need a much smaller buffer for 1 second's worth.
#define DCLL_STREAM_BUFFER_SIZE ( DCLL_GRANULARITY * 64 )
#else
//#define DCLL_STREAM_BUFFER_SIZE (22050 * sizeof(UWORD) * 1) // Number of bytes for 1 seconds of 22khz 16-bit mono sound
#define DCLL_STREAM_BUFFER_SIZE ( DCLL_GRANULARITY * 128 )
#endif
#define DCLL_STREAM_COMMAND_NOTHING 0
#define DCLL_STREAM_COMMAND_START_NEW_SOUND 1
SLONG DCLL_stream_loop;
SLONG DCLL_stream_command;
CBYTE DCLL_stream_new_fname[MAX_PATH];
SLONG DCLL_stream_data_offset;
SLONG DCLL_stream_silence_count;
IDirectSoundBuffer *DCLL_stream_dsb;
MFFileHandle DCLL_stream_file_handle; // The file handle of what we are streaming...
HANDLE DCLL_stream_event;
HANDLE DCLL_stream_thread; // Streaming sound thread
float DCLL_stream_volume_range = 1.0F;
// Used for buffering streaming reads.
char pcDCLL_stream_buffer[DCLL_GRANULARITY];
void DCLL_stream_set_volume_range(float max_vol)
{
SATURATE(max_vol, 0.0F, 1.0F);
//DCLL_stream_volume_range = max_vol;
DCLL_stream_volume(max_vol);
}
#ifdef DEBUG
#define NUM_TRACIES 16
#define MAX_LENGTH_TRACIE 100
char pcTracieString[NUM_TRACIES][MAX_LENGTH_TRACIE];
int m_iDumpedTracieNum = 0;
int m_iTracieNum = 0;
void Tracie ( char *fmt, ... )
{
va_list va;
va_start ( va, fmt );
vsprintf ( pcTracieString[m_iTracieNum++], fmt, va );
if ( m_iTracieNum >= NUM_TRACIES )
{
m_iTracieNum = 0;
}
va_end ( va );
}
void DumpTracies ( void )
{
while ( m_iDumpedTracieNum != m_iTracieNum )
{
TRACE ( pcTracieString[m_iDumpedTracieNum] );
m_iDumpedTracieNum++;
if ( m_iDumpedTracieNum >= NUM_TRACIES )
{
m_iDumpedTracieNum = 0;
}
}
}
// A sort of pseudo-trace thingie for threads.
// Use it like TRACIE(("thing %i\n", iThing ));
#define TRACIE(arg) Tracie arg;
#else
#define TRACIE sizeof
#endif
// Set this to 0 for new-fangled goodness on the DC.
#define OLD_STYLE_THING_THAT_DIDNT_WORK_VERY_WELL 0
//
// The thread that handles streaming.
//
DWORD DCLL_stream_process(int nUnused)
{
while(1)
{
WaitForSingleObject(DCLL_stream_event, INFINITE);
//
// What's hapenned to our event?
//
#if 0
// Debugging - rip out this.
DCLL_stream_command = DCLL_STREAM_COMMAND_NOTHING;
if (DCLL_stream_file_handle != NULL)
{
// Close it.
FileClose ( DCLL_stream_file_handle );
DCLL_stream_file_handle = NULL;
}
#else
switch(DCLL_stream_command)
{
case DCLL_STREAM_COMMAND_NOTHING:
{
DWORD write_pos;
SLONG start;
//
// Find out which bit of the buffer we should overwrite.
//
DWORD dummy;
#ifdef TARGET_DC
// We need to find the write position.
#if OLD_STYLE_THING_THAT_DIDNT_WORK_VERY_WELL
DCLL_stream_dsb->GetCurrentPosition(&dummy, &write_pos);
#else
DCLL_stream_dsb->GetCurrentPosition(&write_pos, &dummy);
#endif
#else
// Different on the PC.
DCLL_stream_dsb->GetCurrentPosition(&write_pos, &dummy);
#endif
// Very occasionally, the read position is just before the end of the buffer, which causes this to fall over.
// So add a little something to tweak it.
#if OLD_STYLE_THING_THAT_DIDNT_WORK_VERY_WELL
if ( write_pos >= ( DCLL_STREAM_BUFFER_SIZE / 2 ) )
#else
#define SOUND_POS_TWEAK_FACTOR 64
if ( ( write_pos >= ( ( DCLL_STREAM_BUFFER_SIZE / 2 ) - SOUND_POS_TWEAK_FACTOR ) ) &&
( write_pos < ( ( DCLL_STREAM_BUFFER_SIZE ) - SOUND_POS_TWEAK_FACTOR ) ) )
#endif
{
//
// Overwrite the first half.
//
start = 0;
TRACIE (( "Write pos 0x%x - overwriting first half\n", write_pos ));
}
else
{
//
// Overwrite the second half.
//
start = DCLL_STREAM_BUFFER_SIZE / 2;
TRACIE (( "Write pos 0x%x - overwriting second half\n", write_pos ));
}
if (DCLL_stream_file_handle == NULL)
{
//
// No more sound data, so fill the sound buffer
// with silence.
//
DCLL_stream_silence_count += 1;
if (DCLL_stream_silence_count >= 2)
{
//
// We can safely stop the sound from playing.
//
DCLL_stream_silence_count = 0;
DCLL_stream_dsb->Stop();
break;
}
}
UBYTE *block1_mem = NULL;
UBYTE *block2_mem = NULL;
DWORD block1_length;
DWORD block2_length;
#ifdef TARGET_DC
#if OLD_STYLE_THING_THAT_DIDNT_WORK_VERY_WELL
DCLL_stream_dsb->Lock(start, DCLL_STREAM_BUFFER_SIZE / 2, (void **) &block1_mem, &block1_length, (void **) &block2_mem, &block2_length, DSBLOCK_FROMWRITECURSOR );
#else
DCLL_stream_dsb->Lock(start, DCLL_STREAM_BUFFER_SIZE / 2, (void **) &block1_mem, &block1_length, (void **) &block2_mem, &block2_length, 0 );
#endif
#else
DCLL_stream_dsb->Lock(start, DCLL_STREAM_BUFFER_SIZE / 2, (void **) &block1_mem, &block1_length, (void **) &block2_mem, &block2_length, 0 );
#endif
if ( block1_mem == NULL )
{
// NADS! Has happened though. Which is pretty damn scary.
ASSERT ( FALSE );
TRACE (( "Eek - the Lock just failed for the sound\n." ));
}
else
{
if (DCLL_stream_file_handle == NULL)
{
//
// Silence...
//
// Can't use memset - must use DWORD writes.
//memset(block1_mem, 0, block1_length);
DWORD *dst1 = (DWORD *)block1_mem;
DWORD count = block1_length;
#ifdef TARGET_DC
ASSERT ( ( (DWORD)dst1 & 3 ) == 0 );
ASSERT ( ( count & 3 ) == 0 );
#endif
count >>= 2;
while ( (count--) > 0 )
{
*dst1++ = 0;
}
}
else
{
DWORD bytes_read;
//
// Read sound data from the file.
//
#ifdef TARGET_DC
// The version that streams in 256 byte chunks.
ASSERT ( ( block1_length & (DCLL_GRANULARITY-1) ) == 0 );
DWORD dwBytesToWrite = block1_length;
UBYTE *pbBlockMem = block1_mem;
while ( dwBytesToWrite > 0 )
{
if ( DCLL_stream_file_handle != NULL )
{
bytes_read = FileRead ( DCLL_stream_file_handle, pcDCLL_stream_buffer, DCLL_GRANULARITY );
if ( bytes_read < DCLL_GRANULARITY )
{
if ( DCLL_stream_loop )
{
// Start from the beginning again.
FileSeek(DCLL_stream_file_handle, SEEK_MODE_BEGINNING, DCLL_stream_data_offset);
DWORD new_bytes_read = FileRead ( DCLL_stream_file_handle, pcDCLL_stream_buffer + bytes_read, DCLL_GRANULARITY - bytes_read );
ASSERT ( new_bytes_read == ( DCLL_GRANULARITY - bytes_read ) );
}
else
{
// Need to pad with silence.
memset ( pcDCLL_stream_buffer + bytes_read, 0, DCLL_GRANULARITY - bytes_read );
FileClose ( DCLL_stream_file_handle );
DCLL_stream_file_handle = NULL;
}
}
}
else
{
bytes_read = 0;
memset ( pcDCLL_stream_buffer, 0, DCLL_GRANULARITY );
}
// And copy the buffer in. MUST BE IN DWORDS.
DWORD *pdwSrc = (DWORD*)pcDCLL_stream_buffer;
DWORD *pdwDst = (DWORD*)pbBlockMem;
for ( int i = 0; i < ( DCLL_GRANULARITY / 4 ); i++ )
{
*pdwDst++ = *pdwSrc++;
}
dwBytesToWrite -= DCLL_GRANULARITY;
pbBlockMem += DCLL_GRANULARITY;
}
#else
#if 0
ReadFile(
DCLL_stream_file_handle,
block1_mem,
block1_length,
&bytes_read,
NULL);
#else
bytes_read = FileRead ( DCLL_stream_file_handle, block1_mem, block1_length );
#endif
if (bytes_read < block1_length)
{
if (DCLL_stream_loop)
{
//
// Go back to the beginning of the data section of the file and
// start looping again.
//
FileSeek(DCLL_stream_file_handle, SEEK_MODE_BEGINNING, DCLL_stream_data_offset);
// SetFilePointer(DCLL_stream_file_handle, DCLL_stream_data_offset, NULL, FILE_BEGIN);
FileRead(DCLL_stream_file_handle, block1_mem + bytes_read, block1_length - bytes_read);
}
else
{
//
// Fill the remainder of the buffer with silence and close the file.
//
// Can't use memset - must be DWORD writes.
//memset(block1_mem + bytes_read, 0, block1_length - bytes_read);
DWORD *dst1 = (DWORD *)( block1_mem + bytes_read );
DWORD count = block1_length - bytes_read;
#ifdef TARGET_DC
ASSERT ( ( (DWORD)dst1 & 3 ) == 0 );
ASSERT ( ( count & 3 ) == 0 );
#endif
count >>= 2;
while ( (count--) > 0 )
{
*dst1++ = 0;
}
#if 0
CloseHandle(DCLL_stream_file_handle);
#else
FileClose ( DCLL_stream_file_handle );
#endif
DCLL_stream_file_handle = NULL;
}
}
#endif
}
/*
memset(block1_mem, 0, block1_length);
if (start)
{
SLONG i;
for (i = 0; i < block1_length; i++)
{
block1_mem[i] = rand();
}
}
*/
#ifdef TARGET_DC
// This has started falling over on the PC. But I don't care.
ASSERT(block2_mem == NULL);
#endif
DCLL_stream_dsb->Unlock(block1_mem, block1_length, block2_mem, block2_length);
}
}
break;
case DCLL_STREAM_COMMAND_START_NEW_SOUND:
{
DWORD status = 0;
DCLL_stream_dsb->GetStatus(&status);
if (status & (DSBSTATUS_LOOPING|DSBSTATUS_PLAYING))
{
//
// If the buffer is playing, then we close
// the current file handle and stop the sound playing.
//
if (DCLL_stream_file_handle)
{
FileClose(DCLL_stream_file_handle);
DCLL_stream_file_handle = NULL;
DCLL_stream_silence_count = 0;
}
DCLL_stream_dsb->Stop();
}
{
BYTE temp[256];
ULONG bytes_read;
DWORD size;
WAVEFORMATEX *pwfx;
BYTE *data_start;
//
// Open the sound file.
//
#ifdef CRAPPY_STREAMING_ADPCM_HACK
// Always load this
if ( strstr ( DCLL_stream_new_fname, "FrontLoop" ) )
{
strcpy ( DCLL_stream_new_fname, "Data\\sfx\\1622DC\\GeneralMusic\\FrontLoopMONOAD.wav" );
}
else
{
strcpy ( DCLL_stream_new_fname, "Data\\sfx\\1622DC\\whoryou.wav" );
}
#endif
#if 0
DCLL_stream_file_handle = CreateFile(DCLL_stream_new_fname, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (DCLL_stream_file_handle == NULL)
#else
DCLL_stream_file_handle = FileOpen ( DCLL_stream_new_fname );
if (DCLL_stream_file_handle == FILE_OPEN_ERROR )
#endif
{
DCLL_stream_file_handle = NULL;
bytes_read = 0;
}
else
{
//
// Read the first 256 bytes to get file header
//
#if 0
ReadFile(DCLL_stream_file_handle, temp, 256, &bytes_read, NULL);
#else
bytes_read = FileRead ( DCLL_stream_file_handle, temp, 256 );
#endif
}
if (bytes_read != 256)
{
DCLL_stream_file_handle = NULL;
DCLL_stream_dsb->Stop();
}
else
{
//
// Parse the header information to get to the sound data.
//
ParseWaveFile((void *) temp, &pwfx, &data_start, &size);
//
// Set file pointer to point to start of data
//
DCLL_stream_data_offset = (SLONG) (data_start - temp);
#if 0
SetFilePointer(DCLL_stream_file_handle, DCLL_stream_data_offset, NULL, FILE_BEGIN);
#else
FileSeek ( DCLL_stream_file_handle, SEEK_MODE_BEGINNING, (int) (data_start - temp) );
#endif
//
// Fill up the sound buffer with the data.
//
UBYTE *block1_mem = NULL;
UBYTE *block2_mem = NULL;
DWORD block1_length;
DWORD block2_length;
DCLL_stream_dsb->Lock(0, 0, (void **) &block1_mem, &block1_length, (void **) &block2_mem, &block2_length, DSBLOCK_ENTIREBUFFER);
if (block1_mem)
{
DWORD bytes_read;
//
// Read sound data from the file.
//
#ifndef TARGET_DC
#if 0
ReadFile(
DCLL_stream_file_handle,
block1_mem,
block1_length,
&bytes_read,
NULL);
#else
bytes_read = FileRead ( DCLL_stream_file_handle, block1_mem, block1_length );
#endif
if (bytes_read < block1_length)
{
//
// Fill the remainder of the buffer with silence and close the file.
//
// Can't use memset - must use DWORD writes.
//memset(block1_mem + bytes_read, 0, block1_length - bytes_read);
DWORD *dst1 = (DWORD *)( block1_mem + bytes_read );
DWORD count = block1_length - bytes_read;
ASSERT ( ( (DWORD)dst1 & 3 ) == 0 );
ASSERT ( ( count & 3 ) == 0 );
count >>= 2;
while ( (count--) > 0 )
{
*dst1++ = 0;
}
#if 0
CloseHandle(DCLL_stream_file_handle);
#else
FileClose ( DCLL_stream_file_handle );
#endif
DCLL_stream_file_handle = NULL;
}
#else
// The version that streams in 256 byte chunks.
ASSERT ( ( block1_length & (DCLL_GRANULARITY-1) ) == 0 );
DWORD dwBytesToWrite = block1_length;
UBYTE *pbBlockMem = block1_mem;
while ( dwBytesToWrite > 0 )
{
if ( DCLL_stream_file_handle != NULL )
{
bytes_read = FileRead ( DCLL_stream_file_handle, pcDCLL_stream_buffer, DCLL_GRANULARITY );
if ( bytes_read < DCLL_GRANULARITY )
{
// Need to pad with silence.
memset ( pcDCLL_stream_buffer + bytes_read, 0, DCLL_GRANULARITY - bytes_read );
FileClose ( DCLL_stream_file_handle );
DCLL_stream_file_handle = NULL;
}
}
else
{
bytes_read = 0;
memset ( pcDCLL_stream_buffer, 0, DCLL_GRANULARITY );
}
// And copy the buffer in. MUST BE IN DWORDS.
DWORD *pdwSrc = (DWORD*)pcDCLL_stream_buffer;
DWORD *pdwDst = (DWORD*)pbBlockMem;
for ( int i = 0; i < ( DCLL_GRANULARITY / 4 ); i++ )
{
*pdwDst++ = *pdwSrc++;
}
dwBytesToWrite -= DCLL_GRANULARITY;
pbBlockMem += DCLL_GRANULARITY;
}
#endif
}
DCLL_stream_dsb->Unlock(block1_mem, block1_length, block2_mem, block2_length);
ASSERT(block2_mem == NULL);
//
// Start the buffer playing.
//
DCLL_stream_dsb->SetCurrentPosition(0);
DCLL_stream_dsb->Play(0, 0, DSBPLAY_LOOPING);
}
}
}
DCLL_stream_command = DCLL_STREAM_COMMAND_NOTHING;
break;
default:
ASSERT(0);
break;
}
#endif
}
return 0;
}
void DCLL_stream_init(void)
{
DWORD thread_id;
//
// Create the event that we use to communicate with
// the streaming thread.
//
DCLL_stream_event = CreateEvent(NULL, FALSE, FALSE, NULL);
//
// Create the streaming buffer...
//
DCLL_stream_dsb = CreateStreamingSoundBuffer(22050, 16, DCLL_STREAM_BUFFER_SIZE);
//
// Spawn off the new thread.
//
DCLL_stream_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DCLL_stream_process, NULL, 0, &thread_id);
ASSERT(DCLL_stream_thread);
#ifdef TARGET_DC
SetThreadPriority ( DCLL_stream_thread, THREAD_PRIORITY_HIGHEST );
#endif
//
// Setup notifications...
//
IDirectSoundNotify *pdsn;
DSBPOSITIONNOTIFY rgdsbpn[2];
/*
IDirectSoundBuffer_QueryInterface(
DCLL_stream_dsb,
IID_IDirectSoundNotify,
(void **)&pdsn);
*/
DCLL_stream_dsb->QueryInterface(IID_IDirectSoundNotify, (void **)&pdsn);
rgdsbpn[0].hEventNotify = DCLL_stream_event;
rgdsbpn[1].hEventNotify = DCLL_stream_event;
rgdsbpn[0].dwOffset = DCLL_STREAM_BUFFER_SIZE / 2;
rgdsbpn[1].dwOffset = DCLL_STREAM_BUFFER_SIZE - 2;
pdsn->SetNotificationPositions(2, rgdsbpn);
//
// No longer need the DirectSoundNotify interface, so release it
//
pdsn->Release();
}
SLONG DCLL_stream_play(CBYTE *fname, SLONG loop)
{
if (DCLL_stream_command != DCLL_STREAM_COMMAND_NOTHING)
{
//
// Waiting to play another sample.
//
if (loop && !DCLL_stream_loop)
{
return FALSE;
}
}
//
// The command to the thread.
//
DCLL_stream_loop = loop;
DCLL_stream_command = DCLL_STREAM_COMMAND_START_NEW_SOUND;
#ifdef TARGET_DC
// Some filenames begin with ".\" which confuses the poor DC
if ( ( fname[0] == '.' ) && ( fname[1] == '\\' ) )
{
fname += 2;
}
#endif
// Some missions have empty briefing names.
if ( fname[0] == '\0' )
{
return FALSE;
}
#ifndef TARGET_DC
strcpy(DCLL_stream_new_fname, fname);
#else
// Some filename have more than one '.', which GDWorkshop converts to an underline.
// They are all of the format blah.ucmfoobar.wav, which changes to
// blah.ucmfoobar_wav
char *pcSrc = fname;
char *pcDst = DCLL_stream_new_fname;
bool bFoundFullStop = FALSE;
while ( *pcSrc != '\0' )
{
*pcDst = *pcSrc++;
if ( *pcDst == '.' )
{
if ( bFoundFullStop )
{
*pcDst = '_';
}
else
{
bFoundFullStop = TRUE;
}
}
pcDst++;
}
*pcDst = '\0';
#if 0
// No longer needed - everything is ADPCM.
#ifdef STREAMING_BUFFER_IS_ADPCM
// If the file ends in MONO, add a further AD to it.
int iTemp = strlen ( DCLL_stream_new_fname );
if ( ( DCLL_stream_new_fname[iTemp-8] == 'M' ) &&
( DCLL_stream_new_fname[iTemp-7] == 'O' ) &&
( DCLL_stream_new_fname[iTemp-6] == 'N' ) &&
( DCLL_stream_new_fname[iTemp-5] == 'O' ) )
{
strcpy ( &(DCLL_stream_new_fname[iTemp-4]), "AD.wav" );
}
#endif
#endif
#endif
//
// Wake up the process.
//
SetEvent(DCLL_stream_event);
return TRUE;
}
void DCLL_stream_stop()
{
#ifndef TARGET_DC
// This rout is buggered on the PC.
return;
#endif
//
// Make sure a sound doesn't sneakily start playing...
//
DCLL_stream_command = DCLL_STREAM_COMMAND_NOTHING;
DCLL_stream_dsb->Stop();
//
// Close the streaming file and stop the buffer from playing.
//
if (DCLL_stream_file_handle)
{
#if 0
CloseHandle(DCLL_stream_file_handle);
#else
FileClose ( DCLL_stream_file_handle );
#endif
DCLL_stream_file_handle = NULL;
DCLL_stream_silence_count = 0;
}
while(1)
{
ULONG status;
HRESULT res;
res = DCLL_stream_dsb->GetStatus(&status);
ASSERT(res == DS_OK);
if ((status & DSBSTATUS_PLAYING) ||
(status & DSBSTATUS_LOOPING))
{
Sleep(10);
DCLL_stream_dsb->Stop();
}
else
{
return;
}
}
}
SLONG DCLL_stream_is_playing()
{
ULONG status;
HRESULT res;
if (DCLL_stream_command == DCLL_STREAM_COMMAND_START_NEW_SOUND)
{
return TRUE;
}
res = DCLL_stream_dsb->GetStatus(&status);
ASSERT(res == DS_OK);
if ((status & DSBSTATUS_PLAYING) ||
(status & DSBSTATUS_LOOPING))
{
return TRUE;
}
else
{
return FALSE;
}
}
// Returns TRUE if the stream has actually started playing,
// i.e. the disk has seeked, etc.
bool DCLL_stream_has_started_streaming ( void )
{
ULONG status;
HRESULT res;
res = DCLL_stream_dsb->GetStatus(&status);
ASSERT(res == DS_OK);
if ((status & DSBSTATUS_PLAYING) ||
(status & DSBSTATUS_LOOPING))
{
return TRUE;
}
else
{
return FALSE;
}
}
void DCLL_stream_wait()
{
while(1)
{
if (DCLL_stream_is_playing())
{
Sleep(10);
}
else
{
return;
}
}
}
void DCLL_stream_volume(float volume)
{
if (DCLL_stream_dsb == NULL)
{
return;
}
SATURATE(volume, 0.0F, 1.0F);
// Done elsewhere.
//volume *= DCLL_stream_volume_range;
volume = powf( volume, 0.1F);
SLONG ivolume = SLONG(DSBVOLUME_MIN + (DSBVOLUME_MAX - DSBVOLUME_MIN) * volume);
DCLL_stream_dsb->SetVolume(ivolume);
}
void DCLL_memstream_init();
void DCLL_init()
{
InitDirectSound();
InitDirectSound3D();
memset(DCLL_sound, 0, sizeof(DCLL_sound));
DCLL_stream_init();
DCLL_memstream_init();
#ifdef DCLL_OUTPUT_STEREO_WAVS
DCLL_handle = fopen("t:\\stereowavs.txt", "wb");
#endif
}
DCLL_Sound *DCLL_load_sound(CBYTE *fname)
{
SLONG i;
DCLL_Sound *ds;
for (i = 0; i < DCLL_MAX_SOUNDS; i++)
{
ds = &DCLL_sound[i];
if (!ds->used)
{
goto found_unused_sound;
}
}
//
// No more sounds left!
//
ASSERT(0);
return NULL;
found_unused_sound:;
ds->used = TRUE;
ds->dsb = LoadSoundBuffer(fname, TRUE);
if (!ds->dsb)
{
ds->used = FALSE;
return NULL;
}
ASSERT(ds->dsb);
(ds->dsb)->QueryInterface(IID_IDirectSound3DBuffer, (void **) &ds->dsb_3d);
return ds;
}
void DCLL_set_volume(DCLL_Sound *ds, float volume)
{
if (ds == NULL)
{
return;
}
SATURATE(volume, 0.0F, 1.0F);
volume = powf(volume, 0.1F);
SLONG ivolume = SLONG(DSBVOLUME_MIN + (DSBVOLUME_MAX - DSBVOLUME_MIN) * volume);
HRESULT hres = ds->dsb->SetVolume(ivolume);
//TRACE ( "Vol %i\n", ivolume );
}
SLONG DCLL_still_playing(DCLL_Sound *ds)
{
ULONG status;
if(ds==NULL)
return(0);
if(ds->dsb==NULL)
return(0);
ds->dsb->GetStatus(&status);
if ((status & DSBSTATUS_PLAYING) || (status & DSBSTATUS_LOOPING))
{
return(1);
}
else
return(0);
}
void DCLL_2d_play_sound(DCLL_Sound *ds, SLONG flag)
{
if (ds == NULL)
{
return;
}
ULONG status;
ASSERT ( ds->dsb != NULL );
if ( ds->dsb_3d != NULL )
{
ds->dsb_3d->SetMode(DS3DMODE_DISABLE, DS3D_IMMEDIATE);
}
ds->dsb->GetStatus(&status);
if ((status & DSBSTATUS_PLAYING) ||
(status & DSBSTATUS_LOOPING))
{
if (flag & DCLL_FLAG_INTERRUPT)
{
ds->dsb->SetCurrentPosition(0);
}
}
else
{
switch(ds->dsb->Play(0, 0, (flag & DCLL_FLAG_LOOP) ? DSBPLAY_LOOPING : 0))
{
case DS_OK:
break;
case DSERR_PRIOLEVELNEEDED:
ASSERT(0);
break;
default:
ASSERT(0);
break;
}
}
}
float DCLL_3d_listener_x;
float DCLL_3d_listener_y;
float DCLL_3d_listener_z;
void DCLL_3d_play_sound(DCLL_Sound *ds, float x, float y, float z, SLONG flag)
{
if (ds == NULL)
{
return;
}
ASSERT(ds->dsb);
ASSERT(ds->dsb_3d);
ds->dsb_3d->SetMode(DS3DMODE_NORMAL, DS3D_IMMEDIATE);
//
// Start playing- or restart if it's already playing.
//
ULONG status;
ds->dsb->GetStatus(&status);
if ((status & DSBSTATUS_PLAYING) ||
(status & DSBSTATUS_LOOPING))
{
if (flag & DCLL_FLAG_INTERRUPT)
{
ds->dsb->SetCurrentPosition(0);
}
}
else
{
ds->dsb->Play(0,0,(flag & DCLL_FLAG_LOOP) ? DSBPLAY_LOOPING : 0);
}
//
// Set position.
//
ds->dsb_3d->SetPosition(x,y,z, DS3D_IMMEDIATE);
}
void DCLL_3d_set_listener(
float x,
float y,
float z,
float matrix[9])
{
DCLL_3d_listener_x = x;
DCLL_3d_listener_y = y;
DCLL_3d_listener_z = z;
if (!g_pds3dl)
{
return;
}
g_pds3dl->SetPosition(x, y, z, DS3D_DEFERRED);
g_pds3dl->SetOrientation(
matrix[6],
matrix[7],
matrix[8],
matrix[3],
matrix[4],
matrix[5],
DS3D_DEFERRED);
g_pds3dl->CommitDeferredSettings();
}
void DCLL_stop_sound(DCLL_Sound *ds)
{
if (ds == NULL)
{
return;
}
ds->dsb->Stop();
}
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(arg) if ( (arg) != NULL ) { (arg)->Release(); (arg) = NULL; }
#endif
void DCLL_free_sound(DCLL_Sound *ds)
{
if (ds == NULL || !ds->used)
{
return;
}
SAFE_RELEASE ( ds->dsb_3d );
SAFE_RELEASE ( ds->dsb );
ds->used = FALSE;
}
void DCLL_fini(void)
{
SLONG i;
DCLL_Sound *ds;
for (i = 0; i < DCLL_MAX_SOUNDS; i++)
{
ds = &DCLL_sound[i];
if (ds->used)
{
DCLL_free_sound(ds);
}
}
//
// Release objects...
//
g_pds3dl->Release();
g_pdsbPrimary->Release();
g_pds->Release();
}
void init_my_dialog(HWND h)
{
}
void my_dialogs_over(HWND h)
{
}
void MilesTerm(void)
{
}
#ifndef TARGET_DC
#include "sound_id.h"
//
// Does the DC conversion of looping samples.
//
void DCLL_looping_sample_conversion(void)
{
SLONG i;
SLONG looping_sample[] =
{
S_SLIDE_START,
S_SEARCH_END,
S_ZIPWIRE,
S_TROPICAL,
S_RAIN_START,
S_AMBIENCE_END,
S_AMB_POLICE1,
S_AMB_POSHEETA,
S_AMB_OFFICE1,
S_TUNE_CLUB_START,
S_RECKONING_LOOP,
S_HELI,
S_TUNE_CLUB,
S_ACIEEED,
S_FRONT_END_LOOP_EDIT,
S_FIRE_HYDRANT,
S_FIRE,
S_CAR_SIREN1,
S_CARX_CRUISE,
S_CARX_IDLE,
S_CAR_REVERSE_LOOP,
-12345
};
CreateDirectory("t:\\SillyDCwavs", NULL);
//
// Moves all these files to to the directory on t:\
//
FILE *handle = fopen("t:\\SillyDCwavs\\dcconv.bat", "wb");
if (!handle)
{
return;
}
fprintf(handle, "mkdir converted\r\n\r\n");
CBYTE src[256];
CBYTE dst[256];
CBYTE jst[256];
CBYTE *ch;
for (i = 0; looping_sample[i] >= 0; i++)
{
sprintf(src, "data\\sfx\\1622\\%s", sound_list[looping_sample[i]]);
for (ch = src; *ch; ch++);
while(*ch != '\\') ch--;
ch++;
strcpy(jst, ch);
sprintf(dst, "t:\\sillyDCwavs\\%s", jst);
CopyFile(src, dst, FALSE);
//
// Mention this filename in the batch file.
//
fprintf(handle, "wavcon %s converted\\%s\r\n", jst, jst);
fprintf(handle, "copy %s n:\\urbanchaos\\thegame\\data\\sfx\\1622DC\\%s /Y\r\n", jst, jst);
fprintf(handle, "copy converted\\%s n:\\urbanchaos\\thegame\\data\\sfx\\1622DC\\%s /Y\r\n", jst, jst);
fprintf(handle, "\r\n");
}
fclose(handle);
}
#endif
// ========================================================
//
// STREAMING FILES FROM MEMORY
//
// ========================================================
//
// The sound to stream.
//
UBYTE *DCLL_memstream_sound;
SLONG DCLL_memstream_sound_length; // length in bytes
UBYTE *DCLL_memstream_sound_upto; // Where we have played upto.
#define DCLL_MEMSTREAM_BUFFER_SIZE (32768)
IDirectSoundBuffer *DCLL_memstream_dsb;
HANDLE DCLL_memstream_event;
HANDLE DCLL_memstream_thread; // Streaming sound thread
//
// The thread that handles memstreaming.
//
DWORD DCLL_memstream_process(void)
{
while(1)
{
WaitForSingleObject(DCLL_memstream_event, INFINITE);
// Debugging - disable it.
#if 1
{
DWORD write_pos;
SLONG start;
//
// Find out which bit of the buffer we should overwrite.
//
DWORD dummy;
#ifdef TARGET_DC
// We need to find the write position.
#if OLD_STYLE_THING_THAT_DIDNT_WORK_VERY_WELL
DCLL_memstream_dsb->GetCurrentPosition(&dummy, &write_pos);
#else
DCLL_memstream_dsb->GetCurrentPosition(&write_pos, &dummy);
#endif
#else
// Different on the PC.
DCLL_memstream_dsb->GetCurrentPosition(&write_pos, &dummy);
#endif
#if OLD_STYLE_THING_THAT_DIDNT_WORK_VERY_WELL
if ( write_pos >= ( DCLL_STREAM_BUFFER_SIZE / 2 ) )
#else
if ( ( write_pos >= ( ( DCLL_STREAM_BUFFER_SIZE / 2 ) - SOUND_POS_TWEAK_FACTOR ) ) &&
( write_pos < ( ( DCLL_STREAM_BUFFER_SIZE ) - SOUND_POS_TWEAK_FACTOR ) ) )
#endif
{
//
// Overwrite the first half.
//
start = 0;
TRACIE (( "Memstream: writing first half\n" ));
}
else
{
//
// Overwrite the second half.
//
start = DCLL_MEMSTREAM_BUFFER_SIZE / 2;
TRACIE (( "Memstream: writing second half\n" ));
}
if (DCLL_memstream_sound == NULL)
{
//
// No more sound data so stop the buffer! Something
// bad must be happening.
//
DCLL_memstream_dsb->Stop();
}
UBYTE *block1_mem = NULL;
UBYTE *block2_mem = NULL;
DWORD block1_length = 0;
DWORD block2_length = 0;
#if OLD_STYLE_THING_THAT_DIDNT_WORK_VERY_WELL
DCLL_memstream_dsb->Lock(start, DCLL_MEMSTREAM_BUFFER_SIZE / 2, (void **) &block1_mem, &block1_length, (void **) &block2_mem, &block2_length, DSBLOCK_FROMWRITECURSOR);
#else
DCLL_memstream_dsb->Lock(start, DCLL_MEMSTREAM_BUFFER_SIZE / 2, (void **) &block1_mem, &block1_length, (void **) &block2_mem, &block2_length, 0);
#endif
//
// Fill the buffer with data.
//
SLONG i;
SLONG count = block1_length >> 2;
SLONG *dst = (SLONG *) block1_mem;
SLONG *src = (SLONG *) DCLL_memstream_sound_upto;
SLONG *end = (SLONG *) (DCLL_memstream_sound + DCLL_memstream_sound_length);
#ifdef TARGET_DC
ASSERT((SLONG(dst) & 3) == 0);
ASSERT((count & 3) == 0);
#endif
for (i = count; i > 0; i--)
{
*dst++ = *src++;
if (src >= end)
{
src = (SLONG *) DCLL_memstream_sound;
}
}
DCLL_memstream_sound_upto = (UBYTE *) src;
DCLL_memstream_dsb->Unlock(block1_mem, block1_length, block2_mem, block2_length);
}
#endif
}
// Doesn't ever exit, but the DC compiler
// complains that it doesn't return a value.
return 0;
}
void DCLL_memstream_init()
{
DWORD thread_id;
//
// Create the event that we use to communicate with the
// memstream thread.
//
DCLL_memstream_event = CreateEvent(NULL, FALSE, FALSE, NULL);
//
// Create the memstream buffer.
//
DCLL_memstream_dsb = CreateStreamingSoundBuffer(22050, 16, DCLL_MEMSTREAM_BUFFER_SIZE);
//
// Spawn off the new thread.
//
DCLL_memstream_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DCLL_memstream_process, NULL, 0, &thread_id);
ASSERT(DCLL_memstream_thread);
#ifdef TARGET_DC
SetThreadPriority(DCLL_memstream_thread, THREAD_PRIORITY_HIGHEST);
#endif
//
// Setup notifications...
//
IDirectSoundNotify *pdsn;
DSBPOSITIONNOTIFY rgdsbpn[2];
DCLL_memstream_dsb->QueryInterface(IID_IDirectSoundNotify, (void **)&pdsn);
rgdsbpn[0].hEventNotify = DCLL_memstream_event;
rgdsbpn[1].hEventNotify = DCLL_memstream_event;
rgdsbpn[0].dwOffset = DCLL_MEMSTREAM_BUFFER_SIZE / 2;
rgdsbpn[1].dwOffset = DCLL_MEMSTREAM_BUFFER_SIZE - 2;
if (pdsn->SetNotificationPositions(2, rgdsbpn) != DS_OK)
{
//
//
//
ASSERT(0);
}
//
// No longer need the DirectSoundNotify interface, so release it
//
pdsn->Release();
}
void DCLL_memstream_load(CBYTE *fname)
{
BYTE temp[256];
ULONG bytes_read;
DWORD size;
WAVEFORMATEX *pwfx;
BYTE *data_start;
MFFileHandle handle;
//
// Stop the buffer.
//
DCLL_memstream_dsb->Stop();
//
// Initialise the sound.
//
DCLL_memstream_unload();
//
// Open the sound file.
//
handle = FileOpen(fname);
if (handle == FILE_OPEN_ERROR )
{
ASSERT ( FALSE );
return;
}
//
// Read the first 256 bytes to get file header
//
bytes_read = FileRead(handle, temp, 256);
if (bytes_read != 256)
{
return;
}
//
// Parse the header information to get to the sound data.
//
ParseWaveFile((void *) temp, &pwfx, &data_start, &size);
//
// Set file pointer to point to start of data
//
FileSeek(handle, SEEK_MODE_BEGINNING, (int) (data_start - temp));
//
// Allocate the sound.
//
DCLL_memstream_sound = (UBYTE *) MemAlloc(size);
DCLL_memstream_sound_length = size;
DCLL_memstream_sound_upto = DCLL_memstream_sound;
ASSERT(DCLL_memstream_sound);
//
// Load in the sound.
//
bytes_read = FileRead(handle, DCLL_memstream_sound, DCLL_memstream_sound_length);
ASSERT(bytes_read == (unsigned)DCLL_memstream_sound_length);
//
// Lock the sound buffer.
//
UBYTE *block1_mem = NULL;
UBYTE *block2_mem = NULL;
DWORD block1_length = 0;
DWORD block2_length = 0;
DCLL_memstream_dsb->Lock(0, 0, (void **) &block1_mem, &block1_length, (void **) &block2_mem, &block2_length, DSBLOCK_ENTIREBUFFER);
//
// Make sure what happens is what we expect.
//
ASSERT(block2_mem == NULL);
ASSERT(block1_length == DCLL_MEMSTREAM_BUFFER_SIZE);
ASSERT(block2_length == 0);
//
// Fill the buffer with data.
//
SLONG i;
SLONG count = DCLL_MEMSTREAM_BUFFER_SIZE >> 2;
SLONG *dst = (SLONG *) block1_mem;
SLONG *src = (SLONG *) DCLL_memstream_sound;
SLONG *end = (SLONG *) (DCLL_memstream_sound + DCLL_memstream_sound_length);
ASSERT((SLONG(dst) & 3) == 0);
ASSERT((count & 3) == 0);
for (i = count; i > 0; i--)
{
*dst++ = *src++;
if (src >= end)
{
src = (SLONG *) DCLL_memstream_sound;
}
}
//
// Unlock the buffer.
//
DCLL_memstream_dsb->Unlock(block1_mem, block1_length, block2_mem, block2_length);
FileClose(handle);
}
void DCLL_memstream_volume(float volume)
{
if (DCLL_memstream_dsb == NULL)
{
return;
}
SATURATE(volume, 0.0F, 1.0F);
volume = powf( volume, 0.1F);
SLONG ivolume = SLONG(DSBVOLUME_MIN + (DSBVOLUME_MAX - DSBVOLUME_MIN) * volume);
DCLL_memstream_dsb->SetVolume(ivolume);
}
void DCLL_memstream_play()
{
//
// Start the buffer playing.
//
ASSERT(DCLL_memstream_sound);
#if 0
// Don't reset it! It's so short, and if you reset it,
// it sounds a bit crappy. Just start playing it again from the
// current position.
DCLL_memstream_dsb->SetCurrentPosition(0);
#endif
DCLL_memstream_dsb->Play(0, 0, DSBPLAY_LOOPING);
DCLL_memstream_volume(DCLL_stream_volume_range * 0.5F);
}
void DCLL_memstream_stop()
{
//
// Stop the sound.
//
DCLL_memstream_dsb->Stop();
}
void DCLL_memstream_unload()
{
Sleep(100); // Give the streaming thread enough time to finish what it's doing!
if (DCLL_memstream_sound)
{
MemFree(DCLL_memstream_sound);
}
DCLL_memstream_sound = NULL;
DCLL_memstream_sound_length = 0;
DCLL_memstream_sound_upto = NULL;
}