音頻
主要的音頻播放類是AudioToolbox.framework和AVFoundation.framework。
音效
AudioToolbox.framework是C語言播放框架,用來播放一些短音頻,本質是將短音頻注冊到系統聲音服務(System Sound Service)。
System Sound Service的限制:
- 時間不能超過30s
- 數據必須是PCM或者IMA4格式
- 音頻只能是.caf、.aif、.wav格式
使用System Sound Service步驟:
- 調用
AudioServicesCreateSystemSoundID(CFURLRef inFileURL, SystemSoundID *outSystemSoundID)
獲取系統聲音ID。 - 如果需要監聽播放完成時進行操作,則調用
AudioServicesAddSystemSoundCompletion(SystemSoundID inSystemSoundID, CFRunLoopRef inRunLoop, CFStringRef inRunLoopMode, AudioServicesSystemSoundCompletionProc inCompletionRoutine, void *inClientData)
注冊回調方法。 - 調用
AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID)
或者AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID)
播放,后者播放完成后代有震動效果。
下面是一個例子:
#import "KCMainViewController.h"
#import <AudioToolbox/AudioToolbox.h>
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self playSoundEffect:@"videoRing.caf"];
}
/*
* 播放完成回調函數
*
* @param soundID 系統聲音ID
* @param clientData 回調時傳遞的數據
*/
void soundCompleteCallback(SystemSoundID soundID,void * clientData){
NSLog(@"播放完成...");
}
/*
* 播放音效文件
*
* @param name 音頻文件名稱
*/
-(void)playSoundEffect:(NSString *)name{
NSString *audioFile=[[NSBundle mainBundle] pathForResource:name ofType:nil];
NSURL *fileUrl=[NSURL fileURLWithPath:audioFile];
//1.獲得系統聲音ID
SystemSoundID soundID = 0;
/*
* inFileUrl:音頻文件url
* outSystemSoundID:聲音id(此函數會將音效文件加入到系統音頻服務中并返回一個長整形ID)
*/
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
//如果需要在播放完之后執行某些操作,可以調用如下方法注冊一個播放完成回調函數
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
//2.播放音頻
AudioServicesPlaySystemSound(soundID); //播放音效
//AudioServicesPlayAlertSound(soundID); //播放音效并震動
}
@end
音樂
音樂播放一般使用AVAudioPlayer類來實現。有關該類的屬性和方法:
屬性 | 說明 |
---|---|
@property(readonly, getter=isPlaying) BOOL playing | 是否正在播放,只讀 |
@property(readonly) NSUInteger numberOfChannels | 音頻聲道數,只讀 |
@property(readonly) NSTimeInterval duration | 音頻時長 |
@property(readonly) NSURL *url | 音頻文件路徑,只讀 |
@property(readonly) NSData *data | 音頻數據,只讀 |
@property float pan | 立體聲平衡,如果為-1.0則完全左聲道,如果0.0則左右聲道平衡,如果為1.0則完全為右聲道 |
@property float volume | 音量大小,范圍0-1.0 |
@property BOOL enableRate | 是否允許改變播放速率 |
@property float rate | 播放速率,范圍0.5-2.0,如果為1.0則正常播放,如果要修改播放速率則必須設置enableRate為YES |
@property NSTimeInterval currentTime | 當前播放時長 |
property(readonly) NSTimeInterval deviceCurrentTime | 輸出設備播放音頻的時間,注意如果播放中被暫停此時間也會繼續累加 |
@property NSInteger numberOfLoops | 循環播放次數,如果為0則不循環,如果小于0則無限循環,大于0則表示循環次數 |
@property(readonly) NSDictionary *settings | 音頻播放設置信息,只讀 |
@property(getter=isMeteringEnabled) BOOL meteringEnabled | 是否啟用音頻測量,默認為NO,一旦啟用音頻測量可以通過updateMeters方法更新測量值 |
對象方法 | 說明 |
---|---|
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError | 使用文件URL初始化播放器,注意這個URL不能是HTTP URL,AVAudioPlayer不支持加載網絡媒體流,只能播放本地文件 |
- (instancetype)initWithData:(NSData *)data error:(NSError **)outError | 使用NSData初始化播放器,注意使用此方法時必須文件格式和文件后綴一致,否則出錯,所以相比此方法更推薦使用上述方法或- (instancetype)initWithData:(NSData *)data fileTypeHint:(NSString *)utiString error:(NSError **)outError方法進行初始化 |
- (BOOL)prepareToPlay; | 加載音頻文件到緩沖區,注意即使在播放之前音頻文件沒有加載到緩沖區程序也會隱式調用此方法。 |
- (BOOL)play; | 播放音頻文件 |
- (BOOL)playAtTime:(NSTimeInterval)time | 在指定的時間開始播放音頻 |
- (void)pause; | 暫停播放 |
- (void)stop; | 停止播放 |
- (void)updateMeters | 更新音頻測量值,注意如果要更新音頻測量值必須設置meteringEnabled為YES,通過音頻測量值可以即時獲得音頻分貝等信息 |
- (float)peakPowerForChannel:(NSUInteger)channelNumber; | 獲得指定聲道的分貝峰值,注意如果要獲得分貝峰值必須在此之前調用updateMeters方法 |
- (float)averagePowerForChannel:(NSUInteger)channelNumber | 獲得指定聲道的分貝平均值,注意如果要獲得分貝平均值必須在此之前調用updateMeters方法 |
@property(nonatomic, copy) NSArray *channelAssignments | 獲得或設置播放聲道 |
代理方法 | 說明 |
---|---|
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag | 音頻播放完成 |
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error | 音頻解碼發生錯誤 |
AVAudioPlayer使用方法:
- 初始化AVAudioPlayer對象,只能指定本地文件。
- 設置播放器屬性。
- 調用play方法播放。
下面是一個播放器例子,界面如下:

代碼如下:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#define kMusicFile @"劉若英 - 原來你也在這里.mp3"
#define kMusicSinger @"劉若英"
#define kMusicTitle @"原來你也在這里"
@interface ViewController ()<AVAudioPlayerDelegate>
@property (nonatomic,strong) AVAudioPlayer *audioPlayer;//播放器
@property (weak, nonatomic) IBOutlet UILabel *controlPanel; //控制面板
@property (weak, nonatomic) IBOutlet UIProgressView *playProgress;//播放進度
@property (weak, nonatomic) IBOutlet UILabel *musicSinger; //演唱者
@property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暫停按鈕(如果tag為0認為是暫停狀態,1是播放狀態)
@property (weak ,nonatomic) NSTimer *timer;//進度更新定時器
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
}
/**
* 初始化UI
*/
-(void)setupUI{
self.title=kMusicTitle;
self.musicSinger.text=kMusicSinger;
}
-(NSTimer *)timer{
if (!_timer) {
_timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:true];
}
return _timer;
}
/**
* 創建播放器
*
* @return 音頻播放器
*/
-(AVAudioPlayer *)audioPlayer{
if (!_audioPlayer) {
NSString *urlStr=[[NSBundle mainBundle]pathForResource:kMusicFile ofType:nil];
NSURL *url=[NSURL fileURLWithPath:urlStr];
NSError *error=nil;
//初始化播放器,注意這里的Url參數只能時文件路徑,不支持HTTP Url
_audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
//設置播放器屬性
_audioPlayer.numberOfLoops=0;//設置為0不循環
_audioPlayer.delegate=self;
[_audioPlayer prepareToPlay];//加載音頻文件到緩存
if(error){
NSLog(@"初始化播放器過程發生錯誤,錯誤信息:%@",error.localizedDescription);
return nil;
}
}
return _audioPlayer;
}
/**
* 播放音頻
*/
-(void)play{
if (![self.audioPlayer isPlaying]) {
[self.audioPlayer play];
self.timer.fireDate=[NSDate distantPast];//恢復定時器
}
}
/**
* 暫停播放
*/
-(void)pause{
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer pause];
self.timer.fireDate=[NSDate distantFuture];//暫停定時器,注意不能調用invalidate方法,此方法會取消,之后無法恢復
}
}
/**
* 點擊播放/暫停按鈕
*
* @param sender 播放/暫停按鈕
*/
- (IBAction)playClick:(UIButton *)sender {
if(sender.tag){
sender.tag=0;
[sender setImage:[UIImage imageNamed:@"playing_btn_play_n"] forState:UIControlStateNormal];
[sender setImage:[UIImage imageNamed:@"playing_btn_play_h"] forState:UIControlStateHighlighted];
[self pause];
}else{
sender.tag=1;
[sender setImage:[UIImage imageNamed:@"playing_btn_pause_n"] forState:UIControlStateNormal];
[sender setImage:[UIImage imageNamed:@"playing_btn_pause_h"] forState:UIControlStateHighlighted];
[self play];
}
}
/**
* 更新播放進度
*/
-(void)updateProgress{
float progress= self.audioPlayer.currentTime /self.audioPlayer.duration;
[self.playProgress setProgress:progress animated:true];
}
#pragma mark - 播放器代理方法
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(@"音樂播放完成...");
}
@end
運行效果:

音頻會話
播放器需要支持后臺播放,方法如下:
-
設置后臺模式:在plist文件中添加Required background modes,并且設置item 0=App plays audio or streams audio/video using AirPlay(其實可以直接通過Xcode在Project Targets-Capabilities-Background Modes中設置)。
- 設置AVAudioSession的類型為AVAudioSessionCategoryPlayback并且調用setActive::方法啟動會話。
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
- 為了能夠讓應用退到后臺之后支持耳機控制,建議添加遠程控制事件(這一步不是后臺播放必須的)。