Skip to content
Permalink
Browse files

Version 6.0.39

  • Loading branch information...
LeBreezyAF committed Oct 14, 2018
1 parent 01580eb commit 1644c35419df2182d0f5a39369e1140897ca6917
@@ -0,0 +1,262 @@
<p align="center">
<img src="https://i.imgur.com/iFLDs6C.png">
<br />
A group of functions, that allows you to bypass the Windows Multimedia API, thus getting rid of the lag caused by its slow buffer system.
</p>

## How can I implement it in my program?
It's quite easy actually.

You can either import the functions through the header and library files here: [DeveloperContent folder](https://github.com/KeppySoftware/OmniMIDI/tree/master/DeveloperContent)<br />
Or you can use this generic example by [Sono (MarcuzD)](https://github.com/MarcuzD), on how to make use of the Keppy's Direct MIDI API with LoadLibrary and GetProcAddress.<br />
It works just like WinMM would, we'll see later the differences between using WinMM as usual, and using WinMM together with Keppy's Direct MIDI:
```c
...
#define ALLOK 0;
#define NOTOK 1;
MMRESULT(WINAPI*mmOutOpen)(LPHMIDIOUT, lphmo, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwCallbackInstance, DWORD dwFlags) = 0;
MMRESULT(WINAPI*mmOutClose)(HMIDIOUT hmo) = 0;
MMRESULT(WINAPI*mmOutShortMsg)(HMIDIOUT hmo, DWORD dwMsg) = 0;
UINT(WINAPI*mmOutGetErrorTextA)(MMRESULT mmrError, LPTSTR, lpText, UINT cchText) = 0;
MMRESULT(WINAPI*KShortMsg)(DWORD msg) = 0;
...
...
int SetupWinMM() {
// Check if WinMM is already in memory
HDMODULE mm= GetModuleHandle("winmm");
// It's not, load it manually from the system folder
if (!mm) mm = LoadLibrary("winmm");
// Something went wrong, return the error
if (!mm) return GetLastError();
// Load the MIDI functions from it
mmOutOpen = (void*)GetProcAddress(mm, "midiOutOpen");
if (!mmOutOpen) return GetLastError();
mmOutClose = (void*)GetProcAddress(mm, "midiOutClose");
if (!mmOutClose) return GetLastError();
mmOutShortMsg = (void*)GetProcAddress(mm, "midiOutShortMsg");
if (!mmOutShortMsg) return GetLastError();
mmOutGetErrorTextA = (void*)GetProcAddress(mm, "midiOutGetErrorTextA");
if (!mmOutGetErrorTextA) return GetLastError();
// Everything's okay, continue
return ALLOK;
}
...
```

Let's initialize WinMM:
```c
...
int mmstatus = SetupWinMM();
if (mmstatus) {
// The program failed to initialize WinMM, close it.
printf("Failed to initialize Windows Multimedia: %i\n", mmstatus);
exit(0);
}
MMRESULT mmres = mmOutOpen(&hmo, 1, 0, 0, CALLBACK_NULL);
if (mmres != MMSYSERR_NOERROR) {
// Device 1 doesn't exist or failed to initialize, let's initialize Microsoft GS instead
mmOutGetErrorTextA(mmres, buf, sizeof(buf));
printf("OutOpen (%1) %s\n", mmres, buf);
mmres = mmOutOpen(&hmo, 0, 0, 0, CALLBACK_NULL);
}
if (mmres != MMSYSERR_NOERROR) {
// Microsoft GS also failed to initialize, close the app
mmOutGetErrorTextA(mmres, buf, sizeof(buf));
printf("OutOpen (%1) %s\n", mmres, buf);
return NOTOK;
}
// Check if OmniMIDI is loaded into memory, and initialize the Keppy's Direct MIDI calls
KShortMsg = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "SendDirectData");
if (KShortMsg) {
// Replace default WinMM function with the one from the application itself
puts("KDMAPI initialized.");
mmOutShortMsg = _KOutShortMsg;
}
...
```
And here's the function from the application itself:
```c
MMRESULT WINAPI _KOutShortMsg(HMIDIOUT hmo, DWORD msg) {
// Pass the MIDI event to the Keppy's Direct MIDI call, and return the WinMM result
return KShortMsg(msg);
}
```
As you can see, we're basically replacing the loaded **midiOutShortMsg** call with our own call, **_KOutShortMsg**, which redirects the messages from WinMM directly to (*lol*) the Keppy's Direct MIDI API calls.
## Is it mandatory for me to implement these features, to use OmniMIDI?<br />
Of course not! These calls are a thing for people who care about low latency and performance.<br />
The driver will work fine with the default WinMM => modMessage system too.<br />
It'll be slower when playing Black MIDIs, and the latency will also be higher, but it'll work just fine.
## What functions are available?
As of August 27th 2018, these are the functions available in the Keppy's Direct MIDI API.<br />
The **"NoBuf"** calls bypass the built-in buffer in OmniMIDI, and directly send the events to the events processing system.<br />
### **InitializeKDMAPIStream**<br />
It initializes the driver, its stream and all its required threads. There are no arguments.
```c
void(WINAPI*KDMInit)() = 0;
KDMInit = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "InitializeKDMAPIStream");
```
<hr />
### **TerminateKMDAPIStream**
It tells the driver to wrap up its stuff and to leave! There are no arguments.
```c
void(WINAPI*KDMStop)() = 0;
KDMStop = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "TerminateKDMAPIStream");
```
<hr />
### **ResetKDMAPIStream**
Resets the MIDI channels. There are no arguments.
```c
void(WINAPI*KDMReset)() = 0;
KDMReset = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "ResetKDMAPIStream");
```
<hr />
### **ReturnKDMAPIVer**
It returns the version of the Keppy's Direct MIDI API, as a string. There are no arguments available.
Used by OmniMIDI Configurator.
```c
char const*(WINAPI*KDMAPIVer)() = 0;
KDMAPIVer = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "ReturnKDMAPIVer");
```
<hr />
### **IsKDMAPIAvailable**
A generic check, useful for people who want to see if KSDAPI v1.2+ is available.<br />
You NEED to call this function at least once, in order to switch the KSDAPI status value in the debug window to active.<br />
There are no arguments available, and you have to manually catch the exception, if the function isn't available.
```c
BOOL(WINAPI*KDMAPIStatus)() = 0;
KDMAPIStatus = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "IsKDMAPIAvailable");
```
<hr />
### **ChangeDriverSettings**
Allows developers to change the driver's settings from within the app, rather than asking the user to change them in the configurator.<br/>
Sending **0/nullptr** will make it fallback to the settings from the registry.<br />
The available arguments are:
- `const Settings* Struct`: A pointer to your struct.
- `DWORD StructSize`: The size of the struct.
```c
VOID(WINAPI*KDMChangeSettings)(const Settings* Struct, DWORD StructSize) = 0;
KDMChangeSettings = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "ChangeDriverSettings");
...
Settings MySettings;
// Change your settings
Settings.MaxVoices = 4;
Settings.AudioFrequency = 22050;
Settings.LiveChanges = TRUE;
// Push the settings to the driver
KDMChangeSettings(&MySettings, sizeof(MySettings));
// Now make the driver fallback to the settings from the registry
KDMChangeSettings(nullptr, 0);
...
```
You can get the code for the struct from **"val.h"**: [Click here!](https://github.com/KeppySoftware/OmniMIDI/blob/master/OmniMIDI/val.h)
<hr />
### **GetDriverDebugInfo**
Allows developers to get the driver's current rendering time and the voices that are currently active in the audio stream.<br />
```c
DebugInfo*(WINAPI*KDMGetDebugInfo)() = 0;
KDMGetDebugInfo = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "GetDriverDebugInfo");
...
DebugInfo* DebugInfoFromDriver;
DebugInfoFromDriver = KDMGetDebugInfo();
//Do something with the info
printf("Current rendering time: %d\n", DebugInfoFromDriver->RenderingTime);
...
```
You can get the code for the struct from **"val.h"**: [Click here!](https://github.com/KeppySoftware/OmniMIDI/blob/master/OmniMIDI/val.h)
<hr />
### **LoadCustomSoundFontsList**
Allows developers to load their own custom SoundFonts or SoundFonts lists.<br />
The available arguments are:
- `const TCHAR* Directory`: A pointer to the unicode char array, containing the path.
```c
VOID(WINAPI*KDMLoadCustomSFList)(const TCHAR* Directory) = 0;
KDMLoadCustomSFList = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "LoadCustomSoundFontsList");
...
TCHAR Directory[MAX_PATH];
wcscpy_s(Directory, MAX_PATH, _TEXT("C:\\MySF.sf2"));
// Forward it to the driver
KDMLoadCustomSFList(&Directory);
...
```
<hr />
### **SendDirectData/SendDirectDataNoBuf**
Allows you to send MIDI events to the driver.<br />
The available arguments are:
- `DWORD dwMsg`: The MIDI event to send to the driver.
```c
MMRESULT(WINAPI*KShortMsg)(DWORD msg) = 0;
KShortMsg = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "SendDirectData"); // Or SendDirectDataNoBuf
```
<hr />
### **SendDirectLongData/SendDirectLongDataNoBuf**
Allows you to send MIDIHDR/System Exclusive events to the driver.<br />
Both functions do the same thing. SendDirectLongDataNoBuf directly calls SendDirectLongData. I left NoBuf for retrocompatibility purpose with old patches.<br />
You can handle the preparation of the buffer through **PrepareLongData**/**UnprepareLongData**.<br />
The available arguments are:
- `MIDIHDR* IIMidiHdr`: The pointer to the MIDIHDR.
```c
MMRESULT(WINAPI*KLongMsg)(MIDIHDR* IIMidiHdr) = 0;
KLongMsg = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "SendDirectLongData"); // Or SendDirectLongDataNoBuf
```
<hr />
### **PrepareLongData**
Allows you to prepare the MIDIHDR buffer, before sending it to the driver through SendDirectLongData/SendDirectLongDataNoBuf.<br />
Once a buffer is prepared, it becomes read-only.<br />
The available arguments are:
- `MIDIHDR* IIMidiHdr`: The pointer to the MIDIHDR.
```c
MMRESULT(WINAPI*KPrepLongMsg)(MIDIHDR* IIMidiHdr) = 0;
KPrepLongMsg = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "PrepareLongData");
```
<hr />
### **UnprepareLongData**
Allows you to unprepare the MIDIHDR buffer, to make it writable again.<br />
It is **MANDATORY** to unprepare a MIDIHDR before editing it, since PrepareLongData locks its data.<br />
The available arguments are:
- `MIDIHDR* IIMidiHdr`: The pointer to the MIDIHDR.
```c
MMRESULT(WINAPI*KUnprepLongMsg)(MIDIHDR* IIMidiHdr) = 0;
KUnprepLongMsg = (void*)GetProcAddress(GetModuleHandle("OmniMIDI"), "UnprepareLongData");
```
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,119 @@
/*
OmniMIDI, a fork of BASSMIDI Driver - Declarations
Thank you Kode54 for allowing me to fork your awesome driver.
*/
#pragma once

#include <wtypes.h>

#ifdef OMNIMIDI_EXPORTS
#define KDMAPI __declspec(dllexport)
#else
#define KDMAPI __declspec(dllimport)
#endif

// Audio engines
#define AUDTOWAV 0
#define DSOUND_ENGINE 1
#define ASIO_ENGINE 2
#define WASAPI_ENGINE 3

typedef struct
{
BOOL AlternativeCPU = FALSE; // Autopanic switch (DEPRECATED)
BOOL CapFramerate = FALSE; // Cap input framerate
BOOL DebugMode = FALSE; // Debug console
BOOL DisableNotesFadeOut = 0; // Disable fade-out
BOOL DontMissNotes = FALSE; // Slow down instead of missing notes

BOOL EnableSFX = TRUE; // Enable or disable FXs
BOOL FastHotkeys = FALSE; // Enable/Disable fast hotkeys
BOOL FullVelocityMode = FALSE; // Enable full velocity mode
BOOL IgnoreNotesBetweenVel = FALSE; // Ignore notes in between two velocity values
BOOL IgnoreAllEvents = FALSE; // Ignore all MIDI events
BOOL IgnoreSysEx = FALSE; // Ignore SysEx events
BOOL IgnoreSysReset = FALSE; // Ignore sysex messages
BOOL LimitTo88Keys = FALSE; // Limit to 88 keys
BOOL LiveChanges = FALSE; // Live changes
BOOL MT32Mode = FALSE; // Roland MT-32 mode
BOOL MonoRendering = TRUE; // Mono rendering (Instead of stereo by default)
BOOL NoBlacklistMessage = TRUE; // Disable blacklist message (DEPRECATED)
BOOL NoteOff1 = 0; // Note cut INT
BOOL NotesCatcherWithAudio = FALSE; // For old-ass PCs
BOOL OverrideInstruments = TRUE; // Override channel instruments
BOOL PreloadSoundFonts = TRUE; // Soundfont preloading
BOOL SincInter = FALSE; // Sinc
BOOL SleepStates = TRUE; // Reduce CPU overhead
BOOL VolumeMonitor = TRUE; // Volume monitoring

DWORD AudioBitDepth = 1; // Floating poDWORD audio
DWORD AudioFrequency = 48000; // Audio frequency
DWORD AudioOutputReg = 0; // Audio output (All devices except AudToWAV and ASIO)
DWORD BufferLength = 0; // Default
DWORD CurrentEngine = WASAPI_ENGINE; // Current engine
DWORD DefaultSFList = 1; // Default soundfont list
DWORD DriverPriority = 0; // Process priority
DWORD Extra8Lists = 0; // Enable extra 8 SoundFont lists
DWORD MaxRenderingTime = 75; // CPU usage INT
DWORD MaxVelIgnore = 1; // Ignore notes in between two velocity values
DWORD MinVelIgnore = 1; // Ignore notes in between two velocity values
DWORD OutputVolume = 10000; // Volume
DWORD TransposeValue = 127; // Pitch shift (127 = None)
DWORD MaxVoices = 500; // Voices limit
DWORD SincConv = 2; // Sinc

// Add more down here
// ------------------
} Settings;

typedef struct
{
FLOAT RenderingTime = 0.0f;
DWORD ActiveVoices[16];

// Add more down here
// ------------------
} DebugInfo;

// Return the KDMAPI version from OmniMIDI as the following output: Major.Minor.Build.Revision (eg. 1.30.0 Rev. 51).
extern "C" KDMAPI BOOL ReturnKDMAPIVer(DWORD &Major, DWORD &Minor, DWORD &Build, DWORD &Revision);

// Checks if KDMAPI is available. You can ignore the output if you want, but you should give the user the choice between WinMM and KDMAPI.
extern "C" KDMAPI BOOL IsKDMAPIAvailable();

// Initializes OmniMIDI through KDMAPI. (Like midiOutOpen)
extern "C" KDMAPI VOID InitializeKDMAPIStream();

// Closes OmniMIDI through KDMAPI. (Like midiOutClose)
extern "C" KDMAPI VOID TerminateKDMAPIStream();

// Resets OmniMIDI and all its MIDI channels through KDMAPI. (Like midiOutReset)
extern "C" KDMAPI VOID ResetKDMAPIStream();

// Send short messages through KDMAPI. (Like midiOutShortMsg)
extern "C" KDMAPI MMRESULT SendDirectData(DWORD dwMsg);

// Send short messages through KDMAPI like SendDirectData, but bypasses the buffer. (Like midiOutShortMsg)
extern "C" KDMAPI MMRESULT SendDirectDataNoBuf(DWORD dwMsg);

// Send long messages through KDMAPI. (Like midiOutLongMsg)
extern "C" KDMAPI MMRESULT SendDirectLongData(MIDIHDR* IIMidiHdr);

// Send long messages through KDMAPI like SendDirectLongData, but bypasses the buffer. (Like midiOutLongMsg)
extern "C" KDMAPI MMRESULT SendDirectLongDataNoBuf(MIDIHDR* IIMidiHdr);

// Prepares the long data, and locks its memory to prevent apps from writing to it.
extern "C" KDMAPI MMRESULT PrepareLongData(MIDIHDR* IIMidiHdr);

// Unlocks the memory, and unprepares the long data.
extern "C" KDMAPI MMRESULT UnprepareLongData(MIDIHDR* IIMidiHdr);

// Push your own settings to the driver through the Settings struct.
extern "C" KDMAPI VOID ChangeDriverSettings(const Settings* Struct, DWORD StructSize);

// Load a custom sflist. (You can also load SF2 and SFZ files)
extern "C" KDMAPI VOID LoadCustomSoundFontsList(const TCHAR* Directory);

// Get a pointer to the debug info of the driver.
extern "C" KDMAPI DebugInfo* WINAPI GetDriverDebugInfo()

0 comments on commit 1644c35

Please sign in to comment.
You can’t perform that action at this time.