1.簡介
CSoundBase是我封裝的一個API類,主要是爲了能方便實現聲音的錄製和播放.目前僅支持WAV的錄製和播放.
完整的代碼見本文第四節.
如果各位朋友發現有BUG需要修正,歡迎和我聯繫,謝謝!
2.使用方法
CSoundBase類的使用非常簡單,首先聲明一個類指針,然後獲取類的實例:
pSoundPlayer->CSoundBase::GetInstance();
假如我們需要在"record"文件夾中錄製一個名字爲"first"的文件,只需很簡單的一條語句:
不過這樣是採用默認的錄製格式,實際錄音中我們可以進行更改.不過在進行這一步之前,我們先看一下這個類:
...{
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.channel = CHANNEL_SINGLE;
waveFormat.samples = SAMPLES_22050;
waveFormat.bits = SAMPLES_44100;
pSoundPlayer->Record(TEXT("record/first.wav"),&waveFormat);
當想停止錄音時,可調用StopRecording():
如果想播放剛剛的錄製的wav文件,只需簡單調用Play()即可:
當然,也可以播放儲存器上任意一處的wav文件,如:
停止播放同樣也很簡單:
由於錄音和播放是分別佔用不同的緩衝區,所以我們可以邊播放邊錄音,當然了,前提是錄音和播放的不能是同一個文件:
pSoundPlayer->Play(TEXT("record/second.wav"));
這時候我們可以調用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消息時,我們不再添加錄音緩存區.
在代碼中表現如下:
...{
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;
}