home *** CD-ROM | disk | FTP | other *** search
- /*===========================================================================*\
- |
- | File: cgsound.cpp
- |
- | Description:
- | Sample Immortal Klowns game sound effects routines.
- |
- | Sound effects are implemented as an object which are
- | loaded from a wave file (.WAV) and can be mixed with
- | other sounds as well as being algorithmically altered
- | (panned from left to right, pitch changes, and volume
- | changes).
- |
- | For demonstration purposes, sounds may be played using a
- | variety of methods.
- | 1) DirectSound - the preferred method, requires
- | that a supported sound board be present
- | and that the DirectSound drivers be installed.
- | Offers greatest flexibility and lowest latency.
- | Doesn't support compressed wave files.
- | 2) waveOut - standard Windows API for low-level sound
- | support. Hardest to code for and not as flexible
- | as DirectSound nor as low in latency. Assumes
- | similar format for all waves. Only allows playing
- | one sound at a time (without using WaveMix).
- | 3) sndPlaySound - the simpliest interface which
- | yields the least flexibility and worst latency.
- | Only one sound may be played at a time!
- |
- | If desired, individual sounds may be played using different
- | methods (there's no advantage to this, it's just for demos).
- |
- |-----------------------------------------------------------------------------
- |
- | Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
- |
- | Written by Moss Bay Engineering, Inc. under contract to Microsoft Corporation
- |
- \*===========================================================================*/
-
- /**************************************************************************
-
- (C) Copyright 1995-1996 Microsoft Corp. All rights reserved.
-
- You have a royalty-free right to use, modify, reproduce and
- distribute the Sample Files (and/or any modified version) in
- any way you find useful, provided that you agree that
- Microsoft has no warranty obligations or liability for any
- Sample Application Files which are modified.
-
- we do not recomend you base your game on IKlowns, start with one of
- the other simpler sample apps in the GDK
-
- **************************************************************************/
-
- //** include files **
- #include <windows.h>
- #include <windowsx.h>
- #include <mmsystem.h>
- #include "linklist.h"
- #include "cgwave.h"
- #include "cgsound.h"
- #include "cgglobl.h"
-
- //** local definitions **
- // Default volume and panning values
- #define MINFREQ_TB 0
- #define MAXFREQ_TB 512
- #define MINPAN_TB 0
- #define MAXPAN_TB 127
- #define MIDPAN_TB 64
- #define MINVOL_TB 0
- #define MAXVOL_TB 127
-
- #define INITVOL_TB 0
- #define INITPAN_TB 0
-
- //** external functions **
- //** external data **
- //** public data **
- BOOL gbQuiet = FALSE;
-
- //** private data **
-
- // To prevent having more than one instance of the same
- // sound in memory, a list of all sounds is kept.
- static CLinkedList *mpWaveList = NULL;
-
- // In order to turn off/on sounds we need a list of actively playing
- // sounds.
- static CLinkedList *pNowPlayingList = NULL;
-
- // Pointer to DirectSound object.
- static LPDIRECTSOUND gpds = NULL;
- static LPDIRECTSOUNDBUFFER lpDSPrimaryBuffer=NULL;
-
- // Handle of WaveOut device.
- static HWAVEOUT hWaveDevice = NULL;
-
- // ----------------------------------------------------------
- // CreatePrimarySoundBuffer - create a primary sound buffer
- // for direct sound. Every DirectSound app needs to have
- // one (and only one) DirectSound buffer.
- // ----------------------------------------------------------
- BOOL CreatePrimarySoundBuffer()
- {
- DSBUFFERDESC dsbd; // buffer description struct
- MMRESULT mmResult; // result of sound calls
-
- // Check if we already have a primary buffer
- if (gpds != NULL)
- {
- return(TRUE);
- }
-
- // Create the Direct Sound Object
- if (DirectSoundCreate(NULL,&gpds, NULL) != 0)
- {
- return(FALSE);
- }
- if( gpds->SetCooperativeLevel( ghMainWnd, DSSCL_NORMAL ) != DS_OK )
- {
- return(FALSE);
- }
-
- // Set up the primary direct sound buffer.
- memset(&dsbd, 0, sizeof(DSBUFFERDESC));
- dsbd.dwSize = sizeof(DSBUFFERDESC);
- dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
- dsbd.dwBufferBytes = 0;
-
- dsbd.lpwfxFormat = NULL;
-
- if ((mmResult = gpds->CreateSoundBuffer(&dsbd,
- &lpDSPrimaryBuffer,
- NULL)) != 0)
- {
- return(FALSE);
- }
-
- return(TRUE);
- }
-
- // ----------------------------------------------------------
- // InitalizeWaveDevice - be sure wave method is ready for use.
- // ----------------------------------------------------------
- BOOL InitializeWaveDevice(
- int WaveMode, // sndPlaySound, waveOut, or DirectSound
- LPWAVEFORMATEX pFormat // default wave format
- )
- {
- // If we are doing waveOut's then we need a handle to the device
- // driver.
- if (WaveMode == USE_WAVEOUT)
- {
-
- // If there isn't a wave device already open, open one
- // using the given format
- if (hWaveDevice == NULL)
- {
- if (waveOutOpen((LPHWAVEOUT)&hWaveDevice
- , WAVE_MAPPER, pFormat, NULL, 0L, 0))
- {
- return(FALSE);
- }
- }
-
- // Must be using DirectSound, make sure we have a primary buffer
- } else {
-
- if (pNowPlayingList == NULL)
- {
- pNowPlayingList = new CLinkedList;
- }
-
- // Create a DirectSound primary buffer
- return (CreatePrimarySoundBuffer());
- }
- return(TRUE);
- }
-
-
- // ----------------------------------------------------------
- // LoadWaveData - read .WAV file information into appropriate
- // memory buffer for playback.
- // ----------------------------------------------------------
- LPWAVEHDR LoadWaveData(
- CSoundEffect *pSound, // sound effect instance
- LPSTR WaveName, // .WAV filename
- int WaveMode // sndPlaySound, waveOut or DirectSound
- )
- {
- LPBYTE lpData = NULL;
- LPWAVEHDR lpWaveHdr = NULL;
- DWORD dwDataSize = 0;
- LPDIRECTSOUNDBUFFER lpDirectSoundBuffer = NULL;
- LPWAVEFORMATEX pwfxInfo = NULL;
-
- // Check to be sure a non-null name was given
- if ((WaveName == NULL) || (*WaveName == '\0'))
- {
- return(NULL);
- }
-
- // For sndPlaySound, we just read the whole file into a buffer
- if (WaveMode == USE_SNDPLAY)
- {
- HANDLE hFile = CreateFile(WaveName, GENERIC_READ, FILE_SHARE_READ
- , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- {
- return(NULL);
- }
-
- dwDataSize = GetFileSize(hFile, NULL);
- if ((dwDataSize == 0xFFFFFFFF) || (dwDataSize == 0))
- {
- CloseHandle(hFile);
- return(NULL);
- }
-
- // Allocate and lock memory for the waveform data. The memory
- // for waveform data must be globally allocated with
- // GMEM_MOVEABLE and GMEM_SHARE flags.
- if ((lpData = (LPBYTE) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE
- , dwDataSize)) == NULL)
- {
- CloseHandle(hFile);
- return(NULL);
- }
-
- // Read the whole wave file in
- ReadFile(hFile, lpData, dwDataSize, &dwDataSize, NULL);
- CloseHandle(hFile);
-
- // Either DirectSound or WaveOut
- } else {
-
- HMMIO hmmioIn;
- MMCKINFO ckInRiff;
- MMCKINFO ckIn;
- UINT cbActualRead;
- UINT cbSize;
-
- pwfxInfo = NULL;
- cbSize = 0;
-
- // Use routines in CGWAVE to open the sound file and
- // parse the data in it.
- if (WaveOpenFile(WaveName, &hmmioIn, &pwfxInfo, &ckInRiff) != 0)
- {
- if (pwfxInfo != NULL)
- GlobalFree(pwfxInfo);
- return(NULL);
- }
-
- // Be sure we have an output device ready to play the sound
- if (!InitializeWaveDevice(WaveMode, pwfxInfo))
- {
- mmioClose(hmmioIn, 0);
- if (pwfxInfo != NULL)
- GlobalFree(pwfxInfo);
- return(NULL);
- }
-
-
- // Position the wave file for reading the wave data
- if (WaveStartDataRead(&hmmioIn, &ckIn, &ckInRiff) != 0)
- {
- mmioClose(hmmioIn, 0);
- if (pwfxInfo != NULL)
- GlobalFree(pwfxInfo);
- return(NULL);
- }
-
- // Ok, size of wave data is in ckIn, allocate that buffer.
- dwDataSize = ckIn.cksize;
-
- // waveOut requires that we allocate the data
- if (WaveMode == USE_WAVEOUT)
- {
-
- if ((lpData = (LPBYTE)GlobalAlloc(GMEM_FIXED
- , dwDataSize)) == NULL)
- {
- mmioClose(hmmioIn, 0);
- if (pwfxInfo != NULL)
- GlobalFree(pwfxInfo);
- return(NULL);
- }
- // For DirectSound, we let it allocate the buffer for us.
- } else {
- DSBUFFERDESC dsbd;
- DWORD dwBSize;
- DWORD dwWrapBSize;
- LPVOID lpWrapPtr;
- DWORD dw; // size place holder
-
- // Set up the secondary direct sound buffer.
- memset(&dsbd, 0, sizeof(DSBUFFERDESC));
- dsbd.dwSize = sizeof(DSBUFFERDESC);
- dsbd.dwFlags = DSBCAPS_CTRLDEFAULT;
- dsbd.dwBufferBytes = dwDataSize;
- dw = pwfxInfo->cbSize + sizeof(WAVEFORMATEX);
-
- if ((dsbd.lpwfxFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GPTR|GMEM_ZEROINIT, dw)) == NULL)
- {
- mmioClose(hmmioIn, 0);
- return NULL;
- }
-
- // Setup the format, frequency, volume, etc.
- memcpy( dsbd.lpwfxFormat, pwfxInfo, dw );
-
- if (gpds->CreateSoundBuffer(&dsbd,
- &lpDirectSoundBuffer,
- NULL) != 0)
- {
- GlobalFreePtr(dsbd.lpwfxFormat);
- mmioClose(hmmioIn, 0);
- return NULL;
- }
-
- GlobalFreePtr(dsbd.lpwfxFormat);
-
- // Need to lock the buffer so that we can write to it!
- if (lpDirectSoundBuffer->Lock(
- 0,
- dwDataSize,
- (LPLPVOID)&lpData,
- &dwBSize,
- &lpWrapPtr,
- &dwWrapBSize,
- 0L) != 0)
- {
- mmioClose(hmmioIn, 0);
- return NULL;
- } else {
- dwDataSize = dwBSize;
- }
- }
-
- // Now read the actual wave data into the data buffer.
- if (WaveReadFile(hmmioIn, dwDataSize, lpData, &ckIn
- , &cbActualRead) != 0)
- {
- // Data didn't get read for some reason!
- if (pwfxInfo != NULL)
- GlobalFree(pwfxInfo);
- if (lpData != NULL)
- GlobalFree(lpData);
- }
-
- // Save info on size of data and close the wave file
- cbSize = cbActualRead;
- dwDataSize = cbSize;
- mmioClose(hmmioIn, 0);
-
- // If we have a DirectSound buffer, set format & unlock it.
- if (lpDirectSoundBuffer != NULL)
- {
- lpDirectSoundBuffer->Unlock(lpData, cbActualRead, NULL, 0 );
- }
-
- }
-
- // Allocate and lock memory for the header. This memory must
- // also be globally allocated with GMEM_MOVEABLE and GMEM_SHARE flags.
- if ((lpWaveHdr = (LPWAVEHDR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE
- , (DWORD)sizeof(WAVEHDR))) == NULL) {
- GlobalFreePtr(lpData);
- return(NULL);
- }
-
- // After allocation, set up and prepare header. This struct will be
- // used to play the sounds at the appropriate time.
- lpWaveHdr->lpData = (LPSTR) lpData;
- lpWaveHdr->dwBufferLength = dwDataSize;
- lpWaveHdr->dwFlags = DSBCAPS_CTRLDEFAULT;
- lpWaveHdr->dwLoops = 0L;
-
- pSound->pWaveInfo->lpDirectSoundBuffer = lpDirectSoundBuffer;
- pSound->pWaveInfo->pwfxInfo = pwfxInfo;
-
- return(lpWaveHdr);
- }
-
- // ----------------------------------------------------------
- // CSoundEffect constructor
- // ----------------------------------------------------------
- CSoundEffect::CSoundEffect(
- LPSTR WaveName, // name of wave file
- DWORD Id, // object id to allow one buffer per object
- BOOL fLoopIt, // loop the sound when played
- int WaveMode // sndPlaySound, waveOut or DirectSound
- )
- {
- pWaveInfo = NULL;
- fLoop = fLoopIt;
- fPlaying = FALSE;
-
- // Guard against wise-guys (we don't mind)
- if ((WaveName == NULL) || (*WaveName == '\0'))
- return;
-
- CSoundEffect::WaveMode = WaveMode;
- curVolume = MAXVOL_TB;
- fMuted = FALSE;
-
- // initialize wave list if necessary
- if (!mpWaveList)
- {
- mpWaveList = new CLinkedList;
- } else {
- // Need to search wave list to see if we already have this
- // wave data cached.
- WAVE_ENTRY *pCurBuffer = (WAVE_ENTRY *) mpWaveList->GetFirst();
-
- while (pCurBuffer && !pWaveInfo)
- {
- // Do the name and mode match?
- if ((lstrcmpi( WaveName, pCurBuffer->mpFileName ) == 0 )
- && (pCurBuffer->hObjectId == Id)
- && (pCurBuffer->WaveMode == WaveMode))
- {
- pCurBuffer->mRefCount++;
- pWaveInfo = pCurBuffer;
- } else {
- pCurBuffer = (WAVE_ENTRY *) mpWaveList->GetNext();
- }
- }
- }
-
- // if we didn't find the wave file in our cache, we need to load it
- if (!pWaveInfo)
- {
- LPWAVEHDR pWaveHdr;
- pWaveInfo = new WAVE_ENTRY;
-
- // Load up the wave data
- if (pWaveHdr = LoadWaveData(this, WaveName, WaveMode))
- {
- // Add to list of known sounds
- pWaveInfo->mpFileName = new char [lstrlen(WaveName)+1];
- lstrcpy(pWaveInfo->mpFileName, WaveName);
- pWaveInfo->pWaveHdr = pWaveHdr;
- pWaveInfo->WaveMode = WaveMode;
- pWaveInfo->hObjectId = Id;
- pWaveInfo->mRefCount = 1;
- mpWaveList->Append( pWaveInfo );
- lpDirectSoundBuffer = pWaveInfo->lpDirectSoundBuffer;
- } else {
- delete pWaveInfo;
- pWaveInfo = NULL;
- }
- } else {
- lpDirectSoundBuffer = pWaveInfo->lpDirectSoundBuffer;
- }
- }
-
- // ----------------------------------------------------------
- // CSoundEffect destructor
- // ----------------------------------------------------------
- CSoundEffect::~CSoundEffect()
- {
- Stop();
-
- if ((pWaveInfo != NULL) && (--pWaveInfo->mRefCount == 0))
- {
- mpWaveList->Remove( pWaveInfo );
-
- delete pWaveInfo->mpFileName;
- if (lpDirectSoundBuffer != NULL)
- {
- lpDirectSoundBuffer->Release();
- } else {
- GlobalFreePtr(pWaveInfo->pWaveHdr->lpData);
- }
- GlobalFreePtr(pWaveInfo->pwfxInfo);
- GlobalFreePtr(pWaveInfo->pWaveHdr);
- delete pWaveInfo; // causes GP fault why?
- pWaveInfo = NULL;
- // !!! should we delete the list if empty?
- }
- }
-
- // ----------------------------------------------------------
- // Play -
- // ----------------------------------------------------------
- void CSoundEffect::Play()
- {
- if ((pWaveInfo == NULL) || (gbQuiet))
- {
- return;
- }
-
- fPlaying = TRUE;
-
- switch (WaveMode)
- {
- case USE_WAVEOUT:
- if (pWaveInfo->pWaveHdr != NULL)
- {
- pWaveInfo->pWaveHdr->dwLoops = fLoop;
- waveOutPrepareHeader(hWaveDevice, pWaveInfo->pWaveHdr, sizeof(WAVEHDR));
- waveOutWrite(hWaveDevice, pWaveInfo->pWaveHdr, sizeof(WAVEHDR));
- waveOutUnprepareHeader(hWaveDevice, pWaveInfo->pWaveHdr, sizeof(WAVEHDR));
- }
- break;
-
- case USE_DSOUND:
- if (lpDirectSoundBuffer != NULL)
- {
-
- // Add sound to active list if it isn't already there
- CSoundEffect *pTemp = (CSoundEffect *)pNowPlayingList->GetFirst();
- while (pTemp != NULL)
- {
- if (pTemp == this)
- {
- break;
- }
- pTemp = (CSoundEffect *)pNowPlayingList->GetNext();
- }
-
- if (pTemp == NULL)
- {
- pNowPlayingList->Add(this);
- }
-
- if( fLoop )
- {
- lpDirectSoundBuffer->SetCurrentPosition( 0 );
- lpDirectSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
- }
- else
- {
- lpDirectSoundBuffer->SetCurrentPosition( 0 );
- lpDirectSoundBuffer->Play(0, 0, 0);
- }
- }
- break;
-
- case USE_SNDPLAY:
- if (pWaveInfo->pWaveHdr != NULL)
- {
- UINT flags = SND_MEMORY | SND_ASYNC;
-
- if (fLoop)
- flags |= SND_LOOP;
-
- sndPlaySound((LPSTR)(pWaveInfo->pWaveHdr->lpData), flags);
- }
- break;
- }
- }
-
- // ----------------------------------------------------------
- // Stop -
- // ----------------------------------------------------------
- void CSoundEffect::Stop()
- {
- if ((pWaveInfo == NULL) || (!fPlaying))
- {
- return;
- }
-
- fPlaying = FALSE;
-
- switch (WaveMode)
- {
- case USE_WAVEOUT:
- if (pWaveInfo->pWaveHdr != NULL)
- {
- waveOutReset(hWaveDevice);
- }
- break;
- case USE_DSOUND:
- if (lpDirectSoundBuffer != NULL)
- {
- pNowPlayingList->Remove(this);
- lpDirectSoundBuffer->Stop();
- }
- break;
-
- case USE_SNDPLAY:
- if (pWaveInfo->pWaveHdr != NULL)
- {
- sndPlaySound(NULL, SND_ASYNC);
- }
- break;
- }
- }
-
- // ----------------------------------------------------------
- // SetPan -
- // ----------------------------------------------------------
- void CSoundEffect::SetPan(int PanFactor)
- {
- // if ((pWaveInfo == NULL) || (curPan == PanFactor))
- if (pWaveInfo == NULL)
- {
- return;
- }
-
- curPan = PanFactor;
-
- switch (WaveMode)
- {
- case USE_WAVEOUT:
- break;
- case USE_DSOUND:
- if (lpDirectSoundBuffer != NULL)
- {
- // parameter pan is 0-127; dsound's pan is -10000 to +10000
- long absPan;
- absPan = (10000 * ((curPan-64)>>2)) / 128;
- lpDirectSoundBuffer->SetPan(absPan);
- }
- break;
- case USE_SNDPLAY:
- break;
- }
- }
-
-
- // ----------------------------------------------------------
- // SetVolume -
- // ----------------------------------------------------------
- void CSoundEffect::SetVolume(int Volume)
- {
- if ((pWaveInfo == NULL) || (curVolume == Volume))
- {
- return;
- }
-
- curVolume = Volume;
- switch (WaveMode)
- {
- case USE_WAVEOUT:
- break;
- case USE_DSOUND:
- if (lpDirectSoundBuffer != NULL)
- {
- // parameter volume is 0-127; dsound's volume is -10000 to 0
- long absVolume;
- absVolume = -((127-curVolume)<<4);
- lpDirectSoundBuffer->SetVolume(absVolume);
- }
- break;
- case USE_SNDPLAY:
- break;
- }
- }
-
-
- // ----------------------------------------------------------
- // SetMute -
- // ----------------------------------------------------------
- void CSoundEffect::SetMute(BOOL fMute)
- {
-
- if ((pWaveInfo == NULL) || (fMuted == fMute))
- {
- return;
- }
-
- fMuted = fMute;
-
- if (fMuted)
- {
- Stop();
- } else {
- Play();
- }
- }
-
- void SetSilence(BOOL fQuiet)
- {
- gbQuiet = fQuiet;
- if (lpDSPrimaryBuffer != NULL)
- {
- CSoundEffect *lpSound = (CSoundEffect *)pNowPlayingList->GetFirst();
- while (lpSound != NULL)
- {
-
- if (fQuiet)
- {
- lpSound->lpDirectSoundBuffer->Stop();
- } else {
- if (lpSound->fLoop)
- {
- lpSound->lpDirectSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
- } else {
- lpSound->lpDirectSoundBuffer->Play(0, 0, 0);
- }
- }
-
- lpSound = (CSoundEffect *)pNowPlayingList->GetNext();
- }
- }
- }
-