分享
 
 
 

API 层实现语音播放

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

昨天写了语音录制(见 http://www.csdn.net/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

waveOutUnprepareHeader

waveOutClose

小提示:最好在 waveOutReset 前先执行 waveOutPause 否则在我的机器上会有一个噪音发生???

需要指出的是,在播放时并不能动态的知道这个声音什么时候就播放完了(总不能大概估算时间就执行 waveOutReset 吧),为了处理这个问题,我想了一个解决办法,就是打开设备的时候指定通知回调函数,在那个回调函数中执行 waveOutReset 就行了(实现时作了一点小改动)

下面将给出源程序,执行时候最好用上次介绍录音的模块生成 "myTest.wav" 文件,然后拷贝到当前目录下用本程序执行播放,(该源程序中要包含调试类文件 RunTimeLog.cpp,见http://www.csdn.net/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 <windows.h>

#include <Mmsystem.h>

#include "RunTimeLog.cpp"

RunTimeLog log;

char lpTemp[256]="";

DWORD CurThreadID;

DWORD FCC(LPSTR lpStr)

{

DWORD Number = lpStr[0] + lpStr[1] *0x100 + lpStr[2] *0x10000 + lpStr[3] *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( "不能开辟放音头文件,Error_Code =", resPrepare );

if ( waveOutPause(phwo) ) log.write("无法 Pause"); else log.write("成功 Pause");

if ( waveOutWrite( phwo, &m_pWaveHdr, sizeof(WAVEHDR) ) ) log.write("开始写入放音数据失败");

else log.write("开始写入放音数据成功");

if ( waveOutRestart(phwo) ) log.write(" 非法 Resume"); else log.write(" Resume 到 Playback");

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

PostThreadMessage( CurThreadID, WM_USER , 11, 22 ); // WM_QUIT

// 进入消息循环

MSG msg;

while(1)

{

if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{ log.numberwrite( "Get The uMsg =", msg.message ); if (msg.message == WM_QUIT) break; }

else { log.write("没有取到消息"); WaitMessage(); }

} // end while

if ( waveOutPause(phwo) ) log.write("无法 Pause"); else log.write("成功 Pause");

if ( waveOutReset(phwo) ) log.write("不能 Reset"); else log.write("成功 Reset");

resPrepare = waveOutUnprepareHeader( phwo, &m_pWaveHdr, sizeof(WAVEHDR) );

if ( resPrepare == MMSYSERR_NOERROR) log.write("释放放音用头文件成功");

else log.numberwrite( "不能释放放音头文件,Error_Code =", resPrepare );

if ( m_pWaveHdr.lpData )

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

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

if (waveOutClose(phwo)==MMSYSERR_NOERROR)log.write("正常关闭放音设备");

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

CloseHandle(fileHandle); fileHandle = INVALID_HANDLE_VALUE;

}

log.last(true);

// ExitProcess(0);

return 0;

}

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有