// ======================================================== // // 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 #include #include "MFStdLib.h" #include #include #ifdef TARGET_DC #include // 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; }