|
|
/*******************************************************************************
* * 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. * <f midiStreamProperty> 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. <p lppropdata> * points to a <t MIDIPROPTIMEDIV> 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. <p lppropdata> * points to a <t MIDIPROPTEMPO> 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. <p lppropdata> points to a <t MIDIPROPCBTIMEOUT> 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 <t MMTIME> * structure. * * @parm UINT | cbmmt | Specifies the size of the <t MMTIME> 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 <f midiOutGetPosition>, set the <e MMTIME.wType> field * of <t MMTIME> to indicate the time format that you desire. After * calling <f midiOutGetPosition>, check the <e MMTIME.wType> field * to determine if the desired time format is supported. If the desired * format is not supported, <e MMTIME.wType> 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 <f midiOutReset> turns off all notes, * <f midiStreamStop> 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 * <f midiStreamRestart> 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); } }
|