mirror of https://github.com/PCSX2/pcsx2.git
3rdparty/portaudio: Updated to a slightly newer revision of portaudio, sicne the fixes in it seemed interesting enough.
I also enabled WDMKS backend, but I had to change some #defines to get it to compile, and I can't seem to be able to use it on my Win7 machine. If anyone on XP wants to try it, it will be at their own risk. (use WDMKS hostApi name on the .ini) git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4850 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
ddcfdb6d77
commit
cb74fd2c10
|
@ -35,7 +35,7 @@ Pa_GetStreamReadAvailable @31
|
|||
Pa_GetStreamWriteAvailable @32
|
||||
Pa_GetSampleSize @33
|
||||
Pa_Sleep @34
|
||||
PaAsio_GetAvailableLatencyValues @50
|
||||
PaAsio_GetAvailableBufferSizes @50
|
||||
PaAsio_ShowControlPanel @51
|
||||
PaUtil_InitializeX86PlainConverters @52
|
||||
PaAsio_GetInputChannelName @53
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
Name="VCCLCompilerTool"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win"
|
||||
PreprocessorDefinitions="_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=1"
|
||||
PreprocessorDefinitions="_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
|
@ -112,7 +112,7 @@
|
|||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win"
|
||||
PreprocessorDefinitions="_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=1"
|
||||
PreprocessorDefinitions="_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
|
@ -179,7 +179,7 @@
|
|||
Name="VCCLCompilerTool"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win"
|
||||
PreprocessorDefinitions="_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=0"
|
||||
PreprocessorDefinitions="_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
|
@ -244,7 +244,7 @@
|
|||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win"
|
||||
PreprocessorDefinitions="_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=0"
|
||||
PreprocessorDefinitions="_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
|
@ -310,7 +310,7 @@
|
|||
Name="VCCLCompilerTool"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win"
|
||||
PreprocessorDefinitions="_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=0"
|
||||
PreprocessorDefinitions="_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
|
@ -851,6 +851,14 @@
|
|||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="wdmks"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\hostapi\wdmks\pa_win_wdmks.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="os"
|
||||
|
@ -858,6 +866,10 @@
|
|||
<Filter
|
||||
Name="win"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\os\win\pa_win_coinitialize.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\os\win\pa_win_hostapis.c"
|
||||
>
|
||||
|
@ -914,6 +926,10 @@
|
|||
RelativePath="..\..\include\pa_mac_core.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\os\win\pa_win_coinitialize.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\pa_win_ds.h"
|
||||
>
|
||||
|
|
|
@ -172,7 +172,7 @@
|
|||
<ClCompile>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
@ -241,7 +241,7 @@
|
|||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
@ -315,7 +315,7 @@
|
|||
<ClCompile>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
|
@ -385,7 +385,7 @@
|
|||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=0;PA_USE_WMME=0;PA_USE_ASIO=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_DEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_DS=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;PA_USE_WMME=0;PA_USE_ASIO=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
@ -616,8 +616,10 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\hostapi\dsound\pa_win_ds.c" />
|
||||
<ClCompile Include="..\..\src\hostapi\dsound\pa_win_ds_dynlink.c" />
|
||||
<ClCompile Include="..\..\src\hostapi\wdmks\pa_win_wdmks.c" />
|
||||
<ClCompile Include="..\..\src\hostapi\wmme\pa_win_wmme.c" />
|
||||
<ClCompile Include="..\..\src\hostapi\wasapi\pa_win_wasapi.c" />
|
||||
<ClCompile Include="..\..\src\os\win\pa_win_coinitialize.c" />
|
||||
<ClCompile Include="..\..\src\os\win\pa_win_hostapis.c" />
|
||||
<ClCompile Include="..\..\src\os\win\pa_win_util.c" />
|
||||
<ClCompile Include="..\..\src\os\win\pa_win_waveformat.c" />
|
||||
|
@ -625,6 +627,7 @@
|
|||
<ClCompile Include="..\..\src\os\win\pa_x86_plain_converters.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\src\hostapi\wdmks\readme.txt" />
|
||||
<None Include="portaudio.def" />
|
||||
<None Include="portaudio_noasio.def" />
|
||||
</ItemGroup>
|
||||
|
@ -638,6 +641,7 @@
|
|||
<ClInclude Include="..\..\include\pa_win_waveformat.h" />
|
||||
<ClInclude Include="..\..\include\pa_win_wmme.h" />
|
||||
<ClInclude Include="..\..\include\portaudio.h" />
|
||||
<ClInclude Include="..\..\src\os\win\pa_win_coinitialize.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
|
|
@ -40,6 +40,9 @@
|
|||
<UniqueIdentifier>{78689f06-8c26-4417-93e2-b809a8c41ee7}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\hostapi\wdmks">
|
||||
<UniqueIdentifier>{0d9360dd-9d00-434a-ab46-7a89b2d35fd0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\common\pa_allocation.c">
|
||||
|
@ -123,6 +126,12 @@
|
|||
<ClCompile Include="..\..\src\os\win\pa_x86_plain_converters.c">
|
||||
<Filter>Source Files\os\win</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\hostapi\wdmks\pa_win_wdmks.c">
|
||||
<Filter>Source Files\hostapi\wdmks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\os\win\pa_win_coinitialize.c">
|
||||
<Filter>Source Files\os\win</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="portaudio.def">
|
||||
|
@ -131,6 +140,9 @@
|
|||
<None Include="portaudio_noasio.def">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\hostapi\wdmks\readme.txt">
|
||||
<Filter>Source Files\hostapi\wdmks</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\include\pa_asio.h">
|
||||
|
@ -160,5 +172,8 @@
|
|||
<ClInclude Include="..\..\include\portaudio.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\os\win\pa_win_coinitialize.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
|
@ -201,11 +201,20 @@ case "${host_os}" in
|
|||
LIBS="-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon"
|
||||
|
||||
if test "x$enable_mac_universal" = "xyes" ; then
|
||||
mac_version_min="-mmacosx-version-min=10.3"
|
||||
if [[ -d /Developer/SDKs/MacOSX10.5.sdk ]] ; then
|
||||
mac_version_min="-mmacosx-version-min=10.3"
|
||||
mac_arches="-arch i386 -arch ppc -arch x86_64 -arch ppc64"
|
||||
mac_sysroot="-isysroot /Developer/SDKs/MacOSX10.5.sdk"
|
||||
elif [[ -d /Developer/SDKs/MacOSX10.6.sdk ]] ; then
|
||||
mac_version_min="-mmacosx-version-min=10.4"
|
||||
mac_arches="-arch i386 -arch x86_64"
|
||||
mac_sysroot="-isysroot /Developer/SDKs/MacOSX10.6.sdk"
|
||||
elif [[ -d /Developer/SDKs/MacOSX10.7.sdk ]] ; then
|
||||
mac_version_min="-mmacosx-version-min=10.4"
|
||||
mac_arches="-arch i386 -arch x86_64"
|
||||
mac_sysroot="-isysroot /Developer/SDKs/MacOSX10.7.sdk"
|
||||
else
|
||||
mac_version_min="-mmacosx-version-min=10.3"
|
||||
mac_arches="-arch i386 -arch ppc"
|
||||
mac_sysroot="-isysroot /Developer/SDKs/MacOSX10.4u.sdk"
|
||||
fi
|
||||
|
@ -230,7 +239,7 @@ case "${host_os}" in
|
|||
|
||||
if [[ "x$with_directx" = "xyes" ]]; then
|
||||
DXDIR="$with_dxdir"
|
||||
add_objects src/hostapi/dsound/pa_win_ds.o src/hostapi/dsound/pa_win_ds_dynlink.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_waveformat.o
|
||||
add_objects src/hostapi/dsound/pa_win_ds.o src/hostapi/dsound/pa_win_ds_dynlink.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_coinitialize.o src/os/win/pa_win_waveformat.o
|
||||
LIBS="-lwinmm -lm -ldsound -lole32"
|
||||
DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -ldsound -lole32"
|
||||
#VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\""
|
||||
|
@ -240,7 +249,7 @@ case "${host_os}" in
|
|||
|
||||
if [[ "x$with_asio" = "xyes" ]]; then
|
||||
ASIODIR="$with_asiodir"
|
||||
add_objects src/hostapi/asio/pa_asio.o src/common/pa_ringbuffer.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/hostapi/asio/iasiothiscallresolver.o $ASIODIR/common/asio.o $ASIODIR/host/asiodrivers.o $ASIODIR/host/pc/asiolist.o
|
||||
add_objects src/hostapi/asio/pa_asio.o src/common/pa_ringbuffer.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_coinitialize.o src/hostapi/asio/iasiothiscallresolver.o $ASIODIR/common/asio.o $ASIODIR/host/asiodrivers.o $ASIODIR/host/pc/asiolist.o
|
||||
LIBS="-lwinmm -lm -lole32 -luuid"
|
||||
DLL_LIBS="${DLL_LIBS} -lwinmm -lm -lole32 -luuid"
|
||||
CFLAGS="$CFLAGS -ffast-math -fomit-frame-pointer -I\$(top_srcdir)/src/common -I\$(top_srcdir)/src/hostapi/asio -I$ASIODIR/host/pc -I$ASIODIR/common -I$ASIODIR/host -UPA_USE_ASIO -DPA_USE_ASIO=1 -DWINDOWS"
|
||||
|
@ -265,7 +274,7 @@ case "${host_os}" in
|
|||
fi
|
||||
|
||||
if [[ "x$with_wasapi" = "xyes" ]]; then
|
||||
add_objects src/hostapi/wasapi/pa_win_wasapi.o src/common/pa_ringbuffer.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_waveformat.o
|
||||
add_objects src/hostapi/wasapi/pa_win_wasapi.o src/common/pa_ringbuffer.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_coinitialize.o src/os/win/pa_win_waveformat.o
|
||||
LIBS="-lwinmm -lm -lole32 -luuid"
|
||||
DLL_LIBS="${DLL_LIBS} -lwinmm -lole32"
|
||||
CFLAGS="$CFLAGS -I\$(top_srcdir)/src/common -I\$(top_srcdir)/src/hostapi/wasapi/mingw-include -UPA_USE_WASAPI -DPA_USE_WASAPI=1"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PA_ASIO_H
|
||||
#define PA_ASIO_H
|
||||
/*
|
||||
* $Id: pa_asio.h 1592 2011-02-04 10:41:58Z rossb $
|
||||
* $Id: pa_asio.h 1667 2011-05-02 15:49:20Z rossb $
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* ASIO specific extensions
|
||||
*
|
||||
|
@ -52,23 +52,30 @@ extern "C"
|
|||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
/** Retrieve legal latency settings for the specificed device, in samples.
|
||||
/** Retrieve legal native buffer sizes for the specificed device, in sample frames.
|
||||
|
||||
@param device The global index of the device about which the query is being made.
|
||||
@param minLatency A pointer to the location which will recieve the minimum latency value.
|
||||
@param maxLatency A pointer to the location which will recieve the maximum latency value.
|
||||
@param preferredLatency A pointer to the location which will recieve the preferred latency value.
|
||||
@param granularity A pointer to the location which will recieve the granularity. This value
|
||||
determines which values between minLatency and maxLatency are available. ie the step size,
|
||||
if granularity is -1 then available latency settings are powers of two.
|
||||
@param minBufferSizeFrames A pointer to the location which will receive the minimum buffer size value.
|
||||
@param maxBufferSizeFrames A pointer to the location which will receive the maximum buffer size value.
|
||||
@param preferredBufferSizeFrames A pointer to the location which will receive the preferred buffer size value.
|
||||
@param granularity A pointer to the location which will receive the "granularity". This value determines
|
||||
the step size used to compute the legal values between minBufferSizeFrames and maxBufferSizeFrames.
|
||||
If granularity is -1 then available buffer size values are powers of two.
|
||||
|
||||
@see ASIOGetBufferSize in the ASIO SDK.
|
||||
|
||||
@todo This function should be renamed to PaAsio_GetAvailableBufferSizes.
|
||||
No reason to use a wildly different name from the ASIO version.
|
||||
@note: this function used to be called PaAsio_GetAvailableLatencyValues. There is a
|
||||
#define that maps PaAsio_GetAvailableLatencyValues to this function for backwards compatibility.
|
||||
*/
|
||||
PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
|
||||
long *minLatency, long *maxLatency, long *preferredLatency, long *granularity );
|
||||
PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device,
|
||||
long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity );
|
||||
|
||||
|
||||
/** Backwards compatibility alias for PaAsio_GetAvailableBufferSizes
|
||||
|
||||
@see PaAsio_GetAvailableBufferSizes
|
||||
*/
|
||||
#define PaAsio_GetAvailableLatencyValues PaAsio_GetAvailableBufferSizes
|
||||
|
||||
|
||||
/** Display the ASIO control panel for the specified device.
|
||||
|
|
|
@ -53,7 +53,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* A pointer to a paMacCoreStreamInfo may be passed as
|
||||
* the hostApiSpecificStreamInfo in the PaStreamParameters struct
|
||||
* when opening a stream or querying the format. Use NULL, for the
|
||||
|
@ -65,17 +65,17 @@ typedef struct
|
|||
unsigned long size; /**size of whole structure including this header */
|
||||
PaHostApiTypeId hostApiType; /**host API for which this data is intended */
|
||||
unsigned long version; /**structure version */
|
||||
unsigned long flags; /* flags to modify behaviour */
|
||||
SInt32 const * channelMap; /* Channel map for HAL channel mapping , if not needed, use NULL;*/
|
||||
unsigned long channelMapSize; /* Channel map size for HAL channel mapping , if not needed, use 0;*/
|
||||
unsigned long flags; /** flags to modify behaviour */
|
||||
SInt32 const * channelMap; /** Channel map for HAL channel mapping , if not needed, use NULL;*/
|
||||
unsigned long channelMapSize; /** Channel map size for HAL channel mapping , if not needed, use 0;*/
|
||||
} PaMacCoreStreamInfo;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Functions
|
||||
*/
|
||||
|
||||
|
||||
/* Use this function to initialize a paMacCoreStreamInfo struct
|
||||
/** Use this function to initialize a paMacCoreStreamInfo struct
|
||||
* using the requested flags. Note that channel mapping is turned
|
||||
* off after a call to this function.
|
||||
* @param data The datastructure to initialize
|
||||
|
@ -83,14 +83,14 @@ typedef struct
|
|||
*/
|
||||
void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, unsigned long flags );
|
||||
|
||||
/* call this after pa_SetupMacCoreStreamInfo to use channel mapping as described in notes.txt.
|
||||
/** call this after pa_SetupMacCoreStreamInfo to use channel mapping as described in notes.txt.
|
||||
* @param data The stream info structure to assign a channel mapping to
|
||||
* @param channelMap The channel map array, as described in notes.txt. This array pointer will be used directly (ie the underlying data will not be copied), so the caller should not free the array until after the stream has been opened.
|
||||
* @param channelMapSize The size of the channel map array.
|
||||
*/
|
||||
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, unsigned long channelMapSize );
|
||||
|
||||
/*
|
||||
/**
|
||||
* Retrieve the AudioDeviceID of the input device assigned to an open stream
|
||||
*
|
||||
* @param s The stream to query.
|
||||
|
@ -99,7 +99,7 @@ void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const
|
|||
*/
|
||||
AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s );
|
||||
|
||||
/*
|
||||
/**
|
||||
* Retrieve the AudioDeviceID of the output device assigned to an open stream
|
||||
*
|
||||
* @param s The stream to query.
|
||||
|
@ -108,7 +108,7 @@ AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s );
|
|||
*/
|
||||
AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s );
|
||||
|
||||
/*
|
||||
/**
|
||||
* Returns a statically allocated string with the device's name
|
||||
* for the given channel. NULL will be returned on failure.
|
||||
*
|
||||
|
@ -124,28 +124,28 @@ AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s );
|
|||
*/
|
||||
const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input );
|
||||
|
||||
/*
|
||||
/**
|
||||
* Flags
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
* The following flags alter the behaviour of PA on the mac platform.
|
||||
* they can be ORed together. These should work both for opening and
|
||||
* checking a device.
|
||||
*/
|
||||
|
||||
/* Allows PortAudio to change things like the device's frame size,
|
||||
/** Allows PortAudio to change things like the device's frame size,
|
||||
* which allows for much lower latency, but might disrupt the device
|
||||
* if other programs are using it, even when you are just Querying
|
||||
* the device. */
|
||||
#define paMacCoreChangeDeviceParameters (0x01)
|
||||
|
||||
/* In combination with the above flag,
|
||||
/** In combination with the above flag,
|
||||
* causes the stream opening to fail, unless the exact sample rates
|
||||
* are supported by the device. */
|
||||
#define paMacCoreFailIfConversionRequired (0x02)
|
||||
|
||||
/* These flags set the SR conversion quality, if required. The wierd ordering
|
||||
/** These flags set the SR conversion quality, if required. The wierd ordering
|
||||
* allows Maximum Quality to be the default.*/
|
||||
#define paMacCoreConversionQualityMin (0x0100)
|
||||
#define paMacCoreConversionQualityMedium (0x0200)
|
||||
|
@ -153,26 +153,26 @@ const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input )
|
|||
#define paMacCoreConversionQualityHigh (0x0400)
|
||||
#define paMacCoreConversionQualityMax (0x0000)
|
||||
|
||||
/*
|
||||
/**
|
||||
* Here are some "preset" combinations of flags (above) to get to some
|
||||
* common configurations. THIS IS OVERKILL, but if more flags are added
|
||||
* it won't be.
|
||||
*/
|
||||
|
||||
/*This is the default setting: do as much sample rate conversion as possible
|
||||
/**This is the default setting: do as much sample rate conversion as possible
|
||||
* and as little mucking with the device as possible. */
|
||||
#define paMacCorePlayNice (0x00)
|
||||
/*This setting is tuned for pro audio apps. It allows SR conversion on input
|
||||
/**This setting is tuned for pro audio apps. It allows SR conversion on input
|
||||
and output, but it tries to set the appropriate SR on the device.*/
|
||||
#define paMacCorePro (0x01)
|
||||
/*This is a setting to minimize CPU usage and still play nice.*/
|
||||
/**This is a setting to minimize CPU usage and still play nice.*/
|
||||
#define paMacCoreMinimizeCPUButPlayNice (0x0100)
|
||||
/*This is a setting to minimize CPU usage, even if that means interrupting the device. */
|
||||
/**This is a setting to minimize CPU usage, even if that means interrupting the device. */
|
||||
#define paMacCoreMinimizeCPU (0x0101)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /** __cplusplus */
|
||||
|
||||
#endif /* PA_MAC_CORE_H */
|
||||
#endif /** PA_MAC_CORE_H */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PORTAUDIO_H
|
||||
#define PORTAUDIO_H
|
||||
/*
|
||||
* $Id: portaudio.h 1594 2011-02-05 14:33:29Z rossb $
|
||||
* $Id: portaudio.h 1702 2011-07-20 03:24:00Z rossb $
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* PortAudio API Header File
|
||||
* Latest version available at: http://www.portaudio.com/
|
||||
|
@ -450,15 +450,15 @@ typedef struct PaDeviceInfo
|
|||
{
|
||||
int structVersion; /* this is struct version 2 */
|
||||
const char *name;
|
||||
PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
|
||||
PaHostApiIndex hostApi; /**< note this is a host API index, not a type id*/
|
||||
|
||||
int maxInputChannels;
|
||||
int maxOutputChannels;
|
||||
|
||||
/* Default latency values for interactive performance. */
|
||||
/** Default latency values for interactive performance. */
|
||||
PaTime defaultLowInputLatency;
|
||||
PaTime defaultLowOutputLatency;
|
||||
/* Default latency values for robust non-interactive applications (eg. playing sound files). */
|
||||
/** Default latency values for robust non-interactive applications (eg. playing sound files). */
|
||||
PaTime defaultHighInputLatency;
|
||||
PaTime defaultHighOutputLatency;
|
||||
|
||||
|
@ -640,11 +640,15 @@ typedef unsigned long PaStreamFlags;
|
|||
|
||||
/**
|
||||
Timing information for the buffers passed to the stream callback.
|
||||
|
||||
Time values are expressed in seconds and are synchronised with the time base used by Pa_GetStreamTime() for the associated stream.
|
||||
|
||||
@see PaStreamCallback, Pa_GetStreamTime
|
||||
*/
|
||||
typedef struct PaStreamCallbackTimeInfo{
|
||||
PaTime inputBufferAdcTime;
|
||||
PaTime currentTime;
|
||||
PaTime outputBufferDacTime;
|
||||
PaTime inputBufferAdcTime; /**< The time when the first sample of the input buffer was captured at the ADC input */
|
||||
PaTime currentTime; /**< The time when the stream callback was invoked */
|
||||
PaTime outputBufferDacTime; /**< The time when the first sample of the output buffer will output the DAC */
|
||||
} PaStreamCallbackTimeInfo;
|
||||
|
||||
|
||||
|
@ -697,9 +701,9 @@ typedef unsigned long PaStreamCallbackFlags;
|
|||
*/
|
||||
typedef enum PaStreamCallbackResult
|
||||
{
|
||||
paContinue=0,
|
||||
paComplete=1,
|
||||
paAbort=2
|
||||
paContinue=0, /**< Signal that the stream should continue invoking the callback and processing audio. */
|
||||
paComplete=1, /**< Signal that the stream should stop invoking the callback and finish once all output samples have played. */
|
||||
paAbort=2 /**< Signal that the stream should stop invoking the callback and finish as soon as possible. */
|
||||
} PaStreamCallbackResult;
|
||||
|
||||
|
||||
|
@ -719,11 +723,10 @@ typedef enum PaStreamCallbackResult
|
|||
@param frameCount The number of sample frames to be processed by
|
||||
the stream callback.
|
||||
|
||||
@param timeInfo The time in seconds when the first sample of the input
|
||||
buffer was received at the audio input, the time in seconds when the first
|
||||
sample of the output buffer will begin being played at the audio output, and
|
||||
the time in seconds when the stream callback was called.
|
||||
See also Pa_GetStreamTime()
|
||||
@param timeInfo Timestamps indicating the ADC capture time of the first sample
|
||||
in the input buffer, the DAC output time of the first sample in the output buffer
|
||||
and the time the callback was invoked.
|
||||
See PaStreamCallbackTimeInfo and Pa_GetStreamTime()
|
||||
|
||||
@param statusFlags Flags indicating whether input and/or output buffers
|
||||
have been inserted or will be dropped to overcome underflow or overflow
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_process.c 1584 2011-02-02 18:58:17Z rossb $
|
||||
* $Id: pa_process.c 1706 2011-07-21 18:44:58Z philburk $
|
||||
* Portable Audio I/O Library
|
||||
* streamCallback <-> host buffer processing adapter
|
||||
*
|
||||
|
@ -223,7 +223,7 @@ PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* Under the assumption that no ADC in existence delivers better than 24bits resoultion,
|
||||
/* Under the assumption that no ADC in existence delivers better than 24bits resolution,
|
||||
we disable dithering when host input format is paInt32 and user format is paInt24,
|
||||
since the host samples will just be padded with zeros anyway. */
|
||||
|
||||
|
@ -428,13 +428,13 @@ void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp )
|
|||
}
|
||||
|
||||
|
||||
unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp )
|
||||
unsigned long PaUtil_GetBufferProcessorInputLatencyFrames( PaUtilBufferProcessor* bp )
|
||||
{
|
||||
return bp->initialFramesInTempInputBuffer;
|
||||
}
|
||||
|
||||
|
||||
unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp )
|
||||
unsigned long PaUtil_GetBufferProcessorOutputLatencyFrames( PaUtilBufferProcessor* bp )
|
||||
{
|
||||
return bp->initialFramesInTempOutputBuffer;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PA_PROCESS_H
|
||||
#define PA_PROCESS_H
|
||||
/*
|
||||
* $Id: pa_process.h 1523 2010-07-10 17:41:25Z dmitrykos $
|
||||
* $Id: pa_process.h 1668 2011-05-02 17:07:11Z rossb $
|
||||
* Portable Audio I/O Library callback buffer processing adapters
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
|
@ -406,25 +406,25 @@ void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
|
|||
void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
|
||||
|
||||
|
||||
/** Retrieve the input latency of a buffer processor.
|
||||
/** Retrieve the input latency of a buffer processor, in frames.
|
||||
|
||||
@param bufferProcessor The buffer processor examine.
|
||||
|
||||
@return The input latency introduced by the buffer processor, in frames.
|
||||
|
||||
@see PaUtil_GetBufferProcessorOutputLatency
|
||||
@see PaUtil_GetBufferProcessorOutputLatencyFrames
|
||||
*/
|
||||
unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
|
||||
unsigned long PaUtil_GetBufferProcessorInputLatencyFrames( PaUtilBufferProcessor* bufferProcessor );
|
||||
|
||||
/** Retrieve the output latency of a buffer processor.
|
||||
/** Retrieve the output latency of a buffer processor, in frames.
|
||||
|
||||
@param bufferProcessor The buffer processor examine.
|
||||
|
||||
@return The output latency introduced by the buffer processor, in frames.
|
||||
|
||||
@see PaUtil_GetBufferProcessorInputLatency
|
||||
@see PaUtil_GetBufferProcessorInputLatencyFrames
|
||||
*/
|
||||
unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
|
||||
unsigned long PaUtil_GetBufferProcessorOutputLatencyFrames( PaUtilBufferProcessor* bufferProcessor );
|
||||
|
||||
/*@}*/
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_ringbuffer.c 1574 2011-02-01 11:18:16Z rossb $
|
||||
* $Id: pa_ringbuffer.c 1694 2011-06-17 08:01:02Z rossb $
|
||||
* Portable Audio I/O Library
|
||||
* Ring Buffer utility.
|
||||
*
|
||||
|
@ -77,14 +77,14 @@ ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buf
|
|||
|
||||
/***************************************************************************
|
||||
** Return number of elements available for reading. */
|
||||
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf )
|
||||
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
PaUtil_ReadMemoryBarrier();
|
||||
return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
|
||||
}
|
||||
/***************************************************************************
|
||||
** Return number of elements available for writing. */
|
||||
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf )
|
||||
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
/* Since we are calling PaUtil_GetRingBufferReadAvailable, we don't need an aditional MB */
|
||||
return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PA_RINGBUFFER_H
|
||||
#define PA_RINGBUFFER_H
|
||||
/*
|
||||
* $Id: pa_ringbuffer.h 1549 2010-10-24 10:21:35Z rossb $
|
||||
* $Id: pa_ringbuffer.h 1694 2011-06-17 08:01:02Z rossb $
|
||||
* Portable Audio I/O Library
|
||||
* Ring Buffer utility.
|
||||
*
|
||||
|
@ -125,7 +125,7 @@ void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf );
|
|||
|
||||
@return The number of elements available for writing.
|
||||
*/
|
||||
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf );
|
||||
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf );
|
||||
|
||||
/** Retrieve the number of elements available in the ring buffer for reading.
|
||||
|
||||
|
@ -133,7 +133,7 @@ ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf );
|
|||
|
||||
@return The number of elements available for reading.
|
||||
*/
|
||||
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf );
|
||||
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf );
|
||||
|
||||
/** Write data to the ring buffer.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_linux_alsa.c 1613 2011-03-01 00:49:52Z philburk $
|
||||
* $Id: pa_linux_alsa.c 1691 2011-05-26 20:19:19Z aknudsen $
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
* ALSA implementation by Joshua Haberman and Arve Knudsen
|
||||
|
@ -79,6 +79,10 @@
|
|||
|
||||
#include "pa_linux_alsa.h"
|
||||
|
||||
#ifndef SND_PCM_TSTAMP_ENABLE
|
||||
#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP
|
||||
#endif
|
||||
|
||||
/* Defines Alsa function types and pointers to these functions. */
|
||||
#define _PA_DEFINE_FUNC(x) typedef typeof(x) x##_ft; static x##_ft *alsa_##x = 0
|
||||
|
||||
|
@ -2768,10 +2772,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
/* Ok, buffer processor is initialized, now we can deduce it's latency */
|
||||
if( numInputChannels > 0 )
|
||||
stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)(
|
||||
PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate);
|
||||
PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) / sampleRate);
|
||||
if( numOutputChannels > 0 )
|
||||
stream->streamRepresentation.streamInfo.outputLatency = outputLatency + (PaTime)(
|
||||
PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate);
|
||||
PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) / sampleRate);
|
||||
|
||||
PA_DEBUG(( "%s: Stream: framesPerBuffer = %lu, maxFramesPerHostBuffer = %lu, latency = i(%f)/o(%f), \n", __FUNCTION__, framesPerBuffer, stream->maxFramesPerHostBuffer, stream->streamRepresentation.streamInfo.inputLatency, stream->streamRepresentation.streamInfo.outputLatency));
|
||||
|
||||
|
|
|
@ -1827,7 +1827,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
/ sampleRate / stream->input->bytesPerFrame;
|
||||
stream->baseStreamRep.streamInfo.inputLatency =
|
||||
bufferDuration +
|
||||
((PaTime)PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) -
|
||||
((PaTime)PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) -
|
||||
stream->maxFramesPerHostBuffer) / sampleRate;
|
||||
assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 );
|
||||
}
|
||||
|
@ -1844,7 +1844,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
}
|
||||
stream->baseStreamRep.streamInfo.outputLatency =
|
||||
bufferDuration +
|
||||
((PaTime)PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) -
|
||||
((PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) -
|
||||
stream->maxFramesPerHostBuffer) / sampleRate;
|
||||
assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 );
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_asio.cpp 1631 2011-03-04 01:45:06Z rossb $
|
||||
* $Id: pa_asio.cpp 1681 2011-05-10 15:58:15Z rossb $
|
||||
* Portable Audio I/O Library for ASIO Drivers
|
||||
*
|
||||
* Author: Stephane Letz
|
||||
|
@ -101,6 +101,8 @@
|
|||
#include "pa_debugprint.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
|
||||
#include "pa_win_coinitialize.h"
|
||||
|
||||
/* This version of pa_asio.cpp is currently only targetted at Win32,
|
||||
It would require a few tweaks to work with pre-OS X Macintosh.
|
||||
To make configuration easier, we define WIN32 here to make sure
|
||||
|
@ -289,6 +291,8 @@ typedef struct
|
|||
|
||||
PaUtilAllocationGroup *allocations;
|
||||
|
||||
PaWinUtilComInitializationResult comInitializationResult;
|
||||
|
||||
AsioDrivers *asioDrivers;
|
||||
void *systemSpecific;
|
||||
|
||||
|
@ -906,8 +910,8 @@ typedef struct PaAsioDeviceInfo
|
|||
PaAsioDeviceInfo;
|
||||
|
||||
|
||||
PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
|
||||
long *minLatency, long *maxLatency, long *preferredLatency, long *granularity )
|
||||
PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device,
|
||||
long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity )
|
||||
{
|
||||
PaError result;
|
||||
PaUtilHostApiRepresentation *hostApi;
|
||||
|
@ -924,9 +928,9 @@ PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
|
|||
PaAsioDeviceInfo *asioDeviceInfo =
|
||||
(PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
|
||||
|
||||
*minLatency = asioDeviceInfo->minBufferSize;
|
||||
*maxLatency = asioDeviceInfo->maxBufferSize;
|
||||
*preferredLatency = asioDeviceInfo->preferredBufferSize;
|
||||
*minBufferSizeFrames = asioDeviceInfo->minBufferSize;
|
||||
*maxBufferSizeFrames = asioDeviceInfo->maxBufferSize;
|
||||
*preferredBufferSizeFrames = asioDeviceInfo->preferredBufferSize;
|
||||
*granularity = asioDeviceInfo->bufferGranularity;
|
||||
}
|
||||
}
|
||||
|
@ -935,12 +939,10 @@ PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
|
|||
}
|
||||
|
||||
/* Unload whatever we loaded in LoadAsioDriver().
|
||||
Also balance the call to CoInitialize(0).
|
||||
*/
|
||||
static void UnloadAsioDriver( void )
|
||||
{
|
||||
ASIOExit();
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -956,23 +958,8 @@ static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const c
|
|||
ASIOError asioError;
|
||||
int asioIsInitialized = 0;
|
||||
|
||||
/*
|
||||
ASIO uses CoCreateInstance() to load a driver. That requires that
|
||||
CoInitialize(0) be called for every thread that loads a driver.
|
||||
It is OK to call CoInitialize(0) multiple times form one thread as long
|
||||
as it is balanced by a call to CoUninitialize(). See UnloadAsioDriver().
|
||||
|
||||
The V18 version called CoInitialize() starting on 2/19/02.
|
||||
That was removed from PA V19 for unknown reasons.
|
||||
Phil Burk added it back on 6/27/08 so that JSyn would work.
|
||||
*/
|
||||
CoInitialize( 0 );
|
||||
|
||||
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) )
|
||||
{
|
||||
/* If this returns an error then it might be because CoInitialize(0) was removed.
|
||||
It should be called right before this.
|
||||
*/
|
||||
result = paUnanticipatedHostError;
|
||||
PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
|
||||
goto error;
|
||||
|
@ -1021,7 +1008,7 @@ error:
|
|||
{
|
||||
ASIOExit();
|
||||
}
|
||||
CoUninitialize();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1053,6 +1040,24 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|
|||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
We initialize COM ourselves here and uninitialize it in Terminate().
|
||||
This should be the only COM initialization needed in this module.
|
||||
|
||||
The ASIO SDK may also initialize COM but since we want to reduce dependency
|
||||
on the ASIO SDK we manage COM initialization ourselves.
|
||||
|
||||
There used to be code that initialized COM in other situations
|
||||
such as when creating a Stream. This made PA work when calling Pa_CreateStream
|
||||
from a non-main thread. However we currently consider initialization
|
||||
of COM in non-main threads to be the caller's responsibility.
|
||||
*/
|
||||
result = PaWinUtil_CoInitialize( paASIO, &asioHostApi->comInitializationResult );
|
||||
if( result != paNoError )
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */
|
||||
|
||||
asioHostApi->allocations = PaUtil_CreateAllocationGroup();
|
||||
|
@ -1065,7 +1070,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|
|||
/* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
|
||||
try
|
||||
{
|
||||
asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */
|
||||
asioHostApi->asioDrivers = new AsioDrivers(); /* invokes CoInitialize(0) in AsioDriverList::AsioDriverList */
|
||||
}
|
||||
catch (std::bad_alloc)
|
||||
{
|
||||
|
@ -1347,8 +1352,11 @@ error:
|
|||
delete asioHostApi->asioDrivers;
|
||||
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
|
||||
|
||||
PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
|
||||
|
||||
PaUtil_FreeMemory( asioHostApi );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1368,9 +1376,11 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
|
|||
PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
|
||||
}
|
||||
|
||||
delete asioHostApi->asioDrivers; /* calls CoUninitialize() */
|
||||
delete asioHostApi->asioDrivers;
|
||||
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
|
||||
|
||||
PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
|
||||
|
||||
PaUtil_FreeMemory( asioHostApi );
|
||||
}
|
||||
|
||||
|
@ -1619,7 +1629,7 @@ static void ZeroOutputBuffers( PaAsioStream *stream, long index )
|
|||
}
|
||||
|
||||
|
||||
static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames,
|
||||
static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames, unsigned long userFramesPerBuffer,
|
||||
PaAsioDriverInfo *driverInfo )
|
||||
{
|
||||
unsigned long result;
|
||||
|
@ -2085,15 +2095,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
if( usingBlockingIo )
|
||||
{
|
||||
/** @todo REVIEW selection of host buffer size for blocking i/o */
|
||||
/* Use default host latency for blocking i/o. */
|
||||
framesPerHostBuffer = SelectHostBufferSize( 0, driverInfo );
|
||||
|
||||
framesPerHostBuffer = SelectHostBufferSize( 0, framesPerBuffer, driverInfo );
|
||||
|
||||
}
|
||||
else /* Using callback interface... */
|
||||
{
|
||||
framesPerHostBuffer = SelectHostBufferSize(
|
||||
(( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
|
||||
? suggestedInputLatencyFrames : suggestedOutputLatencyFrames),
|
||||
? suggestedInputLatencyFrames : suggestedOutputLatencyFrames), framesPerBuffer,
|
||||
driverInfo );
|
||||
}
|
||||
|
||||
|
@ -2369,8 +2379,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
/* Compute total intput latency in seconds */
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
(double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor )
|
||||
+ PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor)
|
||||
(double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor )
|
||||
+ PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor)
|
||||
+ (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
|
||||
+ stream->asioInputLatencyFrames )
|
||||
/ sampleRate;
|
||||
|
@ -2382,10 +2392,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
|
||||
stream->asioInputLatencyFrames,
|
||||
(long)( stream->asioInputLatencyFrames * (1000.0 / sampleRate) ),
|
||||
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
|
||||
(long)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
|
||||
PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
|
||||
(long)( (PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
|
||||
PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor),
|
||||
(long)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
|
||||
PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
|
||||
(long)( (PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
|
||||
));
|
||||
|
||||
/* Determine the size of ring buffer in bytes. */
|
||||
|
@ -2460,8 +2470,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
/* Compute total output latency in seconds */
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
(double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor )
|
||||
+ PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor)
|
||||
(double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
|
||||
+ PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor)
|
||||
+ (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
|
||||
+ stream->asioOutputLatencyFrames )
|
||||
/ sampleRate;
|
||||
|
@ -2473,10 +2483,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
|
||||
stream->asioOutputLatencyFrames,
|
||||
(long)( stream->asioOutputLatencyFrames * (1000.0 / sampleRate) ),
|
||||
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
|
||||
(long)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
|
||||
PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
|
||||
(long)( (PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
|
||||
PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor),
|
||||
(long)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
|
||||
PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
|
||||
(long)( (PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
|
||||
));
|
||||
|
||||
/* Determine the size of ring buffer in bytes. */
|
||||
|
@ -2517,10 +2527,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
callbackBufferProcessorInited = TRUE;
|
||||
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
(double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
|
||||
(double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
|
||||
+ stream->asioInputLatencyFrames) / sampleRate; // seconds
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
(double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
|
||||
(double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
|
||||
+ stream->asioOutputLatencyFrames) / sampleRate; // seconds
|
||||
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
|
||||
|
||||
|
@ -2529,15 +2539,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
|
||||
stream->asioInputLatencyFrames,
|
||||
(long)((stream->asioInputLatencyFrames*1000)/ sampleRate),
|
||||
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
|
||||
(long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
|
||||
PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor),
|
||||
(long)((PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate)
|
||||
));
|
||||
|
||||
PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
|
||||
stream->asioOutputLatencyFrames,
|
||||
(long)((stream->asioOutputLatencyFrames*1000)/ sampleRate),
|
||||
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
|
||||
(long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
|
||||
PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor),
|
||||
(long)((PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate)
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -3836,7 +3846,12 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
|
|||
int asioIsInitialized = 0;
|
||||
PaAsioHostApiRepresentation *asioHostApi;
|
||||
PaAsioDeviceInfo *asioDeviceInfo;
|
||||
PaWinUtilComInitializationResult comInitializationResult;
|
||||
|
||||
/* initialize COM again here, we might be in another thread */
|
||||
result = PaWinUtil_CoInitialize( paASIO, &comInitializationResult );
|
||||
if( result != paNoError )
|
||||
return result;
|
||||
|
||||
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
|
||||
if( result != paNoError )
|
||||
|
@ -3863,9 +3878,6 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
|
|||
|
||||
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
|
||||
|
||||
/* See notes about CoInitialize(0) in LoadAsioDriver(). */
|
||||
CoInitialize(0);
|
||||
|
||||
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
|
||||
{
|
||||
result = paUnanticipatedHostError;
|
||||
|
@ -3914,7 +3926,6 @@ PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErr
|
|||
goto error;
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
|
||||
|
||||
return result;
|
||||
|
@ -3924,7 +3935,8 @@ error:
|
|||
{
|
||||
ASIOExit();
|
||||
}
|
||||
CoUninitialize();
|
||||
|
||||
PaWinUtil_CoUninitialize( paASIO, &comInitializationResult );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -77,8 +77,10 @@ extern "C"
|
|||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* prototypes for functions declared in this file */
|
||||
/* This is a reasonable size for a small buffer based on experience. */
|
||||
#define PA_MAC_SMALL_BUFFER_SIZE (64)
|
||||
|
||||
/* prototypes for functions declared in this file */
|
||||
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
|
||||
/*
|
||||
|
@ -397,6 +399,153 @@ static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
|
|||
return paNoError;
|
||||
}
|
||||
|
||||
/* =================================================================================================== */
|
||||
/**
|
||||
* @internal
|
||||
* @brief Clip the desired size against the allowed IO buffer size range for the device.
|
||||
*/
|
||||
static PaError ClipToDeviceBufferSize( AudioDeviceID macCoreDeviceId,
|
||||
int isInput, UInt32 desiredSize, UInt32 *allowedSize )
|
||||
{
|
||||
UInt32 resultSize = desiredSize;
|
||||
AudioValueRange audioRange;
|
||||
UInt32 propSize = sizeof( audioRange );
|
||||
PaError err = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) );
|
||||
resultSize = MAX( resultSize, audioRange.mMinimum );
|
||||
resultSize = MIN( resultSize, audioRange.mMaximum );
|
||||
*allowedSize = resultSize;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* =================================================================================================== */
|
||||
#if 0
|
||||
static void DumpDeviceProperties( AudioDeviceID macCoreDeviceId,
|
||||
int isInput )
|
||||
{
|
||||
PaError err;
|
||||
int i;
|
||||
UInt32 propSize;
|
||||
UInt32 deviceLatency;
|
||||
UInt32 streamLatency;
|
||||
UInt32 bufferFrames;
|
||||
UInt32 safetyOffset;
|
||||
AudioStreamID streamIDs[128];
|
||||
|
||||
printf("\n======= latency query : macCoreDeviceId = %d, isInput %d =======\n", (int)macCoreDeviceId, isInput );
|
||||
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames));
|
||||
printf("kAudioDevicePropertyBufferFrameSize: err = %d, propSize = %d, value = %d\n", err, propSize, bufferFrames );
|
||||
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset));
|
||||
printf("kAudioDevicePropertySafetyOffset: err = %d, propSize = %d, value = %d\n", err, propSize, safetyOffset );
|
||||
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency));
|
||||
printf("kAudioDevicePropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, deviceLatency );
|
||||
|
||||
AudioValueRange audioRange;
|
||||
propSize = sizeof( audioRange );
|
||||
err = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) );
|
||||
printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, minimum = %g\n", err, propSize, audioRange.mMinimum);
|
||||
printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, maximum = %g\n", err, propSize, audioRange.mMaximum );
|
||||
|
||||
/* Get the streams from the device and query their latency. */
|
||||
propSize = sizeof(streamIDs);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0]));
|
||||
int numStreams = propSize / sizeof(AudioStreamID);
|
||||
for( i=0; i<numStreams; i++ )
|
||||
{
|
||||
printf("Stream #%d = %d---------------------- \n", i, streamIDs[i] );
|
||||
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
|
||||
printf(" kAudioStreamPropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, streamLatency );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* =================================================================================================== */
|
||||
/**
|
||||
* @internal
|
||||
* Calculate the fixed latency from the system and the device.
|
||||
* Sum of kAudioStreamPropertyLatency +
|
||||
* kAudioDevicePropertySafetyOffset +
|
||||
* kAudioDevicePropertyLatency
|
||||
*
|
||||
* Some useful info from Jeff Moore on latency.
|
||||
* http://osdir.com/ml/coreaudio-api/2010-01/msg00046.html
|
||||
* http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html
|
||||
*/
|
||||
static PaError CalculateFixedDeviceLatency( AudioDeviceID macCoreDeviceId, int isInput, UInt32 *fixedLatencyPtr )
|
||||
{
|
||||
PaError err;
|
||||
UInt32 propSize;
|
||||
UInt32 deviceLatency;
|
||||
UInt32 streamLatency;
|
||||
UInt32 safetyOffset;
|
||||
AudioStreamID streamIDs[1];
|
||||
|
||||
// To get stream latency we have to get a streamID from the device.
|
||||
// We are only going to look at the first stream so only fetch one stream.
|
||||
propSize = sizeof(streamIDs);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0]));
|
||||
if( err != paNoError ) goto error;
|
||||
if( propSize == sizeof(AudioStreamID) )
|
||||
{
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioStreamGetProperty(streamIDs[0], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
|
||||
}
|
||||
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset));
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency));
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
*fixedLatencyPtr = deviceLatency + streamLatency + safetyOffset;
|
||||
return err;
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* =================================================================================================== */
|
||||
static PaError CalculateDefaultDeviceLatencies( AudioDeviceID macCoreDeviceId,
|
||||
int isInput, UInt32 *lowLatencyFramesPtr,
|
||||
UInt32 *highLatencyFramesPtr )
|
||||
{
|
||||
UInt32 propSize;
|
||||
UInt32 bufferFrames = 0;
|
||||
UInt32 fixedLatency = 0;
|
||||
UInt32 clippedMinBufferSize = 0;
|
||||
|
||||
//DumpDeviceProperties( macCoreDeviceId, isInput );
|
||||
|
||||
PaError err = CalculateFixedDeviceLatency( macCoreDeviceId, isInput, &fixedLatency );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
// For low latency use a small fixed size buffer clipped to the device range.
|
||||
err = ClipToDeviceBufferSize( macCoreDeviceId, isInput, PA_MAC_SMALL_BUFFER_SIZE, &clippedMinBufferSize );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
// For high latency use the default device buffer size.
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames));
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
*lowLatencyFramesPtr = fixedLatency + clippedMinBufferSize;
|
||||
*highLatencyFramesPtr = fixedLatency + bufferFrames;
|
||||
|
||||
return err;
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* =================================================================================================== */
|
||||
|
||||
static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
|
||||
PaDeviceInfo *deviceInfo,
|
||||
AudioDeviceID macCoreDeviceId,
|
||||
|
@ -407,7 +556,6 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
|
|||
UInt32 i;
|
||||
int numChannels = 0;
|
||||
AudioBufferList *buflist = NULL;
|
||||
UInt32 frameLatency;
|
||||
|
||||
VVDBUG(("GetChannelInfo()\n"));
|
||||
|
||||
|
@ -433,7 +581,7 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
|
|||
else
|
||||
deviceInfo->maxOutputChannels = numChannels;
|
||||
|
||||
if (numChannels > 0) /* do not try to retrieve the latency if there is no channels. */
|
||||
if (numChannels > 0) /* do not try to retrieve the latency if there are no channels. */
|
||||
{
|
||||
/* Get the latency. Don't fail if we can't get this. */
|
||||
/* default to something reasonable */
|
||||
|
@ -441,29 +589,23 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
|
|||
deviceInfo->defaultHighInputLatency = .10;
|
||||
deviceInfo->defaultLowOutputLatency = .01;
|
||||
deviceInfo->defaultHighOutputLatency = .10;
|
||||
propSize = sizeof(UInt32);
|
||||
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
|
||||
if (!err)
|
||||
UInt32 lowLatencyFrames = 0;
|
||||
UInt32 highLatencyFrames = 0;
|
||||
err = CalculateDefaultDeviceLatencies( macCoreDeviceId, isInput, &lowLatencyFrames, &highLatencyFrames );
|
||||
if( err == 0 )
|
||||
{
|
||||
/** FEEDBACK:
|
||||
* This code was arrived at by trial and error, and some extentive, but not exhaustive
|
||||
* testing. Sebastien Beaulieu <seb@plogue.com> has suggested using
|
||||
* kAudioDevicePropertyLatency + kAudioDevicePropertySafetyOffset + buffer size instead.
|
||||
* At the time this code was written, many users were reporting dropouts with audio
|
||||
* programs that probably used this formula. This was probably
|
||||
* around 10.4.4, and the problem is probably fixed now. So perhaps
|
||||
* his formula should be reviewed and used.
|
||||
* */
|
||||
double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
|
||||
|
||||
double lowLatencySeconds = lowLatencyFrames / deviceInfo->defaultSampleRate;
|
||||
double highLatencySeconds = highLatencyFrames / deviceInfo->defaultSampleRate;
|
||||
if (isInput)
|
||||
{
|
||||
deviceInfo->defaultLowInputLatency = 3 * secondLatency;
|
||||
deviceInfo->defaultHighInputLatency = 3 * 10 * secondLatency;
|
||||
deviceInfo->defaultLowInputLatency = lowLatencySeconds;
|
||||
deviceInfo->defaultHighInputLatency = highLatencySeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceInfo->defaultLowOutputLatency = 3 * secondLatency;
|
||||
deviceInfo->defaultHighOutputLatency = 3 * 10 * secondLatency;
|
||||
deviceInfo->defaultLowOutputLatency = lowLatencySeconds;
|
||||
deviceInfo->defaultHighOutputLatency = highLatencySeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -474,6 +616,7 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* =================================================================================================== */
|
||||
static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
|
||||
PaDeviceInfo *deviceInfo,
|
||||
AudioDeviceID macCoreDeviceId,
|
||||
|
@ -797,75 +940,172 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
|
|||
return paFormatIsSupported;
|
||||
}
|
||||
|
||||
|
||||
static void UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( PaMacCoreStream *stream )
|
||||
/* ================================================================================= */
|
||||
static void InitializeDeviceProperties( PaMacCoreDeviceProperties *deviceProperties )
|
||||
{
|
||||
/* FIXME: not sure if this should be the sample rate of the output device or the output unit */
|
||||
Float64 actualOutputSampleRate = stream->outDeviceSampleRate;
|
||||
UInt32 propSize = sizeof(Float64);
|
||||
OSStatus osErr = AudioDeviceGetProperty( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate, &propSize, &actualOutputSampleRate);
|
||||
if( osErr != noErr || actualOutputSampleRate < .01 ) // avoid divide by zero if there's an error
|
||||
actualOutputSampleRate = stream->outDeviceSampleRate;
|
||||
memset( deviceProperties, 0, sizeof(PaMacCoreDeviceProperties) );
|
||||
deviceProperties->sampleRate = 1.0; // Better than random. Overwritten by actual values later on.
|
||||
deviceProperties->samplePeriod = 1.0 / deviceProperties->sampleRate;
|
||||
}
|
||||
|
||||
stream->recipricalOfActualOutputSampleRate = 1. / actualOutputSampleRate;
|
||||
static Float64 CalculateSoftwareLatencyFromProperties( PaMacCoreStream *stream, PaMacCoreDeviceProperties *deviceProperties )
|
||||
{
|
||||
UInt32 latencyFrames = deviceProperties->bufferFrameSize + deviceProperties->deviceLatency + deviceProperties->safetyOffset;
|
||||
return latencyFrames * deviceProperties->samplePeriod; // same as dividing by sampleRate but faster
|
||||
}
|
||||
|
||||
static Float64 CalculateHardwareLatencyFromProperties( PaMacCoreStream *stream, PaMacCoreDeviceProperties *deviceProperties )
|
||||
{
|
||||
return deviceProperties->deviceLatency * deviceProperties->samplePeriod; // same as dividing by sampleRate but faster
|
||||
}
|
||||
|
||||
/* Calculate values used to convert Apple timestamps into PA timestamps
|
||||
* from the device properties. The final results of this calculation
|
||||
* will be used in the audio callback function.
|
||||
*/
|
||||
static void UpdateTimeStampOffsets( PaMacCoreStream *stream )
|
||||
{
|
||||
Float64 inputSoftwareLatency = 0.0;
|
||||
Float64 inputHardwareLatency = 0.0;
|
||||
Float64 outputSoftwareLatency = 0.0;
|
||||
Float64 outputHardwareLatency = 0.0;
|
||||
|
||||
if( stream->inputUnit != NULL )
|
||||
{
|
||||
inputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->inputProperties );
|
||||
inputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->inputProperties );
|
||||
}
|
||||
if( stream->outputUnit != NULL )
|
||||
{
|
||||
outputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->outputProperties );
|
||||
outputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->outputProperties );
|
||||
}
|
||||
|
||||
/* We only need a mutex around setting these variables as a group. */
|
||||
pthread_mutex_lock( &stream->timingInformationMutex );
|
||||
stream->timestampOffsetCombined = inputSoftwareLatency + outputSoftwareLatency;
|
||||
stream->timestampOffsetInputDevice = inputHardwareLatency;
|
||||
stream->timestampOffsetOutputDevice = outputHardwareLatency;
|
||||
pthread_mutex_unlock( &stream->timingInformationMutex );
|
||||
}
|
||||
|
||||
/* ================================================================================= */
|
||||
/* Query sample rate property. */
|
||||
static OSStatus UpdateSampleRateFromDeviceProperty( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput )
|
||||
{
|
||||
PaMacCoreDeviceProperties * deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
|
||||
/* FIXME: not sure if this should be the sample rate of the output device or the output unit */
|
||||
Float64 actualSampleRate = deviceProperties->sampleRate;
|
||||
UInt32 propSize = sizeof(Float64);
|
||||
OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate, &propSize, &actualSampleRate);
|
||||
if( (osErr == noErr) && (actualSampleRate > 1000.0) ) // avoid divide by zero if there's an error
|
||||
{
|
||||
deviceProperties->sampleRate = actualSampleRate;
|
||||
deviceProperties->samplePeriod = 1.0 / actualSampleRate;
|
||||
}
|
||||
return osErr;
|
||||
}
|
||||
|
||||
static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
|
||||
{
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
|
||||
|
||||
pthread_mutex_lock( &stream->timingInformationMutex );
|
||||
UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( stream );
|
||||
pthread_mutex_unlock( &stream->timingInformationMutex );
|
||||
|
||||
return noErr;
|
||||
OSStatus osErr = UpdateSampleRateFromDeviceProperty( stream, inDevice, isInput );
|
||||
if( osErr == noErr )
|
||||
{
|
||||
UpdateTimeStampOffsets( stream );
|
||||
}
|
||||
return osErr;
|
||||
}
|
||||
|
||||
static void UpdateOutputLatencySamplesFromDeviceProperty( PaMacCoreStream *stream )
|
||||
/* ================================================================================= */
|
||||
static OSStatus QueryUInt32DeviceProperty( AudioDeviceID deviceID, Boolean isInput, AudioDevicePropertyID propertyID, UInt32 *outValue )
|
||||
{
|
||||
UInt32 deviceOutputLatencySamples = 0;
|
||||
UInt32 propSize = sizeof(UInt32);
|
||||
OSStatus osErr = AudioDeviceGetProperty( stream->outputDevice, 0, /* isInput= */ FALSE, kAudioDevicePropertyLatency, &propSize, &deviceOutputLatencySamples);
|
||||
if( osErr != noErr )
|
||||
deviceOutputLatencySamples = 0;
|
||||
|
||||
stream->deviceOutputLatencySamples = deviceOutputLatencySamples;
|
||||
UInt32 propertyValue = 0;
|
||||
UInt32 propertySize = sizeof(UInt32);
|
||||
OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, propertyID, &propertySize, &propertyValue);
|
||||
if( osErr == noErr )
|
||||
{
|
||||
*outValue = propertyValue;
|
||||
}
|
||||
return osErr;
|
||||
}
|
||||
|
||||
static OSStatus AudioDevicePropertyOutputLatencySamplesListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
|
||||
static OSStatus AudioDevicePropertyGenericListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
|
||||
{
|
||||
OSStatus osErr = noErr;
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
|
||||
PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
|
||||
UInt32 *valuePtr = NULL;
|
||||
switch( inPropertyID )
|
||||
{
|
||||
case kAudioDevicePropertySafetyOffset:
|
||||
valuePtr = &deviceProperties->safetyOffset;
|
||||
break;
|
||||
|
||||
pthread_mutex_lock( &stream->timingInformationMutex );
|
||||
UpdateOutputLatencySamplesFromDeviceProperty( stream );
|
||||
pthread_mutex_unlock( &stream->timingInformationMutex );
|
||||
case kAudioDevicePropertyLatency:
|
||||
valuePtr = &deviceProperties->deviceLatency;
|
||||
break;
|
||||
|
||||
return noErr;
|
||||
case kAudioDevicePropertyBufferFrameSize:
|
||||
valuePtr = &deviceProperties->bufferFrameSize;
|
||||
break;
|
||||
}
|
||||
if( valuePtr != NULL )
|
||||
{
|
||||
osErr = QueryUInt32DeviceProperty( inDevice, isInput, inPropertyID, valuePtr );
|
||||
if( osErr == noErr )
|
||||
{
|
||||
UpdateTimeStampOffsets( stream );
|
||||
}
|
||||
}
|
||||
return osErr;
|
||||
}
|
||||
|
||||
static void UpdateInputLatencySamplesFromDeviceProperty( PaMacCoreStream *stream )
|
||||
/* ================================================================================= */
|
||||
/*
|
||||
* Setup listeners in case device properties change during the run. */
|
||||
static OSStatus SetupDevicePropertyListeners( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput )
|
||||
{
|
||||
UInt32 deviceInputLatencySamples = 0;
|
||||
UInt32 propSize = sizeof(UInt32);
|
||||
OSStatus osErr = AudioDeviceGetProperty( stream->inputDevice, 0, /* isInput= */ TRUE, kAudioDevicePropertyLatency, &propSize, &deviceInputLatencySamples);
|
||||
if( osErr != noErr )
|
||||
deviceInputLatencySamples = 0;
|
||||
OSStatus osErr = noErr;
|
||||
PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
|
||||
|
||||
stream->deviceInputLatencySamples = deviceInputLatencySamples;
|
||||
// Start with the current values for the device properties.
|
||||
UpdateSampleRateFromDeviceProperty( stream, deviceID, isInput );
|
||||
|
||||
if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
|
||||
kAudioDevicePropertyLatency, &deviceProperties->deviceLatency )) != noErr ) return osErr;
|
||||
if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
|
||||
kAudioDevicePropertyBufferFrameSize, &deviceProperties->bufferFrameSize )) != noErr ) return osErr;
|
||||
if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
|
||||
kAudioDevicePropertySafetyOffset, &deviceProperties->safetyOffset )) != noErr ) return osErr;
|
||||
|
||||
AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
|
||||
AudioDevicePropertyActualSampleRateListenerProc, stream );
|
||||
|
||||
AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioStreamPropertyLatency,
|
||||
AudioDevicePropertyGenericListenerProc, stream );
|
||||
AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
|
||||
AudioDevicePropertyGenericListenerProc, stream );
|
||||
AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
|
||||
AudioDevicePropertyGenericListenerProc, stream );
|
||||
|
||||
return osErr;
|
||||
}
|
||||
|
||||
static OSStatus AudioDevicePropertyInputLatencySamplesListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
|
||||
static void CleanupDevicePropertyListeners( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput )
|
||||
{
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
|
||||
AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
|
||||
AudioDevicePropertyActualSampleRateListenerProc );
|
||||
|
||||
pthread_mutex_lock( &stream->timingInformationMutex );
|
||||
UpdateInputLatencySamplesFromDeviceProperty( stream );
|
||||
pthread_mutex_unlock( &stream->timingInformationMutex );
|
||||
|
||||
return noErr;
|
||||
AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyLatency,
|
||||
AudioDevicePropertyGenericListenerProc );
|
||||
AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
|
||||
AudioDevicePropertyGenericListenerProc );
|
||||
AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
|
||||
AudioDevicePropertyGenericListenerProc );
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================================= */
|
||||
static PaError OpenAndSetupOneAudioUnit(
|
||||
const PaMacCoreStream *stream,
|
||||
const PaStreamParameters *inStreamParams,
|
||||
|
@ -1299,11 +1539,17 @@ static PaError OpenAndSetupOneAudioUnit(
|
|||
ERR_WRAP( AudioUnitInitialize(*audioUnit) );
|
||||
|
||||
if( inStreamParams && outStreamParams )
|
||||
{
|
||||
VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) );
|
||||
}
|
||||
else if( inStreamParams )
|
||||
{
|
||||
VDBUG( ("Opened device %ld for input.\n", *audioDevice ) );
|
||||
}
|
||||
else if( outStreamParams )
|
||||
{
|
||||
VDBUG( ("Opened device %ld for output.\n", *audioDevice ) );
|
||||
}
|
||||
return paNoError;
|
||||
#undef ERR_WRAP
|
||||
|
||||
|
@ -1315,13 +1561,74 @@ static PaError OpenAndSetupOneAudioUnit(
|
|||
return paResult;
|
||||
}
|
||||
|
||||
/* =================================================================================================== */
|
||||
|
||||
static UInt32 CalculateOptimalBufferSize( PaMacAUHAL *auhalHostApi,
|
||||
const PaStreamParameters *inputParameters,
|
||||
const PaStreamParameters *outputParameters,
|
||||
UInt32 fixedInputLatency,
|
||||
UInt32 fixedOutputLatency,
|
||||
double sampleRate,
|
||||
UInt32 requestedFramesPerBuffer )
|
||||
{
|
||||
UInt32 suggested = 0;
|
||||
// Use maximum of suggested input and output latencies.
|
||||
if( inputParameters )
|
||||
{
|
||||
UInt32 suggestedLatencyFrames = inputParameters->suggestedLatency * sampleRate;
|
||||
// Calculate a buffer size assuming we are double buffered.
|
||||
SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedInputLatency;
|
||||
// Prevent negative latency.
|
||||
variableLatencyFrames = MAX( variableLatencyFrames, 0 );
|
||||
suggested = MAX( suggested, (UInt32) variableLatencyFrames );
|
||||
}
|
||||
if( outputParameters )
|
||||
{
|
||||
UInt32 suggestedLatencyFrames = outputParameters->suggestedLatency * sampleRate;
|
||||
SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedOutputLatency;
|
||||
variableLatencyFrames = MAX( variableLatencyFrames, 0 );
|
||||
suggested = MAX( suggested, (UInt32) variableLatencyFrames );
|
||||
}
|
||||
|
||||
VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n",
|
||||
suggested ) );
|
||||
|
||||
if( requestedFramesPerBuffer != paFramesPerBufferUnspecified )
|
||||
{
|
||||
if( suggested > (requestedFramesPerBuffer + 1) )
|
||||
{
|
||||
// If the user asks for higher latency than the requested buffer size would provide
|
||||
// then put multiple user buffers in one host buffer.
|
||||
UInt32 userBuffersPerHostBuffer = (suggested + (requestedFramesPerBuffer - 1)) / requestedFramesPerBuffer;
|
||||
suggested = userBuffersPerHostBuffer * requestedFramesPerBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
// Clip to the capabilities of the device.
|
||||
if( inputParameters )
|
||||
{
|
||||
ClipToDeviceBufferSize( auhalHostApi->devIds[inputParameters->device],
|
||||
true, // In the old code isInput was false!
|
||||
suggested, &suggested );
|
||||
}
|
||||
if( outputParameters )
|
||||
{
|
||||
ClipToDeviceBufferSize( auhalHostApi->devIds[outputParameters->device],
|
||||
false, suggested, &suggested );
|
||||
}
|
||||
VDBUG(("After querying hardware, setting block size to %ld.\n", suggested));
|
||||
|
||||
return suggested;
|
||||
}
|
||||
|
||||
/* =================================================================================================== */
|
||||
/* 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,
|
||||
unsigned long requestedFramesPerBuffer,
|
||||
PaStreamFlags streamFlags,
|
||||
PaStreamCallback *streamCallback,
|
||||
void *userData )
|
||||
|
@ -1332,23 +1639,30 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
int inputChannelCount, outputChannelCount;
|
||||
PaSampleFormat inputSampleFormat, outputSampleFormat;
|
||||
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
|
||||
UInt32 fixedInputLatency = 0;
|
||||
UInt32 fixedOutputLatency = 0;
|
||||
// Accumulate contributions to latency in these variables.
|
||||
UInt32 inputLatencyFrames = 0;
|
||||
UInt32 outputLatencyFrames = 0;
|
||||
UInt32 suggestedLatencyFramesPerBuffer = requestedFramesPerBuffer;
|
||||
|
||||
VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n",
|
||||
inputParameters ? inputParameters->channelCount : -1,
|
||||
inputParameters ? inputParameters->sampleFormat : -1,
|
||||
outputParameters ? outputParameters->channelCount : -1,
|
||||
outputParameters ? outputParameters->sampleFormat : -1,
|
||||
(float) sampleRate,
|
||||
framesPerBuffer ));
|
||||
requestedFramesPerBuffer ));
|
||||
VDBUG( ("Opening Stream.\n") );
|
||||
|
||||
/*These first few bits of code are from paSkeleton with few modifications.*/
|
||||
/* These first few bits of code are from paSkeleton with few modifications. */
|
||||
if( inputParameters )
|
||||
{
|
||||
inputChannelCount = inputParameters->channelCount;
|
||||
inputSampleFormat = inputParameters->sampleFormat;
|
||||
|
||||
/* @todo Blocking read/write on Mac is not yet supported. */
|
||||
if( inputSampleFormat & paNonInterleaved )
|
||||
if( !streamCallback && inputSampleFormat & paNonInterleaved )
|
||||
{
|
||||
return paSampleFormatNotSupported;
|
||||
}
|
||||
|
@ -1378,7 +1692,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
outputSampleFormat = outputParameters->sampleFormat;
|
||||
|
||||
/* @todo Blocking read/write on Mac is not yet supported. */
|
||||
if( outputSampleFormat & paNonInterleaved )
|
||||
if( !streamCallback && outputSampleFormat & paNonInterleaved )
|
||||
{
|
||||
return paSampleFormatNotSupported;
|
||||
}
|
||||
|
@ -1454,70 +1768,25 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
|
||||
|
||||
/* -- handle paFramesPerBufferUnspecified -- */
|
||||
if( framesPerBuffer == paFramesPerBufferUnspecified ) {
|
||||
long requested = 64;
|
||||
|
||||
if( inputParameters )
|
||||
requested = MAX( requested, inputParameters->suggestedLatency * sampleRate / 2 );
|
||||
{
|
||||
CalculateFixedDeviceLatency( auhalHostApi->devIds[inputParameters->device], true, &fixedInputLatency );
|
||||
inputLatencyFrames += fixedInputLatency;
|
||||
}
|
||||
if( outputParameters )
|
||||
requested = MAX( requested, outputParameters->suggestedLatency *sampleRate / 2 );
|
||||
VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n",
|
||||
requested ) );
|
||||
if( requested <= 64 ) {
|
||||
/*requested a realtively low latency. make sure this is in range of devices */
|
||||
/*try to get the device's min natural buffer size and use that (but no smaller than 64).*/
|
||||
AudioValueRange audioRange;
|
||||
UInt32 size = sizeof( audioRange );
|
||||
if( inputParameters ) {
|
||||
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&size, &audioRange ) );
|
||||
if( result )
|
||||
requested = MAX( requested, audioRange.mMinimum );
|
||||
{
|
||||
CalculateFixedDeviceLatency( auhalHostApi->devIds[outputParameters->device], false, &fixedOutputLatency );
|
||||
outputLatencyFrames += fixedOutputLatency;
|
||||
|
||||
}
|
||||
size = sizeof( audioRange );
|
||||
if( outputParameters ) {
|
||||
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&size, &audioRange ) );
|
||||
if( result )
|
||||
requested = MAX( requested, audioRange.mMinimum );
|
||||
}
|
||||
} else {
|
||||
/* requested a realtively high latency. make sure this is in range of devices */
|
||||
/*try to get the device's max natural buffer size and use that (but no larger than 1024).*/
|
||||
AudioValueRange audioRange;
|
||||
UInt32 size = sizeof( audioRange );
|
||||
requested = MIN( requested, 1024 );
|
||||
if( inputParameters ) {
|
||||
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&size, &audioRange ) );
|
||||
if( result )
|
||||
requested = MIN( requested, audioRange.mMaximum );
|
||||
}
|
||||
size = sizeof( audioRange );
|
||||
if( outputParameters ) {
|
||||
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&size, &audioRange ) );
|
||||
if( result )
|
||||
requested = MIN( requested, audioRange.mMaximum );
|
||||
}
|
||||
}
|
||||
/* -- double check ranges -- */
|
||||
if( requested > 1024 ) requested = 1024;
|
||||
if( requested < 64 ) requested = 64;
|
||||
VDBUG(("After querying hardware, setting block size to %ld.\n", requested));
|
||||
framesPerBuffer = requested;
|
||||
|
||||
suggestedLatencyFramesPerBuffer = CalculateOptimalBufferSize( auhalHostApi, inputParameters, outputParameters,
|
||||
fixedInputLatency, fixedOutputLatency,
|
||||
sampleRate, requestedFramesPerBuffer );
|
||||
if( requestedFramesPerBuffer == paFramesPerBufferUnspecified )
|
||||
{
|
||||
requestedFramesPerBuffer = suggestedLatencyFramesPerBuffer;
|
||||
}
|
||||
|
||||
/* -- Now we actually open and setup streams. -- */
|
||||
|
@ -1528,7 +1797,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
result = OpenAndSetupOneAudioUnit( stream,
|
||||
inputParameters,
|
||||
outputParameters,
|
||||
framesPerBuffer,
|
||||
suggestedLatencyFramesPerBuffer,
|
||||
&inputFramesPerBuffer,
|
||||
&outputFramesPerBuffer,
|
||||
auhalHostApi,
|
||||
|
@ -1551,7 +1820,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
result = OpenAndSetupOneAudioUnit( stream,
|
||||
NULL,
|
||||
outputParameters,
|
||||
framesPerBuffer,
|
||||
suggestedLatencyFramesPerBuffer,
|
||||
NULL,
|
||||
&outputFramesPerBuffer,
|
||||
auhalHostApi,
|
||||
|
@ -1565,7 +1834,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
result = OpenAndSetupOneAudioUnit( stream,
|
||||
inputParameters,
|
||||
NULL,
|
||||
framesPerBuffer,
|
||||
suggestedLatencyFramesPerBuffer,
|
||||
&inputFramesPerBuffer,
|
||||
NULL,
|
||||
auhalHostApi,
|
||||
|
@ -1580,6 +1849,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
stream->outputFramesPerBuffer = outputFramesPerBuffer;
|
||||
}
|
||||
|
||||
inputLatencyFrames += stream->inputFramesPerBuffer;
|
||||
outputLatencyFrames += stream->outputFramesPerBuffer;
|
||||
|
||||
if( stream->inputUnit ) {
|
||||
const size_t szfl = sizeof(float);
|
||||
/* setup the AudioBufferList used for input */
|
||||
|
@ -1605,7 +1877,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
* ring buffer to store inpt data while waiting for output
|
||||
* data.
|
||||
*/
|
||||
if( (stream->outputUnit && stream->inputUnit != stream->outputUnit)
|
||||
if( (stream->outputUnit && (stream->inputUnit != stream->outputUnit))
|
||||
|| stream->inputSRConverter )
|
||||
{
|
||||
/* May want the ringSize ot initial position in
|
||||
|
@ -1636,6 +1908,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
middle of the buffer */
|
||||
if( stream->outputUnit )
|
||||
PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer, ringSize / RING_BUFFER_ADVANCE_DENOMINATOR );
|
||||
|
||||
// Just adds to input latency between input device and PA full duplex callback.
|
||||
inputLatencyFrames += ringSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1658,6 +1933,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
outputParameters?outputChannelCount:0 ) ;
|
||||
if( result != paNoError )
|
||||
goto error;
|
||||
|
||||
inputLatencyFrames += ringSize;
|
||||
outputLatencyFrames += ringSize;
|
||||
|
||||
}
|
||||
|
||||
/* -- initialize Buffer Processor -- */
|
||||
|
@ -1672,7 +1951,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
hostOutputSampleFormat,
|
||||
sampleRate,
|
||||
streamFlags,
|
||||
framesPerBuffer,
|
||||
requestedFramesPerBuffer,
|
||||
/* If sample rate conversion takes place, the buffer size
|
||||
will not be known. */
|
||||
maxHostFrames,
|
||||
|
@ -1686,16 +1965,27 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
}
|
||||
stream->bufferProcessorIsInitialized = TRUE;
|
||||
|
||||
/*
|
||||
IMPLEMENT ME: initialise the following fields with estimated or actual
|
||||
values.
|
||||
I think this is okay the way it is br 12/1/05
|
||||
maybe need to change input latency estimate if IO devs differ
|
||||
*/
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)/sampleRate;
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)/sampleRate;
|
||||
// Calculate actual latency from the sum of individual latencies.
|
||||
if( inputParameters )
|
||||
{
|
||||
inputLatencyFrames += PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor);
|
||||
stream->streamRepresentation.streamInfo.inputLatency = inputLatencyFrames / sampleRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->streamRepresentation.streamInfo.inputLatency = 0.0;
|
||||
}
|
||||
|
||||
if( outputParameters )
|
||||
{
|
||||
outputLatencyFrames += PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor);
|
||||
stream->streamRepresentation.streamInfo.outputLatency = outputLatencyFrames / sampleRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->streamRepresentation.streamInfo.outputLatency = 0.0;
|
||||
}
|
||||
|
||||
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
|
||||
|
||||
stream->sampleRate = sampleRate;
|
||||
|
@ -1728,38 +2018,26 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
stream->userInChan = inputChannelCount;
|
||||
stream->userOutChan = outputChannelCount;
|
||||
|
||||
// Setup property listeners for timestamp and latency calculations.
|
||||
pthread_mutex_init( &stream->timingInformationMutex, NULL );
|
||||
stream->timingInformationMutexIsInitialized = 1;
|
||||
|
||||
if( stream->outputUnit ) {
|
||||
UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( stream );
|
||||
stream->recipricalOfActualOutputSampleRate_ioProcCopy = stream->recipricalOfActualOutputSampleRate;
|
||||
|
||||
AudioDeviceAddPropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate,
|
||||
AudioDevicePropertyActualSampleRateListenerProc, stream );
|
||||
|
||||
UpdateOutputLatencySamplesFromDeviceProperty( stream );
|
||||
stream->deviceOutputLatencySamples_ioProcCopy = stream->deviceOutputLatencySamples;
|
||||
|
||||
AudioDeviceAddPropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyLatency,
|
||||
AudioDevicePropertyOutputLatencySamplesListenerProc, stream );
|
||||
|
||||
}else{
|
||||
stream->recipricalOfActualOutputSampleRate = 1.;
|
||||
stream->recipricalOfActualOutputSampleRate_ioProcCopy = 0.;
|
||||
stream->deviceOutputLatencySamples_ioProcCopy = 0;
|
||||
InitializeDeviceProperties( &stream->inputProperties );
|
||||
InitializeDeviceProperties( &stream->outputProperties );
|
||||
if( stream->outputUnit )
|
||||
{
|
||||
Boolean isInput = FALSE;
|
||||
SetupDevicePropertyListeners( stream, stream->outputDevice, isInput );
|
||||
}
|
||||
|
||||
if( stream->inputUnit ) {
|
||||
UpdateInputLatencySamplesFromDeviceProperty( stream );
|
||||
stream->deviceInputLatencySamples_ioProcCopy = stream->deviceInputLatencySamples;
|
||||
|
||||
AudioDeviceAddPropertyListener( stream->inputDevice, 0, /* isInput = */ TRUE, kAudioDevicePropertyLatency,
|
||||
AudioDevicePropertyInputLatencySamplesListenerProc, stream );
|
||||
}else{
|
||||
stream->deviceInputLatencySamples = 0;
|
||||
stream->deviceInputLatencySamples_ioProcCopy = 0;
|
||||
if( stream->inputUnit )
|
||||
{
|
||||
Boolean isInput = TRUE;
|
||||
SetupDevicePropertyListeners( stream, stream->inputDevice, isInput );
|
||||
}
|
||||
UpdateTimeStampOffsets( stream );
|
||||
// Setup copies to be used by audio callback.
|
||||
stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined;
|
||||
stream->timestampOffsetInputDevice_ioProcCopy = stream->timestampOffsetInputDevice;
|
||||
stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice;
|
||||
|
||||
stream->state = STOPPED;
|
||||
stream->xrunFlags = 0;
|
||||
|
@ -1829,6 +2107,7 @@ static OSStatus AudioIOProc( void *inRefCon,
|
|||
PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon;
|
||||
const bool isRender = inBusNumber == OUTPUT_ELEMENT;
|
||||
int callbackResult = paContinue ;
|
||||
double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
|
||||
|
||||
VVDBUG(("AudioIOProc()\n"));
|
||||
|
||||
|
@ -1865,9 +2144,9 @@ static OSStatus AudioIOProc( void *inRefCon,
|
|||
|
||||
if( pthread_mutex_trylock( &stream->timingInformationMutex ) == 0 ){
|
||||
/* snapshot the ioproc copy of timing information */
|
||||
stream->deviceOutputLatencySamples_ioProcCopy = stream->deviceOutputLatencySamples;
|
||||
stream->recipricalOfActualOutputSampleRate_ioProcCopy = stream->recipricalOfActualOutputSampleRate;
|
||||
stream->deviceInputLatencySamples_ioProcCopy = stream->deviceInputLatencySamples;
|
||||
stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined;
|
||||
stream->timestampOffsetInputDevice_ioProcCopy = stream->timestampOffsetInputDevice;
|
||||
stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice;
|
||||
pthread_mutex_unlock( &stream->timingInformationMutex );
|
||||
}
|
||||
|
||||
|
@ -1894,32 +2173,30 @@ static OSStatus AudioIOProc( void *inRefCon,
|
|||
{
|
||||
if( stream->inputUnit == stream->outputUnit ) /* full duplex AUHAL IOProc */
|
||||
{
|
||||
/* FIXME: review. i'm not sure this computation of inputBufferAdcTime is correct for a full-duplex AUHAL */
|
||||
timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime)
|
||||
- stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here?
|
||||
timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime)
|
||||
+ stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy;
|
||||
// Ross and Phil agreed that the following calculation is correct based on an email from Jeff Moore:
|
||||
// http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html
|
||||
// Basically the difference between the Apple output timestamp and the PA timestamp is kAudioDevicePropertyLatency.
|
||||
timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
|
||||
(stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
|
||||
timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
|
||||
}
|
||||
else /* full duplex with ring-buffer from a separate input AUHAL ioproc */
|
||||
{
|
||||
/* FIXME: review. this computation of inputBufferAdcTime is definitely wrong since it doesn't take the ring buffer latency into account */
|
||||
timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime)
|
||||
- stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here?
|
||||
timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime)
|
||||
+ stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy;
|
||||
/* FIXME: take the ring buffer latency into account */
|
||||
timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
|
||||
(stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
|
||||
timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
|
||||
}
|
||||
}
|
||||
else /* output only */
|
||||
{
|
||||
timeInfo.inputBufferAdcTime = 0;
|
||||
timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime)
|
||||
+ stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy;
|
||||
timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
|
||||
}
|
||||
}
|
||||
else /* input only */
|
||||
{
|
||||
timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime)
|
||||
- stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here?
|
||||
timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - stream->timestampOffsetInputDevice_ioProcCopy;
|
||||
timeInfo.outputBufferDacTime = 0;
|
||||
}
|
||||
|
||||
|
@ -2261,16 +2538,16 @@ static PaError CloseStream( PaStream* s )
|
|||
|
||||
if( stream ) {
|
||||
|
||||
if( stream->outputUnit ) {
|
||||
AudioDeviceRemovePropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate,
|
||||
AudioDevicePropertyActualSampleRateListenerProc );
|
||||
AudioDeviceRemovePropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyLatency,
|
||||
AudioDevicePropertyOutputLatencySamplesListenerProc );
|
||||
if( stream->outputUnit )
|
||||
{
|
||||
Boolean isInput = FALSE;
|
||||
CleanupDevicePropertyListeners( stream, stream->outputDevice, isInput );
|
||||
}
|
||||
|
||||
if( stream->inputUnit ) {
|
||||
AudioDeviceRemovePropertyListener( stream->inputDevice, 0, /* isInput = */ TRUE, kAudioDevicePropertyLatency,
|
||||
AudioDevicePropertyInputLatencySamplesListenerProc );
|
||||
if( stream->inputUnit )
|
||||
{
|
||||
Boolean isInput = FALSE;
|
||||
CleanupDevicePropertyListeners( stream, stream->inputDevice, isInput );
|
||||
}
|
||||
|
||||
if( stream->outputUnit ) {
|
||||
|
|
|
@ -113,7 +113,18 @@ typedef struct
|
|||
}
|
||||
PaMacAUHAL;
|
||||
|
||||
|
||||
typedef struct PaMacCoreDeviceProperties
|
||||
{
|
||||
/* Values in Frames from property queries. */
|
||||
UInt32 safetyOffset;
|
||||
UInt32 bufferFrameSize;
|
||||
// UInt32 streamLatency; // Seems to be the same as deviceLatency!?
|
||||
UInt32 deviceLatency;
|
||||
/* Current device sample rate. May change! */
|
||||
Float64 sampleRate;
|
||||
Float64 samplePeriod; // reciprocal
|
||||
}
|
||||
PaMacCoreDeviceProperties;
|
||||
|
||||
/* stream data structure specifically for this implementation */
|
||||
typedef struct PaMacCoreStream
|
||||
|
@ -159,17 +170,24 @@ typedef struct PaMacCoreStream
|
|||
double outDeviceSampleRate;
|
||||
double inDeviceSampleRate;
|
||||
|
||||
PaMacCoreDeviceProperties inputProperties;
|
||||
PaMacCoreDeviceProperties outputProperties;
|
||||
|
||||
/* data updated by main thread and notifications, protected by timingInformationMutex */
|
||||
int timingInformationMutexIsInitialized;
|
||||
pthread_mutex_t timingInformationMutex;
|
||||
Float64 recipricalOfActualOutputSampleRate;
|
||||
UInt32 deviceOutputLatencySamples;
|
||||
UInt32 deviceInputLatencySamples;
|
||||
|
||||
/* while the io proc is active, the following values are only accessed and manipulated by the ioproc */
|
||||
Float64 recipricalOfActualOutputSampleRate_ioProcCopy;
|
||||
UInt32 deviceOutputLatencySamples_ioProcCopy;
|
||||
UInt32 deviceInputLatencySamples_ioProcCopy;
|
||||
/* These are written by the PA thread or from CoreAudio callbacks. Protected by the mutex. */
|
||||
Float64 timestampOffsetCombined;
|
||||
Float64 timestampOffsetInputDevice;
|
||||
Float64 timestampOffsetOutputDevice;
|
||||
|
||||
/* Offsets in seconds to be applied to Apple timestamps to convert them to PA timestamps.
|
||||
* While the io proc is active, the following values are only accessed and manipulated by the ioproc */
|
||||
Float64 timestampOffsetCombined_ioProcCopy;
|
||||
Float64 timestampOffsetInputDevice_ioProcCopy;
|
||||
Float64 timestampOffsetOutputDevice_ioProcCopy;
|
||||
|
||||
}
|
||||
PaMacCoreStream;
|
||||
|
||||
|
|
|
@ -528,14 +528,12 @@ PaError setBestFramesPerBuffer( const AudioDeviceID device,
|
|||
const bool isInput = !isOutput;
|
||||
UInt32 propsize = sizeof(UInt32);
|
||||
OSErr err;
|
||||
Float64 min = -1; /*the min blocksize*/
|
||||
Float64 best = -1; /*the best blocksize*/
|
||||
int i=0;
|
||||
AudioValueRange *ranges;
|
||||
AudioValueRange range;
|
||||
|
||||
if( actualFramesPerBuffer == NULL )
|
||||
{
|
||||
actualFramesPerBuffer = &afpb;
|
||||
|
||||
}
|
||||
|
||||
/* -- try and set exact FPB -- */
|
||||
err = AudioDeviceSetProperty( device, NULL, 0, isInput,
|
||||
|
@ -545,63 +543,41 @@ PaError setBestFramesPerBuffer( const AudioDeviceID device,
|
|||
kAudioDevicePropertyBufferFrameSize,
|
||||
&propsize, actualFramesPerBuffer);
|
||||
if( err )
|
||||
{
|
||||
return ERR( err );
|
||||
}
|
||||
// Did we get the size we asked for?
|
||||
if( *actualFramesPerBuffer == requestedFramesPerBuffer )
|
||||
{
|
||||
return paNoError; /* we are done */
|
||||
}
|
||||
|
||||
/* -- fetch available block sizes -- */
|
||||
err = AudioDeviceGetPropertyInfo( device, 0, isInput,
|
||||
kAudioDevicePropertyBufferSizeRange,
|
||||
&propsize, NULL );
|
||||
if( err )
|
||||
return ERR( err );
|
||||
ranges = (AudioValueRange *)calloc( 1, propsize );
|
||||
if( !ranges )
|
||||
return paInsufficientMemory;
|
||||
// Clip requested value against legal range for the device.
|
||||
propsize = sizeof(AudioValueRange);
|
||||
err = AudioDeviceGetProperty( device, 0, isInput,
|
||||
kAudioDevicePropertyBufferSizeRange,
|
||||
&propsize, ranges );
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&propsize, &range );
|
||||
if( err )
|
||||
{
|
||||
free( ranges );
|
||||
return ERR( err );
|
||||
}
|
||||
VDBUG(("Requested block size of %lu was not available.\n",
|
||||
requestedFramesPerBuffer ));
|
||||
VDBUG(("%lu Available block sizes are:\n",propsize/sizeof(AudioValueRange)));
|
||||
#ifdef MAC_CORE_VERBOSE_DEBUG
|
||||
for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
|
||||
VDBUG( ("\t%g-%g\n",
|
||||
(float) ranges[i].mMinimum,
|
||||
(float) ranges[i].mMaximum ) );
|
||||
#endif
|
||||
VDBUG(("-----\n"));
|
||||
|
||||
/* --- now pick the best available framesPerBuffer -- */
|
||||
for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
|
||||
if( requestedFramesPerBuffer < range.mMinimum )
|
||||
{
|
||||
if( min == -1 || ranges[i].mMinimum < min ) min = ranges[i].mMinimum;
|
||||
if( ranges[i].mMaximum < requestedFramesPerBuffer ) {
|
||||
if( best < 0 )
|
||||
best = ranges[i].mMaximum;
|
||||
else if( ranges[i].mMaximum > best )
|
||||
best = ranges[i].mMaximum;
|
||||
requestedFramesPerBuffer = range.mMinimum;
|
||||
}
|
||||
else if( requestedFramesPerBuffer > range.mMaximum )
|
||||
{
|
||||
requestedFramesPerBuffer = range.mMaximum;
|
||||
}
|
||||
if( best == -1 )
|
||||
best = min;
|
||||
VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) );
|
||||
free( ranges );
|
||||
|
||||
/* --- set the buffer size (ignore errors) -- */
|
||||
requestedFramesPerBuffer = (UInt32) best ;
|
||||
propsize = sizeof( UInt32 );
|
||||
err = AudioDeviceSetProperty( device, NULL, 0, isInput,
|
||||
kAudioDevicePropertyBufferSize,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
propsize, &requestedFramesPerBuffer );
|
||||
/* --- read the property to check that it was set -- */
|
||||
err = AudioDeviceGetProperty( device, 0, isInput,
|
||||
kAudioDevicePropertyBufferSize,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
&propsize, actualFramesPerBuffer );
|
||||
|
||||
if( err )
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_win_ds.c 1606 2011-02-17 15:56:04Z rob_bielik $
|
||||
* $Id: pa_win_ds.c 1685 2011-05-11 21:05:18Z rossb $
|
||||
* Portable Audio I/O Library DirectSound implementation
|
||||
*
|
||||
* Authors: Phil Burk, Robert Marsanyi & Ross Bencina
|
||||
|
@ -41,10 +41,17 @@
|
|||
@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 mechanim.
|
||||
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>
|
||||
|
@ -61,6 +68,11 @@
|
|||
#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"
|
||||
|
@ -74,16 +86,44 @@
|
|||
#include "pa_win_ds_dynlink.h"
|
||||
#include "pa_win_waveformat.h"
|
||||
#include "pa_win_wdmks_utils.h"
|
||||
|
||||
#ifndef PA_USE_WMME
|
||||
#error "Portaudio internal error: PA_USE_WMME=0/1 not defined. pa_hostapi.h should ensure that it is."
|
||||
#endif
|
||||
#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
|
||||
*/
|
||||
|
@ -190,7 +230,7 @@ typedef struct
|
|||
|
||||
/* implementation specific data goes here */
|
||||
|
||||
char comWasInitialized;
|
||||
PaWinUtilComInitializationResult comInitializationResult;
|
||||
|
||||
} PaWinDsHostApiRepresentation;
|
||||
|
||||
|
@ -231,7 +271,6 @@ typedef struct PaWinDsStream
|
|||
UINT inputSize;
|
||||
|
||||
|
||||
MMRESULT timerID;
|
||||
int framesPerDSBuffer;
|
||||
double framesWritten;
|
||||
double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
|
||||
|
@ -247,6 +286,21 @@ typedef struct PaWinDsStream
|
|||
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;
|
||||
|
||||
|
||||
|
@ -1015,32 +1069,15 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
|
|||
int i, deviceCount;
|
||||
PaWinDsHostApiRepresentation *winDsHostApi;
|
||||
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
|
||||
|
||||
PaWinDsDeviceInfo *deviceInfoArray;
|
||||
char comWasInitialized = 0;
|
||||
|
||||
/*
|
||||
If COM is already initialized CoInitialize will either return
|
||||
FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
|
||||
threading mode. In either case we shouldn't consider it an error
|
||||
but we need to be careful to not call CoUninitialize() if
|
||||
RPC_E_CHANGED_MODE was returned.
|
||||
*/
|
||||
|
||||
HRESULT hr = CoInitialize(NULL);
|
||||
if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
|
||||
return paUnanticipatedHostError;
|
||||
|
||||
if( hr != RPC_E_CHANGED_MODE )
|
||||
comWasInitialized = 1;
|
||||
PaWinDs_InitializeDSoundEntryPoints();
|
||||
|
||||
/* initialise guid vectors so they can be safely deleted on error */
|
||||
deviceNamesAndGUIDs.winDsHostApi = NULL;
|
||||
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
|
||||
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
|
||||
|
||||
PaWinDs_InitializeDSoundEntryPoints();
|
||||
|
||||
winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
|
||||
if( !winDsHostApi )
|
||||
{
|
||||
|
@ -1048,7 +1085,11 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
|
|||
goto error;
|
||||
}
|
||||
|
||||
winDsHostApi->comWasInitialized = comWasInitialized;
|
||||
result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
|
||||
if( result != paNoError )
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
|
||||
if( !winDsHostApi->allocations )
|
||||
|
@ -1180,22 +1221,10 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
|
|||
return result;
|
||||
|
||||
error:
|
||||
if( winDsHostApi )
|
||||
{
|
||||
if( winDsHostApi->allocations )
|
||||
{
|
||||
PaUtil_FreeAllAllocations( winDsHostApi->allocations );
|
||||
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
|
||||
}
|
||||
|
||||
PaUtil_FreeMemory( winDsHostApi );
|
||||
}
|
||||
|
||||
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
|
||||
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
|
||||
|
||||
if( comWasInitialized )
|
||||
CoUninitialize();
|
||||
Terminate( winDsHostApi );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1205,25 +1234,20 @@ error:
|
|||
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
|
||||
{
|
||||
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
|
||||
char comWasInitialized = winDsHostApi->comWasInitialized;
|
||||
|
||||
/*
|
||||
IMPLEMENT ME:
|
||||
- clean up any resources not handled by the allocation group
|
||||
*/
|
||||
|
||||
if( winDsHostApi ){
|
||||
if( winDsHostApi->allocations )
|
||||
{
|
||||
PaUtil_FreeAllAllocations( winDsHostApi->allocations );
|
||||
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
|
||||
}
|
||||
|
||||
PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
|
||||
|
||||
PaUtil_FreeMemory( winDsHostApi );
|
||||
}
|
||||
|
||||
PaWinDs_TerminateDSoundEntryPoints();
|
||||
|
||||
if( comWasInitialized )
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1847,9 +1871,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
bufferProcessorIsInitialized = 1;
|
||||
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
(PaTime)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */
|
||||
(PaTime)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
(PaTime)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */
|
||||
(PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */
|
||||
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
|
||||
|
||||
|
||||
|
@ -1861,8 +1885,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
int minLatencyFrames;
|
||||
unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
|
||||
|
||||
#ifdef PA_WIN_DS_USE_WMME_TIMER
|
||||
stream->timerID = 0;
|
||||
|
||||
#endif
|
||||
stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
|
||||
if( stream->processingCompleted == NULL )
|
||||
{
|
||||
|
@ -1870,6 +1895,26 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
goto error;
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
/* Get system minimum latency. */
|
||||
minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
|
||||
|
||||
|
@ -2061,6 +2106,16 @@ error:
|
|||
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 );
|
||||
|
@ -2264,7 +2319,6 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
|
||||
if( framesToXfer > 0 )
|
||||
{
|
||||
|
||||
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
|
||||
|
||||
/* The outputBufferDacTime parameter should indicates the time at which
|
||||
|
@ -2469,6 +2523,75 @@ static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD
|
|||
}
|
||||
}
|
||||
|
||||
#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;
|
||||
MMRESULT mmResult;
|
||||
HANDLE hWaitableTimer;
|
||||
LARGE_INTEGER dueTime;
|
||||
int framesPerWakeup, msecPerWakeup;
|
||||
|
||||
framesPerWakeup = stream->framesPerDSBuffer / 4; /* always poll using quadruple buffering, probably not the best strategy */
|
||||
msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
|
||||
if( msecPerWakeup < 1 ) msecPerWakeup = 1;
|
||||
else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
|
||||
|
||||
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
|
||||
assert( stream->waitableTimer != NULL );
|
||||
|
||||
/* invoke first timeout immediately */
|
||||
dueTime.LowPart = msecPerWakeup * 1000 * 10;
|
||||
dueTime.HighPart = 0;
|
||||
|
||||
/* tick using waitable timer */
|
||||
if( SetWaitableTimer( stream->waitableTimer, &dueTime, msecPerWakeup, 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, msecPerWakeup * 10, /* alertable = */ TRUE );
|
||||
|
||||
}while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION );
|
||||
}
|
||||
|
||||
CancelWaitableTimer( stream->waitableTimer );
|
||||
|
||||
#else
|
||||
|
||||
/* tick using WaitForSingleObject timout */
|
||||
while ( WaitForSingleObject( stream->processingCompleted, msecPerWakeup ) == 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.
|
||||
|
@ -2480,6 +2603,15 @@ static PaError CloseStream( PaStream* 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 )
|
||||
{
|
||||
|
@ -2574,6 +2706,10 @@ static PaError StartStream( PaStream *s )
|
|||
|
||||
ResetEvent( stream->processingCompleted );
|
||||
|
||||
#ifndef PA_WIN_DS_USE_WMME_TIMER
|
||||
ResetEvent( stream->processingThreadCompleted );
|
||||
#endif
|
||||
|
||||
if( stream->bufferProcessor.inputChannelCount > 0 )
|
||||
{
|
||||
// Start the buffer capture
|
||||
|
@ -2596,7 +2732,6 @@ static PaError StartStream( PaStream *s )
|
|||
|
||||
stream->abortProcessing = 0;
|
||||
stream->stopProcessing = 0;
|
||||
stream->isActive = 1;
|
||||
|
||||
if( stream->bufferProcessor.outputChannelCount > 0 )
|
||||
{
|
||||
|
@ -2639,28 +2774,91 @@ static PaError StartStream( PaStream *s )
|
|||
|
||||
if( stream->streamRepresentation.streamCallback )
|
||||
{
|
||||
/* Create timer that will wake us up so we can fill the DSound buffer. */
|
||||
int resolution;
|
||||
TIMECAPS timecaps;
|
||||
|
||||
int timerResolution;
|
||||
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) TimerCallback,
|
||||
timerResolution = msecPerWakeup/4;
|
||||
|
||||
/* 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 ) )
|
||||
{
|
||||
stream->systemTimerResolutionPeriodMs = timerResolution;
|
||||
if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin )
|
||||
stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin;
|
||||
|
||||
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( msecPerWakeup, timerResolution, (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 );
|
||||
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;
|
||||
}
|
||||
|
||||
stream->isStarted = TRUE;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2683,12 +2881,30 @@ static PaError StopStream( PaStream *s )
|
|||
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 )
|
||||
{
|
||||
|
@ -2714,7 +2930,7 @@ static PaError StopStream( PaStream *s )
|
|||
}
|
||||
}
|
||||
|
||||
stream->isStarted = FALSE;
|
||||
stream->isStarted = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_jack.c 1541 2010-09-22 06:33:47Z dmitrykos $
|
||||
* $Id: pa_jack.c 1668 2011-05-02 17:07:11Z rossb $
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
* JACK Implementation by Joshua Haberman
|
||||
|
@ -1296,11 +1296,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
if( stream->num_incoming_connections > 0 )
|
||||
stream->streamRepresentation.streamInfo.inputLatency = (jack_port_get_latency( stream->remote_output_ports[0] )
|
||||
- jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */
|
||||
+ PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor )) / sampleRate;
|
||||
+ PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor )) / sampleRate;
|
||||
if( stream->num_outgoing_connections > 0 )
|
||||
stream->streamRepresentation.streamInfo.outputLatency = (jack_port_get_latency( stream->remote_input_ports[0] )
|
||||
- jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */
|
||||
+ PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor )) / sampleRate;
|
||||
+ PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor )) / sampleRate;
|
||||
|
||||
stream->streamRepresentation.streamInfo.sampleRate = jackSr;
|
||||
stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_unix_oss.c 1602 2011-02-12 09:26:30Z rossb $
|
||||
* $Id: pa_unix_oss.c 1668 2011-05-02 17:07:11Z rossb $
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
* OSS implementation by:
|
||||
|
@ -1241,13 +1241,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
{
|
||||
inputHostFormat = stream->capture->hostFormat;
|
||||
stream->streamRepresentation.streamInfo.inputLatency = inLatency +
|
||||
PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
|
||||
PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) / sampleRate;
|
||||
}
|
||||
if( outputParameters )
|
||||
{
|
||||
outputHostFormat = stream->playback->hostFormat;
|
||||
stream->streamRepresentation.streamInfo.outputLatency = outLatency +
|
||||
PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
|
||||
PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) / sampleRate;
|
||||
}
|
||||
|
||||
/* Initialize buffer processor with fixed host buffer size.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_hostapi_skeleton.c 1603 2011-02-12 10:22:00Z rossb $
|
||||
* $Id: pa_hostapi_skeleton.c 1668 2011-05-02 17:07:11Z rossb $
|
||||
* Portable Audio I/O Library skeleton implementation
|
||||
* demonstrates how to use the common functions to implement support
|
||||
* for a host API
|
||||
|
@ -518,9 +518,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
values.
|
||||
*/
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
(PaTime)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate; /* inputLatency is specified in _seconds_ */
|
||||
(PaTime)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* inputLatency is specified in _seconds_ */
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
(PaTime)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate; /* outputLatency is specified in _seconds_ */
|
||||
(PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* outputLatency is specified in _seconds_ */
|
||||
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
|
||||
|
||||
|
||||
|
|
|
@ -75,6 +75,8 @@
|
|||
#include "pa_debugprint.h"
|
||||
#include "pa_ringbuffer.h"
|
||||
|
||||
#include "pa_win_coinitialize.h"
|
||||
|
||||
#ifndef NTDDI_VERSION
|
||||
|
||||
#undef WINVER
|
||||
|
@ -370,6 +372,8 @@ typedef struct
|
|||
|
||||
/* implementation specific data goes here */
|
||||
|
||||
PaWinUtilComInitializationResult comInitializationResult;
|
||||
|
||||
//in case we later need the synch
|
||||
IMMDeviceEnumerator *enumerator;
|
||||
|
||||
|
@ -512,8 +516,6 @@ void *PaWasapi_ReallocateMemory(void *ptr, size_t size);
|
|||
void PaWasapi_FreeMemory(void *ptr);
|
||||
|
||||
// Local statics
|
||||
static volatile BOOL g_WasapiCOMInit = FALSE;
|
||||
static volatile DWORD g_WasapiInitThread = 0;
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
#define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)
|
||||
|
@ -1057,26 +1059,6 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
return paNoError;
|
||||
}
|
||||
|
||||
/*
|
||||
If COM is already initialized CoInitialize will either return
|
||||
FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
|
||||
threading mode. In either case we shouldn't consider it an error
|
||||
but we need to be careful to not call CoUninitialize() if
|
||||
RPC_E_CHANGED_MODE was returned.
|
||||
*/
|
||||
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||
if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
|
||||
{
|
||||
PRINT(("WASAPI: failed CoInitialize"));
|
||||
return paUnanticipatedHostError;
|
||||
}
|
||||
if (hr != RPC_E_CHANGED_MODE)
|
||||
g_WasapiCOMInit = TRUE;
|
||||
|
||||
// Memorize calling thread id and report warning on Uninitialize if calling thread is different
|
||||
// as CoInitialize must match CoUninitialize in the same thread.
|
||||
g_WasapiInitThread = GetCurrentThreadId();
|
||||
|
||||
paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );
|
||||
if (paWasapi == NULL)
|
||||
{
|
||||
|
@ -1084,6 +1066,12 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
goto error;
|
||||
}
|
||||
|
||||
result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult );
|
||||
if( result != paNoError )
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
paWasapi->allocations = PaUtil_CreateAllocationGroup();
|
||||
if (paWasapi->allocations == NULL)
|
||||
{
|
||||
|
@ -1454,26 +1442,12 @@ static void Terminate( PaUtilHostApiRepresentation *hostApi )
|
|||
PaUtil_DestroyAllocationGroup(paWasapi->allocations);
|
||||
}
|
||||
|
||||
PaWinUtil_CoUninitialize( paWASAPI, &paWasapi->comInitializationResult );
|
||||
|
||||
PaUtil_FreeMemory(paWasapi);
|
||||
|
||||
// Close AVRT
|
||||
CloseAVRT();
|
||||
|
||||
// Uninit COM (checking calling thread we won't unitialize user's COM if one is calling
|
||||
// Pa_Unitialize by mistake from not initializing thread)
|
||||
if (g_WasapiCOMInit)
|
||||
{
|
||||
DWORD calling_thread_id = GetCurrentThreadId();
|
||||
if (g_WasapiInitThread != calling_thread_id)
|
||||
{
|
||||
PRINT(("WASAPI: failed CoUninitializes calling thread[%d] does not match initializing thread[%d]\n",
|
||||
calling_thread_id, g_WasapiInitThread));
|
||||
}
|
||||
else
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
@ -3004,12 +2978,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
// Set Input latency
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
((double)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate)
|
||||
((double)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate)
|
||||
+ ((inputParameters)?stream->in.latencySeconds : 0);
|
||||
|
||||
// Set Output latency
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
((double)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate)
|
||||
((double)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate)
|
||||
+ ((outputParameters)?stream->out.latencySeconds : 0);
|
||||
|
||||
// Set SR
|
||||
|
|
|
@ -125,13 +125,13 @@
|
|||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define NOMMIDS
|
||||
//#define NOMMIDS
|
||||
#define DYNAMIC_GUID(data) {data}
|
||||
#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
|
||||
#undef DEFINE_GUID
|
||||
#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
|
||||
#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
|
||||
#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
|
||||
//#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
|
||||
//#undef DEFINE_GUID
|
||||
//#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
|
||||
//#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
|
||||
//#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
|
||||
#endif
|
||||
|
||||
#include <mmreg.h>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_win_wmme.c 1584 2011-02-02 18:58:17Z rossb $
|
||||
* $Id: pa_win_wmme.c 1717 2011-07-28 12:03:09Z rossb $
|
||||
* pa_win_wmme.c
|
||||
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
|
||||
*
|
||||
|
@ -158,15 +158,15 @@
|
|||
#define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
|
||||
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
|
||||
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
|
||||
#define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
|
||||
#define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_ (16)
|
||||
#define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
|
||||
#define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
|
||||
#else
|
||||
#define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
|
||||
#define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
|
||||
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3)
|
||||
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3) /* always use at least 3 input buffers for full duplex */
|
||||
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
|
||||
#define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
|
||||
#define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_ (16)
|
||||
#define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
|
||||
#define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
|
||||
#endif
|
||||
|
@ -177,6 +177,13 @@
|
|||
#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
|
||||
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
|
||||
|
||||
/* When client suggestedLatency could result in many host buffers, we aim to have around 8,
|
||||
based off Windows documentation that suggests that the kmixer uses 8 buffers. This choice
|
||||
is somewhat arbitrary here, since we havn't observed significant stability degredation
|
||||
with using either more, or less buffers.
|
||||
*/
|
||||
|
||||
#define PA_MME_TARGET_HOST_BUFFER_COUNT_ 8
|
||||
|
||||
#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
|
||||
|
||||
|
@ -791,7 +798,9 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
|
|||
MMRESULT mmresult;
|
||||
WAVEOUTCAPS woc;
|
||||
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
|
||||
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
|
||||
int wdmksDeviceOutputChannelCountIsKnown;
|
||||
#endif
|
||||
|
||||
*success = 0;
|
||||
|
||||
|
@ -1324,126 +1333,135 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
|
|||
}
|
||||
|
||||
|
||||
|
||||
static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
|
||||
unsigned long requestedLatency,
|
||||
unsigned long baseBufferCount, unsigned long minimumBufferCount,
|
||||
unsigned long maximumBufferSize, unsigned long *hostBufferSize,
|
||||
unsigned long *hostBufferCount )
|
||||
static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
unsigned long suggestedLatencyFrames,
|
||||
unsigned long hostBufferSizeFrames,
|
||||
unsigned long minimumBufferCount )
|
||||
{
|
||||
unsigned long sizeMultiplier, bufferCount, latency;
|
||||
unsigned long nextLatency, nextBufferSize;
|
||||
int baseBufferSizeIsPowerOfTwo;
|
||||
/* Calculate the number of buffers of length hostFramesPerBuffer
|
||||
that fit in suggestedLatencyFrames, rounding up to the next integer.
|
||||
|
||||
sizeMultiplier = 1;
|
||||
bufferCount = baseBufferCount;
|
||||
|
||||
/* count-1 below because latency is always determined by one less
|
||||
than the total number of buffers.
|
||||
The exact formula for round-up is:
|
||||
((suggestedLatencyFrames + (hostFramesPerBuffer - 1)) / hostFramesPerBuffer)
|
||||
However we subtract 2 instead of 1 below to account for rounding errors.
|
||||
This ensures we don't erroneously overallocate an extra buffer due to a one-sample rounding error.
|
||||
*/
|
||||
latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
|
||||
|
||||
if( latency > requestedLatency )
|
||||
{
|
||||
unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 2)) / hostBufferSizeFrames);
|
||||
|
||||
/* reduce number of buffers without falling below suggested latency */
|
||||
/* We always need one extra buffer for processing while the rest are queued/playing.
|
||||
i.e. latency is framesPerBuffer * (bufferCount - 1)
|
||||
*/
|
||||
resultBufferCount += 1;
|
||||
|
||||
nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
|
||||
while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
|
||||
{
|
||||
--bufferCount;
|
||||
nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
|
||||
}
|
||||
if( resultBufferCount < minimumBufferCount ) /* clamp to minimum buffer count */
|
||||
resultBufferCount = minimumBufferCount;
|
||||
|
||||
}else if( latency < requestedLatency ){
|
||||
|
||||
baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
|
||||
if( baseBufferSizeIsPowerOfTwo ){
|
||||
|
||||
/* double size of buffers without exceeding requestedLatency */
|
||||
|
||||
nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
|
||||
nextLatency = nextBufferSize * (bufferCount-1);
|
||||
while( nextBufferSize <= maximumBufferSize
|
||||
&& nextLatency < requestedLatency )
|
||||
{
|
||||
sizeMultiplier *= 2;
|
||||
nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
|
||||
nextLatency = nextBufferSize * (bufferCount-1);
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
/* increase size of buffers upto first excess of requestedLatency */
|
||||
|
||||
nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
|
||||
nextLatency = nextBufferSize * (bufferCount-1);
|
||||
while( nextBufferSize <= maximumBufferSize
|
||||
&& nextLatency < requestedLatency )
|
||||
{
|
||||
++sizeMultiplier;
|
||||
nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
|
||||
nextLatency = nextBufferSize * (bufferCount-1);
|
||||
}
|
||||
|
||||
if( nextLatency < requestedLatency )
|
||||
++sizeMultiplier;
|
||||
}
|
||||
|
||||
/* increase number of buffers until requestedLatency is reached */
|
||||
|
||||
latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
|
||||
while( latency < requestedLatency )
|
||||
{
|
||||
++bufferCount;
|
||||
latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
|
||||
}
|
||||
}
|
||||
|
||||
*hostBufferSize = baseBufferSize * sizeMultiplier;
|
||||
*hostBufferCount = bufferCount;
|
||||
return resultBufferCount;
|
||||
}
|
||||
|
||||
|
||||
static void ReselectBufferCount( unsigned long bufferSize,
|
||||
unsigned long requestedLatency,
|
||||
unsigned long baseBufferCount, unsigned long minimumBufferCount,
|
||||
static PaError SelectHostBufferSizeFramesAndHostBufferCount(
|
||||
unsigned long suggestedLatencyFrames,
|
||||
unsigned long userFramesPerBuffer,
|
||||
unsigned long minimumBufferCount,
|
||||
unsigned long preferredMaximumBufferSizeFrames, /* try not to exceed this. for example, don't exceed when coalescing buffers */
|
||||
unsigned long absoluteMaximumBufferSizeFrames, /* never exceed this, a hard limit */
|
||||
unsigned long *hostBufferSizeFrames,
|
||||
unsigned long *hostBufferCount )
|
||||
{
|
||||
unsigned long bufferCount, latency;
|
||||
unsigned long nextLatency;
|
||||
if( userFramesPerBuffer == paFramesPerBufferUnspecified ){
|
||||
|
||||
bufferCount = baseBufferCount;
|
||||
*hostBufferSizeFrames = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_;
|
||||
|
||||
/* count-1 below because latency is always determined by one less
|
||||
than the total number of buffers.
|
||||
}else{
|
||||
|
||||
*hostBufferSizeFrames = userFramesPerBuffer;
|
||||
|
||||
if( *hostBufferSizeFrames > absoluteMaximumBufferSizeFrames ){
|
||||
/* user has requested a user buffer that's larger than what we can handle */
|
||||
|
||||
/* @todo FIXME right now we allow the user to request an oversize host buffer,
|
||||
even though elsewhere in the code there are suggestions that oversize buffers
|
||||
can cause crashes with some drivers. */
|
||||
/* return paBufferTooBig; */
|
||||
}
|
||||
}
|
||||
|
||||
/* compute a host buffer count based on suggestedLatencyFrames and our granularity */
|
||||
|
||||
*hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount );
|
||||
|
||||
/* consider coalescing multiple user buffers into each host buffer */
|
||||
|
||||
if( *hostBufferCount >= PA_MME_TARGET_HOST_BUFFER_COUNT_ * 2 ){
|
||||
|
||||
/* If there are too many host buffers we would like to coalesce
|
||||
them by packing an integer number of user buffers into each host buffer.
|
||||
We try to coalesce such that hostBufferCount will lie between
|
||||
PA_MME_TARGET_HOST_BUFFER_COUNT_ and (PA_MME_TARGET_HOST_BUFFER_COUNT_*2)-1.
|
||||
We limit coalescing to avoid exceeding either absoluteMaximumBufferSizeFrames and
|
||||
preferredMaximumBufferSizeFrames.
|
||||
*/
|
||||
latency = bufferSize * (bufferCount-1);
|
||||
|
||||
if( latency > requestedLatency )
|
||||
{
|
||||
/* reduce number of buffers without falling below suggested latency */
|
||||
unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* min of our limits */
|
||||
? absoluteMaximumBufferSizeFrames
|
||||
: preferredMaximumBufferSizeFrames;
|
||||
|
||||
nextLatency = bufferSize * (bufferCount-2);
|
||||
while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
|
||||
{
|
||||
--bufferCount;
|
||||
nextLatency = bufferSize * (bufferCount-2);
|
||||
}
|
||||
unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / *hostBufferSizeFrames; /* don't coalesce more than this */
|
||||
|
||||
}else if( latency < requestedLatency ){
|
||||
/* we truncate here to compute a conservative value of numUserBuffersPerHostBuffer
|
||||
then we recompute hostBufferCount after coalescing.
|
||||
*/
|
||||
unsigned long numUserBuffersPerHostBuffer = *hostBufferCount / PA_MME_TARGET_HOST_BUFFER_COUNT_;
|
||||
if( numUserBuffersPerHostBuffer > maxUserBuffersPerHostBuffer )
|
||||
numUserBuffersPerHostBuffer = maxUserBuffersPerHostBuffer;
|
||||
|
||||
/* increase number of buffers until requestedLatency is reached */
|
||||
if( numUserBuffersPerHostBuffer > 1 ){
|
||||
*hostBufferSizeFrames *= numUserBuffersPerHostBuffer;
|
||||
|
||||
latency = bufferSize * (bufferCount-1);
|
||||
while( latency < requestedLatency )
|
||||
{
|
||||
++bufferCount;
|
||||
latency = bufferSize * (bufferCount-1);
|
||||
/* recompute hostBufferCount to approximate suggestedLatencyFrames now that hostBufferSizeFrames is larger */
|
||||
*hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount );
|
||||
}
|
||||
}
|
||||
|
||||
*hostBufferCount = bufferCount;
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
static PaError CalculateMaxHostSampleFrameSizeBytes(
|
||||
int channelCount,
|
||||
PaSampleFormat hostSampleFormat,
|
||||
const PaWinMmeStreamInfo *streamInfo,
|
||||
int *hostSampleFrameSizeBytes )
|
||||
{
|
||||
unsigned int i;
|
||||
/* PA WMME streams may aggregate multiple WMME devices. When the stream addresses
|
||||
more than one device in a single direction, maxDeviceChannelCount is the maximum
|
||||
number of channels used by a single device.
|
||||
*/
|
||||
int maxDeviceChannelCount = channelCount;
|
||||
int hostSampleSizeBytes = Pa_GetSampleSize( hostSampleFormat );
|
||||
if( hostSampleSizeBytes < 0 )
|
||||
{
|
||||
return hostSampleSizeBytes; /* the value of hostSampleSize here is an error code, not a sample size */
|
||||
}
|
||||
|
||||
if( streamInfo && ( streamInfo->flags & paWinMmeUseMultipleDevices ) )
|
||||
{
|
||||
maxDeviceChannelCount = streamInfo->devices[0].channelCount;
|
||||
for( i=1; i< streamInfo->deviceCount; ++i )
|
||||
{
|
||||
if( streamInfo->devices[i].channelCount > maxDeviceChannelCount )
|
||||
maxDeviceChannelCount = streamInfo->devices[i].channelCount;
|
||||
}
|
||||
}
|
||||
|
||||
*hostSampleFrameSizeBytes = hostSampleSizeBytes * maxDeviceChannelCount;
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1453,51 +1471,29 @@ static void ReselectBufferCount( unsigned long bufferSize,
|
|||
*/
|
||||
|
||||
static PaError CalculateBufferSettings(
|
||||
unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
|
||||
unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
|
||||
unsigned long *hostFramesPerInputBuffer, unsigned long *hostInputBufferCount,
|
||||
unsigned long *hostFramesPerOutputBuffer, unsigned long *hostOutputBufferCount,
|
||||
int inputChannelCount, PaSampleFormat hostInputSampleFormat,
|
||||
PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
|
||||
PaTime suggestedInputLatency, const PaWinMmeStreamInfo *inputStreamInfo,
|
||||
int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
|
||||
PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
|
||||
double sampleRate, unsigned long framesPerBuffer )
|
||||
PaTime suggestedOutputLatency, const PaWinMmeStreamInfo *outputStreamInfo,
|
||||
double sampleRate, unsigned long userFramesPerBuffer )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
int effectiveInputChannelCount, effectiveOutputChannelCount;
|
||||
int hostInputFrameSize = 0;
|
||||
unsigned int i;
|
||||
|
||||
if( inputChannelCount > 0 )
|
||||
if( inputChannelCount > 0 ) /* stream has input */
|
||||
{
|
||||
int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
|
||||
if( hostInputSampleSize < 0 )
|
||||
{
|
||||
result = hostInputSampleSize;
|
||||
int hostInputFrameSizeBytes;
|
||||
result = CalculateMaxHostSampleFrameSizeBytes(
|
||||
inputChannelCount, hostInputSampleFormat, inputStreamInfo, &hostInputFrameSizeBytes );
|
||||
if( result != paNoError )
|
||||
goto error;
|
||||
}
|
||||
|
||||
if( inputStreamInfo
|
||||
&& ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
|
||||
{
|
||||
/* set effectiveInputChannelCount to the largest number of
|
||||
channels on any one device.
|
||||
*/
|
||||
effectiveInputChannelCount = 0;
|
||||
for( i=0; i< inputStreamInfo->deviceCount; ++i )
|
||||
{
|
||||
if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
|
||||
effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
effectiveInputChannelCount = inputChannelCount;
|
||||
}
|
||||
|
||||
hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
|
||||
|
||||
if( inputStreamInfo
|
||||
&& ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
|
||||
{
|
||||
/* input - using low level latency parameters if provided */
|
||||
|
||||
if( inputStreamInfo->bufferCount <= 0
|
||||
|| inputStreamInfo->framesPerBuffer <= 0 )
|
||||
{
|
||||
|
@ -1505,46 +1501,45 @@ static PaError CalculateBufferSettings(
|
|||
goto error;
|
||||
}
|
||||
|
||||
*framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
|
||||
*hostFramesPerInputBuffer = inputStreamInfo->framesPerBuffer;
|
||||
*hostInputBufferCount = inputStreamInfo->bufferCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long hostBufferSizeBytes, hostBufferCount;
|
||||
/* input - not using low level latency parameters, so compute
|
||||
hostFramesPerInputBuffer and hostInputBufferCount
|
||||
based on userFramesPerBuffer and suggestedInputLatency. */
|
||||
|
||||
unsigned long minimumBufferCount = (outputChannelCount > 0)
|
||||
? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
|
||||
: PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
|
||||
|
||||
unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
|
||||
if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
|
||||
maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
|
||||
|
||||
/* compute the following in bytes, then convert back to frames */
|
||||
|
||||
SelectBufferSizeAndCount(
|
||||
((framesPerBuffer == paFramesPerBufferUnspecified)
|
||||
? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
|
||||
: framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
|
||||
((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
|
||||
4, /* baseBufferCount */
|
||||
minimumBufferCount, maximumBufferSize,
|
||||
&hostBufferSizeBytes, &hostBufferCount );
|
||||
|
||||
*framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
|
||||
*hostInputBufferCount = hostBufferCount;
|
||||
result = SelectHostBufferSizeFramesAndHostBufferCount(
|
||||
(unsigned long)(suggestedInputLatency * sampleRate),
|
||||
userFramesPerBuffer,
|
||||
minimumBufferCount,
|
||||
(unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
|
||||
(PA_MME_MAX_HOST_BUFFER_BYTES_ / hostInputFrameSizeBytes), /* in frames. a hard limit. note truncation due to
|
||||
division is intentional here to limit max bytes */
|
||||
hostFramesPerInputBuffer,
|
||||
hostInputBufferCount );
|
||||
if( result != paNoError )
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*framesPerHostInputBuffer = 0;
|
||||
*hostFramesPerInputBuffer = 0;
|
||||
*hostInputBufferCount = 0;
|
||||
}
|
||||
|
||||
if( outputChannelCount > 0 )
|
||||
if( outputChannelCount > 0 ) /* stream has output */
|
||||
{
|
||||
if( outputStreamInfo
|
||||
&& ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
|
||||
{
|
||||
/* output - using low level latency parameters */
|
||||
|
||||
if( outputStreamInfo->bufferCount <= 0
|
||||
|| outputStreamInfo->framesPerBuffer <= 0 )
|
||||
{
|
||||
|
@ -1552,24 +1547,25 @@ static PaError CalculateBufferSettings(
|
|||
goto error;
|
||||
}
|
||||
|
||||
*framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
|
||||
*hostFramesPerOutputBuffer = outputStreamInfo->framesPerBuffer;
|
||||
*hostOutputBufferCount = outputStreamInfo->bufferCount;
|
||||
|
||||
|
||||
if( inputChannelCount > 0 ) /* full duplex */
|
||||
{
|
||||
if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
|
||||
/* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */
|
||||
|
||||
if( *hostFramesPerInputBuffer != *hostFramesPerOutputBuffer )
|
||||
{
|
||||
if( inputStreamInfo
|
||||
&& ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
|
||||
{
|
||||
/* a custom StreamInfo was used for specifying both input
|
||||
and output buffer sizes, the larger buffer size
|
||||
and output buffer sizes. We require that the larger buffer size
|
||||
must be a multiple of the smaller buffer size */
|
||||
|
||||
if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
|
||||
if( *hostFramesPerInputBuffer < *hostFramesPerOutputBuffer )
|
||||
{
|
||||
if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
|
||||
if( *hostFramesPerOutputBuffer % *hostFramesPerInputBuffer != 0 )
|
||||
{
|
||||
result = paIncompatibleHostApiSpecificStreamInfo;
|
||||
goto error;
|
||||
|
@ -1577,8 +1573,8 @@ static PaError CalculateBufferSettings(
|
|||
}
|
||||
else
|
||||
{
|
||||
assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
|
||||
if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
|
||||
assert( *hostFramesPerInputBuffer > *hostFramesPerOutputBuffer );
|
||||
if( *hostFramesPerInputBuffer % *hostFramesPerOutputBuffer != 0 )
|
||||
{
|
||||
result = paIncompatibleHostApiSpecificStreamInfo;
|
||||
goto error;
|
||||
|
@ -1588,111 +1584,72 @@ static PaError CalculateBufferSettings(
|
|||
else
|
||||
{
|
||||
/* a custom StreamInfo was not used for specifying the input buffer size,
|
||||
so use the output buffer size, and approximately the same latency. */
|
||||
so use the output buffer size, and approximately the suggested input latency. */
|
||||
|
||||
*framesPerHostInputBuffer = *framesPerHostOutputBuffer;
|
||||
*hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
|
||||
*hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
|
||||
|
||||
if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
|
||||
*hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
|
||||
*hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
(unsigned long)(suggestedInputLatency * sampleRate),
|
||||
*hostFramesPerInputBuffer,
|
||||
PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long hostBufferSizeBytes, hostBufferCount;
|
||||
unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
|
||||
unsigned long maximumBufferSize;
|
||||
int hostOutputFrameSize;
|
||||
int hostOutputSampleSize;
|
||||
/* output - no low level latency parameters, so compute hostFramesPerOutputBuffer and hostOutputBufferCount
|
||||
based on userFramesPerBuffer and suggestedOutputLatency. */
|
||||
|
||||
hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
|
||||
if( hostOutputSampleSize < 0 )
|
||||
{
|
||||
result = hostOutputSampleSize;
|
||||
int hostOutputFrameSizeBytes;
|
||||
result = CalculateMaxHostSampleFrameSizeBytes(
|
||||
outputChannelCount, hostOutputSampleFormat, outputStreamInfo, &hostOutputFrameSizeBytes );
|
||||
if( result != paNoError )
|
||||
goto error;
|
||||
}
|
||||
|
||||
if( outputStreamInfo
|
||||
&& ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
|
||||
/* compute the output buffer size and count */
|
||||
|
||||
result = SelectHostBufferSizeFramesAndHostBufferCount(
|
||||
(unsigned long)(suggestedOutputLatency * sampleRate),
|
||||
userFramesPerBuffer,
|
||||
PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_,
|
||||
(unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
|
||||
(PA_MME_MAX_HOST_BUFFER_BYTES_ / hostOutputFrameSizeBytes), /* in frames. a hard limit. note truncation due to
|
||||
division is intentional here to limit max bytes */
|
||||
hostFramesPerOutputBuffer,
|
||||
hostOutputBufferCount );
|
||||
if( result != paNoError )
|
||||
goto error;
|
||||
|
||||
if( inputChannelCount > 0 ) /* full duplex */
|
||||
{
|
||||
/* set effectiveOutputChannelCount to the largest number of
|
||||
channels on any one device.
|
||||
*/
|
||||
effectiveOutputChannelCount = 0;
|
||||
for( i=0; i< outputStreamInfo->deviceCount; ++i )
|
||||
{
|
||||
if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
|
||||
effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
effectiveOutputChannelCount = outputChannelCount;
|
||||
}
|
||||
/* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */
|
||||
|
||||
hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
|
||||
|
||||
maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
|
||||
if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
|
||||
maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
|
||||
|
||||
|
||||
/* compute the following in bytes, then convert back to frames */
|
||||
|
||||
SelectBufferSizeAndCount(
|
||||
((framesPerBuffer == paFramesPerBufferUnspecified)
|
||||
? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
|
||||
: framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
|
||||
((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
|
||||
4, /* baseBufferCount */
|
||||
minimumBufferCount,
|
||||
maximumBufferSize,
|
||||
&hostBufferSizeBytes, &hostBufferCount );
|
||||
|
||||
*framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
|
||||
*hostOutputBufferCount = hostBufferCount;
|
||||
|
||||
|
||||
if( inputChannelCount > 0 )
|
||||
{
|
||||
/* ensure that both input and output buffer sizes are the same.
|
||||
if they don't match at this stage, choose the smallest one
|
||||
and use that for input and output
|
||||
and use that for input and output and recompute the corresponding
|
||||
buffer count accordingly.
|
||||
*/
|
||||
|
||||
if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
|
||||
if( *hostFramesPerOutputBuffer != *hostFramesPerInputBuffer )
|
||||
{
|
||||
if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
|
||||
if( hostFramesPerInputBuffer < hostFramesPerOutputBuffer )
|
||||
{
|
||||
unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
|
||||
*hostFramesPerOutputBuffer = *hostFramesPerInputBuffer;
|
||||
|
||||
minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
|
||||
ReselectBufferCount(
|
||||
framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
|
||||
((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
|
||||
4, /* baseBufferCount */
|
||||
minimumBufferCount,
|
||||
&hostBufferCount );
|
||||
|
||||
*framesPerHostOutputBuffer = framesPerHostBuffer;
|
||||
*hostOutputBufferCount = hostBufferCount;
|
||||
*hostOutputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
(unsigned long)(suggestedOutputLatency * sampleRate),
|
||||
*hostOutputBufferCount,
|
||||
PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ );
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
|
||||
*hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
|
||||
|
||||
minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
|
||||
ReselectBufferCount(
|
||||
framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
|
||||
((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
|
||||
4, /* baseBufferCount */
|
||||
minimumBufferCount,
|
||||
&hostBufferCount );
|
||||
|
||||
*framesPerHostInputBuffer = framesPerHostBuffer;
|
||||
*hostInputBufferCount = hostBufferCount;
|
||||
*hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
(unsigned long)(suggestedInputLatency * sampleRate),
|
||||
*hostFramesPerInputBuffer,
|
||||
PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1700,7 +1657,7 @@ static PaError CalculateBufferSettings(
|
|||
}
|
||||
else
|
||||
{
|
||||
*framesPerHostOutputBuffer = 0;
|
||||
*hostFramesPerOutputBuffer = 0;
|
||||
*hostOutputBufferCount = 0;
|
||||
}
|
||||
|
||||
|
@ -2476,12 +2433,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
bufferProcessorIsInitialized = 1;
|
||||
|
||||
/* stream info input latency is the minimum buffering latency (unlike suggested and default which are *maximums*) */
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
(double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
|
||||
+(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
|
||||
(double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
|
||||
+ framesPerHostInputBuffer) / sampleRate;
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
(double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
|
||||
+(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
|
||||
(double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
|
||||
+ (framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
|
||||
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
|
||||
|
||||
stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Microsoft COM initialization routines
|
||||
* Copyright (c) 1999-2011 Ross Bencina, Dmitry Kostjuchenko
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2011 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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 win_src
|
||||
|
||||
@brief Microsoft COM initialization routines.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <objbase.h>
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_util.h"
|
||||
#include "pa_debugprint.h"
|
||||
|
||||
#include "pa_win_coinitialize.h"
|
||||
|
||||
|
||||
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) && !defined(_WIN32_WCE) /* MSC version 6 and above */
|
||||
#pragma comment( lib, "ole32.lib" )
|
||||
#endif
|
||||
|
||||
|
||||
/* use some special bit patterns here to try to guard against uninitialized memory errors */
|
||||
#define PAWINUTIL_COM_INITIALIZED (0xb38f)
|
||||
#define PAWINUTIL_COM_NOT_INITIALIZED (0xf1cd)
|
||||
|
||||
|
||||
PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult )
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED;
|
||||
|
||||
/*
|
||||
If COM is already initialized CoInitialize will either return
|
||||
FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
|
||||
threading mode. In either case we shouldn't consider it an error
|
||||
but we need to be careful to not call CoUninitialize() if
|
||||
RPC_E_CHANGED_MODE was returned.
|
||||
*/
|
||||
|
||||
hr = CoInitialize(0); /* use legacy-safe equivalent to CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) */
|
||||
if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
|
||||
{
|
||||
PA_DEBUG(("CoInitialize(0) failed. hr=%d\n", hr));
|
||||
|
||||
if( hr == E_OUTOFMEMORY )
|
||||
return paInsufficientMemory;
|
||||
|
||||
{
|
||||
char *lpMsgBuf;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,
|
||||
hr,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
PaUtil_SetLastHostErrorInfo( hostApiType, hr, lpMsgBuf );
|
||||
LocalFree( lpMsgBuf );
|
||||
}
|
||||
|
||||
return paUnanticipatedHostError;
|
||||
}
|
||||
|
||||
if( hr != RPC_E_CHANGED_MODE )
|
||||
{
|
||||
comInitializationResult->state = PAWINUTIL_COM_INITIALIZED;
|
||||
|
||||
/*
|
||||
Memorize calling thread id and report warning on Uninitialize if
|
||||
calling thread is different as CoInitialize must match CoUninitialize
|
||||
in the same thread.
|
||||
*/
|
||||
comInitializationResult->initializingThreadId = GetCurrentThreadId();
|
||||
}
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult )
|
||||
{
|
||||
if( comInitializationResult->state != PAWINUTIL_COM_NOT_INITIALIZED
|
||||
&& comInitializationResult->state != PAWINUTIL_COM_INITIALIZED ){
|
||||
|
||||
PA_DEBUG(("ERROR: PaWinUtil_CoUninitialize called without calling PaWinUtil_CoInitialize\n"));
|
||||
}
|
||||
|
||||
if( comInitializationResult->state == PAWINUTIL_COM_INITIALIZED )
|
||||
{
|
||||
DWORD currentThreadId = GetCurrentThreadId();
|
||||
if( comInitializationResult->initializingThreadId != currentThreadId )
|
||||
{
|
||||
PA_DEBUG(("ERROR: failed PaWinUtil_CoUninitialize calling thread[%d] does not match initializing thread[%d]\n",
|
||||
currentThreadId, comInitializationResult->initializingThreadId));
|
||||
}
|
||||
else
|
||||
{
|
||||
CoUninitialize();
|
||||
|
||||
comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Microsoft COM initialization routines
|
||||
* Copyright (c) 1999-2011 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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 win_src
|
||||
|
||||
@brief Microsoft COM initialization routines.
|
||||
*/
|
||||
|
||||
#ifndef PA_WIN_COINITIALIZE_H
|
||||
#define PA_WIN_COINITIALIZE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
/**
|
||||
@brief Data type used to hold the result of an attempt to initialize COM
|
||||
using PaWinUtil_CoInitialize. Must be retained between a call to
|
||||
PaWinUtil_CoInitialize and a matching call to PaWinUtil_CoUninitialize.
|
||||
*/
|
||||
typedef struct PaWinUtilComInitializationResult{
|
||||
int state;
|
||||
int initializingThreadId;
|
||||
} PaWinUtilComInitializationResult;
|
||||
|
||||
|
||||
/**
|
||||
@brief Initialize Microsoft COM subsystem on the current thread.
|
||||
|
||||
@param hostApiType the host API type id of the caller. Used for error reporting.
|
||||
|
||||
@param comInitializationResult An output parameter. The value pointed to by
|
||||
this parameter stores information required by PaWinUtil_CoUninitialize
|
||||
to correctly uninitialize COM. The value should be retained and later
|
||||
passed to PaWinUtil_CoUninitialize.
|
||||
|
||||
If PaWinUtil_CoInitialize returns paNoError, the caller must later call
|
||||
PaWinUtil_CoUninitialize once.
|
||||
*/
|
||||
PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult );
|
||||
|
||||
|
||||
/**
|
||||
@brief Uninitialize the Microsoft COM subsystem on the current thread using
|
||||
the result of a previous call to PaWinUtil_CoInitialize. Must be called on the same
|
||||
thread as PaWinUtil_CoInitialize.
|
||||
|
||||
@param hostApiType the host API type id of the caller. Used for error reporting.
|
||||
|
||||
@param comInitializationResult An input parameter. A pointer to a value previously
|
||||
initialized by a call to PaWinUtil_CoInitialize.
|
||||
*/
|
||||
void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PA_WIN_COINITIALIZE_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_win_hostapis.c 1635 2011-03-04 04:18:44Z rossb $
|
||||
* $Id: pa_win_hostapis.c 1687 2011-05-19 08:24:02Z rob_bielik $
|
||||
* Portable Audio I/O Library Windows initialization table
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
|
@ -42,8 +42,16 @@
|
|||
@brief Win32 host API initialization function table.
|
||||
*/
|
||||
|
||||
/* This is needed to make this source file depend on CMake option changes
|
||||
and at the same time make it transparent for clients not using CMake.
|
||||
*/
|
||||
#ifdef PORTAUDIO_CMAKE_GENERATED
|
||||
#include "options_cmake.h"
|
||||
#endif
|
||||
|
||||
#include "pa_hostapi.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue