Update portaudio to the current v19 development snapshot. It contains a LOT of changes on WASAPI.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2751 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
gigaherz 2010-03-19 14:00:20 +00:00
parent df401326aa
commit 1d7244d200
17 changed files with 7898 additions and 7890 deletions

View File

@ -41,3 +41,7 @@ PaUtil_InitializeX86PlainConverters @52
PaAsio_GetInputChannelName @53
PaAsio_GetOutputChannelName @54
PaUtil_SetDebugPrintFunction @55
PaWasapi_GetDeviceDefaultFormat @56
PaWasapi_GetDeviceRole @57
PaWasapi_ThreadPriorityBoost @58
PaWasapi_ThreadPriorityRevert @59

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Version="9,00"
Name="portaudio"
ProjectGUID="{0A18A071-125E-442F-AFF7-A3F68ABECF99}"
RootNamespace="portaudio"
@ -1409,7 +1409,7 @@
Name="wasapi"
>
<File
RelativePath="..\..\src\hostapi\wasapi\pa_win_wasapi.cpp"
RelativePath="..\..\src\hostapi\wasapi\pa_win_wasapi.c"
>
</File>
</Filter>
@ -1480,6 +1480,10 @@
RelativePath="..\..\include\pa_win_ds.h"
>
</File>
<File
RelativePath="..\..\include\pa_win_wasapi.h"
>
</File>
<File
RelativePath="..\..\include\pa_win_waveformat.h"
>

View File

@ -1,39 +1,43 @@
EXPORTS
;
Pa_GetVersion @1
Pa_GetVersionText @2
Pa_GetErrorText @3
Pa_Initialize @4
Pa_Terminate @5
Pa_GetHostApiCount @6
Pa_GetDefaultHostApi @7
Pa_GetHostApiInfo @8
Pa_HostApiTypeIdToHostApiIndex @9
Pa_HostApiDeviceIndexToDeviceIndex @10
Pa_GetLastHostErrorInfo @11
Pa_GetDeviceCount @12
Pa_GetDefaultInputDevice @13
Pa_GetDefaultOutputDevice @14
Pa_GetDeviceInfo @15
Pa_IsFormatSupported @16
Pa_OpenStream @17
Pa_OpenDefaultStream @18
Pa_CloseStream @19
Pa_SetStreamFinishedCallback @20
Pa_StartStream @21
Pa_StopStream @22
Pa_AbortStream @23
Pa_IsStreamStopped @24
Pa_IsStreamActive @25
Pa_GetStreamInfo @26
Pa_GetStreamTime @27
Pa_GetStreamCpuLoad @28
Pa_ReadStream @29
Pa_WriteStream @30
Pa_GetStreamReadAvailable @31
Pa_GetStreamWriteAvailable @32
Pa_GetSampleSize @33
Pa_Sleep @34
PaUtil_InitializeX86PlainConverters @52
PaUtil_SetDebugPrintFunction @55
EXPORTS
;
Pa_GetVersion @1
Pa_GetVersionText @2
Pa_GetErrorText @3
Pa_Initialize @4
Pa_Terminate @5
Pa_GetHostApiCount @6
Pa_GetDefaultHostApi @7
Pa_GetHostApiInfo @8
Pa_HostApiTypeIdToHostApiIndex @9
Pa_HostApiDeviceIndexToDeviceIndex @10
Pa_GetLastHostErrorInfo @11
Pa_GetDeviceCount @12
Pa_GetDefaultInputDevice @13
Pa_GetDefaultOutputDevice @14
Pa_GetDeviceInfo @15
Pa_IsFormatSupported @16
Pa_OpenStream @17
Pa_OpenDefaultStream @18
Pa_CloseStream @19
Pa_SetStreamFinishedCallback @20
Pa_StartStream @21
Pa_StopStream @22
Pa_AbortStream @23
Pa_IsStreamStopped @24
Pa_IsStreamActive @25
Pa_GetStreamInfo @26
Pa_GetStreamTime @27
Pa_GetStreamCpuLoad @28
Pa_ReadStream @29
Pa_WriteStream @30
Pa_GetStreamReadAvailable @31
Pa_GetStreamWriteAvailable @32
Pa_GetSampleSize @33
Pa_Sleep @34
PaUtil_InitializeX86PlainConverters @52
PaUtil_SetDebugPrintFunction @55
PaWasapi_GetDeviceDefaultFormat @56
PaWasapi_GetDeviceRole @57
PaWasapi_ThreadPriorityBoost @58
PaWasapi_ThreadPriorityRevert @59

File diff suppressed because it is too large Load Diff

View File

@ -38,16 +38,17 @@ AC_ARG_WITH(asihpi,
AC_ARG_WITH(winapi,
AS_HELP_STRING([--with-winapi],
[Select Windows API support (@<:@wmme|directx|asio|wdmks@:>@@<:@,...@:>@) @<:@wmme@:>@]),
[Select Windows API support (@<:@wmme|directx|asio|wasapi|wdmks@:>@@<:@,...@:>@) @<:@wmme@:>@]),
[with_winapi=$withval], [with_winapi="wmme"])
case "$target_os" in *mingw* | *cygwin*)
with_wmme=no
with_directx=no
with_asio=no
with_wasapi=no
with_wdmks=no
for api in $(echo $with_winapi | sed 's/,/ /g'); do
case "$api" in
wmme|directx|asio|wdmks)
wmme|directx|asio|wasapi|wdmks)
eval with_$api=yes
;;
*)
@ -222,7 +223,7 @@ case "${host_os}" in
PADLL="portaudio.dll"
THREAD_CFLAGS="-mthreads"
SHARED_FLAGS="-shared"
CFLAGS="$CFLAGS -I\$(top_srcdir)/include -DPA_NO_WMME -DPA_NO_ASIO -DPA_NO_WDMKS -DPA_NO_DS"
CFLAGS="$CFLAGS -I\$(top_srcdir)/include -DPA_NO_WMME -DPA_NO_ASIO -DPA_NO_WDMKS -DPA_NO_DS -DPA_NO_WASAPI"
if [[ "x$with_directx" = "xyes" ]]; then
DXDIR="$with_dxdir"
@ -259,6 +260,13 @@ case "${host_os}" in
DLL_LIBS="${DLL_LIBS} -lwinmm"
CFLAGS="$CFLAGS -I\$(top_srcdir)/src/common -UPA_NO_WMME"
fi
if [[ "x$with_wasapi" = "xyes" ]]; then
add_objects src/hostapi/wasapi/pa_win_wasapi.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_waveformat.o
LIBS="-lwinmm -lm -lole32 -luuid"
DLL_LIBS="${DLL_LIBS} -lwinmm"
CFLAGS="$CFLAGS -I\$(top_srcdir)/src/common -I\$(top_srcdir)/src/hostapi/wasapi/mingw-include -UPA_NO_WASAPI"
fi
;;
cygwin* )
@ -396,11 +404,13 @@ case "$target_os" in
test "x$with_directx" = "xyes" && with_directx="$with_directx (${with_dxdir})"
test "x$with_wdmks" = "xyes" && with_wdmks="$with_wdmks (${with_dxdir})"
test "x$with_asio" = "xyes" && with_asio="$with_asio (${with_asiodir})"
test "x$with_wasapi" = "xyes"
AC_MSG_RESULT([
WMME ........................ $with_wmme
DSound ...................... $with_directx
WDMKS ....................... $with_wdmks
ASIO ........................ $with_asio
WASAPI ...................... $with_wasapi
WDMKS ....................... $with_wdmks
])
;;
*darwin*)

View File

@ -0,0 +1,268 @@
#ifndef PA_WIN_WASAPI_H
#define PA_WIN_WASAPI_H
/*
* $Id: $
* PortAudio Portable Real-Time Audio Library
* DirectSound specific extensions
*
* Copyright (c) 1999-2007 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 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 public_header
@brief WASAPI-specific PortAudio API extension header file.
*/
#include "portaudio.h"
#include "pa_win_waveformat.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/* Setup flags */
typedef enum PaWasapiFlags
{
/* puts WASAPI into exclusive mode */
paWinWasapiExclusive = (1 << 0),
/* allows to skip internal PA processing completely */
paWinWasapiRedirectHostProcessor = (1 << 1),
/* assigns custom channel mask */
paWinWasapiUseChannelMask = (1 << 2),
/* selects non-Event driven method of data read/write
Note: WASAPI Event driven core is capable of 2ms latency!!!, but Polling
method can only provide 15-20ms latency. */
paWinWasapiPolling = (1 << 3),
/* forces custom thread priority setting. must be used if PaWasapiStreamInfo::threadPriority
is set to custom value. */
paWinWasapiThreadPriority = (1 << 4)
}
PaWasapiFlags;
#define paWinWasapiExclusive (paWinWasapiExclusive)
#define paWinWasapiRedirectHostProcessor (paWinWasapiRedirectHostProcessor)
#define paWinWasapiUseChannelMask (paWinWasapiUseChannelMask)
#define paWinWasapiPolling (paWinWasapiPolling)
#define paWinWasapiThreadPriority (paWinWasapiThreadPriority)
/* Host processor. Allows to skip internal PA processing completely.
You must set paWinWasapiRedirectHostProcessor flag to PaWasapiStreamInfo::flags member
in order to have host processor redirected to your callback.
Use with caution! inputFrames and outputFrames depend solely on final device setup (buffer
size is just recommendation) but are not changing during run-time once stream is started.
*/
typedef void (*PaWasapiHostProcessorCallback) (void *inputBuffer, long inputFrames,
void *outputBuffer, long outputFrames,
void *userData);
/* Device role */
typedef enum PaWasapiDeviceRole
{
eRoleRemoteNetworkDevice = 0,
eRoleSpeakers,
eRoleLineLevel,
eRoleHeadphones,
eRoleMicrophone,
eRoleHeadset,
eRoleHandset,
eRoleUnknownDigitalPassthrough,
eRoleSPDIF,
eRoleHDMI,
eRoleUnknownFormFactor
}
PaWasapiDeviceRole;
/* Thread priority */
typedef enum PaWasapiThreadPriority
{
eThreadPriorityNone = 0,
eThreadPriorityAudio, //!< Default for Shared mode.
eThreadPriorityCapture,
eThreadPriorityDistribution,
eThreadPriorityGames,
eThreadPriorityPlayback,
eThreadPriorityProAudio, //!< Default for Exclusive mode.
eThreadPriorityWindowManager
}
PaWasapiThreadPriority;
/* Stream descriptor. */
typedef struct PaWasapiStreamInfo
{
unsigned long size; /**< sizeof(PaWasapiStreamInfo) */
PaHostApiTypeId hostApiType; /**< paWASAPI */
unsigned long version; /**< 1 */
unsigned long flags; /**< collection of PaWasapiFlags */
/* Support for WAVEFORMATEXTENSIBLE channel masks. If flags contains
paWinWasapiUseChannelMask this allows you to specify which speakers
to address in a multichannel stream. Constants for channelMask
are specified in pa_win_waveformat.h. Will be used only if
paWinWasapiUseChannelMask flag is specified.
*/
PaWinWaveFormatChannelMask channelMask;
/* Delivers raw data to callback obtained from GetBuffer() methods skipping
internal PortAudio processing inventory completely. userData parameter will
be the same that was passed to Pa_OpenStream method. Will be used only if
paWinWasapiRedirectHostProcessor flag is specified.
*/
PaWasapiHostProcessorCallback hostProcessorOutput;
PaWasapiHostProcessorCallback hostProcessorInput;
/* Specifies thread priority explicitly. Will be used only if paWinWasapiThreadPriority flag
is specified.
Please note, if Input/Output streams are opened simultaniously (Full-Duplex mode)
you shall specify same value for threadPriority or othervise one of the values will be used
to setup thread priority.
*/
PaWasapiThreadPriority threadPriority;
}
PaWasapiStreamInfo;
/** Returns default sound format for device. Format is represented by PaWinWaveFormat or
WAVEFORMATEXTENSIBLE structure.
@param pFormat pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.
@param nFormatSize pize of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.
@param nDevice device index.
@return A non-negative value indicating the number of bytes copied into format decriptor
or, a PaErrorCode (which are always negative) if PortAudio is not initialized
or an error is encountered.
*/
int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice );
/** Returns device role (PaWasapiDeviceRole enum).
@param nDevice device index.
@return A non-negative value indicating device role or, a PaErrorCode (which are always negative)
if PortAudio is not initialized or an error is encountered.
*/
int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice );
/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread
which makes calls to Pa_WriteStream/Pa_ReadStream.
@param hTask a handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority
method to revert thread priority to initial state.
@param nPriorityClass an Id of thread priority of PaWasapiThreadPriority type. Specifying
eThreadPriorityNone does nothing.
@return Error code indicating success or failure.
@see PaWasapi_RevertThreadPriority
*/
PaError PaWasapi_ThreadPriorityBoost( void **hTask, PaWasapiThreadPriority nPriorityClass );
/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread
which makes calls to Pa_WriteStream/Pa_ReadStream.
@param hTask Task handle obtained by PaWasapi_BoostThreadPriority method.
@return Error code indicating success or failure.
@see PaWasapi_BoostThreadPriority
*/
PaError PaWasapi_ThreadPriorityRevert( void *hTask );
/*
IMPORTANT:
WASAPI is implemented for Callback and Blocking interfaces. It supports Shared and Exclusive
share modes.
Exclusive Mode:
Exclusive mode allows to deliver audio data directly to hardware bypassing
software mixing.
Exclusive mode is specified by 'paWinWasapiExclusive' flag.
Callback Interface:
Provides best audio quality with low latency. Callback interface is implemented in
two versions:
1) Event-Driven:
This is the most powerful WASAPI implementation which is capable to provides glitch-free
audio at 2ms latency in Exclusive mode. Lowest possible latency for this mode is
usually - 2ms for HD Audio class audio chips (including on-board audio, 2ms was achieved
on Realtek ALC888/S/T). For Shared mode latency can not go lower than 20ms.
2) Poll-Driven:
Polling is another 2-nd method to operate with WASAPI. It is less efficient than Event-Driven
and provides latency at around 12-13ms. Polling must be used to overcome a system bug
under Windows Vista x64 when application is WOW64(32-bit) and Event-Driven method simply times
out (event handle is never signalled on buffer completion). Please note, such Vista bug
does not exist in Windows 7 x64.
Polling is setup by speciying 'paWinWasapiPolling' flag.
Thread priority can be boosted by specifying 'paWinWasapiBlockingThreadPriorityPro' flag.
Blocking Interface:
Blocking interface is implemented but due to above described Poll-Driven method can not
deliver low latency audio. Specifying too low latency in Shared mode will result in
distorted audio although Exclusive mode adds stability.
Pa_IsFormatSupported:
To check format with correct Share Mode (Exclusive/Shared) you must supply
PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of
PaStreamParameters::hostApiSpecificStreamInfo structure.
Pa_OpenStream:
To set desired Share Mode (Exclusive/Shared) you must supply
PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of
PaStreamParameters::hostApiSpecificStreamInfo structure.
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_WIN_WASAPI_H */

View File

@ -53,17 +53,21 @@
@todo Consider reentrancy and possibly corrupted strdump buffer.
*/
#include <stdio.h>
#include <stdarg.h>
#include "pa_debugprint.h"
// for OutputDebugStringA
#if defined(_MSC_VER) && defined(PA_ENABLE_MSVC_DEBUG_OUTPUT)
#define WIN32_LEAN_AND_MEAN // exclude rare headers
#include "windows.h"
#endif
// User callback
static PaUtilLogCallback userCB = NULL;
static PaUtilLogCallback userCB=0;
// Sets user callback
void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb)
{
userCB = cb;
@ -72,41 +76,51 @@ void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb)
/*
If your platform doesnt have vsnprintf, you are stuck with a
VERY dangerous alternative, vsprintf (with no n)
*/
#if _MSC_VER
/* Some Windows Mobile SDKs don't define vsnprintf but all define _vsnprintf (hopefully).
According to MSDN "vsnprintf is identical to _vsnprintf". So we use _vsnprintf with MSC.
*/
#define VSNPRINTF _vsnprintf
#if _MSC_VER
/* Some Windows Mobile SDKs don't define vsnprintf but all define _vsnprintf (hopefully).
According to MSDN "vsnprintf is identical to _vsnprintf". So we use _vsnprintf with MSC.
*/
#define VSNPRINTF _vsnprintf
#else
#define VSNPRINTF vsnprintf
#define VSNPRINTF vsnprintf
#endif
#define SIZEDUMP 1024
#define PA_LOG_BUF_SIZE 2048
void PaUtil_DebugPrint( const char *format, ... )
{
// Optional logging into Output console of Visual Studio
#if defined(_MSC_VER) && defined(PA_ENABLE_MSVC_DEBUG_OUTPUT)
{
char buf[PA_LOG_BUF_SIZE];
va_list ap;
va_start(ap, format);
VSNPRINTF(buf, sizeof(buf), format, ap);
buf[sizeof(buf)-1] = 0;
OutputDebugStringA(buf);
va_end(ap);
}
#endif
if (userCB)
// Output to User-Callback
if (userCB != NULL)
{
char strdump[SIZEDUMP];
char strdump[PA_LOG_BUF_SIZE];
va_list ap;
va_start( ap, format );
VSNPRINTF( strdump, SIZEDUMP, format, ap );
strdump[SIZEDUMP-1] = 0;
va_start(ap, format);
VSNPRINTF(strdump, sizeof(strdump), format, ap);
strdump[sizeof(strdump)-1] = 0;
userCB(strdump);
va_end( ap );
va_end(ap);
}
else
// Standard output to stderr
{
va_list ap;
va_start( ap, format );
vfprintf( stderr, format, ap );
va_end( ap );
fflush( stderr );
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fflush(stderr);
}
}

View File

@ -4062,3 +4062,4 @@ PaError PaAsio_SetStreamSampleRate( PaStream* s, double sampleRate )
return ValidateAndSetSampleRate( sampleRate );
}

View File

@ -530,12 +530,27 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn
{
PaError result = paNoError;
int i;
PaMacAUHAL *auhalHostApi;
PaMacAUHAL *auhalHostApi = NULL;
PaDeviceInfo *deviceInfoArray;
int unixErr;
VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
SInt32 major;
SInt32 minor;
Gestalt(gestaltSystemVersionMajor, &major);
Gestalt(gestaltSystemVersionMinor, &minor);
// Starting with 10.6 systems, the HAL notification thread is created internally
if (major == 10 && minor >= 6) {
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
OSStatus osErr = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
if (osErr != noErr) {
goto error;
}
}
unixErr = initializeXRunListenerList();
if( 0 != unixErr ) {
return UNIX_ERR(unixErr);

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_win_ds.c 1434 2009-12-09 01:55:50Z rossb $
* $Id: pa_win_ds.c 1450 2010-02-03 00:28:29Z rossb $
* Portable Audio I/O Library DirectSound implementation
*
* Authors: Phil Burk, Robert Marsanyi & Ross Bencina
@ -70,15 +70,22 @@
*/
#include <assert.h>
#include <stdio.h>
#include <string.h> /* strlen() */
#include <initguid.h> /* make sure ds guids get defined */
#include <windows.h>
#include <objbase.h>
/*
We are only using DX3 in here, no need to polute the namespace - davidv
Use the earliest version of DX required, no need to polute 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>
@ -223,9 +230,13 @@ typedef struct PaWinDsStream
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;
@ -236,9 +247,8 @@ typedef struct PaWinDsStream
UINT previousPlayCursor;
UINT outputUnderflowCount;
BOOL outputIsRunning;
/* use double which lets us can play for several thousand years with enough precision */
double dsw_framesWritten;
double framesPlayed;
INT finalZeroBytesWritten; /* used to determine when we've flushed the whole buffer */
/* Input */
LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer;
@ -253,6 +263,10 @@ typedef struct PaWinDsStream
double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
PaStreamCallbackFlags callbackFlags;
PaStreamFlags streamFlags;
int callbackResult;
HANDLE processingCompleted;
/* FIXME - move all below to PaUtilStreamRepresentation */
volatile int isStarted;
@ -1086,9 +1100,9 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
if( result != paNoError )
goto error;
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
{
@ -1418,20 +1432,126 @@ static int PaWinDs_GetMinLatencyFrames( double sampleRate )
}
static HRESULT InitInputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
#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, &stream->pDirectSoundInputBuffer );
if( hr == DS_OK )
hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, &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;
stream->bytesPerInputFrame = nChannels * Pa_GetSampleSize(sampleFormat);
if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
return result;
}
stream->inputSize = bytesPerBuffer;
// ----------------------------------------------------------------------
// Setup the secondary buffer description
ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
captureDesc.dwSize = sizeof(DSCBUFFERDESC);
captureDesc.dwFlags = 0;
captureDesc.dwFlags = 0;
captureDesc.dwBufferBytes = bytesPerBuffer;
captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
@ -1457,28 +1577,20 @@ static HRESULT InitInputBuffer( PaWinDsStream *stream, PaSampleFormat sampleForm
}
static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
{
/** @todo FIXME: if InitOutputBuffer returns an error I'm not sure it frees all resources cleanly */
DWORD dwDataLen;
DWORD playCursor;
HRESULT result;
LPDIRECTSOUNDBUFFER pPrimaryBuffer;
HWND hWnd;
HRESULT hr;
PaWinWaveFormat waveFormat;
DSBUFFERDESC primaryDesc;
DSBUFFERDESC secondaryDesc;
unsigned char* pDSBuffData;
LARGE_INTEGER counterFrequency;
int bytesPerSample = Pa_GetSampleSize(sampleFormat);
stream->outputBufferSizeBytes = bytesPerBuffer;
stream->outputIsRunning = FALSE;
stream->outputUnderflowCount = 0;
stream->dsw_framesWritten = 0;
stream->bytesPerOutputFrame = nChannels * bytesPerSample;
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
@ -1493,7 +1605,7 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor
hWnd = GetDesktopWindow();
// Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
// Exclusize also prevents unexpected sounds from other apps during a performance.
// exclusive also prevents unexpected sounds from other apps during a performance.
if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
{
@ -1511,7 +1623,8 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor
primaryDesc.lpwfxFormat = NULL;
// Create the buffer
if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
&primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
&primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
goto error;
// Set the primary buffer's format
@ -1520,51 +1633,38 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor
sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
nFrameRate, channelMask );
if( IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
{
PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK) return result;
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.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
secondaryDesc.dwBufferBytes = bytesPerBuffer;
secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
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) return result;
// Lock the DS buffer
if ((result = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
&dwDataLen, NULL, 0, 0)) != DS_OK) return result;
// Zero the DS buffer
ZeroMemory(pDSBuffData, dwDataLen);
// Unlock the DS buffer
if ((result = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
if( QueryPerformanceFrequency( &counterFrequency ) )
{
int framesInBuffer = bytesPerBuffer / (nChannels * bytesPerSample);
stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
}
else
{
stream->perfCounterTicksPerBuffer.QuadPart = 0;
}
// Let DSound set the starting write position because if we set it to zero, it looks like the
// buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
&playCursor, &stream->outputBufferWriteOffsetBytes );
if( hr != DS_OK )
{
return hr;
}
stream->dsw_framesWritten = stream->outputBufferWriteOffsetBytes / stream->bytesPerOutputFrame;
/* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
&secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
goto error;
return DS_OK;
error:
if( stream->pDirectSoundPrimaryBuffer )
{
IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
stream->pDirectSoundPrimaryBuffer = NULL;
}
return result;
}
/***********************************************************************************/
@ -1583,6 +1683,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
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;
@ -1719,6 +1821,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
&winDsHostApi->blockingStreamInterface, streamCallback, userData );
}
streamRepresentationIsInitialized = 1;
stream->streamFlags = streamFlags;
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
@ -1761,6 +1867,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result != paNoError )
goto error;
bufferProcessorIsInitialized = 1;
stream->streamRepresentation.streamInfo.inputLatency =
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
@ -1772,12 +1879,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* DirectSound specific initialization */
{
HRESULT hr;
int bytesPerDirectSoundBuffer;
int bytesPerDirectSoundInputBuffer, bytesPerDirectSoundOutputBuffer;
int userLatencyFrames;
int minLatencyFrames;
unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
stream->timerID = 0;
stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
if( stream->processingCompleted == NULL )
{
result = paInsufficientMemory;
goto error;
}
/* Get system minimum latency. */
minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
@ -1812,55 +1927,50 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
}
/* set up i/o parameters */
/* ------------------ OUTPUT */
if( outputParameters )
{
LARGE_INTEGER counterFrequency;
/*
PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
*/
int bytesPerSample = Pa_GetSampleSize(hostOutputSampleFormat);
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
bytesPerDirectSoundOutputBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundOutputBuffer < DSBSIZE_MIN )
{
result = paBufferTooSmall;
goto error;
}
else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
else if( bytesPerDirectSoundOutputBuffer > DSBSIZE_MAX )
{
result = paBufferTooBig;
goto error;
}
hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
((PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device])->lpGUID,
&stream->pDirectSound, NULL );
if( hr != DS_OK )
{
ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
goto error;
}
hr = InitOutputBuffer( stream,
hostOutputSampleFormat,
(unsigned long) (sampleRate + 0.5),
(WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer,
outputChannelMask );
DBUG(("InitOutputBuffer() returns %x\n", hr));
if( hr != DS_OK )
{
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
goto error;
}
/* Calculate value used in latency calculation to avoid real-time divides. */
stream->secondsPerHostByte = 1.0 /
(stream->bufferProcessor.bytesPerHostOutputSample *
outputChannelCount * sampleRate);
stream->outputBufferSizeBytes = bytesPerDirectSoundOutputBuffer;
stream->outputIsRunning = FALSE;
stream->outputUnderflowCount = 0;
stream->bytesPerOutputFrame = outputParameters->channelCount * bytesPerSample;
/* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
if( QueryPerformanceFrequency( &counterFrequency ) )
{
stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->framesPerDSBuffer) / integerSampleRate;
}
else
{
stream->perfCounterTicksPerBuffer.QuadPart = 0;
}
}
/* ------------------ INPUT */
@ -1872,32 +1982,86 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
*/
int bytesPerSample = Pa_GetSampleSize(hostInputSampleFormat);
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
bytesPerDirectSoundInputBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundInputBuffer < DSBSIZE_MIN )
{
result = paBufferTooSmall;
goto error;
}
else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
else if( bytesPerDirectSoundInputBuffer > DSBSIZE_MAX )
{
result = paBufferTooBig;
goto error;
}
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
((PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device])->lpGUID,
&stream->pDirectSoundCapture, NULL );
stream->bytesPerInputFrame = inputParameters->channelCount * bytesPerSample;
stream->inputSize = bytesPerDirectSoundInputBuffer;
}
/* 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, bytesPerDirectSoundInputBuffer,
inputChannelMask,
(PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
hostOutputSampleFormat,
(WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer,
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 isnt 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, bytesPerDirectSoundOutputBuffer,
outputChannelMask );
DBUG(("InitOutputBuffer() returns %x\n", hr));
if( hr != DS_OK )
{
ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
goto error;
}
}
if( inputParameters && !stream->pDirectSoundInputBuffer )
{
hr = InitInputBuffer( stream,
(PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
hostInputSampleFormat,
(unsigned long) (sampleRate + 0.5),
(WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer,
integerSampleRate,
(WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer,
inputChannelMask );
DBUG(("InitInputBuffer() returns %x\n", hr));
if( hr != DS_OK )
@ -1908,7 +2072,6 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
}
}
*s = (PaStream*)stream;
@ -1917,7 +2080,57 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
error:
if( stream )
{
if( stream->processingCompleted != NULL )
CloseHandle( stream->processingCompleted );
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;
}
@ -1942,9 +2155,11 @@ static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
{
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) )
@ -1973,8 +2188,6 @@ static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
}
/* Maintain frame output cursor. */
stream->framesPlayed += (bytesPlayed / stream->bytesPerOutputFrame);
}
numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
@ -2005,9 +2218,8 @@ static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
}
/***********************************************************************************/
static PaError Pa_TimeSlice( PaWinDsStream *stream )
static int TimeSlice( PaWinDsStream *stream )
{
PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/
long numFrames = 0;
long bytesEmpty = 0;
long bytesFilled = 0;
@ -2050,8 +2262,8 @@ static PaError Pa_TimeSlice( PaWinDsStream *stream )
}
// FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
framesToXfer = numInFramesReady = bytesFilled / stream->bytesPerInputFrame;
outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
framesToXfer = numInFramesReady = bytesFilled / stream->bytesPerInputFrame;
outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; // FIXME: this doesn't look right. we're calculating output latency in input branch. also secondsPerHostByte is only initialized for the output stream
/** @todo Check for overflow */
}
@ -2081,7 +2293,7 @@ static PaError Pa_TimeSlice( PaWinDsStream *stream )
/* 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();
timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency;
timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; // FIXME: QueryOutputSpace gets the playback position, we could use that (?)
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
@ -2098,8 +2310,9 @@ static PaError Pa_TimeSlice( PaWinDsStream *stream )
if (hresult != DS_OK)
{
ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
/* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
stream->callbackResult = paComplete;
goto error2;
}
@ -2126,8 +2339,9 @@ static PaError Pa_TimeSlice( PaWinDsStream *stream )
if (hresult != DS_OK)
{
ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
/* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
stream->callbackResult = paComplete;
goto error1;
}
@ -2144,8 +2358,7 @@ static PaError Pa_TimeSlice( PaWinDsStream *stream )
}
}
result = paContinue;
numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result );
numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
stream->framesWritten += numFrames;
if( stream->bufferProcessor.outputChannelCount > 0 )
@ -2156,7 +2369,6 @@ static PaError Pa_TimeSlice( PaWinDsStream *stream )
bytesProcessed = numFrames * stream->bytesPerOutputFrame;
stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
stream->dsw_framesWritten += numFrames;
}
error1:
@ -2171,11 +2383,18 @@ error1:
}
error2:
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
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;
}
return result;
}
/*******************************************************************/
@ -2187,7 +2406,7 @@ static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
DWORD dwsize1 = 0;
DWORD dwsize2 = 0;
long bytesEmpty;
hr = QueryOutputSpace( stream, &bytesEmpty ); // updates framesPlayed
hr = QueryOutputSpace( stream, &bytesEmpty );
if (hr != DS_OK) return hr;
if( bytesEmpty == 0 ) return DS_OK;
// Lock free space in the DS
@ -2205,15 +2424,17 @@ static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
// 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->dsw_framesWritten += bytesEmpty / stream->bytesPerOutputFrame;
stream->finalZeroBytesWritten += dwsize1 + dwsize2;
}
return hr;
}
static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
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;
@ -2228,37 +2449,45 @@ static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWO
{
if( stream->abortProcessing )
{
stream->isActive = 0;
isFinished = 1;
}
else if( stream->stopProcessing )
{
if( stream->bufferProcessor.outputChannelCount > 0 )
{
ZeroAvailableOutputSpace( stream );
/* clear isActive when all sound played */
if( stream->framesPlayed >= stream->framesWritten )
if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
{
stream->isActive = 0;
/* once we've flushed the whole output buffer with zeros we know all data has been played */
isFinished = 1;
}
}
else
{
stream->isActive = 0;
isFinished = 1;
}
}
else
{
if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */
int callbackResult = TimeSlice( stream );
if( callbackResult != paContinue )
{
/* FIXME implement handling of paComplete and paAbort if possible */
/* 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( !stream->isActive )
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 );
}
}
}
@ -2272,6 +2501,8 @@ static PaError CloseStream( PaStream* s )
PaError result = paNoError;
PaWinDsStream *stream = (PaWinDsStream*)s;
CloseHandle( stream->processingCompleted );
// Cleanup the sound buffers
if( stream->pDirectSoundOutputBuffer )
{
@ -2280,6 +2511,12 @@ static PaError CloseStream( PaStream* s )
stream->pDirectSoundOutputBuffer = NULL;
}
if( stream->pDirectSoundPrimaryBuffer )
{
IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
stream->pDirectSoundPrimaryBuffer = NULL;
}
if( stream->pDirectSoundInputBuffer )
{
IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
@ -2299,6 +2536,14 @@ static PaError CloseStream( PaStream* s )
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 );
@ -2307,17 +2552,54 @@ static PaError CloseStream( PaStream* s )
}
/***********************************************************************************/
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 );
if( stream->bufferProcessor.inputChannelCount > 0 )
{
// Start the buffer playback
// Start the buffer capture
if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
{
hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
@ -2341,15 +2623,10 @@ static PaError StartStream( PaStream *s )
if( stream->bufferProcessor.outputChannelCount > 0 )
{
/* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */
result = Pa_TimeSlice( stream );
if( result != paNoError ) return result; // FIXME - what if finished?
QueryPerformanceCounter( &stream->previousPlayTime );
stream->previousPlayCursor = 0;
stream->framesPlayed = 0;
hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
DBUG(("PaHost_StartOutput: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
stream->finalZeroBytesWritten = 0;
hr = ClearOutputBuffer( stream );
if( hr != DS_OK )
{
result = paUnanticipatedHostError;
@ -2357,6 +2634,17 @@ static PaError StartStream( PaStream *s )
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
{
@ -2372,24 +2660,25 @@ static PaError StartStream( PaStream *s )
}
}
/* Create timer that will wake us up so we can fill the DSound buffer. */
if( stream->streamRepresentation.streamCallback )
{
/* Create timer that will wake us up so we can fill the DSound buffer. */
int resolution;
int framesPerWakeup = stream->framesPerDSBuffer / 4;
int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
if( msecPerWakeup < 10 ) msecPerWakeup = 10;
else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
resolution = msecPerWakeup/4;
stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
(DWORD_PTR) stream, TIME_PERIODIC );
}
if( stream->timerID == 0 )
{
stream->isActive = 0;
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
goto error;
stream->timerID = timeSetEvent( msecPerWakeup, resolution, (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( hr );
goto error;
}
}
stream->isStarted = TRUE;
@ -2407,14 +2696,16 @@ static PaError StopStream( PaStream *s )
HRESULT hr;
int timeoutMsec;
stream->stopProcessing = 1;
/* Set timeout at 20% beyond maximum time we might wait. */
timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate);
while( stream->isActive && (timeoutMsec > 0) )
if( stream->streamRepresentation.streamCallback )
{
Sleep(10);
timeoutMsec -= 10;
stream->stopProcessing = 1;
/* Set timeout at 4 times maximum time we might wait. */
timeoutMsec = (int) (4 * MSEC_PER_SECOND * (stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate));
WaitForSingleObject( stream->processingCompleted, timeoutMsec );
}
if( stream->timerID != 0 )
{
timeKillEvent(stream->timerID); /* Stop callback timer. */
@ -2430,6 +2721,9 @@ static PaError StopStream( PaStream *s )
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 */
}
}

View File

@ -45,6 +45,7 @@
*/
#include "pa_win_ds_dynlink.h"
#include "pa_debugprint.h"
PaWinDsDSoundEntryPoints paWinDsDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
@ -101,10 +102,37 @@ static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCE
return E_NOTIMPL;
}
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
static HRESULT WINAPI DummyDirectSoundFullDuplexCreate8(
LPCGUID pcGuidCaptureDevice,
LPCGUID pcGuidRenderDevice,
LPCDSCBUFFERDESC pcDSCBufferDesc,
LPCDSBUFFERDESC pcDSBufferDesc,
HWND hWnd,
DWORD dwLevel,
LPDIRECTSOUNDFULLDUPLEX * ppDSFD,
LPDIRECTSOUNDCAPTUREBUFFER8 * ppDSCBuffer8,
LPDIRECTSOUNDBUFFER8 * ppDSBuffer8,
LPUNKNOWN pUnkOuter)
{
(void)pcGuidCaptureDevice; /* unused parameter */
(void)pcGuidRenderDevice; /* unused parameter */
(void)pcDSCBufferDesc; /* unused parameter */
(void)pcDSBufferDesc; /* unused parameter */
(void)hWnd; /* unused parameter */
(void)dwLevel; /* unused parameter */
(void)ppDSFD; /* unused parameter */
(void)ppDSCBuffer8; /* unused parameter */
(void)ppDSBuffer8; /* unused parameter */
(void)pUnkOuter; /* unused parameter */
return E_NOTIMPL;
}
#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
void PaWinDs_InitializeDSoundEntryPoints(void)
{
paWinDsDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
paWinDsDSoundEntryPoints.hInstance_ = LoadLibraryA("dsound.dll");
if( paWinDsDSoundEntryPoints.hInstance_ != NULL )
{
paWinDsDSoundEntryPoints.DllGetClassObject =
@ -148,9 +176,22 @@ void PaWinDs_InitializeDSoundEntryPoints(void)
GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" );
if( paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL )
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 =
(HRESULT (WINAPI *)(LPCGUID, LPCGUID, LPCDSCBUFFERDESC, LPCDSBUFFERDESC,
HWND, DWORD, LPDIRECTSOUNDFULLDUPLEX *, LPDIRECTSOUNDCAPTUREBUFFER8 *,
LPDIRECTSOUNDBUFFER8 *, LPUNKNOWN))
GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundFullDuplexCreate" );
if( paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 == NULL )
paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 = DummyDirectSoundFullDuplexCreate8;
#endif
}
else
{
DWORD errorCode = GetLastError(); // 126 (0x7E) == ERROR_MOD_NOT_FOUND
PA_DEBUG(("Couldn't load dsound.dll error code: %d \n",errorCode));
/* initialize with dummy entry points to make live easy when ds isn't present */
paWinDsDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
paWinDsDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
@ -158,6 +199,9 @@ void PaWinDs_InitializeDSoundEntryPoints(void)
paWinDsDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 = DummyDirectSoundFullDuplexCreate8;
#endif
}
}

View File

@ -56,9 +56,13 @@
#endif
/*
We are only using DX3 in here, no need to polute the namespace - davidv
Use the earliest version of DX required, no need to polute the namespace
*/
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
#define DIRECTSOUND_VERSION 0x0800
#else
#define DIRECTSOUND_VERSION 0x0300
#endif
#include <dsound.h>
#ifdef __cplusplus
@ -80,6 +84,13 @@ typedef struct
HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
HRESULT (WINAPI *DirectSoundFullDuplexCreate8)(
LPCGUID, LPCGUID, LPCDSCBUFFERDESC, LPCDSBUFFERDESC,
HWND, DWORD, LPDIRECTSOUNDFULLDUPLEX *, LPDIRECTSOUNDCAPTUREBUFFER8 *,
LPDIRECTSOUNDBUFFER8 *, LPUNKNOWN );
#endif
}PaWinDsDSoundEntryPoints;
extern PaWinDsDSoundEntryPoints paWinDsDSoundEntryPoints;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
**************
* WASAPI API *
**************
----------------------------------------
Microsoft Visual Studio 2005SP1/2008/10
----------------------------------------
No specific actions are needed to compile WASAPI API under Visual Studio.
You are only required to install min. Windows Vista SDK (v6.0A) prior
compilation.
----------------------------------------
MinGW (GCC 32-bit)/ MinGW64 (GCC 64-bit)
----------------------------------------
To compile under MinGW you are required to include 'mingw-include' directory
which contains necessary files with WASAPI API. These files are modified
in order to be compiled by MinGW compiler. These files are taken from
Windows Vista SDK (v6.0A). MinGW compilation is tested and proved to be
fully working under 32-bit and 64-bit modes.
MinGW (32-bit) tested: gcc version 4.4.0 (GCC)
MinGW64 (64-bit) tested: gcc version 4.4.4 20100226 (prerelease) (GCC)
PortAudio
/Dmitry Kostjuchenko/
04.03.2010

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_win_hostapis.c 1339 2008-02-15 07:50:33Z rossb $
* $Id: pa_win_hostapis.c 1453 2010-02-16 09:46:08Z dmitrykos $
* Portable Audio I/O Library Windows initialization table
*
* Based on the Open Source API proposed by Ross Bencina
@ -58,7 +58,7 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus
}
@ -80,17 +80,17 @@ PaUtilHostApiInitializer *paHostApiInitializers[] =
PaAsio_Initialize,
#endif
/*
#ifndef PA_NO_WASAPI
PaWinWasapi_Initialize,
PaWasapi_Initialize,
#endif
/*
#ifndef PA_NO_WDMKS
PaWinWdm_Initialize,
#endif
*/
PaSkeleton_Initialize, /* just for testing */
//PaSkeleton_Initialize, /* just for testing */
0 /* NULL terminated array */
};

View File

@ -46,6 +46,38 @@
#include "pa_win_wdmks_utils.h"
#ifndef PA_WDMKS_NO_KSGUID_LIB
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "ksguid.lib" )
#endif
#define pa_KSDATAFORMAT_TYPE_AUDIO KSDATAFORMAT_TYPE_AUDIO
#define pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
#define pa_KSDATAFORMAT_SUBTYPE_PCM KSDATAFORMAT_SUBTYPE_PCM
#define pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX KSDATAFORMAT_SUBTYPE_WAVEFORMATEX
#define pa_KSMEDIUMSETID_Standard KSMEDIUMSETID_Standard
#define pa_KSINTERFACESETID_Standard KSINTERFACESETID_Standard
#define pa_KSPROPSETID_Pin KSPROPSETID_Pin
#else
static const GUID pa_KSDATAFORMAT_TYPE_AUDIO = { STATIC_KSDATAFORMAT_TYPE_AUDIO };
static const GUID pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { STATIC_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT };
static const GUID pa_KSDATAFORMAT_SUBTYPE_PCM = { STATIC_KSDATAFORMAT_SUBTYPE_PCM };
static const GUID pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX = { STATIC_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX };
static const GUID pa_KSMEDIUMSETID_Standard = { STATIC_KSMEDIUMSETID_Standard };
static const GUID pa_KSINTERFACESETID_Standard = { STATIC_KSINTERFACESETID_Standard };
static const GUID pa_KSPROPSETID_Pin = { STATIC_KSPROPSETID_Pin };
#endif
#define pa_IS_VALID_WAVEFORMATEX_GUID(Guid)\
(!memcmp(((PUSHORT)&pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) + 1, ((PUSHORT)(Guid)) + 1, sizeof(GUID) - sizeof(USHORT)))
static PaError WdmGetPinPropertySimple(
HANDLE handle,
unsigned long pinId,
@ -55,7 +87,7 @@ static PaError WdmGetPinPropertySimple(
{
DWORD bytesReturned;
KSP_PIN ksPProp;
ksPProp.Property.Set = KSPROPSETID_Pin;
ksPProp.Property.Set = pa_KSPROPSETID_Pin;
ksPProp.Property.Id = property;
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
ksPProp.PinId = pinId;
@ -85,7 +117,7 @@ static PaError WdmGetPinPropertyMulti(
*ksMultipleItem = 0;
ksPProp.Property.Set = KSPROPSETID_Pin;
ksPProp.Property.Set = pa_KSPROPSETID_Pin;
ksPProp.Property.Id = property;
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
ksPProp.PinId = pinId;
@ -185,7 +217,8 @@ static int KSFilterPinPropertyIdentifiersInclude(
int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput )
{
HANDLE deviceHandle;
int pinCount, pinId, i;
ULONG i;
int pinCount, pinId;
int result = 0;
KSPIN_DATAFLOW requiredDataflowDirection = (isInput ? KSPIN_DATAFLOW_OUT : KSPIN_DATAFLOW_IN );
@ -205,11 +238,11 @@ int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInp
(( communication == KSPIN_COMMUNICATION_SINK) ||
( communication == KSPIN_COMMUNICATION_BOTH))
&& ( KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
KSPROPERTY_PIN_INTERFACES, &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING )
KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING )
|| KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
KSPROPERTY_PIN_INTERFACES, &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) )
KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) )
&& KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
KSPROPERTY_PIN_MEDIUMS, &KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) )
KSPROPERTY_PIN_MEDIUMS, &pa_KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) )
{
KSMULTIPLE_ITEM* item = NULL;
if( WdmGetPinPropertyMulti( deviceHandle, pinId, KSPROPERTY_PIN_DATARANGES, &item ) == paNoError )
@ -218,10 +251,10 @@ int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInp
for( i=0; i < item->Count; ++i ){
if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat)
|| memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0
|| memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0
|| ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 )
if( pa_IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat)
|| memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0
|| memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0
|| ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&pa_KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 )
&& ( memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof(GUID) ) == 0 ) ) )
{
KSDATARANGE_AUDIO *dataRangeAudio = (KSDATARANGE_AUDIO*)dataRange;