iPod library access

iPodライブラリアクセスを使う上での注意点のメモです。
使用バージョン:iOS 5あるいはiOS 6

特徴
iPodプレーヤである曲を再生中にその曲を含むコレクションを設定しても曲は途切れることがない。
説明ではMPMusicPlayerControllerはMPMediaPlaybackデリゲートを実装している。このデリゲートの中でprepareToPlayはrequiredとなっているが対応してないようだ。
(追記2014/12)Base SDKがiOS5.0ではprepareToPlayでアラームが発生する。これはそのバージョンのMPMusicPlayerControllerがMPMediaPlaybackデリゲートを実装していないため。それより後のどこかのバージョンで実装されている模様。


使うプレーヤの選択
オーディオを再生できるのは一度に1つのミュージックプレーヤーのみです。ミュー ジックプレーヤーは、アプリケーションのメインスレッドでのみ使用できます。2種類あります。
iPodMusicPlayer-バックグラウンドで再生可(追記2014/12 iOS 8よりsystemMusicPlayerに変更)
applicationMusicPlayer-バックグランドで再生不可


listenerの設定
ライブラリ更新
曲が変わった時
曲の再生状態が変わった時
ボリュームが変わった時

大量の曲を更新する時はライブラリ更新のメソッドがかなり頻繁によばれます。
iPod再生中にライブラリから曲を削除すると、現在再生中の曲と次の曲は再生されますが、その次の曲は再生されません。エラーも出ません。
停止状態からある曲を再生させた時に呼ばれる順番は
1アイテムチェンジのメソッド(このとき再生状態はstop)
2再生状態変更のメソッド(このとき再生状態はplay)
の順です。
逆に再生状態(または一時停止)から停止させた時は
1再生状態変更のメソッド
2アイテムチェンジのメソッド
の順。
再生状態と一時停止の変更では
再生状態変更のメソッド
のみです。

デバイスで新規プレイリストを作成したときにもライブラリ更新のコールバックが呼ばれます。

Sessionの設定
セッションの設定がしてあると思います。設定はおそらくmediaPlayback。
再生中に割り込みがあった場合、割り込み終了時に再生状態に復帰する。(iPodMusicPlayer、applicationMusicPlayer両方)
電話に出た場合は試してない(iPhoneは持ってない)。


サンプルコード
Appleから提供されているサンプルコードでAddMusicというのがあります。

アプリの内容は
iPodライブラリの中から曲を選びます。複数の曲が選択できます。
一度再生曲を決定しても後から曲の追加が出来ます。
画面は3つあり、
1,曲再生画面
2,再生曲リストを表示する画面
3,再生曲リストに追加する曲を選択する画面(Appleによって用意されている曲選択機能を使う)
です。
再生曲リストに曲があるか無いかによって1から2に行くか1から3に行くかが変わりますのでソースがややこしくなっています。
また3への行き方も1から行く場合と2から行く場合があり、ややこしくなってます。


MPMediaItemPropertyReleaseDate
曲のリリース時期を知りたい時にMPMediaItemPropertyReleaseDateというプロパティで調べると思いますが、これがiOS5現在では機能していません。代わりに@"year"を使うようです。
http://stackoverflow.com/questions/4862646/get-album-year-for-item-in-ipod-library
(追記 2013/10/26 iOS 7.0.3では修正されてます。おそらくiOS 7から使えるようになりました。)

iPodライブラリアクセスを使ったアプリ間を移るとき
①自分のアプリを起動
②ホームボタンで一度閉じる
③iOS標準のiPodアプリを起動
④ホームボタンで一度閉じる
⑤自分のアプリを起動
の順番で操作で何通りかの現象が起こります。

②の実行の時点で自分のアプリの曲が停止状態の場合、
③で曲を再生させておくと
⑤の時点で[iPodMusicPlayer playbackState]がplay。

②の実行の時点で自分のアプリの曲が再生状態の場合、
③④はそのまま再生状態を維持して
⑤の時点で[iPodMusicPlayer playbackState]がplayだったりpauseだったり。
pauseのときでも再生音は出続けているので、内部で何か処理をしているように見える。

nowPlayingItemDidChangeNotificationで再生状態変更の通知を受けて調べてみると⑤になった瞬間に上のようなpause状態になってもすぐにplay状態になる。

いろいろ調べてみると、②の実行の時点で自分のアプリの曲が再生状態の場合、
③で何も操作しない場合は⑤の時点で[iPodMusicPlayer playbackState]がplay、
何か操作すると⑤の時点で[iPodMusicPlayer playbackState]がpauseになる。
②の実行の時点で自分のアプリの曲が停止状態の場合は⑤は常にplay

考察すると、あなたのアプリが再生状態のまま起動を落とした後、他のアプリから操作されました。というのに対して何か処理を行うためかと思う。
またバックグランドの時に他のアプリからコントロールされた通知内容は一度溜めておき、フォアグランドになった瞬間に送られてくる。

また、他のアプリで曲を再生した状態で自分のアプリからsetQueueWithItemCollectionを行い、その後で(曲の再生停止の制御は自分のアプリから行わないで)indexOfNowPlayingItemを実行しても他のアプリでのコレクション内のindexが返る。iPodプレーヤに対するコレクションの設定は各アプリごとにあるのかもしれない。
リピートやシャッフルも各アプリごとに設定が保持されているかもしれないが、調べきれていない。


Artistを使うかAlbumArtistを使うか
両方共、曲の情報の中に含まれている。Artistはその曲を演奏しているアーティスト、AlbumArtistはアルバムに対して付けられるアーティストという考え方。アルバムに対して付けられる情報だが、個々の曲の中のデータに含まれている。(アルバムの情報というものを格納するものがない)
ただし、(ほとんどの場合がそうだが)1つのアルバムのすべての曲が同じアーティストの場合はAlbumArtistは空白になっていることが多い。
取得は
[item valueForProperty:MPMediaItemPropertyArtist]や
[item valueForProperty:MPMediaItemPropertyAlbumArtist]
行うことが多いと思います。
AlbumArtistを取得するときにAlbumArtistが空白の時はArtistの名前が返ります。

ここで、アプリで曲の情報を得るときにどちらを取得するかですが、その情報をどう使うかによって個々の状況で判断することになります。

また、MPMediaItemCollectionのメソッドでrepresentativeItemというのがあります。これは説明によるとコレクション全体に共通した情報を持つ(架空の?)アイテムを返すとのことですが、アーティストがばらばらなアルバムに対してrepresentativeItemを得て、Artistのプロパティを取得したらどうなるかと思い、やってみたら1曲目のアーティストが返ってきました。
試しにBill EvansのPortrait In Jazzというアルバム
1曲目 アーティスト Bill Evans
2曲目 アーティスト Bill Evans Trio
3曲目 アーティスト Bill Evans Trio
4曲目 アーティスト Bill Evans Trio
5曲目 アーティスト Bill Evans Trio
6曲目 アーティスト Bill Evans Trio
7曲目 アーティスト Bill Evans
8曲目 アーティスト Bill Evans Trio
9曲目 アーティスト Bill Evans Trio
10曲目 アーティスト Bill Evans Trio
11曲目 アーティスト Bill Evans Trio
のコレクションに対して、representativeItemを得て、Artistのプロパティを取得するとBill Evansとなりました。他の同じ状態のアルバムに対してやってみましたが、皆、1曲目のデータが返ってきました。representativeItemはそんなに複雑なことはしないようです。

またコンピレーション・アルバムのrepresentativeItemに対してアルバムアーティストを取得しようとしてもnilのようです。Artistだと1曲目のアーティストが返るのですが。


Notification
MPMusicPlayerControllerNowPlayingItemDidChangeNotificationはアプリがForegroundの時には曲が変わるたびに通知されますが、Background状態にあるときに曲が変化した時には、再びForegroundにした時に通知され(メソッドが呼ばれ)ます。このとき通知内容が制限されるようです。
例えば10曲のコレクションを再生している時に3曲目でバックグランドに退いたとします。このまま再生は続いたとして、9曲目で再びForegroundになったとします。このとき通知は2回(メソッドが2回呼ばれる)で各notificationのMPMusicPlayerControllerNowPlayingItemPersistentIDKeyという値にはそれぞれ3曲目と4曲目が入っています。この2つまでというのはどこかで変更できるのかどうかは今のところわかりません。iOS5現在。

NSNumber *stateAsObject = [notification.userInfo objectForKey:@"MPMusicPlayerControllerNowPlayingItemPersistentIDKey"];

playbackStateDidChangeNotificationという再生状態が変化したときの通知の仕組みもあるのですが、こちらも同じように制限されます。Backgroundに入ってから3曲以上変化した場合はinterruptされて停止しても、再びForegroundにした時に通知されません。


曲の変わり目直前から再生するとnowPlayingItemが更新されない問題
nowPlayingItemの値は曲が変わった所で更新される実装のようです。
ところが変わり目直前から再生するとnowPlayingItemが旧曲のままで変わらないことがあります。2、3秒待てば正しい値が返ってくるようになるということもなく、ずっと旧曲のデータが返ってきます。次に値が更新されるのは次に曲が変わった時です。ですが、この次の曲に変わるというのが、現在再生中の曲の再生時間がnowPlayingItemの曲の長さになった時です。現在再生中の曲とnowPlayingItemはずれていますので、少しおかしなことがおこります。1曲目が5分の曲で2曲目が4分の曲だとすると、2曲目が再生されているのにnowPlayingItemは1曲目で、2曲目の再生開始から5分経過しないと3曲目に移行しません。間に1分間の空白が生まれます。逆に1曲目が4分の曲で2曲目が5分の曲だとすると、2曲目の再生の4分のところでnowPlayingItemは更新されて2曲目の値になり、1分後に2曲目が再生し終わるとnowPlayingItemも再生曲も普通に3曲目に変わります。
これはiPod側の挙動によるものなので修正のしようがありません。曲の変わり目直前から再生されるときは、新しい曲の先頭にすこし再生ポイントをずらすなどの処置により避けられると思います。

コメント