From 93af3de251b55d29495a443cb6f775fecd16bab7 Mon Sep 17 00:00:00 2001 From: jthedering <51533355+jthedering@users.noreply.github.com> Date: Sun, 15 May 2022 18:27:24 +0000 Subject: [PATCH] Fixed: Reimplemented Voicemeeter integration (based on official examples) to fix stuttering issues. --- VoicemeeterClient/VoicemeeterClient.cpp | 300 ++++++++++++++---------- VoicemeeterClient/VoicemeeterClient.h | 37 ++- VoicemeeterClient/VoicemeeterClient.rc | Bin 4884 -> 4884 bytes VoicemeeterClient/VoicemeeterRemote.h | 134 ++++++++++- helpers/PrecisionTimer.h | 2 +- 5 files changed, 331 insertions(+), 142 deletions(-) diff --git a/VoicemeeterClient/VoicemeeterClient.cpp b/VoicemeeterClient/VoicemeeterClient.cpp index 09710b8..6b1c595 100644 --- a/VoicemeeterClient/VoicemeeterClient.cpp +++ b/VoicemeeterClient/VoicemeeterClient.cpp @@ -1,6 +1,6 @@ /* This file is part of Equalizer APO, a system-wide equalizer. - Copyright (C) 2017 Jonas Thedering + Copyright (C) 2022 Jonas Thedering This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ */ #include "stdafx.h" -#include + #define WIN32_LEAN_AND_MEAN #include #include @@ -29,15 +29,21 @@ #define voicemeeterKeyPath L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VB:Voicemeeter {17359A74-1236-5467}" #define voicemeeterWowKeyPath L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VB:Voicemeeter {17359A74-1236-5467}" #define uninstallStringValueName L"UninstallString" +#ifdef _WIN64 +#define voicemeeterRemoteFileName L"VoicemeeterRemote64.dll" +#else +#define voicemeeterRemoteFileName L"VoicemeeterRemote.dll" +#endif +#define IDM_RESTART 200 using namespace std; -long __stdcall callback(void* lpUser, long nCommand, void* lpData, long nnn); +static long __stdcall callback(void* lpUser, long nCommand, void* lpData, long nnn); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, - _In_ LPTSTR lpCmdLine, - _In_ int nCmdShow) + _In_ LPWSTR lpCmdLine, + _In_ int nShowCmd) { vector outputs; if (lpCmdLine[0] != 0) @@ -49,30 +55,17 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, LocalFree(argv); } - VoicemeeterClient* client = new VoicemeeterClient(outputs); - - MSG msg; - msg.wParam = 0; - - bool loop = client->restart; - while (loop) + try { - client->start(); - client->restart = false; - - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - loop = client->restart; - - client->stop(); + VoicemeeterClient client(outputs); + client.run(); + return 0; + } + catch (InitError e) + { + MessageBoxW(NULL, e.getMessage().c_str(), L"Init Error", MB_APPLMODAL | MB_OK | MB_ICONERROR); + return -1; } - delete client; - - return (int)msg.wParam; } VoicemeeterClient::VoicemeeterClient(const vector& outputs) @@ -87,108 +80,72 @@ VoicemeeterClient::VoicemeeterClient(const vector& outputs) voicemeeterDirectory = RegistryHelper::readValue(voicemeeterWowKeyPath, uninstallStringValueName); size_t index = voicemeeterDirectory.find_last_of(L'\\'); - HMODULE voicemeeterModule = NULL; - if (index != wstring::npos) - { - wstring setupFilename = voicemeeterDirectory.substr(index + 1); - voicemeeterType = 1; - if (setupFilename == L"VoicemeeterProSetup.exe") - voicemeeterType = 2;// banana - else if (setupFilename == L"Voicemeeter8Setup.exe") - voicemeeterType = 3;// potato - voicemeeterDirectory = voicemeeterDirectory.substr(0, index); + else + throw InitError(L"Voicemeeter is not installed"); -#ifdef _WIN64 - voicemeeterModule = LoadLibraryW((voicemeeterDirectory + L"\\VoicemeeterRemote64.dll").c_str()); -#else - voicemeeterModule = LoadLibraryW((voicemeeterDirectory + L"\\VoicemeeterRemote.dll").c_str()); -#endif - } + HMODULE module = NULL; + module = LoadLibraryW((voicemeeterDirectory + L"\\" voicemeeterRemoteFileName).c_str()); + if (module == NULL) + throw InitError(L"Failed to load " voicemeeterRemoteFileName); memset(&vmr, 0, sizeof(T_VBVMR_INTERFACE)); -#define LOAD_PROC(proc) vmr.proc = (T_ ## proc)GetProcAddress(voicemeeterModule, # proc) - if (voicemeeterModule != NULL) - { - LOAD_PROC(VBVMR_Login); - LOAD_PROC(VBVMR_Logout); - LOAD_PROC(VBVMR_AudioCallbackRegister); - LOAD_PROC(VBVMR_AudioCallbackStart); - LOAD_PROC(VBVMR_AudioCallbackStop); - LOAD_PROC(VBVMR_AudioCallbackUnregister); - LOAD_PROC(VBVMR_GetVoicemeeterType); - restart = true; - } +#define LOAD_PROC(proc) vmr.proc = (T_ ## proc)GetProcAddress(module, # proc);if (vmr.proc == NULL) throw InitError(L"Did not find function \"" # proc L"\" in " voicemeeterRemoteFileName) + LOAD_PROC(VBVMR_Login); + LOAD_PROC(VBVMR_Logout); + LOAD_PROC(VBVMR_GetVoicemeeterType); + LOAD_PROC(VBVMR_IsParametersDirty); + LOAD_PROC(VBVMR_AudioCallbackRegister); + LOAD_PROC(VBVMR_AudioCallbackStart); + LOAD_PROC(VBVMR_AudioCallbackStop); + LOAD_PROC(VBVMR_AudioCallbackUnregister); + + initSoftware(); } VoicemeeterClient::~VoicemeeterClient() { - if (loggedIn) + if (wTimer != 0) { - vmr.VBVMR_Logout(); - loggedIn = false; + KillTimer(NULL, wTimer); + wTimer = 0; } + endSoftware(); } -void VoicemeeterClient::start() +void VoicemeeterClient::run() { - if (!loggedIn) - loggedIn = (vmr.VBVMR_Login() == 0); + wTimer = SetTimer(NULL, 0, 500, NULL); + PostThreadMessage(mainThreadId, WM_COMMAND, IDM_RESTART, 0); - if (loggedIn) + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { - long type; - if (vmr.VBVMR_GetVoicemeeterType(&type) == 0) - voicemeeterType = type; - } - - unsigned outputCount; - if (voicemeeterType == 3) - outputCount = 5; - else if (voicemeeterType == 2) - outputCount = 3; - else - outputCount = 1; - - for (unsigned i = 0; i < outputCount; i++) - { - wstringstream sstream; - sstream << "Output A" << (i + 1); - wstring output = sstream.str(); - if (find(outputs.begin(), outputs.end(), output) != outputs.end()) + switch (msg.message) { - FilterEngine* engine = new FilterEngine(); - engine->setDeviceInfo(false, true, L"Voicemeeter", output, L"", L"Voicemeeter " + output); - engines.push_back(engine); + case WM_COMMAND: + handleCommand(msg.wParam, msg.lParam); + break; + case WM_TIMER: + if (msg.wParam == wTimer) + { + // check if Voicemeeter type has changed + if (vmr.VBVMR_IsParametersDirty() >= 0) + { + if (!connected) + detectVoicemeeterType(); + } + else + { + // Voicemeeter has been shut down + connected = false; + } + } + break; } - else - { - engines.push_back(NULL); - } - - idleSampleCounts.push_back(0); } - - if (vmr.VBVMR_AudioCallbackRegister(VBVMR_AUDIOCALLBACK_OUT, callback, this, "Equalizer APO") != 0) - { - PostThreadMessage(mainThreadId, WM_QUIT, 0, 0); - return; - } - vmr.VBVMR_AudioCallbackStart(); -} - -void VoicemeeterClient::stop() -{ - vmr.VBVMR_AudioCallbackStop(); - vmr.VBVMR_AudioCallbackUnregister(); - - idleSampleCounts.clear(); - for (FilterEngine* engine : engines) - if (engine != NULL) - delete engine; - engines.clear(); } void VoicemeeterClient::handle(long nCommand, void* lpData, long nnn) @@ -198,26 +155,18 @@ void VoicemeeterClient::handle(long nCommand, void* lpData, long nnn) case VBVMR_CBCOMMAND_STARTING: { VBVMR_LPT_AUDIOINFO audioInfo = (VBVMR_LPT_AUDIOINFO)lpData; + sampleRate = (float)audioInfo->samplerate; + maxFrameCount = audioInfo->nbSamplePerFrame; for (FilterEngine* engine : engines) if (engine != NULL) - engine->initialize((float)audioInfo->samplerate, 8, 8, 8, 0, audioInfo->nbSamplePerFrame); + engine->initialize(sampleRate, 8, 8, 8, 0, maxFrameCount); VoicemeeterAPOInfo::saveVoicemeeterSampleRate((unsigned)audioInfo->samplerate); } break; case VBVMR_CBCOMMAND_ENDING: - { - restart = true; - PostThreadMessage(mainThreadId, WM_QUIT, 0, 0); - } break; case VBVMR_CBCOMMAND_CHANGE: - { - VBVMR_LPT_AUDIOINFO audioInfo = (VBVMR_LPT_AUDIOINFO)lpData; - for (FilterEngine* engine : engines) - if (engine != NULL) - engine->initialize((float)audioInfo->samplerate, 8, 8, 8, 0, audioInfo->nbSamplePerFrame); - VoicemeeterAPOInfo::saveVoicemeeterSampleRate((unsigned)audioInfo->samplerate); - } + PostThreadMessage(mainThreadId, WM_COMMAND, IDM_RESTART, 0); break; case VBVMR_CBCOMMAND_BUFFER_OUT: { @@ -262,6 +211,113 @@ void VoicemeeterClient::handle(long nCommand, void* lpData, long nnn) } } +void VoicemeeterClient::initSoftware() +{ + long rep = vmr.VBVMR_Login(); + if (rep < 0) + throw InitError(L"Failed To Login"); + if (vmr.VBVMR_IsParametersDirty() == 0) + detectVoicemeeterType(); + else + connected = false; + unsigned tries = 30; + bool loop = true; + while (loop) + { + loop = false; + char clientName[64] = "Equalizer APO"; + rep = vmr.VBVMR_AudioCallbackRegister(VBVMR_AUDIOCALLBACK_OUT, callback, this, clientName); + if (rep == 1) + { + wchar_t message[512]; + wsprintf(message, L"Voicemeeter Output Insert already in use by:\n%S", clientName); + throw InitError(message); + } + else if (rep != 0) + { + if (tries > 1) + { + // sometimes fails temporarily after restarting VoicemeeterClient + Sleep(100); + tries--; + loop = true; + } + else + { + throw InitError(L"Failed to register audio callback"); + } + } + } +} + +void VoicemeeterClient::detectVoicemeeterType() +{ + long vmType; + long rep = vmr.VBVMR_GetVoicemeeterType(&vmType); + if (rep == 0) + { + connected = true; + + unsigned outputCount; + if (vmType == 3) + outputCount = 5; + else if (vmType == 2) + outputCount = 3; + else + outputCount = 1; + + if (outputCount != engines.size()) + { + idleSampleCounts.clear(); + for (FilterEngine* engine : engines) + if (engine != NULL) + delete engine; + engines.clear(); + + for (unsigned i = 0; i < outputCount; i++) + { + wstringstream sstream; + sstream << "Output A" << (i + 1); + wstring output = sstream.str(); + if (find(outputs.begin(), outputs.end(), output) != outputs.end()) + { + FilterEngine* engine = new FilterEngine(); + engine->setDeviceInfo(false, true, L"Voicemeeter", output, L"", L"Voicemeeter " + output); + if (sampleRate != 0.0f && maxFrameCount != 0) + engine->initialize(sampleRate, 8, 8, 8, 0, maxFrameCount); + engines.push_back(engine); + } + else + { + engines.push_back(NULL); + } + + idleSampleCounts.push_back(0); + } + } + } +} + +void VoicemeeterClient::endSoftware() +{ + if (vmr.VBVMR_Logout != NULL) + vmr.VBVMR_Logout(); + if (vmr.VBVMR_AudioCallbackUnregister != NULL) + vmr.VBVMR_AudioCallbackUnregister(); +} + +void VoicemeeterClient::handleCommand(WPARAM wparam, LPARAM lparam) +{ + switch (LOWORD(wparam)) + { + case IDM_RESTART: + Sleep(50); + if (vmr.VBVMR_AudioCallbackStart != NULL) + vmr.VBVMR_AudioCallbackStart(); + break; + } +} + bool VoicemeeterClient::isBufferSilent(float** sampleData, long sampleCount) { bool silent = true; @@ -282,7 +338,7 @@ bool VoicemeeterClient::isBufferSilent(float** sampleData, long sampleCount) return silent; } -long __stdcall callback(void* lpUser, long nCommand, void* lpData, long nnn) +static long __stdcall callback(void* lpUser, long nCommand, void* lpData, long nnn) { VoicemeeterClient* client = (VoicemeeterClient*)lpUser; client->handle(nCommand, lpData, nnn); diff --git a/VoicemeeterClient/VoicemeeterClient.h b/VoicemeeterClient/VoicemeeterClient.h index 451e453..3bcd41c 100644 --- a/VoicemeeterClient/VoicemeeterClient.h +++ b/VoicemeeterClient/VoicemeeterClient.h @@ -1,6 +1,6 @@ /* This file is part of Equalizer APO, a system-wide equalizer. - Copyright (C) 2017 Jonas Thedering + Copyright (C) 2022 Jonas Thedering This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,20 +29,41 @@ class VoicemeeterClient public: VoicemeeterClient(const std::vector& outputs); ~VoicemeeterClient(); - void start(); - void stop(); - + void run(); void handle(long nCommand, void* lpData, long nnn); - bool restart = false; private: + void initSoftware(); + void detectVoicemeeterType(); + void endSoftware(); + void handleCommand(WPARAM wparam, LPARAM lparam); bool isBufferSilent(float** sampleData, long sampleCount); + std::vector outputs; unsigned long mainThreadId; T_VBVMR_INTERFACE vmr; + size_t wTimer = 0; + bool connected = true; + float sampleRate = 0.0f; + unsigned maxFrameCount = 0; + std::vector engines; std::vector idleSampleCounts; - bool loggedIn = false; - long voicemeeterType = 1; - std::vector outputs; +}; + +class InitError +{ +public: + InitError(const std::wstring& message) + : message(message) + { + } + + std::wstring getMessage() + { + return message; + } + +private: + std::wstring message; }; diff --git a/VoicemeeterClient/VoicemeeterClient.rc b/VoicemeeterClient/VoicemeeterClient.rc index 5cb7e72989c5dd7a24c3ec741742984c45b232eb..4289b87c86092161e538b97b7cbc6e6b1a2b8ea2 100644 GIT binary patch delta 16 XcmbQDHbrei9xt;IgVE+f-o>l{ESLn~ delta 16 XcmbQDHbrei9xt;YgZbt{-o>l{EUpCP diff --git a/VoicemeeterClient/VoicemeeterRemote.h b/VoicemeeterClient/VoicemeeterRemote.h index 179eb23..b0419f7 100644 --- a/VoicemeeterClient/VoicemeeterRemote.h +++ b/VoicemeeterClient/VoicemeeterRemote.h @@ -1,22 +1,20 @@ /******************************************************************************/ -/* Voicemeeter Remote API. V.Burel©2015 */ +/* Voicemeeter Remote API. V.Burel©2015-2021 */ /******************************************************************************/ -/* This Library allows communication with Voicemeeter Applications */ +/* This Library allows communication with Voicemeeter applications */ /* 4 Client Applications can be connected to remote Voicemeeter. */ +/* it also allows communication with MacroButtons application. */ /******************************************************************************/ /* */ /* OFFICIAL LINK : WWW.VOICEMEETER.COM */ /* */ /******************************************************************************/ -/* COPYRIGHT: Vincent Burel(c)2015 All Rights Reserved */ +/* COPYRIGHT: Vincent Burel(c)2015-2021 All Rights Reserved */ /******************************************************************************/ -/* LICENSING: This header file and VoicemeeterRemote.dll are made available */ -/* for evaluation and testing purpose only (to make demo and proof */ -/* of concept). */ /* */ -/* for any commercial exploitation or to get specific support, */ -/* a commercial license needs to be purchased. thanks to contact */ -/* us by e-mail (get our contact on www.vb-audio.com). */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* */ /******************************************************************************/ /* long = 32 bit integer */ /******************************************************************************/ @@ -61,9 +59,10 @@ long __stdcall VBVMR_Logout(void); /** @brief Run Voicemeeter Application (get installation directory and run Voicemeeter Application). - @param vType : Voicemeeter type (1 = Voicemeeter, 2= Voicemeeter Banana). + @param vType : Voicemeeter type (1 = Voicemeeter, 2= Voicemeeter Banana, 3= Voicemeeter Potato, 6 = Potato x64 bits). @return : 0: Ok. - -1: not installed + -1: not installed (UninstallString not found in registry). + -2: unknown vType number */ long __stdcall VBVMR_RunVoicemeeter(long vType); @@ -106,6 +105,11 @@ long __stdcall VBVMR_RunVoicemeeter(long vType); +---------+---------+---------+-------------+-----------+------+------+------+------+------+ | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 2 | 3 | 4 | + VOICEMEETER POTATO STRIP/BUS INDEX ASSIGNMENT + + | Strip 1 | Strip 2 | Strip 2 | Strip 2 | Strip 2 |Virtual Input|Virtual AUX| VAIO3 |BUS A1|BUS A2|BUS A3|BUS A4|BUS A5|BUS B1|BUS B2|BUS B3| + +---------+---------+---------+---------+---------+-------------+-----------+-----------+------+------+------+------+------+------+------+------+ + | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | @return : 0: OK (no error). @@ -255,6 +259,20 @@ long __stdcall VBVMR_GetParameterStringW(char * szParamName, unsigned short * ws +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + VOICEMEETER POTATO CHANNEL ASSIGNMENT + + | Strip 1 | Strip 2 | Strip 3 | Strip 4 | Strip 5 | Virtual Input | Virtual Input AUX | VAIO3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | + + | Output A1 | Output A2 | Output A3 | Output A4 | Output A5 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + + | Virtual Output B1 | Virtual Output B2 | Virtual Output B3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | + @return : 0: OK (no error). -1: error @@ -681,6 +699,66 @@ long __stdcall VBVMR_AudioCallbackUnregister(void); +/******************************************************************************/ +/* */ +/* Macro Buttons */ +/* */ +/******************************************************************************/ + +/** @name Macro Buttons functions +* @{ */ + + /** + @brief Check if Macro Buttons states changed. + Call this function periodically (typically every 50 or 500ms) to know if something happen on MacroButton states . + (this function must be called from one thread only) + + @return: 0: no new status. + >0: last nu logical button status changed. + -1: error (unexpected) + -2: no server. + */ + +long __stdcall VBVMR_MacroButton_IsDirty(void); + + /** + @brief get current status of a given button. + @param nuLogicalButton : button index: 0 to 79) + @param pValue : Pointer on float (32bit float by reference) receiving the wanted value (0.0 = OFF / 1.0 = ON). + @param bitmode: define what kind of value you want to read (see MACROBUTTON_MODE below) + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + -5: structure mismatch + */ + +long __stdcall VBVMR_MacroButton_GetStatus(long nuLogicalButton, float * pValue, long bitmode); + + /** + @brief set current button value. + @param nuLogicalButton : button index: 0 to 79) + @param fValue : float 32 bit value giving the status (0.0 = OFF / 1.0 = ON). + @param bitmode: define what kind of value you want to write/modify (see MACROBUTTON_MODE below) + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + -5: structure mismatch + */ + +long __stdcall VBVMR_MacroButton_SetStatus(long nuLogicalButton, float fValue, long bitmode); + +#define VBVMR_MACROBUTTON_MODE_DEFAULT 0x00000000 //PUSH or RELEASE button +#define VBVMR_MACROBUTTON_MODE_STATEONLY 0x00000002 //change Displayed State only +#define VBVMR_MACROBUTTON_MODE_TRIGGER 0x00000003 //change Trigger State + + + +/** @} */ + + + @@ -725,6 +803,11 @@ typedef long (__stdcall *T_VBVMR_AudioCallbackStart)(void); typedef long (__stdcall *T_VBVMR_AudioCallbackStop)(void); typedef long (__stdcall *T_VBVMR_AudioCallbackUnregister)(void); +typedef long (__stdcall *T_VBVMR_MacroButton_IsDirty)(void); +typedef long (__stdcall *T_VBVMR_MacroButton_GetStatus)(long nuLogicalButton, float * pValue, long bitmode); +typedef long (__stdcall *T_VBVMR_MacroButton_SetStatus)(long nuLogicalButton, float fValue, long bitmode); + + typedef struct tagVBVMR_INTERFACE { @@ -759,6 +842,10 @@ typedef struct tagVBVMR_INTERFACE T_VBVMR_AudioCallbackStop VBVMR_AudioCallbackStop; T_VBVMR_AudioCallbackUnregister VBVMR_AudioCallbackUnregister; + T_VBVMR_MacroButton_IsDirty VBVMR_MacroButton_IsDirty; + T_VBVMR_MacroButton_GetStatus VBVMR_MacroButton_GetStatus; + T_VBVMR_MacroButton_SetStatus VBVMR_MacroButton_SetStatus; + } T_VBVMR_INTERFACE, *PT_VBVMR_INTERFACE, *LPT_VBVMR_INTERFACE; #ifdef VBUSE_LOCALLIB @@ -766,6 +853,31 @@ typedef struct tagVBVMR_INTERFACE void __stdcall VBVMR_SetHinstance(HINSTANCE hinst); #endif + + + + + + + +/******************************************************************************/ +/* LOCAL FUNCTIONS */ +/******************************************************************************/ + +long VBVMR_LocalInit(void); +long VBVMR_LocalEnd(void); +void * VBVMR_GetRequestVB0STREAMPTR(void); + +long VBVMR_SetParametersWEx(unsigned short * szParamScript, long fCopyToClient); + +long VBVMR_LoginEx(long properties); + +long VBVMR_MB_PushSettings(void * lpParam); + + + + + #ifdef __cplusplus } #endif diff --git a/helpers/PrecisionTimer.h b/helpers/PrecisionTimer.h index 8d64010..ba09e07 100644 --- a/helpers/PrecisionTimer.h +++ b/helpers/PrecisionTimer.h @@ -25,7 +25,7 @@ class PrecisionTimer { LARGE_INTEGER freq; - LARGE_INTEGER startCount; + LARGE_INTEGER startCount{}; public: PrecisionTimer()