WaveBox.h
// WaveBox.h:CWaveĽ.
// 
/////////////////////////////////////////////////////////// 

#if !defined(AFX_WAVEBOX_H__DE24CFE1_7501_4DA3_AF18_667A845AA E49__INCLUDED_) 
#define AFX_WAVEBOX_H__DE24CFE1_7501_4DA3_AF18_667A845AAE49__INCLUDE D_ 

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000 


///Ԥ 
#include <windows.h> 
#include <mmsystem.h> 

/// & PCM  
#define WAVE_FILE_MARK  "RIFF"  
#define WAVE_HEAD_MARK  "WAVEfmt "  
#define WAVE_DATA_MARK  "data"  
#define WAVE_PCM_16    16  
#define WAVE_PCM_1  1  

/// wfx֧
#define OFFSET_FILE_LEFT 4
#define OFFSET_HEAD_MARK 8
#define OFFSET_WAVE_PCM1 16
#define OFFSET_WAVE_PCM2 20
#define OFFSET_CHANNELS 22
#define OFFSET_SAMPLESPERSEC   24
#define OFFSET_AVGBYTESPERSEC  28
#define OFFSET_BLOCKALIGN 32 
#define OFFSET_BITSPERSAMPLE    34
#define OFFSET_DATA_MARK  36
#define OFFSET_DATA_SIZE 40
#define OFFSET_WAVEDATA 44
#define HEADER_SIZE OFFSET_WAVEDATA
#define EOF_EXTRA_INFO 60 


/// Ϣ
typedef unsigned int WMsg;  // Ϣ
typedefunsigned int TMsg;   // thread messages 

#define WMSG_WAIT  0   // ˵ȴ
#define WMSG_START 1  // 
#define TMSG_ALIVE 1  // ߳
#define TMSG_CLOSE 0 // ̹߳ر
#define INT_FREE 0   // free
#define INT_USED 1   // ʹ
#define THREAD_EXIT 0xABECEDA // ߳˳ 

/// performance predefines
#define SUPPORT_WAVES    10   // Ԥȷ˼
#define SUPPORT_INTERFACES 10 // Ԥȷ

/// buffering
#define READ_BLOCK  8192   // Ķ(ļ)ߴ
#define BLOCK_SIZE 8192   // пߴ
#define BLOCK_COUNT 20    // п
#define BP_TURN 1          // blocks per turn 

static CRITICAL_SECTION cs; // ٽ 

static unsigned int __stdcall
                   PlayThread(LPVOID lp);   // ߳
static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, // waveOutԭ
                                         UINT uMsg, 
                                         DWORD  dwInstance,  
                                         DWORD  dwParam1,  
                                         DWORD  dwParam2);  
class CWaveBox  
{ 
    struct WAVE  
    { 
        char            *data;  /// 
        unsigned long  size;  /// ߴ
        WAVEFORMATEX     wfx;  /// wfx
        WMsg WMSG; /// {0,1} wait / play 
   }; 

    struct INTERFACE 
    {
        /// 
        HWAVEOUT dev;        /// 豸
        unsigned int state; /// {0,1} free / used 

        ///
        WAVE           *wave;      /// ǰ 

        ///˽
        unsigned long  wpos;     /// ǰλ
        WAVEHDR* wblock;         /// ˿
        volatile int   wfreeblock; /// ʣɿ 
        int              wcurrblock; /// ǰ 
   }; 

public:

    /// Ա
    INTERFACE I[SUPPORT_INTERFACES]; // interface(s)
    WAVE W[SUPPORT_WAVES]; // wave(s)
    unsigned int  wload;  // ǰ˼
    TMsg TMSG; // ߳msg {1,0}/ر 

    /// ԭ
    int Load(TCHAR       *file);   // ˼صWaveBox
    int Play(unsigned int wave); 
    // WaveBoxn(߳)

         CWaveBox();  // ͣ߳ 
   virtual ~CWaveBox();  // ֹ߳ 
 
    /// waveOut open & close 
    int AddInterface(HWAVEOUT    *dev, /// wfxƵ
                            WAVEFORMATEX  *wfx, 
                            volatile int  *wfreeblock); 
    int RemoveInterface(HWAVEOUT dev); 
    /// ӽק wfx 
 
protected:
     
    /// ߳ 
    HANDLE     thread;      /// ߳̾ 
    unsigned int    run;   /// ͣ/
 
    /// ԭ 
 
    /// ˱allocѻ 
    WAVEHDR*    allocateBlocks(int size, int count); 
    /// ѻ 
    void      freeBlocks(WAVEHDR* blockArray); 
}; 
 
#endif // !defined(AFX_WAVEBOX_H__DE24CFE1_7501_4DA3_AF18_667A845AAE49__ INCLUDED_) 
ڹУPlayThread ΪģʽԱڲпԿƲŹ̡LPVOID Դݵ PlayThread ԱڸԷʹԱͷĴ룺


CWaveBox::CWaveBox() 
{

    // init˼
    wload = 0; 
 
    // ߳ͣ<״ʱ߳> 
    run    = 0; 
 
    // ͣ߳ 
   thread  = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)PlayThread, (LPVOID)this, CREATE_SUSPENDED, NULL );  


    // alloc mem   
    for(unsigned int i = 0; i <SUPPORT_INTERFACES; i++) 
    {
        I[i].wblock     = allocateBlocks(BLOCK_SIZE,  BLOCK_COUNT); 
        I[i].wfreeblock = BLOCK_COUNT;
        I[i].wcurrblock = 0;
        I[i].state    = INT_FREE;
        I[i].wpos      = 0; 
   }

    // init msg 
    for(i = 0; i <SUPPORT_WAVES; i++)    W[i].WMSG = WMSG_WAIT; 

     // init cs
     InitializeCriticalSection(&cs);
} 
ĴļݣװṹwfxУͬʱݿҲװڴСڿʼǰҪŵݣһʼ޷ʵּݡ
int CWaveBox::Load(TCHAR *file) 
{
    if(wload == SUPPORT_WAVES) 
        return -1; 

    HANDLE hFile; 

    //ļ 
    if((hFile = CreateFile(file,
                                       GENERIC_READ, 
                                       FILE_SHARE_READ,
                                       NULL,
                                       OPEN_EXISTING, 0,
                                       NULL)) == INVALID_HANDLE_VALUE) return -1; 


    // Ķ˱ 
    char header[HEADER_SIZE];
    unsigned long   rbytes = 0; 

    if(!ReadFile(hFile, header, sizeof(header), &rbytes, NULL)) 
    {CloseHandle(hFile); return -1;} 

    if(!rbytes || rbytes <sizeof(header)) 
    {CloseHandle(hFile); return -1;} 

    /// Ƿǲļ
    if(strncmp(header, WAVE_FILE_MARK, strlen(WAVE_FILE_MARK))) {CloseHandle(hFile); return -1;} 

    if(strncmp(header + OFFSET_HEAD_MARK, WAVE_HEAD_MARK, strlen(WAVE_HEAD_MARK))) {CloseHandle(hFile); return -1;} 

    /// ǷǷѹPCMģʽ
    if (((*(DWORD*)(header + OFFSET_WAVE_PCM1)) != WAVE_PCM_16) || ((*(WORD  *)(header  + OFFSET_WAVE_PCM2))  != WAVE_PCM_1  ))  
    {CloseHandle(hFile);  return -1;}  

    /// '' 
    if(!strncmp(header + OFFSET_DATA_MARK, WAVE_DATA_MARK, strlen(WAVE_DATA_MARK))) W[wload].size = *((DWORD*)(header + OFFSET_DATA_SIZE));
        /* size of data */ 
    else 
    {	/// ݿߴԱȡ 
         /// ûжϢԤݿ
         /// ǷѰ
         W[wload].size = *((DWORD*)(header + OFFSET_FILE_LEFT)); 
         W[wload].size -= (HEADER_SIZE - EOF_EXTRA_INFO);
         /* size of data */ 
   } 

    // Ӳ˱ WAVEFORMATEX
    W[wload].wfx.nSamplesPerSec = 
      *((DWORD*)(header + OFFSET_SAMPLESPERSEC)); /* sample rate */ 
    W[wload].wfx.wBitsPerSample = 
      *((WORD *)(header + OFFSET_BITSPERSAMPLE)); /* sample size */ 
    W[wload].wfx.nChannels =
      *((WORD *)(header + OFFSET_CHANNELS  )); /* channels */
    W[wload].wfx.cbSize = 0; /* size of _extra_ info */ 
    W[wload].wfx.wFormatTag = 
    WAVE_FORMAT_PCM;  
    W[wload].wfx.nBlockAlign  = 
      *((WORD *)(header + OFFSET_BLOCKALIGN));  
    W[wload].wfx.nAvgBytesPerSec = 
      *((DWORD*)(header + OFFSET_AVGBYTESPERSEC)); 

    // ȡݿmem 
    if((W[wload].data = (char *) calloc(W[wload].size, 
                                                 sizeof(char))) == NULL)
    {CloseHandle(hFile); return -1;} 

    char buffer[READ_BLOCK];

    unsigned long   size        = W[wload].size;
    unsigned long   read_block = 0; 
                           rbytes = 0; 

    do /// ƷѹPCMݿ
    {
        if((size -= rbytes) >= READ_BLOCK) read_block = READ_BLOCK;
        else
        if(size && size <READ_BLOCK)  read_block = size;
        else                                   break; 

        if(!ReadFile(hFile, buffer, read_block, &rbytes, NULL)) 
              break; 
        if(rbytes == 0) 
             break; 

        if(rbytes <sizeof(buffer))
             memset(buffer + rbytes, 0, sizeof(buffer) - rbytes); 

        memcpy(&W[wload].data[W[wload].size - size], buffer, rbytes); 

   }while(1); 

    // رļ 
    CloseHandle(hFile); 

    // صǰ˼
    return ++wload; 
} 

PlayҪWMSG_STARTݸPlayThreadʼšſʼʱPlayThreadϢTMSG_ALIVE
int CWaveBox::Play(unsigned int wave) 
{
    // 鲨id
    if(wave <0 || wave >= wload) 
          return -1; 

    // Ϣ 
    EnterCriticalSection(&cs); 
    W[wave].WMSG = WMSG_START; 
    LeaveCriticalSection(&cs); 

    // ߳resume thread<״> 
    if(!run){run = 1; TMSG = TMSG_ALIVE; ResumeThread(thread);}

    return 1; 
} 
16.2.2  д
PlayThreadΪŷ׼ݶУݿСBLOCK_SIZEݿBLOCK_COUNTһӵ10Σ10΢ӳ١
Ϊ˽ͬ⣬Կƶл壬ԵAddInterfaceָ򴫵ݵݿָ룬л忪ʼŵʱͨϢ̡ͬ
/// Ԥ
#define SUPPORT_WAVES 10             // Ԥ˼
#define SUPPORT_INTERFACES 10       // Ԥ

/// 
#define READ_BLOCK 8192 // ȡļ
#define BLOCK_SIZE 8192 // пߴ
#define BLOCK_COUNT 20  // п
#define BP_TURN 1        // blocks per turn

/// AddInterfaceWAI API waveOutOpenΧİװ

int CWaveBox::AddInterface(HWAVEOUT  *dev,
                               WAVEFORMATEX *wfx, 
                               volatile int  *wfreeblock)  
{ 

    // 豸
    if(!waveOutGetNumDevs())
          return -1; 

    // ԴĬϲ豸.WAVE_MAPPERһis
    // mmsystem.hеĳ,ָͨϵͳϵĬϲ豸
    // (һЩ2).

    if(waveOutOpen(dev,
                         WAVE_MAPPER,
                         wfx,
                         (DWORD)waveOutProc,
                         (DWORD)wfreeblock, 
                         CALLBACK_FUNCTION) != MMSYSERR_NOERROR) return -1; 
    return 1; 
} 

/// waveOutProcCALLBACK
/// WAIϢУ豸HWAVEOUT(߳)
/// κ˶Ϣʱͨ
/// 

static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, 
                                         UINT  uMsg,  
                                         DWORD  dwInstance,  
                                         DWORD  dwParam1,  
                                         DWORD  dwParam2    )  
{
    // ָп 
    int* freeBlockCounter = (int*)dwInstance; 

    // ڴ򿪺͹ر豸ĵ.
    if(uMsg != WOM_DONE) 
         return; 

    // ߿п 
    EnterCriticalSection(&cs); 
    (*freeBlockCounter)++; 
    LeaveCriticalSection(&cs); 

} 
16.2.3  PlayThread
÷ΪͬкĳҪWMsgϢWMSG_STARTʱ߳һINT_FREEӿڣнӿڣ򱣴WAVEָ룬ȻINT_USED
static unsigned int __stdcall PlayThread(LPVOID lp) 
{
    /// ȡʾ 
    CWaveBox *wb = (CWaveBox *)lp; 

    ///  <ʹõ / ѡ > 
    register   WMsg             wmsg  = WMSG_WAIT; 
    register   TMsg             tmsg  = TMSG_ALIVE; 
    register   unsigned int   i = 0; 

    /// ߳ѭ
    while(tmsg) 
    {

        /// ''Ϣ
        for(i = 0; i <wb->wload; i++) 
        {
            /// ȡϢ
            EnterCriticalSection(&cs);
            wmsg = wb->W[i].WMSG; 
            LeaveCriticalSection(&cs); 

            /// ? 
            if(wmsg == WMSG_START) break; 
       } 


        /// в 
        if(wmsg == WMSG_START) 

            /// һн 
            for(unsigned int j = 0; j <SUPPORT_INTERFACES; j++) 

                /// н 
                if(wb->I[j].state == INT_FREE) 

                    /// ˸ڽ 
                    if(wb->AddInterface(&wb->I[j].dev,
                                              &wb->W[i].wfx, 
                                               &wb->I[j].wfreeblock)) 
                    {
                        /// ȡָʾ   
                        wb->I[j].wave = &wb->W[i]; 

                        /// ʹõĽ
                        wb->I[j].state = INT_USED; 

                        ///ɲ 
                        EnterCriticalSection(&cs);
                        wb->W[i].WMSG = WMSG_WAIT; 
                        LeaveCriticalSection(&cs); 

                        ///ѭ 
                        break; 
                   } 

                ///////////////////////////////////////////////// 
                ///////////////////////////////////////////////// 
                /// 
                ///                     <main playing loop > 
                /// 
                ///    search for the first marked interface and play attached wave 
                /// 

               for(unsigned int k = 0; k <SUPPORT_INTERFACES; k++) 
               {
                 /// ɽ治Ҫʲô
                 if(wb->I[k].state == INT_FREE) continue; 

                 EnterCriticalSection(&cs); 
                 int free = wb->I[k].wfreeblock; 
                 LeaveCriticalSection(&cs); 

                 /// Ҫȫнʲô 
                 if(free <BP_TURN) continue;

                 WAVEHDR *current = NULL; 

                /// ÿжٿᱻ 
                for(unsigned int m = 0; m <BP_TURN; m++) 
                {
                    /// õǰָʾ
                    current = &wb->I[k].wblock[wb->I[k].wcurrblock]; 

                    // ȷǽʹõıǼϯ 
                    if(current->dwFlags & WHDR_PREPARED)

                         waveOutUnprepareHeader(wb->I[k].dev,
                                                    current,
                                                    sizeof(WAVEHDR)); 

                    /// ڸýʣ 
                    unsigned long left = wb->I[k].wave->size - wb->I[k].wpos; 
                    unsigned long chunk = 0; 

                    if(left >= BLOCK_SIZE)
                          chunk = BLOCK_SIZE;
                    else
                    if(left && left <BLOCK_SIZE) 
                          chunk = left;
                    else
                    {

                        //////////////////// 
                        /// nothing left /// 
                        ////////////////////////////////////////////// 
                        /// 
                        /// <clean job, close waveOutProc threads > 
                        /// 
                        /// лڽ 
                        /// 

                        /// ȡп
                        EnterCriticalSection(&cs); 
                        int free = wb->I[k].wfreeblock; 
                        LeaveCriticalSection(&cs); 

                        if(free == BLOCK_COUNT)  /// еĿ鶼!? 
                        {

                            /// Ϊ׼ĿȻ׼ 
                            for(int i = 0; i <wb->I[k].wfreeblock; i++) if(wb->I[k].wblock[i].dwFlags & WHDR_PREPARED) waveOutUnprepareHeader( wb->I[k].dev, &wb->I[k].wblock[i], sizeof(WAVEHDR)); 

                        /// رս
                        if(wb->RemoveInterface(wb->I[k].dev)) 
                        {
                            /// ͷŽ
                            wb->I[k].wcurrblock = 0;
                            wb->I[k].state = INT_FREE;
                            wb->I[k].wpos = 0;
                            wb->I[k].wave = NULL; 
                       } 
                   } 

                    /// ͣ
                    break; 
               } 

                /// ׼ǰݿ
                memcpy(current->lpData,
                        &wb->I[k].wave->data[wb->I[k].wpos], chunk); 

                current->dwBufferLength = chunk; // ߴ
                wb->I[k].wpos += chunk; // λ 

                /// ׼playback
                waveOutPrepareHeader(wb->I[k].dev,
                          current, sizeof(WAVEHDR)); 

                /// ƶ
                waveOutWrite(wb->I[k].dev, current, sizeof(WAVEHDR)); 

                /// ٿп 
                EnterCriticalSection(&cs);
                wb->I[k].wfreeblock--; 
                LeaveCriticalSection(&cs); 

                /// ָһ
                wb->I[k].wcurrblock %= BLOCK_COUNT; 

       }/// 

   }/// 

    /// ȴ 10ms <save CPU time > Sleep(10); 

    /// ߳Ϣ 
    EnterCriticalSection(&cs); 
    tmsg = wb->TMSG; 
    LeaveCriticalSection(&cs); 
}/// ߳ 

///////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////////// 
/// 
///   ǿƹرȻеĽ 
/// 

     for(i = 0; i <SUPPORT_INTERFACES; i++) 
                   if(wb->I[i].state == INT_USED)
                        if(waveOutReset(wb->I[i].dev) == MMSYSERR_NOERROR) 
                            wb->RemoveInterface(wb->I[i].dev);

     return  THREAD_EXIT; /// ˳ <destructor >
} 
16.2.4  ~CWaveBox()
ͷŶУҲҪڹ̣߳TMSG_CLOSEϢǷ˳̲߳رѭ
CWaveBox::~CWaveBox()
{
    unsigned long exit = 0; 

    if(run) // ̼߳ 
    {
        // ̹߳رϢ 
        EnterCriticalSection(&cs); 
        TMSG = TMSG_CLOSE; 
        LeaveCriticalSection(&cs); 

        do //ȴر 
        {
            GetExitCodeThread(thread, &exit);
            Sleep(10); 

       }while(exit != THREAD_EXIT); 

   }else // ߳ͣ 
    {
        // Ӳر
        GetExitCodeThread(thread, &exit);
        TerminateThread(thread, exit); 
   } 

    // ͷŲ
    for(unsigned int i = 0; i <wload; i++)
        free(W[i].data); 

    // ͷŽ
    for(i = 0; i <SUPPORT_INTERFACES; i++)
         freeBlocks(I[i].wblock); 

    // del cs 
    DeleteCriticalSection(&cs); 

} 
