实现网络音频聊天
编程爱好者
在前几期的文章中,我们已经实现了实时的多人文字聊天和文件传输功能,可以说QQ的基本功能我们都已经实现了。如果文字聊天你觉得有点泛味,还可以使用QQ的一个比较高级的功能——音频聊天。本期我们就来编程实现音频聊天,说不定可以帮你省下不少的电话费哟。
本篇是一个实现局域网音频聊天的程序。要实现音频聊天功能,主要是用WaveIn对象进行音频采集和用WaveOut对象进行音频输出。
如何做音频采集
要使用WaveIn和WaveOut进行音频采集和音频输出,程序必须包含“mmsystem.h”头文件,还要加入多媒体库“winmm.lib”。
音频采集的工作是由WaveIn完成的。以下是用WaveIn实现音频采集的流程(如图1)。

下面是程序的实现代码:
#define BUFF
ER_SIZE 16384
HWAVEIN m
_hWaveIn;//录音设备句柄
PBYTE m_pBu
ffer1, m_pBuffer2;//音频数据缓存
PWAVEHDR m_pWaveHdr1, m_
pWaveHdr2; //音频数据头
WAVEFORMA
TEX m_WaveForMa
tex;//音频格式
//为音频数据头分配内存
m_pWaveHdr1 = new WAVEHDR;
m_pWaveHdr2 = new WAVEHDR;
//为音频数据缓存分配内存
m_pBuffer1 = new BYTE[BUFFER_SI
ZE];
m_pBuffer2 = new BYTE[BUFFER_SI
ZE];
//填写音频格式
m_WaveForMatex.wFormatTag = WAV
E_FORMAT_PCM;//数据格式,WAVE_FO
RMAT_PCM表示脉冲编码调制
m_WaveForMatex.nChannels = 1;//声道
m_WaveForMatex.nSamplesPerSec = 11025;//采样频率
m_WaveForMatex.nAvgBytesPerSec = 11025;//码率
m_WaveForMatex.nBlockAlign = 1;
m_WaveForMatex.wBitsPerSample = 8;//样本大小
m_WaveForMatex.cbSize = 0;
//打开录音设备
if( waveInOpen(
&m_hWaveIn,//录音设备句柄
WAVE_MAPPER,//录音设备ID
&m_WaveForMatex,//音频格式结构的指针
(DWORD)this->m_hWnd,//窗口句柄,表示用该窗口接收录音设备的消息
NULL,
CALLBACK_WINDOW) )//处理消息方式的符号位
{ delete[] m_pBuffer1;
delete[] m_pBuffer2;
AfxMessageBox( "无法打开录音设备" );}
m_pWaveHdr1->lpData = (LPTSTR)m_pBuffer1;//音频数据的内存指针
m_pWaveHdr1->dwBufferLength = BUFFER_SIZE;//音频数据的长度
m_pWaveHdr1->dwBytesRecorded = 0;
m_pWaveHdr1->dwUser = 0;
m_pWaveHdr1->dwFlags = 0;
m_pWaveHdr1->dwLoops = 1;//循环次数
m_pWaveHdr1->lpNext = NULL;
m_pWaveHdr1->reserved = 0;
//为录音设备准备数据头
waveInPrepareHeader( m_hWaveIn, m_pWaveHdr1, sizeof( WAVEHDR ) );
m_pWaveHdr2->lpData = (LPTSTR)m_pBuffer2;
m_pWaveHdr2->dwBufferLength = BUFFER_SIZE;
m_pWaveHdr2->dwBytesRecorded = 0;
m_pWaveHdr2->dwUser = 0;
m_pWaveHdr2->dwFlags = 0;
m_pWaveHdr2->dwLoops = 1;
m_pWaveHdr2->lpNext = NULL;
m_pWaveHdr2->reserved = 0;
waveInPrepareHeader( m_hWaveIn, m_pWaveHdr2, sizeof( WAVEHDR ) );
//为录音设备加入数据头
waveInAddBuffer( m_hWaveIn, m_pWaveHdr1, sizeof( WAVEHDR ) );
waveInAddBuffer( m_hWaveIn, m_pWaveHdr2, sizeof( WAVEHDR ) );
waveInStart( m_hWaveIn );//开始录音
程序分析:上面打开录音设备的函数中,有一个参数是窗口句柄。指定一个窗口句柄后,以后该录音设备的消息将由该指定的窗口所接收。
WaveIn处理的方式也是以消息的方式进行处理的。如用waveInOpen()函数打开一个录音设备后,将会发出MM_WIM_OPEN消息。
WaveIn还有以下相关函数:
● 停止录音
MMRESULT waveInReset( HWAVEIN hwi );
● 关闭录音设备
MMRESULT waveInClose( HWAVEIN hwi );
如何进行音频输出
音频输出的工作由WaveOut对象完成,以下是WaveOut实现输出音频的流程(如图2)。

下面是程序的实现代码:
HWAVEO
UT m_hWaveO
ut;
if(waveOut
Open(&m_hWa
veOut,WAVE_
MAPPER,&m_
WaveForMatex,(DWORD)this
->m_hWnd,NU
LL,CALLBACK
_WINDOW))
{ delete[] m_pBufferOut;
AfxMessag
eBox( "无法打开音频输出设备" );}
m_pWave
HdrOut->lpDat
a = (LPTSTR)m_pBufferOut;
m_pWaveHdrOut->dwBufferLength = BUFFER_SIZE;
m_pWaveHdrOut->dwBytesRecorded = 0;
m_pWaveHdrOut->dwUser = 0;
m_pWaveHdrOut->dwFlags = 0;
m_pWaveHdrOut->dwLoops = 1;
m_pWaveHdrOut->lpNext = NULL;
m_pWaveHdrOut->reserved = 0;
waveOutPrepareHeader( m_hWaveOut, m_pWaveHdrOut, sizeof( WAVEHDR ) );
//播放音频
waveOutWrite( m_hWaveOut, m_pWaveHdrOut, sizeof( WAVEHDR ) );
下面是WaveOut常用的消息:
● MM_WOM_OPEN——打开设备
● MM_WOM_DONE——播放完毕
● MM_WIM_CLOSE——关闭录音设备
关闭音频输出设备:
MMRESULT waveOutClose( HWAVEOUT hwo );
实现网络即时音频聊天
我们已经知道如何使用WaveIn和WaveOut来进行音频采集和音频播放了。现在要实现音频聊天,无非是把音频数据缓存后收发。关于在网络上收发数据的操作前面两期已讲得很多了,把例程代码稍加修改即可。
值得注意的是,如果要在局域网里实现音频聊天,由于网络传输比较快,所以可以考虑使用TCP/IP编程。而要在Internet里实现的话,考虑到语音传输的连续性,一般都采用UDP/IP编程。
编后:到此,我们的网络编程一点通系列文章就全部刊载完毕了。我们通过编程,实现了国内主流聊天软件QQ的文字聊天、文件传输和语音聊天功能,同时也教会了大家如何进行TCP/IP和UDP/IP编程。相信大家如果能在此基础上进一步学习,必定能够在网络编程方面有一番造诣。