aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/adlmidi-2/midiplay.cc8
-rw-r--r--utils/vlc_codec/libadlmidi.c2
-rw-r--r--utils/winmm_drv/CMakeLists.txt146
-rw-r--r--utils/winmm_drv/config/regconfig.c296
-rw-r--r--utils/winmm_drv/config/regconfig.h61
-rw-r--r--utils/winmm_drv/cpl/adlconfig-tool.c20
-rw-r--r--utils/winmm_drv/cpl/adlconfig.c101
-rw-r--r--utils/winmm_drv/cpl/adlconfig.def5
-rw-r--r--utils/winmm_drv/cpl/adlconfig.manifest36
-rw-r--r--utils/winmm_drv/cpl/adlconfig.rc68
-rw-r--r--utils/winmm_drv/cpl/config_dialog.c383
-rw-r--r--utils/winmm_drv/cpl/config_dialog.h12
-rw-r--r--utils/winmm_drv/cpl/opl3icon.icobin0 -> 22486 bytes
-rw-r--r--utils/winmm_drv/cpl/res-cpl.rc3
-rw-r--r--utils/winmm_drv/cpl/res-tool.rc3
-rw-r--r--utils/winmm_drv/cpl/resource.h32
-rw-r--r--utils/winmm_drv/cpl/targetver.h24
-rw-r--r--utils/winmm_drv/installer/drvinst.c273
-rw-r--r--utils/winmm_drv/installer/drvinst.exe.manifest25
-rw-r--r--utils/winmm_drv/installer/drvinst.rc6
-rw-r--r--utils/winmm_drv/installer/install.bat1
-rw-r--r--utils/winmm_drv/installer/uninstall.bat1
-rw-r--r--utils/winmm_drv/src/MidiSynth.cpp720
-rw-r--r--utils/winmm_drv/src/MidiSynth.h90
-rw-r--r--utils/winmm_drv/src/stdafx.h83
-rw-r--r--utils/winmm_drv/src/targetver.h24
-rw-r--r--utils/winmm_drv/src/winmm_drv.cpp387
-rw-r--r--utils/winmm_drv/src/winmm_drv.def6
-rw-r--r--utils/winmm_drv/test/test.c105
29 files changed, 2917 insertions, 4 deletions
diff --git a/utils/adlmidi-2/midiplay.cc b/utils/adlmidi-2/midiplay.cc
index 8a39f2f..b8f9551 100644
--- a/utils/adlmidi-2/midiplay.cc
+++ b/utils/adlmidi-2/midiplay.cc
@@ -1154,7 +1154,7 @@ static void SendStereoAudio(unsigned long count, short* samples)
//static unsigned counter = 0; if(++counter < 8000) return;
-#if defined(__WIN32__) && 0
+#if defined(_WIN32) && 0
// Cheat on dosbox recording: easier on the cpu load.
{count*=2;
std::vector<short> AudioBuffer(count);
@@ -1185,7 +1185,7 @@ static void SendStereoAudio(unsigned long count, short* samples)
reverb_data.chan[w].Process(count);
// Convert to signed 16-bit int format and put to playback queue
-#ifdef __WIN32__
+#ifdef _WIN32
std::vector<short> AudioBuffer(count*2);
const size_t pos = 0;
#else
@@ -1299,7 +1299,7 @@ static void SendStereoAudio(unsigned long count, short* samples)
}
}
#endif
-#ifndef __WIN32__
+#ifndef _WIN32
AudioBuffer_lock.Unlock();
#else
if(!WritePCMfile)
@@ -1491,7 +1491,7 @@ bool AdlInstrumentTester::HandleInputChar(char ch)
DoNoteOff();
break;
case 3:
-#if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__))
+#if !((!defined(_WIN32) || defined(__CYGWIN__)) && !defined(__DJGPP__))
case 27:
#endif
return false;
diff --git a/utils/vlc_codec/libadlmidi.c b/utils/vlc_codec/libadlmidi.c
index d247d30..0d04b6a 100644
--- a/utils/vlc_codec/libadlmidi.c
+++ b/utils/vlc_codec/libadlmidi.c
@@ -109,6 +109,8 @@ static const char * const emulator_type_descriptions[] =
N_("Nuked OPL3 1.8"),
N_("Nuked OPL3 1.7.4 (Optimized)"),
N_("DOSBox"),
+ N_("Opal"),
+ N_("Java OPL3"),
NULL
};
diff --git a/utils/winmm_drv/CMakeLists.txt b/utils/winmm_drv/CMakeLists.txt
new file mode 100644
index 0000000..ae76a0c
--- /dev/null
+++ b/utils/winmm_drv/CMakeLists.txt
@@ -0,0 +1,146 @@
+
+if(NOT WIN32)
+ message(FATAL_ERROR "WinDriver: This component is for Windows Platform only")
+endif()
+
+if(WIN32 AND CMAKE_COMPILER_IS_GNUCXX)
+ option(WITH_WINMMDRV_PTHREADS "Link libwinpthreads statically (when using pthread-based builds)" ON)
+ option(WITH_WINMMDRV_MINGWEX "Link libmingwex statically (when using vanilla MinGW builds)" OFF)
+endif()
+
+#====================================
+# Driver module
+#====================================
+
+set(ADLMIDI_DRIVER_SRC
+ src/winmm_drv.def
+ src/winmm_drv.cpp
+ src/MidiSynth.cpp
+ config/regconfig.c
+)
+
+add_library(adlmididrv MODULE ${ADLMIDI_DRIVER_SRC})
+set_target_properties(adlmididrv PROPERTIES PREFIX "")
+target_link_libraries(adlmididrv PRIVATE winmm ADLMIDI_static)
+target_include_directories(adlmididrv PRIVATE config)
+target_compile_definitions(adlmididrv PRIVATE
+ -D_USRDLL
+ -D_WINDLL
+ -DMM_WIN32DRV_EXPORTS
+ -DENABLE_REG_SERVER
+)
+
+if(WIN32 AND CMAKE_COMPILER_IS_GNUCXX)
+ set_property(TARGET adlmididrv APPEND_STRING PROPERTY LINK_FLAGS " -static-libgcc -static-libstdc++ -Wl,--enable-stdcall-fixup")
+ if(WITH_WINMMDRV_PTHREADS)
+ set_property(TARGET adlmididrv APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-Bstatic,--whole-archive -lpthread -Wl,-Bdynamic,--no-whole-archive")
+ endif()
+ if(WITH_WINMMDRV_MINGWEX)
+ set_property(TARGET adlmididrv APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-Bstatic,--whole-archive -lmingwex -Wl,-Bdynamic,--no-whole-archive")
+ endif()
+endif()
+
+if(NOT MSVC)
+ target_compile_options(adlmididrv PRIVATE "-Wno-cast-function-type")
+endif()
+
+
+
+#====================================
+# Install utility
+#====================================
+
+set(ADLMIDI_INSTALLER_SRC
+ installer/drvinst.c
+)
+
+if(NOT MSVC)
+ list(APPEND ADLMIDI_INSTALLER_SRC
+ installer/drvinst.rc
+ )
+endif()
+
+add_executable(drvsetup ${ADLMIDI_INSTALLER_SRC})
+
+target_compile_definitions(drvsetup PRIVATE
+ -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1
+ -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1
+ -D_CRT_SECURE_NO_WARNINGS
+)
+
+if(WIN32 AND CMAKE_COMPILER_IS_GNUCXX)
+ set_property(TARGET drvsetup APPEND_STRING PROPERTY LINK_FLAGS " -static-libgcc")
+ if(WITH_WINMMDRV_MINGWEX)
+ set_property(TARGET drvsetup APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-Bstatic,--whole-archive -lmingwex -Wl,-Bdynamic,--no-whole-archive")
+ endif()
+endif()
+
+configure_file(installer/install.bat ${CMAKE_BINARY_DIR}/install.bat COPYONLY)
+configure_file(installer/uninstall.bat ${CMAKE_BINARY_DIR}/uninstall.bat COPYONLY)
+
+
+#====================================
+# A unit test to verify the driver
+#====================================
+
+add_executable(drvtest test/test.c)
+target_link_libraries(drvtest PRIVATE winmm)
+target_compile_options(drvtest PRIVATE "-Wno-cast-function-type")
+if(WITH_WINMMDRV_MINGWEX)
+ set_property(TARGET drvtest APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-Bstatic,--whole-archive -lmingwex -Wl,-Bdynamic,--no-whole-archive")
+endif()
+
+
+#====================================
+# Control panel applet
+#====================================
+
+set(ADLMIDI_DRIVER_SRC
+ cpl/adlconfig.def
+ cpl/adlconfig.rc
+ cpl/config_dialog.c
+ config/regconfig.c
+)
+
+add_library(adlmidiconfig MODULE ${ADLMIDI_DRIVER_SRC} cpl/adlconfig.c cpl/res-cpl.rc)
+set_target_properties(adlmidiconfig PROPERTIES PREFIX "" OUTPUT_NAME "libadlconfig" SUFFIX ".cpl")
+target_include_directories(adlmidiconfig PRIVATE config)
+target_compile_definitions(adlmidiconfig PRIVATE
+ -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1
+ -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1
+ -D_CRT_SECURE_NO_WARNINGS
+ -D_UNICODE
+ -DUNICODE
+ -DADL_IS_CPL_APPLET
+)
+
+target_link_libraries(adlmidiconfig PRIVATE comctl32 gdi32 user32)
+
+
+add_executable(adlmidiconfigtool ${ADLMIDI_DRIVER_SRC} cpl/adlconfig-tool.c cpl/res-tool.rc)
+set_target_properties(adlmidiconfigtool PROPERTIES WIN32_EXECUTABLE ON)
+target_include_directories(adlmidiconfigtool PRIVATE config)
+target_compile_definitions(adlmidiconfigtool PRIVATE
+ -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1
+ -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1
+ -D_CRT_SECURE_NO_WARNINGS
+ -D_UNICODE
+ -DUNICODE
+)
+
+target_link_libraries(adlmidiconfigtool PRIVATE comctl32 gdi32 user32)
+
+if(WIN32 AND CMAKE_COMPILER_IS_GNUCXX)
+ target_compile_options(adlmidiconfig PRIVATE "-Wno-cast-function-type")
+ target_compile_options(adlmidiconfigtool PRIVATE "-Wno-cast-function-type")
+ set_property(TARGET adlmidiconfig APPEND_STRING PROPERTY LINK_FLAGS " -static-libgcc -Wl,--enable-stdcall-fixup")
+ set_property(TARGET adlmidiconfigtool APPEND_STRING PROPERTY LINK_FLAGS " -static-libgcc -Wl,--enable-stdcall-fixup")
+ if(WITH_WINMMDRV_MINGWEX)
+ set_property(TARGET adlmidiconfig APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-Bstatic,--whole-archive -lmingwex -Wl,-Bdynamic,--no-whole-archive")
+ set_property(TARGET adlmidiconfigtool APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-Bstatic,--whole-archive -lmingwex -Wl,-Bdynamic,--no-whole-archive")
+ endif()
+endif()
+
+
+#install(TARGETS adlmididrv
+# RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
diff --git a/utils/winmm_drv/config/regconfig.c b/utils/winmm_drv/config/regconfig.c
new file mode 100644
index 0000000..353cea2
--- /dev/null
+++ b/utils/winmm_drv/config/regconfig.c
@@ -0,0 +1,296 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "regconfig.h"
+
+
+#define TOTAL_BYTES_READ 1024
+#define OFFSET_BYTES 1024
+
+static BOOL createRegistryKey(HKEY hKeyParent, const PWCHAR subkey)
+{
+ DWORD dwDisposition; //It verify new key is created or open existing key
+ HKEY hKey;
+ DWORD ret;
+
+ ret = RegCreateKeyExW(hKeyParent, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition);
+ if(ret != ERROR_SUCCESS)
+ {
+ return FALSE;
+ }
+
+ RegCloseKey(hKey); //close the key
+ return TRUE;
+}
+
+static BOOL writeStringToRegistry(HKEY hKeyParent, const PWCHAR subkey, const PWCHAR valueName, PWCHAR strData)
+{
+ DWORD ret;
+ HKEY hKey;
+
+ //Check if the registry exists
+ ret = RegOpenKeyExW(hKeyParent, subkey, 0, KEY_WRITE, &hKey);
+ if(ret == ERROR_SUCCESS)
+ {
+ ret = RegSetValueExW(hKey, valueName, 0, REG_SZ, (LPBYTE)(strData), wcslen(strData) * sizeof(wchar_t));
+ if(ret != ERROR_SUCCESS)
+ {
+ RegCloseKey(hKey);
+ return FALSE;
+ }
+
+ RegCloseKey(hKey);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static BOOL readStringFromRegistry(HKEY hKeyParent, const PWCHAR subkey, const PWCHAR valueName, PWCHAR *readData)
+{
+ HKEY hKey;
+ DWORD len = TOTAL_BYTES_READ;
+ DWORD readDataLen = len;
+ PWCHAR readBuffer = (PWCHAR )malloc(sizeof(PWCHAR)* len);
+
+ if (readBuffer == NULL)
+ return FALSE;
+
+ //Check if the registry exists
+ DWORD ret = RegOpenKeyExW(hKeyParent, subkey, 0, KEY_READ, &hKey);
+
+ if(ret == ERROR_SUCCESS)
+ {
+ ret = RegQueryValueExW(hKey, valueName, NULL, NULL, (BYTE*)readBuffer, &readDataLen);
+
+ while(ret == ERROR_MORE_DATA)
+ {
+ // Get a buffer that is big enough.
+ len += OFFSET_BYTES;
+ readBuffer = (PWCHAR)realloc(readBuffer, len);
+ readDataLen = len;
+ ret = RegQueryValueExW(hKey, valueName, NULL, NULL, (BYTE*)readBuffer, &readDataLen);
+ }
+ if (ret != ERROR_SUCCESS)
+ {
+ RegCloseKey(hKey);
+ return FALSE;
+ }
+
+ *readData = readBuffer;
+ RegCloseKey(hKey);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static BOOL readIntFromRegistry(HKEY hKeyParent, const PWCHAR subkey, const PWCHAR valueName, int *readData)
+{
+ WCHAR *buf = NULL;
+ BOOL ret;
+ ret = readStringFromRegistry(hKeyParent, subkey, valueName, &buf);
+ if(ret && readData)
+ {
+ *readData = _wtoi(buf);
+ }
+ if(buf)
+ free(buf);
+ return ret;
+}
+
+static BOOL writeIntToRegistry(HKEY hKeyParent, const PWCHAR subkey, const PWCHAR valueName, int intData)
+{
+ WCHAR buf[20];
+ BOOL ret;
+
+ ZeroMemory(buf, 20);
+ _snwprintf(buf, 20, L"%d", intData);
+
+ ret = writeStringToRegistry(hKeyParent, subkey, valueName, buf);
+ return ret;
+}
+
+
+
+
+void setupDefault(DriverSettings *setup)
+{
+ setup->useExternalBank = 0;
+ setup->bankId = 68;
+ ZeroMemory(setup->bankPath, MAX_PATH * sizeof(WCHAR));
+ setup->emulatorId = 0;
+
+ setup->flagDeepTremolo = BST_INDETERMINATE;
+ setup->flagDeepVibrato = BST_INDETERMINATE;
+
+ setup->flagSoftPanning = BST_CHECKED;
+ setup->flagScaleModulators = BST_UNCHECKED;
+ setup->flagFullBrightness = BST_UNCHECKED;
+
+ setup->volumeModel = 0;
+ setup->numChips = 4;
+ setup->num4ops = -1;
+}
+
+
+
+static const PWCHAR s_regPath = L"SOFTWARE\\Wohlstand\\libADLMIDI";
+
+void loadSetup(DriverSettings *setup)
+{
+ int iVal;
+ WCHAR *sVal = NULL;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"useExternalBank", &iVal))
+ setup->useExternalBank = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"bankId", &iVal))
+ setup->bankId = iVal;
+
+ if(readStringFromRegistry(HKEY_CURRENT_USER, s_regPath, L"bankPath", &sVal))
+ wcsncpy(setup->bankPath, sVal, MAX_PATH);
+ if(sVal)
+ free(sVal);
+ sVal = NULL;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"emulatorId", &iVal))
+ setup->emulatorId = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"flagDeepTremolo", &iVal))
+ setup->flagDeepTremolo = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"flagDeepVibrato", &iVal))
+ setup->flagDeepVibrato = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"flagSoftPanning", &iVal))
+ setup->flagSoftPanning = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"flagScaleModulators", &iVal))
+ setup->flagScaleModulators = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"flagFullBrightness", &iVal))
+ setup->flagFullBrightness = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"volumeModel", &iVal))
+ setup->volumeModel = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"numChips", &iVal))
+ setup->numChips = iVal;
+
+ if(readIntFromRegistry(HKEY_CURRENT_USER, s_regPath, L"num4ops", &iVal))
+ setup->num4ops = iVal;
+}
+
+void saveSetup(DriverSettings *setup)
+{
+ createRegistryKey(HKEY_CURRENT_USER, s_regPath);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"useExternalBank", setup->useExternalBank);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"bankId", setup->bankId);
+ if(setup->bankPath[0] != L'\0')
+ writeStringToRegistry(HKEY_CURRENT_USER, s_regPath, L"bankPath", setup->bankPath);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"emulatorId", setup->emulatorId);
+
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"flagDeepTremolo", setup->flagDeepTremolo);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"flagDeepVibrato", setup->flagDeepVibrato);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"flagSoftPanning", setup->flagSoftPanning);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"flagScaleModulators", setup->flagScaleModulators);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"flagFullBrightness", setup->flagFullBrightness);
+
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"volumeModel", setup->volumeModel);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"numChips", setup->numChips);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPath, L"num4ops", setup->num4ops);
+}
+
+
+static const PWCHAR s_regPathNotify = L"SOFTWARE\\Wohlstand\\libADLMIDI\\notify";
+
+void sendSignal(int sig)
+{
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPathNotify, L"command", sig);
+}
+
+
+#ifdef ENABLE_REG_SERVER
+
+static HKEY hKey = 0;
+static HANDLE hEvent = 0;
+
+void openSignalListener()
+{
+ LONG errorcode;
+ DWORD dwFilter = REG_NOTIFY_CHANGE_NAME |
+ REG_NOTIFY_CHANGE_ATTRIBUTES |
+ REG_NOTIFY_CHANGE_LAST_SET |
+ REG_NOTIFY_CHANGE_SECURITY;
+
+ createRegistryKey(HKEY_CURRENT_USER, s_regPathNotify);
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPathNotify, L"command", 0);
+
+ errorcode = RegOpenKeyExW(HKEY_CURRENT_USER, s_regPathNotify, 0, KEY_NOTIFY, &hKey);
+ if(errorcode != ERROR_SUCCESS)
+ return;
+
+ hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if(hEvent == NULL)
+ {
+ RegCloseKey(hKey);
+ hKey = 0;
+ return;
+ }
+
+ errorcode = RegNotifyChangeKeyValue(hKey,
+ FALSE,
+ dwFilter,
+ hEvent,
+ TRUE);
+ if(errorcode != ERROR_SUCCESS)
+ {
+ CloseHandle(hEvent);
+ hEvent = 0;
+ RegCloseKey(hKey);
+ hKey = 0;
+ return;
+ }
+}
+
+int hasReloadSetupSignal()
+{
+ DWORD ret;
+ int cmd;
+
+ if(hEvent == 0)
+ return 0;
+
+ ret = WaitForSingleObject(hEvent, 0);
+
+ if(ret == WAIT_OBJECT_0)
+ {
+ readIntFromRegistry(HKEY_CURRENT_USER, s_regPathNotify, L"command", &cmd);
+ return cmd;
+ }
+
+ return 0;
+}
+
+void resetSignal()
+{
+ writeIntToRegistry(HKEY_CURRENT_USER, s_regPathNotify, L"command", 0);
+}
+
+void closeSignalListener()
+{
+ if(hKey)
+ RegCloseKey(hKey);
+ hKey = 0;
+
+ if(hEvent)
+ CloseHandle(hEvent);
+ hEvent = 0;
+}
+
+#endif
diff --git a/utils/winmm_drv/config/regconfig.h b/utils/winmm_drv/config/regconfig.h
new file mode 100644
index 0000000..f854d1b
--- /dev/null
+++ b/utils/winmm_drv/config/regconfig.h
@@ -0,0 +1,61 @@
+#pragma once
+#ifndef REG_SETUP_HHHH
+#define REG_SETUP_HHHH
+
+#include <windef.h>
+#include <winreg.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct DriverSettings_t
+{
+ BOOL useExternalBank;
+ int bankId;
+ WCHAR bankPath[MAX_PATH];
+ int emulatorId;
+
+ BOOL flagDeepTremolo;
+ BOOL flagDeepVibrato;
+
+ BOOL flagSoftPanning;
+ BOOL flagScaleModulators;
+ BOOL flagFullBrightness;
+
+ int volumeModel;
+ int numChips;
+ int num4ops;
+} DriverSettings;
+
+extern const WCHAR g_adlSignalMemory[];
+
+extern void setupDefault(DriverSettings *setup);
+extern void loadSetup(DriverSettings *setup);
+extern void saveSetup(DriverSettings *setup);
+
+
+#define DRV_SIGNAL_RELOAD_SETUP 1
+#define DRV_SIGNAL_RESET_SYNTH 2
+
+// Client
+/**
+ * @brief Ping the running driver to immediately reload the settings
+ */
+extern void sendSignal(int sig);
+
+#ifdef ENABLE_REG_SERVER
+// Server
+extern void openSignalListener();
+extern int hasReloadSetupSignal();
+extern void resetSignal();
+extern void closeSignalListener();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/utils/winmm_drv/cpl/adlconfig-tool.c b/utils/winmm_drv/cpl/adlconfig-tool.c
new file mode 100644
index 0000000..513f5eb
--- /dev/null
+++ b/utils/winmm_drv/cpl/adlconfig-tool.c
@@ -0,0 +1,20 @@
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <cpl.h>
+
+#include "resource.h"
+
+#include "config_dialog.h"
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow)
+{
+ initAdlSetupBox(hInstance, NULL);
+ runAdlSetupBox(hInstance, NULL);
+ return 0;
+
+ UNREFERENCED_PARAMETER(hPrevInstance);
+ UNREFERENCED_PARAMETER(pCmdLine);
+ UNREFERENCED_PARAMETER(nCmdShow);
+}
diff --git a/utils/winmm_drv/cpl/adlconfig.c b/utils/winmm_drv/cpl/adlconfig.c
new file mode 100644
index 0000000..744fc14
--- /dev/null
+++ b/utils/winmm_drv/cpl/adlconfig.c
@@ -0,0 +1,101 @@
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <cpl.h>
+
+#include "resource.h"
+
+#include "config_dialog.h"
+
+static HANDLE hModule = NULL;
+
+BOOL WINAPI DllMain(
+ PVOID hmod,
+ ULONG ulReason,
+ PCONTEXT pctx OPTIONAL
+)
+{
+ if(ulReason != DLL_PROCESS_ATTACH)
+ {
+ return TRUE;
+ }
+ else
+ {
+ hModule = hmod;
+ }
+
+ return TRUE;
+
+ UNREFERENCED_PARAMETER(pctx);
+}
+
+LONG APIENTRY CPlApplet(
+ HWND hwndCPL, // handle of Control Panel window
+ UINT uMsg, // message
+ LONG_PTR lParam1, // first message parameter
+ LONG_PTR lParam2 // second message parameter
+)
+{
+ LPCPLINFO lpCPlInfo;
+ LPNEWCPLINFO lpNewCPlInfo;
+ LONG retCode = 0;
+
+ switch (uMsg)
+ {
+ // first message, sent once
+ case CPL_INIT:
+ initAdlSetupBox(hModule, hwndCPL);
+ return TRUE;
+
+ // second message, sent once
+ case CPL_GETCOUNT:
+ return 1L;
+
+ // third message, sent once per app
+ case CPL_INQUIRE:
+ lpCPlInfo = (LPCPLINFO)lParam2;
+ lpCPlInfo->idIcon = IDI_ICON1;
+ lpCPlInfo->idName = IDC_DRIVERNAME;
+ lpCPlInfo->idInfo = IDC_DRIVERDESC;
+ lpCPlInfo->lData = 0L;
+ break;
+
+ // third message, sent once per app
+ case CPL_NEWINQUIRE:
+ lpNewCPlInfo = (LPNEWCPLINFO)lParam2;
+ lpNewCPlInfo->dwSize = (DWORD) sizeof(NEWCPLINFO);
+ lpNewCPlInfo->dwFlags = 0;
+ lpNewCPlInfo->dwHelpContext = 0;
+ lpNewCPlInfo->lData = 0;
+ lpNewCPlInfo->hIcon = LoadIconW(hModule, (LPCTSTR)MAKEINTRESOURCEW(IDI_ICON1));
+ lpNewCPlInfo->szHelpFile[0] = '\0';
+
+ LoadStringW(hModule, IDC_DRIVERNAME, lpNewCPlInfo->szName, 32);
+ LoadStringW(hModule, IDC_DRIVERDESC, lpNewCPlInfo->szInfo, 64);
+ break;
+
+ // application icon selected
+ case CPL_SELECT:
+ break;
+
+ // application icon double-clicked
+ case CPL_DBLCLK:
+ runAdlSetupBox(hModule, hwndCPL);
+ break;
+
+ case CPL_STOP:
+ break;
+
+ case CPL_EXIT:
+ cleanUpAdlSetupBox(hModule, hwndCPL);
+ break;
+
+ default:
+ break;
+ }
+
+ return retCode;
+
+ UNREFERENCED_PARAMETER(lParam1);
+}
diff --git a/utils/winmm_drv/cpl/adlconfig.def b/utils/winmm_drv/cpl/adlconfig.def
new file mode 100644
index 0000000..97ed971
--- /dev/null
+++ b/utils/winmm_drv/cpl/adlconfig.def
@@ -0,0 +1,5 @@
+LIBRARY "libADLMIDIConfig"
+
+EXPORTS
+ CPlApplet
+ ToolDlgProc
diff --git a/utils/winmm_drv/cpl/adlconfig.manifest b/utils/winmm_drv/cpl/adlconfig.manifest
new file mode 100644
index 0000000..1016f98
--- /dev/null
+++ b/utils/winmm_drv/cpl/adlconfig.manifest
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="*"
+ name="Wohlstand.libADLMIDI.WinMMDriver"
+ type="win32"
+ />
+ <description>libADLMIDI synth Driver settings tool</description>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/utils/winmm_drv/cpl/adlconfig.rc b/utils/winmm_drv/cpl/adlconfig.rc
new file mode 100644
index 0000000..455c906
--- /dev/null
+++ b/utils/winmm_drv/cpl/adlconfig.rc
@@ -0,0 +1,68 @@
+// Generated by ResEdit 1.6.6
+// Copyright (C) 2006-2015
+// http://www.resedit.net
+
+#include <windows.h>
+#include <commctrl.h>
+#include <richedit.h>
+#include "resource.h"
+
+
+
+
+//
+// Dialog resources
+//
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+IDD_CONFIG_BOX DIALOGEX 0, 0, 251, 244
+STYLE DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU
+CAPTION "libADLMIDI settings"
+FONT 8, "MS Shell Dlg", 400, 0, 1
+BEGIN
+ GROUPBOX "Instruments bank", IDC_INS_BANK, 9, 4, 233, 51, 0, WS_EX_LEFT
+ AUTORADIOBUTTON "Internal bank", IDC_BANK_INTERNAL, 15, 17, 63, 8, WS_GROUP | WS_TABSTOP, WS_EX_LEFT
+ COMBOBOX IDC_BANK_ID, 83, 14, 155, 200, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
+ AUTORADIOBUTTON "External bank", IDC_BANK_EXTERNAL, 15, 33, 63, 8, WS_TABSTOP, WS_EX_LEFT
+ EDITTEXT IDC_BANK_PATH, 83, 31, 107, 14, WS_DISABLED | ES_AUTOHSCROLL, WS_EX_LEFT
+ PUSHBUTTON "Browse...", IDC_BROWSE_BANK, 194, 30, 45, 14, 0, WS_EX_LEFT
+ LTEXT "Chip emulator type:", IDC_CHIPEMU_LABEL, 11, 60, 67, 10, SS_LEFT, WS_EX_LEFT
+ COMBOBOX IDC_EMULATOR, 83, 58, 159, 200, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
+ LTEXT "Number of chips", IDC_CHIPNUM_LABEL, 11, 78, 67, 8, SS_LEFT, WS_EX_LEFT
+ COMBOBOX IDC_NUM_CHIPS, 83, 76, 34, 200, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
+ LTEXT "Number of 4OP voices", IDC_4OPSNUM_LABEL, 128, 78, 72, 8, SS_LEFT, WS_EX_LEFT
+ COMBOBOX IDC_NUM_4OPVO, 202, 76, 40, 200, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
+ LTEXT "Volume model:", IDC_VM_LABEL, 11, 95, 67, 13, SS_LEFT, WS_EX_LEFT
+ COMBOBOX IDC_VOLUMEMODEL, 83, 95, 159, 200, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, WS_EX_LEFT
+ AUTOCHECKBOX "Full-panning stereo", IDC_FLAG_SOFTPAN, 11, 115, 227, 10, 0, WS_EX_LEFT
+ AUTO3STATE "Deep tremolo (Auto/Enable/Disable)", IDC_FLAG_TREMOLO, 11, 130, 227, 10, NOT WS_TABSTOP, WS_EX_LEFT
+ AUTO3STATE "Deep vibrato (Auto/Enable/Disable)", IDC_FLAG_VIBRATO, 11, 145, 227, 10, NOT WS_TABSTOP, WS_EX_LEFT
+ AUTOCHECKBOX "Scalable modulation", IDC_FLAG_SCALE, 11, 160, 227, 10, 0, WS_EX_LEFT
+ AUTOCHECKBOX "Full-range brightness (CC74)", IDC_FLAG_FULLBRIGHT, 11, 175, 227, 10, 0, WS_EX_LEFT
+ PUSHBUTTON "Reset synth now", IDC_RESET_SYNTH, 73, 195, 80, 14, 0, WS_EX_LEFT
+ PUSHBUTTON "Restore defaults", IDC_RESTORE_DEFAULTS, 155, 195, 88, 14, 0, WS_EX_LEFT
+ PUSHBUTTON "About...", IDC_ABOUT, 10, 212, 50, 14, 0, WS_EX_LEFT
+ DEFPUSHBUTTON "OK", IDOK, 88, 212, 50, 14, 0, WS_EX_LEFT
+ PUSHBUTTON "Cancel", IDCANCEL, 141, 212, 50, 14, 0, WS_EX_LEFT
+ PUSHBUTTON "Apply", IDC_APPLYBUTTON, 194, 212, 50, 14, 0, WS_EX_LEFT
+ RTEXT "Settings will be applied immediately", IDC_BOTTOMNOTE_LABEL, 7, 227, 235, 8, SS_RIGHT, WS_EX_LEFT
+END
+
+
+
+//
+// String Table resources
+//
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+STRINGTABLE
+BEGIN
+ IDC_DRIVERNAME "libADLMIDI settings"
+ IDC_DRIVERDESC "Settings for libADLMIDI synthesizer"
+END
+
+
+
+//
+// Icon resources
+//
+LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT
+IDI_ICON1 ICON "opl3icon.ico"
diff --git a/utils/winmm_drv/cpl/config_dialog.c b/utils/winmm_drv/cpl/config_dialog.c
new file mode 100644
index 0000000..195524d
--- /dev/null
+++ b/utils/winmm_drv/cpl/config_dialog.c
@@ -0,0 +1,383 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <commctrl.h>
+#include <commdlg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "config_dialog.h"
+#include "resource.h"
+
+#include "regconfig.h"
+
+#ifndef CBM_FIRST
+#define CBM_FIRST 0x1700
+#endif
+#ifndef CB_SETMINVISIBLE
+#define CB_SETMINVISIBLE (CBM_FIRST+1)
+#endif
+
+typedef int (*BankNamesCount)(void);
+typedef const char *const *(*BankNamesList)(void);
+
+static const char *const volume_models_descriptions[] =
+{
+ "Auto (defined by bank)",
+ "Generic",
+ "OPL3 Native",
+ "DMX",
+ "Apogee Sound System",
+ "Win9x SB16 driver",
+ "DMX (Fixed AM)",
+ "Apogee Sound System (Fixed AM)",
+ "Audio Interfaces Library (AIL)",
+ "Win9x Generic FM driver",
+ "HMI Sound Operating System",
+ "HMI Sound Operating System (Old)",
+ NULL
+};
+
+static const char * const emulator_type_descriptions[] =
+{
+ "Nuked OPL3 1.8",
+ "Nuked OPL3 1.7.4 (Optimized)",
+ "DOSBox",
+ "Opal",
+ "Java OPL3",
+ NULL
+};
+
+static DriverSettings g_setup;
+static HINSTANCE s_hModule;
+
+static void syncBankType(HWND hwnd, int type);
+static void sync4ops(HWND hwnd);
+static void syncWidget(HWND hwnd);
+static void buildLists(HWND hwnd);
+static void syncBankType(HWND hwnd, int type);
+static void openCustomBank(HWND hwnd);
+static void updateBankName(HWND hwnd, const WCHAR *filePath);
+
+static void sync4ops(HWND hwnd)
+{
+ char buff[10];
+ int i;
+
+ SendDlgItemMessageW(hwnd, IDC_NUM_4OPVO, CB_RESETCONTENT, 0, 0);
+ SendDlgItemMessageA(hwnd, IDC_NUM_4OPVO, CB_ADDSTRING, (LPARAM)-1, (LPARAM)"AUTO");
+ for(i = 0; i <= g_setup.numChips * 6; i++)
+ {
+ ZeroMemory(buff, 10);
+ snprintf(buff, 10, "%d", i);
+ SendDlgItemMessageA(hwnd, IDC_NUM_4OPVO, CB_ADDSTRING, (LPARAM)0, (LPARAM)buff);
+ }
+ SendDlgItemMessageA(hwnd, IDC_NUM_4OPVO, CB_SETCURSEL, (WPARAM)g_setup.num4ops + 1, (LPARAM)0);
+}
+
+static void syncWidget(HWND hwnd)
+{
+ char buff[10];
+ int i;
+
+ SendDlgItemMessage(hwnd, IDC_BANK_EXTERNAL, BM_SETCHECK, 0, 0);
+ SendDlgItemMessage(hwnd, IDC_BANK_INTERNAL, BM_SETCHECK, 0, 0);
+
+ if(g_setup.useExternalBank == 1)
+ SendDlgItemMessage(hwnd, IDC_BANK_EXTERNAL, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hwnd, IDC_BANK_INTERNAL, BM_SETCHECK, 1, 0);
+
+ syncBankType(hwnd, g_setup.useExternalBank);
+
+ SendDlgItemMessage(hwnd, IDC_FLAG_TREMOLO, BM_SETCHECK, g_setup.flagDeepTremolo, 0);
+ SendDlgItemMessage(hwnd, IDC_FLAG_VIBRATO, BM_SETCHECK, g_setup.flagDeepVibrato, 0);
+ SendDlgItemMessage(hwnd, IDC_FLAG_SOFTPAN, BM_SETCHECK, g_setup.flagSoftPanning, 0);
+ SendDlgItemMessage(hwnd, IDC_FLAG_SCALE, BM_SETCHECK, g_setup.flagScaleModulators, 0);
+ SendDlgItemMessage(hwnd, IDC_FLAG_FULLBRIGHT, BM_SETCHECK, g_setup.flagFullBrightness, 0);
+
+ SendDlgItemMessageW(hwnd, IDC_NUM_CHIPS, CB_RESETCONTENT, 0, 0);
+ for(i = 1; i <= 100; i++)
+ {
+ ZeroMemory(buff, 10);
+ snprintf(buff, 10, "%d", i);
+ SendDlgItemMessageA(hwnd, IDC_NUM_CHIPS, CB_ADDSTRING, (LPARAM)0, (LPARAM)buff);
+ }
+
+ SendDlgItemMessageA(hwnd, IDC_NUM_CHIPS, CB_SETCURSEL, (WPARAM)g_setup.numChips - 1, (LPARAM)0);
+ SendDlgItemMessageA(hwnd, IDC_BANK_ID, CB_SETCURSEL, (WPARAM)g_setup.bankId, (LPARAM)0);
+ updateBankName(hwnd, g_setup.bankPath);
+ SendDlgItemMessageA(hwnd, IDC_EMULATOR, CB_SETCURSEL, (WPARAM)g_setup.emulatorId, (LPARAM)0);
+ SendDlgItemMessageA(hwnd, IDC_VOLUMEMODEL, CB_SETCURSEL, (WPARAM)g_setup.volumeModel, (LPARAM)0);
+
+ sync4ops(hwnd);
+}
+
+static void buildLists(HWND hwnd)
+{
+ int i, bMax;
+ HMODULE lib;
+ const char *const* list;
+ BankNamesCount adl_getBanksCount;
+ BankNamesList adl_getBankNames;
+
+ lib = LoadLibraryW(L"adlmididrv.dll");
+ if(lib)
+ {
+ adl_getBanksCount = (BankNamesCount)GetProcAddress(lib, "adl_getBanksCount");
+ adl_getBankNames = (BankNamesList)GetProcAddress(lib, "adl_getBankNames");
+ if(adl_getBanksCount && adl_getBankNames)
+ {
+ bMax = adl_getBanksCount();
+ list = adl_getBankNames();
+ for(i = 0; i < bMax; i++)
+ {
+ SendDlgItemMessageA(hwnd, IDC_BANK_ID, CB_ADDSTRING, (LPARAM)0, (LPARAM)list[i]);
+ }
+ }
+ else
+ {
+ SendDlgItemMessageA(hwnd, IDC_BANK_ID, CB_ADDSTRING, (LPARAM)0, (LPARAM)"<Can't get calls>");
+ }
+ FreeLibrary(lib);
+ }
+ else
+ {
+ SendDlgItemMessageA(hwnd, IDC_BANK_ID, CB_ADDSTRING, (LPARAM)0, (LPARAM)"<Can't load library>");
+ }
+
+ // Volume models
+ for(i = 0; volume_models_descriptions[i] != NULL; ++i)
+ {
+ SendDlgItemMessageA(hwnd, IDC_VOLUMEMODEL, CB_ADDSTRING, (LPARAM)0, (LPARAM)volume_models_descriptions[i]);
+ }
+
+ // Emulators list
+ for(i = 0; emulator_type_descriptions[i] != NULL; ++i)
+ {
+ SendDlgItemMessageA(hwnd, IDC_EMULATOR, CB_ADDSTRING, (LPARAM)0, (LPARAM)emulator_type_descriptions[i]);
+ }
+}
+
+static void syncBankType(HWND hwnd, int type)
+{
+ EnableWindow(GetDlgItem(hwnd, IDC_BANK_ID), !type);
+ EnableWindow(GetDlgItem(hwnd, IDC_BROWSE_BANK), type);
+}
+
+static void updateBankName(HWND hwnd, const WCHAR *filePath)
+{
+ int i, len = wcslen(filePath);
+ const WCHAR *p = NULL;
+
+ for(i = 0; i < len; i++)
+ {
+ if(filePath[i] == L'\\' || filePath[i] == L'/')
+ p = filePath + i + 1;
+ }
+
+ if(p == NULL)
+ SendDlgItemMessage(hwnd, IDC_BANK_PATH, WM_SETTEXT, (WPARAM)NULL, (LPARAM)L"<none>");
+ else
+ SendDlgItemMessage(hwnd, IDC_BANK_PATH, WM_SETTEXT, (WPARAM)NULL, (LPARAM)p);
+}
+
+static void openCustomBank(HWND hwnd)
+{
+ OPENFILENAMEW ofn;
+ WCHAR szFile[MAX_PATH];
+
+ ZeroMemory(&ofn, sizeof(ofn));
+ ZeroMemory(szFile, sizeof(szFile));
+
+ wcsncpy(szFile, g_setup.bankPath, MAX_PATH);
+
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hInstance = s_hModule;
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFilter = L"WOPL bank file (*.wopl)\0*.WOPL\0All files (*.*)\0*.*\0";
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrTitle = L"Open external bank file";
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_READONLY | OFN_HIDEREADONLY | OFN_EXPLORER;
+
+ if(GetOpenFileNameW(&ofn) == TRUE)
+ {
+ ZeroMemory(g_setup.bankPath, sizeof(g_setup.bankPath));
+ wcsncpy(g_setup.bankPath, szFile, MAX_PATH);
+ updateBankName(hwnd, g_setup.bankPath);
+ }
+}
+
+INT_PTR CALLBACK ToolDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
+{
+ switch(Message)
+ {
+ case WM_INITDIALOG:
+ buildLists(hwnd);
+ syncWidget(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_ABOUT:
+ MessageBoxW(hwnd,
+ L"libADLMIDI - a software MIDI synthesizer with OPL3 FM synth,\n"
+ L"Made by Vitaly Novichkov \"Wohlstand\".\n\n"
+ L"Source code is here: https://github.com/Wohlstand/libADLMIDI",
+ L"About this driver",
+ MB_OK);
+ break;
+
+ case IDC_NUM_CHIPS:
+ if(HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ g_setup.numChips = 1 + SendMessageW((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
+ g_setup.num4ops = -1;
+ sync4ops(hwnd);
+ }
+ break;
+
+ case IDC_NUM_4OPVO:
+ if(HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ g_setup.num4ops = SendMessageW((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0) - 1;
+ }
+ break;
+
+ case IDC_EMULATOR:
+ if(HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ g_setup.emulatorId = SendMessageW((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
+ }
+ break;
+
+ case IDC_VOLUMEMODEL:
+ if(HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ g_setup.volumeModel = SendMessageW((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
+ }
+ break;
+
+ case IDC_BANK_INTERNAL:
+ g_setup.useExternalBank = 0;
+ syncBankType(hwnd, FALSE);
+ break;
+
+ case IDC_BANK_EXTERNAL:
+ g_setup.useExternalBank = 1;
+ syncBankType(hwnd, TRUE);
+ break;
+
+ case IDC_BANK_ID:
+ if(HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ g_setup.bankId = SendMessageW((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
+ }
+ break;
+
+ case IDC_BROWSE_BANK:
+ openCustomBank(hwnd);
+ break;
+
+ case IDC_FLAG_TREMOLO:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ g_setup.flagDeepTremolo = SendDlgItemMessage(hwnd, IDC_FLAG_TREMOLO, (UINT)BM_GETCHECK, 0, 0);
+ }
+ break;
+
+ case IDC_FLAG_VIBRATO:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ g_setup.flagDeepVibrato = SendDlgItemMessage(hwnd, IDC_FLAG_VIBRATO, (UINT)BM_GETCHECK, 0, 0);
+ }
+ break;
+
+ case IDC_FLAG_SOFTPAN:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ g_setup.flagSoftPanning = SendDlgItemMessage(hwnd, IDC_FLAG_SOFTPAN, (UINT)BM_GETCHECK, 0, 0);
+ }
+ break;
+
+ case IDC_FLAG_SCALE:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ g_setup.flagScaleModulators = SendDlgItemMessage(hwnd, IDC_FLAG_SCALE, (UINT)BM_GETCHECK, 0, 0);
+ }
+ break;
+
+ case IDC_FLAG_FULLBRIGHT:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ g_setup.flagFullBrightness = SendDlgItemMessage(hwnd, IDC_FLAG_FULLBRIGHT, (UINT)BM_GETCHECK, 0, 0);
+ }
+ break;
+
+
+ case IDC_RESTORE_DEFAULTS:
+ setupDefault(&g_setup);
+ syncWidget(hwnd);
+ break;
+
+ case IDC_RESET_SYNTH:
+ sendSignal(DRV_SIGNAL_RESET_SYNTH);
+ break;
+
+ case IDC_APPLYBUTTON:
+ saveSetup(&g_setup);
+ sendSignal(DRV_SIGNAL_RELOAD_SETUP);
+ break;
+
+ case IDOK:
+ saveSetup(&g_setup);
+ sendSignal(DRV_SIGNAL_RELOAD_SETUP);
+ EndDialog(hwnd, IDOK);
+ break;
+
+ case IDCANCEL:
+ EndDialog(hwnd, IDCANCEL);
+ break;
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+
+ UNREFERENCED_PARAMETER(lParam);
+}
+
+
+BOOL runAdlSetupBox(HINSTANCE hModule, HWND hwnd)
+{
+ s_hModule = hModule;
+
+ loadSetup(&g_setup);
+
+ DialogBoxW(hModule, MAKEINTRESOURCEW(IDD_CONFIG_BOX), hwnd, ToolDlgProc);
+
+ s_hModule = NULL;
+
+ return TRUE;
+}
+
+BOOL initAdlSetupBox(HINSTANCE hModule, HWND hwnd)
+{
+ InitCommonControls();
+ setupDefault(&g_setup);
+ UNREFERENCED_PARAMETER(hModule);
+ UNREFERENCED_PARAMETER(hwnd);
+ return TRUE;
+}
+
+BOOL cleanUpAdlSetupBox(HINSTANCE hModule, HWND hwnd)
+{
+ UNREFERENCED_PARAMETER(hModule);
+ UNREFERENCED_PARAMETER(hwnd);
+ return TRUE;
+}
diff --git a/utils/winmm_drv/cpl/config_dialog.h b/utils/winmm_drv/cpl/config_dialog.h
new file mode 100644
index 0000000..1cb45d6
--- /dev/null
+++ b/utils/winmm_drv/cpl/config_dialog.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#ifndef SETUP_DIALOG_HHHH
+#define SETUP_DIALOG_HHHH
+
+#include <windef.h>
+
+extern BOOL initAdlSetupBox(HINSTANCE hModule, HWND hwnd);
+extern BOOL runAdlSetupBox(HINSTANCE hModule, HWND hwnd);
+extern BOOL cleanUpAdlSetupBox(HINSTANCE hModule, HWND hwnd);
+
+#endif
diff --git a/utils/winmm_drv/cpl/opl3icon.ico b/utils/winmm_drv/cpl/opl3icon.ico
new file mode 100644
index 0000000..16c3d9a
--- /dev/null
+++ b/utils/winmm_drv/cpl/opl3icon.ico
Binary files differ
diff --git a/utils/winmm_drv/cpl/res-cpl.rc b/utils/winmm_drv/cpl/res-cpl.rc
new file mode 100644
index 0000000..c23b501
--- /dev/null
+++ b/utils/winmm_drv/cpl/res-cpl.rc
@@ -0,0 +1,3 @@
+#include <winresrc.h>
+
+123 RT_MANIFEST "adlconfig.manifest"
diff --git a/utils/winmm_drv/cpl/res-tool.rc b/utils/winmm_drv/cpl/res-tool.rc
new file mode 100644
index 0000000..22e6bfa
--- /dev/null
+++ b/utils/winmm_drv/cpl/res-tool.rc
@@ -0,0 +1,3 @@
+#include <winresrc.h>
+
+1 RT_MANIFEST "adlconfig.manifest"
diff --git a/utils/winmm_drv/cpl/resource.h b/utils/winmm_drv/cpl/resource.h
new file mode 100644
index 0000000..4d41e9f
--- /dev/null
+++ b/utils/winmm_drv/cpl/resource.h
@@ -0,0 +1,32 @@
+#ifndef IDC_STATIC
+#define IDC_STATIC (-1)
+#endif
+
+#define IDI_ICON1 104
+#define IDD_CONFIG_BOX 106
+#define IDC_APPLYBUTTON 1000
+#define IDC_VOLUMEMODEL 1001
+#define IDC_VM_LABEL 1002
+#define IDC_BANK_ID 1003
+#define IDC_INS_BANK 1004
+#define IDC_BANK_INTERNAL 1005
+#define IDC_BANK_EXTERNAL 1006
+#define IDC_BANK_PATH 1007
+#define IDC_BROWSE_BANK 1008
+#define IDC_RESTORE_DEFAULTS 1010
+#define IDC_FLAG_TREMOLO 1011
+#define IDC_FLAG_VIBRATO 1012
+#define IDC_FLAG_SOFTPAN 1013
+#define IDC_EMULATOR 1014
+#define IDC_NUM_CHIPS 1018
+#define IDC_NUM_4OPVO 1019
+#define IDC_FLAG_SCALE 1020
+#define IDC_FLAG_FULLBRIGHT 1021
+#define IDC_ABOUT 1022
+#define IDC_CHIPEMU_LABEL 1023
+#define IDC_CHIPNUM_LABEL 1024
+#define IDC_4OPSNUM_LABEL 1025
+#define IDC_RESET_SYNTH 1026
+#define IDC_BOTTOMNOTE_LABEL 1027
+#define IDC_DRIVERNAME 40000
+#define IDC_DRIVERDESC 40001
diff --git a/utils/winmm_drv/cpl/targetver.h b/utils/winmm_drv/cpl/targetver.h
new file mode 100644
index 0000000..203dfbc
--- /dev/null
+++ b/utils/winmm_drv/cpl/targetver.h
@@ -0,0 +1,24 @@
+#pragma once
+
+// The following macros define the minimum required platform. The minimum required platform
+// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
+// your application. The macros work by enabling all features available on platform versions up to and
+// including the version specified.
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef WINVER // Specifies that the minimum required platform is Windows Vista.
+#define WINVER 0x0600 // Change this to the appropriate value to target other versions of Windows.
+#endif
+
+#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
+#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
+#endif
+
+#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98.
+#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
+#endif
+
+#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 7.0.
+#define _WIN32_IE 0x0700 // Change this to the appropriate value to target other versions of IE.
+#endif
diff --git a/utils/winmm_drv/installer/drvinst.c b/utils/winmm_drv/installer/drvinst.c
new file mode 100644
index 0000000..cbba8f6
--- /dev/null
+++ b/utils/winmm_drv/installer/drvinst.c
@@ -0,0 +1,273 @@
+/* Copyright (C) 2011, 2012 Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+
+const char OPL3EMU_DRIVER_NAME[] = "adlmididrv.dll";
+const char OPL3EMU_CPLAPPLET_NAME[] = "libadlconfig.cpl";
+const char OPL3EMU_SETUPTOOL_NAME[] = "adlmidiconfigtool.exe";
+
+const char WDM_DRIVER_NAME[] = "wdmaud.drv";
+const char SYSTEM_DIR_NAME[] = "SYSTEM32";
+const char SYSTEM_ROOT_ENV_NAME[] = "SYSTEMROOT";
+const char INSTALL_COMMAND[] = "install";
+const char UNINSTALL_COMMAND[] = "uninstall";
+const char DRIVERS_REGISTRY_KEY[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32";
+const char PATH_SEPARATOR[] = "\\";
+
+const char SUCCESSFULLY_INSTALLED_MSG[] = "libADLMIDI synth MIDI Driver successfully installed";
+const char SUCCESSFULLY_UPDATED_MSG[] = "libADLMIDI synth MIDI Driver successfully updated";
+const char SUCCESSFULLY_UNINSTALLED_MSG[] = "libADLMIDI synth MIDI Driver successfully uninstalled";
+const char USAGE_MSG[] = "Usage:\n drvsetup install - install driver\n drvsetup uninstall - uninstall driver";
+
+const char CANNOT_OPEN_REGISTRY_ERR[] = "Cannot open registry key";
+const char CANNOT_INSTALL_NO_PORTS_ERR[] = "Cannot install libADLMIDI synth MIDI driver:\n There is no MIDI ports available";
+const char CANNOT_REGISTER_ERR[] = "Cannot register driver";
+const char CANNOT_UNINSTALL_ERR[] = "Cannot uninstall libADLMIDI synth MIDI driver";
+const char CANNOT_UNINSTALL_NOT_FOUND_ERR[] = "Cannot uninstall libADLMIDI synth MIDI driver:\n There is no driver registry entry found";
+const char CANNOT_INSTALL_PATH_TOO_LONG_ERR[] = "libADLMIDI synth MIDI Driver cannot be installed:\n Installation path is too long";
+const char CANNOT_INSTALL_FILE_COPY_ERR[] = "libADLMIDI synth MIDI Driver failed to install:\n File copying error";
+
+const char INFORMATION_TITLE[] = "Information";
+const char ERROR_TITLE[] = "Error";
+const char REGISTRY_ERROR_TITLE[] = "Registry error";
+const char FILE_ERROR_TITLE[] = "File error";
+
+BOOL registerDriver(BOOL *installMode)
+{
+ char str[255];
+ char drvName[] = "midi0";
+ DWORD len, res;
+ BOOL wdmEntryFound = FALSE;
+ int freeEntry = -1;
+ HKEY hReg;
+ int i;
+
+ if(RegOpenKeyA(HKEY_LOCAL_MACHINE, DRIVERS_REGISTRY_KEY, &hReg))
+ {
+ MessageBoxA(NULL, CANNOT_OPEN_REGISTRY_ERR, REGISTRY_ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ return FALSE;
+ }
+
+ for(i = 0; i < 10; i++)
+ {
+ len = 255;
+ if(i)
+ drvName[4] = '0' + i;
+ else
+ drvName[4] = 0;
+ res = RegQueryValueExA(hReg, drvName, NULL, NULL, (LPBYTE)str, &len);
+ if(res != ERROR_SUCCESS)
+ {
+ if((freeEntry == -1) && (res == ERROR_FILE_NOT_FOUND))
+ freeEntry = i;
+ continue;
+ }
+ if(!_stricmp(str, OPL3EMU_DRIVER_NAME))
+ {
+ RegCloseKey(hReg);
+ *installMode = FALSE;
+ return TRUE;
+ }
+ if(freeEntry != -1) continue;
+ if(strlen(str) == 0)
+ freeEntry = i;
+ else if(!_stricmp(str, WDM_DRIVER_NAME))
+ {
+ // Considering multiple WDM entries are just garbage, though one entry shouldn't be modified
+ if(wdmEntryFound)
+ freeEntry = i;
+ else
+ wdmEntryFound = TRUE;
+ }
+ }
+
+ if(freeEntry == -1)
+ {
+ MessageBoxA(NULL, CANNOT_INSTALL_NO_PORTS_ERR, ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ RegCloseKey(hReg);
+ return FALSE;
+ }
+
+ if(freeEntry)
+ drvName[4] = '0' + freeEntry;
+ else
+ drvName[4] = 0;
+
+ res = RegSetValueExA(hReg, drvName, 0, REG_SZ, (LPBYTE)OPL3EMU_DRIVER_NAME, sizeof(OPL3EMU_DRIVER_NAME));
+ if(res != ERROR_SUCCESS)
+ {
+ MessageBoxA(NULL, CANNOT_REGISTER_ERR, REGISTRY_ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ RegCloseKey(hReg);
+ return FALSE;
+ }
+
+ RegCloseKey(hReg);
+
+ return TRUE;
+}
+
+void unregisterDriver()
+{
+ char str[255];
+ char drvName[] = "midi0";
+ DWORD len, res;
+ HKEY hReg;
+ int i;
+
+ if(RegOpenKeyA(HKEY_LOCAL_MACHINE, DRIVERS_REGISTRY_KEY, &hReg))
+ {
+ MessageBoxA(NULL, CANNOT_OPEN_REGISTRY_ERR, REGISTRY_ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ return;
+ }
+
+ for(i = 0; i < 10; i++)
+ {
+ len = 255;
+ if(i)
+ drvName[4] = '0' + i;
+ else
+ drvName[4] = 0;
+ res = RegQueryValueExA(hReg, drvName, NULL, NULL, (LPBYTE)str, &len);
+ if(res != ERROR_SUCCESS)
+ continue;
+ if(!_stricmp(str, OPL3EMU_DRIVER_NAME))
+ {
+ res = RegDeleteValueA(hReg, drvName);
+ if(res != ERROR_SUCCESS)
+ {
+ MessageBoxA(NULL, CANNOT_UNINSTALL_ERR, REGISTRY_ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ RegCloseKey(hReg);
+ return;
+ }
+ MessageBoxA(NULL, SUCCESSFULLY_UNINSTALLED_MSG, INFORMATION_TITLE, MB_OK | MB_ICONINFORMATION);
+ RegCloseKey(hReg);
+ return;
+ }
+ }
+
+ MessageBoxA(NULL, CANNOT_UNINSTALL_NOT_FOUND_ERR, ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ RegCloseKey(hReg);
+}
+
+void constructSystemDirName(char *pathName)
+{
+ char sysRoot[MAX_PATH + 1];
+ GetEnvironmentVariableA(SYSTEM_ROOT_ENV_NAME, sysRoot, MAX_PATH);
+ strncpy(pathName, sysRoot, MAX_PATH + 1);
+ strncat(pathName, PATH_SEPARATOR, MAX_PATH - strlen(pathName));
+ strncat(pathName, SYSTEM_DIR_NAME, MAX_PATH - strlen(pathName));
+ strncat(pathName, PATH_SEPARATOR, MAX_PATH - strlen(pathName));
+}
+
+void constructDriverPathName(char *pathName, const char*fileName)
+{
+ constructSystemDirName(pathName);
+ strncat(pathName, fileName, MAX_PATH - strlen(pathName));
+}
+
+void deleteFileReliably(char *pathName, const char *fileName)
+{
+ const size_t nameSize = strlen(fileName) + 1;
+ char tmpFilePrefix[MAX_PATH + 1];
+ char tmpDirName[MAX_PATH + 1];
+ char tmpPathName[MAX_PATH + 1];
+
+ if(DeleteFileA(pathName))
+ return;
+ // File doesn't exist, nothing to do
+ if(ERROR_FILE_NOT_FOUND == GetLastError())
+ return;
+
+ // File can't be deleted, rename it and register pending deletion
+ strncpy(tmpFilePrefix, fileName, nameSize);
+ strncat(tmpFilePrefix, ".", nameSize);
+ constructSystemDirName(tmpDirName);
+ GetTempFileNameA(tmpDirName, tmpFilePrefix, 0, tmpPathName);
+ DeleteFileA(tmpPathName);
+ MoveFileA(pathName, tmpPathName);
+ MoveFileExA(tmpPathName, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
+}
+
+BOOL copyFileIntoSystem(const char *fileName, char **argv)
+{
+ char driverPathName[MAX_PATH + 1];
+ char setupPathName[MAX_PATH + 1];
+ int setupPathLen;
+
+ setupPathLen = strrchr(argv[0], '\\') - argv[0];
+ if(setupPathLen > (int)(MAX_PATH - strlen(fileName) + 1 - 2))
+ {
+ MessageBoxA(NULL, CANNOT_INSTALL_PATH_TOO_LONG_ERR, ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ return 2;
+ }
+
+ constructDriverPathName(driverPathName, fileName);
+ deleteFileReliably(driverPathName, fileName);
+
+ strncpy(setupPathName, argv[0], setupPathLen);
+ setupPathName[setupPathLen] = 0;
+ strncat(setupPathName, PATH_SEPARATOR, MAX_PATH - strlen(setupPathName));
+ strncat(setupPathName, fileName, MAX_PATH - strlen(setupPathName));
+
+ if(!CopyFileA(setupPathName, driverPathName, FALSE))
+ {
+ MessageBoxA(NULL, CANNOT_INSTALL_FILE_COPY_ERR, FILE_ERROR_TITLE, MB_OK | MB_ICONEXCLAMATION);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+ char pathName[MAX_PATH + 1];
+ BOOL installMode;
+
+ if(argc != 2 || (_stricmp(INSTALL_COMMAND, argv[1]) != 0 && _stricmp(UNINSTALL_COMMAND, argv[1]) != 0))
+ {
+ MessageBoxA(NULL, USAGE_MSG, INFORMATION_TITLE, MB_OK | MB_ICONINFORMATION);
+ return 1;
+ }
+
+ if(_stricmp(UNINSTALL_COMMAND, argv[1]) == 0)
+ {
+ constructDriverPathName(pathName, OPL3EMU_DRIVER_NAME);
+ deleteFileReliably(pathName, OPL3EMU_DRIVER_NAME);
+ constructDriverPathName(pathName, OPL3EMU_CPLAPPLET_NAME);
+ deleteFileReliably(pathName, OPL3EMU_CPLAPPLET_NAME);
+ constructDriverPathName(pathName, OPL3EMU_SETUPTOOL_NAME);
+ deleteFileReliably(pathName, OPL3EMU_SETUPTOOL_NAME);
+
+ unregisterDriver();
+ return 0;
+ }
+
+ installMode = TRUE;
+ if(!registerDriver(&installMode))
+ return 3;
+
+ if(!copyFileIntoSystem(OPL3EMU_DRIVER_NAME, argv))
+ return 4;
+ if(!copyFileIntoSystem(OPL3EMU_CPLAPPLET_NAME, argv))
+ return 4;
+ if(!copyFileIntoSystem(OPL3EMU_SETUPTOOL_NAME, argv))
+ return 4;
+
+ MessageBoxA(NULL, installMode ? SUCCESSFULLY_INSTALLED_MSG : SUCCESSFULLY_UPDATED_MSG, INFORMATION_TITLE, MB_OK | MB_ICONINFORMATION);
+ return 0;
+}
diff --git a/utils/winmm_drv/installer/drvinst.exe.manifest b/utils/winmm_drv/installer/drvinst.exe.manifest
new file mode 100644
index 0000000..5aba825
--- /dev/null
+++ b/utils/winmm_drv/installer/drvinst.exe.manifest
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <description>libADLMIDI synth Driver Installer</description>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ </application>
+ </compatibility>
+</assembly> \ No newline at end of file
diff --git a/utils/winmm_drv/installer/drvinst.rc b/utils/winmm_drv/installer/drvinst.rc
new file mode 100644
index 0000000..ae8ce72
--- /dev/null
+++ b/utils/winmm_drv/installer/drvinst.rc
@@ -0,0 +1,6 @@
+#include "winuser.h"
+
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+#define RT_MANIFEST 24
+
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "drvinst.exe.manifest"
diff --git a/utils/winmm_drv/installer/install.bat b/utils/winmm_drv/installer/install.bat
new file mode 100644
index 0000000..3daf94d
--- /dev/null
+++ b/utils/winmm_drv/installer/install.bat
@@ -0,0 +1 @@
+.\drvsetup install \ No newline at end of file
diff --git a/utils/winmm_drv/installer/uninstall.bat b/utils/winmm_drv/installer/uninstall.bat
new file mode 100644
index 0000000..537a8e8
--- /dev/null
+++ b/utils/winmm_drv/installer/uninstall.bat
@@ -0,0 +1 @@
+.\drvsetup uninstall \ No newline at end of file
diff --git a/utils/winmm_drv/src/MidiSynth.cpp b/utils/winmm_drv/src/MidiSynth.cpp
new file mode 100644
index 0000000..29cbb78
--- /dev/null
+++ b/utils/winmm_drv/src/MidiSynth.cpp
@@ -0,0 +1,720 @@
+/* Copyright (C) 2011, 2012 Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+
+namespace OPL3Emu
+{
+
+#define DRIVER_MODE
+
+static MidiSynth &midiSynth = MidiSynth::getInstance();
+
+static class MidiStream
+{
+private:
+ static const unsigned int maxPos = 1024;
+ unsigned int startpos;
+ unsigned int endpos;
+ DWORD stream[maxPos][2];
+
+public:
+ MidiStream()
+ {
+ startpos = 0;
+ endpos = 0;
+ }
+
+ DWORD PutMessage(DWORD msg, DWORD timestamp)
+ {
+ unsigned int newEndpos = endpos;
+
+ newEndpos++;
+ if(newEndpos == maxPos) // check for buffer rolloff
+ newEndpos = 0;
+ if(startpos == newEndpos) // check for buffer full
+ return -1;
+ stream[endpos][0] = msg; // ok to put data and update endpos
+ stream[endpos][1] = timestamp;
+ endpos = newEndpos;
+ return 0;
+ }
+
+ DWORD GetMidiMessage()
+ {
+ if(startpos == endpos) // check for buffer empty
+ return -1;
+ DWORD msg = stream[startpos][0];
+ startpos++;
+ if(startpos == maxPos) // check for buffer rolloff
+ startpos = 0;
+ return msg;
+ }
+
+ DWORD PeekMessageTime()
+ {
+ if(startpos == endpos) // check for buffer empty
+ return (DWORD) -1;
+ return stream[startpos][1];
+ }
+
+ DWORD PeekMessageTimeAt(unsigned int pos)
+ {
+ if(startpos == endpos) // check for buffer empty
+ return -1;
+ unsigned int peekPos = (startpos + pos) % maxPos;
+ return stream[peekPos][1];
+ }
+
+ void Clean()
+ {
+ startpos = 0;
+ endpos = 0;
+ memset(stream, 0, sizeof(stream));
+ }
+
+} midiStream;
+
+static class SynthEventWin32
+{
+private:
+ HANDLE hEvent;
+
+public:
+ int Init()
+ {
+ hEvent = CreateEvent(NULL, false, true, NULL);
+ if(hEvent == NULL)
+ {
+ MessageBoxW(NULL, L"Can't create sync object", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 1;
+ }
+ return 0;
+ }
+
+ void Close()
+ {
+ CloseHandle(hEvent);
+ }
+
+ void Wait()
+ {
+ WaitForSingleObject(hEvent, INFINITE);
+ }
+
+ void Release()
+ {
+ SetEvent(hEvent);
+ }
+} synthEvent;
+
+static class WaveOutWin32
+{
+private:
+ HWAVEOUT hWaveOut;
+ WAVEHDR *WaveHdr;
+ HANDLE hEvent;
+ DWORD chunks;
+ DWORD prevPlayPos;
+ DWORD getPosWraps;
+ bool stopProcessing;
+
+public:
+ int Init(Bit16s *buffer, unsigned int bufferSize, unsigned int chunkSize, bool useRingBuffer, unsigned int sampleRate)
+ {
+ DWORD callbackType = CALLBACK_NULL;
+ DWORD_PTR callback = (DWORD_PTR)NULL;
+ hEvent = NULL;
+ if(!useRingBuffer)
+ {
+ hEvent = CreateEvent(NULL, false, true, NULL);
+ callback = (DWORD_PTR)hEvent;
+ callbackType = CALLBACK_EVENT;
+ }
+
+ PCMWAVEFORMAT wFormat = {WAVE_FORMAT_PCM, 2, sampleRate, sampleRate * 4, 4, 16};
+
+ // Open waveout device
+ int wResult = waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMATEX)&wFormat, callback, (DWORD_PTR)&midiSynth, callbackType);
+ if(wResult != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to open waveform output device", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 2;
+ }
+
+ // Prepare headers
+ chunks = useRingBuffer ? 1 : bufferSize / chunkSize;
+ WaveHdr = new WAVEHDR[chunks];
+ LPSTR chunkStart = (LPSTR)buffer;
+ DWORD chunkBytes = 4 * chunkSize;
+ for(UINT i = 0; i < chunks; i++)
+ {
+ if(useRingBuffer)
+ {
+ WaveHdr[i].dwBufferLength = 4 * bufferSize;
+ WaveHdr[i].lpData = chunkStart;
+ WaveHdr[i].dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
+ WaveHdr[i].dwLoops = -1L;
+ }
+ else
+ {
+ WaveHdr[i].dwBufferLength = chunkBytes;
+ WaveHdr[i].lpData = chunkStart;
+ WaveHdr[i].dwFlags = 0L;
+ WaveHdr[i].dwLoops = 0L;
+ chunkStart += chunkBytes;
+ }
+ wResult = waveOutPrepareHeader(hWaveOut, &WaveHdr[i], sizeof(WAVEHDR));
+ if(wResult != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to Prepare Header", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 3;
+ }
+ }
+ stopProcessing = false;
+ return 0;
+ }
+
+ int Close()
+ {
+ stopProcessing = true;
+ SetEvent(hEvent);
+ int wResult = waveOutReset(hWaveOut);
+ if(wResult != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to Reset WaveOut", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 8;
+ }
+
+ for(UINT i = 0; i < chunks; i++)
+ {
+ wResult = waveOutUnprepareHeader(hWaveOut, &WaveHdr[i], sizeof(WAVEHDR));
+ if(wResult != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to Unprepare Wave Header", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 8;
+ }
+ }
+ delete[] WaveHdr;
+ WaveHdr = NULL;
+
+ wResult = waveOutClose(hWaveOut);
+ if(wResult != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to Close WaveOut", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 8;
+ }
+ if(hEvent != NULL)
+ {
+ CloseHandle(hEvent);
+ hEvent = NULL;
+ }
+ return 0;
+ }
+
+ int Start()
+ {
+ getPosWraps = 0;
+ prevPlayPos = 0;
+ for(UINT i = 0; i < chunks; i++)
+ {
+ if(waveOutWrite(hWaveOut, &WaveHdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to write block to device", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 4;
+ }
+ }
+ _beginthread(RenderingThread, 16384, this);
+ return 0;
+ }
+
+ int Pause()
+ {
+ if(waveOutPause(hWaveOut) != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to Pause wave playback", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 9;
+ }
+ return 0;
+ }
+
+ int Resume()
+ {
+ if(waveOutRestart(hWaveOut) != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to Resume wave playback", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 9;
+ }
+ return 0;
+ }
+
+ UINT64 GetPos()
+ {
+ MMTIME mmTime;
+ mmTime.wType = TIME_SAMPLES;
+
+ if(waveOutGetPosition(hWaveOut, &mmTime, sizeof(MMTIME)) != MMSYSERR_NOERROR)
+ {
+ MessageBoxW(NULL, L"Failed to get current playback position", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 10;
+ }
+
+ if(mmTime.wType != TIME_SAMPLES)
+ {
+ MessageBoxW(NULL, L"Failed to get # of samples played", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 10;
+ }
+
+ // Deal with waveOutGetPosition() wraparound. For 16-bit stereo output, it equals 2^27,
+ // presumably caused by the internal 32-bit counter of bits played.
+ // The output of that nasty waveOutGetPosition() isn't monotonically increasing
+ // even during 2^27 samples playback, so we have to ensure the difference is big enough...
+ int delta = mmTime.u.sample - prevPlayPos;
+ if(delta < -(1 << 26))
+ {
+ std::cout << "OPL3: GetPos() wrap: " << delta << "\n";
+ ++getPosWraps;
+ }
+ prevPlayPos = mmTime.u.sample;
+ return mmTime.u.sample + getPosWraps * (1 << 27);
+ }
+
+ static void RenderingThread(void *);
+} s_waveOut;
+
+void WaveOutWin32::RenderingThread(void *)
+{
+ if(s_waveOut.chunks == 1)
+ {
+ // Rendering using single looped ring buffer
+ while(!s_waveOut.stopProcessing)
+ midiSynth.RenderAvailableSpace();
+ }
+ else
+ {
+ while(!s_waveOut.stopProcessing)
+ {
+ bool allBuffersRendered = true;
+ for(UINT i = 0; i < s_waveOut.chunks; i++)
+ {
+ if(s_waveOut.WaveHdr[i].dwFlags & WHDR_DONE)
+ {
+ allBuffersRendered = false;
+ midiSynth.Render((Bit16s *)s_waveOut.WaveHdr[i].lpData, s_waveOut.WaveHdr[i].dwBufferLength / 4);
+ if(waveOutWrite(s_waveOut.hWaveOut, &s_waveOut.WaveHdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
+ MessageBoxW(NULL, L"Failed to write block to device", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ midiSynth.CheckForSignals();
+ }
+ }
+ if(allBuffersRendered)
+ WaitForSingleObject(s_waveOut.hEvent, INFINITE);
+ }
+ }
+}
+
+
+MidiSynth::MidiSynth() :
+ synth(NULL)
+{
+ m_setupInit = false;
+ setupDefault(&m_setup);
+ loadSetup();
+ ::openSignalListener();
+}
+
+MidiSynth::~MidiSynth()
+{
+ if(synth)
+ adl_close(synth);
+ synth = NULL;
+ ::closeSignalListener();
+}
+
+MidiSynth &MidiSynth::getInstance()
+{
+ static MidiSynth *instance = new MidiSynth;
+ return *instance;
+}
+
+// Renders all the available space in the single looped ring buffer
+void MidiSynth::RenderAvailableSpace()
+{
+ DWORD playPos = s_waveOut.GetPos() % bufferSize;
+ DWORD framesToRender;
+
+ if(playPos < framesRendered)
+ {
+ // Buffer wrap, render 'till the end of the buffer
+ framesToRender = bufferSize - framesRendered;
+ }
+ else
+ {
+ framesToRender = playPos - framesRendered;
+ if(framesToRender < chunkSize)
+ {
+ Sleep(1 + (chunkSize - framesToRender) * 1000 / sampleRate);
+ return;
+ }
+ }
+ midiSynth.Render(buffer + 2 * framesRendered, framesToRender);
+}
+
+// Renders totalFrames frames starting from bufpos
+// The number of frames rendered is added to the global counter framesRendered
+void MidiSynth::Render(Bit16s *bufpos, DWORD totalFrames)
+{
+ while(totalFrames > 0)
+ {
+ DWORD timeStamp;
+ // Incoming MIDI messages timestamped with the current audio playback position + midiLatency
+ while((timeStamp = midiStream.PeekMessageTime()) == framesRendered)
+ {
+ DWORD msg = midiStream.GetMidiMessage();
+
+ synthEvent.Wait();
+
+ Bit8u event = msg & 0xFF;
+ Bit8u channel = msg & 0x0F;
+ Bit8u p1 = (msg >> 8) & 0x7f;
+ Bit8u p2 = (msg >> 16) & 0x7f;
+
+ event &= 0xF0;
+
+ if(event == 0xF0)
+ {
+ switch (channel)
+ {
+ case 0xF:
+ adl_reset(synth);
+ break;
+ }
+ }
+
+ switch(event & 0xF0)
+ {
+ case 0x80:
+ adl_rt_noteOff(synth, channel, p1);
+ break;
+ case 0x90:
+ adl_rt_noteOn(synth, channel, p1, p2);
+ break;
+ case 0xA0:
+ adl_rt_noteAfterTouch(synth, channel, p1, p2);
+ break;
+ case 0xB0:
+ adl_rt_controllerChange(synth, channel, p1, p2);
+ break;
+ case 0xC0:
+ adl_rt_patchChange(synth, channel, p1);
+ break;
+ case 0xD0:
+ adl_rt_channelAfterTouch(synth, channel, p1);
+ break;
+ case 0xE0:
+ adl_rt_pitchBendML(synth, channel, p2, p1);
+ break;
+ }
+
+ synthEvent.Release();
+ }
+
+ // Find out how many frames to render. The value of timeStamp == -1 indicates the MIDI buffer is empty
+ DWORD framesToRender = timeStamp - framesRendered;
+ if(framesToRender > totalFrames)
+ {
+ // MIDI message is too far - render the rest of frames
+ framesToRender = totalFrames;
+ }
+
+ synthEvent.Wait();
+ adl_generate(synth, framesToRender * 2, bufpos);
+ synthEvent.Release();
+ framesRendered += framesToRender;
+ bufpos += 2 * framesToRender; // each frame consists of two samples for both the Left and Right channels
+ totalFrames -= framesToRender;
+ }
+
+ // Wrap framesRendered counter
+ if(framesRendered >= bufferSize)
+ framesRendered -= bufferSize;
+}
+
+void MidiSynth::CheckForSignals()
+{
+ int cmd = ::hasReloadSetupSignal();
+
+ if(cmd == 0)
+ return;
+
+ switch(cmd)
+ {
+ case 1: // Reload settings on the fly
+ this->loadSetup();
+ LoadSynthSetup();
+ break;
+
+ case 2:
+ adl_reset(synth);
+ break;
+
+ default:
+ break;
+ }
+
+ if(cmd > 0)
+ ::resetSignal();
+}
+
+unsigned int MidiSynth::MillisToFrames(unsigned int millis)
+{
+ return UINT(sampleRate * millis / 1000.f);
+}
+
+void MidiSynth::LoadSettings()
+{
+ sampleRate = 49716;
+ bufferSize = MillisToFrames(100);
+ chunkSize = MillisToFrames(10);
+ midiLatency = MillisToFrames(0);
+ useRingBuffer = false;
+ if(!useRingBuffer)
+ {
+ // Number of chunks should be ceil(bufferSize / chunkSize)
+ DWORD chunks = (bufferSize + chunkSize - 1) / chunkSize;
+ // Refine bufferSize as chunkSize * number of chunks, no less then the specified value
+ bufferSize = chunks * chunkSize;
+ }
+}
+
+int MidiSynth::Init()
+{
+ LoadSettings();
+ buffer = new Bit16s[2 * bufferSize]; // each frame consists of two samples for both the Left and Right channels
+
+ // Init synth
+ if(synthEvent.Init())
+ return 1;
+
+ synth = adl_init(49716);
+ if(!synth)
+ {
+ MessageBoxW(NULL, L"Can't open Synth", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 1;
+ }
+
+ m_setupInit = false;
+ LoadSynthSetup();
+
+ UINT wResult = s_waveOut.Init(buffer, bufferSize, chunkSize, useRingBuffer, sampleRate);
+ if(wResult) return wResult;
+
+ // Start playing stream
+ adl_generate(synth, bufferSize * 2, buffer);
+ framesRendered = 0;
+
+ wResult = s_waveOut.Start();
+ return wResult;
+}
+
+int MidiSynth::Reset()
+{
+#ifdef DRIVER_MODE
+ return 0;
+#endif
+
+ UINT wResult = s_waveOut.Pause();
+ if(wResult) return wResult;
+
+ synthEvent.Wait();
+
+ if(synth)
+ adl_close(synth);
+
+ synth = adl_init(49716);
+ if(!synth)
+ {
+ MessageBoxW(NULL, L"Can't open Synth", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ return 1;
+ }
+
+ m_setupInit = false;
+ LoadSynthSetup();
+
+ synthEvent.Release();
+
+ wResult = s_waveOut.Resume();
+ return wResult;
+}
+
+void MidiSynth::ResetSynth()
+{
+ synthEvent.Wait();
+ adl_reset(synth);
+ midiStream.Clean();
+ synthEvent.Release();
+}
+
+void MidiSynth::PanicSynth()
+{
+ synthEvent.Wait();
+ adl_panic(synth);
+ synthEvent.Release();
+}
+
+void MidiSynth::PushMIDI(DWORD msg)
+{
+ midiStream.PutMessage(msg, (s_waveOut.GetPos() + midiLatency) % bufferSize);
+}
+
+void MidiSynth::PlaySysex(Bit8u *bufpos, DWORD len)
+{
+ synthEvent.Wait();
+ adl_rt_systemExclusive(synth, bufpos, len);
+ synthEvent.Release();
+}
+
+void MidiSynth::loadSetup()
+{
+ ::loadSetup(&m_setup);
+}
+
+void MidiSynth::LoadSynthSetup()
+{
+ if(!m_setupInit || m_setupCurrent.emulatorId != m_setup.emulatorId)
+ {
+ adl_switchEmulator(synth, m_setup.emulatorId);
+ m_setupCurrent.emulatorId = m_setup.emulatorId;
+ }
+
+ if(!m_setupInit || m_setupCurrent.numChips != m_setup.numChips)
+ {
+ adl_setNumChips(synth, m_setup.numChips);
+ m_setupCurrent.numChips = m_setup.numChips;
+ }
+
+ if(!m_setupInit || m_setupCurrent.flagDeepTremolo != m_setup.flagDeepTremolo)
+ {
+ switch(m_setup.flagDeepTremolo)
+ {
+ case BST_INDETERMINATE:
+ adl_setHTremolo(synth, -1);
+ break;
+ case BST_CHECKED:
+ adl_setHTremolo(synth, 1);
+ break;
+ case BST_UNCHECKED:
+ adl_setHTremolo(synth, 0);
+ break;
+ }
+ m_setupCurrent.flagDeepTremolo = m_setup.flagDeepTremolo;
+ }
+
+ if(!m_setupInit || m_setupCurrent.flagDeepVibrato != m_setup.flagDeepVibrato)
+ {
+ switch(m_setup.flagDeepVibrato)
+ {
+ case BST_INDETERMINATE:
+ adl_setHVibrato(synth, -1);
+ break;
+ case BST_CHECKED:
+ adl_setHVibrato(synth, 1);
+ break;
+ case BST_UNCHECKED:
+ adl_setHVibrato(synth, 0);
+ break;
+ }
+ m_setupCurrent.flagDeepVibrato = m_setup.flagDeepVibrato;
+ }
+
+ if(!m_setupInit || m_setupCurrent.flagSoftPanning != m_setup.flagSoftPanning)
+ {
+ adl_setSoftPanEnabled(synth, m_setup.flagSoftPanning);
+ m_setupCurrent.flagSoftPanning = m_setup.flagSoftPanning;
+ }
+
+
+ if(!m_setupInit || m_setupCurrent.flagScaleModulators != m_setup.flagScaleModulators)
+ {
+ adl_setScaleModulators(synth, m_setup.flagScaleModulators);
+ m_setupCurrent.flagScaleModulators = m_setup.flagScaleModulators;
+ }
+
+ if(!m_setupInit || m_setupCurrent.flagFullBrightness != m_setup.flagFullBrightness)
+ {
+ adl_setFullRangeBrightness(synth, m_setup.flagFullBrightness);
+ m_setupCurrent.flagFullBrightness = m_setup.flagFullBrightness;
+ }
+
+ if(!m_setupInit || m_setupCurrent.volumeModel != m_setup.volumeModel)
+ {
+ adl_setVolumeRangeModel(synth, m_setup.volumeModel);
+ m_setupCurrent.volumeModel = m_setup.volumeModel;
+ }
+
+ if(!m_setupInit || m_setupCurrent.numChips != m_setup.numChips)
+ {
+ adl_setNumChips(synth, m_setup.numChips);
+ m_setupCurrent.numChips = m_setup.numChips;
+ }
+
+ if(!m_setupInit || m_setupCurrent.num4ops != m_setup.num4ops)
+ {
+ adl_setNumFourOpsChn(synth, m_setup.num4ops);
+ m_setupCurrent.num4ops = m_setup.num4ops;
+ }
+
+ if(!m_setupInit ||
+ m_setupCurrent.useExternalBank != m_setup.useExternalBank ||
+ m_setupCurrent.bankId != m_setup.bankId ||
+ wcscmp(m_setupCurrent.bankPath, m_setup.bankPath) != 0
+ )
+ {
+ if(m_setup.useExternalBank)
+ {
+ char pathUtf8[MAX_PATH * 4];
+ ZeroMemory(pathUtf8, MAX_PATH * 4);
+ int len = WideCharToMultiByte(CP_UTF8, 0, m_setup.bankPath, wcslen(m_setup.bankPath), pathUtf8, MAX_PATH * 4, 0, 0);
+ pathUtf8[len] = '\0';
+ adl_openBankFile(synth, pathUtf8);
+ }
+ else
+ adl_setBank(synth, m_setup.bankId);
+
+ m_setupCurrent.useExternalBank = m_setup.useExternalBank;
+ m_setupCurrent.bankId = m_setup.bankId;
+ wcscpy(m_setupCurrent.bankPath, m_setup.bankPath);
+ }
+
+ m_setupInit = true;
+}
+
+void MidiSynth::Close()
+{
+ s_waveOut.Pause();
+ s_waveOut.Close();
+ synthEvent.Wait();
+ //synth->close();
+
+ // Cleanup memory
+ if(synth)
+ adl_close(synth);
+ synth = NULL;
+ delete buffer;
+
+ synthEvent.Close();
+}
+
+}
diff --git a/utils/winmm_drv/src/MidiSynth.h b/utils/winmm_drv/src/MidiSynth.h
new file mode 100644
index 0000000..bf2da02
--- /dev/null
+++ b/utils/winmm_drv/src/MidiSynth.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2011, 2012 Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "stdafx.h"
+#include "regconfig.h"
+
+#ifndef OPL3EMU_MIDISYNTH_H
+#define OPL3EMU_MIDISYNTH_H
+
+typedef unsigned char Bit8u;
+typedef signed char Bit8s;
+typedef unsigned short Bit16u;
+typedef signed short Bit16s;
+typedef unsigned long Bit32u;
+typedef signed long Bit32s;
+typedef unsigned __int64 Bit64u;
+typedef signed __int64 Bit64s;
+typedef unsigned int Bitu;
+typedef signed int Bits;
+
+namespace OPL3Emu
+{
+
+class MidiSynth
+{
+private:
+ unsigned int sampleRate;
+ unsigned int midiLatency;
+ unsigned int bufferSize;
+ unsigned int chunkSize;
+ bool useRingBuffer;
+ bool resetEnabled;
+ float outputGain;
+ float reverbOutputGain;
+ bool reverbEnabled;
+ bool reverbOverridden;
+ Bit8u reverbMode;
+ Bit8u reverbTime;
+ Bit8u reverbLevel;
+
+ Bit16s *buffer;
+ DWORD framesRendered;
+
+ ADL_MIDIPlayer *synth;
+
+ bool m_setupInit;
+ DriverSettings m_setup;
+ DriverSettings m_setupCurrent;
+
+ unsigned int MillisToFrames(unsigned int millis);
+ void LoadSettings();
+
+ MidiSynth();
+ ~MidiSynth();
+
+public:
+ static MidiSynth &getInstance();
+ int Init();
+ void Close();
+ int Reset();
+ void ResetSynth();
+ void PanicSynth();
+ void RenderAvailableSpace();
+ void Render(Bit16s *bufpos, DWORD totalFrames);
+ void CheckForSignals();
+ void PushMIDI(DWORD msg);
+ void PlaySysex(Bit8u *bufpos, DWORD len);
+
+ void loadSetup();
+
+ void LoadSynthSetup();
+};
+
+}
+#endif
diff --git a/utils/winmm_drv/src/stdafx.h b/utils/winmm_drv/src/stdafx.h
new file mode 100644
index 0000000..dd8b15c
--- /dev/null
+++ b/utils/winmm_drv/src/stdafx.h
@@ -0,0 +1,83 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+#include <mmsystem.h>
+#ifndef __MINGW32__
+#include <mmddk.h>
+#endif
+#include <mmreg.h>
+#include <process.h>
+#include <iostream>
+#include <adlmidi.h>
+#include "MidiSynth.h"
+
+#ifdef __MINGW32__
+// Define missing in MinGW macros here
+
+#ifndef EXTERN_C
+#ifdef __cplusplus
+ #define EXTERN_C extern "C"
+ #define EXTERN_C_START extern "C" {
+ #define EXTERN_C_END }
+#else
+ #define EXTERN_C extern
+ #define EXTERN_C_START
+ #define EXTERN_C_END
+#endif
+#endif
+
+#ifndef STDAPICALLTYPE
+#define STDAPICALLTYPE __stdcall
+#endif
+
+#ifndef STDAPI_
+#define STDAPI_(type) EXTERN_C type STDAPICALLTYPE
+#endif
+
+#define DRV_PNPINSTALL (DRV_RESERVED + 11)
+
+#ifndef MMNOMIDI
+
+typedef struct midiopenstrmid_tag
+{
+ DWORD dwStreamID;
+ UINT uDeviceID;
+} MIDIOPENSTRMID;
+
+typedef struct midiopendesc_tag {
+ HMIDI hMidi;
+ DWORD_PTR dwCallback;
+ DWORD_PTR dwInstance;
+ DWORD_PTR dnDevNode;
+ DWORD cIds;
+ MIDIOPENSTRMID rgIds[1];
+} MIDIOPENDESC;
+
+typedef MIDIOPENDESC FAR *LPMIDIOPENDESC;
+
+#endif // MMNOMIDI
+
+#define MODM_GETNUMDEVS 1
+#define MODM_GETDEVCAPS 2
+#define MODM_OPEN 3
+#define MODM_CLOSE 4
+#define MODM_PREPARE 5
+#define MODM_UNPREPARE 6
+#define MODM_DATA 7
+#define MODM_LONGDATA 8
+#define MODM_RESET 9
+#define MODM_GETVOLUME 10
+#define MODM_SETVOLUME 11
+#define MODM_CACHEPATCHES 12
+#define MODM_CACHEDRUMPATCHES 13
+
+#endif // __MINGW32__
diff --git a/utils/winmm_drv/src/targetver.h b/utils/winmm_drv/src/targetver.h
new file mode 100644
index 0000000..658f76f
--- /dev/null
+++ b/utils/winmm_drv/src/targetver.h
@@ -0,0 +1,24 @@
+#pragma once
+
+// The following macros define the minimum required platform. The minimum required platform
+// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
+// your application. The macros work by enabling all features available on platform versions up to and
+// including the version specified.
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef WINVER // Specifies that the minimum required platform is Windows NT4.
+#define WINVER 0x0510 // Change this to the appropriate value to target other versions of Windows.
+#endif
+
+#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows 2000.
+#define _WIN32_WINNT 0x0510 // Change this to the appropriate value to target other versions of Windows.
+#endif
+
+#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98.
+#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
+#endif
+
+#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 5.0.
+#define _WIN32_IE 0x0500 // Change this to the appropriate value to target other versions of IE.
+#endif
diff --git a/utils/winmm_drv/src/winmm_drv.cpp b/utils/winmm_drv/src/winmm_drv.cpp
new file mode 100644
index 0000000..7f65dda
--- /dev/null
+++ b/utils/winmm_drv/src/winmm_drv.cpp
@@ -0,0 +1,387 @@
+/* Copyright (C) 2003, 2004, 2005 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "stdafx.h"
+
+#define MAX_DRIVERS 8
+#define MAX_CLIENTS 8 // Per driver
+
+#ifdef __MINGW32__
+
+#if !defined(MM_MPU401_MIDIOUT) && !defined(MM_MPU401_MIDIOUT)
+typedef struct tagMIDIOUTCAPS2A {
+ WORD wMid;
+ WORD wPid;
+ MMVERSION vDriverVersion;
+ CHAR szPname[MAXPNAMELEN];
+ WORD wTechnology;
+ WORD wVoices;
+ WORD wNotes;
+ WORD wChannelMask;
+ DWORD dwSupport;
+ GUID ManufacturerGuid;
+ GUID ProductGuid;
+ GUID NameGuid;
+} MIDIOUTCAPS2A,*PMIDIOUTCAPS2A,*NPMIDIOUTCAPS2A,*LPMIDIOUTCAPS2A;
+
+typedef struct tagMIDIOUTCAPS2W {
+ WORD wMid;
+ WORD wPid;
+ MMVERSION vDriverVersion;
+ WCHAR szPname[MAXPNAMELEN];
+ WORD wTechnology;
+ WORD wVoices;
+ WORD wNotes;
+ WORD wChannelMask;
+ DWORD dwSupport;
+ GUID ManufacturerGuid;
+ GUID ProductGuid;
+ GUID NameGuid;
+} MIDIOUTCAPS2W,*PMIDIOUTCAPS2W,*NPMIDIOUTCAPS2W,*LPMIDIOUTCAPS2W;
+#endif
+
+#ifndef MM_UNMAPPED
+#define MM_UNMAPPED 0xffff
+#endif
+#ifndef MM_MPU401_MIDIOUT
+#define MM_MPU401_MIDIOUT 10
+#endif
+
+//BOOL APIENTRY DriverCallback(DWORD_PTR dwCallback,
+// DWORD dwFlags,
+// HDRVR hDevice,
+// DWORD dwMsg,
+// DWORD_PTR dwUser,
+// DWORD_PTR dwParam1,
+// DWORD_PTR dwParam2);
+
+typedef BOOL (APIENTRY *DriverCallbackPtr)(DWORD_PTR dwCallback,
+ DWORD dwFlags,
+ HDRVR hDevice,
+ DWORD dwMsg,
+ DWORD_PTR dwUser,
+ DWORD_PTR dwParam1,
+ DWORD_PTR dwParam2);
+
+static HMODULE s_winmm_dll = NULL;
+static DriverCallbackPtr s_DriverCallback = NULL;
+
+#define DriverCallback s_DriverCallback
+
+static void initWorkarounds()
+{
+ s_winmm_dll = LoadLibraryW(L"winmm.dll");
+ if(s_winmm_dll)
+ {
+ s_DriverCallback = (DriverCallbackPtr)GetProcAddress(s_winmm_dll, "DriverCallback");
+ if(!s_DriverCallback)
+ MessageBoxW(NULL, L"Failed to get a workaround for DriverCallback", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ }
+ else
+ {
+ MessageBoxW(NULL, L"Failed to open a library for a workaround for DriverCallback", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION);
+ }
+}
+
+static void freeWorkarounds()
+{
+ if(s_winmm_dll)
+ FreeLibrary(s_winmm_dll);
+ s_winmm_dll = NULL;
+ s_DriverCallback = NULL;
+}
+#endif
+
+static OPL3Emu::MidiSynth &midiSynth = OPL3Emu::MidiSynth::getInstance();
+static bool synthOpened = false;
+//static HWND hwnd = NULL;
+static int driverCount;
+
+struct Driver
+{
+ bool open;
+ int clientCount;
+ HDRVR hdrvr;
+ struct Client
+ {
+ bool allocated;
+ DWORD_PTR instance;
+ DWORD flags;
+ DWORD_PTR callback;
+ DWORD synth_instance;
+ } clients[MAX_CLIENTS];
+} drivers[MAX_DRIVERS];
+
+EXTERN_C LONG __declspec(dllexport) __stdcall DriverProc(DWORD dwDriverID, HDRVR hdrvr, UINT wMessage, LONG dwParam1, LONG dwParam2)
+{
+ (void)dwDriverID; (void)dwParam1; (void)dwParam2;
+
+ switch(wMessage)
+ {
+ case DRV_LOAD:
+#ifdef __MINGW32__
+ initWorkarounds();
+#endif
+ memset(drivers, 0, sizeof(drivers));
+ driverCount = 0;
+ return DRV_OK;
+ case DRV_ENABLE:
+ return DRV_OK;
+ case DRV_OPEN:
+ int driverNum;
+ if(driverCount == MAX_DRIVERS)
+ return 0;
+ else
+ {
+ for(driverNum = 0; driverNum < MAX_DRIVERS; driverNum++)
+ {
+ if(!drivers[driverNum].open)
+ break;
+ if(driverNum == MAX_DRIVERS)
+ return 0;
+ }
+ }
+ drivers[driverNum].open = true;
+ drivers[driverNum].clientCount = 0;
+ drivers[driverNum].hdrvr = hdrvr;
+ driverCount++;
+ return DRV_OK;
+ case DRV_INSTALL:
+ case DRV_PNPINSTALL:
+ return DRV_OK;
+ case DRV_QUERYCONFIGURE:
+ return 0;
+ case DRV_CONFIGURE:
+ return DRVCNF_OK;
+ case DRV_CLOSE:
+ for(int i = 0; i < MAX_DRIVERS; i++)
+ {
+ if(drivers[i].open && drivers[i].hdrvr == hdrvr)
+ {
+ drivers[i].open = false;
+ --driverCount;
+ return DRV_OK;
+ }
+ }
+ return DRV_CANCEL;
+ case DRV_DISABLE:
+ return DRV_OK;
+ case DRV_FREE:
+#ifdef __MINGW32__
+ freeWorkarounds();
+#endif
+ return DRV_OK;
+ case DRV_REMOVE:
+ return DRV_OK;
+ }
+ return DRV_OK;
+}
+
+
+EXTERN_C HRESULT modGetCaps(PVOID capsPtr, DWORD capsSize)
+{
+ MIDIOUTCAPSA *myCapsA;
+ MIDIOUTCAPSW *myCapsW;
+ MIDIOUTCAPS2A *myCaps2A;
+ MIDIOUTCAPS2W *myCaps2W;
+
+ CHAR synthName[] = "libADLMIDI synth\0";
+ WCHAR synthNameW[] = L"libADLMIDI synth\0";
+
+ switch(capsSize)
+ {
+ case(sizeof(MIDIOUTCAPSA)):
+ myCapsA = (MIDIOUTCAPSA *)capsPtr;
+ myCapsA->wMid = MM_UNMAPPED;
+ myCapsA->wPid = MM_MPU401_MIDIOUT;
+ memcpy(myCapsA->szPname, synthName, sizeof(synthName));
+ myCapsA->wTechnology = MOD_MIDIPORT;
+ myCapsA->vDriverVersion = 0x0090;
+ myCapsA->wVoices = 0;
+ myCapsA->wNotes = 0;
+ myCapsA->wChannelMask = 0xffff;
+ myCapsA->dwSupport = 0;
+ return MMSYSERR_NOERROR;
+
+ case(sizeof(MIDIOUTCAPSW)):
+ myCapsW = (MIDIOUTCAPSW *)capsPtr;
+ myCapsW->wMid = MM_UNMAPPED;
+ myCapsW->wPid = MM_MPU401_MIDIOUT;
+ memcpy(myCapsW->szPname, synthNameW, sizeof(synthNameW));
+ myCapsW->wTechnology = MOD_MIDIPORT;
+ myCapsW->vDriverVersion = 0x0090;
+ myCapsW->wVoices = 0;
+ myCapsW->wNotes = 0;
+ myCapsW->wChannelMask = 0xffff;
+ myCapsW->dwSupport = 0;
+ return MMSYSERR_NOERROR;
+
+ case(sizeof(MIDIOUTCAPS2A)):
+ myCaps2A = (MIDIOUTCAPS2A *)capsPtr;
+ myCaps2A->wMid = MM_UNMAPPED;
+ myCaps2A->wPid = MM_MPU401_MIDIOUT;
+ memcpy(myCaps2A->szPname, synthName, sizeof(synthName));
+ myCaps2A->wTechnology = MOD_MIDIPORT;
+ myCaps2A->vDriverVersion = 0x0090;
+ myCaps2A->wVoices = 0;
+ myCaps2A->wNotes = 0;
+ myCaps2A->wChannelMask = 0xffff;
+ myCaps2A->dwSupport = 0;
+ return MMSYSERR_NOERROR;
+
+ case(sizeof(MIDIOUTCAPS2W)):
+ myCaps2W = (MIDIOUTCAPS2W *)capsPtr;
+ myCaps2W->wMid = MM_UNMAPPED;
+ myCaps2W->wPid = MM_MPU401_MIDIOUT;
+ memcpy(myCaps2W->szPname, synthNameW, sizeof(synthNameW));
+ myCaps2W->wTechnology = MOD_MIDIPORT;
+ myCaps2W->vDriverVersion = 0x0090;
+ myCaps2W->wVoices = 0;
+ myCaps2W->wNotes = 0;
+ myCaps2W->wChannelMask = 0xffff;
+ myCaps2W->dwSupport = 0;
+ return MMSYSERR_NOERROR;
+
+ default:
+ return MMSYSERR_ERROR;
+ }
+}
+
+void DoCallback(int driverNum, DWORD_PTR clientNum, DWORD msg, DWORD_PTR param1, DWORD_PTR param2)
+{
+ Driver::Client *client = &drivers[driverNum].clients[clientNum];
+#ifdef __MINGW32__
+ if(s_DriverCallback)
+ initWorkarounds();
+#endif
+ DriverCallback(client->callback, client->flags, drivers[driverNum].hdrvr, msg, client->instance, param1, param2);
+}
+
+LONG OpenDriver(Driver *driver, UINT uDeviceID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ (void)uMsg;
+
+ int clientNum;
+ if(driver->clientCount == 0)
+ clientNum = 0;
+ else if(driver->clientCount == MAX_CLIENTS)
+ return MMSYSERR_ALLOCATED;
+ else
+ {
+ int i;
+ for(i = 0; i < MAX_CLIENTS; i++)
+ {
+ if(!driver->clients[i].allocated)
+ break;
+ }
+ if(i == MAX_CLIENTS)
+ return MMSYSERR_ALLOCATED;
+ clientNum = i;
+ }
+
+ MIDIOPENDESC *desc = (MIDIOPENDESC *)dwParam1;
+ driver->clients[clientNum].allocated = true;
+ driver->clients[clientNum].flags = HIWORD(dwParam2);
+ driver->clients[clientNum].callback = desc->dwCallback;
+ driver->clients[clientNum].instance = desc->dwInstance;
+ *(LONG *)dwUser = clientNum;
+ driver->clientCount++;
+ DoCallback(uDeviceID, clientNum, MOM_OPEN, (DWORD_PTR)NULL, (DWORD_PTR)NULL);
+ return MMSYSERR_NOERROR;
+}
+
+LONG CloseDriver(Driver *driver, UINT uDeviceID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ (void)uMsg; (void)dwParam1; (void)dwParam2;
+
+ if(!driver->clients[dwUser].allocated)
+ return MMSYSERR_INVALPARAM;
+ driver->clients[dwUser].allocated = false;
+ driver->clientCount--;
+ DoCallback(uDeviceID, dwUser, MOM_CLOSE, (DWORD_PTR)NULL, (DWORD_PTR)NULL);
+ return MMSYSERR_NOERROR;
+}
+
+EXTERN_C DWORD __declspec(dllexport) __stdcall modMessage(DWORD uDeviceID, DWORD uMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ MIDIHDR *midiHdr;
+ Driver *driver = &drivers[uDeviceID];
+ DWORD instance;
+ switch(uMsg)
+ {
+ case MODM_OPEN:
+ if(!synthOpened)
+ {
+ if(midiSynth.Init() != 0) return MMSYSERR_ERROR;
+ synthOpened = true;
+ }
+ instance = (DWORD)NULL;
+ DWORD res;
+ res = OpenDriver(driver, uDeviceID, uMsg, dwUser, dwParam1, dwParam2);
+ driver->clients[*(LONG *)dwUser].synth_instance = instance;
+ return res;
+
+ case MODM_CLOSE:
+ if(driver->clients[dwUser].allocated == false)
+ return MMSYSERR_ERROR;
+ if(synthOpened)
+ {
+ midiSynth.Reset();
+ midiSynth.ResetSynth();
+ }
+ return CloseDriver(driver, uDeviceID, uMsg, dwUser, dwParam1, dwParam2);
+
+ case MODM_PREPARE:
+ return MMSYSERR_NOTSUPPORTED;
+
+ case MODM_UNPREPARE:
+ return MMSYSERR_NOTSUPPORTED;
+
+ case MODM_RESET:
+ if(synthOpened)
+ midiSynth.PanicSynth();
+ return MMSYSERR_NOERROR;
+
+ case MODM_GETDEVCAPS:
+ return modGetCaps((PVOID)dwParam1, (DWORD)dwParam2);
+
+ case MODM_DATA:
+ if(driver->clients[dwUser].allocated == false)
+ return MMSYSERR_ERROR;
+ midiSynth.PushMIDI((DWORD)dwParam1);
+ return MMSYSERR_NOERROR;
+
+ case MODM_LONGDATA:
+ if(driver->clients[dwUser].allocated == false)
+ return MMSYSERR_ERROR;
+ midiHdr = (MIDIHDR *)dwParam1;
+ if((midiHdr->dwFlags & MHDR_PREPARED) == 0)
+ return MIDIERR_UNPREPARED;
+ midiSynth.PlaySysex((unsigned char *)midiHdr->lpData, midiHdr->dwBufferLength);
+ midiHdr->dwFlags |= MHDR_DONE;
+ midiHdr->dwFlags &= ~MHDR_INQUEUE;
+ DoCallback(uDeviceID, dwUser, MOM_DONE, dwParam1, (DWORD_PTR)NULL);
+ return MMSYSERR_NOERROR;
+
+ case MODM_GETNUMDEVS:
+ return 0x1;
+
+ default:
+ return MMSYSERR_NOERROR;
+ break;
+ }
+}
diff --git a/utils/winmm_drv/src/winmm_drv.def b/utils/winmm_drv/src/winmm_drv.def
new file mode 100644
index 0000000..8d105bc
--- /dev/null
+++ b/utils/winmm_drv/src/winmm_drv.def
@@ -0,0 +1,6 @@
+LIBRARY adlmidisynth
+EXPORTS
+ DriverProc
+ modMessage
+ adl_getBankNames
+ adl_getBanksCount
diff --git a/utils/winmm_drv/test/test.c b/utils/winmm_drv/test/test.c
new file mode 100644
index 0000000..f6fd2e9
--- /dev/null
+++ b/utils/winmm_drv/test/test.c
@@ -0,0 +1,105 @@
+#include <windows.h>
+#include <stdio.h>
+#include <mmsystem.h>
+
+typedef DWORD (WINAPI * MessagePtr)(UINT, UINT, DWORD, DWORD, DWORD);
+
+#ifndef MODM_GETDEVCAPS
+#define MODM_GETDEVCAPS 2
+#endif
+
+LONG testDriver()
+{
+ HDRVR hdrvr;
+ DRVCONFIGINFO dci;
+ LONG lRes;
+ HMODULE lib;
+ DWORD modRet;
+ MIDIOUTCAPSA myCapsA;
+ MessagePtr modMessagePtr;
+
+ printf("Open...\n");
+ // Open the driver (no additional parameters needed this time).
+ if((hdrvr = OpenDriver(L"adlmididrv.dll", 0, 0)) == 0)
+ {
+ printf("!!! Can't open the driver\n");
+ return -1;
+ }
+
+ printf("Send DRV_QUERYCONFIGURE...\n");
+ // Make sure driver has a configuration dialog box.
+ if(SendDriverMessage(hdrvr, DRV_QUERYCONFIGURE, 0, 0) != 0)
+ {
+ // Set the DRVCONFIGINFO structure and send the message
+ dci.dwDCISize = sizeof (dci);
+ dci.lpszDCISectionName = (LPWSTR)0;
+ dci.lpszDCIAliasName = (LPWSTR)0;
+ printf("Send DRV_CONFIGURE...\n");
+ lRes = SendDriverMessage(hdrvr, DRV_CONFIGURE, 0, (LPARAM)&dci);
+ printf("<-- Got answer: %ld)\n", lRes);
+ }
+ else
+ {
+ printf("<-- No configure\n");
+ lRes = DRVCNF_OK;
+ }
+
+
+ printf("Getting library pointer\n");
+ if((lib = GetDriverModuleHandle(hdrvr)))
+ {
+ printf("Getting modMessage call\n");
+ modMessagePtr = (MessagePtr)GetProcAddress(lib, "modMessage");
+ if(!modMessagePtr)
+ {
+ CloseDriver(hdrvr, 0, 0);
+ printf("!!! modMessage not found!\n");
+ return -1;
+ }
+
+ printf("Getting capabilities...\n");
+ modRet = modMessagePtr(0, MODM_GETDEVCAPS, (DWORD_PTR)NULL, (DWORD_PTR)&myCapsA, sizeof(myCapsA));
+ if(modRet != MMSYSERR_NOERROR)
+ {
+ CloseDriver(hdrvr, 0, 0);
+ printf("!!! modMessage returned an error!\n");
+ return -1;
+ }
+
+ printf("<-- %s\n", myCapsA.szPname);
+ }
+ else
+ {
+ CloseDriver(hdrvr, 0, 0);
+ printf("!!! Error when getting module handler!\n");
+ return -1;
+ }
+
+ printf("Close...\n");
+ // Close the driver (no additional parameters needed this time).
+ if(FAILED(CloseDriver(hdrvr, 0, 0)))
+ {
+ printf("!!! Error when closing\n");
+ return -1;
+ }
+
+ printf("Return...\n");
+
+ return 0;
+}
+
+int main()
+{
+ LONG d = testDriver();
+
+ if(d == 0)
+ {
+ printf("TEST = OK\n");
+ return 0;
+ }
+ else
+ {
+ printf("TEST = FAILED\n");
+ return 1;
+ }
+}