PR

【Flutter】audioplayersで低遅延で再生する方法

Flutter audioplayersを低遅延で再生 ソフトウェア

Flutterの音声ファイル再生パッケージ、audioplayersは軽量で使い勝手が良いパッケージです。簡単なBGMや効果音の再生を行う場合には設定の手間が少なく便利に扱えます。

audioplayers | Flutter package
A Flutter plugin to play multiple audio files simultaneously

開発中、効果音を高速に連続再生したい場面があり、通常の使い方では再生までの遅延が気になったため行った対策を紹介します。

確認環境

  • Flutter 3.32.7
  • audioplayers 6.5.0

setPlayerMode()の設定

playerを低遅延モードに設定することができます。

Dart
final _audioPlayer = AudioPlayer();
_audioPlayer.setPlayerMode(PlayerMode.lowLatency);

setSource()とresume()でファイルを事前読み込みし再生する

audioplayersの基本的な再生方法としては以下のようにplay()メソッドにファイルへのパスを指定します。ただしこの方法はplay()メソッドを呼ぶ時にソースが決定されるため、再生までに若干の遅延が生じることがあります。

Dart
final _audioPlayer = AudioPlayer();
_audioPlayer.play(AssetSource('ファイルへのパス'));  // この時点でソースが決定し、読み込みが開始される

それを避けるためには事前にファイルを読み込むようにします。

Dart
final _audioPlayer = AudioPlayer();

// initState()内等で以下の事前読み込みを行っておく
_audioPlayer.setSource(AssetSource('ファイルへのパス'));

// 再生が必要な箇所でresume()を呼ぶことで低遅延で再生できる
_audioPlayer.resume();

setSource()メソッドによりファイルを事前に読み込むことができます。このコードはあくまで例なので連続して書いていますが、実際にはファイルが固定であれば事前読み込みはinitState()等で行うのが良いでしょう。

setSource()で読み込んだあとはresume()メソッドで再生できます。

なお複数の音声ファイルを使いたい場合、以下のようにインスタンス自体を分けるのが良いと思います。インスタンスを使い回し動的に切り替える実装も考えられますが、ファイル数が極端に多いような場合を除き1インスタンス1ファイルとした方がパフォーマンスは向上すると考えられます。

Dart
// 音声ファイル毎にインスタンスを作成する
final _audioPlayer1 = AudioPlayer();
final _audioPlayer2 = AudioPlayer();

_audioPlayer1.setSource(AssetSource('ファイル1へのパス'));
_audioPlayer2.setSource(AssetSource('ファイル2へのパス'));

連続して再生する場合

setSource()とresume()で再生を行った場合、デフォルトだと再生終了時に再生位置がファイル末尾で止まってしまい、再度resume()を呼んでも再生できません。

そのため再生終了時に自動で再生位置が先頭に戻るようにします。

Dart
final _audioPlayer = AudioPlayer();
// initState()内等で以下の設定を行っておく
_audioPlayer.setReleaseMode(ReleaseMode.stop);

また、効果音の再生中に次の再生が行われる場合への対策として、以下のようにresume()前に再生位置を先頭に戻すようにします。(なおstop()を使うとソースがリセットされてしまうようで、再度resume()ができなくなるようです。)

Dart
_audioPlayer.seek(Duration.zero);
_audioPlayer.resume();

私の確認環境においては上記コードで動作しましたが、より万全を期すなら以下のように再生中かどうかの確認と、確実に処理を完了させるawaitを付与すると良いかもしれません。

Dart
if (_audioPlayer.state == PlayerState.playing) {
  await _audioPlayer.seek(Duration.zero);  // seek()が完了する前にresume()が呼ばれないようawaitする
}
await _audioPlayer.resume();

コメント

タイトルとURLをコピーしました