#include <windows.h>
#include <stdlib.h>
#include <wchar.h>
#include <process.h>
#include <vector>
#include "kbvsti_decoder.h"
#include "kbvsti_config.h"
#define _CRT_SECURE_NO_WARNINGS
#include "vsti_loader.h"
#include "vsti_host.h"

struct MSG_BOX_PARAM
{
    const wchar_t *cszMessage;
    const wchar_t *cszTitle;
    UINT  uType;
};
static unsigned __stdcall MsgBoxThreadProc(void* pParam)
{
    MSG_BOX_PARAM *param = (MSG_BOX_PARAM*)pParam;
    MessageBox(NULL, param->cszMessage, param->cszTitle, param->uType);
    return 0;
}
static void __fastcall ShowMsgBox(const wchar_t *cszMessage, const wchar_t *cszTitle, UINT uType)
{//メッセージループを回さないでダイアログを表示
    MSG_BOX_PARAM param = { cszMessage, cszTitle, uType};
    unsigned thread_id;
    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, MsgBoxThreadProc, &param, 0, &thread_id);
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
}
//=============================================================================
//! @file
//! @brief  プラグイン実装
//! @author Yoshi.
//=============================================================================
//-----------------------------------------------------------------------------
// 定義
//-----------------------------------------------------------------------------
namespace {

// メッセージリスト
typedef std::vector<SmfLoader::MidiMessage> MsgList;

// プロトタイプ宣言
int RenderMidi(VstiHost *pVsti, const MsgList& msg, void* buffer, int samples, bool bReplace, UINT64 cur_pos);
//-----------------------------------------------------------------------------
// MIDIメッセージを送信し、PCMデータを生成する
//-----------------------------------------------------------------------------
int RenderMidi(VstiHost *pVsti, const MsgList& msg, void* buffer, int samples, bool bReplace, UINT64 cur_pos)
{
    if (!pVsti->Render(msg, samples, cur_pos))
    {
        return 0;
    }
    if(!buffer)return samples;
    if (pVsti->IsSupport64()) {
        const double* ch0 = pVsti->GetChannel0_64();
        const double* ch1 = pVsti->GetChannel1_64();
        double* pcm = static_cast<double*>(buffer);
        if (bReplace) {
            for (int i = 0, j = 0; i < samples; ++i)
            {
                pcm[j++] = ch0[i];
                pcm[j++] = ch1[i];
            }
        }
        else {
            for (int i = 0, j = 0; i < samples; ++i)
            {
                pcm[j++] += ch0[i];
                pcm[j++] += ch1[i];
            }
        }
    }
    else {
        const float* ch0 = pVsti->GetChannel0_32();
        const float* ch1 = pVsti->GetChannel1_32();
        float* pcm = static_cast<float*>(buffer);
        if (bReplace) {
            for (int i = 0, j = 0; i < samples; ++i)
            {
                pcm[j++] = ch0[i];
                pcm[j++] = ch1[i];
            }
        }
        else {
            for (int i = 0, j = 0; i < samples; ++i)
            {
                pcm[j++] += ch0[i];
                pcm[j++] += ch1[i];
            }
        }
    }
    return samples;
}

} //namespace

CRITICAL_SECTION KbVstiDecoder::s_cs;
LONG KbVstiDecoder::s_lInitialized = 0;
VstiLoader* KbVstiDecoder::s_pLoader = NULL;
//////////////////////////////////////////////////////////////////////////
KbVstiDecoder::KbVstiDecoder(IKpiConfig *pConfig)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_pConfig = pConfig;
    pConfig->AddRef();
    m_midx = 0; // 今のメッセージインデックス
    m_mnum = 0; // メッセージ総数
    m_time_smp = 0; // 今の再生時間
    m_tend = 0; // 終了再生時間
    m_block_size = 0; // サンプル数
    m_port_num = 0;
    m_loop_idx = -1;
    m_silent_smp = 0;
    m_dVolume = 1.0;
    for (int i = 0; i < _countof(m_pVsti); i++) {
        m_pVsti[i] = NULL;
    }
    m_bShowEditor = false;
    m_bEditorCreate = false;
    m_reset_type = SmfLoader::RESET_TYPE_GM1;
    m_bCache = true;
}
//////////////////////////////////////////////////////////////////////////
KbVstiDecoder::~KbVstiDecoder(void)
{
    CloseEditor();
    for (int i = 0; i < MAX_PORT_NUM; i++) {
        if(!m_pVsti[i])break;
        m_pVsti[i]->Stop();
        s_pLoader->FreeVsti(m_pVsti[i], m_bCache);
    }
    m_pConfig->Release();
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbVstiDecoder::Open(const KPI_MEDIAINFO *cpRequest,
                                    IKpiFile *pFile,
                                    IKpiFolder *pFolder)
{
#ifdef _WIN64
#define SZ_KBVSTI L"kbvsti_x64.kpi"
#else
#define SZ_KBVSTI L"kbvsti_x86.kpi"
#endif
    m_pVsti[0] = GetVsti(m_pConfig);

    kbvsti_Settings settings;
    kbvsti_GetSettings(m_pConfig, &settings);
    if (!m_pVsti[0]) {
        wchar_t szMessage[1024];
        _snwprintf_s(szMessage, _TRUNCATE,
            L"VSTi プラグインのロードに失敗しました。\r\n"
            L"\r\n"
            L"ツール->プラグイン設定->Decoder->" SZ_KBVSTI L"->General->VSTi のパス\r\n"
            L"\r\n"
            L"で正しいプラグインのパスを指定して下さい。\r\n"
            L"動作中の " SZ_KBVSTI " は "
#ifdef _WIN64
            L"x64 版 です。x86 版"
#else
            L"x86 版 です。x64 版"
#endif
            L"の VSTi プラグインは使用出来ません。\r\n"
            L"\r\n"
            L"VSTiPath=%s"
            , settings.szDllPath);
        //エラーメッセージを表示
        //本体の動作に異常をきたすため、メッセージループは回さない
        ShowMsgBox(szMessage, SZ_KBVSTI, MB_OK|MB_TOPMOST|MB_SYSTEMMODAL);
        return 0;
    }
    //値の範囲チェックは kbvsti_GetSettings 内で行われていることを前提とする
    m_dVolume = settings.dVolume;
    m_bCache = settings.bCache;
    m_reset_type = (SmfLoader::ResetType)settings.nReset;
    SmfLoader::LoadOption option;
    option.detect_reset_type = true;
    option.ignore_bank_select = false;// true;
    const BYTE *buf_data;
    size_t buf_size;
    if(!pFile->GetBuffer(&buf_data, &buf_size)){
        return 0;
    }
    int sample_rate = settings.nSampleRate;
    if(sample_rate < 0){
        sample_rate *= -1;//要求を無視
    }
    else if(cpRequest && cpRequest->dwSampleRate != 0){//要求に応じる
        sample_rate = cpRequest->dwSampleRate;
    }
    if (!m_loader.Load(buf_data, buf_size, option, sample_rate)) {
        return 0;
    }
    int port_num = m_loader.GetPortNum();
    //port_num = 4;
    if (port_num > MAX_PORT_NUM) {
        port_num = MAX_PORT_NUM;
    }
    m_port_num = port_num;
    for (int i = 1; i < port_num; i++) {
        m_pVsti[i] = GetVsti(m_pConfig);
        if (!m_pVsti[i]) {
            m_port_num = i + 1;
            break;
        }
    }
    int block_size = 1024;//プラグインによって対応可能な値が異なるようなので設定出来るようにした方が良いかもしれな14い
    for (int i = 0; i < m_port_num; i++) {
        m_pVsti[i]->Setup(sample_rate, block_size);
        m_pVsti[i]->Start();
    }
    block_size = m_pVsti[0]->GetBlockSize();//MAmiVSTi.dll のときは 1024 にならない
    m_MediaInfo.dwSampleRate   = sample_rate;
    m_MediaInfo.nBitsPerSample = m_pVsti[0]->IsSupport64() ? -64 : -32;
    m_MediaInfo.dwChannels      = 2;
    m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.qwLength        = kpi_SampleTo100ns(m_loader.GetDuration(), m_MediaInfo.dwSampleRate);
    m_MediaInfo.dwUnitSample = block_size;
    m_MediaInfo.dwCount = 1;
    m_block_size = block_size;
    m_midx = 0;
    m_mnum = m_loader.GetMidiMessageNum();
    m_time_smp = 0;
    m_tend = m_loader.GetDuration();
    for (int i = 0; i < m_mnum; i++) {
        const SmfLoader::MidiMessage& msg = m_loader.GetMidiMessage(i);
        if ((msg.data & 0xF0) == 0xb0) {
            if (((msg.data & 0xFF00)>>8) == 111) {//loop
                m_loop_idx = i;
                m_MediaInfo.qwLoop = -1;
                break;
            }
        }
    }
    m_bShowEditor = m_pVsti[0]->HasEditor() && settings.bShowEditor;//設定画面がある場合のみ
    return 1;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbVstiDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){
        return 0;
    }
    if(!ppMediaInfo){
        return dwNumber;
    }
    m_MediaInfo.dwNumber = dwNumber;
    *ppMediaInfo = &m_MediaInfo;
    return m_MediaInfo.dwNumber;
}
//////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbVstiDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    //return qwPosSample;
    int i;
    m_silent_smp = 0;
    qwPosSample = m_block_size * (qwPosSample / m_block_size);//m_block_size の倍数にする
    if(qwPosSample == 0 || (qwPosSample < m_time_smp)){//再生位置を戻す場合は先頭からやり直し
    // 戻すときはリセットして先頭からやり直し
        MsgList messages;
        SmfLoader::MidiMessage msg;
        msg.time_smp = 0;
        msg.port = 0;
        //以下、リセットを受信しないプラグイン用
        //RPN/CC 等の初期値の参考 URL
        //https://amei.or.jp/midistandardcommittee/
        //https://amei.or.jp/midistandardcommittee/Recommended_Practice/GM2_japanese.pdf
        for (i = 0; i < 16; i++) {
            msg.data = 0x00007bb0 + i;//オール・ノート・オフ(最後にオール・サウンド・オフも送信するが念の為)
            messages.push_back(msg);
            msg.data = 0x000000b0 + i;//バンク・セレクトMSB
            messages.push_back(msg);
            msg.data = 0x000020b0 + i;//バンク・セレクトLSB
            messages.push_back(msg);
            msg.data = 0x000000c0 + i;//プログラム・チェンジ(初期値=00H)
            messages.push_back(msg);
            msg.data = 0x000000d0 + i;//チャンネル・プレッシャー(初期値=00H)
            messages.push_back(msg);
            msg.data = 0x004000e0 + i;//ピッチ・ベンド(初期値=40H)
            messages.push_back(msg);
        }
        for (i = 0; i < m_port_num; i++) {
            m_pVsti[i]->Render(messages, m_block_size, 0);
        }
        messages.clear();
        {//RPN
            static const struct
            {
                DWORD msb;
                DWORD lsb;
                DWORD val_msb;//初期値(MSB)
                DWORD val_lsb;//初期値(LSB)
            }RPN[] = {
                {0x00, 0x00, 0x02, 0x00}, //ピッチ・ベンド・センシティビティ
                {0x00, 0x01, 0x40, 0x00}, //チャンネル・ファイン・チューニング
                {0x00, 0x02, 0x40, 0x00}, //チャンネル・コース・チューニング
                {0x00, 0x05, 0x00, 0x40}, //モジュレーション(ビブラート)・デプス・レンジ
                {0x7f, 0x7f, 0x00, 0x00}, //RPN ヌル
            };
            int rpn;
            for (rpn = 0; rpn < _countof(RPN); rpn++) {
                for (i = 0; i < 16; i++) {
                    msg.data = 0x00064b0 + (RPN[rpn].msb << 16) + i; //RPN MSB
                    messages.push_back(msg);
                    msg.data = 0x00065b0 + (RPN[rpn].lsb << 16) + i; //RPN LSB
                    messages.push_back(msg);
                    if(RPN[rpn].msb == 0x7f)continue;//RPN ヌルのときはデータエントリー省略
                    msg.data = 0x000006b0 + (RPN[rpn].val_msb << 16) + i;//データエントリー MSB
                    messages.push_back(msg);
                    msg.data = 0x000026b0 + (RPN[rpn].val_lsb << 16) + i;//データエントリー LSB
                    messages.push_back(msg);
                }
                //一度に大量の MIDI メッセージを送らないようにする(念の為)
                //(各rpn毎に送信する)
                for (i = 0; i < m_port_num; i++) {
                    m_pVsti[i]->Render(messages, m_block_size, 0);
                }
                messages.clear();
            }
        }//RPN
        {//コントロールチェンジ(RPN 以外)
            static const struct
            {
                DWORD cc;
                DWORD val;//初期値
            }CC[] = {
            //リセット・オール・コントローラーである程度初期値に戻る筈(プラグインが受信すれば)
                //{0,    0}, //バンクセレクトMSB
                //{32,   0}, //バンクセレクトLSB
                //{6,    0}, //データエントリーMSB 
                //{38,   0}, //データエントリーLSB
                //{98,   0}, //NRPN MSB
                //{99,   0}, //NRPN LSB
                //{100,  0}, //RPN MSB
                //{101,  0}, //RPN LSB
                {1,    0}, //モジュレーション・デプス
                {5,    0}, //ポルタメント・タイム
                {7,  100}, //チャンネル・ボリューム
                {10,  64}, //パン
                {11, 127}, //エクスプレッション
                {64,   0}, //ホールド1(ダンパー)
                {65,   0}, //ポルタメント
                {66,   0}, //ソステヌート
                {67,   0}, //ソフト
                /*
                //VSTSynthFont は以下(71〜78)の初期値をおそらく 0 にしている
                //VSTSynthFont の場合はリセット・オール・コントローラーで初期値に戻るが
                //他のプラグインがどうなのか分からないので送信しないでおく
                //(リセット・オール・コントローラーで初期値に戻ることに期待する)
                {71,  64}, //フィルター・レゾナンス
                {72,  64}, //リリース・タイム
                {73,  64}, //アタック・タイム
                {74,  64}, //ブライトネス
                {75,  64}, //ディケイ・タイム
                {76,  64}, //ビブラート・レイト
                {77,  64}, //ビブラート・デプス
                {78,  64}, //ビブラート・ディレイ
                */
                {91,  40}, //リバーブ・センド・レベル
                {93,   0}, //コーラス・センド・レベル
                //以下を最後に送信
                {121,  0}, //リセット・オール・コントローラー
                {120,  0}, //オール・サウンド・オフ(最初にオール・ノート・オフも送信済みだが念の為)
            };
            int cc;
            for (cc = 0; cc < _countof(CC); cc++) {
                for(i = 0; i < 16; i++){
                    msg.data = 0xb0 + (CC[cc].cc << 8) + ((CC[cc].val) << 16) + i;
                    messages.push_back(msg);
                }
                //一度に大量の MIDI メッセージを送らないようにする(念の為)
                //(各cc毎に送信する)
                for (i = 0; i < m_port_num; i++) {
                    m_pVsti[i]->Render(messages, m_block_size, 0);
                }
                messages.clear();
            }
        }//コントロールチェンジ(RPN 以外)
        //リセットを受信しないプラグイン用ここまで
        //リセット
        const BYTE* GMReset = (const BYTE*)SmfLoader::GetResetMessageData(SmfLoader::RESET_TYPE_GM1);
        const BYTE* XGReset = (const BYTE*)SmfLoader::GetResetMessageData(SmfLoader::RESET_TYPE_XG);
        const BYTE* Reset_config = (const BYTE*)SmfLoader::GetResetMessageData(m_reset_type);//「音源リセット」の設定値
        const BYTE* Reset_detected = (const BYTE*)m_loader.GetResetMessageData();//曲データに含むリセット
        // GM SYSTEM ON は「音源リセット」の設定に関わらず常に送信する
        msg.data = SmfLoader::SYS_EX;
        msg.sysex_dump.assign(GMReset, GMReset + SmfLoader::GetResetMessageSize(SmfLoader::RESET_TYPE_GM1));
        messages.push_back(msg);
        for (i = 0; i < m_port_num; i++) {
            m_pVsti[i]->Render(messages, m_block_size, 0);
        }
        messages.clear();
        // 曲データに XG SYSTEM ON を含む場合、「音源リセット」が「GS RESET」や「GM2 SYSTEM ON」でも送信しない
        // (GS RESET を受信して XG SYSTEM ON を受信しない場合に再生がおかしくなる)
        if (Reset_config != GMReset &&  //指定のリセットが GM SYSTEM ON 以外(既に送信済み)
            Reset_detected != XGReset){ //曲データに XG SYSTEM ON を含まない
            //msg.data = SmfLoader::SYS_EX;
            msg.sysex_dump.assign(Reset_config, Reset_config + SmfLoader::GetResetMessageSize(m_reset_type));
            messages.push_back(msg);
            for (i = 0; i < m_port_num; i++) {
                m_pVsti[i]->Render(messages, m_block_size, 0);
            }
            messages.clear();
        }
        //音が止まるまでダミーデコード
        const DWORD DUMMY_MS = 1000;//実際は 500ms くらいでもほぼ問題ない 
        const int count = MulDiv(DUMMY_MS, m_MediaInfo.dwSampleRate, m_block_size)/1000;
        for (i = 0; i < count; i++) {
            for (int j = 0; j < m_port_num; j++) {
                m_pVsti[j]->Render(messages, m_block_size, 0);
            }
        }
        m_midx = 0;
        m_time_smp = 0;
    }
    //最大5秒間は高速シーク(5秒間で出来るところまで)
    DWORD dwTime = GetTickCount();
    while (m_time_smp < qwPosSample) {
        PlayMidi(NULL);
        m_time_smp += m_block_size;
        if (GetTickCount() - dwTime > 5 * 1000) {
            break;
        }
    }
    return m_time_smp;
}
//////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbVstiDecoder::Render(BYTE* pBuffer, DWORD dwSizeSample)
{
    //return 0;
    if(m_bShowEditor && !m_bEditorCreate){
        ShowEditor();
    }
    const int ret = PlayMidi(pBuffer);

    m_time_smp += ret;
    if (m_dVolume == 1.0) {
        return ret;
    }
    if (m_pVsti[0]->IsSupport64()) {
        double *pcm = (double*)pBuffer;
        for (int i = 0; i < ret * 2; i++) {
            pcm[i] *= m_dVolume;
        }
    }
    else {
        float *pcm = (float*)pBuffer;
        for (int i = 0; i < ret * 2; i++) {
            pcm[i] *= m_dVolume;
        }
    }
    return ret;
}
//////////////////////////////////////////////////////////////////////////
DWORD  WINAPI KbVstiDecoder::UpdateConfig(void *pvReserved)
{
    kbvsti_Settings settings;
    kbvsti_GetSettings(m_pConfig, &settings);
    m_dVolume = settings.dVolume;
    m_bCache = settings.bCache;
    m_bShowEditor = settings.bShowEditor;
    m_reset_type = (SmfLoader::ResetType)settings.nReset;
    if (!m_bCache)FreeVsti(NULL);

    if (m_pVsti[0]->HasEditor()) {
        if (settings.bShowEditor) {
            ShowEditor();
        }
        else {
            CloseEditor();
        }
    }
    return 0;
}
//////////////////////////////////////////////////////////////////////////
int KbVstiDecoder::PlayMidi(void* buffer)
{
    int i;
    MsgList msg_list[MAX_PORT_NUM];
    // 最後までいっている場合は、残りのサンプルを取得するだけ。
    if (m_midx == m_mnum)
    {
        int ret = 0;
        //最大で5秒だけ余計に再生する(終端付近で演奏が切れるため)
        //その間、ほぼ無音である状態が 0.5 秒続いたら終了
        const int max_ms = 5000;//曲長から最大5秒まで(ノートオフ不備等で無音が検出出来ない可能性があるため)
        const int max_silent_smp = m_MediaInfo.dwSampleRate / 2;// 0.5秒
        const UINT64 end_smp = m_tend + kpi_100nsToSample(max_ms * 10000ui64, m_MediaInfo.dwSampleRate);
        if (m_time_smp < end_smp && (m_silent_smp < max_silent_smp)){
            for (i = 0; i < m_port_num; i++) {
                ret = RenderMidi(m_pVsti[i], msg_list[i], buffer, m_block_size, i == 0, m_time_smp);
            }
            //無音の検出(結果がほぼ無音だったらそこで演奏終了)
            if (m_loop_idx < 0) {//ループ曲の場合は終端の無音検出をしない
                const double limit = 0.001;//10^(-60/20) (-60dB)
                if (m_pVsti[0]->IsSupport64()) {
                    double *pcm = (double*)buffer;
                    for (i = m_block_size * 2 - 1; i >= 0; i--) {
                        if (pcm[i] > limit || pcm[i] < -limit) {
                            break;
                        }
                    }
                }
                else {
                    float *pcm = (float*)buffer;
                    for (i = m_block_size * 2 - 1; i >= 0; i--) {
                        if (pcm[i] > limit || pcm[i] < -limit) {
                            break;
                        }
                    }
                }
                if (i < 0) {
                    m_silent_smp += m_block_size;
                    if (m_silent_smp >= max_silent_smp) {//0.5秒以上の無音
                        ret = m_silent_smp - max_silent_smp;
                        return ret;
                    }
                }
                else {
                    m_silent_smp = m_block_size - (i/2 + 1);
                }
            }
        }
        if (m_loop_idx >= 0) {//Loop(CC111)
            m_midx = m_loop_idx;
            m_time_smp = m_loader.GetMidiMessage(m_midx).time_smp;
        }
        else {
            return ret;
        }
    }
    for (i = 0; i < m_port_num; i++) {
        msg_list[i].reserve(128);
    }
    // 現在の演奏時間(+m_block_size)までのメッセージを全て送信する
    const UINT64 time_smp = m_time_smp + m_block_size;
    while (m_midx < m_mnum)
    {
        const SmfLoader::MidiMessage& msg = m_loader.GetMidiMessage(m_midx);
        UINT64 cur_time_smp = msg.time_smp;
        if (cur_time_smp > time_smp) {
            break;
        }
        int port_index = msg.port;
        if (port_index >= m_port_num) {
            port_index = 0;
        }
        //if (msg.data != SmfLoader::END_OF_TRACK) {//END_OF_TRACK は VstiHost::Render 内で無視される
        //}else
        if (msg.data == SmfLoader::SYS_EX_ALL_PORT) {//全てのポートに送信
            for(int i = 0; i < m_port_num; i++){
                msg_list[i].push_back(msg);
            }
        }
        else{
            msg_list[port_index].push_back(msg);
        }
        m_midx++;
    }

    int ret = 0;
    for (i = 0; i < m_port_num; i++) {
        ret = RenderMidi(m_pVsti[i], msg_list[i], buffer, m_block_size, i == 0, m_time_smp);
    }
    return ret;
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbVstiDecoder::Initialize(void)
{
    if(InterlockedIncrement(&s_lInitialized) == 1){
        InitializeCriticalSection(&s_cs);
    }
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbVstiDecoder::Uninitialize(void)
{
    if(InterlockedDecrement(&s_lInitialized) == 0){
        DeleteCriticalSection(&s_cs);
        delete s_pLoader;
        s_pLoader = NULL;
    }
}
//////////////////////////////////////////////////////////////////////////
VstiHost* __fastcall KbVstiDecoder::GetVsti(IKpiConfig *pConfig)
{
    VstiHost *ret = NULL;
    EnterCriticalSection(&s_cs);
    do{
        if(s_pLoader){
            ret = s_pLoader->GetVsti();
            break;
        }
        wchar_t szVstiPath[MAX_PATH];
        kbvsti_GetVstiPath(pConfig, szVstiPath, _countof(szVstiPath));
        if (!szVstiPath[0]) {
            break;
        }
        VstiLoader *pLoader = new VstiLoader();
        if(!pLoader->Init(szVstiPath)){
            delete pLoader;
            break;
        }
        s_pLoader = pLoader;
    }while(1);
    LeaveCriticalSection(&s_cs);
    return ret;
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbVstiDecoder::FreeVsti(VstiHost *pVsti)
{
    EnterCriticalSection(&s_cs);
    s_pLoader->CacheVsti(pVsti);
    LeaveCriticalSection(&s_cs);
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbVstiDecoder::ShowEditor(void)
{
    if(!m_bShowEditor || m_bEditorCreate)return;
    m_bEditorCreate = true;
    int nPosX, nPosY;
    kbvsti_GetEditorPos(m_pConfig, &nPosX, &nPosY);
    int nPort;
    for (nPort = 0; nPort < 4; nPort++) {
        if (m_pVsti[nPort]) {
            m_pVsti[nPort]->ShowEditor(nPosX, nPosY, nPort, m_pVsti, m_port_num);
        }
    }
}
//////////////////////////////////////////////////////////////////////////
void __fastcall KbVstiDecoder::CloseEditor(void)
{
    if(!m_bEditorCreate)return;
    m_bEditorCreate = false;
    for(int i = 0; i < m_port_num; i++){
        m_pVsti[i]->CloseEditor();
    }
    //ウィンドウ左上座標を保存
    int nPosX;
    int nPosY;
    m_pVsti[0]->GetEditorPos(&nPosX, &nPosY);
    kbvsti_SaveEditorPos(m_pConfig, nPosX, nPosY);
}
//////////////////////////////////////////////////////////////////////////

