merge in newer portaudio

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@8835 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Anthony Minessale 2008-06-24 22:15:50 +00:00
parent 50e9411c82
commit 625c524db7
62 changed files with 23721 additions and 8516 deletions

View File

@ -16,7 +16,7 @@ bindir = @bindir@
libdir = @libdir@
includedir = @includedir@
CC = @CC@
CFLAGS = @CFLAGS@ -I$(top_srcdir)/include -I$(top_srcdir)/src/common -I$(top_srcdir)/src/os/unix @DEFS@
CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src/common -I$(top_srcdir)/src/os/unix @CFLAGS@ @DEFS@
LIBS = @LIBS@
AR = @AR@
RANLIB = @RANLIB@
@ -56,62 +56,62 @@ COMMON_OBJS = \
src/common/pa_trace.o
TESTS = \
test/paqa_devs \
test/paqa_errs \
test/patest1 \
test/patest_buffer \
test/patest_callbackstop \
test/patest_clip \
test/patest_dither \
test/patest_hang \
test/patest_in_overflow \
test/patest_latency \
test/patest_leftright \
test/patest_longsine \
test/patest_many \
test/patest_maxsines \
test/patest_multi_sine \
test/patest_out_underflow \
test/patest_pink \
test/patest_prime \
test/patest_read_record \
test/patest_read_write_wire \
test/patest_record \
test/patest_ringmix \
test/patest_saw \
test/patest_sine8 \
test/patest_sine \
test/patest_sine_channelmaps \
test/patest_sine_formats \
test/patest_sine_time \
test/patest_start_stop \
test/patest_stop \
test/patest_stop_playout \
test/patest_toomanysines \
test/patest_underflow \
test/patest_wire \
test/patest_write_sine \
test/pa_devs \
test/pa_fuzz \
test/pa_minlat
bin/paqa_devs \
bin/paqa_errs \
bin/patest1 \
bin/patest_buffer \
bin/patest_callbackstop \
bin/patest_clip \
bin/patest_dither \
bin/patest_hang \
bin/patest_in_overflow \
bin/patest_latency \
bin/patest_leftright \
bin/patest_longsine \
bin/patest_many \
bin/patest_maxsines \
bin/patest_multi_sine \
bin/patest_out_underflow \
bin/patest_pink \
bin/patest_prime \
bin/patest_read_record \
bin/patest_read_write_wire \
bin/patest_record \
bin/patest_ringmix \
bin/patest_saw \
bin/patest_sine8 \
bin/patest_sine \
bin/patest_sine_channelmaps \
bin/patest_sine_formats \
bin/patest_sine_time \
bin/patest_start_stop \
bin/patest_stop \
bin/patest_stop_playout \
bin/patest_toomanysines \
bin/patest_underflow \
bin/patest_wire \
bin/patest_write_sine \
bin/pa_devs \
bin/pa_fuzz \
bin/pa_minlat
# Most of these don't compile yet. Put them in TESTS, above, if
# you want to try to compile them...
ALL_TESTS = \
$(TESTS) \
test/patest_sync \
test/debug_convert \
test/debug_dither_calc \
test/debug_dual \
test/debug_multi_in \
test/debug_multi_out \
test/debug_record \
test/debug_record_reuse \
test/debug_sine_amp \
test/debug_sine \
test/debug_sine_formats \
test/debug_srate \
test/debug_test1
bin/patest_sync \
bin/debug_convert \
bin/debug_dither_calc \
bin/debug_dual \
bin/debug_multi_in \
bin/debug_multi_out \
bin/debug_record \
bin/debug_record_reuse \
bin/debug_sine_amp \
bin/debug_sine \
bin/debug_sine_formats \
bin/debug_srate \
bin/debug_test1
OBJS = $(COMMON_OBJS) $(OTHER_OBJS)
@ -120,7 +120,7 @@ LTOBJS:= $(OBJS:.o=.lo)
SUBDIRS =
@ENABLE_CXX_TRUE@SUBDIRS += bindings/cpp
all: lib/$(PALIB) all-recursive
all: lib/$(PALIB) all-recursive tests
tests: bin-stamp $(TESTS)
@ -128,8 +128,9 @@ tests: bin-stamp $(TESTS)
lib/$(PALIB): lib-stamp $(LTOBJS) $(MAKEFILE) $(PAINC)
$(LIBTOOL) --mode=link $(CC) $(PA_LDFLAGS) -o lib/$(PALIB) $(LTOBJS) $(DLL_LIBS)
$(ALL_TESTS): lib/$(PALIB) $(MAKEFILE) $(PAINC)
$(LIBTOOL) --mode=link $(CC) -o $@ $(CFLAGS) $(top_srcdir)/$@.c lib/$(PALIB) $(LIBS)
$(ALL_TESTS): bin/%: lib/$(PALIB) $(MAKEFILE) $(PAINC) test/%.c
$(LIBTOOL) --mode=link $(CC) -o $@ $(CFLAGS) $(top_srcdir)/test/$*.c lib/$(PALIB) $(LIBS)
install: lib/$(PALIB) portaudio-2.0.pc
$(INSTALL) -d $(DESTDIR)$(libdir)
@ -163,19 +164,16 @@ clean:
distclean: clean
rm -f config.log config.status Makefile libtool portaudio-2.0.pc
.SUFFIXES:
.SUFFIXES: .c .lo .o .cpp
.c.o: $(MAKEFILE) $(PAINC)
%.o: %.c $(MAKEFILE) $(PAINC)
$(CC) -c $(CFLAGS) $< -o $@
.c.lo: $(MAKEFILE) $(PAINC)
%.lo: %.c $(MAKEFILE) $(PAINC)
$(LIBTOOL) --mode=compile $(CC) -c $(CFLAGS) $< -o $@
.cpp.o: $(MAKEFILE) $(PAINC)
%.o: %.cpp $(MAKEFILE) $(PAINC)
$(CXX) -c $(CXXFLAGS) $< -o $@
.asm.o:
%.o: %.asm
$(NASM) $(NASMOPT) -o $@ $<
bin-stamp:
@ -184,9 +182,21 @@ bin-stamp:
lib-stamp:
-mkdir lib
-mkdir -p src/os/win src/os/unix src/os/mac_osx src/common \
src/hostapi/oss src/hostapi/alsa src/hostapi/jack src/hostapi/asihpi \
src/hostapi/wmme src/hostapi/wdmks src/hostapi/dsound src/hostapi/wasapi
-mkdir -p \
src/common \
src/hostapi/alsa \
src/hostapi/asihpi \
src/hostapi/asio \
src/hostapi/coreaudio \
src/hostapi/dsound \
src/hostapi/jack \
src/hostapi/oss \
src/hostapi/wasapi \
src/hostapi/wdmks \
src/hostapi/wmme \
src/os/mac_osx \
src/os/unix \
src/os/win
touch $@
Makefile: Makefile.in config.status

View File

@ -64,7 +64,7 @@ env.Append(CPPPATH=[os.path.join("#", "include"), "common"])
env.SConsignFile(os.path.join(sconsDir, ".sconsign"))
env.SConscriptChdir(False)
sources, sharedLib, staticLib, tests, portEnv = env.SConscript(os.path.join("src", "SConscript"),
sources, sharedLib, staticLib, tests, portEnv, hostApis = env.SConscript(os.path.join("src", "SConscript"),
build_dir=buildDir, duplicate=False, exports=["env"])
if Platform in Posix:
@ -133,6 +133,7 @@ if env["enableStatic"]:
env.Install(includeDir, os.path.join("include", "portaudio.h"))
if env["enableCxx"]:
env.SConscriptChdir(True)
cxxEnv = env.Copy()
@ -145,3 +146,49 @@ if env["enableCxx"]:
env.Default(sharedLibs)
env.Install(libDir, sharedLibs)
env.Install(os.path.join(includeDir, "portaudiocpp"), headers)
# Generate portaudio_config.h header with compile-time definitions of which PA
# back-ends are available, and which includes back-end extension headers
# Host-specific headers
hostApiHeaders = {"ALSA": "pa_linux_alsa.h",
"ASIO": "pa_asio.h",
"COREAUDIO": "pa_mac_core.h",
"JACK": "pa_jack.h",
"WMME": "pa_winwmme.h",
}
def buildConfigH(target, source, env):
"""builder for portaudio_config.h"""
global hostApiHeaders, hostApis
out = ""
for hostApi in hostApis:
out += "#define PA_HAVE_%s\n" % hostApi
hostApiSpecificHeader = hostApiHeaders.get(hostApi, None)
if hostApiSpecificHeader:
out += "#include \"%s\"\n" % hostApiSpecificHeader
out += "\n"
# Strip the last newline
if out[-1] == "\n":
out = out[:-1]
f = file(str(target[0]), 'w')
try: f.write(out)
finally: f.close()
return 0
# Define the builder for the config header
env.Append(BUILDERS={"portaudioConfig": env.Builder(action=Action(buildConfigH,
"generating '$TARGET'"), target_factory=env.fs.File,)})
confH = env.portaudioConfig(File("portaudio_config.h", "include"),
File("portaudio.h", "include"))
env.Default(confH)
env.Install(os.path.join(includeDir, "portaudio"), confH)
for api in hostApis:
if api in hostApiHeaders:
env.Install(os.path.join(includeDir, "portaudio"),
File(hostApiHeaders[api], "include"))

6627
libs/portaudio/aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

7336
libs/portaudio/bindings/cpp/aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not)
dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page
dnl also defines GSTUFF_PKG_ERRORS on error
AC_DEFUN(PKG_CHECK_MODULES, [
succeeded=no
if test -z "$PKG_CONFIG"; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
fi
if test "$PKG_CONFIG" = "no" ; then
echo "*** The pkg-config script could not be found. Make sure it is"
echo "*** in your path, or set the PKG_CONFIG environment variable"
echo "*** to the full path to pkg-config."
echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
else
PKG_CONFIG_MIN_VERSION=0.9.0
if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
AC_MSG_CHECKING(for $2)
if $PKG_CONFIG --exists "$2" ; then
AC_MSG_RESULT(yes)
succeeded=yes
AC_MSG_CHECKING($1_CFLAGS)
$1_CFLAGS=`$PKG_CONFIG --cflags "$2"`
AC_MSG_RESULT($$1_CFLAGS)
AC_MSG_CHECKING($1_LIBS)
$1_LIBS=`$PKG_CONFIG --libs "$2"`
AC_MSG_RESULT($$1_LIBS)
else
$1_CFLAGS=""
$1_LIBS=""
## If we have a custom action on failure, don't print errors, but
## do set a variable so people can do so.
$1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
ifelse([$4], ,echo $$1_PKG_ERRORS,)
fi
AC_SUBST($1_CFLAGS)
AC_SUBST($1_LIBS)
else
echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
echo "*** See http://www.freedesktop.org/software/pkgconfig"
fi
fi
if test $succeeded = yes; then
ifelse([$3], , :, [$3])
else
ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4])
fi
])

File diff suppressed because it is too large Load Diff

View File

@ -35,9 +35,9 @@ Pa_GetStreamReadAvailable @31
Pa_GetStreamWriteAvailable @32
Pa_GetSampleSize @33
Pa_Sleep @34
;PaAsio_GetAvailableLatencyValues @50
;PaAsio_ShowControlPanel @51
PaAsio_GetAvailableLatencyValues @50
PaAsio_ShowControlPanel @51
PaUtil_InitializeX86PlainConverters @52
;PaAsio_GetInputChannelName @53
;PaAsio_GetOutputChannelName @54
PaAsio_GetInputChannelName @53
PaAsio_GetOutputChannelName @54
PaUtil_SetDebugPrintFunction @55

View File

@ -43,7 +43,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\src\common" /I "..\..\include" /I ".\\" /D "WIN32" /D "NDEBUG" /D "_USRDLL" /D "PA_ENABLE_DEBUG_OUTPUT" /D "_CRT_SECURE_NO_DEPRECATE" /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\src\common" /I "..\..\include" /I ".\\" /I "..\..\src\os\win" /D "WIN32" /D "NDEBUG" /D "_USRDLL" /D "PA_ENABLE_DEBUG_OUTPUT" /D "_CRT_SECURE_NO_DEPRECATE" /D "PAWIN_USE_WDMKS_DEVICE_INFO" /FD /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
@ -54,7 +54,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"./Release_x86/portaudio_x86.dll"
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib setupapi.lib /nologo /dll /machine:I386 /out:"./Release_x86/portaudio_x86.dll"
!ELSEIF "$(CFG)" == "portaudio - Win32 Debug"
@ -70,7 +70,7 @@ LINK32=link.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\src\common" /I "..\..\include" /I ".\\" /D "WIN32" /D "_DEBUG" /D "_USRDLL" /D "PA_ENABLE_DEBUG_OUTPUT" /D "_CRT_SECURE_NO_DEPRECATE" /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\src\common" /I "..\..\include" /I ".\\" /I "..\..\src\os\win" /D "WIN32" /D "_DEBUG" /D "_USRDLL" /D "PA_ENABLE_DEBUG_OUTPUT" /D "_CRT_SECURE_NO_DEPRECATE" /D "PAWIN_USE_WDMKS_DEVICE_INFO" /FD /GZ /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
@ -81,7 +81,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"./Debug_x86/portaudio_x86.dll" /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib setupapi.lib /nologo /dll /debug /machine:I386 /out:"./Debug_x86/portaudio_x86.dll" /pdbtype:sept
!ENDIF
@ -211,6 +211,14 @@ SOURCE=..\..\src\hostapi\wmme\pa_win_wmme.c
SOURCE=..\..\src\hostapi\wasapi\pa_win_wasapi.cpp
# End Source File
# End Group
# Begin Group "wdm-ks"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\src\hostapi\wdmks\pa_win_wdmks.c
# End Source File
# End Group
# End Group
# Begin Group "os"
@ -228,6 +236,14 @@ SOURCE=..\..\src\os\win\pa_win_util.c
# End Source File
# Begin Source File
SOURCE=..\..\src\os\win\pa_win_waveformat.c
# End Source File
# Begin Source File
SOURCE=..\..\src\os\win\pa_win_wdmks_utils.c
# End Source File
# Begin Source File
SOURCE=..\..\src\os\win\pa_x86_plain_converters.c
# End Source File
# End Group

View File

@ -5,28 +5,16 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "portaudio", "portaudio.vcpr
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug DirectSound|Win32 = Debug DirectSound|Win32
Debug DirectSound|x64 = Debug DirectSound|x64
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release DirectSound|Win32 = Release DirectSound|Win32
Release DirectSound|x64 = Release DirectSound|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug DirectSound|Win32.ActiveCfg = Debug DirectSound|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug DirectSound|Win32.Build.0 = Debug DirectSound|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug DirectSound|x64.ActiveCfg = Debug DirectSound|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug DirectSound|x64.Build.0 = Debug DirectSound|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.ActiveCfg = Debug|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.Build.0 = Debug|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|x64.ActiveCfg = Debug|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|x64.Build.0 = Debug|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release DirectSound|Win32.ActiveCfg = Release DirectSound|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release DirectSound|Win32.Build.0 = Release DirectSound|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release DirectSound|x64.ActiveCfg = Release DirectSound|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release DirectSound|x64.Build.0 = Release DirectSound|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release|Win32.ActiveCfg = Release|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release|Win32.Build.0 = Release|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release|x64.ActiveCfg = Release|x64

File diff suppressed because it is too large Load Diff

View File

@ -15,11 +15,41 @@ This DLL contains all 3 current win32 PA APIS (MM/DS/ASIO)
You dont need "driver"
2)
*If you have Visual Studio 6.0*, please make sure you have it updated with the latest (and final)
microsoft libraries for it, namely:
Service pack 5:
Latest known URL:
http://msdn2.microsoft.com/en-us/vstudio/aa718363.aspx
Yes there EXISTS a service pack 6 , but the processor pack (below) isnt compatible with it.
Processor Pack(only works with above SP5)
Latest known URL:
http://msdn2.microsoft.com/en-us/vstudio/Aa718349.aspx
This isnt absolutely required for portaudio, but if you plan on using SSE intrinsics and similar things.
Up to you to decide upon Service pack 5 or 6 depending on your need for intrinsics.
2)If you have Visual Studio 6.0, 7.0(VC.NET/2001) or 7.1(VC.2003)
Platform SDK (Feb 2003) :
Latest known URL:
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm
(This will allow your code base to be x64 friendly, with correct defines
for LONG_PTR and such)
NOTE A) Yes you have to use IE activex scripts to install that - wont work in Firefox, you
may have to temporarily change tyour default browser(aint life unfair)
NOTE B) Dont forget to hit "Register PSDK Directories with Visual Studio".
you can make sure its right in VC6 if you open tools/options/directories/include files and you see SDK 2003 as the FIRST entry
(it must be the same for libs)
DirectX 9.0 SDK Update - (Summer 2003)
Latest known URL:
http://www.microsoft.com/downloads/details.aspx?familyid=9216652f-51e0-402e-b7b5-feb68d00f298&displaylang=en
Again register the links in VC6, and check inside vc6 if headers are in second place right after SDk 2003
*If you have 7.0(VC.NET/2001) or 7.1(VC.2003) *
then I suggest you open portaudio.dsp (and convert if needed)
If you have Visual Studio 2005, I suggest you open the portaudio.sln file
*If you have Visual Studio 2005*, I suggest you open the portaudio.sln file
which contains 4 configurations. Win32/x64 in both Release and Debug variants
hit compile and hope for the best.

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
#! /bin/sh
# Configuration validation subroutine script.
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
timestamp='2005-12-11'
timestamp='2003-02-22'
# This file is (in principle) common to ALL GNU software.
# The presence of a machine in this file suggests that SOME GNU software
@ -21,15 +21,14 @@ timestamp='2005-12-11'
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Please send patches to <config-patches@gnu.org>. Submit a context
# diff and a properly formatted ChangeLog entry.
#
@ -71,7 +70,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
@ -84,11 +83,11 @@ Try \`$me --help' for more information."
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit ;;
echo "$timestamp" ; exit 0 ;;
--version | -v )
echo "$version" ; exit ;;
echo "$version" ; exit 0 ;;
--help | --h* | -h )
echo "$usage"; exit ;;
echo "$usage"; exit 0 ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
@ -100,7 +99,7 @@ while test $# -gt 0 ; do
*local*)
# First pass through any local machine types.
echo $1
exit ;;
exit 0;;
* )
break ;;
@ -119,9 +118,7 @@ esac
# Here we must recognize all the valid KERNEL-OS combinations.
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
@ -147,7 +144,7 @@ case $os in
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-apple | -axis | -knuth | -cray)
-apple | -axis)
os=
basic_machine=$1
;;
@ -172,10 +169,6 @@ case $os in
-hiux*)
os=-hiuxwe2
;;
-sco6)
os=-sco5v6
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco5)
os=-sco3.2v5
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
@ -192,10 +185,6 @@ case $os in
# Don't forget version if it is 3.2v4 or newer.
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco5v6*)
# Don't forget version if it is 3.2v4 or newer.
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco*)
os=-sco3.2v2
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
@ -239,16 +228,14 @@ case $basic_machine in
| a29k \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| am33_2.0 \
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
| bfin \
| c4x | clipper \
| clipper \
| d10v | d30v | dlx | dsp16xx \
| fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
| m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
| ip2k \
| m32r | m68000 | m68k | m88k | mcore \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
@ -257,37 +244,30 @@ case $basic_machine in
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
| mt \
| msp430 \
| ns16k | ns32k \
| or32 \
| openrisc | or32 \
| pdp10 | pdp11 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
| pyramid \
| sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
| sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
| sh64 | sh64le \
| sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \
| sparcv8 | sparcv9 | sparcv9b \
| sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
| strongarm \
| tahoe | thumb | tic4x | tic80 | tron \
| tahoe | thumb | tic80 | tron \
| v850 | v850e \
| we32k \
| x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
| x86 | xscale | xstormy16 | xtensa \
| z8k)
basic_machine=$basic_machine-unknown
;;
m32c)
basic_machine=$basic_machine-unknown
;;
m6811 | m68hc11 | m6812 | m68hc12)
# Motorola 68HC11/12.
basic_machine=$basic_machine-unknown
@ -295,9 +275,6 @@ case $basic_machine in
;;
m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
;;
ms1)
basic_machine=mt-unknown
;;
# We use `pc' rather than `unknown'
# because (1) that's what they normally are, and
@ -318,19 +295,19 @@ case $basic_machine in
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
| avr-* \
| bfin-* | bs2000-* \
| bs2000-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
| clipper-* | craynv-* | cydra-* \
| clipper-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
| elxsi-* \
| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
| h8300-* | h8500-* \
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
| m32r-* | m32rle-* \
| ip2k-* \
| m32r-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
| m88110-* | m88k-* | maxq-* | mcore-* \
| m88110-* | m88k-* | mcore-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
@ -339,40 +316,33 @@ case $basic_machine in
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
| mips64vr5900-* | mips64vr5900el-* \
| mipsisa32-* | mipsisa32el-* \
| mipsisa32r2-* | mipsisa32r2el-* \
| mipsisa64-* | mipsisa64el-* \
| mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| mt-* \
| msp430-* \
| none-* | np1-* | ns16k-* | ns32k-* \
| none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
| pyramid-* \
| romp-* | rs6000-* \
| sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \
| sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
| sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \
| sparclite-* \
| sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
| sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
| sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
| tahoe-* | thumb-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
| tron-* \
| v850-* | v850e-* | vax-* \
| we32k-* \
| x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
| xstormy16-* | xtensa-* \
| x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
| xtensa-* \
| ymp-* \
| z8k-*)
;;
m32c-*)
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
386bsd)
@ -389,9 +359,6 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
abacus)
basic_machine=abacus-unknown
;;
adobe68k)
basic_machine=m68010-adobe
os=-scout
@ -406,12 +373,6 @@ case $basic_machine in
basic_machine=a29k-none
os=-bsd
;;
amd64)
basic_machine=x86_64-pc
;;
amd64-*)
basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
amdahl)
basic_machine=580-amdahl
os=-sysv
@ -471,27 +432,12 @@ case $basic_machine in
basic_machine=j90-cray
os=-unicos
;;
craynv)
basic_machine=craynv-cray
os=-unicosmp
;;
cr16c)
basic_machine=cr16c-unknown
os=-elf
;;
crds | unos)
basic_machine=m68k-crds
;;
crisv32 | crisv32-* | etraxfs*)
basic_machine=crisv32-axis
;;
cris | cris-* | etrax*)
basic_machine=cris-axis
;;
crx)
basic_machine=crx-unknown
os=-elf
;;
da30 | da30-*)
basic_machine=m68k-da30
;;
@ -514,10 +460,6 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
;;
dpx20 | dpx20-*)
basic_machine=rs6000-bull
os=-bosx
@ -696,6 +638,10 @@ case $basic_machine in
mips3*)
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
;;
mmix*)
basic_machine=mmix-knuth
os=-mmixware
;;
monitor)
basic_machine=m68k-rom68k
os=-coff
@ -708,9 +654,6 @@ case $basic_machine in
basic_machine=i386-pc
os=-msdos
;;
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
mvs)
basic_machine=i370-ibm
os=-mvs
@ -779,6 +722,10 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
nv1)
basic_machine=nv1-cray
os=-unicosmp
;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@ -786,12 +733,9 @@ case $basic_machine in
basic_machine=hppa1.1-oki
os=-proelf
;;
openrisc | openrisc-*)
or32 | or32-*)
basic_machine=or32-unknown
;;
os400)
basic_machine=powerpc-ibm
os=-os400
os=-coff
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
@ -824,24 +768,18 @@ case $basic_machine in
pentiumpro | p6 | 6x86 | athlon | athlon_*)
basic_machine=i686-pc
;;
pentiumii | pentium2 | pentiumiii | pentium3)
pentiumii | pentium2)
basic_machine=i686-pc
;;
pentium4)
basic_machine=i786-pc
;;
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentiumpro-* | p6-* | 6x86-* | athlon-*)
basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
pentiumii-* | pentium2-*)
basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentium4-*)
basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pn)
basic_machine=pn-gould
;;
@ -874,10 +812,6 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
rdos)
basic_machine=i386-pc
os=-rdos
;;
rom68k)
basic_machine=m68k-rom68k
os=-coff
@ -904,10 +838,6 @@ case $basic_machine in
sb1el)
basic_machine=mipsisa64sb1el-unknown
;;
sei)
basic_machine=mips-sei
os=-seiux
;;
sequent)
basic_machine=i386-sequent
;;
@ -915,9 +845,6 @@ case $basic_machine in
basic_machine=sh-hitachi
os=-hms
;;
sh64)
basic_machine=sh64-unknown
;;
sparclite-wrs | simso-wrs)
basic_machine=sparclite-wrs
os=-vxworks
@ -992,6 +919,10 @@ case $basic_machine in
basic_machine=t90-cray
os=-unicos
;;
tic4x | c4x*)
basic_machine=tic4x-unknown
os=-coff
;;
tic54x | c54x*)
basic_machine=tic54x-unknown
os=-coff
@ -1017,10 +948,6 @@ case $basic_machine in
tower | tower-32)
basic_machine=m68k-ncr
;;
tpf)
basic_machine=s390x-ibm
os=-tpf
;;
udi29k)
basic_machine=a29k-amd
os=-udi
@ -1064,10 +991,6 @@ case $basic_machine in
basic_machine=hppa1.1-winbond
os=-proelf
;;
xbox)
basic_machine=i686-pc
os=-mingw32
;;
xps | xps100)
basic_machine=xps100-honeywell
;;
@ -1098,9 +1021,6 @@ case $basic_machine in
romp)
basic_machine=romp-ibm
;;
mmix)
basic_machine=mmix-knuth
;;
rs6000)
basic_machine=rs6000-ibm
;;
@ -1117,10 +1037,13 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sparc | sparcv8 | sparcv9 | sparcv9b)
sh64)
basic_machine=sh64-unknown
;;
sparc | sparcv9 | sparcv9b)
basic_machine=sparc-sun
;;
cydra)
@ -1193,22 +1116,19 @@ case $os in
| -aos* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
| -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
| -chorusos* | -chorusrdb* \
| -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
| -uxpv* | -beos* | -mpeix* | -udk* \
| -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
| -skyos* | -haiku* | -rdos*)
| -powermax* | -dnix*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@ -1226,15 +1146,12 @@ case $os in
os=`echo $os | sed -e 's|nto|nto-qnx|'`
;;
-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
| -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
| -windows* | -osx | -abug | -netware* | -os9* | -beos* \
| -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
;;
-mac*)
os=`echo $os | sed -e 's|mac|macos|'`
;;
-linux-dietlibc)
os=-linux-dietlibc
;;
-linux*)
os=`echo $os | sed -e 's|linux|linux-gnu|'`
;;
@ -1247,9 +1164,6 @@ case $os in
-opened*)
os=-openedition
;;
-os400*)
os=-os400
;;
-wince*)
os=-wince
;;
@ -1271,9 +1185,6 @@ case $os in
-atheos*)
os=-atheos
;;
-syllable*)
os=-syllable
;;
-386bsd)
os=-bsd
;;
@ -1296,9 +1207,6 @@ case $os in
-sinix*)
os=-sysv4
;;
-tpf*)
os=-tpf
;;
-triton*)
os=-sysv3
;;
@ -1335,9 +1243,6 @@ case $os in
-kaos*)
os=-kaos
;;
-zvmoe)
os=-zvmoe
;;
-none)
;;
*)
@ -1369,9 +1274,6 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
c4x-* | tic4x-*)
os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
os=-tops20
@ -1415,15 +1317,9 @@ case $basic_machine in
*-be)
os=-beos
;;
*-haiku)
os=-haiku
;;
*-ibm)
os=-aix
;;
*-knuth)
os=-mmixware
;;
*-wec)
os=-proelf
;;
@ -1556,15 +1452,9 @@ case $basic_machine in
-mvs* | -opened*)
vendor=ibm
;;
-os400*)
vendor=ibm
;;
-ptx*)
vendor=sequent
;;
-tpf*)
vendor=ibm
;;
-vxsim* | -vxworks* | -windiss*)
vendor=wrs
;;
@ -1589,7 +1479,7 @@ case $basic_machine in
esac
echo $basic_machine$os
exit
exit 0
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)

8604
libs/portaudio/configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -139,11 +139,16 @@ case "${host_os}" in
dnl Mac OS X configuration
AC_DEFINE(PA_USE_COREAUDIO)
CFLAGS="$CFLAGS -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3";
OTHER_OBJS="src/os/mac_osx/pa_mac_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o";
if [[ -d /Developer/SDKs/MacOSX10.5.sdk ]] ; then
SHARED_FLAGS="-Werror -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon -dynamiclib -arch x86_64 -arch ppc64 -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.3";
CFLAGS="-Werror $CFLAGS -arch x86_64 -arch ppc64 -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.3";
else
SHARED_FLAGS="-Werror -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon -dynamiclib -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3";
CFLAGS="-Werror $CFLAGS -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3";
fi
OTHER_OBJS="src/os/mac_osx/pa_mac_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o src/common/pa_ringbuffer.o";
LIBS="-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon";
PADLL="libportaudio.dylib";
SHARED_FLAGS="-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon -dynamiclib -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3";
if [[ $with_macapi = "asio" ]] ; then
if [[ $with_asiodir ]] ; then
ASIODIR="$with_asiodir";
@ -312,7 +317,4 @@ fi
AC_SUBST(ENABLE_CXX_TRUE)
AC_SUBST(ENABLE_CXX_FALSE)
#check for some headers
AC_CHECK_HEADERS([libkern/OSAtomic.h])
AC_OUTPUT([Makefile portaudio-2.0.pc])

View File

@ -0,0 +1,75 @@
#ifndef PA_JACK_H
#define PA_JACK_H
/*
* $Id:
* PortAudio Portable Real-Time Audio Library
* JACK-specific extensions
*
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/** @file
* JACK-specific PortAudio API extension header file.
*/
#include "portaudio.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Set the JACK client name.
*
* During Pa_Initialize, When PA JACK connects as a client of the JACK server, it requests a certain
* name, which is for instance prepended to port names. By default this name is "PortAudio". The
* JACK server may append a suffix to the client name, in order to avoid clashes among clients that
* try to connect with the same name (e.g., different PA JACK clients).
*
* This function must be called before Pa_Initialize, otherwise it won't have any effect. Note that
* the string is not copied, but instead referenced directly, so it must not be freed for as long as
* PA might need it.
* @sa PaJack_GetClientName
*/
PaError PaJack_SetClientName( const char* name );
/** Get the JACK client name used by PA JACK.
*
* The caller is responsible for freeing the returned pointer.
*/
PaError PaJack_GetClientName(const char** clientName);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -2,7 +2,7 @@
#define PA_LINUX_ALSA_H
/*
* $Id: pa_linux_alsa.h 1145 2006-11-22 22:40:04Z aknudsen $
* $Id: pa_linux_alsa.h 1236 2007-06-24 20:39:26Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* ALSA-specific extensions
*
@ -58,17 +58,33 @@ typedef struct PaAlsaStreamInfo
}
PaAlsaStreamInfo;
/** Initialize host API specific structure, call this before setting relevant attributes. */
void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
/** Instruct whether to enable real-time priority when starting the audio thread.
*
* If this is turned on by the stream is started, the audio callback thread will be created
* with the FIFO scheduling policy, which is suitable for realtime operation.
**/
void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
#if 0
void PaAlsa_EnableWatchdog( PaStream *s, int enable );
#endif
int PaAlsa_GetStreamInputCard( PaStream *s, int *card );
/** Get the ALSA-lib card index of this stream's input device. */
PaError PaAlsa_GetStreamInputCard( PaStream *s, int *card );
int PaAlsa_GetStreamOutputCard( PaStream *s, int *card );
/** Get the ALSA-lib card index of this stream's output device. */
PaError PaAlsa_GetStreamOutputCard( PaStream *s, int *card );
/** Set the number of periods (buffer fragments) to configure devices with.
*
* By default the number of periods is 4, this is the lowest number of periods that works well on
* the author's soundcard.
* @param numPeriods The number of periods.
*/
PaError PaAlsa_SetNumPeriods( int numPeriods );
#ifdef __cplusplus
}

View File

@ -50,8 +50,8 @@ extern "C" {
* A pointer to a paMacCoreStreamInfo may be passed as
* the hostApiSpecificStreamInfo in the PaStreamParameters struct
* when opening a stream or querying the format. Use NULL, for the
* defaults. Note that for duplex streams, both infos should be the
* same or behaviour is undefined.
* defaults. Note that for duplex streams, flags for input and output
* should be the same or behaviour is undefined.
*/
typedef struct
{
@ -59,7 +59,7 @@ typedef struct
PaHostApiTypeId hostApiType; /**host API for which this data is intended */
unsigned long version; /**structure version */
unsigned long flags; /* flags to modify behaviour */
long 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;*/
} PaMacCoreStreamInfo;
@ -81,7 +81,7 @@ void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, unsigned long flags )
* @param channelMap The channel map array, as described in notes.txt. This array pointer will be used directly (ie the underlying data will not be copied), so the caller should not free the array until after the stream has been opened.
* @param channelMapSize The size of the channel map array.
*/
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const long * 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

View File

@ -0,0 +1,100 @@
#ifndef PA_WIN_DS_H
#define PA_WIN_DS_H
/*
* $Id: $
* PortAudio Portable Real-Time Audio Library
* DirectSound specific extensions
*
* Copyright (c) 1999-2007 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/** @file
@brief DirectSound-specific PortAudio API extension header file.
*/
#include "portaudio.h"
#include "pa_win_waveformat.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#define paWinDirectSoundUseLowLevelLatencyParameters (0x01)
#define paWinDirectSoundUseChannelMask (0x04)
typedef struct PaWinDirectSoundStreamInfo{
unsigned long size; /**< sizeof(PaWinDirectSoundStreamInfo) */
PaHostApiTypeId hostApiType; /**< paDirectSound */
unsigned long version; /**< 1 */
unsigned long flags;
/* low-level latency setting support
TODO ** NOT IMPLEMENTED **
These settings control the number and size of host buffers in order
to set latency. They will be used instead of the generic parameters
to Pa_OpenStream() if flags contains the paWinDirectSoundUseLowLevelLatencyParameters
flag.
If PaWinDirectSoundStreamInfo structures with paWinDirectSoundUseLowLevelLatencyParameters
are supplied for both input and output in a full duplex stream, then the
input and output framesPerBuffer must be the same, or the larger of the
two must be a multiple of the smaller, otherwise a
paIncompatibleHostApiSpecificStreamInfo error will be returned from
Pa_OpenStream().
unsigned long framesPerBuffer;
*/
/*
support for WAVEFORMATEXTENSIBLE channel masks. If flags contains
paWinDirectSoundUseChannelMask this allows you to specify which speakers
to address in a multichannel stream. Constants for channelMask
are specified in pa_win_waveformat.h
*/
PaWinWaveFormatChannelMask channelMask;
}PaWinDirectSoundStreamInfo;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_WIN_DS_H */

View File

@ -0,0 +1,180 @@
#ifndef PA_WIN_WAVEFORMAT_H
#define PA_WIN_WAVEFORMAT_H
/*
* PortAudio Portable Real-Time Audio Library
* Windows WAVEFORMAT* data structure utilities
* portaudio.h should be included before this file.
*
* Copyright (c) 2007 Ross Bencina
*
* 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
@brief Windows specific PortAudio API extension and utilities header file.
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
The following #defines for speaker channel masks are the same
as those in ksmedia.h, except with PAWIN_ prepended, KSAUDIO_ removed
in some cases, and casts to PaWinWaveFormatChannelMask added.
*/
typedef unsigned long PaWinWaveFormatChannelMask;
/* Speaker Positions: */
#define PAWIN_SPEAKER_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1)
#define PAWIN_SPEAKER_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x2)
#define PAWIN_SPEAKER_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x4)
#define PAWIN_SPEAKER_LOW_FREQUENCY ((PaWinWaveFormatChannelMask)0x8)
#define PAWIN_SPEAKER_BACK_LEFT ((PaWinWaveFormatChannelMask)0x10)
#define PAWIN_SPEAKER_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20)
#define PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER ((PaWinWaveFormatChannelMask)0x40)
#define PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER ((PaWinWaveFormatChannelMask)0x80)
#define PAWIN_SPEAKER_BACK_CENTER ((PaWinWaveFormatChannelMask)0x100)
#define PAWIN_SPEAKER_SIDE_LEFT ((PaWinWaveFormatChannelMask)0x200)
#define PAWIN_SPEAKER_SIDE_RIGHT ((PaWinWaveFormatChannelMask)0x400)
#define PAWIN_SPEAKER_TOP_CENTER ((PaWinWaveFormatChannelMask)0x800)
#define PAWIN_SPEAKER_TOP_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1000)
#define PAWIN_SPEAKER_TOP_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x2000)
#define PAWIN_SPEAKER_TOP_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x4000)
#define PAWIN_SPEAKER_TOP_BACK_LEFT ((PaWinWaveFormatChannelMask)0x8000)
#define PAWIN_SPEAKER_TOP_BACK_CENTER ((PaWinWaveFormatChannelMask)0x10000)
#define PAWIN_SPEAKER_TOP_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20000)
/* Bit mask locations reserved for future use */
#define PAWIN_SPEAKER_RESERVED ((PaWinWaveFormatChannelMask)0x7FFC0000)
/* Used to specify that any possible permutation of speaker configurations */
#define PAWIN_SPEAKER_ALL ((PaWinWaveFormatChannelMask)0x80000000)
/* DirectSound Speaker Config */
#define PAWIN_SPEAKER_DIRECTOUT 0
#define PAWIN_SPEAKER_MONO (PAWIN_SPEAKER_FRONT_CENTER)
#define PAWIN_SPEAKER_STEREO (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT)
#define PAWIN_SPEAKER_QUAD (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \
PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT)
#define PAWIN_SPEAKER_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \
PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_BACK_CENTER)
#define PAWIN_SPEAKER_5POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \
PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \
PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT)
#define PAWIN_SPEAKER_7POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \
PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \
PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \
PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER | PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER)
#define PAWIN_SPEAKER_5POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \
PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \
PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT)
#define PAWIN_SPEAKER_7POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \
PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \
PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \
PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT)
/*
According to the Microsoft documentation:
The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means
that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are
similarly obsolete but are unchanged for compatibility reasons).
*/
#define PAWIN_SPEAKER_5POINT1_BACK PAWIN_SPEAKER_5POINT1
#define PAWIN_SPEAKER_7POINT1_WIDE PAWIN_SPEAKER_7POINT1
/* DVD Speaker Positions */
#define PAWIN_SPEAKER_GROUND_FRONT_LEFT PAWIN_SPEAKER_FRONT_LEFT
#define PAWIN_SPEAKER_GROUND_FRONT_CENTER PAWIN_SPEAKER_FRONT_CENTER
#define PAWIN_SPEAKER_GROUND_FRONT_RIGHT PAWIN_SPEAKER_FRONT_RIGHT
#define PAWIN_SPEAKER_GROUND_REAR_LEFT PAWIN_SPEAKER_BACK_LEFT
#define PAWIN_SPEAKER_GROUND_REAR_RIGHT PAWIN_SPEAKER_BACK_RIGHT
#define PAWIN_SPEAKER_TOP_MIDDLE PAWIN_SPEAKER_TOP_CENTER
#define PAWIN_SPEAKER_SUPER_WOOFER PAWIN_SPEAKER_LOW_FREQUENCY
/*
PaWinWaveFormat is defined here to provide compatibility with
compilation environments which don't have headers defining
WAVEFORMATEXTENSIBLE (e.g. older versions of MSVC, Borland C++ etc.
The fields for WAVEFORMATEX and WAVEFORMATEXTENSIBLE are declared as an
unsigned char array here to avoid clients who include this file having
a dependency on windows.h and mmsystem.h, and also to to avoid having
to write separate packing pragmas for each compiler.
*/
#define PAWIN_SIZEOF_WAVEFORMATEX 18
#define PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE (PAWIN_SIZEOF_WAVEFORMATEX + 22)
typedef struct{
unsigned char fields[ PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE ];
unsigned long extraLongForAlignment; /* ensure that compiler aligns struct to DWORD */
} PaWinWaveFormat;
/*
WAVEFORMATEXTENSIBLE fields:
union {
WORD wValidBitsPerSample;
WORD wSamplesPerBlock;
WORD wReserved;
} Samples;
DWORD dwChannelMask;
GUID SubFormat;
*/
#define PAWIN_INDEXOF_WVALIDBITSPERSAMPLE (PAWIN_SIZEOF_WAVEFORMATEX+0)
#define PAWIN_INDEXOF_DWCHANNELMASK (PAWIN_SIZEOF_WAVEFORMATEX+2)
#define PAWIN_INDEXOF_SUBFORMAT (PAWIN_SIZEOF_WAVEFORMATEX+6)
/*
Use the following two functions to initialize the waveformat structure.
*/
void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat,
int numChannels, PaSampleFormat sampleFormat, double sampleRate );
void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat,
int numChannels, PaSampleFormat sampleFormat, double sampleRate,
PaWinWaveFormatChannelMask channelMask );
/* Map a channel count to a speaker channel mask */
PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_WIN_WAVEFORMAT_H */

View File

@ -1,7 +1,7 @@
#ifndef PA_WIN_WMME_H
#define PA_WIN_WMME_H
/*
* $Id: pa_win_wmme.h 1115 2006-09-06 15:39:48Z rossb $
* $Id: pa_win_wmme.h 1247 2007-08-11 16:29:09Z rossb $
* PortAudio Portable Real-Time Audio Library
* MME specific extensions
*
@ -44,6 +44,7 @@
#include "portaudio.h"
#include "pa_win_waveformat.h"
#ifdef __cplusplus
extern "C"
@ -53,7 +54,7 @@ extern "C"
#define paWinMmeUseLowLevelLatencyParameters (0x01)
#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */
#define paWinMmeUseChannelMask (0x04)
/* By default, the mme implementation drops the processing thread's priority
to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100%
@ -106,6 +107,15 @@ typedef struct PaWinMmeStreamInfo{
PaWinMmeDeviceAndChannelCount *devices;
unsigned long deviceCount;
/*
support for WAVEFORMATEXTENSIBLE channel masks. If flags contains
paWinMmeUseChannelMask this allows you to specify which speakers
to address in a multichannel stream. Constants for channelMask
are specified in pa_win_waveformat.h
*/
PaWinWaveFormatChannelMask channelMask;
}PaWinMmeStreamInfo;

View File

@ -1,8 +1,7 @@
#ifndef PORTAUDIO_H
#define PORTAUDIO_H
/*
* $Id: portaudio.h 1149 2006-11-27 01:42:26Z leland_lucius $
* $Id: portaudio.h 1247 2007-08-11 16:29:09Z rossb $
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.portaudio.com/

View File

@ -33,6 +33,9 @@ basename="s,^.*/,,g"
# function.
progpath="$0"
# define SED for historic ltconfig's generated by Libtool 1.3
test -z "$SED" && SED=sed
# The name of this program:
progname=`echo "$progpath" | $SED $basename`
modename="$progname"
@ -51,6 +54,13 @@ TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)"
if test -n "${ZSH_VERSION+set}" ; then
setopt NO_GLOB_SUBST
fi
# Same for EGREP, and just to be sure, do LTCC as well
if test "X$EGREP" = X ; then
EGREP=egrep
fi
if test "X$LTCC" = X ; then
LTCC=${CC-gcc}
fi
# Check that we have a working $echo.
if test "X$1" = X--no-reexec; then
@ -137,6 +147,30 @@ preserve_args=
lo2o="s/\\.lo\$/.${objext}/"
o2lo="s/\\.${objext}\$/.lo/"
if test -z "$max_cmd_len"; then
i=0
testring="ABCD"
new_result=
# If test is not a shell built-in, we'll probably end up computing a
# maximum length that is only half of the actual maximum length, but
# we can't tell.
while (test "X"`$SHELL $0 --fallback-echo "X$testring" 2>/dev/null` \
= "XX$testring") >/dev/null 2>&1 &&
new_result=`expr "X$testring" : ".*" 2>&1` &&
max_cmd_len="$new_result" &&
test "$i" != 17 # 1/2 MB should be enough
do
i=`expr $i + 1`
testring="$testring$testring"
done
testring=
# Add a significant safety factor because C++ compilers can tack on massive
# amounts of additional arguments before passing them to the linker.
# It appears as though 1/2 is a usable value.
max_cmd_len=`expr $max_cmd_len \/ 2`
fi
#####################################
# Shell function definitions:
# This seems to be the best place for them
@ -257,7 +291,21 @@ func_infer_tag ()
esac
CC_quoted="$CC_quoted $arg"
done
# user sometimes does CC=<HOST>-gcc so we need to match that to 'gcc'
trimedcc=`echo ${CC} | $SED -e "s/${host}-//g"`
# and sometimes libtool has CC=<HOST>-gcc but user does CC=gcc
extendcc=${host}-${CC}
# and sometimes libtool has CC=<OLDHOST>-gcc but user has CC=<NEWHOST>-gcc
# (Gentoo-specific hack because we always export $CHOST)
mungedcc=${CHOST-${host}}-${trimedcc}
case "$@ " in
"cc "* | " cc "* | "${host}-cc "* | " ${host}-cc "*|\
"gcc "* | " gcc "* | "${host}-gcc "* | " ${host}-gcc "*)
tagname=CC
break ;;
"$trimedcc "* | " $trimedcc "* | "`$echo $trimedcc` "* | " `$echo $trimedcc` "*|\
"$extendcc "* | " $extendcc "* | "`$echo $extendcc` "* | " `$echo $extendcc` "*|\
"$mungedcc "* | " $mungedcc "* | "`$echo $mungedcc` "* | " `$echo $mungedcc` "*|\
" $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*)
# The compiler in the base compile command matches
# the one in the tagged configuration.
@ -851,7 +899,7 @@ if test -z "$show_help"; then
# Lock this critical section if it is needed
# We use this script file to make the link, it avoids creating a new file
if test "$need_locks" = yes; then
until $run ln "$progpath" "$lockfile" 2>/dev/null; do
until $run ln "$srcfile" "$lockfile" 2>/dev/null; do
$show "Waiting for $lockfile to be removed"
sleep 2
done
@ -3642,6 +3690,13 @@ EOF
*) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
esac
done
# It is ok to link against an archive when
# building a shared library.
if $AR -t $potlib > /dev/null 2>&1; then
newdeplibs="$newdeplibs $a_deplib"
a_deplib=""
break 2
fi
if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \
| ${SED} 10q \
| $EGREP "$file_magic_regex" > /dev/null; then
@ -5379,6 +5434,11 @@ else
$echo >> $output "\
if test \"\$libtool_execute_magic\" != \"$magic\"; then
# Run the actual program with our arguments.
# Make sure env LD_LIBRARY_PATH does not mess us up
if test -n \"\${LD_LIBRARY_PATH+set}\"; then
export LD_LIBRARY_PATH=\$progdir:\$LD_LIBRARY_PATH
fi
"
case $host in
# Backslashes separate directories on plain windows
@ -5606,9 +5666,53 @@ fi\
$echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2
exit $EXIT_FAILURE
fi
newdependency_libs="$newdependency_libs $libdir/$name"
if test "X$EGREP" = X ; then
EGREP=egrep
fi
# We do not want portage's install root ($D) present. Check only for
# this if the .la is being installed.
if test "$installed" = yes && test "$D"; then
eval mynewdependency_lib=`echo "$libdir/$name" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
else
mynewdependency_lib="$libdir/$name"
fi
# Do not add duplicates
if test "$mynewdependency_lib"; then
my_little_ninja_foo_1=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"`
if test -z "$my_little_ninja_foo_1"; then
newdependency_libs="$newdependency_libs $mynewdependency_lib"
fi
fi
;;
*)
if test "$installed" = yes; then
# Rather use S=WORKDIR if our version of portage supports it.
# This is because some ebuild (gcc) do not use $S as buildroot.
if test "$PWORKDIR"; then
S="$PWORKDIR"
fi
# We do not want portage's build root ($S) present.
my_little_ninja_foo_2=`echo $deplib |$EGREP -e "$S"`
# We do not want portage's install root ($D) present.
my_little_ninja_foo_3=`echo $deplib |$EGREP -e "$D"`
if test -n "$my_little_ninja_foo_2" && test "$S"; then
mynewdependency_lib=""
elif test -n "$my_little_ninja_foo_3" && test "$D"; then
eval mynewdependency_lib=`echo "$deplib" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
else
mynewdependency_lib="$deplib"
fi
else
mynewdependency_lib="$deplib"
fi
# Do not add duplicates
if test "$mynewdependency_lib"; then
my_little_ninja_foo_4=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"`
if test -z "$my_little_ninja_foo_4"; then
newdependency_libs="$newdependency_libs $mynewdependency_lib"
fi
fi
;;
*) newdependency_libs="$newdependency_libs $deplib" ;;
esac
done
dependency_libs="$newdependency_libs"
@ -5660,6 +5764,10 @@ fi\
case $host,$output,$installed,$module,$dlname in
*cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
esac
# Do not add duplicates
if test "$installed" = yes && test "$D"; then
install_libdir=`echo "$install_libdir" |sed -e "s:$D:/:g" -e 's:/\+:/:g'`
fi
$echo > $output "\
# $outputname - a libtool library file
# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP

View File

@ -202,4 +202,10 @@ testNames = ["patest_sine", "paqa_devs", "paqa_errs", "patest1", "patest_buffer"
tests = [env.Program(target=os.path.join("#", "bin", name), source=[os.path.join("#", "test", name + ".c"),
staticLib]) for name in testNames]
Return("sources", "sharedLib", "staticLib", "tests", "env")
# Detect host APIs
hostApis = []
for cppdef in env["CPPDEFINES"]:
if cppdef.startswith("PA_USE_"):
hostApis.append(cppdef[7:-2])
Return("sources", "sharedLib", "staticLib", "tests", "env", "hostApis")

View File

@ -74,7 +74,7 @@ void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb)
VERY dangerous alternative, vsprintf (with no n)
*/
#if (_MSC_VER) && (_MSC_VER < 1400)
#if (_MSC_VER) && (_MSC_VER <= 1400)
#define VSNPRINTF _vsnprintf
#else
#define VSNPRINTF vsnprintf

View File

@ -72,7 +72,7 @@ void PaUtil_DebugPrint( const char *format, ... );
Function exit (simple return value):
"FunctionName returned:\n"
"\tReturnType: returnValue\n\n"
"\tReturnType: returnValue\n"
If the return type is an error code, the error text is displayed in ()
@ -101,9 +101,36 @@ void PaUtil_DebugPrint( const char *format, ... );
#ifdef PA_LOG_API_CALLS
#define PA_LOGAPI(x) PaUtil_DebugPrint x ;
#define PA_LOGAPI(x) PaUtil_DebugPrint x
#define PA_LOGAPI_ENTER(functionName) PaUtil_DebugPrint( functionName " called.\n" )
#define PA_LOGAPI_ENTER_PARAMS(functionName) PaUtil_DebugPrint( functionName " called:\n" )
#define PA_LOGAPI_EXIT(functionName) PaUtil_DebugPrint( functionName " returned.\n" )
#define PA_LOGAPI_EXIT_PAERROR( functionName, result ) \
PaUtil_DebugPrint( functionName " returned:\n" ); \
PaUtil_DebugPrint("\tPaError: %d ( %s )\n", result, Pa_GetErrorText( result ) )
#define PA_LOGAPI_EXIT_T( functionName, resultFormatString, result ) \
PaUtil_DebugPrint( functionName " returned:\n" ); \
PaUtil_DebugPrint("\t" resultFormatString "\n", result )
#define PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( functionName, positiveResultFormatString, result ) \
PaUtil_DebugPrint( functionName " returned:\n" ); \
if( result > 0 ) \
PaUtil_DebugPrint("\t" positiveResultFormatString "\n", result ); \
else \
PaUtil_DebugPrint("\tPaError: %d ( %s )\n", result, Pa_GetErrorText( result ) )
#else
#define PA_LOGAPI(x)
#define PA_LOGAPI_ENTER(functionName)
#define PA_LOGAPI_ENTER_PARAMS(functionName)
#define PA_LOGAPI_EXIT(functionName)
#define PA_LOGAPI_EXIT_PAERROR( functionName, result )
#define PA_LOGAPI_EXIT_T( functionName, resultFormatString, result )
#define PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( functionName, positiveResultFormatString, result )
#endif

View File

@ -1,7 +1,7 @@
#ifndef PA_ENDIANNESS_H
#define PA_ENDIANNESS_H
/*
* $Id: pa_endianness.h 1136 2006-11-22 03:52:34Z rossb $
* $Id: pa_endianness.h 1216 2007-06-10 09:26:00Z aknudsen $
* Portable Audio I/O Library current platform endianness macros
*
* Based on the Open Source API proposed by Ross Bencina
@ -97,7 +97,7 @@ extern "C"
/* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(LITTLE_ENDIAN) || defined(__i386) || defined(_M_IX86)
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(LITTLE_ENDIAN) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__)
#define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
#else
#define PA_BIG_ENDIAN

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_ringbuffer.c 1164 2006-12-21 15:34:50Z bjornroche $
* $Id: pa_ringbuffer.c 1240 2007-07-17 13:05:07Z bjornroche $
* Portable Audio I/O Library
* Ring Buffer utility.
*
@ -71,21 +71,27 @@
*
****************/
#if defined(HAVE_LIBKERN_OSATOMIC_H) && (defined(__APPLE__) || defined(__FreeBSD__))
#if defined(__APPLE__)
# include <libkern/OSAtomic.h>
/* Here are the memory barrier functions. Mac OS X and FreeBSD only provide
full memory barriers, so the three types of barriers are the same. */
/* Here are the memory barrier functions. Mac OS X only provides
full memory barriers, so the three types of barriers are the same,
however, these barriers are superior to compiler-based ones. */
# define PaUtil_FullMemoryBarrier() OSMemoryBarrier()
# define PaUtil_ReadMemoryBarrier() OSMemoryBarrier()
# define PaUtil_WriteMemoryBarrier() OSMemoryBarrier()
#elif defined(__GNUC__)
/* GCC understands volatile asm and "memory" to mean it
/* GCC >= 4.1 has built-in intrinsics. We'll use those */
# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
# define PaUtil_FullMemoryBarrier() __sync_synchronize()
# define PaUtil_ReadMemoryBarrier() __sync_synchronize()
# define PaUtil_WriteMemoryBarrier() __sync_synchronize()
/* as a fallback, GCC understands volatile asm and "memory" to mean it
* should not reorder memory read/writes */
# if defined( __PPC__ )
# elif defined( __PPC__ )
# define PaUtil_FullMemoryBarrier() asm volatile("sync":::"memory")
# define PaUtil_ReadMemoryBarrier() asm volatile("sync":::"memory")
# define PaUtil_WriteMemoryBarrier() asm volatile("sync":::"memory")
# elif defined( __i386__ ) || defined( __i486__ ) || defined( __i586__ ) || defined( __i686__ ) || defined(__x86_64__)
# elif defined( __i386__ ) || defined( __i486__ ) || defined( __i586__ ) || defined( __i686__ ) || defined( __x86_64__ )
# define PaUtil_FullMemoryBarrier() asm volatile("mfence":::"memory")
# define PaUtil_ReadMemoryBarrier() asm volatile("lfence":::"memory")
# define PaUtil_WriteMemoryBarrier() asm volatile("sfence":::"memory")

View File

@ -1,7 +1,7 @@
#ifndef PA_UTIL_H
#define PA_UTIL_H
/*
* $Id: pa_util.h 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_util.h 1229 2007-06-15 16:11:11Z rossb $
* Portable Audio I/O Library implementation utilities header
* common implementation utilities and interfaces
*
@ -113,23 +113,6 @@ void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
/** PA_DEBUG() provides a simple debug message printing facility. The macro
passes it's argument to a printf-like function called PaUtil_DebugPrint()
which prints to stderr and always flushes the stream after printing.
Because preprocessor macros cannot directly accept variable length argument
lists, calls to the macro must include an additional set of parenthesis, eg:
PA_DEBUG(("errorno: %d", 1001 ));
*/
void PaUtil_DebugPrint( const char *format, ... );
#ifdef PA_ENABLE_DEBUG_OUTPUT
#define PA_DEBUG(x) PaUtil_DebugPrint x ;
#else
#define PA_DEBUG(x)
#endif
/* the following functions are implemented in a platform platform specific
.c file
*/

View File

@ -1,11 +1,11 @@
/*
* $Id: pa_linux_alsa.c 1161 2006-12-17 16:16:13Z aknudsen $
* $Id: pa_linux_alsa.c 1278 2007-09-12 17:39:48Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen
*
* Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
* Copyright (c) 2005-2006 Arve Knudsen <aknuds-1@broadpark.no>
* Copyright (c) 2005-2007 Arve Knudsen <aknuds-1@broadpark.no>
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@ -70,6 +70,8 @@
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_endianness.h"
#include "pa_debugprint.h"
#include "pa_linux_alsa.h"
@ -96,6 +98,13 @@
assert( success == aErr_ );
static int aErr_; /* Used with ENSURE_ */
static int numPeriods_ = 4;
int PaAlsa_SetNumPeriods( int numPeriods )
{
numPeriods_ = numPeriods;
return paNoError;
}
typedef enum
{
@ -520,14 +529,19 @@ static int IgnorePlugin( const char *pluginId )
return 0;
}
/* Wrapper around snd_pcm_open which repeatedly retries opening a device for up to a second
* if it is busy. This is because dmix may temporarily hold on to a device after it (dmix)
* has been opened and closed. */
static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode )
/** Open PCM device.
*
* Wrapper around snd_pcm_open which may repeatedly retry opening a device if it is busy, for
* a certain time. This is because dmix may temporarily hold on to a device after it (dmix)
* has been opened and closed.
* @param mode: Open mode (e.g., SND_PCM_BLOCKING).
* @param waitOnBusy: Retry opening busy device for up to one second?
**/
static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode, int waitOnBusy )
{
int tries = 0;
int tries = 0, maxTries = waitOnBusy ? 100 : 0;
int ret = snd_pcm_open( pcmp, name, stream, mode );
for( tries = 0; tries < 100 && -EBUSY == ret; ++tries )
for( tries = 0; tries < maxTries && -EBUSY == ret; ++tries )
{
Pa_Sleep( 10 );
ret = snd_pcm_open( pcmp, name, stream, mode );
@ -539,8 +553,8 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream,
}
if( -EBUSY == ret )
{
PA_DEBUG(( "%s: Failed to open busy device\n",
__FUNCTION__ ));
PA_DEBUG(( "%s: Failed to open busy device '%s'\n",
__FUNCTION__, name ));
}
return ret;
@ -563,7 +577,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
/* Query capture */
if( deviceName->hasCapture &&
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking )
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 )
>= 0 )
{
if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo,
@ -577,7 +591,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
/* Query playback */
if( deviceName->hasPlayback &&
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking )
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 )
>= 0 )
{
if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo,
@ -607,8 +621,12 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 )
{
/* Make device default if there isn't already one or it is the ALSA "default" device */
if( baseApi->info.defaultInputDevice == paNoDevice && baseDeviceInfo->maxInputChannels > 0 )
if( (baseApi->info.defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName,
"default" )) && baseDeviceInfo->maxInputChannels > 0 )
{
baseApi->info.defaultInputDevice = *devIdx;
PA_DEBUG(("Default input device: %s\n", deviceName->name));
}
if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName,
"default" )) && baseDeviceInfo->maxOutputChannels > 0 )
{
@ -639,6 +657,10 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
int res;
int blocking = SND_PCM_NONBLOCK;
char alsaCardName[50];
#ifdef PA_ENABLE_DEBUG_OUTPUT
PaTime startTime = PaUtil_GetTime();
#endif
if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
blocking = 0;
@ -820,6 +842,11 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
/* Loop over list of cards, filling in info. If a device is deemed unavailable (can't get name),
* it's ignored.
*
* Note that we do this in two stages. This is a workaround owing to the fact that the 'dmix'
* plugin may cause the underlying hardware device to be busy for a short while even after it
* (dmix) is closed. The 'default' plugin may also point to the dmix plugin, so the same goes
* for this.
*/
for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
@ -834,6 +861,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx ) );
}
assert( devIdx < numDeviceNames );
/* Now inspect 'dmix' and 'default' plugins */
for( i = 0; i < numDeviceNames; ++i )
{
PaAlsaDeviceInfo* devInfo = &deviceInfoArray[i];
@ -850,6 +878,10 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
baseApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
#ifdef PA_ENABLE_DEBUG_OUTPUT
PA_DEBUG(( "%s: Building device list took %f seconds\n", __FUNCTION__, PaUtil_GetTime() - startTime ));
#endif
end:
return result;
@ -910,8 +942,13 @@ static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
available |= paInt32;
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
#ifdef PA_LITTLE_ENDIAN
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0)
available |= paInt24;
#elif defined PA_BIG_ENDIAN
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0)
available |= paInt24;
#endif
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
available |= paInt16;
@ -936,7 +973,11 @@ static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
return SND_PCM_FORMAT_S16;
case paInt24:
return SND_PCM_FORMAT_S24;
#ifdef PA_LITTLE_ENDIAN
return SND_PCM_FORMAT_S24_3LE;
#elif defined PA_BIG_ENDIAN
return SND_PCM_FORMAT_S24_3BE;
#endif
case paInt32:
return SND_PCM_FORMAT_S32;
@ -986,14 +1027,10 @@ static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStr
PA_DEBUG(( "%s: Opening device %s\n", __FUNCTION__, deviceName ));
if( (ret = OpenPcm( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK )) < 0 )
SND_PCM_NONBLOCK, 1 )) < 0 )
{
/* Not to be closed */
*pcm = NULL;
if( -EBUSY == ret )
{
PA_DEBUG(( "%s: Device is busy\n", __FUNCTION__ ));
}
ENSURE_( ret, -EBUSY == ret ? paDeviceUnavailable : paBadIODeviceCombination );
}
ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
@ -1287,14 +1324,25 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel
snd_pcm_sw_params_alloca( &swParams );
bufSz = (params->suggestedLatency * sampleRate) + self->framesPerBuffer; /* One period does not count as latency */
bufSz = params->suggestedLatency * sampleRate;
ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError );
/* Set the parameters! */
ENSURE_( snd_pcm_hw_params( self->pcm, hwParams ), paUnanticipatedHostError );
{
int r = snd_pcm_hw_params( self->pcm, hwParams );
#ifdef PA_ENABLE_DEBUG_OUTPUT
if( r < 0 )
{
snd_output_t *output = NULL;
snd_output_stdio_attach( &output, stderr, 0 );
snd_pcm_hw_params_dump( hwParams, output );
}
#endif
ENSURE_(r, paUnanticipatedHostError );
}
ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
/* Latency in seconds, one period is not counted as latency */
*latency = (self->bufferSize - self->framesPerBuffer) / sampleRate;
/* Latency in seconds */
*latency = self->bufferSize / sampleRate;
/* Now software parameters... */
ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError );
@ -1473,20 +1521,19 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo
}
}
/* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
/* Using the base number of periods, we try to approximate the suggested latency (+1 period),
finding a combination of period/buffer size which best fits these constraints */
{
unsigned numPeriods = 4, maxPeriods = 0;
unsigned numPeriods = numPeriods_, maxPeriods = 0;
/* It may be that the device only supports 2 periods for instance */
dir = 0;
ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError );
assert( maxPeriods > 1 );
/* One period is not counted as latency */
maxPeriods -= 1;
numPeriods = PA_MIN( maxPeriods, numPeriods );
if( framesPerUserBuffer != paFramesPerBufferUnspecified )
{
/* Try to get a power-of-two of the user buffer size. */
framesPerHostBuffer = framesPerUserBuffer;
if( framesPerHostBuffer < bufferSize )
{
@ -1494,6 +1541,11 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo
{
framesPerHostBuffer *= 2;
}
/* One extra period is preferrable to one less (should be more robust) */
if( bufferSize / framesPerHostBuffer < numPeriods )
{
framesPerHostBuffer /= 2;
}
}
else
{
@ -1614,15 +1666,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
unsigned long framesPerHostBuffer = 0;
int dir = 0;
int accurate = 1;
unsigned numPeriods = numPeriods_;
if( self->capture.pcm && self->playback.pcm )
{
if( framesPerUserBuffer == paFramesPerBufferUnspecified )
{
snd_pcm_uframes_t desiredLatency, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize,
minCapture, minPlayback, maxCapture, maxPlayback;
/* Come up with a common desired latency */
snd_pcm_uframes_t desiredBufSz, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize,
minCapture, minPlayback, maxCapture, maxPlayback;
dir = 0;
ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
@ -1636,9 +1688,9 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination );
desiredLatency = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
desiredBufSz = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
* sampleRate);
/* Clamp desiredLatency */
/* Clamp desiredBufSz */
{
snd_pcm_uframes_t maxBufferSize;
snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback;
@ -1646,7 +1698,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError );
maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback );
desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
desiredBufSz = PA_MIN( desiredBufSz, maxBufferSize );
}
/* Find the closest power of 2 */
@ -1659,13 +1711,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
{
if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
break; /* Ok! */
{
/* OK! */
break;
}
periodSize *= 2;
}
/* 4 periods considered optimal */
optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
/* Find the closest power of 2 */
@ -1676,12 +1730,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
while( optimalPeriodSize >= periodSize )
{
if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) < 0 )
continue;
if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 )
if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 )
>= 0 && snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback,
optimalPeriodSize, 0 ) >= 0 )
{
break;
}
optimalPeriodSize /= 2;
}
if( optimalPeriodSize > periodSize )
periodSize = optimalPeriodSize;
@ -1698,7 +1755,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
else
{
/* Unable to find a common period size, oh well */
optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
self->capture.framesPerBuffer = optimalPeriodSize;
@ -1726,7 +1783,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
dir = 0;
ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError );
if( maxPeriods < 4 )
if( maxPeriods < numPeriods )
{
/* The playback component is trickier to get right, try that first */
first = &self->playback;
@ -2102,7 +2159,9 @@ error:
static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
PaError result = paNoError;
/* XXX: Seems that draining the dmix device may trigger a race condition in ALSA */
/* XXX: snd_pcm_drain tends to lock up, avoid it until we find out more */
abort = 1;
/*
if( stream->capture.pcm && !strcmp( Pa_GetDeviceInfo( stream->capture.device )->name,
"dmix" ) )
{
@ -2113,6 +2172,7 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
abort = 1;
}
*/
if( abort )
{
@ -3448,7 +3508,6 @@ error:
/* Extensions */
/** Initialize host API specific structure, call this before setting relevant attributes. */
void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
{
info->size = sizeof (PaAlsaStreamInfo);
@ -3457,11 +3516,6 @@ void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
info->deviceString = NULL;
}
/** Instruct whether to enable real-time priority when starting the audio thread.
*
* If this is turned on by the stream is started, the audio callback thread will be created
* with the FIFO scheduling policy, which is suitable for realtime operation.
**/
void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
{
PaAlsaStream *stream = (PaAlsaStream *) s;
@ -3476,7 +3530,6 @@ void PaAlsa_EnableWatchdog( PaStream *s, int enable )
}
#endif
/** Get the ALSA-lib card index of this stream's input device. */
PaError PaAlsa_GetStreamInputCard(PaStream* s, int* card) {
PaAlsaStream *stream = (PaAlsaStream *) s;
snd_pcm_info_t* pcmInfo;
@ -3493,7 +3546,6 @@ error:
return result;
}
/** Get the ALSA-lib card index of this stream's output device. */
PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
PaAlsaStream *stream = (PaAlsaStream *) s;
snd_pcm_info_t* pcmInfo;

View File

@ -1,10 +1,10 @@
/*
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
*
*
* PortAudio v18 version of AudioScience HPI driver by Fred Gleason <fredg@salemradiolabs.com>
* PortAudio v19 version of AudioScience HPI driver by Ludwig Schwardt <schwardt@sun.ac.za>
*
*
* Copyright (c) 2003 Fred Gleason
* Copyright (c) 2005,2006 Ludwig Schwardt
*
@ -29,13 +29,13 @@
*/
/*
* The text above constitutes the entire PortAudio license; however,
* 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
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
@ -47,45 +47,45 @@
/** @file
@ingroup hostapi_src
@brief Host API implementation supporting AudioScience cards
@brief Host API implementation supporting AudioScience cards
via the Linux HPI interface.
<h3>Overview</h3>
This is a PortAudio implementation for the AudioScience HPI Audio API
on the Linux platform. AudioScience makes a range of audio adapters customised
for the broadcasting industry, with support for both Windows and Linux.
More information on their products can be found on their website:
http://www.audioscience.com
Documentation for the HPI API can be found at:
http://www.audioscience.com/internet/download/sdk/spchpi.pdf
The Linux HPI driver itself (a kernel module + library) can be downloaded from:
http://www.audioscience.com/internet/download/linux_drivers.htm
<h3>Implementation strategy</h3>
*Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA
*Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA
implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence
of this host API implementation might therefore seem a bit flawed. Unfortunately, at
the time of the creation of this implementation (June 2006), the PA ALSA implementation
could not make use of the existing AudioScience ALSA driver. PA ALSA uses the
"memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the
of this host API implementation might therefore seem a bit flawed. Unfortunately, at
the time of the creation of this implementation (June 2006), the PA ALSA implementation
could not make use of the existing AudioScience ALSA driver. PA ALSA uses the
"memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the
AudioScience ALSA driver only supports the "read-write" access mode. The appropriate
solution to this problem is to add "read-write" support to PortAudio ALSA, thereby
extending the range of soundcards it supports (AudioScience cards are not the only
ones with this problem). Given the author's limited knowledge of ALSA and the
ones with this problem). Given the author's limited knowledge of ALSA and the
simplicity of the HPI API, the second-best solution was born...
The following mapping between HPI and PA was followed:
HPI subsystem => PortAudio host API
HPI adapter => nothing specific
HPI stream => PortAudio device
Each HPI stream is either input or output (not both), and can support
different channel counts, sampling rates and sample formats. It is therefore
a more natural fit to a PA device. A PA stream can therefore combine two
@ -93,19 +93,19 @@
HPI streams can even be on different physical adapters. The two streams ought to be
sample-synchronised when they reside on the same adapter, as most AudioScience adapters
derive their ADC and DAC clocks from one master clock. When combining two adapters
into one full-duplex stream, however, the use of a word clock connection between the
into one full-duplex stream, however, the use of a word clock connection between the
adapters is strongly recommended.
The HPI interface is inherently blocking, making use of read and write calls to
transfer data between user buffers and driver buffers. The callback interface therefore
requires a helper thread ("callback engine") which periodically transfers data (one thread
per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until
enough samples can be transferred (select() or poll() would be better, but currently seems
per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until
enough samples can be transferred (select() or poll() would be better, but currently seems
impossible...). The thread implementation makes use of the Unix thread helper functions
and some pthread calls here and there. If a unified PA thread exists, this host API
implementation might also compile on Windows, as this is the only real Linux-specific
and some pthread calls here and there. If a unified PA thread exists, this host API
implementation might also compile on Windows, as this is the only real Linux-specific
part of the code.
There is no inherent fixed buffer size in the HPI interface, as in some other host APIs.
The PortAudio implementation contains a buffer that is allocated during OpenStream and
used to transfer data between the callback and the HPI driver buffer. The size of this
@ -113,28 +113,28 @@
requested callback buffer size as far as possible. It can become quite huge, as the
AudioScience cards are typically geared towards higher-latency applications and contain
large hardware buffers.
The HPI interface natively supports most common sample formats and sample rates (some
conversion is done on the adapter itself).
Stream time is measured based on the number of processed frames, which is adjusted by the
number of frames currently buffered by the HPI driver.
There is basic support for detecting overflow and underflow. The HPI interface does not
explicitly indicate this, so thresholds on buffer levels are used in combination with
stream state. Recovery from overflow and underflow is left to the PA client.
Blocking streams are also implemented. It makes use of the same polling routines that
the callback interface uses, in order to prevent the allocation of variable-sized
buffers during reading and writing. The framesPerBuffer parameter is therefore still
buffers during reading and writing. The framesPerBuffer parameter is therefore still
relevant, and this can be increased in the blocking case to improve efficiency.
The implementation contains extensive reporting macros (slightly modified PA_ENSURE and
PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback.
Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback
and friends) is not implemented yet. All output is primed with silence.
Please send bug reports etc. to Ludwig Schwardt <schwardt@sun.ac.za>
*/
@ -157,6 +157,7 @@
#include "pa_cpuload.h" /* CPU load measurer */
#include "pa_process.h" /* Buffer processor */
#include "pa_converters.h" /* PaUtilZeroer */
#include "pa_debugprint.h"
/* -------------------------------------------------------------------------- */
@ -305,9 +306,9 @@ PaAsiHpiDeviceInfo;
during the Active state of PortAudio (due to underruns) and also during CallBackFinished in
the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped
PortAudio state, by may also occur in the CallbackFinished state when recording is finished.
Here is a rough match-up:
PortAudio state => HPI state
--------------- ---------
Active => HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED)
@ -328,7 +329,7 @@ typedef struct PaAsiHpiStreamComponent
/** Device information (HPI handles, etc) */
PaAsiHpiDeviceInfo *hpiDevice;
/** Stream handle, as passed to HPI interface.
HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same...
HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same...
(both are HW32 up to version 3.00 of ASIHPI, and hopefully they stay that way) */
HPI_HISTREAM hpiStream;
/** Stream format, as passed to HPI interface */
@ -377,7 +378,7 @@ typedef struct PaAsiHpiStream
int neverDropInput;
/** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data.
It went here instead of to each stream component, as the stream component buffer setup in
PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode.
PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode.
(Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */
void **blockingUserBufferCopy;
@ -408,7 +409,7 @@ typedef struct PaAsiHpiStreamInfo
/** Number of frames played/recorded since last stream reset */
HW32 frameCounter;
/** Amount of data (in bytes) in hardware (on-card) buffer.
This differs from dataSize if bus mastering (BBM) is used, which introduces another
This differs from dataSize if bus mastering (BBM) is used, which introduces another
driver-level buffer to which dataSize/bufferSize then refers. */
HW32 auxDataSize;
/** Total number of data frames currently buffered by HPI driver (host + hw buffers) */
@ -517,9 +518,9 @@ static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long num
This compiles a list of all HPI adapters, and registers a PA device for each input and
output stream it finds. Most errors are ignored, as missing or erroneous devices are
simply skipped.
@param hpiHostApi Pointer to HPI host API struct
@return PortAudio error code (only paInsufficientMemory in practice)
*/
static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi )
@ -726,11 +727,11 @@ error:
This is the only function exported beyond this file. It is called by PortAudio to initialize
the host API. It stores API info, finds and registers all devices, and sets up callback and
blocking interfaces.
@param hostApi Pointer to host API struct
@param hostApiIndex Index of current (HPI) host API
@return PortAudio error code
*/
PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
@ -807,7 +808,7 @@ error:
/** Terminate host API implementation.
This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct
memory. It should be called once for every PaAsiHpi_Initialize call.
@param Pointer to host API struct
*/
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
@ -853,9 +854,9 @@ error:
/** Converts PortAudio sample format to equivalent HPI format.
@param paFormat PortAudio sample format
@return HPI sample format
*/
static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
@ -887,9 +888,9 @@ static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
/** Converts HPI sample format to equivalent PortAudio format.
@param paFormat HPI sample format
@return PortAudio sample format
*/
static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat )
@ -919,20 +920,20 @@ static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat )
/** Creates HPI format struct based on PortAudio parameters.
This also does some checks to see whether the desired format is valid, and whether
This also does some checks to see whether the desired format is valid, and whether
the device allows it. This only checks the format of one half (input or output) of the
PortAudio stream.
@param hostApi Pointer to host API struct
@param parameters Pointer to stream parameter struct
@param sampleRate Desired sample rate
@param hpiDevice Pointer to HPI device struct
@param hpiFormat Resulting HPI format returned here
@return PortAudio error code (typically indicating a problem with stream format)
*/
static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
@ -1005,13 +1006,13 @@ static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostAp
/** Open HPI input stream with given format.
This attempts to open HPI input stream with desired format. If the format is not supported
or the device is unavailable, the stream is closed and a PortAudio error code is returned.
@param hostApi Pointer to host API struct
@param hpiDevice Pointer to HPI device struct
@param hpiFormat Pointer to HPI format struct
@return PortAudio error code (typically indicating a problem with stream format or device)
*/
static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
@ -1060,13 +1061,13 @@ error:
/** Open HPI output stream with given format.
This attempts to open HPI output stream with desired format. If the format is not supported
or the device is unavailable, the stream is closed and a PortAudio error code is returned.
@param hostApi Pointer to host API struct
@param hpiDevice Pointer to HPI device struct
@param hpiFormat Pointer to HPI format struct
@return PortAudio error code (typically indicating a problem with stream format or device)
*/
static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
@ -1115,15 +1116,15 @@ error:
/** Checks whether the desired stream formats and devices are supported
(for both input and output).
This is done by actually opening the appropriate HPI streams and closing them again.
@param hostApi Pointer to host API struct
@param inputParameters Pointer to stream parameter struct for input side of stream
@param outputParameters Pointer to stream parameter struct for output side of stream
@param sampleRate Desired sample rate
@return PortAudio error code (paFormatIsSupported on success)
*/
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
@ -1177,11 +1178,11 @@ error:
/** Obtain HPI stream information.
This obtains info such as stream state and available data/space in buffers. It also
estimates whether an underflow or overflow occurred.
@param streamComp Pointer to stream component (input or output) to query
@param info Pointer to stream info struct that will contain result
@return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError)
*/
static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info )
@ -1262,9 +1263,9 @@ error:
/** Display stream component information for debugging purposes.
@param streamComp Pointer to stream component (input or output) to query
@param stream Pointer to stream struct which contains the component above
*/
static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
@ -1391,7 +1392,7 @@ static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
/** Display stream information for debugging purposes.
@param stream Pointer to stream to query
*/
static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
@ -1457,25 +1458,25 @@ static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
/** Determine buffer sizes and allocate appropriate stream buffers.
This attempts to allocate a BBM (host) buffer for the HPI stream component (either input
This attempts to allocate a BBM (host) buffer for the HPI stream component (either input
or output, as both have similar buffer needs). Not all AudioScience adapters support BBM,
in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen
as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the
estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an
appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream
in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen
as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the
estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an
appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream
buffer which serves as the PortAudio host buffer for this implementation is allocated.
This buffer contains an integer number of user buffers, to simplify buffer adaption in the
buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate
an HPI host buffer of the desired size.
@param streamComp Pointer to stream component struct
@param pollingInterval Polling interval for stream, in milliseconds
@param framesPerPaHostBuffer Size of PortAudio host buffer, in frames
@param suggestedLatency Suggested latency for stream component, in seconds
@return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory)
*/
static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval,
@ -1532,7 +1533,7 @@ static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32
}
}
/* Choose closest memory block boundary (HPI API document states that
"a buffer size of Nx4096 - 20 makes the best use of memory"
"a buffer size of Nx4096 - 20 makes the best use of memory"
(under the entry for HPI_StreamEstimateBufferSize)) */
bbmBufferSize = ((HW32)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20;
streamComp->hostBufferSize = bbmBufferSize;
@ -1646,28 +1647,28 @@ error:
based on the suggested latency. It then opens each requested stream direction with the
appropriate stream format, and allocates the required stream buffers. It sets up the
various PortAudio structures dealing with streams, and estimates the stream latency.
See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters.
@param hostApi Pointer to host API struct
@param s List of open streams, where successfully opened stream will go
@param inputParameters Pointer to stream parameter struct for input side of stream
@param outputParameters Pointer to stream parameter struct for output side of stream
@param sampleRate Desired sample rate
@param framesPerBuffer Desired number of frames per buffer passed to user callback
@param framesPerBuffer Desired number of frames per buffer passed to user callback
(or chunk size for blocking stream)
@param streamFlags Stream flags
@param streamCallback Pointer to user callback function (zero for blocking interface)
@param userData Pointer to user data that will be passed to callback function along with data
@return PortAudio error code
*/
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
@ -1860,12 +1861,12 @@ error:
/** Close PortAudio stream.
When CloseStream() is called, the multi-api layer ensures that the stream has already
When CloseStream() is called, the multi-api layer ensures that the stream has already
been stopped or aborted. This closes the underlying HPI streams and deallocates stream
buffers and structs.
@param s Pointer to PortAudio stream
@return PortAudio error code
*/
static PaError CloseStream( PaStream *s )
@ -1919,9 +1920,9 @@ error:
This resets the output stream and uses PortAudio helper routines to fill the
temp buffer with silence. It then writes two host buffers to the stream. This is supposed
to be called before the stream is started. It has no effect on input-only streams.
@param stream Pointer to stream struct
@return PortAudio error code
*/
static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream )
@ -1962,14 +1963,14 @@ error:
/** Start HPI streams (both input + output).
This starts all HPI streams in the PortAudio stream. Output streams are first primed with
silence, if required. After this call the PA stream is in the Active state.
@todo Implement priming via the user callback
@param stream Pointer to stream struct
@param outputPrimed True if output is already primed (if false, silence will be loaded before starting)
@return PortAudio error code
@return PortAudio error code
*/
static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed )
{
@ -2006,10 +2007,10 @@ error:
The thread will then take care of starting the HPI streams, and this function will block
until the streams actually start. In the case of a blocking interface, the HPI streams
are simply started.
@param s Pointer to PortAudio stream
@return PortAudio error code
@return PortAudio error code
*/
static PaError StartStream( PaStream *s )
{
@ -2025,7 +2026,7 @@ static PaError StartStream( PaStream *s )
{
/* Create and start callback engine thread */
/* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */
PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1. ) );
PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 ) );
}
else
{
@ -2040,15 +2041,15 @@ error:
/** Stop HPI streams (input + output), either softly or abruptly.
If abort is false, the function blocks until the output stream is drained, otherwise it
stops immediately and discards data in the stream hardware buffers.
This function is safe to call from the callback engine thread as well as the main thread.
@param stream Pointer to stream struct
@param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
@return PortAudio error code
@return PortAudio error code
*/
static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort )
{
@ -2098,20 +2099,20 @@ error:
/** Stop or abort PortAudio stream.
This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream),
as opposed to the situation when the callback finishes with a result other than paContinue.
If a stream is in callback mode we will have to inspect whether the background thread has
finished, or we will have to take it out. In either case we join the thread before returning.
In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain).
as opposed to the situation when the callback finishes with a result other than paContinue.
If a stream is in callback mode we will have to inspect whether the background thread has
finished, or we will have to take it out. In either case we join the thread before returning.
In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain).
The PortAudio stream will be in the Stopped state after a call to this function.
Don't call this from the callback engine thread!
Don't call this from the callback engine thread!
@param stream Pointer to stream struct
@param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
@return PortAudio error code
*/
static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort )
@ -2151,10 +2152,10 @@ error:
/** Stop PortAudio stream.
This blocks until the output buffers are drained.
@param s Pointer to PortAudio stream
@return PortAudio error code
@return PortAudio error code
*/
static PaError StopStream( PaStream *s )
{
@ -2164,10 +2165,10 @@ static PaError StopStream( PaStream *s )
/** Abort PortAudio stream.
This discards any existing data in output buffers and stops the stream immediately.
@param s Pointer to PortAudio stream
@return PortAudio error code
@return PortAudio error code
*/
static PaError AbortStream( PaStream *s )
{
@ -2176,14 +2177,14 @@ static PaError AbortStream( PaStream *s )
/** Determine whether the stream is stopped.
A stream is considered to be stopped prior to a successful call to StartStream and after
a successful call to StopStream or AbortStream. If a stream callback returns a value other
A stream is considered to be stopped prior to a successful call to StartStream and after
a successful call to StopStream or AbortStream. If a stream callback returns a value other
than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state).
@param s Pointer to PortAudio stream
@return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or
a PaErrorCode (which are always negative) if PortAudio is not initialized or an
a PaErrorCode (which are always negative) if PortAudio is not initialized or an
error is encountered.
*/
static PaError IsStreamStopped( PaStream *s )
@ -2196,14 +2197,14 @@ static PaError IsStreamStopped( PaStream *s )
/** Determine whether the stream is active.
A stream is active after a successful call to StartStream(), until it becomes inactive either
as a result of a call to StopStream() or AbortStream(), or as a result of a return value
other than paContinue from the stream callback. In the latter case, the stream is considered
A stream is active after a successful call to StartStream(), until it becomes inactive either
as a result of a call to StopStream() or AbortStream(), or as a result of a return value
other than paContinue from the stream callback. In the latter case, the stream is considered
inactive after the last buffer has finished playing.
@param s Pointer to PortAudio stream
@return Returns one (1) when the stream is active (i.e. playing or recording audio),
@return Returns one (1) when the stream is active (i.e. playing or recording audio),
zero (0) when not playing, or a PaErrorCode (which are always negative)
if PortAudio is not initialized or an error is encountered.
*/
@ -2217,12 +2218,12 @@ static PaError IsStreamActive( PaStream *s )
/** Returns current stream time.
This corresponds to the system clock. The clock should run continuously while the stream
This corresponds to the system clock. The clock should run continuously while the stream
is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter
is not good enough.
@param s Pointer to PortAudio stream
@return Stream time, in seconds
*/
static PaTime GetStreamTime( PaStream *s )
@ -2232,9 +2233,9 @@ static PaTime GetStreamTime( PaStream *s )
/** Returns CPU load.
@param s Pointer to PortAudio stream
@return CPU load (0.0 if blocking interface is used)
*/
static double GetStreamCpuLoad( PaStream *s )
@ -2251,8 +2252,8 @@ static double GetStreamCpuLoad( PaStream *s )
abruptly). It also calls the user-supplied StreamFinished callback, and sets the
stream state to CallbackFinished if it was reached via a non-paContinue return from
the user callback function.
@param userData A pointer to an open stream previously created with Pa_OpenStream
@param userData A pointer to an open stream previously created with Pa_OpenStream
*/
static void PaAsiHpi_OnThreadExit( void *userData )
{
@ -2273,7 +2274,7 @@ static void PaAsiHpi_OnThreadExit( void *userData )
}
/* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state)
and implicit stops via paComplete/paAbort (leading to CallbackFinished state)
and implicit stops via paComplete/paAbort (leading to CallbackFinished state)
end up here - need another flag to remind us which is the case */
if( stream->callbackFinished )
stream->state = paAsiHpiCallbackFinishedState;
@ -2281,20 +2282,20 @@ static void PaAsiHpi_OnThreadExit( void *userData )
/** Wait until there is enough frames to fill a host buffer.
The routine attempts to sleep until at least a full host buffer can be retrieved from the
The routine attempts to sleep until at least a full host buffer can be retrieved from the
input HPI stream and passed to the output HPI stream. It will first sleep until enough
output space is available, as this is usually easily achievable. If it is an output-only
stream, it will also sleep if the hardware buffer is too full, thereby throttling the
filling of the output buffer and reducing output latency. The routine then blocks until
enough input samples are available, unless this will cause an output underflow. In the
process, input overflows and output underflows are indicated.
@param stream Pointer to stream struct
@param framesAvail Returns the number of available frames
@param cbFlags Overflows and underflows indicated in here
@return PortAudio error code (only paUnanticipatedHostError expected)
*/
static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
@ -2369,9 +2370,9 @@ static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *fr
/** @todo The paInputOverflow flag should be set in the callback containing the
first input sample following the overflow. That means the block currently sitting
at the fore-front of recording, i.e. typically the one containing the newest (last)
sample in the HPI buffer system. This is most likely not the same as the current
block of data being passed to the callback. The current overflow should ideally
be noted in an overflow list of sorts, with an indication of when it should be
sample in the HPI buffer system. This is most likely not the same as the current
block of data being passed to the callback. The current overflow should ideally
be noted in an overflow list of sorts, with an indication of when it should be
reported. The trouble starts if there are several separate overflow incidents,
given a big input buffer. Oh well, something to try out later... */
if( info.overflow )
@ -2402,18 +2403,18 @@ error:
/** Obtain recording, current and playback timestamps of stream.
The current time is determined by the system clock. This "now" timestamp occurs at the
forefront of recording (and playback in the full-duplex case), which happens later than the
forefront of recording (and playback in the full-duplex case), which happens later than the
input timestamp by an amount equal to the total number of recorded frames in the input buffer.
The output timestamp indicates when the next generated sample will actually be played. This
The output timestamp indicates when the next generated sample will actually be played. This
happens after all the samples currently in the output buffer are played. The output timestamp
therefore follows the current timestamp by an amount equal to the number of frames yet to be
played back in the output buffer.
If the current timestamp is the present, the input timestamp is in the past and the output
timestamp is in the future.
@param stream Pointer to stream struct
@param timeInfo Pointer to timeInfo struct that will contain timestamps
*/
static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo )
@ -2448,16 +2449,16 @@ static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallback
/** Read from HPI input stream and register buffers.
This reads data from the HPI input stream (if it exists) and registers the temp stream
buffers of both input and output streams with the buffer processor. In the process it also
buffers of both input and output streams with the buffer processor. In the process it also
handles input underflows in the full-duplex case.
@param stream Pointer to stream struct
@param numFrames On entrance the number of available frames, on exit the number of
@param numFrames On entrance the number of available frames, on exit the number of
received frames
@param cbFlags Indicates overflows and underflows
@return PortAudio error code
*/
static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames,
@ -2531,11 +2532,11 @@ error:
This completes the processing cycle by writing the temp buffer to the HPI interface.
Additional output underflows are caught before data is written to the stream, as this
action typically remedies the underflow and hides it in the process.
@param stream Pointer to stream struct
@param numFrames The number of frames to write to the output stream
@param cbFlags Indicates overflows and underflows
*/
static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
@ -2573,11 +2574,11 @@ error:
/** Main callback engine.
This function runs in a separate thread and does all the work of fetching audio data from
the AudioScience card via the HPI interface, feeding it to the user callback via the buffer
the AudioScience card via the HPI interface, feeding it to the user callback via the buffer
processor, and delivering the resulting output data back to the card via HPI calls.
It is started and terminated when the PortAudio stream is started and stopped, and starts
the HPI streams on startup.
@param userData A pointer to an open stream previously created with Pa_OpenStream.
*/
static void *CallbackThreadFunc( void *userData )
@ -2720,16 +2721,16 @@ error:
functions can be guaranteed to only be called for blocking streams. */
/** Read data from input stream.
This reads the indicated number of frames into the supplied buffer from an input stream,
This reads the indicated number of frames into the supplied buffer from an input stream,
and blocks until this is done.
@param s Pointer to PortAudio stream
@param buffer Pointer to buffer that will receive interleaved data (or an array of pointers
to a buffer for each non-interleaved channel)
@param frames Number of frames to read from stream
@return PortAudio error code (also indicates overflow via paInputOverflowed)
*/
static PaError ReadStream( PaStream *s,
@ -2787,16 +2788,16 @@ error:
/** Write data to output stream.
This writes the indicated number of frames from the supplied buffer to an output stream,
This writes the indicated number of frames from the supplied buffer to an output stream,
and blocks until this is done.
@param s Pointer to PortAudio stream
@param buffer Pointer to buffer that provides interleaved data (or an array of pointers
to a buffer for each non-interleaved channel)
@param frames Number of frames to write to stream
@return PortAudio error code (also indicates underflow via paOutputUnderflowed)
*/
static PaError WriteStream( PaStream *s,
@ -2854,9 +2855,9 @@ error:
/** Number of frames that can be read from input stream without blocking.
@param s Pointer to PortAudio stream
@return Number of frames, or PortAudio error code
*/
static signed long GetStreamReadAvailable( PaStream *s )
@ -2882,9 +2883,9 @@ error:
/** Number of frames that can be written to output stream without blocking.
@param s Pointer to PortAudio stream
@return Number of frames, or PortAudio error code
*/
static signed long GetStreamWriteAvailable( PaStream *s )

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_asio.cpp 1146 2006-11-23 04:39:43Z rossb $
* $Id: pa_asio.cpp 1230 2007-06-15 16:16:33Z rossb $
* Portable Audio I/O Library for ASIO Drivers
*
* Author: Stephane Letz
@ -132,7 +132,7 @@
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
/* This version of pa_asio.cpp is currently only targetted at Win32,
It would require a few tweaks to work with pre-OS X Macintosh.
@ -1021,6 +1021,7 @@ static ASIOSampleRate defaultSampleRateSearchOrder_[]
/* we look up IsDebuggerPresent at runtime incase it isn't present (on Win95 for example) */
typedef BOOL (WINAPI *IsDebuggerPresentPtr)(VOID);
IsDebuggerPresentPtr IsDebuggerPresent_ = 0;
//FARPROC IsDebuggerPresent_ = 0; // this is the current way to do it apparently according to davidv
PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
@ -1677,6 +1678,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaAsioDriverInfo *driverInfo;
int *inputChannelSelectors = 0;
int *outputChannelSelectors = 0;
bool isExternal = false;
/* unless we move to using lower level ASIO calls, we can only have
one device open at a time */
@ -1792,6 +1794,30 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
/* davidv: listing ASIO Clock sources, there is an ongoing investigation by
me about whether or not call ASIOSetSampleRate if an external Clock is
used. A few drivers expected different things here */
{
ASIOClockSource clocks[32];
long numSources=32;
asioError = ASIOGetClockSources(clocks, &numSources);
if( asioError != ASE_OK ){
PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) ));
}else{
PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources ));
for (int i=0;i<numSources;++i){
PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i,clocks[i].name, clocks[i].isCurrentSource ));
/*
If you have problems with some drivers when externally clocked,
uncomment the next two lines
*/
//if (clocks[i].isCurrentSource)
// isExternal = true;
}
}
}
// check that the device supports the requested sample rate
asioError = ASIOCanSampleRate( sampleRate );
@ -1821,7 +1847,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (oldRate != sampleRate){
PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
asioError = ASIOSetSampleRate( sampleRate );
asioError = ASIOSetSampleRate( isExternal?0:sampleRate );
/* Set sample rate */
if( asioError != ASE_OK )
{

View File

@ -73,17 +73,24 @@ the stream with it. See below for creating a channel map.
Known issues:
- Latency: Latency settings are ignored in most cases. Exceptions are when
doing I/O between different devices and as a hint for selecting a realtively
low or relatively high latency in conjunction with
paHostFramesPerBufferUnspecified. Latency settings are always automatically
bound to "safe" values, however, so setting extreme values here should not be
- Buffering: No buffering beyond that provided by core audio is provided
except where absolutely needed for the implementation to work. This may cause
issues with large framesPerBuffer settings and it also means that no additional
latency will be provided even if a large latency setting is selected.
- Latency: Latency settings are generally ignored. They may be used as a
hint for buffer size in paHostFramesPerBufferUnspecified, or the value may
be used in cases where additional buffering is needed, such as doing input and
output on seperate devices. Latency settings are always automatically bound
to "safe" values, however, so setting extreme values here should not be
an issue.
- Buffer Size: paHostFramesPerBufferUnspecified and specific host buffer sizes
are supported. paHostFramesPerBufferUnspecified works best in "pro" mode,
where the buffer size and sample rate of the audio device is most likely
to match the expected values.
to match the expected values. In the case of paHostFramesPerBuffer, an
appropriate framesPerBuffer value will be used that guarantees minimum
requested latency if that's possible.
- Timing info. It reports on stream time, but I'm probably doing something
wrong since patest_sine_time often reports negative latency numbers. Also,
@ -111,8 +118,7 @@ render quyality property is used to set the sample rate conversion quality
as "documented" here:
http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html
- x86/Universal Binary: to build a universal binary, be sure to use
the darwin makefile and not the usual configure && make combo.
- x86/Universal Binary: Universal binaries can be build.

View File

@ -65,6 +65,7 @@
#include "pa_mac_core_internal.h"
#include <string.h> /* strlen(), memcmp() etc. */
#include <libkern/OSAtomic.h>
#include "pa_mac_core.h"
#include "pa_mac_core_utilities.h"
@ -98,7 +99,7 @@ void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, const unsigned long
/*
* Function declared in pa_mac_core.h. Adds channel mapping to a PaMacCoreStreamInfoStruct
*/
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const long * const channelMap, const unsigned long channelMapSize )
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, const unsigned long channelMapSize )
{
data->channelMap = channelMap;
data->channelMapSize = channelMapSize;
@ -261,11 +262,12 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
int isInput);
static PaError OpenAndSetupOneAudioUnit(
const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
const unsigned long requestedFramesPerBuffer,
unsigned long *actualInputFramesPerBuffer,
unsigned long *actualOutputFramesPerBuffer,
const UInt32 requestedFramesPerBuffer,
UInt32 *actualInputFramesPerBuffer,
UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@ -277,6 +279,55 @@ static PaError OpenAndSetupOneAudioUnit(
#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
/*
* Callback for setting over/underrun flags.
*
*/
static OSStatus xrunCallback(
AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
void* inClientData)
{
PaMacCoreStream *stream = (PaMacCoreStream *) inClientData;
if( stream->state != ACTIVE )
return 0; //if the stream isn't active, we don't care if the device is dropping
if( isInput )
OSAtomicOr32( paInputUnderflow, (uint32_t *)&(stream->xrunFlags) );
else
OSAtomicOr32( paOutputOverflow, (uint32_t *)&(stream->xrunFlags) );
return 0;
}
/*
* Callback called when starting or stopping a stream.
*/
static void startStopCallback(
void * inRefCon,
AudioUnit ci,
AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement )
{
PaMacCoreStream *stream = (PaMacCoreStream *) inRefCon;
UInt32 isRunning;
UInt32 size = sizeof( isRunning );
assert( !AudioUnitGetProperty( ci, kAudioOutputUnitProperty_IsRunning, inScope, inElement, &isRunning, &size ) );
if( isRunning )
return; //We are only interested in when we are stopping
// -- if we are using 2 I/O units, we only need one notification!
if( stream->inputUnit && stream->outputUnit && stream->inputUnit != stream->outputUnit && ci == stream->inputUnit )
return;
PaStreamFinishedCallback *sfc = stream->streamRepresentation.streamFinishedCallback;
if( stream->state == STOPPING )
stream->state = STOPPED ;
if( sfc )
sfc( stream->streamRepresentation.userData );
}
/*currently, this is only used in initialization, but it might be modified
to be used when the list of devices changes.*/
static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
@ -737,11 +788,12 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
}
static PaError OpenAndSetupOneAudioUnit(
const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
const unsigned long requestedFramesPerBuffer,
unsigned long *actualInputFramesPerBuffer,
unsigned long *actualOutputFramesPerBuffer,
const UInt32 requestedFramesPerBuffer,
UInt32 *actualInputFramesPerBuffer,
UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@ -753,7 +805,7 @@ static PaError OpenAndSetupOneAudioUnit(
Component comp;
/*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
AudioStreamBasicDescription desiredFormat;
OSErr result = noErr;
OSStatus result = noErr;
PaError paResult = paNoError;
int line = 0;
UInt32 callbackKey;
@ -881,7 +933,7 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
if( outStreamParams )
if( outStreamParams && outStreamParams != inStreamParams )
{
*audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
ERR_WRAP( AudioUnitSetProperty( *audioUnit,
@ -891,6 +943,19 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
/* -- add listener for dropouts -- */
ERR_WRAP( AudioDeviceAddPropertyListener( *audioDevice,
0,
outStreamParams ? false : true,
kAudioDeviceProcessorOverload,
xrunCallback,
(void *)stream) );
/* -- listen for stream start and stop -- */
ERR_WRAP( AudioUnitAddPropertyListener( *audioUnit,
kAudioOutputUnitProperty_IsRunning,
startStopCallback,
(void *)stream ) );
/* -- set format -- */
bzero( &desiredFormat, sizeof(desiredFormat) );
@ -1010,7 +1075,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Input,
OUTPUT_ELEMENT,
actualOutputFramesPerBuffer,
sizeof(unsigned long) ) );
sizeof(*actualOutputFramesPerBuffer) ) );
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
@ -1025,7 +1090,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Output,
INPUT_ELEMENT,
actualInputFramesPerBuffer,
sizeof(unsigned long) ) );
sizeof(*actualInputFramesPerBuffer) ) );
/* Don't know why this causes problems
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
@ -1305,7 +1370,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/*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;
size_t size = sizeof( audioRange );
UInt32 size = sizeof( audioRange );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
0,
@ -1315,6 +1380,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MAX( requested, audioRange.mMinimum );
}
size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@ -1328,7 +1394,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* 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;
size_t size = sizeof( audioRange );
UInt32 size = sizeof( audioRange );
requested = MIN( requested, 1024 );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
@ -1339,6 +1405,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MIN( requested, audioRange.mMaximum );
}
size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@ -1359,17 +1426,22 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* -- Now we actually open and setup streams. -- */
if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
{ /* full duplex. One device. */
result = OpenAndSetupOneAudioUnit( inputParameters,
UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
result = OpenAndSetupOneAudioUnit( stream,
inputParameters,
outputParameters,
framesPerBuffer,
&(stream->inputFramesPerBuffer),
&(stream->outputFramesPerBuffer),
&inputFramesPerBuffer,
&outputFramesPerBuffer,
auhalHostApi,
&(stream->inputUnit),
&(stream->inputSRConverter),
&(stream->inputDevice),
sampleRate,
stream );
stream->inputFramesPerBuffer = inputFramesPerBuffer;
stream->outputFramesPerBuffer = outputFramesPerBuffer;
stream->outputUnit = stream->inputUnit;
stream->outputDevice = stream->inputDevice;
if( result != paNoError )
@ -1377,11 +1449,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
else
{ /* full duplex, different devices OR simplex */
result = OpenAndSetupOneAudioUnit( NULL,
UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
result = OpenAndSetupOneAudioUnit( stream,
NULL,
outputParameters,
framesPerBuffer,
NULL,
&(stream->outputFramesPerBuffer),
&outputFramesPerBuffer,
auhalHostApi,
&(stream->outputUnit),
NULL,
@ -1390,10 +1465,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
result = OpenAndSetupOneAudioUnit( inputParameters,
result = OpenAndSetupOneAudioUnit( stream,
inputParameters,
NULL,
framesPerBuffer,
&(stream->inputFramesPerBuffer),
&inputFramesPerBuffer,
NULL,
auhalHostApi,
&(stream->inputUnit),
@ -1403,6 +1479,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
stream->inputFramesPerBuffer = inputFramesPerBuffer;
stream->outputFramesPerBuffer = outputFramesPerBuffer;
}
if( stream->inputUnit ) {
@ -1727,14 +1805,14 @@ static OSStatus AudioIOProc( void *inRefCon,
* we do not use the input SR converter or the input ring buffer.
*
*/
OSErr err = 0;
OSStatus err = 0;
unsigned long frames;
/* -- start processing -- */
PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
&timeInfo,
stream->xrunFlags );
stream->xrunFlags = 0;
stream->xrunFlags = 0; //FIXME: this flag also gets set outside by a callback, which calls the xrunCallback function. It should be in the same thread as the main audio callback, but the apple docs just use the word "usually" so it may be possible to loose an xrun notification, if that callback happens here.
/* -- compute frames. do some checks -- */
assert( ioData->mNumberBuffers == 1 );
@ -1868,7 +1946,7 @@ static OSStatus AudioIOProc( void *inRefCon,
PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1 );
} else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) {
/*we underflowed. take what data we can, zero the rest.*/
float data[frames*inChan];
unsigned char data[frames*inChan*flsz];
if( size1 )
memcpy( data, data1, size1 );
if( size2 )
@ -1922,7 +2000,7 @@ static OSStatus AudioIOProc( void *inRefCon,
* if this is an input-only stream, we need to process it more,
* otherwise, we let the output case deal with it.
*/
OSErr err = 0;
OSStatus err = 0;
int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ;
/* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */
do {
@ -2054,6 +2132,18 @@ static PaError CloseStream( PaStream* s )
VDBUG( ( "Closing stream.\n" ) );
if( stream ) {
if( stream->outputUnit )
AudioDeviceRemovePropertyListener( stream->outputDevice,
0,
false,
kAudioDeviceProcessorOverload,
xrunCallback );
if( stream->inputUnit && stream->outputUnit != stream->inputUnit )
AudioDeviceRemovePropertyListener( stream->inputDevice,
0,
true,
kAudioDeviceProcessorOverload,
xrunCallback );
if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
AudioUnitUninitialize( stream->outputUnit );
CloseComponent( stream->outputUnit );
@ -2089,11 +2179,10 @@ static PaError CloseStream( PaStream* s )
return result;
}
static PaError StartStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
OSErr result = noErr;
OSStatus result = noErr;
VVDBUG(("StartStream()\n"));
VDBUG( ( "Starting stream.\n" ) );
@ -2120,11 +2209,25 @@ static PaError StartStream( PaStream *s )
#undef ERR_WRAP
}
// it's not clear from appl's docs that this really waits
// until all data is flushed.
static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element )
{
Boolean isRunning = 1;
while( isRunning ) {
UInt32 s = sizeof( isRunning );
ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s );
if( err )
return err;
Pa_Sleep( 100 );
}
return noErr;
}
static PaError StopStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
OSErr result = noErr;
OSStatus result = noErr;
PaError paErr;
VVDBUG(("StopStream()\n"));
@ -2140,6 +2243,8 @@ static PaError StopStream( PaStream *s )
if( stream->inputUnit == stream->outputUnit && stream->inputUnit )
{
ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) );
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,0) );
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) );
ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) );
}
@ -2148,11 +2253,13 @@ static PaError StopStream( PaStream *s )
if( stream->inputUnit )
{
ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) );
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1));
}
if( stream->outputUnit )
{
ERR_WRAP(AudioOutputUnitStop(stream->outputUnit));
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0) );
ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0));
}
}

View File

@ -85,6 +85,22 @@ static size_t computeSampleSizeFromFormat( PaSampleFormat format )
default: return 0;
}
}
/*
* Same as computeSampleSizeFromFormat, except that if
* the size is not a power of two, it returns the next power of two up
*/
static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format )
{
switch( format ) {
case paFloat32: return 4;
case paInt32: return 4;
case paInt24: return 4;
case paInt16: return 2;
case paInt8: case paUInt8: return 1;
default: return 0;
}
}
/*
@ -114,10 +130,14 @@ PaError initializeBlioRingBuffers(
blio->outputRingBuffer.buffer = NULL;
/* initialize simple data */
blio->ringBufferFrames = ringBufferSize;
blio->inputSampleFormat = inputSampleFormat;
blio->inputSampleSize = computeSampleSizeFromFormat(inputSampleFormat);
blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat);
blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat);
blio->outputSampleFormat = outputSampleFormat;
blio->outputSampleSize = computeSampleSizeFromFormat(outputSampleFormat);
blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat);
blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat);
blio->framesPerBuffer = framesPerBuffer;
blio->inChan = inChan;
blio->outChan = outChan;
@ -142,7 +162,7 @@ PaError initializeBlioRingBuffers(
result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
#endif
if( inChan ) {
data = calloc( ringBufferSize, blio->inputSampleSize );
data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan );
if( !data )
{
result = paInsufficientMemory;
@ -151,11 +171,11 @@ PaError initializeBlioRingBuffers(
assert( 0 == PaUtil_InitializeRingBuffer(
&blio->inputRingBuffer,
ringBufferSize*blio->inputSampleSize,
ringBufferSize*blio->inputSampleSizePow2*inChan,
data ) );
}
if( outChan ) {
data = calloc( ringBufferSize, blio->outputSampleSize );
data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan );
if( !data )
{
result = paInsufficientMemory;
@ -164,7 +184,7 @@ PaError initializeBlioRingBuffers(
assert( 0 == PaUtil_InitializeRingBuffer(
&blio->outputRingBuffer,
ringBufferSize*blio->outputSampleSize,
ringBufferSize*blio->outputSampleSizePow2*outChan,
data ) );
}
@ -247,7 +267,8 @@ PaError resetBlioRingBuffers( PaMacBlio *blio )
bzero( blio->outputRingBuffer.buffer,
blio->outputRingBuffer.bufferSize );
/* Advance buffer */
PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan );
//PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
/* Update isOutputFull. */
#ifdef PA_MAC__BLIO_MUTEX
@ -332,10 +353,10 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->inputSampleSize * blio->inChan )
if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan )
OSAtomicOr32( paInputOverflow, &blio->statusFlags );
toRead = MIN( avail, frameCount * blio->inputSampleSize * blio->inChan );
toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan );
/* copy the data */
/*printf( "reading %d\n", toRead );*/
@ -352,14 +373,14 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->outputSampleSize * blio->outChan )
if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan )
OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
toWrite = MIN( avail, frameCount * blio->outputSampleSize * blio->outChan );
toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan );
if( toWrite != frameCount * blio->outputSampleSize * blio->outChan )
if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan )
bzero( ((char *)output)+toWrite,
frameCount * blio->outputSampleSize * blio->outChan - toWrite );
frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite );
/* copy the data */
/*printf( "writing %d\n", toWrite );*/
assert( toWrite == PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite ) );
@ -413,11 +434,11 @@ PaError ReadStream( PaStream* stream,
#endif
}
} while( avail == 0 );
toRead = MIN( avail, frames * blio->inputSampleSize * blio->inChan );
toRead -= toRead % blio->inputSampleSize * blio->inChan ;
toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan );
toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ;
PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead );
cbuf += toRead;
frames -= toRead / ( blio->inputSampleSize * blio->inChan );
frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan );
if( toRead == avail ) {
#ifdef PA_MAC_BLIO_MUTEX
@ -443,7 +464,7 @@ PaError ReadStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
OSAtomicAnd32( ~paInputOverflow, &blio->statusFlags );
OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags );
ret = paInputOverflowed;
}
@ -491,11 +512,11 @@ PaError WriteStream( PaStream* stream,
}
} while( avail == 0 );
toWrite = MIN( avail, frames * blio->outputSampleSize * blio->outChan );
toWrite -= toWrite % blio->outputSampleSize * blio->outChan ;
toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan );
toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ;
PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite );
cbuf += toWrite;
frames -= toWrite / ( blio->outputSampleSize * blio->outChan );
frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan );
#ifdef PA_MAC_BLIO_MUTEX
if( toWrite == avail ) {
@ -520,7 +541,7 @@ PaError WriteStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
OSAtomicAnd32( ~paOutputUnderflow, &blio->statusFlags );
OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
ret = paOutputUnderflowed;
}
@ -549,7 +570,7 @@ signed long GetStreamReadAvailable( PaStream* stream )
VVDBUG(("GetStreamReadAvailable()\n"));
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer )
/ ( blio->outputSampleSize * blio->outChan );
/ ( blio->outputSampleSizeActual * blio->outChan );
}
@ -559,6 +580,6 @@ signed long GetStreamWriteAvailable( PaStream* stream )
VVDBUG(("GetStreamWriteAvailable()\n"));
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer )
/ ( blio->outputSampleSize * blio->outChan );
/ ( blio->outputSampleSizeActual * blio->outChan );
}

View File

@ -79,10 +79,13 @@
typedef struct {
PaUtilRingBuffer inputRingBuffer;
PaUtilRingBuffer outputRingBuffer;
size_t ringBufferFrames;
PaSampleFormat inputSampleFormat;
size_t inputSampleSize;
size_t inputSampleSizeActual;
size_t inputSampleSizePow2;
PaSampleFormat outputSampleFormat;
size_t outputSampleSize;
size_t outputSampleSizeActual;
size_t outputSampleSizePow2;
size_t framesPerBuffer;

View File

@ -139,6 +139,7 @@ typedef struct PaMacCoreStream
/* We need to preallocate an inputBuffer for reading data. */
AudioBufferList inputAudioBufferList;
AudioTimeStamp startTime;
/* FIXME: instead of volatile, these should be properly memory barriered */
volatile PaStreamCallbackFlags xrunFlags;
volatile bool isTimeSet;
volatile enum {
@ -146,7 +147,8 @@ typedef struct PaMacCoreStream
and the user has called StopStream(). */
CALLBACK_STOPPED = 1, /* callback has requested stop,
but user has not yet called StopStream(). */
STOPPING = 2, /* The stream is in the process of closing.
STOPPING = 2, /* The stream is in the process of closing
because the user has called StopStream.
This state is just used internally;
externally it is indistinguishable from
ACTIVE.*/

View File

@ -199,10 +199,19 @@ PaError PaMacCore_SetError(OSStatus error, int line, int isError)
else
errorType = "Warning";
if ((int)error < -99999 || (int)error > 99999)
DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText));
else
DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText));
char str[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4]))
{
str[0] = str[5] = '\'';
str[6] = '\0';
} else {
// no, format it as an integer
sprintf(str, "%d", (int)error);
}
DBUG(("%s on line %d: err='%s', msg=%s\n", errorType, line, str, errorText));
PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
@ -520,9 +529,9 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
const bool isOutput,
unsigned long requestedFramesPerBuffer,
unsigned long *actualFramesPerBuffer )
const bool isOutput,
UInt32 requestedFramesPerBuffer,
UInt32 *actualFramesPerBuffer )
{
UInt32 afpb;
const bool isInput = !isOutput;

View File

@ -199,7 +199,7 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
const bool isOutput,
unsigned long requestedFramesPerBuffer,
unsigned long *actualFramesPerBuffer );
const bool isOutput,
UInt32 requestedFramesPerBuffer,
UInt32 *actualFramesPerBuffer );
#endif /* PA_MAC_CORE_UTILITIES_H__*/

View File

@ -1,10 +1,10 @@
/*
* $Id: pa_win_ds.c 1116 2006-09-06 15:47:03Z rossb $
* $Id: pa_win_ds.c 1286 2007-09-26 21:34:23Z rossb $
* Portable Audio I/O Library DirectSound implementation
*
* Authors: Phil Burk, Robert Marsanyi & Ross Bencina
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2006 Ross Bencina, Phil Burk, Robert Marsanyi
* Copyright (c) 1999-2007 Ross Bencina, Phil Burk, Robert Marsanyi
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
@ -55,20 +55,34 @@
@todo implement IsFormatSupported
@todo check that CoInitialize() CoUninitialize() are always correctly
paired, even in error cases.
@todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
@todo make sure all buffers have been played before stopping the stream
when the stream callback returns paComplete
@todo retrieve default devices using the DRVM_MAPPER_PREFERRED_GET functions used in the wmme api
these wave device ids can be aligned with the directsound devices either by retrieving
the system interface device name using DRV_QUERYDEVICEINTERFACE or by using the wave device
id retrieved in KsPropertySetEnumerateCallback.
old TODOs from phil, need to work out if these have been done:
O- fix "patest_stop.c"
*/
#include <stdio.h>
#include <string.h> /* strlen() */
#include <windows.h>
#include <objbase.h>
/*
We are only using DX3 in here, no need to polute the namespace - davidv
*/
#define DIRECTSOUND_VERSION 0x0300
#include <dsound.h>
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
#include <dsconf.h>
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
#include "pa_util.h"
#include "pa_allocation.h"
@ -76,8 +90,13 @@
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
#include "pa_win_ds.h"
#include "pa_win_ds_dynlink.h"
#include "pa_win_waveformat.h"
#include "pa_win_wdmks_utils.h"
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "dsound.lib" )
@ -168,9 +187,12 @@ static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
typedef struct PaWinDsDeviceInfo
{
GUID guid;
GUID *lpGUID;
double sampleRates[3];
PaDeviceInfo inheritedDeviceInfo;
GUID guid;
GUID *lpGUID;
double sampleRates[3];
char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
} PaWinDsDeviceInfo;
typedef struct
@ -182,7 +204,8 @@ typedef struct
PaUtilAllocationGroup *allocations;
/* implementation specific data goes here */
PaWinDsDeviceInfo *winDsDeviceInfos;
char comWasInitialized;
} PaWinDsHostApiRepresentation;
@ -269,6 +292,7 @@ typedef struct DSDeviceNameAndGUID{
char *name; // allocated from parent's allocations, never deleted by this structure
GUID guid;
LPGUID lpGUID;
void *pnpInterface; // wchar_t* interface path, allocated using the DS host api's allocation group
} DSDeviceNameAndGUID;
typedef struct DSDeviceNameAndGUIDVector{
@ -280,6 +304,12 @@ typedef struct DSDeviceNameAndGUIDVector{
DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
} DSDeviceNameAndGUIDVector;
typedef struct DSDeviceNamesAndGUIDs{
PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
} DSDeviceNamesAndGUIDs;
static PaError InitializeDSDeviceNameAndGUIDVector(
DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
{
@ -326,6 +356,7 @@ static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidV
newItems[i].lpGUID = &newItems[i].guid;
memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
}
newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
}
LocalFree( guidVector->items );
@ -397,12 +428,153 @@ static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
return FALSE;
}
namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
++namesAndGUIDs->count;
--namesAndGUIDs->free;
return TRUE;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
{
size_t len;
wchar_t *result;
len = wcslen( source );
result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
wcscpy( result, source );
return result;
}
static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
{
int i;
DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
{
for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
{
if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
&& memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
{
deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
(char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
break;
}
}
}
else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
{
for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
{
if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
&& memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
{
deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
(char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
break;
}
}
}
return TRUE;
}
static GUID pawin_CLSID_DirectSoundPrivate =
{ 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
static GUID pawin_DSPROPSETID_DirectSoundDevice =
{ 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
static GUID pawin_IID_IKsPropertySet =
{ 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
/*
FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
with UNICODE file paths to the devices. The DS documentation mentions
at least two techniques by which these Interface paths can be found using IKsPropertySet on
the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
I tried both methods and only the second worked. I found two postings on the
net from people who had the same problem with the first method, so I think the method used here is
more common/likely to work. The probem is that IKsPropertySet_Get returns S_OK
but the fields of the device description are not filled in.
The mechanism we use works by registering an enumeration callback which is called for
every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
with the matching GUID and copies the pointer to the Interface path.
Note that we could have used this enumeration callback to perform the original
device enumeration, however we choose not to so we can disable this step easily.
Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
-- rossb
*/
static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
{
IClassFactory *pClassFactory;
if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
IKsPropertySet *pPropertySet;
if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
ULONG bytesReturned;
data.Callback = KsPropertySetEnumerateCallback;
data.Context = deviceNamesAndGUIDs;
IKsPropertySet_Get( pPropertySet,
&pawin_DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
NULL,
0,
&data,
sizeof(data),
&bytesReturned
);
IKsPropertySet_Release( pPropertySet );
}
pClassFactory->lpVtbl->Release( pClassFactory );
}
/*
The following code fragment, which I chose not to use, queries for the
device interface for a device with a specific GUID:
ULONG BytesReturned;
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
memset (&Property, 0, sizeof(Property));
Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
Property.DeviceId = *lpGUID;
hr = IKsPropertySet_Get( pPropertySet,
&pawin_DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
NULL,
0,
&Property,
sizeof(Property),
&BytesReturned
);
if( hr == S_OK )
{
//pnpInterface = Property.Interface;
}
*/
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
/*
GUIDs for emulated devices which we blacklist below.
@ -418,7 +590,6 @@ static double defaultSampleRateSearchOrder_[] =
{ 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
/************************************************************************************
** Extract capabilities from an output device, and add it to the device info list
** if successful. This function assumes that there is enough room in the
@ -427,18 +598,18 @@ static double defaultSampleRateSearchOrder_[] =
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddOutputDeviceInfoFromDirectSound(
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
{
PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
LPDIRECTSOUND lpDirectSound;
DSCAPS caps;
int deviceOK = TRUE;
PaError result = paNoError;
int i;
/* Copy GUID to the device info structure. Set pointer. */
if( lpGUID == NULL )
{
@ -449,7 +620,6 @@ static PaError AddOutputDeviceInfoFromDirectSound(
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
}
if( lpGUID )
{
@ -529,8 +699,39 @@ static PaError AddOutputDeviceInfoFromDirectSound(
if( deviceOK )
{
deviceInfo->maxInputChannels = 0;
/* Mono or stereo device? */
deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
/* DS output capabilities only indicate supported number of channels
using two flags which indicate mono and/or stereo.
We assume that stereo devices may support more than 2 channels
(as is the case with 5.1 devices for example) and so
set deviceOutputChannelCountIsKnown to 0 (unknown).
In this case OpenStream will try to open the device
when the user requests more than 2 channels, rather than
returning an error.
*/
if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
{
deviceInfo->maxOutputChannels = 2;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
}
else
{
deviceInfo->maxOutputChannels = 1;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( pnpInterface )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
if( count > 0 )
{
deviceInfo->maxOutputChannels = count;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
@ -547,8 +748,8 @@ static PaError AddOutputDeviceInfoFromDirectSound(
for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
{
if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
&& defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
&& defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
{
deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
break;
}
@ -614,11 +815,11 @@ static PaError AddOutputDeviceInfoFromDirectSound(
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddInputDeviceInfoFromDirectSoundCapture(
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
{
PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
DSCCAPS caps;
@ -636,7 +837,6 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
}
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
/** try using CoCreateInstance because DirectSoundCreate was hanging under
@ -678,7 +878,22 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
if( deviceOK )
{
deviceInfo->maxInputChannels = caps.dwChannels;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( pnpInterface )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
if( count > 0 )
{
deviceInfo->maxInputChannels = count;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
}
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
@ -770,17 +985,30 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
PaError result = paNoError;
int i, deviceCount;
PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
PaDeviceInfo *deviceInfoArray;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */
if( FAILED(hr) ){
PaWinDsDeviceInfo *deviceInfoArray;
char comWasInitialized = 0;
/*
If COM is already initialized CoInitialize will either return
FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
threading mode. In either case we shouldn't consider it an error
but we need to be careful to not call CoUninitialize() if
RPC_E_CHANGED_MODE was returned.
*/
HRESULT hr = CoInitialize(NULL);
if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
return paUnanticipatedHostError;
}
if( hr != RPC_E_CHANGED_MODE )
comWasInitialized = 1;
/* initialise guid vectors so they can be safely deleted on error */
inputNamesAndGUIDs.items = NULL;
outputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
PaWinDs_InitializeDSoundEntryPoints();
@ -791,6 +1019,8 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
goto error;
}
winDsHostApi->comWasInitialized = comWasInitialized;
winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !winDsHostApi->allocations )
{
@ -810,32 +1040,39 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
/* DSound - enumerate devices to count them and to gather their GUIDs */
result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( inputNamesAndGUIDs.enumerationError != paNoError )
if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
{
result = inputNamesAndGUIDs.enumerationError;
result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
goto error;
}
if( outputNamesAndGUIDs.enumerationError != paNoError )
if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
{
result = outputNamesAndGUIDs.enumerationError;
result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
goto error;
}
deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( deviceCount > 0 )
{
deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
if( deviceCount > 0 )
{
@ -849,18 +1086,9 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
}
/* allocate all PaDeviceInfo structs in a contiguous block */
deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all DSound specific info structs in a contiguous block */
winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
if( !winDsHostApi->winDsDeviceInfos )
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
@ -868,37 +1096,39 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
for( i=0; i < deviceCount; ++i )
{
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->name = 0;
(*hostApi)->deviceInfos[i] = deviceInfo;
}
for( i=0; i< inputNamesAndGUIDs.count; ++i )
for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
{
result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
inputNamesAndGUIDs.items[i].name,
inputNamesAndGUIDs.items[i].lpGUID );
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
for( i=0; i< outputNamesAndGUIDs.count; ++i )
for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
{
result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
outputNamesAndGUIDs.items[i].name,
outputNamesAndGUIDs.items[i].lpGUID );
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
}
result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
if( result != paNoError )
goto error;
result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( result != paNoError )
goto error;
@ -932,8 +1162,11 @@ error:
PaUtil_FreeMemory( winDsHostApi );
}
TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( comWasInitialized )
CoUninitialize();
return result;
}
@ -943,6 +1176,7 @@ error:
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
char comWasInitialized = winDsHostApi->comWasInitialized;
/*
IMPLEMENT ME:
@ -959,7 +1193,8 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
PaWinDs_TerminateDSoundEntryPoints();
CoUninitialize();
if( comWasInitialized )
CoUninitialize();
}
@ -994,17 +1229,40 @@ static int PaWinDS_GetMinSystemLatency( void )
return minLatencyMsec;
}
static PaError ValidateWinDirectSoundSpecificStreamInfo(
const PaStreamParameters *streamParameters,
const PaWinDirectSoundStreamInfo *streamInfo )
{
if( streamInfo )
{
if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
|| streamInfo->version != 1 )
{
return paIncompatibleHostApiSpecificStreamInfo;
}
}
return paNoError;
}
/***********************************************************************************/
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate )
{
PaError result;
PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
if( inputParameters )
{
inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat;
@ -1015,12 +1273,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
/* check that input device can support inputChannelCount */
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
&& inputChannelCount > inputDeviceInfo->maxInputChannels )
return paInvalidChannelCount;
/* validate inputStreamInfo */
if( inputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
if( result != paNoError ) return result;
}
else
{
@ -1029,6 +1289,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters )
{
outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
@ -1039,12 +1302,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
/* check that output device can support inputChannelCount */
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
&& outputChannelCount > outputDeviceInfo->maxOutputChannels )
return paInvalidChannelCount;
/* validate outputStreamInfo */
if( outputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
if( result != paNoError ) return result;
}
else
{
@ -1115,22 +1380,14 @@ static int PaWinDs_GetMinLatencyFrames( double sampleRate )
}
static HRESULT InitInputBuffer( PaWinDsStream *stream, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
static HRESULT InitInputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
{
DSCBUFFERDESC captureDesc;
WAVEFORMATEX wfFormat;
PaWinWaveFormat waveFormat;
HRESULT result;
stream->bytesPerInputFrame = nChannels * sizeof(short);
stream->bytesPerInputFrame = nChannels * Pa_GetSampleSize(sampleFormat);
// Define the buffer format
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
wfFormat.nChannels = nChannels;
wfFormat.nSamplesPerSec = nFrameRate;
wfFormat.wBitsPerSample = 8 * sizeof(short);
wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
wfFormat.cbSize = 0; /* No extended format info. */
stream->inputSize = bytesPerBuffer;
// ----------------------------------------------------------------------
// Setup the secondary buffer description
@ -1138,34 +1395,50 @@ static HRESULT InitInputBuffer( PaWinDsStream *stream, unsigned long nFrameRate,
captureDesc.dwSize = sizeof(DSCBUFFERDESC);
captureDesc.dwFlags = 0;
captureDesc.dwBufferBytes = bytesPerBuffer;
captureDesc.lpwfxFormat = &wfFormat;
captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
// Create the capture buffer
if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
&captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
// first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
sampleFormat, nFrameRate, channelMask );
if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
&captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
{
PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat, nFrameRate );
if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
&captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
}
stream->readOffset = 0; // reset last read position to start of buffer
return DS_OK;
}
static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
{
/** @todo FIXME: if InitOutputBuffer returns an error I'm not sure it frees all resources cleanly */
DWORD dwDataLen;
DWORD playCursor;
HRESULT result;
LPDIRECTSOUNDBUFFER pPrimaryBuffer;
HWND hWnd;
HRESULT hr;
WAVEFORMATEX wfFormat;
PaWinWaveFormat waveFormat;
DSBUFFERDESC primaryDesc;
DSBUFFERDESC secondaryDesc;
unsigned char* pDSBuffData;
LARGE_INTEGER counterFrequency;
int bytesPerSample = Pa_GetSampleSize(sampleFormat);
stream->outputBufferSizeBytes = bytesPerBuffer;
stream->outputIsRunning = FALSE;
stream->outputUnderflowCount = 0;
stream->dsw_framesWritten = 0;
stream->bytesPerOutputFrame = nChannels * sizeof(short);
stream->bytesPerOutputFrame = nChannels * bytesPerSample;
// We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
// applications's window. Also if that window is closed before the Buffer is closed
@ -1199,16 +1472,19 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
// Create the buffer
if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
&primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
// Define the buffer format
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
wfFormat.nChannels = nChannels;
wfFormat.nSamplesPerSec = nFrameRate;
wfFormat.wBitsPerSample = 8 * sizeof(short);
wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
wfFormat.cbSize = 0; /* No extended format info. */
// Set the primary buffer's format
if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
// first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
sampleFormat, nFrameRate, channelMask );
if( IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
{
PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat, nFrameRate );
if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK) return result;
}
// ----------------------------------------------------------------------
// Setup the secondary buffer description
@ -1216,7 +1492,7 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
secondaryDesc.dwBufferBytes = bytesPerBuffer;
secondaryDesc.lpwfxFormat = &wfFormat;
secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
// Create the secondary buffer
if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
&secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK) return result;
@ -1229,7 +1505,7 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
if ((result = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
if( QueryPerformanceFrequency( &counterFrequency ) )
{
int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
int framesInBuffer = bytesPerBuffer / (nChannels * bytesPerSample);
stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
}
else
@ -1249,7 +1525,6 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
return DS_OK;
}
/***********************************************************************************/
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
@ -1266,13 +1541,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaError result = paNoError;
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
PaWinDsStream *stream = 0;
PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
if( inputParameters )
{
inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat;
suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
@ -1286,12 +1568,19 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
/* check that input device can support inputChannelCount */
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
&& inputChannelCount > inputDeviceInfo->maxInputChannels )
return paInvalidChannelCount;
/* validate hostApiSpecificStreamInfo */
if( inputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
if( result != paNoError ) return result;
if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
inputChannelMask = inputStreamInfo->channelMask;
else
inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
}
else
{
@ -1303,6 +1592,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters )
{
outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
@ -1312,13 +1604,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
return paInvalidDevice;
/* check that output device can support inputChannelCount */
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
/* check that output device can support outputChannelCount */
if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
&& outputChannelCount > outputDeviceInfo->maxOutputChannels )
return paInvalidChannelCount;
/* validate hostApiSpecificStreamInfo */
if( outputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
if( result != paNoError ) return result;
if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
outputChannelMask = outputStreamInfo->channelMask;
else
outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
}
else
{
@ -1384,8 +1683,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( inputParameters )
{
/* IMPLEMENT ME - establish which host formats are available */
PaSampleFormat nativeInputFormats = paInt16;
//PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
}
else
{
@ -1395,8 +1697,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters )
{
/* IMPLEMENT ME - establish which host formats are available */
PaSampleFormat nativeOutputFormats = paInt16;
//PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
}
else
{
@ -1474,7 +1779,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
*/
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
int bytesPerSample = Pa_GetSampleSize(hostOutputSampleFormat);
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
{
result = paBufferTooSmall;
@ -1486,9 +1792,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
((PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device])->lpGUID,
&stream->pDirectSound, NULL );
hr = paWinDsDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
&stream->pDirectSound, NULL );
if( hr != DS_OK )
{
ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
@ -1497,8 +1804,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = InitOutputBuffer( stream,
hostOutputSampleFormat,
(unsigned long) (sampleRate + 0.5),
(WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
(WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer,
outputChannelMask );
DBUG(("InitOutputBuffer() returns %x\n", hr));
if( hr != DS_OK )
{
@ -1520,7 +1829,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
*/
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
int bytesPerSample = Pa_GetSampleSize(hostInputSampleFormat);
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
{
result = paBufferTooSmall;
@ -1532,8 +1842,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
&stream->pDirectSoundCapture, NULL );
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
((PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device])->lpGUID,
&stream->pDirectSoundCapture, NULL );
if( hr != DS_OK )
{
ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
@ -1542,8 +1853,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = InitInputBuffer( stream,
hostInputSampleFormat,
(unsigned long) (sampleRate + 0.5),
(WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
(WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer,
inputChannelMask );
DBUG(("InitInputBuffer() returns %x\n", hr));
if( hr != DS_OK )
{
@ -1885,7 +2198,8 @@ static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWO
}
}
if( !stream->isActive ){
if( !stream->isActive )
{
if( stream->streamRepresentation.streamFinishedCallback != 0 )
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
}

View File

@ -49,6 +49,14 @@
PaWinDsDSoundEntryPoints paWinDsDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
static HRESULT WINAPI DummyDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
(void)rclsid; /* unused parameter */
(void)riid; /* unused parameter */
(void)ppv; /* unused parameter */
return CLASS_E_CLASSNOTAVAILABLE;
}
static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
{
(void)lpcGuidDevice; /* unused parameter */
@ -99,6 +107,12 @@ void PaWinDs_InitializeDSoundEntryPoints(void)
paWinDsDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
if( paWinDsDSoundEntryPoints.hInstance_ != NULL )
{
paWinDsDSoundEntryPoints.DllGetClassObject =
(HRESULT (WINAPI *)(REFCLSID, REFIID , LPVOID *))
GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DllGetClassObject" );
if( paWinDsDSoundEntryPoints.DllGetClassObject == NULL )
paWinDsDSoundEntryPoints.DllGetClassObject = DummyDllGetClassObject;
paWinDsDSoundEntryPoints.DirectSoundCreate =
(HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundCreate" );

View File

@ -48,7 +48,7 @@
#define INCLUDED_PA_DSOUND_DYNLINK_H
/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
breaks DSound.h. Adding the define here fixes the problem. - rossb. */
breaks dsound.h. Adding the define here fixes the problem. - rossb. */
#ifdef __BORLANDC__
#if !defined(WIN32)
#define WIN32
@ -59,8 +59,7 @@
We are only using DX3 in here, no need to polute the namespace - davidv
*/
#define DIRECTSOUND_VERSION 0x0300
#include <DSound.h>
#include <dsound.h>
#ifdef __cplusplus
extern "C"
@ -72,6 +71,8 @@ typedef struct
{
HINSTANCE hInstance_;
HRESULT (WINAPI *DllGetClassObject)(REFCLSID , REFIID , LPVOID *);
HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_jack.c 1151 2006-11-29 02:11:16Z leland_lucius $
* $Id: pa_jack.c 1306 2007-11-27 19:33:53Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* JACK Implementation by Joshua Haberman
@ -32,19 +32,19 @@
*/
/*
* The text above constitutes the entire PortAudio license; however,
* 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
* 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 hostaip_src
@ingroup hostapi_src
*/
#include <string.h>
@ -69,11 +69,13 @@
#include "pa_allocation.h"
#include "pa_cpuload.h"
#include "pa_ringbuffer.h"
#include "pa_debugprint.h"
static int aErr_;
static PaError paErr_; /* For use with ENSURE_PA */
static pthread_t mainThread_;
static char *jackErr_ = NULL;
static const char* clientName_ = "PortAudio";
#define STRINGIZE_HELPER(expr) #expr
#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
@ -381,9 +383,9 @@ static PaError BlockingWriteStream( PaStream* s, const void *data, unsigned long
bytesWritten = PaUtil_WriteRingBuffer( &stream->outFIFO, p, numBytes );
numBytes -= bytesWritten;
p += bytesWritten;
if( numBytes > 0 )
if( numBytes > 0 )
{
/* we use the following algorithm:
/* we use the following algorithm:
* (1) write data
* (2) if some data didn't fit into the ringbuffer, set data_available to 0
* to indicate to the audio that if space becomes available, we want to know
@ -703,8 +705,7 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
PaError result = paNoError;
PaJackHostApiRepresentation *jackHostApi;
int activated = 0;
char *clientName;
int written;
jack_status_t jackStatus = 0;
*hostApi = NULL; /* Initialize to NULL */
UNLESS( jackHostApi = (PaJackHostApiRepresentation*)
@ -716,19 +717,21 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, NULL ), 0 );
/* Try to become a client of the JACK server. If we cannot do
* this, then this API cannot be used. */
* this, then this API cannot be used.
*
* Without the JackNoStartServer option, the jackd server is started
* automatically which we do not want.
*/
clientName = PaUtil_GroupAllocateMemory( jackHostApi->deviceInfoMemory, jack_client_name_size() );
written = snprintf( clientName, jack_client_name_size(), "PortAudio-%d", getpid() );
assert( written < jack_client_name_size() );
jackHostApi->jack_client = jack_client_new( clientName );
if( jackHostApi->jack_client == NULL )
jackHostApi->jack_client = jack_client_open( clientName_, JackNoStartServer, &jackStatus );
if( !jackHostApi->jack_client )
{
/* the V19 development docs say that if an implementation
* detects that it cannot be used, it should return a NULL
* interface and paNoError */
result = paNoError;
goto error;
/* the V19 development docs say that if an implementation
* detects that it cannot be used, it should return a NULL
* interface and paNoError */
PA_DEBUG(( "%s: Couldn't connect to JACK, status: %d\n", __FUNCTION__, jackStatus ));
result = paNoError;
goto error;
}
jackHostApi->hostApiIndex = hostApiIndex;
@ -739,7 +742,6 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
(*hostApi)->info.name = "JACK Audio Connection Kit";
/* Build a device list by querying the JACK server */
ENSURE_PA( BuildDeviceList( jackHostApi ) );
/* Register functions */
@ -772,7 +774,8 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
jack_on_shutdown( jackHostApi->jack_client, JackOnShutdown, jackHostApi );
jack_set_error_function( JackErrorCallback );
jackHostApi->jack_buffer_size = jack_get_buffer_size ( jackHostApi->jack_client );
UNLESS( !jack_set_sample_rate_callback( jackHostApi->jack_client, JackSrCb, jackHostApi ), paUnanticipatedHostError );
/* Don't check for error, may not be supported (deprecated in at least jackdmp) */
jack_set_sample_rate_callback( jackHostApi->jack_client, JackSrCb, jackHostApi );
UNLESS( !jack_set_xrun_callback( jackHostApi->jack_client, JackXRunCb, jackHostApi ), paUnanticipatedHostError );
UNLESS( !jack_set_process_callback( jackHostApi->jack_client, JackCallback, jackHostApi ), paUnanticipatedHostError );
UNLESS( !jack_activate( jackHostApi->jack_client ), paUnanticipatedHostError );
@ -883,7 +886,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
/*
The following check is not necessary for JACK.
- if a full duplex stream is requested, check that the combination
of input and output parameters is supported
@ -891,7 +894,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
Because the buffer adapter handles conversion between all standard
sample formats, the following checks are only required if paCustomFormat
is implemented, or under some other unusual conditions.
- check that input device can support inputSampleFormat, or that
we have the capability to convert from outputSampleFormat to
a native format
@ -902,7 +905,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
*/
/* check that the device supports sampleRate */
#define ABS(x) ( (x) > 0 ? (x) : -(x) )
if( ABS(sampleRate - jack_get_sample_rate(((PaJackHostApiRepresentation *) hostApi)->jack_client )) > 1 )
return paInvalidSampleRate;
@ -1052,7 +1055,7 @@ error:
return result;
}
/* Add stream to processing queue */
/* Add stream to JACK callback processing queue */
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
@ -1224,15 +1227,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( inputChannelCount > 0 )
{
int err = 0;
/* ... remote output ports (that we input from) */
/* Get output ports of our capture device */
snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ inputParameters->device ]->name );
UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern,
NULL, JackPortIsOutput ), paUnanticipatedHostError );
for( i = 0; i < inputChannelCount && jack_ports[i]; i++ )
{
if( (stream->remote_output_ports[i] = jack_port_by_name(
jackHostApi->jack_client, jack_ports[i] )) == NULL )
jackHostApi->jack_client, jack_ports[i] )) == NULL )
{
err = 1;
break;
@ -1249,7 +1252,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
{
int err = 0;
/* ... remote input ports (that we output to) */
/* Get input ports of our playback device */
snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ outputParameters->device ]->name );
UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern,
NULL, JackPortIsInput ), paUnanticipatedHostError );
@ -1298,8 +1301,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->streamRepresentation.streamInfo.sampleRate = jackSr;
stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */
ENSURE_PA( AddStream( stream ) ); /* Add to queue over opened streams */
/* Add to queue of opened streams */
ENSURE_PA( AddStream( stream ) );
*s = (PaStream*)stream;
return result;
@ -1407,7 +1411,7 @@ end:
return result;
}
/* Alter the processing queue if necessary */
/* Update the JACK callback's stream processing queue. */
static PaError UpdateQueue( PaJackHostApiRepresentation *hostApi )
{
PaError result = paNoError;
@ -1433,7 +1437,10 @@ static PaError UpdateQueue( PaJackHostApiRepresentation *hostApi )
node->next = hostApi->toAdd;
}
else
{
/* The only queue entry. */
hostApi->processQueue = (PaJackStream *)hostApi->toAdd;
}
/* If necessary, update stream state */
if( hostApi->toAdd->streamRepresentation.streamInfo.sampleRate != jackSr )
@ -1482,6 +1489,7 @@ error:
return result;
}
/* Audio processing callback invoked periodically from JACK. */
static int JackCallback( jack_nframes_t frames, void *userData )
{
PaError result = paNoError;
@ -1583,24 +1591,27 @@ static PaError StartStream( PaStream *s )
/* Ready the processor */
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
/* connect the ports */
/* Connect the ports. Note that the ports may already have been connected by someone else in
* the meantime, in which case JACK returns EEXIST. */
/* NOTE: I would rather use jack_port_connect which uses jack_port_t's
* instead of port names, but it is not implemented yet. */
if( stream->num_incoming_connections > 0 )
{
for( i = 0; i < stream->num_incoming_connections; i++ )
UNLESS( jack_connect( stream->jack_client,
jack_port_name( stream->remote_output_ports[i] ),
jack_port_name( stream->local_input_ports[i] ) ) == 0, paUnanticipatedHostError );
{
int r = jack_connect( stream->jack_client, jack_port_name( stream->remote_output_ports[i] ),
jack_port_name( stream->local_input_ports[i] ) );
UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError );
}
}
if( stream->num_outgoing_connections > 0 )
{
for( i = 0; i < stream->num_outgoing_connections; i++ )
UNLESS( jack_connect( stream->jack_client,
jack_port_name( stream->local_output_ports[i] ),
jack_port_name( stream->remote_input_ports[i] ) ) == 0, paUnanticipatedHostError );
{
int r = jack_connect( stream->jack_client, jack_port_name( stream->local_output_ports[i] ),
jack_port_name( stream->remote_input_ports[i] ) );
UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError );
}
}
stream->xrun = FALSE;
@ -1654,7 +1665,7 @@ static PaError RealStop( PaJackStream *stream, int abort )
ENSURE_PA( result );
UNLESS( !stream->is_active, paInternalError );
PA_DEBUG(( "%s: Stream stopped\n", __FUNCTION__ ));
error:
@ -1666,27 +1677,19 @@ error:
{
for( i = 0; i < stream->num_incoming_connections; i++ )
{
UNLESS( !jack_port_lock( stream->jack_client, stream->local_input_ports[i] ),
paUnanticipatedHostError );
if( jack_port_connected( stream->local_input_ports[i] ) )
{
UNLESS( !jack_port_disconnect( stream->jack_client, stream->local_input_ports[i] ),
paUnanticipatedHostError );
}
UNLESS( !jack_port_unlock( stream->jack_client, stream->local_input_ports[i] ),
paUnanticipatedHostError );
}
for( i = 0; i < stream->num_outgoing_connections; i++ )
{
UNLESS( !jack_port_lock( stream->jack_client, stream->local_output_ports[i] ),
paUnanticipatedHostError );
if( jack_port_connected( stream->local_output_ports[i] ) )
{
UNLESS( !jack_port_disconnect( stream->jack_client, stream->local_output_ports[i] ),
paUnanticipatedHostError );
}
UNLESS( !jack_port_unlock( stream->jack_client, stream->local_output_ports[i] ),
paUnanticipatedHostError );
}
}
@ -1733,3 +1736,26 @@ static double GetStreamCpuLoad( PaStream* s )
PaJackStream *stream = (PaJackStream*)s;
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}
PaError PaJack_SetClientName( const char* name )
{
if( strlen( name ) > jack_client_name_size() )
{
/* OK, I don't know any better error code */
return paInvalidFlag;
}
clientName_ = name;
return paNoError;
}
PaError PaJack_GetClientName(const char** clientName)
{
PaError result = paNoError;
PaJackHostApiRepresentation* jackHostApi = NULL;
PaJackHostApiRepresentation** ref = &jackHostApi;
ENSURE_PA( PaUtil_GetHostApiRepresentation( (PaUtilHostApiRepresentation**)ref, paJACK ) );
*clientName = jack_get_client_name( jackHostApi->jack_client );
error:
return result;
}

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_unix_oss.c 1103 2006-08-29 20:56:04Z aknudsen $
* $Id: pa_unix_oss.c 1296 2007-10-28 22:43:50Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* OSS implementation by:
@ -32,19 +32,19 @@
*/
/*
* The text above constitutes the entire PortAudio license; however,
* 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
* 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 hostaip_src
@ingroup hostapi_src
*/
#include <stdio.h>
@ -84,6 +84,7 @@
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_unix_util.h"
#include "pa_debugprint.h"
static int sysErr_;
static pthread_t mainThread_;
@ -113,7 +114,7 @@ static pthread_t mainThread_;
*/
static int Get_AFMT_S16_NE( void )
{
long testData = 1;
long testData = 1;
char *ptr = (char *) &testData;
int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
@ -278,7 +279,7 @@ error:
PaUtil_FreeAllAllocations( ossHostApi->allocations );
PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
}
PaUtil_FreeMemory( ossHostApi );
}
return result;
@ -289,7 +290,7 @@ PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name,
PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
{
PaError result = paNoError;
deviceInfo->structVersion = 2;
if( allocations )
{
@ -384,7 +385,7 @@ static PaError QueryDirection( const char *deviceName, StreamMode mode, double *
{
maxNumChannels = (stereo) ? 2 : 1;
}
PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ))
PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ));
}
/* During channel negotiation, the last ioctl() may have failed. This can
@ -440,7 +441,7 @@ static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi
/* douglas:
we have to do this querying in a slightly different order. apparently
some sound cards will give you different info based on their settins.
some sound cards will give you different info based on their settins.
e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
the correct order for OSS is: format, channels, sample rate
*/
@ -507,9 +508,9 @@ static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
/* Find devices by calling QueryDevice on each
* potential device names. When we find a valid one,
* add it to a linked list.
* A: Can there only be 10 devices? */
* A: Set an arbitrary of 100 devices, should probably be a smarter way. */
for( i = 0; i < 10; i++ )
for( i = 0; i < 100; i++ )
{
char deviceName[32];
PaDeviceInfo *deviceInfo;
@ -596,7 +597,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
int tempDevHandle = -1;
int flags;
PaSampleFormat inputSampleFormat, outputSampleFormat;
if( inputParameters )
{
inputChannelCount = inputParameters->channelCount;
@ -625,7 +626,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
{
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
@ -661,7 +662,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidChannelCount;
/* open the device so we can do more tests */
if( inputChannelCount > 0 )
{
result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
@ -677,7 +678,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
deviceInfo = hostApi->deviceInfos[device];
deviceName = (char *)deviceInfo->name;
flags = O_NONBLOCK;
if (inputChannelCount > 0 && outputChannelCount > 0)
flags |= O_RDWR;
@ -695,7 +696,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
error:
if( tempDevHandle >= 0 )
close( tempDevHandle );
close( tempDevHandle );
return result;
}
@ -882,7 +883,7 @@ static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParame
{
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
&ossApi->blockingStreamInterface, callback, userData );
}
}
ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
@ -948,7 +949,7 @@ static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFor
frmts |= paInt16;
else
result = paSampleFormatNotSupported;
*availableFormats = frmts;
error:
@ -975,8 +976,8 @@ static int CalcHigherLogTwo( int n )
return log2;
}
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
StreamMode streamMode, PaOssStreamComponent *master )
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long
framesPerBuffer, StreamMode streamMode, PaOssStreamComponent *master )
{
PaError result = paNoError;
int temp, nativeFormat;
@ -986,7 +987,7 @@ static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component,
int frgmt;
int numBufs;
int bytesPerBuf;
double bufSz;
unsigned long bufSz;
unsigned long fragSz;
audio_buf_info bufInfo;
@ -998,21 +999,21 @@ static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component,
* The hardware need not respect the requested fragment size, so we may have to adapt.
*/
if( framesPerBuffer == paFramesPerBufferUnspecified )
{
bufSz = component->latency * sampleRate;
{
bufSz = (unsigned long)(component->latency * sampleRate);
fragSz = bufSz / 4;
}
else
{
fragSz = framesPerBuffer;
bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
bufSz = (unsigned long)(component->latency * sampleRate) + fragSz; /* Latency + 1 buffer */
}
PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
/* OSS demands at least 2 buffers, and 16 bytes per buffer */
numBufs = PA_MAX( bufSz / fragSz, 2 );
numBufs = (int)PA_MAX( bufSz / fragSz, 2 );
bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
/* The fragment parameters are encoded like this:
@ -1042,7 +1043,7 @@ static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component,
/* reject if there's no sample rate within 1% of the one requested */
if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
{
PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
PA_ENSURE( paInvalidSampleRate );
}
@ -1119,7 +1120,8 @@ static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, un
if( stream->capture )
{
PaOssStreamComponent *component = stream->capture;
PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In,
NULL ) );
assert( component->hostChannelCount > 0 );
assert( component->hostFrames > 0 );
@ -1187,6 +1189,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
int bpInitialized = 0;
double inLatency = 0., outLatency = 0.;
int i = 0;
/* validate platform specific flags */
if( (streamFlags & paPlatformSpecificFlags) != 0 )
@ -1222,7 +1225,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidChannelCount;
}
}
/* Round framesPerBuffer to the next power-of-two to make OSS happy. */
if( framesPerBuffer != paFramesPerBufferUnspecified )
{
framesPerBuffer &= INT_MAX;
for (i = 1; framesPerBuffer > i; i <<= 1) ;
framesPerBuffer = i;
}
/* allocate and do basic initialization of the stream structure */
PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
PA_ENSURE( PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ) );
@ -1230,7 +1241,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
if( inputParameters )
{
inputHostFormat = stream->capture->hostFormat;
@ -1352,7 +1363,7 @@ static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *fr
pollCapture = 0;
}
*/
else if( stream->playback ) /* Timed out, go on with playback? */
else if( stream->playback ) /* Timed out, go on with playback? */
{
/*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
__FUNCTION__, stream->pollTimeout ));*/
@ -1463,7 +1474,7 @@ static PaError PaOssStream_Prepare( PaOssStream *stream )
/* Ok, we have triggered the stream */
stream->triggered = 1;
error:
return result;
}
@ -1502,7 +1513,7 @@ static void OnExit( void *data )
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
PaOssStream_Stop( stream, stream->callbackAbort );
PA_DEBUG(( "OnExit: Stoppage\n" ));
/* Eventually notify user all buffers have played */
@ -1550,13 +1561,13 @@ static void *PaOSS_AudioThreadProc( void *userData )
int initiateProcessing = triggered; /* Already triggered? */
PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
/*
#if ( SOUND_VERSION > 0x030904 )
audio_errinfo errinfo;
#endif
*/
assert( stream );
pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
@ -1594,7 +1605,8 @@ static void *PaOSS_AudioThreadProc( void *userData )
*/
if( !initiateProcessing )
{
PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */
/* Wait on available frames */
PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );
assert( framesAvail % stream->framesPerHostBuffer == 0 );
}
else
@ -1937,8 +1949,7 @@ static signed long GetStreamWriteAvailable( PaStream* s )
if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
return paUnanticipatedHostError;
return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
}

View File

@ -1,6 +1,6 @@
/*
* Portable Audio I/O Library WASAPI implementation
* Copyright (c) 2006 David Viens
* Copyright (c) 2006-2007 David Viens
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@ -40,14 +40,9 @@
@ingroup hostaip_src
@brief WASAPI implementation of support for a host API.
@note This file is provided as a starting point for implementing support for
a new host API. IMPLEMENT ME comments are used to indicate functionality
which much be customised for each implementation.
@note pa_wasapi currently requires VC 2005, and the latest Vista SDK
*/
//these headers are only in Windows SDK CTP Feb 2006 and only work in VC 2005!
#if _MSC_VER >= 1400
#include <windows.h>
#include <MMReg.h> //must be before other Wasapi headers
@ -55,18 +50,28 @@
#include <mmdeviceapi.h>
#include <Avrt.h>
#include <audioclient.h>
#include <Endpointvolume.h>
#include <KsMedia.h>
#include <functiondiscoverykeys.h> // PKEY_Device_FriendlyName
#endif
#include "pa_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
/*
davidv : work in progress. try using with 48000 , then 44100
and shared mode FIRST.
*/
#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_SHARED
//#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_EXCLUSIVE
@ -217,17 +222,18 @@ typedef struct PaWinWasapiStream
//input
PaWinWasapiSubStream in;
IAudioCaptureClient *cclient;
IAudioEndpointVolume *inVol;
//output
PaWinWasapiSubStream out;
IAudioRenderClient *rclient;
IAudioEndpointVolume *outVol;
bool running;
bool closeRequest;
DWORD dwThreadId;
HANDLE hThread;
HANDLE hNotificationEvent;
GUID session;
@ -315,6 +321,8 @@ FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;
FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics=0;
FAvSetMmThreadPriority pAvSetMmThreadPriority=0;
#define setupPTR(fun, type, name) { \
fun = (type) GetProcAddress(hDInputDLL,name); \
if(fun == NULL) { \
@ -379,11 +387,6 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
IMMDeviceCollection* spEndpoints=0;
paWasapi->enumerator = 0;
if (!setupAVRT()){
PRINT(("Windows WASAPI : No AVRT! (not VISTA?)"));
goto error;
}
hResult = CoCreateInstance(
__uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
@ -452,6 +455,7 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
for( UINT i=0; i < paWasapi->deviceCount; ++i ){
PA_DEBUG(("i:%d\n",i));
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
@ -585,7 +589,14 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
hResult = myClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);
IF_FAILED_JUMP(hResult, error);
if (hResult != S_OK){
/*davidv: this happened with my hardware, previously for that same device in DirectSound:
Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat*/
logAUDCLNT_E(hResult);
goto error;
}
myClient->Release();
}
@ -690,37 +701,37 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
}
static void
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE &in){
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in){
const WAVEFORMATEX *old = (WAVEFORMATEX *)&in;
const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
switch (old->wFormatTag){
case WAVE_FORMAT_EXTENSIBLE:{
PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));
if (in.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
}
else if (in.SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));
}
else{
PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
in.SubFormat.Data1,
in.SubFormat.Data2,
in.SubFormat.Data3,
(int)in.SubFormat.Data4[0],
(int)in.SubFormat.Data4[1],
(int)in.SubFormat.Data4[2],
(int)in.SubFormat.Data4[3],
(int)in.SubFormat.Data4[4],
(int)in.SubFormat.Data4[5],
(int)in.SubFormat.Data4[6],
(int)in.SubFormat.Data4[7]));
in->SubFormat.Data1,
in->SubFormat.Data2,
in->SubFormat.Data3,
(int)in->SubFormat.Data4[0],
(int)in->SubFormat.Data4[1],
(int)in->SubFormat.Data4[2],
(int)in->SubFormat.Data4[3],
(int)in->SubFormat.Data4[4],
(int)in->SubFormat.Data4[5],
(int)in->SubFormat.Data4[6],
(int)in->SubFormat.Data4[7]));
}
PRINT(("Samples.wValidBitsPerSample=%d\n", in.Samples.wValidBitsPerSample));
PRINT(("dwChannelMask=0x%X\n",in.dwChannelMask));
PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample));
PRINT(("dwChannelMask=0x%X\n",in->dwChannelMask));
}break;
case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;
@ -742,21 +753,21 @@ LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE &in){
WAVEFORMATXXX is always interleaved
*/
static PaSampleFormat
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE &in){
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){
const WAVEFORMATEX *old = (WAVEFORMATEX *)&in;
const WAVEFORMATEX *old = (WAVEFORMATEX*)in;
switch (old->wFormatTag){
case WAVE_FORMAT_EXTENSIBLE:
{
if (in.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
if (in.Samples.wValidBitsPerSample == 32)
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
if (in->Samples.wValidBitsPerSample == 32)
return paFloat32;
else
return paCustomFormat;
}
else if (in.SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
switch (old->wBitsPerSample){
case 32: return paInt32; break;
case 24: return paInt24;break;
@ -797,7 +808,7 @@ waveformatToPaFormat(const WAVEFORMATEXTENSIBLE &in){
static PaError
waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
waveformatFromParams(WAVEFORMATEXTENSIBLE*wavex,
const PaStreamParameters * params,
double sampleRate){
@ -813,13 +824,13 @@ waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
default: return paSampleFormatNotSupported;break;
}
memset(&wav,0,sizeof(WAVEFORMATEXTENSIBLE));
memset(wavex,0,sizeof(WAVEFORMATEXTENSIBLE));
WAVEFORMATEX *old = (WAVEFORMATEX *)&wav;
WAVEFORMATEX *old = (WAVEFORMATEX *)wavex;
old->nChannels = (WORD)params->channelCount;
old->nSamplesPerSec = (DWORD)sampleRate;
old->wBitsPerSample = bytesPerSample*8;
old->nAvgBytesPerSec = old->nSamplesPerSec * old->nChannels * bytesPerSample;
old->wBitsPerSample = (WORD)(bytesPerSample*8);
old->nAvgBytesPerSec = (DWORD)(old->nSamplesPerSec * old->nChannels * bytesPerSample);
old->nBlockAlign = (WORD)(old->nChannels * bytesPerSample);
//WAVEFORMATEX
@ -834,19 +845,19 @@ waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
old->cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
wav.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
wav.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wav.Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!
wavex->Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!
switch(params->channelCount){
case 1: wav.dwChannelMask = SPEAKER_FRONT_CENTER; break;
case 2: wav.dwChannelMask = 0x1 | 0x2; break;
case 4: wav.dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;
case 6: wav.dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;
case 8: wav.dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;
default: wav.dwChannelMask = 0; break;
case 1: wavex->dwChannelMask = SPEAKER_FRONT_CENTER; break;
case 2: wavex->dwChannelMask = 0x1 | 0x2; break;
case 4: wavex->dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;
case 6: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;
case 8: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;
default: wavex->dwChannelMask = 0; break;
}
}
@ -854,36 +865,160 @@ waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
}
enum PaWasapiFormatAnswer {PWFA_OK,PWFA_NO,PWFA_SUGGESTED};
static PaWasapiFormatAnswer
IsFormatSupportedInternal(IAudioClient * myClient, WAVEFORMATEXTENSIBLE &wavex){
PaWasapiFormatAnswer answer = PWFA_OK;
WAVEFORMATEX *closestMatch=0;
HRESULT hResult = myClient->IsFormatSupported(
//AUDCLNT_SHAREMODE_EXCLUSIVE,
AUDCLNT_SHAREMODE_SHARED,
(WAVEFORMATEX*)&wavex,&closestMatch);
/*
#define paFloat32 ((PaSampleFormat) 0x00000001)
#define paInt32 ((PaSampleFormat) 0x00000002)
#define paInt24 ((PaSampleFormat) 0x00000004)
#define paInt16 ((PaSampleFormat) 0x00000008)
*/
//lifted from pa_wdmks
static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
{
PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
PA_DEBUG(( "chanelCount = %d\n", channelCount ));
pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
pwfext->Format.nChannels = channelCount;
pwfext->Format.nSamplesPerSec = (int)sampleRate;
if(channelCount == 1)
pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
else
pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
if(sampleFormat == paFloat32)
{
pwfext->Format.nBlockAlign = channelCount * 4;
pwfext->Format.wBitsPerSample = 32;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 32;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
else if(sampleFormat == paInt32)
{
pwfext->Format.nBlockAlign = channelCount * 4;
pwfext->Format.wBitsPerSample = 32;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 32;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else if(sampleFormat == paInt24)
{
pwfext->Format.nBlockAlign = channelCount * 3;
pwfext->Format.wBitsPerSample = 24;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 24;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else if(sampleFormat == paInt16)
{
pwfext->Format.nBlockAlign = channelCount * 2;
pwfext->Format.wBitsPerSample = 16;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 16;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
}
/*
#define FORMATTESTS 4
const int BestToWorst[FORMATTESTS]={paFloat32,paInt32,paInt24,paInt16};
*/
#define FORMATTESTS 3
const int BestToWorst[FORMATTESTS]={paFloat32,paInt24,paInt16};
static PaError
GetClosestFormat(IAudioClient * myClient, double sampleRate,const PaStreamParameters * params,
AUDCLNT_SHAREMODE *shareMode, WAVEFORMATEXTENSIBLE *outWavex)
{
//TODO we should try exclusive first and shared after
*shareMode = PORTAUDIO_SHAREMODE;
PaError answer = paInvalidSampleRate;
waveformatFromParams(outWavex,params,sampleRate);
WAVEFORMATEX *sharedClosestMatch=0;
HRESULT hResult=!S_OK;
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)outWavex,NULL);
else
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&outWavex,&sharedClosestMatch);
if (hResult == S_OK)
answer = PWFA_OK;
else if (closestMatch){
WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)closestMatch;
answer = paFormatIsSupported;
else if (sharedClosestMatch){
WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
if (closestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
memcpy(&wavex,closestMatch,sizeof(WAVEFORMATEXTENSIBLE));
else
memcpy(&wavex,closestMatch,sizeof(WAVEFORMATEX));
int closestMatchSR = (int)sharedClosestMatch->nSamplesPerSec;
CoTaskMemFree(closestMatch);
answer = PWFA_SUGGESTED;
if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEXTENSIBLE));
else
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEX));
CoTaskMemFree(sharedClosestMatch);
if ((int)sampleRate == closestMatchSR)
answer = paFormatIsSupported;
else
answer = paInvalidSampleRate;
}else if (hResult != S_OK){
}else {
//it doesnt suggest anything?? ok lets show it the MENU!
//ok fun time as with pa_win_mme, we know only a refusal of the user-requested
//sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything
//so lets only use the number
for (int i=0;i<FORMATTESTS;++i){
WAVEFORMATEXTENSIBLE ext;
wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&ext,NULL);
else
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&ext,&sharedClosestMatch);
if (hResult == S_OK){
memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));
answer = paFormatIsSupported;
break;
}
}
if (answer!=paFormatIsSupported) {
//try MIX format?
//why did it HAVE to come to this ....
WAVEFORMATEX pcm16WaveFormat;
memset(&pcm16WaveFormat,0,sizeof(WAVEFORMATEX));
pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
pcm16WaveFormat.nChannels = 2;
pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate;
pcm16WaveFormat.nBlockAlign = 4;
pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign;
pcm16WaveFormat.wBitsPerSample = 16;
pcm16WaveFormat.cbSize = 0;
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&pcm16WaveFormat,NULL);
else
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&pcm16WaveFormat,&sharedClosestMatch);
if (hResult == S_OK){
memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));
answer = paFormatIsSupported;
}
}
logAUDCLNT_E(hResult);
answer = PWFA_NO;
}
return answer;
@ -891,10 +1026,11 @@ IsFormatSupportedInternal(IAudioClient * myClient, WAVEFORMATEXTENSIBLE &wavex){
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate )
{
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
@ -925,9 +1061,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
WAVEFORMATEXTENSIBLE wavex;
waveformatFromParams(wavex,inputParameters,sampleRate);
IAudioClient *myClient=0;
HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
@ -936,26 +1070,13 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
}
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(myClient,wavex);
WAVEFORMATEXTENSIBLE wavex;
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(myClient,sampleRate,inputParameters,&shareMode,&wavex);
myClient->Release();
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(wavex);
if (wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
if (answer !=paFormatIsSupported)
return answer;
}
else
{
@ -989,9 +1110,6 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
WAVEFORMATEXTENSIBLE wavex;
waveformatFromParams(wavex,outputParameters,sampleRate);
IAudioClient *myClient=0;
HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
@ -1000,26 +1118,13 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
}
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(myClient,wavex);
WAVEFORMATEXTENSIBLE wavex;
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(myClient,sampleRate,outputParameters,&shareMode,&wavex);
myClient->Release();
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(wavex);
if (wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
if (answer !=paFormatIsSupported)
return answer;
}
else
{
@ -1087,35 +1192,26 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (hResult != S_OK)
return paInvalidDevice;
waveformatFromParams(stream->in.wavex,outputParameters,sampleRate);
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(stream->in.client,
stream->in.wavex);
hResult = info.device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->inVol);
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(stream->in.wavex);
if (stream->in.wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
if (hResult != S_OK)
return paInvalidDevice;
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(stream->in.client,sampleRate,inputParameters,&shareMode,&stream->in.wavex);
if (answer !=paFormatIsSupported)
return answer;
//stream->out.period = info.DefaultDevicePeriod;
stream->in.period = info.MinimumDevicePeriod;
hResult = stream->in.client->Initialize(
AUDCLNT_SHAREMODE_SHARED,
//AUDCLNT_SHAREMODE_EXCLUSIVE,
shareMode,
0, //no flags
stream->in.period*3, //tripple buffer
stream->in.period,
0,//stream->out.period,
(WAVEFORMATEX*)&stream->in.wavex,
&stream->session
@ -1142,7 +1238,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* IMPLEMENT ME - establish which host formats are available */
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(stream->in.wavex), inputSampleFormat );
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );
}
else
{
@ -1179,45 +1275,53 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (hResult != S_OK)
return paInvalidDevice;
waveformatFromParams(stream->out.wavex,outputParameters,sampleRate);
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(stream->out.client,sampleRate,outputParameters,&shareMode,&stream->out.wavex);
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(stream->out.client,
stream->out.wavex);
if (answer !=paFormatIsSupported)
return answer;
LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(stream->out.wavex);
if (stream->out.wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
//stream->out.period = info.DefaultDevicePeriod;
// stream->out.period = info.DefaultDevicePeriod;
stream->out.period = info.MinimumDevicePeriod;
hResult = stream->out.client->Initialize(
AUDCLNT_SHAREMODE_SHARED,
//AUDCLNT_SHAREMODE_EXCLUSIVE,
0, //no flags
stream->out.period*3, //tripple buffer
0,//stream->out.period,
(WAVEFORMATEX*)&stream->out.wavex,
&stream->session
);
/*For an exclusive-mode stream that uses event-driven buffering,
the caller must specify nonzero values for hnsPeriodicity and hnsBufferDuration,
and the values of these two parameters must be equal */
if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE){
hResult = stream->out.client->Initialize(
shareMode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
stream->out.period,
stream->out.period,
(WAVEFORMATEX*)&stream->out.wavex,
&stream->session
);
}
else{
hResult = stream->out.client->Initialize(
shareMode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0,
0,
(WAVEFORMATEX*)&stream->out.wavex,
&stream->session
);
}
if (hResult != S_OK){
logAUDCLNT_E(hResult);
return paInvalidDevice;
}
hResult = info.device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->outVol);
if (hResult != S_OK)
return paInvalidDevice;
hResult = stream->out.client->GetBufferSize(&stream->out.bufferSize);
if (hResult != S_OK)
return paInvalidDevice;
@ -1225,16 +1329,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hResult = stream->out.client->GetStreamLatency(&stream->out.latency);
if (hResult != S_OK)
return paInvalidDevice;
double periodsPerSecond = 1.0/nano100ToSeconds(stream->out.period);
double samplesPerPeriod = (double)(stream->out.wavex.Format.nSamplesPerSec)/periodsPerSecond;
//this is the number of samples that are required at each period
stream->out.framesPerHostCallback = (unsigned long)samplesPerPeriod;//unrelated to channels
stream->out.framesPerHostCallback = stream->out.bufferSize; //(unsigned long)samplesPerPeriod;//unrelated to channels
/* IMPLEMENT ME - establish which host formats are available */
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(stream->out.wavex), outputSampleFormat );
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );
}
else
{
@ -1376,7 +1479,10 @@ static PaError CloseStream( PaStream* s )
SAFE_RELEASE(stream->in.client);
SAFE_RELEASE(stream->cclient);
SAFE_RELEASE(stream->rclient);
SAFE_RELEASE(stream->inVol);
SAFE_RELEASE(stream->outVol);
CloseHandle(stream->hThread);
CloseHandle(stream->hNotificationEvent);
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
@ -1385,7 +1491,7 @@ static PaError CloseStream( PaStream* s )
return result;
}
VOID ProcThread(void *client);
DWORD WINAPI ProcThread(void *client);
static PaError StartStream( PaStream *s )
{
@ -1414,7 +1520,7 @@ static PaError StartStream( PaStream *s )
stream->hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
(LPTHREAD_START_ROUTINE) ProcThread,
ProcThread,
(LPVOID) stream, // thread parameter
0, // not suspended
&stream->dwThreadId); // returns thread ID
@ -1667,11 +1773,8 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
}
VOID
ProcThread(void *param){
HRESULT hResult;
void
MMCSS_activate(){
DWORD stuff=0;
HANDLE thCarac = pAvSetMmThreadCharacteristics("Pro Audio",&stuff);
@ -1684,31 +1787,6 @@ ProcThread(void *param){
PRINT(("AvSetMmThreadPriority failed!\n"));
}
PaWinWasapiStream *stream = (PaWinWasapiStream*)param;
HANDLE context;
GUID threadOrderGUID;
memset(&threadOrderGUID,0,sizeof(GUID));
LARGE_INTEGER large;
large.QuadPart = stream->out.period;
BOOL ok = pAvRtCreateThreadOrderingGroup(&context,
&large,
&threadOrderGUID,
#ifdef _DEBUG
0 //THREAD_ORDER_GROUP_INFINITE_TIMEOUT
#else
0 //default is 5 times the 2nd param
#endif
//TEXT("Audio")
);
if (!ok){
PRINT(("AvRtCreateThreadOrderingGroup failed!\n"));
}
//debug
{
HANDLE hh = GetCurrentThread();
@ -1716,9 +1794,23 @@ ProcThread(void *param){
DWORD currclass = GetPriorityClass(GetCurrentProcess());
PRINT(("currprio 0x%X currclass 0x%X\n",currprio,currclass));
}
}
//fill up initial buffer latency??
DWORD WINAPI
ProcThread(void* param){
HRESULT hResult;
MMCSS_activate();
PaWinWasapiStream *stream = (PaWinWasapiStream*)param;
stream->hNotificationEvent = CreateEvent(NULL,
FALSE, //bManualReset are we sure??
FALSE,
"PAWASA");
hResult = stream->out.client->SetEventHandle(stream->hNotificationEvent);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
if (stream->out.client){
hResult = stream->out.client->Start();
@ -1726,67 +1818,94 @@ ProcThread(void *param){
logAUDCLNT_E(hResult);
}
stream->running = true;
stream->running = true;
bool bOne = false;
while( !stream->closeRequest )
{
//lets wait but have a 1 second timeout
DWORD dwResult = WaitForSingleObject(stream->hNotificationEvent, 1000);
switch( dwResult ) {
case WAIT_OBJECT_0: {
unsigned long usingBS = stream->out.framesPerHostCallback;
BYTE* indata = 0;
BYTE* outdata = 0;
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
if (hResult != S_OK || !outdata) {
//logAUDCLNT_E(hResult);
//most probably shared mode and hResult=AUDCLNT_E_BUFFER_TOO_LARGE
UINT32 padding = 0;
hResult = stream->out.client->GetCurrentPadding(&padding);
if (padding == 0)
break;
usingBS = usingBS-padding;
if (usingBS == 0)
break;//huh?
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
if (hResult != S_OK)//what can we do NOW??
break;
//logAUDCLNT_E(hResult);
}
WaspiHostProcessingLoop(indata, usingBS ,outdata, usingBS, stream);
hResult = stream->rclient->ReleaseBuffer(usingBS, 0);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
/* This was suggested, but in my tests it doesnt seem to improve the
locking behaviour some drivers have running in exclusive mode.
if(!ResetEvent(stream->hNotificationEvent)){
logAUDCLNT_E(hResult);
}
*/
}
break;
while(!stream->closeRequest){
BOOL answer = pAvRtWaitOnThreadOrderingGroup(context);
if (!answer){
PRINT(("AvRtWaitOnThreadOrderingGroup failed\n"));
}
unsigned long usingBS = stream->out.framesPerHostCallback;
UINT32 padding=0;
hResult = stream->out.client->GetCurrentPadding(&padding);
logAUDCLNT_E(hResult);
//buffer full dont pursue
if (padding == stream->out.bufferSize)
continue;
//if something is already inside
if (padding > 0){
usingBS = stream->out.bufferSize-padding;
if (usingBS > stream->out.framesPerHostCallback){
//PRINT(("underflow! %d\n",usingBS));
}
else if (usingBS < stream->out.framesPerHostCallback){
//PRINT(("overflow! %d\n",usingBS));
}
}
else
usingBS = stream->out.framesPerHostCallback;
BYTE*indata =0;
BYTE*outdata=0;
hResult = stream->rclient->GetBuffer(usingBS,&outdata);
if (hResult != S_OK || !outdata) {
logAUDCLNT_E(hResult);
continue;
}
WaspiHostProcessingLoop(indata, usingBS
,outdata,usingBS,stream);
hResult = stream->rclient->ReleaseBuffer(usingBS,0);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
}
BOOL bRes = pAvRtDeleteThreadOrderingGroup(context);
if (!bRes){
PRINT(("AvRtDeleteThreadOrderingGroup failure\n"));
}
stream->out.client->Stop();
stream->closeRequest = false;
return 0;
}
#endif //VC 2005
#endif //VC 2005
#if 0
if(bFirst) {
float masteur;
hResult = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
float chan1, chan2;
hResult = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
hResult = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
BOOL bMute;
hResult = stream->outVol->GetMute(&bMute);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
stream->outVol->SetMute(FALSE, NULL);
bFirst = false;
}
#endif

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_win_wdmks.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_win_wdmks.c 1263 2007-08-27 22:59:09Z rossb $
* PortAudio Windows WDM-KS interface
*
* Author: Andrew Baldwin
@ -73,6 +73,7 @@
#include "pa_cpuload.h"
#include "pa_process.h"
#include "portaudio.h"
#include "pa_debugprint.h"
#include <windows.h>
#include <winioctl.h>
@ -1558,6 +1559,11 @@ static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter,
/**
* Build the list of available filters
* Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which
* have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these
* devices initialise a PaWinWdmFilter structure by calling our NewFilter()
* function. We enumerate devices twice, once to count how many there are,
* and once to initialize the PaWinWdmFilter structures.
*/
static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
{

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_win_wmme.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_win_wmme.c 1286 2007-09-26 21:34:23Z rossb $
* pa_win_wmme.c
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
*
@ -88,6 +88,10 @@ Non-critical stuff for the future:
@todo define UNICODE and _UNICODE in the project settings and see what breaks
@todo refactor conversion of MMSYSTEM errors into PA arrors into a single function.
@todo cleanup WAVEFORMATEXTENSIBLE retry in InitializeWaveHandles to not use a for loop
*/
/*
@ -112,7 +116,9 @@ Non-critical stuff for the future:
#include <math.h>
#include <windows.h>
#include <mmsystem.h>
#ifndef UNDER_CE
#include <process.h>
#endif
#include <assert.h>
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
#ifndef __MWERKS__
@ -128,10 +134,24 @@ Non-critical stuff for the future:
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
#include "pa_win_wmme.h"
#include "pa_win_waveformat.h"
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
#include "pa_win_wdmks_utils.h"
#ifndef DRV_QUERYDEVICEINTERFACE
#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12)
#endif
#ifndef DRV_QUERYDEVICEINTERFACESIZE
#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
#endif
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
#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
@ -369,6 +389,8 @@ typedef struct
{
PaDeviceInfo inheritedDeviceInfo;
DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
}
PaWinMmeDeviceInfo;
@ -497,7 +519,7 @@ static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
int winMmeDeviceId, int channels, double sampleRate )
{
PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
WAVEFORMATEX waveFormatEx;
PaWinWaveFormat waveFormat;
if( sampleRate == 11025.0
&& ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
@ -520,15 +542,19 @@ static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
return paNoError;
}
waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
waveFormatEx.nChannels = (WORD)channels;
waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
waveFormatEx.wBitsPerSample = 16;
waveFormatEx.cbSize = 0;
/* first, attempt to query the device using WAVEFORMATEXTENSIBLE,
if this fails we fall back to WAVEFORMATEX */
return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
/* @todo at the moment we only query with 16 bit sample format and directout speaker config*/
PaWin_InitializeWaveFormatExtensible( &waveFormat, channels,
paInt16, sampleRate, PAWIN_SPEAKER_DIRECTOUT );
if( waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ) == paNoError )
return paNoError;
PaWin_InitializeWaveFormatEx( &waveFormat, channels, paInt16, sampleRate );
return waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat );
}
@ -558,6 +584,40 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static int QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels )
{
void *devicePath;
DWORD devicePathSize;
int result = 0;
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
devicePath = PaUtil_AllocateMemory( devicePathSize );
if( !devicePath )
return 0;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1 );
if( count > 0 )
{
*maxChannels = count;
result = 1;
}
}
PaUtil_FreeMemory( devicePath );
return result;
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
{
@ -611,17 +671,30 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
}
deviceInfo->name = deviceName;
deviceInfo->maxInputChannels = wic.wChannels;
/* Sometimes a device can return a rediculously large number of channels.
* This happened with an SBLive card on a Windows ME box.
* If that happens, then force it to 2 channels. PLB20010413
if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later)
* the kernel mixer sits between the application and the driver. As a result,
* wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
* When this happens we assume the device is stereo and set a flag
* so that other channel counts can be tried with OpenStream -- i.e. when
* device*ChannelCountIsKnown is false, OpenStream will try whatever
* channel count you supply.
* see also InitializeOutputDeviceInfo() below.
*/
if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
{
PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", wic.wChannels ));
deviceInfo->maxInputChannels = 2;
winMmeDeviceInfo->deviceInputChannelCountIsKnown = 0;
}else{
deviceInfo->maxInputChannels = wic.wChannels;
winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
winMmeDeviceInfo->deviceInputChannelCountIsKnown =
QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
winMmeDeviceInfo->dwFormats = wic.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
@ -634,6 +707,40 @@ error:
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels )
{
void *devicePath;
DWORD devicePathSize;
int result = 0;
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
devicePath = PaUtil_AllocateMemory( devicePathSize );
if( !devicePath )
return 0;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0 );
if( count > 0 )
{
*maxChannels = count;
result = 1;
}
}
PaUtil_FreeMemory( devicePath );
return result;
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
{
@ -642,7 +749,7 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
MMRESULT mmresult;
WAVEOUTCAPS woc;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
*success = 0;
mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
@ -687,17 +794,30 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
}
deviceInfo->name = deviceName;
deviceInfo->maxOutputChannels = woc.wChannels;
/* Sometimes a device can return a rediculously large number of channels.
* This happened with an SBLive card on a Windows ME box.
* It also happens on Win XP!
if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later)
* the kernel mixer sits between the application and the driver. As a result,
* wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
* When this happens we assume the device is stereo and set a flag
* so that other channel counts can be tried with OpenStream -- i.e. when
* device*ChannelCountIsKnown is false, OpenStream will try whatever
* channel count you supply.
* see also InitializeInputDeviceInfo() above.
*/
if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
{
PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", woc.wChannels ));
deviceInfo->maxOutputChannels = 2;
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 0;
}else{
deviceInfo->maxOutputChannels = woc.wChannels;
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
winMmeDeviceInfo->deviceOutputChannelCountIsKnown =
QueryWaveOutKSFilterMaxChannels( winMmeOutputDeviceId, &deviceInfo->maxOutputChannels );
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
winMmeDeviceInfo->dwFormats = woc.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
@ -743,6 +863,8 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
PaWinMmeDeviceInfo *deviceInfoArray;
int deviceInfoInitializationSucceeded;
PaTime defaultLowLatency, defaultHighLatency;
DWORD waveInPreferredDevice, waveOutPreferredDevice;
DWORD preferredDeviceStatusFlags;
winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
if( !winMmeHostApi )
@ -774,6 +896,19 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
winMmeHostApi->inputDeviceCount = 0;
winMmeHostApi->outputDeviceCount = 0;
#if !defined(DRVM_MAPPER_PREFERRED_GET)
/* DRVM_MAPPER_PREFERRED_GET is defined in mmddk.h but we avoid a dependency on the DDK by defining it here */
#define DRVM_MAPPER_PREFERRED_GET (0x2000+21)
#endif
/* the following calls assume that if wave*Message fails the preferred device parameter won't be modified */
preferredDeviceStatusFlags = 0;
waveInPreferredDevice = -1;
waveInMessage( (HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveInPreferredDevice, (DWORD)&preferredDeviceStatusFlags );
preferredDeviceStatusFlags = 0;
waveOutPreferredDevice = -1;
waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveOutPreferredDevice, (DWORD)&preferredDeviceStatusFlags );
maximumPossibleDeviceCount = 0;
@ -825,7 +960,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
@ -838,8 +975,14 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
if( deviceInfoInitializationSucceeded ){
if( (*hostApi)->info.defaultInputDevice == paNoDevice )
if( (*hostApi)->info.defaultInputDevice == paNoDevice ){
/* if there is currently no default device, use the first one available */
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
}else if( winMmeDeviceId == waveInPreferredDevice ){
/* set the default device to the system preferred device */
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
}
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
@ -860,7 +1003,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
@ -873,9 +1018,15 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
if( deviceInfoInitializationSucceeded ){
if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
if( (*hostApi)->info.defaultOutputDevice == paNoDevice ){
/* if there is currently no default device, use the first one available */
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
}else if( winMmeDeviceId == waveOutPreferredDevice ){
/* set the default device to the system preferred device */
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
}
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
@ -886,7 +1037,6 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
}
}
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
(*hostApi)->Terminate = Terminate;
@ -936,6 +1086,34 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
}
static PaError IsInputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
{
PaError result = paNoError;
if( channelCount > 0
&& deviceInfo->deviceInputChannelCountIsKnown
&& channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){
result = paInvalidChannelCount;
}
return result;
}
static PaError IsOutputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
{
PaError result = paNoError;
if( channelCount > 0
&& deviceInfo->deviceOutputChannelCountIsKnown
&& channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){
result = paInvalidChannelCount;
}
return result;
}
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
@ -980,10 +1158,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
/* check that input device can support inputChannelCount */
if( inputStreamInfo->devices[i].channelCount <= 0
|| inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
if( inputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo,
inputStreamInfo->devices[i].channelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
@ -1002,8 +1184,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
/* check that input device can support inputChannelCount */
if( inputChannelCount > inputDeviceInfo->maxInputChannels )
return paInvalidChannelCount;
paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, inputChannelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
@ -1035,10 +1218,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
/* check that output device can support outputChannelCount */
if( outputStreamInfo->devices[i].channelCount <= 0
|| outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
if( outputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo,
outputStreamInfo->devices[i].channelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
@ -1057,8 +1244,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
/* check that output device can support outputChannelCount */
if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
return paInvalidChannelCount;
paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, outputChannelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
@ -1488,7 +1676,7 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
unsigned long bytesPerHostSample,
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
unsigned int deviceCount, int isInput );
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput );
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
unsigned long hostBufferCount,
@ -1512,13 +1700,11 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
unsigned long bytesPerHostSample,
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
unsigned int deviceCount, int isInput )
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput )
{
PaError result;
MMRESULT mmresult;
unsigned long bytesPerFrame;
WAVEFORMATEX wfx;
signed int i;
signed int i, j;
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
has already been called to zero some fields */
@ -1546,64 +1732,94 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
}
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nSamplesPerSec = (DWORD) sampleRate;
wfx.cbSize = 0;
for( i = 0; i < (signed int)deviceCount; ++i )
{
UINT winMmeDeviceId;
PaWinWaveFormat waveFormat;
UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
/* @todo: consider providing a flag or #define to not try waveformat extensible
this could just initialize j to 1 the first time round. */
winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
wfx.nChannels = (WORD)devices[i].channelCount;
bytesPerFrame = wfx.nChannels * bytesPerHostSample;
wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
wfx.nBlockAlign = (WORD)bytesPerFrame;
wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
/* REVIEW: consider not firing an event for input when a full duplex
stream is being used. this would probably depend on the
neverDropInput flag. */
if( isInput )
mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
else
mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
if( mmresult != MMSYSERR_NOERROR )
for( j = 0; j < 2; ++j )
{
switch( mmresult )
{
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NODRIVER: /* No device driver is present. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
result = paInsufficientMemory;
break;
if( j == 0 )
{
/* first, attempt to open the device using WAVEFORMATEXTENSIBLE,
if this fails we fall back to WAVEFORMATEX */
/* @todo at the moment we only use 16 bit sample format */
PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
paInt16, sampleRate, channelMask );
case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
/* falls through */
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
/* falls through */
default:
result = paUnanticipatedHostError;
if( isInput )
{
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
}
else
{
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
}
}
goto error;
else
{
/* retry with WAVEFORMATEX */
PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount, paInt16, sampleRate );
}
/* REVIEW: consider not firing an event for input when a full duplex
stream is being used. this would probably depend on the
neverDropInput flag. */
if( isInput )
{
mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
(WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
}
else
{
mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
(WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
}
if( mmresult == MMSYSERR_NOERROR )
{
break; /* success */
}
else if( j == 0 )
{
continue; /* try again with WAVEFORMATEX */
}
else
{
switch( mmresult )
{
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NODRIVER: /* No device driver is present. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
result = paInsufficientMemory;
break;
case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
/* falls through */
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
/* This can also occur if we try to open the device with an unsupported
* number of channels. This is attempted when device*ChannelCountIsKnown is
* set to 0.
*/
/* falls through */
default:
result = paUnanticipatedHostError;
if( isInput )
{
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
}
else
{
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
}
}
goto error;
}
}
}
@ -1921,12 +2137,20 @@ static PaError ValidateInputChannelCounts(
unsigned long deviceCount )
{
unsigned int i;
PaWinMmeDeviceInfo *inputDeviceInfo;
PaError paerror;
for( i=0; i < deviceCount; ++i )
{
if( devices[i].channelCount < 1 || devices[i].channelCount
> hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
inputDeviceInfo =
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount );
if( paerror != paNoError )
return paerror;
}
return paNoError;
@ -1938,12 +2162,20 @@ static PaError ValidateOutputChannelCounts(
unsigned long deviceCount )
{
unsigned int i;
PaWinMmeDeviceInfo *outputDeviceInfo;
PaError paerror;
for( i=0; i < deviceCount; ++i )
{
if( devices[i].channelCount < 1 || devices[i].channelCount
> hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
outputDeviceInfo =
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount );
if( paerror != paNoError )
return paerror;
}
return paNoError;
@ -1976,6 +2208,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaSampleFormat inputSampleFormat, outputSampleFormat;
double suggestedInputLatency, suggestedOutputLatency;
PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
unsigned long framesPerHostInputBuffer;
unsigned long hostInputBufferCount;
unsigned long framesPerHostOutputBuffer;
@ -2014,6 +2247,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
if( inputDeviceCount != 1 ){
/* always use direct speakers when using multi-device multichannel mode */
inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
}
else
{
if( inputStreamInfo && inputStreamInfo->flags & paWinMmeUseChannelMask )
inputChannelMask = inputStreamInfo->channelMask;
else
inputChannelMask = PaWin_DefaultChannelMask( inputDevices[0].channelCount );
}
}
else
{
@ -2051,6 +2296,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
if( outputDeviceCount != 1 ){
/* always use direct speakers when using multi-device multichannel mode */
outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
}
else
{
if( outputStreamInfo && outputStreamInfo->flags & paWinMmeUseChannelMask )
outputChannelMask = outputStreamInfo->channelMask;
else
outputChannelMask = PaWin_DefaultChannelMask( outputDevices[0].channelCount );
}
}
else
{
@ -2172,7 +2429,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
{
result = InitializeWaveHandles( winMmeHostApi, &stream->input,
stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
inputDevices, inputDeviceCount, 1 /* isInput */ );
inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ );
if( result != paNoError ) goto error;
}
@ -2180,7 +2437,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
{
result = InitializeWaveHandles( winMmeHostApi, &stream->output,
stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
outputDevices, outputDeviceCount, 0 /* isInput */ );
outputDevices, outputDeviceCount, outputChannelMask, 0 /* isInput */ );
if( result != paNoError ) goto error;
}
@ -2335,6 +2592,7 @@ static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
for( i=0; i < stream->input.deviceCount; ++i )
{
stream->input.waveHeaders[i][ stream->input.currentBufferIndex ].dwFlags &= ~WHDR_DONE;
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
&stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
sizeof(WAVEHDR) );
@ -2637,7 +2895,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg )
for( i=0; i<stream->input.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
@ -2658,7 +2916,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg )
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
@ -2861,6 +3119,7 @@ static PaError StartStream( PaStream *s )
{
for( j=0; j<stream->input.deviceCount; ++j )
{
stream->input.waveHeaders[j][i].dwFlags &= ~WHDR_DONE;
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
if( mmresult != MMSYSERR_NOERROR )
{
@ -2907,7 +3166,7 @@ static PaError StartStream( PaStream *s )
for( j=0; j<stream->output.deviceCount; ++j )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[j][i].dwUser;
int channelCount = (int)stream->output.waveHeaders[j][i].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[j][i].lpData +
@ -3115,7 +3374,7 @@ static PaError StopStream( PaStream *s )
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
@ -3367,7 +3626,7 @@ static PaError ReadStream( PaStream* s,
for( i=0; i<stream->input.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
@ -3472,7 +3731,7 @@ static PaError WriteStream( PaStream* s,
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_unix_util.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_unix_util.c 1232 2007-06-16 14:49:43Z rossb $
* Portable Audio I/O Library
* UNIX platform-specific support functions
*
@ -53,6 +53,7 @@
#include "pa_util.h"
#include "pa_unix_util.h"
#include "pa_debugprint.h"
/*
Track memory allocations to avoid leaks.

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_unix_util.h 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_unix_util.h 1241 2007-07-23 20:08:31Z aknudsen $
* Portable Audio I/O Library
* UNIX platform-specific support functions
*
@ -187,6 +187,7 @@ PaError PaUnixThreading_Initialize();
* @param threadFunc: The function to be executed in the child thread.
* @param waitForChild: If not 0, wait for child thread to call PaUnixThread_NotifyParent. Less than 0 means
* wait for ever, greater than 0 wait for the specified time.
* @param rtSched: Enable realtime scheduling?
* @return: If timed out waiting on child, paTimedOut.
*/
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,

View File

@ -1,5 +1,5 @@
/*
* $Id: pa_win_util.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_win_util.c 1197 2007-05-04 13:07:10Z gordon_gidluck $
* Portable Audio I/O Library
* Win32 platform-specific support functions
*
@ -138,6 +138,10 @@ double PaUtil_GetTime( void )
}
else
{
#ifndef UNDER_CE
return timeGetTime() * .001;
#else
return GetTickCount() * .001;
#endif
}
}

View File

@ -0,0 +1,159 @@
/*
* PortAudio Portable Real-Time Audio Library
* Windows WAVEFORMAT* data structure utilities
* portaudio.h should be included before this file.
*
* Copyright (c) 2007 Ross Bencina
*
* 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.
*/
#include <windows.h>
#include <mmsystem.h>
#include "portaudio.h"
#include "pa_win_waveformat.h"
#if !defined(WAVE_FORMAT_EXTENSIBLE)
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#endif
#if !defined(WAVE_FORMAT_IEEE_FLOAT)
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
static GUID pawin_ksDataFormatSubtypePcm =
{ (USHORT)(WAVE_FORMAT_PCM), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
static GUID pawin_ksDataFormatSubtypeIeeeFloat =
{ (USHORT)(WAVE_FORMAT_IEEE_FLOAT), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat,
int numChannels, PaSampleFormat sampleFormat, double sampleRate )
{
WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat;
int bytesPerSample = Pa_GetSampleSize(sampleFormat);
unsigned long bytesPerFrame = numChannels * bytesPerSample;
if( sampleFormat == paFloat32 )
waveFormatEx->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
else
waveFormatEx->wFormatTag = WAVE_FORMAT_PCM;
waveFormatEx->nChannels = (WORD)numChannels;
waveFormatEx->nSamplesPerSec = (DWORD)sampleRate;
waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame;
waveFormatEx->nBlockAlign = (WORD)bytesPerFrame;
waveFormatEx->wBitsPerSample = bytesPerSample * 8;
waveFormatEx->cbSize = 0;
}
void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat,
int numChannels, PaSampleFormat sampleFormat, double sampleRate,
PaWinWaveFormatChannelMask channelMask )
{
WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat;
int bytesPerSample = Pa_GetSampleSize(sampleFormat);
unsigned long bytesPerFrame = numChannels * bytesPerSample;
waveFormatEx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
waveFormatEx->nChannels = (WORD)numChannels;
waveFormatEx->nSamplesPerSec = (DWORD)sampleRate;
waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame;
waveFormatEx->nBlockAlign = (WORD)bytesPerFrame;
waveFormatEx->wBitsPerSample = bytesPerSample * 8;
waveFormatEx->cbSize = 22;
*((WORD*)&waveFormat->fields[PAWIN_INDEXOF_WVALIDBITSPERSAMPLE]) =
waveFormatEx->wBitsPerSample;
*((DWORD*)&waveFormat->fields[PAWIN_INDEXOF_DWCHANNELMASK]) = channelMask;
if( sampleFormat == paFloat32 )
*((GUID*)&waveFormat->fields[PAWIN_INDEXOF_SUBFORMAT]) =
pawin_ksDataFormatSubtypeIeeeFloat;
else
*((GUID*)&waveFormat->fields[PAWIN_INDEXOF_SUBFORMAT]) =
pawin_ksDataFormatSubtypePcm;
}
PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels )
{
switch( numChannels ){
case 1:
return PAWIN_SPEAKER_MONO;
case 2:
return PAWIN_SPEAKER_STEREO;
case 3:
return PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_FRONT_RIGHT;
case 4:
return PAWIN_SPEAKER_QUAD;
case 5:
return PAWIN_SPEAKER_QUAD | PAWIN_SPEAKER_FRONT_CENTER;
case 6:
/* The meaning of the PAWIN_SPEAKER_5POINT1 flag has changed over time:
http://msdn2.microsoft.com/en-us/library/aa474707.aspx
We use PAWIN_SPEAKER_5POINT1 (not PAWIN_SPEAKER_5POINT1_SURROUND)
because on some cards (eg Audigy) PAWIN_SPEAKER_5POINT1_SURROUND
results in a virtual mixdown placing the rear output in the
front _and_ rear speakers.
*/
return PAWIN_SPEAKER_5POINT1;
/* case 7: */
case 8:
return PAWIN_SPEAKER_7POINT1;
}
/* Apparently some Audigy drivers will output silence
if the direct-out constant (0) is used. So this is not ideal.
*/
return PAWIN_SPEAKER_DIRECTOUT;
/* Note that Alec Rogers proposed the following as an alternate method to
generate the default channel mask, however it doesn't seem to be an improvement
over the above, since some drivers will matrix outputs mapping to non-present
speakers accross multiple physical speakers.
if(nChannels==1) {
pwfFormat->dwChannelMask = SPEAKER_FRONT_CENTER;
}
else {
pwfFormat->dwChannelMask = 0;
for(i=0; i<nChannels; i++)
pwfFormat->dwChannelMask = (pwfFormat->dwChannelMask << 1) | 0x1;
}
*/
}

View File

@ -0,0 +1,260 @@
/*
* PortAudio Portable Real-Time Audio Library
* Windows WDM KS utilities
*
* Copyright (c) 1999 - 2007 Andrew Baldwin, Ross Bencina
*
* 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.
*/
#include <windows.h>
#include <mmreg.h>
#include <ks.h>
#include <ksmedia.h>
#include <stdio.h> // just for some development printfs
#include "portaudio.h"
#include "pa_util.h"
#include "pa_win_wdmks_utils.h"
static PaError WdmGetPinPropertySimple(
HANDLE handle,
unsigned long pinId,
unsigned long property,
void* value,
unsigned long valueSize )
{
DWORD bytesReturned;
KSP_PIN ksPProp;
ksPProp.Property.Set = KSPROPSETID_Pin;
ksPProp.Property.Id = property;
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
ksPProp.PinId = pinId;
ksPProp.Reserved = 0;
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
value, valueSize, &bytesReturned, NULL ) == 0 || bytesReturned != valueSize )
{
return paUnanticipatedHostError;
}
else
{
return paNoError;
}
}
static PaError WdmGetPinPropertyMulti(
HANDLE handle,
unsigned long pinId,
unsigned long property,
KSMULTIPLE_ITEM** ksMultipleItem)
{
unsigned long multipleItemSize = 0;
KSP_PIN ksPProp;
DWORD bytesReturned;
*ksMultipleItem = 0;
ksPProp.Property.Set = KSPROPSETID_Pin;
ksPProp.Property.Id = property;
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
ksPProp.PinId = pinId;
ksPProp.Reserved = 0;
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp.Property,
sizeof(KSP_PIN), NULL, 0, &multipleItemSize, NULL ) == 0 && GetLastError() != ERROR_MORE_DATA )
{
return paUnanticipatedHostError;
}
*ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
if( !*ksMultipleItem )
{
return paInsufficientMemory;
}
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
(void*)*ksMultipleItem, multipleItemSize, &bytesReturned, NULL ) == 0 || bytesReturned != multipleItemSize )
{
PaUtil_FreeMemory( ksMultipleItem );
return paUnanticipatedHostError;
}
return paNoError;
}
static int GetKSFilterPinCount( HANDLE deviceHandle )
{
DWORD result;
if( WdmGetPinPropertySimple( deviceHandle, 0, KSPROPERTY_PIN_CTYPES, &result, sizeof(result) ) == paNoError ){
return result;
}else{
return 0;
}
}
static KSPIN_COMMUNICATION GetKSFilterPinPropertyCommunication( HANDLE deviceHandle, int pinId )
{
KSPIN_COMMUNICATION result;
if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_COMMUNICATION, &result, sizeof(result) ) == paNoError ){
return result;
}else{
return KSPIN_COMMUNICATION_NONE;
}
}
static KSPIN_DATAFLOW GetKSFilterPinPropertyDataflow( HANDLE deviceHandle, int pinId )
{
KSPIN_DATAFLOW result;
if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_DATAFLOW, &result, sizeof(result) ) == paNoError ){
return result;
}else{
return (KSPIN_DATAFLOW)0;
}
}
static int KSFilterPinPropertyIdentifiersInclude(
HANDLE deviceHandle, int pinId, unsigned long property, const GUID *identifierSet, unsigned long identifierId )
{
KSMULTIPLE_ITEM* item = NULL;
KSIDENTIFIER* identifier;
int i;
int result = 0;
if( WdmGetPinPropertyMulti( deviceHandle, pinId, property, &item) != paNoError )
return 0;
identifier = (KSIDENTIFIER*)(item+1);
for( i = 0; i < (int)item->Count; i++ )
{
if( !memcmp( (void*)&identifier[i].Set, (void*)identifierSet, sizeof( GUID ) ) &&
( identifier[i].Id == identifierId ) )
{
result = 1;
break;
}
}
PaUtil_FreeMemory( item );
return result;
}
/* return the maximum channel count supported by any pin on the device.
if isInput is non-zero we query input pins, otherwise output pins.
*/
int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput )
{
HANDLE deviceHandle;
int pinCount, pinId, i;
int result = 0;
KSPIN_DATAFLOW requiredDataflowDirection = (isInput ? KSPIN_DATAFLOW_OUT : KSPIN_DATAFLOW_IN );
if( !wcharDevicePath )
return 0;
deviceHandle = CreateFileW( (LPCWSTR)wcharDevicePath, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL );
if( deviceHandle == INVALID_HANDLE_VALUE )
return 0;
pinCount = GetKSFilterPinCount( deviceHandle );
for( pinId = 0; pinId < pinCount; ++pinId )
{
KSPIN_COMMUNICATION communication = GetKSFilterPinPropertyCommunication( deviceHandle, pinId );
KSPIN_DATAFLOW dataflow = GetKSFilterPinPropertyDataflow( deviceHandle, pinId );
if( ( dataflow == requiredDataflowDirection ) &&
(( communication == KSPIN_COMMUNICATION_SINK) ||
( communication == KSPIN_COMMUNICATION_BOTH))
&& ( KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
KSPROPERTY_PIN_INTERFACES, &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING )
|| KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
KSPROPERTY_PIN_INTERFACES, &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) )
&& KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
KSPROPERTY_PIN_MEDIUMS, &KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) )
{
KSMULTIPLE_ITEM* item = NULL;
if( WdmGetPinPropertyMulti( deviceHandle, pinId, KSPROPERTY_PIN_DATARANGES, &item ) == paNoError )
{
KSDATARANGE *dataRange = (KSDATARANGE*)(item+1);
for( i=0; i < item->Count; ++i ){
if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat)
|| memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0
|| memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0
|| ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 )
&& ( memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof(GUID) ) == 0 ) ) )
{
KSDATARANGE_AUDIO *dataRangeAudio = (KSDATARANGE_AUDIO*)dataRange;
/*
printf( ">>> %d %d %d %d %S\n", isInput, dataflow, communication, dataRangeAudio->MaximumChannels, devicePath );
if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX, sizeof(GUID) ) == 0 )
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WAVEFORMATEX\n" );
else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_DSOUND, sizeof(GUID) ) == 0 )
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_DSOUND\n" );
else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WILDCARD, sizeof(GUID) ) == 0 )
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WILDCARD\n" );
else
printf( "\tspecifier: ?\n" );
*/
/*
We assume that very high values for MaximumChannels are not useful and indicate
that the driver isn't prepared to tell us the real number of channels which it supports.
*/
if( dataRangeAudio->MaximumChannels < 0xFFFFUL && (int)dataRangeAudio->MaximumChannels > result )
result = (int)dataRangeAudio->MaximumChannels;
}
dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
}
PaUtil_FreeMemory( item );
}
}
}
CloseHandle( deviceHandle );
return result;
}

View File

@ -0,0 +1,65 @@
#ifndef PA_WIN_WDMKS_UTILS_H
#define PA_WIN_WDMKS_UTILS_H
/*
* PortAudio Portable Real-Time Audio Library
* Windows WDM KS utilities
*
* Copyright (c) 1999 - 2007 Ross Bencina, Andrew Baldwin
*
* 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
@brief Utilities for working with the Windows WDM KS API
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
Query for the maximum number of channels supported by any pin of the
specified device. Returns 0 if the query fails for any reason.
@param wcharDevicePath A system level PnP interface path, supplied as a WCHAR unicode string.
Declard as void* to avoid introducing a dependency on wchar_t here.
@param isInput A flag specifying whether to query for input (non-zero) or output (zero) channels.
*/
int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_WIN_WDMKS_UTILS_H */

View File

@ -124,7 +124,7 @@ static const double ditheredInt16Scaler_ = 0x7FFE;
#define PA_DITHER_BITS_ (15)
/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<<PA_DITHER_BITS_)-1))
#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1<<PA_DITHER_BITS_)-1))
static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)

View File

@ -4,7 +4,7 @@
@author Ross Bencina <rossb@audiomulch.com>
*/
/*
* $Id: patest_callbackstop.c 1162 2006-12-18 20:40:35Z bjornroche $
* $Id: patest_callbackstop.c 1294 2007-10-24 20:51:22Z bjornroche $
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com/
@ -62,6 +62,7 @@ typedef struct
unsigned long generatedFramesCount;
volatile int callbackReturnedPaComplete;
volatile int callbackInvokedAfterReturningPaComplete;
char message[100];
}
TestData;
@ -113,6 +114,16 @@ static int TestCallback( const void *input, void *output,
}
}
/*
* This routine is called by portaudio when playback is done.
*/
static void StreamFinished( void* userData )
{
TestData *data = (TestData *) userData;
printf( "Stream Completed: %s\n", data->message );
}
/*----------------------------------------------------------------------------*/
int main(void);
int main(void)
@ -153,6 +164,10 @@ int main(void)
&data );
if( err != paNoError ) goto error;
sprintf( data.message, "Loop: XX" );
err = Pa_SetStreamFinishedCallback( stream, &StreamFinished );
if( err != paNoError ) goto error;
printf("Repeating test %d times.\n", NUM_LOOPS );
for( i=0; i < NUM_LOOPS; ++i )
@ -161,6 +176,7 @@ int main(void)
data.generatedFramesCount = 0;
data.callbackReturnedPaComplete = 0;
data.callbackInvokedAfterReturningPaComplete = 0;
sprintf( data.message, "Loop: %d", i );
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;

View File

@ -0,0 +1,205 @@
/*
* $Id: $
* Portable Audio I/O Library
* Windows DirectSound surround sound output test
*
* Copyright (c) 2007 Ross Bencina
*
* 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.
*/
#include <stdio.h>
#include <math.h>
#include <windows.h> /* required when using pa_win_wmme.h */
#include <mmsystem.h> /* required when using pa_win_wmme.h */
#include "portaudio.h"
#include "pa_win_ds.h"
#define NUM_SECONDS (12)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (100)
#define CHANNEL_COUNT (6)
typedef struct
{
float sine[TABLE_SIZE];
int phase;
int currentChannel;
int cycleCount;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i,j;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
for( j = 0; j < CHANNEL_COUNT; ++j ){
if( j == data->currentChannel && data->cycleCount < 4410 ){
*out++ = data->sine[data->phase];
data->phase += 1 + j; // play each channel at a different pitch so they can be distinguished
if( data->phase >= TABLE_SIZE ){
data->phase -= TABLE_SIZE;
}
}else{
*out++ = 0;
}
}
data->cycleCount++;
if( data->cycleCount > 44100 ){
data->cycleCount = 0;
++data->currentChannel;
if( data->currentChannel >= CHANNEL_COUNT )
data->currentChannel -= CHANNEL_COUNT;
}
}
return paContinue;
}
/*******************************************************************/
int main(int argc, char* argv[])
{
PaStreamParameters outputParameters;
PaWinDirectSoundStreamInfo directSoundStreamInfo;
PaStream *stream;
PaError err;
paTestData data;
int i;
int deviceIndex;
printf("PortAudio Test: output a sine blip on each channel. SR = %d, BufSize = %d, Chans = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER, CHANNEL_COUNT);
err = Pa_Initialize();
if( err != paNoError ) goto error;
deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex( paDirectSound ) )->defaultOutputDevice;
if( argc == 2 ){
sscanf( argv[1], "%d", &deviceIndex );
}
printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.phase = 0;
data.currentChannel = 0;
data.cycleCount = 0;
outputParameters.device = deviceIndex;
outputParameters.channelCount = CHANNEL_COUNT;
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/* it's not strictly necessary to provide a channelMask for surround sound
output. But if you want to be sure which channel mask PortAudio will use
then you should supply one */
directSoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);
directSoundStreamInfo.hostApiType = paDirectSound;
directSoundStreamInfo.version = 1;
directSoundStreamInfo.flags = paWinDirectSoundUseChannelMask;
directSoundStreamInfo.channelMask = PAWIN_SPEAKER_5POINT1; /* request 5.1 output format */
outputParameters.hostApiSpecificStreamInfo = &directSoundStreamInfo;
if( Pa_IsFormatSupported( 0, &outputParameters, SAMPLE_RATE ) == paFormatIsSupported ){
printf( "Pa_IsFormatSupported reports device will support %d channels.\n", CHANNEL_COUNT );
}else{
printf( "Pa_IsFormatSupported reports device will not support %d channels.\n", CHANNEL_COUNT );
}
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play for %d seconds.\n", NUM_SECONDS );
Pa_Sleep( NUM_SECONDS * 1000 );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View File

@ -45,6 +45,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include "portaudio.h"
/* #define SAMPLE_RATE (17932) // Test failure to open with this value. */
@ -55,25 +56,39 @@
#define DITHER_FLAG (0) /**/
/* Select sample format. */
#if 1
#if 0
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#define SAMPLE_SIZE (4)
#define SAMPLE_SILENCE (0.0f)
#define CLEAR(a) bzero( (a), FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE )
#define PRINTF_S_FORMAT "%.8f"
#elif 1
#elif 0
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SIZE (2)
#define SAMPLE_SILENCE (0)
#define CLEAR(a) bzero( (a), FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE )
#define PRINTF_S_FORMAT "%d"
#elif 1
#define PA_SAMPLE_TYPE paInt24
#define SAMPLE_SIZE (3)
#define SAMPLE_SILENCE (0)
#define CLEAR(a) bzero( (a), FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE )
#define PRINTF_S_FORMAT "%d"
#elif 0
#define PA_SAMPLE_TYPE paInt8
typedef char SAMPLE;
#define SAMPLE_SIZE (1)
#define SAMPLE_SILENCE (0)
#define CLEAR(a) bzero( (a), FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE )
#define PRINTF_S_FORMAT "%d"
#else
#define PA_SAMPLE_TYPE paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SIZE (1)
#define SAMPLE_SILENCE (128)
#define CLEAR( a ) { \
int i; \
for( i=0; i<FRAMES_PER_BUFFER*NUM_CHANNELS; i++ ) \
((unsigned char *)a)[i] = (SAMPLE_SILENCE); \
}
#define PRINTF_S_FORMAT "%d"
#endif
@ -85,22 +100,21 @@ int main(void)
PaStreamParameters inputParameters, outputParameters;
PaStream *stream = NULL;
PaError err;
SAMPLE *sampleBlock;
char *sampleBlock;
int i;
int numBytes;
printf("patest_read_write_wire.c\n"); fflush(stdout);
numBytes = FRAMES_PER_BUFFER * NUM_CHANNELS * sizeof(SAMPLE);
sampleBlock = (SAMPLE *) malloc( numBytes );
numBytes = FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE ;
sampleBlock = (char *) malloc( numBytes );
if( sampleBlock == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
}
for( i=0; i<FRAMES_PER_BUFFER*NUM_CHANNELS; i++ )
sampleBlock[i] = (SAMPLE_SILENCE);
CLEAR( sampleBlock );
err = Pa_Initialize();
if( err != paNoError ) goto error;
@ -150,8 +164,7 @@ int main(void)
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
for( i=0; i<FRAMES_PER_BUFFER*NUM_CHANNELS; i++ )
sampleBlock[i] = (SAMPLE_SILENCE);
CLEAR( sampleBlock );
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;

View File

@ -4,7 +4,7 @@
@author Phil Burk http://www.softsynth.com
*/
/*
* $Id: patest_record.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: patest_record.c 1281 2007-09-16 11:06:51Z aknudsen $
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com

View File

@ -5,7 +5,7 @@
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id: patest_sine.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: patest_sine.c 1294 2007-10-24 20:51:22Z bjornroche $
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com/
@ -59,6 +59,7 @@ typedef struct
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
char message[20];
}
paTestData;
@ -93,6 +94,15 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer,
return paContinue;
}
/*
* This routine is called by portaudio when playback is done.
*/
static void StreamFinished( void* userData )
{
paTestData *data = (paTestData *) userData;
printf( "Stream Completed: %s\n", data->message );
}
/*******************************************************************/
int main(void);
int main(void)
@ -133,6 +143,10 @@ int main(void)
&data );
if( err != paNoError ) goto error;
sprintf( data.message, "No Message" );
err = Pa_SetStreamFinishedCallback( stream, &StreamFinished );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;

View File

@ -0,0 +1,184 @@
/** @file patest_sine.c
@ingroup test_src
@brief Plays sine waves using sme simple channel maps.
Designed for use with COreAudio, but should made to work with other APIs
@author Bjorn Roche <bjorn@xowave.com>
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id: patest_sine.c 1097 2006-08-26 08:27:53Z rossb $
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com/
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#ifdef __APPLE__
#include "pa_mac_core.h"
#endif
#define NUM_SECONDS (5)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
return paContinue;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
paTestData data;
#ifdef __APPLE__
PaMacCoreStreamInfo macInfo;
const SInt32 channelMap[4] = { -1, -1, 0, 1 };
#endif
int i;
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
printf("Output will be mapped to channels 2 and 3 instead of 0 and 1.\n");
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.left_phase = data.right_phase = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
/** setup host specific info */
#ifdef __APPLE__
PaMacCore_SetupStreamInfo( &macInfo, paMacCorePlayNice );
PaMacCore_SetupChannelMap( &macInfo, channelMap, 4 );
for( i=0; i<4; ++i )
printf( "channel %d name: %s\n", i, PaMacCore_GetChannelName( Pa_GetDefaultOutputDevice(), i, false ) );
#else
printf( "Channel mapping not supported on this platform. Reverting to normal sine test.\n" );
#endif
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
#ifdef __APPLE__
outputParameters.hostApiSpecificStreamInfo = &macInfo;
#else
outputParameters.hostApiSpecificStreamInfo = NULL;
#endif
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play for %d seconds.\n", NUM_SECONDS );
Pa_Sleep( NUM_SECONDS * 1000 );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View File

@ -0,0 +1,206 @@
/*
* $Id: $
* Portable Audio I/O Library
* Windows MME surround sound output test
*
* Copyright (c) 2007 Ross Bencina
*
* 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.
*/
#include <stdio.h>
#include <math.h>
#include <windows.h> /* required when using pa_win_wmme.h */
#include <mmsystem.h> /* required when using pa_win_wmme.h */
#include "portaudio.h"
#include "pa_win_wmme.h"
#define NUM_SECONDS (12)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (100)
#define CHANNEL_COUNT (6)
typedef struct
{
float sine[TABLE_SIZE];
int phase;
int currentChannel;
int cycleCount;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i,j;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
for( j = 0; j < CHANNEL_COUNT; ++j ){
if( j == data->currentChannel && data->cycleCount < 4410 ){
*out++ = data->sine[data->phase];
data->phase += 1 + j; // play each channel at a different pitch so they can be distinguished
if( data->phase >= TABLE_SIZE ){
data->phase -= TABLE_SIZE;
}
}else{
*out++ = 0;
}
}
data->cycleCount++;
if( data->cycleCount > 44100 ){
data->cycleCount = 0;
++data->currentChannel;
if( data->currentChannel >= CHANNEL_COUNT )
data->currentChannel -= CHANNEL_COUNT;
}
}
return paContinue;
}
/*******************************************************************/
int main(int argc, char* argv[])
{
PaStreamParameters outputParameters;
PaWinMmeStreamInfo wmmeStreamInfo;
PaStream *stream;
PaError err;
paTestData data;
int i;
int deviceIndex;
printf("PortAudio Test: output a sine blip on each channel. SR = %d, BufSize = %d, Chans = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER, CHANNEL_COUNT);
err = Pa_Initialize();
if( err != paNoError ) goto error;
deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex( paMME ) )->defaultOutputDevice;
if( argc == 2 ){
sscanf( argv[1], "%d", &deviceIndex );
}
printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.phase = 0;
data.currentChannel = 0;
data.cycleCount = 0;
outputParameters.device = deviceIndex;
outputParameters.channelCount = CHANNEL_COUNT;
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/* it's not strictly necessary to provide a channelMask for surround sound
output. But if you want to be sure which channel mask PortAudio will use
then you should supply one */
wmmeStreamInfo.size = sizeof(PaWinMmeStreamInfo);
wmmeStreamInfo.hostApiType = paMME;
wmmeStreamInfo.version = 1;
wmmeStreamInfo.flags = paWinMmeUseChannelMask;
wmmeStreamInfo.channelMask = PAWIN_SPEAKER_5POINT1; /* request 5.1 output format */
outputParameters.hostApiSpecificStreamInfo = &wmmeStreamInfo;
if( Pa_IsFormatSupported( 0, &outputParameters, SAMPLE_RATE ) == paFormatIsSupported ){
printf( "Pa_IsFormatSupported reports device will support %d channels.\n", CHANNEL_COUNT );
}else{
printf( "Pa_IsFormatSupported reports device will not support %d channels.\n", CHANNEL_COUNT );
}
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play for %d seconds.\n", NUM_SECONDS );
Pa_Sleep( NUM_SECONDS * 1000 );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}