Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble using Windows MIDI API (no callbacks when playing)

Tags:

c

windows

midi

I have an USB-connected MIDI-keyboard. It works fine in other applications. However, in my own program it does not. The midiInOpen() call goes through, I get one callback (from opening the device) but I don't get any callbacks when playing the keyboard.

By using midiInGetDevCaps() I can see that I'm using the correct device.

Any ideas? Could the other (commercial) applications use some other API?

static void CALLBACK myFunc(HMIDIIN handle, UINT uMsg,
                            DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
   printf("Callback!"\n);
}

int main() {

   unsigned long result;
   HMIDIIN       inHandle;

   result = midiInOpen(&inHandle, 0, (DWORD)myFunc, 0, CALLBACK_FUNCTION);
   if (result)
   {
      printf("There was an error opening MIDI\n");
   }

   while(1) { Sleep(1); }
}
like image 995
Pibben Avatar asked Oct 28 '25 19:10

Pibben


2 Answers

You need to call midiInstart. You also need to pump messages. Nothing is going to happen if you're calling Sleep and not servicing input.

Here's a bit of a tutorial on Windows MIDI.

Here's an extract of a class (WinMidiIn) I wrote for Win MIDI input (lots of error handling removed, class member types can be inferred from the calls they are passed to as params):

{
    MMRESULT res = ::midiInOpen(&mMidiIn, mDeviceIdx, (DWORD_PTR)MidiInCallbackProc, (DWORD_PTR)this, 
        CALLBACK_FUNCTION | MIDI_IO_STATUS);
    if (MMSYSERR_NOERROR != res)
        return;

    const int kDataBufLen = 512;
    int idx;
    for (idx = 0; idx < MIDIHDR_CNT; ++idx)
    {
        mMidiHdrs[idx].lpData = (LPSTR) ::malloc(kDataBufLen);
        mMidiHdrs[idx].dwBufferLength = kDataBufLen;

        res = ::midiInPrepareHeader(mMidiIn, &mMidiHdrs[idx], (UINT)sizeof(MIDIHDR));
        res = ::midiInAddBuffer(mMidiIn, &mMidiHdrs[idx], sizeof(MIDIHDR));
    }

    res = ::midiInStart(mMidiIn);

    for (;;)
    {
        DWORD result;
        MSG msg;

        // Read all of the messages in this next loop, 
        // removing each message as we read it.
        while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // If it is a quit message, exit.
            if (msg.message == WM_QUIT)
                break;

            // Otherwise, dispatch the message.
            ::DispatchMessage(&msg);
        }

        // Wait for any message sent or posted to this queue 
        // or for one of the passed handles be set to signaled.
        result = ::MsgWaitForMultipleObjects(1, &mDoneEvent, FALSE, INFINITE, QS_ALLINPUT);

        // The result tells us the type of event we have.
        if (result == (WAIT_OBJECT_0 + 1))
        {
            // New messages have arrived. 
            // Continue to the top of the always while loop to 
            // dispatch them and resume waiting.
            continue;
        }
        else if (WAIT_TIMEOUT == result)
            continue;
        else if (WAIT_OBJECT_0 == result)
            break; // done event fired
        else
            break; // ??
    }

    res = ::midiInReset(mMidiIn);

    for (idx = 0; idx < MIDIHDR_CNT; ++idx)
    {
        res = ::midiInUnprepareHeader(mMidiIn, &mMidiHdrs[idx], (UINT)sizeof(MIDIHDR));
        ::free(mMidiHdrs[idx].lpData);
        mMidiHdrs[idx].lpData = NULL;
    }

    res = ::midiInClose(mMidiIn);
    mMidiIn = NULL;
}



void CALLBACK 
MidiInCallbackProc(HMIDIIN hmi, 
                          UINT wMsg, 
                          DWORD dwInstance, 
                          DWORD dwParam1, 
                          DWORD dwParam2)
{
    MMRESULT res;
    LPMIDIHDR hdr;
    WinMidiIn * _this = (WinMidiIn *) dwInstance;

    switch (wMsg)
    {
    case MIM_DATA:
        //  dwParam1 is the midi event with status in the low byte of the low word
        //  dwParam2 is the event time in ms since the start of midi in
        // data: LOBYTE(dwParam1), HIBYTE(dwParam1), LOBYTE(HIWORD(dwParam1))
        break;
    case MIM_ERROR:
        break;
    case MIM_LONGDATA:
        //  dwParam1 is the lpMidiHdr
        //  dwParam2 is the event time in ms since the start of midi in
        hdr = (LPMIDIHDR) dwParam1;
        // sysex: (byte*)hdr->lpData, (int)hdr->dwBytesRecorded
        res = ::midiInAddBuffer(_this->mMidiIn, hdr, sizeof(MIDIHDR));
        break;
    case MIM_LONGERROR:
        hdr = (LPMIDIHDR) dwParam1;
        res = ::midiInAddBuffer(_this->mMidiIn, hdr, sizeof(MIDIHDR));
        break;
    }
}
like image 190
sean e Avatar answered Oct 30 '25 11:10

sean e


You can find an example here https://gist.github.com/yoggy/1485181 I post the code here in case the link becomes dead

You may be missing the midiInStart()

#include <SDKDDKVer.h>

#include <Windows.h>

#include <stdio.h>
#include <conio.h>

#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")

void PrintMidiDevices()
{
    UINT nMidiDeviceNum;
    MIDIINCAPS caps;

    nMidiDeviceNum = midiInGetNumDevs();
    if (nMidiDeviceNum == 0) {
        fprintf(stderr, "midiInGetNumDevs() return 0...");
        return;
    }

    printf("== PrintMidiDevices() == \n");
    for (unsigned int i = 0; i < nMidiDeviceNum; ++i) {
        midiInGetDevCaps(i, &caps, sizeof(MIDIINCAPS));
        printf("\t%d : name = %s\n", i, caps.szPname);
    }
    printf("=====\n");
}

void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
    switch(wMsg) {
    case MIM_OPEN:
        printf("wMsg=MIM_OPEN\n");
        break;
    case MIM_CLOSE:
        printf("wMsg=MIM_CLOSE\n");
        break;
    case MIM_DATA:
        printf("wMsg=MIM_DATA, dwInstance=%08x, dwParam1=%08x, dwParam2=%08x\n", dwInstance, dwParam1, dwParam2);
        break;
    case MIM_LONGDATA:
        printf("wMsg=MIM_LONGDATA\n"); 
        break;
    case MIM_ERROR:
        printf("wMsg=MIM_ERROR\n");
        break;
    case MIM_LONGERROR:
        printf("wMsg=MIM_LONGERROR\n");
        break;
    case MIM_MOREDATA:
        printf("wMsg=MIM_MOREDATA\n");
        break;
    default:
        printf("wMsg = unknown\n");
        break;
    }
    return;
}

int main(int argc, char* argv[])
{
    HMIDIIN hMidiDevice = NULL;;
    DWORD nMidiPort = 0;
    UINT nMidiDeviceNum;
    MMRESULT rv;

    PrintMidiDevices();

    nMidiDeviceNum = midiInGetNumDevs();
    if (nMidiDeviceNum == 0) {
        fprintf(stderr, "midiInGetNumDevs() return 0...");
        return -1;
    }

    rv = midiInOpen(&hMidiDevice, nMidiPort, (DWORD)(void*)MidiInProc, 0, CALLBACK_FUNCTION);
    if (rv != MMSYSERR_NOERROR) {
        fprintf(stderr, "midiInOpen() failed...rv=%d", rv);
        return -1;
    }

    midiInStart(hMidiDevice);

    while(true) {
        if (!_kbhit()) {
            Sleep(100);
            continue;
        }
        int c = _getch();
        if (c == VK_ESCAPE) break;
        if (c == 'q') break;
    }

    midiInStop(hMidiDevice);
    midiInClose(hMidiDevice);
    hMidiDevice = NULL;

    return 0;
}
like image 35
Maxime Viargues Avatar answered Oct 30 '25 12:10

Maxime Viargues