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

你可能感兴趣的:(EVC录音详解)