[iOS][Swift]AVAudioPlayerを使う(複数の曲をあつかう)

AVAudioPlayerを使う例として
[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(再生速度の変更、複数のプレイヤー) | nackpan Blog
という記事を書きました。

今回は、AVAudioPlayerで複数のアイテムを扱います。
複数のアイテムを選択したのち、順次再生されるようにしたいと思います。


サンプルプロジェクトを作りました
(プロジェクト一式はGitHubにあります nackpan/AVAudioPlayerDemo3
ss2015-09-23 16 18 32

「選曲」ボタンで、複数の曲を選択し再生を開始します。
「再生・一時停止」ボタンで、再生と一時停止を切り替えます。
「<<」ボタンで前の曲に移ります。
「>>」ボタンで次の曲に移ります。
Message Labelに現在の曲情報が表示されます。

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

プレイヤークラスの作成

前回までのサンプルでは、ViewController.swiftに全部書いていました。
しかし、ViewController.swiftになにもかも書いていくと、どこが何の役割なのかがわかりづらくなってしまいます。
なので、プレイヤークラスを別に作成しました。
今回のサンプルでは、SimplePlayerクラスと名付けました。

複数のアイテムを扱う

プレイヤーが、MPMediaItemの配列をもち、currentIndexで現在のindexを示すこととします。
アイテム末尾到達、「>>」ボタンタップ、「<<」ボタンタップがおこなわれると、currentIndexを変更します。
別のアイテムになるたびにプレイヤーに新アイテムをセットします。

/// プレイヤーにitemをセットして更新
func updatePlayer() {
    let item = mediaItems[currentIndex]
    // MPMediaItemのassetURLからプレイヤーを作成する
    if let url: NSURL = item.assetURL {
        do {
            // itemのassetURLからプレイヤーを作成する
            audioPlayer = try AVAudioPlayer(contentsOfURL: url)
            
            // audioPlayerのdelegate先をselfに設定
            // (アイテム末尾に到達したときに呼ばれるaudioPlayerDidFinishPlaying()を受ける)
            audioPlayer?.delegate = self
            
        } catch  {
            // エラー発生してプレイヤー作成失敗
            audioPlayer = nil
            
            // 「再生中」ではない
            nowPlaying = false
            
            // 戻る
            return
            
        }
        
    } else {
        
        audioPlayer = nil

        // 「再生中」ではない
        nowPlaying = false
    }
}
urlがnilのアイテムもアイテム配列に含めている

今回のサンプルでは、mediaItemsにurlがnilのMPMediaItemも含めています。
その場合のアイテム再生時には、「urlがnilなので再生できない」旨のメッセージを表示して、そこで再生を停止しています。
(実際のアプリでは、事前にチェックしてurlがnilのものを取り除いておく、あるいは再生中にnilのものがあれば飛ばすなどの処理のほうがよさそうですが…)

アイテム末尾到達を知る

AVAudioPlayerDelegateのaudioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) メソッドで知ることができます。

プレイヤーをAVAudioPlayerDelegateプロトコルに準拠させます

class SimplePlayer: NSObject, AVAudioPlayerDelegate {

アイテム末尾に到達した際にaudioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool)が呼ばれるので、そこで必要な処理を行います。
このサンプルでは、
・範囲内であればindexを進めて次の曲の再生に移る
・すべてのアイテムの再生が終わったならそこで再生を終えて、indexを0に戻して最初のアイテム情報を表示する
処理を行っています。

    /// アイテム末尾に到達したときに呼ばれる
    func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
        
        
        // 最後の曲の場合は終了。そうでないなら次の曲へ
        if currentIndex >= mediaItems.count - 1{
            
            // 終了
            
            // indexを0に戻す
            currentIndex = 0
            
            // 新たなitemでプレイヤー作成
            updatePlayer()
            
            // ポーズする
            pause()
            
            
            return
            
        } else {
            
            // 次の曲へ。
            nextItem()
            
            
        }
    }

サンプルプロジェクトのGitHub
nackpan/AVAudioPlayerDemo3

関連

[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(MPMusicPlayerController使用) | nackpan Blog
[iOS][Swift]MPMediaQueryを使って曲を絞り込む | nackpan Blog
[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(再生速度の変更、複数のプレイヤー) | nackpan Blog

[iOS][Swift]画面ロックさせない

iPhone/iPadは、自動ロック設定をした場合、画面に触っていないと所定の時間になると画面が薄暗くなった後、真っ暗になってスリープモードになります。

オーディオプレイヤー系アプリだと、かなりのあいだ画面に触らないことがあります。
スリープモードになると困る場合には

    
    // 画面ロックをしない
    UIApplication.sharedApplication().idleTimerDisabled = true

とすることで、画面ロック(スクリーンロック)をさせないでおくことができます。

その必要がなくなった場合には、

    
    // 所定の時間がたつと画面ロックする
    UIApplication.sharedApplication().idleTimerDisabled = false

とすることで、所定の時間が経つと画面ロックするようになります。

[iOS][Swift]AVAudioPlayerを使う(リソースファイルを使う)

(2019/02/16 更新 Xcode 10.1 + Swift 4.2)

[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog
では、ミュージックライブラリから取り出したアイテムを使ってAVAudioPlayerをinitializeする方法を紹介しました。
MPMediaItemからアイテムのassetURLを取得して、そのNSURLからAVAudioPlayerを作りました。

今回は、リソースファイルからAVAudioPlayerを作成する方法を紹介します。

サンプルの概要

今回使用するサンプルプログラムでは
* プロジェクトに加えておいた2種類のオーディオファイルから選択
* 選んだファイル名を表示
* 選んだファイルの再生、停止、一時停止
を行います。

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

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

AVAudioPlayerを使って、リソースファイルを再生する

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

リソースファイルをプロジェクトに加える

File > Add Files To “<プロジェクト名>” から、オーディオファイルをプロジェクトに加えます。
今回は2種類使いますので、二つのオーディオファイルを加えてください。
名前は、”sound01.mp3″、”sound02.mp3″としてください。
ss 2016-02-28 0.24.17-2

UI部品レイアウト

ボタンとラベルを配置。
ss 2016-02-28 0.15.56

ラベルとViewController.swiftをOutletを作成して接続。
ss 2016-02-28 0.16.46
ss 2016-02-28 0.16.33

ボタンとViewController.swiftをActionを作成して接続。
ss 2016-02-28 0.19.19
ss 2016-02-28 0.18.31

必要なフレームワークをimport

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

import UIKit
import AVFoundation


class ViewController: UIViewController {

プレイヤーをpropertyとして加える

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

class ViewController: UIViewController {

    var audioPlayer:AVAudioPlayer?
    
    override func viewDidLoad() {

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

リソースファイルがしめすNSURLからプレイヤーを作成

「Sound01」「Sound02」ボタンを押した時の処理

「Sound01」を押したときは”sound01.mp3″で、「Sound02」ボタンを押したときは”sound02.mp3″をセットしてプレイヤーをinitializeします。

        
    
    // 「Sound01」ボタンが押されたら、プレイヤーに"sound01.mp3"をセットしてinitializeする
    @IBAction func btn01Tapped(sender: AnyObject) {
        // リソースファイル("sound01.mp3")をオーディオプレイヤーにセットしてinitializeする
        let fileName: String? = "sound01"
        let fileType:String? = "mp3"
        if let url = Bundle.main.url(forResource: fileName, withExtension: fileType) {
            do {
                audioPlayer = try AVAudioPlayer(contentsOf: url)
                
                // ラベル表示
                messageLabel.text = fileName
            } catch {
                // プレイヤー作成失敗
                // その場合は、プレイヤーをnilとする
                audioPlayer = nil
            }
            
        } else {
            // urlがnilなので再生できない
            fatalError("Url is nil.")
        }
    }
    
    // 「Sound02」ボタンが押されたら、プレイヤーに"sound02.mp3"をセットしてinitializeする
    @IBAction func btn02Tapped(sender: AnyObject) {
        // リソースファイル("sound02.mp3")をオーディオプレイヤーにセットしてinitializeする
        let fileName: String? = "sound02"
        let fileType:String? = "mp3"
        if let url = Bundle.main.url(forResource: fileName, withExtension: fileType) {
            do {
                audioPlayer = try AVAudioPlayer(contentsOf: url)
                
                // ラベル表示
                messageLabel.text = fileName
            } catch {
                // プレイヤー作成失敗
                // その場合は、プレイヤーをnilとする
                audioPlayer = nil
            }
            
        } else {
            // urlがnilなので再生できない
            fatalError("Url is nil.")
        }
    }
    

このサンプルコードでは、プレイヤーの作成が失敗した場合は、プレイヤーをnilとしています。
与えられたリソースファイルが、オーディオファイルじゃなくて画像ファイルだった、なんて場合にはプレイヤー作成が失敗します。
また、ファイル名が間違っていた場合は、URLが取得できないということで、エラーになります。

「再生」「一時停止」「停止」ボタンを押した時の処理
    @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()
        }
    }

「再生」「一時停止」「停止」ボタンで再生をコントロールできます。

ViewController.swift全文

import UIKit
import AVFoundation

class ViewController: UIViewController {
    
    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.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    // 「Sound01」ボタンが押されたら、プレイヤーに"sound01.mp3"をセットしてinitializeする
    @IBAction func btn01Tapped(sender: AnyObject) {
        // リソースファイル("sound01.mp3")をオーディオプレイヤーにセットしてinitializeする
        let fileName: String? = "sound01"
        let fileType:String? = "mp3"
        if let url = Bundle.main.url(forResource: fileName, withExtension: fileType) {
            do {
                audioPlayer = try AVAudioPlayer(contentsOf: url)
                
                // ラベル表示
                messageLabel.text = fileName
            } catch {
                // プレイヤー作成失敗
                // その場合は、プレイヤーをnilとする
                audioPlayer = nil
            }
            
        } else {
            // urlがnilなので再生できない
            fatalError("Url is nil.")
        }
    }
    
    // 「Sound02」ボタンが押されたら、プレイヤーに"sound02.mp3"をセットしてinitializeする
    @IBAction func btn02Tapped(sender: AnyObject) {
        // リソースファイル("sound02.mp3")をオーディオプレイヤーにセットしてinitializeする
        let fileName: String? = "sound02"
        let fileType:String? = "mp3"
        if let url = Bundle.main.url(forResource: fileName, withExtension: fileType) {
            do {
                audioPlayer = try AVAudioPlayer(contentsOf: url)
                
                // ラベル表示
                messageLabel.text = fileName
            } catch {
                // プレイヤー作成失敗
                // その場合は、プレイヤーをnilとする
                audioPlayer = nil
            }
            
        } else {
            // urlがnilなので再生できない
            fatalError("Url is 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]ミュージックライブラリにアクセスして音楽を再生する(MPMusicPlayerController使用) | nackpan Blog
[iOS][Swift]MPMediaQueryを使って曲を絞り込む | nackpan Blog
[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(再生速度の変更、複数のプレイヤー) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(複数の曲をあつかう) | nackpan Blog

[iOS][Swift]AVAudioPlayerを使う(再生速度の変更、複数のプレイヤー)

[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog
で、AVAudioPlayerを使いました。
今回は、そこで使っていなかったAVAudioPlayerの機能のいくつかを使ってみたいと思います。
• 再生速度の変更
• 複数のプレイヤーの使用
です。

再生速度の変更

プレイヤーのrateプロパティを変更することで、再生速度を変更できます。
ただし、そのさいのプレイヤーは、enableRateプロパティをtrueにしておく必要があります。

    
    // (playerはAVAudioPlayerのインスタンス)
    // 再生レート変更可能にする
    player.enableRate = true
    // 再生速度を0.7にする
    player.rate = 0.7

複数のプレイヤーの使用

AVAudioPlayerは複数のプレイヤーを同時に再生することができます。
なので、たとえば、歌を再生すると同時に、歓声だとか、ざわめき音など環境音的なものを重ねて再生するなんてこともできます。

    
    var audioPlayer0: AVAudioPlayer?
    var audioPlayer1: AVAudioPlayer?

のように、プロパティとして、二つのプレイヤーを設置して、

    
    audioPlayer0 = try? AVAudioPlayer(contentsOfURL: url0)
    audioPlayer1 = try? AVAudioPlayer(contentsOfURL: url1)
    
    if audioPlayer0 == nil || audioPlayer1 == nil {
        // 再生できません
    
        return
    }
    
    
    // 再生開始
    if let player0 = audioPlayer0, player1 = audioPlayer1 {
        
        // 再生
        player0.play()
        player1.play()
    }

とした場合に、同時再生できます。

サンプルプロジェクト

再生速度の変更と、2曲の同時再生を行うサンプルを作りました。
ss 2015-09-20 21 28 37

「選曲」ボタンを押すと、メディアピッカーが現れるので、そこで2曲選択してください。
すると、選択した2曲を同時に再生しはじめます。
再生速度とボリュームを調整できます。

プロジェクトはGitHubにあります。
nackpan/AVAudioPlayerDemo2

関連

[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(リソースファイルを使う) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(複数の曲をあつかう) | nackpan Blog

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

[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

[iOS][Swift]MPMediaQueryを使って曲を絞り込む

[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(MPMusicPlayerController使用) | nackpan Blog
では、MPMusicPlayerControllerを用いて、ミュージックライブラリにアクセスしました。

そのときは、メディアピッカーを使いました。
ss 2015-09-13 11 41 39

今回は、メディアクエリを使用して、アイテムを取得する方法を紹介します。
iPodライブラリアクセス プログラミングガイド(PDF)での「iPodライブラリの使用」部分の話になります

MPMediaQueryクラス
MPMediaQuery Class Reference

MPMediaQueryクラスを使うと、
「アルバムごとにまとめて」取得
「アーティスト名を指定して」取得
などグループ化したり、対象を絞り込んだりして、アイテムを取得することができます

グルーピング

グルーピングクエリには、
albumsQuery
artistsQuery
songsQuery
playlistsQuery
podcastsQuery
audiobooksQuery
compilationsQuery
composersQuery
があります

グルーピングクエリは、MPMediaQuery!クラスを返します
MPMediaQueryのプロパティ
• collections
• items
に取得した内容が入っています。

collectionsは、MPMediaItemCollectionのarrayです
(MPMediaItemCollectionはMPMediaItem(ミュージックライブラリアイテムを表すクラス)を集めて管理するクラスです)
(albumsQueryの場合だと、アルバム一つ分が一つのMPMediaItemCollectionになっています)

itemsは、MPMediaItem(ミュージックライブラリアイテムを表すクラス)のarrayです。

MPMediaItem Class Reference
MPMediaItemCollection Class Reference

アルバムごとにグルーピングして取得したのち、アルバムのタイトルを表示する例を示します

// 「アルバム」単位で取得
let query = MPMediaQuery.albumsQuery()

// 取得したアルバム情報を表示
if let collections = query.collections {
    for collection in collections {
        if let representativeTitle = collection.representativeItem!.albumTitle {
            print("アルバム名: \(representativeTitle)  曲数: \(collection.items.count)")
        }
    }
}

フィルター

MPMediaQueryは、対象を絞るフィルターを加えることができます。
それには
addFilterPredicate(_ predicate: MPMediaPredicate!)
を用います

アルバムごとにグルーピングして取得する例を先ほど示しましたが、そのさい、cloudItem(端末にないアイテム)を取り除く例を示します。

let query = MPMediaQuery.albumsQuery()

query.addFilterPredicate(MPMediaPropertyPredicate(value: false, forProperty: MPMediaItemPropertyIsCloudItem))

// 取得したアルバム情報を表示
if let collections = query.collections {
    for collection in collections {
        if let representativeTitle = collection.representativeItem!.albumTitle {
            print("アルバム名: \(representativeTitle)  曲数: \(collection.items.count)")
        }
    }
}

MPMusicPlayerControllerでのクエリの利用

MPMusicPlayerControllerは、クエリを元に曲目キューをセットする
setQueueWithQuery(_ query: MPMediaQuery)
メソッドがあるので、クエリをそのまま渡してプレイヤーを作成することができます

let query = MPMediaQuery.albumsQuery()
// プレイヤーにqueryから作成したキューをセット
// (playerはMPMusicPlayerControllerのインスタンスを示す)
player.setQueueWithQuery(query)

アーティスト名で絞り込むサンプル

サンプルプロジェクトを作成しました
ファイル 2015-09-16 22 10 16
テキストフィールドにアーティスト名を入力して「実行」ボタンを押すと、入力したテキストを含むアーティストの曲を表示し、再生を開始します

        // アーティスト名で絞り込む
        // (ここのMPMediaPropertyPredicateで使われている変数artistは、String型でユーザーが入力したテキストから作成したもの)
        let predicate = MPMediaPropertyPredicate(value: artist, forProperty: MPMediaItemPropertyArtist, comparisonType: MPMediaPredicateComparison.Contains)
        query.addFilterPredicate(predicate)

フィルターで、「指定したテキストを含むアーティスト」を選出しています。

プロジェクトはGitHubにあります
nackpan/MediaQueryExample

ためしてみてくださいね

関連

[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(MPMusicPlayerController使用) | nackpan Blog
[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(再生速度の変更、複数のプレイヤー) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(リソースファイルを使う) | nackpan Blog
[iOS][Swift]AVAudioPlayerを使う(複数の曲をあつかう) | nackpan Blog

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

ミュージックライブラリにアクセスしてオーディオファイルを再生するにはいくつかの方法があります
• MPMusicPlayerControllerを使う
• AVAudioPlayerを使う
• AVPlayerを使う
自分が使用したことがあるのはこの3つです

MPMusicPlayerControllerを使う方法は、iPodライブラリアクセス プログラミングガイド(PDF)に記されている方法です。
iPhone OS 3.0時代から使える方法です。

AVAudioPlayer、AVPlayerを使ってミュージックライブラリにアクセスするのは、
iOS 6以降、可能になりました。(What’s New in iOS – iOS 6.0のAV Foundation Frameworkの項目を参照)

ミュージックライブラリのアイテムを再生するさいの違い

• MPMusicPlayerControllerでは、Apple Musicで「マイミュージックに追加」をしたアイテムも再生することができます。「オフラインで再生可能にする」を行っていないアイテムも再生出来ます(再生開始までにやや間がある)。このあたりの挙動は「ミュージック」アプリと同様。
ただし、ホームシェアリングのアイテムはMPMusicPlayerControllerのプレイヤーでは再生できません。

• AVAudioPlayerでは、再生速度を変更したり、ステレオ音声で音量割り振りを変えたり(パンニング)、ループ回数を指定したりできます。詳しくはAVAudioPlayer Class Referenceへ。
再生速度変更がすぐできるので、「Rendow」作成のさいに使用しました。
しかし、MPMusicPlayerControllerにくらべて再生出来るアイテムに制限があります
・クラウド上のアイテム(=端末にないアイテム)は再生できない。(購入したものでも端末に入れていないものは再生できない)
・Apple Musicで「マイミュージックに追加」したアイテムは「オフラインで再生可能にする」にしたものでも再生できません。

• AVPlayerは、動画(映像と音声)を再生できます。
AVPlayerでは、assetという単位を用います。ある動画の一部分を指定してassetとしたり、動画の一部分を複数くっつけたものをひとつのassetとして扱うことができます。
再生速度の変更ができます。
しかし、AVAudioPlayerと同様に、
・端末にないアイテム
・Apple Musicで「マイミュージックに追加」「オフラインで再生可能にする」にしたアイテム
は再生できません。


今回は、MPMusicPlayerControllerを使う方法を記します

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

iPodライブラリアクセス プログラミングガイド(PDF)
iPod Library Access Programming Guide(英語版)
このappleのドキュメントにライブラリにアクセスして再生する方法、必要な曲を選択する方法、現在再生中の音楽の情報を知る方法等、まとめてあります。
(ちなみに、日本語ドキュメント – Apple DeveloperのページにappleのiOS用日本語ドキュメントがまとめてあります。英語版へのリンクもあります)
今回は、ミュージックライブラリから音楽を選択すると音楽がスタートするようにし、再生、一時停止、停止ができるようにします。

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

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

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

import UIKit
import MediaPlayer

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

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

class ViewController: UIViewController, MPMediaPickerControllerDelegate {
    
    var player = MPMusicPlayerController()

    override func viewDidLoad() {

プレイヤーのインスタンスを作成。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        player = MPMusicPlayerController.applicationMusicPlayer()
        //player = MPMusicPlayerController.systemMusicPlayer()
        
    }

(ここで、applicationMusicPlayerではなく、systemMusicPlayerを用いると、「ミュージック」アプリでの再生状況(再生アイテムや、シャッフル、リピートなどのモード)を反映したものになる)

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

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

import UIKit
import MediaPlayer

class ViewController: UIViewController, MPMediaPickerControllerDelegate {
    @IBAction func pick(sender: AnyObject) {
        // MPMediaPickerControllerのインスタンスを作成
        let picker = MPMediaPickerController()
        // ピッカーのデリゲートを設定
        picker.delegate = self
        // 複数選択を不可にする。(trueにすると、複数選択できる)
        picker.allowsPickingMultipleItems = false
        // ピッカーを表示する
        presentViewController(picker, animated: true, completion: nil)
        
    }
    
    // メディアアイテムピッカーでアイテムを選択完了したときに呼び出される
    func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
        
        // 選択した曲情報がmediaItemCollectionに入っているので、これをplayerにセット。
        player.setQueueWithItemCollection(mediaItemCollection)
        // 再生開始
        player.play()
        // ピッカーを閉じ、破棄する
        dismissViewControllerAnimated(true, completion: nil)
        
    }
    
    
    //選択がキャンセルされた場合に呼ばれる
    func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
        // ピッカーを閉じ、破棄する
        dismissViewControllerAnimated(true, completion: nil)
    }

各ボタンのアクションに、「再生」「一時停止」「停止」機能を加えます。

    @IBAction func pushPlay(sender: AnyObject) {
        player.play()
    }

    @IBAction func pushPause(sender: AnyObject) {
        player.pause()
    }

    @IBAction func pushStop(sender: AnyObject) {
        player.stop()
    }

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


プロジェクトはGitHubにあります。
nackpan/MPMusicPlayerControllerDemo

ViewController.swift全文

//
//  ViewController.swift
//  musicplayerDemo
//
//  Created by KUWAJIMA MITSURU on 2015/09/13.
//  Copyright (c) 2015年 nackpan. All rights reserved.
//

import UIKit
import MediaPlayer

class ViewController: UIViewController, MPMediaPickerControllerDelegate {
    
    var player = MPMusicPlayerController()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        player = MPMusicPlayerController.applicationMusicPlayer()
        //player = MPMusicPlayerController.systemMusicPlayer()
        
    }

    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
        // ピッカーを表示する
        presentViewController(picker, animated: true, completion: nil)
        
    }
    
    // メディアアイテムピッカーでアイテムを選択完了したときに呼び出される
    func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
        
        // 選択した曲情報がmediaItemCollectionに入っているので、これをplayerにセット。
        player.setQueueWithItemCollection(mediaItemCollection)
        // 再生開始
        player.play()
        // ピッカーを閉じ、破棄する
        dismissViewControllerAnimated(true, completion: nil)
        
    }
    
    
    //選択がキャンセルされた場合に呼ばれる
    func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
        // ピッカーを閉じ、破棄する
        dismissViewControllerAnimated(true, completion: nil)
    }
    

    @IBAction func pushPlay(sender: AnyObject) {
        player.play()
    }

    @IBAction func pushPause(sender: AnyObject) {
        player.pause()
    }

    @IBAction func pushStop(sender: AnyObject) {
        player.stop()
    }



}

関連

[iOS][Swift]ミュージックライブラリの音楽の再生、情報の表示(MPMusicPlayerController使用) | nackpan Blog
[iOS][Swift]MPMediaQueryを使って曲を絞り込む | nackpan Blog
[iOS][Swift]ミュージックライブラリにアクセスして音楽を再生する(AVAudioPlayer使用) | nackpan Blog

2014年6月進捗状況

6月が過ぎ去った。

アプリ開発、6月の計画というか方針 | nackpan Blog

6月中にやりたかったこと

・「語学学習支援プレイヤー」のアップデート
  ・再生速度変更機能の追加
  ・バックグラウンド再生機能の追加

・オーディオファイルを無音部分で分割するアプリを製作する

 

・・・しかし、どちらも完成しなかった。・・・残念。
語学学習支援プレイヤー」のアップデートのほうは、必要な機能(再生速度変更、アイテムに無音部分を付与、バックグラウンドで複数アイテム再生、無音位置配列の保存、オーディオファイルの照合)をそれぞれ実験してきちんと動くことまで確認できた。あとは、本体に統合しましょう。
無音分割のほうは、全然手をつけられませんでした。

思ってたより、ずいぶんと時間がかかっております。

再生速度の変更

再生速度の変更は、AVPlayerにrateプロパティがあるので、これを使えばすぐにできるだろうと考えていた。
やってみた。rate=0.5。「こんにちは」をスローで再生。
「こ
みたいな、たいそうぼわぼわした音になった。
困った。
青木直史「サウンドプログラミング入門」を買ってきた。
「音の高さを変化させずに音の高さを自由にコントロールするテクニックが、タイムストレッチです。」
スロー再生とかで行っている処理は、タイムストレッチというのか。
波の周期を求めて、縮めたり引き延ばしたりする。なかなか大変そうだね。
タイムストレッチで調べたら、Dirac 3というライブラリがあった。
これ使おうか、と思っていたとき。
この段階で、あらためてAV Foundationフレームワーク関連のreferenceを見てみると、
AVAudioMixInputParameters Class Reference にaudioTimePitchAlgorithmというプロパティがあって、再生速度を変更する際のアルゴリズムを指定できるとある。声に適しているのは、AVAudioTimePitchAlgorithmTimeDomain。

    AVMutableAudioMixInputParameters *mixParameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:audioTrack];

    // for voice
    mixParameters.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmTimeDomain;

    AVMutableAudioMix* audioMix = [AVMutableAudioMix audioMix];
    audioMix.inputParameters = @[mixParameters];

    AVPlayerItem* playerItem = [AVPlayerItem playerItemWithAsset:composition];
    playerItem.audioMix = audioMix;

こんな感じでやってみたら、声の再生が「こ〜ん〜に〜ち〜は〜」という期待していた音になった。よかった。

バックグラウンド再生

そんで、バックグラウンド再生。
iOSでは、バックグラウンドでできることは限定されてるけど、オーディオ再生はできる。
オーディオ再生はいいのだけれど、止めて適宜時間を測ってまた再生するという部分がバックグラウンドでできない。
それで、当初MPMediaPlayerController使ってたときはバックグラウンド再生には対応していなかった。
で、語学学習支援プレイヤーver1.3.0からは、AVPlayerを使っている。
AV Foundation Programming Guide: About AV Foundation
AV Foundation プログラミングガイド (日本語)(PDF)
ここのプログラミングガイド、assetの編集に関する部分で、assetを切ったり複数のassetをくっつけたりしてできたものをassetとして扱えるとある。ということは、assetに適切な長さの無音部分をくっつけて新たなassetを作ってそれを連続再生するという形にすれば、「語学学習支援プレイヤー」でもバックグラウンドでの連続再生ができるんじゃないかなと考えた。
やってみた。うまくいった。よかった。

無音位置配列の保存

これまでは、曲を選択すると毎回オーディオファイルの無音位置を調べていた。
多数のファイル、長い時間のファイルだと調べ終わるまで時間がかかっている。
しかし、バックグラウンド再生ができるようにしたばあい、アプリを起動して再生開始した後、すぐにバックグラウンドへ移行するケースが考えられる。まだ無音位置がわかっていないうちにバックグラウンドへ移行すると、期待通りの再生ができない。
そのため、できるだけはやく無音位置がわかるようにしたい。
そこで、あらかじめ無音位置配列を保存しておいて、それが使える場合は使うようにする。
この辺りの処理を行うのにCore Dataを使うことにした。
参考書。
國井貴浩「iOS Core Data徹底入門
以前、半分ぐらい、いよいよCoreDataが登場するところまで読み進めていた。
データ永続化について順々に説きすすめていく本。バイナリで読み書き。NSUserDefaultで読み書き。NSDictionaryを使ったXMLファイルを読み書き。SQLiteを使用。CoreDataを使用。と、すすめていく。各方式にどのような特徴があるのかを示す。
今回の用途では、Core Dataの一部分の機能を使えばよさそう。
ということで、無音位置保存機能と照合機能を作った。

今後の展望

必要な機能はそろったので、「語学学習支援プレイヤー」に組み込んで行こう。