📄 dsound_wrapper.c
字号:
/* * $Id: dsound_wrapper.c,v 1.3 2003/03/02 08:01:45 dmazzoni Exp $ * Simplified DirectSound interface. * * Author: Phil Burk & Robert Marsanyi * * PortAudio Portable Real-Time Audio Library * For more information see: http://www.softsynth.com/portaudio/ * DirectSound Implementation * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * Any person wishing to distribute modifications to the Software is * requested to send the modifications to the original developer so that * they can be incorporated into the canonical version. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */#include <stdio.h>#include <stdlib.h>#include <math.h>#define INITGUID // Needed to build IID_IDirectSoundNotify. See objbase.h for info.#include <objbase.h>#include <unknwn.h>#include "dsound_wrapper.h"#include "pa_trace.h"/************************************************************************************/void DSW_Term( DSoundWrapper *dsw ){ // Cleanup the sound buffers if (dsw->dsw_OutputBuffer) { IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer ); dsw->dsw_OutputBuffer = NULL; }#if SUPPORT_AUDIO_CAPTURE if (dsw->dsw_InputBuffer) { IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer ); dsw->dsw_InputBuffer = NULL; } if (dsw->dsw_pDirectSoundCapture) { IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture ); dsw->dsw_pDirectSoundCapture = NULL; }#endif /* SUPPORT_AUDIO_CAPTURE */ if (dsw->dsw_pDirectSound) { IDirectSound_Release( dsw->dsw_pDirectSound ); dsw->dsw_pDirectSound = NULL; }}/************************************************************************************/HRESULT DSW_Init( DSoundWrapper *dsw ){ memset( dsw, 0, sizeof(DSoundWrapper) ); return 0;}/************************************************************************************/HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID ){ // Create the DS object HRESULT hr = DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL ); if( hr != DS_OK ) return hr; return hr;}/************************************************************************************/HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer ){ DWORD dwDataLen; DWORD playCursor; HRESULT result; LPDIRECTSOUNDBUFFER pPrimaryBuffer; HWND hWnd; HRESULT hr; WAVEFORMATEX wfFormat; DSBUFFERDESC primaryDesc; DSBUFFERDESC secondaryDesc; unsigned char* pDSBuffData; LARGE_INTEGER counterFrequency; dsw->dsw_OutputSize = bytesPerBuffer; dsw->dsw_OutputRunning = FALSE; dsw->dsw_OutputUnderflows = 0; dsw->dsw_FramesWritten = 0; dsw->dsw_BytesPerFrame = nChannels * sizeof(short); // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the // applications's window. Also if that window is closed before the Buffer is closed // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.) // So we will use GetDesktopWindow() which was suggested by Miller Puckette. // hWnd = GetForegroundWindow(); hWnd = GetDesktopWindow(); // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz. // Exclusize also prevents unexpected sounds from other apps during a performance. if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound, hWnd, DSSCL_EXCLUSIVE)) != DS_OK) { return hr; } // ----------------------------------------------------------------------- // Create primary buffer and set format just so we can specify our custom format. // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz. // Setup the primary buffer description ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC)); primaryDesc.dwSize = sizeof(DSBUFFERDESC); primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth primaryDesc.dwBufferBytes = 0; primaryDesc.lpwfxFormat = NULL; // Create the buffer if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result; // Define the buffer format wfFormat.wFormatTag = WAVE_FORMAT_PCM; wfFormat.nChannels = nChannels; wfFormat.nSamplesPerSec = nFrameRate; wfFormat.wBitsPerSample = 8 * sizeof(short); wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8; wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; wfFormat.cbSize = 0; /* No extended format info. */ // Set the primary buffer's format if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result; // ---------------------------------------------------------------------- // Setup the secondary buffer description ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC)); secondaryDesc.dwSize = sizeof(DSBUFFERDESC); secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; secondaryDesc.dwBufferBytes = bytesPerBuffer; secondaryDesc.lpwfxFormat = &wfFormat; // Create the secondary buffer if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result; // Lock the DS buffer if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData, &dwDataLen, NULL, 0, 0)) != DS_OK) return result; // Zero the DS buffer ZeroMemory(pDSBuffData, dwDataLen); // Unlock the DS buffer if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result; if( QueryPerformanceFrequency( &counterFrequency ) ) { int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short)); dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate; AddTraceMessage("dsw_CounterTicksPerBuffer = %d\n", dsw->dsw_CounterTicksPerBuffer.LowPart ); } else { dsw->dsw_CounterTicksPerBuffer.QuadPart = 0; } // Let DSound set the starting write position because if we set it to zero, it looks like the // buffer is full to begin with. This causes a long pause before sound starts when using large buffers. hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset ); if( hr != DS_OK ) { return hr; } dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerFrame; /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */ return DS_OK;}/************************************************************************************/HRESULT DSW_StartOutput( DSoundWrapper *dsw ){ HRESULT hr; QueryPerformanceCounter( &dsw->dsw_LastPlayTime ); dsw->dsw_LastPlayCursor = 0; dsw->dsw_FramesPlayed = 0; hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 ); if( hr != DS_OK ) { return hr; } // Start the buffer playback in a loop. if( dsw->dsw_OutputBuffer != NULL ) { hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING ); if( hr != DS_OK ) { return hr; } dsw->dsw_OutputRunning = TRUE; } return 0;}/************************************************************************************/HRESULT DSW_StopOutput( DSoundWrapper *dsw ){ // Stop the buffer playback if( dsw->dsw_OutputBuffer != NULL ) { dsw->dsw_OutputRunning = FALSE; return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); } else return 0;}/************************************************************************************/HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ){ HRESULT hr; DWORD playCursor; DWORD writeCursor; long numBytesEmpty; long playWriteGap; // Query to see how much room is in buffer. // Note: Even though writeCursor is not used, it must be passed to prevent DirectSound from dieing // under WinNT. The Microsoft documentation says we can pass NULL but apparently not. // Thanks to Max Rheiner for the fix. hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); if( hr != DS_OK ) { return hr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -