[iOS 8] AVFoundationのAVAudioPlayerNodeで音楽ファイルを再生してみる
はじめに
前回の記事では、iOS 8のオーディオ関連フレームワークの新機能について大まかに解説しました。今回は、iOS 8でAVFoundationフレームワークに新規追加されたAVAudioEngineやAVAudioPlayerNodeを使用した実装を解説していきます。(AVAudioEngineなどについての説明は前回の記事を御覧ください。)
目次
今回作るアプリについて
実装 (ファイルから再生する)
以下が実装例です。
「Nodeを作成して、Engineに装着し、Engineをスタートさせる」という流れになります。
@import AVFoundation; @interface FilePlayerViewController () @property (nonatomic, strong) AVAudioEngine *engine; @property (nonatomic, strong) AVAudioPlayerNode *audioPlayerNode; @property (nonatomic, strong) AVAudioFile *audioFile; @end @implementation FilePlayerViewController - (void)viewDidLoad { [super viewDidLoad]; self.engine = [AVAudioEngine new]; // [1] AVAudioFile オブジェクトを準備する NSString *path = [[NSBundle mainBundle] pathForResource:@"loop.m4a" ofType:nil]; self.audioFile = [[AVAudioFile alloc] initForReading:[NSURL fileURLWithPath:path] error:nil]; // [2] AVAudioPlayerNode オブジェクトを準備する self.audioPlayerNode = [AVAudioPlayerNode new]; [self.engine attachNode:self.audioPlayerNode]; // [3] Node 同士を繋ぐ AVAudioMixerNode *mixerNode = [self.engine mainMixerNode]; [self.engine connect:self.audioPlayerNode to:mixerNode format:self.audioFile.processingFormat]; // [4] Engine の処理を開始する NSError *error; [self.engine startAndReturnError:&error]; if (error) { NSLog(@"error:%@", error); } } #pragma mark - private methods - (void)play { // [5] オーディオファイルの再生をスケジュールする [self.audioPlayerNode scheduleFile:self.audioFile atTime:nil completionHandler:nil]; // [6] 再生する [self.audioPlayerNode play]; } - (IBAction)didTapPlayButton:(id)sender { if (self.audioPlayerNode.isPlaying) { [self.audioPlayerNode stop]; } else { [self play]; } } - (IBAction)didChangeVolumeSliderValue:(id)sender { float value = ((UISlider *)sender).value; // [7] ボリュームを操作する self.audioPlayerNode.volume = value; } - (IBAction)didChangePanSliderValue:(id)sender { float value = ((UISlider *)sender).value; // [8] パンを操作する self.audioPlayerNode.pan = value; } @end
[1] AVAudioFileオブジェクトを準備する
まずは、AVAudioFileを作成します。これはオーディオファイルを表すオブジェクトであり、ファイルのフォーマット情報などももっています。
[2] AVAudioPlayerNode オブジェクトを準備する
AVAudioPlayerNodeを作成して、AVAudioEngineに装着します。AVAudioPlayerNodeはファイルやバッファからのオーディオ再生を行うためのNodeです。
[3] Node 同士を繋ぐ
[2]で作成したAVAudioPlayerNodeとAVAudioEngineが内部に持つミキサーNodeを接続します。これでファイルを再生するためのNodeは全て接続完了になります。
[4] Engine の処理を開始する
Audio Engine の処理を開始します。Nodeの接続等に問題がなければこれで準備完了です。
[5] オーディオファイルの再生をスケジュールする
AVAudioPlayerNodeのscheduleFile:atTime:completionHandler:メソッドを使用してオーディオファイルの再生をスケジュールします。
- (void)scheduleFile:(AVAudioFile *)file atTime:(AVAudioTime *)when completionHandler:(AVAudioNodeCompletionHandler)completionHandler
今回は引数whenにnilを指定しているので(また、他に再生がスケジュールされてもいないので)、「すぐに再生する」という処理がスケジュールされます。時間を指定することで指定時間後に再生することもできます。
[6] 再生する
[5]でスケジュールされた処理が実行されます。
[7] ボリュームを操作する
AVAudioPlayerNodeのvolumeプロパティの値を変更することでボリュームを操作します。
[8] パンを操作する
AVAudioPlayerNodeのpanプロパティの値を変更することでパン(音の定位)を操作します。
実装 (バッファから再生する)
変更する部分は以下の通りです。
[9] オーディオバッファを用意する
viewDidLoadメソッドの12〜15行目にコードを追加します。オーディオファイルからAVAudioPCMBufferオブジェクトを作成しています。作成したAVAudioPCMBufferオブジェクトはplayメソッドのほうで使用します。
- (void)viewDidLoad { [super viewDidLoad]; self.engine = [AVAudioEngine new]; NSString *path = [[NSBundle mainBundle] pathForResource:@"loop.m4a" ofType:nil]; self.audioFile = [[AVAudioFile alloc] initForReading:[NSURL fileURLWithPath:path] error:nil]; // [9] オーディオバッファを用意する AVAudioFormat *audioFormat = self.audioFile.processingFormat; AVAudioFrameCount length = (AVAudioFrameCount)self.audioFile.length; self.audioPCMBuffer = [[AVAudioPCMBuffer alloc]initWithPCMFormat:audioFormat frameCapacity:length]; [self.audioFile readIntoBuffer:self.audioPCMBuffer error:nil]; // self.audioPlayerNode = [AVAudioPlayerNode new]; [self.engine attachNode:self.audioPlayerNode]; AVAudioMixerNode *mixerNode = [self.engine mainMixerNode]; [self.engine connect:self.audioPlayerNode to:mixerNode format:self.audioFile.processingFormat]; NSError *error; [self.engine startAndReturnError:&error]; if (error) { NSLog(@"error:%@", error); } }
[10] オーディオバッファの再生をスケジュールする
オーディオバッファからの再生なので、AVAudioPlayerNodeのscheduleBuffer:atTime:options:completionHandler:メソッドを使用して再生をスケジュールします。
- (void)scheduleBuffer:(AVAudioPCMBuffer *)buffer atTime:(AVAudioTime *)when options:(AVAudioPlayerNodeBufferOptions)options completionHandler:(AVAudioNodeCompletionHandler)completionHandler
画面上のスイッチのON/OFFによって、AVAudioPlayerNodeBufferOptions型のoptions引数の値だけ異なるものを指定しています。
内容 | |
---|---|
AVAudioPlayerNodeBufferLoops | バッファを無限にループ |
AVAudioPlayerNodeBufferInterrupts | 再生中のバッファを中断させる |
AVAudioPlayerNodeBufferInterruptsAtLoop | 再生中のバッファの1ループが終了するまで待つ |
- (void)play { // [10] オーディオバッファの再生をスケジュールする if (self.loopSwitch.isOn) { [self.audioPlayerNode scheduleBuffer:self.audioPCMBuffer atTime:nil options:AVAudioPlayerNodeBufferLoops completionHandler:nil]; } else { [self.audioPlayerNode scheduleBuffer:self.audioPCMBuffer atTime:nil options:AVAudioPlayerNodeBufferInterrupts completionHandler:nil]; } // Start playback [self.audioPlayerNode play]; }
まとめ
本記事では、AVFoundationのAVAudioEngineやAVAudioPlayerNodeを使用して簡易的な音楽再生アプリを実装してみました。 今回作成したサンプルプロジェクトは以下のリポジトリで公開していますので動かしてみてください。