//==========================================================================; // // init.c // // Copyright (c) 1991-1999 Microsoft Corporation // // Description: // // // History: // 11/15/92 cjp [curtisp] // //==========================================================================; #include #include #include #include #include #include #include #ifdef USE_ACMTHUNK #include "acmthunk.h" #endif #include "msacmmap.h" #include "profile.h" #include "debug.h" // // // // ACMGLOBALINFO acmglobalinfo; ACMGARB acmgarb; PACMGARB gpag; //--------------------------------------------------------------------------; // // LRESULT mapWaveGetDevCaps // // Description: // // // Arguments: // BOOL fInput: TRUE if input. // // LPWAVEOUTCAPS pwc: Pointer to a WAVEOUTCAPS structure to receive // the information. Used for both input and output. Output structure // contains input structure plus extras.... // // UINT cbSize: Size of the WAVEOUTCAPS structure. // // Return (MMRESULT): // // History: // 06/14/93 cjp [curtisp] // //--------------------------------------------------------------------------; MMRESULT FNGLOBAL mapWaveGetDevCaps ( BOOL fInput, LPWAVEOUTCAPS pwc, UINT cbSize ) { MMRESULT mmr; WAVEOUTCAPS woc; UINT cWaveDevs; BOOL fFoundOnlyOneMappableDeviceID; UINT uMappableDeviceID; UINT i; if (fInput) { cbSize = min(sizeof(WAVEINCAPS), cbSize); cWaveDevs = gpag->cWaveInDevs; } else { cbSize = min(sizeof(WAVEOUTCAPS), cbSize); cWaveDevs = gpag->cWaveOutDevs; } // // Determine if there is only one mappable device ID. If there is only // one, then set fFoundOnlyOneMappableID=TRUE and put the device ID // in uMappableDeviceID. // fFoundOnlyOneMappableDeviceID = FALSE; for (i=0; i < cWaveDevs; i++) { if (fInput) { mmr = (MMRESULT)waveInMessage((HWAVEIN)LongToHandle(i), DRV_QUERYMAPPABLE, 0L, 0L); } else { mmr = (MMRESULT)waveOutMessage((HWAVEOUT)LongToHandle(i), DRV_QUERYMAPPABLE, 0L, 0L); } if (MMSYSERR_NOERROR == mmr) { if (fFoundOnlyOneMappableDeviceID) { fFoundOnlyOneMappableDeviceID = FALSE; break; } uMappableDeviceID = i; fFoundOnlyOneMappableDeviceID = TRUE; } } // // If there is only one mappable device ID, then get the caps from it to // fill in the dwSupport fields. Otherwise, let's hardcode the dwSupport // field. // if (fFoundOnlyOneMappableDeviceID) { if (fInput) { mmr = waveInGetDevCaps(uMappableDeviceID, (LPWAVEINCAPS)&woc, cbSize); } else { mmr = waveOutGetDevCaps(uMappableDeviceID, &woc, cbSize); } } else { woc.dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME; mmr = MMSYSERR_NOERROR; } // // Bail on error // if (MMSYSERR_NOERROR != mmr) { return (mmr); } // // // woc.wMid = MM_MICROSOFT; woc.wPid = MM_WAVE_MAPPER; woc.vDriverVersion = VERSION_MSACMMAP; woc.wChannels = 2; LoadString(gpag->hinst, IDS_ACM_CAPS_DESCRIPTION, woc.szPname, SIZEOF(woc.szPname)); // // // woc.dwFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16; _fmemcpy(pwc, &woc, cbSize); return (MMSYSERR_NOERROR); } // waveGetDevCaps() //--------------------------------------------------------------------------; // // UINT GetPCMSupportFlags // // Description: // // // Arguments: // PZYZPCMFORMATS pzpf // UINT iaPCMFormats: // // Return (VOID): // // History: // 06/14/93 cjp [curtisp] // 03/13/94 fdy [frankye] // Modifed the interface to take pzpf and an index into it. // Modifed the function to set flags to indicate which wave // devices support the format in question. // //--------------------------------------------------------------------------; VOID FNLOCAL GetPCMSupportFlags ( PZYZPCMFORMAT pzpf, UINT iaPCMFormats ) { PCMWAVEFORMAT wfPCM; UINT uSamplesPerSec; UINT u, n, i; #define WFQFLAGS (WAVE_FORMAT_QUERY | WAVE_ALLOWSYNC) WAIT_FOR_MUTEX(gpag->hMutexSettings); // // set all supported formats to 'not supported' // for (i = gpag->cWaveInDevs; i; i--) pzpf[iaPCMFormats].uFlagsInput[i-1] = 0; for (i = gpag->cWaveOutDevs; i; i--) pzpf[iaPCMFormats].uFlagsOutput[i-1] = 0; // // we need to try 4 different format types: // Mono 8 Bit // Stereo 8 Bit // Mono 16 Bit // Stereo 16 Bit // for (u = 0; u < 4; u++) { // // set the stuff that is constant for all 4 formats // uSamplesPerSec = pzpf[iaPCMFormats].uSamplesPerSec; wfPCM.wf.wFormatTag = WAVE_FORMAT_PCM; wfPCM.wf.nSamplesPerSec = uSamplesPerSec; switch (u) { case 0: wfPCM.wf.nChannels = 1; wfPCM.wf.nAvgBytesPerSec= uSamplesPerSec; wfPCM.wf.nBlockAlign = 1; wfPCM.wBitsPerSample = 8; break; case 1: wfPCM.wf.nChannels = 2; wfPCM.wf.nAvgBytesPerSec= uSamplesPerSec * 2; wfPCM.wf.nBlockAlign = 2; wfPCM.wBitsPerSample = 8; break; case 2: wfPCM.wf.nChannels = 1; wfPCM.wf.nAvgBytesPerSec= uSamplesPerSec * 2; wfPCM.wf.nBlockAlign = 2; wfPCM.wBitsPerSample = 16; break; case 3: wfPCM.wf.nChannels = 2; wfPCM.wf.nAvgBytesPerSec= uSamplesPerSec * 4; wfPCM.wf.nBlockAlign = 4; wfPCM.wBitsPerSample = 16; break; } // // first query ALL ENABLED INPUT devices for the wfPCM format // if (gpag->pSettings->fPreferredOnly && (gpag->pSettings->uIdPreferredIn != -1)) { i = gpag->pSettings->uIdPreferredIn; n = gpag->pSettings->uIdPreferredIn + 1; } else { i = 0; n = gpag->cWaveInDevs; } for (; i < n; i++) { #ifndef _WIN32 if (!waveInOpen(NULL, i, (LPWAVEFORMAT)&wfPCM, 0L, 0L, WFQFLAGS)) #else if (!waveInOpen(NULL, i, (LPWAVEFORMATEX)&wfPCM, 0L, 0L, WFQFLAGS)) #endif { pzpf[iaPCMFormats].uFlagsInput[i] |= (ZYZPCMF_IN_M08 << u); } } // // now query ALL ENABLED OUTPUT devices for the wfPCM format // if (gpag->pSettings->fPreferredOnly && (gpag->pSettings->uIdPreferredOut != -1)) { i = gpag->pSettings->uIdPreferredOut; n = gpag->pSettings->uIdPreferredOut + 1; } else { i = 0; n = gpag->cWaveOutDevs; } for (; i < n; i++) { #ifndef _WIN32 if (!waveOutOpen(NULL, i, (LPWAVEFORMAT)&wfPCM, 0L, 0L, WFQFLAGS)) #else if (!waveOutOpen(NULL, i, (LPWAVEFORMATEX)&wfPCM, 0L, 0L, WFQFLAGS)) #endif { pzpf[iaPCMFormats].uFlagsOutput[i] |= (ZYZPCMF_OUT_M08 << u); } } } // // finally return // #if 0 // def DEBUG DPF(3, "PCM Support: %uHz, In[%d]=%04xh, Out[%d]=%04xh", pzpf[iaPCMFormats].uSamplesPerSec, iaPCMFormats, *pzpf[iaPCMFormats].uFlagsInput, iaPCMFormats, *pzpf[iaPCMFormats].uFlagsOutput); #endif RELEASE_MUTEX(gpag->hMutexSettings); return; } // GetPCMSupportFlags() //--------------------------------------------------------------------------; // // BOOL GetWaveFormats // // Description: // // // Arguments: // PZYZPCMFORMAT pzpf: // // Return (BOOL): // // History: // 06/14/93 cjp [curtisp] // 03/13/94 fdy [frankye] // Expanded the ZYZPCMFORMAT structure to include flags which // indicate which wave device supports a given format. This // function will now set these flags. Note that // the code that is #if 0'd WILL NOT WORK given these changes, so // if anybody ever resuscitates that code, you better modify it! // //--------------------------------------------------------------------------; BOOL FNGLOBAL GetWaveFormats ( PZYZPCMFORMAT pzpf ) { UINT u; #if 0 WAVEOUTCAPS woc; WAVEINCAPS wic; UINT n; DWORD dwInFormats; DWORD dwOutFormats; // // first things first: get all 'standard' supported formats from the // current selected devices for input and output... // dwInFormats = 0L; if (gpag->fPreferredOnly && (gpag->uIdPreferredIn != -1)) { if (!waveInGetDevCaps(gpag->uIdPreferredIn, &wic, sizeof(wic))) dwInFormats = wic.dwFormats; } else { n = gpag->cWaveInDevs; for (i = 0; i < n; i++) { if (!waveInGetDevCaps(i, &wic, sizeof(wic))) dwInFormats |= wic.dwFormats; } } dwOutFormats = 0L; if (gpag->fPreferredOnly && (gpag->uIdPreferredOut != -1)) { if (!waveOutGetDevCaps(gpag->uIdPreferredOut, &woc, sizeof(woc))) dwOutFormats = woc.dwFormats; } else { n = gpag->cWaveOutDevs; for (i = 0; i < n; i++) { if (!waveOutGetDevCaps(i, &woc, sizeof(woc))) dwOutFormats |= woc.dwFormats; } } #endif // // now step through each sample rate in the pzpf structure and set all // the appropriate bits for whether it is supported, etc.. // for (u = 0; pzpf[u].uSamplesPerSec; u++) { // // we need to special case a few of the sample rates, etc to get // this whole thing working--once the grunt work is done here // (and only once during initialization), then the data is easily // accessible/used... // switch (pzpf[u].uSamplesPerSec) { // // NOTE! it would be nice if we could rely on the caps // structure being correct on drivers.... but alas, Media Vision // found a way to mess that up also (on some of their hundreds // of releases of their drivers). so ALWAYS query for the // format support. // // by the way, the reason they ship their drivers with this // bug (and possibly other OEM's) is due to Sound Recorder // (apparently their only test app?!?) only doing queries // and never looking at the caps bits. // #if 0 case 11025: pzpf[u].uFlags = (WORD)(dwInFormats & WAVE_FORMAT_11k) << 8; pzpf[u].uFlags |= (WORD)(dwOutFormats & WAVE_FORMAT_11k); break; case 22050: pzpf[u].uFlags = (WORD)(dwInFormats & WAVE_FORMAT_22k) >> 4 << 8; pzpf[u].uFlags |= (WORD)(dwOutFormats & WAVE_FORMAT_22k) >> 4; break; case 44100: pzpf[u].uFlags = (WORD)(dwInFormats & WAVE_FORMAT_44k) >> 8 << 8; pzpf[u].uFlags |= (WORD)(dwOutFormats & WAVE_FORMAT_44k) >> 8; break; #else case 11025: case 22050: case 44100: #endif case 5510: case 6620: case 8000: case 9600: case 16000: case 18900: case 27420: case 32000: case 33075: case 37800: case 48000: GetPCMSupportFlags(pzpf, u); break; } } // // reset these--they are auto determined while the mapper is being // used... // WAIT_FOR_MUTEX(gpag->hMutexSettings); gpag->pSettings->fSyncOnlyOut = FALSE; gpag->pSettings->fSyncOnlyIn = FALSE; RELEASE_MUTEX(gpag->hMutexSettings); return (TRUE); } // GetWaveFormats() //--------------------------------------------------------------------------; // // BOOL mapSettingsRestore // // Description: // // // Arguments: // None. // // Return (BOOL): // // History: // 06/14/93 cjp [curtisp] // //--------------------------------------------------------------------------; BOOL FNGLOBAL mapSettingsRestore ( void ) { DWORD dwFlags; UINT ii; DWORD cbSize; PUINT pFlags; DPF(1, "mapSettingsRestore:"); // // // gpag->cWaveOutDevs = waveOutGetNumDevs(); gpag->cWaveInDevs = waveInGetNumDevs(); // Number of devices per sampling rate... cbSize = gpag->cWaveOutDevs + gpag->cWaveInDevs; // Number of total flags... // cbSize *= (sizeof(gaPCMFormats)/sizeof(gaPCMFormats[0])); cbSize *= 15; // It's fifteen; Trust Me - Fwong. // Size in bytes... cbSize *= sizeof(UINT); pFlags = (PUINT)GlobalAllocPtr(GHND, cbSize); if (NULL == pFlags) { // // Hmm... How do we cope. // return FALSE; } ZeroMemory(pFlags, cbSize); if (NULL != gaPCMFormats[0].uFlagsInput) { GlobalFreePtr(gaPCMFormats[0].uFlagsInput); } for (ii = 0; ;ii++) { if (0 == gaPCMFormats[ii].uSamplesPerSec) { break; } gaPCMFormats[ii].uFlagsInput = pFlags; pFlags += gpag->cWaveInDevs; gaPCMFormats[ii].uFlagsOutput = pFlags; pFlags += gpag->cWaveOutDevs; } WAIT_FOR_MUTEX(gpag->hMutexSettings); // gpag->fPrestoSyncAsync = (BOOL)IRegReadDwordDefault( hkeyMapper, gszKeyPrestoSyncAsync, 0 ); gpag->fPrestoSyncAsync = FALSE; // // find the waveOut device that is selected as preferred // if (!waveOutMessage((HWAVEOUT)LongToHandle(WAVE_MAPPER), DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&gpag->pSettings->uIdPreferredOut, (DWORD_PTR)&dwFlags)) { gpag->pSettings->fPreferredOnly = (0 != (DRVM_MAPPER_PREFERRED_FLAGS_PREFERREDONLY)); } else { gpag->pSettings->uIdPreferredOut = (UINT)(-1); gpag->pSettings->fPreferredOnly = TRUE; } // // find the waveIn device that is selected as preferred // if (!waveInMessage((HWAVEIN)LongToHandle(WAVE_MAPPER), DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&gpag->pSettings->uIdPreferredIn, (DWORD_PTR)&dwFlags)) { gpag->pSettings->fPreferredOnly = (0 != (DRVM_MAPPER_PREFERRED_FLAGS_PREFERREDONLY)); } else { gpag->pSettings->uIdPreferredOut = (UINT)(-1); gpag->pSettings->fPreferredOnly = TRUE; } // // reread/cache all the PCM format info from the devices selected, etc. // GetWaveFormats(gaPCMFormats); RELEASE_MUTEX(gpag->hMutexSettings); return (TRUE); } // mapSettingsRestore() //==========================================================================; // // // // //==========================================================================; //--------------------------------------------------------------------------; // // LRESULT mapDriverEnable // // Description: // // // Arguments: // HDRVR hdrvr: // // Return (LRESULT): // // History: // 09/18/93 cjp [curtisp] // //--------------------------------------------------------------------------; LRESULT FNGLOBAL mapDriverEnable ( HDRVR hdrvr ) { #ifdef USE_ACMTHUNK BOOL f; #endif DWORD dw; DPF(1, "mapDriverEnable(hdrvr=%.04Xh)", hdrvr); #ifdef USE_ACMTHUNK f = acmThunkInitialize(); if (!f) { DPF(0, "!ACM thunk cannot be initialized!"); return (0L); } #endif dw = acmGetVersion(); if (VERSION_MSACMMAP > HIWORD(dw)) { DPF(0, "!requires version %u.%.02u of the ACM!", VERSION_MSACMMAP_MAJOR, VERSION_MSACMMAP_MINOR); #ifdef USE_ACMTHUNK acmThunkTerminate(); #endif return (0L); } mapSettingsRestore(); gpag->fEnabled = TRUE; // // the return value is ignored, but return non-zero anyway // return (1L); } // mapDriverEnable() //--------------------------------------------------------------------------; // // LRESULT mapDriverDisable // // Description: // // // Arguments: // HDRVR hdrvr: // // Return (LRESULT): // // History: // 09/18/93 cjp [curtisp] // //--------------------------------------------------------------------------; LRESULT FNGLOBAL mapDriverDisable ( HDRVR hdrvr ) { DPF(1, "mapDriverDisable(hdrvr=%.04Xh)", hdrvr); if (gpag->fEnabled) { gpag->fEnabled = FALSE; } #ifdef USE_ACMTHUNK acmThunkTerminate(); #endif // // the return value is ignored, but return non-zero anyway // return (1L); } // mapDriverDisable() //==========================================================================; // // // // //==========================================================================; //--------------------------------------------------------------------------; // // LRESULT mapDriverInstall // // Description: // // // Arguments: // HDRVR hdrvr: // // Return (LRESULT): // // History: // 09/25/93 cjp [curtisp] // //--------------------------------------------------------------------------; LRESULT FNGLOBAL mapDriverInstall ( HDRVR hdrvr ) { DPF(1, "mapDriverInstall(hdrvr=%.04Xh)", hdrvr); // // // return (DRVCNF_RESTART); } // mapDriverInstall() //--------------------------------------------------------------------------; // // LRESULT mapDriverRemove // // Description: // // // Arguments: // HDRVR hdrvr: // // Return (LRESULT): // // History: // 09/25/93 cjp [curtisp] // //--------------------------------------------------------------------------; LRESULT FNGLOBAL mapDriverRemove ( HDRVR hdrvr ) { DPF(1, "mapDriverRemove(hdrvr=%.04Xh)", hdrvr); // // // return (DRVCNF_RESTART); } // mapDriverRemove() //==========================================================================; // // WIN 16 SPECIFIC SUPPORT // //==========================================================================; #ifndef WIN32 //--------------------------------------------------------------------------; // // int WEP // // Description: // The infamous useless WEP(). Note that this procedure needs to be // in a FIXED segment under Windows 3.0. Under Windows 3.1 this is // not necessary. // // Arguments: // WORD wUseless: Should tell whether Windows is exiting or not. // // Return (int): // Always return non-zero. // // History: // 04/29/93 cjp [curtisp] // //--------------------------------------------------------------------------; EXTERN_C int FNEXPORT WEP ( WORD wUseless ) { DPF(1, "WEP(wUseless=%u)", wUseless); // // always return non-zero // return (1); } // WEP() //--------------------------------------------------------------------------; // // int LibMain // // Description: // Library initialization code. // // This routine must guarantee the following things so CODEC's don't // have to special case code everywhere: // // o will only run in Windows 3.10 or greater (our exehdr is // marked appropriately). // // o will only run on >= 386 processor. only need to check // on Win 3.1. // // Arguments: // HINSTANCE hinst: Our module instance handle. // // WORD wDataSeg: Our data segment selector. // // WORD cbHeapSize: The heap size from the .def file. // // LPSTR pszCmdLine: The command line. // // Return (int): // Returns non-zero if the initialization was successful and 0 otherwise. // // History: // 11/15/92 cjp [curtisp] // //--------------------------------------------------------------------------; int FNGLOBAL LibMain ( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR pszCmdLine ) { // // we ONLY work on >= 386. if we are on a wimpy processor, scream in // pain and die a horrible death! // // NOTE! do this check first thing and get out if on a 286. we are // compiling with -G3 and C8's libentry garbage does not check for // >= 386 processor. the following code does not execute any 386 // instructions (not complex enough).. // #if (WINVER < 0x0400) if (GetWinFlags() & WF_CPU286) { return (FALSE); } #endif DbgInitialize(TRUE); DPF(1, "LibMain(hinst=%.4Xh, wDataSeg=%.4Xh, cbHeapSize=%u, pszCmdLine=%.8lXh)", hinst, wDataSeg, cbHeapSize, pszCmdLine); DPF(5, "!*** break for debugging ***"); // // everything looks good to go in Win 16 land. // gpag = &acmgarb; gpag->hinst = hinst; // Note: in Win16 there's only one instance of the mapper gpag->pSettings = &(acmglobalinfo); return (TRUE); } // LibMain() #else // WIN32 //==========================================================================; // // WIN 32 SPECIFIC SUPPORT // //==========================================================================; //--------------------------------------------------------------------------; // // PACMGLOBALINFO mapAllocateGlobalInfo // // Description: // Either creates the common buffer among all instances of the mapper // or it finds the common buffer. // // Arguments: // None. // // Return (PACMGLOBALINFO): // Returns a pointer to global info structure. // // History: // 01/21/98 Fwong Adding multi-instance support. // 01/24/99 FrankYe Back to simple single process support, since // since winmm has been modified to hold the // preferred device settings. // //--------------------------------------------------------------------------; PACMGLOBALINFO mapAllocateGlobalInfo ( void ) { // We could actually use a critical section instead of a mutex here. gpag->hMutexSettings = CreateMutex(NULL, FALSE, NULL); return &(acmglobalinfo); } // mapAllocateGlobalInfo() //--------------------------------------------------------------------------; // // void mapFreeGlobalInfo // // Description: // Cleans up the objects associated with the global memory buffer. // // Arguments: // PACMGLOBALINFO pagi: Base buffer for global info. // // Return (void): // // History: // 01/21/98 Fwong Adding multi-instance support. // 01/24/99 FrankYe Back to simple single process support, since // since winmm has been modified to hold the // preferred device settings. // //--------------------------------------------------------------------------; void mapFreeGlobalInfo ( PACMGLOBALINFO pagi ) { if(NULL != gpag->hMutexSettings) CloseHandle(gpag->hMutexSettings); } // mapFreeGlobalInfo() //--------------------------------------------------------------------------; // // BOOL DllEntryPoint // // Description: // This is the standard DLL entry point for Win 32. // // Arguments: // HINSTANCE hinst: Our instance handle. // // DWORD dwReason: The reason we've been called--process/thread attach // and detach. // // LPVOID lpReserved: Reserved. Should be NULL--so ignore it. // // Return (BOOL): // Returns non-zero if the initialization was successful and 0 otherwise. // // History: // 11/15/92 cjp [curtisp] // //--------------------------------------------------------------------------; BOOL FNEXPORT DllEntryPoint ( HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved ) { switch (dwReason) { case DLL_PROCESS_ATTACH: DbgInitialize(TRUE); gpag = &acmgarb; gpag->hinst = hinst; gpag->pSettings = mapAllocateGlobalInfo(); DisableThreadLibraryCalls(hinst); DPF(1, "DllEntryPoint(hinst=%.08lXh, DLL_PROCESS_ATTACH)", hinst); return (TRUE); case DLL_PROCESS_DETACH: mapFreeGlobalInfo(gpag->pSettings); DPF(1, "DllEntryPoint(hinst=%.08lXh, DLL_PROCESS_DETACH)", hinst); return (TRUE); } return (TRUE); } // DllEntryPoint() #endif