API 层实现语音录制
API 层实现语音录制 | ||
发表日期:2003-03-31 | 作者:temp77[] 出处: |
我从前出于需要,上网寻找了关于录音部分的源代码,收获不大,现在把自己摸索的部分源代码公开出来吧,希望对部分人有用。 其实要实现声音的录制,可以用多种的方式(例如 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 层实现语音录制相关推荐
- 移动端语音html,掘金:H5实现移动端语音录制功能
年前做了一期语音口令的年度活动,从语音录制.上传到智能检测,以及后续的语音播放组件,语音录制的实现的方式是基于微信的JSSDK,本篇主要把语音录制板块整理了一下,供大家参考: 各位看官先上眼: 功能梳 ...
- 微信小程序 语音录制功能和文件(ppt、word、excel、pdf、txt格式)上传
语音录制 提醒:因为开发者工具不能接收语音文件,所以要使用真机调试测试噢~ 思路:使用小程序API wx.startRecord .wx.stopRecord 方法(调用前 记得 app.js 添加 ...
- 微信小程序之----语音录制功能
完整代码,请读者耐心看完 注意 首先创建录音对象 const recorderManager = wx.getRecorderManager() const innerAudioContext = w ...
- 即时通讯-语音录制及播放
即时通讯 即时通信的要点就是消息内容不大,并且传输迅速,并且是即时到达,实时通知的. 所以我们对语音进行一些处理,语音处理的过程如下: 录制录音 获取数据 编码保存 接收数据 数据解码 播放录音 为什 ...
- spring api层打包_Spring项目的按层打包已过时
spring api层打包 我认为Spring应用程序不应该以逐层方法构造. 在我看来,按功能打包更有意义. 首先,让我简要描述每种方法. "按层打包"(在非Java世界中为&qu ...
- android 仿微信聊天界面 以及语音录制功能,Android仿微信录制语音功能
本文实例为大家分享了Android仿微信录制语音的具体代码,供大家参考,具体内容如下 前言 我把录音分成了两部分 1.UI界面,弹窗读秒 2.一个类(包含开始.停止.创建文件名功能) 第一部分 由于6 ...
- Po.et正式推出为出版商打造的API层应用—Frost
点击上方 "蓝色字" 可关注我们! 编辑:铅笔盒 当一个项目宣称落地时,应该是怎样的姿势? 我们来看看版权类区块链项目Po.et的开发进展: Po.et CTO Po.et近日正式 ...
- android模拟微信聊天功能,android仿微信聊天界面 语音录制功能
本例为模仿微信聊天界面UI设计,文字发送以及语言录制UI. 1先看效果图: 第一:chat.xml设计 android:layout_width="fill_parent" and ...
- IM软件中的语音录制与播放【iOS】
前言 自从微信推出语音聊天后,人们的通讯方式发生了巨大变化,硬是把智能手机变成了对讲机.之后也成为了各种实时通讯软件不可或缺的功能.前一阵子微信公众号中展开了一场"发送语音消息利弊" ...
最新文章
- 真正的职场强者,都是闭环主义高手
- 洛谷P1659 养猪
- dw替换多个html标签,DW查找替换的技巧
- 解决6410 WINCE6 应用层调用SetSystemPowerState api关机无效的问题
- Typecho 支持黑暗模式的后台主题插件
- BZOJ3833 : [Poi2014]Solar lamps
- Linux 环境下 Git 安装与基本配置
- 计算机新增桌面的路径,自动更改Windows新建用户桌面文件默认路径修改
- 软件测试——PreDate函数
- 移动警务系统建设方案
- unity连接mysql数据库-最新详细
- Office 2007 Foxit PDF预览器/处理程序
- 6种php加密解密方法
- 既风骚又哲理的10句话,不服不行
- H3C无线AC+AP配置2—有密码登录
- 全新上线,亿图图示小程序
- 零基础编写图片服务器(1)
- 二维码生成和解码(二)
- 彻底卸载vm 十分有用
- php多级审批功能,工作流定义:动态多级审批的应用场景?
热门文章
- Linux 中/etc/profile、~/.bash_profile 环境变量执行过程
- 正月初二 | 2月13日 星期六 | 大年初一单日票房创新纪录;除夕当天外卖订单增七成;央视春晚直播受众达11.4亿人...
- 头条 上传图片大小_【标签头条】北京市启用进口冷链食品追溯平台;全球包裹热潮助推标签业发展;数字水印实现大规模垃圾分类;安慕希的麻将酸奶包装好真实...
- 第三届“信息论与编码”中大论坛诚邀您的参加
- 作者:曾琛(1987-),女,就职于中国科学院计算技术研究所。
- 作者:张国惠(1978-),男,美国新墨西哥大学土木工程系助理教授、博士生导师。...
- 基本不等式解决乘积最大问题(洛谷P1887题题解,Java语言描述)
- 洛谷P1534题解(Java语言描述)
- 计算机基本概念及简单的二进制运算
- 机器学习week8 ex7 review