📄 dsound_wrapper.c
字号:
/*
* $Id: dsound_wrapper.c,v 1.1 2006/06/27 10:15:03 sumomo 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 + -