void MidiUartWinClass::midiSendShort(unsigned char status, unsigned char byte1, unsigned char byte2) { midiOutShortMsg(outHandle, MAKE_SHORT_MSG(status, byte1, byte2)); }
void midi_Instrument(HMIDIOUT * midiout, int chn, int inst) { if (inst<128) midiOutShortMsg(*midiout, 256*inst+191+chn); // 1-127: Instruments for noteOn, 128 - 255: Controller numbers }
static void test_midiOut_device(UINT udev, HWND hwnd) { HMIDIOUT hm; MMRESULT rc; MIDIOUTCAPSA capsA; DWORD ovolume; UINT udevid; MIDIHDR mhdr; rc = midiOutGetDevCapsA(udev, &capsA, sizeof(capsA)); ok(!rc, "midiOutGetDevCaps(dev=%d) rc=%s\n", udev, mmsys_error(rc)); if (!rc) { trace("* %s: manufacturer=%d, product=%d, tech=%d, support=%X: %d voices, %d notes\n", capsA.szPname, capsA.wMid, capsA.wPid, capsA.wTechnology, capsA.dwSupport, capsA.wVoices, capsA.wNotes); ok(!((MIDIMAPPER==udev) ^ (MOD_MAPPER==capsA.wTechnology)), "technology %d on device %d\n", capsA.wTechnology, udev); if (MOD_MIDIPORT == capsA.wTechnology) { ok(capsA.wVoices == 0 && capsA.wNotes == 0, "external device with notes or voices\n"); ok(capsA.wChannelMask == 0xFFFF, "external device channel mask %x\n", capsA.wChannelMask); ok(!(capsA.dwSupport & (MIDICAPS_VOLUME|MIDICAPS_LRVOLUME|MIDICAPS_CACHE)), "external device support=%X\n", capsA.dwSupport); } } if (hwnd) rc = midiOutOpen(&hm, udev, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW); else rc = midiOutOpen(&hm, udev, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION); if (rc == MMSYSERR_NOTSUPPORTED) { skip( "MIDI out not supported\n" ); return; } ok(!rc, "midiOutOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc)); if (rc) return; test_notification(hwnd, "midiOutOpen", MOM_OPEN, 0); rc = midiOutGetVolume(hm, &ovolume); ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume rc=%s\n", mmsys_error(rc)); /* The native mapper responds with FFFFFFFF initially, * real devices with the volume GUI SW-synth settings. */ if (!rc) trace("Current volume %x on device %d\n", ovolume, udev); /* The W95 ESFM Synthesis device reports NOTENABLED although * GetVolume by handle works and music plays. */ rc = midiOutGetVolume(UlongToHandle(udev), &ovolume); ok((capsA.dwSupport & MIDICAPS_VOLUME) ? rc==MMSYSERR_NOERROR || broken(rc==MMSYSERR_NOTENABLED) : rc==MMSYSERR_NOTSUPPORTED, "midiOutGetVolume(dev=%d) rc=%s\n", udev, mmsys_error(rc)); rc = midiOutGetVolume(hm, NULL); ok(rc==MMSYSERR_INVALPARAM, "midiOutGetVolume NULL rc=%s\n", mmsys_error(rc)); /* Tests with midiOutSetvolume show that the midi mapper forwards * the value to the real device, but Get initially always reports * FFFFFFFF. Therefore, a Get+SetVolume pair with the mapper is * not adequate to restore the value prior to tests. */ if (winetest_interactive && (capsA.dwSupport & MIDICAPS_VOLUME)) { DWORD volume2 = (ovolume < 0x80000000) ? 0xC000C000 : 0x40004000; rc = midiOutSetVolume(hm, volume2); ok(!rc, "midiOutSetVolume rc=%s\n", mmsys_error(rc)); if (!rc) { DWORD volume3; rc = midiOutGetVolume(hm, &volume3); ok(!rc, "midiOutGetVolume new rc=%s\n", mmsys_error(rc)); if (!rc) trace("New volume %x on device %d\n", volume3, udev); todo_wine ok(volume2==volume3, "volume Set %x = Get %x\n", volume2, volume3); rc = midiOutSetVolume(hm, ovolume); ok(!rc, "midiOutSetVolume restore rc=%s\n", mmsys_error(rc)); } } rc = midiOutGetDevCapsA((UINT_PTR)hm, &capsA, sizeof(capsA)); ok(!rc, "midiOutGetDevCaps(dev=%d) by handle rc=%s\n", udev, mmsys_error(rc)); rc = midiInGetDevCapsA((UINT_PTR)hm, (LPMIDIINCAPSA)&capsA, sizeof(DWORD)); ok(rc==MMSYSERR_BADDEVICEID, "midiInGetDevCaps(dev=%d) by out handle rc=%s\n", udev, mmsys_error(rc)); { DWORD e = 0x006F4893; /* velocity, note (#69 would be 440Hz) channel */ trace("ShortMsg type %x\n", LOBYTE(LOWORD(e))); rc = midiOutShortMsg(hm, e); ok(!rc, "midiOutShortMsg rc=%s\n", mmsys_error(rc)); if (!rc) Sleep(400); /* Hear note */ } memset(&mhdr, 0, sizeof(mhdr)); mhdr.dwFlags = MHDR_DONE; mhdr.dwUser = 0x56FA552C; mhdr.dwOffset = 0xDEADBEEF; mhdr.dwBufferLength = 70000; /* > 64KB! */ mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength); ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength); if (mhdr.lpData) { rc = midiOutLongMsg(hm, &mhdr, sizeof(mhdr)); ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags); test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER); rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1); ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags); /* Since at least w2k, midiOutPrepare clears the DONE and INQUEUE flags. w95 didn't. */ /* mhdr.dwFlags |= MHDR_INQUEUE; would cause w95 to return STILLPLAYING from Unprepare */ rc = midiOutPrepareHeader(hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE)/*w9x*/ || mhdr.dwFlags == MHDR_PREPARED, "dwFlags=%x\n", mhdr.dwFlags); trace("MIDIHDR flags=%x when unsent\n", mhdr.dwFlags); /* No flag is cleared when already prepared. */ mhdr.dwFlags |= MHDR_DONE|MHDR_INQUEUE; rc = midiOutPrepareHeader(hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags); mhdr.dwFlags |= MHDR_INQUEUE; rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr)); ok(rc==MIDIERR_STILLPLAYING, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags == (MHDR_PREPARED|MHDR_DONE|MHDR_INQUEUE), "dwFlags=%x\n", mhdr.dwFlags); mhdr.dwFlags &= ~MHDR_INQUEUE; rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags == MHDR_DONE, "dwFlags=%x\n", mhdr.dwFlags); mhdr.dwFlags |= MHDR_INQUEUE; rc = midiOutUnprepareHeader(hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags == (MHDR_INQUEUE|MHDR_DONE), "dwFlags=%x\n", mhdr.dwFlags); HeapFree(GetProcessHeap(), 0, mhdr.lpData); } ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser); ok(mhdr.dwOffset==0xDEADBEEF, "MIDIHDR.dwOffset changed to %x\n", mhdr.dwOffset); rc = midiOutGetID(hm, &udevid); ok(!rc, "midiOutGetID rc=%s\n", mmsys_error(rc)); if(!rc) ok(udevid==udev, "midiOutGetID gives %d, expect %d\n", udevid, udev); rc = midiOutReset(hm); /* Quiet everything */ ok(!rc, "midiOutReset rc=%s\n", mmsys_error(rc)); rc = midiOutClose(hm); ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiOutClose", MOM_CLOSE, 0); rc = midiOutOpen(&hm, udev, 0, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW); /* w95 broken(rc==MMSYSERR_INVALPARAM) see WINMM_CheckCallback */ ok(!rc, "midiOutOpen(dev=%d) 0 CALLBACK_WINDOW rc=%s\n", udev, mmsys_error(rc)); /* PostMessage(hwnd=0) redirects to PostThreadMessage(GetCurrentThreadId()) * which PeekMessage((HWND)-1) queries. */ test_notification((HWND)-1, "midiOutOpen WINDOW->THREAD", 0, WHATEVER); test_notification(hwnd, "midiOutOpen WINDOW", 0, WHATEVER); if (!rc) { rc = midiOutClose(hm); ok(!rc, "midiOutClose rc=%s\n", mmsys_error(rc)); test_notification((HWND)-1, "midiOutClose WINDOW->THREAD", 0, WHATEVER); test_notification(hwnd, "midiOutClose", 0, WHATEVER); } test_notification(hwnd, "midiOut over", 0, WHATEVER); }
static void midi_win32short(CMMIDI midi, UINT32 msg) { waitlastexclusiveout(midi); midiOutShortMsg(midi->out.win32.hmidiout, (DWORD)msg); }
// ----------------------------------------------------------------------- // メッセージ判別 // ----------------------------------------------------------------------- void MIDI_Message(BYTE mes) { if (!hOut) { return; } switch(mes) { // ここの対応はお好みで case MIDI_TIMING: case MIDI_START: case MIDI_CONTINUE: case MIDI_STOP: case MIDI_ACTIVESENSE: return; case MIDI_SYSTEMRESET: // 一応イリーガル〜 return; } if (MIDI_CTRL == MIDICTRL_READY) { // 初回限定 if (mes & 0x80) { // status MIDI_POS = 0; switch(mes & 0xf0) { case 0xc0: case 0xd0: MIDI_CTRL = MIDICTRL_2BYTES; break; case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: MIDI_LAST = mes; // この方が失敗しないなり… MIDI_CTRL = MIDICTRL_3BYTES; break; default: switch(mes) { case MIDI_EXCLUSIVE: MIDI_CTRL = MIDICTRL_EXCLUSIVE; break; case MIDI_TIMECODE: MIDI_CTRL = MIDICTRL_TIMECODE; break; case MIDI_SONGPOS: MIDI_CTRL = MIDICTRL_SYSTEM; MIDI_SYSCOUNT = 3; break; case MIDI_SONGSELECT: MIDI_CTRL = MIDICTRL_SYSTEM; MIDI_SYSCOUNT = 2; break; case MIDI_TUNEREQUEST: MIDI_CTRL = MIDICTRL_SYSTEM; MIDI_SYSCOUNT = 1; break; default: return; } break; } } else { // Key-onのみな気がしたんだけど忘れた… // running status MIDI_BUF[0] = MIDI_LAST; MIDI_POS = 1; MIDI_CTRL = MIDICTRL_3BYTES; } } else if ( (mes&0x80) && ((MIDI_CTRL!=MIDICTRL_EXCLUSIVE)||(mes!=MIDI_EOX)) ) { // メッセージのデータ部にコントロールバイトが出た時…(GENOCIDE2) // status MIDI_POS = 0; switch(mes & 0xf0) { case 0xc0: case 0xd0: MIDI_CTRL = MIDICTRL_2BYTES; break; case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: MIDI_LAST = mes; // この方が失敗しないなり… MIDI_CTRL = MIDICTRL_3BYTES; break; default: switch(mes) { case MIDI_EXCLUSIVE: MIDI_CTRL = MIDICTRL_EXCLUSIVE; break; case MIDI_TIMECODE: MIDI_CTRL = MIDICTRL_TIMECODE; break; case MIDI_SONGPOS: MIDI_CTRL = MIDICTRL_SYSTEM; MIDI_SYSCOUNT = 3; break; case MIDI_SONGSELECT: MIDI_CTRL = MIDICTRL_SYSTEM; MIDI_SYSCOUNT = 2; break; case MIDI_TUNEREQUEST: MIDI_CTRL = MIDICTRL_SYSTEM; MIDI_SYSCOUNT = 1; break; default: return; } break; } } MIDI_BUF[MIDI_POS++] = mes; switch(MIDI_CTRL) { case MIDICTRL_2BYTES: if (MIDI_POS >= 2) { if (ENABLE_TONEMAP) { if (((MIDI_BUF[0] & 0xf0) == 0xc0) && (TONE_CH[MIDI_BUF[0] & 0x0f] < MIMPI_RHYTHM)) { MIDI_BUF[1] = TONEMAP[ TONE_CH[MIDI_BUF[0] & 0x0f] ][ MIDI_BUF[1] & 0x7f ]; } } MIDI_Waitlastexclusiveout(); midiOutShortMsg(hOut, MIDIOUTS(MIDI_BUF[0], MIDI_BUF[1], 0)); MIDI_CTRL = MIDICTRL_READY; } break; case MIDICTRL_3BYTES: if (MIDI_POS >= 3) { MIDI_Waitlastexclusiveout(); midiOutShortMsg(hOut, MIDIOUTS(MIDI_BUF[0], MIDI_BUF[1], MIDI_BUF[2])); MIDI_CTRL = MIDICTRL_READY; } break; case MIDICTRL_EXCLUSIVE: if (mes == MIDI_EOX) { MIDI_Waitlastexclusiveout(); MIDI_Sendexclusive(MIDI_BUF, MIDI_POS); MIDI_CTRL = MIDICTRL_READY; } else if (MIDI_POS >= MIDIBUFFERS) { // おーばーふろー MIDI_CTRL = MIDICTRL_READY; } break; case MIDICTRL_TIMECODE: if (MIDI_POS >= 2) { if ((mes == 0x7e) || (mes == 0x7f)) { // exclusiveと同じでいい筈… MIDI_CTRL = MIDICTRL_EXCLUSIVE; } else { MIDI_CTRL = MIDICTRL_READY; } } break; case MIDICTRL_SYSTEM: if (MIDI_POS >= MIDI_SYSCOUNT) { MIDI_CTRL = MIDICTRL_READY; } break; } }
static void midi_flush_current_buffer(void) { MMRESULT rv; MIDIEVENT * evt; BOOL needsPrepare = FALSE; if (!currentMidiBuffer) { return; } evt = (MIDIEVENT *) currentMidiBuffer->hdr.lpData; if (!midiThread) { // immediate messages don't use a MIDIEVENT header so strip it off and // make some adjustments currentMidiBuffer->hdr.dwBufferLength = currentMidiBuffer->hdr.dwBytesRecorded - 12; currentMidiBuffer->hdr.dwBytesRecorded = 0; currentMidiBuffer->hdr.lpData = (LPSTR) &evt->dwParms[0]; if (currentMidiBuffer->hdr.dwBufferLength > 0) { needsPrepare = TRUE; } } else { needsPrepare = TRUE; } if (needsPrepare) { // playing a file, or sending a sysex when not playing means // we need to prepare the buffer rv = midiOutPrepareHeader( (HMIDIOUT) midiStream, ¤tMidiBuffer->hdr, sizeof(MIDIHDR) ); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM midi_flush_current_buffer midiOutPrepareHeader"); return; } currentMidiBuffer->prepared = TRUE; } if (midiThread) { // midi file playing, so send events to the stream LL_Add( (MidiBuffer*) &activeMidiBuffers, currentMidiBuffer, next, prev ); rv = midiStreamOut(midiStream, ¤tMidiBuffer->hdr, sizeof(MIDIHDR)); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM midi_flush_current_buffer midiStreamOut"); midi_dispose_buffer(currentMidiBuffer, "midi_flush_current_buffer"); return; } //fprintf(stderr, "WinMM midi_flush_current_buffer queued buffer %p\n", currentMidiBuffer); } else { // midi file not playing, so send immediately if (currentMidiBuffer->hdr.dwBufferLength > 0) { rv = midiOutLongMsg( (HMIDIOUT) midiStream, ¤tMidiBuffer->hdr, sizeof(MIDIHDR) ); if (rv == MMSYSERR_NOERROR) { // busy-wait for Windows to be done with it while (!(currentMidiBuffer->hdr.dwFlags & MHDR_DONE)) ; //fprintf(stderr, "WinMM midi_flush_current_buffer sent immediate long\n"); } else { midi_error(rv, "WinMM midi_flush_current_buffer midiOutLongMsg"); } } else { rv = midiOutShortMsg( (HMIDIOUT) midiStream, evt->dwEvent ); if (rv == MMSYSERR_NOERROR) { //fprintf(stderr, "WinMM midi_flush_current_buffer sent immediate short\n"); } else { midi_error(rv, "WinMM midi_flush_current_buffer midiOutShortMsg"); } } midi_dispose_buffer(currentMidiBuffer, "midi_flush_current_buffer"); } currentMidiBuffer = 0; }
void WinMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count) { // Setting snd_midiprecache to false disables this precaching, since it // does involve sleeping for more than a miniscule amount of time. if (!snd_midiprecache) { return; } uint8_t bank[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int i, chan; for (i = 0, chan = 0; i < count; ++i) { int instr = instruments[i] & 127; int banknum = (instruments[i] >> 7) & 127; int percussion = instruments[i] >> 14; if (percussion) { if (bank[9] != banknum) { midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (banknum << 16)); bank[9] = banknum; } midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | 9 | ((instruments[i] & 0x7f) << 8) | (1 << 16)); } else { // Melodic if (bank[chan] != banknum) { midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (banknum << 16)); bank[chan] = banknum; } midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_PRGMCHANGE | chan | (instruments[i] << 8)); midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | chan | (60 << 8) | (1 << 16)); if (++chan == 9) { // Skip the percussion channel chan = 10; } } // Once we've got an instrument playing on each melodic channel, sleep to give // the driver time to load the instruments. Also do this for the final batch // of instruments. if (chan == 16 || i == count - 1) { Sleep(250); for (chan = 15; chan-- != 0; ) { // Turn all notes off midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | chan | (123 << 8)); } // And now chan is back at 0, ready to start the cycle over. } } // Make sure all channels are set back to bank 0. for (i = 0; i < 16; ++i) { if (bank[i] != 0) { midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (0 << 16)); } } }
Synthesizer::Synthesizer(int instrument) { midiOutOpen(&device, -1, 0, 0, 0); // Set instrument to 0 = Acoustic Grand Piano midiOutShortMsg(device, DWORD(0x0C0 | 0 | (instrument << 8) | (0 << 16))); }
static DWORD modData(MIDIMAPDATA* mom, DWORD_PTR dwParam) { BYTE lb = LOBYTE(LOWORD(dwParam)); WORD chn = lb & 0x0F; DWORD ret = MMSYSERR_NOERROR; if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR; if (!mom->ChannelMap[chn]) return MMSYSERR_NOERROR; switch (lb & 0xF0) { case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xC0: case 0xD0: case 0xE0: if (mom->ChannelMap[chn]->loaded == 0) { if (midiOutOpen(&mom->ChannelMap[chn]->hMidi, mom->ChannelMap[chn]->uDevID, 0L, 0L, CALLBACK_NULL) == MMSYSERR_NOERROR) mom->ChannelMap[chn]->loaded = 1; else mom->ChannelMap[chn]->loaded = -1; /* FIXME: should load here the IDF midi data... and allow channel and * patch mappings */ } if (mom->ChannelMap[chn]->loaded > 0) { /* change channel */ dwParam &= ~0x0F; dwParam |= mom->ChannelMap[chn]->aChn[chn]; if ((LOBYTE(LOWORD(dwParam)) & 0xF0) == 0xC0 /* program change */ && mom->ChannelMap[chn]->lpbPatch) { BYTE patch = HIBYTE(LOWORD(dwParam)); /* change patch */ dwParam &= ~0x0000FF00; dwParam |= mom->ChannelMap[chn]->lpbPatch[patch]; } ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam); } break; case 0xF0: for (chn = 0; chn < 16; chn++) { if (mom->ChannelMap[chn]->loaded > 0) ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam); } break; default: FIXME("ooch %lx\n", dwParam); } return ret; }
void midiSendShort(unsigned char status, unsigned char byte1, unsigned char byte2) { debugPrintf(2, "midiSendShort %x %x %x\n", status, byte1, byte2); midiOutShortMsg(outHandle, MAKE_SHORT_MSG(status, byte1, byte2)); }
void CMidiDevice::Out (WORD wPort_, BYTE bVal_) { // Protect against very long System Exclusive blocks if ((m_nOut == (sizeof(m_abOut)-1)) && bVal_ != 0xf7) { TRACE("!!! MIDI: System Exclusive buffer overflow, discarding %#02x\n", bVal_); return; } // Do we have the start of a message while an incomplete message remains? if (m_nOut && (bVal_ & 0x80)) { TRACE("!!! MIDI: Discarding incomplete %d byte message\n", m_nOut); m_nOut = 0; } // Is the start of the message a non-status byte? else if (!m_nOut && !(bVal_ & 0x80)) { // Use the previous status byte if there is one if (m_abOut[0] & 0x80) m_nOut = 1; // Discard the byte as there isn't much we can do with it else { TRACE("!!! MIDI: Discarding leading non-status byte: %#02x\n", bVal_); return; } } // Add the new byte to the message we're building up m_abOut[m_nOut++] = bVal_; // Spot the end of a System Exclusive variable length block (we don't do anything with it yet) if (m_abOut[0] == 0xf0 && bVal_ == 0xf7) TRACE("MIDI: Variable block of %d bytes\n", m_nOut - 2); // Break out if the command we're building up hasn't got the required number of parameters yet else if (((m_abOut[0] & 0xfd) == 0xf1) || ((m_abOut[0] & 0xe0) == 0xc0)) // 1 byte { if (m_nOut != 2) return; } else if ((m_abOut[0] & 0xf0) == 0xf0) // 0 bytes { if (m_nOut != 1) return; } else { if (m_nOut != 3) return; } #ifdef _DEBUG switch (m_nOut) { case 1: TRACE("MIDI: Sending 1 byte message from: %02x\n", m_abOut[0]); break; case 2: TRACE("MIDI: Sending 2 byte message from: %02x %02x\n", m_abOut[0], m_abOut[1]); break; case 3: TRACE("MIDI: Sending 3 byte message from: %02x %02x %02x\n", m_abOut[0], m_abOut[1], m_abOut[2]); break; case 4: TRACE("MIDI: Sending 4 byte message from: %02x %02x %02x %02x\n", m_abOut[0], m_abOut[1], m_abOut[2], m_abOut[3]); break; default: TRACE("MIDI: Sending %d byte message from: %02x %02x %02x %02x ...\n", m_nOut, m_abOut[0], m_abOut[1], m_abOut[2], m_abOut[3]); break; } #endif // Output the MIDI message if (m_hMidiOut) midiOutShortMsg(m_hMidiOut, *reinterpret_cast<DWORD*>(&m_abOut)); // Prepare for the next message, clearing out m_nOut = m_abOut[1] = m_abOut[2] = m_abOut[3] = 0; }