开发者

Trouble using Windows MIDI API (no callbacks when playing)

开发者 https://www.devze.com 2023-02-27 00:37 出处:网络
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 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.

开发者_如何学C

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); }
}


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;
    }
}


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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号