Click here to Skip to main content
12,359,458 members (56,657 online)
Click here to Skip to main content

Stats

325K views
11.3K downloads
139 bookmarked
Posted

Playing .WAV files using the Windows Multi-Media Library

, 25 Mar 2004
This is a simple program that uses the Windows Multi-Media Library
programs
DirectX
Games
bin
wavetest.exe
src
Sound
wavelib
makefile
test
makefile
inc
/***********************************************************************
 * wavelib.c
 *  
 *    Audio Library
 *
 *
 *  Supports .WAV files, Very Simplistic Parser
 *
 *
 * Toby Opferman Copyright (c) 2003
 *
 ***********************************************************************/
 
 
 #include <windows.h>
 #include <mmsystem.h>
 #include <wavelib.h>
 
 
 
 
 /***********************************************************************
  * Internal Structures
  ***********************************************************************/
typedef struct {
    
    UCHAR IdentifierString[4];
    DWORD dwLength;

} RIFF_CHUNK, *PRIFF_CHUNK;


typedef struct {

    WORD  wFormatTag;         // Format category
    WORD  wChannels;          // Number of channels
    DWORD dwSamplesPerSec;    // Sampling rate
    DWORD dwAvgBytesPerSec;   // For buffer estimation
    WORD  wBlockAlign;        // Data block size
    WORD  wBitsPerSample;
    

} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER;


typedef struct _wave_sample {

     WAVEFORMATEX WaveFormatEx;
     char *pSampleData;
     UINT Index;
     UINT Size;
     DWORD dwId;
     DWORD bPlaying;
     struct _wave_sample *pNext;

} WAVE_SAMPLE, *PWAVE_SAMPLE;
 
#define SAMPLE_SIZE    (2*2*2000) 

typedef struct {
     
     HWAVEOUT hWaveOut;
     HANDLE hEvent;
     HANDLE hThread;
     WAVE_SAMPLE WaveSample;
     BOOL bWaveShouldDie;
     WAVEHDR WaveHdr[8];
     char AudioBuffer[8][SAMPLE_SIZE];
     BOOL bPaused;

} WAVELIB, *PWAVELIB;



 /***********************************************************************
  * Internal Functions
  ***********************************************************************/
void CALLBACK WaveLib_WaveOutputCallback(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
BOOL WaveLib_OpenWaveSample(CHAR *pFileName, PWAVE_SAMPLE pWaveSample);
void WaveLib_WaveOpen(HWAVEOUT hWaveOut, PWAVELIB pWaveLib);
void WaveLib_WaveDone(HWAVEOUT hWaveOut, PWAVELIB pWaveLib);
DWORD WINAPI WaveLib_AudioThread(PVOID pDataInput);
void WaveLib_CreateThread(PWAVELIB pWaveLib);
void WaveLib_SetupAudio(PWAVELIB pWaveLib);
void WaveLib_WaveClose(HWAVEOUT hWaveOut, PWAVELIB pWaveLib);
void WaveLib_AudioBuffer(PWAVELIB pWaveLib, UINT Index);


 /***********************************************************************
  * WaveLib_Init
  *  
  *    Audio!
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
 HWAVELIB WaveLib_Init(PCHAR pWaveFile, BOOL bPause)
 {
     PWAVELIB pWaveLib = NULL;
 
     if(pWaveLib = (PWAVELIB)LocalAlloc(LMEM_ZEROINIT, sizeof(WAVELIB)))
     {
         pWaveLib->bPaused = bPause;

         if(WaveLib_OpenWaveSample(pWaveFile, &pWaveLib->WaveSample))
         {
             if(waveOutOpen(&pWaveLib->hWaveOut, WAVE_MAPPER, &pWaveLib->WaveSample.WaveFormatEx, (ULONG)WaveLib_WaveOutputCallback, (ULONG)pWaveLib, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
             {
                WaveLib_UnInit((HWAVELIB)pWaveLib);
                pWaveLib = NULL;
             }
             else
             {
 
                 if(pWaveLib->bPaused)
                 {
                     waveOutPause(pWaveLib->hWaveOut);
                 }

                 WaveLib_CreateThread(pWaveLib);
             }
         }
         else
         {
             WaveLib_UnInit((HWAVELIB)pWaveLib);
             pWaveLib = NULL;
         }
     }

     return (HWAVELIB)pWaveLib;
 }










 /***********************************************************************
  * WaveLib_Pause
  *  
  *    Audio!
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
 void WaveLib_Pause(HWAVELIB hWaveLib, BOOL bPause)
 {
     PWAVELIB pWaveLib = (PWAVELIB)hWaveLib;

     pWaveLib->bPaused = bPause;

     if(pWaveLib->bPaused)
     {
         waveOutPause(pWaveLib->hWaveOut);
     }
     else
     {
         waveOutRestart(pWaveLib->hWaveOut);
     }
 }

 /***********************************************************************
  * WaveLib_Init
  *  
  *    Audio!
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
 void WaveLib_UnInit(HWAVELIB hWaveLib)
 {
     PWAVELIB pWaveLib = (PWAVELIB)hWaveLib;

     if(pWaveLib)
     {
         if(pWaveLib->hThread)
         {
             pWaveLib->bWaveShouldDie = TRUE;

             SetEvent(pWaveLib->hEvent);
             WaitForSingleObject(pWaveLib->hThread, INFINITE);

             CloseHandle(pWaveLib->hEvent);
             CloseHandle(pWaveLib->hThread);
         }

         if(pWaveLib->hWaveOut)
         {
             waveOutClose(pWaveLib->hWaveOut);
         }


         if(pWaveLib->WaveSample.pSampleData)
         {
             LocalFree(pWaveLib->WaveSample.pSampleData);
         }

         LocalFree(pWaveLib);
     }

 }
 
 
 /***********************************************************************
  * WaveLib_WaveOutputCallback
  *  
  *    Audio Callback 
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/ 
void CALLBACK WaveLib_WaveOutputCallback(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
    PWAVELIB pWaveLib = (PWAVELIB)dwInstance;

    switch(uMsg)
    {
      case WOM_OPEN:
            WaveLib_WaveOpen(hwo, pWaveLib);
            break;

       case WOM_DONE:
            WaveLib_WaveDone(hwo, pWaveLib);
            break;

       case WOM_CLOSE:
            WaveLib_WaveClose(hwo, pWaveLib);
            break;
    }
}



 
 /***********************************************************************
  * WaveLib_WaveOpen
  *  
  *    Audio Callback 
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
void WaveLib_WaveOpen(HWAVEOUT hWaveOut, PWAVELIB pWaveLib)
{
  // Do Nothing
}


 /***********************************************************************
  * WaveLib_WaveDone
  *  
  *    Audio Callback 
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
void WaveLib_WaveDone(HWAVEOUT hWaveOut, PWAVELIB pWaveLib)
{
    SetEvent(pWaveLib->hEvent);
}


 /***********************************************************************
  * WaveLib_WaveClose
  *  
  *    Audio Callback 
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
void WaveLib_WaveClose(HWAVEOUT hWaveOut, PWAVELIB pWaveLib)
{
  // Do Nothing
}



 /***********************************************************************
  * WaveLib_OpenWaveFile
  *  
  *    Audio Callback 
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
BOOL WaveLib_OpenWaveSample(CHAR *pFileName, PWAVE_SAMPLE pWaveSample)
{
    BOOL bSampleLoaded = FALSE;
    HANDLE hFile;
    RIFF_CHUNK RiffChunk = {0};
    DWORD dwBytes, dwReturnValue;
    WAVE_FILE_HEADER WaveFileHeader;
    DWORD dwIncrementBytes;

    if(hFile = CreateFile(pFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL))
    {
        char szIdentifier[5] = {0};

        SetFilePointer(hFile, 12, NULL, FILE_CURRENT);
        

        ReadFile(hFile, &RiffChunk, sizeof(RiffChunk), &dwBytes, NULL);
        ReadFile(hFile, &WaveFileHeader, sizeof(WaveFileHeader), &dwBytes, NULL);

        pWaveSample->WaveFormatEx.wFormatTag      = WaveFileHeader.wFormatTag;         
        pWaveSample->WaveFormatEx.nChannels       = WaveFileHeader.wChannels;          
        pWaveSample->WaveFormatEx.nSamplesPerSec  = WaveFileHeader.dwSamplesPerSec;    
        pWaveSample->WaveFormatEx.nAvgBytesPerSec = WaveFileHeader.dwAvgBytesPerSec;   
        pWaveSample->WaveFormatEx.nBlockAlign     = WaveFileHeader.wBlockAlign;  
        pWaveSample->WaveFormatEx.wBitsPerSample  = WaveFileHeader.wBitsPerSample;
        pWaveSample->WaveFormatEx.cbSize          = 0;

        dwIncrementBytes = dwBytes;

        do {
             SetFilePointer(hFile, RiffChunk.dwLength - dwIncrementBytes, NULL, FILE_CURRENT);
             
             dwReturnValue = GetLastError();

             if(dwReturnValue == 0)
             {
                 dwBytes = ReadFile(hFile, &RiffChunk, sizeof(RiffChunk), &dwBytes, NULL);
             
                 dwIncrementBytes = 0;

                 memcpy(szIdentifier, RiffChunk.IdentifierString, 4); 
             }

        } while(_stricmp(szIdentifier, "data") && dwReturnValue == 0) ;

        if(dwReturnValue == 0)
        {
            pWaveSample->pSampleData = (char *)LocalAlloc(LMEM_ZEROINIT, RiffChunk.dwLength);

            pWaveSample->Size = RiffChunk.dwLength;

            ReadFile(hFile, pWaveSample->pSampleData, RiffChunk.dwLength, &dwBytes, NULL);

            CloseHandle(hFile);

            bSampleLoaded = TRUE;
        }
    }

    return bSampleLoaded;
}





 /***********************************************************************
  * WaveLib_CreateThread
  *  
  *    Audio Callback 
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
void WaveLib_CreateThread(PWAVELIB pWaveLib)
{
    DWORD dwThreadId;

    pWaveLib->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    pWaveLib->hThread = CreateThread(NULL, 0, WaveLib_AudioThread, pWaveLib, 0, &dwThreadId);

}

 /***********************************************************************
  * WaveLib_AudioThread
  *  
  *    Audio Thread
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
DWORD WINAPI WaveLib_AudioThread(PVOID pDataInput)
{
    PWAVELIB pWaveLib = (PWAVELIB)pDataInput;
    DWORD dwReturnValue = 0;
    UINT Index;

    WaveLib_SetupAudio(pWaveLib);

    while(!pWaveLib->bWaveShouldDie)
    {
        WaitForSingleObject(pWaveLib->hEvent, INFINITE);

        for(Index = 0; Index < 8; Index++)
        {
            if(pWaveLib->WaveHdr[Index].dwFlags & WHDR_DONE)
            {
               WaveLib_AudioBuffer(pWaveLib, Index);
               waveOutWrite(pWaveLib->hWaveOut, &pWaveLib->WaveHdr[Index], sizeof(WAVEHDR));
            }
        }
    }

    waveOutReset(pWaveLib->hWaveOut);

    return dwReturnValue;
}




 /***********************************************************************
  * WaveLib_AudioMixer
  *  
  *    Audio Mixer
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
void WaveLib_AudioBuffer(PWAVELIB pWaveLib, UINT Index)
{
    UINT uiBytesNotUsed = SAMPLE_SIZE;

    pWaveLib->WaveHdr[Index].dwFlags &= ~WHDR_DONE;

    if(pWaveLib->WaveSample.Size - pWaveLib->WaveSample.Index < uiBytesNotUsed)
    {
        memcpy(pWaveLib->AudioBuffer[Index], pWaveLib->WaveSample.pSampleData + pWaveLib->WaveSample.Index, pWaveLib->WaveSample.Size - pWaveLib->WaveSample.Index);

        uiBytesNotUsed -= (pWaveLib->WaveSample.Size - pWaveLib->WaveSample.Index);

        memcpy(pWaveLib->AudioBuffer[Index], pWaveLib->WaveSample.pSampleData, uiBytesNotUsed);

        pWaveLib->WaveSample.Index = uiBytesNotUsed;

        uiBytesNotUsed = 0;
    }
    else
    {
       memcpy(pWaveLib->AudioBuffer[Index], pWaveLib->WaveSample.pSampleData + pWaveLib->WaveSample.Index, uiBytesNotUsed);

       pWaveLib->WaveSample.Index += SAMPLE_SIZE;
       uiBytesNotUsed = 0;
    }

    pWaveLib->WaveHdr[Index].lpData = pWaveLib->AudioBuffer[Index];

    pWaveLib->WaveHdr[Index].dwBufferLength = SAMPLE_SIZE - uiBytesNotUsed;
}






 /***********************************************************************
  * WaveLib_SetupAudio
  *  
  *    Audio Thread
  *
  * Parameters
  *     
  * 
  * Return Value
  *     Handle To This Audio Session
  *
  ***********************************************************************/
void WaveLib_SetupAudio(PWAVELIB pWaveLib)
{
    UINT Index = 0;

    for(Index = 0; Index < 8; Index++)
    {
        pWaveLib->WaveHdr[Index].dwBufferLength = SAMPLE_SIZE;
        pWaveLib->WaveHdr[Index].lpData         = pWaveLib->AudioBuffer[Index]; 

        waveOutPrepareHeader(pWaveLib->hWaveOut, &pWaveLib->WaveHdr[Index], sizeof(WAVEHDR));

        WaveLib_AudioBuffer(pWaveLib, Index);

        waveOutWrite(pWaveLib->hWaveOut, &pWaveLib->WaveHdr[Index], sizeof(WAVEHDR));

    }
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Toby Opferman
Engineer Intel
United States United States
Toby Opferman has worked in just about all aspects of Windows development including applications, services and drivers.

He has also played a variety of roles professionally on a wide range of projects. This has included pure researching roles, architect roles and developer roles. He also was also solely responsible for debugging traps and blue screens for a number of years.

Previously of Citrix Systems he is very experienced in the area of Terminal Services. He currently works on Operating Systems and low level architecture at Intel.

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160621.1 | Last Updated 26 Mar 2004
Article Copyright 2004 by Toby Opferman
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid