en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

1.04.2013

使用 AVAudioPlayer 來播放音樂

AVAudioPlayer 是在 iOS 裝置中用來播放音樂的一個重要類別,它被包含在 AVFoundation.framework 套件中,透過 AVAudioPlayer 我們除了可以播放音樂之外,還可以取得正在播放的音軌位置、聲音大小或是聲道數目等等,因此,它常被使用來製作音樂撥放器等相關的應用程式。如果你想尋找的是播放單純的「音效」而非「音樂」,請參考 Core Audio 的 System Sound 音效播放一文。

環境設定
既然 AVAudioPlayer 是包含在 AVFoundation.framework 之中,在使用上就別忘了把函式庫和 Framework 加到專案中,對於新增 Framework 有問題的讀者們,請參考 Xcode 4 新增 Framework 的方法一文。
#import <AVFoundation/AVAudioPlayer.h>
替專案增加 AVFoundation.framework

接著在實作類別中的 @interface 區段中,建立一個 AVAudioPlayer 型態的全域變數,方便稍後用它來實作播放音樂的總總功能。
AVAudioPlayer *myPlayer;

ps:在類別的 import 上,你可以選擇只匯入關鍵的 AVAudioPlayer.h(如上述的程式碼),或是直接使用 AVFoundation/AVFoundation.h 將整個 AVFoundation.framework 中所有的套件都一次匯入。


載入音樂
在這個部分,我們要將先前宣告的 myPlayer 全域變數與音樂檔案做連結,當然,如果你的音樂檔案不再本機端而是存放於網路,你也可以透過連結網路位置的方式來播放音樂檔案(並非音樂串流)。
//檔案存放於本機
//取得播放檔案的位置
NSURL* url = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:@"FileName" ofType:@"mp3"]];

//與音樂檔案做連結
NSError* error = nil;
myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];

if (!url || error) {
    //錯誤處理常式
}

//檔案存放於網路
//取得播放檔案的位置
NSURL *url = [NSURL URLWithString:@"http://FileName.mp3"];
NSData *myNetworkData = [NSData dataWithContentsOfURL:url];

//與音樂檔案做連結
NSError* error = nil;
myPlayer = [[AVAudioPlayer alloc] initWithData:myNetworkData error:&error];

if (!url || error) {
    //錯誤處理常式
}


控制項目與檔案資訊
AVAudioPlayer 的控制項目包含「播放」、「暫停」、「跳轉」與「音量控制」等等,另外在資訊的部份像是「音軌總長度」、「目前播放位置」或是「聲道數目」等等都可以透過 AVAudioPlayer 來取得檔案的相關資訊。
//設定音量0.0~1.0
[myPlayer setVolume:0.5];

//設定播放次數-1為無限循環
[myPlayer setNumberOfLoops:0];

//設定播放位置(重頭播放)
[myPlayer setCurrentTime:0.0];

//播放
[myPlayer play];

//暫停
[myPlayer pause];

//停止
[myPlayer stop];

//相關資訊
NSLog(@"音樂總長度 %f", [myPlayer duration]);
NSLog(@"目前播放位置 %f", [myPlayer currentTime]);
NSLog(@"聲道數目 %d", [myPlayer numberOfChannels]);

你可以自行組合上述這些控制項目來實作簡單的控制應用,像是暫停/播放或是往後快轉數秒的按鈕等等。
//暫停/播放
if ([myPlayer isPlaying]) {
    [myPlayer pause];
} else {
    [myPlayer play];
}

//往後跳轉1秒
NSTimeInterval currentTime = [myPlayer currentTime];
[myPlayer setCurrentTime:currentTime + 1];


AVAudioPlayerDelegate 協定
在 AVAudioPlayer 的類別中的 AVAudioPlayerDelegate 協定,可以幫助我們捕捉音樂在播放完成時的那個瞬間,也就是當音樂播放完成時(不論成功與否)都會觸發此協定中所定義的方法函式。
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    NSLog(@"播放完畢");
}

另外,在 AVAudioPlayerDelegate 協定之中,還有兩個比較常用的方法函式,像是當電話來時,音樂被強迫中斷,或是當結束通話時恢復音樂播放的瞬間,我們都可以透過下列兩個方法函式來捕捉(需使用實體裝置執行時才會生效)。
//電話中斷
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
    NSLog(@"被中斷");
}

- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags {
    NSLog(@"從中斷中恢復");
}

在使用上,你必須先替實作的類別的 @interface 區段上加上 <AVAudioPlayerDelegate>,並且在程式碼中使用 setDelegate 來決定要實作的位置,關於協定的相關資訊請參考Protocol 協定的使用方式一文。


背景執行(模擬器無法使用)
想要讓音樂能夠在背景執行,必須要具備 iOS 4.0 以上的條件,你可以參考偵測 Device 是否支援多工或背景執行的方法一文,來判斷目前的裝置是否能夠支援此項功能。

在實作上,首先必須先到專案的 info.plist 中增加背景執行的權限 Required background modes,並設定執行的類別為 App plays audio,如下圖。

替專案增加背景執行的權限

接著,替專案的 AVAudioSession 做以下設定(在此必須記得 import AVAudioSession.h,如果你先前已經將整個 AVFoundation.h 都匯入,則可以省略此步驟)。
NSError* error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (error) {
    //錯誤處理常式
}

error = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if (error) {
    //錯誤處理常式
}

你可以在任何地方安插上述程式碼,但是,必須確保此程式碼在應用程式進入背景作業之前就會被執行到,另外,你可以參考使用 Remote Control 來控制退至背景播放的音樂一文,來控制背景執行中的音樂。


ps:其他有關 AVAudioPlayer 類別的相關資訊,請參考 iOS Developer Library 官方網站。





35 則留言:

  1. 請問如果是把音樂存在app的Document裡面
    有辦法播放嗎?

    回覆刪除
  2. 可以阿~~只要有檔案路徑就可以播放

    記得將路徑替換成 Document 的預設位置。

    回覆刪除
  3. 請問一下我應該怎麼做呢?

    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *musicString = [documentPaths objectAtIndex:0];

    NSString *urlString = [NSString stringWithFormat:@"%@/到不了的幸福.mp3",musicString];

    NSURL *url = [[NSURL alloc]initFileURLWithPath:urlString];

    其他的地方都沒變
    可是好像沒有辦法播耶...
    請問可以幫忙我一下嗎 感謝

    回覆刪除
  4. 你好 可以播了 原來是路徑設定錯了...
    謝謝你
    另外在順便請問一下
    有辦法等歌播完的時候直接跳下一首嗎?

    回覆刪除
    回覆
    1. 直接跳下一首唷,這我就真的沒研究了~~不過應該有會內建方的方法函式可以知道音樂或是音效是否「播放完畢」,你可以利用這個方法函式來播放你的歌曲,當然,你必須把所有要播放的歌曲建立一個播放清單,它的形態可能會是一個陣列,裡面存放著不同歌曲的路徑,大概就是這樣,希望有幫到你的忙。

      刪除
    2. -(void)audioPlayerDidFinishPlaying

      刪除
    3. wow~
      感謝你替我解答!

      刪除
  5. 請問可以直接播放網路上的音檔嗎

    回覆刪除
    回覆
    1. 如果是有支援的格式應該都沒問題,您可以試試看 NSURL *url = [NSURL URLWithString:@"網路位置"]

      刪除
  6. 你好 在請教一個問題
    如果我想要再退出app的時候 還是有播放音樂的效果 (就是點home鍵兩下可以直接控制音樂那樣)
    請問AVAudioPlayer做的到嗎 還是有別的framework可以處理
    謝謝~~

    回覆刪除
    回覆
    1. 抱歉,關於這個我不是很清楚耶 「home鍵兩下可以直接控制音樂」這應該是 ipod app 的功能

      至於您所說的退出 app 還可以聽到音樂,這屬於「背景執行」方面,你可以在 info.plist 內設定 Application does not run in background ,如果你想要判斷你的裝置是否支援背景執行,可以參考「偵測 Device 是否支援多工或背景執行的方法」一文。

      再來回答您的問題 AVAudioPlayer 的確可以讓你的音樂背景執行,但是我不確定是否可以按兩下home叫出使用者介面,因為這應該是屬於ipod應用程式的,要看apple有沒有釋出這類的framework。

      刪除
  7. 請問一下AVAudioPlayer要背景播音樂要怎麼做呢?
    只要設定Application does not run in background 就好了嗎?
    我設定了YES 跟 NO 好像都沒有反應
    是不是還需要寫程式碼才能達成
    感謝~

    回覆刪除
    回覆
    1. 應該預設都是會背景執行,我可以推薦你列下列的官方文件,對於背景執行和多工有比較多的說明

      http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW3

      另外這是我之前寫的,確定你的app是在對應的選項內

      有些應用程式即使在退到背景之後仍然可以繼續執行,例如電話、導航或是音樂等,這些程式並不會進入先前所說的暫停狀態,反而能與其他應用程式同步執行,如果要製作此類的應用程式可以參考 iOS 官方文件的 UIBackgroundModes,並從 Info.plist 中的 UIBackgroundModes 設定,參數形態為字串陣列的 audio、voip、location 參數值。

      刪除
  8. 匿名1/02/2013

    你好,我想請問當按下HOME鍵跳出,在切入回程式裡時可否是音樂重新播放?

    回覆刪除
    回覆
    1. 您好:

      按下home之後再跳回來,音樂會接下去播放,如果你想要重新播放(重0:00的位置播放),你可以利用以下方法函式
      - (void)applicationWillEnterForeground:(UIApplication *)application

      將時間軸設置在0:00的位置,不過這會簽撤到背景作業,你可以前往官方網站了解背景作業的相關說明。
      http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW3

      刪除
  9. 您好~ 我建立了以下的程式碼 編譯也沒有問題 但是就是無法在模擬器上 播放音樂
    我把 mp3 import 到 Xcode 中的 supporting files 的資料夾~ 這樣會有問題嗎?
    我有參考上述的設定 ”Required background modes“ 設定完成 仍沒辦法播放~ 希望老師能指點迷津 感謝

    NSString *musicFilePath = [[NSBundle mainBundle] pathForResource:@"kids" ofType:@"mp3"];
    NSURL *musicURL = [[NSURL alloc] initFileURLWithPath:musicFilePath];
    AVAudioPlayer *thePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil];

    [thePlayer prepareToPlay];
    [thePlayer setVolume:1];
    thePlayer.numberOfLoops = -1;
    [thePlayer play];

    回覆刪除
    回覆
    1. 你好:
      執行上都沒有任何問題?試試看給他一個不存在的檔案名稱,或是網路位置,會不會也是相同結果。
      另外,檔案放置的位置沒有錯,而Required background modes只是背景執行的功能而已,就算不設定此參數也應該要能夠播放。

      刪除
  10. 你好,我按Home鍵切換到背景下,音樂是有繼續播放,但此時電話來了,音樂就中斷,通話結束也沒有繼續播放
    我有加下面這段(不過只在前景有效...............請問有解嗎??????)
    - (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags {
    NSLog(@"繼續播放"); [music play];
    }

    回覆刪除
    回覆
    1. 您好:

      電話來音樂中斷應該也會觸發一個涵式,你要先暫停你的音樂。

      由於你的部分已經涉及到背景處理,如果Remote Control無法解決你的問題,你可以要尋找背景中斷的相關議題。


      你可以參考,iOS 應用程式狀態的變遷一文找出可能執行到的涵式
      http://furnacedigital.blogspot.tw/2013/07/ios.html

      不過還是建議你在做此動作之前,先使用中斷點確定程式並沒有跑到您預想的位置。

      刪除
  11. 匿名8/13/2015

    你好
    我需要用到網路URL 所以使用了AVPLAYER

    我想請問
    他是否也適用
    - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {}
    若否
    可以請問要如何讓AVPLAYER自動播放下一首
    或者是否有類似的協定可以用來知道已播放完畢

    回覆刪除
    回覆
    1. 你好:
      如果是在AVPLAYER中播放的,都適用唷!

      刪除
    2. 匿名8/14/2015

      (已有在@interface 區段上加上AVAudioPlayerDelegate 協定 )

      刪除
    3. 匿名8/14/2015

      你好 想在跟你確認一 下
      我是用AVPlayer 不是用AVAudioPlayer
      是可以正常播放
      就是不能進
      - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {}

      刪除
    4. 您好

      要把 delegate指向你實作的controller喔,如果按照上面寫法,要寫成 myPlayer.delegate = self,你新實作的函式才會生效

      刪除
    5. 匿名8/14/2015

      你好
      已剛試過
      只是AVPlayer類別的好像沒有delegate可以設
      我知道AVAudioPlayer可以設 但是AVPlayer似乎不行

      刪除
    6. 您好:

      我誤會你意思了 如果你是用 AVPlayer 的確是沒有這些東西,
      參考AVPlayer官方文件「https://developer.apple.com/library/ios//documentation/AVFoundation/Reference/AVPlayer_Class/index.html」
      你可以使用 AVPlayerItemDidPlayToEndTimeNotification 來觸發播放結束時的函式,幫你找了一個NSNotificationCenter的範例你參考看
      http://stackoverflow.com/questions/27805657/swift-avplayer-how-to-track-when-song-finished-playing

      刪除
    7. 匿名8/27/2015

      好喔
      非常感謝
      已解決問題了 ><

      刪除
    8. 恭喜你嘿!

      刪除
    9. 匿名8/27/2015

      你好
      其實建議可以介紹一下observer
      我有看過這邊關於notification那篇
      只是那個只能主動用程式觸發
      後來又去APPLE官網找了一下資料
      發現observer其實很好用 (值有變可以被動觸發)
      這樣一主動一被動應該會比較完整 ><

      刪除
    10. 好的,感謝您的建議!

      刪除
  12. 您好
    請問如果我想用AVAudioPlayer播放本機現有的音樂
    要如何搜尋音樂檔案?

    回覆刪除
    回覆
    1. 你好

      你要搜尋整個裝置的檔案 判斷副檔名 才能確定是不是可以播放的檔案

      刪除
    2. 感謝您的回覆
      我用MPMeiaQuery把抓到的路徑給AVAudioPlayer就可以播了
      不用再確認副檔名

      刪除
    3. 你太客氣了!沒幫上什麼忙

      刪除