2014년 11월 21일 금요일

waveIn/waveOut 함수 안전하게 사용하는 법

윈도우의 waveIn/waveOut 계열 함수는 콜백을 등록해서 호출받는 방식으로 동작하는데 콜백함수는 wave 드라이버의 쓰레드에서 호출하기 때문에 콜백함수에서 윈도우 시스템 API를 호출하면 죽거나 데드락이 걸리는 문제가 발생한다. 이것을 해결하기 위해 큐를 사용해 외부 쓰레드에서 waveIn/waveOut 결과를 처리한다.

< WaveBuffer.h >
 #ifndef __WAVE_BUFFER_H__  
 #define __WAVE_BUFFER_H__  
   
 #include <windows.h>  
 #include <MMSystem.h>  
   
 #include "BufferQueue.h"  
   
 class WaveBuffer {  
 public:  
      WAVEHDR*     pWaveHdr;  
      int               length;  
      bool          bDeleteData;  
   
      WaveBuffer(WAVEHDR *hdr, int len, bool deleteData = true) {  
           pWaveHdr = hdr;  
           length = len;  
           bDeleteData = deleteData;  
      }  
   
      virtual ~WaveBuffer() {  
           if (pWaveHdr && bDeleteData) {  
                delete[] pWaveHdr->lpData;  
                delete pWaveHdr;  
           }  
      }  
 };  
   
 class WaveBufferQueue {  
 public:  
      WaveBufferQueue(bool deleteData = true);  
      virtual ~WaveBufferQueue();  
   
      int push_back(WAVEHDR *pWaveHdr);  
      WaveBuffer* pop_front();  
   
      void clear() { m_pWaveQue->clear(); }  
      int count() { return m_pWaveQue->count(); }  
   
 private:  
      BufferQueue<WaveBuffer>     *m_pWaveQue;  
      int          m_nCount;  
      bool     m_bDeleteData;  
 };  
   
   
 #endif  
   
BufferQueue 템플릿 클래스는 C++ double linked-list Template Queue 포스트 참조

< WaveBuffer.cpp >
 #include "WaveBuffer.h"  
   
   
 WaveBufferQueue::WaveBufferQueue(bool deleteData)  
 {  
      m_pWaveQue = new BufferQueue<WaveBuffer>(0);     // 무제한 버퍼  
      m_bDeleteData = deleteData;  
 }  
   
 WaveBufferQueue::~WaveBufferQueue()  
 {  
      if (m_pWaveQue) {  
           delete m_pWaveQue;  
           m_pWaveQue = NULL;  
      }  
 }  
   
 int WaveBufferQueue::push_back(WAVEHDR *pWaveHdr)  
 {  
      WaveBuffer *wave = new WaveBuffer(pWaveHdr, pWaveHdr->dwBufferLength, m_bDeleteData);  
      return m_pWaveQue->push_back(wave);  
 }  
   
 WaveBuffer* WaveBufferQueue::pop_front()  
 {  
      return m_pWaveQue->pop_front();  
 }  
   

< WaveIn.h >
 #ifndef __WAVE_IN_H__  
 #define __WAVE_IN_H__  
   
 #include <windows.h>  
 #include <MMSystem.h>  
   
 #include "WaveBuffer.h"  
   
 class WaveIn  
 {  
 public:  
      WaveIn();  
      virtual ~WaveIn();  
   
      /* WaveIn API */  
      int open(WAVEFORMATEX waveInFormat, int buff_count, double duration);  
      void close();  
      void setWaveInFunc(void (*func)(WAVEHDR *pHdr, void *pData), void *pData)   
      {   
           waveInHandlerFunc = func;   
           m_pWaveInHandlerData = pData;   
      }  
   
 public:  
      void processWaveInDone(WAVEHDR *pHdr);  
      void processWaveInClose();  
      void running();       
   
 protected:  
      HWAVEIN          m_hWaveIn;  
      HANDLE          m_hWaveInThread;  
      BOOL          m_bWaveInRun;  
      WAVEHDR*     m_pWaveInHdr;  
      int               m_nWaveInHdrCount;  
   
      int                         m_nWaveInHdrPrepareCount;  
      WaveBufferQueue*     m_pWaveBufferQue;  
   
      WAVEFORMATEX     m_waveInFormatEx;  
   
      void (*waveInHandlerFunc)(WAVEHDR *pHdr, void *pData);  
      void *m_pWaveInHandlerData;  
   
      void waveInPrintError(MMRESULT result, LPCTSTR str);  
 };  
   
 #endif  
   

<WaveIn.cpp >
 #include "WaveIn.h"  
 #include "GlobalEnv.h"  
 #include <assert.h>  
 #include <process.h>  
 #include <stdio.h>  
   
 #pragma comment(lib, "winmm.lib")  
   
 WaveIn::WaveIn()  
 {  
      m_hWaveIn = NULL;  
      m_hWaveInThread = NULL;  
      m_bWaveInRun = FALSE;  
   
      memset(&m_waveInFormatEx, 0, sizeof(m_waveInFormatEx));  
      m_pWaveInHdr = NULL;  
      m_nWaveInHdrCount = 0;  
   
      m_pWaveBufferQue = NULL;  
   
      waveInHandlerFunc = NULL;  
      m_pWaveInHandlerData = NULL;  
 }  
   
 WaveIn::~WaveIn()  
 {  
      close();  
 }  
   
 unsigned __stdcall Thread_WaveIn(LPVOID lpParam)  
 {  
      WaveIn *pWaveIn = (WaveIn *)lpParam;  
      pWaveIn->running();  
      return 0;  
 }  
   
 void WaveIn::running()  
 {  
      MMRESULT mRes;  
      WAVEHDR *pHdr;  
      WaveBuffer *pWave;  
   
      while (1)  
      {  
           pWave = m_pWaveBufferQue->pop_front();  
           if (pWave == NULL)  
           {  
                Sleep(1);  
                continue;  
           }  
   
           pHdr = pWave->pWaveHdr;  
   
           mRes = waveInUnprepareHeader(m_hWaveIn, pHdr, sizeof(WAVEHDR));  
           if (mRes != MMSYSERR_NOERROR) {  
                waveInPrintError(mRes, "waveInUnprepareHeader");  
                assert(FALSE);  
                continue;  
           }  
           m_nWaveInHdrPrepareCount--;  
   
           if (waveInHandlerFunc)  
                waveInHandlerFunc(pHdr, m_pWaveInHandlerData);  
   
           delete pWave;  
   
           if (m_bWaveInRun)  
           {  
                mRes = waveInPrepareHeader(m_hWaveIn, pHdr, sizeof(WAVEHDR));  
                if (mRes != MMSYSERR_NOERROR) {  
                     waveInPrintError(mRes, "waveInPrepareHeader");  
                     assert(FALSE);  
                     continue;  
                }  
   
                mRes = waveInAddBuffer(m_hWaveIn, pHdr, sizeof(WAVEHDR));  
                if (mRes != MMSYSERR_NOERROR) {  
                     waveInPrintError(mRes, "waveInAddBuffer");  
                     assert(FALSE);  
                     continue;  
                }  
                m_nWaveInHdrPrepareCount++;  
           }  
           else  
           {  
                //DXPRINTF("[%s] WaveIn Hdr Count:%d\n", __FUNCTION__, m_nWaveInHdrPrepareCount);  
                if (m_pWaveBufferQue->count() == 0 && m_nWaveInHdrPrepareCount == 0) {  
                     break;  
                }  
           }  
      }  
 }  
   
 void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)  
 {  
      WaveIn *pWaveIn = (WaveIn *)dwInstance;  
   
      switch(uMsg)  
      {  
      case WIM_OPEN:  
           DXPRINTF("WIM_OPEN\n");  
           break;  
      case WIM_CLOSE:  
           DXPRINTF("WIM_CLOSE\n");  
           pWaveIn->processWaveInClose();  
           break;  
      case WIM_DATA:   
           pWaveIn->processWaveInDone((WAVEHDR *)dwParam1);  
           break;  
      default:  
           break;  
      }  
 }  
   
 void WaveIn::processWaveInDone(WAVEHDR *pHdr)  
 {  
      if (m_pWaveBufferQue)  
           m_pWaveBufferQue->push_back(pHdr);  
 }  
   
 void WaveIn::processWaveInClose()  
 {  
 }  
   
 int WaveIn::open(WAVEFORMATEX waveInFormat, int buff_count, double duration)  
 {  
      MMRESULT mRes;  
   
      if (m_bWaveInRun)  
           return -1;  
   
      memset(&m_waveInFormatEx, 0, sizeof(m_waveInFormatEx));  
      m_waveInFormatEx.wFormatTag = WAVE_FORMAT_PCM;  
      m_waveInFormatEx.nChannels = waveInFormat.nChannels;  
      m_waveInFormatEx.nSamplesPerSec = waveInFormat.nSamplesPerSec;  
      m_waveInFormatEx.wBitsPerSample = waveInFormat.wBitsPerSample;  
      m_waveInFormatEx.nBlockAlign = m_waveInFormatEx.nChannels*m_waveInFormatEx.wBitsPerSample/8;  
      m_waveInFormatEx.nAvgBytesPerSec = m_waveInFormatEx.nSamplesPerSec*m_waveInFormatEx.nBlockAlign;  
      m_waveInFormatEx.cbSize = 0;  
   
      mRes = waveInOpen(&m_hWaveIn, WAVE_MAPPER, &m_waveInFormatEx,   
           (DWORD_PTR)waveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);  
   
      if (mRes != MMSYSERR_NOERROR) {  
           waveInPrintError(mRes, "waveInOpen");  
           return -2;  
      }  
   
      /* prepare wavein header */  
      int buff_size = (int)(m_waveInFormatEx.nAvgBytesPerSec*duration);  
      buff_size *= m_waveInFormatEx.nChannels;  
   
      m_nWaveInHdrCount = buff_count;  
      m_pWaveInHdr = new WAVEHDR[m_nWaveInHdrCount];  
   
      for (int i=0; i<m_nWaveInHdrCount; i++)  
      {  
           memset(&m_pWaveInHdr[i], 0, sizeof(WAVEHDR));  
           m_pWaveInHdr[i].lpData = new char[buff_size];  
           m_pWaveInHdr[i].dwBufferLength = buff_size;  
           m_pWaveInHdr[i].dwUser = i;  
   
           mRes = waveInPrepareHeader(m_hWaveIn, &m_pWaveInHdr[i], sizeof(WAVEHDR));  
           if (mRes != MMSYSERR_NOERROR) {  
                waveInPrintError(mRes, "waveInPrepareHeader");  
                assert(FALSE);  
                continue;  
           }  
   
           mRes = waveInAddBuffer(m_hWaveIn, &m_pWaveInHdr[i], sizeof(WAVEHDR));  
           if (mRes != MMSYSERR_NOERROR) {  
                waveInPrintError(mRes ,"waveInAddBuffer");  
                assert(FALSE);  
                continue;  
           }  
      }  
   
      m_nWaveInHdrPrepareCount = m_nWaveInHdrCount;  
   
      m_pWaveBufferQue = new WaveBufferQueue(false);  
   
      m_bWaveInRun = TRUE;  
      m_hWaveInThread = (HANDLE)_beginthreadex(NULL, 0, Thread_WaveIn, this, 0, NULL);  
   
      mRes = waveInStart(m_hWaveIn);  
      if (mRes != MMSYSERR_NOERROR) {  
           waveInPrintError(mRes, "waveInStart");  
           return -1;  
      }  
   
      return 0;  
 }  
   
 void WaveIn::close()  
 {  
      MMRESULT mRes;  
   
      if (!m_bWaveInRun || !m_hWaveIn)  
           return;  
   
      m_bWaveInRun = FALSE;  
   
      WaitForSingleObject(m_hWaveInThread, INFINITE);  
   
      mRes = waveInStop(m_hWaveIn);  
      if (mRes != MMSYSERR_NOERROR)  
           waveInPrintError(mRes, "waveInStop");  
   
      for (int i=0; i<m_nWaveInHdrCount; i++)  
           delete[] m_pWaveInHdr[i].lpData;  
      delete m_pWaveInHdr;  
   
      mRes = waveInClose(m_hWaveIn);  
      if (mRes != MMSYSERR_NOERROR)  
           waveInPrintError(mRes ,"waveInClose");  
   
      if (m_pWaveBufferQue) {  
           delete m_pWaveBufferQue;  
           m_pWaveBufferQue = NULL;  
      }  
   
      CloseHandle(m_hWaveInThread);  
 }  
   
 void WaveIn::waveInPrintError(MMRESULT result, LPCTSTR str)  
 {  
      char errmsg[128] = {0};  
      waveInGetErrorText(result, errmsg, sizeof(errmsg));  
      DXPRINTF("%s waveInError: %d %s\n", str, result, errmsg);  
 }  
   

< WaveOut.h >
 #ifndef __WAVE_OUT_H__  
 #define __WAVE_OUT_H__  
   
 #include <windows.h>  
 #include <MMSystem.h>  
 #include <MMReg.h>  
 #include <stdio.h>  
   
 #include "WaveBuffer.h"  
   
 class WaveOut  
 {  
 public:  
      WaveOut();  
      virtual ~WaveOut();  
   
      /* WaveOut API */  
      int open(WAVEFORMATEX waveOutFormat, DWORD channel_layout);  
      void close();  
      int waveOutAdd(unsigned char *buff, int len);  
   
      void setAudioVolume(int val);  
      DWORD getAudioVolume();  
      void reset();  
      void setMaxWaveOutBufferCount(int maxCount) { m_nMaxWaveOutBufferCount = maxCount; }  
        
      int channels() { return m_waveOutFormatEx.Format.nChannels; }  
      int sampleRate() { return m_waveOutFormatEx.Format.nSamplesPerSec; }  
      int channelLayout() { return m_waveOutFormatEx.dwChannelMask; }  
   
 public:  
      void processWaveOutDone(WAVEHDR *pHdr);  
      void processWaveOutClose();  
      void running();  
   
 protected:  
      HWAVEOUT     m_hWaveOut;  
      HANDLE          m_hWaveOutThread;  
      BOOL          m_bWaveOutRun;  
      int               m_nMaxWaveOutBufferCount;  
   
      int                         m_nWaveOutHdrPrepareCount;  
      WaveBufferQueue*     m_pWaveBufferQue;  
   
      DWORD          m_nAudioVolume;  
   
      WAVEFORMATEXTENSIBLE     m_waveOutFormatEx;  
      WAVEOUTCAPS                    m_waveOutCaps;  
   
      void waveOutPrintError(MMRESULT result, LPCTSTR str);  
   
      FILE     *m_pFile;     // audio dump  
 };  
   
 #endif  
   

< WaveOut.cpp >
 #include "WaveOut.h"  
 #include "GlobalEnv.h"  
 #include <assert.h>  
 #include <process.h>  
 #include <stdio.h>  
   
 #pragma comment(lib, "winmm.lib")  
   
 WaveOut::WaveOut()  
 {  
      m_hWaveOut = NULL;  
      m_hWaveOutThread = NULL;  
      m_bWaveOutRun = FALSE;  
   
      m_nWaveOutHdrPrepareCount = 0;  
      m_pWaveBufferQue = new WaveBufferQueue();  
   
      m_nAudioVolume = 0xFFFFFFFF;  
   
      ZeroMemory(&m_waveOutCaps, sizeof(WAVEOUTCAPS));  
      ZeroMemory(&m_waveOutFormatEx, sizeof(WAVEFORMATEXTENSIBLE));  
   
      m_nMaxWaveOutBufferCount = 3;  
   
      m_pFile = NULL;  
 }  
   
 WaveOut::~WaveOut()  
 {  
      close();  
      DX_DELETE_OBJECT(m_pWaveBufferQue);  
 }  
   
 unsigned __stdcall Thread_WaveOut(LPVOID lpParam)  
 {  
      WaveOut *pWaveOut = (WaveOut *)lpParam;  
      pWaveOut->running();  
      return 0;  
 }  
   
 void WaveOut::running()  
 {  
      MMRESULT mRes;  
      WaveBuffer *pWave;  
      WAVEHDR *pHdr;  
   
      while (1)  
      {  
           pWave = m_pWaveBufferQue->pop_front();  
           if (pWave == NULL) {  
                Sleep(1);  
                goto skip;  
           }  
             
           pHdr = pWave->pWaveHdr;  
   
           mRes = waveOutUnprepareHeader(m_hWaveOut, pHdr, sizeof(WAVEHDR));  
           if (mRes != MMSYSERR_NOERROR) {  
                waveOutPrintError(mRes, "waveOutUnprepareHeader");  
                assert(FALSE);  
                continue;  
           }  
   
           delete pWave;  
           InterlockedDecrement((LPLONG)&m_nWaveOutHdrPrepareCount);  
   
 skip:  
           if (!m_bWaveOutRun)  
           {  
                if (m_pWaveBufferQue->count() == 0 && m_nWaveOutHdrPrepareCount == 0) {  
                     break;  
                }  
           }  
      }  
 }  
   
 void WaveOut::processWaveOutDone(WAVEHDR *pHdr)  
 {       
      if (m_pWaveBufferQue) {  
           m_pWaveBufferQue->push_back(pHdr);  
      }  
 }  
   
 void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)  
 {  
      WaveOut *pWaveOut = (WaveOut *)dwInstance;  
   
      switch(uMsg)  
      {  
      case WOM_OPEN:  
           DXPRINTF("WOM_OPEN\n");  
           break;  
      case WOM_CLOSE:  
           DXPRINTF("WOM_CLOSE\n");  
           break;  
      case WOM_DONE:  
           pWaveOut->processWaveOutDone((WAVEHDR *)dwParam1);  
           break;  
      default:  
           break;  
      }  
 }  
   
 int WaveOut::waveOutAdd(unsigned char *buff, int len)  
 {  
      MMRESULT mRes;  
   
      if (!m_bWaveOutRun)  
           return -1;  
   
      //DXPRINTF("wave out buffer count : %d (%d)\n", m_nWaveOutHdrPrepareCount, len);  
      if (m_nMaxWaveOutBufferCount > 0) {  
           if (m_nWaveOutHdrPrepareCount > m_nMaxWaveOutBufferCount) {  
                DXPRINTF("wave out buffer overflow : %d (%d)\n", m_nWaveOutHdrPrepareCount, len);  
                return 0;  
           }  
      }  
   
      WAVEHDR *pHdr = new WAVEHDR;  
      ZeroMemory(pHdr, sizeof(WAVEHDR));  
   
      pHdr->lpData = new char[len];  
      memcpy(pHdr->lpData, buff, len);  
      pHdr->dwBufferLength = len;  
      pHdr->dwFlags = 0;  
   
      mRes = waveOutPrepareHeader(m_hWaveOut, pHdr, sizeof(WAVEHDR));  
      if (mRes != MMSYSERR_NOERROR) {  
           waveOutPrintError(mRes, "waveOutPrepareHeader");  
           delete[] pHdr->lpData;  
           delete pHdr;  
           assert(FALSE);  
           return -2;  
      }  
   
      mRes = waveOutWrite(m_hWaveOut, pHdr, sizeof(WAVEHDR));  
      if (mRes != MMSYSERR_NOERROR) {  
           waveOutPrintError(mRes, "waveOutWrite");  
           delete[] pHdr->lpData;  
           delete pHdr;  
           assert(FALSE);  
           return -3;  
      }  
   
      InterlockedIncrement((LPLONG)&m_nWaveOutHdrPrepareCount);  
   
      if (m_pFile) fwrite(buff, len, 1, m_pFile);  
   
      return 0;  
 }  
   
 int WaveOut::open(WAVEFORMATEX waveOutFormat, DWORD channel_layout)  
 {  
      MMRESULT mRes;  
   
      if (m_bWaveOutRun)  
           return -1;  
   
      m_waveOutFormatEx;  
      memset(&m_waveOutFormatEx, 0, sizeof(WAVEFORMATEXTENSIBLE));  
      m_waveOutFormatEx.Format.wFormatTag = WAVE_FORMAT_PCM;  
      m_waveOutFormatEx.Format.nChannels = waveOutFormat.nChannels;  
      m_waveOutFormatEx.Format.nSamplesPerSec = waveOutFormat.nSamplesPerSec;  
      m_waveOutFormatEx.Format.wBitsPerSample = waveOutFormat.wBitsPerSample;  
      m_waveOutFormatEx.Format.nBlockAlign = waveOutFormat.nChannels*waveOutFormat.wBitsPerSample/8;  
      m_waveOutFormatEx.Format.nAvgBytesPerSec = waveOutFormat.nSamplesPerSec*waveOutFormat.nBlockAlign;  
   
      if (waveOutFormat.nChannels <= 2) {  
           m_waveOutFormatEx.Format.cbSize = 0;  
      } else {  
           m_waveOutFormatEx.dwChannelMask = channel_layout;  
           m_waveOutFormatEx.Samples.wValidBitsPerSample = waveOutFormat.wBitsPerSample;  
           m_waveOutFormatEx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;  
           m_waveOutFormatEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;  
           m_waveOutFormatEx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);  
      }  
   
      mRes = waveOutOpen(&m_hWaveOut, WAVE_MAPPER, (WAVEFORMATEX *)&m_waveOutFormatEx,  
           (DWORD_PTR)waveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);  
   
      if (mRes != MMSYSERR_NOERROR) {  
           waveOutPrintError(mRes, "waveOutOpen");  
           return -2;  
      }  
   
      mRes = waveOutSetVolume(m_hWaveOut, m_nAudioVolume);  
      if (mRes != MMSYSERR_NOERROR) {  
           waveOutPrintError(mRes, "waveOutWrite");  
      }  
   
      m_bWaveOutRun = TRUE;  
      m_hWaveOutThread = (HANDLE)_beginthreadex(NULL, 0, Thread_WaveOut, this, 0, NULL);  
      if (!m_hWaveOutThread) {  
           DXPRINTF("[%s] waveout thread create error, err:%d\n", __FUNCTION__, GetLastError());  
           return -3;  
      }  
   
      ZeroMemory(&m_waveOutCaps, sizeof(WAVEOUTCAPS));  
      mRes = waveOutGetDevCaps((UINT_PTR)m_hWaveOut, &m_waveOutCaps, sizeof(WAVEOUTCAPS));  
      if (mRes != MMSYSERR_NOERROR) {  
           waveOutPrintError(mRes, "waveOutGetDevCaps");  
      }  
   
 #if 0  
      m_pFile = fopen("audio.wav", "wb");  
 #endif  
   
      return 0;  
 }  
   
 void WaveOut::close()  
 {  
      MMRESULT mRes;  
   
      if (!m_bWaveOutRun || !m_hWaveOut)  
           return;  
   
      waveOutReset(m_hWaveOut);  
      m_bWaveOutRun = FALSE;  
   
      WaitForSingleObject(m_hWaveOutThread, INFINITE);  
   
      mRes = waveOutClose(m_hWaveOut);  
      if (mRes != MMSYSERR_NOERROR)  
           waveOutPrintError(mRes, "waveOutClose");  
   
      CloseHandle(m_hWaveOutThread);  
      m_hWaveOutThread = NULL;  
   
      if (m_pFile) {  
           fclose(m_pFile);  
           m_pFile = NULL;  
      }  
 }  
   
 void WaveOut::setAudioVolume(int val)  
 {  
      m_nAudioVolume = val;  
   
      if (m_hWaveOut) {  
           MMRESULT mRes = waveOutSetVolume(m_hWaveOut, m_nAudioVolume);  
           if (mRes != MMSYSERR_NOERROR) {  
                waveOutPrintError(mRes, "waveOutSetVolume");  
           }  
      }  
 }  
   
 DWORD WaveOut::getAudioVolume()  
 {  
      if (m_hWaveOut) {  
           MMRESULT mRes = waveOutGetVolume(m_hWaveOut, &m_nAudioVolume);  
           if (mRes == MMSYSERR_NOERROR) return m_nAudioVolume;  
           waveOutPrintError(mRes, "waveOutGetVolume");  
      }  
      return 0;  
 }  
   
 void WaveOut::reset()  
 {  
      if (m_hWaveOut)  
           waveOutReset(m_hWaveOut);  
 }  
   
 void WaveOut::waveOutPrintError(MMRESULT result, LPCTSTR str)  
 {  
      char errmsg[128] = {0};  
      waveOutGetErrorText(result, errmsg, sizeof(errmsg));  
      DXPRINTF("%s waveOutError: %d %s\n", str, result, errmsg);  
 }  
   

< WaveOut 사용법 >
WaveOut* m_pWaveOut = new WaveOut();
...
// waveout 열기
int open(int channel, int sample_rate, int channel_layout)
{
// open waveout
WAVEFORMATEX waveformat;
memset(&waveformat, 0, sizeof(WAVEFORMATEX));
waveformat.wFormatTag = WAVE_FORMAT_PCM;
waveformat.nChannels = channel;
waveformat.nSamplesPerSec = sample_rate;
waveformat.wBitsPerSample = 16;
waveformat.nBlockAlign = waveformat.nChannels*waveformat.wBitsPerSample/8;
waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec*waveformat.nBlockAlign;
waveformat.cbSize = 0;

int ret = m_pWaveOut->open(waveformat, channel_layout);
if (ret < 0) m_pWaveOut->close();
return ret;
}

// waveout 출력 - buff : 오디오 pcm 데이터, size : 오디오 pcm 데이터 사이즈
int waveout(unsigned char *buff, int size)
{
     return m_pWaveOut->waveOutAdd(buff, size);
}


// waveout 닫기
void close()
{
     m_pWaveOut->close();
}