API 层实现语音录制

原创 2007年10月12日 17:18:00
我从前出于需要,上网寻找了关于录音部分的源代码,收获不大,现在把自己摸索的部分源代码公开出来吧,希望对部分人有用。

 

   其实要实现声音的录制,可以用多种的方式(例如 mci ),我这里介绍的是在 API 的层面来实现,你可以对其中进行更多的控制。比方说,要音频裸数据存到你指定的内存中去。

 

   先说一下要用到的 API , 录音通常都是使用 waveInXXX 一类 API 的,最主要的是 waveInStart (顾名思义就是开始录音),然后是 waveInStop(它能够在你指定的缓冲满的时候返回) ; 如果不用 waveInStop 可以使用 waveInReset(不同于Stop的是该函数不等缓冲满就马上返回那样你才可以终止录音),不过最好跟 waveInGetPosition 配合使用。

 

  说完这些函数,不得不提的是为以上几个函数做准备工作的函数(注意配对使用), waveInOpen 和 waveInClose 配对( waveInOpen 里面指定音频的格式,比方说立体声 和16位音质等); waveInPrepareHeader 和 waveInUnprepareHeader 配对( waveInPrepareHeader 里面指定用来录音缓冲的大小和首地址),紧跟着 waveInPrepareHeader要例行公事调用 waveInAddBuffer( 作用未详,不多说了)。

 

  详细调用过程可以看下面

 

 

 

(之前最好调用 waveInGetNumDevs 看看有没有可用的设备)

 

waveInOpen (该处用 WAVEFORMATEX 结构指定音频格式)

 

 

 

   waveInPrepareHeader (该处用 WAVEHDR 结构的 lpData 成员指定缓冲首地址)

 

   waveInAddBuffer

 

 

 

    waveInStart

 

     (录制中....)

 

    waveInStop (warning:一定要缓冲满了才返回)

 

 

 

   waveInUnprepareHeader

 

 

 

waveInClose

 

 

 

 

 

 

 

  需要指出的是,上面的代码你不能随心所欲的停止录音过程(如果你指定的缓冲非常大,比方说足够录音一个小时,那么你就要乖乖的等上一个小时),如果希望马上停止,请使用下面的方法。

 

 

 

 

 

    waveInStart

 

    (录制中....)

 

    (n 时间后,用户提出停止请求)

 

    waveInGetPosition (保留该值,用来设置 WAVEHDR 结构的 dwBytesRecorded 成员)

 

    waveInReset

 

    (重新设置 WAVEHDR 结构的 dwBytesRecorded 成员)

 

 

 

 

 

 

 

  整个过程就是如此的几句,下面将给出源程序以验证。

 

 

 

 

 

  需要说明一下,正常情况下录音以后,总共耗费的内存(以字节算) 会保存在WAVEHDR 结构的 dwBytesRecorded 成员中,可用的音频裸数据当然就放进你指定的内存缓冲中去了,你大可不必等 waveInUnprepareHeader 就可以马上拿来用了。

 

 

 

 

 

  ( 补充一个内容,粗略讲解一下 .wav 文件的文件头格式 )

 

  看下面的结构,一开始的八个字节是一个结构, 第一个 四字节 是标志,刚好等于 ascii 的 "RIFF" ,第二个四字节是总的文件长度减去8。你可以验证一下。(参照16进制阅读数字的方法)

 

  跟着是第二个数据结构,占12个字节。如果你是标准的 wave 格式的文件,那么就是紧接着两个标志,第一个四字节的标志是 ascii 的 "WAVE" ,第二个 四字节的标志就是 ascii 的 "fmt "(注意,有一个空格),然后剩下的四字节里面藏的是 WAVEFORMATEX 结构的大小(参照MSDN),应该是18个字节。

 

  然后,当然就是 18个字节的 WAVEFORMATEX 结构里面的值。详细的就不说了,自己查一下 MSDN。 (需要注意的是,这个 WAVEFORMATEXEX 的末一个成员,讲了可能的长度扩充)

 

  在这 18 个字节后面 (按照旧时候的方法),应该是跟着一个8字节的结构的,然后就是 "裸数据" 的开始地址了,这八字节结构的开始四字节是标志,应该等于 ascii 的 "data",然后紧跟着的四字节就是裸数据的大小了,也就是最重要的部分。

 

  好了,如此一来,你就可以得到音频裸数据的起始位置(紧紧跟在含"data"标志的数据结构的后面),还有就是音频数据的长度了。应该是所有的问题都很容易解决的。

 

  

 

  不过,还要注意,就是现在的很多 .wav 文件都会外加一个数据结构(12字节),就插在 WAVEFORMATEX 的后面和 含 "data" 的数据结构前面。 这个外加的结构 第一个四字节是标志,等于 ascii 码的 "fact",然后第二个四字节的值在大部分情况下都等于 4, 第三个四字节的值也是等于 音频裸数据的长度。 基本上就是这样了。

 

 

 

 

 

   下面给出的源程序文件只要加进新建的 VC win32工程中,编译即可,执行效果是录音三秒后自动生成 mytest.wav 文件供播放测试(记得选好默认录音通道)。

 

  废话不多说,给出源程序(该源程序中要包含 RunTimeLog.cpp,见http://www.csdn.net/develop/Read_Article.asp?Id=17477) 希望对大家有用。(全文完)

 

   (全文完 - 2003年03月27日_am: 11时27分)

 

 

 

 

 

 

 

// ******************* FileName: WinMain.cpp *****************************

 

 

 

// 该源程序需要加入到 VC6 的 Win32 Application 的 empty Project 中

 

// 请包含我自定义的调试类,见 #include "RunTimeLog.cpp"

 

// 对于工程的 Link 选项,至少要包含以下库: msvcrt.lib kernel32.lib user32.lib Winmm.lib

 

 

 

 

 

#define WIN32_LEAN_AND_MEAN   // Say No to MFC !!

 

 

 

#include <windows.h>

 

#include <Mmsystem.h>

 

 

 

#include "RunTimeLog.cpp"

 

 

 

RunTimeLog log;

 

 

 

 

 

char lpTemp[256]="";

 

 

 

 

 

DWORD FCC(LPSTR lpStr)

 

{

 

  DWORD Number = lpStr[0] + lpStr[1] *0x100 + lpStr[2] *0x10000 + lpStr[3] *0x1000000 ;

 

  return Number;

 

}

 

 

 

 

 

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

 

          LPSTR lpCmdLine, int nCmdShow )

 

{

 

 

 

 CreateMutex( NULL, false, "MyMutex");

 

  if ( GetLastError() == ERROR_ALREADY_EXISTS )

 

  { log.write("Exists and Exit"); log.last(); ExitProcess( NULL); }

 

 

 

 

 

  log.write("Program Start.");

 

  log.nobuff = true;

 

 

 

DWORD datasize = 48000;

 

  

 

  // 最常用法

 

WAVEFORMATEX waveformat;

 

waveformat.wFormatTag=WAVE_FORMAT_PCM;

 

waveformat.nChannels=1;

 

waveformat.nSamplesPerSec=8000;

 

waveformat.nAvgBytesPerSec=8000;

 

waveformat.nBlockAlign=1;

 

waveformat.wBitsPerSample=8; //指定录音格式

 

waveformat.cbSize=0;

 

 

 

 wsprintf( lpTemp, "WAVEFORMATEX size = %lu", sizeof(WAVEFORMATEX) );

 

 log.write(lpTemp);

 

 

 

 

 

 

 

HWAVEIN m_hWaveIn;

 

 

 

if ( waveInGetNumDevs() ) log.write("有可以使用的 WaveIn 通道"); else log.write("没有可以使用的 waveIn 通道");

 

 

 

int res=waveInOpen(&m_hWaveIn,WAVE_MAPPER, &waveformat, (DWORD)NULL,0L,CALLBACK_WINDOW); //打开录音设备

 

 

 

if ( res == MMSYSERR_NOERROR ) log.write("打开 waveIn 成功"); // 验证创建是否成功

 

else {

 

   wsprintf(lpTemp, "打开 waveIn 通道失败,Error_Code = 0x%x", res );

 

   log.write(lpTemp);

 

} // End of 验证创建是否成功

 

 

 

 

 

 

 

WAVEHDR m_pWaveHdr;

 

 

 

 m_pWaveHdr.lpData = (char *)GlobalLock( GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, datasize) );

 

 memset(m_pWaveHdr.lpData, 0, datasize );

 

 m_pWaveHdr.dwBufferLength = datasize;

 

 m_pWaveHdr.dwBytesRecorded = 0;

 

 m_pWaveHdr.dwUser = 0;

 

 m_pWaveHdr.dwFlags = 0;

 

 m_pWaveHdr.dwLoops = 0;

 

 

 

 

 

 wsprintf( lpTemp, "WAVEHDR size = %lu", sizeof(WAVEHDR) );

 

 log.write(lpTemp);

 

 

 

 

 

int resPrepare = waveInPrepareHeader( m_hWaveIn, &m_pWaveHdr, sizeof(WAVEHDR) ); //准备内存块录音

 

 

 

if ( resPrepare == MMSYSERR_NOERROR) log.write("准备录音用头文件成功");

 

 else {

 

   wsprintf(lpTemp, "不能开辟录音头文件,Error_Code = 0x%03X", resPrepare );

 

   log.write(lpTemp);

 

} // End of 验证开辟缓冲

 

 

 

 

 

 

 

 resPrepare = waveInAddBuffer( m_hWaveIn, &m_pWaveHdr, sizeof(WAVEHDR) );

 

 

 

if ( resPrepare == MMSYSERR_NOERROR) log.write("准备录音用内存成功");

 

 else {

 

   wsprintf(lpTemp, "不能开辟录音用缓冲,Error_Code = 0x%03X", resPrepare );

 

   log.write(lpTemp);

 

} // End of 验证开辟缓冲

 

 

 

 

 

 

 

 

 

 log.write(""); // 写入空字符串可以分行

 

 

 

 

 

 if (! waveInStart(m_hWaveIn) ) log.write("开始录音"); else log.write("开始录音失败");

 

 

 

 Sleep(3200);

 

 

 

 

 

MMTIME mmt;

 

mmt.wType = TIME_BYTES;

 

log.numberwrite( "sizeof(MMTIME) =", sizeof(MMTIME) );

 

log.numberwrite( "sizeof(UINT) =", sizeof(UINT) );

 

 

 

if ( waveInGetPosition(m_hWaveIn, &mmt, sizeof(MMTIME)) ) log.write("不能取得音频长度");

 

 else log.numberwrite( "取得现在音频位置 =", mmt.u.cb );

 

 

 

if (mmt.wType == TIME_BYTES) { log.write("得到的 TIME_BYTES 格式的音频长度"); }

 

  else log.write("指定的 TIME_BYTES 格式音频长度不支持");

 

 

 

 

 

// if (! waveInStop(m_hWaveIn) ) log.write("停止录音"); else  log.write("停止录音失败");

 

 

 

 if (! waveInReset(m_hWaveIn) ) log.write("重置内存区成功"); else log.write("重置内存区失败");

 

 

 

 

 

m_pWaveHdr.dwBytesRecorded = mmt.u.cb;

 

 

 

 

 

 

 

DWORD NumToWrite=0; DWORD dwNumber = 0;

 

 

 

HANDLE FileHandle =

 

    CreateFile( "myTest.wav", GENERIC_WRITE, FILE_SHARE_READ, NULL,

 

          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

 

 

 

// memset(m_pWaveHdr.lpData, 0, datasize);

 

 

 

dwNumber = FCC("RIFF");

 

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

 

 

 

dwNumber = m_pWaveHdr.dwBytesRecorded + 18 + 20;

 

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

 

 

 

dwNumber = FCC("WAVE");

 

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

 

 

 

dwNumber = FCC("fmt ");

 

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

 

 

 

dwNumber = 18L;

 

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

 

 

 

WriteFile(FileHandle, &waveformat, sizeof(WAVEFORMATEX), &NumToWrite, NULL);

 

 

 

dwNumber = FCC("data");

 

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

 

 

 

dwNumber = m_pWaveHdr.dwBytesRecorded;

 

WriteFile(FileHandle, &dwNumber, 4, &NumToWrite, NULL);

 

 

 

WriteFile(FileHandle, m_pWaveHdr.lpData, m_pWaveHdr.dwBytesRecorded, &NumToWrite, NULL);

 

 

 

SetEndOfFile(FileHandle);

 

CloseHandle( FileHandle );  FileHandle = INVALID_HANDLE_VALUE; // 收尾关闭句柄

 

 

 

log.write("应该已生成 myTest.wav 文件");

 

 

 

 

 

 

 

 if ( waveInUnprepareHeader(m_hWaveIn, &m_pWaveHdr, sizeof(WAVEHDR)) ) log.write("Un_Prepare Header 失败"); else log.write("Un_Prepare Header 成功");

 

 

 

 if ( GlobalFree(GlobalHandle( m_pWaveHdr.lpData )) ) log.write("Global Free 失败"); else log.write("Global Free 成功");

 

 

 

 

 

 

 

if (res == MMSYSERR_NOERROR ) //关闭录音设备

 

if (waveInClose(m_hWaveIn)==MMSYSERR_NOERROR)log.write("正常关闭录音设备");

 

else log.write("非正常关闭录音设备");

 

 

 

 

 

 

 

  log.last(true);

 

  // ExitProcess(0);

 

  return 0;

 

}

 

 

 

// ******************* End of File *****************************

 

API 层实现语音录制

我从前出于需要,上网寻找了关于录音部分的源代码,收获不大,现在把自己摸索的部分源代码公开出来吧,希望对部分人有用。     其实要实现声音的录制,可以用多种的方式(例如 mci ),我这里介绍的是在 ...
  • c0ffee1982
  • c0ffee1982
  • 2007-11-19 12:20:00
  • 341

API 层实现语音录制 1

我从前出于需要,上网寻找了关于录音部分的源代码,收获不大,现在把自己摸索的部分源代码公开出来吧,希望对部分人有用。     其实要实现声音的录制,可以用多种的方式(例如 mci ),我这里介绍的是在 ...
  • benny5609
  • benny5609
  • 2007-12-06 09:24:00
  • 909

IM 软件中的语音录制与播放【iOS】

原文链接:http://blog.csdn.net/gang544043963/article/details/52266903 自从微信推出语音聊天后,人们的通讯方式发生了巨大变化,硬是把智能手机...
  • qq_34047841
  • qq_34047841
  • 2016-11-17 14:31:51
  • 343

Android 录音功能(语音录制保存,播放)

// 音量,各个音量的图片 private int[] mVolumeImage = { R.drawable.record_animate01_icon, R.dra...
  • dreamon0714
  • dreamon0714
  • 2015-08-10 18:12:53
  • 823

cocos lua 实现原声语音 流程概述

由于游戏需要,在cocos里面集成了自己原声的语音系统。虽然做的过程踩了很多的坑,并且过程很痛苦。这个过程需要你去了解很多的东西。 我是直接使用的lua 调用C++,c++在通过jni 调用安卓jav...
  • u011488256
  • u011488256
  • 2016-08-26 17:15:04
  • 2928

android语音录制与播放

android语音录制可以通过MediaRecorder和AudioRecorder。 MediaRecorder本来是多媒体录制控件,可以同时录制视频和语音,当不指定视频源时就只录制语音;Audi...
  • cheweilol
  • cheweilol
  • 2015-06-04 10:47:22
  • 553

仿微信,录制音频并发送功能

MyRecorder(仿微信,录制音频并发送功能)①布局实现(activity_main.xml) 布局采用线性布局,上面使用的一个ListView,下面使用的是一个自定义的Butt...
  • linglingchenchen
  • linglingchenchen
  • 2016-07-11 13:46:34
  • 8421

学习HTML5音频录制

之前玩过WebRTC,通过HTML5可以轻松实现语音视频聊天,网上的示例很多。最近想了解下HTML5能否录制音频,用什么接口可以做到。这里分享下学习资料。 浏览器 要获取音频和视频,需要用到g...
  • yushulx
  • yushulx
  • 2016-10-18 10:50:06
  • 6296

Android语音录制和播放

楼主本来打算昨晚写的,但是昨天是万圣节,晚上不想动弹,所以推到今天来写了!简单说明一下,由于项目中用到了语音的录制、播放和上传,所以将功能抽取了出来,整理一下,也方便小伙伴们使用。效果就跟微信上语音聊...
  • AndroidStudioo
  • AndroidStudioo
  • 2017-11-01 21:00:45
  • 646

使用Web Audio API实现基于浏览器的Web端录音

有时候会有通过Web端录音的需求,那么如何实现呢,通过Web Audio API能够实现,具体可以查看官网的API,下面是实现单声道录音(压缩音频大小)的一个例子,共需要两个js文件,具体如下: 第...
  • rqs849469198
  • rqs849469198
  • 2015-12-31 11:18:28
  • 12949
收藏助手
不良信息举报
您举报文章:API 层实现语音录制
举报原因:
原因补充:

(最多只允许输入30个字)