イントロ
私は2022年のはじめにジェネレーティブ(生成)アンビエントミュージックのアルバム「Púrpura」をリリースしました。この記事ではActor-Mixer階層を使ったジェネレーティブミュージックシステムの作成から、Unityを使った音楽演奏まで、Wwiseを使用してどのようにアルバムを作成したのかをご紹介したいと思います。
ジェネレーティブミュージックのコンセプトを簡単に説明しますと、要はリアルタイムで作曲されていく音楽のことで、作曲家が事前に定義したルールに基づきシステムが即興で作曲しますが、ある程度のランダム性が伴います。最も単純なジェネレーティブミュージックの場合、再生する音符(またはサンプル)と次の音符を再生するまでの時間をシステムのルールで決めます。興味のある方はブライアン・イーノのこちらの講演の記録をお読みいただくことをおすすめします。
「Púrpura」のジェネレーティブシステム
アルバム「Púrpura」の曲はどれも似たシステムで生成されています。各曲とも4~5種類ほどの楽器で演奏されていて、楽器ごとにランダムに再生されるフレーズのプールがあります。楽曲別に親アクターミキサーオブジェクトをActor-Mixer階層で作成し、この親の中に楽器のランダムコンテナを設定しました。
下図はRitoという楽曲ですが、使われている楽器がBass(ベース)、Cello(チェロ)、Flute(フルート)、PadGranular(グラニュラーパッド)、そしてOrgan(オルガン)の5種類です。グラニュラーパッドを見るとフレーズが7個あり、それらがランダムに再生されることが分かります。
図1 - Rito のActor-Mixer階層
各ランダムコンテナをContinuous(連続)の再生に設定し、Loop(ループ)をInfinite(無限)に設定し、トランジションのタイプをDelay(ディレイ)にしました。こうすることでこのランダムコンテナを再生した時に1つのフレーズがランダムに選択され再生され、そのフレーズが終わると一定の期間をおいた後に別のフレーズがランダムに選択され再生され、そのまま停止の指示が出るまでこれが何回も繰り返されます。
図2 - ランダムコンテナの設定
ランダム性をさらに高めるために、DelayのDuration(期間)もランダム化しています。
図3 - DelayのDurationのランダム化
もしWwiseで断続的なアンビエントサウンドをゲームに実装したご経験があるとすると、これは基本的に同じセットアップです。
私がアルバム制作で最も意識していたことの1つは、時間の経過と共に楽曲のソニック密度を有機的に変化させることで、味気のない無音状態になってしまうのを避けつつ、音楽が呼吸できる空間を確保したいと考えました。そのためにはWwiseで値を微調整することと、音楽素材自体のイテレーションを行うことのバランスが重要で、どの楽曲もシステムに適した形式で書くように心がけました。
楽曲をWwiseで「演奏」する方法は単純で、ランダムコンテナを同時に再生して好きなだけ稼働させただけです。演奏を録音するためにオーディオインターフェースの出力をDAWの入力として設定してトラックを作成し、この時フィードバックループを防ぐためにもちろんモニタリングはオフにしました。それぞれの楽曲を何回かレコーディングし、気に入ったものを集めてアルバムに仕上げました。すべてがうまく組み合わせられる様子を、こちらのオープニングトラックでお聞きください:
Alva:インテンシティRTPC
アルバムの最初の4曲は基本的に上記と同じシステムを使用していますが、フィナーレのAlvaはさらに進化し、時間の経過と共にインテンシティレベルをRTPCで制御しました。Alvaではアルバムのほかの楽曲のサンプルだけを使い、これらのサンプルを崩して処理し、音のコラージュを制作しました。Wwiseエフェクトも取り入れ、楽曲全体を通して新しいテキスチャをダイナミックに作成しました。こちらのリンクから聞くことができます:
この楽曲だけセットアップがほかと少し異なります。まず最初に、この楽曲ではInteractive Music階層を使用してチェロのフレーズを最後までループさせていて、これがActor-Mixer階層で再生される各種楽器の土台となっています。Playlistコンテナはイントロフレーズを1度再生してから、ループのフレーズに移ります。
図4 - ミュージックプレイリストのセットアップ
Actor-Mixer階層のセットアップも少し異なります。ここにはSub(サブベース)、Cello(チェロ)、Flute(フルート)、そしてOboe(オーボエ)が入っていて、最後の3つの楽器の親として別のアクターミキサーオブジェクトがあり、ベースと分けてこれらを処理できるようになっています。
図5 - Alva のActor-Mixer階層
この楽曲ではInteractive Music階層のチェロだけがはじめに再生され、インテンシティRTPCを使いActor-Mixer楽器を1つずつフェードインさせています。サブベース楽器のセットアップはこのようになっています:
Fig. 6 - インテンシティRTPC:ボイスボリューム
Actor-Mixer階層のチェロ、フルート、オーボエは、Wwise HarmonizerとWwise Guitar Distortionエフェクトによってリアルタイムに処理されます。インテンシティRTPCはHarmonizerのDryレベルとWetレベル、そしてGuitar DistortionエフェクトのDistortion Driveを制御します。こちらがHarmonizerのRTPCカーブです。
図7 - インテンシティRTPC:Harmonizerエフェクト
Interactive Music階層のチェロにも同じ2つのエフェクトが適用されていて、RTPCカーブは似ています。サブベースに対するHarmonizerエフェクトやGuitar Distortionエフェクトも、違う値を設定したインテンシティRTPCに制御されています。さらに以下のようにインテンシティRTPCを使用しているものの上にWwise Tremoloエフェクトを追加し、LFO周波数を増加させています:
図8 - インテンシティRTPC:Tremoloエフェクト
Master-Mixer階層で2つのAUXバスとして設定されているWwise DelayとWwise RoomVerbのエフェクトがアルバム全体で使われ、すべてに空間的なまとまりがあります。ほかの楽曲のAUXセンドレベルは固定ですが、AlvaのインテンシティRTPCがアクターミキサーの楽器のUser-Defined Auxiliary Send Volumeを制御し、楽曲の進行に合わせてボリュームを大きくしています。
このパズルの最後のピースが、ランダムコンテナオブジェクトの設定です。楽器のフルート、オーボエ、チェロをランダムコンテナではなくシーケンスコンテナとして設定し、トランジションのタイプはDelayではなくTrigger Rateに設定しました。
図9 - シーケンスコンテナのセットアップ
トリガーレートを制御するためにインテンシティRTPCを使っているため、楽曲がすすむにつれ、シーケンスコンテナがそれぞれの楽器をより早くトリガーしていきます。
図10 - インテンシティRTPC:トリガーのDuration
楽曲が終わる頃にシーケンスコンテナが毎秒1フレーズを再生するようにトリガーレートの速さを設定してあり、各楽器が自分自身の上に連続的に重なるようになっています。この設定とすべてのエフェクトを同じRTPCで制御する設定を合体させ、別々であったはずの楽器が幽玄な「雲」に姿を変えていきます。
インテンシティRTPCを演奏するためにUnityのアニメーションカーブを使用する
WwiseでAlvaを設定した後に最後に残った問題は、インテンシティRTPCの値をどう音楽的に増加させるかでした。レコーディングしながら値を手動で設定する方法は、面倒な上に安定性に欠けることが分かりました。WwiseでRTPCの補間をスルーレート(slew rate)に設定したところで、求めているニュアンスを出せませんでした。結局私はWwiseを真っ白なUnityプロジェクトと統合し、アニメーションカーブを使いRTPCを駆動させることにしました。
「アニメーションカーブ」という名前が付いていますが、単に時間を表すX軸に対し、Y軸の値を決めるグラフです。これがAlvaのインテンシティRTPCを駆動させるためにたどり着いたカーブです:
図11 - アニメーションカーブのグラフ
すべてをフックしてつなげるために、私は簡単なC#スクリプトを書いてアニメーションカーブの値をWwiseのRTPCに送るようにしました。RTPC用にアニメーションカーブの変数を宣言してから、RTPCの最小値から最大値までにかかる秒数を定義する別の変数を宣言しました。
図12 - C#スクリプト:パブリック変数
これらをパブリック変数として宣言し、インスペクターから設定できるようにしました。そうすることでインスペクターのグラフ印をクリックして手作業でアニメーションカーブを編集できるようになりました。
図13 - C#スクリプト:インスペクター
図11で示したようにカーブのX軸もY軸も0~1の範囲でノーマライズされています。そこでX軸が私が希望する時間の長さ(270秒)に、Y軸がWwiseで定義したRTPCの範囲(0~100)になるように調整しました。これを達成するために以下の3行のコードを書き、Update関数の中で実行して毎フレーム1回計算されるようにしました:
図14 - C#スクリプト:アップデート関数
最初にtimer変数を作成してフレームごとにTime.deltaTimeをインクリメントさせ、楽曲がはじまってから何秒経過したのかをカウントできるようにしました。
次にタイマー変数を利用して時間に合わせてカーブのY値を取得する必要がありました。ここで私が使用したのはEvaluate関数で、X座標を引数として渡すと、グラフのY値を出力します。私はtimer変数を引数として渡し、フレーム毎にXの値がインクリメントされるようにし、これをduration変数で割り、0から1に到達するまでの時間を270秒にしました。
次のステップはY軸の値を調整してRTPC値の範囲と適合させることでした。Mathf.Lerp関数を使用してY軸値(Evaluate関数で取得)を使い、0から100まで補間をしました。続いてその計算結果をrtpcValue変数に書き込みました。
最後のステップは単純で、rtpcValue変数をSetGlobalValue関数を使用してWwiseに渡します。
アウトロ
「Púrpura」は私がWwiseを自分の作曲作業に組み込む練習をしながらできたもので、Wwiseを音楽の制作の助けに使い、単なる音楽の再生に使っているだけではありません。インプリメンテーションは音楽を作曲した後に行う過程と思われがちですが、オーディオミドルウェアをクリエイティブな作業段階で取り入れることで、インタラクティブ媒体用のエクスペリエンスを、よりユニークなものにつくりやすくしてくれると私は思います。
「Púrpura」のアルバム全体を聞いてみたい方は、ぜひBandcampやこちらの音楽ストリーミングサービスをご確認ください。
コメント