JUCE类库音频播放详解2-2

六大基本类

要播放音频,至少需要用到6个音频类和1个线程类,这些类环环相扣,缺一不可,共同构成了播放音频文件的完整流程。先详解6个音频类:

1、AudioFormatManager(音频格式管理器):JUCE基类,无父无子,主要作用有二:注册或获取本程序所能使用的各类音频格式,将具体的音频文件或音频数据流转换为AudioFormatReader类的指针。这两个作用构成了整个音频播放链中的第一个环节,具体流程有三,3行语句即可:

// 内容组件类声明本类的栈对象

AudioFormatManager formatManager;

// 内容组件类的构造函数中,本类对象注册所有能识别的音频格式

formatManager.registerBasicFormats();

// 内容组件类的功能性函数中,本类对象调用核心函数createReaderFor(),将具体的音频文件或音频数据转换为AudioFormatReader类的指针。本例audioFile是具体的音频文件(引用型对象)

AudioFormatReader* reader = formatManager.createReaderFor (audioFile);

除使用本类对象注册音频格式并创建AudioFormatReader对象之外,本类的常用功能还有:获取当前已经注册的音频格式的数量(返回类型为int),获取已经注册的某个音频格式(返回类型为AudioFormat*),返回已注册的音频格式的扩展名(返回类型为String)。

2、AudioFormatReader(音频格式读取器):JUCE抽象基类,无父类,本类更准确的命名应该是“音频数据读取器”,其所读取的音频数据的格式为音频格式管理器中已经注册的所有音频格式。主要功能:从音频文件或数据流中读取采样,所读取的采样(即本类对象)作为构造参数来初始化AudioSource派生类的对象。本类有两个派生类:AudioCDReader,用来读取音频CD;AudioSubsectionReader,用于读取音频文件中的一段数据。

如果是单纯的播放音频文件或音频数据,则内容组件类无需声明本类对象,在功能性函数中临时声明一个指针即可。声明的同时进行定义,即将音频格式管理器对象依据具体的文件或数据流而创建的本类的指针值赋值给它,以使本类能够正确的读取该文件或数据流。
// 内容组件类的功能性函数中声明并定义本类对象,直接获取函数的返回值。

// audioFile为具体的音频文件。

AudioFormatReader* reader = formatManager.createReaderFor (audioFile);

赋值后,本类对象供AudioFormatReaderSource使用(见下一个环节)。经过赋值的本类对象可返回所关联的音频文件的格式描述(String),从音频流中读取采样(bool),填充一个AudioSampleBuffer的其中一段(void),查找音频流中的最大采样值(void),查找指定范围的采样(int64),获取数据流的音频采样率(double),每个采样的字节数(int),音频流的采样总数(int64),判断音频文件是否立体声(音频流的通道数,int),音频数据是否浮点格式(bool),读取音频数据流中的元数据(StringPairArray),等等。以上这些也是本类的主要功能与作用。

3、AudioFormatReaderSource(音频格式读取器来源):用于生成连续的音频流。AudioFormatReader对象用来初始化本类对象,而本类对象则作为要播放的音频来源数据供AudioTransportSource类的setSource()函数所用。可用本类对象设置是否循环播放。具体流程有三,3行语句即可(设置循环语句可选):

// 内容组件类中声明本类对象,作用域指针类型

ScopedPointer<AudioFormatReaderSource> audioFileSource;

// 内容组件类的功能性函数中实例化,用AudioFormatReader类的指针初始化之。2参为本类对象不存在时,是否一并销毁audioFormatReader对象

audioFileSource = new AudioFormatReaderSource (reader, true);

// 设置是否循环播放(可选)

audioFileSource->setLooping (true);

// 内容组件类的功能性函数中,紧接上一行语句,AudioTransportSource类的对象设置音频来源

transportSource.setSource (audioFileSource, 32768, &thread,
reader->sampleRate);

4、AudioTransportSource(音频走带来源):本类双重继承自PositionableAudioSource(见上一个环节)和ChangeBroadcaster类,用于控制其他音频来源。可使用本类对象配合AudioSourcePlayer和AudioIODevice来控制某个音频文件或音频数据流的播放。内容组件类中需声明本类的栈对象,而后在功能性函数中,本类对象将所需的数据设置为AudioFormatReaderSource对象,这些数据供AudioSourcePlayer使用。同时设置预读取缓冲采样数,所需的时间片线程对象、采样率、所需的通道数。具体的函数为setSource(),这是本类的核心函数,其原型为:

void setSource (PositionableAudioSource * newSource,
int readAheadBufferSize = 0,
TimeSliceThread *readAheadThread = nullptr,
double sourceSampleRateToCorrectFor = 0.0,
int maxNumChannels = 2)

本类的主要功能:开始发送某个音频文件的数据(播放),停止发送(停止播放),设置或获取当前的播放位置,音量增益衰减,是否正在播放,是否处于循环模式,等等。

5、AudioSourcePlayer(音频来源播放器):本类作用有二,一是关联AudioTransportSource的音频数据,二是将所关联的音频数据连续不断的发送给AudioDeviceManager对象。本类可实现总的音量增减,因为所有的音频数据最后都交给本类来播放。内容组件类的构造函数中,本类对象的数据来源设置为AudioTransportSource类的对象。同时,AudioDeviceManager对象不断回调本对象,最终实现音频播放功能。具体流程有三:

// 内容组件类声明对象,栈对象

AudioSourcePlayer audioSourcePlayer;

// 内容组件类的构造函数中,本类对象设置所关联的音频来源

audioSourcePlayer.setSource (&transportSource);

// 紧接着上一行语句,AudioDeviceManager对象回调本类对象

deviceManager.addAudioCallback (&audioSourcePlayer);

AudioSourcePlayer继承自AudioIODeviceCallback,可将音频进出数据传递给
AudioIODevice,而AudioIODevice对象则保存于下一个环节的AudioDeviceManager中。当AudioIODevice需要发送或接收下一个数据块时,使用高优先级的后台线程反复调用AudioIODeviceCallback的audioDeviceIOCallback()函数。因此,AudioDeviceManager对象调用addAudioCallback()函数添加AudioSourcePlayer对象的回调后,即可实现音频数据连续不断的播放和接收。

AudioIODeviceCallback类最重要的纯虚函数是audioDeviceIOCallback()。

该类有两个派生类:

– AudioProcessorPlayer:播放AudioProcessor的音频数据。通过核心函数setProcessor()设置所关联的AudioProcessor,而AudioDeviceManager对象通过addAudioCallback()将本类对象添加为回调,即可实现播放AudioProcessor的数据。本类双重继承自AudioIODeviceCallback类和MidiInputCallback类,因此,除了可以将所关联的音频数据传递给音频设备(音频驱动和硬件)之外,还可以处理发送给它MIDI数据,所需的函数为handleIncomingMidiMessage()。

– AudioSourcePlayer:播放音频文件或音频数据。核心函数为:setSource (AudioSource*)。可在此进行音量的总控制。所设置的音频文件或音频数据的播放原理见上。

6、AudioDeviceManager(音频设备管理器):本类继承自ChangeBroadcaster可变生成器类,应作为程序的全局性对象,当设备变化时,本类自动进行回调,以适应新的设置。本类是整个音频处理的最后一个环节,代表当前所设置的音频驱动(音频硬件),将所添加的回调对象持续不断的发送给音频驱动,以发出声音。它本身可以设置并获取所使用的音频驱动及进出端口。通常,将本类对象声明为栈对象。其他要使用本类的类,需声明该对象的引用型对象,通过构造参数传入。要设置音频设备,可通过AudioDeviceSelectorComponent对象配合模态对话框来完成,见上一小节。

内容组件的构造函数中,本类对象用addAudioCallback()函数回调AudioIODeviceCallback类的堆对象(指针),即本例中的AudioSourcePlayer,将收到的数据持续不断的传送给当前设置的音频驱动(硬件),最终实现播放声音。注意:类的析构函数中,本类对象需调用removeAudioCallback()函数移除回调。本环节的流程:

// 内容组件类中声明对象

AudioDeviceManager deviceManager;

// 内容组件类的构造函数中初始化音频设备,添加回调

deviceManager.initialise (0, 255, savedAudioState, true);

deviceManager.addAudioCallback (&audioSourcePlayer);

// 内容组件类的析构函数中移除回调

deviceManager.removeAudioCallback (&audioSourcePlayer);

AudioDeviceManager类还可添加或移除MIDI回调,设置或获取默认的MIDI输出设备,获取或创建JUCE所支持的所有音频驱动,播放测试音频,开启并获取当前的输入电平,获取当前CPU的占用率,将当前设置转换为XmlElement对象以供存储或加载,返回同步访问音频或MIDI回调所使用的锁对象(CriticalSection),等等。

除了这6大音频类对象之外,还有一个TimeSliceThread(时间片线程)对象不能缺少,内容组件类中声明该类的栈对象,构造函数中启动并设置线程优先级。类的功能性函数中,AudioTransportSource设置来源时,要用时间片线程对象作为参数。如需播放磁盘中的音频文件,内容组件类的功能性函数中还需用到显式或隐式的File文件对象,该文件被AudioFormatManager转换为AudioFormatReader对象,而AudioFormatReader对象作为构造参数来实例化AudioFormatReaderSource对象,供AudioTransportSource设置来源所用。

理清了这六大基本类,以及最简单的连接关系之后,具体编程时就可以天马行空,为所欲为了。其使用技巧非常之多,非常之灵活。简单如编写一个音频播放器,复杂的比如专业级的、极端繁琐的多轨录音与混缩软件,视频非编软件的音频播放与控制部分等等等等……

完整示例:(略)