登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

面包会有的

... ...

 
 
 

日志

 
 

灰鸽子有个功能就是语音控制,在打游戏时我把它当语音电话用,效果出奇的好。  

2011-11-05 00:54:14|  分类: 音频采集 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
灰鸽子有个功能就是语音控制,在打游戏时我把它当语音电话用,效果出奇的好。 

编程思路: 

对语音控制思路为:先在服务端录音然后通用网络传输最后在客户端播放,下面我们分别讨论录音,传输,放音的实现步骤 

录音实现: 

对计算机录音我们可以使用一系列API,简单过程如下 
waveInOpen                        打开录音设备 
waveInPrepareHeader                准备录音缓冲区 
waveInAddBuffer                将缓冲区加入队列 
waveInStart                        开始录音 
waveInUnPrepareHeader        释放录音缓冲区 
waveInReset                         停止录音 
waveInClose                        关闭录音设备 

放音实现: 

对计算机放音,简单过程如下 

waveOutOpen                打开回放设备 
waveOutPrepareHeader准备放音缓冲区 
waveOutWrite                开始播放 
waveOutRest                停止放音 
waveOutClose                关闭回放设备 
放音与录音相差无几,在后面的实例中将详细说明它的的使用 

文件传输: 

对于未经压缩处理的音频数据,它的体积是相当壮观的,对音频数据有效的压缩可以提高传输效率,为了方便本文没有对 
数据进行压缩,而直接使用TCP进行传输 


连续录/放音实现方法: 

为了实现声音的平滑播放,在录放音时通常准备两个以上的缓冲区,当一个缓冲区用完后,将发出一个结束消息,并自动 
转入下个缓冲区。当录音完成时会发出一个 MM_WIM_DATA消息,当放音完成时会发出一个MM_WOM_DONE消息。 

两个重要的结构: 

1.声音采样格式 
原形如下: 
typedef struct { 
    WORD  wFormatTag;                 //数据格式,一般为WAVE_FORMAT_PCM即脉冲编码 
    WORD  nChannels;                 //声道 
    DWORD nSamplesPerSec;         //采样频率 
    DWORD nAvgBytesPerSec;         //每秒数据量 
    WORD  nBlockAlign; 
    WORD  wBitsPerSample;        //样本大小 
    WORD  cbSize; 
} WAVEFORMATEX;   
对于这个结构我们通常使用默认或固定的值 

2.音频数据块缓存结构WAVEHDR 
其声明如下:   
type struct{ 
LPSTR lpData;                 //指向锁定的数据缓冲区的指针 
DWORD dwBufferLength;         //数据缓冲区的大小 
DWORD dwByteRecorded;         //录音时指明缓冲区中的数据量 
DWORD dwUser;                 //用户数据 
DWORD dwFlag;                 //提供缓冲区信息的标志 
DWORD dwLoops;                 //循环播放的次数 
struct wavehdr_tag *lpNext; //保留 
DWORD reserved;                 //保留 
} WAVEHDR; 

声音的采集和播放都要使用这个音频数据块结构,实际上主要用到的就是第一个成员变量lpData和第二个成员变量dwBufferLength。         


相关AIP的使用: 

waveInOpen的原型如下 

MMRESULT waveInOpen( 
  LPHWAVEIN phwi,            //输入设备句柄一个指向HWAVEIN的指针 
  UINT uDeviceID,            //输入设备ID 
  LPWAVEFORMATEX pwfx,       //录音格式指针 
  DWORD dwCallback,          //处理MM_***消息的回调函数或窗口句柄 
  DWORD dwCallbackInstance,   
  DWORD fdwOpen              //处理消息方式的符号位 
); 

  

在打开录音设置后就要指定录音缓冲区 
它原形如下: 
MMRESULT waveInPrepareHeader( 
  HWAVEIN hwi,                     
  LPWAVEHDR pwh,                   
  UINT cbwh                       
); 
其中HWAVEIN hwi为我们上面用waveInOpen打开的句柄,pwh为音频数据块缓存结构WAVEHDR。 
其它的操作都比较简单就不再一一说明了,可参照MSDN使用。 


服务端实现: 


在开始前我们需要加载winmm.lib库和 mmsystem.h头文件 
#include <mmsystem.h> 
#pragma comment(lib,"winmm") 

在开始录音按钮上添加如下代码: 


        m_RecStart.EnableWindow(false);                        //停用录音按钮 
        m_RecStart.SetWindowText("录音中...");        //改变按钮文字 
        m_exit.SetFocus();                                                //设置焦点按钮 

        wavehdr=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR))); 

        //录音采样格式 
        waveform.wFormatTag=WAVE_FORMAT_PCM; 
        waveform.nChannels=1; 
        waveform.nSamplesPerSec=11025; 
        waveform.nAvgBytesPerSec=11025; 
        waveform.nBlockAlign=1; 
        waveform.wBitsPerSample=8; 
        waveform.cbSize=0; 

        //设定缓冲结构 
        wavehdr->lpData=(LPTSTR)buffer; 
        wavehdr->dwBufferLength=BUFFER_SIZE; 
        wavehdr->dwBytesRecorded=0; 
        wavehdr->dwUser=0; 
        wavehdr->dwFlags=0; 
        wavehdr->dwLoops=1; 
        wavehdr->lpNext=NULL; 
        wavehdr->reserved=0; 

        //打开录音设备函数 
        if (waveInOpen(&hWaveIn,WAVE_MAPPER,&waveform,(DWORD)this->m_hWnd,NULL,CALLBACK_WINDOW)) 
        { 

                AfxMessageBox("Audio can not be open!"); 
        } 
        

        for(int i=0;i<2;i++)//加入2个缓冲区 
        { 
        //为录音设备准备缓冲区 
        waveInPrepareHeader(hWaveIn,wavehdr,sizeof(WAVEHDR)); 
        //给输入设备增加一个缓存 
        waveInAddBuffer (hWaveIn, wavehdr, sizeof (WAVEHDR)) ; 
        } 
        waveInStart (hWaveIn) ;//开始录音 

当缓存录满后系统将发出MM_WIM_DATA消息,我们添加消息处理函数,当收到MM_WIM_DATA消息时就将数据发给 
客户端处理,对于添加消息的方法可以参考一下VC教程。在MM_WIM_DATA消息中发送数据代码如下: 

void CCCDlg::OnMM_WIM_DATA(UINT wParam,LONG lParam)//录音完成 


        //释放录音缓冲区 
        waveInUnprepareHeader(hWaveIn,wavehdr,sizeof(WAVEHDR)); 
        //拷贝录音数据 
        CopyMemory(buffer,wavehdr->lpData,wavehdr->dwBufferLength); 
        //调用函数发送数据 
        SendBuffer(buffer); 
        //重新准备缓冲区 
        waveInPrepareHeader(hWaveIn,wavehdr,sizeof(WAVEHDR)); 
        //重新加入缓冲区 
        waveInAddBuffer (hWaveIn, wavehdr, sizeof (WAVEHDR)) ; 

当录音完成后系统会自动转入下个缓冲区,继续录音,我们就释放录音缓冲区,然后拷贝数据,最后重新加入缓冲区,这样就实现了对声音的循环录制。 

发送数据函数SendBuffer(buffer)代码如下: 
void SendBuffer(char *buffer) 

        WSADATA wsadata; 
        SOCKET client; 
        SOCKADDR_IN serveraddr; 
        int port=5555; 
        WORD ver=MAKEWORD(2,2);                                                        //判断winsock版本 
        WSAStartup(ver,&wsadata);                                                //初始SOCKET 
        client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
        serveraddr.sin_family=AF_INET; 
        serveraddr.sin_port=htons(port); 
        serveraddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); 
        connect(client,(SOCKADDR*)&serveraddr,sizeof(serveraddr)); 
        send(client,buffer,BUFFER_SIZE,0);//发送数据 
        closesocket(client); 
        WSACleanup(); 


  

客户端实现: 

在对话框上添加监听按钮,并加入响应代码: 

void CSSDlg::OnStart() 

                m_start.SetWindowText("监听中...");                //改变按钮文字 
                m_start.EnableWindow(false);                        //停用录音按钮 
                hwnd=m_hWnd; 
                ::SendMessage(hwnd,MM_WOM_DONE,0,0);                //发送MM_WOM_DONE消息 


MM_WOM_DONE消息函数代码如下: 

void CSSDlg::OnMM_WOM_DONE(UINT wParam,LONG lParam)//放音结束 

        WSADATA wsadata; 
        SOCKET server; 
        SOCKET client; 
        SOCKADDR_IN serveraddr; 
        SOCKADDR_IN clientaddr; 
        int port=5555; 
        WORD ver=MAKEWORD(2,2);                                                        //判断winsock版本 
        WSAStartup(ver,&wsadata);                                                //初始SOCKET 

        char *buffer=(char *)malloc(BUFFER_SIZE);                //分配空间 
        if (!buffer) 
        { 
                AfxMessageBox("Memory error!"); 
        } 

        server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 

        serveraddr.sin_family=AF_INET; 
        serveraddr.sin_port=htons(port); 
        serveraddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY); 


        bind(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr)); 
        listen(server,5); 
        int len=sizeof(clientaddr); 
        client=accept(server,(sockaddr *)&clientaddr,&len); 

        if(recv(client,buffer,BUFFER_SIZE,0)) 
        { 
                wavehdr->lpData=(LPTSTR)buffer; 
                wavehdr->dwBufferLength=BUFFER_SIZE; 
                wavehdr->dwBytesRecorded=0; 
                wavehdr->dwUser=0; 
                wavehdr->dwFlags=0; 
                wavehdr->dwLoops=1; 
                wavehdr->lpNext=NULL; 
                wavehdr->reserved=0; 

                waveform.wFormatTag                =        WAVE_FORMAT_PCM; 
                waveform.nChannels                =        1; 
                waveform.nSamplesPerSec        =        11025; 
                waveform.nAvgBytesPerSec=        11025; 
                waveform.nBlockAlign        =        1; 
                waveform.wBitsPerSample        =        8; 
                waveform.cbSize                        =        0; 

        waveOutOpen(&hWaveOut,WAVE_MAPPER,&waveform,(DWORD)hwnd,NULL,CALLBACK_WINDOW) 
        waveOutPrepareHeader (hWaveOut, wavehdr, sizeof (WAVEHDR)) 
        waveOutWrite (hWaveOut, wavehdr, sizeof (WAVEHDR)) 
        } 

        closesocket(server); 
        closesocket(client); 
        WSACleanup(); 


我们用SendMessage(hwnd,MM_WOM_DONE,0,0)手动发送MM_WOM_DONE消息后,程序开始接受网络数据并进行播放,当播放结束时又自动发出一个MM_WOM_DONE消息,从而实现循环接受数据并播放。 

现在就实现了对目标语音的监控,但对语音的平滑播放,压缩传输,还差好多,如果有兴趣的话还可以把上文的服务端与客户端代码整合的一块实现双向通信,做个网络电话,不过在木马中好像没太大必要呵呵 
  评论这张
 
阅读(1401)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018