iOS學習筆記23-音效與音樂

一循签、音頻

在iOS中级乐,音頻播放從形式上可以分為音效播放和音樂播放。

  • 音效:
  • 主要指一些短音頻的播放县匠,這類音頻一般不需要進行進度风科、循環(huán)等控制。
  • 在iOS中乞旦,音效我們是使用AudioToolbox.framework框架實現(xiàn)贼穆。
  • 音樂:
  • 主要指一些較長的音頻,通常需要對播放進行精確控制兰粉。
  • 在iOS中故痊,音樂我們是使用AVFoundation.framework框架實現(xiàn)。

二玖姑、音效

AudioToolbox.framework框架是一套基于C語言的框架
它的實現(xiàn)原理是將短音頻注冊到系統(tǒng)聲音服務(wù)(SystemSoundService)

系統(tǒng)聲音服務(wù)的一些限制:
  1. 音頻播放時間不能超過30s
  1. 數(shù)據(jù)必須是PCM或者IMA4格式愕秫,現(xiàn)在的基本都符合
  2. 音頻文件必須是CAF慨菱、AIF、WAV的一種戴甩,實際上有些MP3格式也可以播放符喝。
使用步驟:
  1. 導入AudioToolbox.framework框架,添加頭文件:
#import <AudioToolbox/AudioToolbox.h>
  1. 獲取音效文件的路徑URL
  2. 添加進系統(tǒng)聲音服務(wù)等恐,獲得系統(tǒng)聲音ID洲劣,ID是區(qū)分不同音效的唯一標示
void AudioServicesCreateSystemSoundID(
          CFURLRef inFileURL,    /* 音效文件URL,需要把NSURL橋接成CGURLRef */
          SystemSoundID *outSystemSoundID /* 返回音效唯一標示ID */
);
  1. 如果需要監(jiān)聽音效播放完成课蔬,需要綁定回調(diào)函數(shù)
void AudioServicesAddSystemSoundCompletion(  
        SystemSoundID inSystemSoundID, /* 音效ID */
        CFRunLoopRef inRunLoop, /* 所在循環(huán)囱稽,一般為NULL */
        CFStringRef inRunLoopMode,/* 循環(huán)模式,一般為NULL */
        void (*)(SystemSoundID,void*) inCompletionRoutine,/* 回調(diào)C語言函數(shù)指針 */
        void *inClientData /* 回調(diào)函數(shù)的參數(shù) */
);
  1. 開始播放二跋,有兩種播放模式:
/* 開始播放音效 */
void AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID);
/* 開始播放音效并帶震動 */
void AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID);
下面是使用實例:
- (void)viewDidLoad{
    [super viewDidLoad];
    [self playSoundEffect:@"bellTone.wav"];
}
#pragma mark -音效
/* 音效播放完成的回調(diào)函數(shù)战惊,這個是C語言函數(shù),第一個參數(shù)是音效ID扎即,第二個是萬能參數(shù) */
void soundCompleteCallBack(SystemSoundID soundID, void *clientData)
{
    NSLog(@"播放完成");
}
- (void)playSoundEffect:(NSString *)name {
    //獲取音效文件路徑
    NSString *filePath = [[NSBundle mainBundle] pathForResource:name ofType:nil];
    //創(chuàng)建音效文件URL
    NSURL *fileUrl = [NSURL URLWithString:filePath];
    //音效聲音的唯一標示ID
    SystemSoundID soundID = 0;
    //將音效加入到系統(tǒng)音效服務(wù)中吞获,NSURL需要橋接成CFURLRef,會返回一個長整形ID谚鄙,用來做音效的唯一標示
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
    //設(shè)置音效播放完成后的回調(diào)C語言函數(shù)
    AudioServicesAddSystemSoundCompletion(soundID,NULL,NULL,soundCompleteCallBack,NULL);
    //開始播放音效
    AudioServicesPlaySystemSound(soundID);
}

三各拷、音樂

如果播放較大的音頻,需要對其進行精確控制闷营,我們需要使用到另外一個框架烤黍,即:
AVFoundation.framework框架,它支持多種音頻格式傻盟,可以進行精確控制速蕊。
音樂播放功能我們使用到的是AVFoundation.frameworkAVAudioPlayer音樂播放器來實現(xiàn)。

下面有一些AVAudioPlayer類的常用屬性:
@property (readonly, getter=isPlaying) BOOL playing;//是否正在播放
@property NSTimeInterval currentTime;//當前已經(jīng)播放的時間
@property (readonly) NSTimeInterval duration;//播放的總時間
@property float volume;//音量大小
@property float rate;//播放速率娘赴,默認為1.0
@property (readonly) NSURL *url;//音樂文件的URL
下面是常用的對象方法:
/* 初始化方法 */
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;
- (BOOL)prepareToPlay;/* 把音樂文件加載到緩存區(qū) */
- (BOOL)play;/* 開始播放音樂 */
- (BOOL)playAtTime:(NSTimeInterval)time;/* 在指定時間開始播放音樂 */
- (void)pause;/* 中斷音樂规哲,可以通過調(diào)用play方法繼續(xù)播放 */
- (void)stop;/* 終止音樂,無法繼續(xù)播放 */
常用的代理方法:
/* 播放完成時調(diào)用 */
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
/* 音頻解碼發(fā)生錯誤時調(diào)用 */
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error;
使用步驟:
  1. 導入AVFoundation.framework框架诽表,添加頭文件:
#import <AVFoundation/AVFoundation.h>
  1. 獲取音樂文件的本地文件路徑URL唉锌,不能使用網(wǎng)絡(luò)URL
  2. 創(chuàng)建音樂播放器對象AVAudioPlayer,并設(shè)置屬性和代理竿奏。
  3. 調(diào)用perpareToPlay方法加載音樂文件到緩沖區(qū)
  4. 調(diào)用play方法播放袄简,調(diào)用pause中斷播放
  5. 實現(xiàn)AVAudioPlayerDelegate方法,監(jiān)聽播放完成议双。
我們來做個簡易的音樂播放器吧:
  1. 可以循環(huán)播放音樂
  1. 可以中斷音樂
  2. 可以切換不同的音樂
  3. 可以知道播放進度
下面是具體的簡易音樂播放器項目代碼:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController () <AVAudioPlayerDelegate>

@property (nonatomic, strong) NSArray *musicArray;/*< 音樂列表 */
@property (nonatomic, strong) IBOutlet UIProgressView *progress;/*< 進度條 */
@property (nonatomic, strong) AVAudioPlayer *audioPlayer;/*< 音樂播放器對象 */
@property (nonatomic, strong) NSTimer *timer;/* 定時器 */
@property (nonatomic, strong) IBOutlet UILabel *titleLabel;/* 顯示標題 */
@property (nonatomic, assign) NSInteger selectMusic;/* 當前播放音樂的索引 */

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //音樂文件列表
    self.musicArray = @[@"大夢想家",@"江南",@"酒干倘賣無",
                        @"泡沫",@"夏洛特煩惱",@"演員"];
    //創(chuàng)建定時器痘番,用于不斷更新進度條
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 
                                                  target:self 
                                                selector:@selector(updataProgress) 
                                                userInfo:nil 
                                                 repeats:YES];
    //創(chuàng)建播放器
    [self initAudioPlayerWithNumber:0];
    //播放器開始播放音樂
    [self.audioPlayer play];
}
/* 初始化播放器 */
- (void)initAudioPlayerWithNumber:(NSInteger)num{
    if (num >= self.musicArray.count) {
        return;
    }
    //獲取音樂文件路徑
    NSString *name = self.musicArray[num];
    NSString *filePath = [[NSBundle mainBundle] pathForResource:name 
                                                         ofType:@"mp3"];
    //創(chuàng)建音樂文件URL捉片,必須是fileURLWithPath平痰,AVAudioPlayer不支持HTTP的URL
    NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
    //創(chuàng)建播放器
    NSError *error = nil;
    AVAudioPlayer *audioPlayer =
            [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
    if (error) {
        NSLog(@"初始化播放器錯誤,錯誤:%@",error.localizedDescription);
        return;
    }
    audioPlayer.numberOfLoops = 0;//設(shè)置循環(huán)次數(shù)汞舱,0表示不循環(huán)
    audioPlayer.delegate = self;//設(shè)置代理
    [audioPlayer prepareToPlay];//加載音樂文件到緩存,還不會播放
    
    self.selectMusic = num;//保存當前的音樂索引
    self.audioPlayer = audioPlayer;//保存當前播放器
    self.titleLabel.text = name;//設(shè)置標題
}
/* 進度條更新方法宗雇,每0.5秒更新一次 */
- (void)updataProgress{
    //currentTime是當前播放時間昂芜,duration是播放總時長
    CGFloat progress = self.audioPlayer.currentTime / self.audioPlayer.duration;
    [self.progress setProgress:progress animated:YES];
}
/* 點擊播放按鈕 */
- (IBAction)musicPlay:(id)sender {
    //如果播放器沒有在播放,才需要調(diào)用播放方法
    if (![self.audioPlayer isPlaying]) {
        [self.audioPlayer play];
        //恢復定時器
        self.timer.fireDate = [NSDate distantPast];
    }
}
/* 點擊暫停按鈕 */
- (IBAction)musicPause:(id)sender {
    //如果播放器在播放赔蒲,才需要調(diào)用暫停方法
    if ([self.audioPlayer isPlaying]) {
        [self.audioPlayer pause];
        //停止定時器
        self.timer.fireDate = [NSDate distantFuture];
    }
}
/* 點擊下一首歌按鈕 */
- (IBAction)next:(id)sender {
    //音樂索引循環(huán)遞增
    self.selectMusic = (self.selectMusic + 1)%self.musicArray.count;
    //重新設(shè)置播放器
    [self initAudioPlayerWithNumber:self.selectMusic];
    //播放器播放
    [self.audioPlayer play];
}
#pragma mark - AVAudioPlayerDelegate代理方法
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    //自動跳轉(zhuǎn)播放下一首歌
    [self next:nil];
}
效果圖

(⊙﹏⊙)b泌神,也是夠簡陋的,你可以做精美一點舞虱,比如如果每首歌對應(yīng)一個圖片欢际,到時候切歌的時候,圖片顯示在背景矾兜,一定賞心悅目多了损趋,O(∩_∩)O~,我沒有找到好的圖片椅寺,就這樣吧浑槽。

四、后臺播放音樂

事實上上面的播放器有一點問題返帕,那就是一旦進入后臺桐玻,我們的音樂器就自動暫停了,不可以/(ㄒoㄒ)/~~荆萤!那該怎么辦呢镊靴?

1. 設(shè)置后臺運行模式

設(shè)置info.plist文件,添加字段Required background modes观腊,
并設(shè)置App plays audio or streams audio/video using AirPlay

2. 使用音頻會話邑闲,設(shè)置后臺運行類型
//獲取音頻會話單例對象
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
//設(shè)置會話為后臺播放
[audioSession setCategory:AVAudioSessionCategoryPlayback error:NULL];
//激活修改,啟動會話
[audioSession setActive:YES error:NULL];

音頻會話除了后臺播放外梧油,還可以有以下的播放模式:


音頻會話播放類型列表
上面的代碼只需修改初始化播放器方法即可實現(xiàn)后臺播放:
/* 初始化播放器 */
- (void)initAudioPlayerWithNumber:(NSInteger)num{
    if (num >= self.musicArray.count) {
        return;
    }
    //獲取音樂文件路徑
    NSString *name = self.musicArray[num];
    NSString *filePath = [[NSBundle mainBundle] pathForResource:name 
                                                         ofType:@"mp3"];
    //創(chuàng)建音樂文件URL苫耸,必須是fileURLWithPath,AVAudioPlayer不支持HTTP的URL
    NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
    //創(chuàng)建播放器
    NSError *error = nil;
    AVAudioPlayer *audioPlayer =
            [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
    if (error) {
        NSLog(@"初始化播放器錯誤,錯誤:%@",error.localizedDescription);
        return;
    }
    audioPlayer.numberOfLoops = 0;//設(shè)置循環(huán)次數(shù)儡陨,0表示不循環(huán)
    audioPlayer.delegate = self;//設(shè)置代理
    [audioPlayer prepareToPlay];//加載音樂文件到緩存褪子,還不會播放
    
    //獲取音頻會話單例對象
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    //設(shè)置會話為后臺播放
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:NULL];
    //激活修改,啟動會話
    [audioSession setActive:YES error:NULL];

    self.selectMusic = num;//保存當前的音樂索引
    self.audioPlayer = audioPlayer;//保存當前播放器
    self.titleLabel.text = name;//設(shè)置標題
}

這樣骗村,我們就可以后臺播放了嫌褪,(o)/~。

有什么問題可以在下方評論區(qū)出去胚股,好意見我會采納的笼痛,打造一個大家都滿意的學習筆記,O(∩_∩)O哈!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缨伊,一起剝皮案震驚了整個濱河市摘刑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刻坊,老刑警劉巖枷恕,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谭胚,居然都是意外死亡徐块,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門灾而,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胡控,“玉大人,你說我怎么就攤上這事旁趟⊥” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵轻庆,是天一觀的道長癣猾。 經(jīng)常有香客問我,道長余爆,這世上最難降的妖魔是什么纷宇? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蛾方,結(jié)果婚禮上像捶,老公的妹妹穿的比我還像新娘。我一直安慰自己桩砰,他們只是感情好拓春,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著亚隅,像睡著了一般硼莽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上煮纵,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天懂鸵,我揣著相機與錄音,去河邊找鬼行疏。 笑死匆光,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的酿联。 我是一名探鬼主播终息,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼夺巩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了周崭?” 一聲冷哼從身側(cè)響起劲够,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎休傍,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹲姐,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡磨取,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了柴墩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忙厌。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖江咳,靈堂內(nèi)的尸體忽然破棺而出逢净,到底是詐尸還是另有隱情,我是刑警寧澤歼指,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布爹土,位于F島的核電站,受9級特大地震影響踩身,放射性物質(zhì)發(fā)生泄漏胀茵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一挟阻、第九天 我趴在偏房一處隱蔽的房頂上張望琼娘。 院中可真熱鬧,春花似錦附鸽、人聲如沸脱拼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熄浓。三九已至,卻和暖如春省撑,著一層夾襖步出監(jiān)牢的瞬間玉组,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工丁侄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惯雳,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓鸿摇,卻偏偏與公主長得像石景,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容