




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
API 层实现语音录制我从前出于需要,上网寻找了关于录音部分的源代码,收获不大,现在把自己摸索的部分源代码公开出来吧,希望对部分人有用。其实要实现声音的录制,可以用多种的方式(例如 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 文件供播放测试(记得选好默认录音通道)。 废话不多说,给出源程序 / * 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 #include #include RunTimeLog.cpp RunTimeLog log; char lpTemp256=; DWORD FCC(LPSTR lpStr) DWORD Number = lpStr0 + lpStr1 *0x100 + lpStr2 *0x10000 + lpStr3 *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 *播放录音昨天写了语音录制(见 /develop/Read_Article.asp?Id=17627 ),现在继续讲语音播放。 要用到 .wav 文件头内容部分的请参看上一文语音录制 里的相关介绍。(我希望把这两个模块用在我正做的local语音通讯试验中) 好的,上次的程序生成了一个 myTest.wav 的音频文件,根据上次的文件格式,那么从开头数第21个字节开始就是 WAVEFORMATEX 的结构了,提供你的指针、读到你的结构中去吧。 还有,裸音频数据的长度,在开头第43个字节开始的地方,然后,从开头数第47个字节的位置就是音频裸数据的起始地址了,把数据也读入到你的缓冲中吧。(如果地址从00开始算,刚才给出的位置就减1) 小事已经具备,开始干吧。按照上次的编排,先讲一下用到的 API 。放音通常都是使用 waveOutXXX一类的 API 。 最主要的是 waveOutWrite (马上播放指定的音频缓冲),配对的是 waveOutReset(马上停止声音的播放), 外加对播放的控制 waveOutPause (暂停播放,注意: 可以在 waveOutWrite 前就已经暂停 ) 和 waveOutRestart (继续被暂停的播放)。 说完这些函数,也提一下为以上几个函数做准备工作的函数吧, waveOutOpen 和 waveOutClose 配对( waveOutOpen 里面指定音频格式,还有通知回调函数的地址 ),waveOutPrepareHeader 和 waveOutUnprepareHeader 配对(也是在 waveOutPrepareHeader 里面指定用来播放的缓冲大小和首地址)就是这么多了。下面看详细调用过程简介。waveOutOpen (指定音频格式和回调函数地址) waveOutPrepareHeader (指定音频缓冲的地址) waveOutWrite (放音中.) waveOutPause (你可以尝试在 waveOutWrite 之前暂停) (声音暂停) waveOutRestart (继续播放中.) waveOutReset waveOutUnprepareHeaderwaveOutClose 小提示:最好在 waveOutReset 前先执行 waveOutPause 否则在我的机器上会有一个噪音发生? 需要指出的是,在播放时并不能动态的知道这个声音什么时候就播放完了(总不能大概估算时间就执行 waveOutReset 吧),为了处理这个问题,我想了一个解决办法,就是打开设备的时候指定通知回调函数,在那个回调函数中执行 waveOutReset 就行了(实现时作了一点小改动) 下面将给出源程序,执行时候最好用上次介绍录音的模块生成 myTest.wav 文件,然后拷贝到当前目录下用本程序执行播放,(该源程序中要包含调试类文件 RunTimeLog.cpp,见/develop/Read_Article.asp?Id=17477 详细调试信息请看 XXX.log) (另:程序只是做试验用,没有注释,敬请见谅 ) (全文完 - 2003年03月28日_pm: 03时35分 海风: 昨天忘署名了) / * 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 #include #include RunTimeLog.cppRunTimeLog log;char lpTemp256=; DWORD CurThreadID;DWORD FCC(LPSTR lpStr) DWORD Number = lpStr0 + lpStr1 *0x100 + lpStr2 *0x10000 + lpStr3 *0x1000000 ; return Number;void CALLBACK waveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) ; log.numberwrite( Get a waveOutProc uMsg =, uMsg ); if (uMsg = WOM_DONE) log.write(WOM_DONE); PostThreadMessage( CurThreadID, WM_QUIT , 11, 22 ); / WM_QUIT return ;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); CurThreadID = GetCurrentThreadId( ); / 取得当前线程的 ID (标识号) log.write(Program Start.); / log.msg(Start Test); log.nobuff = true;DWORD datasize = 48000;WAVEFORMATEX waveformat;waveformat.wFormatTag=0; /WAVE_FORMAT_PCM;waveformat.nChannels=0; /1;waveformat.nSamplesPerSec=0; /8000;waveformat.nAvgBytesPerSec=0; /8000;waveformat.nBlockAlign=0; /1;waveformat.wBitsPerSample=0; /8; /指定录音格式waveformat.cbSize=0; wsprintf( lpTemp, WAVEFORMATEX size = %lu, sizeof(WAVEFORMATEX) ); log.write(lpTemp); / 打开文件,然后读入全部的文件长度,HANDLE fileHandle = CreateFile( myTest.wav, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle) log.write(打开可读文件); else log.write(尝试打开文件失败);if (fileHandle) DWORD dwtemp = 0; / FCC(RIFF); DWORD ReadCount = 0; DWORD bufflong = 0; ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL ); if ( dwtemp = FCC(RIFF) ) log.write(找到 RIFF 文件标志); else log.write(没有找到 RIFF 文件标志); ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL ); bufflong = dwtemp; log.numberwrite(总的文件大小, bufflong + 8 ); log.numberwrite(真的文件大小, GetFileSize(fileHandle, NULL) ); if ( (bufflong+8)!=GetFileSize(fileHandle, NULL) ) bufflong = GetFileSize(fileHandle, NULL) - 8; SetFilePointer( fileHandle, 8, NULL, FILE_CURRENT); bufflong = bufflong - 8; ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL ); bufflong = bufflong - 4; log.numberwrite( sizeof(WAVEFORMAT) =, dwtemp ); ReadFile( fileHandle , &waveformat, dwtemp, &ReadCount, NULL ); bufflong = bufflong - dwtemp; log.numberwrite( waveformat.wBitsPerSample =, waveformat.wBitsPerSample ); log.numberwrite( waveformat.nSamplesPerSec =, waveformat.nSamplesPerSec ); while (dwtemp != bufflong) ReadFile( fileHandle , &dwtemp, 4, &ReadCount, NULL ); bufflong = bufflong - 4; if ( (bufflong 8)|(bufflong bufflong - 128) ) log.write(找不到准确的缓冲大小); break; log.numberwrite( bufflong =, bufflong ); datasize = bufflong;HWAVEOUT phwo;if ( waveOutGetNumDevs() ) log.write(有可以使用的 WaveOut 通道); else log.write(没有可以使用的 waveOut 通道);int res=waveOutOpen( &phwo, WAVE_MAPPER, &waveformat, (DWORD)waveOutProc, NULL, CALLBACK_FUNCTION ); /打开录音设备if ( res = MMSYSERR_NOERROR ) log.write(打开 waveOut 成功); / 验证创建是否成功 else log.numberwrite( 打开 waveOut 失败,Error_Code =, res );WAVEHDR m_pWaveHdr; m_pWaveHdr.lpData = (char *)GlobalLock( GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, datasize) ); memset(m_pWaveHdr.lpData, 0, datasize );/ 读入磁盘数据 ReadFile( fileHandle , m_pWaveHdr.lpData, datasize, &ReadCount, NULL ); m_pWaveHdr.dwBufferLength = datasize; m_pWaveHdr.dwBytesRecorded = 0; m_pWaveHdr.dwUser = 0; m_pWaveHdr.dwFlags = 0; m_pWaveHdr.dwLoops = 0; log.numberwrite( WAVEHDR size =, sizeof(WAVEHDR) );UINT resPrepare = 0;resPrepare = waveOutPrepareHeader( phwo, &m_pWaveHdr, sizeof(WAVEHDR) );if ( resPrepare = MMSYSERR_NOERROR) log.write(准备放音用头文件成功); else log.numberwrite( 不能开辟放音头文件,Err
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年发展对象考试试题库与答案
- 2025年学历类自考学前儿童发展-幼儿园教育活动设计与组织参考题库含答案解析(5套试卷)
- 2025年学历类自考学前儿童体育教育-外国文学史参考题库含答案解析(5套试卷)
- 2025年学历类自考医学心理学-中国行政史参考题库含答案解析(5套试卷)
- 基于深度学习的网络攻击识别与防御模型-洞察及研究
- 2025年学历类自考中外教育简史-法学概论参考题库含答案解析(5套试卷)
- 项目建设借款合同范本
- 洗车用具购买合同范本
- 设备清洗合同范本
- 酒店劳务派遣合同范本
- 市儿科质量控制分中心工作考评表
- CA6140车床后托架(831001型号)的机械加工工艺规程及夹具设计774262
- SH/T 0358-199510号航空液压油
- 幼儿园中班数学课件:《图形的组合与分解-欢欢的礼物》
- 劳动保障监察业务知识
- 新入辅导员职员工培训
- 泡泡玛特公司战略分析
- 保安公司安全生产培训课件
- 毕业设计工作进程记录
- 投资银行学课件
- 普通话声母资料
评论
0/150
提交评论