//==========================================================================; // // msacmmap.c // // Copyright (c) 1992-1999 Microsoft Corporation // // Description: // // // History: // 9/18/93 cjp [curtisp] // //==========================================================================; #include #include #include #include #include #include #include #include #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()