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:
gigaherz 2011-08-07 12:40:25 +00:00
parent ddcfdb6d77
commit cb74fd2c10
29 changed files with 6814 additions and 4148 deletions

View File

@ -35,7 +35,7 @@ Pa_GetStreamReadAvailable @31
Pa_GetStreamWriteAvailable @32 Pa_GetStreamWriteAvailable @32
Pa_GetSampleSize @33 Pa_GetSampleSize @33
Pa_Sleep @34 Pa_Sleep @34
PaAsio_GetAvailableLatencyValues @50 PaAsio_GetAvailableBufferSizes @50
PaAsio_ShowControlPanel @51 PaAsio_ShowControlPanel @51
PaUtil_InitializeX86PlainConverters @52 PaUtil_InitializeX86PlainConverters @52
PaAsio_GetInputChannelName @53 PaAsio_GetInputChannelName @53

View File

@ -47,7 +47,7 @@
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
InlineFunctionExpansion="1" InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win" 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 <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@ -112,7 +112,7 @@
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win" 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 <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@ -179,7 +179,7 @@
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
InlineFunctionExpansion="1" InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win" 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 <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@ -244,7 +244,7 @@
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win" 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 <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@ -310,7 +310,7 @@
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
InlineFunctionExpansion="1" InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\src\common,..\..\include,.\,..\..\src\os\win" 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 <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@ -851,6 +851,14 @@
> >
</File> </File>
</Filter> </Filter>
<Filter
Name="wdmks"
>
<File
RelativePath="..\..\src\hostapi\wdmks\pa_win_wdmks.c"
>
</File>
</Filter>
</Filter> </Filter>
<Filter <Filter
Name="os" Name="os"
@ -858,6 +866,10 @@
<Filter <Filter
Name="win" Name="win"
> >
<File
RelativePath="..\..\src\os\win\pa_win_coinitialize.c"
>
</File>
<File <File
RelativePath="..\..\src\os\win\pa_win_hostapis.c" RelativePath="..\..\src\os\win\pa_win_hostapis.c"
> >
@ -914,6 +926,10 @@
RelativePath="..\..\include\pa_mac_core.h" RelativePath="..\..\include\pa_mac_core.h"
> >
</File> </File>
<File
RelativePath="..\..\src\os\win\pa_win_coinitialize.h"
>
</File>
<File <File
RelativePath="..\..\include\pa_win_ds.h" RelativePath="..\..\include\pa_win_ds.h"
> >

View File

@ -172,7 +172,7 @@
<ClCompile> <ClCompile>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> </ClCompile>
<ResourceCompile> <ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -241,7 +241,7 @@
</Midl> </Midl>
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> </ClCompile>
<ResourceCompile> <ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -315,7 +315,7 @@
<ClCompile> <ClCompile>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <WholeProgramOptimization>true</WholeProgramOptimization>
</ClCompile> </ClCompile>
<ResourceCompile> <ResourceCompile>
@ -385,7 +385,7 @@
</Midl> </Midl>
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>..\..\src\common;..\..\include;.\;..\..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> </ClCompile>
<ResourceCompile> <ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -616,8 +616,10 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\hostapi\dsound\pa_win_ds.c" /> <ClCompile Include="..\..\src\hostapi\dsound\pa_win_ds.c" />
<ClCompile Include="..\..\src\hostapi\dsound\pa_win_ds_dynlink.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\wmme\pa_win_wmme.c" />
<ClCompile Include="..\..\src\hostapi\wasapi\pa_win_wasapi.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_hostapis.c" />
<ClCompile Include="..\..\src\os\win\pa_win_util.c" /> <ClCompile Include="..\..\src\os\win\pa_win_util.c" />
<ClCompile Include="..\..\src\os\win\pa_win_waveformat.c" /> <ClCompile Include="..\..\src\os\win\pa_win_waveformat.c" />
@ -625,6 +627,7 @@
<ClCompile Include="..\..\src\os\win\pa_x86_plain_converters.c" /> <ClCompile Include="..\..\src\os\win\pa_x86_plain_converters.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\src\hostapi\wdmks\readme.txt" />
<None Include="portaudio.def" /> <None Include="portaudio.def" />
<None Include="portaudio_noasio.def" /> <None Include="portaudio_noasio.def" />
</ItemGroup> </ItemGroup>
@ -638,6 +641,7 @@
<ClInclude Include="..\..\include\pa_win_waveformat.h" /> <ClInclude Include="..\..\include\pa_win_waveformat.h" />
<ClInclude Include="..\..\include\pa_win_wmme.h" /> <ClInclude Include="..\..\include\pa_win_wmme.h" />
<ClInclude Include="..\..\include\portaudio.h" /> <ClInclude Include="..\..\include\portaudio.h" />
<ClInclude Include="..\..\src\os\win\pa_win_coinitialize.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -40,6 +40,9 @@
<UniqueIdentifier>{78689f06-8c26-4417-93e2-b809a8c41ee7}</UniqueIdentifier> <UniqueIdentifier>{78689f06-8c26-4417-93e2-b809a8c41ee7}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl</Extensions> <Extensions>h;hpp;hxx;hm;inl</Extensions>
</Filter> </Filter>
<Filter Include="Source Files\hostapi\wdmks">
<UniqueIdentifier>{0d9360dd-9d00-434a-ab46-7a89b2d35fd0}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\src\common\pa_allocation.c"> <ClCompile Include="..\..\src\common\pa_allocation.c">
@ -123,6 +126,12 @@
<ClCompile Include="..\..\src\os\win\pa_x86_plain_converters.c"> <ClCompile Include="..\..\src\os\win\pa_x86_plain_converters.c">
<Filter>Source Files\os\win</Filter> <Filter>Source Files\os\win</Filter>
</ClCompile> </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>
<ItemGroup> <ItemGroup>
<None Include="portaudio.def"> <None Include="portaudio.def">
@ -131,6 +140,9 @@
<None Include="portaudio_noasio.def"> <None Include="portaudio_noasio.def">
<Filter>Resource Files</Filter> <Filter>Resource Files</Filter>
</None> </None>
<None Include="..\..\src\hostapi\wdmks\readme.txt">
<Filter>Source Files\hostapi\wdmks</Filter>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\include\pa_asio.h"> <ClInclude Include="..\..\include\pa_asio.h">
@ -160,5 +172,8 @@
<ClInclude Include="..\..\include\portaudio.h"> <ClInclude Include="..\..\include\portaudio.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\os\win\pa_win_coinitialize.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

File diff suppressed because it is too large Load Diff

View File

@ -201,11 +201,20 @@ case "${host_os}" in
LIBS="-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon" LIBS="-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon"
if test "x$enable_mac_universal" = "xyes" ; then if test "x$enable_mac_universal" = "xyes" ; then
mac_version_min="-mmacosx-version-min=10.3"
if [[ -d /Developer/SDKs/MacOSX10.5.sdk ]] ; then 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_arches="-arch i386 -arch ppc -arch x86_64 -arch ppc64"
mac_sysroot="-isysroot /Developer/SDKs/MacOSX10.5.sdk" 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 else
mac_version_min="-mmacosx-version-min=10.3"
mac_arches="-arch i386 -arch ppc" mac_arches="-arch i386 -arch ppc"
mac_sysroot="-isysroot /Developer/SDKs/MacOSX10.4u.sdk" mac_sysroot="-isysroot /Developer/SDKs/MacOSX10.4u.sdk"
fi fi
@ -230,7 +239,7 @@ case "${host_os}" in
if [[ "x$with_directx" = "xyes" ]]; then if [[ "x$with_directx" = "xyes" ]]; then
DXDIR="$with_dxdir" 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" LIBS="-lwinmm -lm -ldsound -lole32"
DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -ldsound -lole32" DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -ldsound -lole32"
#VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"" #VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\""
@ -240,7 +249,7 @@ case "${host_os}" in
if [[ "x$with_asio" = "xyes" ]]; then if [[ "x$with_asio" = "xyes" ]]; then
ASIODIR="$with_asiodir" 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" LIBS="-lwinmm -lm -lole32 -luuid"
DLL_LIBS="${DLL_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" 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 fi
if [[ "x$with_wasapi" = "xyes" ]]; then 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" LIBS="-lwinmm -lm -lole32 -luuid"
DLL_LIBS="${DLL_LIBS} -lwinmm -lole32" 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" CFLAGS="$CFLAGS -I\$(top_srcdir)/src/common -I\$(top_srcdir)/src/hostapi/wasapi/mingw-include -UPA_USE_WASAPI -DPA_USE_WASAPI=1"

View File

@ -1,7 +1,7 @@
#ifndef PA_ASIO_H #ifndef PA_ASIO_H
#define 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 * PortAudio Portable Real-Time Audio Library
* ASIO specific extensions * ASIO specific extensions
* *
@ -52,23 +52,30 @@ extern "C"
#endif /* __cplusplus */ #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 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 minBufferSizeFrames A pointer to the location which will receive the minimum buffer size value.
@param maxLatency A pointer to the location which will recieve the maximum latency value. @param maxBufferSizeFrames A pointer to the location which will receive the maximum buffer size value.
@param preferredLatency A pointer to the location which will recieve the preferred latency 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 recieve the granularity. This value @param granularity A pointer to the location which will receive the "granularity". This value determines
determines which values between minLatency and maxLatency are available. ie the step size, the step size used to compute the legal values between minBufferSizeFrames and maxBufferSizeFrames.
if granularity is -1 then available latency settings are powers of two. If granularity is -1 then available buffer size values are powers of two.
@see ASIOGetBufferSize in the ASIO SDK. @see ASIOGetBufferSize in the ASIO SDK.
@todo This function should be renamed to PaAsio_GetAvailableBufferSizes. @note: this function used to be called PaAsio_GetAvailableLatencyValues. There is a
No reason to use a wildly different name from the ASIO version. #define that maps PaAsio_GetAvailableLatencyValues to this function for backwards compatibility.
*/ */
PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device,
long *minLatency, long *maxLatency, long *preferredLatency, long *granularity ); 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. /** Display the ASIO control panel for the specified device.

View File

@ -53,7 +53,7 @@ extern "C" {
#endif #endif
/* /**
* A pointer to a paMacCoreStreamInfo may be passed as * A pointer to a paMacCoreStreamInfo may be passed as
* the hostApiSpecificStreamInfo in the PaStreamParameters struct * the hostApiSpecificStreamInfo in the PaStreamParameters struct
* when opening a stream or querying the format. Use NULL, for the * 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 */ unsigned long size; /**size of whole structure including this header */
PaHostApiTypeId hostApiType; /**host API for which this data is intended */ PaHostApiTypeId hostApiType; /**host API for which this data is intended */
unsigned long version; /**structure version */ unsigned long version; /**structure version */
unsigned long flags; /* flags to modify behaviour */ unsigned long flags; /** flags to modify behaviour */
SInt32 const * channelMap; /* Channel map for HAL channel mapping , if not needed, use NULL;*/ 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 channelMapSize; /** Channel map size for HAL channel mapping , if not needed, use 0;*/
} PaMacCoreStreamInfo; } PaMacCoreStreamInfo;
/* /**
* Functions * 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 * using the requested flags. Note that channel mapping is turned
* off after a call to this function. * off after a call to this function.
* @param data The datastructure to initialize * @param data The datastructure to initialize
@ -83,14 +83,14 @@ typedef struct
*/ */
void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, unsigned long flags ); 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 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 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. * @param channelMapSize The size of the channel map array.
*/ */
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, unsigned long channelMapSize ); void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, unsigned long channelMapSize );
/* /**
* Retrieve the AudioDeviceID of the input device assigned to an open stream * Retrieve the AudioDeviceID of the input device assigned to an open stream
* *
* @param s The stream to query. * @param s The stream to query.
@ -99,7 +99,7 @@ void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const
*/ */
AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s ); AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s );
/* /**
* Retrieve the AudioDeviceID of the output device assigned to an open stream * Retrieve the AudioDeviceID of the output device assigned to an open stream
* *
* @param s The stream to query. * @param s The stream to query.
@ -108,7 +108,7 @@ AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s );
*/ */
AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s ); AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s );
/* /**
* Returns a statically allocated string with the device's name * Returns a statically allocated string with the device's name
* for the given channel. NULL will be returned on failure. * 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 ); const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input );
/* /**
* Flags * Flags
*/ */
/* /**
* The following flags alter the behaviour of PA on the mac platform. * The following flags alter the behaviour of PA on the mac platform.
* they can be ORed together. These should work both for opening and * they can be ORed together. These should work both for opening and
* checking a device. * 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 * which allows for much lower latency, but might disrupt the device
* if other programs are using it, even when you are just Querying * if other programs are using it, even when you are just Querying
* the device. */ * the device. */
#define paMacCoreChangeDeviceParameters (0x01) #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 * causes the stream opening to fail, unless the exact sample rates
* are supported by the device. */ * are supported by the device. */
#define paMacCoreFailIfConversionRequired (0x02) #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.*/ * allows Maximum Quality to be the default.*/
#define paMacCoreConversionQualityMin (0x0100) #define paMacCoreConversionQualityMin (0x0100)
#define paMacCoreConversionQualityMedium (0x0200) #define paMacCoreConversionQualityMedium (0x0200)
@ -153,26 +153,26 @@ const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input )
#define paMacCoreConversionQualityHigh (0x0400) #define paMacCoreConversionQualityHigh (0x0400)
#define paMacCoreConversionQualityMax (0x0000) #define paMacCoreConversionQualityMax (0x0000)
/* /**
* Here are some "preset" combinations of flags (above) to get to some * Here are some "preset" combinations of flags (above) to get to some
* common configurations. THIS IS OVERKILL, but if more flags are added * common configurations. THIS IS OVERKILL, but if more flags are added
* it won't be. * 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. */ * and as little mucking with the device as possible. */
#define paMacCorePlayNice (0x00) #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.*/ and output, but it tries to set the appropriate SR on the device.*/
#define paMacCorePro (0x01) #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) #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) #define paMacCoreMinimizeCPU (0x0101)
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /** __cplusplus */
#endif /* PA_MAC_CORE_H */ #endif /** PA_MAC_CORE_H */

View File

@ -1,7 +1,7 @@
#ifndef PORTAUDIO_H #ifndef PORTAUDIO_H
#define 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 Portable Real-Time Audio Library
* PortAudio API Header File * PortAudio API Header File
* Latest version available at: http://www.portaudio.com/ * Latest version available at: http://www.portaudio.com/
@ -450,15 +450,15 @@ typedef struct PaDeviceInfo
{ {
int structVersion; /* this is struct version 2 */ int structVersion; /* this is struct version 2 */
const char *name; 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 maxInputChannels;
int maxOutputChannels; int maxOutputChannels;
/* Default latency values for interactive performance. */ /** Default latency values for interactive performance. */
PaTime defaultLowInputLatency; PaTime defaultLowInputLatency;
PaTime defaultLowOutputLatency; 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 defaultHighInputLatency;
PaTime defaultHighOutputLatency; PaTime defaultHighOutputLatency;
@ -640,11 +640,15 @@ typedef unsigned long PaStreamFlags;
/** /**
Timing information for the buffers passed to the stream callback. 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{ typedef struct PaStreamCallbackTimeInfo{
PaTime inputBufferAdcTime; PaTime inputBufferAdcTime; /**< The time when the first sample of the input buffer was captured at the ADC input */
PaTime currentTime; PaTime currentTime; /**< The time when the stream callback was invoked */
PaTime outputBufferDacTime; PaTime outputBufferDacTime; /**< The time when the first sample of the output buffer will output the DAC */
} PaStreamCallbackTimeInfo; } PaStreamCallbackTimeInfo;
@ -697,9 +701,9 @@ typedef unsigned long PaStreamCallbackFlags;
*/ */
typedef enum PaStreamCallbackResult typedef enum PaStreamCallbackResult
{ {
paContinue=0, paContinue=0, /**< Signal that the stream should continue invoking the callback and processing audio. */
paComplete=1, paComplete=1, /**< Signal that the stream should stop invoking the callback and finish once all output samples have played. */
paAbort=2 paAbort=2 /**< Signal that the stream should stop invoking the callback and finish as soon as possible. */
} PaStreamCallbackResult; } PaStreamCallbackResult;
@ -719,11 +723,10 @@ typedef enum PaStreamCallbackResult
@param frameCount The number of sample frames to be processed by @param frameCount The number of sample frames to be processed by
the stream callback. the stream callback.
@param timeInfo The time in seconds when the first sample of the input @param timeInfo Timestamps indicating the ADC capture time of the first sample
buffer was received at the audio input, the time in seconds when the first in the input buffer, the DAC output time of the first sample in the output buffer
sample of the output buffer will begin being played at the audio output, and and the time the callback was invoked.
the time in seconds when the stream callback was called. See PaStreamCallbackTimeInfo and Pa_GetStreamTime()
See also Pa_GetStreamTime()
@param statusFlags Flags indicating whether input and/or output buffers @param statusFlags Flags indicating whether input and/or output buffers
have been inserted or will be dropped to overcome underflow or overflow have been inserted or will be dropped to overcome underflow or overflow

View File

@ -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 * Portable Audio I/O Library
* streamCallback <-> host buffer processing adapter * streamCallback <-> host buffer processing adapter
* *
@ -223,7 +223,7 @@ PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
goto error; 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, 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. */ 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; return bp->initialFramesInTempInputBuffer;
} }
unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp ) unsigned long PaUtil_GetBufferProcessorOutputLatencyFrames( PaUtilBufferProcessor* bp )
{ {
return bp->initialFramesInTempOutputBuffer; return bp->initialFramesInTempOutputBuffer;
} }

View File

@ -1,7 +1,7 @@
#ifndef PA_PROCESS_H #ifndef PA_PROCESS_H
#define 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 * Portable Audio I/O Library callback buffer processing adapters
* *
* Based on the Open Source API proposed by Ross Bencina * Based on the Open Source API proposed by Ross Bencina
@ -406,25 +406,25 @@ void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
void PaUtil_ResetBufferProcessor( 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. @param bufferProcessor The buffer processor examine.
@return The input latency introduced by the buffer processor, in frames. @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. @param bufferProcessor The buffer processor examine.
@return The output latency introduced by the buffer processor, in frames. @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 );
/*@}*/ /*@}*/

View File

@ -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 * Portable Audio I/O Library
* Ring Buffer utility. * Ring Buffer utility.
* *
@ -77,14 +77,14 @@ ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buf
/*************************************************************************** /***************************************************************************
** Return number of elements available for reading. */ ** 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(); PaUtil_ReadMemoryBarrier();
return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
} }
/*************************************************************************** /***************************************************************************
** Return number of elements available for writing. */ ** 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 */ /* Since we are calling PaUtil_GetRingBufferReadAvailable, we don't need an aditional MB */
return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf)); return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf));

View File

@ -1,7 +1,7 @@
#ifndef PA_RINGBUFFER_H #ifndef PA_RINGBUFFER_H
#define 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 * Portable Audio I/O Library
* Ring Buffer utility. * Ring Buffer utility.
* *
@ -125,7 +125,7 @@ void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf );
@return The number of elements available for writing. @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. /** 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. @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. /** Write data to the ring buffer.

View File

@ -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 * PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com * Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen * ALSA implementation by Joshua Haberman and Arve Knudsen
@ -79,6 +79,10 @@
#include "pa_linux_alsa.h" #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. */ /* 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 #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 */ /* Ok, buffer processor is initialized, now we can deduce it's latency */
if( numInputChannels > 0 ) if( numInputChannels > 0 )
stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)( stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)(
PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate); PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) / sampleRate);
if( numOutputChannels > 0 ) if( numOutputChannels > 0 )
stream->streamRepresentation.streamInfo.outputLatency = outputLatency + (PaTime)( 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)); 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));

View File

@ -1827,7 +1827,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/ sampleRate / stream->input->bytesPerFrame; / sampleRate / stream->input->bytesPerFrame;
stream->baseStreamRep.streamInfo.inputLatency = stream->baseStreamRep.streamInfo.inputLatency =
bufferDuration + bufferDuration +
((PaTime)PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) - ((PaTime)PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) -
stream->maxFramesPerHostBuffer) / sampleRate; stream->maxFramesPerHostBuffer) / sampleRate;
assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 ); assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 );
} }
@ -1844,7 +1844,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
} }
stream->baseStreamRep.streamInfo.outputLatency = stream->baseStreamRep.streamInfo.outputLatency =
bufferDuration + bufferDuration +
((PaTime)PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) - ((PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) -
stream->maxFramesPerHostBuffer) / sampleRate; stream->maxFramesPerHostBuffer) / sampleRate;
assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 ); assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 );
} }

View File

@ -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 * Portable Audio I/O Library for ASIO Drivers
* *
* Author: Stephane Letz * Author: Stephane Letz
@ -101,6 +101,8 @@
#include "pa_debugprint.h" #include "pa_debugprint.h"
#include "pa_ringbuffer.h" #include "pa_ringbuffer.h"
#include "pa_win_coinitialize.h"
/* This version of pa_asio.cpp is currently only targetted at Win32, /* 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. It would require a few tweaks to work with pre-OS X Macintosh.
To make configuration easier, we define WIN32 here to make sure To make configuration easier, we define WIN32 here to make sure
@ -289,6 +291,8 @@ typedef struct
PaUtilAllocationGroup *allocations; PaUtilAllocationGroup *allocations;
PaWinUtilComInitializationResult comInitializationResult;
AsioDrivers *asioDrivers; AsioDrivers *asioDrivers;
void *systemSpecific; void *systemSpecific;
@ -906,8 +910,8 @@ typedef struct PaAsioDeviceInfo
PaAsioDeviceInfo; PaAsioDeviceInfo;
PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device, PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device,
long *minLatency, long *maxLatency, long *preferredLatency, long *granularity ) long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity )
{ {
PaError result; PaError result;
PaUtilHostApiRepresentation *hostApi; PaUtilHostApiRepresentation *hostApi;
@ -924,9 +928,9 @@ PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
PaAsioDeviceInfo *asioDeviceInfo = PaAsioDeviceInfo *asioDeviceInfo =
(PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
*minLatency = asioDeviceInfo->minBufferSize; *minBufferSizeFrames = asioDeviceInfo->minBufferSize;
*maxLatency = asioDeviceInfo->maxBufferSize; *maxBufferSizeFrames = asioDeviceInfo->maxBufferSize;
*preferredLatency = asioDeviceInfo->preferredBufferSize; *preferredBufferSizeFrames = asioDeviceInfo->preferredBufferSize;
*granularity = asioDeviceInfo->bufferGranularity; *granularity = asioDeviceInfo->bufferGranularity;
} }
} }
@ -935,12 +939,10 @@ PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
} }
/* Unload whatever we loaded in LoadAsioDriver(). /* Unload whatever we loaded in LoadAsioDriver().
Also balance the call to CoInitialize(0).
*/ */
static void UnloadAsioDriver( void ) static void UnloadAsioDriver( void )
{ {
ASIOExit(); ASIOExit();
CoUninitialize();
} }
/* /*
@ -956,23 +958,8 @@ static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const c
ASIOError asioError; ASIOError asioError;
int asioIsInitialized = 0; 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( !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; result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" ); PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
goto error; goto error;
@ -1021,7 +1008,7 @@ error:
{ {
ASIOExit(); ASIOExit();
} }
CoUninitialize();
return result; return result;
} }
@ -1053,6 +1040,24 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
goto error; 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->asioDrivers = 0; /* avoid surprises in our error handler below */
asioHostApi->allocations = PaUtil_CreateAllocationGroup(); asioHostApi->allocations = PaUtil_CreateAllocationGroup();
@ -1065,7 +1070,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
/* Allocate the AsioDrivers() driver list (class from ASIO SDK) */ /* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
try try
{ {
asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */ asioHostApi->asioDrivers = new AsioDrivers(); /* invokes CoInitialize(0) in AsioDriverList::AsioDriverList */
} }
catch (std::bad_alloc) catch (std::bad_alloc)
{ {
@ -1347,8 +1352,11 @@ error:
delete asioHostApi->asioDrivers; delete asioHostApi->asioDrivers;
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */ asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
PaUtil_FreeMemory( asioHostApi ); PaUtil_FreeMemory( asioHostApi );
} }
return result; return result;
} }
@ -1368,9 +1376,11 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); 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 */ asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
PaUtil_FreeMemory( asioHostApi ); 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 ) PaAsioDriverInfo *driverInfo )
{ {
unsigned long result; unsigned long result;
@ -2085,15 +2095,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( usingBlockingIo ) if( usingBlockingIo )
{ {
/** @todo REVIEW selection of host buffer size for blocking i/o */ /** @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... */ else /* Using callback interface... */
{ {
framesPerHostBuffer = SelectHostBufferSize( framesPerHostBuffer = SelectHostBufferSize(
(( suggestedInputLatencyFrames > suggestedOutputLatencyFrames ) (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
? suggestedInputLatencyFrames : suggestedOutputLatencyFrames), ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames), framesPerBuffer,
driverInfo ); driverInfo );
} }
@ -2369,8 +2379,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* Compute total intput latency in seconds */ /* Compute total intput latency in seconds */
stream->streamRepresentation.streamInfo.inputLatency = stream->streamRepresentation.streamInfo.inputLatency =
(double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor ) (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor )
+ PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor)
+ (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
+ stream->asioInputLatencyFrames ) + stream->asioInputLatencyFrames )
/ sampleRate; / 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", PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
stream->asioInputLatencyFrames, stream->asioInputLatencyFrames,
(long)( stream->asioInputLatencyFrames * (1000.0 / sampleRate) ), (long)( stream->asioInputLatencyFrames * (1000.0 / sampleRate) ),
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor), PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor),
(long)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ), (long)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
(long)( (PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) (long)( (PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
)); ));
/* Determine the size of ring buffer in bytes. */ /* Determine the size of ring buffer in bytes. */
@ -2460,8 +2470,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* Compute total output latency in seconds */ /* Compute total output latency in seconds */
stream->streamRepresentation.streamInfo.outputLatency = stream->streamRepresentation.streamInfo.outputLatency =
(double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor ) (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
+ PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor)
+ (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
+ stream->asioOutputLatencyFrames ) + stream->asioOutputLatencyFrames )
/ sampleRate; / 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", PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
stream->asioOutputLatencyFrames, stream->asioOutputLatencyFrames,
(long)( stream->asioOutputLatencyFrames * (1000.0 / sampleRate) ), (long)( stream->asioOutputLatencyFrames * (1000.0 / sampleRate) ),
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor), PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor),
(long)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ), (long)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
(long)( (PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) (long)( (PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
)); ));
/* Determine the size of ring buffer in bytes. */ /* Determine the size of ring buffer in bytes. */
@ -2517,10 +2527,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
callbackBufferProcessorInited = TRUE; callbackBufferProcessorInited = TRUE;
stream->streamRepresentation.streamInfo.inputLatency = stream->streamRepresentation.streamInfo.inputLatency =
(double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
+ stream->asioInputLatencyFrames) / sampleRate; // seconds + stream->asioInputLatencyFrames) / sampleRate; // seconds
stream->streamRepresentation.streamInfo.outputLatency = stream->streamRepresentation.streamInfo.outputLatency =
(double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
+ stream->asioOutputLatencyFrames) / sampleRate; // seconds + stream->asioOutputLatencyFrames) / sampleRate; // seconds
stream->streamRepresentation.streamInfo.sampleRate = sampleRate; 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", PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
stream->asioInputLatencyFrames, stream->asioInputLatencyFrames,
(long)((stream->asioInputLatencyFrames*1000)/ sampleRate), (long)((stream->asioInputLatencyFrames*1000)/ sampleRate),
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor), PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor),
(long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate) (long)((PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate)
)); ));
PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
stream->asioOutputLatencyFrames, stream->asioOutputLatencyFrames,
(long)((stream->asioOutputLatencyFrames*1000)/ sampleRate), (long)((stream->asioOutputLatencyFrames*1000)/ sampleRate),
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor), PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor),
(long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate) (long)((PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate)
)); ));
} }
@ -3836,7 +3846,12 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
int asioIsInitialized = 0; int asioIsInitialized = 0;
PaAsioHostApiRepresentation *asioHostApi; PaAsioHostApiRepresentation *asioHostApi;
PaAsioDeviceInfo *asioDeviceInfo; 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 ); result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
if( result != paNoError ) if( result != paNoError )
@ -3863,9 +3878,6 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
/* See notes about CoInitialize(0) in LoadAsioDriver(). */
CoInitialize(0);
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) ) if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
{ {
result = paUnanticipatedHostError; result = paUnanticipatedHostError;
@ -3914,7 +3926,6 @@ PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErr
goto error; goto error;
} }
CoUninitialize();
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) )); PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
return result; return result;
@ -3924,7 +3935,8 @@ error:
{ {
ASIOExit(); ASIOExit();
} }
CoUninitialize();
PaWinUtil_CoUninitialize( paASIO, &comInitializationResult );
return result; return result;
} }

View File

@ -77,8 +77,10 @@ extern "C"
{ {
#endif /* __cplusplus */ #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 ); PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
/* /*
@ -397,6 +399,153 @@ static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
return paNoError; 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, static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
PaDeviceInfo *deviceInfo, PaDeviceInfo *deviceInfo,
AudioDeviceID macCoreDeviceId, AudioDeviceID macCoreDeviceId,
@ -407,7 +556,6 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
UInt32 i; UInt32 i;
int numChannels = 0; int numChannels = 0;
AudioBufferList *buflist = NULL; AudioBufferList *buflist = NULL;
UInt32 frameLatency;
VVDBUG(("GetChannelInfo()\n")); VVDBUG(("GetChannelInfo()\n"));
@ -433,39 +581,33 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
else else
deviceInfo->maxOutputChannels = numChannels; 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. */ /* Get the latency. Don't fail if we can't get this. */
/* default to something reasonable */ /* default to something reasonable */
deviceInfo->defaultLowInputLatency = .01; deviceInfo->defaultLowInputLatency = .01;
deviceInfo->defaultHighInputLatency = .10; deviceInfo->defaultHighInputLatency = .10;
deviceInfo->defaultLowOutputLatency = .01; deviceInfo->defaultLowOutputLatency = .01;
deviceInfo->defaultHighOutputLatency = .10; deviceInfo->defaultHighOutputLatency = .10;
propSize = sizeof(UInt32); UInt32 lowLatencyFrames = 0;
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); UInt32 highLatencyFrames = 0;
if (!err) 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 double lowLatencySeconds = lowLatencyFrames / deviceInfo->defaultSampleRate;
* kAudioDevicePropertyLatency + kAudioDevicePropertySafetyOffset + buffer size instead. double highLatencySeconds = highLatencyFrames / deviceInfo->defaultSampleRate;
* At the time this code was written, many users were reporting dropouts with audio if (isInput)
* programs that probably used this formula. This was probably {
* around 10.4.4, and the problem is probably fixed now. So perhaps deviceInfo->defaultLowInputLatency = lowLatencySeconds;
* his formula should be reviewed and used. deviceInfo->defaultHighInputLatency = highLatencySeconds;
* */ }
double secondLatency = frameLatency / deviceInfo->defaultSampleRate; else
if (isInput) {
{ deviceInfo->defaultLowOutputLatency = lowLatencySeconds;
deviceInfo->defaultLowInputLatency = 3 * secondLatency; deviceInfo->defaultHighOutputLatency = highLatencySeconds;
deviceInfo->defaultHighInputLatency = 3 * 10 * secondLatency; }
} }
else
{
deviceInfo->defaultLowOutputLatency = 3 * secondLatency;
deviceInfo->defaultHighOutputLatency = 3 * 10 * secondLatency;
}
}
} }
PaUtil_FreeMemory( buflist ); PaUtil_FreeMemory( buflist );
return paNoError; return paNoError;
@ -474,6 +616,7 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
return err; return err;
} }
/* =================================================================================================== */
static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi, static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
PaDeviceInfo *deviceInfo, PaDeviceInfo *deviceInfo,
AudioDeviceID macCoreDeviceId, AudioDeviceID macCoreDeviceId,
@ -797,75 +940,172 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paFormatIsSupported; 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 */ memset( deviceProperties, 0, sizeof(PaMacCoreDeviceProperties) );
Float64 actualOutputSampleRate = stream->outDeviceSampleRate; deviceProperties->sampleRate = 1.0; // Better than random. Overwritten by actual values later on.
UInt32 propSize = sizeof(Float64); deviceProperties->samplePeriod = 1.0 / deviceProperties->sampleRate;
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;
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 ) static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
{ {
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData; PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
OSStatus osErr = UpdateSampleRateFromDeviceProperty( stream, inDevice, isInput );
pthread_mutex_lock( &stream->timingInformationMutex ); if( osErr == noErr )
UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( stream ); {
pthread_mutex_unlock( &stream->timingInformationMutex ); UpdateTimeStampOffsets( stream );
}
return noErr; return osErr;
} }
static void UpdateOutputLatencySamplesFromDeviceProperty( PaMacCoreStream *stream ) /* ================================================================================= */
static OSStatus QueryUInt32DeviceProperty( AudioDeviceID deviceID, Boolean isInput, AudioDevicePropertyID propertyID, UInt32 *outValue )
{ {
UInt32 deviceOutputLatencySamples = 0; UInt32 propertyValue = 0;
UInt32 propSize = sizeof(UInt32); UInt32 propertySize = sizeof(UInt32);
OSStatus osErr = AudioDeviceGetProperty( stream->outputDevice, 0, /* isInput= */ FALSE, kAudioDevicePropertyLatency, &propSize, &deviceOutputLatencySamples); OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, propertyID, &propertySize, &propertyValue);
if( osErr != noErr ) if( osErr == noErr )
deviceOutputLatencySamples = 0; {
*outValue = propertyValue;
stream->deviceOutputLatencySamples = deviceOutputLatencySamples; }
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; 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 ); case kAudioDevicePropertyLatency:
UpdateOutputLatencySamplesFromDeviceProperty( stream ); valuePtr = &deviceProperties->deviceLatency;
pthread_mutex_unlock( &stream->timingInformationMutex ); 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; OSStatus osErr = noErr;
UInt32 propSize = sizeof(UInt32); PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
OSStatus osErr = AudioDeviceGetProperty( stream->inputDevice, 0, /* isInput= */ TRUE, kAudioDevicePropertyLatency, &propSize, &deviceInputLatencySamples);
if( osErr != noErr )
deviceInputLatencySamples = 0;
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 ); AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyLatency,
UpdateInputLatencySamplesFromDeviceProperty( stream ); AudioDevicePropertyGenericListenerProc );
pthread_mutex_unlock( &stream->timingInformationMutex ); AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
AudioDevicePropertyGenericListenerProc );
return noErr; AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
AudioDevicePropertyGenericListenerProc );
} }
/* ================================================================================= */
static PaError OpenAndSetupOneAudioUnit( static PaError OpenAndSetupOneAudioUnit(
const PaMacCoreStream *stream, const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams, const PaStreamParameters *inStreamParams,
@ -1299,11 +1539,17 @@ static PaError OpenAndSetupOneAudioUnit(
ERR_WRAP( AudioUnitInitialize(*audioUnit) ); ERR_WRAP( AudioUnitInitialize(*audioUnit) );
if( inStreamParams && outStreamParams ) if( inStreamParams && outStreamParams )
VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) ); {
VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) );
}
else if( inStreamParams ) else if( inStreamParams )
VDBUG( ("Opened device %ld for input.\n", *audioDevice ) ); {
VDBUG( ("Opened device %ld for input.\n", *audioDevice ) );
}
else if( outStreamParams ) else if( outStreamParams )
VDBUG( ("Opened device %ld for output.\n", *audioDevice ) ); {
VDBUG( ("Opened device %ld for output.\n", *audioDevice ) );
}
return paNoError; return paNoError;
#undef ERR_WRAP #undef ERR_WRAP
@ -1315,13 +1561,74 @@ static PaError OpenAndSetupOneAudioUnit(
return paResult; 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 */ /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s, PaStream** s,
const PaStreamParameters *inputParameters, const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters, const PaStreamParameters *outputParameters,
double sampleRate, double sampleRate,
unsigned long framesPerBuffer, unsigned long requestedFramesPerBuffer,
PaStreamFlags streamFlags, PaStreamFlags streamFlags,
PaStreamCallback *streamCallback, PaStreamCallback *streamCallback,
void *userData ) void *userData )
@ -1332,23 +1639,30 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
int inputChannelCount, outputChannelCount; int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat; PaSampleFormat inputSampleFormat, outputSampleFormat;
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; 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", 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->channelCount : -1,
inputParameters ? inputParameters->sampleFormat : -1, inputParameters ? inputParameters->sampleFormat : -1,
outputParameters ? outputParameters->channelCount : -1, outputParameters ? outputParameters->channelCount : -1,
outputParameters ? outputParameters->sampleFormat : -1, outputParameters ? outputParameters->sampleFormat : -1,
(float) sampleRate, (float) sampleRate,
framesPerBuffer )); requestedFramesPerBuffer ));
VDBUG( ("Opening Stream.\n") ); 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 ) if( inputParameters )
{ {
inputChannelCount = inputParameters->channelCount; inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat; inputSampleFormat = inputParameters->sampleFormat;
/* @todo Blocking read/write on Mac is not yet supported. */ /* @todo Blocking read/write on Mac is not yet supported. */
if( inputSampleFormat & paNonInterleaved ) if( !streamCallback && inputSampleFormat & paNonInterleaved )
{ {
return paSampleFormatNotSupported; return paSampleFormatNotSupported;
} }
@ -1378,7 +1692,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
outputSampleFormat = outputParameters->sampleFormat; outputSampleFormat = outputParameters->sampleFormat;
/* @todo Blocking read/write on Mac is not yet supported. */ /* @todo Blocking read/write on Mac is not yet supported. */
if( outputSampleFormat & paNonInterleaved ) if( !streamCallback && outputSampleFormat & paNonInterleaved )
{ {
return paSampleFormatNotSupported; return paSampleFormatNotSupported;
} }
@ -1454,70 +1768,25 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
/* -- handle paFramesPerBufferUnspecified -- */
if( framesPerBuffer == paFramesPerBufferUnspecified ) { if( inputParameters )
long requested = 64; {
if( inputParameters ) CalculateFixedDeviceLatency( auhalHostApi->devIds[inputParameters->device], true, &fixedInputLatency );
requested = MAX( requested, inputParameters->suggestedLatency * sampleRate / 2 ); inputLatencyFrames += fixedInputLatency;
if( outputParameters ) }
requested = MAX( requested, outputParameters->suggestedLatency *sampleRate / 2 ); if( outputParameters )
VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n", {
requested ) ); CalculateFixedDeviceLatency( auhalHostApi->devIds[outputParameters->device], false, &fixedOutputLatency );
if( requested <= 64 ) { outputLatencyFrames += fixedOutputLatency;
/*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 ); suggestedLatencyFramesPerBuffer = CalculateOptimalBufferSize( auhalHostApi, inputParameters, outputParameters,
if( inputParameters ) { fixedInputLatency, fixedOutputLatency,
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device], sampleRate, requestedFramesPerBuffer );
0, if( requestedFramesPerBuffer == paFramesPerBufferUnspecified )
false, {
kAudioDevicePropertyBufferFrameSizeRange, requestedFramesPerBuffer = suggestedLatencyFramesPerBuffer;
&size, &audioRange ) );
if( result )
requested = MAX( requested, audioRange.mMinimum );
}
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;
} }
/* -- Now we actually open and setup streams. -- */ /* -- Now we actually open and setup streams. -- */
@ -1528,7 +1797,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
result = OpenAndSetupOneAudioUnit( stream, result = OpenAndSetupOneAudioUnit( stream,
inputParameters, inputParameters,
outputParameters, outputParameters,
framesPerBuffer, suggestedLatencyFramesPerBuffer,
&inputFramesPerBuffer, &inputFramesPerBuffer,
&outputFramesPerBuffer, &outputFramesPerBuffer,
auhalHostApi, auhalHostApi,
@ -1551,7 +1820,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
result = OpenAndSetupOneAudioUnit( stream, result = OpenAndSetupOneAudioUnit( stream,
NULL, NULL,
outputParameters, outputParameters,
framesPerBuffer, suggestedLatencyFramesPerBuffer,
NULL, NULL,
&outputFramesPerBuffer, &outputFramesPerBuffer,
auhalHostApi, auhalHostApi,
@ -1565,7 +1834,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
result = OpenAndSetupOneAudioUnit( stream, result = OpenAndSetupOneAudioUnit( stream,
inputParameters, inputParameters,
NULL, NULL,
framesPerBuffer, suggestedLatencyFramesPerBuffer,
&inputFramesPerBuffer, &inputFramesPerBuffer,
NULL, NULL,
auhalHostApi, auhalHostApi,
@ -1580,6 +1849,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->outputFramesPerBuffer = outputFramesPerBuffer; stream->outputFramesPerBuffer = outputFramesPerBuffer;
} }
inputLatencyFrames += stream->inputFramesPerBuffer;
outputLatencyFrames += stream->outputFramesPerBuffer;
if( stream->inputUnit ) { if( stream->inputUnit ) {
const size_t szfl = sizeof(float); const size_t szfl = sizeof(float);
/* setup the AudioBufferList used for input */ /* 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 * ring buffer to store inpt data while waiting for output
* data. * data.
*/ */
if( (stream->outputUnit && stream->inputUnit != stream->outputUnit) if( (stream->outputUnit && (stream->inputUnit != stream->outputUnit))
|| stream->inputSRConverter ) || stream->inputSRConverter )
{ {
/* May want the ringSize ot initial position in /* May want the ringSize ot initial position in
@ -1636,6 +1908,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
middle of the buffer */ middle of the buffer */
if( stream->outputUnit ) if( stream->outputUnit )
PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer, ringSize / RING_BUFFER_ADVANCE_DENOMINATOR ); 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 ) ; outputParameters?outputChannelCount:0 ) ;
if( result != paNoError ) if( result != paNoError )
goto error; goto error;
inputLatencyFrames += ringSize;
outputLatencyFrames += ringSize;
} }
/* -- initialize Buffer Processor -- */ /* -- initialize Buffer Processor -- */
@ -1672,7 +1951,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hostOutputSampleFormat, hostOutputSampleFormat,
sampleRate, sampleRate,
streamFlags, streamFlags,
framesPerBuffer, requestedFramesPerBuffer,
/* If sample rate conversion takes place, the buffer size /* If sample rate conversion takes place, the buffer size
will not be known. */ will not be known. */
maxHostFrames, maxHostFrames,
@ -1686,16 +1965,27 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
} }
stream->bufferProcessorIsInitialized = TRUE; stream->bufferProcessorIsInitialized = TRUE;
/* // Calculate actual latency from the sum of individual latencies.
IMPLEMENT ME: initialise the following fields with estimated or actual if( inputParameters )
values. {
I think this is okay the way it is br 12/1/05 inputLatencyFrames += PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor);
maybe need to change input latency estimate if IO devs differ stream->streamRepresentation.streamInfo.inputLatency = inputLatencyFrames / sampleRate;
*/ }
stream->streamRepresentation.streamInfo.inputLatency = else
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)/sampleRate; {
stream->streamRepresentation.streamInfo.outputLatency = stream->streamRepresentation.streamInfo.inputLatency = 0.0;
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)/sampleRate; }
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->streamRepresentation.streamInfo.sampleRate = sampleRate;
stream->sampleRate = sampleRate; stream->sampleRate = sampleRate;
@ -1728,38 +2018,26 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->userInChan = inputChannelCount; stream->userInChan = inputChannelCount;
stream->userOutChan = outputChannelCount; stream->userOutChan = outputChannelCount;
// Setup property listeners for timestamp and latency calculations.
pthread_mutex_init( &stream->timingInformationMutex, NULL ); pthread_mutex_init( &stream->timingInformationMutex, NULL );
stream->timingInformationMutexIsInitialized = 1; stream->timingInformationMutexIsInitialized = 1;
InitializeDeviceProperties( &stream->inputProperties );
if( stream->outputUnit ) { InitializeDeviceProperties( &stream->outputProperties );
UpdateReciprocalOfActualOutputSampleRateFromDeviceProperty( stream ); if( stream->outputUnit )
stream->recipricalOfActualOutputSampleRate_ioProcCopy = stream->recipricalOfActualOutputSampleRate; {
Boolean isInput = FALSE;
AudioDeviceAddPropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate, SetupDevicePropertyListeners( stream, stream->outputDevice, isInput );
AudioDevicePropertyActualSampleRateListenerProc, stream ); }
if( stream->inputUnit )
UpdateOutputLatencySamplesFromDeviceProperty( stream ); {
stream->deviceOutputLatencySamples_ioProcCopy = stream->deviceOutputLatencySamples; Boolean isInput = TRUE;
SetupDevicePropertyListeners( stream, stream->inputDevice, isInput );
AudioDeviceAddPropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyLatency,
AudioDevicePropertyOutputLatencySamplesListenerProc, stream );
}else{
stream->recipricalOfActualOutputSampleRate = 1.;
stream->recipricalOfActualOutputSampleRate_ioProcCopy = 0.;
stream->deviceOutputLatencySamples_ioProcCopy = 0;
}
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;
} }
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->state = STOPPED;
stream->xrunFlags = 0; stream->xrunFlags = 0;
@ -1829,6 +2107,7 @@ static OSStatus AudioIOProc( void *inRefCon,
PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon; PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon;
const bool isRender = inBusNumber == OUTPUT_ELEMENT; const bool isRender = inBusNumber == OUTPUT_ELEMENT;
int callbackResult = paContinue ; int callbackResult = paContinue ;
double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
VVDBUG(("AudioIOProc()\n")); VVDBUG(("AudioIOProc()\n"));
@ -1865,9 +2144,9 @@ static OSStatus AudioIOProc( void *inRefCon,
if( pthread_mutex_trylock( &stream->timingInformationMutex ) == 0 ){ if( pthread_mutex_trylock( &stream->timingInformationMutex ) == 0 ){
/* snapshot the ioproc copy of timing information */ /* snapshot the ioproc copy of timing information */
stream->deviceOutputLatencySamples_ioProcCopy = stream->deviceOutputLatencySamples; stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined;
stream->recipricalOfActualOutputSampleRate_ioProcCopy = stream->recipricalOfActualOutputSampleRate; stream->timestampOffsetInputDevice_ioProcCopy = stream->timestampOffsetInputDevice;
stream->deviceInputLatencySamples_ioProcCopy = stream->deviceInputLatencySamples; stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice;
pthread_mutex_unlock( &stream->timingInformationMutex ); pthread_mutex_unlock( &stream->timingInformationMutex );
} }
@ -1894,32 +2173,30 @@ static OSStatus AudioIOProc( void *inRefCon,
{ {
if( stream->inputUnit == stream->outputUnit ) /* full duplex AUHAL IOProc */ 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 */ // Ross and Phil agreed that the following calculation is correct based on an email from Jeff Moore:
timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) // http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html
- stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here? // Basically the difference between the Apple output timestamp and the PA timestamp is kAudioDevicePropertyLatency.
timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
+ stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; (stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
} }
else /* full duplex with ring-buffer from a separate input AUHAL ioproc */ 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 */ /* FIXME: take the ring buffer latency into account */
timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
- stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here? (stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
+ stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy;
} }
} }
else /* output only */ else /* output only */
{ {
timeInfo.inputBufferAdcTime = 0; timeInfo.inputBufferAdcTime = 0;
timeInfo.outputBufferDacTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
+ stream->deviceOutputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy;
} }
} }
else /* input only */ else /* input only */
{ {
timeInfo.inputBufferAdcTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime) timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - stream->timestampOffsetInputDevice_ioProcCopy;
- stream->deviceInputLatencySamples_ioProcCopy * stream->recipricalOfActualOutputSampleRate_ioProcCopy; // FIXME should be using input sample rate here?
timeInfo.outputBufferDacTime = 0; timeInfo.outputBufferDacTime = 0;
} }
@ -2261,16 +2538,16 @@ static PaError CloseStream( PaStream* s )
if( stream ) { if( stream ) {
if( stream->outputUnit ) { if( stream->outputUnit )
AudioDeviceRemovePropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyActualSampleRate, {
AudioDevicePropertyActualSampleRateListenerProc ); Boolean isInput = FALSE;
AudioDeviceRemovePropertyListener( stream->outputDevice, 0, /* isInput = */ FALSE, kAudioDevicePropertyLatency, CleanupDevicePropertyListeners( stream, stream->outputDevice, isInput );
AudioDevicePropertyOutputLatencySamplesListenerProc );
} }
if( stream->inputUnit ) { if( stream->inputUnit )
AudioDeviceRemovePropertyListener( stream->inputDevice, 0, /* isInput = */ TRUE, kAudioDevicePropertyLatency, {
AudioDevicePropertyInputLatencySamplesListenerProc ); Boolean isInput = FALSE;
CleanupDevicePropertyListeners( stream, stream->inputDevice, isInput );
} }
if( stream->outputUnit ) { if( stream->outputUnit ) {

View File

@ -113,7 +113,18 @@ typedef struct
} }
PaMacAUHAL; 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 */ /* stream data structure specifically for this implementation */
typedef struct PaMacCoreStream typedef struct PaMacCoreStream
@ -159,17 +170,24 @@ typedef struct PaMacCoreStream
double outDeviceSampleRate; double outDeviceSampleRate;
double inDeviceSampleRate; double inDeviceSampleRate;
PaMacCoreDeviceProperties inputProperties;
PaMacCoreDeviceProperties outputProperties;
/* data updated by main thread and notifications, protected by timingInformationMutex */ /* data updated by main thread and notifications, protected by timingInformationMutex */
int timingInformationMutexIsInitialized; int timingInformationMutexIsInitialized;
pthread_mutex_t timingInformationMutex; 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 */ /* These are written by the PA thread or from CoreAudio callbacks. Protected by the mutex. */
Float64 recipricalOfActualOutputSampleRate_ioProcCopy; Float64 timestampOffsetCombined;
UInt32 deviceOutputLatencySamples_ioProcCopy; Float64 timestampOffsetInputDevice;
UInt32 deviceInputLatencySamples_ioProcCopy; 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; PaMacCoreStream;

View File

@ -524,84 +524,60 @@ PaError setBestFramesPerBuffer( const AudioDeviceID device,
UInt32 requestedFramesPerBuffer, UInt32 requestedFramesPerBuffer,
UInt32 *actualFramesPerBuffer ) UInt32 *actualFramesPerBuffer )
{ {
UInt32 afpb; UInt32 afpb;
const bool isInput = !isOutput; const bool isInput = !isOutput;
UInt32 propsize = sizeof(UInt32); UInt32 propsize = sizeof(UInt32);
OSErr err; OSErr err;
Float64 min = -1; /*the min blocksize*/ AudioValueRange range;
Float64 best = -1; /*the best blocksize*/
int i=0;
AudioValueRange *ranges;
if( actualFramesPerBuffer == NULL ) if( actualFramesPerBuffer == NULL )
actualFramesPerBuffer = &afpb; {
actualFramesPerBuffer = &afpb;
}
/* -- try and set exact FPB -- */
/* -- try and set exact FPB -- */ err = AudioDeviceSetProperty( device, NULL, 0, isInput,
err = AudioDeviceSetProperty( device, NULL, 0, isInput,
kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSize,
propsize, &requestedFramesPerBuffer); propsize, &requestedFramesPerBuffer);
err = AudioDeviceGetProperty( device, 0, isInput, err = AudioDeviceGetProperty( device, 0, isInput,
kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSize,
&propsize, actualFramesPerBuffer); &propsize, actualFramesPerBuffer);
if( err ) if( err )
return ERR( err ); {
if( *actualFramesPerBuffer == requestedFramesPerBuffer ) return ERR( err );
return paNoError; /* we are done */ }
// Did we get the size we asked for?
if( *actualFramesPerBuffer == requestedFramesPerBuffer )
{
return paNoError; /* we are done */
}
/* -- fetch available block sizes -- */ // Clip requested value against legal range for the device.
err = AudioDeviceGetPropertyInfo( device, 0, isInput, propsize = sizeof(AudioValueRange);
kAudioDevicePropertyBufferSizeRange, err = AudioDeviceGetProperty( device, 0, isInput,
&propsize, NULL ); kAudioDevicePropertyBufferFrameSizeRange,
if( err ) &propsize, &range );
if( err )
{
return ERR( err ); return ERR( err );
ranges = (AudioValueRange *)calloc( 1, propsize ); }
if( !ranges ) if( requestedFramesPerBuffer < range.mMinimum )
return paInsufficientMemory; {
err = AudioDeviceGetProperty( device, 0, isInput, requestedFramesPerBuffer = range.mMinimum;
kAudioDevicePropertyBufferSizeRange, }
&propsize, ranges ); else if( requestedFramesPerBuffer > range.mMaximum )
if( err ) {
{ requestedFramesPerBuffer = range.mMaximum;
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( 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;
}
}
if( best == -1 )
best = min;
VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) );
free( ranges );
/* --- set the buffer size (ignore errors) -- */ /* --- set the buffer size (ignore errors) -- */
requestedFramesPerBuffer = (UInt32) best ; propsize = sizeof( UInt32 );
propsize = sizeof( UInt32 );
err = AudioDeviceSetProperty( device, NULL, 0, isInput, err = AudioDeviceSetProperty( device, NULL, 0, isInput,
kAudioDevicePropertyBufferSize, kAudioDevicePropertyBufferFrameSize,
propsize, &requestedFramesPerBuffer ); propsize, &requestedFramesPerBuffer );
/* --- read the property to check that it was set -- */ /* --- read the property to check that it was set -- */
err = AudioDeviceGetProperty( device, 0, isInput, err = AudioDeviceGetProperty( device, 0, isInput,
kAudioDevicePropertyBufferSize, kAudioDevicePropertyBufferFrameSize,
&propsize, actualFramesPerBuffer ); &propsize, actualFramesPerBuffer );
if( err ) if( err )

View File

@ -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 * Portable Audio I/O Library DirectSound implementation
* *
* Authors: Phil Burk, Robert Marsanyi & Ross Bencina * Authors: Phil Burk, Robert Marsanyi & Ross Bencina
@ -41,10 +41,17 @@
@ingroup hostapi_src @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 <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> /* strlen() */ #include <string.h> /* strlen() */
#define _WIN32_WINNT 0x0400 /* required to get waitable timer APIs */
#include <initguid.h> /* make sure ds guids get defined */ #include <initguid.h> /* make sure ds guids get defined */
#include <windows.h> #include <windows.h>
#include <objbase.h> #include <objbase.h>
@ -61,6 +68,11 @@
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
#include <dsconf.h> #include <dsconf.h>
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ #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_util.h"
#include "pa_allocation.h" #include "pa_allocation.h"
@ -74,16 +86,44 @@
#include "pa_win_ds_dynlink.h" #include "pa_win_ds_dynlink.h"
#include "pa_win_waveformat.h" #include "pa_win_waveformat.h"
#include "pa_win_wdmks_utils.h" #include "pa_win_wdmks_utils.h"
#include "pa_win_coinitialize.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
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "dsound.lib" ) #pragma comment( lib, "dsound.lib" )
#pragma comment( lib, "winmm.lib" ) #pragma comment( lib, "winmm.lib" )
#pragma comment( lib, "kernel32.lib" )
#endif #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 provided in newer platform sdks and x64
*/ */
@ -190,7 +230,7 @@ typedef struct
/* implementation specific data goes here */ /* implementation specific data goes here */
char comWasInitialized; PaWinUtilComInitializationResult comInitializationResult;
} PaWinDsHostApiRepresentation; } PaWinDsHostApiRepresentation;
@ -231,7 +271,6 @@ typedef struct PaWinDsStream
UINT inputSize; UINT inputSize;
MMRESULT timerID;
int framesPerDSBuffer; int framesPerDSBuffer;
double framesWritten; double framesWritten;
double secondsPerHostByte; /* Used to optimize latency calculation for outTime */ double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
@ -247,6 +286,21 @@ typedef struct PaWinDsStream
volatile int isActive; volatile int isActive;
volatile int stopProcessing; /* stop thread once existing buffers have been returned */ volatile int stopProcessing; /* stop thread once existing buffers have been returned */
volatile int abortProcessing; /* stop thread immediately */ 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; } PaWinDsStream;
@ -1015,32 +1069,15 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
int i, deviceCount; int i, deviceCount;
PaWinDsHostApiRepresentation *winDsHostApi; PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs; DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
PaWinDsDeviceInfo *deviceInfoArray; PaWinDsDeviceInfo *deviceInfoArray;
char comWasInitialized = 0;
/* PaWinDs_InitializeDSoundEntryPoints();
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;
/* initialise guid vectors so they can be safely deleted on error */ /* initialise guid vectors so they can be safely deleted on error */
deviceNamesAndGUIDs.winDsHostApi = NULL; deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL; deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL; deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
PaWinDs_InitializeDSoundEntryPoints();
winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) ); winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
if( !winDsHostApi ) if( !winDsHostApi )
{ {
@ -1048,7 +1085,11 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
goto error; goto error;
} }
winDsHostApi->comWasInitialized = comWasInitialized; result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
if( result != paNoError )
{
goto error;
}
winDsHostApi->allocations = PaUtil_CreateAllocationGroup(); winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !winDsHostApi->allocations ) if( !winDsHostApi->allocations )
@ -1180,22 +1221,10 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
return result; return result;
error: error:
if( winDsHostApi )
{
if( winDsHostApi->allocations )
{
PaUtil_FreeAllAllocations( winDsHostApi->allocations );
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
}
PaUtil_FreeMemory( winDsHostApi );
}
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs ); TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs ); TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( comWasInitialized ) Terminate( winDsHostApi );
CoUninitialize();
return result; return result;
} }
@ -1205,25 +1234,20 @@ error:
static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{ {
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
char comWasInitialized = winDsHostApi->comWasInitialized;
/* if( winDsHostApi ){
IMPLEMENT ME: if( winDsHostApi->allocations )
- clean up any resources not handled by the allocation group {
*/ PaUtil_FreeAllAllocations( winDsHostApi->allocations );
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
}
if( winDsHostApi->allocations ) PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
{
PaUtil_FreeAllAllocations( winDsHostApi->allocations ); PaUtil_FreeMemory( winDsHostApi );
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
} }
PaUtil_FreeMemory( winDsHostApi );
PaWinDs_TerminateDSoundEntryPoints(); PaWinDs_TerminateDSoundEntryPoints();
if( comWasInitialized )
CoUninitialize();
} }
@ -1847,9 +1871,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
bufferProcessorIsInitialized = 1; bufferProcessorIsInitialized = 1;
stream->streamRepresentation.streamInfo.inputLatency = 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 = 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; stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
@ -1861,8 +1885,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
int minLatencyFrames; int minLatencyFrames;
unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5); unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
#ifdef PA_WIN_DS_USE_WMME_TIMER
stream->timerID = 0; stream->timerID = 0;
#endif
stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL ); stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
if( stream->processingCompleted == NULL ) if( stream->processingCompleted == NULL )
{ {
@ -1870,6 +1895,26 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error; 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. */ /* Get system minimum latency. */
minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate ); minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
@ -2061,6 +2106,16 @@ error:
if( stream->processingCompleted != NULL ) if( stream->processingCompleted != NULL )
CloseHandle( stream->processingCompleted ); 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 ) if( stream->pDirectSoundOutputBuffer )
{ {
IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer ); IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
@ -2264,7 +2319,6 @@ static int TimeSlice( PaWinDsStream *stream )
if( framesToXfer > 0 ) if( framesToXfer > 0 )
{ {
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
/* The outputBufferDacTime parameter should indicates the time at which /* 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 When CloseStream() is called, the multi-api layer ensures that
the stream has already been stopped or aborted. the stream has already been stopped or aborted.
@ -2480,6 +2603,15 @@ static PaError CloseStream( PaStream* s )
CloseHandle( stream->processingCompleted ); 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 // Cleanup the sound buffers
if( stream->pDirectSoundOutputBuffer ) if( stream->pDirectSoundOutputBuffer )
{ {
@ -2574,6 +2706,10 @@ static PaError StartStream( PaStream *s )
ResetEvent( stream->processingCompleted ); ResetEvent( stream->processingCompleted );
#ifndef PA_WIN_DS_USE_WMME_TIMER
ResetEvent( stream->processingThreadCompleted );
#endif
if( stream->bufferProcessor.inputChannelCount > 0 ) if( stream->bufferProcessor.inputChannelCount > 0 )
{ {
// Start the buffer capture // Start the buffer capture
@ -2596,7 +2732,6 @@ static PaError StartStream( PaStream *s )
stream->abortProcessing = 0; stream->abortProcessing = 0;
stream->stopProcessing = 0; stream->stopProcessing = 0;
stream->isActive = 1;
if( stream->bufferProcessor.outputChannelCount > 0 ) if( stream->bufferProcessor.outputChannelCount > 0 )
{ {
@ -2639,28 +2774,91 @@ static PaError StartStream( PaStream *s )
if( stream->streamRepresentation.streamCallback ) if( stream->streamRepresentation.streamCallback )
{ {
/* Create timer that will wake us up so we can fill the DSound buffer. */ TIMECAPS timecaps;
int resolution;
int timerResolution;
int framesPerWakeup = stream->framesPerDSBuffer / 4; int framesPerWakeup = stream->framesPerDSBuffer / 4;
int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate; int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
if( msecPerWakeup < 10 ) msecPerWakeup = 10; if( msecPerWakeup < 10 ) msecPerWakeup = 10;
else if( msecPerWakeup > 100 ) msecPerWakeup = 100; else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
resolution = msecPerWakeup/4; timerResolution = msecPerWakeup/4;
stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) TimerCallback,
/* 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 ); (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
if( stream->timerID == 0 ) if( stream->timerID == 0 )
{ {
stream->isActive = 0; stream->isActive = 0;
result = paUnanticipatedHostError; result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
goto error; goto error;
} }
#else
/* Create processing thread which calls TimerCallback */
stream->processingThread = CREATE_THREAD( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
if( !stream->processingThread )
{
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
goto error;
}
if( !SetThreadPriority( stream->processingThread, THREAD_PRIORITY_TIME_CRITICAL ) )
{
result = paUnanticipatedHostError;
PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
goto error;
}
#endif
} }
stream->isStarted = TRUE; stream->isActive = 1;
stream->isStarted = 1;
assert( result == paNoError );
return result;
error: 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; return result;
} }
@ -2683,12 +2881,30 @@ static PaError StopStream( PaStream *s )
WaitForSingleObject( stream->processingCompleted, timeoutMsec ); WaitForSingleObject( stream->processingCompleted, timeoutMsec );
} }
#ifdef PA_WIN_DS_USE_WMME_TIMER
if( stream->timerID != 0 ) if( stream->timerID != 0 )
{ {
timeKillEvent(stream->timerID); /* Stop callback timer. */ timeKillEvent(stream->timerID); /* Stop callback timer. */
stream->timerID = 0; 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 ) if( stream->bufferProcessor.outputChannelCount > 0 )
{ {
@ -2714,7 +2930,7 @@ static PaError StopStream( PaStream *s )
} }
} }
stream->isStarted = FALSE; stream->isStarted = 0;
return result; return result;
} }

View File

@ -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 * PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com * Latest Version at: http://www.portaudio.com
* JACK Implementation by Joshua Haberman * JACK Implementation by Joshua Haberman
@ -1296,11 +1296,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( stream->num_incoming_connections > 0 ) if( stream->num_incoming_connections > 0 )
stream->streamRepresentation.streamInfo.inputLatency = (jack_port_get_latency( stream->remote_output_ports[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 */ - 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 ) if( stream->num_outgoing_connections > 0 )
stream->streamRepresentation.streamInfo.outputLatency = (jack_port_get_latency( stream->remote_input_ports[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 */ - 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->streamRepresentation.streamInfo.sampleRate = jackSr;
stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */ stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */

View File

@ -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 * PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com * Latest Version at: http://www.portaudio.com
* OSS implementation by: * OSS implementation by:
@ -1241,13 +1241,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
{ {
inputHostFormat = stream->capture->hostFormat; inputHostFormat = stream->capture->hostFormat;
stream->streamRepresentation.streamInfo.inputLatency = inLatency + stream->streamRepresentation.streamInfo.inputLatency = inLatency +
PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate; PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) / sampleRate;
} }
if( outputParameters ) if( outputParameters )
{ {
outputHostFormat = stream->playback->hostFormat; outputHostFormat = stream->playback->hostFormat;
stream->streamRepresentation.streamInfo.outputLatency = outLatency + stream->streamRepresentation.streamInfo.outputLatency = outLatency +
PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate; PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) / sampleRate;
} }
/* Initialize buffer processor with fixed host buffer size. /* Initialize buffer processor with fixed host buffer size.

View File

@ -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 * Portable Audio I/O Library skeleton implementation
* demonstrates how to use the common functions to implement support * demonstrates how to use the common functions to implement support
* for a host API * for a host API
@ -518,9 +518,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
values. values.
*/ */
stream->streamRepresentation.streamInfo.inputLatency = 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 = 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; stream->streamRepresentation.streamInfo.sampleRate = sampleRate;

View File

@ -75,6 +75,8 @@
#include "pa_debugprint.h" #include "pa_debugprint.h"
#include "pa_ringbuffer.h" #include "pa_ringbuffer.h"
#include "pa_win_coinitialize.h"
#ifndef NTDDI_VERSION #ifndef NTDDI_VERSION
#undef WINVER #undef WINVER
@ -370,6 +372,8 @@ typedef struct
/* implementation specific data goes here */ /* implementation specific data goes here */
PaWinUtilComInitializationResult comInitializationResult;
//in case we later need the synch //in case we later need the synch
IMMDeviceEnumerator *enumerator; IMMDeviceEnumerator *enumerator;
@ -512,8 +516,6 @@ void *PaWasapi_ReallocateMemory(void *ptr, size_t size);
void PaWasapi_FreeMemory(void *ptr); void PaWasapi_FreeMemory(void *ptr);
// Local statics // Local statics
static volatile BOOL g_WasapiCOMInit = FALSE;
static volatile DWORD g_WasapiInitThread = 0;
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
#define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__) #define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)
@ -1057,26 +1059,6 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
return paNoError; 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) ); paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );
if (paWasapi == NULL) if (paWasapi == NULL)
{ {
@ -1084,6 +1066,12 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error; goto error;
} }
result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult );
if( result != paNoError )
{
goto error;
}
paWasapi->allocations = PaUtil_CreateAllocationGroup(); paWasapi->allocations = PaUtil_CreateAllocationGroup();
if (paWasapi->allocations == NULL) if (paWasapi->allocations == NULL)
{ {
@ -1454,26 +1442,12 @@ static void Terminate( PaUtilHostApiRepresentation *hostApi )
PaUtil_DestroyAllocationGroup(paWasapi->allocations); PaUtil_DestroyAllocationGroup(paWasapi->allocations);
} }
PaWinUtil_CoUninitialize( paWASAPI, &paWasapi->comInitializationResult );
PaUtil_FreeMemory(paWasapi); PaUtil_FreeMemory(paWasapi);
// Close AVRT // Close AVRT
CloseAVRT(); 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 // Set Input latency
stream->streamRepresentation.streamInfo.inputLatency = stream->streamRepresentation.streamInfo.inputLatency =
((double)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate) ((double)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate)
+ ((inputParameters)?stream->in.latencySeconds : 0); + ((inputParameters)?stream->in.latencySeconds : 0);
// Set Output latency // Set Output latency
stream->streamRepresentation.streamInfo.outputLatency = stream->streamRepresentation.streamInfo.outputLatency =
((double)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate) ((double)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate)
+ ((outputParameters)?stream->out.latencySeconds : 0); + ((outputParameters)?stream->out.latencySeconds : 0);
// Set SR // Set SR

View File

@ -125,13 +125,13 @@
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
#define NOMMIDS //#define NOMMIDS
#define DYNAMIC_GUID(data) {data} #define DYNAMIC_GUID(data) {data}
#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */ //#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
#undef DEFINE_GUID //#undef DEFINE_GUID
#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data} //#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data) //#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n) //#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
#endif #endif
#include <mmreg.h> #include <mmreg.h>

View File

@ -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 * pa_win_wmme.c
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME) * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
* *
@ -154,21 +154,21 @@
#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */ #define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
#if PA_MME_USE_HIGH_DEFAULT_LATENCY_ #if PA_MME_USE_HIGH_DEFAULT_LATENCY_
#define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4) #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
#define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4) #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_FULL_DUPLEX_ (4)
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_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_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 */ #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 #else
#define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2) #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
#define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (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_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_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 */ #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 #endif
/* Use higher latency for NT because it is even worse at real-time /* Use higher latency for NT because it is even worse at real-time
@ -177,6 +177,13 @@
#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2) #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_) #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) #define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
@ -791,7 +798,9 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
MMRESULT mmresult; MMRESULT mmresult;
WAVEOUTCAPS woc; WAVEOUTCAPS woc;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
int wdmksDeviceOutputChannelCountIsKnown; int wdmksDeviceOutputChannelCountIsKnown;
#endif
*success = 0; *success = 0;
@ -1324,126 +1333,135 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
} }
static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames(
static void SelectBufferSizeAndCount( unsigned long baseBufferSize, unsigned long suggestedLatencyFrames,
unsigned long requestedLatency, unsigned long hostBufferSizeFrames,
unsigned long baseBufferCount, unsigned long minimumBufferCount, unsigned long minimumBufferCount )
unsigned long maximumBufferSize, unsigned long *hostBufferSize,
unsigned long *hostBufferCount )
{ {
unsigned long sizeMultiplier, bufferCount, latency; /* Calculate the number of buffers of length hostFramesPerBuffer
unsigned long nextLatency, nextBufferSize; that fit in suggestedLatencyFrames, rounding up to the next integer.
int baseBufferSizeIsPowerOfTwo;
sizeMultiplier = 1; The exact formula for round-up is:
bufferCount = baseBufferCount; ((suggestedLatencyFrames + (hostFramesPerBuffer - 1)) / hostFramesPerBuffer)
However we subtract 2 instead of 1 below to account for rounding errors.
/* count-1 below because latency is always determined by one less This ensures we don't erroneously overallocate an extra buffer due to a one-sample rounding error.
than the total number of buffers.
*/ */
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); if( resultBufferCount < minimumBufferCount ) /* clamp to minimum buffer count */
while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) resultBufferCount = minimumBufferCount;
{
--bufferCount;
nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
}
}else if( latency < requestedLatency ){ return resultBufferCount;
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;
} }
static void ReselectBufferCount( unsigned long bufferSize, static PaError SelectHostBufferSizeFramesAndHostBufferCount(
unsigned long requestedLatency, unsigned long suggestedLatencyFrames,
unsigned long baseBufferCount, unsigned long minimumBufferCount, unsigned long userFramesPerBuffer,
unsigned long *hostBufferCount ) 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; if( userFramesPerBuffer == paFramesPerBufferUnspecified ){
unsigned long nextLatency;
bufferCount = baseBufferCount; *hostBufferSizeFrames = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_;
/* count-1 below because latency is always determined by one less }else{
than the total number of buffers.
*/
latency = bufferSize * (bufferCount-1);
if( latency > requestedLatency ) *hostBufferSizeFrames = userFramesPerBuffer;
{
/* reduce number of buffers without falling below suggested latency */
nextLatency = bufferSize * (bufferCount-2); if( *hostBufferSizeFrames > absoluteMaximumBufferSizeFrames ){
while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) /* user has requested a user buffer that's larger than what we can handle */
{
--bufferCount;
nextLatency = bufferSize * (bufferCount-2);
}
}else if( latency < requestedLatency ){ /* @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
/* increase number of buffers until requestedLatency is reached */ can cause crashes with some drivers. */
/* return paBufferTooBig; */
latency = bufferSize * (bufferCount-1);
while( latency < requestedLatency )
{
++bufferCount;
latency = bufferSize * (bufferCount-1);
} }
} }
*hostBufferCount = bufferCount; /* 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.
*/
unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* min of our limits */
? absoluteMaximumBufferSizeFrames
: preferredMaximumBufferSizeFrames;
unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / *hostBufferSizeFrames; /* don't coalesce more than this */
/* 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;
if( numUserBuffersPerHostBuffer > 1 ){
*hostBufferSizeFrames *= numUserBuffersPerHostBuffer;
/* recompute hostBufferCount to approximate suggestedLatencyFrames now that hostBufferSizeFrames is larger */
*hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount );
}
}
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( static PaError CalculateBufferSettings(
unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount, unsigned long *hostFramesPerInputBuffer, unsigned long *hostInputBufferCount,
unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount, unsigned long *hostFramesPerOutputBuffer, unsigned long *hostOutputBufferCount,
int inputChannelCount, PaSampleFormat hostInputSampleFormat, int inputChannelCount, PaSampleFormat hostInputSampleFormat,
PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo, PaTime suggestedInputLatency, const PaWinMmeStreamInfo *inputStreamInfo,
int outputChannelCount, PaSampleFormat hostOutputSampleFormat, int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo, PaTime suggestedOutputLatency, const PaWinMmeStreamInfo *outputStreamInfo,
double sampleRate, unsigned long framesPerBuffer ) double sampleRate, unsigned long userFramesPerBuffer )
{ {
PaError result = paNoError; 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 ); int hostInputFrameSizeBytes;
if( hostInputSampleSize < 0 ) result = CalculateMaxHostSampleFrameSizeBytes(
{ inputChannelCount, hostInputSampleFormat, inputStreamInfo, &hostInputFrameSizeBytes );
result = hostInputSampleSize; if( result != paNoError )
goto error; 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 if( inputStreamInfo
&& ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
{ {
/* input - using low level latency parameters if provided */
if( inputStreamInfo->bufferCount <= 0 if( inputStreamInfo->bufferCount <= 0
|| inputStreamInfo->framesPerBuffer <= 0 ) || inputStreamInfo->framesPerBuffer <= 0 )
{ {
@ -1505,46 +1501,45 @@ static PaError CalculateBufferSettings(
goto error; goto error;
} }
*framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer; *hostFramesPerInputBuffer = inputStreamInfo->framesPerBuffer;
*hostInputBufferCount = inputStreamInfo->bufferCount; *hostInputBufferCount = inputStreamInfo->bufferCount;
} }
else 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) unsigned long minimumBufferCount = (outputChannelCount > 0)
? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
: PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_; : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize); result = SelectHostBufferSizeFramesAndHostBufferCount(
if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) (unsigned long)(suggestedInputLatency * sampleRate),
maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; userFramesPerBuffer,
minimumBufferCount,
/* compute the following in bytes, then convert back to frames */ (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
SelectBufferSizeAndCount( division is intentional here to limit max bytes */
((framesPerBuffer == paFramesPerBufferUnspecified) hostFramesPerInputBuffer,
? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ hostInputBufferCount );
: framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */ if( result != paNoError )
((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ goto error;
4, /* baseBufferCount */
minimumBufferCount, maximumBufferSize,
&hostBufferSizeBytes, &hostBufferCount );
*framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
*hostInputBufferCount = hostBufferCount;
} }
} }
else else
{ {
*framesPerHostInputBuffer = 0; *hostFramesPerInputBuffer = 0;
*hostInputBufferCount = 0; *hostInputBufferCount = 0;
} }
if( outputChannelCount > 0 ) if( outputChannelCount > 0 ) /* stream has output */
{ {
if( outputStreamInfo if( outputStreamInfo
&& ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
{ {
/* output - using low level latency parameters */
if( outputStreamInfo->bufferCount <= 0 if( outputStreamInfo->bufferCount <= 0
|| outputStreamInfo->framesPerBuffer <= 0 ) || outputStreamInfo->framesPerBuffer <= 0 )
{ {
@ -1552,24 +1547,25 @@ static PaError CalculateBufferSettings(
goto error; goto error;
} }
*framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer; *hostFramesPerOutputBuffer = outputStreamInfo->framesPerBuffer;
*hostOutputBufferCount = outputStreamInfo->bufferCount; *hostOutputBufferCount = outputStreamInfo->bufferCount;
if( inputChannelCount > 0 ) /* full duplex */ if( inputChannelCount > 0 ) /* full duplex */
{ {
if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer ) /* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */
if( *hostFramesPerInputBuffer != *hostFramesPerOutputBuffer )
{ {
if( inputStreamInfo if( inputStreamInfo
&& ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
{ {
/* a custom StreamInfo was used for specifying both input /* 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 */ 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; result = paIncompatibleHostApiSpecificStreamInfo;
goto error; goto error;
@ -1577,8 +1573,8 @@ static PaError CalculateBufferSettings(
} }
else else
{ {
assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer ); assert( *hostFramesPerInputBuffer > *hostFramesPerOutputBuffer );
if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 ) if( *hostFramesPerInputBuffer % *hostFramesPerOutputBuffer != 0 )
{ {
result = paIncompatibleHostApiSpecificStreamInfo; result = paIncompatibleHostApiSpecificStreamInfo;
goto error; goto error;
@ -1588,111 +1584,72 @@ static PaError CalculateBufferSettings(
else else
{ {
/* a custom StreamInfo was not used for specifying the input buffer size, /* 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; *hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
*hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ) *hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
*hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; (unsigned long)(suggestedInputLatency * sampleRate),
*hostFramesPerInputBuffer,
PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
} }
} }
} }
} }
else else
{ {
unsigned long hostBufferSizeBytes, hostBufferCount; /* output - no low level latency parameters, so compute hostFramesPerOutputBuffer and hostOutputBufferCount
unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; based on userFramesPerBuffer and suggestedOutputLatency. */
unsigned long maximumBufferSize;
int hostOutputFrameSize;
int hostOutputSampleSize;
hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat ); int hostOutputFrameSizeBytes;
if( hostOutputSampleSize < 0 ) result = CalculateMaxHostSampleFrameSizeBytes(
{ outputChannelCount, hostOutputSampleFormat, outputStreamInfo, &hostOutputFrameSizeBytes );
result = hostOutputSampleSize; if( result != paNoError )
goto error; goto error;
}
if( outputStreamInfo /* compute the output buffer size and count */
&& ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
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 /* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */
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;
}
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. /* ensure that both input and output buffer sizes are the same.
if they don't match at this stage, choose the smallest one 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_; *hostOutputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
ReselectBufferCount( (unsigned long)(suggestedOutputLatency * sampleRate),
framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */ *hostOutputBufferCount,
((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ );
4, /* baseBufferCount */
minimumBufferCount,
&hostBufferCount );
*framesPerHostOutputBuffer = framesPerHostBuffer;
*hostOutputBufferCount = hostBufferCount;
} }
else else
{ {
unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer; *hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; *hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
ReselectBufferCount( (unsigned long)(suggestedInputLatency * sampleRate),
framesPerHostBuffer * hostInputFrameSize, /* bufferSize */ *hostFramesPerInputBuffer,
((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
4, /* baseBufferCount */
minimumBufferCount,
&hostBufferCount );
*framesPerHostInputBuffer = framesPerHostBuffer;
*hostInputBufferCount = hostBufferCount;
} }
} }
} }
@ -1700,7 +1657,7 @@ static PaError CalculateBufferSettings(
} }
else else
{ {
*framesPerHostOutputBuffer = 0; *hostFramesPerOutputBuffer = 0;
*hostOutputBufferCount = 0; *hostOutputBufferCount = 0;
} }
@ -2476,12 +2433,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
bufferProcessorIsInitialized = 1; bufferProcessorIsInitialized = 1;
/* stream info input latency is the minimum buffering latency (unlike suggested and default which are *maximums*) */
stream->streamRepresentation.streamInfo.inputLatency = stream->streamRepresentation.streamInfo.inputLatency =
(double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
+(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate; + framesPerHostInputBuffer) / sampleRate;
stream->streamRepresentation.streamInfo.outputLatency = stream->streamRepresentation.streamInfo.outputLatency =
(double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
+(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; + (framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
stream->streamRepresentation.streamInfo.sampleRate = sampleRate; stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0; stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;

View File

@ -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;
}
}
}

View File

@ -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 */

View File

@ -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 * Portable Audio I/O Library Windows initialization table
* *
* Based on the Open Source API proposed by Ross Bencina * Based on the Open Source API proposed by Ross Bencina
@ -42,8 +42,16 @@
@brief Win32 host API initialization function table. @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" #include "pa_hostapi.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {