/******************************************************************************* * * Module Name: midistrm.c * * MIDI Streams implementation * * Created: 9 Feb 1995 SteveDav * * Copyright (c) 1995-1999 Microsoft Corporation * \******************************************************************************/ #include "winmmi.h" /* * MIDI Streaming API Port: For the time being, the assumption * is that the devices are static. This code was designed to * be PnP friendly, with devices coming and going. The * validation of devices will be commented out for now, but in * the future when NT is a more dynamic OS, the validation will * need to be added back. * */ extern BOOL CreatehwndNotify(VOID); CRITICAL_SECTION midiStrmHdrCritSec; WINMMAPI MMRESULT WINAPI midiDisconnect ( HMIDI hmi, HMIDIOUT hmo, LPVOID lpv) { dprintf2(("midiDisconnect(%08X,%08X,%08X)", hmi, hmo, lpv)); return midiInSetThru (hmi, hmo, FALSE); } WINMMAPI MMRESULT WINAPI midiConnect ( HMIDI hmi, HMIDIOUT hmo, LPVOID lpv) { dprintf2(("midiConnect(%08X,%08X,%08X)", hmi, hmo, lpv)); return midiInSetThru (hmi, hmo, TRUE); } /*+ midiInSetThru * * Establish a thruing midiOut handle for a midiIn device. This is * done by first calling the driver to let the driver do the thruing, * if the driver returns UNSUPPORTED a single thruing handle can * be established by simulation in DriverCallback * *-====================================================================*/ MMRESULT midiInSetThru ( HMIDI hmi, HMIDIOUT hmo, BOOL bAdd) { MMRESULT mmr = MMSYSERR_ERROR; // this value should never get returned.... UINT uType; dprintf2(("midiInSetThru(%X,%X,%d)", hmi, hmo, bAdd)); AcquireHandleListResourceShared(); // allow first handle to be either midi in or midi out // (so that we can send DRVM_ADD_THRU messages to dummy // output drivers.) // // we simulate thruing only for input handles though... // if (BAD_HANDLE(hmi, TYPE_MIDIIN) && BAD_HANDLE(hmi, TYPE_MIDIOUT)) { ReleaseHandleListResource(); return MMSYSERR_INVALHANDLE; } uType = GetHandleType(hmi); if (bAdd) { if (BAD_HANDLE(hmo, TYPE_MIDIOUT)) { ReleaseHandleListResource(); return (MMSYSERR_INVALHANDLE); } // !!! Devices are static on NT for now. // //if (!mregQueryValidHandle(HtoPT(PMIDIDEV, hmo)->hmd)) // return MMSYSERR_NODRIVER; mmr = (MMRESULT)midiMessage ((HMIDI)hmi, DRVM_ADD_THRU, (DWORD_PTR)(UINT_PTR)hmo, 0l); if (mmr == MMSYSERR_NOTSUPPORTED && uType == TYPE_MIDIIN) { // dont allow more than one handle to be added // if (HtoPT(PMIDIDEV, hmi)->pmThru) mmr = MIDIERR_NOTREADY; else { // add the handle. // HtoPT(PMIDIDEV, hmi)->pmThru = HtoPT(PMIDIDEV, hmo); mmr = MMSYSERR_NOERROR; } } } else { mmr = (MMRESULT)midiMessage ((HMIDI)hmi, DRVM_REMOVE_THRU, (DWORD_PTR)(UINT_PTR)hmo, 0l); if (mmr == MMSYSERR_NOTSUPPORTED && uType == TYPE_MIDIIN) mmr = MMSYSERR_NOERROR; if ( ! hmo || (PMIDIDEV)hmo == HtoPT(PMIDIDEV, hmi)->pmThru) HtoPT(PMIDIDEV, hmi)->pmThru = NULL; else mmr = MMSYSERR_INVALPARAM; } return mmr; } WINMMAPI MMRESULT WINAPI midiStreamOpen( LPHMIDISTRM phms, LPUINT puDeviceID, DWORD cMidi, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen) { PMIDISTRM pms = NULL; PMIDISTRMID pmsi; PMIDISTRMID pmsiSave; MIDIOPENDESC* pmod = NULL; DWORD cbHandle; DWORD idx; MIDIOUTCAPS moc; MMRESULT mmrc = MMSYSERR_NOERROR; MMRESULT mmrc2; UINT msg; V_WPOINTER((LPVOID)phms, sizeof(HMIDISTRM), MMSYSERR_INVALPARAM); V_DCALLBACK(dwCallback, HIWORD(fdwOpen), MMSYSERR_INVALPARAM); *phms = NULL; // Allocate both the handle and the OPENDESC structure. // // NOTE: Using cMidi-1 because rgIds is defined as having 1 element // cbHandle = sizeof(MIDISTRM) + cMidi * ELESIZE(MIDISTRM, rgIds[0]); if ((0 == cMidi) || (cbHandle >= 0x00010000L)) return MMSYSERR_INVALPARAM; pms = HtoPT(PMIDISTRM, NewHandle(TYPE_MIDISTRM, NULL, (UINT)cbHandle)); if (NULL == pms) { dprintf1(("mSO: NewHandle() failed!")); return MMSYSERR_NOMEM; } // Implicitly acquired with NewHandle()... ReleaseHandleListResource(); pmod = (MIDIOPENDESC*)LocalAlloc(LPTR, (UINT)(sizeof(MIDIOPENDESC) + (cMidi-1) * ELESIZE(MIDIOPENDESC, rgIds[0]))); if (NULL == pmod) { dprintf1(("mSO: !LocalAlloc(MIDIOPENDESC)")); mmrc = MMSYSERR_NOMEM; goto midiStreamOpen_Cleanup; } pms->fdwOpen = fdwOpen; pms->dwCallback = dwCallback; pms->dwInstance = dwInstance; pms->cIds = cMidi; // Scan through the given device ID's. Determine if the underlying // driver supports stream directly. If so, then get it's HMD and uDeviceID, // etc. Else flag this as an emulator ID. // pmsi = pms->rgIds; for (idx = 0; idx < cMidi; idx++, pmsi++) { dprintf1(("mSO: pmsi->fdwId %08lX", (DWORD)pmsi->fdwId)); mmrc = midiOutGetDevCaps(puDeviceID[idx], &moc, sizeof(moc)); if (MMSYSERR_NOERROR != mmrc) { puDeviceID[idx] = (UINT)MIDISTRM_ERROR; goto midiStreamOpen_Cleanup; } if (moc.dwSupport & MIDICAPS_STREAM) { // Find the driver supporting the device ID. Note that mregFindDevice implicitly // adds a referance (usage) to the driver (i.e. the hmd). dprintf1(("mSO: Dev %u MIDICAPS_STREAM! dwSupport %08lX", (UINT)idx, moc.dwSupport)); mmrc = mregFindDevice(puDeviceID[idx], TYPE_MIDIOUT, &pmsi->hmd, &pmsi->uDevice); if (MMSYSERR_NOERROR != mmrc) { dprintf(("mregFindDevice barfed %u", (UINT)mmrc)); puDeviceID[idx] = (UINT)MIDISTRM_ERROR; goto midiStreamOpen_Cleanup; } else { dprintf1(("mregFindDevice: hmd %04X", (UINT_PTR)pmsi->hmd)); } } else { dprintf1(("mSO: Dev %u emulated.", (UINT)idx)); pmsi->fdwId |= MSI_F_EMULATOR; pmsi->hmd = NULL; pmsi->uDevice = puDeviceID[idx]; } } // At this point, the puDeviceID array's elements contain either device | // IDs or the error value MIDISTRM_ERROR. Also the pmsi array elements // corresponding to device IDs supporting MIDICAPS_STREAM will have a // non-NULL pmsi->hmd with a reference count (usage) on it. pmsi->uDevice // will be a driver-relative device ID. Other pmsi elements will have a // NULL pmsi->hmd and pmsi->fdwId will have MSI_F_EMULATOR set. // pmsi->uDevice will be a midiOut device ID (not a driver relative ID). // Scan through the list again, but this time actually open the devices. // pmod->hMidi = PTtoH(HMIDI, pms); pmod->dwCallback = (DWORD_PTR)midiOutStreamCallback; pmod->dwInstance = 0; msg = MODM_OPEN; pms->cDrvrs = 0; for(;;) { // // Set pmsiSave to identify the first unopened device. Break loop // if all are opened. // pmsiSave = NULL; pmsi = pms->rgIds; for (idx = 0; idx < cMidi; idx++, pmsi++) { if (!(pmsi->fdwId & MSI_F_OPENED)) { pmsiSave = pmsi; break; } } if (NULL == pmsiSave) break; // // Group together all IDs implemented by the same driver // pmod->cIds = 0; for(; idx < cMidi; idx++, pmsi++) { if (pmsi->hmd == pmsiSave->hmd) { pmod->rgIds[pmod->cIds].uDeviceID = pmsi->uDevice; pmod->rgIds[pmod->cIds++].dwStreamID = idx; } } pmsiSave->fdwId |= MSI_F_FIRST; // // Open the driver // if (!(pmsiSave->fdwId & MSI_F_EMULATOR)) { pmsiSave->drvMessage = HtoPT(PMMDRV, pmsiSave->hmd)->drvMessage; // pmsiSave->dnDevNode = pmod->dnDevNode = mregQueryDevNode(pmsiSave->hmd); mmrc = (MMRESULT)((*pmsiSave->drvMessage)( 0, msg, (DWORD_PTR)(LPDWORD)&pmsiSave->dwDrvUser, (DWORD_PTR)(LPMIDIOPENDESC)pmod, CALLBACK_FUNCTION|MIDI_IO_COOKED)); if (MMSYSERR_NOERROR == mmrc) { mregIncUsage(pmsiSave->hmd); } } else { mmrc = (MMRESULT)mseMessage(msg, (DWORD_PTR)(LPDWORD)&pmsiSave->dwDrvUser, (DWORD_PTR)(LPMIDIOPENDESC)pmod, CALLBACK_FUNCTION); } if (MMSYSERR_NOERROR != mmrc) { idx = (DWORD)(pmsiSave - pms->rgIds); puDeviceID[idx] = (UINT)MIDISTRM_ERROR; goto midiStreamOpen_Cleanup; } // // Now flag all IDs implemented by the same driver as MSI_F_OPENED // ++pms->cDrvrs; pmsi = pms->rgIds; for (idx = 0; idx < cMidi; idx++, pmsi++) { if (pmsi->hmd == pmsiSave->hmd) { pmsi->fdwId |= MSI_F_OPENED; if (!(pmsiSave->fdwId & MSI_F_EMULATOR)) { if (mmInitializeCriticalSection(&pmsi->CritSec)) { pmsi->fdwId |= MSI_F_INITIALIZEDCRITICALSECTION; } else { mmrc = MMSYSERR_NOMEM; } } } } } if (MMSYSERR_NOERROR == mmrc && !CreatehwndNotify()) { dprintf(("Cannot create hwndNotify for async messages!")); mmrc = MMSYSERR_ERROR; } dprintf2(("midiStreamOpen: HMIDISTRM %04X", (WORD)pms)); midiStreamOpen_Cleanup: if (NULL != pmod) LocalFree((HLOCAL)pmod); // // If there was an error, close any drivers we opened and free resources // associated with them. Note do not free pms yet here, as we need it in // additional cleanup further below. // if (MMSYSERR_NOERROR != mmrc) { if (NULL != pms) { msg = MODM_CLOSE; pmsi = pms->rgIds; for (idx = 0; idx < pms->cIds; idx++, pmsi++) { if ((pmsi->fdwId & (MSI_F_OPENED|MSI_F_FIRST)) == (MSI_F_OPENED|MSI_F_FIRST)) { mmrc2 = (MMRESULT)midiStreamMessage(pmsi, msg, 0L, 0L); if (MMSYSERR_NOERROR == mmrc2 && !(pmsi->fdwId & MSI_F_EMULATOR)) { if (pmsi->fdwId & MSI_F_INITIALIZEDCRITICALSECTION) { DeleteCriticalSection(&pmsi->CritSec); pmsi->fdwId &= ~MSI_F_INITIALIZEDCRITICALSECTION; } mregDecUsage(pmsi->hmd); } else { dprintf1(("midiStreamOpen_Cleanup: Close returned %u", mmrc2)); } } } } } else { *phms = PTtoH(HMIDISTRM, pms); msg = MM_MOM_OPEN; DriverCallback(pms->dwCallback, HIWORD(pms->fdwOpen), (HDRVR)PTtoH(HMIDISTRM, pms), msg, pms->dwInstance, 0, 0); } // // Now release driver references added by mregFindDevice. Those that are // actually still in use have had an extra reference added and thus will // still have a reference count on them even after the release done here. // if (pms) { pmsi = pms->rgIds; for (pmsi = pms->rgIds, idx = 0; idx < pms->cIds; idx++, pmsi++) { if (pmsi->hmd) mregDecUsage(pmsi->hmd); } } // // Free pms if there was an error // if ((MMSYSERR_NOERROR != mmrc) && (pms)) FreeHandle((PTtoH(HMIDI, pms))); return mmrc; } WINMMAPI MMRESULT WINAPI midiStreamClose( HMIDISTRM hms) { PMIDISTRM pms; PMIDISTRMID pmsi; DWORD idx; MMRESULT mmrc; V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE); dprintf1(("midiStreamClose(%04X)", (WORD)hms)); pms = HtoPT(PMIDISTRM, hms); pmsi = pms->rgIds; for (idx = 0; idx < pms->cIds; idx++, pmsi++) { if ((pmsi->fdwId & (MSI_F_OPENED|MSI_F_FIRST)) == (MSI_F_OPENED|MSI_F_FIRST)) { mmrc = (MMRESULT)midiStreamMessage(pmsi, MODM_CLOSE, 0L, 0L); if (MMSYSERR_NOERROR == mmrc && !(pmsi->fdwId & MSI_F_EMULATOR)) { WinAssert(pmsi->fdwId & MSI_F_INITIALIZEDCRITICALSECTION); DeleteCriticalSection(&pmsi->CritSec); pmsi->fdwId &= ~MSI_F_INITIALIZEDCRITICALSECTION; mregDecUsage(pmsi->hmd); } else { dprintf1(("midiStreamClose: Close returned %u", mmrc)); } } } dprintf1(("DriverCallback(%04X)", (WORD)hms)); DriverCallback(pms->dwCallback, HIWORD(pms->fdwOpen), (HDRVR)hms, MM_MOM_CLOSE, pms->dwInstance, 0, 0); dprintf1(("FreeHandle(%04X)", (WORD)hms)); FreeHandle(hms); return MMSYSERR_NOERROR; } /**************************************************************************** * @doc EXTERNAL MIDI M5 * * @func MMRESULT | midiStreamProperty | Sets or retrieves properties * of a MIDI data stream associated with a MIDI input or output device. * * @parm HMIDI | hm | Specifies the handle of the MIDI device that the * property is associated with. * * @parm LPBYTE | lppropdata | Specifies a pointer to the property data. * * @parm DWORD | dwProperty | Contains flags that specify the action * to perform and identify the appropriate property of the MIDI data stream. * requires setting two flags in each use. One flag * (either MIDIPROP_GET or MIDIPROP_SET) specifies an action. The other * identifies a specific property to examine or edit. * * @flag MIDIPROP_SET | Set the given property. * @flag MIDIPROP_GET | Retrieve the current setting of the given property. * @flag MIDIPROP_TIMEDIV | Time division property. * This property is valid for both input and output devices.

* points to a structure. This property can be set only * when the device is stopped. * * @flag MIDIPROP_TEMPO | Tempo property. * This property is valid for both input and output devices.

* points to a structure. The current tempo value can be * retrieved at any time. This function can set the tempo for input devices. * Output devices set the tempo by inserting PMSG_TEMPO events into the * MIDI data. * * @flag MIDIPROP_CBTIMEOUT | Timeout value property. * This property specifies the timeout value for loading buffers when a * MIDI device is in MIDI_IO_COOKED and MIDI_IO_RAW modes. The current * timeout value sets the maximum number of milliseconds that a buffer will * be held once any data is placed in it. If this timeout expires, the * buffer will be returned to the application even though it might not be * completely full.

points to a structure. * * @comm These properties are the default properties defined by MMSYSTEM. * Driver writers may implement and document their own properties. * * @rdesc The return value is one of the following values: * @flag MMSYSERR_INVALPARAM | The given handle or flags are invalid. * @flag MIDIERR_BADOPENMODE | The given handle is not open in MIDI_IO_COOKED * or MIDI_IO_RAW mode. * ***************************************************************************/ MMRESULT WINAPI midiStreamProperty( HMIDISTRM hms, LPBYTE lppropdata, DWORD dwProperty) { MMRESULT mmrc; V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE); if ((!(dwProperty&MIDIPROP_SET)) && (!(dwProperty&MIDIPROP_GET))) return MMSYSERR_INVALPARAM; V_RPOINTER(lppropdata, sizeof(DWORD), MMSYSERR_INVALPARAM); if (dwProperty&MIDIPROP_SET) { V_RPOINTER(lppropdata, (UINT)(*(LPDWORD)(lppropdata)), MMSYSERR_INVALPARAM); } else { V_WPOINTER(lppropdata, (UINT)(*(LPDWORD)(lppropdata)), MMSYSERR_INVALPARAM); } mmrc = (MMRESULT)midiStreamBroadcast(HtoPT(PMIDISTRM, hms), MODM_PROPERTIES, (DWORD_PTR)lppropdata, dwProperty); return mmrc; } /***************************************************************************** * @doc EXTERNAL MIDI * * @api MMRESULT | midiOutGetPosition | Retrieves the current * playback position of the specified MIDI output device. * * @parm HMIDIOUT | hmo | Specifies a handle to the MIDI output device. * * @parm LPMMTIME | pmmt | Specifies a far pointer to an * structure. * * @parm UINT | cbmmt | Specifies the size of the structure. * * @rdesc Returns zero if the function is successful. Otherwise, it returns * an error number. Possible error values are: * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid. * * @comm Before calling , set the field * of to indicate the time format that you desire. After * calling , check the field * to determine if the desired time format is supported. If the desired * format is not supported, will specify an alternative * format. * * The position is set to zero when the device is opened, reset, or * stopped. ****************************************************************************/ MMRESULT WINAPI midiStreamPosition( HMIDISTRM hms, LPMMTIME pmmt, UINT cbmmt) { MMRESULT mmrc; V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE); V_WPOINTER(pmmt, cbmmt, MMSYSERR_INVALPARAM); mmrc = (MMRESULT)midiStreamMessage(HtoPT(PMIDISTRM, hms)->rgIds, MODM_GETPOS, (DWORD_PTR)pmmt, (DWORD)cbmmt); return mmrc; } /***************************************************************************** * @doc EXTERNAL MIDI * * @api MMRESULT | midiStreamStop | Turns off all notes on all MIDI * channels for the specified MIDI output device. Any pending * system-exclusive or polymessage output buffers are marked as done and * returned to the application. While turns off all notes, * turns off only those notes that have been turned on * by a MIDI note-on message. * * @parm HMIDIOUT | hMidiOut | Specifies a handle to the MIDI output * device. * * @rdesc Returns zero if the function is successful. Otherwise, it returns * an error number. Possible error values are: * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid. * @flag MIDIERR_BADOPENMODE | Specified device handle is not opened in * MIDI_IO_COOKED mode. * * @comm To turn off all notes, a note-off message for each note for each * channel is sent. In addition, the sustain controller is turned off for * each channel. * * @xref midiOutLongMsg midiOutClose midiOutReset ****************************************************************************/ MMRESULT WINAPI midiStreamStop(HMIDISTRM hms) { PMIDISTRM pms; MMRESULT mmrc; V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE); pms = HtoPT(PMIDISTRM, hms); mmrc = (MMRESULT)midiStreamBroadcast(pms, MODM_STOP, 0, 0); return mmrc; } /***************************************************************************** * @doc EXTERNAL MIDI * * @api MMRESULT | midiStreamPause | Pauses playback on a specified * MIDI output device. The current playback position is saved. Use * to resume playback from the current playback position. * This call is only valid for handles opened in MIDI_IO_COOKED mode. * * @parm HMIDIOUT | hmo | Specifies a handle to the MIDI output * device. * * @rdesc Returns zero if the function is successful. Otherwise, it returns * an error number. Possible error values are: * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified device was not opened with * the MIDI_IO_COOKED flag. * * @comm Calling this function when the output is already paused has no * effect, and the function returns zero. * * @xref midiStreamRestart ****************************************************************************/ MMRESULT WINAPI midiStreamPause( HMIDISTRM hms) { MMRESULT mmrc; V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE); mmrc = (MMRESULT)midiStreamBroadcast(HtoPT(PMIDISTRM, hms), MODM_PAUSE, 0, 0); return mmrc; } /***************************************************************************** * @doc EXTERNAL MIDI * * @api MMRESULT | midiStreamRestart | Restarts a paused MIDI * output device. * * @parm HMIDIOUT | hmo | Specifies a handle to the MIDI output * device. * * @rdesc Returns zero if the function is successful. Otherwise, it returns * an error number. Possible error values are: * @flag MMSYSERR_INVALHANDLE | Specified device handle is invalid. * @flag MMSYSERR_INVALPARAM | Specified device was not opened with * the MIDI_IO_COOKED flag. * * @comm Calling this function when the output is not paused has no * effect, and the function returns zero. * * @xref midiOutPause ****************************************************************************/ MMRESULT WINAPI midiStreamRestart( HMIDISTRM hms) { MMRESULT mmrc; MMTIME mmt; DWORD tkTime; DWORD msTime; PMIDISTRM pms; PMIDISTRMID pmsi; DWORD idx; V_HANDLE(hms, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE); tkTime = 0; pms = HtoPT(PMIDISTRM, hms); for (idx = 0, pmsi = pms->rgIds; idx < pms->cIds; idx++, pmsi++) if (pmsi->fdwId & MSI_F_FIRST) { mmt.wType = TIME_TICKS; mmrc = (MMRESULT)midiStreamMessage(pmsi, MODM_GETPOS, (DWORD_PTR)&mmt, sizeof(mmt)); if (mmrc) { dprintf(("midiOutRestart: Device %u returned %u", idx, mmrc)); return mmrc; } if (mmt.wType == TIME_TICKS) { if (mmt.u.ticks > tkTime) tkTime = mmt.u.ticks; } else { dprintf(("midiOutRestart: Device %u does not support ticks", idx)); return MIDIERR_NOTREADY; } } // Fudge time to allow device setup // msTime = timeGetTime(); dprintf(("midiOutRestart: Tick %lu timeGetTime %lu", tkTime, msTime)); mmrc = (MMRESULT)midiStreamBroadcast(pms, MODM_RESTART, msTime, tkTime); return mmrc; } MMRESULT WINAPI midiStreamOut( HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr, UINT cbMidiHdr) { PMIDISTRMID pmsi; PMIDISTRM pms; UINT idx; UINT cSent; LPMIDIHDR lpmhWork; BOOL fCallback; MMRESULT mmrc; dprintf2(( "midiStreamOut(%04X, %08lX, %08lX)", (UINT_PTR)hMidiStrm, (DWORD_PTR)lpMidiHdr, lpMidiHdr->dwBytesRecorded)); V_HANDLE(hMidiStrm, TYPE_MIDISTRM, MMSYSERR_INVALHANDLE); V_HEADER(lpMidiHdr, cbMidiHdr, TYPE_MIDIOUT, MMSYSERR_INVALPARAM); pms = HtoPT(PMIDISTRM, hMidiStrm); for (pmsi = pms->rgIds, idx = 0; idx < pms->cIds; idx++, pmsi++) if ( (!(pmsi->fdwId & MSI_F_EMULATOR)) && (!(pmsi->hmd)) ) return MMSYSERR_NODRIVER; if (!(lpMidiHdr->dwFlags&MHDR_PREPARED)) { dprintf1(( "midiOutPolyMsg: !MHDR_PREPARED")); return MIDIERR_UNPREPARED; } if (lpMidiHdr->dwFlags&MHDR_INQUEUE) { dprintf1(( "midiOutPolyMsg: Still playing!")); return MIDIERR_STILLPLAYING; } if (lpMidiHdr->dwBytesRecorded > lpMidiHdr->dwBufferLength || (lpMidiHdr->dwBytesRecorded & 3)) { dprintf1(( "Bytes recorded too long or not DWORD aligned.")); return MMSYSERR_INVALPARAM; } // // Polymsg buffers are limited to 64k in order that we (and the driver) // not have to do huge pointer manipulation. // Length must also be DWORD aligned. // if ((lpMidiHdr->dwBufferLength > 65535L) || (lpMidiHdr->dwBufferLength&3)) { dprintf1(( "midiOutPolyMsg: Buffer > 64k or not DWORD aligned")); return MMSYSERR_INVALPARAM; } EnterCriticalSection(&midiStrmHdrCritSec); LeaveCriticalSection(&midiStrmHdrCritSec); lpMidiHdr->dwReserved[MH_REFCNT] = 0; lpMidiHdr->dwFlags |= (MHDR_SENDING|MHDR_INQUEUE|MHDR_ISSTRM); lpmhWork = (LPMIDIHDR)lpMidiHdr->dwReserved[MH_SHADOW]; pmsi = pms->rgIds; for (idx = 0, cSent = 0; idx < pms->cIds; idx++, pmsi++) { if (pmsi->fdwId & MSI_F_FIRST) { lpmhWork->dwBytesRecorded = lpMidiHdr->dwBytesRecorded; lpmhWork->dwFlags |= MHDR_ISSTRM; mmrc = (MMRESULT)midiStreamMessage(pmsi, MODM_STRMDATA, (DWORD_PTR)lpmhWork, sizeof(*lpmhWork)); if (mmrc == MMSYSERR_NOERROR) ++lpMidiHdr->dwReserved[MH_REFCNT], ++cSent; lpmhWork++; } } fCallback = FALSE; EnterCriticalSection(&midiStrmHdrCritSec); lpMidiHdr->dwFlags &= ~MHDR_SENDING; if (cSent && 0 == lpMidiHdr->dwReserved[MH_REFCNT]) { fCallback = TRUE; } LeaveCriticalSection(&midiStrmHdrCritSec); if (fCallback) { lpMidiHdr->dwFlags &= ~MHDR_INQUEUE; lpMidiHdr->dwFlags |= MHDR_DONE; DriverCallback(pms->dwCallback, HIWORD(pms->fdwOpen), (HDRVR)hMidiStrm, MM_MOM_DONE, pms->dwInstance, (DWORD_PTR)lpMidiHdr, 0); } if (!cSent) { lpMidiHdr->dwFlags &= ~MHDR_INQUEUE; return mmrc; } else return MMSYSERR_NOERROR; } DWORD FAR PASCAL midiStreamMessage(PMIDISTRMID pmsi, UINT msg, DWORD_PTR dwP1, DWORD_PTR dwP2) { MMRESULT mrc; if (!(pmsi->fdwId & MSI_F_EMULATOR)) { EnterCriticalSection(&pmsi->CritSec); mrc = (*(pmsi->drvMessage)) (0, msg, pmsi->dwDrvUser, dwP1, dwP2); try { LeaveCriticalSection(&pmsi->CritSec); } except(EXCEPTION_EXECUTE_HANDLER) { } return mrc; } else { mrc = mseMessage(msg, pmsi->dwDrvUser, dwP1, dwP2); } return mrc; } DWORD FAR PASCAL midiStreamBroadcast( PMIDISTRM pms, UINT msg, DWORD_PTR dwP1, DWORD_PTR dwP2) { DWORD idx; DWORD mmrc; DWORD mmrcRet; PMIDISTRMID pmsi; ENTER_MM_HANDLE((HMIDI)pms); mmrcRet = MMSYSERR_NOERROR; pmsi = pms->rgIds; for (idx = pms->cIds; idx; idx--, pmsi++) { if (pmsi->fdwId & MSI_F_FIRST) { mmrc = midiStreamMessage(pmsi, msg, dwP1, dwP2); if (MMSYSERR_NOERROR != mmrc) mmrcRet = mmrc; } } LEAVE_MM_HANDLE((HMIDI)pms); return mmrcRet; } void CALLBACK midiOutStreamCallback( HMIDISTRM hMidiOut, WORD wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { PMIDISTRM pms = HtoPT(PMIDISTRM, hMidiOut); LPMIDIHDR lpmh = (LPMIDIHDR)dwParam1; if (MM_MOM_POSITIONCB == wMsg) { LPMIDIHDR lpmh2 = (LPMIDIHDR)lpmh->dwReserved[MH_PARENT]; lpmh2->dwOffset = lpmh->dwOffset; DriverCallback(pms->dwCallback, HIWORD(pms->fdwOpen), (HDRVR)hMidiOut, MM_MOM_POSITIONCB, pms->dwInstance, (DWORD_PTR)lpmh2, 0); return; } else if (MM_MOM_DONE != wMsg) return; #ifdef DEBUG { DWORD dwDelta = timeGetTime() - (DWORD)lpmh->dwReserved[7]; if (dwDelta > 1) dprintf1(("Took %lu ms to deliver callback!", dwDelta)); } #endif lpmh = (LPMIDIHDR)lpmh->dwReserved[MH_PARENT]; dprintf2(("mOSCB PMS %04X HDR %08lX", (UINT_PTR)pms, (DWORD_PTR)lpmh)); EnterCriticalSection(&midiStrmHdrCritSec); --lpmh->dwReserved[MH_REFCNT]; if (0 == lpmh->dwReserved[MH_REFCNT] && (!(lpmh->dwFlags & MHDR_SENDING))) { lpmh->dwFlags &= ~MHDR_INQUEUE; lpmh->dwFlags |= MHDR_DONE; LeaveCriticalSection(&midiStrmHdrCritSec); #ifdef DEBUG lpmh->dwReserved[7] = timeGetTime(); #endif DriverCallback(pms->dwCallback, HIWORD(pms->fdwOpen), (HDRVR)hMidiOut, MM_MOM_DONE, pms->dwInstance, (DWORD_PTR)lpmh, 0); } else { LeaveCriticalSection(&midiStrmHdrCritSec); } }