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

posted @ 2006-06-13 23:01  我的一天  阅读(352)  评论(0编辑  收藏  举报