Figure 3 WAVE.C
#include "windonut.h"
/*=================================================================
*
* File: wave.c
* Content: Routines for getting waves from files or resources
*
******************************************************************/
#include <windowsx.h>
#include <mmsystem.h>
typedef struct tagWAVEFILE
{
DWORD cbSize; // Size of file
LPWAVEFORMATEX pwfxInfo; // Wave Header
LPBYTE pbData; // Wave Bits
}
WAVEFILE, *LPWAVEFILE;
static const char c_szWAV[] = "WAV";
// Function Prototypes
BOOL wave_ParseWaveMemory(void *pvRes,
WAVEFORMATEX **ppWaveHeader,
BYTE **ppbWaveData,
DWORD *pcbWaveSize);
/////////////////////////////////////////////////////////////////
//
// WAVE_LoadResource: Gets a wave file into the memory pointed
// to by pWaveFile from a resource.
//
/////////////////////////////////////////////////////////////////
LPVOID WAVE_LoadResource
(LPSTR lpName, // Filename to use
HANDLE hModule, // hInst of app with WAVE
LPWAVEFILE pWaveFile) // Points to the struct to fill
{
HRSRC hResInfo;
HGLOBAL hResData;
void *pvRes;
DWORD dwSize;
LPVOID lpMemory;
// Find the resource and load into memory
if (((hResInfo = FindResource(hModule, lpName, c_szWAV)) != NULL) &&
((hResData = LoadResource(hModule, hResInfo)) != NULL) &&
((pvRes = LockResource(hResData)) != NULL))
{
// If we found it, copy the bits from the resource into
// our own chunk of memory
dwSize = GlobalSize(pvRes);
lpMemory = MEMORY_New (dwSize);
memcpy (lpMemory, pvRes, dwSize);
UnlockResource(hResData);
FreeResource(hResData);
// Parse it out
if (wave_ParseWaveMemory(lpMemory,
&(pWaveFile->pwfxInfo),
&(pWaveFile->pbData),
&(pWaveFile->cbSize)))
{
return lpMemory; // OK
}
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
//
// WAVE_LoaFile: Gets a wave file into the memory
// pointed to by pWaveFile from a file
//
///////////////////////////////////////////////////////////////////////////////
LPVOID WAVE_LoadFile
(LPSTR szFileName, // Filename to use
LPWAVEFILE pWaveFile) // Points to the struct to fill
{
HANDLE hFile;
OFSTRUCT of;
LPVOID lpMemory;
DWORD dwSize, dwSizeRead;
// Open da file and read da bits
hFile = (HANDLE)OpenFile ( szFileName, &of, OF_READ );
dwSize = _llseek ( (HFILE)hFile, 0, FILE_END );
_llseek ( (HFILE)hFile, 0, FILE_BEGIN );
lpMemory = MEMORY_New (dwSize);
if (!ReadFile(hFile, lpMemory, dwSize, &dwSizeRead, NULL ))
MessageBox ( NULL, "Readfile failed", "", MB_OK );
_lclose ( (HFILE)hFile );
// Parse it out
if (wave_ParseWaveMemory(lpMemory,
&(pWaveFile->pwfxInfo),
&(pWaveFile->pbData),
&(pWaveFile->cbSize)))
{
return lpMemory; // OK
}
return NULL; // Failure
}
//////////////////////////////////////////////////////////////////
//
// wave_ParseWaveMemory
// Parses a chunk of memory into the header and samples.
// This is done by looking for the "fmt " and "data"
// fields in the memory.
//
//////////////////////////////////////////////////////////////////
BOOL wave_ParseWaveMemory
(LPVOID lpChunkOfMemory, // Points to raw ram
LPWAVEFORMATEX *lplpWaveHeader, // Points to pointer to header
LPBYTE *lplpWaveSamples,// Points to pointer to samples
LPDWORD lpcbWaveSize) // Points to size
{
LPDWORD pdw;
LPDWORD pdwEnd;
DWORD dwRiff;
DWORD dwType;
DWORD dwLength;
// Set defaults to NULL or zero
if (lplpWaveHeader)
*lplpWaveHeader = NULL;
if (lplpWaveSamples)
*lplpWaveSamples = NULL;
if (lpcbWaveSize)
*lpcbWaveSize = 0;
// Set up DWORD pointers to the start of the chunk
// of memory.
pdw = (DWORD *)lpChunkOfMemory;
// Get the type and length of the chunk of memory
dwRiff = *pdw++;
dwLength = *pdw++;
dwType = *pdw++;
// Using the mmioFOURCC macro (part of Windows SDK), ensure
// that this is a RIFF WAVE chunk of memory
if (dwRiff != mmioFOURCC('R', 'I', 'F', 'F'))
return FALSE; // not even RIFF
if (dwType != mmioFOURCC('W', 'A', 'V', 'E'))
return FALSE; // not a WAV
// Find the pointer to the end of the chunk of memory
pdwEnd = (DWORD *)((BYTE *)pdw + dwLength-4);
// Run through the bytes looking for the tags
while (pdw < pdwEnd)
{
dwType = *pdw++;
dwLength = *pdw++;
switch (dwType)
{
// Found the format part
case mmioFOURCC('f', 'm', 't', ' '):
if (lplpWaveHeader && !*lplpWaveHeader)
{
if (dwLength < sizeof(WAVEFORMAT))
return FALSE; // something's wrong! Not a WAV
// Set the lplpWaveHeader to point to this part of
// the memory chunk
*lplpWaveHeader = (LPWAVEFORMATEX)pdw;
// Check to see if the other two items have been
// filled out yet (the bits and the size of the
// bits). If so, then this chunk of memory has
// been parsed out and we can exit
if ((!lplpWaveSamples || *lplpWaveSamples) &&
(!lpcbWaveSize || *lpcbWaveSize))
{
return TRUE;
}
}
break;
// Found the samples
case mmioFOURCC('d', 'a', 't', 'a'):
if ((lplpWaveSamples && !*lplpWaveSamples) ||
(lpcbWaveSize && !*lpcbWaveSize))
{
// Point the samples pointer to this part of the
// chunk of memory.
if (lplpWaveSamples) *lplpWaveSamples = (LPBYTE)pdw;
// Set the size of the wave
if (lpcbWaveSize) *lpcbWaveSize = dwLength;
// Make sure we have our header pointer set up.
// If we do, we can exit
if (!lplpWaveHeader || *lplpWaveHeader)
return TRUE;
}
break;
} // End case
// Move the pointer through the chunk of memory
pdw = (DWORD *)((BYTE *)pdw + ((dwLength+1)&~1));
}
// Failed! If we made it here, we did not get all the peices
// of the wave
return FALSE;
}
Figure 4 dwFlags in DSBUFFERDESC
Buffer Description Flags | Description |
DSBCAPS_PRIMARYBUFFER | Buffer is a primary sound buffer. |
DSBCAPS_STATIC | Indicates that the buffer will be used for static sound data. Typically used for buffers that are loaded once and played many times. These buffers are candidates for hardware (on-board) memory. |
DSBCAPS_LOCHARDWARE | Forces the buffer to use hardware mixing, even if DSBCAPS_STATIC is not specified. If this device does not support hardware mixing or the required hardware memory is not available, CreateSoundBuffer will fail. Note that there is no guarantee that a mixing channel will be available for this buffer-the application must make sure of this. |
DSBCAPS_LOCSOFTWARE | Forces the buffer to be stored in software (main system) memory and use software mixing, even if DSBCAPS_STATIC is specified and hardware resources are available. |
DSBCAPS_CTRLALL | Buffer must have all control capabilities. |
DSBCAPS_CTRLDEFAULT | Buffer should have default control options. This is the same as specifying the Pan, Volume, and Frequency flags. |
DSBCAPS_CTRLFREQUENCY | Buffer must have frequency control capability. |
DSBCAPS_CTRLPAN | Buffer must have pan control capability. |
DSBCAPS_CTRLVOLUME | Buffer must have volume control capability. |
Figure 5 Creating a Sound Buffer
// These variables are used in Steps 1 and 2
LPVOID lpWaveData;
WAVEFILE WaveFile;
DSBUFFERDESC dsbd;
LPDIRECTSOUNDBUFFER lpDSB;
// These variables are used in step 3, further down below
BYTE *pbData = NULL;
BYTE *pbData2 = NULL;
DWORD dwLength;
DWORD dwLength2;
lpWaveData = WAVE_LoadFile ( "Tub Full of Tires.wav", &WaveFile );
// Step 1: Set up the direct sound buffer.
memset(&dsbd, 0, sizeof(DSBUFFERDESC));
dsbd.dwSize = sizeof(DSBUFFERDESC);
// We want a buffer that lives on the sound card's memory
// (DSBCAPS_STATIC) and can have the pan, volume, and
// frequency adjusted (DSBCAPS_CTRLDEFAULT)
dsbd.dwFlags = DSBCAPS_CTRLDEFAULT | DSBCAPS_STATIC ;
// Set up the size and format
dsbd.dwBufferBytes = WaveFile.cbSize;
dsbd.lpwfxFormat = WaveFile.pwfxInfo; // Must be a PCM format!
// Step 2: Create the buffer
if (DS_OK != lpDirectSound->lpVtbl->
CreateSoundBuffer(lpDirectSound,
&dsbd,
&lpDSB),
NULL))
{
MessageBox (hWnd,
"Silent Bob sez CreateSoundBuffer failed",
"MSJ",
MB_OK );
}
Figure 6 Copying Chunks of Data into a Buffer
// Lock down the DirectSound buffer
if (DS_OK == lpDSB->lpVtbl->
Lock(lpDSB, // Points to DirectSoundBuffer
0, // Offset into buffer to start writing
WaveFile.cbSize, // Size of wave file to copy in
&pbData, // Points to first block of sound data
&dwLength, // Length of first block of data
&pbData2, // Points to second block of sound data
&dwLength2, // Length of second block of data
0L)) // Flags
{
// Copy first chunk
memcpy(pbData, WaveFile.pbData, dwLength);
// Copy second chunk
if (dwLength2) memcpy(pbData2, WaveFile.pbData+dwLength , dwLength2);
// Free up the memory allocated in the WAVE_LoadFile function, since we
// have copied it to the buffer
MEMORY_Delete (lpWaveData);
// Unlock the buffer
if (DS_OK != lpDSB->lpVtbl->Unlock(pbData, dwLength, pbData2, dwLength2))
{
MessageBox (hWnd, "Silent Bob sez Unlock failed", "MSJ", MB_OK );
}
}
else
{
MessageBox (hWnd, "Silent Bob sez Lock failed", "MSJ", MB_OK );
}
Figure 8 Playing a Sound Buffer
// Set position back to zero. Works if buffer is stopped or playing.
lpDSB->lpVtbl->SetCurrentPosition( lpDSB, 0 );
// Play buffer from current position. Has no effect on playing buffers,
// except if you specific new flags (last parameter), in which case the
// new flag value will replace the old flag value).
lpDSB->lpVtbl->Play( lpDSB, 0, 0, 0 );
// Get the status of the wave pointed to by lpDSB
if (DS_OK == lpDSB->lpVtbl->
GetStatus(lpDSB, &dwStatus))
{
if (dwStatus & DSBPLAY_PLAYING)
{
// Don't bother playing, just restart
if (DS_OK != lpDSB->lpVtbl->
SetCurrentPosition(lpDSB, 0))
{
MessageBox (hWnd, "Silent Bob sez SetCurrentPosition failed",
"MSJ", MB_OK );
} // endif scp
} // endif dwStatus
else
{
if (DS_OK != lpDSB->lpVtbl->
Play(lpDSB, // Buffer to play
0, // Reserved1
0, // Reserved2
0)) // Zero or DSBPLAY_LOOPING
{
MessageBox (hWnd, "Silent Bob sez Play failed",
"MSJ", MB_OK );
}
}
} // endif GetStatus
else
{
MessageBox (hWnd, "Silent Bob sez GetStatus failed",
"MSJ", MB_OK );
}
Figure 9 Doubling the Sampling Rate
// Find out the current sample rate
if (DS_OK == lpDS->lpVtbl->GetFrequency (lpDS, &dwFrequency))
{
// Double it, keeping it under 100000
dwFrequency = min (dwFrequency*2, 100000);
// Set the new rate
if (DS_OK != SetFrequency (lpDS, dwFrequency))
{
MessageBox (hWnd, "Silent Bob sez SetFrequency failed", "MSJ", MB_OK );
}
}
else
{
// Frequency will fail if the DirectSound buffer
// was not created with the DSBCAPS_CTRLFREQUENCY
// flag (or DSBCAPS_CTRLDEFAULT).
MessageBox (hWnd, "Silent Bob sez GetFrequency failed", "MSJ", MB_OK );
}
Figure 10 DirectSound Wrapper Functions
DIRECTSOUND_Enable | Enables DirectSound |
DIRECTSOUND_Disable | Disables DirectSound |
DIRECTSOUND_LoadWave | Loads a WAV file into a buffer |
DIRECTSOUND_UnLoad | Unloads and destroys a buffer |
DIRECTSOUND_Play | Plays a buffer |
DIRECTSOUND_Stop | Stops playing a buffer |
DIRECTSOUND_IsPlaying | Checks to see if a buffer is playing |
DIRECTSOUND_SetFrequency | Setsabuffer's frequency |
DIRECTSOUND_SetBalance | Sets a buffer's left to right balance |
DIRECTSOUND_SetVolume | Sets a buffer's volume |
DIRECTSOUND_SetLooping | Sets a buffer's looping |
DIRECTSOUND_GetFrequency | Gets a buffer's freuqnecy |
DIRECTSOUND_GetBalance | Gets a buffer's balance |
DIRECTSOUND_GetVolume | Gets a buffer's volume |
DIRECTSOUND_GetLooping | Gets a buffer's looping status |
Figure 12 JOYINFOEX
typedef struct joyinfoex_tag
{
DWORD dwSize; // Sizeof (JOYINFOEX)
DWORD dwFlags; // See documentation
DWORD dwXpos; // X position
DWORD dwYpos; // Y position
DWORD dwZpos; // Z position
DWORD dwRpos; // Rudder position
DWORD dwUpos; // U position (5th axis, such as throttle)
DWORD dwVpos; // V position (6th axis)
DWORD dwButtons; // Button states
DWORD dwButtonNumber; // Number of the button depressed
DWORD dwPOV; // Point-of-view Hat (See Figure 13)
DWORD dwReserved1; // Off-limits!
DWORD dwReserved2; // Don't touch!
}
JOYINFOEX;
Figure 13 Point of View Hat Values
POV Flags | Defined Value | Meaning |
JOY_POVBACKWARD | 18,000 | POV Hat is being pressed backward. |
JOY_POVCENTERED | -1 | POV Hat is in the neutral position. 1 means no angle. |
JOY_POVFORWARD | 0 | POV Hat is being pressed forward. 0 represents 0.00 degrees or straight ahead. |
JOY_POVLEFT | 27,000 | POV Hat is being pressed to the left. 27,000 represents 270.00 degrees or 90.00 to the left. |
JOY_POVRIGHT | 9,000 | POV Hat is being pressed to the right. |
Figure 14 Converting the x Axis Range
enum
{
JOYDIR_LEFT,
JOYDIR_RIGHT,
JOYDIR_CENTER
};
JOYINFOEX JoyInfoEx;
int iJoyPos;
BOOL bButton1, bButton2;
JoyInfoEx.dwSize = sizeof(JoyInfoEx);
JoyInfoEx.dwFlags = JOY_RETURNX | JOY_RETURNBUTTONS;
joyGetPosEx ( JOYSTICKID1, &JoyInfoEx );
if (JoyInfoEx.dwXpos < 16384)
iJoyPos = JOYDIR_LEFT;
else
if (JoyInfoEx.dwXpos > 65535-16384)
iJoyPos = JOYDIR_RIGHT;
else
iJoyPos = JOYDIR_CENTER;
bButton1 = (BOOL)(JoyInfoEx.dwButtons & JOY_BUTTON1);
bButton2 = (BOOL)(JoyInfoEx.dwButtons & JOY_BUTTON2);
Figure 15 ReadJoyStick
void ReadJoyStick ( void )
{
JOYINFOEX JoyInfoEx;
// In case the call to joyGetPosEx fails (for reasons
// such as the user doesn't have a joystick), set the
// defaults to benign values
giJoyPos = JOYDIR_CENTER;
gbButton1 = FALSE;
gbButton2 = FALSE;
// Get the joystick's X-axis and buttons
JoyInfoEx.dwSize = sizeof(JoyInfoEx);
JoyInfoEx.dwFlags = JOY_RETURNX | JOY_RETURNBUTTONS;
// We will always use Joystick #1
if (JOYERR_NOERROR == joyGetPosEx ( JOYSTICKID1, &JoyInfoEx ))
{
// Check for the ranges to be at least halfway left
// or right and convert into our constants
if (JoyInfoEx.dwXpos < 16384)
giJoyPos = JOYDIR_LEFT;
else
if (JoyInfoEx.dwXpos > 65535-16384)
giJoyPos = JOYDIR_RIGHT;
// Read the buttons
gbButton1 = (BOOL)(JoyInfoEx.dwButtons & JOY_BUTTON1);
gbButton2 = (BOOL)(JoyInfoEx.dwButtons & JOY_BUTTON2);
}
}