diff options
author | sanine <sanine.not@pm.me> | 2022-08-27 23:52:56 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-08-27 23:52:56 -0500 |
commit | a4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 (patch) | |
tree | 13bd5bfa15e6fea2a12f176bae79adf9c6fd0933 /portaudio/src/hostapi/dsound/pa_win_ds.c | |
parent | bde3e4f1bb7b8f8abca0884a7d994ee1c17a66b1 (diff) |
add plibsys
Diffstat (limited to 'portaudio/src/hostapi/dsound/pa_win_ds.c')
-rw-r--r-- | portaudio/src/hostapi/dsound/pa_win_ds.c | 3259 |
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( ¤tTime ); - 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; -} |