はじめに
ADXアンバサダーとして執筆しておりますSigと申します。
この記事ではアンリアルエンジンとサウンドミドルウェア「ADX for UE」を連携させ、インタラクティブミュージックの要素である「クォンタイズ」を実装してみます。
自分で音楽を奏でられる遊びや、ゲーム内の演出や音をBGMのタイミングに合わせたりといった演出を作ることができます。
当記事ではUE5.2を使用しています。
基本的にブループリントのみでの実装を想定しています。
ADXはインディー向けの「LE版」であれば、無料で使用できます。
https://game.criware.jp/products/adx-le/
なお、ADX2はADXへ名称が変更になりましたが、ツール構成は変更ありません(2がないから古いほう、というわけではありません)。
記事執筆時点のADX for UEのSDKバージョンは「ADX LE UE SDK(2.00.00.00)」です。
前提
ADX for UEの導入や基本的な使い方は以下の記事にあります。必要に応じて参照してください。
ADX for UEの導入で、一歩上のサウンド表現を(導入編)
https://qiita.com/SigRem/items/4250925f6d66a4fd287a
ADX for UEの導入で、一歩上のサウンド表現を(実践編)
https://qiita.com/SigRem/items/c089b71c42e898980a46
クォンタイズとは
楽曲制作において、クォンタイズはばらばらに配置された音符(ノート)を楽曲に合わせたテンポに配置する機能のことです。
本来無段階に鳴らすことのできるサウンドに最小単位を設け、それに沿って再生することで音楽として成立させます。
インタラクティブミュージックにおいてもクォンタイズは有効です。
ゲーム中に再生するサウンドを保留しキューに入れておき、BGMのテンポに合わせて実際に再生を開始することで両者を統一させ、まるで最初からひとつのサウンドだったかのように聞かせることが可能です。
今回の記事の実装を試したいけど手頃な音声素材がない……という方のために、1フレーズではありますが素材を用意しましたのでご利用ください。
https://github.com/SigRem221/adx2forue4-booksample/tree/master/ADX_QuantizeSample
https://drive.google.com/file/d/1-PhYlpIMZRkZgW1XaM_95IGsI4RTCeK0/view?usp=share_link
(どちらのリンクも内容は同じものです)
実装
AtomCraftでサウンドをセットアップする
マテリアルのインポート
AtomCraftにサウンド素材となるマテリアルをインポートします。
扱うファイルが多くなるため、用途に合わせてフォルダ分けしておくと便利です。
「マテリアルルートフォルダー」を右クリックし、「新規オブジェクト」→「マテリアルフォルダーの作成」でフォルダが作成できます。
画像ではドラムループ、ピアノパート、リード、ストリングスに分けてあります。
フォルダごとに素材を振り分けてインポートしました。
ループする素材についてループ情報の上書きを行います。
マテリアルを選択し、インスペクターにてループ情報の上書きを「True」に、ループタイプを「ループ」にします。
これでドラムループが自動的にループ再生されるようになります。
ドラムループパートの作成
楽器パートごとにキューを作成します。
まずはドラムループです。
キューシートのワークユニットを右クリックし、「新規オブジェクト」→「キュー『ポリフォニック』の作成」を選択します。
オーソドックスなキューが作られるので、ドラムループ素材をキューに入れます。
次にビートのタイミングをUE側で取得できるよう、ビート同期情報を設定します。
トラックリストの空欄で右クリックし、「新規オブジェクト」→「ビート同期情報の作成」を選択します。
「BeatSync」という表示が追加されました。
ビートの情報を設定するには、楽曲のBPMの値が必要です。
BPMが分からない場合、対象のマテリアルを右クリックして「BPM解析」を選択します。
ログに「BPM "142"」という表示がされ、楽曲のBPMが142であることが分かりました。
キューの「BeatSync」を選択し、インスペクターにてBPMに「142」を入力します。
必須ではありませんが、分かりやすいようタイムラインの表示単位も変えておくと役立ちます。
トラックリストの上にある時計の右の逆三角をクリックし、「タイムベースの編集」を開きます。
ここのBPMにも「142」を設定しておきます。
タイムライン表示が楽曲のテンポに合わせられました。
次の画像ではちょうどいい場所で曲がループしているのが分かります。
クォンタイズする楽器パートの作成
次にピアノやリードなどの楽曲パートのキューを作成します。
キューシートを右クリックし、「新規オブジェクト」→「キュー『ランダム』の作成」か「キュー『ランダムノーリピート』の作成」を選択します。
これらの違いは何度もランダムなトラックを再生した際に、「同じトラック」を続けて再生するかどうかです。
作成されたキューに対して、1トラックにひとつずつマテリアルから対応する素材を入れていきます。
試しに再生してみましょう。毎回違うトラックがランダムで再生されるはずです。
リード、ストリングパートに対しても同じようにキューを作成します。
これらもマテリアルの数だけトラックを持つことになります。
キューシートのビルド
ここまででひとまずAtomCraftでの作業は完了です。
ツールバーからキューシートをビルドします。
複数のキューシートを作成した場合、すべてのキューシートにチェックがついていることを確認してください。
チェックがついていないキューシートはビルドされません。
UE5でクォンタイズを実装する
キューシートのインポート
AtomCraftでビルドしたacf、acbファイルをUE5のコンテンツブラウザ(またはコンテンツドロワー)にドラッグアンドドロップし、インポートします。
ワークユニットやキューシートが複数ある場合、acbファイルも複数出力されています。すべてインポートしてください。
acfファイルのインポート時のダイアログではどちらも「Yes」を選択します。
これにより、このプロジェクトで使用するAtom Configファイルが自動的に設定されます。
acf、acbファイルをインポートすると、コンテンツブラウザにアセットとして追加されます。
キューシートを開き、正常に再生できるか一度確認しておきましょう。
サウンドの配置
ドラムループのキューをレベル上にドラッグアンドドロップして配置します。
配置したキューを選択しておき、「Blueprint」→「Open Level Blueprint」でレベルブループリントを開きます。
ブループリントからビート情報を取得する
イベントグラフ上で右クリックして「Create a Reference to (キュー名)」を選択し、リファレンスノードを作成します。
リファレンスノードから線を伸ばし、Get Atom Componentを選択して配置します。
Get Atom Componentからさらに線を伸ばし、「assign beat」と検索して出てくるAssign On Atom Beat Sync Callbackを選択すると、ふたつのノードが配置されます。
自動的に作られたカスタムイベントに適当に名前をつけておきます。
これでビートごとにイベントが走るようになりました。
テスト用に文字を表示してみます。
カスタムイベントから線を伸ばし、Print Stringノードでデバッグメッセージを出します。
実行してみると、ウィンドウ左上にドラムのテンポに合わせてメッセージが表示されます。
合わせてサウンドも鳴らしてみましょう。
「atom 2d」と検索し、出てきたPlay Sound 2Dノードを配置します。
再生するキュー(サウンド)を指定するために、新規に変数を用意します。
「My Blueprint」パネルの「VARIABLES」で+ボタンを押し、変数を追加します。
適当に名前をつけます。変数の型は「Atom Cue Sheet」型の「Object Reference」です。
コンパイルすると初期値が設定可能になるので、Detailsパネルでキューシートを指定します。
変数をイベントグラフにドラッグ&ドロップし、Get CueSheet_IMQノードを配置します。
Get CueSheet_IMQノードから線を伸ばし、Get Sound Cue by Nameノードを配置します。
名前を指定してキューシートからキューを取得するものです。
「Cue_A_Piano」を指定してピアノのサウンドのキューを取得します。
この状態でゲームを実行してみましょう。音楽のテンポに合わせて、絶え間なくピアノのサウンドが再生されるはずです。
サウンドのクォンタイズ
各楽曲パートをゲーム中にクォンタイズし、再生する処理を作っていきます。
パートごとに入力を受け取れるよう、変数を追加しましょう。
今回はシンプルな例として、Bool型変数をパートごとに作成しています。
Input Keyイベントノードで1,2,3キーが押されたことを感知し、対応する変数をTrueにします。
カスタムイベントから線を伸ばし、入力によって変数がTrueになっていた場合、サウンドを再生して変数をFalseに戻す処理を書きます。
これでビートごとに楽器キーの入力判定と再生が行われることになります。
パートごとに同じ処理を作ります。Sequenceノードを使うと処理を順番に書けるため、グラフが分かりやすくなるのでおすすめです。
変数「bPlayPiano」が「True」の場合(つまり、直前に1キーが押されていた場合)、ピアノのサウンドを再生して「bPlayPiano」を「False」に戻します。
これで、1キーを押した直後のビートのみピアノのサウンドが再生されることになります。
同じように、他のパートについても処理を作ります。ほぼコピペでOKです。
さて、ここで一度テストプレイしてみましょう。1,2,3キーを押すと次のビートタイミングで楽器が演奏されるはずです。
しかし、サンプルのストリングスパートは少し尺が長いため、連続で押すと重なって演奏されてしまい、不自然に感じます。
演奏タイミングをパートごとに設定する
解決方法は簡単です。
カスタムイベントの「Beat Sync Info」から線を伸ばし、Breakノードをつなげるとビート同期情報の分解ができ、詳細な情報を取り出すことができます。
Break Atom Beat Sync Infoノードの「Beat Count」からは小節内で現在何ビート目のタイミングかを受け取れます。
試しに「Beat Count」をPrint Stringノードにつなぎ、メッセージとして出力してみます。
ビートのカウントは0から始まり、4つめのタイミングでまた0に戻ります。小節内のビート数が正しく取得できているのが分かります。
これを踏まえて、ストリングスの再生処理にBranchノードを噛ませます。
条件を**「Beat Cnt == 0」**とすると、各小節の始めのみにストリングスの演奏判定が行われることになります。
この状態でテストプレイすると、ストリングスの音は重ならずに演奏されるようになっているはずです。
さらに演出を追加する場合や、ユーザーの入力体験を高める方法もまとめていますのでこちらの記事も参照してください(こちらは前バージョンで書かれた記事になります)。
ADX2 for UE4でつくるクォンタイズ(テクニック補完編)
https://qiita.com/SigRem/items/207ef9c8b70c1cc798b0