嘉应学院毕业论文(设计)
3.2语音通话模块的流程图
第四章 linux音频编程
4.1 音频编程简介
音频信号是一种连续变化的模拟信号,但计算机只能处理和记录二进制的数字信号,由自然音源得到的音频信号必须经过一定的变换,成为数字音频信号之后,才能送到计算机中作进一步的处理。
数字音频系统通过将声波的波型转换成一系列二进制数据,来实现对原始声音的现,实现这一步骤的设备常被称为模/数转换器(A/D)。A/D转换器以每秒钟上万次的速率对声波进行采样,每个采样点都记录下了原始模拟声波在某一时刻的状态,通常称之为样本sample),而每一秒钟所采样的数目则称为采样频率,通过将一串连续的样本连接起来,就可以在计算机中描述一段声音了。对于采样过程中的每一个样本来说,数字音频系统会分配一定存储位来记录声波的振幅,一般称之为采样分辩率或者采样精度,采样精度越高,声音还原时就会越细腻。
数字音频涉及到的概念非常多,对于在Linux下进行音频编程的程序员来说,最重要的是理解声音数字化的两个关键步骤:采样和量化。采样就是每隔一定时间就读一次声音信号的幅度,而量化则是将采样得到的声音信号幅度转换为数字值,从本质上讲,采样是时间上的数字化,而量化则是幅度上的数字化。下面介绍几个在进行音频编程时经常需要用到的技术指标:
(1).采样频率
采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。采样
12
嘉应学院毕业论文(设计)
频率的选择应该遵循奈奎斯特(Harry Nyquist)采样理论:如果对某一模拟信号进行采样,则采样后可还原的最高信号频率只有采样频率的一半,或者说只要采样频率高于输入信号最高频率的两倍,就能从采样信号系列重构原始信号。正常人听觉的频率范围大约在20Hz~20kHz之间,根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采样频率,还可以达到DVD的音质。
(2).采样的位数
采样位数可以理解为采 集卡处理声音的解析度。这个数值越大,解析度就越高,录制和回放的声音就越真实。我们首先要知道:电脑中的声音文件是用数字0和1来 表示的。所以在电脑上录音的本质就是把模拟声音信号转换成数字信号。反之,在播放时则是把数字信号还原成模拟声音信号输出。采集卡的位是指采集卡在采集和 播放声音文件时所使用数字声音信号的二进制位数。采集卡的位客观地反映了数字声音信号对输入声音信号描述的准确程度。8位代表2的8次方--256,16 位则代表2的16次方--64K。比较一下,一段相同的音乐信息,16位声卡能把它分为64K个精度单位进行处理,而8位声卡只能处理256个精度单位, 造成了较大的信号损失,最终的采样效果自然是无法相提并论的。
常用的采样位数有8位、12位和16位。采样位数越高,信号的动态范围越大,数字化后的音频信号就越可能接近原始信号,但所需要的存贮空间也越大。
(3).声道数
声道数是反映音频数字化质量的另一个重要因素,它有单声道和双声道之分。双声道又称为立体声,在硬件中有两条线路,音质和音色都要优于单声道,但数字化后占据的存储空间的大小要比单声道多一倍。
出于对安全性方面的考虑,Linux下的应用程序无法直接对声卡这类硬件设备进行操作,而是必须通过内核提供的驱动程序才能完成。在Linux上进行音频编程的本质就是要借助于驱动程序,来完成对声卡的各种操作。
目前Linux 有三个主流的声卡驱动程序集:OSS/Lite(也称为OSS/Free)、OSS/Full(商业软件)、ALSA(自由软件)。
OSS/Lite 是现在linux内核中自带的声卡驱动程序集,最初由 Hannu Savolainen 开发。后来 Hannu 跑去开发 Open Sound System(也就是上面所说的OSS/Full)。由于 Hannu 的“逃跑”,RH 资助 Alan Cox 增强 Hannu 开发的驱动程序,并使它们完全模块化。现在 Alan Cox 是内核声卡驱程集的维护人。OSS/Lite 从kernel-2.0开始并入内核,
13
嘉应学院毕业论文(设计)
现在大家使用的声卡驱程默认都是OSS/Lite中的。
OSS/Full 是由 4Front Technologies 开发并销售的商业软件。它可以驱动很多声卡并且可以用在很多 UNIX 系统中。OSS/Full 完全兼容以前基于 OSS/Lite 开发的应用程序。作为一个商业软件,你虽然可以使用它,但是你得不到它的源代码,并且你必须为此而付钱。
ALSA 标准是一个先进的linux声音体系。它包含内核驱动集合,API库和工具对Linux声音进行支持。ALSA 包含一系列内核驱动对不同的声卡进行支持,还提供了libasound的API库。用这些进行写程序不需要打开设备等操作,所以编程人员在写程序的时候不会被底层的东西困扰。与此相反OSS/Free 驱动在内核层次调用,需要指定设备名和调用ioctl。为提供向后兼容, ALSA 提供内核模块模仿 OSS/Free 驱动,所以大多数的程序不需要改动。 ALSA 拥有调用插件的能力对新设备提供扩展,包括那些用软件模拟出来的虚拟设备。 ALSA 还提供一组命令行工具包括 mixer, sound file player 和工具控制一些特别的声卡的特别的作用。
4.2 ALSA音频编程
4.2.1 ALSA库的安装
在linux终端下执行:
$ sudo apt-get install libasound2-dev 安装完毕!
4.2.2 音频数据的采集和播放的实现
一个典型的音频程序应该具有以下结构: (1)打开音频设备
(2)为设备设置参数(主要参数有三个:采样率、采样位数、通道数) (3)向音频设备读/写音频数据 (4)关闭设备
Alsa库为我们实现这些操作提供了丰富的接口,我们利用Alsa接口把语音的采集和播放封装成一个类class CWavPlayer,下面是class CWavPlayer的部分代码:
class CWavPlayer {
14
嘉应学院毕业论文(设计)
public:
CWavPlayer(uint32_t sample_rate = 8000, uint16_t bit_per_sample = 16, uint16_t channels = 2); //构造函数 CWavPlayer(CWavPlayer &other); ~CWavPlayer();
public:
void wav_play(int fd);
//音频播放
void read_wav_header(int fd);
void write_wav_header(int fd, int dtime = 20);
uint32_t snd_read_pcm(uint32_t rcount, char *wave_buf); void open_pcm_devices(snd_pcm_stream_t mode =
SND_PCM_STREAM_PLAYBACK); //打开音频设备
void wav_record(uint16_t dtimes, int fd); //采集音频数据 void prepare_wav_params(const int dtime); void print_wav() const; void set_pcm_params();
//print the params of wav struct
//设置设备参数
void alsa_record(char *sendBuffer); void set_pcm_mixer();
//设置混音器
CWavPlayer &operator=(CWavPlayer &other);
public:
private:
uint32_t sample_rate;
//采样率 //采样位数 //声道
uint32_t get_chunk_byte() const { return chunk_byte;} snd_pcm_t *get_handle() const { return handle;}
snd_pcm_uframes_t get_period_size() const { return period_size;}
uint16_t bit_per_sample; uint16_t channels; uint32_t chunk_byte;
//周期长度(bytes)
15
嘉应学院毕业论文(设计)
};
uint32_t bit_per_frame; snd_pcm_t *handle;
//帧大小 //设备句柄 //周期
//the point of struct
snd_pcm_uframes_t period_size; WAVHeader_t *wav;
下面介绍一下系统中语音的采集部分的实现。 1.首先让我们封装一个打开音频设备的函数:
void CWavPlayer::open_pcm_devices(snd_pcm_stream_t mode) {
int rc;
if ((rc = snd_pcm_open(&handle, \ { } }
snd_pcm_open是Alsa库提供的打开设备调用函数,这里我们指定打开缺省的音频设备,并根据参数mode将设备置为录音或是播放状态,如果参数mode是SND_PCM_STREAM_ PLAYBACK,则为打开播放设备;如果参数mode是SND_PCM_STREAM_CAPTURE,则为打开录音设备。如果设备打开成功,pcm_handle便指向该设备句柄。
2.第二步是设置参数,参数设置不当将会导致音频设备无法正常工作。在设置参数前,我们需要了解一下各个参数的含义以及一些基本概念。
(1)样本长度(sample):样本是记录音频数据最基本的单位,常见的有8位和16位。 (2)通道数(channel):该参数为1表示单声道,2则是立体声。
(3)桢(frame):桢记录了一个声音单元,其长度为样本长度与通道数的乘积。 (4)采样率(rate):每秒钟采样次数,该次数是针对桢而言。
(5)周期(period):音频设备一次处理所需要的桢数,对于音频设备的数据访问以及音频数据的存储,都是以此为单位。
交错模式(interleaved):是一种音频数据的记录方式,在交错模式下,数据以连续
std::cerr << \exit(1);
16