//==========================================
//TITLE:
//    CSoundBase实现录音与播放
//AUTHOR:
//    norains
//DATE:
//    Wednesday  10-January -2007
//Environment:
//  EVC4.0 + Standard SDK
//==========================================

1.简介
 
  CSoundBase是我封装的一个API类,主要是为了能方便实现声音的录制和播放.目前仅支持WAV的录制和播放.
  完整的代码见本文第四节.
  如果各位朋友发现有BUG需要修正,欢迎和我联系,谢谢!
 
 
2.使用方法
 
  CSoundBase类的使用非常简单,首先声明一个类指针,然后获取类的实例:

CSoundBase *pSoundPlayer; pSoundPlayer->CSoundBase::GetInstance();

假如我们需要在"record"文件夹中录制一个名字为"first"的文件,只需很简单的一条语句:
pSoundPlayer->Record(TEXT("record/first.wav"));

不过这样是采用默认的录制格式,实际录音中我们可以进行更改.不过在进行这一步之前,我们先看一下这个类:
typedef struct { ChannelType channel; SamplesPerSecType samples; BitsPerSampleType bits; }WAVEFORMAT_SETTING,*PWAVEFORMAT_SETTING; 

我们来看看这个类成员代表的意义:
  channel
   声道数,其取值可以为:CHANNEL_SINGLE(单声道),CHANNEL_DOUBLE(双声道).
  samples
   采样率,其取值可以为SAMPLES_11025,SAMPLES_22050,SAMPLES_44100
  bits
   采样位,其取值为 BITS_8,BITS_16
  
  如果我们录制的一个文件有如下要求:单声道,采样率为22050MHZ,采样位为16位,代码非常简单,如下:

WAVEFORMAT_SETTING waveFormat;
  waveFormat.channel = CHANNEL_SINGLE;
  waveFormat.samples = SAMPLES_22050;
  waveFormat.bits = SAMPLES_44100;
  pSoundPlayer->Record(TEXT("record/first.wav"),&waveFormat);

当想停止录音时,可调用StopRecording():

pSoundPlayer->StopRecording(); 

 如果想播放刚刚的录制的wav文件,只需简单调用Play()即可: 

pSoundPlayer->Play(); 

 当然,也可以播放储存器上任意一处的wav文件,如:

pSoundPlayer->Play(TEXT("record/second.wav")); 

停止播放同样也很简单:  

pSoundPlayer->StopPlaying(); 

由于录音和播放是分别占用不同的缓冲区,所以我们可以边播放边录音,当然了,前提是录音和播放的不能是同一个文件:

pSoundPlayer->Record(TEXT("record/first.wav")); pSoundPlayer->Play(TEXT("record/second.wav")); 

这时候我们可以调用StopAll()函数同时停止录音和播放: 

pSoundPlayer->StopAll();

3.CSoundBase的实现细节 

 3.1 waveInUnprepareHeader()的死锁
  有的追求完美的朋友可能会觉得,为什么waveInUnprepareHeader()不放在WIM_CLOSE消息的响应函数OnWIM_DATA()中.恩,这个方法我曾经想过,也曾经尝试过,不过很可惜失败了.因为在回调函数中调用waveInUnprepareHeader()会导致死锁,从而使程序崩溃.所以在处理WIM_DATA消息时,很巧妙地没有调用waveInUnprepareHeader()来卸载,而是直接把已经录制完毕并且其数据已经写到文件中的内存作为新录制缓冲区添加:waveInAddBuffer (m_hWaveIn, (PWAVEHDR) wParam, sizeof (WAVEHDR)).这样即可避免了死锁,又减少了分配内存的花销,可谓一箭双雕吧.
 
 3.2 WIM_DATA消息的响应
  细心的朋友可能会发现,在StopRecording()函数里调用waveInClose()之前还调用了waveInReset().因为根据文档,如果在调用waveInClose()之前调用waveInAddBuffer()添加的内存没有返回释放,则waveInClose()将调用失败,从另一个角度来说,此时系统将不会回调WIM_CLOSE消息.故在waveInClose()函数之前调用waveInReset()来释放之前映射的内存.
  不过这时候会有一个小问题,就是调用waveInReset()时系统会发送WIM_DATA消息.所以我们在WIM_DATA消息的响应函数中需要做个小小的判断,就是在响应调用waveInReset()而返回的WIM_DATA消息时,我们不再添加录音缓存区.
  在代码中表现如下:

  if(m_bRecording == TRUE) { waveInAddBuffer (m_hWaveIn, (PWAVEHDR) wParam, sizeof (WAVEHDR)) ; }

 3.3 更多实现细节
  由于代码的总体思路在我的另一篇文章中已经详细说明,在此略为不表.
  有兴趣的朋友可参考《EVC录音详解》一文:http://blog.csdn.net/norains/archive/2006/06/13/795777.aspx
4.CSoundBase 完整源代码  

/**///  
// SoundBase.h: interface for the CSoundBase class.                 //
/**///
//AUTHOR:                                                           //
//    norains                                                       //
//VERSION:                                                          //
//  1.0.0                                                         //
//DATE:                                                             //
//    Wednesday  10-January -2007                                   //
//Environment:                                                      //
//  EVC4.0 + Standard SDK                                         //
/**///
#ifndef SOUNDBASE_H
#define SOUNDBASE_H

#include "mmsystem.h"
//------------------------------------------------------------------------------
//Macro define
#define MAX_SAVEPATH_LENGTH   500 //The length of saved path

//------------------------------------------------------------------------------
//Value type

enum ChannelType
{
 CHANNEL_SINGLE,
 CHANNEL_DOUBLE
};

enum SamplesPerSecType
{
 SAMPLES_11025,
 SAMPLES_22050,
 SAMPLES_44100
};

enum BitsPerSampleType
{
 BITS_8,
 BITS_16
};
//---------------------------------------------------------------------------
//Struct

//Wave format data
typedef struct
{
 ChannelType channel;
 SamplesPerSecType samples;
 BitsPerSampleType bits;
}WAVEFORMAT_SETTING,*PWAVEFORMAT_SETTING;
//------------------------------------------------------------------------------
class CSoundBase  
{
public:
 void StopPlaying();
 void StopRecording();
 void StopAll();
 static CSoundBase* GetInstance();
 BOOL Play(const TCHAR *pszPath = NULL);
 BOOL Record(TCHAR *pszPath,const PWAVEFORMAT_SETTING pWaveFormat = NULL);

 virtual ~CSoundBase();
protected:
 void OnWIM_OPEN(WPARAM wParam, LPARAM lParam);
 void OnWIM_DATA(WPARAM wParam, LPARAM lParam);
 void OnWIM_CLOSE(WPARAM wParam, LPARAM lParam);
 BOOL WriteWaveFileHeader(TCHAR *pszFilename, const PWAVEFORMATEX pWFX, DWORD dwBufferSize,BOOL bCover);
 static void CALLBACK WaveInProc(HWAVEIN hWi,UINT uMsg, DWORD dwInstance,  DWORD dwParam1, DWORD dwParam2);
 CSoundBase();
 void SetRecordWaveFormat(const PWAVEFORMAT_SETTING pWaveFormat);
 
 static CSoundBase *m_pInstance;
 WAVEFORMATEX m_WaveFormatEx;
 BOOL m_bRecording;
 HANDLE m_hSaveFile;
 HWAVEIN m_hWaveIn;
 PBYTE m_pBuffer1;
 PBYTE m_pBuffer2;
 PWAVEHDR m_pWaveHdr1;
 PWAVEHDR m_pWaveHdr2;
 DWORD m_dwDataLength; //The length of the data
 TCHAR m_szSavePath[MAX_SAVEPATH_LENGTH]; //The path to save


};

#endif // SOUNDBASE_H 
/  
// SoundBase.cpp: implementation of the CSoundBase class.          //
/**//

#include "stdafx.h"
#include "SoundBase.h"

 

//------------------------------------------------------------------------------
//Macro define
#define  INP_BUFFER_SIZE  16*1024 //The input buffer size

#define RIFF_FILE       mmioFOURCC('R','I','F','F')
#define RIFF_WAVE       mmioFOURCC('W','A','V','E')
#define RIFF_FORMAT     mmioFOURCC('f','m','t',' ')
#define RIFF_CHANNEL    mmioFOURCC('d','a','t','a')

//Default values
#define DEFAULT_CHANNEL  CHANNEL_SINGLE
#define DEFAULT_SAMPLES  SAMPLES_11025
#define DEFAULT_BITS  BITS_8

//------------------------------------------------------------------------------
//The struct

//FileHeader
typedef struct
{
   DWORD   dwRiff;     // Type of file header.
   DWORD   dwSize;     // Size of file header.
   DWORD   dwWave;     // Type of wave.
} RIFF_FILEHEADER, *PRIFF_FILEHEADER;

 

//ChunkHeader
typedef struct
{
   DWORD   dwCKID;        // Type Identification for current chunk header.
   DWORD   dwSize;        // Size of current chunk header.
} RIFF_CHUNKHEADER, *PRIFF_CHUNKHEADER;
//---------------------------------------------------------------------------------

//-------------------------------------------------------------------
//Static member initialize
CSoundBase *CSoundBase::m_pInstance = NULL; //If you don't initialize,the GetInstance() will link erro.


/**///
// Construction/Destruction
/**///

CSoundBase::CSoundBase()
{
 memset(m_szSavePath,0,sizeof(m_szSavePath));
 memset(&m_WaveFormatEx,0,sizeof(m_WaveFormatEx));
 m_pBuffer1 = NULL;
 m_pBuffer2 = NULL;
 m_pWaveHdr1 = NULL;
 m_pWaveHdr2 = NULL;
 m_hWaveIn = NULL;
 m_hSaveFile = NULL;
 m_bRecording = FALSE;
 m_dwDataLength = 0;


}

CSoundBase::~CSoundBase()
{

 if(m_pWaveHdr1 != NULL)
 {
  free(m_pWaveHdr1);
  m_pWaveHdr1 = NULL;
 }
 
 if(m_pWaveHdr2 != NULL)
 {
  free(m_pWaveHdr2);
  m_pWaveHdr2 = NULL;
 }
 
 if(m_pBuffer1 != NULL)
 {
  free(m_pBuffer1);
  m_pBuffer1 = NULL;
 }
 
 if(m_pBuffer2 != NULL)
 {
  free(m_pBuffer2);
  m_pBuffer2 = NULL;
 }

}

//----------------------------------------------------------------------
//Decription:
// Start to record.
//
//Parameter:
// pszPath: [in] The record path
//
//Retrun Values:
// TRUE: Succeed.
// FALSE: Failed.
//----------------------------------------------------------------------
BOOL CSoundBase::Record(TCHAR *pszPath,const PWAVEFORMAT_SETTING pWaveFormat)
{
 BOOL bResult = FALSE;
 _tcscpy(m_szSavePath,pszPath);

 SetRecordWaveFormat(pWaveFormat);
 
 if (waveInOpen(&m_hWaveIn,WAVE_MAPPER,&m_WaveFormatEx,(DWORD)WaveInProc,NULL,CALLBACK_FUNCTION) != MMSYSERR_NOERROR )
 {
  goto END;
 }

 m_pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
 m_pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);
 if(m_pBuffer1 == NULL || m_pBuffer2 == NULL)
 {
  goto END;
 }


 //allocate memory for wave header
 m_pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
 m_pWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR))); 

 if(m_pWaveHdr1 == NULL || m_pWaveHdr2 == NULL )
 {
  goto END;
 }
 
 m_pWaveHdr1->lpData = (LPSTR)m_pBuffer1;
 m_pWaveHdr1->dwBufferLength = INP_BUFFER_SIZE;
 m_pWaveHdr1->dwBytesRecorded = 0;
 m_pWaveHdr1->dwUser = 0;
 m_pWaveHdr1->dwFlags = 0;
 m_pWaveHdr1->dwLoops = 1;
 m_pWaveHdr1->lpNext = NULL;
 m_pWaveHdr1->reserved = 0; 
 waveInPrepareHeader(m_hWaveIn,m_pWaveHdr1,sizeof(WAVEHDR));
 
 m_pWaveHdr2->lpData = (LPSTR)m_pBuffer2;
 m_pWaveHdr2->dwBufferLength = INP_BUFFER_SIZE;
 m_pWaveHdr2->dwBytesRecorded = 0;
 m_pWaveHdr2->dwUser = 0;
 m_pWaveHdr2->dwFlags = 0;
 m_pWaveHdr2->dwLoops = 1;
 m_pWaveHdr2->lpNext = NULL;
 m_pWaveHdr2->reserved = 0; 
 waveInPrepareHeader(m_hWaveIn,m_pWaveHdr2,sizeof(WAVEHDR));

 // Add the buffers 
 waveInAddBuffer (m_hWaveIn, m_pWaveHdr1, sizeof (WAVEHDR)) ;
 waveInAddBuffer (m_hWaveIn, m_pWaveHdr2, sizeof (WAVEHDR)) ;


 if (WriteWaveFileHeader(m_szSavePath,&m_WaveFormatEx,0,TRUE) == FALSE)
 {

  goto END;
 }

 //norains: Open the existing wave file incording to add wave data
 m_hSaveFile = CreateFile(m_szSavePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
 if( m_hSaveFile == INVALID_HANDLE_VALUE ) 
 {
  goto END;
    }
 //Set the file pointer to the end.
 SetFilePointer(m_hSaveFile,0,NULL,FILE_END);

 //Begin recording
 waveInStart (m_hWaveIn) ;

 bResult = TRUE;
END:
 if(bResult == FALSE)
 {
  if(m_pWaveHdr1 != NULL)
  {
   free(m_pWaveHdr1);
   m_pWaveHdr1 = NULL;
  }

  if(m_pWaveHdr2 != NULL)
  {
   free(m_pWaveHdr2);
   m_pWaveHdr2 = NULL;
  }

  if(m_pBuffer1 != NULL)
  {
   free(m_pBuffer1);
   m_pBuffer1 = NULL;
  }

  
  if(m_pBuffer2 != NULL)
  {
   free(m_pBuffer2);
   m_pBuffer2 = NULL;
  }

  if(m_hWaveIn != NULL)
  {
   //Stop recording, close the device
   waveInReset(m_hWaveIn);
   waveInClose(m_hWaveIn);
   m_hWaveIn = NULL;
  }
 }
 return bResult;
}

//----------------------------------------------------------------------
//Decription:
// Stop recording and playing
//
//Parameter:
// NULL
//
//Retrun Values:
// NULL
//----------------------------------------------------------------------
void CSoundBase::StopAll()
{
 StopRecording();
 StopPlaying();
}

//----------------------------------------------------------------------
//Decription:
// Stop playing
//
//Parameter:
// NULL
//
//Retrun Values:
// NULL
//----------------------------------------------------------------------
void CSoundBase::StopPlaying()
{
 sndPlaySound(NULL,SND_ASYNC);
}

//----------------------------------------------------------------------
//Decription:
// Stop recording
//
//Parameter:
// NULL
//
//Retrun Values:
// NULL
//----------------------------------------------------------------------
void CSoundBase::StopRecording()
{
 if(m_bRecording == FALSE)
 {
  return;
 }

 m_bRecording = FALSE;
 waveInReset(m_hWaveIn);
 waveInClose(m_hWaveIn);

 //Please don't call waveInUnprepareHeader() in the WIM_CLOSE,
 //or it will work badly !
 waveInUnprepareHeader (m_hWaveIn, m_pWaveHdr1, sizeof (WAVEHDR)) ;
 waveInUnprepareHeader (m_hWaveIn, m_pWaveHdr2, sizeof (WAVEHDR)) ;
  
 if(m_pWaveHdr1 != NULL)
 {
  free(m_pWaveHdr1);
  m_pWaveHdr1 = NULL;
 }

 if(m_pWaveHdr2 != NULL)
 {
  free(m_pWaveHdr2);
  m_pWaveHdr2 = NULL;
 }

 if(m_pBuffer1 != NULL)
 {
  free(m_pBuffer1);
  m_pBuffer1 = NULL;
 }
  
 if(m_pBuffer2 != NULL)
 {
  free(m_pBuffer2);
  m_pBuffer2 = NULL;
 }

 
}

//----------------------------------------------------------------------
//Decription:
// Start to play.
//
//Parameter:
// pszPath: [in] The playing path
//
//Retrun Values:
// TRUE: Succeed.
// FALSE: Failed.
//----------------------------------------------------------------------
BOOL CSoundBase::Play(const TCHAR *pszPath)
{ 
 TCHAR szPath[MAX_SAVEPATH_LENGTH] = {0};
 if(pszPath != NULL)
 {
  _tcscpy(szPath,pszPath);
 }
 else
 {
  _tcscpy(szPath,m_szSavePath);
 }

 if(sndPlaySound(szPath,SND_ASYNC) == FALSE)
 {
  return FALSE;
 }
 return TRUE; 

}


//----------------------------------------------------------------------
//Decription:
// Set the wave format for recording.
//
//Parameter:
// pWaveFormat: [in] The wave format to set.
//
//Retrun Values:
// TRUE: Succeed.
// FALSE: Failed.
//----------------------------------------------------------------------
void CSoundBase::SetRecordWaveFormat(const PWAVEFORMAT_SETTING pWaveFormat)
{
 WAVEFORMAT_SETTING waveFormatSetting;
 if(pWaveFormat == NULL)
 {
  waveFormatSetting.channel = DEFAULT_CHANNEL;
  waveFormatSetting.samples = DEFAULT_SAMPLES;
  waveFormatSetting.bits =  DEFAULT_BITS;
 }
 else
 {
  waveFormatSetting = *pWaveFormat;
 }

 m_WaveFormatEx.wFormatTag=WAVE_FORMAT_PCM;
 m_WaveFormatEx.cbSize=0; //When the wFormatTag is PCM, the parameter is abort.

 switch(waveFormatSetting.channel)
 {
  case CHANNEL_SINGLE:
   m_WaveFormatEx.nChannels = 1;
   break;
  case CHANNEL_DOUBLE:
   m_WaveFormatEx.nChannels = 2;
   break;
 }

 switch(waveFormatSetting.samples)
 {
  case SAMPLES_11025:
   m_WaveFormatEx.nSamplesPerSec = 11025;
   break;
  case SAMPLES_22050:
   m_WaveFormatEx.nSamplesPerSec = 22050;
   break;
  case SAMPLES_44100:
   m_WaveFormatEx.nSamplesPerSec = 44100;
   break;
 }


 switch(waveFormatSetting.bits)
 {
  case BITS_8:
   m_WaveFormatEx.wBitsPerSample = 8;
   break;
  case BITS_16:
   m_WaveFormatEx.wBitsPerSample = 16;
   break;
 }
 
 m_WaveFormatEx.nBlockAlign=m_WaveFormatEx.nChannels * m_WaveFormatEx.wBitsPerSample / 8; 
 m_WaveFormatEx.nAvgBytesPerSec=m_WaveFormatEx.nBlockAlign * m_WaveFormatEx.nSamplesPerSec;

}


//----------------------------------------------------------------------
//Decription:
// Get instance
//
//Parameter:
// Null
//
//Retrun Values:
// The pointer to object
//----------------------------------------------------------------------
CSoundBase* CSoundBase::GetInstance()
{
 if(m_pInstance == NULL)
 {
  m_pInstance = new CSoundBase;
 }
 return m_pInstance;
}


//----------------------------------------------------------------------
//Decription:
// WaveIn Process
//
//Parameter:
// hwi: [in] Handle to the waveform-audio device associated with the callback function
// uMsg: [in] Waveform-audio input message. It can be one of the messages shown :WIM_CLOSE,WIM_DATA,WIM_OPEN
// dwInstance: [in] User instance data specified with waveInOpen. 
// dwParam1: [in] Message parameter. 
// dwParam2: [in] Message parameter. 
//
//Retrun Values:
// DWORD type.
//----------------------------------------------------------------------
void CALLBACK CSoundBase::WaveInProc(HWAVEIN hWi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
 switch(uMsg)
 {
  case WIM_CLOSE:
   m_pInstance->OnWIM_CLOSE(dwParam1,dwParam2);
   break;
  case WIM_DATA:
   m_pInstance->OnWIM_DATA(dwParam1,dwParam2);
   break;
  case WIM_OPEN:
   m_pInstance->OnWIM_OPEN(dwParam1,dwParam2);
   break;
 }

}

 

//----------------------------------------------------------------------
//Decription:
// Write the wave file header.
//
//Parameter:
// pszFilename: [in] The path to save
// pWFX: [in] The information to write
// dwBufferSize: [in] The size of wave buffer
// bCover: [in] Cover writing or not 
//
//Retrun Values:
// TRUE: Succeed.
// FASLE: Failed.
//----------------------------------------------------------------------
BOOL CSoundBase::WriteWaveFileHeader(TCHAR *pszFilename, const PWAVEFORMATEX pWFX, DWORD dwBufferSize, BOOL bCover)
{

 RIFF_FILEHEADER FileHeader;
 RIFF_CHUNKHEADER WaveHeader;
 RIFF_CHUNKHEADER DataHeader;
 DWORD dwBytesWritten;
 HANDLE fh;
 BOOL bResult = FALSE;
    
 // Fill in the file, wave and data headers
 WaveHeader.dwCKID = RIFF_FORMAT;
 WaveHeader.dwSize = sizeof(WAVEFORMATEX) + pWFX->cbSize;

 // the DataHeader chunk contains the audio data
 DataHeader.dwCKID = RIFF_CHANNEL;
 DataHeader.dwSize = dwBufferSize;

 // The FileHeader
 FileHeader.dwRiff = RIFF_FILE;
 FileHeader.dwSize = sizeof(WaveHeader) + WaveHeader.dwSize + sizeof(DataHeader) + DataHeader.dwSize;
 FileHeader.dwWave = RIFF_WAVE;

    // Open wave file
 if(bCover==TRUE)
 {
  //If the file existed , cover writng
  fh = CreateFile(pszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
    }
 else
 {
  //Open the file existed
  fh = CreateFile(pszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  //Move the pointer to the begin
  SetFilePointer(fh,0,NULL,FILE_BEGIN);
 }
 if( fh == INVALID_HANDLE_VALUE ) {
        DEBUGMSG(1, (TEXT("Error opening %s. Error code = 0x%08x "), pszFilename, GetLastError()));
    goto ERROR_EXIT;
    }

    // write the riff file
    if (! WriteFile(fh, &FileHeader, sizeof(FileHeader), &dwBytesWritten, NULL)) {
        DEBUGMSG(1, (TEXT("Error writing file header ")));
  goto ERROR_EXIT;
    }

    // write the wave header
    if (! WriteFile(fh, &WaveHeader, sizeof(WaveHeader), &dwBytesWritten, NULL)) {
        DEBUGMSG(1, (TEXT("Error writing wave header ")));
  goto ERROR_EXIT;
    }

    // write the wave format
    if (! WriteFile(fh, pWFX, WaveHeader.dwSize, &dwBytesWritten, NULL)) {
        DEBUGMSG(1, (TEXT("Error writing wave format ")));
  goto ERROR_EXIT;
    }

    // write the data header
    if (! WriteFile(fh, &DataHeader, sizeof(DataHeader), &dwBytesWritten, NULL)) {
        DEBUGMSG(1, (TEXT("Error writing PCM data header ")));
  goto ERROR_EXIT;
    }
    // Success
    bResult = TRUE;

ERROR_EXIT:
 if(fh != INVALID_HANDLE_VALUE)
 {
  CloseHandle(fh);
    }
 return bResult;
}


//----------------------------------------------------------------------
//Decription:
// On WIM_CLOSE
//------------------------------------------------------------------------
void CSoundBase::OnWIM_CLOSE(WPARAM wParam, LPARAM lParam)
{
 
 if(m_hSaveFile != NULL)
 {
  CloseHandle(m_hSaveFile);
  m_hSaveFile = NULL;
 }
 if (0 != m_dwDataLength)
 {
  //Write the data length to the file.
  WriteWaveFileHeader(m_szSavePath,&m_WaveFormatEx,m_dwDataLength,FALSE);
 }

}

//----------------------------------------------------------------------
//Decription:
// On WIM_DATA
//------------------------------------------------------------------------
void CSoundBase::OnWIM_DATA(WPARAM wParam, LPARAM lParam)
{

 DWORD dwBytesRecorded = ((PWAVEHDR)wParam)->dwBytesRecorded;

 PBYTE pSaveBuffer;
 //allocate memory for save buffer
 pSaveBuffer = reinterpret_cast<PBYTE>(malloc(dwBytesRecorded)); 
 if(pSaveBuffer == NULL)
 {
  waveInClose (m_hWaveIn) ;
  return;
 }

 //Copy the data to the save buffer.
 CopyMemory (pSaveBuffer, ((PWAVEHDR)wParam)->lpData, dwBytesRecorded) ;

 //Write the memory data to the file
 DWORD dwBytesWritten;
 if(WriteFile(m_hSaveFile, pSaveBuffer, dwBytesRecorded, &dwBytesWritten, NULL) != TRUE)
 {
  if(m_bRecording == TRUE)
  {
   waveInClose (m_hWaveIn) ;
  }
 }

 m_dwDataLength += dwBytesWritten ;

 free(pSaveBuffer); 

 //If m_bRecording is FALSE, it may call the function waveInReset().So don't add buffer.
 if(m_bRecording == TRUE)
 {
  // Send out a new buffer.The new buffer is the original full buffer, used again.
  waveInAddBuffer (m_hWaveIn, (PWAVEHDR) wParam, sizeof (WAVEHDR)) ;
 }
}

//----------------------------------------------------------------------
//Decription:
// On WIM_OPEN
//------------------------------------------------------------------------
void CSoundBase::OnWIM_OPEN(WPARAM wParam, LPARAM lParam)
{
 m_bRecording = TRUE;
 m_dwDataLength = 0;
}