音を極めろ！AudioMixer

※この記事は2024年11月8日のUnity⚪︎⚪︎完全理解したイベント、Unityエフェクト完全理解したの登壇資料です。
![](https://storage.googleapis.com/zenn-user-upload/5b23dfa69291-20241107.png)
## **AudioMixerを使う**

AudioMixerは、視覚的な操作インターフェースを通じて、音響経験が少ないプログラマーにも音声ミキシングやエフェクト処理を直感的に行えるようにしたツールです。音響設定のプロフェッショナルな管理ができることで、ゲームにおける豊かなサウンド体験を提供でき、プレイヤーの没入感が向上します。


### **AudioMixerとは？**

Unityの**AudioMixer**は、音声を効果的に管理・制御するためのツールです。
これは、複数の音源（BGM、効果音、ボイスなど）をまとめてミキシングし、音量やエフェクトの調整を視覚的に行える機能を提供します。実際のオーディオミキサーを模したインターフェースで、音声の流れやエフェクトの適用を直感的に理解しやすくなっており、特に音響経験のないプログラマーにも扱いやすい設計になっています。

### **AudioMixerの特徴と利点**

1. **視覚的な操作が可能**  
   - 音源のグループ化やエフェクトの適用を、視覚的に確認しながら操作できます。
   - ドラッグ＆ドロップやスライダーで音量やエフェクトのパラメータをリアルタイムに調整し、変更結果を即座に確認可能です。

2. **エフェクトの適用とスナップショット機能**  
   - 音源にリバーブ、エコーなどのエフェクトを追加でき、シーンや状況に応じて音響の雰囲気をダイナミックに変更できます。
   - スナップショット機能により、ミキサーの設定状態を保存・切り替えることができ、場面に応じた音響効果の演出が容易に行えます。

3. **プログラマーにとっての操作性**  
   - プログラマーが実際のミキサーに不慣れでも、音声の流れを視覚的に把握できるため、音響処理の基礎を理解しやすくなります。
   - スクリプトから音量やエフェクトの強度を動的に制御することができ、リアルタイムでの音声操作が可能です。

4. **プロフェッショナルな音響体験の提供**  
   - 音量バランスやエフェクトを一貫して管理でき、ゲーム全体のオーディオ品質を向上させます。
   - サウンドデザイナーとプログラマーが同じツールを使用することで、コミュニケーションが円滑になり、音響面での品質向上が期待できます。
![](https://storage.googleapis.com/zenn-user-upload/10f60c72611e-20241107.png)
## AudioSourceのエフェクト
**AudioSourceで使用する各フィルターやリバーブのコンポーネント**と、**AudioMixerで使用するエフェクト**にはいくつかの違いがあります。
主に、**適用範囲、目的、管理方法**の面で異なります。

- **AudioSourceのエフェクト**は、特定のサウンドに対する個別のエフェクトに向いており、より細かいカスタマイズが必要な場合に使われます。
- **AudioMixerのエフェクト**は、複数のサウンドをまとめて管理・調整するのに向いており、ゲームの状況やシーンに応じた統一された音響設定を簡単に行えます。

用途に応じて、AudioSourceとAudioMixerのエフェクトを使い分けることで、効率的に音響演出を管理できます。

### 1. **適用範囲の違い**

- **AudioSourceのエフェクト**：
  - 個々のオーディオソースに対してフィルターやリバーブを適用します。
  - 例えば、特定のキャラクターの声や特定のサウンドエフェクトにのみリバーブやフィルターをかける場合に便利です。

- **AudioMixerのエフェクト**：
  - AudioMixerグループ単位で適用されるため、複数のAudioSourceにまとめてエフェクトをかけることができます。
  - BGMや環境音、効果音などのカテゴリーごとにエフェクトを適用したいときに便利です。

### 2. **目的の違い**

- **AudioSourceのエフェクト**：
  - **個別のサウンドに対する特定のエフェクト調整**が目的です。キャラクターが特定の位置や状態にいる場合にだけエフェクトをかけるなど、特定のサウンドに限定して設定を細かく調整できます。

- **AudioMixerのエフェクト**：
  - **シーン全体やカテゴリ単位での統一されたエフェクト調整**が目的です。例えば、複数の効果音グループにまとめてフィルターをかけ、シーン全体の効果音を一括して制御することができます。ゲームのシーンやシチュエーションに応じて、音響の雰囲気を統一する際に適しています。

### 3. **管理方法の違い**

- **AudioSourceのエフェクトの管理**：
  - 各AudioSourceに直接コンポーネントとして追加されます（例：**Audio Low Pass Filter**、**Audio High Pass Filter**、**Audio Reverb Filter**）。
  - コンポーネントを直接AudioSourceに加えるため、エフェクト設定がそのAudioSource専用となり、個別に管理されます。
  - AudioSourceの設定変更はスクリプトでも可能ですが、他のサウンドと統一された操作は難しく、複数のサウンドに同時に変更を加えたい場合には手間がかかります。

- **AudioMixerのエフェクトの管理**：
  - AudioMixerグループにエフェクトを追加し、**AudioMixerウィンドウ**で調整します。AudioMixerにはスナップショット機能もあり、設定の切り替えも容易です。
  - 複数のAudioSourceが同じAudioMixerグループにルーティングされている場合、グループ全体にエフェクトをかけられるため、統一された音響設定を適用できます。
  - プログラムからもエクスポーズパラメータを通じてエフェクトを一括制御できるため、シーンの変化に応じてエフェクトを柔軟に管理できます。

### 4. **適用可能なエフェクトの種類**

- **AudioSourceのフィルター**：
  - 主に**Audio Low Pass Filter**、**Audio High Pass Filter**、**Audio Reverb Filter**などのシンプルなフィルターが用意されています。これらのフィルターは特定のサウンドだけに簡易的な効果を付けるためのものです。

- **AudioMixerのエフェクト**：
  - AudioMixerでは、リバーブやフィルターに加え、**コンプレッサー**や**イコライザー**、**ディレイ**など、より幅広いエフェクトを使用できます。これにより、複雑な音響効果の調整や、サウンドグループ全体の処理が可能になります。

なので、個別の音源AudioClipをMixしたい場合はAudioSource単位で、全体のトーナリティーバランスを触りたい場合はAudioMixerといった具合です。
![](https://storage.googleapis.com/zenn-user-upload/cadd974da65b-20241107.png)
## スナップショットという機能

AudioMixerの機能であるスナップショットは、**各グループの音量・ピッチ調整、エフェクトのパラメータ設定、ルーティング状態の保存・呼び出し**に最適な機能です。
ギターのエフェクターペダルをイメージしてもらえるとわかりやすいかと。

しかし、エフェクトの追加・削除やグループ構成の変更、エフェクトの適用順序の変更など、構造に関わる部分の操作はスナップショットでは扱えません。

### **スナップショットでできること**

1. **音量やピッチの調整**  
   - 各グループ（バス）ごとの音量やピッチの設定を保存して、必要に応じてスナップショットで切り替えることが可能です。
   - 例えば、戦闘シーンではBGMの音量を上げ、日常シーンでは控えめにするといった調整ができます。

2. **エフェクトのパラメータ設定**  
   - リバーブ、フィルター、イコライザーなど、各エフェクトの強度や周波数の数値設定をスナップショットに保存できます。
   - これにより、洞窟内ではリバーブを強めて反響音を再現し、屋外ではエフェクトを控えめにして自然な音響にする、といった環境に応じた音響の変化が可能です。

3. **グループごとのルーティング状態の管理**  
   - スナップショットを通じて、どの音源がどのグループに属し、どのグループがどのようにルーティングされているかを保存しておけます。
   - シーンによってルーティングを変更し、特定の音声のみを強調するといった設定を活用できます。

4. **エフェクトのオン・オフの切り替え**  
   - 各エフェクトを有効化・無効化する状態もスナップショットに保存されます。
   - 例えば、緊張感を演出する場面ではフィルターをオンにし、普段はオフにすることで、音声の質感をシーンごとに変えることができます。

### **スナップショットでできないこと**

1. **エフェクトそのものの追加・削除**  
   - スナップショットでエフェクトのパラメータやオン・オフは変更できますが、エフェクト自体を追加したり削除したりすることはできません。エフェクトの構成を変更するには、AudioMixer自体を調整する必要があります。
   - 例：新しいリバーブエフェクトを追加するには、スナップショットではなくAudioMixerの設定自体を変更する必要があります。

2. **新しいグループ（バス）の追加・削除**  
   - スナップショットではグループ構造自体を変えることはできません。あらかじめ必要なグループ構成を作成し、その中で音量やエフェクトのパラメータを設定・保存しておく必要があります。
   - 例：新しいサウンドグループを追加したい場合は、ミキサーの編集が必要です。

3. **エフェクトの順序の変更**  
   - スナップショットはエフェクトの適用順序を変更することができません。エフェクトの順序を変えたい場合は、最初にAudioMixer上でエフェクト順を設定しておく必要があります。
   - 例：フィルターをリバーブの後に適用したい場合、スナップショットでは対応できないため、ミキサー内でのエフェクト順を手動で変更する必要があります。
![](https://storage.googleapis.com/zenn-user-upload/5b23dfa69291-20241107.png)
## エフェクター

### **1. ハイパスフィルター (High-Pass Filter)**

**ハイパスフィルター**は、低い周波数の音をカットし、高い周波数の音だけを通すフィルターです。特定の音の「こもり感」を減らしたり、環境音に明瞭感を与えたりするために使用されます。

- **使い方のポイント**：
  - **カットオフ周波数**：カットオフ周波数を設定し、その周波数よりも低い音をカットします。カットオフ周波数を上げるとより多くの低音がカットされ、スッキリした印象の音になります。
  - **適用シーン例**：車内や狭い空間の音響効果を演出する際、音声の低音をカットすることでクリアな音声表現を実現できます。

### **2. ローパスフィルター (Low-Pass Filter)**

**ローパスフィルター**は、逆に高い周波数の音をカットし、低い周波数の音だけを通すフィルターです。これにより、音が「こもった」感じになり、距離感や障害物越しの音響を表現するのに便利です。

- **使い方のポイント**：
  - **カットオフ周波数**：こちらもカットオフ周波数を設定し、その周波数よりも高い音をカットします。カットオフ周波数を下げることで、より高音成分がカットされ、音がよりこもった感じに聞こえます。
  - **適用シーン例**：壁越しや水中での音響、遠くの物音など、明瞭さを落としたい状況で使います。

### **3. リバーブ (Reverb)**

**リバーブ**は、音に残響を加えるエフェクトで、空間の広さや反響具合を表現するために使います。リバーブを使うことで、音が室内で反響しているような効果や、広大な場所での余韻を演出することができます。

- **使い方のポイント**：
  - **ルームサイズ**：空間の広さを設定します。広い部屋や大聖堂のような空間を表現したい場合は、ルームサイズを大きく設定します。
  - **デシケイ**（音の減衰時間）：反響音が消えるまでの時間を設定します。長くすることで音が持続し、残響が豊かな印象になります。
  - **適用シーン例**：洞窟やホールの音響、広い空間での演出などに使います。
![](https://storage.googleapis.com/zenn-user-upload/3e3d34d880cc-20241107.png)
## エフェクトのパラメーター(変数)

### フィルターとリバーブの値

**レゾナンス**（または「Q値」）は、ハイパスフィルターやローパスフィルターにおける**特定の周波数の強調**を表すパラメータです。これにより、カットオフ周波数付近の音が強調され、エッジの効いたサウンド効果を作り出すことができます。
レゾナンスは、フィルターで音を単にカットするだけでなく、カットオフ周波数付近にピークを持たせ、音に独特のキャラクターを加えるためのパラメータです。

### **レゾナンスの役割と使い方**

- **カットオフ周波数の強調**：レゾナンスを上げると、カットオフ周波数の周辺音が強調され、独特のピークが発生します。この強調効果は、特に電子音やフィルターをかけたボイスなど、際立たせたいサウンドで有用です。

- **高レゾナンスの効果**：高めのレゾナンスを設定すると、カットオフ周波数の周辺が鋭く強調され、より尖った音や「鳴り」を感じさせる音になります。この効果は、シンセサイザーのフィルター操作でよく使われ、音に存在感を与えたい場合に便利です。

- **低レゾナンスの効果**：レゾナンスを低くすると、周波数の強調が減り、カットオフ周波数で音がスムーズに減衰していきます。より自然な音のカットにしたい場合や、控えめなフィルター効果を狙いたい場合に適しています。

### **実用例**

- **ローパスフィルターでの使用**：
  - 高い周波数のカットに加え、カットオフ周波数付近にレゾナンスを加えると、こもった音の中に特徴的なピークが生まれ、独特なムードやエフェクトを作り出せます。
  - 例：キャラクターが遠くから話しかける声にローパスフィルターをかけ、少しレゾナンスを加えて特徴的なこもり音を演出する。

- **ハイパスフィルターでの使用**：
  - 低音をカットしつつレゾナンスを上げることで、特定の高音成分が際立つシャープなサウンドにできます。
  - 例：背景音としてかすかに聞こえる風の音にハイパスフィルターをかけ、レゾナンスを少し加えることで風切り音を強調する。

以下に、`AudioMixerManager`、`AudioSnapshotEnumGenerator`、およびそれらの使用方法と仕組みを含むドキュメントをまとめました。このドキュメントは、各クラスの目的と設定方法、主な機能を分かりやすく説明することを目的としています。
![](https://storage.googleapis.com/zenn-user-upload/e6411efff4e7-20241107.png)
## スナップショット切り替えコード

このドキュメントは、Unityの`AudioMixer`のスナップショット管理を効率化するためのシングルトンパターンに基づいた`AudioMixerManager`クラス、スナップショット名をenumとして自動生成する`AudioSnapshotEnumGenerator`、およびテストとデバッグ用の`AudioMixerTester`クラスについて説明します。

- **`AudioMixerManager`** : シングルトンとして`AudioMixer`のスナップショットを一元管理し、簡単に切り替えが可能。
- **`AudioSnapshotEnumGenerator`** : `AudioMixerSnapshot`の名前を列挙型(enum)として自動生成し、コードでのスナップショット指定を効率化。
- **`AudioMixerTester`** : 現在のスナップショットの状態を画面上で確認し、デバッグのために手動でスナップショットを切り替えるインターフェースを提供。

### クラス一覧
1. `AudioMixerManager`: シングルトンパターンを使用し、`AudioMixer`のスナップショットを簡単に切り替えられるマネージャークラス。
2. `AudioSnapshotEnumGenerator`: プロジェクト内の`AudioMixerSnapshot`を列挙型(enum)として自動生成するエディタ拡張。
3. `AudioMixerTester`: 現在アクティブなスナップショットを画面上で可視化し、簡単に切り替えをテストできるデバッグ用クラス。

## 1. AudioMixerManager

### クラスの目的
`AudioMixerManager`は、プロジェクト内の`AudioMixer`のスナップショットを簡単に管理し、シングルトンパターンを用いてどこからでもスナップショットの切り替えを行えるようにするクラスです。シーン内の`AudioMixerManager`を1つ配置して使用します。

### 主な機能
- **シングルトンパターン**：どのスクリプトからでも簡単にアクセス可能。
- **スナップショットのキャッシュ**：`AudioSnapshots` enumに基づいて、キャッシュを構築し、スナップショットの高速なアクセスを実現。
- **起動時のデフォルトスナップショットの適用**：デフォルトスナップショットとフェード時間を指定し、シーン起動時に適用するかを制御。

### プロパティとメソッド
#### プロパティ
- `Instance` : `AudioMixerManager`のシングルトンインスタンス。
- `audioMixers` : シーン内で利用する`AudioMixer`の配列。
- `defaultSnapshot` : デフォルトで適用するスナップショットを`AudioSnapshots` enumで指定。
- `useDefaultSnapshotOnStart` : シーン起動時にデフォルトスナップショットを適用するかを指定。
- `defaultTransitionTime` : スナップショットの切り替え時に使用するデフォルトのフェード時間（秒単位）。

#### メソッド
- `SwitchSnapshot(AudioSnapshots snapshotEnum, float transitionTime = -1f)` : 指定した`AudioSnapshots`を切り替える静的メソッド。`transitionTime`が指定されない場合は`defaultTransitionTime`が使用されます。

### 使用方法
1. シーン内に空のGameObjectを作成し、`AudioMixerManager`スクリプトをアタッチします。
2. インスペクタで`audioMixers`に使用する`AudioMixer`を追加し、デフォルトのスナップショットや起動時の適用フラグなどを設定します。
3. 他のスクリプトから`AudioMixerManager.SwitchSnapshot(AudioSnapshots.Mixer1_SnapshotA)`のように呼び出してスナップショットを切り替えます。

### AudioMixerManager.cs

```csharp
using UnityEngine;
using UnityEngine.Audio;
using System.Collections.Generic;

namespace dsgarage.Audio
{
    /// <summary>
    /// AudioMixerManager は、シングルトンとして AudioMixer のスナップショットを一元管理し、
    /// どこからでも簡単にスナップショットを切り替えることができるマネージャークラスです。
    /// </summary>
    public class AudioMixerManager : MonoBehaviour
    {
        // シングルトンインスタンス
        public static AudioMixerManager Instance { get; private set; }

        // AudioMixerとスナップショットのキャッシュ
        private Dictionary<string, AudioMixerSnapshot> snapshotCache = new Dictionary<string, AudioMixerSnapshot>();

        // デフォルトのスナップショットと起動時の適用フラグ
        public AudioSnapshots defaultSnapshot;
        public bool useDefaultSnapshotOnStart = true;

        // 遷移時間（フェード時間）
        [SerializeField] private float defaultTransitionTime = 1.0f;

        private void Awake()
        {
            // シングルトンのインスタンスを設定
            if (Instance == null)
            {
                Instance = this;
                DontDestroyOnLoad(gameObject); // シーン間でオブジェクトを保持
            }
            else
            {
                Destroy(gameObject); // 重複するインスタンスがある場合は削除
                return;
            }

            // 起動時にシーン内のAudioMixerを検索してキャッシュを構築
            BuildSnapshotCache();

            // 起動時にデフォルトのスナップショットを適用するかどうかを確認
            if (useDefaultSnapshotOnStart)
            {
                SwitchSnapshot(defaultSnapshot, defaultTransitionTime);
            }
        }

        /// <summary>
        /// シーン内の AudioMixer を検索し、スナップショットキャッシュを構築します。
        /// </summary>
        private void BuildSnapshotCache()
        {
            snapshotCache.Clear();

            // シーン内のすべての AudioMixer を検索
            AudioMixer[] mixersInScene = Resources.FindObjectsOfTypeAll<AudioMixer>();

            foreach (AudioMixer mixer in mixersInScene)
            {
                foreach (AudioSnapshots snapshot in System.Enum.GetValues(typeof(AudioSnapshots)))
                {
                    string snapshotName = snapshot.ToString();
                    string[] parts = snapshotName.Split('_');

                    if (parts.Length == 2 && parts[0] == mixer.name)
                    {
                        AudioMixerSnapshot mixerSnapshot = mixer.FindSnapshot(parts[1]);
                        if (mixerSnapshot != null)
                        {
                            snapshotCache[snapshotName] = mixerSnapshot;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// スナップショットを切り替える静的関数
        /// </summary>
        /// <param name="snapshotEnum">AudioSnapshots で指定するスナップショット</param>
        /// <param name="transitionTime">オプションの遷移時間（省略時は defaultTransitionTime を使用）</param>
        public static void SwitchSnapshot(AudioSnapshots snapshotEnum, float transitionTime = -1f)
        {
            if (Instance == null)
            {
                Debug.LogError("AudioMixerManager インスタンスが初期化されていません。");
                return;
            }

            // transitionTime が指定されていなければデフォルトの遷移時間を使用
            if (transitionTime < 0)
            {
                transitionTime = Instance.defaultTransitionTime;
            }

            // enum からキャッシュのキーとなる名前を取得
            string snapshotEnumName = snapshotEnum.ToString();
            if (Instance.snapshotCache.TryGetValue(snapshotEnumName, out AudioMixerSnapshot snapshot))
            {
                snapshot.audioMixer.TransitionToSnapshots(new[] { snapshot }, new float[] { 1.0f }, transitionTime);
                Debug.Log($"スナップショットに切り替えました: {snapshotEnumName} in AudioMixer: {snapshot.audioMixer.name}");
            }
            else
            {
                Debug.LogWarning($"スナップショット '{snapshotEnumName}' がキャッシュ内に見つかりません。");
            }
        }
    }
}

```

## 2. AudioSnapshotEnumGenerator

### クラスの目的
`AudioSnapshotEnumGenerator`は、プロジェクト内のすべての`AudioMixerSnapshot`を検索し、それらの名前を列挙型(enum)として`AudioSnapshots`として自動生成するエディタ拡張です。これにより、スナップショットの名前を簡単に指定でき、スナップショットの名前を手打ちする手間が省けます。

### 主な機能
- **列挙型の自動生成**：`AudioMixerSnapshot`をプロジェクト内から検索し、`AudioSnapshots` enumとして自動生成。
- **名前の整合性を確保**：`AudioMixer`名とスナップショット名を組み合わせて、ユニークなenum名を生成。

### 使用方法
1. メニューから `Tools > Generate AudioSnapshots Enum` を選択します。
2. プロジェクト内の`AudioMixerSnapshot`をもとに`AudioSnapshots` enumが生成され、`Assets/Scripts/AudioSnapshotsEnum.cs`に保存されます。
3. 生成された`AudioSnapshots` enumは、`AudioMixerManager`などからスナップショット名の指定に使用できます。

### 注意事項
- `AudioSnapshotEnumGenerator`は、`AudioMixer`名とスナップショット名が`[MixerName]_[SnapshotName]`の形式でない場合は正しく動作しない可能性があるため、命名規則に従うことが推奨されます。

はい、`AudioMixer`の名前と`AudioMixerSnapshot`の名前を組み合わせて、各enumの名前にすることが可能です。

そのために、次のステップで進めます：
1. `AudioMixerSnapshot`の親である`AudioMixer`の名前を取得する。
2. 取得した名前とスナップショットの名前を組み合わせて、enum名として使用する。

以下にその修正版のコードを示します。

### AudioSnapshotEnumGenerator.cs

```csharp

using UnityEditor;
using UnityEngine;
using UnityEngine.Audio;
using System.IO;
using System.Text;

namespace dsgarage.Audio
{
    /// <summary>
    /// AudioSnapshotEnumGenerator は、プロジェクト内のすべての AudioMixerSnapshot アセットから列挙型 (enum) を自動生成するエディタ拡張です。
    /// 生成された列挙型 (AudioSnapshots) は、コード内で AudioMixerSnapshot を簡単に参照するために使用します。
    /// </summary>
    public class AudioSnapshotEnumGenerator
    {
        /// <summary>
        /// プロジェクト内のすべての AudioMixerSnapshot を含む AudioSnapshots という列挙型 (enum) を生成します。
        /// 生成された列挙型は、指定されたディレクトリに AudioSnapshotsEnum.cs として保存されます。
        /// </summary>
        [MenuItem("Tools/Generate AudioSnapshots Enum")]
        public static void GenerateEnum()
        {
            // Enumスクリプトを保存するディレクトリとファイル名
            string directoryPath = "Assets/Scripts";
            string enumPath = Path.Combine(directoryPath, "AudioSnapshotsEnum.cs");

            // ディレクトリが存在しない場合は作成
            if (!Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
            }

            // AssetDatabase からプロジェクト内のすべての AudioMixerSnapshot アセットを検索
            string[] snapshotGuids = AssetDatabase.FindAssets("t:AudioMixerSnapshot");
            if (snapshotGuids.Length == 0)
            {
                Debug.LogWarning("プロジェクトに AudioMixerSnapshots が見つかりませんでした。");
                return;
            }

            // Enum スクリプトの内容を構築
            StringBuilder enumScript = new StringBuilder();
            enumScript.AppendLine("public enum AudioSnapshots");
            enumScript.AppendLine("{");

            foreach (string guid in snapshotGuids)
            {
                // GUID からパスを取得し、AudioMixerSnapshot オブジェクトをロード
                string path = AssetDatabase.GUIDToAssetPath(guid);
                AudioMixerSnapshot snapshot = AssetDatabase.LoadAssetAtPath<AudioMixerSnapshot>(path);
                if (snapshot != null)
                {
                    // スナップショット名にスペースが含まれる場合はアンダースコアに置換
                    enumScript.AppendLine("    " + snapshot.name.Replace(" ", "_") + ",");
                }
            }

            enumScript.AppendLine("}");

            // ファイルに書き込み
            File.WriteAllText(enumPath, enumScript.ToString(), Encoding.UTF8);
            Debug.Log("AudioSnapshots enum が " + enumPath + " に生成されました。");

            // AssetDatabase を更新して、新しい enum がすぐに使用できるようにする
            AssetDatabase.Refresh();
        }
    }
}

```


## 3. AudioMixerTester

### クラスの目的
`AudioMixerTester`は、デバッグ用のクラスであり、選択した`AudioSnapshots`のスナップショットを簡単に切り替え、現在どのスナップショットがアクティブかを確認できるインターフェースを提供します。

### 主な機能
- **選択されたスナップショットの切り替え**：`AudioSnapshots` enumから指定したスナップショットに簡単に切り替え可能。
- **スナップショットのデバッグ表示**：`OnGUI`メソッドを使用して、画面上に現在のスナップショットを表示し、切り替えボタンを提供。

### 使用方法
1. シーン内に新しいGameObjectを作成し、`AudioMixerTester`スクリプトをアタッチします。
2. `selectedSnapshot`に切り替えたい`AudioSnapshots`のenum値を設定し、`transitionTime`を調整します。
3. 実行時に、画面の左上に現在のスナップショット名が表示され、切り替えボタンを押すと、`AudioMixerManager`経由でスナップショットが切り替わります。

### プロパティとメソッド
#### プロパティ
- `selectedSnapshot` : インスペクタで切り替えたいスナップショットを選択します。
- `transitionTime` : スナップショットの切り替え時に使用するフェード時間（秒単位）。

#### メソッド
- `SwitchToSelectedSnapshot()` : インスペクタで指定されたスナップショットに切り替えるメソッド。
- `OnGUI()` : `OnGUI`を使用して画面上に現在のスナップショット名と切り替えボタンを表示します。

### AudioMixerTester.cs


```csharp
using UnityEngine;

namespace dsgarage.Audio
{
    /// <summary>
    /// AudioMixerTester は、AudioMixerManager のスナップショットの切り替えをテストするためのデバッグ用クラスです。
    /// 現在のスナップショット状態を画面上に表示し、選択されたスナップショットに切り替え可能です。
    /// </summary>
    public class AudioMixerTester : MonoBehaviour
    {
        // AudioMixerManagerを参照（起動時に自動検索）
        private AudioMixerManager audioMixerManager;

        // 切り替え用のAudioSnapshots選択
        public AudioSnapshots selectedSnapshot;

        // 遷移時間（フェード時間）
        public float transitionTime = 1.0f;

        private AudioSnapshots lastSnapshot;

        private void Start()
        {
            // シーン内のAudioMixerManagerを検索して設定
            audioMixerManager = FindObjectOfType<AudioMixerManager>();
            if (audioMixerManager == null)
            {
                Debug.LogError("AudioMixerManager was not found in the scene.");
                return;
            }

            // 初回のスナップショット表示更新
            UpdateSnapshotDisplay();
        }

        private void Update()
        {
            // 選択されたスナップショットが変更されたら切り替え
            if (selectedSnapshot != lastSnapshot)
            {
                SwitchToSelectedSnapshot();
                lastSnapshot = selectedSnapshot;
            }
        }

        /// <summary>
        /// 選択されたスナップショットに切り替えます。
        /// </summary>
        public void SwitchToSelectedSnapshot()
        {
            if (audioMixerManager != null)
            {
                AudioMixerManager.SwitchSnapshot(selectedSnapshot, transitionTime);
                UpdateSnapshotDisplay();
            }
        }

        /// <summary>
        /// 現在のスナップショット名を画面上に表示します。
        /// </summary>
        private void OnGUI()
        {
            // デバッグ情報の表示位置を設定
            GUIStyle labelStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = 16,
                fontStyle = FontStyle.Bold,
                normal = { textColor = Color.white }
            };

            // ボタンとラベルの位置を調整
            if (GUI.Button(new Rect(10, 10, 200, 30), "Switch to Selected Snapshot"))
            {
                SwitchToSelectedSnapshot();
            }

            // ボタンの下に現在のスナップショットを表示
            GUI.Label(new Rect(10, 50, 300, 30), "Current Snapshot: " + selectedSnapshot.ToString(), labelStyle);
        }

        /// <summary>
        /// スナップショットの切り替えをデバッグログに出力します。
        /// </summary>
        private void UpdateSnapshotDisplay()
        {
            Debug.Log("Switched to snapshot: " + selectedSnapshot.ToString());
        }
    }
}

```