MIDIを再生しながらWAVを鳴らしてみる

前回のDirectX Audioの続きです。今回は、複数のサウンドを同時に再生させるというものです。BGMとしてMIDIファイルを演奏させながら、効果音などとしてウェーブファイルを再生させる、といった使い方ができます。

プライマリセグメントとセカンダリセグメント

MirectMusic(またはDirectX Audio)では、セグメントは演奏させる時の役割として、2つに分けられます。

1つはプライマリセグメントです。これは1つのパフォーマンス中で一度に1つだけ演奏でき、パフォーマンス全体のテンポなどを決定します。前回まで作成したスクリプトでは、すべてセグメントをプライマリセグメントとして再生させています。

もう1つはセカンダリセグメントです。これはプライマリセグメントと共に演奏されるサウンドのことで、通常は短いサウンドなどを演奏するのに使われます。

プライマリセグメントは同時に1つしか再生させることができませんが、セカンダリセグメントは同時に複数を再生させることができます。したがって、BGMとして演奏するサウンドをプライマリセグメントとして再生させ、効果音などをセカンダリセグメントとして再生させるということができます。

セカンダリセグメントとして再生させる

セグメントをプライマリセグメントとして演奏させるか、セカンダリセグメントとして演奏させるかは、演奏時に指定します。 IDirectMusicPerformance8::PlaySegment メソッドまたは IDirectMusicPerformance8::PlaySegmentEx メソッドを呼び出す際に、動作フラグ(dwFlags パラメータ)として、 DMUS_SEGF_SECONDARY フラグ (0x0080) を指定した場合にはセカンダリセグメントとして演奏されます。 DMUS_SEGF_SECONDARY フラグを指定しなかった場合にはプライマリセグメントとして演奏されます。

; pPerformance->PlaySegmentEx()
; セカンダリセグメントとして再生
pm.0 = pSegment
pm.1 = 0
pm.2 = 0
pm.3 = 0x0080                 ; DMUS_SEGF_SECONDARY
pm.4 = 0, 0                    ; 64ビット値は2つのパラメータとして扱う
pm.6 = 0
pm.7 = 0
pm.8 = 0
icall pPerformance, IDirectMusicPerformance8_PlaySegmentEx, pm, 9

セグメントのリピート演奏

セグメントごとに、演奏の繰り返し回数を設定することができます。繰り返し回数を設定するには、 IDirectMusicSegment8::SetRepeats メソッドを呼び出します。

HRESULT SetRepeats(
    DWORD   dwRepeats
);

dwRepeats には、繰り返す回数を指定します。このパラメータに 0 を指定すると、一度だけ演奏します。また、 -1 (DMUS_SEG_REPEAT_INFINITE) を指定すると、繰り返し回数は無限回となり、明示的に停止されるまで演奏を繰り返すことになります。

; pSegment->SetRepeats( DMUS_SEG_REPEAT_INFINITE );
pm = -1                 ; 繰り返し演奏させる場合は -1 を指定
icall pSegment, IDirectMusicSegment8_SetRepeats, pm, 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_IDirectMusicPerformance8 "{679c4137-c62e-4147-b2b4-9d569acb254c}"

    ; IDirectMusicPerformance8 Methods in VTable Order
    #define IDirectMusicPerformance8_QueryInterface              0
    #define IDirectMusicPerformance8_AddRef                      1
    #define IDirectMusicPerformance8_Release                     2
    #define IDirectMusicPerformance8_Init                        3
    #define IDirectMusicPerformance8_PlaySegment                 4
    #define IDirectMusicPerformance8_Stop                        5
    #define IDirectMusicPerformance8_GetSegmentState             6
    #define IDirectMusicPerformance8_SetPrepareTime              7
    #define IDirectMusicPerformance8_GetPrepareTime              8
    #define IDirectMusicPerformance8_SetBumperLength             9
    #define IDirectMusicPerformance8_GetBumperLength             10
    #define IDirectMusicPerformance8_SendPMsg                    11
    #define IDirectMusicPerformance8_MusicToReferenceTime        12
    #define IDirectMusicPerformance8_ReferenceToMusicTime        13
    #define IDirectMusicPerformance8_IsPlaying                   14
    #define IDirectMusicPerformance8_GetTime                     15
    #define IDirectMusicPerformance8_AllocPMsg                   16
    #define IDirectMusicPerformance8_FreePMsg                    17
    #define IDirectMusicPerformance8_GetGraph                    18
    #define IDirectMusicPerformance8_SetGraph                    19
    #define IDirectMusicPerformance8_SetNotificationHandle       20
    #define IDirectMusicPerformance8_GetNotificationPMsg         21
    #define IDirectMusicPerformance8_AddNotificationType         22
    #define IDirectMusicPerformance8_RemoveNotificationType      23
    #define IDirectMusicPerformance8_AddPort                     24
    #define IDirectMusicPerformance8_RemovePort                  25
    #define IDirectMusicPerformance8_AssignPChannelBlock         26
    #define IDirectMusicPerformance8_AssignPChannel              27
    #define IDirectMusicPerformance8_PChannelInfo                28
    #define IDirectMusicPerformance8_DownloadInstrument          29
    #define IDirectMusicPerformance8_Invalidate                  30
    #define IDirectMusicPerformance8_GetParam                    31
    #define IDirectMusicPerformance8_SetParam                    32
    #define IDirectMusicPerformance8_GetGlobalParam              33
    #define IDirectMusicPerformance8_SetGlobalParam              34
    #define IDirectMusicPerformance8_GetLatencyTime              35
    #define IDirectMusicPerformance8_GetQueueTime                36
    #define IDirectMusicPerformance8_AdjustTime                  37
    #define IDirectMusicPerformance8_CloseDown                   38
    #define IDirectMusicPerformance8_GetResolvedTime             39
    #define IDirectMusicPerformance8_MIDIToMusic                 40
    #define IDirectMusicPerformance8_MusicToMIDI                 41
    #define IDirectMusicPerformance8_TimeToRhythm                42
    #define IDirectMusicPerformance8_RhythmToTime                43
    #define IDirectMusicPerformance8_InitAudio                   44
    #define IDirectMusicPerformance8_PlaySegmentEx               45
    #define IDirectMusicPerformance8_StopEx                      46
    #define IDirectMusicPerformance8_ClonePMsg                   47
    #define IDirectMusicPerformance8_CreateAudioPath             48
    #define IDirectMusicPerformance8_CreateStandardAudioPath     49
    #define IDirectMusicPerformance8_SetDefaultAudioPath         50
    #define IDirectMusicPerformance8_GetDefaultAudioPath         51
    #define IDirectMusicPerformance8_GetParamEx                  52

    #define IID_IDirectMusicLoader8      "{19e7c08c-0a44-4e6a-a116-595a7cd5de8c}"

    ; IDirectMusicLoader8 Methods in VTable Order
    #define IDirectMusicLoader8_QueryInterface             0
    #define IDirectMusicLoader8_AddRef                     1
    #define IDirectMusicLoader8_Release                    2
    #define IDirectMusicLoader8_GetObject                  3
    #define IDirectMusicLoader8_SetObject                  4
    #define IDirectMusicLoader8_SetSearchDirectory         5
    #define IDirectMusicLoader8_ScanDirectory              6
    #define IDirectMusicLoader8_CacheObject                7
    #define IDirectMusicLoader8_ReleaseObject              8
    #define IDirectMusicLoader8_ClearCache                 9
    #define IDirectMusicLoader8_EnableCache                10
    #define IDirectMusicLoader8_EnumObject                 11
    #define IDirectMusicLoader8_CollectGarbage             12
    #define IDirectMusicLoader8_ReleaseObjectByUnknown     13
    #define IDirectMusicLoader8_LoadObjectFromFile         14

    #define IID_IDirectMusicSegment8     "{c6784488-41a3-418f-aa15-b35093ba42d4}"

    ; IDirectMusicSegment8 Methods in VTable Order
    #define IDirectMusicSegment8_QueryInterface             0
    #define IDirectMusicSegment8_AddRef                     1
    #define IDirectMusicSegment8_Release                    2
    #define IDirectMusicSegment8_GetLength                  3
    #define IDirectMusicSegment8_SetLength                  4
    #define IDirectMusicSegment8_GetRepeats                 5
    #define IDirectMusicSegment8_SetRepeats                 6
    #define IDirectMusicSegment8_GetDefaultResolution       7
    #define IDirectMusicSegment8_SetDefaultResolution       8
    #define IDirectMusicSegment8_GetTrack                   9
    #define IDirectMusicSegment8_GetTrackGroup              10
    #define IDirectMusicSegment8_InsertTrack                11
    #define IDirectMusicSegment8_RemoveTrack                12
    #define IDirectMusicSegment8_InitPlay                   13
    #define IDirectMusicSegment8_GetGraph                   14
    #define IDirectMusicSegment8_SetGraph                   15
    #define IDirectMusicSegment8_AddNotificationType        16
    #define IDirectMusicSegment8_RemoveNotificationType     17
    #define IDirectMusicSegment8_GetParam                   18
    #define IDirectMusicSegment8_SetParam                   19
    #define IDirectMusicSegment8_Clone                      20
    #define IDirectMusicSegment8_SetStartPoint              21
    #define IDirectMusicSegment8_GetStartPoint              22
    #define IDirectMusicSegment8_SetLoopPoints              23
    #define IDirectMusicSegment8_GetLoopPoints              24
    #define IDirectMusicSegment8_SetPChannelsUsed           25
    #define IDirectMusicSegment8_SetTrackConfig             26
    #define IDirectMusicSegment8_GetAudioPathConfig         27
    #define IDirectMusicSegment8_Compose                    28
    #define IDirectMusicSegment8_Download                   29
    #define IDirectMusicSegment8_Unload                     30

モジュールスクリプト daudio.as

インターフェースメソッドの呼び出しをモジュールにまとめました。

    #module "daudio"

    #include "def.as"

    ; ======================================================================
    ;  DirectX Audio の初期化(パフォーマンスの作成・初期化、ローダー作成)
    ; ======================================================================
    #deffunc DA_Init
    mref stt, 64

    ; ローダーの作成
    createobj pLoader, CLSID_DirectMusicLoader, IID_IDirectMusicLoader8
    if pLoader == 0 : stt = 1 : return

    ; パフォーマンスの作成
    createobj pPerformance, CLSID_DirectMusicPerformance, IID_IDirectMusicPerformance8
    if pPerformance == 0 : stt = 1 : return

    ; パフォーマンスの初期化
    ; pPerf->InitAudio( NULL, NULL, NULL, DMUS_APATH_SHARED_STEREOPLUSREVERB,
    ;                   16, DMUS_AUDIOF_ALL, NULL );
    pm = 0, 0, 0, 1, 16, $3f, 0
    icall pPerformance, IDirectMusicPerformance8_InitAudio, pm, 7
    if dllret < 0 : stt = 1 : return

    stt = 0
    return


    ; ======================================================================
    ;  サウンドファイルの読み込み(セグメント作成)
    ; ======================================================================
    #deffunc DA_LoadFile  val, str
    mref retSegment, 16     ; セグメントインターフェースを格納する変数
    mref setfilename, 33    ; ファイル名

    retSegment = 0
    sdim filename, 260 : filename = setfilename

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

    ; pLoader->LoadObjectFromFile( CLSID_DirectMusicSegment,
    ;       IID_IDirectMusicSegment8, &w_filename, &pSegment );
    clsidconv  clsid, CLSID_DirectMusicSegment
    pm.0 = clsid
    iidconv  iid, IID_IDirectMusicSegment8
    pm.1 = iid
    getptr  pm.2, w_filename
    getptr  pm.3, retSegment
    icall pLoader, IDirectMusicLoader8_LoadObjectFromFile, pm, 4
    if dllret < 0 : stt = 1 : return

    ; retSegment->Download( pPerformance );
    pm = pPerformance
    icall retSegment, IDirectMusicSegment8_Download, pm, 1
    if dllret < 0 : stt = 1 : return

    stt = 0
    return


    ; ======================================================================
    ;  セグメントの演奏
    ; ======================================================================
    #deffunc DA_Play int, int
    mref pSegment, 0        ; セグメントインターフェースポインタ
    mref fSecondary, 1      ; 1 のときセカンダリセグメント

    ; pPerformance->PlaySegmentEx( pSegment, NULL, NULL, ***, (__int64)0,
    ;                              NULL, NULL, NULL);
    pm = pSegment, 0, 0, 0, 0, 0, 0, 0, 0
    if fSecondary : pm.3 = 128         ; DMUS_SEGF_SECONDARY
    icall pPerformance, IDirectMusicPerformance8_PlaySegmentEx, pm, 9
    if dllret < 0 : stt = 1 : return

    stt = 0
    return

    ; ======================================================================
    ;  セグメントの演奏の停止
    ; ======================================================================
    #deffunc DA_Stop
    mref pSegment, 0        ; セグメントインターフェースポインタ

    ; pPerformance->StopEx(pSegment,(__int64) 0,0);
    pm = pSegment, 0, 0, 0           ; 64ビット値は2つのパラメータとして扱う
    icall pPerformance, IDirectMusicPerformance8_StopEx, pm, 4
    if dllret < 0 : stt = 1 : return

    stt = 0
    return

    ; ======================================================================
    ;  繰り返し回数の設定
    ; ======================================================================
    #deffunc DA_Repeat int, int
    mref pSegment           ; セグメントインターフェースポインタ
    mref nRepeats, 1        ; 回数(0 は1回のみ, -1 は無限回)

    ; pSegment->SetRepeats( nRepeats );
    pm = nRepeats
    icall pSegment, IDirectMusicSegment8_SetRepeats, pm, 1
    if dllret < 0 : stt = 1 : return

    stt = 0
    return

    ; ======================================================================
    ;  セグメントの解放
    ; ======================================================================
    #deffunc DA_FreeSegment int
    mref pSegment           ; セグメントインターフェースポインタ

    ; pSegment->Unload(pPerformance);
    pm = pPerformance
    icall pSegment, IDirectMusicSegment8_Unload, pm, 1

    ; pSegment->Release();
    release pSegment

    return

    ; ======================================================================
    ;  後始末(パフォーマンス・ローダーの解放/終了時に自動的に呼び出される)
    ; ======================================================================
    #deffunc DA_Free  onexit

    ; ローダーの解放
    if pLoader {
        ; pLoader->Release();
        release pLoader
        pLoader = 0
    }

    ; パフォーマンスの解放
    if pPerformance {
        ; pPerformance->CloseDown();
        icall pPerformance, IDirectMusicPerformance8_CloseDown

        ; pPerformance->Release();
        release pPerformance
        pPerformance = 0
    }

    return

    #global

メインスクリプト

ファイルをロードして演奏するサンプルです。

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

    #include "daudio.as"

    sdim buf, 260, 4
    sdim filename, 260, 4

    screen 0, 500, 360
    objsize 90
    repeat 4
        x = 20 : y = cnt*90 + 30
        pos x, y-25
        if cnt==0 {
            mes "プライマリセグメント"
        } else {
            mes "セカンダリセグメント"+cnt
        }
        pos x, y     : button "open", *lb_open : btnid_o.cnt = stat
        pos x+100, y : button "play", *lb_play : btnid_p.cnt = stat
        pos x+200, y : button "stop", *lb_stop : btnid_s.cnt = stat
        pos x+300, y : chkbox "リピート", floop.cnt
        pos x, y+30  : mesbox buf.cnt, 450, 25, 2 : boxid.cnt = stat
    loop

    ; DirectX Audio の初期化
    DA_Init
    if stat : dialog "DirectX Audio の初期化に失敗" : end
    stop

*lb_open
    ; 押されたボタンを判定
    repeat 4
        if stat == btnid_o.cnt : idx = cnt : break
    loop
    ; ファイルを開く
    dialog "mid;*.wav", 16, "サウンドファイル"
    if stat == 0 : stop
    filename.idx = refstr
    if pSegment.idx {
        ; すでに読み込まれている場合は解放
        DA_FreeSegment pSegment.idx
    }
    ; ファイルを読み込んでセグメントを作成
    DA_LoadFile pSegment.idx, filename.idx
    if stat : dialog "ファイル読み込みに失敗" : stop
    ; ファイル名を表示
    objprm boxid.idx, filename.idx
    stop

*lb_play
    ; 押されたボタンを判定
    repeat 4
        if stat == btnid_p.cnt : idx = cnt : break
    loop
    if pSegment.idx {
        ; リピート回数の設定
        if floop.idx {
            DA_Repeat pSegment.idx, -1
        } else {
            DA_Repeat pSegment.idx, 0
        }
        ; セグメントの演奏
        if idx == 0 {
            DA_Play pSegment.0       ; プライマリセグメントとして演奏
        } else {
            DA_Play pSegment.idx, 1  ; セカンダリセグメントとして演奏
        }
    }
    stop

*lb_stop
    ; 押されたボタンを判定
    repeat 4
        if stat == btnid_s.cnt : idx = cnt : break
    loop
    ; セグメントの演奏を停止
    if pSegment.idx : DA_Stop pSegment.idx
    stop