Testé à l'époque : j'envoyais un sysex pour explorer le réseau MIDI, et je détectais un Triton.
Donc les sysexes fonctionnent correctement avec ce code.
Attention: bien allouer / désallouer des buffers pour les sysexes avec HeapAlloc() / HeapFree() !!! (en relisant mon code de débutant en C++ (snif), j'ai l'impression qu'un new/delete devraient aussi convenir, mais que je passais des buffers provenant de la pile, ce qui fait planter le pilote MIDI une fois qu'on a quitté la fonction, allez savoir pourquoi :oups)
D'ici quelques mois, j'aurai réutilisé ce fragment, ou pas (je pense plutôt passer par quelque chose de mieux testé, tel RTMidi)
#ifndef __MIDIDRIVER_H__ #define __MIDIDRIVER_H__ #include <mmsystem.h> #include "charlib.h" class CMidiOut { private: int iMidiDevice; HMIDIOUT handle; void Open(int iMidiOutDevice); void Close(void); public: CMidiOut(int iMidiOutDevice); ~CMidiOut(void); operator bool(void) { return (handle != 0); } bool SendSysex(const CChunk<unsigned char> *C); }; const int maxsysx = 100; class CMidiIn { private: int iMidiDevice; HMIDIOUT handle; MIDIHDR midiHdr; CChunkList<unsigned char> cursysex; public: CChunkList<unsigned char> sysexs; CMidiIn(int bufsize = 1024); ~CMidiIn(); void Start(int iMidiDevice); void Stop(void); friend void CALLBACK midiCallback(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); }; CChunk<unsigned char>* SysexDialog(int iMidiOutDevice, int iMidiInDevice, CChunk<unsigned char> *SysexOut, unsigned timeout = 200); bool TestSysexConnection(int iMidiOutDevice, int iMidiInDevice, unsigned timeout = 200); #endif
#include <vcl.h> #include "MidiDriver.h" #include "sysex.h" //--------------------------------------------------------------------------- void PrintMidiOutErrorMsg(unsigned long err) { char buffer[1024]; switch (midiOutGetErrorText(err, buffer, 1024)) { case 0: ShowMessage(buffer); break; case MMSYSERR_BADERRNUM: ShowMessage("Strange error number returned!"); break; case MMSYSERR_INVALPARAM: ShowMessage("Specified pointer is invalid!"); break; default: ShowMessage("Unable to allocate/lock memory!"); } } //--------------------------------------------------------------------------- CMidiOut::CMidiOut(int iMidiOutDevice) { Open(iMidiOutDevice); } CMidiOut::~CMidiOut() { Close(); } void CMidiOut::Open(int iMidiOutDevice) { unsigned err; err = midiOutOpen(&handle, iMidiDevice = iMidiOutDevice, 0, 0, CALLBACK_NULL); if (err) PrintMidiOutErrorMsg(err); } void CMidiOut::Close(void) { if (handle) { unsigned err; err = midiOutClose(handle); if (err) PrintMidiOutErrorMsg(err); } handle = 0; } bool CMidiOut::SendSysex(const CChunk<unsigned char> *C) { MIDIHDR midiHdr; bool res = false; unsigned err; if (C && C->len) { midiHdr.lpData = C->buf; midiHdr.dwBufferLength = C->len; midiHdr.dwFlags = 0; err = midiOutPrepareHeader(handle, &midiHdr, sizeof(MIDIHDR)); if (err) PrintMidiOutErrorMsg(err); else { midiOutLongMsg(handle, &midiHdr, sizeof(MIDIHDR)); while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(handle, &midiHdr, sizeof(MIDIHDR))) ; res = true; } } return res; } //--------------------------------------------------------------------------- void PrintMidiInErrorMsg(unsigned long err) { char buffer[1024]; switch (midiInGetErrorText(err, buffer, 1024)) { case 0: ShowMessage(buffer); break; case MMSYSERR_BADERRNUM: ShowMessage("Strange error number returned!"); break; case MMSYSERR_INVALPARAM: ShowMessage("Specified pointer is invalid!"); break; default: ShowMessage("Unable to allocate/lock memory!"); } } //--------------------------------------------------------------------------- void CALLBACK midiCallback(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { switch (uMsg) { case MIM_LONGDATA: { unsigned err; LPMIDIHDR lpMidiHeader = (LPMIDIHDR)dwParam1; unsigned char *ptr = (unsigned char *)(lpMidiHeader->lpData); unsigned bytes = lpMidiHeader->dwBytesRecorded; if (bytes) { CMidiIn *recorder = (CMidiIn*) dwInstance; recorder->cursysex.Add(new CChunk<unsigned char>(bytes, ptr)); if (ptr[--bytes] == 0xf7) { recorder->sysexs.Add(recorder->cursysex.Compact()); recorder->cursysex.Clear(); } lpMidiHeader->dwBytesRecorded = 0; if (0 != (err = midiInAddBuffer(handle, lpMidiHeader, sizeof(MIDIHDR)))) PrintMidiInErrorMsg(err); } break; } case MIM_OPEN: break; case MIM_CLOSE: break; case MIM_ERROR: break; case MIM_LONGERROR: break; case MIM_MOREDATA: break; } } //--------------------------------------------------------------------------- CMidiIn::CMidiIn(int bufsize) { handle = 0; midiHdr.dwFlags = 0; midiHdr.lpData = (char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize); midiHdr.dwBufferLength = bufsize; cursysex.Clear(); }; CMidiIn::~CMidiIn() { Stop(); HeapFree(GetProcessHeap(), 0, midiHdr.lpData); }; void CMidiIn::Start(int iMidiDevice) { long err; sysexs.Clear(); cursysex.Clear(); if ((err = midiInOpen(&handle, iMidiDevice, (DWORD)midiCallback, (DWORD)this, CALLBACK_FUNCTION | MIDI_IO_STATUS)) == 0) { midiInPrepareHeader(handle, &midiHdr, sizeof(MIDIHDR)); midiInAddBuffer(handle, &midiHdr, sizeof(MIDIHDR)); midiInStart(handle); } else PrintMidiInErrorMsg(err); } void CMidiIn::Stop(void) { long err; if (handle) { if ((err = midiInReset(handle)) != 0) PrintMidiInErrorMsg(err); midiInUnprepareHeader(handle, &midiHdr, sizeof(MIDIHDR)); if ((err = midiInClose(handle)) != 0) PrintMidiInErrorMsg(err); handle = 0; } } //--------------------------------------------------------------------------- void CALLBACK midiCallbackForDialog(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { if (uMsg == MIM_LONGDATA) { unsigned err; LPMIDIHDR lpMidiHeader = (LPMIDIHDR)dwParam1; unsigned char *ptr = (unsigned char *)(lpMidiHeader->lpData); unsigned bytes = lpMidiHeader->dwBytesRecorded; if (bytes) { CChunkList<unsigned char> *cursysex = (CChunkList<unsigned char>*) dwInstance; cursysex->Add(new CChunk<unsigned char>(bytes, ptr)); lpMidiHeader->dwUser++; if (ptr[--bytes] != 0xf7) { lpMidiHeader->dwBytesRecorded = 0; if (0 != (err = midiInAddBuffer(handle, lpMidiHeader, sizeof(MIDIHDR)))) PrintMidiInErrorMsg(err); } } } } CChunk<unsigned char>* SysexDialog(int iMidiOutDevice, int iMidiInDevice, CChunk<unsigned char> *SysexOut, unsigned timeout) { CChunkList<unsigned char> cursysex; CMidiOut midiout(iMidiOutDevice); if (midiout && SysexOut) { long err; unsigned long start; HMIDIOUT handle; LPMIDIHDR midiHdr = (LPMIDIHDR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MIDIHDR)); midiHdr->dwFlags = 0; midiHdr->dwUser = 0; /* used for "Got first Long_message" */ midiHdr->lpData = (char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024); midiHdr->dwBufferLength = 1024; if ((err = midiInOpen(&handle, iMidiInDevice, (DWORD)midiCallbackForDialog, (DWORD)&cursysex, CALLBACK_FUNCTION | MIDI_IO_STATUS)) == 0) { midiInPrepareHeader(handle, midiHdr, sizeof(MIDIHDR)); midiInAddBuffer(handle, midiHdr, sizeof(MIDIHDR)); midiInStart(handle); midiout.SendSysex(SysexOut); start = GetTickCount(); while (midiHdr->dwFlags & MHDR_INQUEUE) { if (!midiHdr->dwUser && ((GetTickCount() - start) > timeout)) break; SleepEx(100, TRUE); } if ((err = midiInReset(handle)) != 0) PrintMidiInErrorMsg(err); midiInUnprepareHeader(handle, midiHdr, sizeof(MIDIHDR)); if ((err = midiInClose(handle)) != 0) PrintMidiInErrorMsg(err); } else PrintMidiInErrorMsg(err); HeapFree(GetProcessHeap(), 0, midiHdr->lpData); HeapFree(GetProcessHeap(), 0, midiHdr); } return cursysex.Compact(); } bool TestSysexConnection(int iMidiOutDevice, int iMidiInDevice, unsigned timeout) { bool res = false; if ((iMidiOutDevice > -2) && (iMidiInDevice > -2)) { CChunk<unsigned char> *cursysex = SysexDialog(iMidiOutDevice, iMidiInDevice, SysEx::DeviceInquiry, timeout); res = (cursysex->len > 0); delete cursysex; } return res; }
Commentaire