1. ホーム
  2. Blog
  3. iphone_dev_jp勉強会で発表した「連続再生アプリをつくろう」のサンプルソースupしました

iphone_dev_jp勉強会で発表した「連続再生アプリをつくろう」のサンプルソースupしました

フナミ タカオ

フナミ タカオ

イベント,コラム

フナミタカオです。
先週末(7/21)に、開催された、第3回iphone_dev_jp 東京iPhone/Mac勉強会で、プレゼンさせていただいた、「AVQueuePlayerのTips - 連続再生アプリをつくろう - Inside of Attaca」のサンプルソースをgithubにあげました。

サンプルソース

git hub : https://github.com/funami/MusicStoreLite

MTLよりリリースした、iTunesMusicStoreの連続試聴アプリ「Attacca」から、エッセンスを取り出した、ソースとなっています。(ただし、通信エラー等のハンドリング部分は、複雑なので、サンプルソースには、取り込んでいません)

Attacca

↑こちらのアプリの音楽連続再生エンジンから、取り出しました

アプリとしても動作でき、世界のITunesストアで、音楽を販売している国ごとのランキングを表示し、連続再生できるようになっています。

音楽を連続再生するための、いくつかのポイントを取り込んだソースになっており

  1. 1.バックグラウンドでの再生
  2. 2.電話、アラームの割り込むからの復帰
  3. 3.リモートコントローラーへの対応
  4. 4.ロック画面への音楽のアートワークの表示
  5. 5.ストリーミングによる、連続再生

に対応しています。


FNMusicPlayManagerというシングルトンのインスタンスが、再生リストを保持して、連続再生や、リモートコントローラー等の接続をおこなっています。

サンプルアプリの概要


国一覧から、


ランキング表示して、連続再生

海外のランキングを眺めているのは、結構、新鮮。
個人的なお気に入りは、「香港」と「台湾」!


たとえば、Kary Ng とか、いかがでしょう?まったく、しらなかったけど、いい感じ(日本のストアでは買えないです)


1.バックグラウンドでの再生
・MusicStoreLite-Info.plist

<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

・FNMusicPlayManager
 // バックグラウンドでも、再生を続けるために、AVAudioSessionをAVAudioSessionCategoryPlaybackに
 AVAudioSession *session = [AVAudioSession sharedInstance];
session.delegate = sharedMusicManager; // 電話等から、復帰時に、再生を再開できるように、delegate接続
NSError *error;
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
[session setActive:YES error:&error];

2.電話、アラームの割り込むからの復帰
・FNMusicPlayManager
AVAudioSessionのDelegateを仕掛けておく
AVAudioSession *session = [AVAudioSession sharedInstance];
session.delegate = sharedMusicManager; // 電話等から、復帰時に、再生を再開できるように、delegate接続
Delegateで、電話等の割り込み直前(beginInterruption)と直後のコールバック(endInterruptionWithFlags)で、復帰の実装をおこなう


#pragma mark -
#pragma mark Interruption event handling
- (void)beginInterruption
{
    //NSLog(@"beginInterruption:%d %d",self.playingMusic,_shouldResume);
    self.playingMusic = NO;
}
- (void)endInterruptionWithFlags:(NSUInteger)flags
{
    //NSLog(@"endInterruptionWithFlags:%d %d %d",self.playingMusic,_shouldResume,flags);
    if (flags == AVAudioSessionInterruptionFlags_ShouldResume){
        [[AVAudioSession sharedInstance] setActive: YES error: nil];
        if (_shouldResume){
            [self play];
        }
    }
}
- (void)inputIsAvailableChanged
{
    // TODO:イヤホンから、スピーカにかわったとき or その逆の挙動をここで実装する
}


3.リモートコントローラーへの対応
・FNMusicPlayManager
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
で、リモートコントローラからのイベント受け取りを開始

[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
ちなみに、終わるときは、こちら

・FNMusicStoreAppDelegate
レスポンダーにイベントとして、コールバックされるので、このアプリではFNMusicPlayManagerにわたす
#pragma mark -
#pragma mark Remote-control event handling
// Respond to remote control events
// リモコンからのイベントをここで、キャッチして、FNMusicPlayManagerにおくります。
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
    [[FNMusicPlayManager sharedManager] remoteControlReceivedWithEvent:receivedEvent];
}


4.ロック画面への音楽のアートワークの表示
・FNMusicPlayManager
- (void)updatePlayingInfo{
    Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");

    if (playingInfoCenter) {
        NSDictionary *item = [self.playList objectAtIndex:self.currentIndex];

        NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
        [songInfo setObject:[[item objectForKey:@"im:name"] objectForKey:@"label"] forKey:MPMediaItemPropertyTitle];
        [songInfo setObject:[[item objectForKey:@"im:artist"] objectForKey:@"label"] forKey:MPMediaItemPropertyArtist];
        [songInfo setObject:[[[item objectForKey:@"im:collection"] objectForKey:@"im:name"] objectForKey:@"label"] forKey:MPMediaItemPropertyAlbumTitle];
        NSURL *imageURL = nil;
        NSArray *imgs = [item objectForKey:@"im:image"];
        for (NSDictionary *img in imgs){
            NSDictionary *attributes = [img objectForKey:@"attributes"];
            if (attributes != nil){
                if ([[attributes objectForKey:@"height"] isEqualToString:@"170"]){
                    NSString *urlString = [img objectForKey:@"label"];
                    imageURL = [NSURL URLWithString:urlString];
                }
            }
        }

        MPMediaItemArtwork *artwork = [self artwork:imageURL.absoluteString];
        if (artwork){
            [songInfo setObject:artwork forKey:MPMediaItemPropertyArtwork];
        }else{
            [songInfo setObject:[[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"no_image.png"]] forKey:MPMediaItemPropertyArtwork];
        }

        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
        //NSLog(@"songInfo:%@",songInfo);
    }
}
setNowPlayingInfoメッソドで表示したい、文字、画像を渡せば、表示してくれます。

5.ストリーミングによる、連続再生
・FNMusicPlayManager
- (void)playAtIndex:(NSInteger)index
.......
        NSMutableArray *palyerItems = [NSMutableArray array];
        int musicCount = [_playList count];
        // palyerItems に AVPlayerItemを追加
        for (int i = index ; i < musicCount ; i++){
            NSURL *url = [_palyURLs objectAtIndex:i];
            if (url != nil){
                AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
                [palyerItems addObject:playerItem];
            }
        }
        // AVQueuePlayerのインスタンスつくる
        _player = [AVQueuePlayer queuePlayerWithItems:palyerItems];
.......
のあたりAVPlayerItem の配列から、AVQueuePlayerを作る。再生準備ができたらplayメソッドで再生開始
曲の変更は
#pragma mark KVO
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
.......
で監視
AVQueuePlayerは連続再生と、次の曲への移動は簡単だが、曲飛ばしや、戻るのは不得意。
サンプルでは、次の曲への移行以外は、playAtIndexを呼び直して、palyerItemsの配列から、全曲分の作り直している、これはかなり効率が悪い。
解決方法は、Exploring AV Foundation等参照して工夫してみてください。また、通信状態がわるいとAVQueuePlayerは停止してしまうのですが、停止したことをハンドリングしていません。
本来は、停止を監視して、必要に応じて、通信状態がよくなったら再生復帰するとか、ユーザーにフィードバックする等が必要です。
参考:AV Foundation Programming Guide


また、音楽ファイルの一覧は、iTunes Store RSS フィードジェネレータ のランキングレスポンスを利用しています。 Affiliate Search APIを使うと、ランキングだけでなく、フリーワード検索等もできるようになります。ただ、レスポンス構造が異なるので、今回のサンプルだと、改造が必要ですね。 以上さっくりですが、ぜひ、ソース眺めてみてください。


関連資料


スライド

動画

おまけ

7/27日現在の、香港のランキングを、サンプルアプリで表示するとこんな感じ

Happy Dev Music Player Apps!

トップへ戻る