Windows 下音频数据采集和播放
音频操作所需头文件和链接库
#include<mmreg.h>
#pragma comment(lib, "winmm.lib")
由于音频采集过程是一个持续过程,所以建议为它们各自分配一个线程,而使用MFC的 CWinThread 类是一个不错的选择,笔者就是利用CWinThread类将这两个功能封装成了两个独立的类,为以后的使用提供了很大的便利性。笔者在此为读者提供本人写好的一个工程,此工程为视频语音采集的不完善版,目前实现语音本地采集与播放,VFW视频采集与显示(视频不清晰),在后续章节会将VFW视频采集进行总结,敬请期待。。。。。
工程下载地址:http://pan.baidu.com/share/link?shareid=190628&uk=2735225556 中选择 VideoPlay.rar 下载,此项目是用vs2010编译
一、音频采集
操作步骤:
1、分配数据buffer,通过WAVEHDR结构体保存,准备存储采集到的音频数据,此处应该根据采集频率设置足量的buffer
void CRecodeSound::PreCreateHeader()
{
for(int i=0;i<MAXRECBUFFER;i++)
m_RecHead[i]=CreateWaveHeader();
m_IsAllocated = 1;
}
LPWAVEHDR CRecodeSound::CreateWaveHeader()
{
LPWAVEHDR lpHdr = new WAVEHDR;if(lpHdr==NULL)
{
m_RecodeLog.WriteString(TEXT("\n Unable to allocate the memory"));
return NULL;
}ZeroMemory(lpHdr, sizeof(WAVEHDR));
char* lpByte = new char[RECBUFFER];//m_WaveFormatEx.nBlockAlign*SOUNDSAMPLES)];if(lpByte==NULL)
{
m_RecodeLog.WriteString(TEXT("\n Unable to allocate the memory"));
return NULL;
}
lpHdr->lpData = lpByte;
lpHdr->dwBufferLength =RECBUFFER; // (m_WaveFormatEx.nBlockAlign*SOUNDSAMPLES);
return lpHdr;}
2、初始化音频格式结构体 WAVEFORMATEX。
memset(&m_WaveFormatEx, 0, sizeof(m_WaveFormatEx));
m_WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;//声音格式为PCM
m_WaveFormatEx.nChannels = 1; //采样声道数,对于单声道音频设置为1,立体声设置为2
m_WaveFormatEx.wBitsPerSample = 8;//采样比特 8bits/次
m_WaveFormatEx.cbSize = 0;//一般为0
m_WaveFormatEx.nSamplesPerSec = 8000; //采样率 16000 次/秒
m_WaveFormatEx.nBlockAlign = 1; //一个块的大小,采样bit的字节数乘以声道数
m_WaveFormatEx.nAvgBytesPerSec = 8000; //每秒的数据率,就是每秒能采集多少字节的数据3、waveInOpen打开音频输入设备准备开始采集
//开启音频采集
MMRESULT mmReturn = ::waveInOpen( &m_hRecord, WAVE_MAPPER,
&m_WaveFormatEx, ::GetCurrentThreadId(), 0, CALLBACK_THREAD);//Error has occured while opening device
if(mmReturn != MMSYSERR_NOERROR ) //打开采集失败
{
displayError(mmReturn,"Open");
return ;//FALSE;
}4、waveInPrepareHeader 和 waveInAddBuffer 配合将准备好的buffer提供给设备
//将准备好的buffer提供给音频输入设备
for(int i=0; i < MAXRECBUFFER ; i++)
{
//准备一个bufrer给输入设备
mmReturn = ::waveInPrepareHeader(m_hRecord,m_RecHead[i], sizeof(WAVEHDR));
//发送一个buffer给指定的输入设备,当buffer填满将会通知程序
mmReturn = ::waveInAddBuffer(m_hRecord, m_RecHead[i], sizeof(WAVEHDR));
}5、waveInStart正式开始采集
//开启指定的输入采集设备
mmReturn = ::waveInStart(m_hRecord);if(mmReturn!=MMSYSERR_NOERROR ) //开始采集失败
displayError(mmReturn,"Start");
else
m_IsRecoding = TRUE;6、每当一个buffer数据填满时,会触发 MM_WIM_DATA 消息,在程序中捕获此消息,通过消息传递过来的 lParam,为指向数据buffer的WAVEHDR指针。采集到此数据时可以根据程序需要对其做相应的处理。本程序是直接将采集到的数据提供给播放线程直接播放,你也可以通过socket发送到远端在播放,就可以网络语音了。
void CRecodeSound::OnSoundData(WPARAM wParam, LPARAM lParam)
{
m_RecodeLog.WriteString(TEXT("\nIn the onsound data"));if(m_IsRecoding==FALSE) //如果当前不在采集状态
return ;//FALSE;LPWAVEHDR lpHdr = (LPWAVEHDR) lParam;
if(lpHdr->dwBytesRecorded==0 || lpHdr==NULL)
return ;//ERROR_SUCCESS;//使采集过程,知道此buffer已经沾满,不能再填充
::waveInUnprepareHeader(m_hRecord, lpHdr, sizeof(WAVEHDR));//将采集到的声音发送给播放线程
((CVideoPlayDlg *)m_pDlg)->m_pPlaySound->PostThreadMessage(WM_PLAYSOUND_PLAYBLOCK, lpHdr->dwBytesRecorded, (LPARAM)lpHdr->lpData);
// Send recorded audio to remote host…
/*
if(lpHdr->lpData!=NULL )
( (CVideoNetDlg*) dlg )->daudio.SendAudioData((unsigned char *)lpHdr->lpData,lpHdr->dwBytesRecorded);
*/if(m_IsRecoding)
{
//重新将buffer恢复到准备填充状态
::waveInPrepareHeader(m_hRecord, lpHdr, sizeof(WAVEHDR));
::waveInAddBuffer(m_hRecord, lpHdr, sizeof(WAVEHDR));
}
}7、在要停止采集是使用waveInStop停止采集数据。
mmReturn = ::waveInStop(m_hRecord);8、停止采集成功,立即waveInReset重置设备,重置设备将会导致所有的采集buffer反馈给程序。
if(!mmReturn) //停止采集成功,立即重置设备,重置设备将会导致所有的buffer反馈给程序
{
m_IsRecoding = FALSE;
mmReturn = ::waveInReset(m_hRecord); //重置设备
}9、延时一段时间,等待所有的数据buffer都被程序处理完成
Sleep(500); //等待一段时间,使buffer反馈完成10、waveInClose关闭设备
if(!mmReturn) //重置设备成功,立即关闭设备
mmReturn = ::waveInClose(m_hRecord); //关闭设备
二、音频播放
操作步骤:
1、初始化音频数据格式结构体 WAVEFORMATEX
//初始化音频格式结构体
memset(&m_WaveFormatEx, 0, sizeof(m_WaveFormatEx));
m_WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
m_WaveFormatEx.nChannels = 1;
m_WaveFormatEx.wBitsPerSample = 8;
m_WaveFormatEx.cbSize = 0;
m_WaveFormatEx.nSamplesPerSec = 8000;
m_WaveFormatEx.nAvgBytesPerSec = 8000 ;
m_WaveFormatEx.nBlockAlign = 1;2、打开音频输出设备
//打开音频输出设备
mmReturn = ::waveOutOpen( &m_hPlay, WAVE_MAPPER,
&m_WaveFormatEx, ::GetCurrentThreadId(), 0, CALLBACK_THREAD);3、设置音频输出音量
if(mmReturn) //打开设备失败
displayError(mmReturn,"PlayStart");
else
{
m_IsPlaying = TRUE;
DWORD volume = 0xffffffff;
waveOutSetVolume(m_hPlay, volume);//设置输出设备的输出量
}
4、等待要输出的数据,通过waveOutPrepareHeader将数据提交给设备准备输出,通过waveOutWrite将提交给设备的数据输出。
void CPlaySound::OnWriteSoundData(WPARAM wParam, LPARAM lParam)
{
MMRESULT mmResult = 0;
int length=(int) wParam;
if(m_IsPlaying == FALSE)
return ; //FALSE;m_PlayLog.WriteString(TEXT("\nplaying sound data…."));
// Prepare wave header for playing
WAVEHDR *lpHdr=new WAVEHDR;
memset(lpHdr,0,sizeof(WAVEHDR));
lpHdr->lpData=(char *)lParam;
lpHdr->dwBufferLength=length;//将要输出的数据写入buffer
mmResult = ::waveOutPrepareHeader(m_hPlay, lpHdr, sizeof(WAVEHDR));if(mmResult)
{
m_PlayLog.WriteString(TEXT("\nError while preparing header"));
return ;//ERROR_SUCCESS;
}//将输出数据发送给输出设备
mmResult = ::waveOutWrite(m_hPlay, lpHdr, sizeof(WAVEHDR));if(mmResult)
{
m_PlayLog.WriteString(TEXT("\nError while writing to device"));
return ;//ERROR_SUCCESS;
}
return ;//ERROR_SUCCESS;
}5、当提交给设备的数据输出结束,设备会发送一条MM_WOM_DONE消息反馈给设备,设备应该用waveOutUnprepareHeader将提交给设备输出的数据清除。
void CPlaySound::OnEndPlaySoundData(WPARAM wParam, LPARAM lParam)
{
LPWAVEHDR lpHdr = (LPWAVEHDR) lParam;if(lpHdr)
{
::waveOutUnprepareHeader(m_hPlay, lpHdr, sizeof(WAVEHDR));//音频输出结束,清空buffer
}
return ;//ERROR_SUCCESS;
}6、结束输出前先用waveOutReset重置输出设备,重置能够使输出设备全部buffer输出结束,所以在waveOutReset后要延迟一段时间,然后调用waveOutClose关闭设备。
void CPlaySound::OnStopPlaying(WPARAM wParam, LPARAM lParam)
{MMRESULT mmReturn = 0;
if(m_IsPlaying == FALSE)
return;// FALSE;m_PlayLog.WriteString(TEXT("\n Stopped playing"));
mmReturn = ::waveOutReset(m_hPlay);//重置输出设备,重置能够使输出设备全部buffer输出结束
if(!mmReturn)
{
m_IsPlaying = FALSE;
Sleep(500); //等待所有buffer输出完成
mmReturn = ::waveOutClose(m_hPlay);//关闭设备
}
}
Windows 下音频数据采集和播放的更多相关文章
- Windows PCM音频捕获与播放实现
在WINDOWS下,音频函数有多种类型,如MCI.多媒体OLE控制.高级音频等,使用方法都比较简单.但如果想编写一个功能较强大的音频处理程序,那就必须使用低级音频函数和多媒体文件I/O来控制音频设备的 ...
- windows下使用waveout函数族播放wav文件
要使用waveout函数组,族,首先要知道几个数据结构,首先是这个 typedef struct tWAVEFORMATEX { WORD wFormatTag; /* 格式的类型 */ WORD n ...
- < python音频库:Windows下pydub安装配置、过程出现的问题及常用API >
< python音频库:Windows下pydub安装配置.过程出现的问题及常用API > 背景 刚从B站上看过倒放挑战之后也想体验下,心血来潮一个晚上完成了基本的实现.其中倒放与播放部分 ...
- windows下Qt播放flash
Qt 版本:5.1.0 32bit 代码部分,参考1即可.配置发行时,需要将NPSWF32放在项目的plugins\目录中. 否则,可能找不到,导致无法运行. QWebSettings *setti ...
- 一步步实现windows版ijkplayer系列文章之四——windows下编译ijkplyer版ffmpeg
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- java实现windows下amr转换为mp3(可实现微信语音和qq语音转换)
最近做一个项目需要将微信的语音文件放在页面进行播放,查了好多资料发现,web页面直接播放并没有一个好的解决方案,于是就想到了先将amr文件转换成易于在页面播放的mp3文件,然后在进行播放,现在将amr ...
- Windows下Visual studio 2013 编译 Audacity
编译的Audacity版本为2.1.2,由于实在windows下编译,其源代码可以从Github上取得 git clone https://github.com/audacity/audacity. ...
- Live555流媒体服务器编译(Windows下)
最近在回顾之前做过的相关项目,其中包括live555流媒体服务器相关,今天先把live555开源框架在Windows下的编译方法记录一下. live555是一套使用使用开放的标准协议(RTP/RTCP ...
- WINDOWS下如何安装GCC(转载http://nirvana.cublog.cn;作者:北斗星君(黄庠魁))
第一章 在视窗操作系统下的GCC 第一节 GCC家族概览 GCC 是一个原本用于 Unix-like 系统下编程的编译器.不过,现在 GCC 也有了许多 Win32 下的移植版本.所以,也许对于许多 ...
随机推荐
- PE文件结构详解(五)延迟导入表
PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...
- PHP之set_error_handler()函数讲解
定义和用法 set_error_handler() 函数设置用户自定义的错误处理函数. 该函数用于创建运行时期间的用户自己的错误处理方法. 该函数会返回旧的错误处理程序,若失败,则返回 null. 语 ...
- Mac OS X 安装并测试 OpenCV
1. 安装 打开官网的Linux安装OpenCV的网页,打开这个网页的目的不是按照它所提供的步骤安装OpenCV(因为你会遇到一个坑,下文会提到),而是为了安装一些依赖的包或库. 其中的pkg-con ...
- 【转】System.DateTime.Now.ToString()的一些用法
C#中的日期处理函数 //2007年4月24日 this.TextBox6.Text = System.DateTime.Now.ToString("D"); ...
- 如何用 ANTLR 4 实现自己的脚本语言?
ANTLR 是一个 Java 实现的词法/语法分析生成程序,目前最新版本为 4.5.2,支持 Java,C#,JavaScript 等语言,这里我们用 ANTLR 4.5.2 来实现一个自己的脚本语言 ...
- ios学习笔记block回调的应用(一个简单的例子)
一.什么是Blocks Block是一个C级别的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似,但是其运行需要编译器和运行时支持,从ios4.0开始就很好的支持Block. 二. ...
- [转]数据结构之Trie树
1. 概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. Trie一词来自retrieve,发音为/tr ...
- 【mongoDB中级篇②】索引与expain
索引的操作 数据库百分之八十的工作基本上都是查询,而索引能帮我们更快的查询到想要的数据.但是其降低了数据的写入速度,所以要权衡常用的查询字段,不必在太多字段上建立索引. 在mongoDB中默认是用bt ...
- Sina App Engine(SAE)入门教程(7)- Storage使用
参考阅读 sae storage api 文档 Storage 说明文档 Storage 大文件上传说明 storage是什么? 因为sae禁用了代码环境的本地读写,但是在网站运行的过程中,必定会出现 ...
- QC、IQC、IPQC、FQC、OQC、QA分别的定义
QC:即英文(Quality Control)的简称,中文意义是品质控制,其在ISO8402:1994的定义是“为达到品质要求所采取的作业技术的活动”.有些推行ISO9000的组织会设置这样一个部门或 ...