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

マルチプルインスタンス対応の為の修正箇所が多すぎて書ききれない

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

dmusici.h を dmusici_h\ 以下に置く(VS2022 にない為)
kbdmusic.kpi で使用しているのと同じもの。VS のどのバージョンのものだったかは不明
------------------------------------------------------

core/dxmus.core/GuruGuruSMF4 各プロジェクトの追加のインクルードディレクトリに
dmusici_h を追加

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

GuruGuruSMF4.cpp

#pragma comment(lib, "dxguid.lib")
↑をコメントアウト(VS2022 にない為)

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

core\stdafx.cpp / dxmus_core\stdafx.cpp の冒頭に
#include <initguid.h>

を追加（リンカエラー対策）
(#pragma comment(lib, "dxguid.lib") の代わり)

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

core\stdafx.h / dxmus_core\stdafx.h / GuruGuruSMF4\stdafx.h

#ifndef WINVER 等の部分をコメントアウト

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

smf.cpp

// デストラクタ
GgsCode::~GgsCode()
{
    Clear();
    delete[] binary;//メモリリーク回避 by Kobarin
}

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

SmfInterpreter.cpp

void SmfInterpreter::Initialize(Smf* newSmf)
{
    delete[] trackData;// メモリリーク回避 by Kobarin
    smf = newSmf;
    trackCount = smf->GetTrackCount();
    trackData = new TrackData[trackCount];
    for (int i=0; i<trackCount; i++){
        TrackData& trk = trackData[i];
        trk.SmfTrack = smf->GetTrack(i);
    }

    Rewind();
}

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

PowerControlWindow.h

    class PowerControlWindow
    {
    private:
        HWND _hWnd;
        HANDLE _hEvent; //by Kobarin (ウィンドウの作成が終わるまで待つ用)
        HANDLE _hThread;//by Kobarin (スレッドが終了するまで待てるように)
        void*  _ggs_ctrl;//by Kobarin(マルチプルインスタンス対応用)
        wchar_t _class_name[128];//by Kobarin
    public:
        PowerControlWindow(void *ggs_contrller);    // コンストラクタ
        ...

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

PowerControlWindow.cpp
スリープ時にデバイスを閉じる処理
おそらくオリジナルの v4.0.7 が FreeLibrary 時にフリーズする原因
VS2022 でビルドすると修正しなくてもフリーズしないが多分偶然
DllMain の DLL_PROCESS_DETACH でこのクラスで生成されるスレッドを終了するのが
直接の原因(多分)、他にもウィンドウが作成される前にデストラクタで SendMessage
する可能性がある、マルチプルインスタンス対応の為の修正等

    /* del by Kobarin
    void thread_entry(void* p_userdata)
    {
        PowerControlWindow* p_win = (PowerControlWindow*)p_userdata;
        p_win->createWindow();
        p_win->MessageLoop();
    }
    */
    //by Kobarin
    unsigned __stdcall thread_entry(void* p_userdata)
    {
        PowerControlWindow* p_win = (PowerControlWindow*)p_userdata;
        p_win->createWindow();
        p_win->MessageLoop();
        return 0;
    }
    //ここまで

    // コンストラクタ
    PowerControlWindow::PowerControlWindow(GgsController *ggs_ctrl)
    {
        //_beginthread(thread_entry, 0, this);//del by Kobarin
        //by Kobarin
        _hWnd = NULL;
        _ggs_ctrl = ggs_ctrl;
        _class_name[0] = 0;
        _hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        _hThread = (HANDLE)_beginthreadex(NULL, 0, thread_entry, this, 0, NULL);
        //ここまで
    }

    // デストラクタ
    PowerControlWindow::~PowerControlWindow()
    {
        WaitForSingleObject(_hEvent, INFINITE);//by Kobarin(ウィンドウが作成し終わるまで待つ)
        SendMessage(_hWnd, WM_CLOSE, NULL, NULL);
        //by Kobarin
        WaitForSingleObject(_hThread, INFINITE);
        CloseHandle(_hEvent);
        CloseHandle(_hThread);
        //ここまで
    }

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

        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,
            NULL, NULL, GetModuleHandle(NULL), NULL
        );
        SetWindowLongPtr(_hWnd, GWLP_USERDATA, (LONG_PTR)_ggs_ctrl);//by Kobarin
        //ShowWindow(_hWnd, SW_SHOWDEFAULT);
        UpdateWindow(_hWnd);
        SetEvent(_hEvent);//by Kobarin(ウィンドウの作成完了)
    }
    /*
    void PowerControlWindow::SetCallBackSuspend(void (*func)())
    {//廃止 by Kobarin 代わりに GgsController::Suspend() を呼ぶ

        callBackSuspend = func;
    }
    */
    void PowerControlWindow::MessageLoop()
    {
        MSG msg = {0};

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

    LRESULT WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch(uMsg)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        case WM_POWERBROADCAST:
            switch(wParam)
            {
            case PBT_APMSUSPEND:{
                //if (callBackSuspend)  callBackSuspend();
                GgsController* ctrl = (GgsController*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
                if(ctrl)ctrl->Suspend();
                break;
            }
            case PBT_APMRESUMESUSPEND:
                break;
            }
            break;

        }
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

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

controller.h/cpp

マルチプルインスタンス対応の為、singlton 廃止

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

port.h/cpp

デバイス初期化バグ修正(スレッドの同期のバグ)
FreeLibrary 前に GGS4Uninitialize_k を呼ばずに DllMain で後始末した結果にも関係すると思われる

    // DirectMusicPortを扱うクラス
    class Port
    {
    private:
        IDirectMusic8* music;
        ...
        // スレッド関連
        HANDLE handle_thread;
        volatile bool isWorkerOn;
        volatile bool isBusy;
        CRITICAL_SECTION lockObject;
        std::queue<PortCommand> commands;
        GGSERROR resultOpen;    // 返り値(Open専用)
        HANDLE event_cmd; //by Kobarin コマンドが要求されたらシグナル
        HANDLE event_done;//by Kobarin コマンドが完了したらシグナル
        ...

    void Port::OnWorker()
    {
        while(isWorkerOn)
        {
            //isBusy = true;
            WaitForSingleObject(event_cmd, INFINITE);//by Kobarin
            while(!commands.empty())
            {
                PortCommand cmd = commands.front();
                switch(cmd.type)
                {
                case CommandType_Open:
                    resultOpen = Open2(cmd.handle);
                    break;
                case CommandType_Close:
                    Close2();
                    break;
                default:
                    break;
                }

                EnterCriticalSection(&lockObject);
                commands.pop();
                LeaveCriticalSection(&lockObject);
            }
            SetEvent(event_done);//by Kobarin
            //isBusy = false;
            //Sleep(1);
        }
        //DeleteCriticalSection(&lockObject);//デストラクタに移動 by Kobarin
        //#ifdef _DEBUG
        //OutputDebugString(L"OnWorker() terminated\n");
        //#endif
        //DllMain 内で後始末すると↑が表示されないのにスレッドが終了している
        //FreeLibrary の前に GGS4Uninitialize_k を呼べば表示される
        //マルチプルインスタンス版の API だけを使った場合も表示される
    }

    // コマンドをキューに入れる
    void Port::PostCommand(PortCommand cmd)
    {
        EnterCriticalSection(&lockObject);
        commands.push(cmd);
        LeaveCriticalSection(&lockObject);
        SetEvent(event_cmd);//by Kobarin
    }

    // コマンドの実行が終わるまでウェイト
    // 急ぐ処理は0 時間がかかるなら1が妥当
    //by Kobarin sleep は無視
    void Port::Wait(int sleep)
    {
        //while(isBusy) Sleep(sleep);
        //コマンドを実行する前にスレッドが終了しても復帰するようにする
        //DllMain 内で後始末されるとスレッドが強制終了されるらしい？
        //DllMain 内で後始末されると ret == WAIT_OBJECT_0 になるが
        //OnWorker() 内の isWorkerOn のループの外が通る形跡がない
        HANDLE hHandles[] = {handle_thread, event_done};
        //DWORD ret =
        WaitForMultipleObjects(2, hHandles, FALSE, INFINITE);
    }

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

Device.h

    class Device : public DeviceBase, public IDevice
    {
    private:
        bool isOpen;
        IDirectMusicTimer* times;
        GuruGuruSmf::Dxmus::Controller* ctrl;//by Kobarin
    public:
        Device(IDirectMusicTimer* timer, GuruGuruSmf::Dxmus::Controller* ctrl); // コンストラクタ

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

Device.cpp

    // コンストラクタ
    Device::Device(IDirectMusicTimer* timer, GuruGuruSmf::Dxmus::Controller *pctrl)
    {
        times = timer;
        isOpen = false;
        ctrl = pctrl; //by Kobarin
    }

    // デバイスを閉じる
    void Device::Close()
    {
        if (isOpen){
            ctrl/*GuruGuruSmf::Dxmus::Controller::GetInstance()*/->PortClose();
            isOpen = false;
        }
    }

    その他
    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());
        ...


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

GuruGuruSMF4.cpp

    //by Kobarin
    //後始末用API追加(FreeLibrary する前に呼ばないといけない)
    void WINAPI GGS4Uninitialize_k()
    {
        Ggs4Controller::FreeInstance();
    }
    //ここまで

    //by Kobarin マルチプルインスタンス対応用
    // インスタンス作成
    // 第１引数(void* p) の NULL チェックはしない(NULL を渡すと落ちる)
    void* WINAPI GGS4CreateInstance_k(void)
    {
        return Ggs4Controller::CreateInstance();
    }
    // インスタンス破棄
    void WINAPI GGS4FreeInstance_k(void* p)
    {
        Ggs4Controller* ggs = (Ggs4Controller*)p;
        ggs->Free();
    }
    // デバイスを開く
    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 と GGSFreeInstance_k 以外の新設 API は
    元の API の第一引数に void* を追加(GGS4CreateInstance_k で返した値を渡す)

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

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

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

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

Ggs4Controller.h/cpp

マルチプルインスタンス対応
クラスのメンバに GuruGuruSmf::Dxmus::Controller* ctrl; を追加
サスペンド関連

その他色々

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

GuruGuruSMF4.def

; 後始末用 API by Kobarin
  GGS4Uninitialize_k; DllMain での後始末をテストするときは GGS4CreateInstance_k とともにコメントアウト
; マルチプルインスタンス用 API by Kobarin
; マルチプルインスタンス用 API のみを使う場合は GGS4Uninitialize_k は呼ばなくても問題ない(呼んでも問題ない)
  GGS4CreateInstance_k ;公式版との互換性をテストするときはコメントアウト
  GGS4FreeInstance_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

色々
