ゲーム開発
Unity
UnrealEngine
C++
Blender
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20240328_01_unity_cri_basic

      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜

      サウンドUnityCRIADXオーディオスペクトラム
      2024-03-28

      マイケル
      マイケル
      みなさんこんにちは! マイケルです!
      エレキベア
      エレキベア
      こんにちクマ〜〜〜
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27
      マイケル
      マイケル
      以前の記事にて、UnityAudio、CRIADX、Wwiseで切り替え可能なサンプルプロジェクトを作成しました。 今回はその中でも「CRIADXを使用した基本動作の実装方法」について紹介していきます! 具体的には下記のような再生サンプルと、周波数データを用いたオーディオスペクトラムの実装になります。
      20240327_01_unity-multi_audio_03
      ▲サンプル1:基本機能

      20240327_01_unity-multi_audio_04
      ▲サンプル2:オーディオスペクトラム

      エレキベア
      エレキベア
      前UnityAudioで実装した内容と比較すると違いが分かりやすそうクマね
      【Unity】第一回 UnityAudioを使いこなす 〜サウンド再生処理編〜
      2024-01-22
      【Unity】第二回 UnityAudioを使いこなす 〜AudioMixer活用編〜
      2024-01-22
      【Unity】第三回 UnityAudioを使いこなす 〜オーディオスペクトラム描画編〜
      2024-01-22
      マイケル
      マイケル
      作成したサンプルのリポジトリと、CRI関連処理は下記になります! こちらも合わせてご参照ください!

      GitHub - unity-multi-audio

      ▲サンプルプロジェクト

      フォルダ
      内容
      CriAtomCraftプロジェクト
      APIを実行する処理
      サンプルゲーム固有のオーディオ関連処理
      サンプルゲームに関する処理
      CriAtomCraftプロジェクトから出力したサウンドデータ
      エレキベア
      エレキベア
      中々ボリュームのあるサンプルクマ

      参考書籍

      マイケル
      マイケル
      今回実装するにあたり、下記書籍を参考にさせていただきました!

      Unityサウンド エキスパート養成講座

      マイケル
      マイケル
      UnityAudioとCRIADXを使用した実装について紹介されています。 ゲームサウンドにフォーカスした数少ない書籍なので、ぜひご一読ください!
      エレキベア
      エレキベア
      Unityでのサウンド実装について幅広く知れる一冊クマね

      CRI ADX、Unityの基本設定

      マイケル
      マイケル
      まずは基本となるCRIAtomCraftとUnityでの使用方法について簡単な流れを記載します。 詳細については公式サイトのチュートリアルやマニュアルをご参照ください!

      CRIWARE 公式チュートリアル

      CRIAtomCraft側

      マイケル
      マイケル
      まずはCriAtomCraft側で音源を取り込み、CueSheet、Cueというものを作成します。 Cueが実際にゲーム側から指定して再生する単位で、CueSheetはCueをまとまりごとに管理するものになります。

      CRI ADX2(AtomCraft) 入門編01 音データの登録、ツール上での再生

      20240328_01_unity_cri_basic_01
      ▲Cue、CueSheetの定義

      マイケル
      マイケル
      そして作成したCue等の情報をCRI独自の形式(ACF、ACB、AWB)で書き出し、それをUnity側で読み込んで使用する流れになります。 公式サイトからの引用になりますが、下記のようなイメージです。
      20240328_01_unity_cri_basic_04
      ▲流れのイメージ

      形式
      概要
      ACFファイル
      カテゴリやBus設定など全般的な設定
      ACBファイル
      CueSheetのバイナリ情報
      AWBファイル
      ストリーム再生に設定した波形データ
      エレキベア
      エレキベア
      AtomCraftが音源とゲームエンジンの間に挟まるイメージクマね
      マイケル
      マイケル
      書き出しは「ビルド > Atomキューシートバイナリのビルド」から行います。 無料のLE版では選択できないですが、有料版ではiOS、Switchといったプラットフォームを指定して出力できます。
      20240328_01_unity_cri_basic_02
      ▲バイナリの書き出し

      20240328_01_unity_cri_basic_03
      ▲ターゲットを指定してビルドする

      エレキベア
      エレキベア
      作成したデータをUnity側で使う準備ができたクマね

      Unity側

      マイケル
      マイケル
      次は作成したデータをUnity側で読み込んで使用する方法についてです。 チュートリアルでいうとUnity側の入門編が該当します。
      20240328_01_unity_cri_basic_08

      CRI ADX(Unity) チュートリアル

      マイケル
      マイケル
      まずはCRIの処理を行うために必要な ・CRIAtom ・LibraryInitializer ・ErrorHandler のコンポーネントを用意し、CueSheet等の設定を行います。
      20240328_01_unity_cri_basic_05
      ▲グローバルオブジェクトの作成

      マイケル
      マイケル
      そして基本的な再生位置となるCRIAtomListenerコンポーネント再生対象となるCRIAtomSourceコンポーネントをそれぞれオブジェクトにアタッチし、Cue等の設定を行います。
      20240328_01_unity_cri_basic_07
      ▲Listenerコンポーネントのアタッチ(基本はカメラ)

      20240328_01_unity_cri_basic_06
      ▲Sourceコンポーネントのアタッチ(再生するオブジェクト)

      エレキベア
      エレキベア
      基本的にコンポーネントを設定して扱うクマね
      マイケル
      マイケル
      あとは下記のようにコンポーネントを取得してPlay関数を呼べば再生されます。 また、3D位置による再生を行わない場合、CriAtomExPlayerを使用してコンポーネントをアタッチせずに再生することもできます。
          private CriAtomSource _atomSource;
      
          private void Start()
          {
              _atomSource = (CriAtomSource)GetComponent("CriAtomSource");
          }
      
          public void PlaySound()
          {
              _atomSource.Play();
          }
      
      ▲CriAtomSourceによる再生
          private CriAtomExPlayer _atomExPlayer;
      
          private void Start()
          {
              _atomExPlayer = new CriAtomExPlayer();
          }
      
          public void PlaySound()
          {
              var atomExAcb = CriWare.CriAtom.GetAcb("[CueSheetName]");
              _atomExPlayer.SetCue(atomExAcb, "[CueName]");
              _atomExPlayer.Start();
          }
      
      ▲CriAtomExPlayerによる再生
      マイケル
      マイケル
      今回作成したサンプルではグローバルオブジェクトの生成や初期設定等も含めて全てスクリプトで行っています。 再生処理に関しても基本的にCriAtomExPlayerを使用しますが、3D位置による再生のみCriAtomSourceをアタッチして再生するよう実装しました。
      エレキベア
      エレキベア
      サウンド担当でも触れるようにコンポーネントで操作できるようになっていても、なるべくスクリプトで制御するようにした方がシーンやオブジェクトが汚れずにすみそうクマね

      基本機能のサンプル実装

      マイケル
      マイケル
      では早速サンプルの実装内容について紹介します。 まずは下記の基本機能をまとめたサンプルについてです。
      エレキベア
      エレキベア
      フェード再生からDSPエフェクト、音量調整まで よくある機能がまとまっているクマね

      CueSheetの読込

      マイケル
      マイケル
      まずは使用するCueSheetを読み込む必要があります。 今回は下記のようなCueSheet情報クラスを定義しました。
              /// <summary>
              /// CueSheet情報
              /// </summary>
              public class CueSheetInfo
              {
                  /// <summary>
                  /// CueSheet名
                  /// </summary>
                  public string Name;
      
                  /// <summary>
                  /// 初期化時にロードするか?
                  /// </summary>
                  public bool IsLoadOnInitialize;
      
                  /// <summary>
                  /// AWBファイルが存在するか?
                  /// </summary>
                  public bool IsExistAwbFile;
      
                  /// <summary>
                  /// ループ再生するか?
                  /// </summary>
                  public bool IsPlayLoop = false;
              }
      
      ▲CueSheet情報の定義
      マイケル
      マイケル
      そして渡されてきた情報を元にCueSheetを読み込みます。 あわせて今回はCueSheetに対応するCriAtomExPlayerを作成するように実装してみました。
              /// <summary>
              /// 生成したCueAtomExPlayerのキャッシュ
              /// key: CueSheet名, value: CueAtomExPlayer
              /// </summary>
              private readonly Dictionary<string, CriAtomExPlayer> _criAtomExPlayerCache = new Dictionary<string, CriAtomExPlayer>();
      
      ・・・略・・・
      
              /// <summary>
              /// CueSheetの登録
              /// </summary>
              /// <param name="cueSheetInfo">CueSheet情報</param>
              public void RegisterCueSheet(ICriAdxApiService.CueSheetInfo cueSheetInfo)
              {
                  // CriAtomExPlayerの生成
                  CreateCriAtomExPlayer(cueSheetInfo);
      
                  // CueSheet情報を追加
                  var cueSheetName = cueSheetInfo.Name;
                  CriWare.CriAtom.AddCueSheet(
                      cueSheetName,
                      Path.Combine(_initializeSetting.AssetFilePath, $"{cueSheetName}.acb"),
                      cueSheetInfo.IsExistAwbFile ? Path.Combine(_initializeSetting.AssetFilePath, $"{cueSheetName}.awb") : null);
              }
      
      ・・・略・・・
      
              /// <summary>
              /// CriAtomExPlayerの追加
              /// </summary>
              /// <param name="cueSheetInfo"></param>
              /// <returns></returns>
              private void CreateCriAtomExPlayer(ICriAdxApiService.CueSheetInfo cueSheetInfo)
              {
                  if (cueSheetInfo == null || string.IsNullOrEmpty(cueSheetInfo.Name))
                  {
                      Debug.LogError($"invalid CreateCriAtomExPlayer parameters.");
                      return;
                  }
      
                  // 既に生成済か?
                  var cueSheetName = cueSheetInfo.Name;
                  if (_criAtomExPlayerCache != null
                      &&_criAtomExPlayerCache.ContainsKey(cueSheetName))
                  {
                      Debug.LogWarning($"already create CriAtomExPlayer => {cueSheetName}");
                      return;
                  }
      
                  // CriAtomExPlayerを生成してキャッシュ
                  var criAtomExPlayer = new CriAtomExPlayer();
      
                  criAtomExPlayer.Loop(cueSheetInfo.IsPlayLoop);
                  _criAtomExPlayerCache.TryAdd(cueSheetName, criAtomExPlayer);
              }
      
      ▲Cuesheetの登録とPlayerの作成
      マイケル
      マイケル
      ちなみにCueSheetはその時使用する分だけ、動的にロード/アンロードすることもできます。 下記は読み込んだCueSheetを削除する処理です。
              /// <summary>
              /// CueSheetの削除
              /// </summary>
              /// <param name="cueSheetName">CueSheet名</param>
              public void RemoveCueSheet(string cueSheetName)
              {
                  if (!_criAtomExPlayerCache.ContainsKey(cueSheetName))
                  {
                      return;
                  }
      
                  // CriAtomExPlayerの削除
                  var criAtomPlayer = _criAtomExPlayerCache[cueSheetName];
                  criAtomPlayer.Dispose();
                  _criAtomExPlayerCache.Remove(cueSheetName);
      
                  // 設定したCueSheet情報も削除
                  CriWare.CriAtom.RemoveCueSheet(cueSheetName);
              }
      
      ▲CueSheetの削除
      エレキベア
      エレキベア
      これでCueを再生する準備ができたクマね

      BGM再生

      マイケル
      マイケル
      そしてCueの再生処理は下記になります。 ボリュームやピッチ等も変更できるため、オプション指定で渡せるようにしてあります。
              /// <summary>
              /// 指定したCueを再生する
              /// </summary>
              /// <param name="cueSheetName">CueSheet名</param>
              /// <param name="cueName">Cue名</param>
              /// <param name="option">再生オプション</param>
              public void Play(string cueSheetName, string cueName, ICriAdxApiService.SoundPlayOption option = null)
              {
                  var criAtomExPlayer = GetCriAtomExPlayer(cueSheetName);
                  if (criAtomExPlayer == null)
                  {
                      Debug.LogError($"not register CueSheet => {cueSheetName}");
                      return;
                  }
      
                  // フェード設定を切り替える
                  var fadeTime = option?.FadeTimeMs ?? 0;
                  ChangeCriAtomExPlayerFadeSettings(criAtomExPlayer, fadeTime);
                  if (fadeTime <= 0)
                  {
                      Stop(cueSheetName);
                  }
      
                  // オプションに応じて再生
                  var atomExAcb = CriWare.CriAtom.GetAcb(cueSheetName);
                  criAtomExPlayer.SetVolume(option?.Volume ?? 1f);
                  criAtomExPlayer.SetPitch(option?.Pitch ?? 0f);
                  criAtomExPlayer.SetCue(atomExAcb, cueName);
      
                  var criAtomPlayback = criAtomExPlayer.Start();
                  _criAtomPlaybackCache[cueName] = criAtomPlayback;
              }
      
      ・・・略・・・
      
              /// <summary>
              /// 指定したCueSheetの再生を停止する
              /// </summary>
              /// <param name="cueSheetName">CueSheet名</param>
              /// <param name="option">停止オプション</param>
              public void Stop(string cueSheetName, ICriAdxApiService.SoundStopOption option = null)
              {
                  var criAtomExPlayer = GetCriAtomExPlayer(cueSheetName);
                  if (criAtomExPlayer == null)
                  {
                      return;
                  }
      
                  // フェード設定を切り替える
                  ChangeCriAtomExPlayerFadeSettings(criAtomExPlayer, option?.FadeTimeMs ?? 0);
      
                  // CueSheetの再生を停止する
                  criAtomExPlayer.Stop();
              }
      
      ▲Cueの再生/停止
      マイケル
      マイケル
      フェード再生についてはAttachFader処理を呼び出すことで、Playerごとに切り換えることができます。 今回は下記のように実装しました。
              /// <summary>
              /// CriAtomExPlayerのフェード設定 キャッシュ情報
              /// </summary>
              private readonly Dictionary<CriAtomExPlayer, int> _currentFadeTimeMsCache = new Dictionary<CriAtomExPlayer, int>();
      
              /// <summary>
              /// CriAtomExPlayerのフェード設定を切り替える
              /// </summary>
              /// <param name="criAtomExPlayer">CriAtomExPlayer</param>
              /// <param name="fadeTimeMs">フェードさせる時間</param>
              private void ChangeCriAtomExPlayerFadeSettings(CriAtomExPlayer criAtomExPlayer, int fadeTimeMs = 0)
              {
                  if (criAtomExPlayer == null)
                  {
                      return;
                  }
      
                  // 設定の変更がない場合は何も行わない
                  var isFirstSettings = !_currentFadeTimeMsCache.ContainsKey(criAtomExPlayer);
                  var currentFadeTime = isFirstSettings ? 0 : _currentFadeTimeMsCache[criAtomExPlayer];
                  if (fadeTimeMs == currentFadeTime)
                  {
                      return;
                  }
      
                  // フェード設定を変更
                  if (isFirstSettings)
                  {
                      criAtomExPlayer.AttachFader();
                  }
                  criAtomExPlayer.SetFadeInTime(fadeTimeMs);
                  criAtomExPlayer.SetFadeOutTime(fadeTimeMs);
                  _currentFadeTimeMsCache[criAtomExPlayer] = fadeTimeMs;
              }
      
      ▲フェード再生設定
      エレキベア
      エレキベア
      フェーダーが用意されてるのはありがたいクマね

      SEの3D再生

      マイケル
      マイケル
      そして3D位置再生を行う場合にはCriAtomSourceを使用する必要がありますが、 こちらはGameObjectを受け取り、スクリプト内でアタッチするよう実装しています。
              /// <summary>
              /// 指定したCueを再生する(3D位置再生)
              /// </summary>
              /// <param name="gameObject">対象GameObject</param>
              /// <param name="cueSheetName">CueSheet名</param>
              /// <param name="cueName">Cue名</param>
              /// <param name="option">再生オプション</param>
              public void Play3d(GameObject gameObject, string cueSheetName, string cueName, ICriAdxApiService.SoundPlayOption option = null)
              {
                  // CriAtomSourceの生成
                  var criAtomSource = gameObject.GetComponent<CriAtomSource>();
                  if (criAtomSource == null)
                  {
                      criAtomSource = gameObject.AddComponent<CriAtomSource>();
                      criAtomSource.cueSheet = cueSheetName;
                      criAtomSource.use3dPositioning = true;
                  }
      
                  // フェード設定を切り替える
                  var criAtomExPlayer = criAtomSource.player;
                  ChangeCriAtomExPlayerFadeSettings(criAtomExPlayer, option?.FadeTimeMs ?? 0);
      
                  // オプションに応じて再生
                  criAtomSource.cueName = cueName;
                  criAtomSource.volume = option?.Volume ?? 1f;
                  criAtomSource.pitch = option?.Pitch ?? 0f;
      
                  var criAtomPlayback = criAtomSource.Play();
                  _criAtomPlaybackCache[cueName] = criAtomPlayback;
              }
      
      ▲SEの3D位置再生
      エレキベア
      エレキベア
      use3dPositioningフラグも設定する必要があるのクマね

      音量調整

      マイケル
      マイケル
      ミキサーによる音量調整については、今回はカテゴリを定義して振り分けることで調整しました。 下記のようにBGM、SEのカテゴリを作成しています。

      参考:CRI ADX2(AtomCraft) 初級編09 カテゴリ設定

      20240328_01_unity_cri_basic_09
      ▲BGM、SEのカテゴリ振り分け

      マイケル
      マイケル
      これらのボリュームを取得/設定する関数は下記になります。 マスターボリュームに関してはBusから取得できるので、Busとカテゴリの2つを用意しました。
              /// <summary>
              /// 指定バスのボリューム取得
              /// </summary>
              /// <param name="busName">バス名</param>
              /// <returns></returns>
              public float GetBusVolume(string busName)
              {
                  CriAtomExAsr.GetBusVolume(busName, out var volume);
                  return volume;
              }
      
              /// <summary>
              /// 指定バスのボリューム設定
              /// </summary>
              /// <param name="busName">バス名</param>
              /// <param name="volume">ボリューム</param>
              public void SetBusVolume(string busName, float volume) => CriAtomExAsr.SetBusVolume(busName, volume);
      
              /// <summary>
              /// 指定カテゴリのボリューム取得
              /// </summary>
              /// <param name="categoryName">カテゴリ名</param>
              /// <returns></returns>
              public float GetCategoryVolume(string categoryName) => CriWare.CriAtom.GetCategoryVolume(categoryName);
      
              /// <summary>
              /// 指定カテゴリのボリューム設定
              /// </summary>
              /// <param name="categoryName">カテゴリ名</param>
              /// <param name="volume">ボリューム</param>
              public void SetCategoryVolume(string categoryName, float volume) => CriWare.CriAtom.SetCategoryVolume(categoryName, volume);
      
      ▲バス、カテゴリボリュームの取得/設定
              /// <summary>
              /// Masterボリューム
              /// </summary>
              public float MasterVolume
              {
                  get => _criAdxApiService.GetBusVolume(GameCriAdxAudioSettings.CriAdx.BusName.Master);
                  set => _criAdxApiService.SetBusVolume(GameCriAdxAudioSettings.CriAdx.BusName.Master, value);
              }
      
              /// <summary>
              /// BGMボリューム
              /// </summary>
              public float BgmVolume
              {
                  get => _criAdxApiService.GetCategoryVolume(GameCriAdxAudioSettings.CriAdx.CategoryName.Bgm);
                  set => _criAdxApiService.SetCategoryVolume(GameCriAdxAudioSettings.CriAdx.CategoryName.Bgm, value);
              }
      
              /// <summary>
              /// SEボリューム
              /// </summary>
              public float SeVolume
              {
                  get => _criAdxApiService.GetCategoryVolume(GameCriAdxAudioSettings.CriAdx.CategoryName.Se);
                  set => _criAdxApiService.SetCategoryVolume(GameCriAdxAudioSettings.CriAdx.CategoryName.Se, value);
              }
      
      ▲ボリュームの紐付け
      エレキベア
      エレキベア
      CueSheetの他にカテゴリ分け出来るのは便利クマね ミキサー的な使い方もできるクマ

      DSPエフェクトの適用

      マイケル
      マイケル
      エフェクトの適用については、 ・バスを新たに定義してエフェクトを追加する ・各バスのパラメータ値をスナップショットとして保存する といった手順で用意しました。 ざっくりにはなりますが下記のようなイメージになります。

      参考:CRI ADX2(AtomCraft) 中級編17 DSPバス(ミキサー)とエフェクトの追加

      20240328_01_unity_cri_basic_10
      ▲リバーブのスナップショット設定

      20240328_01_unity_cri_basic_11
      ▲ディストーションのスナップショット設定

      マイケル
      マイケル
      実装は簡単でApplyDspBusSnapshotに対してスナップショット名と遷移させる時間を渡すことで切り換えることができます。
              /// <summary>
              /// DSPバスのスナップショットを切り替える
              /// </summary>
              /// <param name="snapshotName">スナップショット名</param>
              /// <param name="fadeTimeMs">フェードさせる時間</param>
              public void ChangeBusSnapshot(string snapshotName, int fadeTimeMs)
              {
                  CriAtomEx.ApplyDspBusSnapshot(snapshotName, fadeTimeMs);
              }
      
      ▲snapshotの切り替え処理
              /// <summary>
              /// BGMエフェクト変更
              /// </summary>
              /// <param name="effectType">エフェクトタイプ</param>
              public void ChangeBgmEffect(IGameAudioService.EffectType effectType)
              {
                  switch (effectType)
                  {
                      case IGameAudioService.EffectType.Normal:
                          _criAdxApiService.ChangeBusSnapshot(GameCriAdxAudioSettings.CriAdx.BusSnapshotName.Normal, 1000);
                          break;
                      case IGameAudioService.EffectType.Reverb:
                          _criAdxApiService.ChangeBusSnapshot(GameCriAdxAudioSettings.CriAdx.BusSnapshotName.BgmReverb, 1000);
                          break;
                      case IGameAudioService.EffectType.Distortion:
                          _criAdxApiService.ChangeBusSnapshot(GameCriAdxAudioSettings.CriAdx.BusSnapshotName.BgmDistortion, 1000);
                          break;
                  }
              }
      
      ▲エフェクト変更処理
      エレキベア
      エレキベア
      UnityAudioに用意されてるスナップショット機能と似たイメージクマね

      オーディオスペクトラムの実装

      マイケル
      マイケル
      基本的な再生処理は以上になりますが、最後に周波数データを取得してオーディオスペクトラムを描画する方法についても紹介します。
      エレキベア
      エレキベア
      これはUnityAudioでも同じことが出来たクマね
      マイケル
      マイケル
      CRIではCriAtomExOutputAnalyzerを使用して再生中の音源の周波数データを取得することができます。 実装は下記のようになります。

      参考:CRIWARE Unity Plugin Manual スペクトル解析結果を対数周波数スケールに変換する

              /// <summary>
              /// スペクトラムアナライザ
              /// </summary>
              private CriAtomExOutputAnalyzer _spectrumAnalyzer;
      
              /// <summary>
              /// スペクトラムアナライザの設定
              /// CriAtomExPlayerを再生する前に紐づけておく必要がある
              /// </summary>
              /// <param name="cueSheetName">CueSheet名</param>
              /// <param name="sampleCount">サンプル数</param>
              public void SetSpectrumAnalyzer(string cueSheetName, int sampleCount)
              {
                  if (!_criAtomExPlayerCache.ContainsKey(cueSheetName))
                  {
                      return;
                  }
                  var criAtomExPlayer = _criAtomExPlayerCache[cueSheetName];
      
                  // 既に作成されていたらデタッチ、破棄する
                  _spectrumAnalyzer?.DetachExPlayer();
                  _spectrumAnalyzer?.Dispose();
      
                  // スペクトラムアナライザ用の設定
                  var config = new CriAtomExOutputAnalyzer.Config();
                  config.enableSpectrumAnalyzer = true;
                  config.numSpectrumAnalyzerBands = sampleCount;
                  _spectrumAnalyzer = new CriAtomExOutputAnalyzer(config);
                  _spectrumAnalyzer.AttachExPlayer(criAtomExPlayer);
              }
      
              /// <summary>
              /// スペクトラムアナライザから周波数データを取得する
              /// </summary>
              /// <param name="sampleCount">サンプル数</param>
              /// <param name="isConvertDecibel">デシベル値に変換するか?</param>
              /// <returns></returns>
              public float[] GetSpectrumData(int sampleCount, bool isConvertDecibel)
              {
                  var result = new float[sampleCount];
                  if (_spectrumAnalyzer == null)
                  {
                      Debug.LogError("not called 'CreateSpectrumAnalyzer' method.");
                      return result;
                  }
      
                  // 周波数データを取得
                  _spectrumAnalyzer.GetSpectrumLevels(ref result);
      
                  // デシベルに変換
                  // https://game.criware.jp/manual/unity_plugin/latest/contents/classCriWare_1_1CriAtomExOutputAnalyzer.html#a6b99d6b5310af38efe20ff834c59c4e0
                  if (isConvertDecibel)
                  {
                      result = result.Select((value) =>
                      {
                          if (value <= Mathf.Epsilon)
                          {
                              return 0f;
                          }
                          return 20.0f * Mathf.Log10(value);
                      }).ToArray();
                  }
      
                  return result;
              }
      
      ▲周波数データの取得
              /// <summary>
              /// スペクトラムアナライザの設定
              /// </summary>
              /// <param name="eventName">サウンドイベント名</param>
              /// <param name="sampleCount">サンプル数</param>
              /// <returns>作成に成功したか?</returns>
              public bool SetSpectrumAnalyzer(string eventName, int sampleCount)
              {
                  _criAdxApiService.SetSpectrumAnalyzer(GameCriAdxAudioSettings.CriAdx.GetCueSheetName(eventName), sampleCount);
                  return true;
              }
      
              /// <summary>
              /// スペクトラムアナライザから周波数データを取得する
              /// </summary>
              /// <param name="sampleCount">サンプル数</param>
              /// <param name="isConvertDecibel">デシベル値に変換するか?</param>
              /// <returns></returns>
              public float[] GetSpectrumData(int sampleCount, bool isConvertDecibel)
              {
                  return _criAdxApiService.GetSpectrumData(sampleCount, isConvertDecibel);
              }
      
      ▲アナライザの設定とデータ取得
      エレキベア
      エレキベア
      実装方法もシンプルで分かりやすいクマね
      マイケル
      マイケル
      取得した周波数データを使用して具体的にどう描画するか?については下記記事にまとめてありますので、気になった方はぜひこちらもご参照ください!
      【Unity】第三回 UnityAudioを使いこなす 〜オーディオスペクトラム描画編〜
      2024-01-22
      エレキベア
      エレキベア
      実装方法もまた違って見比べてみると面白いクマ〜〜

      おわりに

      マイケル
      マイケル
      というわけで今回はCRIの基本的な使用方法についてでした! どうだったかな?
      エレキベア
      エレキベア
      ツール側でいろいろ用意されている分、UnityAudioの時と比べて実装がシンプルになった気がするクマ〜〜
      マイケル
      マイケル
      ツールでの操作は覚えないといけないけど、サウンド担当とプログラマの実装を分担できるのは大きな魅力だよね!
      マイケル
      マイケル
      次回は、サウンドミドルウェアの醍醐味である「インタラクティブミュージック」の実装について紹介していこうと思います! お楽しみに!!
      エレキベア
      エレキベア
      クマ〜〜〜〜〜

      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜 〜完〜

      【Unity】第一回 UnityAudioを使いこなす 〜サウンド再生処理編〜
      2024-01-22
      【Unity】第二回 UnityAudioを使いこなす 〜AudioMixer活用編〜
      2024-01-22
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27

      サウンドUnityCRIADXオーディオスペクトラム
      2024-03-28

      関連記事
      【Unity】Timeline × Excelでスライドショーを効率よく制作する
      2024-10-31
      【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
      2024-05-28
      【ゲーム数学】第九回 p5.jsで学ぶゲーム数学「フーリエ解析」
      2024-05-12
      【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
      2024-04-14
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-28
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27