くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Flutterで音声再生(audioplayers)

FlutterでもBGMや効果音を再生したいなと思い、
いろいろ調べてみたときの備忘録(*´ω`*)

参考サイトを見てみると、いろいろ違いがありそうだけど、
公式のCasual Games Toolkitで紹介されていたaudioplayersを試してみる。

インストール

公式ドキュメントはGitHubのmdファイルっぽいので、
このあたりを見ながら、進める感じ。

$ fvm flutter pub add audioplayers

基本的な使い方

// ** playerを作成
final player = AudioPlayer();

// ** 音声ファイルの設定
await player.setSource(AssetSource('sounds/coin.wav'));
await player.setSourceUrl(url);
await player.play(DeviceFileSource(localFile));

// ** 再生などの操作
await player.resume();
await player.seek(Duration(milliseconds: 1200));
await player.pause();
await player.stop();
await player.dispose();

// ** 音量などの設定
// 音量
await player.setVolume(1.0);       //  0.0〜1.0
// 左右のバランス
await player.setBalance(0.0);      // -1.0〜1.0
// 再生速度
await player.setPlaybackRate(1.0);
// プレイバック時の動作
await player.setReleaseMode(ReleaseMode.stop);   // 停止
await player.setReleaseMode(ReleaseMode.release); // リソースの開放
await player.setReleaseMode(ReleaseMode.loop); // ループ再生

// ** PlayerMode
// BGMなど長いメディアやストリーム再生向け(default)
await player.setPlayerMode(PlayerMode.mediaPlayer);
// 効果音など短い音声ファイル向け。UIの描画への影響が軽減されるらしい
await player.setPlayerMode(PlayerMode.lowLatency);

stop()pause()の違いは、

  • stopの場合は、最初から再生される
  • pauseの場合は、途中から再生される

Tips

小ネタいろいろ

複数の音を同時に再生する

1playerで1音声なので、同時に再生したい数だけ
playerインスタンスを用意すればOK

// BGM用
final bgmPlayer = AudioPlayer(playerId: "bgm");
// 効果音用(sound effects / SE)
final sfxPlayer = AudioPlayer(playerId: "sfx");

Super Dashだと、BGMで1つ、効果音は種類ごとにplayerを作成してる。

マナーモードでは音がならないようにする

デフォルトだと、マナーモードでも音がなるので、
マナーモードだと無音になるようにするための設定が必要。

// AudioContextConfigでマナーモードの尊重をする設定に
const audioContext = AudioContextConfig(respectSilence: true).build();
// or 個別で指定する
const audioContext = AudioContext(
  iOS: AudioContextIOS(
    category: AVAudioSessionCategory.ambient, // .palybackから変更
  ),
  android: AudioContextAndroid(), // Androidでは該当する設定なし
);

// 反映
await AudioPlayer.global.setAudioContext(audioContext);

画面を閉じたら停止する

アプリを終了せずに、アプリを閉じると、
バックグラウンドで動作し続けるため、
音声だけ流れる状態が起きる。

AppLifecycleStateを参照してコントロールが必要

final AppLifecycleState state = // ...
switch (_lifecycleNotifier!.value) {
  // アプリを閉じる場合
  case AppLifecycleState.paused:
  case AppLifecycleState.detached:
    // TODO: すべての音声を停止する
  
  // アプリを表示する場合
  case AppLifecycleState.resumed:
    // TODO: 音声を開始/再開する
  
  // 閉じる途中の状態は、何もしない
  case AppLifecycleState.inactive:
  case AppLifecycleState.hidden:
    break;
}

事前読み込み/キャッシュに保存

効果音など、すぐに使いたい音声の場合、
事前に読み込んでおくことができる。

await AudioCache.instance.loadAll([
  "sound/xxx.mp3",
  "sound/yyy.wav",
]);

連打時の効果音

再生中に再度playを実行しても、同じ音声だと再生されない。

なので、再生中かどうかを見て、処理を分けるとよい

if (player.state == PlayerState.playing) {
  // 再生中なら最初の位置に戻す
  await player.seek(Duration.zero);
} else {
  // それ以外の場合は、再生する
  await player.play(AssetSource("xxx.mp3"));
}

以上!! これでなんかいろいろできそう(*´ω`*)

参考にしたサイト様