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.
18,000 represents 180.00 degrees or back.

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.
9,000 represents 90.00 degrees or 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);
    }
}