在iOS中音頻播放從形式上可以分為音效播放和音樂(lè)播放喻频。前者主要指的是一些短音頻播放,通常作為點(diǎn)綴音頻褒颈,對(duì)于這類音頻不需要進(jìn)行進(jìn)度、循環(huán)等控制励堡。后者指的是一些較長(zhǎng)的音頻谷丸,通常是主音頻,對(duì)于這些音頻的播放通常需要進(jìn)行精確的控制应结。在iOS中播放兩類音頻分別使用AudioToolbox.framework和AVFoundation.framework來(lái)完成音效和音樂(lè)播放刨疼。
***音效
AudioToolbox.framework是一套基于C語(yǔ)言的框架,使用它來(lái)播放音效其本質(zhì)是將短音頻注冊(cè)到系統(tǒng)聲音服務(wù)(System Sound Service)鹅龄。System Sound Service是一種簡(jiǎn)單揩慕、底層的聲音播放服務(wù),但是它本身也存在著一些限制:
音頻播放時(shí)間不能超過(guò)30s
數(shù)據(jù)必須是PCM或者IMA4格式
音頻文件必須打包成.caf扮休、.aif迎卤、.wav中的一種(注意這是官方文檔的說(shuō)法,實(shí)際測(cè)試發(fā)現(xiàn)一些.mp3也可以播放)
使用System Sound Service 播放音效的步驟如下:
1.調(diào)用AudioServicesCreateSystemSoundID( CFURLRef inFileURL, SystemSoundID* outSystemSoundID)函數(shù)獲得系統(tǒng)聲音ID玷坠。
2.如果需要監(jiān)聽(tīng)播放完成操作蜗搔,則使用AudioServicesAddSystemSoundCompletion( SystemSoundID inSystemSoundID,
CFRunLoopRef inRunLoop, CFStringRef inRunLoopMode, AudioServicesSystemSoundCompletionProc inCompletionRoutine, void* inClientData)方法注冊(cè)回調(diào)函數(shù)。
3.調(diào)用AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID) 或者AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID) 方法播放音效(后者帶有震動(dòng)效果)侨糟。
下面是一個(gè)簡(jiǎn)單的示例程序:
//
// KCMainViewController.m
// Audio
//
// Created by Kenshin Cui on 14/03/30.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
// 音效播放
#import "KCMainViewController.h"
#import <AudioToolbox/AudioToolbox.h>
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self playSoundEffect:@"videoRing.caf"];
}
/**
* 播放完成回調(diào)函數(shù)
*
* @param soundID 系統(tǒng)聲音ID
* @param clientData 回調(diào)時(shí)傳遞的數(shù)據(jù)
*/
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.獲得系統(tǒng)聲音ID
SystemSoundID soundID=0;
/**
* inFileUrl:音頻文件url
* outSystemSoundID:聲音id(此函數(shù)會(huì)將音效文件加入到系統(tǒng)音頻服務(wù)中并返回一個(gè)長(zhǎng)整形ID)
*/
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
//如果需要在播放完之后執(zhí)行某些操作碍扔,可以調(diào)用如下方法注冊(cè)一個(gè)播放完成回調(diào)函數(shù)
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
//2.播放音頻
AudioServicesPlaySystemSound(soundID);//播放音效
// AudioServicesPlayAlertSound(soundID);//播放音效并震動(dòng)
}
@end
音樂(lè)
如果播放較大的音頻或者要對(duì)音頻有精確的控制則System Sound Service可能就很難滿足實(shí)際需求了,通常這種情況會(huì)選擇使用AVFoundation.framework中的AVAudioPlayer來(lái)實(shí)現(xiàn)秕重。AVAudioPlayer可以看成一個(gè)播放器不同,它支持多種音頻格式,而且能夠進(jìn)行進(jìn)度溶耘、音量二拐、播放速度等控制。首先簡(jiǎn)單看一下AVAudioPlayer常用的屬性和方法:
AVAudioPlayer的使用比較簡(jiǎn)單:
1.初始化AVAudioPlayer對(duì)象凳兵,此時(shí)通常指定本地文件路徑百新。
2.設(shè)置播放器屬性,例如重復(fù)次數(shù)庐扫、音量大小等饭望。
3.調(diào)用play方法播放仗哨。
下面就使用AVAudioPlayer實(shí)現(xiàn)一個(gè)簡(jiǎn)單播放器,在這個(gè)播放器中實(shí)現(xiàn)了播放铅辞、暫停厌漂、顯示播放進(jìn)度功能,當(dāng)然例如調(diào)節(jié)音量斟珊、設(shè)置循環(huán)模式苇倡、甚至是聲波圖像(通過(guò)分析音頻分貝值)等功能都可以實(shí)現(xiàn),這里就不再一一演示囤踩。界面效果如下:
當(dāng)然由于AVAudioPlayer一次只能播放一個(gè)音頻文件旨椒,所有上一曲、下一曲其實(shí)可以通過(guò)創(chuàng)建多個(gè)播放器對(duì)象來(lái)完成堵漱,這里暫不實(shí)現(xiàn)综慎。播放進(jìn)度的實(shí)現(xiàn)主要依靠一個(gè)定時(shí)器實(shí)時(shí)計(jì)算當(dāng)前播放時(shí)長(zhǎng)和音頻總時(shí)長(zhǎng)的比例,另外為了演示委托方法怔锌,下面的代碼中也實(shí)現(xiàn)了播放完成委托方法寥粹,通常如果有下一曲功能的話播放完可以觸發(fā)下一曲音樂(lè)播放变过。下面是主要代碼:
//
// ViewController.m
// KCAVAudioPlayer
//
// Created by Kenshin Cui on 14/03/30.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#define kMusicFile @"劉若英 - 原來(lái)你也在這里.mp3"
#define kMusicSinger @"劉若英"
#define kMusicTitle @"原來(lái)你也在這里"
@interface ViewController ()<AVAudioPlayerDelegate>
@property (nonatomic,strong) AVAudioPlayer *audioPlayer;//播放器
@property (weak, nonatomic) IBOutlet UILabel *controlPanel; //控制面板
@property (weak, nonatomic) IBOutlet UIProgressView *playProgress;//播放進(jìn)度
@property (weak, nonatomic) IBOutlet UILabel *musicSinger; //演唱者
@property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暫停按鈕(如果tag為0認(rèn)為是暫停狀態(tài)埃元,1是播放狀態(tài))
@property (weak ,nonatomic) NSTimer *timer;//進(jìn)度更新定時(shí)器
@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;
}
/**
* 創(chuàng)建播放器
*
* @return 音頻播放器
*/
-(AVAudioPlayer *)audioPlayer{
if (!_audioPlayer) {
NSString *urlStr=[[NSBundle mainBundle]pathForResource:kMusicFile ofType:nil];
NSURL *url=[NSURL fileURLWithPath:urlStr];
NSError *error=nil;
//初始化播放器,注意這里的Url參數(shù)只能時(shí)文件路徑媚狰,不支持HTTP Url
_audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
//設(shè)置播放器屬性
_audioPlayer.numberOfLoops=0;//設(shè)置為0不循環(huán)
_audioPlayer.delegate=self;
[_audioPlayer prepareToPlay];//加載音頻文件到緩存
if(error){
NSLog(@"初始化播放器過(guò)程發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
return nil;
}
}
return _audioPlayer;
}
/**
* 播放音頻
*/
-(void)play{
if (![self.audioPlayer isPlaying]) {
[self.audioPlayer play];
self.timer.fireDate=[NSDate distantPast];//恢復(fù)定時(shí)器
}
}
/**
* 暫停播放
*/
-(void)pause{
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer pause];
self.timer.fireDate=[NSDate distantFuture];//暫停定時(shí)器岛杀,注意不能調(diào)用invalidate方法,此方法會(huì)取消崭孤,之后無(wú)法恢復(fù)
}
}
/**
* 點(diǎn)擊播放/暫停按鈕
*
* @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];
}
}
/**
* 更新播放進(jìn)度
*/
-(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(@"音樂(lè)播放完成...");
}
@end
運(yùn)行效果:
音頻會(huì)話
事實(shí)上上面的播放器還存在一些問(wèn)題类嗤,例如通常我們看到的播放器即使退出到后臺(tái)也是可以播放的,而這個(gè)播放器如果退出到后臺(tái)它會(huì)自動(dòng)暫停辨宠。如果要支持后臺(tái)播放需要做下面幾件事情:
1.設(shè)置后臺(tái)運(yùn)行模式:在plist文件中添加Required background modes遗锣,并且設(shè)置item 0=App plays audio or streams audio/video using AirPlay(其實(shí)可以直接通過(guò)Xcode在Project Targets-Capabilities-Background Modes中設(shè)置)
2.設(shè)置AVAudioSession的類型為AVAudioSessionCategoryPlayback并且調(diào)用setActive::方法啟動(dòng)會(huì)話。
AVAudioSession *audioSession=[AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
3.為了能夠讓應(yīng)用退到后臺(tái)之后支持耳機(jī)控制嗤形,建議添加遠(yuǎn)程控制事件(這一步不是后臺(tái)播放必須的)
前兩步是后臺(tái)播放所必須設(shè)置的精偿,第三步主要用于接收遠(yuǎn)程事件,這部分內(nèi)容之前的文章中有詳細(xì)介紹赋兵,如果這一步不設(shè)置雖讓也能夠在后臺(tái)播放笔咽,但是無(wú)法獲得音頻控制權(quán)(如果在使用當(dāng)前應(yīng)用之前使用其他播放器播放音樂(lè)的話,此時(shí)如果按耳機(jī)播放鍵或者控制中心的播放按鈕則會(huì)播放前一個(gè)應(yīng)用的音頻)霹期,并且不能使用耳機(jī)進(jìn)行音頻控制叶组。第一步操作相信大家都很容易理解,如果應(yīng)用程序要允許運(yùn)行到后臺(tái)必須設(shè)置历造,正常情況下應(yīng)用如果進(jìn)入后臺(tái)會(huì)被掛起甩十,通過(guò)該設(shè)置可以上應(yīng)用程序繼續(xù)在后臺(tái)運(yùn)行船庇。但是第二步使用的AVAudioSession有必要進(jìn)行一下詳細(xì)的說(shuō)明。
在iOS中每個(gè)應(yīng)用都有一個(gè)音頻會(huì)話侣监,這個(gè)會(huì)話就通過(guò)AVAudioSession來(lái)表示溢十。AVAudioSession同樣存在于AVFoundation框架中,它是單例模式設(shè)計(jì)达吞,通過(guò)sharedInstance進(jìn)行訪問(wèn)张弛。在使用Apple設(shè)備時(shí)大家會(huì)發(fā)現(xiàn)有些應(yīng)用只要打開(kāi)其他音頻播放就會(huì)終止,而有些應(yīng)用卻可以和其他應(yīng)用同時(shí)播放酪劫,在多種音頻環(huán)境中如何去控制播放的方式就是通過(guò)音頻會(huì)話來(lái)完成的吞鸭。下面是音頻會(huì)話的幾種會(huì)話模式:
注意:是否遵循靜音鍵表示在播放過(guò)程中如果用戶通過(guò)硬件設(shè)置為靜音是否能關(guān)閉聲音。
根據(jù)前面對(duì)音頻會(huì)話的理解覆糟,相信大家開(kāi)發(fā)出能夠在后臺(tái)播放的音頻播放器并不難刻剥,但是注意一下,在前面的代碼中也提到設(shè)置完音頻會(huì)話類型之后需要調(diào)用setActive::方法將會(huì)話激活才能起作用滩字。類似的造虏,如果一個(gè)應(yīng)用已經(jīng)在播放音頻,打開(kāi)我們的應(yīng)用之后設(shè)置了在后臺(tái)播放的會(huì)話類型麦箍,此時(shí)其他應(yīng)用的音頻會(huì)停止而播放我們的音頻漓藕,如果希望我們的程序音頻播放完之后(關(guān)閉或退出到后臺(tái)之后)能夠繼續(xù)播放其他應(yīng)用的音頻的話則可以調(diào)用setActive::方法關(guān)閉會(huì)話。代碼如下:
//
// ViewController.m
// KCAVAudioPlayer
//
// Created by Kenshin Cui on 14/03/30.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
// AVAudioSession 音頻會(huì)話
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#define kMusicFile @"劉若英 - 原來(lái)你也在這里.mp3"
#define kMusicSinger @"劉若英"
#define kMusicTitle @"原來(lái)你也在這里"
@interface ViewController ()<AVAudioPlayerDelegate>
@property (nonatomic,strong) AVAudioPlayer *audioPlayer;//播放器
@property (weak, nonatomic) IBOutlet UILabel *controlPanel; //控制面板
@property (weak, nonatomic) IBOutlet UIProgressView *playProgress;//播放進(jìn)度
@property (weak, nonatomic) IBOutlet UILabel *musicSinger; //演唱者
@property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暫停按鈕(如果tag為0認(rèn)為是暫停狀態(tài)挟裂,1是播放狀態(tài))
@property (weak ,nonatomic) NSTimer *timer;//進(jìn)度更新定時(shí)器
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
}
/**
* 顯示當(dāng)面視圖控制器時(shí)注冊(cè)遠(yuǎn)程事件
*
* @param animated 是否以動(dòng)畫的形式顯示
*/
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//開(kāi)啟遠(yuǎn)程控制
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
//作為第一響應(yīng)者
//[self becomeFirstResponder];
}
/**
* 當(dāng)前控制器視圖不顯示時(shí)取消遠(yuǎn)程控制
*
* @param animated 是否以動(dòng)畫的形式消失
*/
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
//[self resignFirstResponder];
}
/**
* 初始化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;
}
/**
* 創(chuàng)建播放器
*
* @return 音頻播放器
*/
-(AVAudioPlayer *)audioPlayer{
if (!_audioPlayer) {
NSString *urlStr=[[NSBundle mainBundle]pathForResource:kMusicFile ofType:nil];
NSURL *url=[NSURL fileURLWithPath:urlStr];
NSError *error=nil;
//初始化播放器享钞,注意這里的Url參數(shù)只能時(shí)文件路徑,不支持HTTP Url
_audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
//設(shè)置播放器屬性
_audioPlayer.numberOfLoops=0;//設(shè)置為0不循環(huán)
_audioPlayer.delegate=self;
[_audioPlayer prepareToPlay];//加載音頻文件到緩存
if(error){
NSLog(@"初始化播放器過(guò)程發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
return nil;
}
//設(shè)置后臺(tái)播放模式
AVAudioSession *audioSession=[AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
// [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
[audioSession setActive:YES error:nil];
//添加通知诀蓉,拔出耳機(jī)后暫停播放
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
}
return _audioPlayer;
}
/**
* 播放音頻
*/
-(void)play{
if (![self.audioPlayer isPlaying]) {
[self.audioPlayer play];
self.timer.fireDate=[NSDate distantPast];//恢復(fù)定時(shí)器
}
}
/**
* 暫停播放
*/
-(void)pause{
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer pause];
self.timer.fireDate=[NSDate distantFuture];//暫停定時(shí)器栗竖,注意不能調(diào)用invalidate方法,此方法會(huì)取消渠啤,之后無(wú)法恢復(fù)
}
}
/**
* 點(diǎn)擊播放/暫停按鈕
*
* @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];
}
}
/**
* 更新播放進(jìn)度
*/
-(void)updateProgress{
float progress= self.audioPlayer.currentTime /self.audioPlayer.duration;
[self.playProgress setProgress:progress animated:true];
}
/**
* 一旦輸出改變則執(zhí)行此方法
*
* @param notification 輸出改變通知對(duì)象
*/
-(void)routeChange:(NSNotification *)notification{
NSDictionary *dic=notification.userInfo;
int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue];
//等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示舊輸出不可用
if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];
//原設(shè)備為耳機(jī)則暫停
if ([portDescription.portType isEqualToString:@"Headphones"]) {
[self pause];
}
}
// [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
// NSLog(@"%@:%@",key,obj);
// }];
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
}
#pragma mark - 播放器代理方法
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(@"音樂(lè)播放完成...");
//根據(jù)實(shí)際情況播放完成可以將會(huì)話關(guān)閉狐肢,其他音頻應(yīng)用繼續(xù)播放
[[AVAudioSession sharedInstance]setActive:NO error:nil];
}
@end
在上面的代碼中還實(shí)現(xiàn)了拔出耳機(jī)暫停音樂(lè)播放的功能,這也是一個(gè)比較常見(jiàn)的功能沥曹。在iOS7及以后的版本中可以通過(guò)通知獲得輸出改變的通知份名,然后拿到通知對(duì)象后根據(jù)userInfo獲得是何種改變類型,進(jìn)而根據(jù)情況對(duì)音樂(lè)進(jìn)行暫停操作架专。
擴(kuò)展--播放音樂(lè)庫(kù)中的音樂(lè)
眾所周知音樂(lè)是iOS的重要組成播放同窘,無(wú)論是iPod、iTouch部脚、iPhone還是iPad都可以在iTunes購(gòu)買音樂(lè)或添加本地音樂(lè)到音樂(lè)庫(kù)中同步到你的iOS設(shè)備想邦。在MediaPlayer.frameowork中有一個(gè)MPMusicPlayerController用于播放音樂(lè)庫(kù)中的音樂(lè)。
下面先來(lái)看一下MPMusicPlayerController的常用屬性和方法:
MPMusicPlayerController有兩種播放器:applicationMusicPlayer和systemMusicPlayer委刘,前者在應(yīng)用退出后音樂(lè)播放會(huì)自動(dòng)停止丧没,后者在應(yīng)用停止后不會(huì)退出播放狀態(tài)鹰椒。
MPMusicPlayerController加載音樂(lè)不同于前面的AVAudioPlayer是通過(guò)一個(gè)文件路徑來(lái)加載,而是需要一個(gè)播放隊(duì)列呕童。在MPMusicPlayerController中提供了兩個(gè)方法來(lái)加載播放隊(duì)列:- (void)setQueueWithQuery:(MPMediaQuery *)query和- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection漆际,正是由于它的播放音頻來(lái)源是一個(gè)隊(duì)列,因此MPMusicPlayerController支持上一曲夺饲、下一曲等操作奸汇。
那么接下來(lái)的問(wèn)題就是如何獲取MPMediaQueue或者M(jìn)PMediaItemCollection?MPMediaQueue對(duì)象有一系列的類方法來(lái)獲得媒體隊(duì)列:
- (MPMediaQuery *)albumsQuery;
- (MPMediaQuery *)artistsQuery;
- (MPMediaQuery *)songsQuery;
- (MPMediaQuery *)playlistsQuery;
- (MPMediaQuery *)podcastsQuery;
- (MPMediaQuery *)audiobooksQuery;
- (MPMediaQuery *)compilationsQuery;
- (MPMediaQuery *)composersQuery;
- (MPMediaQuery *)genresQuery;
有了這些方法往声,就可以很容易獲到歌曲擂找、播放列表、專輯媒體等媒體隊(duì)列了浩销,這樣就可以通過(guò):- (void)setQueueWithQuery:(MPMediaQuery *)query方法設(shè)置音樂(lè)來(lái)源了贯涎。又或者得到MPMediaQueue之后創(chuàng)建MPMediaItemCollection,使用- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection設(shè)置音樂(lè)來(lái)源慢洋。
有時(shí)候可能希望用戶自己來(lái)選擇要播放的音樂(lè)塘雳,這時(shí)可以使用MPMediaPickerController,它是一個(gè)視圖控制器普筹,類似于UIImagePickerController败明,選擇完播放來(lái)源后可以在其代理方法中獲得MPMediaItemCollection對(duì)象。
無(wú)論是通過(guò)哪種方式獲得MPMusicPlayerController的媒體源斑芜,可能都希望將每個(gè)媒體的信息顯示出來(lái)肩刃,這時(shí)候可以通過(guò)MPMediaItem對(duì)象獲得。一個(gè)MPMediaItem代表一個(gè)媒體文件杏头,通過(guò)它可以訪問(wèn)媒體標(biāo)題、專輯名稱沸呐、專輯封面醇王、音樂(lè)時(shí)長(zhǎng)等等。無(wú)論是MPMediaQueue還是MPMediaItemCollection都有一個(gè)items屬性崭添,它是MPMediaItem數(shù)組寓娩,通過(guò)這個(gè)屬性可以獲得MPMediaItem對(duì)象。
下面就簡(jiǎn)單看一下MPMusicPlayerController的使用呼渣,在下面的例子中簡(jiǎn)單演示了音樂(lè)的選擇棘伴、播放、暫停屁置、通知焊夸、下一曲、上一曲功能蓝角,相信有了上面的概念阱穗,代碼讀起來(lái)并不復(fù)雜(示例中是直接通過(guò)MPMeidaPicker進(jìn)行音樂(lè)選擇的饭冬,但是仍然提供了兩個(gè)方法getLocalMediaQuery和getLocalMediaItemCollection來(lái)演示如何直接通過(guò)MPMediaQueue獲得媒體隊(duì)列或媒體集合):
//
// ViewController.m
// MPMusicPlayerController
//
// Created by Kenshin Cui 14/03/30
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//
#import "ViewController.h"
#import <MediaPlayer/MediaPlayer.h>
@interface ViewController ()<MPMediaPickerControllerDelegate>
@property (nonatomic,strong) MPMediaPickerController *mediaPicker;//媒體選擇控制器
@property (nonatomic,strong) MPMusicPlayerController *musicPlayer; //音樂(lè)播放器
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)dealloc{
[self.musicPlayer endGeneratingPlaybackNotifications];
}
/**
* 獲得音樂(lè)播放器
*
* @return 音樂(lè)播放器
*/
-(MPMusicPlayerController *)musicPlayer{
if (!_musicPlayer) {
_musicPlayer=[MPMusicPlayerController systemMusicPlayer];
[_musicPlayer beginGeneratingPlaybackNotifications];//開(kāi)啟通知,否則監(jiān)控不到MPMusicPlayerController的通知
[self addNotification];//添加通知
//如果不使用MPMediaPickerController可以使用如下方法獲得音樂(lè)庫(kù)媒體隊(duì)列
//[_musicPlayer setQueueWithItemCollection:[self getLocalMediaItemCollection]];
}
return _musicPlayer;
}
/**
* 創(chuàng)建媒體選擇器
*
* @return 媒體選擇器
*/
-(MPMediaPickerController *)mediaPicker{
if (!_mediaPicker) {
//初始化媒體選擇器揪阶,這里設(shè)置媒體類型為音樂(lè)昌抠,其實(shí)這里也可以選擇視頻、廣播等
// _mediaPicker=[[MPMediaPickerController alloc]initWithMediaTypes:MPMediaTypeMusic];
_mediaPicker=[[MPMediaPickerController alloc]initWithMediaTypes:MPMediaTypeAny];
_mediaPicker.allowsPickingMultipleItems=YES;//允許多選
// _mediaPicker.showsCloudItems=YES;//顯示icloud選項(xiàng)
_mediaPicker.prompt=@"請(qǐng)選擇要播放的音樂(lè)";
_mediaPicker.delegate=self;//設(shè)置選擇器代理
}
return _mediaPicker;
}
/**
* 取得媒體隊(duì)列
*
* @return 媒體隊(duì)列
*/
-(MPMediaQuery *)getLocalMediaQuery{
MPMediaQuery *mediaQueue=[MPMediaQuery songsQuery];
for (MPMediaItem *item in mediaQueue.items) {
NSLog(@"標(biāo)題:%@,%@",item.title,item.albumTitle);
}
return mediaQueue;
}
/**
* 取得媒體集合
*
* @return 媒體集合
*/
-(MPMediaItemCollection *)getLocalMediaItemCollection{
MPMediaQuery *mediaQueue=[MPMediaQuery songsQuery];
NSMutableArray *array=[NSMutableArray array];
for (MPMediaItem *item in mediaQueue.items) {
[array addObject:item];
NSLog(@"標(biāo)題:%@,%@",item.title,item.albumTitle);
}
MPMediaItemCollection *mediaItemCollection=[[MPMediaItemCollection alloc]initWithItems:[array copy]];
return mediaItemCollection;
}
#pragma mark - MPMediaPickerController代理方法
//選擇完成
-(void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{
MPMediaItem *mediaItem=[mediaItemCollection.items firstObject];//第一個(gè)播放音樂(lè)
//注意很多音樂(lè)信息如標(biāo)題鲁僚、專輯炊苫、表演者、封面冰沙、時(shí)長(zhǎng)等信息都可以通過(guò)MPMediaItem的valueForKey:方法得到,但是從iOS7開(kāi)始都有對(duì)應(yīng)的屬性可以直接訪問(wèn)
// NSString *title= [mediaItem valueForKey:MPMediaItemPropertyAlbumTitle];
// NSString *artist= [mediaItem valueForKey:MPMediaItemPropertyAlbumArtist];
// MPMediaItemArtwork *artwork= [mediaItem valueForKey:MPMediaItemPropertyArtwork];
//UIImage *image=[artwork imageWithSize:CGSizeMake(100, 100)];//專輯圖片
NSLog(@"標(biāo)題:%@,表演者:%@,專輯:%@",mediaItem.title ,mediaItem.artist,mediaItem.albumTitle);
[self.musicPlayer setQueueWithItemCollection:mediaItemCollection];
[self dismissViewControllerAnimated:YES completion:nil];
}
//取消選擇
-(void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker{
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - 通知
/**
* 添加通知
*/
-(void)addNotification{
NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(playbackStateChange:) name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:self.musicPlayer];
}
/**
* 播放狀態(tài)改變通知
*
* @param notification 通知對(duì)象
*/
-(void)playbackStateChange:(NSNotification *)notification{
switch (self.musicPlayer.playbackState) {
case MPMusicPlaybackStatePlaying:
NSLog(@"正在播放...");
break;
case MPMusicPlaybackStatePaused:
NSLog(@"播放暫停.");
break;
case MPMusicPlaybackStateStopped:
NSLog(@"播放停止.");
break;
default:
break;
}
}
#pragma mark - UI事件
- (IBAction)selectClick:(UIButton *)sender {
[self presentViewController:self.mediaPicker animated:YES completion:nil];
}
- (IBAction)playClick:(UIButton *)sender {
[self.musicPlayer play];
}
- (IBAction)puaseClick:(UIButton *)sender {
[self.musicPlayer pause];
}
- (IBAction)stopClick:(UIButton *)sender {
[self.musicPlayer stop];
}
- (IBAction)nextClick:(UIButton *)sender {
[self.musicPlayer skipToNextItem];
}
- (IBAction)prevClick:(UIButton *)sender {
[self.musicPlayer skipToPreviousItem];
}
@end
錄音
除了上面說(shuō)的劝评,在AVFoundation框架中還要一個(gè)AVAudioRecorder類專門處理錄音操作,它同樣支持多種音頻格式倦淀。與AVAudioPlayer類似蒋畜,你完全可以將它看成是一個(gè)錄音機(jī)控制類,下面是常用的屬性和方法:
AVAudioRecorder很多屬性和方法跟AVAudioPlayer都是類似的,但是它的創(chuàng)建有所不同撞叽,在創(chuàng)建錄音機(jī)時(shí)除了指定路徑外還必須指定錄音設(shè)置信息姻成,因?yàn)殇浺魴C(jī)必須知道錄音文件的格式、采樣率愿棋、通道數(shù)科展、每個(gè)采樣點(diǎn)的位數(shù)等信息,但是也并不是所有的信息都必須設(shè)置糠雨,通常只需要幾個(gè)常用設(shè)置才睹。關(guān)于錄音設(shè)置詳見(jiàn)幫助文檔中的“AV Foundation Audio Settings Constants”。
下面就使用AVAudioRecorder創(chuàng)建一個(gè)錄音機(jī)甘邀,實(shí)現(xiàn)了錄音琅攘、暫停、停止松邪、播放等功能坞琴,實(shí)現(xiàn)效果大致如下:
在這個(gè)示例中將實(shí)行一個(gè)完整的錄音控制,包括錄音逗抑、暫停剧辐、恢復(fù)、停止邮府,同時(shí)還會(huì)實(shí)時(shí)展示用戶錄音的聲音波動(dòng)荧关,當(dāng)用戶點(diǎn)擊完停止按鈕還會(huì)自動(dòng)播放錄音文件。程序的構(gòu)建主要分為以下幾步:
1.設(shè)置音頻會(huì)話類型為AVAudioSessionCategoryPlayAndRecord褂傀,因?yàn)槌绦蛑袪砍兜戒浺艉筒シ挪僮鳌?br>
2.創(chuàng)建錄音機(jī)AVAudioRecorder忍啤,指定錄音保存的路徑并且設(shè)置錄音屬性,注意對(duì)于一般的錄音文件要求的采樣率紊服、位數(shù)并不高檀轨,需要適當(dāng)設(shè)置以保證錄音文件的大小和效果胸竞。
3.設(shè)置錄音機(jī)代理以便在錄音完成后播放錄音,打開(kāi)錄音測(cè)量保證能夠?qū)崟r(shí)獲得錄音時(shí)的聲音強(qiáng)度参萄。(注意聲音強(qiáng)度范圍-160到0,0代表最大輸入)
4.創(chuàng)建音頻播放器AVAudioPlayer卫枝,用于在錄音完成之后播放錄音。
5.創(chuàng)建一個(gè)定時(shí)器以便實(shí)時(shí)刷新錄音測(cè)量值并更新錄音強(qiáng)度到UIProgressView中顯示讹挎。
6.添加錄音校赤、暫停、恢復(fù)筒溃、停止操作马篮,需要注意錄音的恢復(fù)操作其實(shí)是有音頻會(huì)話管理的,恢復(fù)時(shí)只要再次調(diào)用record方法即可怜奖,無(wú)需手動(dòng)管理恢復(fù)時(shí)間等浑测。
下面是主要代碼:
//
// ViewController.m
// AVAudioRecorder
//
// Created by Kenshin Cui on 14/03/30.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#define kRecordAudioFile @"myRecord.caf"
@interface ViewController ()<AVAudioRecorderDelegate>
@property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音頻錄音機(jī)
@property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音頻播放器,用于播放錄音文件
@property (nonatomic,strong) NSTimer *timer;//錄音聲波監(jiān)控(注意這里暫時(shí)不對(duì)播放進(jìn)行監(jiān)控)
@property (weak, nonatomic) IBOutlet UIButton *record;//開(kāi)始錄音
@property (weak, nonatomic) IBOutlet UIButton *pause;//暫停錄音
@property (weak, nonatomic) IBOutlet UIButton *resume;//恢復(fù)錄音
@property (weak, nonatomic) IBOutlet UIButton *stop;//停止錄音
@property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音頻波動(dòng)
@end
@implementation ViewController
#pragma mark - 控制器視圖方法
- (void)viewDidLoad {
[super viewDidLoad];
[self setAudioSession];
}
#pragma mark - 私有方法
/**
* 設(shè)置音頻會(huì)話
*/
-(void)setAudioSession{
AVAudioSession *audioSession=[AVAudioSession sharedInstance];
//設(shè)置為播放和錄音狀態(tài)歪玲,以便可以在錄制完之后播放錄音
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession setActive:YES error:nil];
}
/**
* 取得錄音文件保存路徑
*
* @return 錄音文件路徑
*/
-(NSURL *)getSavePath{
NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
urlStr=[urlStr stringByAppendingPathComponent:kRecordAudioFile];
NSLog(@"file path:%@",urlStr);
NSURL *url=[NSURL fileURLWithPath:urlStr];
return url;
}
/**
* 取得錄音文件設(shè)置
*
* @return 錄音設(shè)置
*/
-(NSDictionary *)getAudioSetting{
NSMutableDictionary *dicM=[NSMutableDictionary dictionary];
//設(shè)置錄音格式
[dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
//設(shè)置錄音采樣率迁央,8000是電話采樣率,對(duì)于一般錄音已經(jīng)夠了
[dicM setObject:@(8000) forKey:AVSampleRateKey];
//設(shè)置通道,這里采用單聲道
[dicM setObject:@(1) forKey:AVNumberOfChannelsKey];
//每個(gè)采樣點(diǎn)位數(shù),分為8滥崩、16岖圈、24、32
[dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
//是否使用浮點(diǎn)數(shù)采樣
[dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
//....其他設(shè)置等
return dicM;
}
/**
* 獲得錄音機(jī)對(duì)象
*
* @return 錄音機(jī)對(duì)象
*/
-(AVAudioRecorder *)audioRecorder{
if (!_audioRecorder) {
//創(chuàng)建錄音文件保存路徑
NSURL *url=[self getSavePath];
//創(chuàng)建錄音格式設(shè)置
NSDictionary *setting=[self getAudioSetting];
//創(chuàng)建錄音機(jī)
NSError *error=nil;
_audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];
_audioRecorder.delegate=self;
_audioRecorder.meteringEnabled=YES;//如果要監(jiān)控聲波則必須設(shè)置為YES
if (error) {
NSLog(@"創(chuàng)建錄音機(jī)對(duì)象時(shí)發(fā)生錯(cuò)誤钙皮,錯(cuò)誤信息:%@",error.localizedDescription);
return nil;
}
}
return _audioRecorder;
}
/**
* 創(chuàng)建播放器
*
* @return 播放器
*/
-(AVAudioPlayer *)audioPlayer{
if (!_audioPlayer) {
NSURL *url=[self getSavePath];
NSError *error=nil;
_audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
_audioPlayer.numberOfLoops=0;
[_audioPlayer prepareToPlay];
if (error) {
NSLog(@"創(chuàng)建播放器過(guò)程中發(fā)生錯(cuò)誤蜂科,錯(cuò)誤信息:%@",error.localizedDescription);
return nil;
}
}
return _audioPlayer;
}
/**
* 錄音聲波監(jiān)控定制器
*
* @return 定時(shí)器
*/
-(NSTimer *)timer{
if (!_timer) {
_timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES];
}
return _timer;
}
/**
* 錄音聲波狀態(tài)設(shè)置
*/
-(void)audioPowerChange{
[self.audioRecorder updateMeters];//更新測(cè)量值
float power= [self.audioRecorder averagePowerForChannel:0];//取得第一個(gè)通道的音頻,注意音頻強(qiáng)度范圍時(shí)-160到0
CGFloat progress=(1.0/160.0)*(power+160.0);
[self.audioPower setProgress:progress];
}
#pragma mark - UI事件
/**
* 點(diǎn)擊錄音按鈕
*
* @param sender 錄音按鈕
*/
- (IBAction)recordClick:(UIButton *)sender {
if (![self.audioRecorder isRecording]) {
[self.audioRecorder record];//首次使用應(yīng)用時(shí)如果調(diào)用record方法會(huì)詢問(wèn)用戶是否允許使用麥克風(fēng)
self.timer.fireDate=[NSDate distantPast];
}
}
/**
* 點(diǎn)擊暫定按鈕
*
* @param sender 暫停按鈕
*/
- (IBAction)pauseClick:(UIButton *)sender {
if ([self.audioRecorder isRecording]) {
[self.audioRecorder pause];
self.timer.fireDate=[NSDate distantFuture];
}
}
/**
* 點(diǎn)擊恢復(fù)按鈕
* 恢復(fù)錄音只需要再次調(diào)用record短条,AVAudioSession會(huì)幫助你記錄上次錄音位置并追加錄音
*
* @param sender 恢復(fù)按鈕
*/
- (IBAction)resumeClick:(UIButton *)sender {
[self recordClick:sender];
}
/**
* 點(diǎn)擊停止按鈕
*
* @param sender 停止按鈕
*/
- (IBAction)stopClick:(UIButton *)sender {
[self.audioRecorder stop];
self.timer.fireDate=[NSDate distantFuture];
self.audioPower.progress=0.0;
}
#pragma mark - 錄音機(jī)代理方法
/**
* 錄音完成导匣,錄音完成后播放錄音
*
* @param recorder 錄音機(jī)對(duì)象
* @param flag 是否成功
*/
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
if (![self.audioPlayer isPlaying]) {
[self.audioPlayer play];
}
NSLog(@"錄音完成!");
}
@end
運(yùn)行效果:
音頻隊(duì)列服務(wù)
大家應(yīng)該已經(jīng)注意到了,無(wú)論是前面的錄音還是音頻播放均不支持網(wǎng)絡(luò)流媒體播放慌烧,當(dāng)然對(duì)于錄音來(lái)說(shuō)這種需求可能不大逐抑,但是對(duì)于音頻播放來(lái)說(shuō)有時(shí)候就很有必要了。AVAudioPlayer只能播放本地文件屹蚊,并且是一次性加載所以音頻數(shù)據(jù),初始化AVAudioPlayer時(shí)指定的URL也只能是File URL而不能是HTTP URL进每。當(dāng)然汹粤,將音頻文件下載到本地然后再調(diào)用AVAudioPlayer來(lái)播放也是一種播放網(wǎng)絡(luò)音頻的辦法,但是這種方式最大的弊端就是必須等到整個(gè)音頻播放完成才能播放田晚,而不能使用流式播放嘱兼,這往往在實(shí)際開(kāi)發(fā)中是不切實(shí)際的。那么在iOS中如何播放網(wǎng)絡(luò)流媒體呢贤徒?就是使用AudioToolbox框架中的音頻隊(duì)列服務(wù)Audio Queue Services芹壕。
使用音頻隊(duì)列服務(wù)完全可以做到音頻播放和錄制汇四,首先看一下錄音音頻服務(wù)隊(duì)列:
一個(gè)音頻服務(wù)隊(duì)列Audio Queue有三部分組成:
三個(gè)緩沖器Buffers:每個(gè)緩沖器都是一個(gè)存儲(chǔ)音頻數(shù)據(jù)的臨時(shí)倉(cāng)庫(kù)。
一個(gè)緩沖隊(duì)列Buffer Queue:一個(gè)包含音頻緩沖器的有序隊(duì)列踢涌。
一個(gè)回調(diào)Callback:一個(gè)自定義的隊(duì)列回調(diào)函數(shù)通孽。
聲音通過(guò)輸入設(shè)備進(jìn)入緩沖隊(duì)列中,首先填充第一個(gè)緩沖器睁壁;當(dāng)?shù)谝粋€(gè)緩沖器填充滿之后自動(dòng)填充下一個(gè)緩沖器背苦,同時(shí)會(huì)調(diào)用回調(diào)函數(shù);在回調(diào)函數(shù)中需要將緩沖器中的音頻數(shù)據(jù)寫入磁盤潘明,同時(shí)將緩沖器放回到緩沖隊(duì)列中以便重用行剂。下面是Apple官方關(guān)于音頻隊(duì)列服務(wù)的流程示意圖:
類似的,看一下音頻播放緩沖隊(duì)列钳降,其組成部分和錄音緩沖隊(duì)列類似厚宰。
但是在音頻播放緩沖隊(duì)列中,回調(diào)函數(shù)調(diào)用的時(shí)機(jī)不同于音頻錄制緩沖隊(duì)列遂填,流程剛好相反铲觉。將音頻讀取到緩沖器中,一旦一個(gè)緩沖器填充滿之后就放到緩沖隊(duì)列中城菊,然后繼續(xù)填充其他緩沖器备燃;當(dāng)開(kāi)始播放時(shí),則從第一個(gè)緩沖器中讀取音頻進(jìn)行播放凌唬;一旦播放完之后就會(huì)觸發(fā)回調(diào)函數(shù)建车,開(kāi)始播放下一個(gè)緩沖器中的音頻,同時(shí)填充第一個(gè)緩沖器放游添;填充滿之后再次放回到緩沖隊(duì)列蹈胡。下面是詳細(xì)的流程:
當(dāng)然,要明白音頻隊(duì)列服務(wù)的原理并不難更耻,問(wèn)題是如何實(shí)現(xiàn)這個(gè)自定義的回調(diào)函數(shù)测垛,這其中我們有大量的工作要做,控制播放狀態(tài)秧均、處理異常中斷食侮、進(jìn)行音頻編碼等等。由于牽扯內(nèi)容過(guò)多目胡,而且不是本文目的锯七,如果以后有時(shí)間將另開(kāi)一篇文章重點(diǎn)介紹,目前有很多第三方優(yōu)秀框架可以直接使用誉己,例如AudioStreamer眉尸、FreeStreamer。由于前者當(dāng)前只有非ARC版本,所以下面不妨使用FreeStreamer來(lái)簡(jiǎn)單演示在線音頻播放的過(guò)程噪猾,當(dāng)然在使用之前要做如下準(zhǔn)備工作:
1.拷貝FreeStreamer中的Reachability.h霉祸、Reachability.m和Common、astreamer兩個(gè)文件夾中的內(nèi)容到項(xiàng)目中袱蜡。
2.添加FreeStreamer使用的類庫(kù):CFNetwork.framework丝蹭、AudioToolbox.framework、AVFoundation.framework戒劫、libxml2.dylib半夷、MediaPlayer.framework。
3.如果引用libxml2.dylib編譯不通過(guò)迅细,需要在Xcode的Targets-Build Settings-Header Build Path中添加$(SDKROOT)/usr/include/libxml2巫橄。
4.將FreeStreamer中的FreeStreamerMobile-Prefix.pch文件添加到項(xiàng)目中并將Targets-Build Settings-Precompile Prefix Header設(shè)置為YES,在Targets-Build Settings-Prefix Header設(shè)置為$(SRCROOT)/項(xiàng)目名稱/FreeStreamerMobile-Prefix.pch
(因?yàn)閄code6默認(rèn)沒(méi)有pch文件)
然后就可以編寫代碼播放網(wǎng)絡(luò)音頻了:
//
// ViewController.m
// AudioQueueServices
//
// Created by Kenshin Cui on 14/03/30.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
// 使用FreeStreamer實(shí)現(xiàn)網(wǎng)絡(luò)音頻播放
#import "ViewController.h"
#import "FSAudioStream.h"
@interface ViewController ()
@property (nonatomic,strong) FSAudioStream *audioStream;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.audioStream play];
}
/**
* 取得本地文件路徑
*
* @return 文件路徑
*/
-(NSURL *)getFileUrl{
NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"劉若英 - 原來(lái)你也在這里.mp3" ofType:nil];
NSURL *url=[NSURL fileURLWithPath:urlStr];
return url;
}
-(NSURL *)getNetworkUrl{
NSString *urlStr=@"http://192.168.1.102/liu.mp3";
NSURL *url=[NSURL URLWithString:urlStr];
return url;
}
/**
* 創(chuàng)建FSAudioStream對(duì)象
*
* @return FSAudioStream對(duì)象
*/
-(FSAudioStream *)audioStream{
if (!_audioStream) {
NSURL *url=[self getNetworkUrl];
//創(chuàng)建FSAudioStream對(duì)象
_audioStream=[[FSAudioStream alloc]initWithUrl:url];
_audioStream.onFailure=^(FSAudioStreamError error,NSString *description){
NSLog(@"播放過(guò)程中發(fā)生錯(cuò)誤茵典,錯(cuò)誤信息:%@",description);
};
_audioStream.onCompletion=^(){
NSLog(@"播放完成!");
};
[_audioStream setVolume:0.5];//設(shè)置聲音
}
return _audioStream;
}
@end
其實(shí)FreeStreamer的功能很強(qiáng)大湘换,不僅僅是播放本地、網(wǎng)絡(luò)音頻那么簡(jiǎn)單统阿,它還支持播放列表彩倚、檢查包內(nèi)容、RSS訂閱扶平、播放中斷等很多強(qiáng)大的功能帆离,甚至還包含了一個(gè)音頻分析器,有興趣的朋友可以訪問(wèn)官網(wǎng)查看詳細(xì)用法
原著網(wǎng)址:http://www.cnblogs.com/kenshincui/p/4186022.html#soundEffect