控制回放音量
音频队列对象为您提供两种控制回放音量的方法。
您可以通过调用AudioQueueSetParameter 函数并传入kAudioQueueParam_Volume 参数来直接设置回放的音量,如程序清单7-8所示,音量的变化会立即生效。
程序清单7-8 直接设置回放的音量
Float32 volume = 1; // linear scale, range from 0.0 through 1.0
|
AudioQueueSetParameter (
|
myAQstruct.audioQueueObject,
|
kAudioQueueParam_Volume,
|
volume
|
);
|
您还可以通过AudioQueueEnqueueBufferWithParameters 函数来设置音频队列缓冲区的回放音量。这个函数可以指定音频队列缓冲区进入队列时携带的音频队列设置。通过这个函数做出的改变在音频队列缓冲区开始播放的时候生效。
在上述的两种情况下,对音频队列的音量所做的修改都会一直保持下来,直到再次被改变。
指示回放音量
您可以通过下面的方式得到音频队列对象的当前回放音量:
-
启用音频队列对象的音量计,具体方法是将其kAudioQueueProperty_EnableLevelMetering 属性设置为true 。
-
查询音频队列对象的kAudioQueueProperty_CurrentLevelMeter 属性。
这个属性的值是一个AudioQueueLevelMeterState 结构的数组,每个声道都有一个相对应的结构。程序清单7-9显示了这个结构的内容:
程序清单7-9 AudioQueueLevelMeterState 结构
typedef struct AudioQueueLevelMeterState {
|
Float32 mAveragePower;
|
Float32 mPeakPower;
|
}; AudioQueueLevelMeterState;
|
同时播放多路声音
为了同时播放多路声音,需要为每路声音创建一个回放音频队列对象,并对每个音频队列调用 AudioQueueEnqueueBufferWithParameters 函数,将第一个音频缓冲区排入队列,使之开始播放。
在基于iPhone OS的设备中同时播放声音时,音频格式是很关键的。如果要同时播放,您需要使用线性PCM (无压缩) 音频格式或特定的有压缩音频格式,具体描述请参见“音频回放和录制格式”部分。
使用OpenAL播放和定位声音
开源的OpenAL音频API位于iPhone OS系统的OpenAL框架中,它提供了一个优化接口,用于定位正在回放的立体声场中的声音。使用OpenAL进行声音的播放、定位、和移动是很简单的—其工作方式和其它平台一样。此外,OpenAL还可以进行混音。OpenAL使用Core
Audio的I/O单元进行回放,从而使延迟最低。
由于所有的这些原因,OpenAL是iPhone OS设备中游戏程序的最好选择。当然,OpenAL也是一般的iPhone OS应用程序进行音频播放的良好选择。
iPhone OS对OpenAL 1.1的支持是构建在Core Audio之上的。更多的信息请参见iPhone OS系统的OpenAL FAQ。如果需要有关OpenAL的文档,请参见http://的OpenAL网站;如果需要演示如何播放OpenAL音频的示例程序,请参见oalTouch。
录制音频
在iPhone OS系统上,可以通过AVAudioRecorder 类和音频队列服务来进行音频录制,而Core
Audio则为其提供底层的支持。这些接口所做的工作包括连接音频硬件、管理内存、以及在需要时使用编解码器。您可以录制“音频的回放和录制格式”部分列出的所有格式的音频。
本部分将介绍如何通过AVAudioRecorder 类和音频队列服务在iPhone OS系统上录制音频。
通过AVAudioRecorder类进行录制
iPhone OS上最简单的录音方法是使用AVAudioRecorder 类,类的具体描述请参见AVAudioRecorder类参考。该类提供了一个高度精简的Objective-C接口。通过这个接口,您可以轻松实现诸如暂停/重启录音这样的功能,以及处理音频中断。同时,您还可以对录制格式保持完全的控制。
进行录制时,您需要提供一个声音文件的URL、建立音频会话、以及配置录音对象。进行这些准备工作的一个良好时机就是应用程序启动的时候,如程序清单7-10所示。诸如soundFileURL 和recording 这样的变量都在类接口文件中进行声明。
程序清单7-10 建立音频会话和声音文件的URL
- (void) viewDidLoad {
|
|
[super viewDidLoad];
|
|
NSString *tempDir = NSTemporaryDirectory ();
|
NSString *soundFilePath = [tempDir stringByAppendingString: @"sound.caf"];
|
|
NSURL *newURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
|
self.soundFileURL = newURL;
|
[newURL release];
|
|
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
audioSession.delegate = self;
|
[audioSession setActive: YES error: nil];
|
|
recording = NO;
|
playing = NO;
|
}
|
您需要在接口声明中加入AVAudioSessionDelegate 、AVAudioRecorderDelegate 、AVAudioPlayerDelegate (如果同时支持声音回放的话)协议。
然后,就可以实现如程序清单7-11所示的录制方法。
程序清单7-11 一个基于AVAudioRecorder类的录制/停止方法
-(IBAction) recordOrStop: (id) sender {
|
|
if (recording) {
|
|
[soundRecorder stop];
|
recording = NO;
|
self.soundRecorder = nil;
|
|
[recordOrStopButton setTitle: @"Record" forState: UIControlStateNormal];
|
[recordOrStopButton setTitle: @"Record" forState: UIControlStateHighlighted];
|
|
[[AVAudioSession sharedInstance] setActive: NO error: nil];
|
|
} else {
|
|
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryRecord error: nil];
|
|
NSDictionary *recordSettings =
|
[[NSDictionary alloc] initWithObjectsAndKeys:
|
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
|
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
|
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
|
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
|
nil];
|
|
AVAudioRecorder *newRecorder = [[AVAudioRecorder alloc] initWithURL: soundFileURL
|
settings: recordSettings
|
error: nil];
|
[recordSettings release];
|
self.soundRecorder = newRecorder;
|
[newRecorder release];
|
|
soundRecorder.delegate = self;
|
[soundRecorder prepareToRecord];
|
[soundRecorder record];
|
[recordOrStopButton setTitle: @"Stop" forState: UIControlStateNormal];
|
[recordOrStopButton setTitle: @"Stop" forState: UIControlStateHighlighted];
|
|
recording = YES;
|
}
|
}
|
有关AVAudioRecorder 类的更多信息,请参见AVAudioRecorder类参考。
用音频队列服务进行录制
用音频队列服务进行录制时,您的应用程序需要配置音频会话、实例化一个录音音频队列对象,并为其提供一个回调函数。回调函数负责将音频数据存入内存以备随时使用,或者写入文件进行长期存储。
声音的录制发生在iPhone OS的系统定义级别(system-defined level)。系统会从用户选择的音频源取得输入—比如内置的麦克风、耳机麦克风(如果连接到iPhone上的话)、或者其它输入源。
和声音的回放一样,您可以通过查询音频队列对象的kAudioQueueProperty_CurrentLevelMeter 属性来取得当前的录制音量,具体描述请见“指示回放音量”部分。
有关如何通过音频队列服务录制音频的详细实例,请参见音频队列服务编程指南的录制音频部分,实例代码则请见iPhone
Dev Center网站上的SpeakHere。
解析音频流
为了播放音频流内容,比如来自网络连接的音频流,可以结合使用音频文件流服务和音频队列服务。音频文件流服务负责从常见的、采用网络位流格式的音频文件容器中解析出音频数据和元数据。您也可以用它来解析磁盘文件中的数据包和元数据。
iPhone OS可以解析的音频文件和位流格式和Mac OS X相同,具体如下:
在取得音频数据包之后,您就可以以任何iPhone OS系统支持的格式进行播放,这些格式在“音频回放和录制格式”部分中列出。
为了获得最好的性能,处理网络音频流的应用程序应该仅使用来自Wi-Fi连接的数据。您可以通过iPhone OS提供的System Configuration框架及其SCNetworkReachability.h 头文件定义的接口来确定什么网络是可到达和可用的。如果需要实例代码,请参见iPhone
Dev Center网站的Reachability工程。
为了连接网络音频流,可以使用iPhone OS系统中的Core Foundation框架中的接口,比如CFHTTPMesaage 接口,具体描述请见CFHTTPMessage参考。通过音频文件流服务解析网络数据包,将它恢复为音频数据包,然后放入缓冲区,发送给负责回放的音频队列对象。
音频文件流服务依赖于音频文件服务定义的接口,比如AudioFramePacketTranslation 结构和AudioFilePacketTableInfo 结构,具体描述请见音频文件服务参考。
有关如何使用流的更多信息,请参见音频文件流服务参考。实例代码则请参见位于<Xcode>/Examples/CoreAudio/Services/ 目录下的AudioFileStream例子工程,其中<Xcode>是开发工具所在的目录。
iPhone OS系统上的音频单元支持
iPhone OS提供一组音频插件,称为音频单元,可以用于所有的应用程序。您可以通过Audio Unit框架提供的接口来打开、连接、和使用音频单元;还可以定义定制的音频单元,在自己的应用程序内部使用。由于应用程序必须静态连接定制的音频单元,所以iPhone
OS系统上的其它应用程序不能使用您开发的音频单元。
表7-3列出了iPhone OS提供的音频单元。
表7-3 系统提供的音频单元
音频单元
|
描述
|
转换器单元
|
转换器单元,类型为kAudioUnitSubType_AUConverter ,用于音频数据的格式转换。
|
iPod均衡器单元
|
iPod EQ单元,类型为kAudioUnitSubType_AUiPodEQ ,提供一个简单的、基于预设的均衡器,可以在应用程序中使用。
|
3D混音器单元
|
3D混音器单元,类型为kAudioUnitSubType_AU3DMixerEmbedded ,用于混合多个音频流,指定立体声输出移动,操作采样率,等等。
|
多通道混音器单元
|
多通道混音器单元,类型为kAudioUnitSubType_MultiChannelMixer ,用于将多个音频流混合成为单一的音频流。
|
一般输出单元
|
一般输出单元,类型为kAudioUnitSubType_GenericOutput ,支持和线性PCM格式互相转换,可以用于开始或结束一个音频单元图。
|
I/O单元
|
I/O单元,类型为kAudioUnitSubType_RemoteIO ,用于连接音频输入和输入硬件,支持实时I/O。如何使用音频单元的实例代码请见aurioTouch工程。
|
语音处理I/O单元
|
语音处理I/O单元,类型为kAudioUnitSubType_VoiceProcessingIO ,具有I/O单元的特征,同时为了支持双向交流,加入了回响抑制功能。
|
有关系统音频单元的更多信息,请参见系统音频单元访问指南。
iPhone音频的最佳实践
操作音频的贴士
在操作iPhone OS系统上的音频内容时,您需要记住表7-4列出的基本贴士。
表7-4 音频贴士
贴士
|
动作
|
正确地使用压缩音频
|
对于AAC、MP3、和ALAC (Apple Lossless) 音频,解码过程是由硬件来完成的,虽然比较有效,但同时只能解码一个音频流。如果您需要同时播放多路声音,请使用IMA4 (压缩) 或者线性PCM (无压缩) 格式来存储那些文件。
|
将音频转换为您需要的数据格式和文件格式
|
Mac OS X的afconvert 工具可以进行很多数据格式和文件类型的转换。请参见“iPhone
OS偏好的音频格式” 部分和afconvert 工具的手册页面。
|
评价音频的内存使用问题
|
当您使用音频队列服务播放音频时,需要编写一个回调函数,负责将较短的音频数据片断发送到音频队列的缓冲区。在某些情况下,将整个音频文件载入内存是最佳 的选择,这样可以使播放时的磁盘访问尽最少;而在另外一些情况下,最好的方法则是每次只载入足够填满缓冲区的数据。请测试和评价哪种策略对您的应用程序最 好。
|
限制音频的采样率和位深度,减少音频文件的尺寸
|
采样率和每个样本的位深度对无压缩音频的尺寸有直接的影响。如果您需要播放很多这样的声音,则应该考虑降低这些指标,以减少音频数据的内存开销。举例来 说,相对于使用采样率为44.1 kHz的音频作为声音效果, 您可以使用采样率为32 kHz(或可能更低)的音频,仍然可以得到很合理的品质。
|
选择恰当的技术
|
使用Core Audio的系统声音服务来播放警告和用户界面声音效果。当您希望使用便利的高级接口来定位立体声场中的声音,或者要求很低的回放延迟时,则应该使用 OpenAL。如果需要从文件或网络数据流中解析出音频数据,可以使用音频文件服务接口。如果只是简单回放一路或多路声音,则应该使用AVAudioPlayer 类。对于具有其它音频功能的应用程序,包括音频流的回放和音频录制,可以使用音频队列服务。
|
低延迟编码
|
如果需要尽可能低的回放延迟,可以使用OpenAL,或者直接使用I/O单元。
|
iPhone OS偏好的音频格式
对于无压缩(最高品质)音频,请使用封装在CAF文件中的、16位、低位在前(little endian)的线性PCM音频数据。您可以用Mac OS X的afconvert 命令行工具来将音频文件转换为上述格式:
/usr/bin/afconvert -f caff -d LEI16 {INPUT} {OUTPUT}
|
afconvert 工具可以进行广泛的音频数据格式和文件类型转换。您可以通过afconvert 的手册页面,以及在shell提示符下键入afconvert
-h 命令获取更多信息。
对于压缩音频,当每次只需播放一个声音,或者当不需要和iPod同时播放音频时,适合使用AAC格式的CAF或m4a文件。
当您需要在同时播放多路声音时减少内存开销时,请使用IMA4 (IMA/ADPCM) 压缩格式,这样可以减少文件尺寸,同时在解压缩过程中对CPU的影响又最小。和线性PCM数据一样,请将IMA4数据封装在CAF文件中。
在iPhone OS使用视频
录制视频
从iPhone OS 3.0开始,您可以在具有录制支持的设备上录制视频,包括当时的音频。显示视频录制界面的方法是创建和推出一个UIImagePickerController 对象,和显示静态图片照相机界面完全一样。
在录制视频时,您必须首先检查是否存在照相机源类型 (UIImagePickerControllerSourceTypeCamera )
,以及照相机是否支持电影媒体类型 (kUTTypeMovie ) 。根据您为mediaTypes 属性分配的媒体类型的不同,选择器对象可以直接显示静态图像照相机,或者视频摄像机,还可以显示一个选择界面,让用户选择。
使用UIImagePickerControllerDelegate 协议,注册为图像选择器的委托。在视频录制完成时,您的委托对象的 imagePickerController:didFinishPickingMediaWithInfo: 方法会备调用。
对于支持录制的设备,您也可以从用户照片库中选择之前录制的视频。
有关如何使用图像选择器的更多信息,请参见UIImagePickerController类参考。
播放视频文件
在iPhone OS系统上,应用程序可以通过Media Player框架(MediaPlayer.framework ) 来播放视频文件。视频的回放只支持全屏模式,需要播放场景切换动画的游戏开发者或需要播放媒体文件的其它开发者可以使用。当应用程序开始播放视频时,媒体 播放器界面就会接管,将屏幕渐变为黑色,然后渐渐显示视频内容。视频播放界面上可以显示或者不显示调整回放的用户控件。您可以通过部分或全部激活这些控件
(如图7-2所示),使用户可以改变音量、改变回放点、开始或停止视频的播放。如果禁用所有的控件,视频会一直播放,直到结束。
图7-2 带有播放控制的媒体播放器界面

在开始播放前,您必须知道希望播放的URL。对于应用程序提供的文件,这个URL通常是指向应用程序包中某个文件的指针;但是,它也可以是指向远程服务器文件的指针。您可以用这个URL来实例化一个新的MPMoviePlayerController 类的实例。这个类负责视频文件的回放和管理用户交互,比如响应用户对播放控制(如果显示的话)的触击动作。简单调用控制器的play 方法,就可以开始播放了。
程序清单7-12显示一个实例方法,功能是播放位于指定URL的视频。play方法是异步的调用,在电影播放时会将控制权返回给调用者。电影控制器负责将电影载入一个全屏的视图,并通过动画效果将电影放到应用程序现有内容的上方。在视频回放完成后,电影控制器会向委托对象发出一个通告,该委托对象负责在不再需要时释放电影控制器。
程序清单7-12 播放全屏电影
-(void)playMovieAtURL:(NSURL*)theURL
|
{
|
MPMoviePlayerController* theMovie = [[MPMoviePlayerController alloc] initWithContentURL:theURL];
|
|
theMovie.scalingMode = MPMovieScalingModeAspectFill;
|
theMovie.movieControlMode = MPMovieControlModeHidden;
|
|
// Register for the playback finished notification.
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
selector:@selector(myMovieFinishedCallback:)
|
name:MPMoviePlayerPlaybackDidFinishNotification
|
object:theMovie];
|
|
// Movie playback is asynchronous, so this method returns immediately.
|
[theMovie play];
|
}
|
|
// When the movie is done, release the controller.
|
-(void)myMovieFinishedCallback:(NSNotification*)aNotification
|
{
|
MPMoviePlayerController* theMovie = [aNotification object];
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
name:MPMoviePlayerPlaybackDidFinishNotification
|
object:theMovie];
|
|
// Release the movie instance created in playMovieAtURL:
|
[theMovie release];
|
}
|
有关Media Player框架的各个类的更多信息,请参见Media
Player框架参考。有关它支持的视频格式列表,请参见iPhone
OS技术概览。
|