トップ «前の日記(2013-04-29) 最新 次の日記(2013-05-07)» 編集

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|

2013-05-06 [iOS]耳コピ用AudioPlayer(その5)

耳コピ用の最低限の機能は実装した。まだ、iPad対応やSongs以外の一覧対応、指定範囲の繰り返し再生等が未実装だが。

本来はオーディオセッションは、アプリケーションの単位で管理する物だと思うが、まだ、仮なので再生画面での管理とした。

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
}
 
- (void)viewDidUnload
{
    [[AVAudioSession sharedInstance] setActive:NO error:nil];
    self.dict = nil;
    self.playerItem = nil;
    self.player = nil;
    [super viewDidUnload];
}

また、viewDidUnloadはメモリ不足時のView解放の為に呼ばれるので、位置としては適切ではないと思うが、アプリケーション単位での管理に変更する際に見直す予定だ。

再生位置をスライダで表示/変更できるようにしたのだが、再生中のスライダの更新をコールバックのハンドラで行う事にした。

- (void)viewWillAppear:(BOOL)animated
{
    DBGMSG(@"%s, dict:%@", __func__, self.dict);
    [super viewWillAppear:animated];
    
    /* 選択された曲 */
    NSURL       *url = [self.dict objectForKey:@"URL"];
    self.playerItem = [AVPlayerItem playerItemWithURL:url];
    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
    
    /* 再生位置(先頭) */
    self.currentTimeSlider.minimumValue = 0.0;
    self.currentTimeSlider.maximumValue = CMTimeGetSeconds(self.playerItem.duration);
    self.currentTimeSlider.value = 0.0;
    
    /* 生成速度(停止) */
    self.rateSlider.minimumValue = 0.0;
    self.rateSlider.maximumValue = 2.0;
    self.rateSlider.value = 0.0;
    
    /* 再生位置の更新 */
    const double interval = (0.5f * self.currentTimeSlider.maximumValue)
                            / self.currentTimeSlider.bounds.size.width;
    const CMTime time     = CMTimeMakeWithSeconds(interval, NSEC_PER_SEC);
    __block DetailViewController * __weak blockWeakSelf = self;
    self.playerTimeObserver = [self.player addPeriodicTimeObserverForInterval:time
                                                                        queue:NULL
                                                                   usingBlock:^( CMTime time ) {
                                                                       DetailViewController *tempSelf = blockWeakSelf;
                                                                       if (! tempSelf) return;
                                                                       [tempSelf _updateCurrentTimeSlider];
                                                                   }];
}

また、末尾まで再生したら通知を受け取るようにした。これらについては、破棄の処理も必要だ。

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(_playerDidPlayToEndTime:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(_playerTimeJumped:)
                                                 name:AVPlayerItemTimeJumpedNotification
                                               object:nil];
}
 
- (void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    if (self.playerTimeObserver) {
        [self.player removeTimeObserver:self.playerTimeObserver];
        self.playerTimeObserver = nil;
    }
    
    [super viewWillDisappear:animated];
}

再生とは再生速度を1.0にする事。停止とは再生速度を0.0にする事になるため、再生速度を自由に変更できるアプリケーションでは、再生と停止のボタンは無くても構わないが、用意した。再生ボタンを押下されると、停止状態なら再生速度を1.0に設定している。

- (IBAction)play:(id)sender
{
    if (0.0 == self.rateSlider.value) {
        self.player.rate = 1.0;
        [self _updateRateSlider];
    }
}
 
- (IBAction)stop:(id)sender
{
    self.player.rate = 0.0;
    [self _updateRateSlider];
}

再生位置と再生速度のスライダで値が変更されたら、再生位置と再生速度を変更するメソッドだ。

- (IBAction)currentTimeSliderDidChanged:(id)sender
{
    [self.player seekToTime:CMTimeMakeWithSeconds(self.currentTimeSlider.value, NSEC_PER_SEC)];
}
 
- (IBAction)rateSliderDidChanged:(id)sender
{
    self.player.rate = self.rateSlider.value;
}

逆に、再生位置と再生速度をスライダに反映するメソッドだ。

- (void)_updateCurrentTimeSlider
{
    const double duration = CMTimeGetSeconds( [self.player.currentItem duration] );
    const double time     = CMTimeGetSeconds([self.player currentTime]);
    const float  value    = (self.currentTimeSlider.maximumValue - self.currentTimeSlider.minimumValue )
                            * time / duration + self.currentTimeSlider.minimumValue;
    
    [self.currentTimeSlider setValue:value];
}
 
- (void)_updateRateSlider
{
    self.rateSlider.value = self.player.rate;
}

最後まで再生したら、再生位置を先頭に戻している。

- (void)_playerDidPlayToEndTime:(NSNotification *)notification
{
    [self.player seekToTime:CMTimeMakeWithSeconds(0.0, NSEC_PER_SEC)];
    self.player.rate = 0.0;
    [self currentTimeSliderDidChanged:nil];
}

これが再生画面。

再生画面

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/AudioPlayer - GitHub

_ 関連情報

iPhone Core Audioプログラミング
Technical Q&A QA1668Playing media while in the background using AV Foundation

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

トップ «前の日記(2013-04-29) 最新 次の日記(2013-05-07)» 編集