コンテンツとコーディングのギャップの橋渡し: DirectX オーディオ スクリプティング入門

Scott Selfon
Audio Content Consultant, Xbox Advanced Technology Group

November 2001

要約: 本ドキュメントは、Microsoft DirectMusic 用のコンテンツ作成ツールである Microsoft DirectMusic Producer のさまざまな側面を扱った一連の記事のうちの一編です。このドキュメントではMicrosoft DirectX オーディオ スクリプティングを紹介します、これはコンテンツを使用しているアプリケーションとは独立の、再生の細い制御に役立つ単純なプログラミング言語です。作曲者またはサウンド デザイナの立場から焦点を当てています。

はじめに

ゲームのようなコンピュータ アプリケーション用のオーディオ コンテンツ開発時の最も大きな問題の 1 つは、技術指向の開発者と、クリエイティビティ指向のコンテンツ作成者の間に存在するコミュニケーションの障壁です。この障壁のために、ゲームのサウンド エフェクト、環境音、ミュージックの作成プロセスで、さまざまな問題が発生することがあります。

  • サウンドトラックが最初の試みで完璧になることはめったになく、作成/試聴/編集のサイクルには時間がかかる。
  • サウンド デザイナは、開発チームがコード内で変更点を実装するまで、最終的なエフェクトを聴くことができない。
  • ゲーム内のオーディオの実装は、どうすれば最高のエフェクトを実現できるかということよりも、どうすればプログラミングが一番簡単になるかという観点から決まることが多い。

Microsoft(R) DirectX(R) オーディオ スクリプティングは、作曲者が単なるコンテンツを作成するのではなく、開発者が呼び出すトリガをベースにしてオーディオの動作を指定できるようにすることで、これらの問題への解決策を提供します。

この記事では以下のトピックを扱っています。

  • DirectX オーディオ スクリプティングの概要
  • ウェーブの再生
  • コンテンツのデバッグ: [Message] ウィンドウ
  • セグメントの再生と停止
  • パラメータの使用(または、ミュージックをより音楽的にする方法)
  • セカンダリ セグメント: サウンドのレイヤ化
  • オブジェクト名(または、これを本当に入力する必要はあるのか?)
  • 基本的なシナリオ: ミュージック、サウンド エフェクト、会話
  • 高度なトピック
  • 関連情報

DirectX オーディオ スクリプティングの概要

DirectX オーディオ スクリプティングにより、作曲者やサウンド デザイナはオーディオの動作を制御できるようになります。作曲者とサウンド デザイナは、DirectMusic(R) Producer で、Microsoft Visual Basic(R) Scripting Edition をベースにした言語である AudioVBScript を使ってスクリプト ルーチンを作成します。これらのルーチンは、どのコンテンツが再生されるのかを決定し、セグメントの再生に使用するオーディオパス、他のセグメントを基準としたキューのタイミング、マスター ボリューム、マスター テンポなどのさまざまなパラメータを制御できます。開発者の側は、変数の更新と読み取りを行い、ルーチンを呼び出すだけで済みます。

スクリプティングは、サウンド エフェクトの再生といった単純なものから、動的に変動するミュージカル スコアの作成といった複雑なものまで、さまざまなオーディオ デザインに使えます。スクリプティングされたコンテンツは、開発者が制御するコンテンツと連動して実行できます。

ウェーブの再生

まず、非常に単純なスクリプトの作成から始めましょう。

  1. DirectMusic Producer を実行し、プロジェクトを開くか作成する。

  2. [File] メニューの [Insert Files into Project] を選択し、.wav ファイルを開く。アプリケーションは、DirectMusic Producer 環境に固有の追加情報を含んでいるデザイン時の.wvpファイルを挿入します。

    図 1. プロジェクト ツリーの中のウェーブ ファイル

  3. [File] メニューの [New] を選択し、[Script] をクリックする。

  4. Script Content フォルダを展開し、ウェーブ アイコンを Reference Runtime フォルダにドラッグする。これにより、スクリプトからウェーブにアクセスできます。

    図 2. スクリプト内のウェーブ ファイル

  5. Script1 ノードか、その左のスクリプト アイコンをダブルクリックして、[Script Designer] ウィンドウを開く。

  6. ウィンドウの左側に、次のように入力する (Source というラベルの付いた部分):

    Sub  PlayMe
        hello.play
    End Sub 
    

スクリプトにドラッグしたウェーブに "hello" 以外の名前が付いていた場合には、スクリプト内の "hello" の部分をウェーブの名前に置き換える必要があります。

画面の別の部分をクリックするか [Refresh] ボタンをクリックすると、ルーチンの名前 PlayMe が [Routines] ウィンドウに表示されます。これが表示されない場合には、ルーチン全体を正しく入力しているかどうかを確認してください。

図 3. [Routines] ウィンドウ

これで最初のスクリプト ルーチンが完成しました! [Routines] ウィンドウでルーチン名をダブルクリックしてテストしてください。ウェーブが 1 回だけ再生されるはずです。

ウェーブが長い場合には、途中で停止できるようにすると便利です。このためには、次のルーチンを使用します。

Sub  StopMe
    hello.stop
End Sub  

これらのルーチンを [Routines] ウィンドウ内でのダブルクリックは、アプリケーションが行う操作と同じである点に注意してください。アプリケーションは単に "PlayMe" という名前のルーチンか "StopMe" という名前のルーチンをトリガしているだけです。これらのルーチンは、どのように編集しても、アプリケーション開発者の観点からは何も変わりません。

コンテンツのデバッグ: [Message] ウィンドウ

ルーチン名がなかなか [Routines] ウィンドウに表示されなくて困ることはありませんか? DirectMusic Producer は、スクリプトがなぜ動作していないのかを理解するのに役立つデバッグ コンソールを、[Message]ウィンドウの形で提供しています。このウィンドウは [Add-Ins] メニューから開くことができます。

このウィンドウが開いていると、編集エリアの外がクリックされるか、[Refresh] ボタンがクリックされて、スクリプトが更新されるたびに、エラー メッセージが表示されます。たとえば、次のようなルーチンを入力したとします。

Sub Play Me

hello.play

End Sub

[Message] ウィンドウには次のようなエラーが表示されます。

図 4. [Message] ウィンドウに表示されるスクリプト エラー

この例では、ルーチン名の "Play" と "Me" の間にスペースがあります。名前の中にスペースは使えないので、エラー メッセージが表示されます。1 行目の 10 列目がどこの位置なのかを確認するには、メインの DirectMusic Producer ウィンドウの右下隅のステータス バーを見ながら、カーソルを Source text エリア内で動かします。

図 5. ステータス バーに表示されるカーソル位置

次に、エラーを修正して [Refresh] ボタンをクリックすると、[Message] ウィンドウにはエラー メッセージが表示されなくなります。ウィンドウは [Message] ウィンドウの [Messages] メニューの [Clear] をクリックすれば、いつでもクリアできます。

セグメントの再生と停止

DirectMusic ベースのコンテンツの基本的な構成要素であるセグメントは、ウェーブとまったく同じように再生できます。セグメントと、その再生のために必要なその他のファイル ( 1 つまたは複数の DLS コレクションやそれ以外のファイルが含まれます) を作成したら、単にセグメントをスクリプトの Embed Runtime または Reference Runtime フォルダにドラッグします。

図 6. スクリプト参照フォルダの中のセグメント

スクリプトから再生できるように、スクリプトの Reference Runtime フォルダにセグメントが格納されます。

次に、ウェーブのために作成したものと同じような 2 つのルーチンを作成します。ルーチン名には一意性を持たせてください。

Sub PlayBackgroundMusic
    town.play
End Sub

Sub StopBackgroundMusic
    town.stop
End Sub

パラメータの使い方 (または、ミュージックをより音楽的にする方法)

(単純な .wav ファイルではない) オーサリングされたセグメントでは、サウンドを単に切り詰めるのではなく、いつどのようにして停止させるかという点について、より音楽的な選択を行いたい場合があります。たとえば、Town セグメントを次のダウンビートで停止するというような指定です。DirectMusic はこの種の動作を強力にサポートしており、AudioVBScript では Play メソッドと Stop メソッドのオプション パラメータを通して公開しています。

Sub  StopBackgroundMusic
    Town.stop AtMeasure
End Sub  

このルーチンは、ルーチンが呼び出された後の次のダウンビートで、セグメントを停止します。他の境界タイプについても、AtImmediate、AtGrid、AtBeat などの似たようなフラグが用意されています。

もう 1 つの一般的なシナリオとして、セグメントが終わったときに、ミュージックの別の部分を再生したり、無音状態に移行するというような場合があります。ミュージックの終了部分を、TownEnd.sgp という名前の独立したセグメントとして作成したとします。次に、このエンディング セグメントを次のビートで再生するスクリプト ルーチンを作成します。ミュージックは TownEnd が終了した時点で停止します。

Sub EndBackgroundMusic
    TownEnd.play AtBeat 
End Sub

DirectMusic では、プライマリ セグメントは一度に 1 つしか再生できないことに注意してください。別のプライマリ セグメントを再生すると、それまで再生されていたプライマリ セグメントは停止し、置き換えられます。したがって、プログラムに対して TownEnd を再生するように指示すると、暗黙のうちに Town は次のビートで停止されることになります。TownEnd が完了すると、パフォーマンスは無音状態になります。

セカンダリ セグメント: サウンドのレイヤ化

一般的には、複数のコンテンツを同時に再生しなくてはならないことが多いでしょう。バックグラウンド ミュージックを含んでいるセグメント、環境音を含んでいるセグメント、そして 1 つまたは複数のサウンド エフェクトを同時に再生しなくてはならない場合があります。このような状況では、セカンダリ セグメントが役立ちます。1 つのプライマリ セグメントの上に任意の数のセカンダリ セグメントをレイヤ化して置くことができます。唯一の制限は、パフォーマンスが 1 つのアクティブ テンポしか持てないということです。

   

テンポとは独立に再生されるクロック タイム セグメントまたはトラックを使うことで、ウェーブ ベースのコンテンツはこの制限を回避できます。詳細については、DirectMusic Producer のヘルプで "clock time" を検索してください。.

新しいプライマリ セグメントを開始しても、現在再生中のセカンダリ セグメントには影響はありません。

セグメントをセカンダリ セグメントとして再生するには、単に IsSecondary フラグを使用します。このフラグとIsControl フラグなしで再生されるセグメントは、すべてプライマリ セグメントとして再生されます。

Sub  SayHello
    hello.play IsSecondary
End Sub

また、境界フラグとコントロール フラグを足し合わせる (単項のプラス記号を挟む) ことで混在が可能になります。たとえば、アンダースコアで適切なタイミングが訪れるまで、キャラクタに "hello" と言わせるのを待たせてみましょう。このためには、プライマリ セグメント (つまりミュージック) のマーカー トラックに 1 つまたは複数のマーカーを配置します。その後で、ルーチンに対して、プライマリ セグメント内の次のマーカーまで待つように指示します。

Sub  WaitToSayHello
    hello.play IsSecondary + AtMarker
End Sub  

これはセカンダリ セグメントなので、"Hello" のサウンドは、プライマリ セグメントのミュージカル スコアに影響を与えずに再生されます。

オブジェクト名 (または、これを本当に入力する必要はあるのか?)

オリジナルのセグメントまたはウェーブ ファイルの名前に縛られる必要はあるのでしょうか? たとえば、BigBossCackleLoudly という名前のウェーブがあったとして、スクリプト内では本当にこの長い名前を使用する必要があるのでしょうか? その答えは No です。ソース ウェーブに影響を与えずに、スクリプト参照の名前を変更できます。このためには、スクリプトの Embed Runtime または Reference Runtime フォルダでオブジェクトを右クリックし、ショートカット メニューの [Change Content Identifier] をクリックします。その後、スクリプトがこのオブジェクトに対して使用する名前を変更できます。これを後で行う場合には、スクリプトの実際のテキストを調べ、前の名前で行っていた参照をすべて置き換えなくてはならないことに注意してください。

図 7. スクリプトの Reference Runtime フォルダでウェーブの名前を変更する

基本的なシナリオ: ミュージック、サウンド エフェクト、会話

これらの概念をすべて組み合わせて、基本的なスクリプトを作成しましょう。音楽的に適切な境界で、互いにトランジションし、開始、停止できる、2 つのバックグラウンド ミュージックを作成します。このミュージックからキックできるサウンド エフェクトと会話の断片も作成します。これらの処理はいずれもプログラマに対しては抽象化されているので、下位のコンテンツでは、ウェーブ、バリエーション付きのウェーブ トラックを使ったセグメント、MIDI と DLS 再生を使ったセグメントなどの任意のものが使用できます。

セットアップ

まずインテンシティの低いスコアの再生から始めます。特定のキャラクタが部屋に入ってくると、そのキャラクタはプレーヤーに対して決闘を申し込みます。すると、ミュージックをインテンシティの高いスコアに切り替えます。しばらく剣での戦いが行われた後に、そのキャラクタは勝利を収め、相手は最後の呼吸とともに秘密を明かします。その後、ミュージックのインテンシティは元に戻ります。

コンテンツ

実際のコンテンツは、タイトルに適した別のものに交換できます。スクリプトが参照する名前は、拡張子の左にある文字列としていますが (たとえば ambient.sgp の場合、スクリプトは "ambient" を使用します)、前述したように、必要なら参照名を変更できます。

サンプルのコンテンツは以下の要素から構成されています。

  • **ミュージック
    **ambient.sgp という名前のセグメントと、swordfight.sgp という名前のセグメントがあります。また、transition_up.sgp という名前 (環境音から決闘音へと移行) と transition_down.sgp という名前 (決闘音から環境音へ移行) の 2 つの短いトランジション セグメントがあります。
  • **会話
    **2 つのウェーブ ファイルがあります。1 つは "I challenge you to a duel" (challenge.wvp) という内容で、もう 1 つは相手が致命傷を負った後に喋るセリフ (tell_secret.wvp) です。
  • **サウンド エフェクト
    **剣を抜いたときのノイズ (sword_draw.wav)、剣が当たるときのノイズ (clash.sgp、剣のサウンド エフェクトのいくつかのバリエーションから選択を行う、ウェーブ トラック付きのセグメント)、そして可変のダメージ ノイズ (damage.sgp) があります。

トリガ

開発者とともに、開発者がどのトリガを呼び出すのかを決定しておく必要があります。前述のシナリオをベースにすれば、次の表に示す内容で十分でしょう。説明文により、開発者はどのような動作が期待されているのかを把握できます。

ルーチン 説明
EnterScene シーンの開始時にミュージックを開始。
DuelerEnters 悪者がキャラクタに挨拶をし、決闘を申し込むときの会話。
BeginFight ミュージックを変更し、剣を抜くサウンド エフェクトを再生。
SwordClash 剣が接触するたびに再生するサウンド エフェクト。
SwordDamage 剣が当たるたびに再生するサウンド エフェクト。
EndFight 悪者が秘密を明かすときの会話。また、ミュージックを元のインテンシティに戻す。
ExitScene ミュージックを停止。

スクリプト

最初に、個々のソース ファイルをスクリプトの Embed Runtime コンテナにドラッグします。この例では、開発者は単にスクリプトをロードしたいだけで、コンテンツの他の部分が同時にロードされるかどうかは気にしていないものとします。

図 8. スクリプト内に埋め込まれたウェーブとセグメント

その後、上に述べた個々のトリガのためのルーチンを作成します。ちなみに、アポストロフィーの右のテキストはコメントと見なされ、スクリプト エンジンからは無視されます。コメントは、特にしばらくしてから見直したときに、スクリプトの内容を理解するのに役立ちます。

Sub  EnterScene
    ' 初めてシーンに入ったときに呼び出される。
    ' ミュージックをプライマリ セグメントとして再生する。
     ambient.play
End Sub
 
Sub  DuelerEnters
    ' キャラクタに挨拶させ、決闘を申し込ませる。 
    challenge.play IsSecondary
End Sub  
 
Sub  BeginFight
    ' 決闘を受け入れたら、ミュージックを変更し、 
    ' 剣を抜くサウンド エフェクトをトリガする。
 
    ' サウンド エフェクトを再生する。
    sword_draw.play IsSecondary + AtImmediate
 
    ' 環境音 ミュージックの次のメジャーの終わりで、
    ' トランジション セグメントを再生する。
    transition_up.play AtMeasure
 
    ' トランジションの再生が終了したら、
    ' 決闘のミュージックを再生する。
    swordfight.play AtFinish
End Sub
 
Sub  SwordClash

    ' 剣が接触するたびに呼び出す。
    clash.play IsSecondary + AtImmediate
End Sub  
 
Sub  SwordDamage
    ' 剣が当たるたびに呼び出す。
    damage.play IsSecondary + AtImmediate
End Sub
 
Sub  EndFight
    ' 決闘者が秘密を明かす。
         tell_secret.play IsSecondary
 
    ' 同時に、現在のプライマリ セグメント(swordfight)を置き換えることで、
    ' ミュージックを元のインテンシティに戻す。
    transition_down.play AtBeat
    ambient.play AtFinish
End Sub  

Sub  ExitScene
    ' ミュージックを停止する
    ambient.stop AtMeasure
End Sub 

最後のルーチンでは、キャラクタは、環境音ミュージックが再生されているときにのみ、シーンを出ることができると仮定しています。キャラクタがいつでもシーンを終了できる場合には、現在再生中のセグメント (PlayingSegment オブジェクト)を追跡しておいて、そのセグメントを停止するか、現在再生されている可能性のあるすべてのセグメントを停止する必要があります。再生されていないセグメントに対して Stop を呼び出しても害はありません。

高度なトピック

ここまでで述べたのは、DirectX オーディオ スクリプティングの機能の氷山の一角に過ぎません。しかし、コンテンツそのものが大きなパワーを持っているため、単にコンテンツの断片の再生と停止を行うだけで、さまざまな効果を生み出すことができます。

以下に、検討の価値がある、オーディオ スクリプティングのその他の側面をいくつか示します。

  • Segment.Play のパラメータ
    このメソッドには4種類のパラメータが渡せます。この記事では、フラグについてしか触れませんでした。それ以外にも、このコンテンツの再生に使用するオーディオパス、このセグメントの前に再生するトランジション セグメント(われわれの例では Play
    を 2 度呼び出すことで手作業で実現しています)、および置換する PlayingSegment オブジェクトを指定できます。
  • オーディオパス
    上記のシナリオでシーンを終了するときに、ミュージックを 5 秒間でフェードアウトさせたい場合にはどうすればいいでしょうか? アクティブなオーディオパスの Audiopath.SetVolume
    スクリプティング メソッドを使用すると、オーディオパスのすべてのパフォーマンスを、一定の時間でフェードインまたはフェードアウトできます。また、SetMasterVolume メソッドを使用すると、パフォーマンス全体のボリュームをフェードインまたはフェードアウトできます。
  • **変数
    **スクリプトとアプリケーションは、変数を使って情報を受け渡すことができます。スクリプト内で宣言されたグローバル変数は、アプリケーションからも見えます。
  • スクリプトによるランダム化
    AudioVBScript には乱数を返す Rand
    関数があります。これにより、たとえばトリガが生じたときに、5 つのセグメントのうちの 1 つをランダムに再生できます。セグメントには、二度と同じようには再生されない可変コンテンツを持たせることができますが、この機能を使えばさらに柔軟性を持たせることができます。

自己変更型ミュージック

セグメントは、再生中の特定のタイミングでスクリプト ルーチンをトリガするスクリプト トラックを持つことが可能です。この機能を使って、自己変更型ミュージック、つまりアプリケーションからの入力なしに変化するミュージックを作成できます。たとえば、セグメントは再生中にグルーヴ レベルの変更や、セカンダリ セグメントのランダムなトリガが可能です。

関連情報

DirectX オーディオ スクリプト作成方法の詳細については、DirectMusic Producer のヘルプを参照してください。

アプリケーション内でのスクリプトの使用方法の詳細については、DirectX Software Development Kit (SDK)のDirectX Audio ヘルプを参照してください。

このシリーズの以下の記事では、DirectMusic Producer の使い方に関する詳しい情報を提供しています。