【大苦戦】【個人開発】ダイアル錠のアプリに音を追加してみた【Flutter】

こんにちは、海岸蒼です。

本記事では、ダイアル錠を再現したアプリにダイアルを回したときの効果音を追加したのでその報告と、
Flutterで音を追加する際の注意ポイントについて記載します。

<h2>ダイアル錠を再現したアプリ</h2>

Brute Forceというモバイルアプリを作成しました。

<img width="500" alt="TwitterPR.001.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/037767c9-2f41-2da5-bd05-bcdaf667fd74.jpeg">

詳しくはこちらの記事をご覧ください。

https://qiita.com/Umigishi-Aoi/items/93aabf5f0cd43410498f

見た目は再現したものの、何か一味足りない状態でした。
それを解決したのが今回のアップデートです。

・　ダイアルを回した時
・　開けるボタンを押して開かなかった時
・　開けるボタンを押して開いた時
の3つのタイミングで効果音を追加しました。

作者としては、この音の追加で再現度が数倍上がったと思っています。

実際に触って確かめてもらえると幸いです。

DLリンクはこちら
iOS : https://apps.apple.com/jp/app/brute-force-puzzle/id1586258328
Android : https://play.google.com/store/apps/details?id=com.zerokaraapp.bruteforce

<h2>Flutterで音を追加する際の注意ポイント</h2>

今回の効果音追加、かなり苦戦しました。
実装に苦戦したというよりは、実装後の音質の変化や動作の重さの解消にです。

あまりに苦戦した結果、リリースには音を追加しなかったほどです。

このノウハウを共有すべく、以下の3点について記載します。

・　使ったパッケージ
・　問題１：動作の重さ
・　問題２：音質の変化

<h3>使ったパッケージ</h3>
audioplayersというパッケージを使用しました。

https://pub.dev/packages/audioplayers

このパッケージのAudioCasheというクラスを使うと、プロジェクトフォルダ内に保存したmp3の音声データを
簡単に再生することが可能です。

使い方の紹介記事はたくさんありますが、一つ紹介しておきます。

https://cbtdev.net/flutter-audiocache/

今回のアプリではこちらを使って効果音を追加しました。

<h3>問題１：動作の重さ</h3>

上のパッケージ追加で音は鳴るようになったのですが、ここで問題が発生しました。

何回も音を鳴らしていると、途中で動作が固まってしまうのです。

今回のアプリではダイアルを回した時に音が鳴るように設定しています。
仕様上、ダイアルは何回も回すのでここで動作が固まってしまうようだと、
アプリとして成り立たなくなってしまします。

この原因と解決策ですが、こちらの記事で解説されていました。

https://zenn.dev/asteroid/articles/31104fc13cd19b256381

音を鳴らすたびAudioPlayerを生成してしまうのを防ぐため、
**あらかじめAudioCasheにAudioPlayerを渡しておく**、というのが解決策です。

この解決策を取り入れたのが、以下のコードとなります。
(Riverpodを使った実装となります。)

```dart
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'app.dart';

//AudioCashインスタンスのプロバイダー
final audioCasheProvider = StateProvider((ref) {
  Map _audioMap= Map();
  return _audioMap;
});

void main(){

　　Map _audioMap = Map();
  _audioMap.addAll({
    'kachi.mp3' : AudioCache(fixedPlayer: AudioPlayer(mode: PlayerMode.LOW_LATENCY)),
    'false_open.mp3' : AudioCache(fixedPlayer: AudioPlayer(mode: PlayerMode.LOW_LATENCY)),
    'success_open.mp3' : AudioCache(fixedPlayer: AudioPlayer(mode: PlayerMode.LOW_LATENCY)),
  });

　　runApp(
    ProviderScope(
      overrides: [
        audioCasheProvider
            .overrideWithProvider(StateProvider((ref) => _audioMap)),
      ],
      child: const BruteForce(),
    ),
  );
```

実際のコードから切り抜いてきたので一部整合性が取れていないかもしれませんが、
このような形で実装しました。

音声を再生するときは、

```dart
ref.read(audioCasheProvider).state['kachi.mp3'].play('kachi.mp3',isNotification: true);
```

とplayメソッドを呼び出します。

これで実装するとある程度無茶なダイアルの回し方をしても固まることがなくなりました。

ただ、次の問題が発生してしまうのです。

<h3>問題２：音質の変化</h3>

上の問題を解決するためにAudioPlayerを渡す実装をすると、
元の音源に対して明らかに音質が変わってしまいます。

「カチッ」という音が「ボソッ」という音になる感じです。

これだと、狙いの音が出ないので困ってしまいました。

解決策は**再生する音声データの先頭部分に空白を作る**ことでした。

感覚的なんですが、「ボソッ」という音、音声データの先端が切り取られた音に聞こえたんですよね。
試しに音声データを加工して、先端に0.06秒ほどの空白を入れて実装してみました。

すると、見事に「カチッ」という音が復活しました。

原因は不明ですが、AudioPlayerを渡す実装をすると先端の音が切り取られてしまうようです。

対症療法的な解決ですが、大事な音が切り取られないよう先端に空白を入れる、というのが解決策となります。

<h2>まとめ</h2>

今回はダイアル錠を再現したアプリにダイアルを回したときの効果音を追加したのでその報告と、
Flutterで音を追加する際の注意ポイントについて記載します。

最初のリリースに入れるのを諦めるほどに苦戦した問題でしたが、
無事解決して実装することができて良かったです。

この効果音の追加結果については実際に触って確かめてみてください。
本当にダイアル錠を回してる感覚を味わえますよ！

最後に、もう一度ダウンロードリンクを紹介して、この記事を終わります。
読んでいただき、ありがとうございました。

iOS : https://apps.apple.com/jp/app/brute-force-puzzle/id1586258328
Android : https://play.google.com/store/apps/details?id=com.zerokaraapp.bruteforce