[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用)

(2019/01/24 更新 Xcode 10.1 + Swift 4.2)

先日の記事
[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(MPMusicPlayerController使用) | nackpan Blog
[iOS][Swift]MPMediaQueryを使って曲を絞り込む | nackpan Blog
では、MPMusicPlayerControllerをあつかいましたが、今回は、AVAudioPlayerを使ってミュージックライブラリにアクセスする方法について記したいと思います。

AVAudioPlayer
AVAudioPlayer Class Reference

サンプルの概要

今回使用するサンプルプログラムでは
* メディアアイテムピッカーから一曲選択
* プレイヤーで再生、停止、一時停止
* 再生中にアイテムのタイトルを表示
を行います。

ユーザーデータにアクセスすることを明示する

iOS 10以降では、ユーザーデータにアクセスする際に、許可を得ることが必要になりました。
そのさいのメッセージをあらかじめ登録しておかないと強制終了してしまいます。

具体的な手順については、こちらの記事を参照してください。
[iOS 10] 各種ユーザーデータへアクセスする目的を記述することが必須になるようです | DevelopersIO

iOS10から必要になったユーザデータにアクセスする為に表示する文言をローカライズする – Qiita

プレイヤーにアイテムをセットする

ミュージックライブラリのオーディオアイテムはMPMediaItemクラスであらわされています
MPMediaItem Class Reference
アイテムのURL(assetURL)、タイトル、アルバム名、アーティスト名、時間などのmetadataをもっています

MPMusicPlayerControllerの場合は、MPMediaItemを複数集めてあつかうMPMediaItemCollectionをセットしました。あるいは、MPMediaQueryをセットしました。(MPMusicPlayerControllerでは、複数の曲目をセットできます。)

        
    // MPMediaItemCollectionをplayerにセットしてキューを作成
    player.setQueue(with: mediaItemCollection)
    
    // あるいは、MPMediaQueryをセットしてキューを作成
    player.setQueue(with: query)

AVAudioPlayerの場合は、オーディオアイテムのURLもしくはDataをセットしてinitializeします。
ミュージックライブラリアイテムを扱う場合、AVAudioPlayerのinitializeに使うURLはMPMediaItemのassetURLになります

        
    // (itemはMPMediaItemを示しています)
    if let url: NSURL = item.assetURL {
        do {
            // itemのassetURLからプレイヤーを作成する
            audioPlayer = try AVAudioPlayer(contentsOf: url)
        } catch  {
            // エラー発生してプレイヤー作成失敗
            audioPlayer = nil
        }
    } else {
        // urlはnilだった
        
        // プレイヤーはnilとしておく
        audioPlayer = nil
    }

適切なURLでなかった場合は、プレイヤーの作成は失敗してnilを返します。
また、AVAudioPlayerのinitializeにはNSURL型が必要です。
(クラウド上のアイテムやApple Musicから「マイミュージックに追加」したアイテムのassetURLは、nilを返します。
なので再生できません)

AVAudioPlayerを使って、ミュージックライブラリのアイテムにアクセスする

ViewController.swift全文は記事の末尾にもあります。

iOSシミュレータでは動作しないので、実機を用いてください。

Single View Applicationでプロジェクトを作成。
ss 2015-09-16 6.46.53

ViewController.SwiftにMediaPlayerフレームワーク、AVFoundationフレームワークをimportします。

import UIKit
import MediaPlayer
import AVFoundation


class ViewController: UIViewController {

ボタンを配置。
ss 2015-09-13 9.08.24

ボタンとViewController.swiftをactionを作成して接続。
ss 2015-09-13 9.12.24
ss 2015-09-13 9.09.50

AVAudioPlayerでは、クラウドアイテムやApple Musicで「マイミュージックに追加」したアイテムなどはurlを取得するとnilを返すので再生できません。
アイテムのタイトル表示、またurlがnilだった場合にはそれを示すメッセージラベルをひとつ追加しました。
ss 2015-09-19 14.05.11

このラベルとViewController.swiftをoutletで接続します。
ss 2015-09-19 14.06.18-2

プレイヤーを表すpropertyをViewController.swiftに加えます。

class ViewController: UIViewController {

    var audioPlayer:AVAudioPlayer?
    
    override func viewDidLoad() {

AVAuioPlayerは、play()やpause()などメソッドを実行する際に、urlを指定してinitializeしていないとエラーになります。
そこで、今回はAVAudioPlayer?型として宣言して初期値をnilとしています。
そしてaudioPlayerがnilの場合は、play()やpause()などを実行しないようにしています。

曲を選択するために、メディアアイテムピッカーを用います。
(メディアアイテムピッカーというのは、iOSで用意されているあらかじめ設定済みのViewController。ミュージックライブラリの選択画面と同じようなことが出来る)
ss 2015-09-13 11 41 39

メディアアイテムピッカーでの「選択完了したとき」や「キャンセルされたとき」のイベントを、ViewControllerで受け取れるようにします。
そのために、ViewControllerをメディアアイテムピッカーのデリゲートとして設定します。
「選曲」ボタンを押すと、メディアアイテムピッカーを作成して、デリゲートの設定を行い、ライブラリの曲を選択できるようにします。
「選択完了したとき」「キャンセルされたとき」のメソッドを記述します。

import UIKit
import MediaPlayer
import AVFoundation

class ViewController: UIViewController, MPMediaPickerControllerDelegate {
    
    @IBAction func pick(sender: AnyObject) {
        // MPMediaPickerControllerのインスタンスを作成
        let picker = MPMediaPickerController()
        // ピッカーのデリゲートを設定
        picker.delegate = self
        // 複数選択を不可にする。(trueにすると、複数選択できる)
        picker.allowsPickingMultipleItems = false
        // ピッカーを表示する
        present(picker, animated: true, completion: nil)
        
    }
    
    // メディアアイテムピッカーでアイテムを選択完了したときに呼び出される
    func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
        // このfunctionを抜ける際にピッカーを閉じ、破棄する
        // (defer文はfunctionを抜ける際に実行される)
        defer {
            dismiss(animated: true, completion: nil)
        }

        // 選択した曲情報がmediaItemCollectionに入っている
        // mediaItemCollection.itemsから入っているMPMediaItemの配列を取得できる
        let items = mediaItemCollection.items
        if items.isEmpty {
            // itemが一つもなかったので戻る
            return
        }
        
        // 先頭のMPMediaItemを取得し、そのassetURLからプレイヤーを作成する
        let item = items[0]
        if let url: NSURL = item.assetURL {
            do {
                // itemのassetURLからプレイヤーを作成する
                audioPlayer = try AVAudioPlayer(contentsOf: url)
            } catch  {
                // エラー発生してプレイヤー作成失敗
                
                // messageLabelに失敗したことを表示
                messageLabel.text = "このurlは再生できません"
                
                audioPlayer = nil
                
                // 戻る
                return
            }
            
            // 再生開始
            if let player = audioPlayer {
                player.play()
                
                // メッセージラベルに曲タイトルを表示
                // (MPMediaItemが曲情報を持っているのでそこから取得)
                let title = item.title ?? ""
                messageLabel.text = title
            }
        } else {
            // messageLabelに失敗したことを表示
            messageLabel.text = "アイテムのurlがnilなので再生できません"
            
            audioPlayer = nil
        }
    }
    
    //選択がキャンセルされた場合に呼ばれる
    func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
        // ピッカーを閉じ、破棄する
        dismiss(animated: true, completion: nil)
    }

コード中に出てくる

let title = item.title ?? ""

での’??’は、Nil Coalescing Operatorと呼ばれる演算子で、

a != nil ? a! : b

と同じ意味になりますが、記述量を少なくできます。
[Swift]演算子 ‘??’ の意味 | nackpan Blog

各ボタンのアクションに、「再生」「一時停止」「停止」機能を加えます。
このさい、プレイヤーがnilの場合は、なにもせず戻るようにしています。

    @IBAction func pushPlay(sender: AnyObject) {
        // 再生
        if let player = audioPlayer {
            player.play()
        }
    }
   
    @IBAction func pushPause(sender: AnyObject) {
        // 一時停止
        if let player = audioPlayer {
            player.pause()
        }
    }
    
    @IBAction func pushStop(sender: AnyObject) {
        // 停止
        if let player = audioPlayer {
            player.stop()
        }
    }

iOSシミュレータでは動作しません。実機を用いてください。 「選曲」ボタンを押すと、メディアアイテムピッカーが表示され、曲を選択すると音楽がスタートします。「再生」ボタンで音楽の再生。「一時停止」ボタンで一時停止。「停止」ボタンで音楽を止めて、再生位置を一番始めに戻します。

ViewController.swift全文

import UIKit
import MediaPlayer
import AVFoundation

class ViewController: UIViewController, MPMediaPickerControllerDelegate {

    // プレイヤー用のproperty
    var audioPlayer:AVAudioPlayer?


    @IBOutlet weak var messageLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // メッセージラベルのテキストをクリア
        messageLabel.text = ""
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    @IBAction func pick(sender: AnyObject) {
        // MPMediaPickerControllerのインスタンスを作成
        let picker = MPMediaPickerController()
        // ピッカーのデリゲートを設定
        picker.delegate = self
        // 複数選択を不可にする。(trueにすると、複数選択できる)
        picker.allowsPickingMultipleItems = false
        // ピッカーを表示する
        present(picker, animated: true, completion: nil)
        
    }
    
    // メディアアイテムピッカーでアイテムを選択完了したときに呼び出される
    func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
        
        // このfunctionを抜ける際にピッカーを閉じ、破棄する
        // (defer文はfunctionを抜ける際に実行される)
        defer {
            dismissViewControllerAnimated(true, completion: nil)
        }
        
        
        // 選択した曲情報がmediaItemCollectionに入っている
        // mediaItemCollection.itemsから入っているMPMediaItemの配列を取得できる
        let items = mediaItemCollection.items
        if items.isEmpty {
            // itemが一つもなかったので戻る
            return
        }
        
        // 先頭のMPMediaItemを取得し、そのassetURLからプレイヤーを作成する
        let item = items[0]
        if let url = item.assetURL {
            do {
                // itemのassetURLからプレイヤーを作成する
                audioPlayer = try AVAudioPlayer(contentsOf: url)
            } catch  {
                // エラー発生してプレイヤー作成失敗
                
                // messageLabelに失敗したことを表示
                messageLabel.text = "このurlは再生できません"
                
                audioPlayer = nil

                
                // 戻る
                return
                
            }
            
            // 再生開始
            if let player = audioPlayer {
                player.play()
                
                // メッセージラベルに曲タイトルを表示
                // (MPMediaItemが曲情報を持っているのでそこから取得)
                let title = item.title ?? ""
                messageLabel.text = title
                
            }
        } else {
            // messageLabelに失敗したことを表示
            messageLabel.text = "アイテムのurlがnilなので再生できません"
            
            audioPlayer = nil
        }
        
    }
    
    
    //選択がキャンセルされた場合に呼ばれる
    func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
        // ピッカーを閉じ、破棄する
        dismissViewControllerAnimated(true, completion: nil)
    }
    

    @IBAction func pushPlay(sender: AnyObject) {
        // 再生
        if let player = audioPlayer {
            player.play()
        }
    }
   
    @IBAction func pushPause(sender: AnyObject) {
        // 一時停止
        if let player = audioPlayer {
            player.pause()
        }
    }
    
    @IBAction func pushStop(sender: AnyObject) {
        // 停止
        if let player = audioPlayer {
            player.stop()
        }
    }
 

}

関連

[iOS][Swift]AVAudioPlayerを使う(再生速度の変更、複数のプレイヤー) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(リソースファイルを使う) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(複数の曲をあつかう) | nackpan Blog

投稿者:

nackpan

nackpan

iOSアプリを作っています。 リピーティングに便利な「語学学習支援プレイヤー」つくりました。

「[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用)」への7件のフィードバック

  1. let audioPath = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(“”, ofType: “mp3”)!)
    を使わずにどんな曲のパスを取得できる方法はありますか? 
    このコードの場合は決まった曲のパスを選択しますよね?
    また、ファイルのパスを取得した場合、どのようにそのパスをコピーすればいいですか?

    長くなってすみません

    僕の途中のコードはこんな感じです。

    //
    // ViewController.swift
    // KeyboardExtension
    //
    // Created by Rayne on 2/11/16.
    // Copyright © 2016 Kappa. All rights reserved.
    //

    import UIKit
    import MediaPlayer

    class ViewController: UIViewController, MPMediaPickerControllerDelegate {

    var player = MPMusicPlayerController()
    var audioPlayer:AVAudioPlayer?

    override func viewDidLoad() {
    super.viewDidLoad()

    // 再生する audio ファイルのパスを取得
    let audioPath = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(“”, ofType: “mp3”)!)

    player = MPMusicPlayerController.applicationMusicPlayer()

    // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
    }

    @IBAction func pick(sender: AnyObject) {
    let picker = MPMediaPickerController()
    // MPMediaPickerControllerのインスタンスを作成
    picker.delegate = self
    // Settings for the delegate Picker
    picker.allowsPickingMultipleItems = false
    // 複数選択を不可にする。(trueにすると、複数選択できる)
    presentViewController(picker, animated: true, completion: nil)
    }

    // メディアアイテムピッカーでアイテムを選択完了したときに呼び出される
    func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {

    player.setQueueWithItemCollection(mediaItemCollection)
    // 選択した曲情報がmediaItemCollectionに入っているので、これをplayerにセット。

    var item = mediaItemCollection.items[0] as? MPMediaItem
    print(item?.assetURL)
    // TODO: ここから開発

    player.play()
    // 再生開始

    mediaPicker.dismissViewControllerAnimated(true, completion: nil)
    // ピッカーを閉じ、破棄する
    }
    //選択がキャンセルされた場合に呼ばれる
    func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
    mediaPicker.dismissViewControllerAnimated(true, completion: nil)
    // ピッカーを閉じ、破棄する
    }

    }

  2. Lucaさん、こんにちは

    iPhoneで音楽を再生させる場合、ミュージックライブラリの曲の場合ではMPMusicPlayerControllerを使う方法やAVAudioPlayerを使う方法などがあります。
    この記事を参考にしてください。
    [iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(MPMusicPlayerController使用) | nackpan Blog

    リソースファイルを再生する場合、AVAudioPlayerを使う方法があります。
    その例をブログ記事に書いてましたが、断片的なコードでわかりづらかったので書き改めました。
    こちらを参考にしてください。
    [iOS][Swift]AVAudioPlayerを使う(リソースファイルを使う) | nackpan Blog

  3. 参考ありがとうございます

    私が今作りたいアプリではユーザーがその曲を iTunes Libraryから選択することと、それを後で何かに付属する機能を作りたいです。ですから今の段階では曲を再生する、ということより曲の場所/Path を取得する方法を知りたいです。

  4. Lucaさん、こんにちは
    iTunes Libraryの曲は実際の保存箇所を直接指定することはできません。

    ライブラリの曲情報はMPMediaItemで表されているのでそれを取得します。
    MPMediaItemはassetURLや、タイトル、アルバム情報などが入っているクラスです。

    メディアアイテムピッカーを使うと、選択した曲のMPMediaItemの集まりを取得できます。
    (参考:[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(MPMusicPlayerController使用) | nackpan Blog

    MPMediaQueryを使って条件を指定して問い合わせると、適合したMPMediaItemの集まりが取得できます。
    参考:[iOS][Swift]MPMediaQueryを使って曲を絞り込む | nackpan Blog
    こちらの例では、「アーティスト名」を指定すると適合する曲が一覧で表示されるサンプルがあります。

  5. 素人ながらとても参考にさせて頂いています。

    [iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用)にかんして、view controller全文を使用して実機にて試させて頂いたのですが、swift4.2,iOS12でiPhoneXmaxで選曲の時にミュージックライブラリが表示されずフリーズしてしまい、音楽を再生できずに困っています。swift4.2でエラーの起きた箇所についてはfixなどで修正しました。

    音楽ライブラリからアルバム単位で音楽を連続再生しながら必要に応じて再生速度を調整できるようなアプリを作りたいと思っています。
    再生速度を調整するにはAVFoundationのimportが必要と思われ、ライブラリにアクセス出来るアプリの作り方を調べていたところここにたどり着きました。大変恐縮ですがよろしくお願いします。

  6. 江本拓さん、こんにちは
    コメントありがとうございます。

    情報が古くなっていて申し訳ないです。

    iOS 10以降では、ユーザーデータにアクセスする際に、許可を得ることが必要になりました。
    関係するキーとメッセージをInfo.plistにあらかじめ登録しておかないと強制終了してしまいます。

    具体的な手順については、こちらの記事が参考になるかと思います。
    [iOS 10] 各種ユーザーデータへアクセスする目的を記述することが必須になるようです | DevelopersIO

    iOS10から必要になったユーザデータにアクセスする為に表示する文言をローカライズする – Qiita

    取り急ぎお返事いたします。
    お役に立てば幸いです。

  7. お忙しいところコメントありがとうございます。
    挫折しかけていたのですが参考を元に勉強させていただきます!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください