|
|
//==========================================================================;
//
// msacmmap.c
//
// Copyright (c) 1992-1999 Microsoft Corporation
//
// Description:
//
//
// History:
// 9/18/93 cjp [curtisp]
//
//==========================================================================;
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <mmddkp.h>
#include <mmreg.h>
#include <msacm.h>
#include <msacmdrv.h>
#include <memory.h>
#include "muldiv32.h"
#include "msacmmap.h"
#include "debug.h"
extern ACMGARB acmgarb;
//
//
//
ZYZPCMFORMAT gaPCMFormats[] = { { NULL, NULL, 5510}, { NULL, NULL, 6620}, { NULL, NULL, 8000}, { NULL, NULL, 9600}, { NULL, NULL, 11025}, { NULL, NULL, 16000}, { NULL, NULL, 18900}, { NULL, NULL, 22050}, { NULL, NULL, 27420}, { NULL, NULL, 32000}, { NULL, NULL, 33075}, { NULL, NULL, 37800}, { NULL, NULL, 44100}, { NULL, NULL, 48000}, { NULL, NULL, 0} // terminator
// WARNING!!! WARNING!!!
// If you change this array size update the size in:
// init.c:mapSettingsRestore
};
//==========================================================================;
//
// -= INTERRUPT TIME CODE FOR WIN 16 =-
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// BOOL mapWaveDriverCallback
//
// Description:
// This calls DriverCallback for a WAVEHDR.
//
// NOTE! this routine must be in a FIXED segment in Win 16.
//
// Arguments:
// LPMAPSTREAM pms: Pointer to instance data.
//
// UINT uMsg: The message.
//
// DWORD dw1: Message DWORD (dw2 is always set to 0).
//
// Return (BOOL):
// The result is non-zero if the function was able to do the callback.
// Zero is returned if no callback was made.
//
// History:
// 11/15/92 cjp [curtisp]
//
//--------------------------------------------------------------------------;
#ifndef WIN32
#pragma alloc_text(FIX_TEXT, mapWaveDriverCallback)
//
// NOTE! we *DO NOT* turn off optimization for Win 3.1 builds to keep the
// compiler from using extended registers (we compile with -G3). this
// function causes no extended registers to be used (like mapWaveCallback
// does).
//
// !!! IF YOU TOUCH ANY OF THIS CODE, YOU MUST VERIFY THAT NO EXTENDED
// !!! REGISTERS GET USED IN WIN 3.1 OR YOU WILL BREAK EVERYTHING !!!
//
// #if (WINVER <= 0x030A)
// #pragma optimize("", off)
// #endif
//
#endif
EXTERN_C BOOL FNGLOBAL mapWaveDriverCallback ( LPMAPSTREAM pms, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2 ) { BOOL f;
//
// invoke the callback function, if it exists. dwFlags contains
// wave driver specific flags in the LOWORD and generic driver
// flags in the HIWORD
//
if (0L == pms->dwCallback) return (FALSE);
f = DriverCallback(pms->dwCallback, // user's callback DWORD
HIWORD(pms->fdwOpen), // callback flags
(HDRVR)pms->hwClient, // handle to the wave device
uMsg, // the message
pms->dwInstance, // user's instance data
dw1, // first DWORD
dw2); // second DWORD
return (f); } // mapWaveDriverCallback()
//
// #ifndef WIN32
// #if (WINVER <= 0x030A)
// #pragma optimize("", on)
// #endif
// #endif
//
//--------------------------------------------------------------------------;
//
// void mapWaveCallback
//
// Description:
//
//
// Arguments:
// HWAVE hw:
//
// UINT uMsg:
//
// DWORD dwUser:
//
// DWORD dwParam1:
//
// DWORD dwParam2:
//
// Return (void):
//
// History:
// 08/02/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
#ifndef WIN32
#pragma alloc_text(FIX_TEXT, mapWaveCallback)
//
// NOTE! we turn off optimization for Win 3.1 builds to keep the compiler
// from using extended registers (we compile with -G3). it is not safe
// in Win 3.1 to use extended registers at DriverCallback time unless we
// save them ourselves. i don't feel like writing the assembler code for
// that when it buys us almost nothing..
//
// everything is cool under Win 4.0 since DriverCallback is 386 aware.
//
#if (WINVER <= 0x030A)
#pragma optimize("", off)
#endif
#endif
EXTERN_C void FNCALLBACK mapWaveCallback ( HWAVE hw, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) { LPWAVEHDR pwh; LPMAPSTREAM pms;
#if !defined(WIN32) && (WINVER <= 0x030A)
_asm _emit 0x66 ; pushad _asm _emit 0x60 #endif
//
// WARNING DANGER WARNING DANGER WARNING DANGER WARNING DANGER WARNING
//
// THIS IS AT INTERRUPT TIME--DO NOT CALL ANY FUNCTIONS THAT
// YOU ARE NOT ABSOLUTELY SURE YOU CAN CALL AT INTERRUPT TIME!
//
// out debugging 'DPF' stuff is NOT interrupt safe
//
// WARNING DANGER WARNING DANGER WARNING DANGER WARNING DANGER WARNING
//
pms = (LPMAPSTREAM)dwUser;
//
//
//
switch (uMsg) { //
// eat the WIM_OPEN and WIM_CLOSE messages for 'mapped' input
// since we must deal with them specially (due to our background
// task).
//
case WIM_OPEN: case WIM_CLOSE: if (NULL != pms->has) break; mapWaveDriverCallback(pms, uMsg, 0L, 0L); break;
//
// eat the WOM_OPEN and WOM_CLOSE messages for 'mapped' output
// because we deal with them specially in mapWaveOpen and
// mapWaveClose. See comments in those functions.
//
// note that we're checking pms->had, not pms->has, cuz this message
// may come thru on the physical device open after we've decidec that
// we wish to map (using the acm driver represented by had) but
// before we've opened a stream (which would be represented by has).
//
case WOM_OPEN: case WOM_CLOSE: if (NULL != pms->had) break; mapWaveDriverCallback(pms, uMsg, 0L, 0L); break;
//
// dwParam1 is the 'shadow' LPWAVEHDR that is done.
//
case WOM_DONE: //
// get the shadow header
//
pwh = (LPWAVEHDR)dwParam1;
//
// passthrough mode?
//
if (NULL != pms->has) { //
// get the client's header and set done bit
//
pwh = (LPWAVEHDR)pwh->dwUser;
pwh->dwFlags |= WHDR_DONE; pwh->dwFlags &= ~WHDR_INQUEUE; }
//
// nofify the client that the block is done
//
mapWaveDriverCallback(pms, WOM_DONE, (DWORD_PTR)pwh, 0); break;
//
// dwParam1 is the 'shadow' LPWAVEHDR that is done.
//
case WIM_DATA: //DPF(2, "WIM_DATA: callback");
if (NULL == pms->has) { //
// passthrough mode--notify the client that the block is
// done
//
mapWaveDriverCallback(pms, WIM_DATA, dwParam1, 0L); break; }
//
// convert mode--convert data then callback user.
//
if (!PostAppMessage(pms->htaskInput, WIM_DATA, 0, dwParam1)) { //
// !!! ERROR what can we do....?
//
//DPF(0, "!WIM_DATA: XXXXXXXXXXX ERROR Post message failed XXXXXX");
} else { #ifdef WIN32
InterlockedIncrement((LPLONG)&pms->nOutstanding); #endif // WIN32
} break;
default: mapWaveDriverCallback(pms, uMsg, dwParam1, dwParam2); break;
}
#if !defined(WIN32) && (WINVER <= 0x030A)
_asm _emit 0x66 ; popad _asm _emit 0x61 #endif
} // mapWaveCallback()
#if !defined(WIN32) && (WINVER <= 0x030A)
#pragma optimize("", on)
#endif
//==========================================================================;
//
//
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// MMRESULT mapWaveGetPosition
//
// Description:
// Get the stream position in samples or bytes.
//
// Arguments:
// LPMAPSTREAM pms:
//
// LPMMTIME pmmt: Pointer to an MMTIME structure.
//
// UINT uSize: Size of the MMTIME structure.
//
// Return (DWORD):
//
// History:
// 07/19/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL mapWaveGetPosition ( LPMAPSTREAM pms, LPMMTIME pmmt, UINT cbmmt ) { MMRESULT mmr; DWORD dw;
if (cbmmt < sizeof(MMTIME)) { DPF(0, "!mapWaveGetPosition: bad size passed for MMTIME (%u)", cbmmt); return (MMSYSERR_ERROR); }
if ((TIME_SAMPLES != pmmt->wType) && (TIME_BYTES != pmmt->wType)) { DPF(1, "mapWaveGetPosition: time format %u?!? forcing TIME_BYTES!", pmmt->wType); pmmt->wType = TIME_BYTES; }
//
// get the position in samples or bytes..
//
// if an error occured .OR. we are passthrough mode (has is NULL)
// then just return result--otherwise we need to convert the real
// time to the client's time...
//
mmr = pms->fnWaveGetPosition(pms->hwReal, pmmt, cbmmt); if (MMSYSERR_NOERROR != mmr) { DPF(0, "!mapWaveGetPosition: physical get position failed? mmr=%u", mmr); return (mmr); }
//
// in passthrough mode?
//
if (NULL == pms->has) { return (mmr); }
//
// convert real time to client's time
//
switch (pmmt->wType) { case TIME_SAMPLES: dw = pmmt->u.sample; pmmt->u.sample = MulDivRN(dw, pms->pwfxClient->nSamplesPerSec, pms->pwfxReal->nSamplesPerSec);
DPF(4, "GetPos(SAMPLES) real=%lu, client=%lu", dw, pmmt->u.sample); break;
case TIME_BYTES: dw = pmmt->u.cb; pmmt->u.cb = MulDivRN(dw, pms->pwfxClient->nAvgBytesPerSec, pms->pwfxReal->nAvgBytesPerSec); DPF(4, "GetPos(BYTES) real=%lu, client=%lu", dw, pmmt->u.cb); break;
default: DPF(0, "!mapWaveGetPosition() received unrecognized return format!"); return (MMSYSERR_ERROR); }
return (MMSYSERR_NOERROR); } // mapWaveGetPosition()
//==========================================================================;
//
// Notes on error code priorities FrankYe 09/28/94
//
// The error code that is returned to the client and the error code
// that is returned by internal functions are not always the same. The
// primary reason for this is the way we handle MMSYSERR_ALLOCATED and
// WAVERR_BADFORMAT in multiple device situations.
//
// For example, suppose we have two devices. If one returns ALLOCATED and
// the other returns BADFORMAT then we prefer to return ALLOCATED to the
// client because BADFORMAT implies no devices understand the format. So,
// for the client, we prefer to return ALLOCATED over BADFORMAT.
//
// On the other hand, we want the mapper to be able to take advantage of
// situations where all the devices are allocated. If all devices are
// allocated then there is no need to continue trying to find a workable
// map stream. So, for internal use, we prefer BADFORMAT over ALLOCATED.
// That way if we see ALLOCATED then we know _all_ devices are allocated
// and we can abort trying to create a map stream. (If the client sees
// ALLOCATED, it only means that at least one device is allocated.)
//
// Client return codes are usually stored in the mmrClient member of the
// MAPSTREAM structure. Internal return codes are returned via
// function return values.
//
// Below are functions that prioritize error codes and update error codes
// given the last err, the current err, and the priorities of the errs.
// Notice that the prioritization of the err codes for the client is very
// similar to for internal use. The only difference is the ordering of
// MMSYSERR_ALLOCATED and WAVERR_BADFORMAT for reasons stated above.
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// UINT mapErrGetClientPriority
//
// Description:
//
// Arguments:
// MMRESULT mmr :
//
// Return (VOID):
//
// History:
// 09/29/94 Frankye Created
//
//--------------------------------------------------------------------------;
UINT FNLOCAL mapErrGetClientPriority( MMRESULT mmr ) { switch (mmr) { case MMSYSERR_NOERROR: return 6; case MMSYSERR_ALLOCATED: return 5; case WAVERR_BADFORMAT: return 4; case WAVERR_SYNC: return 3; case MMSYSERR_NOMEM: return 2; default: return 1; case MMSYSERR_ERROR: return 0; } }
//--------------------------------------------------------------------------;
//
// VOID mapErrSetClientError
//
// Description:
//
// Arguments:
// LPMMRESULT lpmmr :
//
// MMRESULT mmr :
//
// Return (VOID):
//
// History:
// 09/29/94 Frankye Created
//
//--------------------------------------------------------------------------;
VOID FNLOCAL mapErrSetClientError( LPMMRESULT lpmmr, MMRESULT mmr ) { if (mapErrGetClientPriority(mmr) > mapErrGetClientPriority(*lpmmr)) { *lpmmr = mmr; } }
//--------------------------------------------------------------------------;
//
// UINT mapErrGetPriority
//
// Description:
//
// Arguments:
// MMRESULT mmr :
//
// Return (VOID):
//
// History:
// 09/29/94 Frankye Created
//
//--------------------------------------------------------------------------;
UINT FNLOCAL mapErrGetPriority( MMRESULT mmr ) { switch (mmr) { case MMSYSERR_NOERROR: return 6; case WAVERR_BADFORMAT: return 5; case MMSYSERR_ALLOCATED: return 4; case WAVERR_SYNC: return 3; case MMSYSERR_NOMEM: return 2; default: return 1; case MMSYSERR_ERROR: return 0; } }
//--------------------------------------------------------------------------;
//
// VOID mapErrSetError
//
// Description:
//
// Arguments:
// LPMMRESULT lpmmr :
//
// MMRESULT mmr :
//
// Return (VOID):
//
// History:
// 09/29/94 Frankye Created
//
//--------------------------------------------------------------------------;
VOID FNLOCAL mapErrSetError( LPMMRESULT lpmmr, MMRESULT mmr ) { if (mapErrGetPriority(mmr) > mapErrGetPriority(*lpmmr)) { *lpmmr = mmr; } }
//--------------------------------------------------------------------------;
//
// UINT mapDriverOpenWave
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// LPWAVEFORMATEX pwfx:
//
// Return (UINT):
//
//
//--------------------------------------------------------------------------;
UINT FNLOCAL mapDriverOpenWave ( LPMAPSTREAM pms, LPWAVEFORMATEX pwfx ) { MMRESULT mmr; MMRESULT mmrReturn; BOOL fPrefOnly; BOOL fQuery; UINT uPrefDevId; UINT uDevId; UINT cNumDevs; BOOL fTriedMappableId; BOOL fFoundNonmappableId;
fQuery = (0 != (WAVE_FORMAT_QUERY & pms->fdwOpen));
//
// there are four different cases we need to handle when trying
// to open a compatible wave device (for either input or output):
//
// 1. the normal case is 'no preference'--which means that
// the user has selected '[none]' in the combo box for
// the preferred wave device. in this case, gpag->uIdPreferredXXX
// will be -1 (and gpag->fPreferredOnly is ignored).
//
// 2. the next two cases are when a device has been chosen as the
// 'preferred device'--so gpag->uIdPreferredXXX will be the device
// id of this preferred device. so the two cases are then:
//
// a. if gpag->pSettings->fPreferredOnly is FALSE, then try the
// 'preferred' device first, and if that fails, try all
// remaining devices.
//
// b. if gpag->pSettings->fPreferredOnly is TRUE, then we will
// ONLY try the preferred device--if that fails, we do NOT
// continue.
//
// 3. a device ID to which the mapper should map may have been
// specified using the WAVE_MAPPED flag.
//
//
//
// --== See if we are supposed to map to a specified device ==--
//
//
if (pms->fdwOpen & WAVE_MAPPED) { DWORD fdwOpen;
DPF(3, "mapDriverOpenWave: WAVE_MAPPED flag specified");
//
// The device ID to which to map was specified by MMSYSTEM in the
// uMappedDeviceID member of the WAVEOPENDESC structure passed in
// the WODM_OPEN message. It was saved in pms->uMappedDeviceID by
// mapWaveOpen().
//
uDevId = pms->uMappedDeviceID; fdwOpen = CALLBACK_FUNCTION | LOWORD(pms->fdwOpen); fdwOpen &= ~WAVE_MAPPED;
mmrReturn = pms->fnWaveOpen(&pms->hwReal, uDevId, pwfx, (DWORD_PTR)mapWaveCallback, (DWORD_PTR)pms, fdwOpen);
DPF(3, "--->opening device %d--mmr=%u", uDevId, mmrReturn);
if (MMSYSERR_NOERROR == mmrReturn) { pms->uIdReal = uDevId; }
mapErrSetClientError(&pms->mmrClient, mmrReturn); return (mmrReturn); }
//
// --== ==--
//
//
// Init some local vars
//
WAIT_FOR_MUTEX(gpag->hMutexSettings);
if (pms->fInput) { uPrefDevId = gpag->pSettings->uIdPreferredIn; cNumDevs = gpag->cWaveInDevs; } else { uPrefDevId = gpag->pSettings->uIdPreferredOut; cNumDevs = gpag->cWaveOutDevs; }
fTriedMappableId = FALSE; fFoundNonmappableId = FALSE;
fPrefOnly = (WAVE_MAPPER == uPrefDevId) ? FALSE : gpag->pSettings->fPreferredOnly;
mmrReturn = MMSYSERR_ERROR;
RELEASE_MUTEX(gpag->hMutexSettings);
//
// --== If we have a prefered device Id, then try opening it ==--
//
if (WAVE_MAPPER != uPrefDevId) { mmr = MMSYSERR_NOERROR; if (!fQuery) { mmr = pms->fnWaveOpen(&pms->hwReal, uPrefDevId, pwfx, 0L, 0L, WAVE_FORMAT_QUERY | LOWORD(pms->fdwOpen)); DPF(4, "---> querying preferred device %d--mmr=%u", uPrefDevId, mmr); DPF(4, "---> opened with flags = %08lx", WAVE_FORMAT_QUERY | LOWORD(pms->fdwOpen));
}
if (MMSYSERR_NOERROR == mmr) { mmr = pms->fnWaveOpen(&pms->hwReal, uPrefDevId, pwfx, (DWORD_PTR)mapWaveCallback, (DWORD_PTR)pms, CALLBACK_FUNCTION | LOWORD(pms->fdwOpen)); }
DPF(3, "---> opening preferred device %d--mmr=%u", uPrefDevId, mmr); DPF(3, "---> opened with flags = %08lx", CALLBACK_FUNCTION | LOWORD(pms->fdwOpen));
mapErrSetClientError(&pms->mmrClient, mmr); mapErrSetError(&mmrReturn, mmr);
if ((WAVERR_SYNC == mmr) && (fPrefOnly || (1 == cNumDevs))) { WAIT_FOR_MUTEX(gpag->hMutexSettings);
if (pms->fInput) { DPF(1, "--->preferred only INPUT device is SYNCRONOUS!"); gpag->pSettings->fSyncOnlyIn = TRUE; } else { DPF(1, "--->preferred only OUTPUT device is SYNCRONOUS!"); gpag->pSettings->fSyncOnlyOut = TRUE; }
RELEASE_MUTEX(gpag->hMutexSettings);
return (mmrReturn); }
if ((MMSYSERR_NOERROR == mmr) || fPrefOnly) { if (MMSYSERR_NOERROR == mmr) { pms->uIdReal = uPrefDevId; }
return (mmrReturn); }
fTriedMappableId = TRUE; }
//
// The prefered ID didn't work. Now we will step through each device
// ID and try to open it. We'll skip the uPrefDevId since we already
// tried it above. We will also skip device IDs that are not mappable
// devices (determined by sending DRV_QUERYMAPPABLE to the ID).
//
for (uDevId = 0; uDevId < cNumDevs; uDevId++) {
if (uDevId == uPrefDevId) continue;
mmr = pms->fnWaveMessage((HWAVE)LongToHandle(uDevId), DRV_QUERYMAPPABLE, 0L, 0L); if (MMSYSERR_NOERROR != mmr) { DPF(3, "--->skipping non-mappable device %d", uDevId); fFoundNonmappableId = TRUE; continue; }
if (!fQuery) { mmr = pms->fnWaveOpen(&pms->hwReal, uDevId, pwfx, 0L, 0L, WAVE_FORMAT_QUERY | LOWORD(pms->fdwOpen)); DPF(4, "---> querying device %d--mmr=%u", uDevId, mmr); }
if (MMSYSERR_NOERROR == mmr) { mmr = pms->fnWaveOpen(&pms->hwReal, uDevId, pwfx, (DWORD_PTR)mapWaveCallback, (DWORD_PTR)pms, CALLBACK_FUNCTION | LOWORD(pms->fdwOpen));
DPF(3, "---> opening device %d--mmr=%u", uDevId, mmr); }
mapErrSetClientError(&pms->mmrClient, mmr); mapErrSetError( &mmrReturn, mmr );
if (MMSYSERR_NOERROR == mmr) { pms->uIdReal = uDevId; return (mmrReturn); }
fTriedMappableId = TRUE;
}
if (fFoundNonmappableId && !fTriedMappableId) { mapErrSetClientError(&pms->mmrClient, MMSYSERR_ALLOCATED); mapErrSetError(&mmrReturn, MMSYSERR_ALLOCATED); }
return (mmrReturn);
} // mapDriverOpenWave()
//--------------------------------------------------------------------------;
//
// BOOL FindBestPCMFormat
//
// Description:
//
//
// Arguments:
// LPWAVEFORMATEX pwfx:
//
// LPWAVEFORMATEX pwfPCM:
//
// BOOL fInput:
//
// UINT uDeviceId:
//
//
// Return (BOOL):
//
// History:
// 03/13/94 fdy [frankye]
// Expanded interface and function to take uDeviceId which specifies
// the wave device for which we want to FindBestPCMFormat. fInput
// specifies whether this device is an input or output device.
//
//
//--------------------------------------------------------------------------;
BOOL FNLOCAL FindBestPCMFormat ( LPWAVEFORMATEX pwfx, LPWAVEFORMATEX pwfxPCM, BOOL fInput, UINT uDeviceId ) { BYTE bChannels; BYTE bBitsPerSample; UINT uBlockAlign; UINT i, j; UINT w; UINT uNeededBits; DWORD dwPrevError; DWORD dwError; DWORD dwSamplesPerSec; UINT uFlags;
//
// -= the PCM mis-mapper =-
//
// i'm sure this will generate all sorts of neat bug reports and
// complaints, but this is the algorithm we use to choose a PCM
// format:
//
// o we regard stereo as very important to maintain. the reason
// for this is that if a file was authored as stereo, there
// was probably a good reason for doing so...
//
// o the next most important component is the sample frequency;
// we try to find the closest supported sample frequency
//
// o finally, we don't care about bits per sample
// so we'll try to maintain the input size and change it if
// we need to
//
dwSamplesPerSec = pwfx->nSamplesPerSec; bChannels = (BYTE)pwfx->nChannels;
//
// build a bit pattern that we can look for..
//
findbest_Loop:
uNeededBits = ZYZPCMF_OUT_M08 | ZYZPCMF_OUT_M16; if (bChannels == 2) uNeededBits <<= 1;
if (fInput) uNeededBits <<= 8;
dwPrevError = (DWORD)-1;
//
// first find the closest sample rate that supports the current number
// of channels
//
for (j = (UINT)-1, i = 0; gaPCMFormats[i].uSamplesPerSec; i++) { //
// if no bits that we are looking for are set, then continue
// searching--if any of our bits are set, then check if this
// sample rate is better than our previous choice...
//
uFlags = fInput ? gaPCMFormats[i].uFlagsInput[uDeviceId] : gaPCMFormats[i].uFlagsOutput[uDeviceId]; if (uFlags & uNeededBits) { if (dwSamplesPerSec > (DWORD)gaPCMFormats[i].uSamplesPerSec) dwError = dwSamplesPerSec - gaPCMFormats[i].uSamplesPerSec; else dwError = (DWORD)gaPCMFormats[i].uSamplesPerSec - dwSamplesPerSec;
if (dwError < dwPrevError) { j = i; dwPrevError = dwError; } } }
//
// if we didn't find a format that will work, then shift the channels
// around and try again...
//
if (j == (UINT)-1) { //
// if we already tried channel shifting, then we're hosed... this
// would probably mean that no wave devices are installed that
// can go in fInput... like if the person only has the PC
// Squeaker--you cannot record...
//
if ((BYTE)pwfx->nChannels != bChannels) { DPF(0, "!FindBestPCMFormat: failed to find suitable format!"); return (FALSE); }
//
// shift the channels and try again
//
bChannels = (bChannels == (BYTE)2) ? (BYTE)1 : (BYTE)2; goto findbest_Loop; }
//
// j = the index to the format that we should be using
// uNeededBits = the bits used to find 'j'
// fInput = the direction we are trying to go with the data
// bChannels = the number of channels that we need to use
//
uFlags = fInput ? gaPCMFormats[j].uFlagsInput[uDeviceId] : gaPCMFormats[j].uFlagsOutput[uDeviceId]; w = uFlags & uNeededBits;
//
// normalize our bits to Mono Output--relative bit positions are the
// same for input/output stereo/mono
//
if (fInput) w >>= 8;
if (bChannels == 2) w >>= 1;
//
// if both 8 and 16 bit are supported by the out device AND the source
// format is PCM, then use the one that matches the source format
//
if ((pwfx->wFormatTag == WAVE_FORMAT_PCM) && ((w & ZYZPCMF_OUT_MONO) == ZYZPCMF_OUT_MONO)) { bBitsPerSample = (BYTE)pwfx->wBitsPerSample; }
//
// either not PCM source or device does not support both 8 and 16 bit;
// so choose whatever is available for the destination
//
else { bBitsPerSample = (w & ZYZPCMF_OUT_M16) ? (BYTE)16 : (BYTE)8; }
dwSamplesPerSec = gaPCMFormats[j].uSamplesPerSec; uBlockAlign = ((bBitsPerSample >> 3) << (bChannels >> 1));
//
// finally fill in the PCM destination format structure with the PCM
// format we decided is 'best'
//
pwfxPCM->wFormatTag = WAVE_FORMAT_PCM; pwfxPCM->nChannels = bChannels; pwfxPCM->nBlockAlign = (WORD)uBlockAlign; pwfxPCM->nSamplesPerSec = dwSamplesPerSec; pwfxPCM->nAvgBytesPerSec = dwSamplesPerSec * uBlockAlign; pwfxPCM->wBitsPerSample = bBitsPerSample;
return (TRUE); } // FindBestPCMFormat()
//==========================================================================;
//
//
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// MMRESULT mapDriverFindMethod0
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (MMRESULT):
//
// History:
// 08/04/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL mapDriverFindMethod0 ( LPMAPSTREAM pms ) { MMRESULT mmr;
//
// suggest anything!
//
mmr = acmFormatSuggest(pms->had, pms->pwfxClient, pms->pwfxReal, pms->cbwfxReal, 0L);
if (MMSYSERR_NOERROR == mmr) { //
// can it open real time?
//
mmr = acmStreamOpen(NULL, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY); if (MMSYSERR_NOERROR != mmr) { return (WAVERR_BADFORMAT); }
mmr = mapDriverOpenWave(pms, pms->pwfxReal); } else { mmr = WAVERR_BADFORMAT; }
return (mmr); } // mapDriverFindMethod0()
//--------------------------------------------------------------------------;
//
// MMRESULT mapDriverFindMethod1
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (MMRESULT):
//
// History:
// 08/04/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL mapDriverFindMethod1 ( LPMAPSTREAM pms ) { MMRESULT mmr;
//
// suggest PCM format for the Client
//
pms->pwfxReal->wFormatTag = WAVE_FORMAT_PCM;
mmr = acmFormatSuggest(pms->had, pms->pwfxClient, pms->pwfxReal, pms->cbwfxReal, ACM_FORMATSUGGESTF_WFORMATTAG);
if (MMSYSERR_NOERROR == mmr) { //
// can it open real time?
//
mmr = acmStreamOpen(NULL, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY); if (MMSYSERR_NOERROR != mmr) { return (WAVERR_BADFORMAT); }
mmr = mapDriverOpenWave(pms, pms->pwfxReal); } else { mmr = WAVERR_BADFORMAT; }
return (mmr); } // mapDriverFindMethod1()
//--------------------------------------------------------------------------;
//
// MMRESULT mapDriverFindMethod2
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (MMRESULT):
//
// History:
// 08/04/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL mapDriverFindMethod2 ( LPMAPSTREAM pms ) { MMRESULT mmr;
//
// suggest MONO PCM format for the Client
//
pms->pwfxReal->wFormatTag = WAVE_FORMAT_PCM; pms->pwfxReal->nChannels = 1;
mmr = acmFormatSuggest(pms->had, pms->pwfxClient, pms->pwfxReal, pms->cbwfxReal, ACM_FORMATSUGGESTF_WFORMATTAG | ACM_FORMATSUGGESTF_NCHANNELS);
if (MMSYSERR_NOERROR == mmr) { //
// can it open real time?
//
mmr = acmStreamOpen(NULL, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY); if (MMSYSERR_NOERROR != mmr) { return (WAVERR_BADFORMAT); }
mmr = mapDriverOpenWave(pms, pms->pwfxReal); } else { mmr = WAVERR_BADFORMAT; }
return (mmr); } // mapDriverFindMethod2()
//--------------------------------------------------------------------------;
//
// MMRESULT mapDriverFindMethod3
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (MMRESULT):
//
// History:
// 08/04/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL mapDriverFindMethod3 ( LPMAPSTREAM pms ) { MMRESULT mmr;
//
// suggest 8 bit PCM format for the Client
//
pms->pwfxReal->wFormatTag = WAVE_FORMAT_PCM; pms->pwfxReal->wBitsPerSample = 8;
mmr = acmFormatSuggest(pms->had, pms->pwfxClient, pms->pwfxReal, pms->cbwfxReal, ACM_FORMATSUGGESTF_WFORMATTAG | ACM_FORMATSUGGESTF_WBITSPERSAMPLE);
if (MMSYSERR_NOERROR == mmr) { //
// can it open real time?
//
mmr = acmStreamOpen(NULL, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY); if (MMSYSERR_NOERROR != mmr) { return (WAVERR_BADFORMAT); }
mmr = mapDriverOpenWave(pms, pms->pwfxReal); } else { mmr = WAVERR_BADFORMAT; }
return (mmr); } // mapDriverFindMethod3()
//--------------------------------------------------------------------------;
//
// MMRESULT mapDriverFindMethod4
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (MMRESULT):
//
// History:
// 08/04/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL mapDriverFindMethod4 ( LPMAPSTREAM pms ) { MMRESULT mmr;
//
// suggest 8 bit MONO PCM format for the Client
//
pms->pwfxReal->wFormatTag = WAVE_FORMAT_PCM; pms->pwfxReal->nChannels = 1; pms->pwfxReal->wBitsPerSample = 8;
mmr = acmFormatSuggest(pms->had, pms->pwfxClient, pms->pwfxReal, pms->cbwfxReal, ACM_FORMATSUGGESTF_WFORMATTAG | ACM_FORMATSUGGESTF_NCHANNELS | ACM_FORMATSUGGESTF_WBITSPERSAMPLE);
if (MMSYSERR_NOERROR == mmr) { //
// can it open real time?
//
mmr = acmStreamOpen(NULL, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY); if (MMSYSERR_NOERROR != mmr) { return (WAVERR_BADFORMAT); }
mmr = mapDriverOpenWave(pms, pms->pwfxReal); } else { mmr = WAVERR_BADFORMAT; }
return (mmr); } // mapDriverFindMethod4()
//--------------------------------------------------------------------------;
//
// MMRESULT mapDriverFindMethod5
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (MMRESULT):
//
// History:
// 08/04/93 cjp [curtisp]
// 03/13/94 fdy [frankye]
// Modified function to first try to find the best pcm format for
// the prefered device, and if that fails, then try for each wave
// device that exists in the system.
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL mapDriverFindMethod5 ( LPMAPSTREAM pms ) { MMRESULT mmr; BOOL f; UINT uPrefDevId; UINT cNumDevs; BOOL fPrefOnly; UINT i;
//
//
//
WAIT_FOR_MUTEX(gpag->hMutexSettings);
if (pms->fInput) { uPrefDevId = gpag->pSettings->uIdPreferredIn; cNumDevs = gpag->cWaveInDevs; } else { uPrefDevId = gpag->pSettings->uIdPreferredOut; cNumDevs = gpag->cWaveOutDevs; } fPrefOnly = (WAVE_MAPPER == uPrefDevId) ? FALSE : gpag->pSettings->fPreferredOnly;
//
//
//
mmr = WAVERR_BADFORMAT; if ((-1) != uPrefDevId) { f = FindBestPCMFormat(pms->pwfxClient, pms->pwfxReal, pms->fInput, uPrefDevId); if (f) { mmr = acmStreamOpen(NULL, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY); if (MMSYSERR_NOERROR == mmr) { mmr = mapDriverOpenWave(pms, pms->pwfxReal); } else { mmr = WAVERR_BADFORMAT; } } }
if ( (MMSYSERR_NOERROR != mmr) && (!fPrefOnly) ) { for (i=0; i < cNumDevs; i++) { if (i == uPrefDevId) { //
// Already tried this one.
//
continue; } f = FindBestPCMFormat(pms->pwfxClient, pms->pwfxReal, pms->fInput, i); if (f) { mmr = acmStreamOpen(NULL, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY); if (MMSYSERR_NOERROR == mmr) { mmr = mapDriverOpenWave(pms, pms->pwfxReal); } else { mmr = WAVERR_BADFORMAT; } } if (MMSYSERR_NOERROR == mmr) { break; } } }
RELEASE_MUTEX(gpag->hMutexSettings);
return (mmr); } // mapDriverFindMethod5()
//--------------------------------------------------------------------------;
//
// BOOL mapDriverEnumCallback
//
// Description:
//
//
// Arguments:
// HACMDRIVERID hadid:
//
// DWORD_PTR dwInstance:
//
// DWORD fdwSupport:
//
// Return (BOOL):
//
// History:
// 09/18/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
BOOL FNCALLBACK mapDriverEnumCallback ( HACMDRIVERID hadid, DWORD_PTR dwInstance, DWORD fdwSupport ) { LPMAPSTREAM pms; MMRESULT mmr; ACMFORMATTAGDETAILS aftd;
pms = (LPMAPSTREAM)dwInstance;
//
// check if support required
//
if (0 == (pms->fdwSupport & fdwSupport)) { //
// skip to next driver..
//
return (TRUE); }
aftd.cbStruct = sizeof(aftd); aftd.dwFormatTag = pms->pwfxClient->wFormatTag; aftd.fdwSupport = 0L;
mmr = acmFormatTagDetails((HACMDRIVER)hadid, &aftd, ACM_FORMATTAGDETAILSF_FORMATTAG); if (MMSYSERR_NOERROR != mmr) { return (TRUE); }
if (0 == (pms->fdwSupport & aftd.fdwSupport)) { return (TRUE); }
mmr = acmDriverOpen(&pms->had, hadid, 0L); if (MMSYSERR_NOERROR != mmr) { return (TRUE); }
switch (pms->uHeuristic) { case 0: //
// try 'any' suggested destination
//
mmr = mapDriverFindMethod0(pms); break;
case 1: //
// try 'any PCM' suggested destination
//
mmr = mapDriverFindMethod1(pms); break;
case 2: //
// try 'any mono PCM' suggested destination
//
mmr = mapDriverFindMethod2(pms); break;
case 3: //
// try 'any 8 bit PCM' suggested destination
//
mmr = mapDriverFindMethod3(pms); break;
case 4: //
// try 'any mono 8 bit PCM' suggested destination
//
mmr = mapDriverFindMethod4(pms); break;
case 5: //
// search for best PCM format available by wave hardware
//
mmr = mapDriverFindMethod5(pms); break; }
pms->mmrClient = mmr;
if (MMSYSERR_NOERROR == mmr) { return (FALSE); }
acmDriverClose(pms->had, 0L); pms->had = NULL;
return (TRUE); } // mapDriverEnumCallback()
//--------------------------------------------------------------------------;
//
// MMRESULT FindConverterMatch
//
// Description:
// Test all drivers to see if one can convert the requested format
// into a format supported by an available wave device
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (MMRESULT):
//
// History:
// 06/15/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
MMRESULT FNLOCAL FindConverterMatch ( LPMAPSTREAM pms ) { MMRESULT mmr; int iHeuristic; DWORD fdwSupport;
//
// for the 'suggest PCM ' passes, allow what is needed
//
if (WAVE_FORMAT_PCM == pms->pwfxClient->wFormatTag) { fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER; } else { fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC; }
//
//
//
//
//
pms->mmrClient = WAVERR_BADFORMAT;
pms->had = NULL; for (iHeuristic = 0; iHeuristic < MAX_HEURISTIC; iHeuristic++) { pms->uHeuristic = iHeuristic;
if (0 == iHeuristic) { //
// for the 'suggest anything' pass, allow converters and codecs
//
pms->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER | ACMDRIVERDETAILS_SUPPORTF_CODEC; } else { //
// for the 'suggest PCM ' passes, allow what is needed
//
pms->fdwSupport = fdwSupport; }
mmr = acmDriverEnum(mapDriverEnumCallback, (DWORD_PTR)pms, 0L); if (MMSYSERR_NOERROR == mmr) { if (NULL != pms->had) { return (MMSYSERR_NOERROR); } } }
return (pms->mmrClient); } // FindConverterMatch()
//==========================================================================;
//
//
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// DWORD mapWaveClose
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// Return (DWORD):
//
// History:
// 06/15/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
DWORD FNLOCAL mapWaveClose ( LPMAPSTREAM pms ) { MMRESULT mmr;
//
//
//
mmr = pms->fnWaveClose(pms->hwReal); if (MMSYSERR_NOERROR != mmr) { DPF(0, "!mapWaveClose: physical device failed close! mmr=%u", mmr); return (mmr); }
//
// if this is input and its background task is alive, kill it
//
if (pms->fInput && (0 != pms->htaskInput)) { #ifdef WIN32
PostAppMessage(pms->htaskInput, WM_QUIT, 0, 0L); WaitForSingleObject(pms->hInput, INFINITE); CloseHandle(pms->hInput); CloseHandle(pms->hStoppedEvent); #else
if ((0 == gpag->cInputStreams) || (NULL == gpag->htaskInput)) { DPF(0, "!input mapping htask=%.04Xh, reference count=%u?!?", gpag->htaskInput, gpag->cInputStreams);
//
// should NEVER happen, but if it does make sure we don't blow
//
gpag->cInputStreams = 0; gpag->htaskInput = NULL;
pms->htaskInput = NULL; } else { #ifdef DEBUG
if (pms->htaskInput != gpag->htaskInput) { DPF(0, "!pms->htaskInput=%.04Xh != gpag->htaskInput%.04Xh!", pms->htaskInput, gpag->htaskInput); } #endif
gpag->cInputStreams--;
if (0 != gpag->cInputStreams) { //
// yield to input mapping task--this will allow all
// unserviced messages to be processed. this could be made
// better and will have to be for win 32...
//
DPF(1, "YIELDING to background input mapping task=%.04Xh", gpag->htaskInput); if (IsTask(gpag->htaskInput)) { DirectedYield(gpag->htaskInput); } else { DPF(0, "!gpag->taskInput=%.04Xh is dead!", gpag->htaskInput);
gpag->cInputStreams = 0; gpag->htaskInput = NULL; } DPF(1, "done YIELDING to background input mapping task=%.04Xh", gpag->htaskInput); } else { //
// destroy converter task and yield to it until all
// messages get pumped through...
//
DPF(1, "KILLING background input mapping task=%.04Xh", gpag->htaskInput);
if (gpag->htaskInput != NULL) { PostAppMessage(gpag->htaskInput, WM_QUIT, 0, 0L); while (IsTask(gpag->htaskInput)) { DirectedYield(gpag->htaskInput); } }
DPF(1, "done killing background input mapping task=%.04Xh", gpag->htaskInput); gpag->htaskInput = NULL; }
pms->htaskInput = NULL; } #endif // !WIN32
}
//
// done with stream (and driver)...
//
if (NULL != pms->has) { acmStreamClose(pms->has, 0L); acmDriverClose(pms->had, 0L);
if (pms->fInput) { //
// this must be done _AFTER_ destroying our background input
// mapping task
//
mapWaveDriverCallback(pms, WIM_CLOSE, 0L, 0L); } else { //
// this must be done _AFTER_ the calls the ACM APIs since
// some versions of the ACM will yield within its APIs.
// Otherwise, for MCIWAVE, the signal to the MCIWAVE background
// task would occur prematurely.
//
mapWaveDriverCallback(pms, WOM_CLOSE, 0L, 0L); } }
//
// free the allocated memory for our mapping stream instance
//
GlobalFreePtr(pms);
return (MMSYSERR_NOERROR); } // mapWaveClose()
//--------------------------------------------------------------------------;
//
// DWORD mapWaveOpen
//
// Description:
//
//
// Arguments:
// BOOL fInput:
//
// UINT uId:
//
// DWORD dwUser:
//
// LPWAVEOPENDESC pwod:
//
// DWORD fdwOpen:
//
// Return (DWORD):
//
//
//--------------------------------------------------------------------------;
DWORD FNLOCAL mapWaveOpen ( BOOL fInput, UINT uId, DWORD_PTR dwUser, LPWAVEOPENDESC pwod, DWORD fdwOpen ) { MMRESULT mmr; LPMAPSTREAM pms; // pointer to per-instance info struct
LPMAPSTREAM pmsT; // temp stream pointer
DWORD cbms; LPWAVEFORMATEX pwfx; // pointer to passed format
UINT cbwfxSrc; DWORD cbwfxDst; BOOL fQuery; BOOL fAsync;
//
//
//
fQuery = (0 != (WAVE_FORMAT_QUERY & fdwOpen)); fAsync = (0 == (WAVE_ALLOWSYNC & fdwOpen)); pwfx = (LPWAVEFORMATEX)pwod->lpFormat;
DPF(2, "mapWaveOpen(%s,%s,%s): Tag=%u, %lu Hz, %u Bit, %u Channel(s)", fInput ? (LPSTR)"in" : (LPSTR)"out", fQuery ? (LPSTR)"query" : (LPSTR)"real", fAsync ? (LPSTR)"async" : (LPSTR)"SYNC", pwfx->wFormatTag, pwfx->nSamplesPerSec, pwfx->wBitsPerSample, pwfx->nChannels);
if (gpag->fPrestoSyncAsync) { fdwOpen |= WAVE_ALLOWSYNC; fAsync = FALSE; }
WAIT_FOR_MUTEX(gpag->hMutexSettings);
if (fAsync) { if (fInput) { if (gpag->pSettings->fSyncOnlyIn) { DPF(1, "--->failing because input device is syncronous!"); RELEASE_MUTEX(gpag->hMutexSettings); return (WAVERR_SYNC); } } else { if (gpag->pSettings->fSyncOnlyOut) { DPF(1, "--->failing because output device is syncronous!"); RELEASE_MUTEX(gpag->hMutexSettings); return (WAVERR_SYNC); } } }
RELEASE_MUTEX(gpag->hMutexSettings);
//
// determine how big the complete wave format header is--this is the
// size of the extended waveformat structure plus the cbSize field.
// note that for PCM, this is only sizeof(PCMWAVEFORMAT)
//
if (WAVE_FORMAT_PCM == pwfx->wFormatTag) { cbwfxSrc = sizeof(PCMWAVEFORMAT); } else { //
// because MMSYSTEM does not (currently) validate for the extended
// format information, we validate this pointer--this will keep
// noelc and davidmay from crashing Windows with corrupt files.
//
cbwfxSrc = sizeof(WAVEFORMATEX) + pwfx->cbSize; if (IsBadReadPtr(pwfx, cbwfxSrc)) { return (MMSYSERR_INVALPARAM); } }
//
// allocate mapping stream instance structure
//
// for Win 16, this structure must be _page locked in global space_
// so our low level interrupt time callbacks can munge the headers
// without exploding
//
// size is the struct size + size of one known format + largest
// possible mapped destination format size. We don't determine
// the size of the largest possible mapped destination format until
// we know we do in fact have to map this format. When we make this
// determination, we will realloc this.
//
cbms = sizeof(*pms) + cbwfxSrc; pms = (LPMAPSTREAM)GlobalAllocPtr(GMEM_FIXED|GMEM_SHARE|GMEM_ZEROINIT, cbms); if (NULL == pms) { DPF(0, "!mapWaveOpen(): could not alloc %lu bytes for map stream!", cbms); return (MMSYSERR_NOMEM); }
//
// now fill it with info
//
pms->fInput = fInput; pms->fdwOpen = fdwOpen; pms->dwCallback = pwod->dwCallback; pms->dwInstance = pwod->dwInstance; pms->hwClient = pwod->hWave; if (fdwOpen & WAVE_MAPPED) { pms->uMappedDeviceID = pwod->uMappedDeviceID; } pms->pwfxClient = (LPWAVEFORMATEX)(pms + 1); pms->pwfxReal = NULL; // filled in later if needed
pms->cbwfxReal = 0; // filled in later if needed
pms->uIdReal = (UINT)-1;
_fmemcpy(pms->pwfxClient, pwfx, cbwfxSrc);
//
// set up our function jump table so we don't have to constantly
// check for input vs output--makes for smaller and faster code.
//
if (fInput) { pms->fnWaveOpen = (MAPPEDWAVEOPEN)waveInOpen; pms->fnWaveClose = (MAPPEDWAVECLOSE)waveInClose; pms->fnWavePrepareHeader = (MAPPEDWAVEPREPAREHEADER)waveInPrepareHeader; pms->fnWaveUnprepareHeader = (MAPPEDWAVEUNPREPAREHEADER)waveInUnprepareHeader; pms->fnWaveWrite = (MAPPEDWAVEWRITE)waveInAddBuffer; pms->fnWaveGetPosition = (MAPPEDWAVEGETPOSITION)waveInGetPosition; pms->fnWaveMessage = (MAPPEDWAVEMESSAGE)waveInMessage; } else { pms->fnWaveOpen = (MAPPEDWAVEOPEN)waveOutOpen; pms->fnWaveClose = (MAPPEDWAVECLOSE)waveOutClose; pms->fnWavePrepareHeader = (MAPPEDWAVEPREPAREHEADER)waveOutPrepareHeader; pms->fnWaveUnprepareHeader = (MAPPEDWAVEUNPREPAREHEADER)waveOutUnprepareHeader; pms->fnWaveWrite = (MAPPEDWAVEWRITE)waveOutWrite; pms->fnWaveGetPosition = (MAPPEDWAVEGETPOSITION)waveOutGetPosition; pms->fnWaveMessage = (MAPPEDWAVEMESSAGE)waveOutMessage; }
//
// give mmsystem an instance dword that will be passed back to the
// mapper on all subsequent calls..
//
*((PDWORD_PTR)dwUser) = (DWORD_PTR)pms;
//
// try to open another *real* wave device with this format
// if another device can deal with the format we will do
// nothing but act as a pass through
//
// if someone could open the format, go into passthrough mode.
//
pms->mmrClient = MMSYSERR_ERROR; mmr = mapDriverOpenWave(pms, pwfx); if (MMSYSERR_NOERROR == mmr) { #ifdef DEBUG
{ if (DbgGetLevel() > 2) { if (fInput) { WAVEINCAPS wic;
waveInGetDevCaps(pms->uIdReal, &wic, sizeof(wic)); wic.szPname[SIZEOF(wic.szPname) - 1] = '\0';
DPF(3, "--->'" DEVFMT_STR "' native support succeeded.", (LPTSTR)wic.szPname); } else { WAVEOUTCAPS woc;
waveOutGetDevCaps(pms->uIdReal, &woc, sizeof(woc)); woc.szPname[SIZEOF(woc.szPname) - 1] = '\0';
DPF(3, "--->'" DEVFMT_STR "' native support succeeded.", (LPTSTR)woc.szPname); } } } #endif
if (fQuery) { GlobalFreePtr(pms); } return (MMSYSERR_NOERROR); }
//
// If this was a WAVE_FORMAT_DIRECT then don't bother
// trying to setup a conversion stream. Note WAVE_FORMAT_DIRECT is
// new for Win95.
//
if (0 != (WAVE_FORMAT_DIRECT & pms->fdwOpen)) { mmr = pms->mmrClient; GlobalFreePtr(pms); return mmr; }
//
// If all devices are allocated, don't go on to try to create
// a conversion stream.
//
if (MMSYSERR_ALLOCATED == mmr) { mmr = pms->mmrClient; GlobalFreePtr(pms); return mmr; }
//
// There was at least one unallocated device that could not open
// the format.
//
// determine size of largest possible mapped destination format and
// fill in all the necessary remaining pms information required
// for mapping.
//
mmr = acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &cbwfxDst); if (MMSYSERR_NOERROR != mmr) { DPF(0, "!mapWaveOpen() FAILING BECAUSE MAX FORMAT SIZE UNKNOWN?"); GlobalFreePtr(pms); return (MMSYSERR_ERROR); }
cbms = sizeof(*pms) + cbwfxSrc + cbwfxDst; pmsT = pms; pms = (LPMAPSTREAM)GlobalReAllocPtr(pmsT, cbms, GMEM_MOVEABLE|GMEM_ZEROINIT); if (NULL == pms) { DPF(0, "!mapWaveOpen(): could not realloc %lu bytes for map stream!", cbms); GlobalFreePtr(pmsT); return (MMSYSERR_NOMEM); }
//
// now fill in remaining info necessary for mapping.
//
pms->pwfxClient = (LPWAVEFORMATEX)(pms + 1); pms->pwfxReal = (LPWAVEFORMATEX)((LPBYTE)(pms + 1) + cbwfxSrc); pms->cbwfxReal = cbwfxDst; if (fInput) { pms->pwfxSrc = pms->pwfxReal; pms->pwfxDst = pms->pwfxClient; } else { pms->pwfxSrc = pms->pwfxClient; pms->pwfxDst = pms->pwfxReal; }
//
// give mmsystem an instance dword that will be passed back to the
// mapper on all subsequent calls. this was done earlier but pms
// may have changed since we've done a GlobalReAllocPtr.
//
*((PDWORD_PTR)dwUser) = (DWORD_PTR)pms;
//
// no one could open the format
//
mmr = FindConverterMatch(pms); if (MMSYSERR_NOERROR != mmr) { DPF(2, "--->failing open, unable to find supporting ACM driver!");
//
// return the error we got when attempting to open a
// converter / wave driver...
//
GlobalFreePtr(pms); return (mmr); }
//
//
//
DPF(2, "--->MAPPING TO: Tag=%u, %lu Hz, %u Bit, %u Channel(s)", pms->pwfxReal->wFormatTag, pms->pwfxReal->nSamplesPerSec, pms->pwfxReal->wBitsPerSample, pms->pwfxReal->nChannels);
if (fQuery) { acmDriverClose(pms->had, 0L); GlobalFreePtr(pms);
return (MMSYSERR_NOERROR); }
//
//
//
mmr = acmStreamOpen(&pms->has, pms->had, pms->pwfxSrc, pms->pwfxDst, NULL, 0L, 0L, 0L); if (MMSYSERR_NOERROR != mmr) { DPF(0, "!mapWaveOpen: opening stream failed! mmr=%u", mmr);
pms->fnWaveClose(pms->hwReal);
acmDriverClose(pms->had, 0L); GlobalFreePtr(pms);
if (mmr < WAVERR_BASE) { return (mmr); }
return (WAVERR_BADFORMAT); }
//
//
//
if (fInput) { #ifndef WIN32
if ((NULL == gpag->htaskInput) || !IsTask(gpag->htaskInput)) #endif
{ #ifndef WIN32
if (0 != gpag->cInputStreams) { DPF(0, "!cInputStreams=%u and should be zero! (gpag->htaskInput=%.04Xh)", gpag->cInputStreams, gpag->htaskInput);
gpag->cInputStreams = 0; } #endif
#ifdef DEBUG
gpag->fFaultAndDie = (BOOL)GetPrivateProfileInt(TEXT("msacm.drv"), TEXT("FaultAndDie"), 0, TEXT("system.ini")); #endif
//
// create the task to do the conversion in..
//
#ifdef WIN32
pms->nOutstanding = 0; if ((pms->hStoppedEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL || (pms->hInput = CreateThread(NULL, 300, (LPTHREAD_START_ROUTINE) mapWaveInputConvertProc, (LPVOID)pms->hStoppedEvent, 0, (LPDWORD)&pms->htaskInput)) == NULL) { if (pms->hStoppedEvent != NULL) { CloseHandle(pms->hStoppedEvent); } #else
gpag->htaskInput = NULL; if (mmTaskCreate((LPTASKCALLBACK)mapWaveInputConvertProc, (HTASK FAR *)&gpag->htaskInput, 0L)) { #endif
DPF(0, "!mapWaveOpen: unable to create task for input mapping!");
pms->fnWaveClose(pms->hwReal);
acmStreamClose(pms->has, 0L); acmDriverClose(pms->had, 0L);
GlobalFreePtr(pms);
return (MMSYSERR_NOMEM); }
//
// make sure _at least one_ message is present in the background
// task's queue--this will keep DirectedYield from hanging
// in GetMessage if an app opens input with no callback and
// immediately closes the handle (like testing if the device
// is available--ACMAPP and WaveTst do this!).
//
#ifndef WIN32
PostAppMessage(gpag->htaskInput, WM_NULL, 0, 0L); DirectedYield(gpag->htaskInput); #else
//
// Make sure the thread has started - otherwise PostAppMessage
// won't work because the thread won't have a message queue.
//
WaitForSingleObject(pms->hStoppedEvent, INFINITE); #endif // !WIN32
}
gpag->cInputStreams++;
#ifndef WIN32
pms->htaskInput = gpag->htaskInput; #endif
//
// NOTE! we *MUST* send the WIM_OPEN callback _AFTER_ creating the
// input mapping task. our function callback (mapWaveCallback)
// simply eats the physical WIM_OPEN message. if this is not done
// this way, we get into a task lock with MCIWAVE's background
// task...
//
mapWaveDriverCallback(pms, WIM_OPEN, 0L, 0L); } else { //
// We send the WOM_OPEN callback here after opening the stream
// instead of in our function callback (mapWaveCallback). Some
// versions of the acm cause a yield to occur within its APIs, and
// this would allow a signal to reach the MCIWAVE background task
// prematurely (it would get to the MCIWAVE background task before
// its state had changed from TASKIDLE to TASKBUSY).
//
mapWaveDriverCallback(pms, WOM_OPEN, 0L, 0L); }
//
// made it! succeed the open
//
return (MMSYSERR_NOERROR); } // mapWaveOpen()
//--------------------------------------------------------------------------;
//
// DWORD mapWavePrepareHeader
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// LPWAVEHDR pwh:
//
// Return (DWORD):
//
// History:
// 06/15/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
DWORD FNLOCAL mapWavePrepareHeader ( LPMAPSTREAM pms, LPWAVEHDR pwh ) { MMRESULT mmr; LPWAVEHDR pwhShadow; LPACMSTREAMHEADER pash; DWORD cbShadow; DWORD dwLen; DWORD fdwSize;
//
// if we are in convert mode, allocate a 'shadow' wave header
// and buffer to hold the converted wave bits
//
// we need to pagelock the callers header but *not* his buffer
// because we touch it in wXdWaveMapCallback (to set the DONE bit)
//
// here is the state of the dwUser and reserved fields in
// both buffers.
//
// client's header (sent to the wavemapper by the 'user')
//
// reserved points to the stream header used for
// conversions with the ACM. the wavemapper
// is the driver so we can use this.
// dwUser for use by the 'user' (client)
//
// shadow header (sent to the real device by the wavemapper)
//
// reserved for use by the real device
// dwUser points to the client's header. (the
// wavemapper is the user in this case)
//
// acm stream header (created by us for conversion work)
//
// dwUser points to mapper stream instance (pms)
// dwSrcUser points to shadow header
// dwDstUser original source buffer size (prepared with)
//
if (NULL == pms->has) { //
// no conversion required just pass through
//
mmr = pms->fnWavePrepareHeader(pms->hwReal, pwh, sizeof(WAVEHDR));
return (mmr); }
//
//
//
//
dwLen = pwh->dwBufferLength; if (pms->fInput) { UINT u;
#ifndef WIN32
if (!IsTask(pms->htaskInput)) { DPF(0, "mapWavePrepareHeader: background task died! pms->htaskInput=%.04Xh", pms->htaskInput);
pms->htaskInput = NULL; return (MMSYSERR_NOMEM); } #endif // !WIN32
//
// block align the destination buffer if the caller didn't read
// our documentation...
//
u = pms->pwfxClient->nBlockAlign; dwLen = (dwLen / u) * u;
#ifdef DEBUG
if (dwLen != pwh->dwBufferLength) { DPF(1, "mapWavePrepareHeader: caller passed _unaligned_ buffer for recording (%lu->%lu)!", pwh->dwBufferLength, dwLen); } #endif
//
// determine size for shadow buffer (the buffer that we will give
// to the _real_ device). give a _block aligned_ destination buffer
//
fdwSize = ACM_STREAMSIZEF_DESTINATION; } else { //
// determine size for the shadow buffer (this will be the buffer
// that we convert to before writing the data to the underlying
// device).
//
fdwSize = ACM_STREAMSIZEF_SOURCE; }
mmr = acmStreamSize(pms->has, dwLen, &dwLen, fdwSize); if (MMSYSERR_NOERROR != mmr) { DPF(0, "!mapWavePrepareHeader: failed to get conversion size! mmr=%u", mmr); return (MMSYSERR_NOMEM); }
//
//
//
DPF(2, "mapWavePrepareHeader(%s): Client=%lu Bytes, Shadow=%lu Bytes", pms->fInput ? (LPSTR)"in" : (LPSTR)"out", pwh->dwBufferLength, dwLen);
//
// allocate the shadow WAVEHDR
//
// NOTE: add four bytes to guard against GP faulting with stos/lods
// code that accesses the last byte/word/dword in a segment--very
// easy to do...
//
cbShadow = sizeof(WAVEHDR) + sizeof(ACMSTREAMHEADER) + dwLen + 4; pwhShadow = (LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE, cbShadow); if (NULL == pwhShadow) { DPF(0, "!mapWavePrepareHeader(): could not alloc %lu bytes for shadow!", cbShadow); return (MMSYSERR_NOMEM); }
//
//
//
pash = (LPACMSTREAMHEADER)(pwhShadow + 1);
pash->cbStruct = sizeof(*pash); pash->fdwStatus = 0L; pash->dwUser = (DWORD_PTR)pms;
//
// fill in the shadow wave header, the dwUser field will point
// back to the original header, so we can get back to it
//
pwhShadow->lpData = (LPBYTE)(pash + 1); pwhShadow->dwBufferLength = dwLen; pwhShadow->dwBytesRecorded = 0; pwhShadow->dwUser = (DWORD_PTR)pwh;
//
// now prepare the shadow wavehdr
//
if (pms->fInput) { pwhShadow->dwFlags = 0L; pwhShadow->dwLoops = 0L;
//
// input: our source is the shadow (we get data from the
// physical device and convert it into the clients buffer)
//
pash->pbSrc = pwhShadow->lpData; pash->cbSrcLength = pwhShadow->dwBufferLength; pash->dwSrcUser = (DWORD_PTR)pwhShadow; pash->pbDst = pwh->lpData; pash->cbDstLength = pwh->dwBufferLength; pash->dwDstUser = pwhShadow->dwBufferLength; } else { pwhShadow->dwFlags = pwh->dwFlags & (WHDR_BEGINLOOP|WHDR_ENDLOOP); pwhShadow->dwLoops = pwh->dwLoops;
//
// output: our source is the client (we get data from the
// client and convert it into something for the physical
// device)
//
pash->pbSrc = pwh->lpData; pash->cbSrcLength = pwh->dwBufferLength; pash->dwSrcUser = (DWORD_PTR)pwhShadow; pash->pbDst = pwhShadow->lpData; pash->cbDstLength = pwhShadow->dwBufferLength; pash->dwDstUser = pwh->dwBufferLength; }
mmr = pms->fnWavePrepareHeader(pms->hwReal, pwhShadow, sizeof(WAVEHDR)); if (MMSYSERR_NOERROR == mmr) { mmr = acmStreamPrepareHeader(pms->has, pash, 0L); if (MMSYSERR_NOERROR != mmr) { pms->fnWaveUnprepareHeader(pms->hwReal, pwhShadow, sizeof(WAVEHDR)); } }
//
//
//
if (MMSYSERR_NOERROR != mmr) { GlobalFreePtr(pwhShadow); return (mmr); }
//
// now pagelock the callers header, only the header!!!
//
// globalpagelock will pagelock the complete object--and this could
// be bad if the caller allocated the header as the first part
// of a large memory block. also globalpagelock only works on the
// _first_ selector of the tile...
//
// not necessary in win 32.
//
#ifndef WIN32
acmHugePageLock((LPBYTE)pwh, sizeof(*pwh)); #endif
//
// the reserved field of the callers WAVEHDR will contain the
// shadow LPWAVEHDR
//
pwh->reserved = (DWORD_PTR)pash; pwh->dwFlags |= WHDR_PREPARED;
return (MMSYSERR_NOERROR); } // mapWavePrepareHeader()
//--------------------------------------------------------------------------;
//
// DWORD mapWaveUnprepareHeader
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// LPWAVEHDR pwh:
//
// Return (DWORD):
//
// History:
// 06/15/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
DWORD FNLOCAL mapWaveUnprepareHeader ( LPMAPSTREAM pms, LPWAVEHDR pwh ) { MMRESULT mmr; LPWAVEHDR pwhShadow; LPACMSTREAMHEADER pash; DWORD cbShadowData;
//
// if we are not in convert mode, then passthrough to physical device
// otherwise, free the 'shadow' wave header and buffer, etc
//
if (NULL == pms->has) { //
// no conversion required just pass through
//
mmr = pms->fnWaveUnprepareHeader(pms->hwReal, pwh, sizeof(WAVEHDR));
return (mmr); }
//
//
//
//
//
pash = (LPACMSTREAMHEADER)pwh->reserved; pwhShadow = (LPWAVEHDR)pash->dwSrcUser;
if (pms->fInput) { cbShadowData = (DWORD)pash->dwDstUser;
pash->cbSrcLength = (DWORD)pash->dwDstUser; ////////pash->cbDstLength = xxx; !!! don't touch this !!!
} else { cbShadowData = pash->cbDstLength;
pash->cbSrcLength = (DWORD)pash->dwDstUser; ////////pash->cbDstLength = xxx; !!! don't touch this !!!
}
acmStreamUnprepareHeader(pms->has, pash, 0L);
pwhShadow->dwBufferLength = cbShadowData; pms->fnWaveUnprepareHeader(pms->hwReal, pwhShadow, sizeof(WAVEHDR));
//
// unprepare the shadow and caller's buffers (for the caller, this
// just means un-page lock the WAVEHDR)
//
// we only page lock stuff in Win 16--not Win 32.
//
#ifndef WIN32
acmHugePageUnlock((LPBYTE)pwh, sizeof(*pwh)); #endif
//
// free the shadow buffer--mark caller's wave header as unprepared
// and succeed the call
//
GlobalFreePtr(pwhShadow);
pwh->reserved = 0L; pwh->dwFlags &= ~WHDR_PREPARED;
return (MMSYSERR_NOERROR); } // mapWaveUnprepareHeader()
//--------------------------------------------------------------------------;
//
// DWORD mapWaveWriteBuffer
//
// Description:
//
//
// Arguments:
// LPMAPSTREAM pms:
//
// LPWAVEHDR pwh:
//
// Return (DWORD):
//
// History:
// 06/15/93 cjp [curtisp]
//
//--------------------------------------------------------------------------;
DWORD FNLOCAL mapWaveWriteBuffer ( LPMAPSTREAM pms, LPWAVEHDR pwh ) { MMRESULT mmr; LPWAVEHDR pwhShadow; LPACMSTREAMHEADER pash; DWORD cbShadowData;
//
// no conversion required just pass through
//
if (NULL == pms->has) { mmr = pms->fnWaveWrite(pms->hwReal, pwh, sizeof(WAVEHDR)); return (mmr); }
//
//
//
DPF(2, "mapWaveWriteBuffer(%s): Flags=%.08lXh, %lu Bytes, %lu Loops", pms->fInput ? (LPSTR)"in" : (LPSTR)"out", pwh->dwFlags, pwh->dwBufferLength, pwh->dwLoops);
//
// get the conversion stream header...
//
pash = (LPACMSTREAMHEADER)pwh->reserved; if (NULL == pash) { DPF(0, "!mapWaveWriteBuffer: very strange--reserved field is 0???"); return (WAVERR_UNPREPARED); }
pwhShadow = (LPWAVEHDR)pash->dwSrcUser;
if (pms->fInput) { UINT u;
#ifndef WIN32
if (!IsTask(pms->htaskInput)) { DPF(0, "mapWaveWriteBuffer: background task died! pms->htaskInput=%.04Xh", pms->htaskInput);
pms->htaskInput = NULL; return (MMSYSERR_NOMEM); } #endif // !WIN32
//
// again, we must block align the input buffer
//
//
u = pms->pwfxClient->nBlockAlign; cbShadowData = (pwh->dwBufferLength / u) * u;
#ifdef DEBUG
if (cbShadowData != pwh->dwBufferLength) { DPF(1, "mapWaveWriteBuffer: caller passed _unaligned_ buffer for recording (%lu->%lu)!", pwh->dwBufferLength, cbShadowData); } #endif
//
// determine amount of data we need from the _real_ device. give a
// _block aligned_ destination buffer...
//
mmr = acmStreamSize(pms->has, cbShadowData, &cbShadowData, ACM_STREAMSIZEF_DESTINATION);
if (MMSYSERR_NOERROR != mmr) { DPF(0, "!mapWaveWriteBuffer: failed to get conversion size! mmr=%u", mmr); return (MMSYSERR_NOMEM); }
pwhShadow->dwBufferLength = cbShadowData; pwhShadow->dwBytesRecorded = 0L;
//
// clear the done bit of the caller's wave header (not done) and
// add the shadow buffer to the real (maybe) device's queue...
//
// note that mmsystem _should_ be doing this for us, but alas
// it does not in win 3.1... i might fix this for chicago.
//
pwh->dwFlags &= ~WHDR_DONE; } else { //
// do the conversion
//
pash->cbDstLengthUsed = 0L; if (0L != pwh->dwBufferLength) { pash->pbSrc = pwh->lpData; pash->cbSrcLength = pwh->dwBufferLength; pash->pbDst = pwhShadow->lpData; ////////////pash->cbDstLength = xxx; !!! leave as is !!!
mmr = acmStreamConvert(pms->has, pash, 0L); if (MMSYSERR_NOERROR != mmr) { DPF(0, "!waveOutWrite: conversion failed! mmr=%.04Xh", mmr); pash->cbDstLengthUsed = 0L; } }
if (0L == pash->cbDstLengthUsed) { DPF(1, "waveOutWrite: nothing converted--no data in output buffer."); }
pwhShadow->dwFlags = pwh->dwFlags; pwhShadow->dwLoops = pwh->dwLoops;
pwhShadow->dwBufferLength = pash->cbDstLengthUsed; }
pwh->dwFlags |= WHDR_INQUEUE; mmr = pms->fnWaveWrite(pms->hwReal, pwhShadow, sizeof(WAVEHDR)); if (MMSYSERR_NOERROR != mmr) { pwh->dwFlags &= ~WHDR_INQUEUE; DPF(0, "!pms->fnWaveWrite failed!, pms=%.08lXh, mmr=%u!", pms, mmr); }
return (mmr); } // mapWaveWriteBuffer()
//==========================================================================;
//
//
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// LRESULT DriverProc
//
// Description:
//
//
// Arguments:
// DWORD dwId: For most messages, dwId is the DWORD value that
// the driver returns in response to a DRV_OPEN message. Each time
// that the driver is opened, through the DrvOpen API, the driver
// receives a DRV_OPEN message and can return an arbitrary, non-zero
// value. The installable driver interface saves this value and returns
// a unique driver handle to the application. Whenever the application
// sends a message to the driver using the driver handle, the interface
// routes the message to this entry point and passes the corresponding
// dwId. This mechanism allows the driver to use the same or different
// identifiers for multiple opens but ensures that driver handles are
// unique at the application interface layer.
//
// The following messages are not related to a particular open instance
// of the driver. For these messages, the dwId will always be zero.
//
// DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN
//
// HDRVR hdrvr: This is the handle returned to the application
// by the driver interface.
//
// UINT uMsg: The requested action to be performed. Message
// values below DRV_RESERVED are used for globally defined messages.
// Message values from DRV_RESERVED to DRV_USER are used for defined
// driver protocols. Messages above DRV_USER are used for driver
// specific messages.
//
// LPARAM lParam1: Data for this message. Defined separately for
// each message.
//
// LPARAM lParam2: Data for this message. Defined separately for
// each message.
//
// Return (LRESULT):
// Defined separately for each message.
//
// History:
// 11/16/92 cjp [curtisp]
//
//--------------------------------------------------------------------------;
EXTERN_C LRESULT FNEXPORT DriverProc ( DWORD_PTR dwId, HDRVR hdrvr, UINT uMsg, LPARAM lParam1, LPARAM lParam2 ) { LRESULT lr; LPDWORD pdw;
switch (uMsg) { case DRV_INSTALL: lr = mapDriverInstall(hdrvr); return (lr);
case DRV_REMOVE: lr = mapDriverRemove(hdrvr); return (lr);
case DRV_LOAD: case DRV_FREE: return (1L);
case DRV_OPEN: case DRV_CLOSE: return (1L);
case DRV_CONFIGURE: case DRV_QUERYCONFIGURE: return (0L);
case DRV_ENABLE: lr = mapDriverEnable(hdrvr); return (lr);
case DRV_DISABLE: lr = mapDriverDisable(hdrvr); return (lr);
#ifndef WIN32
case DRV_EXITAPPLICATION: lr = acmApplicationExit(GetCurrentTask(), lParam1); return (lr); #endif
case DRV_MAPPER_PREFERRED_INPUT_GET: pdw = (LPDWORD)lParam1; if (NULL != pdw) { WAIT_FOR_MUTEX(gpag->hMutexSettings);
*pdw = MAKELONG(LOWORD(gpag->pSettings->uIdPreferredIn), LOWORD(gpag->pSettings->fPreferredOnly));
RELEASE_MUTEX(gpag->hMutexSettings);
return (MMSYSERR_NOERROR); } return (MMSYSERR_INVALPARAM);
case DRV_MAPPER_PREFERRED_OUTPUT_GET: pdw = (LPDWORD)lParam1; if (NULL != pdw) { WAIT_FOR_MUTEX(gpag->hMutexSettings);
*pdw = MAKELONG(LOWORD(gpag->pSettings->uIdPreferredOut), LOWORD(gpag->pSettings->fPreferredOnly));
RELEASE_MUTEX(gpag->hMutexSettings);
return (MMSYSERR_NOERROR); } return (MMSYSERR_INVALPARAM); }
if (uMsg >= DRV_USER) return (MMSYSERR_NOTSUPPORTED); else return (DefDriverProc(dwId, hdrvr, uMsg, lParam1, lParam2)); } // DriverProc()
|