DirectMusic でMIDIファイルを演奏してみる

DirectX

hspdx.dll によって提供される DirectDraw や、MIAさんの dsoundex.dll によって提供される DirectSound などなど、HSPでもお馴染みとなっている DirectX の存在は、皆さんも知っていることと思います。

DirectX は、Windowsが提供するシステムコンポーネントの1つです。ハードウェアの持つ能力を直接制御することによって、できる限り細かく、高速な機能を提供できるように設計されています。

DirectX は、用途に応じた複数のコンポーネントを提供しています。以下に、その例をいくつか示します。

DirectX は COM の形で提供されていて、そのオブジェクトクラスの数は多く、使われるインターフェースもまた膨大なものになります。このページでは、それらすべてを取り上げていくことはできないため、DirectX の機能のごく一部のみを取り扱っていくつもりです。もしもほかのインターフェースが提供する機能を使いたい場合は、このページで記述されているものだけでは足りません。マイクロソフトのページから DirectX SDK をダウンロードし、インストールすることで DirectX に関係するヘッダファイルやリファレンス(英語版)が手に入ります。

また、マイクロソフトのホームページでは DirectX 7.0 および DirectX 8.0 の日本語版のリファレンスが公開されているので、そちらもあわせて参照してください。

DirectMusic

今回は、MIDIファイルを演奏する目的で DirectMusic を使ってみたいと思います。

DirectMusic は、DirectX 6.1 より追加されたコンポーネントで、MIDIなどのメッセージベースのサウンドを再生させる機能を持ち、主に音楽を再生するのに使用されます。内部では、シンセサイザがメッセージに対応するウェーブ(波形)データを DirectSound に送り、それを DirectSound が合成して実際に音を発生させる、ということが行なわれます。(実際にはもっと複雑ですが。)


先に言っておきますが、 DirectMusic を使ってうまく再生されるMIDIファイルは GM フォーマットのものだけです。 Roland 88 (GS) とか XG とかは、ほとんどがうまく演奏されないようです。

手順

では、実際に MIDI ファイルを再生させてみることにします。手順は以下のようになります。かなり複雑ですが、注意してみていきましょう。

  1. パフォーマンスオブジェクトを作成してインターフェースを取得し、 IDirectMusicPerformance::Init メソッドで初期化する。

  2. IDirectMusicPerformance::AddPort メソッドを呼び出してパフォーマンスにポートを割り当てる。

  3. ローダーオブジェクトを作成して IDirectMusicLoader インターフェースを取得する。

  4. DMUS_OBJECTDESC 構造体にロードするファイルの情報を格納して IDirectMusicLoader::GetObject メソッドでファイルからセグメントオブジェクトを作成し、 IDirectMusicSegment インターフェースを取得する。

  5. IDirectMusicSegment::SetParam メソッドで、GUID_StandardMIDIFile を指定し、SMF(スタンダードMIDIファイル)として再生されるように設定する。

  6. IDirectMusicSegment::SetParam メソッドで GUID_Download を指定し、音色データをダウンロードしてパフォーマンスに割り当てる。

  7. IDirectMusicPerformance::PlaySegment メソッドでセグメントを再生する。

  8. 演奏を停止させる場合には IDirectMusicPerformance::Stop メソッドを呼び出す。

  9. IDirectMusicSegment::SetParam メソッドで GUID_Unload を指定し、音色データをアンロードする。

  10. IDirectMusicPerformance::CloseDown メソッドでパフォーマンスオブジェクトをクローズする。

  11. IDirectMusicSegmentIDirectMusicLoaderIDirectMusicPerformance インターフェースの Release メソッドを呼び出して、オブジェクトを解放する。

IID Explorer によって取得された各 CLSID ・ IID や、各メソッドのインデックスなどの定義は、定義用のスクリプトを別ファイル(def.as)で作成しておきます。

パフォーマンスの作成と初期化

DirectMusic では、 DirectMusic パフォーマンスオブジェクト(以下パフォーマンスと呼ぶ)が演奏全体の管理・処理を行ないます。

まずはパフォーマンスを作成し、IDirectMusicPerformance インターフェースを取得することから行ないます。

パフォーマンスのクラスIDは

{d2ac2881-b39b-11d1-8704-00600893b1bd}

です。また、 IDirectMusicPerformance インターフェースのインターフェースIDは

{07d43d03-6523-11d2-871d-00600893b1bd}

になります。これら2つを createobj 命令に渡すことで、インターフェースポインタを取得できます。

これらのクラスID・インターフェースIDは、別スクリプト def.as で定義しておきます。

#include "llmod.as"
#include "rrmod/com/lollipop.as"

#include "def.as"

; パフォーマンスオブジェクトの作成
createobj pPerformance, CLSID_DirectMusicPerformance, IID_IDirectMusicPerformance

その後、IDirectMusicPerformance::Init メソッドを呼び出して、パフォーマンスを初期化しなくてはなりません。

HRESULT Init(
    IDirectMusic**  ppDirectMusic,
    LPDIRECTSOUND   pDirectSound,
    HWND            hwnd
);

ppDirectMusic には、今回は DirectMusic オブジェクトが内部管理されるように 0 (NULL) を指定します。

pDirectSound には、アプリケーションが他で DirectSound を使用している場合は、そのオブジェクトの IDirectSound インターフェイスポインタを指定しなければいけないことになっています。これは、DirectSound オブジェクトが1つのプロセス1つだけしか作成できないことになっているためです。今回はそのようなことはないので、 0 (NULL) を指定しておくことで、内部で自動的に DirectSound オブジェクトが作成され管理されます。

hwnd には、 DirectSound の作成に使うウィンドウハンドルを指定します。 0 (NULL) を指定することで、前面ウィンドウが指定されます。

; IDirectMusicPerformance::Init メソッド
pm = 0, 0, 0
icall  pPerformance, IDirectMusicPerformance_Init, pm, 3

ポートの割り当て

次に、パフォーマンスにポートを割り当てます。これには IDirectMusicPerformance::AddPort メソッドを呼び出します。

HRESULT AddPort(
    IDirectMusicPort*  pPort
);

pPort にはポートオブジェクトを表すインターフェースポインタを指定しますが、今回はデフォルトポートを使用するので 0 (NULL) を指定します。

; IDirectMusicPerformance::AddPort メソッド
pm = 0
icall pPerformance, IDirectMusicPerformance_AddPort, pm, 1

ローダーの作成

DirectMusic では、 DirectMusic ローダーオブジェクト(以下ローダーと呼ぶ)が、他のオブジェクトのロードを行ないます。スタンダードMIDIファイル(SMF)のロードもローダーで行なうことができます。

ローダーオブジェクトを作成して、IDirectMusicLoader インターフェースを取得しなくてはなりません。ローダーのクラスIDは

{d2ac2892-b39b-11d1-8704-00600893b1bd}

です。また、 IDirectMusicLoader インターフェースのインターフェースIDは

{2ffaaca2-5dca-11d2-afa6-00aa0024d8b6}

になります。これらを createobj 命令に渡すことで、ローダーのインターフェースポインタを取得できます。

; ローダーオブジェクトの作成
createobj pLoader, CLSID_DirectMusicLoader, IID_IDirectMusicLoader

セグメントのロード

DirectMusic セグメントオブジェクト(以下セグメントと呼ぶ)は、シーケンス化されたサウンドデータを表すオブジェクトです。DirectMusic では、1つのSMFのデータを、1つのセグメントオブジェクトとして扱います。

ファイルをロードしてその内容からセグメントを作成するには、 DMUS_OBJECTDESC 構造体にロードするファイルの情報を格納して IDirectMusicLoader::GetObject メソッドを呼び出します。

DMUS_OBJECTDESC 構造体は以下のように定義されています。

typedef struct _DMUS_OBJECTDESC {
    DWORD         dwSize;
    DWORD         dwValidData;
    GUID          guidObject;
    GUID          guidClass;
    FILETIME      ftDate;
    DMUS_VERSION  vVersion;
    WCHAR         wszName[64];
    WCHAR         wszCategory[64];
    WCHAR         wszFileName[260];
    LONGLONG      llMemLength;
    PBYTE         pbMemData;
    IStream *     pStream;
} DMUS_OBJECTDESC, *LPDMUS_OBJECTDESC;

dwSize メンバはこの構造体のサイズを表すもので、ここでは 848 を指定します。dwValidData メンバは構造体の有効メンバを指定するもので、今回はファイル名をフルパス指定できるように DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH を指定します。

今回は、まず guidClass メンバに、セグメントオブジェクトのクラスIDを格納しなくてはなりません。セグメントのクラス ID は、

{d2ac2882-b39b-11d1-8704-00600893b1bd}

で表されますが、これは文字列形式で表したものなので、 GUID (CLSID) 構造体の形に直してから格納します。クラスIDを文字列形式から構造体に変換するには、 lollipop モジュールで定義されている clsidconv 命令を使用することができます。

clsidconv v1, s2, [n3
]
v1 :構造体を格納する変数
s2 :文字列表記の GUID
n3 :変数 v1 の先頭からのインデックス(バイト数)

この命令はちょっと特殊なもので、パラメータ n3 を省略した場合と、 n3 を明示的に指定した場合とで、動作がやや異なってきます。 n3 を省略した場合、この命令は、メソッドのパラメータとして CLSID の参照(構造体のアドレス)が渡せるように、 v1.0 には構造体のアドレスが格納され、実際の構造体データは v1.1v1.4 に格納される仕組みになっています。一方、 n3 を明示的に指定した場合には、変数 v1 の先頭から n3 バイトの位置に、変換したCLSID構造体を格納するようになります。

今回は DMUS_OBJECTDESC 構造体の guidClass メンバ、すなわち、構造体として割り当てた変数の24バイト目の位置に格納するので、パラメータ n3 には明示的に 24 を指定すればよいことになります。

次に、wszFileName メンバにファイル名を格納します。dwValidData メンバに DMUS_OBJ_FULLPATH フラグを指定した場合は、フルパスで指定することになりますが、このファイル名はワイド文字列(Unicode 文字列)で指定しなくてはなりません。したがって、いったん Unicode に変換してから、コピーすることになります。(WCHAR は、2バイトのワイド文字を表す型なので、wszFileName メンバのサイズは 520 バイトになります。)

; DMUS_OBJECTDESC 構造体
dim  objdesc, 212
objdesc.0 = 848
objdesc.1 = 0x00000032      ; DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME
                            ; | DMUS_OBJ_FULLPATH

; CLSID を文字列形式から構造体に変換し objdesc の24バイト目に格納
clsidconv objdesc, CLSID_DirectMusicSegment, 24

; ファイル名のUnicode変換
sdim  filename, 260
sdim  w_filename, 520       ; Unicode文字列を格納
filename = "C:\\My Documents\\test.mid"
to_uni  w_filename, filename, -1      ; Unicodeに変換
memcpy  objdesc.78, w_filename, 520

DMUS_OBJECTDESC 構造体にデータを格納できたら、 IDirectMusicLoader::GetObject メソッドを呼び出して IDirectMusicSegment インターフェースを取得します。

HRESULT GetObject(
    LPDMUS_OBJECTDESC pDesk,
    REFIID            riid,
    LPVOID           *ppv
);

pDesk には、情報を格納した DMUS_OBJECTDESC 構造体のアドレスを指定します。 riid には、取得するインターフェースのインターフェースIDを指定します。 ppv には、取得したインターフェースポインタを格納するための変数のアドレスを指定します。

riid には、 IDirectMusicSegment インターフェースのインターフェースIDを指定します。このインターフェースIDは、

{f96029a2-4282-11d2-8717-00600893b1bd}

で表されます。ただし、ここでは、インターフェースIDを格納した GUID 構造体(IID 構造体)のアドレスを指定しなければなりません。インターフェースIDを構造体の形式に変換するには、 lollipop モジュールで定義されている iidconv 命令を使います。

iidconv v1, s2, [n3]
v1 :IID 構造体を格納する変数
s2 :文字列表記の IID
n3 :変数 v1 の先頭からのインデックス(バイト数)

この命令もまた clsidconv 命令と同じで、 n3 を省略した場合と明示的に指定した場合とで、動作が異なってきます。ここでは n3 を省略することによって、 v1.0 には構造体のアドレスが格納され、実際の構造体データが v1.1v1.4 に格納されます。そして、 GetObject メソッドのパラメータとしては、構造体のアドレスを渡さなければならないので、 v1.0 に格納された値を渡すことになります。

getptr  pm.0, objdesc     ; DMUS_OBJECTDESC 構造体アドレス取得

iidconv iid, IID_IDirectMusicSegment   ; IID を文字列形式から構造体に変換
pm.1 = iid.0                      ; IID 構造体のアドレス

getptr pm.2, pSegment             ; インターフェースを格納する変数

; IDirectMusicLoader::GetObject メソッド
icall pLoader, IDirectMusicLoader_GetObject, pm, 3

セグメントのパラメータ設定

次に、セグメントのパラメータを設定します。

まずは、 GUID_StandardMIDIFile をパラメータとして IDirectMusicSegment::SetParam メソッドを呼び出します。

HRESULT SetParam(
    REFGUID      rguidType,
    DWORD        dwGroupBits,
    DWORD        dwIndex,
    MUSIC_TIME   mtTime,
    void*        pParam
);

このメソッドは、セグメントが所有しているトラックのパラメータを設定するものです。

rguidType パラメータはどのパラメータを設定するのかを指定するもので、いくつかの定義された GUID のうちの1つを指定します。ここでは、 GUID_StandardMIDIFile を指定して、トラックがSMFとして再生されるように設定します。この GUID は、

{06621075-E92E-11D1-A8C5-00C04FA3726E}

と定義されています。実際には、この GUID を格納した GUID 構造体のアドレスを格納します。この GUID はインターフェースIDではありませんが、インターフェースIDの場合と同様に iidconv 命令を使うことができます。

SetParam メソッドの dwGroupBits パラメータはグループビットと呼ばれるもので、どのグループに属するトラックを設定するかを指定するものですが、ここではすべてのトラックグループを指定するために 0xFFFFFFFF (すなわち -1 )を指定します。 dwIndex は、グループ中のどのトラックに設定するかを指定しますが、ここではすべてのトラックに設定するために 0x80000000 (DMUS_SEG_ALLTRACKS) を指定します。 mtTime はここでは使われませんから、 0 を指定しておきます。 pParam もまたここでは使用されないので、 0 (NULL) を指定しておきます。

; pSegment->SetParam( GUID_StandardMIDIFile, 0xFFFFFFFF,
;                      DMUS_SEG_ALLTRACKS, 0, NULL );
iidconv guid, GUID_StandardMIDIFile
pm = guid, -1, 0x80000000, 0, 0
icall pSegment, IDirectMusicSegment_SetParam, pm, 5

次に、音色データをパフォーマンスにダウンロードします。これには、 GUID_Download をパラメータとして IDirectMusicSegment::SetParam メソッドを呼び出します。

rguidType パラメータには GUID_Download を指定します。この GUID は、

{d2ac28a7-b39b-11d1-8704-00600893b1bd}

と定義されています。 pParam パラメータには、パフォーマンスオブジェクトの pParam インターフェースポインタを指定します。他のパラメータは前と同じです。

; pSegment->SetParam( GUID_Download, 0xFFFFFFFF, DMUS_SEG_ALLTRACKS,
;                     0, (void*)pPerformance );
iidconv guid, GUID_Download
pm = guid, -1, 0x80000000, 0, pPerformance
icall pSegment, IDirectMusicSegment_SetParam, pm, 5

セグメントの演奏

セグメントを再生させるには、IDirectMusicPerformance::PlaySegment メソッドを呼び出します。

HRESULT PlaySegment(
    IDirectMusicSegment* pSegment,
    DWORD      dwFlags,
    __int64    i64StartTime,
    IDirectMusicSegmentState** ppSegmentState
);

pSegment パラメータには、再生させるセグメントオブジェクトの IDirectMusicSegment インターフェースポインタを指定します。 dwFlags には動作を指定するフラグを指定します。ここでは、 0 を指定しておいて問題ありません。 i64StartTime は、セグメントの開始タイムを指定する64ビット値です。すぐに再生を開始する場合は 0 を指定します。 ppSegmentState には、演奏するセグメントに関してのセグメント状態を表す IDirectMusicSegmentState インターフェースポインタを受け取る変数のアドレスを指定しますが、必要ない場合は 0 (NULL) を指定することができます。

このメソッドを呼び出すときに注意しなければならないのは、 i64StartTime パラメータが __int64 型である、すなわち、64ビット(8バイト)値であることです。メソッド呼び出し時には、この値は2つの4バイト値に分解されて渡されるという仕組みになっています。そのため、スクリプト上で記述する場合にはこのパラメータを2つぶんの引数として扱う必要があります。パラメータ数も 4 ではなく 5 を指定しなければなりません。

; pPerformance->PlaySegment( pSegment, 0, (__int64) 0, NULL );
pm.0 = pSegment
pm.1 = 0
pm.2 = 0, 0                  ; 64ビット値は2つ分のパラメータとして扱う
pm.4 = 0
icall pPerformance, IDirectMusicPerformance_PlaySegment, pm, 5

演奏の停止

演奏を停止させるには、 IDirectMusicPerformance::Stop メソッドを呼び出します。

HRESULT Stop(
    IDirectMusicSegment* pSegment,
    IDirectMusicSegmentState* pSegmentState,
    MUSIC_TIME   mtTime,
    DWORD        dwFlags
);

各パラメータの説明は省略します。直ちに演奏を停止させるには、すべてのパラメータに 0 を指定すれば OK です。

; pPerformance->Stop( NULL, NULL, 0, 0 );
pm = 0, 0, 0, 0
icall pPerformance, IDirectMusicPerformance_Stop, pm, 4

音色のアンロードとセグメントの解放

パフォーマンスにダウンロードしてあった音色データをアンロードします。これには、 GUID_Unload をパラメータとして IDirectMusicSegment::SetParam メソッドを呼び出します。 GUID_Unload は、

{d2ac28a8-b39b-11d1-8704-00600893b1bd}

と定義されています。

音色をアンロードできたら、 release 命令でセグメントを解放します。

; pSegment->SetParam( GUID_Unload, 0xFFFFFFFF, DMUS_SEG_ALLTRACKS,
;                     0, (void*)pPerformance );
iidconv guid, GUID_Unload
pm = guid, -1, 0x80000000, 0, pPerformance
icall pSegment, IDirectMusicSegment_SetParam, pm, 5

; セグメントの解放
release  pSegment

実際には、あとでパフォーマンスをクローズするときにアンロードされていない音色はすべて自動的に解放されるため、明示的にアンロードする必要はありませんが。

パフォーマンスのクローズと解放

パフォーマンスオブジェクトを閉じるには、 IDirectMusicPerformance::CloseDown メソッドを呼び出します。

HRESULT CloseDown();

このメソッドのパラメータはありません。パラメータなしのメソッドを呼び出す場合、 icall 命令の第3および第4パラメータを省略することができます。

このメソッドは、 IDirectMusicPerformance::Init メソッドに対応するもので、 Init メソッドを呼び出してパフォーマンスの初期化を行なった場合には必ず CloseDown メソッドによってパフォーマンスを閉じなければならないことになってます。

パフォーマンスをクローズできたら、 release 命令でパフォーマンスをを解放します。

; pPerformance->CloseDown();
icall pPerformance, IDirectMusicPerformance_CloseDown

; パフォーマンスの解放
release  pPerformance

ローダーの解放

最後に、ローダーオブジェクトがまだ残っているので、これを解放します。

; ローダーの解放
release  pLoader

今回は終了時にローダーの解放を行なっていますが、これ以上ファイルを読み込むことはないと判った時点で、解放してしまっても問題ありません。

ファイルを読み込むたびにローダーオブジェクトを作成・解放するということもできますが、ローダーが読み込んだオブジェクトはローダー自身によってキャッシュされているため、複数のファイルを読み込む場合には、1つのローダーを作成して、それを繰り返し使って読み込むほうが高速になります。

サンプルスクリプト

定義用スクリプトファイル def.as

IID Explorer から取得されたオブジェクトのクラスID・インターフェースIDや、インターフェースのメソッドのインデックス、また、その他の GUID の定義を行なっています。メインスクリプトでこのファイルをインクルードする必要があります。

    ; 定義用スクリプト def.as

    ; クラスIDの定義
    #define CLSID_DirectMusicPerformance "{d2ac2881-b39b-11d1-8704-00600893b1bd}"
    #define CLSID_DirectMusicLoader      "{d2ac2892-b39b-11d1-8704-00600893b1bd}"
    #define CLSID_DirectMusicSegment     "{d2ac2882-b39b-11d1-8704-00600893b1bd}"

    ; インターフェースIDとメソッドインデックスの定義
    #define IID_IDirectMusicPerformance  "{07D43D03-6523-11D2-871D-00600893B1BD}"

    ; IDirectMusicPerformance Methods in VTable Order
    #define IDirectMusicPerformance_QueryInterface             0
    #define IDirectMusicPerformance_AddRef                     1
    #define IDirectMusicPerformance_Release                    2
    #define IDirectMusicPerformance_Init                       3
    #define IDirectMusicPerformance_PlaySegment                4
    #define IDirectMusicPerformance_Stop                       5
    #define IDirectMusicPerformance_GetSegmentState            6
    #define IDirectMusicPerformance_SetPrepareTime             7
    #define IDirectMusicPerformance_GetPrepareTime             8
    #define IDirectMusicPerformance_SetBumperLength            9
    #define IDirectMusicPerformance_GetBumperLength            10
    #define IDirectMusicPerformance_SendPMsg                   11
    #define IDirectMusicPerformance_MusicToReferenceTime       12
    #define IDirectMusicPerformance_ReferenceToMusicTime       13
    #define IDirectMusicPerformance_IsPlaying                  14
    #define IDirectMusicPerformance_GetTime                    15
    #define IDirectMusicPerformance_AllocPMsg                  16
    #define IDirectMusicPerformance_FreePMsg                   17
    #define IDirectMusicPerformance_GetGraph                   18
    #define IDirectMusicPerformance_SetGraph                   19
    #define IDirectMusicPerformance_SetNotificationHandle      20
    #define IDirectMusicPerformance_GetNotificationPMsg        21
    #define IDirectMusicPerformance_AddNotificationType        22
    #define IDirectMusicPerformance_RemoveNotificationType     23
    #define IDirectMusicPerformance_AddPort                    24
    #define IDirectMusicPerformance_RemovePort                 25
    #define IDirectMusicPerformance_AssignPChannelBlock        26
    #define IDirectMusicPerformance_AssignPChannel             27
    #define IDirectMusicPerformance_PChannelInfo               28
    #define IDirectMusicPerformance_DownloadInstrument         29
    #define IDirectMusicPerformance_Invalidate                 30
    #define IDirectMusicPerformance_GetParam                   31
    #define IDirectMusicPerformance_SetParam                   32
    #define IDirectMusicPerformance_GetGlobalParam             33
    #define IDirectMusicPerformance_SetGlobalParam             34
    #define IDirectMusicPerformance_GetLatencyTime             35
    #define IDirectMusicPerformance_GetQueueTime               36
    #define IDirectMusicPerformance_AdjustTime                 37
    #define IDirectMusicPerformance_CloseDown                  38
    #define IDirectMusicPerformance_GetResolvedTime            39
    #define IDirectMusicPerformance_MIDIToMusic                40
    #define IDirectMusicPerformance_MusicToMIDI                41
    #define IDirectMusicPerformance_TimeToRhythm               42
    #define IDirectMusicPerformance_RhythmToTime               43

    #define IID_IDirectMusicLoader     "{2FFAACA2-5DCA-11D2-AFA6-00AA0024D8B6}"

    ; IDirectMusicLoader Methods in VTable Order
    #define IDirectMusicLoader_QueryInterface         0
    #define IDirectMusicLoader_AddRef                 1
    #define IDirectMusicLoader_Release                2
    #define IDirectMusicLoader_GetObject              3
    #define IDirectMusicLoader_SetObject              4
    #define IDirectMusicLoader_SetSearchDirectory     5
    #define IDirectMusicLoader_ScanDirectory          6
    #define IDirectMusicLoader_CacheObject            7
    #define IDirectMusicLoader_ReleaseObject          8
    #define IDirectMusicLoader_ClearCache             9
    #define IDirectMusicLoader_EnableCache            10
    #define IDirectMusicLoader_EnumObject             11

    #define IID_IDirectMusicSegment     "{F96029A2-4282-11D2-8717-00600893B1BD}"

    ; IDirectMusicSegment Methods in VTable Order
    #define IDirectMusicSegment_QueryInterface             0
    #define IDirectMusicSegment_AddRef                     1
    #define IDirectMusicSegment_Release                    2
    #define IDirectMusicSegment_GetLength                  3
    #define IDirectMusicSegment_SetLength                  4
    #define IDirectMusicSegment_GetRepeats                 5
    #define IDirectMusicSegment_SetRepeats                 6
    #define IDirectMusicSegment_GetDefaultResolution       7
    #define IDirectMusicSegment_SetDefaultResolution       8
    #define IDirectMusicSegment_GetTrack                   9
    #define IDirectMusicSegment_GetTrackGroup              10
    #define IDirectMusicSegment_InsertTrack                11
    #define IDirectMusicSegment_RemoveTrack                12
    #define IDirectMusicSegment_InitPlay                   13
    #define IDirectMusicSegment_GetGraph                   14
    #define IDirectMusicSegment_SetGraph                   15
    #define IDirectMusicSegment_AddNotificationType        16
    #define IDirectMusicSegment_RemoveNotificationType     17
    #define IDirectMusicSegment_GetParam                   18
    #define IDirectMusicSegment_SetParam                   19
    #define IDirectMusicSegment_Clone                      20
    #define IDirectMusicSegment_SetStartPoint              21
    #define IDirectMusicSegment_GetStartPoint              22
    #define IDirectMusicSegment_SetLoopPoints              23
    #define IDirectMusicSegment_GetLoopPoints              24
    #define IDirectMusicSegment_SetPChannelsUsed           25

    ; その他のGUIDの定義
    #define GUID_StandardMIDIFile  "{06621075-e92e-11d1-a8c5-00c04fa3726e}"
    #define GUID_Download          "{d2ac28a7-b39b-11d1-8704-00600893b1bd}"
    #define GUID_Unload            "{d2ac28a8-b39b-11d1-8704-00600893b1bd}"

メインスクリプト

    #include "llmod.as"
    #include "unicode.as"
    #include "rrmod/com/lollipop.as"

    #include "def.as"

    ; ファイルのロード
    dialog "mid", 16
    if stat == 0 : end

    sdim filename, 260     ; ファイル名を格納する変数
    filename = refstr

    ; パフォーマンス作成
    createobj pPerformance, CLSID_DirectMusicPerformance, IID_IDirectMusicPerformance
    if pPerformance == 0 {
        dialog "パフォーマンス作成に失敗", 1
        goto *lb_free
    }

    ; パフォーマンス初期化
    ; pPerformance->Init( NULL, NULL, NULL);
    pm = 0, 0, 0
    icall pPerformance, IDirectMusicPerformance_Init, pm, 3
    if dllret < 0 {
        dialog "パフォーマンスの初期化に失敗", 1
        goto *lb_free
    }

    ; パフォーマンス初期化済みを示すフラグ
    f_init = 1

    ; ポートの割り当て
    ; pPerformance->AddPort( NULL );
    pm = 0
    icall pPerformance, IDirectMusicPerformance_AddPort, pm, 1
    if dllret < 0 {
        dialog "ポートの割り当てに失敗に失敗", 1
        goto *lb_free
    }

    ; ローダー作成
    createobj pLoader, CLSID_DirectMusicLoader, IID_IDirectMusicLoader

    ; DMUS_OBJECTDESC 構造体
    dim  objdesc, 212
    objdesc.0 = 848
    objdesc.1 = 0x00000032      ; DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME
                                ;  | DMUS_OBJ_FULLPATH

    ; CLSID を文字列形式から構造体に変換し objdesc の24バイト以降に格納
    clsidconv objdesc, CLSID_DirectMusicSegment, 24

    ; ファイル名のUnicode変換
    sdim  w_filename, 520                 ; Unicodeを格納する変数
    to_uni  w_filename, filename, -1      ; Unicodeに変換
    memcpy  objdesc.78, w_filename, 520

    ; IDirectMusicLoader::GetObject メソッド
    getptr  pm.0, objdesc             ; DMUS_OBJECTDESC 構造体アドレス取得
    iidconv iid, IID_IDirectMusicSegment   ; IID を文字列形式から構造体に変換
    pm.1 = iid.0                      ; IID 構造体のアドレス
    getptr pm.2, pSegment             ; インターフェースを格納する変数
    icall pLoader, IDirectMusicLoader_GetObject, pm, 3
    if (dllret < 0)|(pSegment == 0) {
        dialog "ファイルロードに失敗", 1
        goto *lb_free
    }

    ; pSegment->SetParam( GUID_StandardMIDIFile, 0xFFFFFFFF,
    ;                      DMUS_SEG_ALLTRACKS, 0, NULL );
    iidconv guid, GUID_StandardMIDIFile
    pm = guid, -1, 0x80000000, 0, 0
    icall pSegment, IDirectMusicSegment_SetParam, pm, 5
    if dllret < 0 {
        dialog "トラックパラメータ設定1 失敗", 1
        goto *lb_free
    }

    ; pSegment->SetParam( GUID_Download, 0xFFFFFFFF, DMUS_SEG_ALLTRACKS,
    ;                     0, (void*)pPerformance );
    iidconv guid, GUID_Download
    pm = guid, -1, 0x80000000, 0, pPerformance
    icall pSegment, IDirectMusicSegment_SetParam, pm, 5
    if dllret < 0 {
        dialog "トラックパラメータ設定2(音色ダウンロード)失敗", 1
        goto *lb_free
    }

    ; セグメントの再生
    ; pPerformance->PlaySegment( pSegment, 0, (__int64) 0, NULL );
    pm.0 = pSegment, 0, 0, 0, 0      ; 64ビット値は2つのパラメータとして扱う
    icall pPerformance, IDirectMusicPerformance_PlaySegment, pm, 5
    if dllret < 0 {
        dialog "セグメントの再生に失敗", 1
        goto *lb_free
    }

  ; 停止ボタン
    button "停止", *lb_stop

    onexit *lb_stop
    stop

*lb_stop
    ; 演奏の停止
    ; pPerformance->Stop( NULL, NULL, 0, 0 );
    pm = 0, 0, 0, 0
    icall pPerformance, IDirectMusicPerformance_Stop, pm, 4

*lb_free
    ; 終了時の処理(各オブジェクトの解放)
    if pSegment {
        ; pSegment->SetParam( GUID_Unload, 0xFFFFFFFF, DMUS_SEG_ALLTRACKS,
        ;                     0, (void*)pPerformance );
        iidconv guid, GUID_Unload
        pm = guid, -1, 0x80000000, 0, pPerformance
        icall pSegment, IDirectMusicSegment_SetParam, pm, 5

        ; セグメントの解放
        release  pSegment
        pSegment = 0
    }
    if pPerformance {
        if f_init {
            ; pPerformance->CloseDown();
            icall pPerformance, IDirectMusicPerformance_CloseDown
        }
        ; パフォーマンスの解放
        release  pPerformance
        pPerformance = 0
    }
    if pLoader {
        ; ローダーの解放
        release  pLoader
        pLoader = 0
    }

    end