static int BlockOut(NativeMidiSong *song) { MMRESULT err; int BlockSize; if ((song->MusicLoaded) && (song->NewEvents)) { // proff 12/8/98: Added for savety midiOutUnprepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR)); if (song->NewPos>=song->Size) return 0; BlockSize=(song->Size-song->NewPos); if (BlockSize<=0) return 0; if (BlockSize>36000) BlockSize=36000; song->MidiStreamHdr.lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos); song->NewPos+=BlockSize; song->MidiStreamHdr.dwBufferLength=BlockSize; song->MidiStreamHdr.dwBytesRecorded=BlockSize; song->MidiStreamHdr.dwFlags=0; err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR)); if (err!=MMSYSERR_NOERROR) return 0; err=midiStreamOut(hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR)); return 0; } return 1; }
static PmError winmm_write_flush(PmInternal *midi, PmTimestamp timestamp) { midiwinmm_type m = (midiwinmm_type) midi->descriptor; assert(m); if (m->hdr) { m->error = midiOutPrepareHeader(m->handle.out, m->hdr, sizeof(MIDIHDR)); if (m->error) { /* do not send message */ } else if (midi->latency == 0) { /* As pointed out by Nigel Brown, 20Sep06, dwBytesRecorded * should be zero. This is set in get_free_sysex_buffer(). * The msg length goes in dwBufferLength in spite of what * Microsoft documentation says (or doesn't say). */ m->hdr->dwBufferLength = m->hdr->dwBytesRecorded; m->hdr->dwBytesRecorded = 0; m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR)); } else { m->error = midiStreamOut(m->handle.stream, m->hdr, sizeof(MIDIHDR)); } midi->fill_base = NULL; m->hdr = NULL; if (m->error) { m->hdr->dwFlags = 0; /* release the buffer */ return pmHostError; } } return pmNoError; }
static MMRESULT playStream(HMIDISTRM hm, LPMIDIHDR lpMidiHdr) { MMRESULT rc = midiStreamOut(hm, lpMidiHdr, sizeof(MIDIHDR)); /* virtual machines may return MIDIERR_STILLPLAYING from the next request * even after MHDR_DONE is set. It's still too early, so add MHDR_INQUEUE. */ if (!rc) while (!(lpMidiHdr->dwFlags & MHDR_DONE) || (lpMidiHdr->dwFlags & MHDR_INQUEUE)) { Sleep(100); } return rc; }
void MPU_FinishBuffer( int buffer ) { if (!eventcnt[buffer]) return; ZeroMemory(&bufferheaders[buffer], sizeof(MIDIHDR)); bufferheaders[buffer].lpData = eventbuf[buffer]; bufferheaders[buffer].dwBufferLength = bufferheaders[buffer].dwBytesRecorded = eventcnt[buffer]; midiOutPrepareHeader((HMIDIOUT)hmido, &bufferheaders[buffer], sizeof(MIDIHDR)); midiStreamOut(hmido, &bufferheaders[buffer], sizeof(MIDIHDR)); // printf("Sending %d bytes (buffer %d)\n",eventcnt[buffer],buffer); _MPU_BuffersWaiting++; }
static void FAR PASCAL s_MidiCallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) { WinmidiObject *obj = (WinmidiObject *) dwUser; int retVal; UNUSED(hms); UNUSED(dw1); UNUSED(dw2); assert(obj); /* Only process Done messages. */ if(uMsg != MOM_DONE) { return; } while(obj->m_playNode) { /* Clear the playing flag. */ obj->m_playNode->m_blockState = BLOCK_PLAYED; /* Play the next block, if available. */ obj->m_playNode = obj->m_playNode->m_next; if(obj->m_playNode) { /* Check to see if we have exhausted the ready blocks. */ if(obj->m_playNode->m_blockState != BLOCK_READY) { return; } obj->m_playNode->m_blockState = BLOCK_PLAYING; retVal = midiStreamOut(obj->m_midiOut, &obj->m_playNode->m_header, sizeof(obj->m_playNode->m_header)); if(retVal == MMSYSERR_NOERROR) { return; } else { fprintf(stderr, "Error occurred while advancing MIDI block pointer.\n"); } } else { retVal = midiStreamPause(obj->m_midiOut); if(retVal != MMSYSERR_NOERROR) { fprintf(stderr, "Error occurred while pausing MIDI playback."); } return; } } }
static int s_PrepareBlockNodes(WinmidiObject *obj) { UINT retVal; MidiBlockNode *node; MidiBlockNode *firstReady = 0; assert(obj); /* Go through the list and prepare all written blocks. */ node = obj->m_list; while(node) { if(node->m_blockState == BLOCK_WRITING) { if(!firstReady) { firstReady = node; } retVal = midiOutPrepareHeader((HMIDIOUT)obj->m_midiOut, &node->m_header, sizeof(node->m_header)); if(retVal != MMSYSERR_NOERROR) { s_SetMidiError("preparing header", retVal); return 0; } node->m_blockState = BLOCK_READY; } node = node->m_next; } /* If we actually prepared some blocks for playing, queue the first * one. */ if(!obj->m_playNode && firstReady) { obj->m_playNode = firstReady; (void)midiStreamOut(obj->m_midiOut, &firstReady->m_header, sizeof(firstReady->m_header)); } retVal = midiStreamRestart(obj->m_midiOut); if(retVal != MMSYSERR_NOERROR) { s_SetMidiError("restarting MIDI stream", retVal); return 0; } return 1; }
static void test_midiStream(UINT udev, HWND hwnd) { HMIDISTRM hm; MMRESULT rc, rc2; MIDIHDR mhdr; union { MIDIPROPTEMPO tempo; MIDIPROPTIMEDIV tdiv; } midiprop; if (hwnd) rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW); else rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION); if (rc == MMSYSERR_NOTSUPPORTED) { skip( "MIDI stream not supported\n" ); return; } ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc)); if (rc) return; test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0); midiprop.tempo.cbStruct = sizeof(midiprop.tempo); rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO); ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc)); ok(midiprop.tempo.dwTempo==500000, "default stream tempo %u microsec per quarter note\n", midiprop.tempo.dwTempo); midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv); rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TIMEDIV); ok(!rc, "midiStreamProperty TIMEDIV rc=%s\n", mmsys_error(rc)); todo_wine ok(24==LOWORD(midiprop.tdiv.dwTimeDiv), "default stream time division %u\n", midiprop.tdiv.dwTimeDiv); memset(&mhdr, 0, sizeof(mhdr)); mhdr.dwFlags = 0; mhdr.dwUser = 0x56FA552C; mhdr.dwOffset = 1234567890; mhdr.dwBufferLength = sizeof(strmEvents); mhdr.dwBytesRecorded = mhdr.dwBufferLength; mhdr.lpData = (LPSTR)&strmEvents[0]; if (mhdr.lpData) { rc = midiOutLongMsg((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER); rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1); ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc)); rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc)); ok(mhdr.dwFlags & MHDR_PREPARED, "MHDR.dwFlags when prepared %x\n", mhdr.dwFlags); /* The device is still in paused mode and should queue the message. */ rc = midiStreamOut(hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiStreamOut old size rc=%s\n", mmsys_error(rc)); rc2 = rc; trace("MIDIHDR flags=%x when submitted\n", mhdr.dwFlags); /* w9X/me does not set MHDR_ISSTRM when StreamOut exits, * but it will be set on all systems after the job is finished. */ Sleep(90); /* Wine <1.1.39 started playing immediately */ test_notification(hwnd, "midiStream still paused", 0, WHATEVER); /* MSDN asks to use midiStreamRestart prior to midiStreamOut() * because the starting state is 'pause', but some apps seem to * work with the inverse order: queue everything, then play. */ rc = midiStreamRestart(hm); ok(!rc, "midiStreamRestart rc=%s\n", mmsys_error(rc)); if (!rc2) while(mhdr.dwFlags & MHDR_INQUEUE) { trace("async MIDI still queued\n"); Sleep(100); } /* Checking INQUEUE is not the recommended way to wait for the end of a job, but we're testing. */ /* MHDR_ISSTRM is not necessarily set when midiStreamOut returns * rather than when the queue is eventually processed. */ ok(mhdr.dwFlags & MHDR_ISSTRM, "MHDR.dwFlags %x no ISSTRM when out of queue\n", mhdr.dwFlags); if (!rc2) while(!(mhdr.dwFlags & MHDR_DONE)) { /* Never to be seen except perhaps on multicore */ trace("async MIDI still not done\n"); Sleep(100); } ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags %x not DONE when out of queue\n", mhdr.dwFlags); test_notification(hwnd, "midiStream callback", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr); /* Native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */ ok(1234567890!=mhdr.dwOffset, "play left MIDIHDR.dwOffset at %u\n", mhdr.dwOffset); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutUnprepare #2 rc=%s\n", mmsys_error(rc)); trace("MIDIHDR stream flags=%x when finished\n", mhdr.dwFlags); ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags when done %x\n", mhdr.dwFlags); test_position(hm, TIME_MS, TIME_MS); test_position(hm, TIME_TICKS, TIME_TICKS); todo_wine test_position(hm, TIME_MIDI, TIME_MIDI); test_position(hm, TIME_SMPTE, TIME_MS); test_position(hm, TIME_SAMPLES, TIME_MS); test_position(hm, TIME_BYTES, TIME_MS); Sleep(400); /* Hear note */ midiprop.tempo.cbStruct = sizeof(midiprop.tempo); rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO); ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc)); ok(0x0493E0==midiprop.tempo.dwTempo, "stream set tempo %u\n", midiprop.tdiv.dwTimeDiv); rc = midiStreamRestart(hm); ok(!rc, "midiStreamRestart #2 rc=%s\n", mmsys_error(rc)); mhdr.dwFlags |= MHDR_ISSTRM; /* Preset flags (e.g. MHDR_ISSTRM) do not disturb. */ rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutPrepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc)); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)); ok(!rc, "midiOutUnprepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc)); rc = midiStreamRestart(hm); ok(!rc, "midiStreamRestart #3 rc=%s\n", mmsys_error(rc)); } ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser); ok(0==((MIDISHORTEVENT*)&strmEvents)[0].dwStreamID, "dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID); /* dwBytesRecorded controls how much is played, not dwBufferLength * allowing to immediately forward packets from midiIn to midiOut */ mhdr.dwOffset = 1234123123; mhdr.dwBufferLength = sizeof(strmNops); trace("buffer: %u\n", mhdr.dwBufferLength); mhdr.dwBytesRecorded = 0; mhdr.lpData = (LPSTR)&strmNops[0]; strmNops[0].dwEvent |= MEVT_F_CALLBACK; strmNops[1].dwEvent |= MEVT_F_CALLBACK; rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc)); rc = playStream(hm, &mhdr); ok(!rc, "midiStreamOut 0 bytes recorded rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr); test_notification(hwnd, "0 bytes recorded", 0, WHATEVER); /* FIXME: check dwOffset within callback * instead of the unspecified value afterwards */ ok(1234123123==mhdr.dwOffset || broken(0==mhdr.dwOffset), "play 0 set MIDIHDR.dwOffset to %u\n", mhdr.dwOffset); /* w2k and later only set dwOffset when processing MEVT_T_CALLBACK, * while w9X/me/nt always sets it. Have Wine behave like w2k because the * dwOffset slot does not exist in the small size MIDIHDR. */ mhdr.dwOffset = 1234123123; mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT); rc = playStream(hm, &mhdr); ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "1 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "1 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr); test_notification(hwnd, "1 of 2 events", 0, WHATEVER); ok(0==mhdr.dwOffset, "MIDIHDR.dwOffset 1/2 changed to %u\n", mhdr.dwOffset); mhdr.dwOffset = 1234123123; mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT); rc = playStream(hm, &mhdr); ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr); test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr); test_notification(hwnd, "2 of 2 events", 0, WHATEVER); ok(sizeof(MIDISHORTEVENT)==mhdr.dwOffset, "MIDIHDR.dwOffset 2/2 changed to %u\n", mhdr.dwOffset); ok(mhdr.dwBytesRecorded == 2*sizeof(MIDISHORTEVENT), "dwBytesRecorded changed to %u\n", mhdr.dwBytesRecorded); strmNops[0].dwEvent &= ~MEVT_F_CALLBACK; strmNops[1].dwEvent &= ~MEVT_F_CALLBACK; mhdr.dwOffset = 1234123123; rc = playStream(hm, &mhdr); ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "0 CB in 2 events", MOM_DONE, (DWORD_PTR)&mhdr); test_notification(hwnd, "0 CB in 2 events", 0, WHATEVER); /* w9X/me/nt set dwOffset to the position played last */ ok(1234123123==mhdr.dwOffset || broken(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset); mhdr.dwBytesRecorded = mhdr.dwBufferLength-1; rc = playStream(hm, &mhdr); ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded modulo MIDIEVENT rc=%s\n", mmsys_error(rc)); if (!rc) { test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr); } mhdr.dwBytesRecorded = mhdr.dwBufferLength+1; rc = playStream(hm, &mhdr); ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBufferLength<dwBytesRecorded rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "past MIDIHDR tests", 0, WHATEVER); rc = midiStreamStop(hm); ok(!rc, "midiStreamStop rc=%s\n", mmsys_error(rc)); ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); ok(0==strmNops[0].dwStreamID, "dwStreamID[0] set to %x\n", strmNops[0].dwStreamID); ok(0==strmNops[1].dwStreamID, "dwStreamID[1] set to %x\n", strmNops[1].dwStreamID); 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) { mhdr.dwFlags = 0; /* PrepareHeader detects the too large buffer is for a stream. */ rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); todo_wine ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare stream too large rc=%s\n", mmsys_error(rc)); rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr)); ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc)); HeapFree(GetProcessHeap(), 0, mhdr.lpData); } rc = midiStreamClose(hm); ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc)); test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0); test_notification(hwnd, "midiStream over", 0, WHATEVER); rc = midiStreamOpen(&hm, &udev, 1, 0, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION); ok(!rc /*w2k*/|| rc==MMSYSERR_INVALPARAM/*w98*/, "midiStreamOpen NULL function rc=%s\n", mmsys_error(rc)); if (!rc) { trace("Device %d accepts NULL CALLBACK_FUNCTION\n", udev); rc = midiStreamClose(hm); ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc)); } rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)0xDEADBEEF, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW); ok(rc==MMSYSERR_INVALPARAM, "midiStreamOpen bad window rc=%s\n", mmsys_error(rc)); if (!rc) { rc = midiStreamClose(hm); ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc)); } }
int WinMIDIDevice::StreamOut(MidiHeader *header) { auto syshdr = (MIDIHDR*)header->lpNext; assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]); return midiStreamOut(MidiOut, syshdr, sizeof(MIDIHDR)); }
void WinMIDIStreamer::Play(int looped) { UINT i; // Do we need to prepare the MIDI data? if(!registered) { // The song is already loaded in the song buffer. DeregisterSong(); // Prepare the buffers. if(song) { LPMIDIHDR mh = GetFreeBuffer(); MIDIEVENT mev; DWORD* ptr; // First add the tempo. ptr = (DWORD *) mh->lpData; *ptr++ = 0; *ptr++ = 0; *ptr++ = (MEVT_TEMPO << 24) | 1000000; // One second. mh->dwBytesRecorded = 3 * sizeof(DWORD); // Start reading the events. readPos = (byte *) song + ((musheader_t*)song)->scoreStart; readTime = 0; while(GetNextEvent(&mev)) { // Is the buffer getting full? if(mh->dwBufferLength - mh->dwBytesRecorded < 3 * sizeof(DWORD)) { // Try to get more buffer. if(!ResizeWorkBuffer(mh)) { // Not possible, buffer size has reached the limit. // We need to start working on another one. midiOutPrepareHeader((HMIDIOUT) midiStr, mh, sizeof(*mh)); mh = GetFreeBuffer(); if(!mh) return; // Oops. } } // Add the event. ptr = (DWORD *) (mh->lpData + mh->dwBytesRecorded); *ptr++ = mev.dwDeltaTime; *ptr++ = 0; *ptr++ = mev.dwEvent; mh->dwBytesRecorded += 3 * sizeof(DWORD); } // Prepare the last buffer, too. midiOutPrepareHeader((HMIDIOUT) midiStr, mh, sizeof(*mh)); } // Now there is a registered song. registered = TRUE; } playing = true; Reset(); // Stream out all buffers. for(i = 0; i < MAX_BUFFERS; ++i) { if(midiBuffers[i].dwUser) { loopBuffer = &midiBuffers[i]; midiStreamOut(midiStr, &midiBuffers[i], sizeof(midiBuffers[i])); } } // If we aren't looping, don't bother. if(!looped) loopBuffer = NULL; // Start playing. midiStreamRestart(midiStr); }
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; }