//--------------------------------------------------------------------------; // // File: DSoundHW.c // // Copyright (c) 1995 Microsoft Corporation. All Rights Reserved. // // Abstract: // // // Contents: // IDSHWQueryInterface() // IDSHWAddRef() // IDSHWRelease() // IDSHWCreateSoundBuffer() // IDSHWEnumSoundBuffers() // IDSHWGetCaps() // IDSHWDuplicateSoundBuffer() // IDSHWSetCooperativeLevel() // IDSHWCompact(); // IDSHWGetSpeakerConfig() // IDSHWSetSpeakerConfig() // IDSHWInitialize() // DSHWCreateTable() // GetTopUnownedWindow() added by angusm on 11/27/95 // CreateFocusThread() added by angusm on 12/1/95 // cSoundObjects() added by angusm on 11/30/95 // // History: // Date By Reason // ==== == ====== // 3/5/96 angusm Added use of fInitialized // 03/20/06 angusm Added support for Sticky Focus // //--------------------------------------------------------------------------; #include "dsoundpr.h" #ifndef DSBLD_EMULONLY #include #endif #include "grace.h" #include "flocks.h" /* Global Constansts */ #define POLL_INTERVAL 250 /* number of miliseconds between polls * for active window */ #define WAKEFOCUSTHREAD "DSWakeFocusThread" /* event name for Focus Thread * event. */ #define FOCUSSTARTUPEVENT "DSFocusStartupEvent" /* event name for handshake * after Focus Thread * startup code */ /* Function Prototypes */ HWND GetTopUnownedWindow (HWND hWindow); BOOL IsValidDSApp (DWORD dwTid); void EndFocusThread(); DWORD WINAPI FocusTracker (LPVOID lpvPollInterval); __inline void DseUpdateActivationState (LPDSOUNDEXTERNAL pdse); __inline void DsbeDeactivateIfNecessary (LPDSBUFFEREXTERNAL); __inline void ActivateFocusWindow (void); // From DirectDraw - for subclassing extern HRESULT _stdcall DSoundHelp( HWND hWnd,WNDPROC lpWndProc,DWORD dwPID ); LRESULT CALLBACK SubclassWndProc( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ); LPDSOUNDINFO gpdsinfo; //--------------------------------------------------------------------------; // // HRESULT DseVerifyValidFormat // // Description: // This function returns DS_OK if the specified format is valid for // this sound device. // //--------------------------------------------------------------------------; HRESULT DseVerifyValidFormat ( LPDSOUNDEXTERNAL pdse, LPWAVEFORMATEX pwfx ) { DSCAPS dsc; HRESULT hr; ZeroMemory(&dsc, sizeof(dsc)); dsc.dwSize = sizeof(dsc); hr = DsGetCaps (pdse->pds, &dsc); if (DS_OK != hr) { return hr; } if (WAVE_FORMAT_PCM != pwfx->wFormatTag) return DSERR_BADFORMAT; if( ( (pwfx->nChannels == 1) && !(dsc.dwFlags & DSCAPS_PRIMARYMONO) ) || ( (pwfx->nChannels == 2) && !(dsc.dwFlags & DSCAPS_PRIMARYSTEREO) ) || ( (pwfx->wBitsPerSample == 8) && !(dsc.dwFlags & DSCAPS_PRIMARY8BIT) ) || ( (pwfx->wBitsPerSample == 16) && !(dsc.dwFlags & DSCAPS_PRIMARY16BIT) ) ) { return DSERR_BADFORMAT; } return DS_OK; } //--------------------------------------------------------------------------; // // HRESULT DseSaveAppFormat // // Description: // This function saves the specified format as the app format in the // specified dse object. // //--------------------------------------------------------------------------; HRESULT DseSaveAppFormat(LPDSOUNDEXTERNAL pdse, LPWAVEFORMATEX pwfxAppCaller) { LPWAVEFORMATEX pwfxApp; int cbFormat; cbFormat = SIZEOF_WAVEFORMATEX(pwfxAppCaller); pwfxApp = (LPWAVEFORMATEX)MemAlloc(cbFormat); if (NULL == pwfxApp) { DPF(0, "DseSaveAppFormat: error: Out of memory saving app's format"); return DSERR_OUTOFMEMORY; } CopyMemory(pwfxApp, pwfxAppCaller, cbFormat); if (NULL != pdse->pwfxApp) { DPF(1, "Freeing previous app format"); MemFree(pdse->pwfxApp); } pdse->pwfxApp = pwfxApp; return DS_OK; } //--------------------------------------------------------------------------; // // HRESULT IDSHWQueryInterface // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWQueryInterface ( LPDIRECTSOUND pids, REFIID riid, LPVOID FAR* ppvObj ) { LPDSOUNDEXTERNAL pdse; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::QueryInterface - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; if( 0 == pdse->uRefCount ) { RPF("IDirectSound::QueryInterface - Invalid ref count"); return DSERR_INVALIDPARAM; } if (IDSHWINITIALIZEF_INITIALIZED == pdse->fInitialized) { ASSERT(VALID_DSOUND_PTR(pdse->pds)); } if( riid == NULL ) { RPF("IDirectSound::QueryInterface - NULL riid"); return DSERR_INVALIDPARAM; } if( ppvObj == NULL ) { RPF("IDirectSound::QueryInterface - NULL ppvObj"); return DSERR_INVALIDPARAM; } ENTER_DLL_CSECT(); if( IsEqualGUID(riid,&IID_IDirectSound) || IsEqualGUID(riid,&IID_IUnknown) ) { *ppvObj = pdse; pdse->lpVtbl->AddRef((LPDIRECTSOUND)pdse); LEAVE_DLL_CSECT(); return DS_OK; } else { LEAVE_DLL_CSECT(); return DSERR_NOINTERFACE; } LEAVE_DLL_CSECT(); return DSERR_GENERIC; } // IDSHWQueryInterface() //--------------------------------------------------------------------------; // // IDSHWAddRef // // Description: // This function implements the standard IUnknown::AddRef() // // Arguments: // pids "this" pointer // // Return (ULONG): // Estimate of the value of the reference count. // // History: // 02/11/96 angusm Removed need to have (uRefCount != 0) // //--------------------------------------------------------------------------; LONG DsAddRef(LPDSOUND pds) { pds->uRefCount++; return (int)pds->uRefCount; } ULONG FAR PASCAL IDSHWAddRef ( LPDIRECTSOUND pids ) { LPDSOUNDEXTERNAL pdse; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::AddRef - Invalid Object"); return 0; } pdse = (LPDSOUNDEXTERNAL)pids; if( 0 == pdse->uRefCount) { RPF("IDirectSound::AddRef - Invalid Object or ref count"); return 0; } ENTER_DLL_CSECT(); pdse->uRefCount++; LEAVE_DLL_CSECT(); return pdse->uRefCount; } // IDSHWAddRef() LONG DsRelease(LPDSOUND pds) { LPDSOUND pdsList; LPDSOUND pdsPrev; HANDLE hMixThread; if (0 != --pds->uRefCount) { DPF(3,"DsRelease done, ref count now %X", pds->uRefCount ); ASSERT((LONG)pds->uRefCount > 0); return pds->uRefCount; } DPF(2,"Destroying DirectSound object"); // Free the primary we created if( pds->pdsbePrimary != NULL ) { IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)(pds->pdsbePrimary)); } pds->pdsbePrimary = NULL; // if (!(DS_INTERNALF_WAVEEMULATED & pds->fdwInternal)) { hMixThread = mxTerminate(pds); } else { hMixThread = NULL; } // Remove it from the list pdsList = gpdsinfo->pDSoundObj; pdsPrev = NULL; while( pdsList != NULL ) { if( pds == pdsList ) { if( pdsPrev ) { // Previous element in list remove it pdsPrev->pNext = pdsList->pNext; } else { // Head of list - remove gpdsinfo->pDSoundObj = pdsList->pNext; } pdsList = NULL; } else { pdsPrev = pdsList; pdsList = pdsList->pNext; } } pds->pNext = NULL; // If we allocated a heap manager for the driver if (DSDHEAP_CREATEHEAP == pds->dwHeapType) { VidMemFini(pds->pDriverHeap); pds->pDriverHeap = NULL; } // Free the Wave Blit structure we use on eumlation MemFree( pds->pdswb ); // FINI layer to VxD if( pds->hHal) { DPF(3,"Close HAL layer %X", pds->hHal ); vxdDrvClose( pds->hHal ); DPF(3,"Finished Close HAL layer" ); } pds->hHal = INVALID_HANDLE_VALUE; // Close mmsystem wave handle if we opened one for this driver if (pds->hwo) { // ASSERT(DSDDESC_DOMMSYSTEMOPEN & pds->dsDrvDesc.dwFlags); HelperWaveClose( (DWORD)(pds->hwo) ); pds->hwo = NULL; } #ifdef DEBUG // Validity checking if(!(pds->fdwInternal & DS_INTERNALF_ALLOCATED)) { DPF(0,"******* Already Freed dsound HW object ********"); } if( pds->pdsb != NULL ) { DPF(0,"******* PDSB != NULL ********"); } if( pds->pdsbPrimary != NULL ) { DPF(0,"******* PDSBPrimary != NULL ********"); } if( pds->hPlaybackThread != NULL ) { DPF(0,"******* Playback thread != NULL ********"); } if( pds->hwo != NULL ) { DPF(0,"******* HWO != NULL ********"); } if( pds->dwBuffersPlaying != 0 ) { DPF(0,"******* BuffersPlaying != NULL ********"); } #endif pds->dwSig = 0xdeaddead; MemFree( pds ); DPF(3,"Release done %X ref count now 0", pds); // // Wait for mixer thread to die // if (NULL != hMixThread) { DWORD dwResult; HANDLE hHelper; HANDLE hMixThreadOurs; DPF(3, "IDSHWRelease: note: waiting for mixer thread to terminate"); hHelper = OpenProcess(PROCESS_DUP_HANDLE, FALSE, gpdsinfo->pidHelper); if (hHelper) { if (DuplicateHandle(hHelper, hMixThread, GetCurrentProcess(), &hMixThreadOurs, SYNCHRONIZE | THREAD_TERMINATE, FALSE, DUPLICATE_CLOSE_SOURCE)) { dwResult = WaitForSingleObjectEx(hMixThreadOurs, INFINITE, FALSE); ASSERT(WAIT_OBJECT_0 == dwResult); dwResult = CloseHandle(hMixThreadOurs); ASSERT(dwResult); } dwResult = CloseHandle(hHelper); ASSERT(dwResult); } } return 0; } // DsRelease() void DseTerminate(LPDSOUNDEXTERNAL pdse) { LPDSOUNDEXTERNAL pdseList; LPDSOUNDEXTERNAL pdsePrev; HANDLE hFocusLock; // Now release any buffers this process may have accessed. // Release all buffers for this process for this object FreeBuffersForProcess( pdse ); // We use DSoundHelp to give us APM window notifications. If we are // building only for emulation mode then we don't need APM notifications // since APM will be handled entirely by the WAVE drivers that we are // using for emulation. #ifndef DSBLD_EMULONLY // Remove the window from the subclass list // Window is stored by PID DSoundHelp( NULL, SubclassWndProc, HackGetCurrentProcessId() ); #endif // Remove it from the list if (FALSE == GetFocusLock(&hFocusLock)) { DPF (3, "Could not get Focus Lock"); } pdseList = gpdsinfo->pDSoundExternalObj; pdsePrev = NULL; while( pdseList != NULL ) { if( pdse == pdseList ) { if( pdsePrev ) { // Previous element in list remove it pdsePrev->pNext = pdseList->pNext; } else { // Head of list - remove gpdsinfo->pDSoundExternalObj = pdseList->pNext; } pdseList = NULL; } else { pdsePrev = pdseList; pdseList = pdseList->pNext; } } pdse->pNext = NULL; if (FALSE == ReleaseFocusLock(hFocusLock)) { DPF (3, "Could not release Focus Lock."); } // If an app wave format was allocated for this, then free it. if (pdse->pwfxApp) MemFree(pdse->pwfxApp); pdse->pwfxApp = NULL; // DsRelease(pdse->pds); } ULONG FAR PASCAL IDSHWRelease(LPDIRECTSOUND pIDs) { LPDSOUNDEXTERNAL pdse; ULONG cRef; pdse = (LPDSOUNDEXTERNAL)pIDs; ENTER_DLL_CSECT(); cRef = --pdse->uRefCount; if (0 == cRef) { // Check to see if the object is initialized if (IDSHWINITIALIZEF_UNINITIALIZED != pdse->fInitialized) { DPF(1, "DseRelease: deleting object %08Xh", pdse); /* End Focus Thread */ if (1 == cSoundObjects()) EndFocusThread(); DseTerminate(pdse); pdse->fInitialized = IDSHWINITIALIZEF_UNINITIALIZED; } DPF(0, "Freeing pdse %08Xh", pdse); MemFree( pdse ); } LEAVE_DLL_CSECT(); return cRef; } //--------------------------------------------------------------------------; // // HRESULT DseCreateDsbe // // Description: // This function operates on a Dse object to create a Dsb object // // Arguments: // // Return (HRESULT): // //--------------------------------------------------------------------------; HRESULT DseCreateDsbe ( LPDSOUNDEXTERNAL pdse, LPDSBUFFERDESC pdsbd, LPDSBUFFEREXTERNAL *ppdsbe ) { LPDSBUFFER pdsb; LPDSBUFFEREXTERNAL pdsbe; LPDSBUFFER pdsb1; LPDSBUFFEREXTERNAL pdsbe1; UINT uDevID; LPDSOUND pds; DWORD cbMixBufferSize; DWORD cbFormatSize; DWORD dw; DWORD dwForce; LPDSPROCESS pDSPID; DSCAPS dsc; BOOL fTryHardware, fTrySoftware; HRESULT hr; HRESULT hrReturn; DSBUFFERDESC dsbdTemp; HRESULT hrTemp; DPF(3,"DseCreateDsbe()"); pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::CreateSoundBuffer - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } hrReturn = DS_OK; *ppdsbe = NULL; dwForce = 0; uDevID = pds->uDeviceID; _fmemcpy( &dsbdTemp, pdsbd, sizeof(DSBUFFERDESC)); // If the user wants a primary then it must be Hardware // If they're trying to force a software primary, fail them // Set the hardware flag otherwise // Also set that this buffer can be blt to // If it's not a request for a primary, then set our internal force flag if( (dsbdTemp.dwFlags & DSBCAPS_PRIMARYBUFFER) ) { if( (dsbdTemp.dwFlags & DSBCAPS_LOCSOFTWARE) ) { RPF("IDirectSound::CreateSoundBuffer - Invalid attempt to force a software primary buffer!"); return DSERR_INVALIDPARAM; } dsbdTemp.dwFlags |= DSBCAPS_LOCHARDWARE; dsbdTemp.dwFlags |= DSBCAPS_CTRLWAVEBLTDST; // Caller mustn't specify primary specify buffer size. if ( (0 != dsbdTemp.dwBufferBytes) ) { RPF("IDirectSound::CreateSoundBuffer - Primary buffers must be created with dwBufferBytes = 0"); return DSERR_INVALIDPARAM; } else { dsbdTemp.dwBufferBytes = DEFAULT_PRIMARY_SIZE; } // We don't allow any apps to specify a format for primary buffers. if (NULL != dsbdTemp.lpwfxFormat) { RPF("IDirectSound::CreateSoundBuffer - error: app must not specify primary buffer format"); return DSERR_INVALIDCALL; } } else { // NOTE: There is a conflict of motives with these flags: the user is // telling us what they want to do, but we use them internally to tell // ourselves what to do, so save off the user's intentions into dwForce // and then tell them what they actually got at the end of the create if( (dsbdTemp.dwFlags & DSBCAPS_LOCSOFTWARE) ) { dsbdTemp.dwFlags &= (~DSBCAPS_LOCSOFTWARE); dwForce = DSBCAPS_LOCSOFTWARE; } else if( (dsbdTemp.dwFlags & DSBCAPS_LOCHARDWARE) ) { dsbdTemp.dwFlags &= (~DSBCAPS_LOCHARDWARE); dwForce = DSBCAPS_LOCHARDWARE; } } // Check that buffer has a real size... if( dsbdTemp.dwBufferBytes <= 4 ) { RPF("IDirectSound::CreateSoundBuffer - Buffer size too small"); return DSERR_INVALIDPARAM; } if( dsbdTemp.dwBufferBytes >= 0x10000000 ) { RPF("IDirectSound::CreateSoundBuffer - Buffer size too large"); return DSERR_INVALIDPARAM; } // // If the app is creating a primary, and a primary already exists for // the app, then return the app's existing primary buffer object. // if (DSBCAPS_PRIMARYBUFFER & dsbdTemp.dwFlags) { pdsbe = pdse->pdsbe; while (NULL != pdsbe) { if (DSB_INTERNALF_PRIMARY & pdsbe->pdsb->fdwDsbI) break; pdsbe = pdsbe->pNext; } if (NULL != pdsbe) { // // If they requested vol control, specify that this pdsbe is allowed. // if( dsbdTemp.dwFlags & DSBCAPS_CTRLVOLUME ) { pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLVOLUMEPRIMARY; } // // If they requested pan control, specify that this pdsbe is allowed. // if( dsbdTemp.dwFlags & DSBCAPS_CTRLPAN ) { pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLPANPRIMARY; } // Addref and return pointer to existing buffer pdsbe->lpVtbl->AddRef((LPDIRECTSOUNDBUFFER)pdsbe); *ppdsbe = pdsbe; return DS_OK; } } // Allocate the dsbe object pdsbe = (LPDSBUFFEREXTERNAL)MemAlloc(sizeof(DSBUFFEREXTERNAL)); if(NULL == pdsbe) { DPF(0,"CreateSoundBuffer object (External) alloc fail"); goto CREATE_ERROR_LAST; } pdsbe->lpVtbl = gpdsinfo->lpVtblDSb; pdsbe->pdse = pdse; pdsbe->uRefCount = 1; pdsbe->dwPID = GetCurrentProcessId(); pdsbe->dwPriority = pdse->dwPriority; pdsbe->pNext = pdse->pdsbe; pdse->pdsbe = pdsbe; // HACK HACK Multiple Primaries // Check to see if primary buffer is already allocated. if( (dsbdTemp.dwFlags & DSBCAPS_PRIMARYBUFFER ) && (pds->pdsbPrimary) ) { // We are asking for another primary buffer... DPF(3,"Primary Object already allocated" ); ASSERT( VALID_DSBUFFER_PTR(pds->pdsbPrimary) ); ASSERT( DSBUFFSIG == pds->pdsbPrimary->dwSig ); // Point external object to this primary pdsb = pds->pdsbPrimary; pdsbe->pdsb = pds->pdsbPrimary; // If this is not waveemulated, create an alias pointer to the data // buffer. Note that this only reserves linear address space. // Physical memory is committed on Lock. if (!(pdsb->pds->fdwInternal & DS_INTERNALF_WAVEEMULATED)) { pdsbe->pDSBufferAlias = vxdMemReserveAlias(pdsb->pDSBuffer, pdsb->cbBufferSize); } else { pdsbe->pDSBufferAlias = NULL; } // // If they requested vol control, specify that this pdsbe is allowed. // if( dsbdTemp.dwFlags & DSBCAPS_CTRLVOLUME ) { pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLVOLUMEPRIMARY; } // // If they requested pan control, specify that this pdsbe is allowed. // if( dsbdTemp.dwFlags & DSBCAPS_CTRLPAN ) { pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLPANPRIMARY; } // Addref existing object and return pointer to it pdsbe->lpVtbl->AddRef((LPDIRECTSOUNDBUFFER)pdsbe); // The refcount on this external primary should be 1 for the create // But it is now 2 since addref needs a + refcount to work // So reset it to 1 pdsbe->uRefCount = 1; DPF(2, "Return buffer ext %X, core %X mem ptr %X size %X", pdsbe, pdsb, pdsb->pDSBuffer ,pdsb->cbBufferSize ); // If the pdse is in focus and it is WRITEPRIMARY, then the // primary dsbe for this app should be stopped and so must be // the internal primary dsb. // // We also need to set the internal primary format, even when // not WRITEPRIMARY. // if (pdse->tidSound == gpdsinfo->tidSoundFocus || pdse->tidSound == gpdsinfo->tidStuckFocus) { HRESULT hr; if (pdse->dwPriority >= DSSCL_WRITEPRIMARY) { IDsbStopI(pds->pdsbPrimary, FALSE); } if (NULL != pdse->pwfxApp) { hr = IDsbSetFormatI( pds->pdsbPrimary, pdse->pwfxApp, 0 ); } else { hr = IDsbSetFormatI( pds->pdsbPrimary, &pds->wfxDefault, 0 ); } if( DS_OK != hr ) { RPF("IDirectSound::CreateSoundBuffer - Couldn't set primary format when creating primary buffer."); } } DsbeDeactivateIfNecessary (pdsbe); // Return pointer to new buffer *ppdsbe = pdsbe; return DS_OK; } // Allocate the dsb object pdsb = (LPDSBUFFER)MemAlloc(sizeof(DSBUFFER)); if(NULL == pdsb) { DPF(0,"CreateSoundBuffer object alloc fail"); goto CREATE_ERROR_BUFFEREXTERNAL; } pdsb->dwSig = DSBUFFSIG; DPF(1, "Allocating DSBUFFER obj 0x%8x",pdsb); // Point external object to this pdsbe->pdsb = pdsb; pdsb->fdwBufferDesc = dsbdTemp.dwFlags; pdsb->dwPrimaryNumber = dsbdTemp.dwReserved; // The reserved field is dwPrimaryNumber pdsb->cbBufferSize = dsbdTemp.dwBufferBytes; pdsb->pdsb3d = NULL; pdsb->pds = pds; pdsb->fdwDsbI = DSB_INTERNALF_STOP; pdsb->uRefCount = 1; pdsb->pNext = pds->pdsb; pds->pdsb = pdsb; pdsb->pdsbDuplicateNext = pdsb; pdsb->pdsbDuplicatePrev = pdsb; // Set up hack internal use external objct // This will be used by the mixer for calling // methods like stop // It will never be used for addref or release pdsb->dsbe.lpVtbl = gpdsinfo->lpVtblDSb; pdsb->dsbe.pdse = pdse; pdsb->dsbe.uRefCount = 1; pdsb->dsbe.dwPID = DWBUFFER_INTERNAL_PID; pdsb->dsbe.pNext = NULL; pdsb->dsbe.pdsb = pdsb; pdsb->dsbe.dwPriority = pdse->dwPriority; // If we are allocating the primary and this is the first primary // then initialize the ds obj to show the primary // The DSEPrimary will only be used internally if( dsbdTemp.dwFlags & DSBCAPS_PRIMARYBUFFER ) { pds->pdsbPrimary = pdsb; pdsb->fdwDsbI |= DSB_INTERNALF_PRIMARY; } // Alloc Process ID list pDSPID = (LPDSPROCESS)MemAlloc(sizeof(DSPROCESS)); if(NULL == pDSPID) { DPF(1,"IDSCreateSoundBuffer process alloc fail"); goto CREATE_ERROR_BUFFER; } pDSPID->dwPID = HackGetCurrentProcessId(); pDSPID->dwProcessRefCount = 1; pDSPID->pNext = NULL; pdsb->plProcess = pDSPID; // Get the ds object's caps dsc.dwSize = sizeof( DSCAPS ); hrTemp = DsGetCaps(pdse->pds, &dsc); ASSERT (DS_OK == hrTemp); DPF(3,"Caps Flags from Driver %X free buffers %X", dsc.dwFlags, dsc.dwFreeHwMixingAllBuffers ); // Allocate a buffer to hold the format // First get max format size // HACK HACK - NO Need to bring in ACM just for this... // Assume size will be reasonable // If we have a format use that size // Add on 16 bytes of slop space // Format may be changed to a larger one if( dsbdTemp.lpwfxFormat != NULL ) { dw = SIZEOF_WAVEFORMATEX( dsbdTemp.lpwfxFormat ); cbFormatSize = dw + 16; } else { cbFormatSize = SIZEOF_WAVEFORMATEX(&pds->wfxDefault); } // Now allocate memory pdsb->pwfx = (LPWAVEFORMATEX)MemAlloc(cbFormatSize); if(NULL == pdsb->pwfx) { DPF(1,"IDSHWCreateSoundBuffer object alloc fail"); goto CREATE_ERROR_PROCESS; } // Now copy the format if( dsbdTemp.lpwfxFormat != NULL ) { // We have a real format given DPF(3,"Format Given %X", dsbdTemp.lpwfxFormat ); if( !ValidPCMFormat( dsbdTemp.lpwfxFormat ) ) { RPF("IDirectSound::CreateSoundBuffer - Not a valid PCM format"); // Note that later on we should handle non pcm formats hrReturn = DSERR_BADFORMAT; goto CREATE_ERROR_FORMAT; } dw = SIZEOF_WAVEFORMATEX( dsbdTemp.lpwfxFormat ); CopyMemory( pdsb->pwfx, dsbdTemp.lpwfxFormat, dw ); } else { // Not a real format - set to default for now // Note this is only possible in the primary case. DPF(3,"No format create default." ); CopyMemory(pdsb->pwfx, &pds->wfxDefault, SIZEOF_WAVEFORMATEX(&pds->wfxDefault)); } //Set the helInfo stuff pdsb->helInfo.dwSampleRate = 0; pdsb->helInfo.hfFormat = 0; pdsb->helInfo.lVolume = 0; pdsb->helInfo.lPan = 0; pdsb->helInfo.dwLVolume = 0xffff; pdsb->helInfo.dwRVolume = 0xffff; pdsb->helInfo.dwMVolume = 0xffff; // IF there is a format to set if( pdsb->pwfx->wFormatTag == WAVE_FORMAT_PCM ) { pdsb->helInfo.dwSampleRate = pdsb->pwfx->nSamplesPerSec; pdsb->helInfo.hfFormat = 0; if( pdsb->pwfx->wBitsPerSample == 8 ) { pdsb->helInfo.hfFormat |= (H_8_BITS | H_UNSIGNED); } else { pdsb->helInfo.hfFormat |= (H_16_BITS | H_SIGNED); } if( pdsb->pwfx->nChannels == 2 ) { pdsb->helInfo.hfFormat |= (H_STEREO | H_ORDER_LR); } else { pdsb->helInfo.hfFormat |= H_MONO; } } else { DPF(0, "****************** set NON PCM format ******************" ); // Note that later on we should handle non pcm formats hrReturn = DSERR_BADFORMAT; goto CREATE_ERROR_FORMAT; } pdsb->helInfo.hfFormat |= H_LOOP; pdsb->fdwDsbI |= DSB_INTERNALF_LOOPING; // If this buffer is emulated on the WAVE APIs // then create and use that buffer if( pds->fdwInternal & DS_INTERNALF_WAVEEMULATED ) { if( dwForce & DSBCAPS_LOCHARDWARE ) { RPF("IDirectSound::CreateSoundBuffer - Can't create a hardware buffer when using wave emulation."); hrReturn = DSERR_INVALIDCALL; goto CREATE_ERROR_FORMAT; } // If the WAVEBLTDST flag is not set, you are not allowed // to WaveBlt into this buffer! if(!( dsbdTemp.dwFlags & DSBCAPS_CTRLWAVEBLTDST )) { // We will not need a mix buffer pdsb->pMixBuffer = NULL; } else { // Buffer will have stuff mixed into it.... // Default mix buffer size of 32 K. cbMixBufferSize = 0x00008000; pdsb->cbMixBufferSize = cbMixBufferSize; pdsb->pMixBuffer = (LPBYTE)MemAlloc(cbMixBufferSize); if(NULL == pdsb->pMixBuffer) { DPF(1,"IDSHWCreateSoundBuffer mix buffer alloc fail"); goto CREATE_ERROR_FORMAT; } } hr = WaveEmulateCreateSoundBuffer( pds, pdsb, &dsbdTemp ); // Set the grace mixer info pdsb->cSamples = pdsb->cbBufferSize / pdsb->pwfx->nBlockAlign; switch (pdsb->pwfx->nBlockAlign) { case 1: pdsb->uBlockAlignShift = 0; break; case 2: pdsb->uBlockAlignShift = 1; break; case 4: pdsb->uBlockAlignShift = 2; break; default: // Unsupported block align ASSERT(FALSE); } // For emulated primary buffers, we don't use an alias, // we use the real thing pdsbe->pDSBufferAlias = NULL; DsbeDeactivateIfNecessary (pdsbe); *ppdsbe = pdsbe; if( hr == DS_OK ) { return DS_OK; } else { hrReturn = hr; goto CREATE_ERROR_MIX; } } DPF(3," Check for try HW buffers %ld ", dsc.dwFreeHwMixingAllBuffers ); if (!(DSBCAPS_LOCSOFTWARE & dwForce)) { // if it's static, see if there are _any_ free HwMixingBuffers, // otherwise see if there are any free _streaming_ buffers if ( ((DSBCAPS_STATIC & dsbdTemp.dwFlags) && (dsc.dwFreeHwMixingAllBuffers > 0)) || (dsc.dwFreeHwMixingStreamingBuffers > 0) ) { DPF(3," Check for try HW FLAGS %X ", dsc.dwFlags ); DPF(3," Check for try HW format chann %d ", pdsb->pwfx->nChannels); DPF(3," Check for try HW format bits %d ",pdsb->pwfx->wBitsPerSample); // Card has secondary buffers in HW if( ( ( (pdsb->pwfx->nChannels == 1) && (dsc.dwFlags & DSCAPS_SECONDARYMONO) ) || ( (pdsb->pwfx->nChannels == 2) && (dsc.dwFlags & DSCAPS_SECONDARYSTEREO) ) ) && ( ( (pdsb->pwfx->wBitsPerSample == 8) && (dsc.dwFlags & DSCAPS_SECONDARY8BIT) ) || ( (pdsb->pwfx->wBitsPerSample == 16) && (dsc.dwFlags & DSCAPS_SECONDARY16BIT) ) ) ) { // Card can support mono/stereo format requested // Card can support 8/16 bit format requested DPF(3," Try a hardware buffer" ); // If it was a primary this flag was already set dsbdTemp.dwFlags |= DSBCAPS_LOCHARDWARE; } } } // // Check whether this is a primary or HW buffer. If // so then try to use the HAL code // fTryHardware = (0 != (dsbdTemp.dwFlags & DSBCAPS_LOCHARDWARE)); fTrySoftware = !(DSBCAPS_PRIMARYBUFFER & dsbdTemp.dwFlags) && !(DSBCAPS_LOCHARDWARE & dwForce); if (!fTryHardware && !fTrySoftware) { hrReturn = DSERR_INVALIDCALL; goto CREATE_ERROR_FORMAT; } if (fTryHardware) { // in order to implement sound focus with secondary hardware // buffers, we need CTRLVOLUME on them. if (0 == (DSBCAPS_PRIMARYBUFFER & dsbdTemp.dwFlags)) { dsbdTemp.dwFlags |= DSBCAPS_CTRLVOLUME; } hrReturn = DsCreateHardwareBuffer(pds, pdsb, &dsbdTemp, &fTrySoftware); if (DS_OK == hrReturn) fTrySoftware = FALSE; } if (fTrySoftware) hrReturn = DsCreateSoftwareBuffer(pds, pdsb, &dsbdTemp); if (DS_OK != hrReturn) goto CREATE_ERROR_FORMAT; // // If the creating app doesn't have sound focus then we need to // immediately deactivate the new buffer // DsbeDeactivateIfNecessary (pdsbe); // // // DsbFillSilence( pdsb ); // If the WAVEBLTDST flag is not set, you are not allowed // to WaveBlt into this buffer! if(!( dsbdTemp.dwFlags & DSBCAPS_CTRLWAVEBLTDST )) { // We will not need a mix buffer pdsb->pMixBuffer = NULL; } else { // Buffer will have stuff mixed into it.... // If we've just created the hardware primary buffer, then let's // allocate a mixer buffer that is certainly large enough if (DSB_INTERNALF_PRIMARY & pdsb->fdwDsbI) { // May need up to 4X the buffer size cbMixBufferSize = pdsb->cbBufferSize * 4; } else { // Default mix buffer size of 32 K. cbMixBufferSize = 0x00008000; } pdsb->cbMixBufferSize = cbMixBufferSize; pdsb->pMixBuffer = (LPBYTE)MemAlloc(cbMixBufferSize); if(NULL == pdsb->pMixBuffer) { DPF(1,"IDSHWCreateSoundBuffer mix buffer alloc fail"); goto CREATE_ERROR_MIX; } } // Set the grace mixer info pdsb->cSamples = pdsb->cbBufferSize / pdsb->pwfx->nBlockAlign; switch (pdsb->pwfx->nBlockAlign) { case 1: pdsb->uBlockAlignShift = 0; break; case 2: pdsb->uBlockAlignShift = 1; break; case 4: pdsb->uBlockAlignShift = 2; break; default: // Unsupported block align ASSERT(FALSE); } // If this is the first buffer for this app, and it wasn't a // primary buffer, and this app has focus, then we need to set // the format of the primary buffer. if ((NULL == pdsbe->pNext) && (!(DSB_INTERNALF_PRIMARY & pdsbe->pdsb->fdwDsbI))) { if( pdse->tidSound == gpdsinfo->tidSoundFocus || pdse->tidSound == gpdsinfo->tidStuckFocus ) { HRESULT hr; if (NULL != pdse->pwfxApp) { hr = IDsbSetFormatI( pds->pdsbPrimary, pdse->pwfxApp, 0 ); } else { hr = IDsbSetFormatI( pds->pdsbPrimary, &pds->wfxDefault, 0 ); } if( DS_OK != hr ) { DPF( 0, "Couldn't set primary format when creating primary buffer."); } } } // If this is a primary buffer, then create an alias pointer to the data // buffer. Note that this only reserves linear address space. Physical // memory is not commited. For secondary buffers, we use the internal // buffer ptr. ASSERT(!(pds->fdwInternal & DS_INTERNALF_WAVEEMULATED)); if (DSB_INTERNALF_PRIMARY & pdsb->fdwDsbI) { pdsbe->pDSBufferAlias = vxdMemReserveAlias(pdsb->pDSBuffer, pdsb->cbBufferSize); } else { pdsbe->pDSBufferAlias = NULL; } DPF(2, "Return buffer ext %X, core %X mem ptr %X size %X", pdsbe, pdsb, pdsb->pDSBuffer ,pdsb->cbBufferSize ); // Return pointer to new buffer *ppdsbe = pdsbe; DPF(3,"IDSHWCreateSoundBuffer Exit"); return DS_OK; CREATE_ERROR_MIX: if( pdsb->pMixBuffer != NULL ) { MemFree(pdsb->pMixBuffer); } CREATE_ERROR_FORMAT: MemFree(pdsb->pwfx); CREATE_ERROR_PROCESS: MemFree(pdsb->plProcess); CREATE_ERROR_BUFFER: // if this buffer was added to the pDS obj list, remove it if(pds->pdsb == pdsb) { pds->pdsb = pdsb->pNext; } else { for(pdsb1 = pds->pdsb; pdsb1 != NULL; pdsb1 = pdsb1->pNext) { if(pdsb1->pNext == pdsb) { pdsb1->pNext = pdsb->pNext; pdsb->pNext = NULL; break; } } } DPF(1, "Freeing DSBUFFER obj 0x%8x",pdsb); pdsb->dwSig = 0xdeaddead; MemFree(pdsb); CREATE_ERROR_BUFFEREXTERNAL: if( pdsbe == pds->pdsbePrimary ) { // This is the original alloc of the primary // clean up DS obj pds->pdsbPrimary = NULL; pds->pdsbePrimary = NULL; } // If this buffer was added to the pDSE list, remove it if(pdse->pdsbe == pdsbe) { pdse->pdsbe = pdsbe->pNext; } else { for(pdsbe1 = pdse->pdsbe; pdsbe1 != NULL; pdsbe1 = pdsbe1->pNext) { if(pdsbe1->pNext == pdsbe) { pdsbe1->pNext = pdsbe->pNext; pdsbe->pNext = NULL; break; } } } // If we allocated a wave format, free it. if (NULL != pdse->pwfxApp) MemFree(pdse->pwfxApp); // MemFree(pdsbe); CREATE_ERROR_LAST: if( hrReturn != DS_OK ) { return hrReturn; } else { return DSERR_OUTOFMEMORY; } } //--------------------------------------------------------------------------; // // LPDIRECTSOUNDBUFFER IDSHWCreateSoundBuffer // // Description: // This function is the member function for CreateSoundBuffer. // // Arguments: // LPDIRECTSOUND pids: Pointer to Direct Sound Object. // // LPDSBUFFERCREATE pdsbc: Pointer to a DSBufferCreate structure. // // Return (LPDSBUFFER): // Pointer to a DSBUFFER structure. // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/01/95 Fwong Making sense out of non-sense. // 11/30/95 angusm Added creation of Focus Thread. // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWCreateSoundBuffer ( LPDIRECTSOUND pids, LPDSBUFFERDESC pdsbd, LPLPDIRECTSOUNDBUFFER lplpDirectSoundBuffer, IUnknown FAR *pUnkOuter ) { LPDSBUFFEREXTERNAL pdsbe; LPDSOUNDEXTERNAL pdse; HRESULT hrReturn; DPF(3,"IDSHWCreateSoundBuffer"); if( !VALID_DWORD_PTR(lplpDirectSoundBuffer) ) { RPF("IDirectSound::CreateSoundBuffer - Invalid lplpDirectSoundBuffer"); return DSERR_INVALIDPARAM; } *lplpDirectSoundBuffer = NULL; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::CreateSoundBuffer - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } if( !VALID_DSOUND_PTR(pdse->pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::CreateSoundBuffer - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } if( !VALID_DSBUFFERDESC_PTR(pdsbd) ) { RPF("IDirectSound::CreateSoundBuffer - Invalid Buffer Description or dwSize member."); return DSERR_INVALIDPARAM; } if( 0 != pdsbd->dwReserved ) { RPF("IDirectSound::CreateSoundBuffer - DSBUFFERDESC.dwReserved must be zero."); return DSERR_INVALIDPARAM; } if( pdsbd->dwFlags & (~DSBCAPS_VALIDFLAGS)) { RPF("IDirectSound::CreateSoundBuffer - Invalid CAPS flags sent to CreateSoundBuffer"); return DSERR_INVALIDPARAM; } if( (DSBCAPS_LOCHARDWARE | DSBCAPS_LOCSOFTWARE) == (pdsbd->dwFlags & (DSBCAPS_LOCHARDWARE | DSBCAPS_LOCSOFTWARE)) ) { RPF("IDirectSound::CreateSoundBuffer - Both DSBCAPS_LOCHARDWARE and DSBCAPS_LOCSOFTWARE flags were specified: failing"); return DSERR_INVALIDPARAM; } if( pUnkOuter != NULL ) { RPF("IDirectSound::CreateSoundBuffer - pUnkOuter must be NULL for this rev!"); return DSERR_NOAGGREGATION; } // If this is not a primary then format must be set if( !(pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) ) { if( !VALID_WAVEFORMATEX_PTR((pdsbd->lpwfxFormat)) ) { RPF("IDirectSound::CreateSoundBuffer - Invalid Format pointer"); return DSERR_BADFORMAT; } } // // If this is a primary, then we only allow DSBCAPS_CTRLVOLUME and // DSBCAPS_CTRLPAN controls on it. In fact, we should really mask off // this flag before we call down to the driver to create the buffer // (just in case the call were to fail), but in fact the primary will be // created by DirectSoundCreate without that control, and subsequent // creations will simply create a new external object, so we're OK for // now. REMIND HACKHACK BUGBUG fix this. // if( pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER ) { if( pdsbd->dwFlags & DSBCAPS_CTRLFREQUENCY ) { RPF("IDirectSound::CreateSoundBuffer - Primary buffers don't support frequency control."); return DSERR_CONTROLUNAVAIL; } if( pdsbd->dwFlags & DSBCAPS_STATIC ) { RPF("IDirectSound::CreateSoundBuffer - Primary buffers can't be static!"); return DSERR_INVALIDPARAM; } } ENTER_DLL_CSECT(); hrReturn = DseCreateDsbe(pdse, pdsbd, &pdsbe); LEAVE_DLL_CSECT(); *lplpDirectSoundBuffer = (LPDIRECTSOUNDBUFFER)pdsbe; return hrReturn; } // IDSHWCreateSoundBuffer() //--------------------------------------------------------------------------; // // LPDSBUFFER IDuplicateSoundBuffer // // Description: // This function is the member function for DuplicateSoundBuffer. // // Arguments: // LPDIRECTSOUND pids: Pointer to Direct Sound Object. // // LPDSBUFFERCREATE pdsbc: Pointer to a DSBufferCreate structure. // // Return (LPDSBUFFER): // Pointer to a DSBUFFER structure. // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/01/95 Fwong Making sense out of non-sense. // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWDuplicateSoundBuffer ( LPDIRECTSOUND pids, LPDIRECTSOUNDBUFFER pidsbCurrent, LPLPDIRECTSOUNDBUFFER ppidsbD ) { LPDSBUFFER pdsbCurrent; LPDSBUFFEREXTERNAL pdsbeCurrent; LPDSBUFFER pdsbNew; LPDSBUFFEREXTERNAL pdsbeNew; LPDSOUND pds; LPDSOUNDEXTERNAL pdse; DWORD cbMixBufferSize; DWORD cbFormatSize; DWORD dw; LPDSPROCESS pDSPID; DSCAPS dsc; HRESULT hrReturn; HRESULT hr; DPF(3,"IDSHWDuplicateSoundBuffer"); if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } if( !VALID_DSBUFFERE_PTR(pidsbCurrent) ) { RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdsbeCurrent = (LPDSBUFFEREXTERNAL)pidsbCurrent; pdsbCurrent = pdsbeCurrent->pdsb; if( !VALID_DSBUFFER_PTR(pdsbCurrent) || (0 == pdsbeCurrent->uRefCount)) { RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } ASSERT( DSBUFFSIG == pdsbCurrent->dwSig ); // If this is a primary then return failure if( pdsbCurrent == pds->pdsbPrimary ) { RPF("IDirectSound::DuplicateSoundBuffer - Can not duplicate primary"); return DSERR_INVALIDCALL; } hrReturn = DS_OK; *ppidsbD = NULL; ENTER_DLL_CSECT(); cbMixBufferSize = pdsbCurrent->cbMixBufferSize; // Allocate the external dsb object pdsbeNew = (LPDSBUFFEREXTERNAL)MemAlloc(sizeof(DSBUFFEREXTERNAL)); if(NULL == pdsbeNew) { DPF(0,"DuplicateSoundBuffer object (External) alloc fail"); goto DUPLICATE_ERROR_LAST; } pdsbeNew->lpVtbl = gpdsinfo->lpVtblDSb; pdsbeNew->pdse = pdse; pdsbeNew->uRefCount = 1; pdsbeNew->dwPID = GetCurrentProcessId(); pdsbeNew->dwPriority = pdsbeCurrent->dwPriority; pdsbeNew->pNext = pdse->pdsbe; pdse->pdsbe = pdsbeNew; // We are not going to dup primaryies no need to mess // Allocate the dsb object pdsbNew = (LPDSBUFFER)MemAlloc(sizeof(DSBUFFER)); if(NULL == pdsbNew) { DPF(0,"DuplicateSoundBuffer object alloc fail"); goto DUPLICATE_ERROR_BUFFEREXTERNAL; } pdsbNew->dwSig = DSBUFFSIG; DPF(1, "Allocating DSBUFFER obj 0x%8x",pdsbNew); // Point external object to this pdsbeNew->pdsb = pdsbNew; pdsbNew->fdwBufferDesc = pdsbCurrent->fdwBufferDesc; pdsbNew->dwPrimaryNumber = pdsbCurrent->dwPrimaryNumber; pdsbNew->pDSBuffer = pdsbCurrent->pDSBuffer; pdsbNew->cbBufferSize = pdsbCurrent->cbBufferSize; pdsbNew->dwCardAddress = pdsbCurrent->dwCardAddress; pdsbNew->pdsb3d = NULL; pdsbNew->pds = pds; pdsbNew->fdwDsbI = pdsbCurrent->fdwDsbI; pdsbNew->fdwDsbI |= DSB_INTERNALF_STOP; pdsbNew->fdwDsbI &= ~DSB_INTERNALF_LOOPING; pdsbNew->uRefCount = 1; pdsbNew->pNext = pds->pdsb; pds->pdsb = pdsbNew; pdsbNew->pdsbDuplicateNext = pdsbCurrent; pdsbNew->pdsbDuplicatePrev = pdsbCurrent->pdsbDuplicatePrev; pdsbCurrent->pdsbDuplicatePrev->pdsbDuplicateNext = pdsbNew; pdsbCurrent->pdsbDuplicatePrev = pdsbNew; // Set up hack internal use external objct // This will be used by the mixer for calling // methods like stop // It will never be used for addref or release pdsbNew->dsbe.lpVtbl = gpdsinfo->lpVtblDSb; pdsbNew->dsbe.pdse = pdse; pdsbNew->dsbe.uRefCount = 1; pdsbNew->dsbe.dwPID = DWBUFFER_INTERNAL_PID; pdsbNew->dsbe.pNext = NULL; pdsbNew->dsbe.pdsb = pdsbNew; pdsbNew->dsbe.dwPriority = pdsbeCurrent->dwPriority; //Allocate the mix buffer pdsbNew->cbMixBufferSize = cbMixBufferSize; pdsbNew->pMixBuffer = NULL; if( cbMixBufferSize != 0 ) { pdsbNew->pMixBuffer = (LPBYTE)MemAlloc(cbMixBufferSize); if(NULL == pdsbNew->pMixBuffer) { DPF(1,"IDSHWDuplicateSoundBuffer mix buffer alloc fail"); goto DUPLICATE_ERROR_BUFFER; } } // Alloc Process ID list pDSPID = (LPDSPROCESS)MemAlloc(sizeof(DSPROCESS)); if(NULL == pDSPID) { DPF(1,"IDSDuplicateSoundBuffer process alloc fail"); goto DUPLICATE_ERROR_MIX; } pDSPID->dwPID = HackGetCurrentProcessId(); pDSPID->dwProcessRefCount = 1; pDSPID->pNext = NULL; pdsbNew->plProcess = pDSPID; // Allocate a buffer to hold the format dw = SIZEOF_WAVEFORMATEX( pdsbCurrent->pwfx ); cbFormatSize = dw + 16; // Now allocate memory pdsbNew->pwfx = NULL; pdsbNew->pwfx = (LPWAVEFORMATEX)MemAlloc(cbFormatSize); if(NULL == pdsbNew->pwfx) { DPF(1,"IDSHWDuplicateSoundBuffer object alloc fail"); goto DUPLICATE_ERROR_PROCESS; } dw = SIZEOF_WAVEFORMATEX( pdsbCurrent->pwfx ); CopyMemory( pdsbNew->pwfx, pdsbCurrent->pwfx, dw ); //Set the helInfo stuff pdsbNew->helInfo.dwSampleRate = pdsbNew->pwfx->nSamplesPerSec; pdsbNew->helInfo.hfFormat = pdsbCurrent->helInfo.hfFormat; pdsbNew->helInfo.lVolume = pdsbCurrent->helInfo.lVolume; pdsbNew->helInfo.lPan = pdsbCurrent->helInfo.lPan; pdsbNew->helInfo.dwLVolume = pdsbCurrent->helInfo.dwLVolume; pdsbNew->helInfo.dwRVolume = pdsbCurrent->helInfo.dwRVolume; pdsbNew->helInfo.dwMVolume = pdsbCurrent->helInfo.dwMVolume; // No need to handle Wave Emulated separatly since primary // is the same and emulated are the same as other emulated // If the other buffer is HW then see if we can make this one. if (DSB_INTERNALF_HARDWARE & pdsbCurrent->fdwDsbI) { // Check to see if we can make this a HW buffer // Get the caps and see if we have it free.... dsc.dwSize = sizeof( DSCAPS ); hr = DsGetCaps(pdse->pds, &dsc); ASSERT (DS_OK == hr); if( (dsc.dwFreeHwMixingAllBuffers > 0 ) ) { DPF(3," Check for try HW FLAGS %X ", dsc.dwFlags ); DPF(3," Check for try HW format chann %d ", pdsbNew->pwfx->nChannels); DPF(3," Check for try HW format bits %d ", pdsbNew->pwfx->wBitsPerSample); // Card has secondary buffers in HW // We are OK } else { // Other buffer is in HW and we can not make this one in HW // Fail call. RPF("IDirectSound::DuplicateSoundBuffer - HW does not have resources to duplicate"); hrReturn = DSERR_ALLOCATED; goto DUPLICATE_ERROR_FORMAT; } } // Check to see if this is a primary or HW buffer // If so then use the HAL code if (DSB_INTERNALF_HARDWARE & pdsbCurrent->fdwDsbI) { // We are asking for a HW buffer... DPF(3,"HW Duplicate HW buffer"); DPF(3,"HW Duplicate - sample rate %d", pdsbNew->helInfo.dwSampleRate ); DPF(3,"HW Duplicate - hfForamt %X", pdsbNew->helInfo.hfFormat ); hrReturn = vxdDrvDuplicateSoundBuffer( pds->hHal, pdsbCurrent->hBuffer, &pdsbNew->hBuffer ); if (DS_OK != hrReturn) { DPF(1,"IDSHWDuplicateSoundBuffer buffer alloc fail"); goto DUPLICATE_ERROR_FORMAT; } ASSERT(NULL != pdsbNew->hBuffer); DPF(3,"IDSHWDuplicateSoundBuffer buffer alloc OK"); } else { DPF(3,"HW Duplicate Emulated secondary buffer"); } // Should be using same snd data buffer ASSERT(pdsbNew->pDSBuffer == pdsbCurrent->pDSBuffer); ASSERT(pdsbNew->cbBufferSize == pdsbCurrent->cbBufferSize); DPF(3,"--------Dup data for Emulated obj %X buff %X len %X", pdsbNew, pdsbNew->pDSBuffer, pdsbNew->cbBufferSize ); // // If the creating app doesn't have sound focus then we need to // immediately deactivate the new buffer // DsbeDeactivateIfNecessary (pdsbeNew); // Set the grace info pdsbNew->cSamples = pdsbNew->cbBufferSize / pdsbNew->pwfx->nBlockAlign; switch (pdsbNew->pwfx->nBlockAlign) { case 1: pdsbNew->uBlockAlignShift = 0; break; case 2: pdsbNew->uBlockAlignShift = 1; break; case 4: pdsbNew->uBlockAlignShift = 2; break; default: ASSERT(FALSE); } *ppidsbD = (LPDIRECTSOUNDBUFFER)pdsbeNew; DPF(3,"IDSHWDuplicateSoundBuffer Exit"); LEAVE_DLL_CSECT( ); return DS_OK; //DUPLICATE_ERROR_DATA: // Do not free since we are duplicating DUPLICATE_ERROR_FORMAT: MemFree(pdsbNew->pwfx); DUPLICATE_ERROR_PROCESS: MemFree(pdsbNew->plProcess); DUPLICATE_ERROR_MIX: if( pdsbNew->pMixBuffer != NULL ) { MemFree(pdsbNew->pMixBuffer); } DUPLICATE_ERROR_BUFFER: // Remove it from duplicate list pdsbNew->pdsbDuplicateNext->pdsbDuplicatePrev = pdsbNew->pdsbDuplicatePrev; pdsbNew->pdsbDuplicatePrev->pdsbDuplicateNext = pdsbNew->pdsbDuplicateNext; // Remove from buffer list (inserted at head) // ASSERT(pds->pdsb == pdsbNew); pds->pdsb = pds->pdsb->pNext; DPF(1, "Freeing DSBUFFER obj 0x%8x",pdsbNew); pdsbNew->dwSig = 0xdeaddead; MemFree(pdsbNew); DUPLICATE_ERROR_BUFFEREXTERNAL: // Unlink pdsbNew from external list (inserted at head) // ASSERT(pdse->pdsbe == pdsbeNew); pdse->pdsbe = pdse->pdsbe->pNext; MemFree(pdsbeNew); DUPLICATE_ERROR_LAST: LEAVE_DLL_CSECT( ); if( hrReturn != DS_OK ) { return hrReturn; } else { return DSERR_OUTOFMEMORY; } } // IDSHWDuplicateSoundBuffer() #if 0 //--------------------------------------------------------------------------; // // IDSHWEnumSoundBuffers // // Description: // // Arguments: // // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWEnumSoundBuffers ( LPDIRECTSOUND pids, LPDSENUMBUFFERSCALLBACK pfnEnumCB, LPVOID lpContext ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; LPDSBUFFEREXTERNAL pdsbeSearch; LPDSBUFFERDESC lpdsbd; LPWAVEFORMATEX pwfx; DWORD dw; BOOL fRet; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::EnumSoundBuffers - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::EnumSoundBuffers - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } DPF(0,"Entering EnumSoundBuffers method"); ENTER_DLL_CSECT(); if(( lpdsbd = MemAlloc( sizeof(DSBUFFERDESC) )) == NULL ) { RPF("IDirectSound::EnumSoundBuffers - Couldn't allocate DSBUFFERDESC structure!"); LEAVE_DLL_CSECT(); DPF(0,"Leaving EnumSoundBuffers method"); return DSERR_OUTOFMEMORY; } pdsbeSearch = pdse->pdsbe; while( pdsbeSearch != NULL ) { _fmemset( lpdsbd, 0, sizeof(DSBUFFERDESC)); // In case we want to use non-PCM format structures, we can do so, // because we're allowing for dw = SIZEOF_WAVEFORMATEX( pdsbeSearch->pdsb->pwfx ) + 16; if(( pwfx = MemAlloc( dw )) == NULL ) { RPF("IDirectSound::EnumSoundBuffers - Couldn't allocate WAVEFORMATEX structure!"); MemFree( lpdsbd ); LEAVE_DLL_CSECT(); DPF(0,"Leaving EnumSoundBuffers method"); return DSERR_OUTOFMEMORY; } dw = SIZEOF_WAVEFORMATEX( pdsbeSearch->pdsb->pwfx ); CopyMemory( pwfx, pdsbeSearch->pdsb->pwfx, dw ); lpdsbd->dwSize = sizeof(DSBUFFERDESC); lpdsbd->cbBuffer = pdsbeSearch->pdsb->cbBufferSize; lpdsbd->lpwfxFormat = pwfx; lpdsbd->dwFlags = pdsbeSearch->pdsb->fdwBufferDesc; lpdsbd->dwReserved = pdsbeSearch->pdsb->dwPrimaryNumber; lpdsbd->lpDirectSoundBuffer = (LPDIRECTSOUNDBUFFER)pdsbeSearch; pdsbeSearch->lpVtbl->AddRef((LPDIRECTSOUNDBUFFER)pdsbeSearch ); DPF(1,"EnumSoundBuffers: Calling enumeration callback"); fRet = pfnEnumCB( lpdsbd, lpContext ); MemFree( pwfx ); // Break out early if they told us not to continue... if( fRet != TRUE ) { DPF(1,"Ending enumeration prematurely"); break; } pdsbeSearch = pdsbeSearch->pNext; } MemFree( lpdsbd ); LEAVE_DLL_CSECT(); DPF(0,"Leaving EnumSoundBuffers method"); return DS_OK; } #endif // 0 //--------------------------------------------------------------------------; // // DsGetCapsNative // // Description: This sets capabilty flags for supported hardware // // Arguments: pDSCaps pointer to capabilities structure to be filled // pds pointer to direct sound object // // Concurrency: sequential // // Assumptions: pds->hHal, pds->dwHeapType, pds->DriverHeap // are initialized // // Return (HRESULT): DS_OK Success // DS_xxxxx on failure // //--------------------------------------------------------------------------; HRESULT DsGetCapsNative ( LPDSCAPS pDSCaps, LPDSOUND pds ) { DSDRIVERCAPS drvCaps; HRESULT hr; DPF (2, "DsGetCapsNative: Enter Function"); DPF (2, "DsGetCapsNative: pDSCaps = %x", pDSCaps); DPF (2, "DsGetCapsNative: pds = %x", pds); ZeroMemory(&drvCaps, sizeof(drvCaps)); hr = vxdDrvGetCaps(pds->hHal, &drvCaps); if (DS_OK != hr) { DPF(0, "DsGetCapsNative: vxdDrvGetCaps failure"); return hr; } // Check flags returned from driver to see if they are all // valid flags for a driver if (drvCaps.dwFlags & (~DSCAPS_VALIDDRIVERFLAGS)) { RPF ("Sound Device Driver may be broken. Returned non-valid" "flags to Direct Sound: %x", drvCaps.dwFlags); ASSERT (0); } pDSCaps->dwFlags = drvCaps.dwFlags & DSCAPS_VALIDDRIVERFLAGS; pDSCaps->dwMinSecondarySampleRate = drvCaps.dwMinSecondarySampleRate; pDSCaps->dwMaxSecondarySampleRate = drvCaps.dwMaxSecondarySampleRate; pDSCaps->dwPrimaryBuffers = drvCaps.dwPrimaryBuffers; pDSCaps->dwMaxHwMixingAllBuffers = drvCaps.dwMaxHwMixingAllBuffers; pDSCaps->dwMaxHwMixingStaticBuffers = drvCaps.dwMaxHwMixingStaticBuffers; pDSCaps->dwMaxHwMixingStreamingBuffers = drvCaps.dwMaxHwMixingStreamingBuffers; pDSCaps->dwFreeHwMixingAllBuffers = drvCaps.dwFreeHwMixingAllBuffers; pDSCaps->dwFreeHwMixingStaticBuffers = drvCaps.dwFreeHwMixingStaticBuffers; pDSCaps->dwFreeHwMixingStreamingBuffers = drvCaps.dwFreeHwMixingStreamingBuffers; pDSCaps->dwMaxHw3DAllBuffers = drvCaps.dwMaxHw3DAllBuffers; pDSCaps->dwMaxHw3DStaticBuffers = drvCaps.dwMaxHw3DStaticBuffers; pDSCaps->dwMaxHw3DStreamingBuffers = drvCaps.dwMaxHw3DStreamingBuffers; pDSCaps->dwFreeHw3DAllBuffers = drvCaps.dwFreeHw3DAllBuffers; pDSCaps->dwFreeHw3DStaticBuffers = drvCaps.dwFreeHw3DStaticBuffers; pDSCaps->dwFreeHw3DStreamingBuffers = drvCaps.dwFreeHw3DStreamingBuffers; pDSCaps->dwTotalHwMemBytes = drvCaps.dwTotalHwMemBytes; pDSCaps->dwFreeHwMemBytes = drvCaps.dwFreeHwMemBytes; pDSCaps->dwMaxContigFreeHwMemBytes = drvCaps.dwMaxContigFreeHwMemBytes; // // If we're using vmemmgr to manage the hw memory... // if ((DSDHEAP_CREATEHEAP == pds->dwHeapType) || (DSDHEAP_USEDIRECTDRAWHEAP == pds->dwHeapType)) { ASSERT(pds->pDriverHeap); pDSCaps->dwFreeHwMemBytes = VidMemAmountFree(pds->pDriverHeap); pDSCaps->dwMaxContigFreeHwMemBytes = VidMemLargestFree(pds->pDriverHeap); } // REMIND what about these? pDSCaps->dwUnlockTransferRateHwBuffers = 0; pDSCaps->dwPlayCpuOverheadSwBuffers = 0; DPF (2, "DsGetCapsNative: Leave Function"); return DS_OK; } //--------------------------------------------------------------------------; // // DsGetCapsEmulated // // Description: This sets capability flags for emulated hardware // // Arguments: pDSCaps pointer to capabilities structure to be filled // pds pointer to Direct Sound Object // // Concurrency: sequential // // Assumptions: pds->uDeviceID is initialized // // Return (HRESULT): DS_OK Success // DSERR_GENERIC Error getting capabilities wave // out device // DS_xxxxx on failure // //--------------------------------------------------------------------------; HRESULT DsGetCapsEmulated ( LPDSCAPS pDSCaps, LPDSOUND pds ) { WAVEOUTCAPS woc; MMRESULT mmr; DPF (2, "DsGetCapsEmulated: Enter Function"); DPF (2, "DsGetCapsEmulated: pDSCaps = %x", pDSCaps); DPF (2, "DsGetCapsEmulated: pds = %x", pds); mmr = waveOutGetDevCaps( pds->uDeviceID, &woc, sizeof(woc) ); if( mmr != MMSYSERR_NOERROR ) { DPF (0, "DsGetCapsEmulated: WaveOutGetCaps failed"); return DSERR_GENERIC; } pDSCaps->dwFlags = 0; if( woc.dwFormats & WAVE_FORMAT_1M08 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO; pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT; } if( woc.dwFormats & WAVE_FORMAT_2M08 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO; pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT; } if( woc.dwFormats & WAVE_FORMAT_4M08 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO; pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT; } if( woc.dwFormats & WAVE_FORMAT_1S08 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO; pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT; } if( woc.dwFormats & WAVE_FORMAT_2S08 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO; pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT; } if( woc.dwFormats & WAVE_FORMAT_4S08 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO; pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT; } if( woc.dwFormats & WAVE_FORMAT_1M16 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO; pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT; } if( woc.dwFormats & WAVE_FORMAT_2M16 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO; pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT; } if( woc.dwFormats & WAVE_FORMAT_4M16 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO; pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT; } if( woc.dwFormats & WAVE_FORMAT_1S16 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO; pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT; } if( woc.dwFormats & WAVE_FORMAT_2S16 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO; pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT; } if( woc.dwFormats & WAVE_FORMAT_4S16 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO; pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT; } if( woc.dwFormats & WAVE_FORMAT_1S16 ) { pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO; pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT; } pDSCaps->dwFlags |= DSCAPS_EMULDRIVER; pDSCaps->dwMinSecondarySampleRate = 100; pDSCaps->dwMaxSecondarySampleRate = 100000; pDSCaps->dwPrimaryBuffers = 1; pDSCaps->dwMaxHwMixingAllBuffers = 0; pDSCaps->dwMaxHwMixingStaticBuffers = 0; pDSCaps->dwMaxHwMixingStreamingBuffers = 0; pDSCaps->dwFreeHwMixingAllBuffers = 0; pDSCaps->dwFreeHwMixingStaticBuffers = 0; pDSCaps->dwFreeHwMixingStreamingBuffers = 0; pDSCaps->dwMaxHw3DAllBuffers = 0; pDSCaps->dwMaxHw3DStaticBuffers = 0; pDSCaps->dwMaxHw3DStreamingBuffers = 0; pDSCaps->dwFreeHw3DAllBuffers = 0; pDSCaps->dwFreeHw3DStaticBuffers = 0; pDSCaps->dwFreeHw3DStreamingBuffers = 0; pDSCaps->dwTotalHwMemBytes = 0; pDSCaps->dwFreeHwMemBytes = 0; pDSCaps->dwMaxContigFreeHwMemBytes = 0; // REMIND what about these? pDSCaps->dwUnlockTransferRateHwBuffers = 0; pDSCaps->dwPlayCpuOverheadSwBuffers = 0; DPF (2, "DsGetCapsEmulated: Leave Function"); return DS_OK; } //--------------------------------------------------------------------------; // // DsGetCaps // // Description: This function gets the capabilty flags for the given // device. // // Arguments: pds pointer to direct sound object to retrieve // capabilities on. // pDSCaps pointer to capabilities structure to be filled // // Concurrency: synchronous // // Return (HRESULT): DS_OK on Success // refer to DsGetCapsNative or DsGetCapsEmulated // error codes // //--------------------------------------------------------------------------; HRESULT DsGetCaps ( LPDSOUND pds, LPDSCAPS pDSCaps ) { HRESULT hr; DPF (2, "DsGetCaps: Enter Function"); DPF (2, "DsGetCaps: pds = %x", pds); DPF (2, "DsGetCaps: pDSCaps = %x", pDSCaps); ENTER_DLL_CSECT(); if( pds->fdwInternal & DS_INTERNALF_WAVEEMULATED ) { DPF (3, "DsGetCaps: getting emulated capabilities"); hr = DsGetCapsEmulated (pDSCaps, pds); } else { DPF (3, "DsGetCaps: getting native capabilities"); hr = DsGetCapsNative (pDSCaps, pds); } if (DS_OK == hr) { // Set driver version, as calculated on pds object creation. pDSCaps->dwReserved1 = pds->dwDriverVersionMinor; pDSCaps->dwReserved2 = pds->dwDriverVersionMajor; // Set certified bit. if( pds->fdwInternal & DS_INTERNALF_CERTIFIED ) { pDSCaps->dwFlags |= DSCAPS_CERTIFIED; } DPF (3, "DsGetCaps: version information:"); DPF (3, "DsGetCaps: dwDriverVersionMinor = %d", pDSCaps->dwReserved1); DPF (3, "DsGetCaps: dwDriverVersionMajor = %d", pDSCaps->dwReserved2); DPF (3, "DsGetCaps: Certified? = %d", pDSCaps->dwFlags & DSCAPS_CERTIFIED); } else { DPF (1, "DsGetCaps: error getting capabilities (%d)", hr); } LEAVE_DLL_CSECT(); DPF (2, "DsGetCaps: Leave Function"); return hr; } //--------------------------------------------------------------------------; // // IDSHWGetCaps // // Description: Direct Sound Object member to determine hardware // capabilities. // // Arguments: pids pointer to Direct Sound Object // pDSCaps pointer to DS Capabilities structures // // Return (HRESULT): // DS_OK Success // DSERR_UNINITIALIZED if object has not be initialized // DSERR_INVALIDPARAM a parameter could not be validated // DSERR_GENERIC any error getting Capabilities // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWGetCaps ( LPDIRECTSOUND pids, LPDSCAPS pDSCaps ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; HRESULT hr; // Validation Code if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::GetCaps - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if object has been initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::GetCaps - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } if( !VALID_DSCAPS_PTR(pDSCaps) ) { RPF("IDirectSound::GetCaps - Invalid DSCAPS structure or dwSize member."); return DSERR_INVALIDPARAM; } hr = DsGetCaps (pdse->pds, pDSCaps); return hr; } //--------------------------------------------------------------------------; // // DSDeactivateApp // // In: hWnd Handle of application to deactivate // bSticky DSDEACTIVATEAPPF_NONSTICKY will deactivate only // non-sticky buffers // DSDEACTIVATEAPPF_ALL will deactivate all buffers //--------------------------------------------------------------------------; void DSDeactivateApp(DWORD tid, enum DSDeactivateAppF bSticky) { LPDSOUNDEXTERNAL pdse; LPDSBUFFEREXTERNAL pdsbe; DPF(0,"###### Deactivate tid %X ######", tid); for (pdse = gpdsinfo->pDSoundExternalObj; NULL != pdse; pdse = pdse->pNext) { if (tid == pdse->tidSound) { for (pdsbe = pdse->pdsbe; NULL != pdsbe; pdsbe = pdsbe->pNext) { if ((DSDEACTIVATEAPPF_ALL == bSticky) || (FALSE == (DSBCAPS_STICKYFOCUS & pdsbe->pdsb->fdwBufferDesc))) { DSBufferDeactivate(pdsbe); } } if (!(pdse->pds->fdwInternal & DS_INTERNALF_WAVEEMULATED)) { mxSignalRemix(pdse->pds, 0); } } } } //--------------------------------------------------------------------------; // // DSActivateApp // //--------------------------------------------------------------------------; void DSActivateApp(DWORD tid) { LPDSOUND pds; LPDSBUFFER pdsb; LPDSOUNDEXTERNAL pdse; LPDSBUFFEREXTERNAL pdsbe; DWORD dwActivePrio; BOOL fWritePrimary; // // Find the highest priority setting of all pdse under this hwnd // dwActivePrio = DSSCL_NORMAL; for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) { if (tid == pdse->tidSound && pdse->dwPriority > dwActivePrio) { dwActivePrio = pdse->dwPriority; } } DPF(0,"##### Activate tid %X Active Prio %X #####", tid, dwActivePrio); fWritePrimary = (dwActivePrio >= DSSCL_WRITEPRIMARY); // // For all of the app's ds objects, set the format of the internal // primary buffer to the app's format. Also play or stop the internal // primary buffer as appropriate for the app we are activating. // // If we're WRITEPRIMARY, mark everyone else's external buffers as // lost // for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) { DPF(2, "Activating pdse %08Xh", pdse); if (tid == pdse->tidSound) { ASSERT(NULL != pdse->pds->pdsbPrimary); if (NULL == pdse->pwfxApp) { ASSERT(0 != pdse->pds->wfxDefault.wFormatTag); IDsbSetFormatI(pdse->pds->pdsbPrimary, &pdse->pds->wfxDefault, 0); } else { IDsbSetFormatI(pdse->pds->pdsbPrimary, pdse->pwfxApp, 0); } // // If we're not WRITEPRIMARY, then Play/Stop the primary per the // the count of playing buffers // // We must be sure to call Play/Stop on the internal primary dsb's // contained dsbe object so that Play/Stop doesn't operate on the // app's primary dsbe object. // for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) { pdsb = pdsbe->pdsb; pds = pdsb->pds; if (pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) { ASSERT(pdsb == pds->pdsbPrimary); // If the external primary has been played, then cPlayPrimary must be >0. ASSERT( !( (pdsbe->fdwDsbeI&DSBE_INTERNALF_PLAYING) && (0==pds->cPlayPrimary) ) ); if( (!fWritePrimary) && ( (pds->cPlayPrimary > 0) || (pds->dwBuffersPlaying > 0) ) ) { DPF(3, "DSActivateApp: playing primary dsb %08Xh, pdsb"); IDirectSoundBuffer_Play((LPDIRECTSOUNDBUFFER)pds->pdsbePrimary, 0, 0, DSBPLAY_LOOPING); } else { DPF(3, "DSActivateApp: stopping primary dsb %08Xh, pdsb"); IDsbStopI(pdsb, FALSE); } } } } else if (fWritePrimary) { for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) { // // Stop all playing buffers // if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_STOP) ) { IDsbStopI(pdsbe->pdsb, FALSE); } // // Lose all secondary buffers. // if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) ) { pdsbe->fdwDsbeI |= DSBE_INTERNALF_LOST; } // // WRITEPRIMARY primaries should already be lost. // ASSERT( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY ) || ( pdsbe->dwPriority < DSSCL_WRITEPRIMARY ) || ( pdsbe->fdwDsbeI & DSBE_INTERNALF_LOST ) ); } } } // // For every pdse that are in focus, we activate all its pdsbe. // We also signal the mixer to remix for the pdse's pds. // for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) { if (tid == pdse->tidSound) { for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) { DSBufferActivate(pdsbe); } if (!(pdse->pds->fdwInternal & DS_INTERNALF_WAVEEMULATED)) { mxSignalRemix(pdse->pds, 0); } } } } //--------------------------------------------------------------------------; // // void apmResume // // Description: // // Arguments: // none // // Return (void): // // History: // 09/11/95 FrankYe Created // //--------------------------------------------------------------------------; void apmResume(void) { LPDSOUND pds; ENTER_DLL_CSECT(); if (gpdsinfo->fApmSuspended) { gpdsinfo->fApmSuspended = FALSE; for (pds = gpdsinfo->pDSoundObj; pds; pds = pds->pNext) { IDsbSetFormatI( pds->pdsbPrimary, &pds->wfxDefault, IDSBSETFORMATIF_ALWAYS ); } } /* Update Focus and Stuck handles */ ActivateFocusWindow(); LEAVE_DLL_CSECT(); } //--------------------------------------------------------------------------; // // void apmSuspend // // Description: // // Arguments: // none // // Return (void): // // History: // 09/11/95 FrankYe Created // //--------------------------------------------------------------------------; void apmSuspend(void) { LPDSOUNDEXTERNAL pdse; LPDSBUFFEREXTERNAL pdsbe; ENTER_DLL_CSECT(); DSDeactivateApp(gpdsinfo->tidSoundFocus, DSDEACTIVATEAPPF_ALL); for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) { for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) { if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) ) { // // Stop all playing buffers // if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_STOP) ) { IDsbStopI(pdsbe->pdsb, FALSE); } // // Lose all secondary buffers. // pdsbe->fdwDsbeI |= DSBE_INTERNALF_LOST; } } for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) { if (pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) { if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_STOP) ) { IDsbStopI(pdsbe->pdsb, FALSE); } // // WRITEPRIMARY primaries should already be lost. // ASSERT( ( pdsbe->dwPriority < DSSCL_WRITEPRIMARY ) || ( pdsbe->fdwDsbeI & DSBE_INTERNALF_LOST ) ); } } } gpdsinfo->fApmSuspended = TRUE; LEAVE_DLL_CSECT(); } //--------------------------------------------------------------------------; // // SubclassWndProc // // We use APM notifications to help use get the native DS drivers // into a state proper for suspending. If we are compiling for // emulation mode only, then we don't need to catch these APM // notifications. // //--------------------------------------------------------------------------; #ifndef DSBLD_EMULONLY LRESULT CALLBACK SubclassWndProc ( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam ) { DPF(9,"Sublcass Window %X Message %X wParam %X, lParam %X ", (DWORD)hWnd, (DWORD)uMessage, (DWORD)wParam, (DWORD)lParam ); switch(uMessage) { case WM_POWERBROADCAST: switch (wParam) { case PBT_APMSUSPEND: apmSuspend(); return TRUE; case PBT_APMRESUMESUSPEND: apmResume(); return TRUE; } } return 0; } // SubclassWndProc() #endif //--------------------------------------------------------------------------; // // IDSHWSetCooperativeLevel // // Description: // // Arguments: // // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWSetCooperativeLevel ( LPDIRECTSOUND pids, HWND hwnd, DWORD dwPriority ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; LPDSBUFFEREXTERNAL pdsbeSearch; DWORD dwProcessIdCaller; DWORD tid; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::SetCooperativeLevel - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::SetCooperativeLevel - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } if( !VALID_HWND(hwnd) ) { RPF("IDirectSound::SetCooperativeLevel - Invalid hwnd"); return DSERR_INVALIDPARAM; } if((dwPriority < DSSCL_FIRST) || (dwPriority > DSSCL_LAST)) { RPF("IDirectSound::SetCooperativeLevel - Invalid DSSCL_* passed to SetCooperativeLevel"); return DSERR_INVALIDPARAM; } // // Due to bugs in the wave emulation code, we don't allow writeprimary // level on unsupported cards. This should be fixed. BUGBUG REMIND // if( ( pds->fdwInternal & DS_INTERNALF_WAVEEMULATED ) && ( dwPriority >= DSSCL_WRITEPRIMARY ) ) { RPF("IDirectSound::SetCooperativeLevel - DSSCL_WRITEPRIMARY level not available on emulated drivers."); return DSERR_INVALIDCALL; } /* set the actual window handle that sound */ /* focus is tracked on to be the top unowned */ /* window. */ hwnd = GetTopUnownedWindow (hwnd); tid = GetWindowThreadProcessId(hwnd, NULL); DPF(3,"=== Enter SetCooperativeLevel ==="); ENTER_DLL_CSECT(); dwProcessIdCaller = GetCurrentProcessId(); // We use DSoundHelp to give us APM window notifications. If we are // building only for emulation mode then we don't need APM notifications // since APM will be handled entirely by the WAVE drivers that we are // using for emulation. #ifndef DSBLD_EMULONLY // The first time this is called, register the window if( pdse->hwndCooperative == 0 ) { DSoundHelp( hwnd, SubclassWndProc, dwProcessIdCaller ); } else { // If we are given a new window, then release old window and add new if( pdse->hwndCooperative != hwnd ) { DSoundHelp( NULL, SubclassWndProc, dwProcessIdCaller ); DSoundHelp( hwnd, SubclassWndProc, dwProcessIdCaller ); } } #endif pdse->tidSound = tid; pdse->hwndCooperative = hwnd; pdse->dwPriority = dwPriority; // Update all buffers under this object to reflect the new priority level pdsbeSearch = pdse->pdsbe; while( pdsbeSearch != NULL ) { pdsbeSearch->dwPriority = dwPriority; pdsbeSearch = pdsbeSearch->pNext; } DseUpdateActivationState(pdse); LEAVE_DLL_CSECT(); DPF(3,"=== Exit SetCooperativeLevel ==="); return DS_OK; } //--------------------------------------------------------------------------; // // IDSHWCompact // // Description: // // Arguments: // // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWCompact ( LPDIRECTSOUND pids ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::Compact - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::Compact - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } if (DSSCL_NORMAL == pdse->dwPriority) { RPF("IDirectSound::Compact - Compact with priority DSSCL_NORMAL"); return DSERR_PRIOLEVELNEEDED; } return DS_OK; } //--------------------------------------------------------------------------; // // IDSHWGetSpeakerConfig // // Description: // // Arguments: // // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWGetSpeakerConfig ( LPDIRECTSOUND pids, LPDWORD pdwConfig ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::GetSpeakerConfig - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::GetSpeakerConfig - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } if( !VALID_DWORD_PTR(pdwConfig)) { RPF("IDirectSound::GetSpeakerConfig - Invalid pointer passed to GetSpeakerConfig()"); return DSERR_INVALIDPARAM; } ENTER_DLL_CSECT(); *pdwConfig = pdse->dwSpeakerConfig; LEAVE_DLL_CSECT(); return DS_OK; } //--------------------------------------------------------------------------; // // IDSHWSetSpeakerConfig // // Description: // // Arguments: // // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWSetSpeakerConfig ( LPDIRECTSOUND pids, DWORD dwConfig ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::SetSpeakerConfig - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { RPF("IDirectSound::SetSpeakerConfig - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } if(( dwConfig < DSSPEAKER_FIRST ) || ( dwConfig > DSSPEAKER_LAST )) { RPF("IDirectSound::SetSpeakerConfig - Invalid Config value"); return DSERR_INVALIDPARAM; } ENTER_DLL_CSECT(); // We're not doing a darn thing with this other than saving away the // value until we get 3D sound... pdse->dwSpeakerConfig = dwConfig; LEAVE_DLL_CSECT(); return DS_OK; } //--------------------------------------------------------------------------; // // IDSHWInitialize // // Description: // // Arguments: // // Return (): // // History: // 02/11/96 angusm Added init code from DirectSoundCreate // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWInitialize ( LPDIRECTSOUND pids, GUID FAR *lpGUID ) { LPDSOUNDEXTERNAL pdse; GUID guid, guidLast; DSDRIVERDESC dsDrvDesc; UINT cWaves; UINT uPreferredWaveId; UINT uWaveId; BOOL fPreferredOnly; HRESULT hr; HRESULT hrClient; if( !VALID_DSOUNDE_PTR(pids) ) { RPF("IDirectSound::Initialize - Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_INITIALIZED == pdse->fInitialized) { return DSERR_ALREADYINITIALIZED; } cWaves = waveOutGetNumDevs(); cWaves = min(cWaves, LIMIT_WAVE_DEVICES-1); if (0 == cWaves) return DSERR_NODRIVER; if (!wavGetPreferredId(&uPreferredWaveId, &fPreferredOnly)) { return DSERR_NODRIVER; } ENTER_DLL_CSECT(); if (NULL == lpGUID) { hrClient = DSERR_NODRIVER; #if defined(RDEBUG) || defined(DEBUG) if (!gpdsinfo->fEnumOnlyWaveDevs) #endif { hr = wavGetDrvGuidFromId(uPreferredWaveId, &guid); if (DS_OK == hr) { hr = hrClient = DseInitializeFromGuid(pdse, &guid); } if ((DS_OK != hr) && !fPreferredOnly) { ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc)); hr = vxdDrvGetNextDriverDesc(NULL, &guid, &dsDrvDesc); while (DS_OK == hr) { if ((DS_OK == wavGetIdFromDrvGuid(&guid, &uWaveId)) && wavIsMappable(uWaveId)) { hr = hrClient = DseInitializeFromGuid(pdse, &guid); if (DS_OK == hr) break; } guidLast = guid; ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc)); hr = vxdDrvGetNextDriverDesc(&guidLast, &guid, &dsDrvDesc); } } } if ((DS_OK != hrClient) && (uPreferredWaveId < cWaves)) { guid = gpdsinfo->aguidWave[uPreferredWaveId]; hr = hrClient = DseInitializeFromGuid(pdse, &guid); } if ((DS_OK != hrClient) && !fPreferredOnly) { uWaveId = 0; while (uWaveId < cWaves) { guid = gpdsinfo->aguidWave[uWaveId]; if (wavIsMappable(uWaveId)) { hr = hrClient = DseInitializeFromGuid(pdse, &guid); if (DS_OK == hr) break; } uWaveId++; } } } else { // NULL != lpGUID hrClient = DseInitializeFromGuid(pdse, lpGUID); } /* Check to see if this is the first buffer */ /* created, and start Focus Thread. */ if ((DS_OK == hrClient) && (1 == cSoundObjects())) { if (!CreateFocusThread()) { hrClient = DSERR_GENERIC; DseTerminate(pdse); } } if (DS_OK == hrClient) { pdse->fInitialized = IDSHWINITIALIZEF_INITIALIZED; } LEAVE_DLL_CSECT(); return hrClient; } // IDSHWInitialize() // This code is disabled for now... #ifdef ENABLE_EXCLUSIVEMODE_STUFF //--------------------------------------------------------------------------; // // IDSHWGetExclusiveModeOwner // // Description: // // Arguments: // // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWGetExclusiveModeOwner ( LPDIRECTSOUND pids, LPHANDLE phProcess ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; if( !VALID_DSOUNDE_PTR(pids) ) { DPF(0,"Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { DPF(0,"Invalid Object or ref count"); return DSERR_INVALIDPARAM; } ENTER_DLL_CSECT(); *phProcess = pds->hExclusiveOwner; LEAVE_DLL_CSECT(); return DS_OK; } //--------------------------------------------------------------------------; // // IDSHWSetExclusiveModeOwner // // Description: // // Arguments: // // Return (HRESULT): // DSERR_UNINITIALIZED if object has not be initialized // // History: // 02/11/96 angusm Added check for initialization // //--------------------------------------------------------------------------; HRESULT FAR PASCAL IDSHWSetExclusiveModeOwner ( LPDIRECTSOUND pids, DWORD fdwFlags ) { LPDSOUND pds; LPDSOUNDEXTERNAL pdse; if( !VALID_DSOUNDE_PTR(pids) ) { DPF(0,"Invalid Object or ref count"); return DSERR_INVALIDPARAM; } pdse = (LPDSOUNDEXTERNAL)pids; /* Check to see if the object is initialized */ if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) { RPF("Direct Sound Object is uninitialized."); return DSERR_UNINITIALIZED; } pds = pdse->pds; if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) { DPF(0,"Invalid Object or ref count"); return DSERR_INVALIDPARAM; } ENTER_DLL_CSECT(); if( (pds->hExclusiveOwner != NULL) && (pds->hExclusiveOwner != (HANDLE)GetCurrentProcessId()) ) { DPF(0,"Exclusive mode already set"); LEAVE_DLL_CSECT(); return DSERR_GENERIC; } if( fdwFlags & DS_EXCLUSIVEF_NONEXCLUSIVE ) { pds->fdwExclusive = 0; pds->hExclusiveOwner = 0; } else { pds->fdwExclusive = fdwFlags; pds->hExclusiveOwner = (HANDLE)GetCurrentProcessId(); } LEAVE_DLL_CSECT(); return DSERR_OK; } #endif void FNGLOBAL DSHWCreateTable ( LPDSOUNDCALLBACKS lpVtbl ) { lpVtbl->QueryInterface = IDSHWQueryInterface; lpVtbl->AddRef = IDSHWAddRef; lpVtbl->Release = IDSHWRelease; lpVtbl->CreateSoundBuffer = IDSHWCreateSoundBuffer; lpVtbl->GetCaps = IDSHWGetCaps; lpVtbl->DuplicateSoundBuffer = IDSHWDuplicateSoundBuffer; lpVtbl->SetCooperativeLevel = IDSHWSetCooperativeLevel; lpVtbl->Compact = IDSHWCompact; lpVtbl->GetSpeakerConfig = IDSHWGetSpeakerConfig; lpVtbl->SetSpeakerConfig = IDSHWSetSpeakerConfig; lpVtbl->Initialize = IDSHWInitialize; } // DSHWCreateTable() /* GetTopUnownedWindow * This function finds the top-most unowned window ancesting for the * given window handle. * * IN: a valid Window handle * OUT: the top-most unowned window ancesting from hWindow * SIDE EFFECTS: none * NOTES: This function must be called in a Dll Critical Section * * REVISION HISTORY: * 11/27/95 angusm Initial Version * 12/06/95 angusm Removed supurpholous call to GetWindow( , GW_OWNER) * 12/10/95 angusm Code style cleanup */ HWND GetTopUnownedWindow (HWND hWindow) { HWND hTempWindow = hWindow; while (NULL != hTempWindow) { hWindow = hTempWindow; ASSERT (hWindow); hTempWindow = GetParent (hWindow); /* This call will return the owner, if on exists, for top level windows. */ } return hWindow; } /* FocusTracker * This function is a thread that runs in the process space of DDHelp, * and polls to see which window is the top unowned window. If the window * has changed the sound focus changes. * * IN: the number of miliseconds to wait between polls * OUT: none * SIDE EFFECTS: The sound focus will change * NOTES: 1. The hwndStuckSoundFocus, and validity of the current Focus handle may * change outside of this thread. (In apmResume and SetCooperativeL. * * REVISION HISTORY: * 11/27/95 angusm Initial Version * 12/10/95 angusm added FocusLock creation * 12/10/95 angusm Code style cleanup * 03/20/96 angusm Added Sticky focus logic */ DWORD WINAPI FocusTracker (LPVOID lpvPollInterval) { HWND hNewWindowFocus; BOOL fIsNewWindowValid; BOOL fIsNewWindowMinimized; HWND hOldWindowFocus = NULL; DWORD tidOldWindowFocus = 0; BOOL fIsOldWindowValid = FALSE; BOOL fIsOldWindowMinimized = FALSE; HANDLE hWaitEvent; HANDLE hFocusLock; HANDLE hStartupEvent; DWORD nRetVal; WINDOWPLACEMENT wndplPlacement; DWORD tid; DPF (2, "FocusTracker: Enter Function (lpvPollInterval = %x)", lpvPollInterval); /* Create "Wait" Event */ hWaitEvent = CreateEvent (NULL, /* No security attribs */ TRUE, /* manual-reset */ FALSE, /* nonsignaled */ WAKEFOCUSTHREAD); /* event name */ if (NULL == hWaitEvent) { /* On Error ... */ DPF(0, "FocusTracker: Could not create DSWakeFocusThread " "event; exiting" ); ExitThread (0); } /* Grab Focus Lock */ if (FALSE == CreateFocusLock (&hFocusLock)) { DPF(1, "FocusTracker: Could not create Focus Lock" ); } /* Open Focus Thread startup startup verification event */ hStartupEvent = OpenEvent (EVENT_MODIFY_STATE, /* only allowed to pulse */ FALSE, /* non inheritable */ FOCUSSTARTUPEVENT); if (NULL == hStartupEvent) { /* On Error ... */ DPF(0, "FocusTracker: Could not open Startup Event; exiting" ); if (FALSE == DestroyFocusLock (hFocusLock)) { DPF (1, "FocusTracker: Could not destroy focus lock."); } if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0); ExitThread (0); } /* Handle Cleanup */ if (FALSE == SetEvent (hStartupEvent)) ASSERT (0); if (FALSE == CloseHandle (hStartupEvent)) ASSERT (0); wndplPlacement.length = sizeof(WINDOWPLACEMENT); while (1) /* Focus Tracking Loop */ { DWORD dwWaitResult; hNewWindowFocus = GetTopUnownedWindow (GetForegroundWindow()); if (hOldWindowFocus != hNewWindowFocus) { DWORD tidNewFocus; DPF(3, "FocusTracker: New Focus = %x Old Focus = %x", hNewWindowFocus, hOldWindowFocus); tidNewFocus = hNewWindowFocus ? GetWindowThreadProcessId(hNewWindowFocus, NULL) : 0; fIsNewWindowValid = IsValidDSApp(tidNewFocus); nRetVal = ENTER_DLL_CSECT_OR_EVENT (hWaitEvent); if (WAIT_OBJECT_0 == nRetVal) { /* If Wait Event is signaled */ DPF (3, "FocusTracker: breaking on Wait Event"); break; } /* Else we have taken the DLL_CSECT */ if (hNewWindowFocus) { GetWindowPlacement (hNewWindowFocus, &wndplPlacement); fIsNewWindowMinimized = (SW_SHOWMINIMIZED == wndplPlacement.showCmd); } else { fIsNewWindowMinimized = FALSE; } if (fIsNewWindowValid && !fIsNewWindowMinimized) /* New window uses Direct Sound and is not minimized */ { DPF (3, "FocusTracker: switching to DS app" " gpdsinfo->tidStuckFocus = %x, " "tidNewFocus = %x", gpdsinfo->tidStuckFocus, tidNewFocus); // If focus is switching back to the hwnd that was stuck // then we don't need to deactivate the stuck hwnd. if (gpdsinfo->tidStuckFocus != tidNewFocus) { DSDeactivateApp (gpdsinfo->tidStuckFocus, DSDEACTIVATEAPPF_ALL); gpdsinfo->tidStuckFocus = tidNewFocus; } DSActivateApp (tidNewFocus); gpdsinfo->tidSoundFocus = tidNewFocus; gpdsinfo->hwndSoundFocus = hNewWindowFocus; } else /* New window is not associated with a Direct Sound object, or the application is minimized */ { DPF (3, "FocusTracker: switching away from DS app" " hOldWindowFocus = %x", hOldWindowFocus); fIsOldWindowValid = IsValidDSApp (tidOldWindowFocus); if (fIsOldWindowValid && gpdsinfo->hwndSoundFocus) { DSDeactivateApp (gpdsinfo->tidSoundFocus, DSDEACTIVATEAPPF_NONSTICKY); } if (fIsNewWindowMinimized && fIsNewWindowValid) /* there is a minimized ds app */ { gpdsinfo->tidSoundFocus = 0; gpdsinfo->hwndSoundFocus = NULL; } else { gpdsinfo->tidSoundFocus = tidNewFocus; gpdsinfo->hwndSoundFocus = hNewWindowFocus; } } hOldWindowFocus = hNewWindowFocus; tidOldWindowFocus = tidNewFocus; fIsOldWindowValid = fIsNewWindowValid; fIsOldWindowMinimized = fIsNewWindowMinimized; LEAVE_DLL_CSECT(); } else if (fIsOldWindowValid && fIsOldWindowMinimized) { DPF (3, "FocusTracker: switching DS from minimization" " hOldWindowFocus = %x", hOldWindowFocus); ASSERT (hOldWindowFocus); GetWindowPlacement (hOldWindowFocus, &wndplPlacement); fIsNewWindowMinimized = (SW_SHOWMINIMIZED == wndplPlacement.showCmd); /* Old window is a DS app but has been maximized */ if (!fIsNewWindowMinimized) { nRetVal = ENTER_DLL_CSECT_OR_EVENT (hWaitEvent); if (WAIT_OBJECT_0 == nRetVal) { /* If Wait Event is signaled */ DPF (3, "FocusTracker: breaking on Wait Event"); break; } /* Else we have taken the DLL_CSECT */ tid = GetWindowThreadProcessId(hOldWindowFocus, NULL); if (gpdsinfo->tidStuckFocus != tid) { DSDeactivateApp (gpdsinfo->tidStuckFocus, DSDEACTIVATEAPPF_ALL); gpdsinfo->tidStuckFocus = tid; } DSActivateApp(tid); gpdsinfo->tidSoundFocus = tid; gpdsinfo->hwndSoundFocus = hOldWindowFocus; fIsOldWindowMinimized = fIsNewWindowMinimized; LEAVE_DLL_CSECT(); } } dwWaitResult = WaitForSingleObjectEx(hWaitEvent, (DWORD)lpvPollInterval, FALSE); if (WAIT_OBJECT_0 == dwWaitResult) break; ASSERT(WAIT_TIMEOUT == dwWaitResult); } /* while (1) */ /* Free Focus Lock */ if (FALSE == DestroyFocusLock (hFocusLock)) { DPF (1, "Could not destroy focus lock."); } /* Cleanup Handles */ if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0); if (FALSE == CloseHandle (gpdsinfo->hFocusTracker)) ASSERT (0); DPF (2, "FocusTracker: Exit Function"); ExitThread (0); return 0; /* This should never be reached. */ } /* IsValidDSApp * This function walks the external Direct Sound structures to see if the * given tid is a valid direct sound thread. * * IN: any tid * OUT: TRUE if it is a Direct Sound thread, FALSE otherwise * SIDE EFFECTS: none * NOTES: This function must be called in a Dll Critical Section if you * need the returned value to be correct. Otherwise, the returned value * may be a false answer. * * REVISION HISTORY: * 11/27/95 angusm Initial Version * 12/06/95 angusm appended to NOTES: * 12/10/95 angusm Added FocusLock calls * 12/10/95 angusm Code style cleanup */ BOOL IsValidDSApp (DWORD dwTid) { LPDSOUNDEXTERNAL pdse = gpdsinfo->pDSoundExternalObj; BOOL fReturnValue = FALSE; HANDLE hFocusLock; if (FALSE == GetFocusLock (&hFocusLock)) { DPF(2, "Dsound: IsValidDSApp: Error getting Focus Lock."); return FALSE; } while (NULL != pdse) { if (dwTid == (pdse->tidSound)) { ASSERT (0 != pdse->tidSound); fReturnValue = TRUE; break; } pdse = pdse->pNext; } if (FALSE == ReleaseFocusLock(hFocusLock)) { DPF (2, "Dsound: IsValidDSApp: Could not release Focus Lock."); ASSERT (0); } return fReturnValue; } /* cSoundObjects * This function walks the external Direct Sound structures to count them. * * IN: none * OUT: The number of external structures if 0 or 1, or 2 for 2 or more * structures. * SIDE EFFECTS: none * NOTES: This function must be called in a Dll Critical Section * * REVISION HISTORY: * 12/1/95 angusm Initial Version * 12/10/95 angusm Code style cleanup */ int cSoundObjects () { LPDSOUNDEXTERNAL pdse; int cNumObjects = 0; pdse = gpdsinfo->pDSoundExternalObj; while ((NULL != pdse) && (2 >= cNumObjects)) { pdse = pdse->pNext; cNumObjects++; } return cNumObjects; } /* CreateFocusThread * This function creates the Focus Thread. * * IN: none * OUT: TRUE if Thread was created, or FALSE otherwise * SIDE EFFECTS: a Focus Thread is created, and hWakeFocusThread, and * hFocusThread handles are created. * NOTES: This function must be called in a Dll Critical Section * * REVISION HISTORY: * 12/1/95 angusm Initial Version * 12/10/95 angusm Code style cleanup */ BOOL CreateFocusThread () { HANDLE hFocusStartupEvent; DWORD dwResult; hFocusStartupEvent = CreateEvent (NULL, /* no security attribs */ FALSE, /* auto-reset */ FALSE, /* non signaled */ FOCUSSTARTUPEVENT); if (NULL == hFocusStartupEvent) { DPF (1, "Dsound: CreateFocusThread: Could not create " "Focus Startup Event"); return FALSE; } gpdsinfo->hFocusTracker = HelperCreateDSFocusThread ((LPTHREAD_START_ROUTINE) FocusTracker, /* thread function */ (LPVOID) POLL_INTERVAL, /* parameter */ 0, /* normal creation flags */ NULL); /* unwanted threadid */ if (NULL == gpdsinfo->hFocusTracker) { DPF(1, "Could not start Focus Tracker thread in helper; exiting" ); if (FALSE == CloseHandle (hFocusStartupEvent)) ASSERT (0); return FALSE; } dwResult = WaitForSingleObjectEx(hFocusStartupEvent, INFINITE, FALSE); ASSERT(WAIT_OBJECT_0 == dwResult); if (FALSE == CloseHandle (hFocusStartupEvent)) ASSERT (0); return TRUE; } /* EndFocusThread * This function will send an event to the Focus Thread running in DDHelp, * and wait to see if it terminates. If it does not, the thread will be * terminated. * * IN: none * OUT: none * SIDE EFFECTS: 1. Focus Thread will be killed. * 2. hWakeFocusThread handle will be closed. * 3. hFocusTracker hadnle will be closed. * NOTES: This function must be called in a Dll Critical Section * * REVISION HISTORY: * 11/30/95 angusm Initial Version * 12/10/95 angusm Code style cleanup */ void EndFocusThread () { HANDLE hWaitEvent, hHelper, hOurFocusTracker; DWORD dwResult; hWaitEvent = CreateEvent (NULL, FALSE, /* auto-reset */ TRUE, /* if event object does not already exists, create one signaled */ WAKEFOCUSTHREAD); /* event name */ if (NULL == hWaitEvent) { DPF (1, "Dsound: EndFocusThread: Could not create " "wait event"); return; } hHelper = OpenProcess (PROCESS_DUP_HANDLE, FALSE, /* no inheritance */ gpdsinfo->pidHelper); /* Helper PID */ if (NULL == hHelper) { DPF (1, "Dsound: EndFocusThread: Could not open process"); if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0); return; } if (FALSE == DuplicateHandle (hHelper, /* source process */ gpdsinfo->hFocusTracker, /* source handle */ GetCurrentProcess(), /* our process */ &hOurFocusTracker, /* our new handle */ SYNCHRONIZE | THREAD_TERMINATE, /* access flags */ FALSE, /* no inheritance */ 0)) /* no options */ { /* On Error ... */ DPF (1, "Dsound: EndFocusThread: Could not duplicate handle"); if (FALSE == CloseHandle (hHelper)) ASSERT (0); if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0); return; } if (FALSE == SetEvent (hWaitEvent)) ASSERT (0); if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0); dwResult = WaitForSingleObjectEx(hOurFocusTracker, INFINITE, FALSE); ASSERT(WAIT_OBJECT_0 == dwResult); if (FALSE == CloseHandle (hHelper)) ASSERT (0); if (FALSE == CloseHandle (hOurFocusTracker)) ASSERT (0); return; } /* * DseUpdateActivationState * * Given the current tidSoundFocus and tidSticky, this function activates * or deactivates all dsbe for a dse */ __inline void DseUpdateActivationState(LPDSOUNDEXTERNAL pdse) { LPDSBUFFEREXTERNAL pdsbe; WINDOWPLACEMENT wndplPlacement; BOOL fIsMinimized; wndplPlacement.length = sizeof(WINDOWPLACEMENT); ASSERT (pdse->hwndCooperative); if (NULL != gpdsinfo->hwndSoundFocus) { GetWindowPlacement (gpdsinfo->hwndSoundFocus, &wndplPlacement); fIsMinimized = (SW_SHOWMINIMIZED == wndplPlacement.showCmd); } else fIsMinimized = FALSE; if ((gpdsinfo->tidSoundFocus == pdse->tidSound) && !fIsMinimized) { if (gpdsinfo->tidStuckFocus != pdse->tidSound) { DSDeactivateApp (gpdsinfo->tidStuckFocus, DSDEACTIVATEAPPF_ALL); } gpdsinfo->tidStuckFocus = gpdsinfo->tidSoundFocus; } for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) { if ((gpdsinfo->tidSoundFocus != pdsbe->pdse->tidSound) && !((gpdsinfo->tidStuckFocus == pdsbe->pdse->tidSound) && (DSBCAPS_STICKYFOCUS & pdsbe->pdsb->fdwBufferDesc))) { DSBufferDeactivate (pdsbe); } else { DSBufferActivate (pdsbe); } } return; } /* DsbeDeactivateIfNecessary * * This function deactivates a buffer only if the buffer is current in focus. */ __inline void DsbeDeactivateIfNecessary (LPDSBUFFEREXTERNAL pdsbe) { DPF (2, "DsbeDeactivateIfNecessary: entered pdsbe = %x", pdsbe); if ((gpdsinfo->tidSoundFocus != pdsbe->pdse->tidSound) && !((gpdsinfo->tidStuckFocus == pdsbe->pdse->tidSound) && (DSBCAPS_STICKYFOCUS & pdsbe->pdsb->fdwBufferDesc))) { DSBufferDeactivate (pdsbe); } return; } /* ActivateFocusWindow * * This function will set the gpdsinfo->hwndSticky handle properly. In addition * it will Deactivate the current sticky hwnd and activate the the in focus * hwnd. * * NOTE: This function must be called within a CSECT */ __inline void ActivateFocusWindow (void) { if (gpdsinfo->tidSoundFocus != gpdsinfo->tidStuckFocus) { DSDeactivateApp (gpdsinfo->tidStuckFocus, DSDEACTIVATEAPPF_ALL); gpdsinfo->tidStuckFocus = gpdsinfo->tidSoundFocus; } DSActivateApp (gpdsinfo->tidSoundFocus); }