RSS
热门关键字:  计算机有关资料  33252  mkv  word  MKV_NT
当前位置 : 主页>vc编程>多媒体/游戏>列表

API层实现语音播放

来源: 作者:海风 时间:2007-09-18 点击:
  上篇写了语音录制(见 http://www.fortime.net/html/directshow/20070918/2619.html ),现在继续讲语音播放。  要用到 .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,详细调试信息请看 "XXX.log") (另:程序只是做试验用,没有注释,敬请见谅 )

// *******************  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 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  *****************************
最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册