HX_RESULT CHXAudioDeviceDS::_DoOpen(const HXAudioFormat* pFormat) { HX_RESULT theErr = HXR_FAIL; if(!m_hwnd || !m_hSoundDll) return theErr; // close open resources InternalClose() ; /* Get the IDirectSound interface */ HXBOOL bUsingDS8 = TRUE; FPDIRECTSOUNDCREATE fpCreateDS = (FPDIRECTSOUNDCREATE) ::GetProcAddress(m_hSoundDll, TEXT("DirectSoundCreate8") ); if(!fpCreateDS) { bUsingDS8 = FALSE; fpCreateDS = (FPDIRECTSOUNDCREATE) ::GetProcAddress(m_hSoundDll, TEXT("DirectSoundCreate")); if(!fpCreateDS) return theErr; } theErr = fpCreateDS(NULL, &m_pDSDev, NULL); if (FAILED(theErr)) return theErr; /* set the cooperative level. Because we want control over the format of the primary buffer (16 bit, multichannel!), we need DSSCL_PRIORITY. */ m_pDSDev->SetCooperativeLevel(m_hwnd, DSSCL_PRIORITY ); /* set the format of the primary buffer. This will fail on WDM systems (because the kernel mixer termines the primary buffer format), but is important on non-WDM systems. This might change the m_WaveFormat structure from a WAVE_FORMAT_EXTENSIBLE to a WAVEFORMATEX. Ignore the result. */ SetPrimaryBufferFormat() ; /* Now open a secondary buffer. */ DSBUFFERDESC bufferDesc; ::memset(&bufferDesc, 0, sizeof(DSBUFFERDESC)); bufferDesc.dwSize = sizeof(DSBUFFERDESC); bufferDesc.lpwfxFormat = m_pWaveFormat; // Manipulate the buffer size so that is is an exact multiple of the block size. // This will ensure that our write positions on the buffer are the same in every loop. // We need to do this so that we have fixed playback notification positions marking the end each write block. m_nBlocksPerBuffer = (m_pWaveFormat->nAvgBytesPerSec*BUFFER_TIME)/pFormat->uMaxBlockSize; m_ulTotalBuffer = pFormat->uMaxBlockSize*m_nBlocksPerBuffer; m_ulLoopTime = (double)m_ulTotalBuffer / (double)m_pWaveFormat->nAvgBytesPerSec; bufferDesc.dwBufferBytes = m_ulTotalBuffer ; bufferDesc.dwFlags = DSBCAPS_CTRLVOLUME | // so we can control the volume DSBCAPS_GETCURRENTPOSITION2 | // finer position reports DSBCAPS_CTRLPOSITIONNOTIFY | // have them reported here DSBCAPS_GLOBALFOCUS | // take control! DSBCAPS_STICKYFOCUS | #ifdef HELIX_FEATURE_AUDIO_DEVICE_HOOKS (bUsingDS8 && m_pWaveFormat->nChannels <= 2 && !m_bOpaqueFormat ? DSBCAPS_CTRLFX : 0); #else 0; #endif /* Again, try with WAVE_FORMAT_EXTENSIBLE first, but only if multichannel. */ theErr = !DS_OK ; if (m_pWaveFormat->nChannels > 2 || m_bOpaqueFormat) { if(!m_bOpaqueFormat) m_pWaveFormat->wFormatTag = WAVE_FORMAT_EXTENSIBLE; theErr = m_pDSDev->CreateSoundBuffer(&bufferDesc, &m_pSecondaryBuffer, NULL); } if (theErr != DS_OK && !m_bOpaqueFormat) { /* and if that fails, try WAVEFORMATEX */ m_pWaveFormat->wFormatTag = WAVE_FORMAT_PCM; theErr = m_pDSDev->CreateSoundBuffer(&bufferDesc, &m_pSecondaryBuffer, NULL); } /* call it a day and count our blessings. */ switch (theErr) { case DS_OK: theErr = HXR_OK; break; case DSERR_OUTOFMEMORY: theErr = HXR_OUTOFMEMORY; break; default: theErr = HXR_FAIL; break; } if (SUCCEEDED(theErr) && m_pSecondaryBuffer) { m_eState = E_DEV_OPENED; KillThreadAndEvent(); SetWindowLong(m_hwnd, GWL_USERDATA, (LONG)this); // Create the event to be signalled on playback position notifications and the thread to wait for those events to be signalled. m_hDSNotifyEvent = CreateEvent(NULL, TRUE, FALSE, kDSWaitEvent); // now set the notification positions for direct sound playback. IDirectSoundNotify* pNotify = NULL; m_pSecondaryBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&pNotify); if(pNotify && m_hDSNotifyEvent) { DSBPOSITIONNOTIFY* aPositionNotify = new DSBPOSITIONNOTIFY[m_nBlocksPerBuffer]; if(aPositionNotify) { for(int i = 0; i < m_nBlocksPerBuffer; i++) { aPositionNotify[i].dwOffset = i * pFormat->uMaxBlockSize; aPositionNotify[i].hEventNotify = m_hDSNotifyEvent; } } pNotify->SetNotificationPositions(m_nBlocksPerBuffer, aPositionNotify); delete[] aPositionNotify; DWORD dwWaitThreadID(0); m_hWaitThread = CreateThread(NULL, 0, EventThreadProc, (LPVOID)this, 0, &dwWaitThreadID); SetDebuggerThreadName(dwWaitThreadID, "DirectSound Audio Device Thread"); SetThreadPriority( m_hWaitThread, THREAD_PRIORITY_HIGHEST ); } HX_RELEASE(pNotify); m_pSecondaryBuffer->SetVolume(DSBVOLUME_MAX); m_pSecondaryBuffer->SetCurrentPosition(0); // detect whether we are playing remotely(via Remote Desktop) // disable audio device hook if it is to avoid poor audio quality int bRemoteSession = GetSystemMetrics(SM_REMOTESESSION); if(!bRemoteSession && bUsingDS8 && m_pWaveFormat->nChannels <= 2 && !m_bOpaqueFormat) { LoadDirectSoundFilter(); } } m_ulCurrPlayTime = 0; m_ulCurrLoopTime = 0; m_ulLoops = 0; // Setup converter to convert from samples/sec to milliseconds m_TSConverter.SetBase(m_pWaveFormat->nSamplesPerSec, 1000); return theErr; }
//----------------------------------------------------------------------------- // Name: CSoundManager::CreateStreaming() // Desc: //----------------------------------------------------------------------------- HRESULT CSoundManager::CreateStreaming( CStreamingSound** ppStreamingSound, LPTSTR strWaveFileName, DWORD dwCreationFlags, GUID guid3DAlgorithm, DWORD dwNotifyCount, DWORD dwNotifySize, HANDLE hNotifyEvent ) { HRESULT hr; if( m_pDS == NULL ) return CO_E_NOTINITIALIZED; if( strWaveFileName == NULL || ppStreamingSound == NULL || hNotifyEvent == NULL ) return E_INVALIDARG; IDirectSoundBuffer* pDSBuffer = NULL; DWORD dwDSBufferSize = NULL; CWaveFile* pWaveFile = NULL; DSBPOSITIONNOTIFY* aPosNotify = NULL; IDirectSoundNotify* pDSNotify = NULL; pWaveFile = new CWaveFile(); if( pWaveFile == NULL ) return E_OUTOFMEMORY; pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); // Figure out how big the DirectSound buffer should be dwDSBufferSize = dwNotifySize * dwNotifyCount; // Set up the direct sound buffer. Request the NOTIFY flag, so // that we are notified as the sound buffer plays. Note, that using this flag // may limit the amount of hardware acceleration that can occur. DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = dwCreationFlags | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = pWaveFile->m_pwfx; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) ) { // If wave format isn't then it will return // either DSERR_BADFORMAT or E_INVALIDARG if( hr == DSERR_BADFORMAT || hr == E_INVALIDARG ) return DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr ); return DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr ); } // Create the notification events, so that we know when to fill // the buffer as the sound plays. if( FAILED( hr = pDSBuffer->QueryInterface( IID_IDirectSoundNotify, (VOID**)&pDSNotify ) ) ) { SAFE_DELETE_ARRAY( aPosNotify ); return DXTRACE_ERR( TEXT("QueryInterface"), hr ); } aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ]; if( aPosNotify == NULL ) return E_OUTOFMEMORY; for( DWORD i = 0; i < dwNotifyCount; i++ ) { aPosNotify[i].dwOffset = (dwNotifySize * i) + dwNotifySize - 1; aPosNotify[i].hEventNotify = hNotifyEvent; } // Tell DirectSound when to notify us. The notification will come in the from // of signaled events that are handled in WinMain() if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount, aPosNotify ) ) ) { SAFE_RELEASE( pDSNotify ); SAFE_DELETE_ARRAY( aPosNotify ); return DXTRACE_ERR( TEXT("SetNotificationPositions"), hr ); } SAFE_RELEASE( pDSNotify ); SAFE_DELETE_ARRAY( aPosNotify ); // Create the sound *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize ); return S_OK; }
/* nInterval is in SECONDS. The buffer we create is 3 times the interval, so we make three notifications that will be placed at every interval. pProc will be called at approximately the interval specified. */ HRESULT WaveCapture::Initialize(int nDevice, WAVEFORMATEX* pFormat, WAVECAPTUREPROC pProc, PVOID pUser, int nInterval) { DSCCAPS dsc; DSCBUFFERDESC1 dscbDesc; HRESULT hr; DSBPOSITIONNOTIFY NotifyPositions[3]; IDirectSoundNotify* pdsn; this->Uninitialize(); this->pProc = pProc; this->pUser = pUser; this->nInterval = nInterval; // Validation. if(pFormat->wFormatTag != WAVE_FORMAT_PCM) { return E_FAIL; } // Copy the wave format for our own purposes. memcpy(&this->Format, pFormat, sizeof(WAVEFORMATEX)); m_iDevice = nDevice; memset(&m_Device, 0, sizeof(GUID)); DirectSoundCaptureEnumerate(WaveCapture::DSEnumCallback, this); // Create DirectSoundCapture object. if(FAILED(hr = DirectSoundCaptureCreate(&m_Device, &this->pdsc, NULL))) { return E_FAIL; } /* Here is where we should check the capabilities of the sound card against the format that was passed in. */ dsc.dwSize = sizeof(DSCCAPS); if(FAILED(hr = this->pdsc->GetCaps(&dsc))) { SAFE_RELEASE(this->pdsc); return E_FAIL; } /* Create the capture buffer. */ dscbDesc.dwSize = sizeof(dscbDesc); dscbDesc.dwFlags = DSCBCAPS_WAVEMAPPED; dscbDesc.dwBufferBytes = (pFormat->nAvgBytesPerSec * nInterval) * 3; dscbDesc.dwReserved = 0; dscbDesc.lpwfxFormat = &this->Format; if(FAILED(hr = this->pdsc->CreateCaptureBuffer((DSCBUFFERDESC*)&dscbDesc, &this->pdsb, NULL))) { SAFE_RELEASE(this->pdsc); return E_FAIL; } /* Create the notifications */ this->hEvents[0] = CreateEvent(NULL, FALSE, FALSE, NULL); this->hEvents[1] = CreateEvent(NULL, FALSE, FALSE, NULL); this->hEvents[2] = CreateEvent(NULL, FALSE, FALSE, NULL); this->hEvents[3] = CreateEvent(NULL, FALSE, FALSE, NULL); NotifyPositions[0].dwOffset = 0; NotifyPositions[0].hEventNotify = this->hEvents[0]; NotifyPositions[1].dwOffset = (this->Format.nAvgBytesPerSec * nInterval); NotifyPositions[1].hEventNotify = this->hEvents[1]; NotifyPositions[2].dwOffset = (this->Format.nAvgBytesPerSec * nInterval) * 2; NotifyPositions[2].hEventNotify = this->hEvents[2]; if(FAILED(hr = this->pdsb->QueryInterface(IID_IDirectSoundNotify, (PVOID*)&pdsn))) { SAFE_RELEASE(this->pdsb); SAFE_RELEASE(this->pdsc); return E_FAIL; } if(FAILED(hr = pdsn->SetNotificationPositions(3, NotifyPositions))) { SAFE_RELEASE(pdsn); SAFE_RELEASE(this->pdsb); SAFE_RELEASE(this->pdsc); return E_FAIL; } SAFE_RELEASE(pdsn); /* Create our own buffer so when we pass data to pProc, the data is ALWAYS contiguous. */ this->nBufferSize = (pFormat->nAvgBytesPerSec * nInterval) * 3; this->pBuffer = (PBYTE)malloc(this->nBufferSize); /* Initialize our own information. */ this->dwLastPosition = 0; this->nPassesLeft = 1; /* Now create our recording thread so life can continue. */ UINT dwThreadId; this->hThread = (HANDLE)_beginthreadex( 0, 0, WaveCapture::RecordingThread, this, 0, &dwThreadId); return S_OK; }