Skip to content

Instantly share code, notes, and snippets.

@antlauzon
Created January 2, 2019 07:15
Show Gist options
  • Save antlauzon/b56ec72f3a4cce1fcfbf663eb98893cd to your computer and use it in GitHub Desktop.
Save antlauzon/b56ec72f3a4cce1fcfbf663eb98893cd to your computer and use it in GitHub Desktop.
WinMM Midi Latency Test
#include <windows.h>
#include <stdio.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <conio.h>
#define BUFFERSIZE 200
#define BUFFEROUTSIZE 120
#define NUM_SEND 500
HANDLE ghMutex;
int processedCount = 0;
int finished = 0;
unsigned char SysXBuffer[256];
unsigned char SysXFlag = 0;
LARGE_INTEGER start;
LARGE_INTEGER end;
LARGE_INTEGER frequency;
__int64 sum_latencies = 0;
void PrintMidiInErrorMsg(unsigned long err);
void PrintMidiOutErrorMsg(unsigned long err);
void CALLBACK midiCallback(HMIDIIN handle,
UINT uMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2);
void sendMidi(UINT deviceId);
void listenMidi(UINT deviceId);
void listDevices();
void PrintMidiInErrorMsg(unsigned long err) {
char buffer[BUFFERSIZE];
if (!(err = midiInGetErrorText(err, &buffer[0], BUFFERSIZE))) {
printf("%s\r\n", &buffer[0]);
}
else if (err == MMSYSERR_BADERRNUM) {
printf("Strange error number returned!\r\n");
}
else if (err == MMSYSERR_INVALPARAM) {
printf("Specified pointer is invalid!\r\n");
}
else {
printf("Unable to allocate/lock memory!\r\n");
}
}
void PrintMidiOutErrorMsg(unsigned long err) {
char buffer[BUFFEROUTSIZE];
if (!(err = midiOutGetErrorText(err, &buffer[0], BUFFEROUTSIZE))) {
printf("%s\r\n", &buffer[0]);
}
else if (err == MMSYSERR_BADERRNUM) {
printf("Strange error number returned!\r\n");
}
else {
printf("Specified pointer is invalid!\r\n");
}
}
void CALLBACK midiCallback(HMIDIIN handle,
UINT uMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2)
{
QueryPerformanceCounter(&end);
LPMIDIHDR lpMIDIHeader;
unsigned char * ptr;
TCHAR buffer[80];
unsigned char bytes;
if (uMsg == MIM_DATA) {
UCHAR bdata = (UCHAR) (dwParam1 & 0x000000FF);
if (bdata == (UCHAR) 0x90 ||bdata == (UCHAR) 0x80) {
sum_latencies += (end.QuadPart - start.QuadPart);
if (processedCount == NUM_SEND - 1) {
__int64 sum_latency_nanos =
(sum_latencies* 1000000000)/frequency.QuadPart;
__int64 sum_latency_micros =
(sum_latencies * 1000000)/frequency.QuadPart;
__int64 avg_latency_ticks = sum_latencies / NUM_SEND;
__int64 avg_latency_nanos = (sum_latency_nanos / NUM_SEND);
__int64 avg_latency_micros = (sum_latency_micros / NUM_SEND);
printf("sum_latencies: %lld\n", sum_latencies);
printf("avg_latency_ticks: %lld\n", avg_latency_ticks);
printf("avg_latency_nanos: %lld\n", avg_latency_nanos);
printf("avg_latench_micros: %lld\n", avg_latency_micros);
printf("freq: %lld\n", frequency.QuadPart);
midiInClose(handle);
finished = 1;
}
processedCount += 1;
ReleaseMutex(ghMutex);
}
}
}
void sendMidi(UINT deviceId) {
HMIDIOUT handle;
unsigned long err;
union {
DWORD dwData;
UCHAR bData[4];
} u;
u.bData[0] = (UCHAR)0x90;
u.bData[1] = (UCHAR)60;
u.bData[2] = (UCHAR)127;
u.bData[3] = 0;
if (!(err = midiOutOpen(&handle, deviceId, 0, 0, CALLBACK_NULL))) {
for (int i = 0; i < NUM_SEND; i++) {
WaitForSingleObject(ghMutex, INFINITE);
QueryPerformanceCounter(&start);
if ((err = midiOutShortMsg(handle, u. dwData))) {
PrintMidiOutErrorMsg(err);
}
Sleep(1);
if (u.bData[0] == (UCHAR)0x90) {
u.bData[0] = (UCHAR)0x80;
u.bData[1] = (UCHAR)0x3C;
u.bData[2] = (UCHAR)0x7F;
} else {
u.bData[0] = (UCHAR)0x90;
u.bData[1] = (UCHAR)60;
u.bData[2] = (UCHAR)127;
}
}
midiOutClose(handle);
} else {
printf("Error opening the default MIDI Out device!\r\n");
PrintMidiOutErrorMsg(err);
}
}
void listenMidi(UINT deviceId) {
HMIDIIN handle;
MIDIHDR midiHdr;
unsigned long err;
if (!(err = midiInOpen(&handle,
deviceId ,
(DWORD)midiCallback,
0,
CALLBACK_FUNCTION))) {
midiHdr.lpData = (LPBYTE)&SysXBuffer[0];
midiHdr.dwBufferLength = sizeof(SysXBuffer);
midiHdr.dwFlags = 0;
err = midiInPrepareHeader(handle, &midiHdr, sizeof(MIDIHDR));
if (!err) {
err = midiInAddBuffer(handle, &midiHdr, sizeof(MIDIHDR));
if (!err) {
err = midiInStart(handle);
if (!err) {
printf("\r\nListening!\n");
}
}
}
if (err) PrintMidiInErrorMsg(err);
} else {
printf("Error opening the default MIDI In Device!\r\n");
PrintMidiInErrorMsg(err);
}
}
void listDevices() {
MIDIOUTCAPS moc;
MIDIINCAPS mic;
long iNumDevs, i;
char buffer[80];
unsigned char chr;
MMRESULT mmerr;
// Midi Mapper is -1 Device
iNumDevs = midiOutGetNumDevs();
printf("numOutDevs %d\n", iNumDevs);
for (i = 0; i < iNumDevs; i++) {
if (!i) printf("MIDI Output Devices =======================\r\n\n");
if (!midiOutGetDevCaps(i, &moc, sizeof(MIDIOUTCAPS))) {
printf("\r\n Device ID #%ld: %s\r\n", i, moc.szPname);
}
}
iNumDevs = midiInGetNumDevs();
for (i = 0; i < iNumDevs; i++) {
if (!i) printf("\r\nMIDI Input Devices =======================\r\n");
if (!midiInGetDevCaps(i, &mic, sizeof(MIDIINCAPS))) {
printf("\r\n Device ID #%ld: %s\r\n", i, mic.szPname);
}
}
}
int main(int argc, char **argv) {
ghMutex = CreateMutex(NULL, FALSE, NULL);
if (argc < 3) {
listDevices();
} else {
UINT deviceIdIn = (UINT) atoi(argv[1]);
UINT deviceIdOut = (UINT) atoi(argv[2]);
start.QuadPart = 0;
end.QuadPart = 0;
QueryPerformanceFrequency(&frequency);
listenMidi(deviceIdIn);
sendMidi(deviceIdOut);
while (!finished) {
Sleep(0);
}
CloseHandle(ghMutex);
}
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment