Swift Playgroundsで学ぶiOSプログラミング

ABリピートも可能なレベルメーター付きオーディオプレーヤーを作る

文●柴田文彦 編集●吉田ヒロ

2017年10月24日 17時00分

リピート再生を実現する

 A点、B点のボタンがタップされた際の処理を担当するposAButonTappedとposBButtonTappedの各メソッドは、当然ながらいずれもビューコントローラー内に記述します。

A点とB点を設定するためのメソッドをビューコントローラー内に追加します。いずれも、その時点のプレーヤーの再生時間を、それぞれの値としてコピーしています。値が初期値から動いたらリピート再生モードをオンにする処理も共通です

 A点では、プレーヤーの現在の再生位置(秒)をpositionAに代入し、その値を「分:秒」のフォーマットでラベルに表示します。またその値が0.0でなければ、リピートモードを表すrepPlayをtrueに設定しています。

 B点も同様に、現在の再生位置をpositionBに代入し、ラベルに表示します。その値が音楽ファイルの長さ、つまり再生位置の最後尾に一致していない場合は、リピートモードをtrueにします。なお、B点の値をプレーヤーの再生位置(currentTime)そのものにせずに、スライダーの位置から計算しているのは、スライダーが最後尾にあっても、プレーヤーのcurrentTimeの値が0.0になってしまう場合があるからです。

 再生位置を表す秒の数値(Double)を「分:秒」の文字列に変換する処理がよく出てくるので、後の方で記述するformatTimeというメソッドを作りました。前回に記述したtSliderMovedのメソッドも、それを使うように書き換えています。

 実際のリピート処理は、時間スライダーの位置をアップデートするupdateSliderメソッドの中で実行しています。といっても非常に簡単で、リピートモードがtrueのときにプレーヤーの再生位置(currentTime)がB点の値を超えたら、再生位置を強制的にA点に戻すだけです。これによって、A点とB点の間の再生を繰り返すようになります。

実際のリピート再生処理は、時間スライダー位置のアップデート処理の中で実行させます。処理は非常に単純で、プレーヤーの再生時間がB点を通過したら、強制的にA点に戻すだけです

 なお、この図には秒の数値を「分:秒」の文字列に変換するformatTimeメソッドも含まれています。中身は、前回に書いたDateComponentFormatterクラスを利用したものとまったく同じです。

 以上のプログラムで、ABリピート機能が実現できました。外国語学習にも、演奏の耳コピーにも便利に使えるでしょう。

ABリピート機能を実現したオーディオプレーヤーの動作中の画面です。A点とB点の設定は、再生中でも可能です。再生停止中に時間スライダーを任意の場所に動かしてからボタンをタップして設定することもできます

プログレスバーをレベルメーターとして代用する

 次にレベルメーター機能を追加しましょう。すでに述べたようにメーター自体はプログレスバー(UIProgressView)で代用します。ビューコントローラーの先頭部分で、2チャンネルの音楽ファイルを想定してlPowerとrPowerの2本を用意します。チャンネルの名前を表示するラベル、lMarkとrMarkも追加します。

レベルメーターを追加するために、2本のプログレスバー(UIProgressView)と、それぞれのチャンネルを明示するためのラベルを追加しています

 viewDidLoadメソッドの中では、その2組のラベルとプログレスバーも初期設定しておきます。チャンネル名は文字の両側をスペースで挟んで「 L 」と「 R 」にしました。ラベルの背景色は黄色、プログレスバーの溝の部分の色は緑にしています。ここは好きな色に設定してください。

追加したプログレスバーとラベルの初期設定は、viewDidLoadメソッドの中で実行します。ラベルには背景色を付けて、1文字のラベルがそれなりに目立つようにしています。そのラベルの背景色は黄色、プログレスバーのティント色は緑に設定しています

 AVAudioPlayerの再生中の音声のレベルを知るためには、あらかじめ設定しておかなければならないことが1つあります。それは、プレーヤーのプロパティ、isMeteringEnabledをtrueに設定しておくことです。これもviewDidLoadメソッドの最後に設定します。

再生中のオーディオのレベルを測定するためには、プレーヤーのisMeteringEnabled属性を、あらかじめtrueに設定しておく必要があります

 2組のラベルとプログレスバーのレイアウトも、viewWillLayoutSubviewsメソッドの中で設定します。この例では、ABリピートのためとボタンとラベルのさらに下に、LチャンネルとRチャンネルが縦に重なるように配置してみました。

レベルメーター用のプログレスバーは、スライダーと同様に横長なので縦に2つ並べて配置しました。それぞれラベルは、バーの左端に近い位置に対応するバーに揃えて配置しています

平均パワーの測定結果を連続表示する

 レベルメーターの表示自体には直接関係ありませんが、時間スライダーの表示をアップデートするのに使っているコアアニメーションのタイマーの設定も変更しておきます。前回は、スライダーの更新だけだったので1秒に1回で十分ということで、preferredFramesPerSecondを1に設定していました。レベルメーターの更新には、これでは更新(フレーム)レートが低過ぎて、カクカクした動きになってしまいます。これは1秒に10回を表す10にしてみました。再生ボタンのタップを処理するpButtonTappedメソッドの最後に近い部分の設定を変更します。

実際のレベルメーターは、時間スライダーの位置の更新のついでに新しいレベルに書き換えることにします。前回は、時間スライダーの更新を1秒に1回に設定しました。それだとレベルメーターの更新にはインターバルが長すぎるので、1秒に10回更新するように変更します

 実際にレベルメーターを更新するコードは、時間スライダーの位置を更新するupdateSliderメソッドの中に追加します。レベルメーターに表示する値を取得する直前にプレーヤーのupdateMetersメソッドを実行してから、averagePowerメソッドでチャンネルを指定して読み込みます。読み取った値はデシベル値なので、簡単な式で0.0〜1.0の間の値に変換し、それをそのままプログレスバーの値として書き込んでいるだけです。アニメーションをオンにしているので、適当に補間が入ってスムーズなバーの動きになります。

再生中のオーディオのレベルは、プレーヤーのupdateMetersメソッドを実行することで更新されますが、その値はaveragePowerメソッドを使ってチャンネルごとに取得します。これは平均値ですが、他にピーク値を取得するpeakPowerメソッドもあります

 実際に動かしてみると、再生音に応じてレベルメーターが動くようすが観察できます。

以上の操作で、Lチャンネル、Rチャンネルそれぞれのレベルを表示するメーターを装備したオーディオプレーヤーができました。レート(再生速度)の調整やABリピート機能もあるので、いろいろな用途に利用できるのではないでしょうか

 なお、このプログラムではプログラム内のボリュームスライダーの値に関係なく、元の音楽ファイルの再生レベルを表示しています。もしボリューム設定を反映したレベルメーターにしたい場合は、レベルメーターのプログレスバーの値に、ボリューム(vSlider)の値を掛け合わせるだけで実現できます。パンの設定につても同様です。

次回の予定

 3回にわたって作り込み、徐々に機能を追加してきたオーディオプレーヤーですが、とりあえず今回で完成したものとします。オーディオの次はビデオ、ということで次回はビデオプレーヤーの作成取り組みたいと考えています。これについても、フレームワークの選択により、いろいろなレベルのものが考えられますが、とりあえずはもっとも簡単に実現できるものから入るつもりです。

■今回作ったプログラム

mobileASCII.jp TOPページへ