summaryrefslogtreecommitdiff
path: root/portaudio/src/hostapi/dsound/pa_win_ds.c
diff options
context:
space:
mode:
Diffstat (limited to 'portaudio/src/hostapi/dsound/pa_win_ds.c')
-rw-r--r--portaudio/src/hostapi/dsound/pa_win_ds.c3259
1 files changed, 0 insertions, 3259 deletions
diff --git a/portaudio/src/hostapi/dsound/pa_win_ds.c b/portaudio/src/hostapi/dsound/pa_win_ds.c
deleted file mode 100644
index 2ccb4f8..0000000
--- a/portaudio/src/hostapi/dsound/pa_win_ds.c
+++ /dev/null
@@ -1,3259 +0,0 @@
-/*
- * $Id$
- * Portable Audio I/O Library DirectSound implementation
- *
- * Authors: Phil Burk, Robert Marsanyi & Ross Bencina
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2007 Ross Bencina, 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.
- *
- * 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.
- */
-
-/*
- * The text above constitutes the entire PortAudio license; however,
- * the PortAudio community also makes the following non-binding requests:
- *
- * 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. It is also
- * requested that these non-binding requests be included along with the
- * license above.
- */
-
-/** @file
- @ingroup hostapi_src
-*/
-
-/* Until May 2011 PA/DS has used a multimedia timer to perform the callback.
- We're replacing this with a new implementation using a thread and a different timer mechanism.
- Defining PA_WIN_DS_USE_WMME_TIMER uses the old (pre-May 2011) behavior.
-*/
-//#define PA_WIN_DS_USE_WMME_TIMER
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h> /* strlen() */
-
-#define _WIN32_WINNT 0x0400 /* required to get waitable timer APIs */
-#include <initguid.h> /* make sure ds guids get defined */
-#include <windows.h>
-#include <objbase.h>
-
-
-/*
- Use the earliest version of DX required, no need to pollute the namespace
-*/
-#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
-#define DIRECTSOUND_VERSION 0x0800
-#else
-#define DIRECTSOUND_VERSION 0x0300
-#endif
-#include <dsound.h>
-#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
-#include <dsconf.h>
-#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
-#ifndef PA_WIN_DS_USE_WMME_TIMER
-#ifndef UNDER_CE
-#include <process.h>
-#endif
-#endif
-
-#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 "pa_debugprint.h"
-
-#include "pa_win_ds.h"
-#include "pa_win_ds_dynlink.h"
-#include "pa_win_waveformat.h"
-#include "pa_win_wdmks_utils.h"
-#include "pa_win_coinitialize.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" )
-#pragma comment( lib, "kernel32.lib" )
-#endif
-
-/* use CreateThread for CYGWIN, _beginthreadex for all others */
-#ifndef PA_WIN_DS_USE_WMME_TIMER
-
-#if !defined(__CYGWIN__) && !defined(UNDER_CE)
-#define CREATE_THREAD (HANDLE)_beginthreadex
-#undef CLOSE_THREAD_HANDLE /* as per documentation we don't call CloseHandle on a thread created with _beginthreadex */
-#define PA_THREAD_FUNC static unsigned WINAPI
-#define PA_THREAD_ID unsigned
-#else
-#define CREATE_THREAD CreateThread
-#define CLOSE_THREAD_HANDLE CloseHandle
-#define PA_THREAD_FUNC static DWORD WINAPI
-#define PA_THREAD_ID DWORD
-#endif
-
-#if (defined(UNDER_CE))
-#pragma comment(lib, "Coredll.lib")
-#elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
-#pragma comment(lib, "winmm.lib")
-#endif
-
-PA_THREAD_FUNC ProcessingThreadProc( void *pArg );
-
-#if !defined(UNDER_CE)
-#define PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT /* use waitable timer where possible, otherwise we use a WaitForSingleObject timeout */
-#endif
-
-#endif /* !PA_WIN_DS_USE_WMME_TIMER */
-
-
-/*
- provided in newer platform sdks and x64
- */
-#ifndef DWORD_PTR
- #if defined(_WIN64)
- #define DWORD_PTR unsigned __int64
- #else
- #define DWORD_PTR unsigned long
- #endif
-#endif
-
-#define PRINT(x) PA_DEBUG(x);
-#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_DS_WIN_9X_DEFAULT_LATENCY_ (.500)
-#define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.600)
-#else
-#define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.140)
-#define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.280)
-#endif
-
-#define PA_DS_WIN_WDM_DEFAULT_LATENCY_ (.120)
-
-/* we allow the polling period to range between 1 and 100ms.
- prior to August 2011 we limited the minimum polling period to 10ms.
-*/
-#define PA_DS_MINIMUM_POLLING_PERIOD_SECONDS (0.001) /* 1ms */
-#define PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS (0.100) /* 100ms */
-#define PA_DS_POLLING_JITTER_SECONDS (0.001) /* 1ms */
-
-#define SECONDS_PER_MSEC (0.001)
-#define MSECS_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 CollectGUIDsProcW(LPGUID lpGUID,
- LPCWSTR lpszDesc,
- LPCWSTR lpszDrvName,
- LPVOID lpContext );
-
-/************************************************************************************/
-/********************** Structures **************************************************/
-/************************************************************************************/
-/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct PaWinDsDeviceInfo
-{
- PaDeviceInfo inheritedDeviceInfo;
- GUID guid;
- GUID *lpGUID;
- double sampleRates[3];
- char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
- char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
-} PaWinDsDeviceInfo;
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- /* implementation specific data goes here */
-
- PaWinUtilComInitializationResult comInitializationResult;
-
-} PaWinDsHostApiRepresentation;
-
-
-/* PaWinDsStream - a stream data structure specifically for this implementation */
-
-typedef struct PaWinDsStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
-/* DirectSound specific data. */
-#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- LPDIRECTSOUNDFULLDUPLEX8 pDirectSoundFullDuplex8;
-#endif
-
-/* Output */
- LPDIRECTSOUND pDirectSound;
- LPDIRECTSOUNDBUFFER pDirectSoundPrimaryBuffer;
- LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer;
- DWORD outputBufferWriteOffsetBytes; /* last write position */
- INT outputBufferSizeBytes;
- INT outputFrameSizeBytes;
- /* Try to detect play buffer underflows. */
- LARGE_INTEGER perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
- LARGE_INTEGER previousPlayTime;
- DWORD previousPlayCursor;
- UINT outputUnderflowCount;
- BOOL outputIsRunning;
- INT finalZeroBytesWritten; /* used to determine when we've flushed the whole buffer */
-
-/* Input */
- LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
- LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer;
- INT inputFrameSizeBytes;
- UINT readOffset; /* last read position */
- UINT inputBufferSizeBytes;
-
-
- int hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */
- double framesWritten;
- double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
- double pollingPeriodSeconds;
-
- PaStreamCallbackFlags callbackFlags;
-
- PaStreamFlags streamFlags;
- int callbackResult;
- HANDLE processingCompleted;
-
-/* 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 */
-
- UINT systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */
-
-#ifdef PA_WIN_DS_USE_WMME_TIMER
- MMRESULT timerID;
-#else
-
-#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- HANDLE waitableTimer;
-#endif
- HANDLE processingThread;
- PA_THREAD_ID processingThreadId;
- HANDLE processingThreadCompleted;
-#endif
-
-} PaWinDsStream;
-
-
-/* Set minimal latency based on the current OS version.
- * NT has higher latency.
- */
-static double PaWinDS_GetMinSystemLatencySeconds( void )
-{
-/*
-NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
-versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
-Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
-is faster, for now we just disable the deprecation warning.
-See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
-See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
-*/
-#pragma warning (disable : 4996) /* use of GetVersionEx */
-
- double minLatencySeconds;
- /* Set minimal latency based on whether NT or other OS.
- * NT has higher latency.
- */
-
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize = sizeof( osvi );
- GetVersionEx( &osvi );
- DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
- DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
- DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
- /* Check for NT */
- if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
- {
- minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
- }
- else if(osvi.dwMajorVersion >= 5)
- {
- minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
- }
- else
- {
- minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_;
- }
- return minLatencySeconds;
-
-#pragma warning (default : 4996)
-}
-
-
-/*************************************************************************
-** Return minimum workable latency required for this host. This is returned
-** As the default stream latency in PaDeviceInfo.
-** Latency can be optionally set by user by setting an environment variable.
-** For example, to set latency to 200 msec, put:
-**
-** set PA_MIN_LATENCY_MSEC=200
-**
-** in the AUTOEXEC.BAT file and reboot.
-** If the environment variable is not set, then the latency will be determined
-** based on the OS. Windows NT has higher latency than Win95.
-*/
-#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
-#define PA_ENV_BUF_SIZE (32)
-
-static double PaWinDs_GetMinLatencySeconds( double sampleRate )
-{
- char envbuf[PA_ENV_BUF_SIZE];
- DWORD hresult;
- double minLatencySeconds = 0;
-
- /* Let user determine minimal latency by setting environment variable. */
- hresult = GetEnvironmentVariableA( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
- if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
- {
- minLatencySeconds = atoi( envbuf ) * SECONDS_PER_MSEC;
- }
- else
- {
- minLatencySeconds = PaWinDS_GetMinSystemLatencySeconds();
-#if PA_USE_HIGH_LATENCY
- PRINT(("PA - Minimum Latency set to %f msec!\n", minLatencySeconds * MSECS_PER_SECOND ));
-#endif
- }
-
- return minLatencySeconds;
-}
-
-
-/************************************************************************************
-** Duplicate and convert the input string using the group 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 wchar_t* src )
-{
- char *result = 0;
-
- if( src != NULL )
- {
- size_t len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
-
- result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
- if( result ) {
- if (WideCharToMultiByte(CP_UTF8, 0, src, -1, result, (int)len, NULL, NULL) == 0) {
- result = 0;
- }
- }
- }
- 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;
- void *pnpInterface; // wchar_t* interface path, allocated using the DS host api's allocation group
-} DSDeviceNameAndGUID;
-
-typedef struct DSDeviceNameAndGUIDVector{
- PaUtilAllocationGroup *allocations;
- PaError enumerationError;
-
- int count;
- int free;
- DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
-} DSDeviceNameAndGUIDVector;
-
-typedef struct DSDeviceNamesAndGUIDs{
- PaWinDsHostApiRepresentation *winDsHostApi;
- DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
- DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
-} DSDeviceNamesAndGUIDs;
-
-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) );
- }
- newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
- }
-
- 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 CollectGUIDsProcW(LPGUID lpGUID,
- LPCWSTR lpszDesc,
- LPCWSTR lpszDrvName,
- LPVOID lpContext )
-{
- DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
- PaError error;
-
- (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
- {
- namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
- &namesAndGUIDs->items[namesAndGUIDs->count].guid;
-
- memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
- }
-
- namesAndGUIDs->items[namesAndGUIDs->count].name =
- DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
- if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
- {
- namesAndGUIDs->enumerationError = paInsufficientMemory;
- return FALSE;
- }
-
- namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
-
- ++namesAndGUIDs->count;
- --namesAndGUIDs->free;
-
- return TRUE;
-}
-
-
-#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
-
-static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
-{
- size_t len;
- wchar_t *result;
-
- len = wcslen( source );
- result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
- wcscpy( result, source );
- return result;
-}
-
-static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
-{
- int i;
- DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
-
- /*
- Apparently data->Interface can be NULL in some cases.
- Possibly virtual devices without hardware.
- So we check for NULLs now. See mailing list message November 10, 2012:
- "[Portaudio] portaudio initialization crash in KsPropertySetEnumerateCallback(pa_win_ds.c)"
- */
- if( data->Interface )
- {
- if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
- {
- for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
- {
- if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
- && memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
- {
- deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
- (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
- break;
- }
- }
- }
- else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
- {
- for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
- {
- if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
- && memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
- {
- deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
- (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
- break;
- }
- }
- }
- }
-
- return TRUE;
-}
-
-
-static GUID pawin_CLSID_DirectSoundPrivate =
-{ 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
-
-static GUID pawin_DSPROPSETID_DirectSoundDevice =
-{ 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
-
-static GUID pawin_IID_IKsPropertySet =
-{ 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
-
-
-/*
- FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
- with UNICODE file paths to the devices. The DS documentation mentions
- at least two techniques by which these Interface paths can be found using IKsPropertySet on
- the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
- property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
- I tried both methods and only the second worked. I found two postings on the
- net from people who had the same problem with the first method, so I think the method used here is
- more common/likely to work. The problem is that IKsPropertySet_Get returns S_OK
- but the fields of the device description are not filled in.
-
- The mechanism we use works by registering an enumeration callback which is called for
- every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
- with the matching GUID and copies the pointer to the Interface path.
- Note that we could have used this enumeration callback to perform the original
- device enumeration, however we choose not to so we can disable this step easily.
-
- Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
- http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
-
- -- rossb
-*/
-static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
-{
- IClassFactory *pClassFactory;
-
- if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
- IKsPropertySet *pPropertySet;
- if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
-
- DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
- ULONG bytesReturned;
-
- data.Callback = KsPropertySetEnumerateCallback;
- data.Context = deviceNamesAndGUIDs;
-
- IKsPropertySet_Get( pPropertySet,
- &pawin_DSPROPSETID_DirectSoundDevice,
- DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
- NULL,
- 0,
- &data,
- sizeof(data),
- &bytesReturned
- );
-
- IKsPropertySet_Release( pPropertySet );
- }
- pClassFactory->lpVtbl->Release( pClassFactory );
- }
-
- /*
- The following code fragment, which I chose not to use, queries for the
- device interface for a device with a specific GUID:
-
- ULONG BytesReturned;
- DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
-
- memset (&Property, 0, sizeof(Property));
- Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
- Property.DeviceId = *lpGUID;
-
- hr = IKsPropertySet_Get( pPropertySet,
- &pawin_DSPROPSETID_DirectSoundDevice,
- DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
- NULL,
- 0,
- &Property,
- sizeof(Property),
- &BytesReturned
- );
-
- if( hr == S_OK )
- {
- //pnpInterface = Property.Interface;
- }
- */
-}
-#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
-
-
-/*
- GUIDs for emulated devices which we blacklist below.
- are there more than two of them??
-*/
-
-GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
-GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
-
-
-#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
-static double defaultSampleRateSearchOrder_[] =
- { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
- 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
-
-/************************************************************************************
-** Extract capabilities from an output device, and add it to the device info list
-** if successful. This function assumes that there is enough room in the
-** device info list to accommodate all entries.
-**
-** The device will not be added to the device list if any errors are encountered.
-*/
-static PaError AddOutputDeviceInfoFromDirectSound(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
-{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
- PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
- HRESULT hr;
- LPDIRECTSOUND lpDirectSound;
- DSCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
- int i;
-
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- }
-
- if( lpGUID )
- {
- if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
- IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
- {
- PA_DEBUG(("BLACKLISTED: %s \n",name));
- return paNoError;
- }
- }
-
- /* Create a DirectSound object for the specified GUID
- Note that using CoCreateInstance doesn't work on windows CE.
- */
- hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
-
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSound, (void**)&lpDirectSound );
-
- if( hr == S_OK )
- {
- hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
- }
- */
-
- if( hr != DS_OK )
- {
- if (hr == DSERR_ALLOCATED)
- PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
- DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
- if (lpGUID)
- DBUG(("%s's GUID: {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, 0x%x} \n",
- name,
- lpGUID->Data1,
- lpGUID->Data2,
- lpGUID->Data3,
- lpGUID->Data4[0],
- lpGUID->Data4[1],
- lpGUID->Data4[2],
- lpGUID->Data4[3],
- lpGUID->Data4[4],
- lpGUID->Data4[5],
- lpGUID->Data4[6],
- lpGUID->Data4[7]));
-
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSound_GetCaps( lpDirectSound, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
-
-#if PA_USE_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
-#endif
-
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = 0;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
-
- /* DS output capabilities only indicate supported number of channels
- using two flags which indicate mono and/or stereo.
- We assume that stereo devices may support more than 2 channels
- (as is the case with 5.1 devices for example) and so
- set deviceOutputChannelCountIsKnown to 0 (unknown).
- In this case OpenStream will try to open the device
- when the user requests more than 2 channels, rather than
- returning an error.
- */
- if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
- {
- deviceInfo->maxOutputChannels = 2;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
- }
- else
- {
- deviceInfo->maxOutputChannels = 1;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
-
- /* Guess channels count from speaker configuration. We do it only when
- pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
- */
-#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( !pnpInterface )
-#endif
- {
- DWORD spkrcfg;
- if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
- {
- int count = 0;
- switch (DSSPEAKER_CONFIG(spkrcfg))
- {
- case DSSPEAKER_HEADPHONE: count = 2; break;
- case DSSPEAKER_MONO: count = 1; break;
- case DSSPEAKER_QUAD: count = 4; break;
- case DSSPEAKER_STEREO: count = 2; break;
- case DSSPEAKER_SURROUND: count = 4; break;
- case DSSPEAKER_5POINT1: count = 6; break;
-#ifndef DSSPEAKER_7POINT1
-#define DSSPEAKER_7POINT1 0x00000007
-#endif
- case DSSPEAKER_7POINT1: count = 8; break;
-#ifndef DSSPEAKER_7POINT1_SURROUND
-#define DSSPEAKER_7POINT1_SURROUND 0x00000008
-#endif
- case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
-#ifndef DSSPEAKER_5POINT1_SURROUND
-#define DSSPEAKER_5POINT1_SURROUND 0x00000009
-#endif
- case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
- }
- if( count )
- {
- deviceInfo->maxOutputChannels = count;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
- }
- }
-
-#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( pnpInterface )
- {
- int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
- if( count > 0 )
- {
- deviceInfo->maxOutputChannels = count;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
- }
-#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
-
- /* initialize defaultSampleRate */
-
- if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
- {
- /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
-
- for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
- {
- if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
- && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
- {
- deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
- break;
- }
- }
- }
- else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
- {
- if( caps.dwMinSecondarySampleRate == 0 )
- {
- /*
- ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
- ** But it supports continuous sampling.
- ** So fake range of rates, and hope it really supports it.
- */
- deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
-
- DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
- }
- else
- {
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- }
- }
- else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
- {
- /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
- ** But we know that they really support a range of rates!
- ** So when we see a ridiculous set of rates, assume it is a range.
- */
- deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
- DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
- }
- else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
-
- //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
- // dwFlags | DSCAPS_CONTINUOUSRATE
-
- deviceInfo->defaultLowInputLatency = 0.;
- deviceInfo->defaultHighInputLatency = 0.;
-
- deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
- deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
- }
- }
-
- IDirectSound_Release( lpDirectSound );
- }
-
- if( deviceOK )
- {
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
- }
-
- return result;
-}
-
-
-/************************************************************************************
-** Extract capabilities from an input device, and add it to the device info list
-** if successful. This function assumes that there is enough room in the
-** device info list to accommodate all entries.
-**
-** The device will not be added to the device list if any errors are encountered.
-*/
-static PaError AddInputDeviceInfoFromDirectSoundCapture(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
-{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
- PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
- HRESULT hr;
- LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
- DSCCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
-
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- }
-
- hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
-
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
- */
- if( hr != DS_OK )
- {
- DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
-#if PA_USE_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
-#endif
-
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = caps.dwChannels;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
-
- deviceInfo->maxOutputChannels = 0;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
-
-#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( pnpInterface )
- {
- int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
- if( count > 0 )
- {
- deviceInfo->maxInputChannels = count;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
- }
- }
-#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
-
-/* constants from a WINE patch by Francois Gouget, see:
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
-
- ---
- Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
- From: Francois Gouget <fgouget@ ... .fr>
- To: Ross Bencina <rbencina@ ... .au>
- Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
-
- [snip]
-
- I give you permission to use the patch below under the BSD license.
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
-
- [snip]
-*/
-#ifndef WAVE_FORMAT_48M08
-#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
-#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
-#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
-#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
-#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
-#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
-#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
-#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
-#endif
-
- /* defaultSampleRate */
- if( caps.dwChannels == 2 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4S16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48S16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2S16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1S16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96S16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
- }
- else if( caps.dwChannels == 1 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4M16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48M16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2M16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1M16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96M16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
- }
- else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
-
- deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
- deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
-
- deviceInfo->defaultLowOutputLatency = 0.;
- deviceInfo->defaultHighOutputLatency = 0.;
- }
- }
-
- IDirectSoundCapture_Release( lpDirectSoundCapture );
- }
-
- if( deviceOK )
- {
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
- }
-
- return result;
-}
-
-
-/***********************************************************************************/
-PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- int i, deviceCount;
- PaWinDsHostApiRepresentation *winDsHostApi;
- DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
- PaWinDsDeviceInfo *deviceInfoArray;
-
- PaWinDs_InitializeDSoundEntryPoints();
-
- /* initialise guid vectors so they can be safely deleted on error */
- deviceNamesAndGUIDs.winDsHostApi = NULL;
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
-
- winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
- if( !winDsHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- memset( winDsHostApi, 0, sizeof(PaWinDsHostApiRepresentation) ); /* ensure all fields are zeroed. especially winDsHostApi->allocations */
-
- result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
- if( result != paNoError )
- {
- goto error;
- }
-
- winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !winDsHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &winDsHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paDirectSound;
- (*hostApi)->info.name = "Windows DirectSound";
-
- (*hostApi)->info.deviceCount = 0;
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
-
-
-/* DSound - enumerate devices to count them and to gather their GUIDs */
-
- result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
-
- result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
-
- paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
-
- paWinDsDSoundEntryPoints.DirectSoundEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
-
- if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
- goto error;
- }
-
- if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
- goto error;
- }
-
- deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
-
-#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( deviceCount > 0 )
- {
- deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
- FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
- }
-#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
-
- if( deviceCount > 0 )
- {
- /* allocate array for pointers to PaDeviceInfo structs */
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all PaDeviceInfo structs in a contiguous block */
- deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( i=0; i < deviceCount; ++i )
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = 0;
- (*hostApi)->deviceInfos[i] = deviceInfo;
- }
-
- for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
- {
- result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
- if( result != paNoError )
- goto error;
- }
-
- for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
- {
- result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
- if( result != paNoError )
- goto error;
- }
- }
-
- result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
-
- result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
-
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
-
- Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi );
-
- return result;
-}
-
-
-/***********************************************************************************/
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
-
- if( winDsHostApi ){
- if( winDsHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winDsHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
- }
-
- PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
-
- PaUtil_FreeMemory( winDsHostApi );
- }
-
- PaWinDs_TerminateDSoundEntryPoints();
-}
-
-static PaError ValidateWinDirectSoundSpecificStreamInfo(
- const PaStreamParameters *streamParameters,
- const PaWinDirectSoundStreamInfo *streamInfo )
-{
- if( streamInfo )
- {
- if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
- || streamInfo->version != 2 )
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
-
- if( streamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
- {
- if( streamInfo->framesPerBuffer <= 0 )
- return paIncompatibleHostApiSpecificStreamInfo;
-
- }
- }
-
- return paNoError;
-}
-
-/***********************************************************************************/
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaError result;
- PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
- PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
-
- if( inputParameters )
- {
- inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
- inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
-
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
- && inputChannelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
- if( result != paNoError ) return result;
- }
- else
- {
- inputChannelCount = 0;
- }
-
- if( outputParameters )
- {
- outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
- outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
-
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
- && outputChannelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
- if( result != paNoError ) return result;
- }
- else
- {
- outputChannelCount = 0;
- }
-
- /*
- IMPLEMENT ME:
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported if necessary
-
- - check that the device supports sampleRate
-
- Because the buffer adapter handles conversion between all standard
- sample formats, the following checks are only required if paCustomFormat
- is implemented, or under some other unusual conditions.
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- */
-
- return paFormatIsSupported;
-}
-
-
-#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
-static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
- PaWinDsDeviceInfo *inputDevice,
- PaSampleFormat hostInputSampleFormat,
- WORD inputChannelCount,
- int bytesPerInputBuffer,
- PaWinWaveFormatChannelMask inputChannelMask,
- PaWinDsDeviceInfo *outputDevice,
- PaSampleFormat hostOutputSampleFormat,
- WORD outputChannelCount,
- int bytesPerOutputBuffer,
- PaWinWaveFormatChannelMask outputChannelMask,
- unsigned long nFrameRate
- )
-{
- HRESULT hr;
- DSCBUFFERDESC captureDesc;
- PaWinWaveFormat captureWaveFormat;
- DSBUFFERDESC secondaryRenderDesc;
- PaWinWaveFormat renderWaveFormat;
- LPDIRECTSOUNDBUFFER8 pRenderBuffer8;
- LPDIRECTSOUNDCAPTUREBUFFER8 pCaptureBuffer8;
-
- // capture buffer description
-
- // only try wave format extensible. assume it's available on all ds 8 systems
- PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
- hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
- nFrameRate, inputChannelMask );
-
- ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
- captureDesc.dwSize = sizeof(DSCBUFFERDESC);
- captureDesc.dwFlags = 0;
- captureDesc.dwBufferBytes = bytesPerInputBuffer;
- captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
-
- // render buffer description
-
- PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
- hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
- nFrameRate, outputChannelMask );
-
- ZeroMemory(&secondaryRenderDesc, sizeof(DSBUFFERDESC));
- secondaryRenderDesc.dwSize = sizeof(DSBUFFERDESC);
- secondaryRenderDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- secondaryRenderDesc.dwBufferBytes = bytesPerOutputBuffer;
- secondaryRenderDesc.lpwfxFormat = (WAVEFORMATEX*)&renderWaveFormat;
-
- /* note that we don't create a primary buffer here at all */
-
- hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
- inputDevice->lpGUID, outputDevice->lpGUID,
- &captureDesc, &secondaryRenderDesc,
- GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
- DSSCL_EXCLUSIVE,
- &stream->pDirectSoundFullDuplex8,
- &pCaptureBuffer8,
- &pRenderBuffer8,
- NULL /* pUnkOuter must be NULL */
- );
-
- if( hr == DS_OK )
- {
- PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
-
- /* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
-
- hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, (LPVOID *)&stream->pDirectSoundInputBuffer );
-
- if( hr == DS_OK )
- hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, (LPVOID *)&stream->pDirectSoundOutputBuffer );
-
- /* release the ds 8 interfaces, we don't need them */
- IUnknown_Release( pCaptureBuffer8 );
- IUnknown_Release( pRenderBuffer8 );
-
- if( !stream->pDirectSoundInputBuffer || !stream->pDirectSoundOutputBuffer ){
- /* couldn't get pre ds 8 interfaces for some reason. clean up. */
- if( stream->pDirectSoundInputBuffer )
- {
- IUnknown_Release( stream->pDirectSoundInputBuffer );
- stream->pDirectSoundInputBuffer = NULL;
- }
-
- if( stream->pDirectSoundOutputBuffer )
- {
- IUnknown_Release( stream->pDirectSoundOutputBuffer );
- stream->pDirectSoundOutputBuffer = NULL;
- }
-
- IUnknown_Release( stream->pDirectSoundFullDuplex8 );
- stream->pDirectSoundFullDuplex8 = NULL;
- }
- }
- else
- {
- PA_DEBUG(("DirectSoundFullDuplexCreate failed. hr=%d\n", hr));
- }
-
- return hr;
-}
-#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
-
-
-static HRESULT InitInputBuffer( PaWinDsStream *stream,
- PaWinDsDeviceInfo *device,
- PaSampleFormat sampleFormat,
- unsigned long nFrameRate,
- WORD nChannels,
- int bytesPerBuffer,
- PaWinWaveFormatChannelMask channelMask )
-{
- DSCBUFFERDESC captureDesc;
- PaWinWaveFormat waveFormat;
- HRESULT result;
-
- if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
- device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
- ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
- return result;
- }
-
- // Setup the secondary buffer description
- ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
- captureDesc.dwSize = sizeof(DSCBUFFERDESC);
- captureDesc.dwFlags = 0;
- captureDesc.dwBufferBytes = bytesPerBuffer;
- captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
-
- // Create the capture buffer
-
- // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
- PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
- sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
- nFrameRate, channelMask );
-
- if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
- &captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
- {
- PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
- PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
-
- if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
- &captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
- }
-
- stream->readOffset = 0; // reset last read position to start of buffer
- return DS_OK;
-}
-
-
-static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device,
- PaSampleFormat sampleFormat, unsigned long nFrameRate,
- WORD nChannels, int bytesPerBuffer,
- PaWinWaveFormatChannelMask channelMask )
-{
- HRESULT result;
- HWND hWnd;
- HRESULT hr;
- PaWinWaveFormat waveFormat;
- DSBUFFERDESC primaryDesc;
- DSBUFFERDESC secondaryDesc;
-
- if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
- device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
- ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
- return hr;
- }
-
- // 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();
- //
- // FIXME: The example code I have on the net creates a hidden window that
- // is managed by our code - I think we should do that - one hidden
- // window for the whole of Pa_DS
- //
- hWnd = GetDesktopWindow();
-
- // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
- // exclusive also prevents unexpected sounds from other apps during a performance.
- if ((hr = IDirectSound_SetCooperativeLevel( stream->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( stream->pDirectSound,
- &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
- goto error;
-
- // Set the primary buffer's format
-
- // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
- PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
- sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
- nFrameRate, channelMask );
-
- if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
- {
- PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
- PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
-
- if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
- goto error;
- }
-
- // ----------------------------------------------------------------------
- // Setup the secondary buffer description
- ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
- secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
- secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- secondaryDesc.dwBufferBytes = bytesPerBuffer;
- secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; /* waveFormat contains whatever format was negotiated for the primary buffer above */
- // Create the secondary buffer
- if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
- &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
- goto error;
-
- return DS_OK;
-
-error:
-
- if( stream->pDirectSoundPrimaryBuffer )
- {
- IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
- stream->pDirectSoundPrimaryBuffer = NULL;
- }
-
- return result;
-}
-
-
-static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
- unsigned long *pollingPeriodFrames,
- int isFullDuplex,
- unsigned long suggestedInputLatencyFrames,
- unsigned long suggestedOutputLatencyFrames,
- double sampleRate, unsigned long userFramesPerBuffer )
-{
- unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
- unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
- unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
-
- if( userFramesPerBuffer == paFramesPerBufferUnspecified )
- {
- unsigned long targetBufferingLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames );
-
- *pollingPeriodFrames = targetBufferingLatencyFrames / 4;
- if( *pollingPeriodFrames < minimumPollingPeriodFrames )
- {
- *pollingPeriodFrames = minimumPollingPeriodFrames;
- }
- else if( *pollingPeriodFrames > maximumPollingPeriodFrames )
- {
- *pollingPeriodFrames = maximumPollingPeriodFrames;
- }
-
- *hostBufferSizeFrames = *pollingPeriodFrames
- + max( *pollingPeriodFrames + pollingJitterFrames, targetBufferingLatencyFrames);
- }
- else
- {
- unsigned long targetBufferingLatencyFrames = suggestedInputLatencyFrames;
- if( isFullDuplex )
- {
- /* In full duplex streams we know that the buffer adapter adds userFramesPerBuffer
- extra fixed latency. so we subtract it here as a fixed latency before computing
- the buffer size. being careful not to produce an unrepresentable negative result.
-
- Note: this only works as expected if output latency is greater than input latency.
- Otherwise we use input latency anyway since we do max(in,out).
- */
-
- if( userFramesPerBuffer < suggestedOutputLatencyFrames )
- {
- unsigned long adjustedSuggestedOutputLatencyFrames =
- suggestedOutputLatencyFrames - userFramesPerBuffer;
-
- /* maximum of input and adjusted output suggested latency */
- if( adjustedSuggestedOutputLatencyFrames > targetBufferingLatencyFrames )
- targetBufferingLatencyFrames = adjustedSuggestedOutputLatencyFrames;
- }
- }
- else
- {
- /* maximum of input and output suggested latency */
- if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames )
- targetBufferingLatencyFrames = suggestedOutputLatencyFrames;
- }
-
- *hostBufferSizeFrames = userFramesPerBuffer
- + max( userFramesPerBuffer + pollingJitterFrames, targetBufferingLatencyFrames);
-
- *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), targetBufferingLatencyFrames / 16 );
-
- if( *pollingPeriodFrames > maximumPollingPeriodFrames )
- {
- *pollingPeriodFrames = maximumPollingPeriodFrames;
- }
- }
-}
-
-
-static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames,
- unsigned long *pollingPeriodFrames,
- double sampleRate, unsigned long userFramesPerBuffer )
-{
- unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
- unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
- unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
-
- *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), hostBufferSizeFrames / 16 );
-
- if( *pollingPeriodFrames > maximumPollingPeriodFrames )
- {
- *pollingPeriodFrames = maximumPollingPeriodFrames;
- }
-}
-
-
-static void SetStreamInfoLatencies( PaWinDsStream *stream,
- unsigned long userFramesPerBuffer,
- unsigned long pollingPeriodFrames,
- double sampleRate )
-{
- /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames,
- and the configuration of the buffer processor */
-
- unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified)
- ? pollingPeriodFrames
- : userFramesPerBuffer;
-
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- /* stream info input latency is the minimum buffering latency
- (unlike suggested and default which are *maximums*) */
- stream->streamRepresentation.streamInfo.inputLatency =
- (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
- + effectiveFramesPerBuffer) / sampleRate;
- }
- else
- {
- stream->streamRepresentation.streamInfo.inputLatency = 0;
- }
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- stream->streamRepresentation.streamInfo.outputLatency =
- (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
- + (stream->hostBufferSizeFrames - effectiveFramesPerBuffer)) / sampleRate;
- }
- else
- {
- stream->streamRepresentation.streamInfo.outputLatency = 0;
- }
-}
-
-
-/***********************************************************************************/
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
-
-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 )
-{
- PaError result = paNoError;
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
- PaWinDsStream *stream = 0;
- int bufferProcessorIsInitialized = 0;
- int streamRepresentationIsInitialized = 0;
- PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
- PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
- int userRequestedHostInputBufferSizeFrames = 0;
- int userRequestedHostOutputBufferSizeFrames = 0;
- unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
- PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
- PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
- unsigned long pollingPeriodFrames = 0;
-
- if( inputParameters )
- {
- inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
- inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
-
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
-
- /* IDEA: the following 3 checks could be performed by default by pa_front
- unless some flag indicated otherwise */
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
- && inputChannelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate hostApiSpecificStreamInfo */
- inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
- if( result != paNoError ) return result;
-
- if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
- userRequestedHostInputBufferSizeFrames = inputStreamInfo->framesPerBuffer;
-
- if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
- inputChannelMask = inputStreamInfo->channelMask;
- else
- inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
- }
- else
- {
- inputChannelCount = 0;
- inputSampleFormat = 0;
- suggestedInputLatencyFrames = 0;
- }
-
-
- if( outputParameters )
- {
- outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
- outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
-
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support outputChannelCount */
- if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
- && outputChannelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate hostApiSpecificStreamInfo */
- outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
- if( result != paNoError ) return result;
-
- if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
- userRequestedHostOutputBufferSizeFrames = outputStreamInfo->framesPerBuffer;
-
- if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
- outputChannelMask = outputStreamInfo->channelMask;
- else
- outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
- }
- else
- {
- outputChannelCount = 0;
- outputSampleFormat = 0;
- suggestedOutputLatencyFrames = 0;
- }
-
- /*
- If low level host buffer size is specified for both input and output
- the current code requires the sizes to match.
- */
-
- if( (userRequestedHostInputBufferSizeFrames > 0 && userRequestedHostOutputBufferSizeFrames > 0)
- && userRequestedHostInputBufferSizeFrames != userRequestedHostOutputBufferSizeFrames )
- return paIncompatibleHostApiSpecificStreamInfo;
-
-
-
- /*
- IMPLEMENT ME:
-
- ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- - alter sampleRate to a close allowable rate if possible / necessary
-
- - validate suggestedInputLatency and suggestedOutputLatency parameters,
- use default values where necessary
- */
-
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
-
- stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */
-
- if( streamCallback )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->callbackStreamInterface, streamCallback, userData );
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->blockingStreamInterface, streamCallback, userData );
- }
-
- streamRepresentationIsInitialized = 1;
-
- stream->streamFlags = streamFlags;
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
-
- if( inputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- PaSampleFormat nativeInputFormats = paInt16;
- /* PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
-
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
- }
- else
- {
- hostInputSampleFormat = 0;
- }
-
- if( outputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- PaSampleFormat nativeOutputFormats = paInt16;
- /* PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
-
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
- }
- else
- {
- hostOutputSampleFormat = 0;
- }
-
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- 0, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
- /* This next mode is required because DS can split the host buffer when it wraps around. */
- paUtilVariableHostBufferSizePartialUsageAllowed,
- streamCallback, userData );
- if( result != paNoError )
- goto error;
-
- bufferProcessorIsInitialized = 1;
-
-
-/* DirectSound specific initialization */
- {
- HRESULT hr;
- unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
-
- stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
- if( stream->processingCompleted == NULL )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
-#ifdef PA_WIN_DS_USE_WMME_TIMER
- stream->timerID = 0;
-#endif
-
-#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL );
- if( stream->waitableTimer == NULL )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
-#endif
-
-#ifndef PA_WIN_DS_USE_WMME_TIMER
- stream->processingThreadCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
- if( stream->processingThreadCompleted == NULL )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
-#endif
-
- /* set up i/o parameters */
-
- if( userRequestedHostInputBufferSizeFrames > 0 || userRequestedHostOutputBufferSizeFrames > 0 )
- {
- /* use low level parameters */
-
- /* since we use the same host buffer size for input and output
- we choose the highest user specified value.
- */
- stream->hostBufferSizeFrames = max( userRequestedHostInputBufferSizeFrames, userRequestedHostOutputBufferSizeFrames );
-
- CalculatePollingPeriodFrames(
- stream->hostBufferSizeFrames, &pollingPeriodFrames,
- sampleRate, framesPerBuffer );
- }
- else
- {
- CalculateBufferSettings( (unsigned long*)&stream->hostBufferSizeFrames, &pollingPeriodFrames,
- /* isFullDuplex = */ (inputParameters && outputParameters),
- suggestedInputLatencyFrames,
- suggestedOutputLatencyFrames,
- sampleRate, framesPerBuffer );
- }
-
- stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;
-
- DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n",
- stream->hostBufferSizeFrames, stream->pollingPeriodSeconds, sampleRate ));
-
-
- /* ------------------ OUTPUT */
- if( outputParameters )
- {
- LARGE_INTEGER counterFrequency;
-
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
- */
-
- int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat);
- stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes;
-
- stream->outputBufferSizeBytes = stream->hostBufferSizeFrames * stream->outputFrameSizeBytes;
- if( stream->outputBufferSizeBytes < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( stream->outputBufferSizeBytes > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
-
- /* Calculate value used in latency calculation to avoid real-time divides. */
- stream->secondsPerHostByte = 1.0 /
- (stream->bufferProcessor.bytesPerHostOutputSample *
- outputChannelCount * sampleRate);
-
- stream->outputIsRunning = FALSE;
- stream->outputUnderflowCount = 0;
-
- /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
- if( QueryPerformanceFrequency( &counterFrequency ) )
- {
- stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->hostBufferSizeFrames) / integerSampleRate;
- }
- else
- {
- stream->perfCounterTicksPerBuffer.QuadPart = 0;
- }
- }
-
- /* ------------------ INPUT */
- if( inputParameters )
- {
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
- */
-
- int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat);
- stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes;
-
- stream->inputBufferSizeBytes = stream->hostBufferSizeFrames * stream->inputFrameSizeBytes;
- if( stream->inputBufferSizeBytes < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( stream->inputBufferSizeBytes > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
- }
-
- /* open/create the DirectSound buffers */
-
- /* interface ptrs should be zeroed when stream is zeroed. */
- assert( stream->pDirectSoundCapture == NULL );
- assert( stream->pDirectSoundInputBuffer == NULL );
- assert( stream->pDirectSound == NULL );
- assert( stream->pDirectSoundPrimaryBuffer == NULL );
- assert( stream->pDirectSoundOutputBuffer == NULL );
-
-
- if( inputParameters && outputParameters )
- {
-#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- /* try to use the full-duplex DX8 API to create the buffers.
- if that fails we fall back to the half-duplex API below */
-
- hr = InitFullDuplexInputOutputBuffers( stream,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
- hostInputSampleFormat,
- (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
- inputChannelMask,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
- hostOutputSampleFormat,
- (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
- outputChannelMask,
- integerSampleRate
- );
- DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
- /* ignore any error returned by InitFullDuplexInputOutputBuffers.
- we retry opening the buffers below */
-#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
- }
-
- /* create half duplex buffers. also used for full-duplex streams which didn't
- succeed when using the full duplex API. that could happen because
- DX8 or greater isn't installed, the i/o devices aren't the same
- physical device. etc.
- */
-
- if( outputParameters && !stream->pDirectSoundOutputBuffer )
- {
- hr = InitOutputBuffer( stream,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
- hostOutputSampleFormat,
- integerSampleRate,
- (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
- outputChannelMask );
- DBUG(("InitOutputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
- if( inputParameters && !stream->pDirectSoundInputBuffer )
- {
- hr = InitInputBuffer( stream,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
- hostInputSampleFormat,
- integerSampleRate,
- (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
- inputChannelMask );
- DBUG(("InitInputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
- }
-
- SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate );
-
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( stream )
- {
- if( stream->processingCompleted != NULL )
- CloseHandle( stream->processingCompleted );
-
-#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- if( stream->waitableTimer != NULL )
- CloseHandle( stream->waitableTimer );
-#endif
-
-#ifndef PA_WIN_DS_USE_WMME_TIMER
- if( stream->processingThreadCompleted != NULL )
- CloseHandle( stream->processingThreadCompleted );
-#endif
-
- if( stream->pDirectSoundOutputBuffer )
- {
- IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
- IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
- stream->pDirectSoundOutputBuffer = NULL;
- }
-
- if( stream->pDirectSoundPrimaryBuffer )
- {
- IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
- stream->pDirectSoundPrimaryBuffer = NULL;
- }
-
- if( stream->pDirectSoundInputBuffer )
- {
- IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
- IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
- stream->pDirectSoundInputBuffer = NULL;
- }
-
- if( stream->pDirectSoundCapture )
- {
- IDirectSoundCapture_Release( stream->pDirectSoundCapture );
- stream->pDirectSoundCapture = NULL;
- }
-
- if( stream->pDirectSound )
- {
- IDirectSound_Release( stream->pDirectSound );
- stream->pDirectSound = NULL;
- }
-
-#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- if( stream->pDirectSoundFullDuplex8 )
- {
- IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
- stream->pDirectSoundFullDuplex8 = NULL;
- }
-#endif
- if( bufferProcessorIsInitialized )
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
-
- if( streamRepresentationIsInitialized )
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
-
- PaUtil_FreeMemory( stream );
- }
-
- return result;
-}
-
-
-/************************************************************************************
- * Determine how much space can be safely written to in DS buffer.
- * Detect underflows and overflows.
- * Does not allow writing into safety gap maintained by DirectSound.
- */
-static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
-{
- HRESULT hr;
- DWORD playCursor;
- DWORD writeCursor;
- long numBytesEmpty;
- long playWriteGap;
- // Query to see how much room is in buffer.
- hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
- &playCursor, &writeCursor );
- if( hr != DS_OK )
- {
- return hr;
- }
-
- // Determine size of gap between playIndex and WriteIndex that we cannot write into.
- playWriteGap = writeCursor - playCursor;
- if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap
-
- /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
- /* Attempt to detect playCursor wrap-around and correct it. */
- if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) )
- {
- /* How much time has elapsed since last check. */
- LARGE_INTEGER currentTime;
- LARGE_INTEGER elapsedTime;
- long bytesPlayed;
- long bytesExpected;
- long buffersWrapped;
-
- QueryPerformanceCounter( &currentTime );
- elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart;
- stream->previousPlayTime = currentTime;
-
- /* How many bytes does DirectSound say have been played. */
- bytesPlayed = playCursor - stream->previousPlayCursor;
- if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap
- stream->previousPlayCursor = playCursor;
-
- /* Calculate how many bytes we would have expected to been played by now. */
- bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart);
- buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes;
- if( buffersWrapped > 0 )
- {
- playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
- bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
- }
- }
- numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
- if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
-
- /* Have we underflowed? */
- if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) )
- {
- if( stream->outputIsRunning )
- {
- stream->outputUnderflowCount += 1;
- }
-
- /*
- From MSDN:
- The write cursor indicates the position at which it is safe
- to write new data to the buffer. The write cursor always leads the
- play cursor, typically by about 15 milliseconds' worth of audio
- data.
- It is always safe to change data that is behind the position
- indicated by the lpdwCurrentPlayCursor parameter.
- */
-
- stream->outputBufferWriteOffsetBytes = writeCursor;
- numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap;
- }
- *bytesEmpty = numBytesEmpty;
- return hr;
-}
-
-/***********************************************************************************/
-static int TimeSlice( PaWinDsStream *stream )
-{
- long numFrames = 0;
- long bytesEmpty = 0;
- long bytesFilled = 0;
- long bytesToXfer = 0;
- long framesToXfer = 0; /* the number of frames we'll process this tick */
- long numInFramesReady = 0;
- long numOutFramesReady = 0;
- long bytesProcessed;
- HRESULT hresult;
- double outputLatency = 0;
- double inputLatency = 0;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0};
-
-/* Input */
- LPBYTE lpInBuf1 = NULL;
- LPBYTE lpInBuf2 = NULL;
- DWORD dwInSize1 = 0;
- DWORD dwInSize2 = 0;
-/* Output */
- LPBYTE lpOutBuf1 = NULL;
- LPBYTE lpOutBuf2 = NULL;
- DWORD dwOutSize1 = 0;
- DWORD dwOutSize2 = 0;
-
- /* How much input data is available? */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- HRESULT hr;
- DWORD capturePos;
- DWORD readPos;
- long filled = 0;
- // Query to see how much data is in buffer.
- // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
- // so let's pass a pointer just to be safe.
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos );
- if( hr == DS_OK )
- {
- filled = readPos - stream->readOffset;
- if( filled < 0 ) filled += stream->inputBufferSizeBytes; // unwrap offset
- bytesFilled = filled;
-
- inputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
- }
- // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
-
- framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes;
-
- /** @todo Check for overflow */
- }
-
- /* How much output room is available? */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- UINT previousUnderflowCount = stream->outputUnderflowCount;
- QueryOutputSpace( stream, &bytesEmpty );
- framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes;
-
- /* Check for underflow */
- /* FIXME QueryOutputSpace should not adjust underflow count as a side effect.
- A query function should be a const operator on the stream and return a flag on underflow. */
- if( stream->outputUnderflowCount != previousUnderflowCount )
- stream->callbackFlags |= paOutputUnderflow;
-
- /* We are about to compute audio into the first byte of empty space in the output buffer.
- This audio will reach the DAC after all of the current (non-empty) audio
- in the buffer has played. Therefore the output time is the current time
- plus the time it takes to play the non-empty bytes in the buffer,
- computed here:
- */
- outputLatency = ((double)(stream->outputBufferSizeBytes - bytesEmpty)) * stream->secondsPerHostByte;
- }
-
- /* if it's a full duplex stream, set framesToXfer to the minimum of input and output frames ready */
- if( stream->bufferProcessor.inputChannelCount > 0 && stream->bufferProcessor.outputChannelCount > 0 )
- {
- framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
- }
-
- if( framesToXfer > 0 )
- {
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- /* The outputBufferDacTime parameter should indicates the time at which
- the first sample of the output buffer is heard at the DACs. */
- timeInfo.currentTime = PaUtil_GetTime();
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
- stream->callbackFlags = 0;
-
- /* Input */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- timeInfo.inputBufferAdcTime = timeInfo.currentTime - inputLatency;
-
- bytesToXfer = framesToXfer * stream->inputFrameSizeBytes;
- hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
- stream->readOffset, bytesToXfer,
- (void **) &lpInBuf1, &dwInSize1,
- (void **) &lpInBuf2, &dwInSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
- /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
- stream->callbackResult = paComplete;
- goto error2;
- }
-
- numFrames = dwInSize1 / stream->inputFrameSizeBytes;
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
- /* Is input split into two regions. */
- if( dwInSize2 > 0 )
- {
- numFrames = dwInSize2 / stream->inputFrameSizeBytes;
- PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
- }
- }
-
- /* Output */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /*
- We don't currently add outputLatency here because it appears to produce worse
- results than not adding it. Need to do more testing to verify this.
- */
- /* timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; */
- timeInfo.outputBufferDacTime = timeInfo.currentTime;
-
- bytesToXfer = framesToXfer * stream->outputFrameSizeBytes;
- hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
- stream->outputBufferWriteOffsetBytes, bytesToXfer,
- (void **) &lpOutBuf1, &dwOutSize1,
- (void **) &lpOutBuf2, &dwOutSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
- /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
- stream->callbackResult = paComplete;
- goto error1;
- }
-
- numFrames = dwOutSize1 / stream->outputFrameSizeBytes;
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
-
- /* Is output split into two regions. */
- if( dwOutSize2 > 0 )
- {
- numFrames = dwOutSize2 / stream->outputFrameSizeBytes;
- PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
- }
- }
-
- numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
- stream->framesWritten += numFrames;
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /* FIXME: an underflow could happen here */
-
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * stream->outputFrameSizeBytes;
- stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
- IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
- }
-
-error1:
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- /* FIXME: an overflow could happen here */
-
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * stream->inputFrameSizeBytes;
- stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputBufferSizeBytes;
- IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
- }
-error2:
-
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
- }
-
- if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
- {
- /* don't return completed until the buffer processor has been drained */
- return paContinue;
- }
- else
- {
- return stream->callbackResult;
- }
-}
-/*******************************************************************/
-
-static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
-{
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- long bytesEmpty;
- hr = QueryOutputSpace( stream, &bytesEmpty );
- if (hr != DS_OK) return hr;
- if( bytesEmpty == 0 ) return DS_OK;
- // Lock free space in the DS
- hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes,
- bytesEmpty, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy the buffer into the DS
- ZeroMemory(lpbuf1, dwsize1);
- if(lpbuf2 != NULL)
- {
- ZeroMemory(lpbuf2, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + dwsize1 + dwsize2) % stream->outputBufferSizeBytes;
- IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
-
- stream->finalZeroBytesWritten += dwsize1 + dwsize2;
- }
- return hr;
-}
-
-
-static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
-{
- PaWinDsStream *stream;
- int isFinished = 0;
-
- /* suppress unused variable warnings */
- (void) uID;
- (void) uMsg;
- (void) dw1;
- (void) dw2;
-
- stream = (PaWinDsStream *) dwUser;
- if( stream == NULL ) return;
-
- if( stream->isActive )
- {
- if( stream->abortProcessing )
- {
- isFinished = 1;
- }
- else if( stream->stopProcessing )
- {
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- ZeroAvailableOutputSpace( stream );
- if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
- {
- /* once we've flushed the whole output buffer with zeros we know all data has been played */
- isFinished = 1;
- }
- }
- else
- {
- isFinished = 1;
- }
- }
- else
- {
- int callbackResult = TimeSlice( stream );
- if( callbackResult != paContinue )
- {
- /* FIXME implement handling of paComplete and paAbort if possible
- At the moment this should behave as if paComplete was called and
- flush the buffer.
- */
-
- stream->stopProcessing = 1;
- }
- }
-
- if( isFinished )
- {
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
-
- stream->isActive = 0; /* don't set this until the stream really is inactive */
- SetEvent( stream->processingCompleted );
- }
- }
-}
-
-#ifndef PA_WIN_DS_USE_WMME_TIMER
-
-#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
-
-static void CALLBACK WaitableTimerAPCProc(
- LPVOID lpArg, // Data value
- DWORD dwTimerLowValue, // Timer low value
- DWORD dwTimerHighValue ) // Timer high value
-
-{
- (void)dwTimerLowValue;
- (void)dwTimerHighValue;
-
- TimerCallback( 0, 0, (DWORD_PTR)lpArg, 0, 0 );
-}
-
-#endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
-
-
-PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
-{
- PaWinDsStream *stream = (PaWinDsStream *)pArg;
- LARGE_INTEGER dueTime;
- int timerPeriodMs;
-
- timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
- if( timerPeriodMs < 1 )
- timerPeriodMs = 1;
-
-#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- assert( stream->waitableTimer != NULL );
-
- /* invoke first timeout immediately */
- dueTime.LowPart = timerPeriodMs * 1000 * 10;
- dueTime.HighPart = 0;
-
- /* tick using waitable timer */
- if( SetWaitableTimer( stream->waitableTimer, &dueTime, timerPeriodMs, WaitableTimerAPCProc, pArg, FALSE ) != 0 )
- {
- DWORD wfsoResult = 0;
- do
- {
- /* wait for processingCompleted to be signaled or our timer APC to be called */
- wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, timerPeriodMs * 10, /* alertable = */ TRUE );
-
- }while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION );
- }
-
- CancelWaitableTimer( stream->waitableTimer );
-
-#else
-
- /* tick using WaitForSingleObject timeout */
- while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT )
- {
- TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 );
- }
-#endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
-
- SetEvent( stream->processingThreadCompleted );
-
- return 0;
-}
-
-#endif /* !PA_WIN_DS_USE_WMME_TIMER */
-
-/***********************************************************************************
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
-*/
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- CloseHandle( stream->processingCompleted );
-
-#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- if( stream->waitableTimer != NULL )
- CloseHandle( stream->waitableTimer );
-#endif
-
-#ifndef PA_WIN_DS_USE_WMME_TIMER
- CloseHandle( stream->processingThreadCompleted );
-#endif
-
- // Cleanup the sound buffers
- if( stream->pDirectSoundOutputBuffer )
- {
- IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
- IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
- stream->pDirectSoundOutputBuffer = NULL;
- }
-
- if( stream->pDirectSoundPrimaryBuffer )
- {
- IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
- stream->pDirectSoundPrimaryBuffer = NULL;
- }
-
- if( stream->pDirectSoundInputBuffer )
- {
- IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
- IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
- stream->pDirectSoundInputBuffer = NULL;
- }
-
- if( stream->pDirectSoundCapture )
- {
- IDirectSoundCapture_Release( stream->pDirectSoundCapture );
- stream->pDirectSoundCapture = NULL;
- }
-
- if( stream->pDirectSound )
- {
- IDirectSound_Release( stream->pDirectSound );
- stream->pDirectSound = NULL;
- }
-
-#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- if( stream->pDirectSoundFullDuplex8 )
- {
- IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
- stream->pDirectSoundFullDuplex8 = NULL;
- }
-#endif
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-/***********************************************************************************/
-static HRESULT ClearOutputBuffer( PaWinDsStream *stream )
-{
- PaError result = paNoError;
- unsigned char* pDSBuffData;
- DWORD dwDataLen;
- HRESULT hr;
-
- hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
- DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- return hr;
-
- // Lock the DS buffer
- if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
- &dwDataLen, NULL, 0, 0)) != DS_OK )
- return hr;
-
- // Zero the DS buffer
- ZeroMemory(pDSBuffData, dwDataLen);
- // Unlock the DS buffer
- if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
- return hr;
-
- // 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.
- if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
- &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK)
- return hr;
-
- /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
-
- return DS_OK;
-}
-
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
-
- stream->callbackResult = paContinue;
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- ResetEvent( stream->processingCompleted );
-
-#ifndef PA_WIN_DS_USE_WMME_TIMER
- ResetEvent( stream->processingThreadCompleted );
-#endif
-
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- // Start the buffer capture
- if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
- {
- hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
- }
-
- DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
- stream->framesWritten = 0;
- stream->callbackFlags = 0;
-
- stream->abortProcessing = 0;
- stream->stopProcessing = 0;
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- QueryPerformanceCounter( &stream->previousPlayTime );
- stream->finalZeroBytesWritten = 0;
-
- hr = ClearOutputBuffer( stream );
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
-
- if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) )
- {
- stream->callbackFlags = paPrimingOutput;
-
- TimeSlice( stream );
- /* we ignore the return value from TimeSlice here and start the stream as usual.
- The first timer callback will detect if the callback has completed. */
-
- stream->callbackFlags = 0;
- }
-
- // Start the buffer playback in a loop.
- if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here
- {
- hr = IDirectSoundBuffer_Play( stream->pDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING );
- DBUG(("PaHost_StartOutput: IDirectSoundBuffer_Play returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- stream->outputIsRunning = TRUE;
- }
- }
-
- if( stream->streamRepresentation.streamCallback )
- {
- TIMECAPS timecaps;
- int timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
- if( timerPeriodMs < 1 )
- timerPeriodMs = 1;
-
- /* set windows scheduler granularity only as fine as needed, no finer */
- /* Although this is not fully documented by MS, it appears that
- timeBeginPeriod() affects the scheduling granulatity of all timers
- including Waitable Timer Objects. So we always call timeBeginPeriod, whether
- we're using an MM timer callback via timeSetEvent or not.
- */
- assert( stream->systemTimerResolutionPeriodMs == 0 );
- if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) ) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 )
- {
- /* aim for resolution 4 times higher than polling rate */
- stream->systemTimerResolutionPeriodMs = (UINT)((stream->pollingPeriodSeconds * MSECS_PER_SECOND) * .25);
- if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin )
- stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin;
- if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax )
- stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMax;
-
- if( timeBeginPeriod( stream->systemTimerResolutionPeriodMs ) != MMSYSERR_NOERROR )
- stream->systemTimerResolutionPeriodMs = 0; /* timeBeginPeriod failed, so we don't need to call timeEndPeriod() later */
- }
-
-
-#ifdef PA_WIN_DS_USE_WMME_TIMER
- /* Create timer that will wake us up so we can fill the DSound buffer. */
- /* We have deprecated timeSetEvent because all MM timer callbacks
- are serialised onto a single thread. Which creates problems with multiple
- PA streams, or when also using timers for other time critical tasks
- */
- stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback,
- (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
-
- if( stream->timerID == 0 )
- {
- stream->isActive = 0;
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
-#else
- /* Create processing thread which calls TimerCallback */
-
- stream->processingThread = CREATE_THREAD( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
- if( !stream->processingThread )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
-
- if( !SetThreadPriority( stream->processingThread, THREAD_PRIORITY_TIME_CRITICAL ) )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
-#endif
- }
-
- stream->isActive = 1;
- stream->isStarted = 1;
-
- assert( result == paNoError );
- return result;
-
-error:
-
- if( stream->pDirectSoundOutputBuffer != NULL && stream->outputIsRunning )
- IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
- stream->outputIsRunning = FALSE;
-
-#ifndef PA_WIN_DS_USE_WMME_TIMER
- if( stream->processingThread )
- {
-#ifdef CLOSE_THREAD_HANDLE
- CLOSE_THREAD_HANDLE( stream->processingThread ); /* Delete thread. */
-#endif
- stream->processingThread = NULL;
- }
-#endif
-
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError StopStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
- int timeoutMsec;
-
- if( stream->streamRepresentation.streamCallback )
- {
- stream->stopProcessing = 1;
-
- /* Set timeout at 4 times maximum time we might wait. */
- timeoutMsec = (int) (4 * MSECS_PER_SECOND * (stream->hostBufferSizeFrames / stream->streamRepresentation.streamInfo.sampleRate));
-
- WaitForSingleObject( stream->processingCompleted, timeoutMsec );
- }
-
-#ifdef PA_WIN_DS_USE_WMME_TIMER
- if( stream->timerID != 0 )
- {
- timeKillEvent(stream->timerID); /* Stop callback timer. */
- stream->timerID = 0;
- }
-#else
- if( stream->processingThread )
- {
- if( WaitForSingleObject( stream->processingThreadCompleted, 30*100 ) == WAIT_TIMEOUT )
- return paUnanticipatedHostError;
-
-#ifdef CLOSE_THREAD_HANDLE
- CloseHandle( stream->processingThread ); /* Delete thread. */
- stream->processingThread = NULL;
-#endif
-
- }
-#endif
-
- if( stream->systemTimerResolutionPeriodMs > 0 ){
- timeEndPeriod( stream->systemTimerResolutionPeriodMs );
- stream->systemTimerResolutionPeriodMs = 0;
- }
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- // Stop the buffer playback
- if( stream->pDirectSoundOutputBuffer != NULL )
- {
- stream->outputIsRunning = FALSE;
- // FIXME: what happens if IDirectSoundBuffer_Stop returns an error?
- hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
-
- if( stream->pDirectSoundPrimaryBuffer )
- IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */
- }
- }
-
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- // Stop the buffer capture
- if( stream->pDirectSoundInputBuffer != NULL )
- {
- // FIXME: what happens if IDirectSoundCaptureBuffer_Stop returns an error?
- hr = IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
- }
- }
-
- stream->isStarted = 0;
-
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError AbortStream( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- stream->abortProcessing = 1;
- return StopStream( s );
-}
-
-
-/***********************************************************************************/
-static PaError IsStreamStopped( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return !stream->isStarted;
-}
-
-
-/***********************************************************************************/
-static PaError IsStreamActive( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return stream->isActive;
-}
-
-/***********************************************************************************/
-static PaTime GetStreamTime( PaStream *s )
-{
- /* suppress unused variable warnings */
- (void) s;
-
- return PaUtil_GetTime();
-}
-
-
-/***********************************************************************************/
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-/***********************************************************************************
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-/***********************************************************************************/
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-/***********************************************************************************/
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-/***********************************************************************************/
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}