static void midi_dispose_buffer(MidiBuffer * node, const char * caller) { MMRESULT rv; if (node->prepared) { rv = midiOutUnprepareHeader( (HMIDIOUT) midiStream, &node->hdr, sizeof(MIDIHDR) ); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM %s/midi_dispose_buffer midiOutUnprepareHeader", caller); } node->prepared = FALSE; } if (midiThread) { // remove the node from the activeMidiBuffers list LL_Remove( node, next, prev ); // when playing, we keep the buffers LL_Add( (MidiBuffer*) &spareMidiBuffers, node, next, prev ); //fprintf(stderr, "WinMM %s/midi_dispose_buffer recycling buffer %p\n", caller, node); } else { // when not, we throw them away free(node); //fprintf(stderr, "WinMM %s/midi_dispose_buffer freeing buffer %p\n", caller, node); } }
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; }
/* winmm_out_callback -- recycle sysex buffers */ static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { PmInternal *midi = (PmInternal *) dwInstance; midiwinmm_type m = (midiwinmm_type) midi->descriptor; LPMIDIHDR hdr = (LPMIDIHDR) dwParam1; int err = 0; /* set to 0 so that no buffer match will also be an error */ /* Future optimization: eliminate UnprepareHeader calls -- they aren't necessary; however, this code uses the prepared-flag to indicate which buffers are free, so we need to do something to flag empty buffers if we leave them prepared */ /* printf("out_callback: hdr %x, wMsg %x, MOM_DONE %x\n", hdr, wMsg, MOM_DONE); */ if (wMsg == MOM_DONE) { MMRESULT ret = midiOutUnprepareHeader(m->handle.out, hdr, sizeof(MIDIHDR)); assert(ret == MMSYSERR_NOERROR); } /* notify waiting sender that a buffer is available */ err = SetEvent(m->buffer_signal); assert(err); /* false -> error */ }
void WinMIDIStreamer::DeregisterSong(void) { int i; if(!registered) return; // First stop the song. Stop(); // This is the actual unregistration. for(i = 0; i < MAX_BUFFERS; ++i) { if(midiBuffers[i].dwUser) { LPMIDIHDR mh = &midiBuffers[i]; midiOutUnprepareHeader((HMIDIOUT) midiStr, mh, sizeof(*mh)); free(mh->lpData); // Clear for re-use. memset(mh, 0, sizeof(*mh)); } } registered = FALSE; }
void JackWinMMEDriver::CloseOutput(MidiSlot* slot) { MMRESULT res; int retry = 0; if (slot->fHandle == 0) return; HMIDIOUT handle = (HMIDIOUT)slot->fHandle; res = midiOutReset(handle); if (res != MMSYSERR_NOERROR) jack_error("midiOutReset error"); midiOutUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); do { res = midiOutClose(handle); if (res != MMSYSERR_NOERROR) jack_error("midiOutClose error"); Sleep(10); retry++; } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); if (slot->fHeader) { GlobalFreePtr(slot->fHeader); } }
void hmp_stop(hmp_file *hmp) { MIDIHDR *mhdr; if (!hmp->stop) { hmp->stop = 1; //PumpMessages(); midiStreamStop(hmp->hmidi); while (hmp->bufs_in_mm) { //PumpMessages(); G3_SLEEP(0); } } while ((mhdr = hmp->evbuf)) { midiOutUnprepareHeader((HMIDIOUT)hmp->hmidi, mhdr, sizeof(MIDIHDR)); hmp->evbuf = mhdr->lpNext; delete[] mhdr; mhdr = NULL; } if (hmp->hmidi) { midiStreamClose(hmp->hmidi); hmp->hmidi = NULL; } }
int flushMidiDevice (MidiDevice *midi) { int ok = 1; if (midi->count > 0) { MMRESULT error; MIDIHDR header; header.lpData = midi->buffer; header.dwBufferLength = midi->count; header.dwFlags = 0; if ((error = midiOutPrepareHeader(midi->handle, &header, sizeof(header))) == MMSYSERR_NOERROR) { if ((error = midiOutLongMsg(midi->handle, &header, sizeof(header))) == MMSYSERR_NOERROR) { midi->count = 0; } else { logMidiOutError(error, LOG_ERR, "midiOutLongMsg"); ok = 0; } while ((error = midiOutUnprepareHeader(midi->handle, &header, sizeof(header))) == MIDIERR_STILLPLAYING) { approximateDelay(1); } if (error != MMSYSERR_NOERROR) { logMidiOutError(error, LOG_ERR, "midiOutUnprepareHeader"); } } else { logMidiOutError(error, LOG_ERR, "midiOutPrepareHeader"); ok = 0; } } return ok; }
static void CALLBACK MidiOutProc(HMIDIOUT hMidiOut, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { MFDevice *pDevice = (MFDevice*)dwInstance; MFMidiPC_MidiOutputDevice *pMidi = (MFMidiPC_MidiOutputDevice*)pDevice->pInternal; switch (wMsg) { case MOM_OPEN: MFDebug_Log(0, MFStr("Opened MIDI output device: %s", pDevice->strings[MFDS_ID])); break; case MOM_CLOSE: MFDebug_Log(0, MFStr("Opened MIDI output device: %s", pDevice->strings[MFDS_ID])); break; case MOM_DONE: { MIDIHDR *pHdr = (MIDIHDR*)dwParam1; MMRESULT r = midiOutUnprepareHeader(pMidi->hMidiOut, pHdr, sizeof(*pHdr)); if (r != MMSYSERR_NOERROR) { wchar_t errorBuffer[256]; midiOutGetErrorText(r, errorBuffer, sizeof(errorBuffer)); MFDebug_Warn(1, MFStr("Failed to cleanup MIDI message: %s", MFString_WCharAsUTF8(errorBuffer))); } // TODO: return to pool... break; } case MOM_POSITIONCB: MFDebug_Log(0, "MIDI output device: Position CB"); break; } }
MMRESULT Midi_SendLong(MIDI *midi, BYTE *buffer, size_t bufSize) { HMIDIOUT midiOut = midi->midiOut; MIDIHDR outMsgHdr = { buffer, /* sysex message */ bufSize, /* sysex message length */ bufSize }; MMRESULT error = MMSYSERR_NOERROR; if (midi->outPort == -1) { return MMSYSERR_NOERROR; } error = midiOutPrepareHeader(midiOut, &outMsgHdr, sizeof outMsgHdr); if (error) return error; error = midiOutLongMsg(midiOut, &outMsgHdr, sizeof outMsgHdr); if (error) goto UnprepareHeader; if (WaitForSingleObject(midi->outEvent, 500) == WAIT_TIMEOUT) { // ? goto UnprepareHeader; } ResetEvent(midi->outEvent); UnprepareHeader: error = midiOutUnprepareHeader(midiOut, &outMsgHdr, sizeof outMsgHdr); if (error == MIDIERR_STILLPLAYING) { SleepEx(20, FALSE); goto UnprepareHeader; } return error; }
static int s_CleanUpBlockNodes(WinmidiObject *obj) { UINT retVal; MidiBlockNode *node, *nextNode; assert(obj); node = obj->m_list; while(node) { nextNode = node->m_next; if(node->m_blockState == BLOCK_PLAYED) { obj->m_list = node->m_next; if(node == obj->m_listEnd) { obj->m_listEnd = 0; } /* Dispose of the block. */ retVal = midiOutUnprepareHeader((HMIDIOUT)obj->m_midiOut, &node->m_header, sizeof(node->m_header)); if(retVal != MMSYSERR_NOERROR) { s_SetMidiError("unpreparing header", retVal); return 0; } else { (void)GlobalFreePtr(node->m_header.lpData); free(node); } } node = nextNode; } return 1; }
// ----------------------------------------------------------------------- // えくすくるーしぶを送り終えるまで待つお // ----------------------------------------------------------------------- void MIDI_Waitlastexclusiveout(void) { // エクスクルーシヴ送信完了まで待ちましょう〜 if (MIDI_EXCVWAIT) { while(midiOutUnprepareHeader(hOut, &hHdr, sizeof(MIDIHDR)) == MIDIERR_STILLPLAYING); MIDI_EXCVWAIT = 0; } }
void MidiDriver_WIN::close() { if (!_isOpen) return; _isOpen = false; MidiDriver_MPU401::close(); midiOutUnprepareHeader(_mo, &_streamHeader, sizeof(_streamHeader)); check_error(midiOutClose(_mo)); CloseHandle(_streamEvent); }
static void waitlastexclusiveout(CMMIDI midi) { if (midi->midiexcvwait) { midi->midiexcvwait = 0; while(midiOutUnprepareHeader(midi->out.win32.hmidiout, &midi->out.win32.midihdr, sizeof(midi->out.win32.midihdr)) == MIDIERR_STILLPLAYING) { } } }
static int midi_init(void){ MIDIHDR mhMidi; /* GMリセット用データ */ BYTE abyGMReset[] = {0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7}; /* MIDIデバイスオープン */ if (midiOutOpen(&g_hMidi, MIDIMAPPER, 0, 0, 0) != MMSYSERR_NOERROR) { return 0; } ZeroMemory(&mhMidi, sizeof(mhMidi)); /* GMリセット送信用バッファ設定 */ mhMidi.lpData = (LPSTR)abyGMReset; mhMidi.dwBufferLength = 6; mhMidi.dwBytesRecorded = 6; midiOutPrepareHeader(g_hMidi, &mhMidi, sizeof(mhMidi)); /* GMリセットメッセージ送信 */ if (midiOutLongMsg(g_hMidi, &mhMidi, sizeof(mhMidi)) != MMSYSERR_NOERROR) { midiOutUnprepareHeader(g_hMidi, &mhMidi, sizeof(mhMidi)); std::cout<<"MIDI音源の初期化に失敗しました。"<<std::endl; midiOutClose(g_hMidi); return 0; } /* GMリセット完了待機 */ while ((mhMidi.dwFlags & MHDR_DONE) == 0); midiOutUnprepareHeader(g_hMidi, &mhMidi, sizeof(mhMidi)); return 0; }
//--------------------------------------------------------------------------- static void TVPMIDIOut(tjs_uint8 *data,int len) { if(!TVPMIDIOutHandle) return; TVPCheckMIDIDelay(); MIDIHDR hdr; hdr.lpData = (char*)data; hdr.dwFlags = 0; hdr.dwBufferLength = len; midiOutPrepareHeader(TVPMIDIOutHandle, &hdr, sizeof(MIDIHDR)); midiOutLongMsg(TVPMIDIOutHandle, &hdr, sizeof(MIDIHDR)); midiOutUnprepareHeader(TVPMIDIOutHandle, &hdr, sizeof(MIDIHDR)); }
int WinMIDIDevice::UnprepareHeader(MidiHeader *header) { auto syshdr = (MIDIHDR*)header->lpNext; if (syshdr != nullptr) { assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]); header->lpNext = nullptr; return midiOutUnprepareHeader((HMIDIOUT)MidiOut, syshdr, sizeof(MIDIHDR)); } else { return MMSYSERR_NOERROR; } }
void MidiUartWinClass::midiSendLong(unsigned char *buf, unsigned long len) { MIDIHDR midiHdr; midiHdr.lpData = (CHAR *)buf; midiHdr.dwBufferLength = len; midiHdr.dwFlags = 0; UINT err = midiOutPrepareHeader(outHandle, &midiHdr, sizeof(MIDIHDR)); if (err) { char errBuf[256]; midiOutGetErrorText(err, errBuf, sizeof(errBuf)); printf("error sending long message: %s\n", errBuf); } while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(outHandle, &midiHdr, sizeof(MIDIHDR))) ; }
/*--------------------------------------------------------------------- Function: MPU_SendMidiImmediate Sends a MIDI message immediately to the the music device. ---------------------------------------------------------------------*/ void MPU_SendMidiImmediate( char *data, int count ) { MIDIHDR mhdr; static int masks[3] = { 0x00ffffffl, 0x0000ffffl, 0x000000ffl }; if (!count) return; if (count<=3) midiOutShortMsg((HMIDIOUT)hmido, (*((long*)data)) & masks[count-1]); else { ZeroMemory(&mhdr, sizeof(mhdr)); mhdr.lpData = data; mhdr.dwBufferLength = count; midiOutPrepareHeader((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR)); midiOutLongMsg((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR)); while (!(mhdr.dwFlags & MHDR_DONE)) ; midiOutUnprepareHeader((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR)); } }
BOOL CMidiDevice::SendLongMsg(LPBYTE pSys, DWORD dwMsgLen) { UINT err; MIDIHDR hdr; if(m_StateOut == OPENEDOUT) { // lock buffer and store pointer in MIDIHDR hdr.lpData = (LPSTR)pSys; if(hdr.lpData) { // store its size in the MIDIHDR hdr.dwBufferLength = dwMsgLen; // flags must be set to 0 hdr.dwFlags = 0; // prepare the buffer and MIDIHDR err = midiOutPrepareHeader(m_hMidiOut, &hdr, sizeof(MIDIHDR)); if(!err) { // send the buffer to midi out device err = midiOutLongMsg(m_hMidiOut, &hdr, sizeof(MIDIHDR)); if(err) { return FALSE; } // unprepare the buffer and MIDIHDR while(MIDIERR_STILLPLAYING == midiOutUnprepareHeader(m_hMidiOut, &hdr, sizeof(MIDIHDR))) { // should put a delay in here rather than a busy-wait Sleep(5); } return TRUE; } } } return FALSE; }
void CALLBACK MPU_MIDICallback(HMIDIOUT handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { int i; switch (uMsg) { case MOM_DONE: midiOutUnprepareHeader((HMIDIOUT)handle, (MIDIHDR*)dwParam1, sizeof(MIDIHDR)); for (i=0;i<NUMBUFFERS;i++) { if (dwParam1 == (long)&bufferheaders[i]) { eventcnt[i] = 0; // marks the buffer as free // printf("Finished buffer %d\n",i); _MPU_BuffersWaiting--; break; } } return; default: return; } }
void MidiOutput::sendMessageNow (const MidiMessage& message) { const MidiOutHandle* const handle = static_cast <const MidiOutHandle*> (internal); if (message.getRawDataSize() > 3 || message.isSysEx()) { MIDIHDR h = { 0 }; h.lpData = (char*) message.getRawData(); h.dwBufferLength = message.getRawDataSize(); h.dwBytesRecorded = message.getRawDataSize(); if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR) { MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR)); if (res == MMSYSERR_NOERROR) { while ((h.dwFlags & MHDR_DONE) == 0) Sleep (1); int count = 500; // 1 sec timeout while (--count >= 0) { res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR)); if (res == MIDIERR_STILLPLAYING) Sleep (2); else break; } } } } else { midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()); } }
void MIDIDeviceOutJack::longMsgOut(const char* data, int size) { m_hdr.dwBufferLength = size; m_hdr.dwBytesRecorded = size; m_hdr.lpData = (char*)data; MMRESULT result; result = midiOutPrepareHeader((HMIDIOUT)m_handle, &m_hdr, sizeof(m_hdr)); if (result != MMSYSERR_NOERROR) error("MIDIDeviceOutJack::longMsgOut (prepare)", result); result = midiOutLongMsg((HMIDIOUT)m_handle, &m_hdr, sizeof(m_hdr)); if (result != MMSYSERR_NOERROR) error("MIDIDeviceOutJack::longMsgOut", result); result = midiOutUnprepareHeader((HMIDIOUT)m_handle, &m_hdr, sizeof(m_hdr)); if (result != MMSYSERR_NOERROR) error("MIDIDeviceOutJack::longMsgOut (unprepare)", result); }
int MidiOutPort_visual::rawsend(uchar* array, int size) { // Note: this function will work in Windows 95 and Windows NT. // This function will not work in Windows 3.x because a // different memory model is necessary. if (size > 64000 || size < 1) { cerr << "Warning: cannot write a MIDI stream larger than 64kB" << endl; return 0; } MIDIHDR midiheader; // structure for sending an array of MIDI bytes midiheader.lpData = (char *)array; midiheader.dwBufferLength = size; // midiheader.dwBytesRecorded = size; // example program doesn't set midiheader.dwFlags = 0; // flags must be set to 0 if (getPort() == -1) { return -1; } int status = midiOutPrepareHeader(device[getPort()], &midiheader, sizeof(MIDIHDR)); if (status != MMSYSERR_NOERROR) { return 0; } status = midiOutLongMsg(device[getPort()], &midiheader, sizeof(MIDIHDR)); if (status != MMSYSERR_NOERROR) { return 0; } while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(device[getPort()], &midiheader, sizeof(MIDIHDR))) { Sleep(1); // sleep for 1 millisecond } return 1; }
void midiSendLong(unsigned char *buf, unsigned long len) { MIDIHDR midiHdr; HANDLE hBuffer; hBuffer = GlobalAlloc(GHND, len); if (!hBuffer) { logPrintf(LOG_ERROR, "error allocating buffer for sysex\n"); return; } midiHdr.lpData = (LPBYTE)GlobalLock(hBuffer); if (midiHdr.lpData) { midiHdr.dwBufferLength = len; midiHdr.dwFlags = 0; debugPrintf(2, "midiSendLong: \n"); debugHexdump(2, buf, len); UINT err = midiOutPrepareHeader(outHandle, &midiHdr, sizeof(MIDIHDR)); if (!err) { memcpy(midiHdr.lpData, buf, len); err = midiOutLongMsg(outHandle, &midiHdr, sizeof(MIDIHDR)); if (err) { char errBuf[256]; midiOutGetErrorText(err, errBuf, sizeof(errBuf)); logPrintf(LOG_ERROR, "error sending long message: %s\n", errBuf); } while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(outHandle, &midiHdr, sizeof(MIDIHDR))) { ; } debugPrintf(2, "midiSendLong finished\n"); } } GlobalUnlock(hBuffer); GlobalFree(hBuffer); }
/* winmm_streamout_callback -- unprepare (free) buffer header */ static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { PmInternal *midi = (PmInternal *) dwInstance; midiwinmm_type m = (midiwinmm_type) midi->descriptor; LPMIDIHDR hdr = (LPMIDIHDR) dwParam1; int err; /* Even if an error is pending, I think we should unprepare msgs and signal their arrival */ /* printf("streamout_callback: hdr %x, wMsg %x, MOM_DONE %x\n", hdr, wMsg, MOM_DONE); */ if (wMsg == MOM_DONE) { MMRESULT ret = midiOutUnprepareHeader(m->handle.out, hdr, sizeof(MIDIHDR)); assert(ret == MMSYSERR_NOERROR); } /* signal client in case it is blocked waiting for buffer */ err = SetEvent(m->buffer_signal); assert(err); /* false -> error */ }
void Win32MidiOutputDevice::writeSysEx(QByteArray message) { if(message.isEmpty()) return; if (isOpen() == false) return; MIDIHDR midiHdr; /* Store pointer in MIDIHDR */ midiHdr.lpData = (LPSTR)message.data(); /* Store its size in the MIDIHDR */ midiHdr.dwBufferLength = message.count(); /* Flags must be set to 0 */ midiHdr.dwFlags = 0; UINT err; /* Prepare the buffer and MIDIHDR */ err = midiOutPrepareHeader(m_handle, &midiHdr, sizeof(MIDIHDR)); if (!err) { /* Output the SysEx message */ err = midiOutLongMsg(m_handle, &midiHdr, sizeof(MIDIHDR)); if (err) qDebug() << "Error while sending SysEx message"; /* Unprepare the buffer and MIDIHDR */ while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(m_handle, &midiHdr, sizeof(MIDIHDR))) { /* Should put a delay in here rather than a busy-wait */ } } /* Close the MIDI device */ midiOutClose(m_handle); }
void MidiDriver_WIN::sysEx(const byte *msg, uint16 length) { if (!_isOpen) return; if (WaitForSingleObject (_streamEvent, 2000) == WAIT_TIMEOUT) { warning ("Could not send SysEx - MMSYSTEM is still trying to send data"); return; } assert(length+2 <= 266); midiOutUnprepareHeader(_mo, &_streamHeader, sizeof(_streamHeader)); // Add SysEx frame _streamBuffer[0] = 0xF0; memcpy(&_streamBuffer[1], msg, length); _streamBuffer[length+1] = 0xF7; _streamHeader.lpData = (char *)_streamBuffer; _streamHeader.dwBufferLength = length + 2; _streamHeader.dwBytesRecorded = length + 2; _streamHeader.dwUser = 0; _streamHeader.dwFlags = 0; MMRESULT result = midiOutPrepareHeader(_mo, &_streamHeader, sizeof(_streamHeader)); if (result != MMSYSERR_NOERROR) { check_error (result); return; } ResetEvent(_streamEvent); result = midiOutLongMsg(_mo, &_streamHeader, sizeof(_streamHeader)); if (result != MMSYSERR_NOERROR) { check_error(result); SetEvent(_streamEvent); return; } }
static DWORD modLongData(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD dwParam2) { WORD chn; DWORD ret = MMSYSERR_NOERROR; MIDIHDR mh; if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR; mh = *lpMidiHdr; for (chn = 0; chn < 16; chn++) { if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0) { mh.dwFlags = 0; midiOutPrepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh)); ret = midiOutLongMsg(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh)); midiOutUnprepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh)); if (ret != MMSYSERR_NOERROR) break; } } return ret; }
void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) { unsigned int nBytes = message->size(); if ( nBytes == 0 ) { errorString_ = "RtMidiOut::sendMessage: message argument is empty!"; error( RtError::WARNING ); return; } MMRESULT result; WinMidiData *data = static_cast<WinMidiData *> (apiData_); if ( message->at(0) == 0xF0 ) { // Sysex message // Allocate buffer for sysex data. char *buffer = (char *) malloc( nBytes ); if ( buffer == NULL ) { errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!"; error( RtError::MEMORY_ERROR ); } // Copy data to buffer. for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message->at(i); // Create and prepare MIDIHDR structure. MIDIHDR sysex; sysex.lpData = (LPSTR) buffer; sysex.dwBufferLength = nBytes; sysex.dwFlags = 0; result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { free( buffer ); errorString_ = "RtMidiOut::sendMessage: error preparing sysex header."; error( RtError::DRIVER_ERROR ); } // Send the message. result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { free( buffer ); errorString_ = "RtMidiOut::sendMessage: error sending sysex message."; error( RtError::DRIVER_ERROR ); } // Unprepare the buffer and MIDIHDR. while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); free( buffer ); } else { // Channel or system message. // Make sure the message size isn't too big. if ( nBytes > 3 ) { errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!"; error( RtError::WARNING ); return; } // Pack MIDI bytes into double word. DWORD packet; unsigned char *ptr = (unsigned char *) &packet; for ( unsigned int i=0; i<nBytes; ++i ) { *ptr = message->at(i); ++ptr; } // Send the message immediately. result = midiOutShortMsg( data->outHandle, packet ); if ( result != MMSYSERR_NOERROR ) { errorString_ = "RtMidiOut::sendMessage: error sending MIDI message."; error( RtError::DRIVER_ERROR ); } } }
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)); } }