diff options
author | Vitaly Novichkov <Wohlstand@users.noreply.github.com> | 2020-09-21 00:43:58 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-21 00:43:58 +0300 |
commit | 9b51dd7d667e97338a92711543fc3e942faae52f (patch) | |
tree | 63539cbf427912686565dab81b3aa820346967b5 /utils/winmm_drv | |
parent | e082315b04cbc75b4cfce744e9274bb8c5cedd38 (diff) | |
download | libADLMIDI-9b51dd7d667e97338a92711543fc3e942faae52f.tar.gz libADLMIDI-9b51dd7d667e97338a92711543fc3e942faae52f.tar.bz2 libADLMIDI-9b51dd7d667e97338a92711543fc3e942faae52f.zip |
Added WinMM driver for windows (#236)
* ADLMIDI2: Fixed an MSVC build
However, it may work glitchy!
* Experimental WinMM MIDI driver
TODO: Make a control panel to configure the driver
* WinMM-DRV: A workaround for older MinGW
* WinMM-DRV: WIP control panel appled / tool
* WinMM-DRV: Better test a driver's work
* WinMM-DRV: More progress on a control panel
* WinMM-DRV: And more progress on this control panel
It's almost completed, I need to make the working setup through a registry.
* WinMM-DRV: Driver is almost ready
However, control panel can't ping a driver to reload settings yet
* WinMM-DRV: Fix the missing volume model setup
* WinMM-DRV: Setup reload on the fly now work!
* WinMM-DRV: Stabilize the thing
* WinMM-DRV: Stabilizing
* WinMM-DRV: Avoid all "setup" in names
to avoid a stupid PCA thing:
https://stackoverflow.com/questions/17660404/how-to-programmatically-disable-program-compatibility-assistant-in-windows-7-and
* A small warning fix at the WOPL module
* WinMM-DRV: Fixed some warnings
* WinMM-DRV: Avoid PCA dialog on the installer
* WiNMM-DRV: Make the linking of pthread being optional
Required to use MinGW-w64 toolchain with the "win32" threading mode
* WinMM-DRV: Receive the MODM_RESET to reset the MIDI state
* WinMM-DRV: Attempt to fix a sound distorsion on song song
* WinMM-Drv: Fixed a build on some older MinGW toolchains
* WinMM-Drv: Fixed default settings not being loaded
Diffstat (limited to 'utils/winmm_drv')
24 files changed, 3014 insertions, 0 deletions
diff --git a/utils/winmm_drv/CMakeLists.txt b/utils/winmm_drv/CMakeLists.txt new file mode 100644 index 0000000..e65f281 --- /dev/null +++ b/utils/winmm_drv/CMakeLists.txt @@ -0,0 +1,126 @@ + +if(NOT WIN32) + message(FATAL_ERROR "WinDriver: This component is for Windows Platform only") +endif() + +option(WITH_WINMMDRV_PTHREADS "Link libwinpthreads statically (when using pthread-based builds)" ON) + +#==================================== +# 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() +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") +endif() + + +#==================================== +# 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") + + +#==================================== +# 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) +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) +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") +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..517bab8 --- /dev/null +++ b/utils/winmm_drv/config/regconfig.c @@ -0,0 +1,297 @@ +#include <stdlib.h> +#include <stdio.h> + +#include <windef.h> +#include <winbase.h> +#include <winuser.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; + 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, sizeof(setup->bankPath)); + 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); + 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-tool.exe.manifest b/utils/winmm_drv/cpl/adlconfig-tool.exe.manifest new file mode 100644 index 0000000..1016f98 --- /dev/null +++ b/utils/winmm_drv/cpl/adlconfig-tool.exe.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.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.cpl.manifest b/utils/winmm_drv/cpl/adlconfig.cpl.manifest new file mode 100644 index 0000000..1016f98 --- /dev/null +++ b/utils/winmm_drv/cpl/adlconfig.cpl.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.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.rc b/utils/winmm_drv/cpl/adlconfig.rc new file mode 100644 index 0000000..75551fd --- /dev/null +++ b/utils/winmm_drv/cpl/adlconfig.rc @@ -0,0 +1,142 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +///////////////////////////////////////////////////////////////////////////// +// Russian (Russia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +#ifdef ADL_IS_CPL_APPLET +MANIFSEST_RES_CPL RT_MANIFEST "adlconfig.cpl.manifest" +#else +MANIFSEST_RES RT_MANIFEST "adlconfig-tool.exe.manifest" +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "opl3icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG_BOX DIALOGEX 0, 0, 243, 242 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "libADLMIDI settings" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,81,210,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,210,50,14 + PUSHBUTTON "Apply",IDC_APPLYBUTTON,191,210,50,14 + COMBOBOX IDC_VOLUMEMODEL,79,95,155,200,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Volume model:",IDC_VM_LABEL,5,97,66,8 + COMBOBOX IDC_BANK_ID,79,15,155,200,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Instruments bank",IDC_INS_BANK,4,2,234,51 + CONTROL "Internal bank",IDC_BANK_INTERNAL,"Button",BS_AUTORADIOBUTTON | WS_GROUP,9,16,63,10 + CONTROL "External bank",IDC_BANK_EXTERNAL,"Button",BS_AUTORADIOBUTTON,9,33,67,10 + EDITTEXT IDC_BANK_PATH,79,31,107,14,ES_AUTOHSCROLL | WS_DISABLED + PUSHBUTTON "Browse...",IDC_BROWSE_BANK,187,31,48,14 + PUSHBUTTON "Restore defaults",IDC_RESTORE_DEFAULTS,153,193,88,14 + CONTROL "Deep tremolo (Auto/Enable/Disable)",IDC_FLAG_TREMOLO, + "Button",BS_AUTO3STATE | WS_TABSTOP,5,124,227,10 + CONTROL "Deep virbrato (Auto/Enable/Disable)",IDC_FLAG_VIBRATO, + "Button",BS_AUTO3STATE | WS_TABSTOP,5,141,227,10 + CONTROL "Full-panning stereo",IDC_FLAG_SOFTPAN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,108,227,10 + COMBOBOX IDC_EMULATOR,79,56,155,200,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Chip emulator type:",IDC_CHIPEMU_LABEL,5,58,67,8 + LTEXT "Number of chips",IDC_CHIPNUM_LABEL,5,75,69,8 + COMBOBOX IDC_NUM_CHIPS,79,74,33,200,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_NUM_4OPVO,193,74,41,200,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Number of 4OP voices",IDC_4OPSNUM_LABEL,115,76,72,8 + CONTROL "Scalable modulation",IDC_FLAG_SCALE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,157,228,10 + CONTROL "Full-range brightness (CC74)",IDC_FLAG_FULLBRIGHT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,173,228,10 + PUSHBUTTON "About...",IDC_ABOUT,5,210,50,14 + PUSHBUTTON "Reset synth now",IDC_RESET_SYNTH,69,193,80,14 + RTEXT "Settings will be applied immediately",IDC_BOTTOMNOTE_LABEL,0,227,241,8 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_SETUP_BOX, DIALOG + BEGIN + RIGHTMARGIN, 241 + BOTTOMMARGIN, 241 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDC_DRIVERNAME "libADLMIDI settings" + IDC_DRIVERDESC "Settings for libADLMIDI synthesizer" +END + +#endif // Russian (Russia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/utils/winmm_drv/cpl/config_dialog.c b/utils/winmm_drv/cpl/config_dialog.c new file mode 100644 index 0000000..8489273 --- /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; +} + +WINBOOL initAdlSetupBox(HINSTANCE hModule, HWND hwnd) +{ + InitCommonControls(); + setupDefault(&g_setup); + UNREFERENCED_PARAMETER(hModule); + UNREFERENCED_PARAMETER(hwnd); + return TRUE; +} + +WINBOOL 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 Binary files differnew file mode 100644 index 0000000..16c3d9a --- /dev/null +++ b/utils/winmm_drv/cpl/opl3icon.ico diff --git a/utils/winmm_drv/cpl/resource.h b/utils/winmm_drv/cpl/resource.h new file mode 100644 index 0000000..bd6b662 --- /dev/null +++ b/utils/winmm_drv/cpl/resource.h @@ -0,0 +1,50 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by adlsetup.rc +// +#include <winresrc.h> + +#define MANIFSEST_RES 1 +#define MANIFSEST_RES_CPL 123 +#define IDD_CONFIG_BOX 101 +#define IDI_ICON1 102 +#define IDC_DRIVERNAME 103 +#define IDC_DRIVERDESC 104 + +#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 + + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1028 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif 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..e354691 --- /dev/null +++ b/utils/winmm_drv/installer/drvinst.c @@ -0,0 +1,276 @@ +/* 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 <windef.h> +#include <winbase.h> +#include <winuser.h> +#include <winreg.h> +#include <winerror.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[nameSize + 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/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..73a7d34 --- /dev/null +++ b/utils/winmm_drv/src/winmm_drv.cpp @@ -0,0 +1,386 @@ +/* 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: + 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; + } +} |