3.1 任务需求分析
在本小节主要利用openal实现对音频文件的播放。在功能上需要实现可以利用ply_music
按钮播放本地音频文件。界面上我们沿用2.1小节中所建立的工程,在-(IBAction) ply_music:
(id)sender中注释掉利用AudioToolbox.framework框架以及利用AVFoundation.framework播放音频文件的方法,将相应的open al代码添加进去。除此之外还需要实现可以播放本地pcm音频文件,以及了解3D音频特效的实现方法。
本小节的主要目的在于:
(1)了解open al的跨平台音频处理库。
(2)掌握ios环境下利用open al播放音频文件的方法。
(3)掌握利用open al直接播发本地pcm音频类型文件的原理和方法。
(4)了解open al在3D音效技术处理上的应用。
3.2 OPEN AL概述
在正式开始学习这一章节之前我们首先要了解什么是open al技术?以及open al技术的要点?Open al主要针对三个对象:分别是声源(source)、缓冲器(buffer)、listener(听众)。OPEN AL是一款跨平台的音效处理库。它的最大特点是根据source(声音源)以及listener(听者)之间的相对位置(三维笛卡儿坐标系下)来实现3D的音频特效。OPEN AL在风格上与OPEN GL非常类似。具有OPEN GL基础的同志们可以很快速的掌握这个音效处理库的使用。
OPENAL支持多种音效平台,如Mac OS X、iOS、GNU/Linux、Windows PC等等。因此很方便的应用在苹果设备中。
图1是OPEN AL的结构框架:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM0YTMwUjMwETMyATM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
从层次上来分,OPENAL主要包括音频播放部分以及数据缓冲部分。要想可以正常的播放音频信号,open al中需要包括一个device(音频设备)、最少一个Context、一个或者多个声源source,还要有一个或者多个缓冲buffer。对于每个context来说只能对应一个listener。其中
Device表示用于播放音频的具体音频设备。Context表示用于播放音频的上下文环境。Source代表用于存放原始音频信号的数据源。Buffer用于在播放过程中充当source数据缓存。
下面通过最简单的步骤构造一个基于open al的音频播放器,帮组大家快速的掌握利用open al播放音频文件的方法。具体来说构件播放器的步骤分为open al初始化部分以及Open al音频播放部分。
open al初始化部分:
1、打开音频设备(device),并获取设备的id号。
2、创建该音频设备的播放环境,并对其进行匹配。
3、创建音频数据缓存空间(buffer),并获取数据缓存空间的id号。
4、创建声源数据原始空间(source),并获取声源空间id号。
Open al音频播放部分:
1、打开所需要播放的音频文件,并获取音频文件的音频格式(如音频编解码方式、量化位数等)
、音频文件大小、采样频率。
2、根据音频文件的相应参数将刚刚打开的音频文件数据源装载到buffer缓存中。
3、设置音源(source)以及听众(linstner)的具体位置。二者之间的相对位置不同,听到的声音效果也不同,例如有时你会听到声音在你的后面,有时你会听到声音在你的前面。
4、将声源(source)与缓存(buffer)关联起来。
5、利用open al api播放音频文件。
在了解了open al初始化和音频播放流程之后,我们已经对open al的使用过程有了一个清晰的认识,但在在开始分析源代码之前还需要了解以下open al常用到的一些数据结构。
ALuint source;
ALuint buffer;
ALCcontext* context;
ALCdevice* device;
Source存放原始数据的内容,buffer存放数据缓存内容。Context描述了上下文信息,device获取设备的id号。
通过alGenBuffers函数(c语言中)可以调用open al中用到的相关参数
AL_FREQUENCY:表示缓冲器的频率参数,单位是赫兹。
AL_BITS:表示缓冲器的量化位数。
AL_CHANIVELS:表示缓冲器的音频通道个数,可以为1或者2。
AL_SIZE:表示缓冲器的大小。单位是字节。
AL_DATA:表示缓冲器的数据地址。描述了数据源的起始位置。
设置alGenSource可以调用很多声源参数:
AL_POSITION:设置声源的X、Y、Z三个方向上的坐标。
AL_MIN_GAIN:设置声源的最小增益。
AL_VELOCITY:设置声源的速度失量。
……
当然还有很多其他的速度失量信息,这里由于篇幅关系就不一一介绍。
Open al虽然是第三方提供的音频处理库,但在ios中已经通过openal framework添加进来。当然在使用open al中相应函数的时候需要将open al的相应文件添加到工程中。
3.3源程序分析
首先先来看一段利用openal播放本地音频文件的源程序:
1 device = alcOpenDevice(NULL);
2 context = alcCreateContext(device, 0);
3 alcMakeContextCurrent(context);
4 alGenBuffers(1, &buffer);
5 alGenSources(1, &source);
6 ALenum source_format;
7 ALsizei source_size;
8 ALsizei source_freq;
9 NSString *ply_music = [[NSBundlemainBundle] pathForResource:@"music"
ofType:@"wav"];
10 NSURL *musicURL = [NSURLfileURLWithPath:ply_music];
11 CFURLRef music_URL = (__bridgeCFURLRef)musicURL;
12 source_data =MyGetOpenALAudioData(music_URL, &source_size,&source_format, &source_freq);
13 alBufferDataStaticProc(buffer, source_format,source_data, source_size, source_freq);
14 alSourcei(source,AL_BUFFER, buffer);
// alSourcefv 根据所要产生的音频3D效果设置声源位置。。。。。
15 alSourcePlay(source);
该段源代码位于ViewController.m文件ply_music方法中。在原工程的基础上注释掉利用AVFoundation.framework框架播放音频文件的代码。将此段代码添加进来。就可以实现对本地音频文件的播放。
程序第一行通过alcOpenDevice打开所需要播放的音频文件设备。如果打开正确则返回音频设备的id号device。第二行建立播放音频文件的上下文运行环境。将音频设备的id号作为参数输入。第三行匹配音频设备播放音频文件的上下文环境。运行此语句会有以下参数输出:
AudioStreamBasicDescription: 2 ch, 44100 Hz, 'lpcm' (0x00000029) 32-bit little-endianfloat, deinterleaved其中2表示音频文件分为左右两个通道,44100 Hz 为音频文件的采样速率。'lpcm'为音频文件数据的编解码方式,32-bit为音频文件量化位数。little-endian表明数据的存储方式是小端模式,float为浮点类型数据。第四行用于产生buffer的缓冲区域。其中1表示缓冲区域的标示符。标示符可以是任意的数字,但不同的buffer缓冲区域不能具有相同的标示符。Buffer为数据缓冲区域的地址。第五行用于建立source数据源存储区域。其中1表示数据源标示符,其命名规则与buffer缓冲区域一样。Source用于存储声源数据。第六行到第八行定义音频文件特征参数。第九行到第十一行定义包含音频文件路径、文件名称、文件类型在内的CFURLRef数据类型。第十二行通过music_URL文件路径获取音频文件参数,如声源文件大小、格式等信息。第十三行将一定大小、并具有特定格式的音频文件声源数据装载到buffer缓存中。第十四行建立source声源与buffer缓存之间的联系。第十五行利用open al播放音频文件。在代码的第十四行和第十五行之间是一段注释掉的声源位置和听者位置的设置,通过设置这两者之间的相对位置以及相对速度既可以实现类似游戏效果中的3D音频特效。
由于整个工程沿用的前一小节音频播放器的设计方法,因此在界面设计、规划变量并连接输入输出口、以及工程的设计上都是一致的。不同点在于,如果正常利用open al播放音频还需要将open al.framework添加进去。在测试open al时,点击ply_music可以听到一段优美的音乐。
3.4 利用open al处理pcm音频文件。
在处理pcm文件之前,我们首先需要来分析一下,wav中存放的音频文件都有哪些非常重要的参数,以及这些参数我们如何利用open al来设置。我们通过matlab仿真工具,读取一个测试用wav音频文件:
[x,fs,nbits,c]=wavread('music.wav')
其中x存储的是wav音频文件的具体数据。fs为音频文件的采样频率,nbits存放音频文件的量化位数。以上三个参数尤为重要。在这里x的时域波形图如图1所示:可以看到图像好像有绿色和蓝色两种颜色。我们进一步将图像放大处理可以看到:这里其实是两种波形叠加在一起的效果。
通过分析x的数据结构:size(x)
ans =
882688 2
x是一个二维矩阵。每一个维度对应着一个音频的通道,同时在波形上体现在如图所示的结果。
接下来我们来分析经过解码之后的音频数据,我们选择前10个点进行分析:
ans =
0.0321 0.0334 0.0352 0.0367 0.0388 0.0422 0.0457 0.0486 0.0507 0.0505
matlab对音频信号处理是采用归一化的数据类型。为了可以进一步探究open al是如何利用这些数据进行播放的,我们需要将数据还原为最原始的数据类型。刚才我们分析了wav中比较重要的音频文件参数。X、fs、nbits。其中nbits为音频信号的量化位数。因此我们可以对数据x进一步分析:x*32767。
ans =
1.0e+03 *
1.0530 1.0950 1.1540 1.2020 1.2730 1.3840 1.4970 1.5930 1.6599 1.6559
这就是我们实际中通过音频设备采集到的数字音频信号。根据实际中的量化位数,乘以不同量化因子。
我们再回到open al播放音频信号的程序中。
source_data = MyGetOpenALAudioData(music_URL, &source_size,&source_format, &source_freq);通过上一小节的分析我们可以知道,open al读取进来的数据是存放在source_data所标示的地址中的。我们接下来要分析这里面存储的数据是不是我们所期待的。
我们可以编写一小段测试代码,并通过xcode的debug调试命令可以的到以下结果。
int i;
short t;
UInt32 *source_data1=source_data;
for (i=0;i<10;i++) {
t=*(source_data1+i);
}
下面利用刚才我们新建的工程播放本地pcm音频文件。首先利用matlab对wav文件进行解码。将解码之后的pcm文件保存下来。注意利用matlab解码之后的pcm文件是被归一化在-1到+1之间的数据,要根据wav头文件信息中的参数,即nbits量化位数装化为相应的数据。例如在此工程中使用的是16bit量化。因此将matlab解码之后的pcm数据乘以32767。转化为short数据类型。至于文件扩展名,可以定义为.h文件,也可以定义为.txt文件。在本小节中使用的是short_pqfile.h来保存数据文件。注意利用matlab对文件保存,也具有一定的格式。下面截取了一段pcm音频数据。可见这段音频数据按照n*1的数据结构来排列,数据类型为浮点型科学计数法。只所以设计成这种形式时为了方便我们在后续的文件操作中数据读取方便。
1.0240000e+04
1.1177000e+04
1.1819000e+04
1.2099000e+04
1.1969000e+04 … …
以下是具体实现的源代码。在使用的时候将此段代码放置在:
source_data = MyGetOpenALAudioData(music_URL, &source_size,&source_format, &source_freq);
….与alBufferDataStaticProc(buffer, source_format,source_data, source_size, source_freq)代码之间。
//pcm播放测试代码
1 const char *filename1="/Users/zhengwei/Desktop/audio_play/audio_play_open_pcm/short_pqfile.h";
2 FILE *f1=fopen(filename1,"r");
3 int i=0;short t;float ui;
4 UInt32 *source_data1=source_data;
5 while(!feof(f1))
{
6 fscanf(f1,"%f",&ui);
7 t=(short)ui;
8 *(source_data1+i++)=t;
}
9 fclose(f1);
10 source_size=i*4;
程序第一行定义文件全名所在的本地路径。注意这个short_pqfile.h文件在我的电脑中是这个路径,但是在你的电脑中可能就不是这个路径,因此在使用的时候需要将文件存放的路径改成目前你正在使用的工程路径。第二行根据文件路径利用文本打开函数fopen打开文本。第三行定义pcm音频播放程序所需要的一些参数。i定义文本文件中pcm文件中数据的个数,ui读取文本文件中的数据,t将读取的浮点类型数据转化为short短整型数据。第四行备份原始wav音频数据文件的存储地址。第五行判断文件中的内容是否读取完毕。第六行从文件f1中将数据保存在ui地址中。第七到第八行将从文本文件中读取的pcm数据转化为short类型,然后存在source_data1数据源地址中。并且i同时负责地址的步进以及pcm数据个数的计算。第九行关闭文本文件。第十行中i数据需要乘以4.因为这里采用的openal播放参数为双通道,16bit。而source_size代表的是字节的个数。因此这里需要乘以4。这里需要注意的是,我采用的pcm文件与原工程中所播放的wav音频文件的音频参数是基本相同的。唯一不通的是数据的大小,因此我在播放本地音频文件的时候仅仅修改了音频数据大小source_size参数,对其他参数如音频通道、采样频率都沿用原工程中的参数。如果读者想播放自己所下载的pcm文件的时候,只休要将相应参数修改为你所想要播放的文件参数即可。
再次点击ply_music按钮就可以听到一段优美的音乐。同时利用matlab中sound函数也可以直接播放音频文件。二者的声音效果听起来应当是一致的。注意在使用sound函数时,需要将sound(x,fs)中的fs设置为wav音频文件格式中相同的采样频率。
3.5 open al在3D音效中的使用
三维音效实际上是利用声源与听众在空间三维坐标中的位置、状态而形成的一种特殊的声音效果。比如有人在你前面对着你说话和在你后面背着你说话,或者另一人站着不动和你说话和在一个高速运行的轿车中和你说话,效果是明显不同的。而open al所提供的API就提供了一组丰富的声源、听众位置参数设置API以及速度方向控制API,可以很方便的实现音频的3D效果。
下面就一些常用的open al的API做简单的介绍:
ALfloat SourcePos[] = { 0.0, 0.0, 0.0 }; 主要用于设置声源位置
ALfloat SourceVel[] = { 0.0, 0.0, 0.0 }; 主要用于设置声源速度
ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 }; 主要用于设置听者的位置
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 }; 主要用于设置听者的速度
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 }; 主要用于设置听者的运动方向。… …
可见实现一个3d的音频效果我们主要关注以下几个参数:声源的位置,听着的位置。声源的速度,听着的速度。主义这里都是采用三维坐标来描述的。通过声源、听众、位置、速度的几种组合方式,可以模拟出在不同方向,不同距离上的三位立体声音效果。而速度信息,以及听着的方向,主要用来实现在相对运动的过程中实现的多普勒频移等一些特殊的音效处理。