📄 pa_win_ds.c
字号:
/*
* $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $
* Portable Audio I/O Library DirectSound implementation
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
*
* 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.
*/
/** @file
@todo implement paInputOverflow callback status flag
@todo implement paNeverDropInput.
@todo implement host api specific extension to set i/o buffer sizes in frames
@todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
@todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
@todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
a native portaudio error code. Standard DirectSound result codes are documented at msdn.
@todo implement IsFormatSupported
@todo check that CoInitialize() CoUninitialize() are always correctly
paired, even in error cases.
@todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
@todo make sure all buffers have been played before stopping the stream
when the stream callback returns paComplete
old TODOs from phil, need to work out if these have been done:
O- fix "patest_stop.c"
*/
#include <stdio.h>
#include <string.h> /* strlen() */
#include "pa_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "dsound_wrapper.h"
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "dsound.lib" )
#pragma comment( lib, "winmm.lib" )
#endif
#include <pj/unicode.h>
#define PRINT(x) /* { printf x; fflush(stdout); } */
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */
#define PA_USE_HIGH_LATENCY (0)
#if PA_USE_HIGH_LATENCY
#define PA_WIN_9X_LATENCY (500)
#define PA_WIN_NT_LATENCY (600)
#else
#define PA_WIN_9X_LATENCY (140)
#define PA_WIN_NT_LATENCY (280)
#endif
#define PA_WIN_WDM_LATENCY (120)
#define SECONDS_PER_MSEC (0.001)
#define MSEC_PER_SECOND (1000)
/* prototypes for functions declared in this file */
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus
}
#endif /* __cplusplus */
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate,
unsigned long framesPerBuffer,
PaStreamFlags streamFlags,
PaStreamCallback *streamCallback,
void *userData );
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
static PaError AbortStream( PaStream *stream );
static PaError IsStreamStopped( PaStream *s );
static PaError IsStreamActive( PaStream *stream );
static PaTime GetStreamTime( PaStream *stream );
static double GetStreamCpuLoad( PaStream* stream );
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
static signed long GetStreamReadAvailable( PaStream* stream );
static signed long GetStreamWriteAvailable( PaStream* stream );
/* FIXME: should convert hr to a string */
#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
/************************************************* DX Prototypes **********/
static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext );
/************************************************************************************/
/********************** Structures **************************************************/
/************************************************************************************/
/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
typedef struct PaWinDsDeviceInfo
{
GUID guid;
GUID *lpGUID;
double sampleRates[3];
} PaWinDsDeviceInfo;
typedef struct
{
PaUtilHostApiRepresentation inheritedHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations;
/* implementation specific data goes here */
PaWinDsDeviceInfo *winDsDeviceInfos;
} PaWinDsHostApiRepresentation;
/* PaWinDsStream - a stream data structure specifically for this implementation */
typedef struct PaWinDsStream
{
PaUtilStreamRepresentation streamRepresentation;
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
PaUtilBufferProcessor bufferProcessor;
/* DirectSound specific data. */
DSoundWrapper directSoundWrapper;
MMRESULT timerID;
BOOL ifInsideCallback; /* Test for reentrancy. */
int framesPerDSBuffer;
double framesWritten;
double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
PaStreamCallbackFlags callbackFlags;
/* FIXME - move all below to PaUtilStreamRepresentation */
volatile int isStarted;
volatile int isActive;
volatile int stopProcessing; /* stop thread once existing buffers have been returned */
volatile int abortProcessing; /* stop thread immediately */
} PaWinDsStream;
/************************************************************************************
** Duplicate the input string using the allocations allocator.
** A NULL string is converted to a zero length string.
** If memory cannot be allocated, NULL is returned.
**/
static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
{
char *result = 0;
if( src != NULL )
{
size_t len = strlen(src);
result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
if( result )
memcpy( (void *) result, src, len+1 );
}
else
{
result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
if( result )
result[0] = '\0';
}
return result;
}
/************************************************************************************
** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
** information during device enumeration.
*/
typedef struct DSDeviceNameAndGUID{
char *name; // allocated from parent's allocations, never deleted by this structure
GUID guid;
LPGUID lpGUID;
} DSDeviceNameAndGUID;
typedef struct DSDeviceNameAndGUIDVector{
PaUtilAllocationGroup *allocations;
PaError enumerationError;
int count;
int free;
DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
} DSDeviceNameAndGUIDVector;
static PaError InitializeDSDeviceNameAndGUIDVector(
DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
{
PaError result = paNoError;
guidVector->allocations = allocations;
guidVector->enumerationError = paNoError;
guidVector->count = 0;
guidVector->free = 8;
guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
if( guidVector->items == NULL )
result = paInsufficientMemory;
return result;
}
static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
{
PaError result = paNoError;
DSDeviceNameAndGUID *newItems;
int i;
/* double size of vector */
int size = guidVector->count + guidVector->free;
guidVector->free += size;
newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
if( newItems == NULL )
{
result = paInsufficientMemory;
}
else
{
for( i=0; i < guidVector->count; ++i )
{
newItems[i].name = guidVector->items[i].name;
if( guidVector->items[i].lpGUID == NULL )
{
newItems[i].lpGUID = NULL;
}
else
{
newItems[i].lpGUID = &newItems[i].guid;
memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
}
}
LocalFree( guidVector->items );
guidVector->items = newItems;
}
return result;
}
/*
it's safe to call DSDeviceNameAndGUIDVector multiple times
*/
static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
{
PaError result = paNoError;
if( guidVector->items != NULL )
{
if( LocalFree( guidVector->items ) != NULL )
result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
guidVector->items = NULL;
}
return result;
}
/************************************************************************************
** Collect preliminary device information during DirectSound enumeration
*/
static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext )
{
DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
PaError error;
PJ_DECL_ANSI_TEMP_BUF(atemp,128)
(void) lpszDrvName; /* unused variable */
if( namesAndGUIDs->free == 0 )
{
error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
if( error != paNoError )
{
namesAndGUIDs->enumerationError = error;
return FALSE;
}
}
/* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
if( lpGUID == NULL )
{
namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
}
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -