GuruGuruSMF4.dll v4.0.8 ソースコード修正箇所

VS2022 でビルドする為の修正
複数インスタンス対応
Port.h/Port.cpp 大幅修正
GM/GS/XG のマスターボリュームSysExに対応(チャンネルボリュームに置き換えて処理)
処理が大幅に遅れているときに遅れを取り戻さないように修正(スリープモードのサスペンド処理が不要になる？)
DirectMusic のときはスリープモードに入るときのサスペンド処理を無効化

以下、v0.08 以降(修正箇所が多すぎて記すのを諦めた)
複数ポート対応(広範囲に渡る修正の為書ききれず)
リズムパートの SysEx を使う曲データを正常に再生出来ないのを改善(DmPortPlayer.h/cpp)
リセットを含む非ループ曲のループ時、先頭に戻ったらリセットを送信するように修正(DmPortPlayer.h/cpp)

------------------------------------------------------

dmusici.h を dmusici_h\ 以下に置く(VS2022 にない為)
(kbdmusic.kpi で使用しているのと同じもの。VS のどのバージョンのものだったかは不明)
GuruGuruSMF4 プロジェクトの追加のインクルードディレクトリに dmusici_h を追加

Core、dxmus.core の各プロジェクトを使わず GuruGuruSMF4 のプロジェクトにソースファイル群を追加
(stdafx.h/cpp を除く)

Core\stdafx.h/cpp と dxmus\stdafx.h/cpp を削除(プリコンパイル済みヘッダーを使用する場合に支障が出る為)

------------------------------------------------------

GuruGuruSMF4\stdafx.h

#ifndef WINVER から #ifndef _WIN32_IE までコメントアウト

// TODO: プログラムに必要な追加ヘッダーをここで参照してください。
//by Kobarin
#include <cguid.h> //GUID_NULL
//Core\stdafx.h より
//#define GGS_LOGGING       // ログ取得モード
#include <MMSystem.h>
#include <vector>
#include <set>
#include "Core/Debug.h"
//dxmus.core\stdafx.h より
#include <dmusici.h>
#include <dmusicc.h>
//ここまで

------------------------------------------------------

GuruGuruSMF4\stdafx.cpp

#include <initguid.h> //by Kobarin
#include "stdafx.h"

------------------------------------------------------

GuruGuruSMF4.cpp

前半の "winmm.lib" 以外の #pragma comment をコメントアウト

複数インスタンス用API追加

    //by Kobarin 複数インスタンス対応用
    // インスタンス作成
    // 第１引数(void* p) の NULL チェックはしない(NULL を渡すと落ちる)
    void* WINAPI GGS4CreateInstance_k(void)
    {
        return Ggs4Controller::CreateInstance();
    }
    // インスタンス破棄
    void WINAPI GGS4DeleteInstance_k(void* p)
    {
        Ggs4Controller* ggs = (Ggs4Controller*)p;
        ggs->DeleteInstance();
    }
    // デバイスを開く
    GGSERROR WINAPI GGS4OpenDevice_k(void* p, int deviceId, HWND handle)
    {
        Ggs4Controller* ggs = (Ggs4Controller*)p;
        return ggs->OpenDevice(deviceId, handle);
    }
    ...
    // SMFの情報を取得
    GGSERROR WINAPI GGS4GetSmfInformation_k(void* p, SmfInformation* info, int size, int musicId)
    {
        Ggs4Controller* ggs = (Ggs4Controller*)p;
        return ggs->GetSmfInformation(info, size, musicId);
    }
    //複数インスタンス対応用 ここまで

    GGS4CreateInstance_k と GGS4DeleteInstance_k 以外の新設 API は
    元の API の第一引数に void* を追加(GGS4CreateInstance_k で返した値を渡す)

------------------------------------------------------

PowerControlWindow.h(無効化したので使われないが)

    class PowerControlWindow
    {
    private:
        HWND _hWnd;
        HANDLE _hCreated;       // ウィンドウの作成終了待機用
        HANDLE _hThread;    // スレッド終了待機用
        bool _closing;
        ICallBackPowerControlWindow* _callBackObject;   // コールバック用のオブジェクト
        wchar_t _class_name[128];//by Kobarin 複数インスタンス用
        ...

------------------------------------------------------

PowerControlWindow.cpp(無効化したので使われないが)

複数インスタンス対応


    // コンストラクタ
    PowerControlWindow::PowerControlWindow(GgsController *ggs_ctrl)
    {
        _class_name[0] = 0;//by Kobarin
        _callBackObject = obj;
        _closing = false;
        ...
    }

    void PowerControlWindow::Create()
    {
        //by Kobarin
        //複数インスタンス対応の為、クラス名をインスタンスごとに異なるものにする
        _snwprintf_s(_class_name, _TRUNCATE, L"%s-%08x-%08x", g_AppName, GetCurrentThreadId(), GetTickCount());
        //
        WNDCLASSEX wcex;
        ...
        //wcex.lpszClassName  = g_AppName;
        wcex.lpszClassName  = _class_name;//by Kobarin

        wcex.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);

        RegisterClassEx(&wcex);

        _hWnd = CreateWindow(
            //g_AppName, g_AppName, WS_OVERLAPPEDWINDOW,
            _class_name, g_AppName, WS_OVERLAPPEDWINDOW,
            //CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
            0, 0, 300, 300,
            NULL, NULL, GetModuleHandle(NULL), NULL
        );
    }
    void PowerControlWindow::MessageLoop()
    {
        MSG msg = {0};

        while (GetMessage(&msg, NULL, 0, 0))
        {
            if(msg.message == WM_QUIT)
            {
                break;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        //UnregisterClass(g_AppName, GetModuleHandle(NULL));
        UnregisterClass(_class_name, GetModuleHandle(NULL));//by Kobarin
    }

------------------------------------------------------

controller.h/cpp

複数インスタンス対応の為、singlton 廃止
PerformancePlayer 廃止

------------------------------------------------------

Port.h/cpp

DirectMusic 関連のオブジェクトの全ての操作をそれを作成したスレッド内のみで
行うように大幅修正

------------------------------------------------------

Compiler.cpp

    //
    // ExclusiveEventExecutor
    //

    // エクスクルーシブ解析用
    static const byte codeGMSystemOn[] = {0x7e,0x7f,0x09};
    static const byte codeMasterVolume[] = {0x7f,0x7f,0x04,0x01};
    static const byte codeXGSystemOn[] = {0x43,0xff,0x4c,0x00,0x00,0x7e,0x00};
    static const byte codeGS[] = {0x41,0xff,0x42,0x12};
    static const byte codeGSReset[] = {0x40,0x00,0x7f};
    static const byte codeGSModeSet[] = {0x00,0x00,0x7f};

    //static const byte codeMasterVolumeGS[] = {0x40,0x00,0x04};//by Kobarin
    //static const byte codeMasterVolumeXG[] = {0x43,0xff,0x4c,0x00,0x00,0x04};//by Kobarin

    // エクスクルーシブ用
    void ExclusiveEventExecutor::Execute(SmfEvent* smfEvent, ICompilerCore* core)
    {
        int len = smfEvent->ExclusiveLength;
        if (len < 5) return;    // 長すぎたり短いのはまとめてスルー

        byte* data = smfEvent->ExclusiveData;
        if (data[len-1] != 0xf7){
            return;     // あやしいバイナリは無視
        }
        // 解析
        if (len == 5){
            if (CompareExclusive(data, codeGMSystemOn, 0, 3)){
                TRACE(L"GM System On\n");
                return; // GMシステムオン
            }
        }else if (len == 7){
            if (CompareExclusive(data, codeMasterVolume, 0, 4)){
                TRACE(L"MasterVolume\n");
                //return;   // マスターボリューム Portモードで動作が不安定らしい
                //by Kobarin 音が鳴ったり鳴らなくなったりで確かに不安定
                //後でチャンネルボリュームに変換する為、イベントは登録しておく(return をコメントアウト)
                //Device.h/cpp を参照
            }
        }else if (len == 8){
            if (CompareExclusive(data, codeXGSystemOn, 0, 7)){
                TRACE(L"XG System On\n");
                return; // XGシステムオン
            }
            /*
            //by Kobarin
            //DirectMusic が GM MasterVolume を正常に処理してくれれば Device.h/cpp の修正は不要なのに
            //ここをコメントアウトせずに処理するなら Device.cpp の SendLongMessage で XG MasterVolume の処理が不要になる
            else if (CompareExclusive(data, codeMasterVolumeXG, 0, 6)) {
            //XG MasterVolume を GM MasterVolume に置換
                TRACE(L"XG MasterVolume\n");
                BYTE master_vol_gm[] = { 0x7F,0x7F,0x04,0x01,0x00,0x00,0xF7 };
                master_vol_gm[5] = data[7];
                SmfEvent ev;
                memcpy(&ev, smfEvent, sizeof(ev));
                ev.ExclusiveLength = 7;
                ev.ExclusiveData = master_vol_gm;
                core->WriteExclusiveEvent(&ev);
                return;
            }//ここまで
            */
        }else if (len == 10){
            if (CompareExclusive(data, codeGS, 0, 4)){  // GS系
                if (CompareExclusive(data, codeGSReset, 4, 3)){
                    TRACE(L"GS Reset\n");
                    return; // GSリセット
                }else if (CompareExclusive(data, codeGSModeSet, 4, 3)){
                    TRACE(L"GS Mode Set\n");
                    return; // GSモードセットのどちらか
                }else if (data[4] == 0x40 && (data[5]&0xf0) == 0x10 && data[6] == 0x15){
                    // リズムパート指定
                    int ch = Constant::GSChannel[data[5] & 0x0f];
                    core->WriteUseRhythmPartEvent(smfEvent, ch, data[7] != 0);
                    TRACE2(L"Use Rhythm Part[%d] = %d\n", ch, data[7]!=0);
                    return;
                }
                /*
                //by Kobarin
                //DirectMusic が GM MasterVolume を正常に処理してくれれば Device.h/cpp の修正は不要なのに
                //ここをコメントアウトせずに処理するなら Device.cpp の SendLongMessage で GS MasterVolume の処理が不要になる
                else if (CompareExclusive(data, codeMasterVolumeGS, 4, 3)) {
                //GS MasterVolume を GM MasterVolume に置換
                    TRACE(L"GS MasterVolume\n");
                    BYTE master_vol_gm[] = {0x7F,0x7F,0x04,0x01,0x00,0x00,0xF7};
                    master_vol_gm[5] = data[7];
                    SmfEvent ev;
                    memcpy(&ev, smfEvent, sizeof(ev));
                    ev.ExclusiveLength = 7;
                    ev.ExclusiveData = master_vol_gm;
                    core->WriteExclusiveEvent(&ev);
                    return;
                }//ここまで
                */
            }
        }

        core->WriteExclusiveEvent(smfEvent);
    }

------------------------------------------------------

Device.h

    class Device : public DeviceBase, public IDevice
    {
    private:
        bool isOpen;
        IDirectMusicTimer* times;
        GuruGuruSmf::Dxmus::Controller* ctrl;//by Kobarin
        BYTE ch_vols[16];//by Kobarin
        BYTE master_vol;//by Kobarin

    public:
        Device(IDirectMusicTimer* timer, GuruGuruSmf::Dxmus::Controller* ctrl); // コンストラクタ

------------------------------------------------------

Device.cpp

    // コンストラクタ
    Device::Device(IDirectMusicTimer* timer, GuruGuruSmf::Dxmus::Controller *pctrl)
    {
        times = timer;
        isOpen = false;
        //by Kobarin
        ctrl = pctrl;
        for (int i = 0; i < 16; i++) {
            ch_vols[i] = 0xff;
        }
        master_vol = 127;
        //ここまで
    }
    // デバイスを開く
    GGSERROR Device::Open(int deviceId, HWND handle)
    {
        Close();

        GGSERROR err = ctrl/*GuruGuruSmf::Dxmus::Controller::GetInstance()*/->PortOpen(handle);
        if (err != 0) return err;
        isOpen = true;

        //by Kobarin
        for (int i = 0; i < 16; i++) {
            ch_vols[i] = 0xff;
        }
        master_vol = 127;
        //ここまで

        return GgsError::NoError;
    }
    // デバイスを閉じる
    void Device::Close()
    {
        if (isOpen){
            ctrl/*GuruGuruSmf::Dxmus::Controller::GetInstance()*/->PortClose();
            isOpen = false;
        }
    }
    // 長いMIDIメッセージを送信
    GGSERROR Device::SendLongMessage(const byte* data, int length)
    {
    //by Kobarin
    //マスターボリューム系 SysEx をチャンネルボリュームに置き換えて送信
        static const byte MasterVolGM[] = {0xf0,0x7f,0x7f,0x04,0x01};
        static const byte MasterVolGS[] = {0xf0,0x41,0xff,0x42,0x12,0x40,0x00,0x04};//3バイト目(dev id)は見ない
        static const byte MasterVolXG[] = {0xf0,0x43,0xff,0x4c,0x00,0x00,0x04};     //
        //Compiler.cpp::ExclusiveEventExecutor::Execute() の実装により、GS Reset 以外は来ない
        //static const byte ResetGM[] = {0xf0,0x7e,0x7f,0x09,0x01,0xf7};//GM System On
        static const byte ResetGS[] = {0xf0,0x41,0x10,0x42,0x12,0x40,0x00,0x7f,0x00,0x41,0xf7};//GS Reset
        //static const byte ResetGS2[] ={0xf0,0x41,0x10,0x42,0x12,0x00,0x00,0x7f,0x00,0x01,0xf7};//System Mode Set
        //static const byte ResetXG[] = {0xf0,0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,0xf7};//XG System On

        bool is_mastervol = false;
        bool is_reset = false;
        if(length == 8 && memcmp(data, MasterVolGM, 5) == 0){//GM MasterVolume
            is_mastervol = true;
            master_vol = data[6];
        }
        else if(length == 11 && memcmp(data, MasterVolGS, 2) == 0 && memcmp(data+3, MasterVolGS+3, 5) == 0){
            is_mastervol = true;
            master_vol = data[8];
        }
        else if (length == 9 && memcmp(data, MasterVolXG, 2) == 0 && memcmp(data+3, MasterVolXG+3, 4) == 0) {
            is_mastervol = true;
            master_vol = data[7];
        }
        //else if(length == 6 && memcmp(data, ResetGM, 6) == 0){//GM System On
        //  is_reset = true;
        //}
        else if(length == 11){
            if (memcmp(data, ResetGS, 11) == 0){//GS Reset
                is_reset = true;
            }
            //else if(memcmp(data, ResetGS2, 11) == 0) {//System Mode Set
            //  is_reset = true;
            //}
        }
        //else if(length == 9 && memcmp(data, ResetXG, 9) == 0){//XG System On
        //  is_reset = true;
        //}
        if(is_mastervol){//マスターボリューム
        //SysEx は送信せず、全チャンネルにチャンネルボリュームを送信
            for (int i = 0; i < 16; i++) {
                if(ch_vols[i] == 0xff){//未初期化
                    ch_vols[i] = 100;//チャンネルボリュームの初期値は100(Volume.cpp でもそうしている)
                }
                SendShortMessage(0xb0 + i, 7, ch_vols[i]);//ch_vols[i] はマスターボリューム適用前の現在の値
            }
            return GgsError::NoError;
        }
        else if(is_reset){//リセット
        //ボリューム値を初期値に戻しておく
            for(int i = 0; i < 16; i++){
                ch_vols[i] = 0xff;
            }
            master_vol = 127;
        }
//ここまで
    ...

    // 短いMIDIメッセージを送信
    GGSERROR Device::SendShortMessage(int status, int data1, int data2)
    {
    //by Kobarin
        if(data1 == 7 && (status&0xf0)==0xb0){//チャンネルボリューム
        //マスターボリューム系 SysEx の音量指定も反映させる
            int ch = status & 0x0f;
            ch_vols[ch] = data2;//マスターボリューム適用前の値を保持しておく(マスターボリューム系SysExが呼ばれたときに全チャンネルに反映させる為)
            //int vol = data2 * master_vol / 127;
            int vol = data2 + (master_vol-127);
            if(vol < 0)vol = 0;
            data2 = vol;
        }
    //ここまで
    ...


    その他
    GuruGuruSmf::Dxmus::Controller::GetInstance() => ctrl に変更

------------------------------------------------------

DmPerfomancePlayer.h/cpp

複数インスタンス対応（使われないので省略）

------------------------------------------------------

DmPortPlayer.h/cpp

複数インスタンス対応
クラスのメンバに GuruGuruSmf::Dxmus::Controller* ctrl; を追加
    // 演奏！
    GGSERROR Player::Play(ISmfController* smfCtrl, int option, int seek, int fadeInTime, int newKeyShift)
    {
        ...
        ctrl/*GuruGuruSmf::Dxmus::Controller::GetInstance()*/->PortSetVoiceList((DWORD*)voiceList->GetItems(), voiceList->GetCount());
        ...

DmPortPlayer.cpp

    // デバイスを閉じる
    void Player::CloseDevice()
    {
        if (!device->GetIsOpen()) return;

        TRACE(L"DmPort.Player: CloseDevice : Stop\n");
        Stop(0);
        TRACE(L"DmPort.Player: CloseDevice : Sleep\n");
        //Sleep(150);   // FinalizeMusicでリセットを送信しているため
        //↑ del by Kobarin
        //直ぐにデバイスを閉じてしまうので仮に取りこぼしがあっても（あるのか？）音が止まるだけで待ちは不要と判断
        TRACE(L"DmPort.Player: CloseDevice : device->Close()\n");
        device->Close();
    }

------------------------------------------------------

Times.h/cpp
複数インスタンス対応
クラスのメンバに GuruGuruSmf::Dxmus::Controller* ctrl; を追加

    // DirectMusicから基準時間を読んでくる
    GGSERROR PlayerTimes::ReadMasterClock()
    {
        return ctrl/*GuruGuruSmf::Dxmus::Controller::GetInstance()*/->PortGetReferenceTime(&nowDmTime);
    }

    // メッセージ処理時間を過ぎていたらtrue
    //bool PassedPrepareTime(){ return markerDmTime > nowDmTime + Constant::PrepareTime; }
    bool PassedPrepareTime() { //by Kobarin
        __int64 time = markerDmTime - nowDmTime - Constant::PrepareTime;
        if(time < -10000i64*1000*5){//5秒以上遅れてたら無理して遅れを取り戻そうとしない
        //デバッグ中、スリープモードからの復帰時、物凄くPCに負荷がかかっているときとか
        //ループ再生中、長時間のスリープモードからの復帰時に起きそうな不具合が修正される？(テスト出来ないが)
            markerDmTime = fixedQueueDmTime = nowDmTime + Constant::PrepareTime;
        }
        return time > 0;
    }


------------------------------------------------------

Ggs4Controller.h/cpp

複数インスタンス対応
クラスのメンバに GuruGuruSmf::Dxmus::Controller* ctrl; を追加

その他色々

    // デバイスを開く
    GGSERROR GgsController::OpenDevice(int deviceId, HWND handle)
    {
        CloseDevice();

		if(deviceId != Device::DirectMusic){//by Kobarin DirectMusic のときはサスペンド処理をしない
			winHidden = new PowerControlWindow(this);
		}

------------------------------------------------------

GuruGuruSMF4.def

; 複数インスタンス用 API by Kobarin
  GGS4CreateInstance_k ;公式版との互換性をテストするときはコメントアウト
  GGS4DeleteInstance_k
  GGS4OpenDevice_k
  GGS4CloseDevice_k
  GGS4AddDlsW_k
  GGS4AddDlsA_k
  GGS4Play_k
  GGS4Stop_k
  GGS4Pause_k
  GGS4Restart_k
  GGS4SetMasterTempo_k
  GGS4SetMasterVolume_k
  GGS4SetMasterPitch_k
  GGS4GetPlayerStatus_k
  GGS4AddListFromFileW_k
  GGS4AddListFromFileA_k
  GGS4AddListFromMemory_k
  GGS4DeleteListItem_k
  GGS4ClearList_k
  GGS4EnumList_k
  GGS4GetSmfInformation_k

------------------------------------------------------

GuruGuruSMF4_Cpp_Dynamic.h/cpp

色々
