关于语音聊天(wave系列函数播放文件、网络音频)的实现方法

    早在上大学的时候,就想设计一个网络音频播放器,可以点歌,由于技术、懒散等原因,一直没有去实现(这里说的技术原因指的是对音频API不熟悉,使用WM控件或者DShow等高科技不在本文讨论范围内,本文主要讨论wave系列函数来实现音频播放)。

    这几天因为项目需要,有机会全身心去研究wave系列函数,虽然截至目前仍没完全搞清楚其内部原理,不过对于局域网流畅播放音频,基本上没什么问题了。

    对于网络音频播放,缓存是在所难免的,我设计的是缓存3秒数据,循环覆盖(意思是缓冲区是3秒音频数据的大小,从网络收到的数据循环写入缓冲区,比如300K缓存,每次网络接收40K,当缓冲区装了280K的时候,再次接收数据时,就把20K存入到缓冲区的尾部,然后剩下的20K存入缓冲区头部,下次接收从缓冲区20K的地方开始存储)。

    为了音频播放流程,线程从文件读取音频数据或播放音频的节奏很重要,为了简单方便,我是这样设计的:线程从文件每次读取8分之一秒音频大小,读取间隔休眠120毫秒,同样,播放音频时,设计的是8个缓冲区,每个缓冲区的大小也是8分之一秒音频的大小,然后休眠120毫秒,这样听起来基本上感觉还可以。

    最后简单描述一下程序流程:服务端,当接收到客户端连接时,首选从指定的音频文件读取头部信息发给客户端,然后每次读取8分之一秒音频大小的数据发给客户端,每次发送间隔120毫秒。客户端,连上服务器后,首先接收音频头数据,初始化waveout,然后不停接收音频数据存入缓冲区,在放音线程里面,当发现播放的音频块个数小于接收的音频块个数,就从缓冲区复制音频数据来播放,每次播放休眠120毫秒。

    下面是从网上弄的音频文件头部结构体:
#pragma pack( 1 )

typedef struct _WaveFileHeader
{
 char   cChunkID[4];           // R I F F
 int      nChunkSize;            // FileSize - 8 bytes
 char   cFormat[4];              // W A V E
 char   cSubChunk1ID[4];   // F M T
 int      nSubChunk1Size;    // this struct size - 8 bytes
 short  sAudioFormat;         // PCM
 short  sNumChannels;       // numbers of channel
 int      nSampleRate;          // Samples per second
 int      nBytesRate;             // bytes per second
 short  sBlockAlign;             // align of block
 short  sBitsPerSample;      // bits per sample
 char   cSubChunk2ID[4];    // d a t a
 int      nSubChunk2Size;
}WaveFileHeader;

#pragma pack()

这里是演示程序的下载地址:下载

源代码下载地址:下载

 

后记:今天听歌的时候,意外发现放歌有点噪音,反复检查代码,没什么问题,后来才意识到是设计的缺陷,如果音频采样率很特殊,造成每秒的数据率除以8后,不能保持数据对齐(sBlockAlign),就会有噪音,问题已修正。

.

你可能感兴趣的:(技术)