evc教程_EVC录音详解

//=====================================================================================================

//TITLE:

//  EVC录音详解

//AUTHOR:

//  norains

//DATE:

//  Friday 9-June-2006

//=====================================================================================================

借助evc在wince下实现录音不是一件难事.恩,的确不是一件难事.本文主要解释如何使用wavein,并且把声音以wave文件形式保存到储存器中.

最先,我们要分配两个缓冲区.因为数据首先要保存到内存中,两个内存缓存区间可以较快进行切换,可以避免录音有断断续续的现象.

#define  INP_BUFFER_SIZE 16*1024 //输入的缓冲区长度

PBYTE pBuffer1,pBuffer2; //保存输入数据的两个缓冲区

pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);

pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);

if (!pBuffer1 || !pBuffer2)

{

if (pBuffer1) free(pBuffer1);

if (pBuffer2) free(pBuffer2);

AfxMessageBox(L"Memory erro!");

return ;

}

接下来需要设置录音的方式,需要用到WAVEFORMATEX结构.声道数,采样位和采样率都可以在这结构中设置.

WAVEFORMATEX waveform;

waveform.wFormatTag=WAVE_FORMAT_PCM;  //录音的格式

waveform.cbSize=0;          //方式为WAVE_FORMAT_PCM时此参数可以忽略

waveform.nChannels=1;         //声道数,数值可为1或2

waveform.nSamplesPerSec=11025;    //采样率,数值有:11025,22050,44100

waveform.wBitsPerSample=8;      //采样位,数值有:8,16

waveform.nBlockAlign=waveform.nChannels * waveform.wBitsPerSample / 8;

waveform.nAvgBytesPerSec=waveform.nBlockAlign * waveform.nSamplesPerSec;

设置完毕之后,就可以用waveInOpen函数打开输入设备.

HWAVEIN hWaveIn; //输入设备句柄

if (waveInOpen(&hWaveIn,WAVE_MAPPER,&waveform,(DWORD)this->m_hWnd,NULL,CALLBACK_WINDOW))

{

free(pBuffer1);

free(pBuffer2);

AfxMessageBox(L"无法打开录音设备");

return;

}

设备可以打开后,就需要初始化两个输入缓存区的声音文件头了.声音文件头主要是在录音时,记录相关的数据,以方便后期的处理.

PWAVEHDR pWaveHdr1,pWaveHdr2;

pWaveHdr1->lpData=(LPSTR)pBuffer1;      //缓冲区地址

pWaveHdr1->dwBufferLength=INP_BUFFER_SIZE;  //缓冲区长度

pWaveHdr1->dwBytesRecorded=0;

pWaveHdr1->dwUser=0;

pWaveHdr1->dwFlags=0;

pWaveHdr1->dwLoops=1;

pWaveHdr1->lpNext=NULL;

pWaveHdr1->reserved=0;

waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));  //将缓冲区信息和输入设备相关联

waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ; //将缓冲区地址和输入设备相关联

在对PWAVEHDR进行赋值时,本程序中需要设置的其实只有lpData和dwBufferLength.接下来将pWaveHdr2同pWaveHdr1进行相关处理(略).

由于我们是要将录音数据以文件形式保存到非易失性存储器上,所以在开始录音之前我们需要先建立文件,并且把相关的文件头信息写入(WriteWaveFileHeader是自写函数,代码附在文章最后).

//先写文件头

MMRESULT mr;

mr=WriteWaveFileHeader(strSavePath,&waveform,0,TRUE);

if(mr != MMSYSERR_NOERROR)

{

AfxMessageBox(L"文件保存失败!");

//停止录音,关闭设备

waveInReset(hWaveIn);

return;

}

//获取文件句柄,方便之后对其添加数据.

m_fh = CreateFile(strSavePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

if( m_fh == INVALID_HANDLE_VALUE )

{

AfxMessageBox(L"添加数据音频数据错误");

return ;

}

一切准备就绪之后,就可以调用函数waveInStart()来进行真正的录音了:

waveInStart(hWaveIn);

在录音过程中,有三个回调函数系统会自动调用,分别是:OnMM_WIM_OPEN(),OnMM_WIM_DATA()和OnMM_WIM_CLOSE().顾名思义,这三个函数分别在这三种情况下调用:开始录音时;缓冲区用完时;录音关闭时.其中OnMM_WIM_OPEN()和OnMM_WIM_CLOSE()只调用一次.本程序最重要是对OnMM_WIM_DATA()函数进行处理.

相关代码如下:

void OnMM_WIM_DATA(UINT wParam, LONG lParam)

{

//bEnding是一个外部定义的BOOL变量,用来判断外部是否按下"停止"按钮;是则不分配内存,直接返回.

if (bEnding)

{

//关闭录音

waveInClose (hWaveIn) ;

return ;

}

//dwDataLength是一个外部定义的DWORD变量,用来记录录音数据的长度.

dwDataLength += ((PWAVEHDR) lParam)->dwBytesRecorded ;

//将内存数据写到文件中

//pSaveBuffer是外部定义的一个临时缓存

pSaveBuffer=(PBYTE)realloc (pSaveBuffer, ((PWAVEHDR) lParam)->dwBytesRecorded);

CopyMemory (pSaveBuffer, ((PWAVEHDR) lParam)->lpData,((PWAVEHDR) lParam)->dwBytesRecorded) ;

m_bAddSuc=AddWaveFileDate(m_fh,pSaveBuffer,((PWAVEHDR) lParam)->dwBytesRecorded);

if(m_bAddSuc==FALSE)

{

//加入不成功

waveInClose (hWaveIn) ;

return ;

}

//加入新的内存

waveInAddBuffer (hWaveIn, (PWAVEHDR) lParam, sizeof (WAVEHDR)) ;

}

录音完毕则调用OnMM_WIM_CLOSE(),我们在此函数体里进行相关的收尾清除工作

void CRecordDlg::OnMM_WIM_CLOSE(UINT wParam, LONG lParam)

{

//关闭文件句柄

CloseHandle(m_fh);

if (0==dwDataLength)

{

//长度为0,可能录音失败

return;

}

//重写一次文件头,将文件长度写入文件中

MMRESULT mr;

mr=WriteWaveFileHeader(strSavePath,&waveform,dwDataLength,FALSE);

if(mr != MMSYSERR_NOERROR)

{

AfxMessageBox(L"重写文件头失败!");

return;

}

waveInUnprepareHeader (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;

waveInUnprepareHeader (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;

free (pBuffer1) ;

free (pBuffer2) ;

}

至此,整个录音程序结束.

附录:相关文件函数

//******************************************************************************************

//写wav文件头

//---------------------------------------------------------------

//pszFilename:保存的路径

//pWFX:保存的格式信息

//dwBufferSize:保存WAV的长度

//bCover:创建文件时如果原文件存在,是否截断(在此函数意义是:是新建文件写文件头,还是改写文件头)

//*******************************************************************************************

//----------------------------------------------------------------

MMRESULT CRecordDlg::WriteWaveFileHeader(LPCTSTR pszFilename, PWAVEFORMATEX pWFX, DWORD dwBufferSize,BOOL bCover)

{

RIFF_FILEHEADER FileHeader;

RIFF_CHUNKHEADER WaveHeader;

RIFF_CHUNKHEADER DataHeader;

DWORD dwBytesWritten;

HANDLE fh;

MMRESULT mmRet = MMSYSERR_ERROR;

// 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;

//-------------------------------------------------

//追踪一下

DWORD i=sizeof(WaveHeader);

i=sizeof(WaveHeader) + WaveHeader.dwSize;

i=sizeof(WaveHeader) + WaveHeader.dwSize + sizeof(DataHeader);

i=sizeof(WaveHeader) + WaveHeader.dwSize + sizeof(DataHeader) + DataHeader.dwSize;

//--------------------------------------------------

// Open wave file

if(bCover==TRUE)

{

//如果原文件已存在,则把原文件截断(覆盖)

fh = CreateFile(pszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);

}

else

{

//打开已存在的原文件

fh = CreateFile(pszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

//将文件指针移到文件头

SetFilePointer(fh,0,NULL,FILE_BEGIN);

}

if( fh == INVALID_HANDLE_VALUE ) {

RETAILMSG(1, (TEXT("Error opening %s. Error code = 0x%08x/n"), pszFilename, GetLastError()));

//AfxMessageBox(L"error open file in writing header");

return mmRet;

}

// write the riff file

if (! WriteFile(fh, &FileHeader, sizeof(FileHeader), &dwBytesWritten, NULL)) {

RETAILMSG(1, (TEXT("Error writing file header/r/n")));

//AfxMessageBox(L"erro writing file header");

goto ERROR_EXIT;

}

// write the wave header

if (! WriteFile(fh, &WaveHeader, sizeof(WaveHeader), &dwBytesWritten, NULL)) {

RETAILMSG(1, (TEXT("Error writing wave header/r/n")));

//AfxMessageBox(L"erroer writing wave header");

goto ERROR_EXIT;

}

// write the wave format

if (! WriteFile(fh, pWFX, WaveHeader.dwSize, &dwBytesWritten, NULL)) {

RETAILMSG(1, (TEXT("Error writing wave format/r/n")));

//AfxMessageBox(L"error writing wave formate");

goto ERROR_EXIT;

}

// write the data header

if (! WriteFile(fh, &DataHeader, sizeof(DataHeader), &dwBytesWritten, NULL)) {

RETAILMSG(1, (TEXT("Error writing PCM data header/r/n")));

//AfxMessageBox(L"error wrting pcm data header");

goto ERROR_EXIT;

}

/*-----------------------------------------------------------------------------

//此函数只是为了写文件头,不写录音数据

// write the PCM data

if (! WriteFile(fh, pBufferBits, DataHeader.dwSize, &dwBytesWritten, NULL)) {

RETAILMSG(1, (TEXT("Error writing PCM data/r/n")));

AfxMessageBox(L"error wrting pcm data");

goto ERROR_EXIT;

}

---------------------------------------------------------------------------------*/

// Success

mmRet = MMSYSERR_NOERROR;

ERROR_EXIT:

CloseHandle(fh);

return mmRet;

}

//****************************************************************************************

//将wav的数据加到现存的一个文件中

//-----------------------------------------------------------------

//LPCTSTR pszFilename 要追加的文件名

//PBYTE pBufferBits 要写入的数据

//DWORD dwBufferSize 要写入数据的长度

//-----------------------------------------------------------------

//*****************************************************************************************

BOOL CRecordDlg::AddWaveFileDate(HANDLE fh, PBYTE pBufferBits, DWORD dwBufferSize)

{

//HANDLE fh;

DWORD dwBytesWritten;

/*

// Open the existing wave file

fh = CreateFile(pszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

if( fh == INVALID_HANDLE_VALUE )

{

RETAILMSG(1, (TEXT("Error opening %s. Error code = 0x%08x/n"), pszFilename, GetLastError()));

AfxMessageBox(L"error open file in the adding");

return FALSE;

}

//将文件指针移到文件尾

SetFilePointer(fh,0,NULL,FILE_END);

*/

// write the PCM data

if (! WriteFile(fh, pBufferBits, dwBufferSize, &dwBytesWritten, NULL)) {

RETAILMSG(1, (TEXT("Error writing PCM data/r/n")));

//AfxMessageBox(L"当前存储器已满");

goto ERROR_EXIT;

}

//CloseHandle(fh);

return TRUE;

ERROR_EXIT:

//CloseHandle(fh);

return FALSE;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值