diff --git a/configure b/configure index 49457336386..74cf3a94b7e 100755 --- a/configure +++ b/configure @@ -9483,6 +9483,187 @@ done +AUDIOIOLIBS="" + + +for ac_header in libaudioio.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};; + no:yes ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + echo "$as_me:$LINENO: checking for AudioIOGetVersion in -laudioio" >&5 +echo $ECHO_N "checking for AudioIOGetVersion in -laudioio... $ECHO_C" >&6 +if test "${ac_cv_lib_audioio_AudioIOGetVersion+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-laudioio $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char AudioIOGetVersion (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +AudioIOGetVersion (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_audioio_AudioIOGetVersion=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_audioio_AudioIOGetVersion=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_audioio_AudioIOGetVersion" >&5 +echo "${ECHO_T}$ac_cv_lib_audioio_AudioIOGetVersion" >&6 +if test $ac_cv_lib_audioio_AudioIOGetVersion = yes; then + AUDIOIOLIBS="-laudioio" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBAUDIOIO 1 +_ACEOF + +fi + +fi + +done + + + echo "$as_me:$LINENO: checking whether mmap64 works defined as mmap" >&5 echo $ECHO_N "checking whether mmap64 works defined as mmap... $ECHO_C" >&6 if test "${ac_cv_mmap64_works+set}" = set; then @@ -11615,6 +11796,7 @@ done + for ac_header in \ @@ -11625,6 +11807,7 @@ for ac_header in \ float.h \ ieeefp.h \ io.h \ + libaudioio.h \ libio.h \ libutil.h \ link.h \ @@ -13810,7 +13993,7 @@ MAKE_DLL_RULES=dlls/Makedll.rules MAKE_PROG_RULES=programs/Makeprog.rules -ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules programs/Makeprog.rules Makefile debugger/Makefile dlls/Makefile dlls/advapi32/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/d3d8/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dsound/Makefile dlls/gdi/Makefile dlls/glu32/Makefile dlls/icmp/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/kernel/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt20/Makefile dlls/msvideo/Makefile dlls/netapi32/Makefile dlls/ntdll/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleaut32/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/user/Makefile dlls/version/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winenas/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winspool/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile library/Makefile miscemu/Makefile ole/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/osversioncheck/Makefile programs/progman/Makefile programs/regapi/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/regtest/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineconsole/Makefile programs/winefile/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/wmc/Makefile tools/wpp/Makefile tools/wrc/Makefile tsx11/Makefile unicode/Makefile" +ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules programs/Makeprog.rules Makefile debugger/Makefile dlls/Makefile dlls/advapi32/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/d3d8/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dsound/Makefile dlls/gdi/Makefile dlls/glu32/Makefile dlls/icmp/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/kernel/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt20/Makefile dlls/msvideo/Makefile dlls/netapi32/Makefile dlls/ntdll/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleaut32/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/user/Makefile dlls/version/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winspool/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile library/Makefile miscemu/Makefile ole/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/osversioncheck/Makefile programs/progman/Makefile programs/regapi/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/regtest/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineconsole/Makefile programs/winefile/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/wmc/Makefile tools/wpp/Makefile tools/wrc/Makefile tsx11/Makefile unicode/Makefile" cat >confcache <<\_ACEOF @@ -14378,8 +14561,9 @@ do "dlls/winmm/midimap/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/midimap/Makefile" ;; "dlls/winmm/wavemap/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/wavemap/Makefile" ;; "dlls/winmm/winealsa/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/winealsa/Makefile" ;; - "dlls/winmm/winenas/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/winenas/Makefile" ;; "dlls/winmm/winearts/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/winearts/Makefile" ;; + "dlls/winmm/wineaudioio/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/wineaudioio/Makefile" ;; + "dlls/winmm/winenas/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/winenas/Makefile" ;; "dlls/winmm/wineoss/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winmm/wineoss/Makefile" ;; "dlls/winnls/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winnls/Makefile" ;; "dlls/winsock/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/winsock/Makefile" ;; @@ -14618,6 +14802,7 @@ s,@ARTSLIBS@,$ARTSLIBS,;t t s,@ARTSINCL@,$ARTSINCL,;t t s,@ALSALIBS@,$ALSALIBS,;t t s,@NASLIBS@,$NASLIBS,;t t +s,@AUDIOIOLIBS@,$AUDIOIOLIBS,;t t s,@DLLWRAP@,$DLLWRAP,;t t s,@ac_ct_DLLWRAP@,$ac_ct_DLLWRAP,;t t s,@DLLIBS@,$DLLIBS,;t t diff --git a/configure.ac b/configure.ac index 5f2983eb28b..9cd74c7d2d7 100644 --- a/configure.ac +++ b/configure.ac @@ -577,6 +577,14 @@ AC_CHECK_HEADERS(audio/audiolib.h, NASLIBS="-laudio -lXt \$(X_LIBS) \$(XLIB)"],, [-lXt $X_LIBS -lXext -lX11 $X_EXTRA_LIBS])]) +dnl **** Check for libaudioio (which can be used to get solaris audio support) **** + +AC_SUBST(AUDIOIOLIBS,"") +AC_CHECK_HEADERS(libaudioio.h, + [AC_CHECK_LIB(audioio,AudioIOGetVersion, + [AUDIOIOLIBS="-laudioio" + AC_DEFINE(HAVE_LIBAUDIOIO, 1, [Define if you have libaudioIO])])]) + dnl **** Check for broken glibc mmap64 **** AC_CACHE_CHECK( [whether mmap64 works defined as mmap], ac_cv_mmap64_works, @@ -922,6 +930,7 @@ AC_CHECK_HEADERS(\ float.h \ ieeefp.h \ io.h \ + libaudioio.h \ libio.h \ libutil.h \ link.h \ @@ -1412,8 +1421,9 @@ dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile -dlls/winmm/winenas/Makefile dlls/winmm/winearts/Makefile +dlls/winmm/wineaudioio/Makefile +dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile diff --git a/dlls/Makefile.in b/dlls/Makefile.in index 81d300b1c9b..15514e5af61 100644 --- a/dlls/Makefile.in +++ b/dlls/Makefile.in @@ -98,6 +98,7 @@ BASEDIRS = \ winmm/wavemap \ winmm/winealsa \ winmm/winearts \ + winmm/wineaudioio \ winmm/winenas \ winmm/wineoss \ winnls \ @@ -241,6 +242,7 @@ all: \ windebug.dll$(DLLEXT) \ winealsa.drv$(DLLEXT) \ winearts.drv$(DLLEXT) \ + wineaudioio.drv$(DLLEXT) \ winedos.dll$(DLLEXT) \ winemp3.acm$(DLLEXT) \ winenas.drv$(DLLEXT) \ @@ -509,6 +511,9 @@ winealsa.drv$(DLLEXT): winmm/winealsa/winealsa.drv$(DLLEXT) winearts.drv$(DLLEXT): winmm/winearts/winearts.drv$(DLLEXT) $(RM) $@ && $(LN_S) winmm/winearts/winearts.drv$(DLLEXT) $@ +wineaudioio.drv$(DLLEXT): winmm/wineaudioio/wineaudioio.drv$(DLLEXT) + $(RM) $@ && $(LN_S) winmm/wineaudioio/wineaudioio.drv$(DLLEXT) $@ + winedos.dll$(DLLEXT): winedos/winedos.dll$(DLLEXT) $(RM) $@ && $(LN_S) winedos/winedos.dll$(DLLEXT) $@ @@ -636,6 +641,7 @@ version/version.dll$(DLLEXT): version win32s/w32skrnl.dll$(DLLEXT): win32s winmm/winealsa/winealsa.drv$(DLLEXT): winmm/winealsa winmm/winearts/winearts.drv$(DLLEXT): winmm/winearts +winmm/wineaudioio/wineaudioio.drv$(DLLEXT): winmm/wineaudioio winedos/winedos.dll$(DLLEXT): winedos msacm/winemp3/winemp3.acm$(DLLEXT): msacm/winemp3 winmm/winenas/winenas.drv$(DLLEXT): winmm/winenas @@ -734,6 +740,7 @@ version/__install__: version.dll$(DLLEXT) win32s/__install__: w32skrnl.dll$(DLLEXT) winmm/winealsa/__install__: winealsa.drv$(DLLEXT) winmm/winearts/__install__: winearts.drv$(DLLEXT) +winmm/wineaudioio/__install__: wineaudioio.drv$(DLLEXT) winedos/__install__: winedos.dll$(DLLEXT) msacm/winemp3/__install__: winemp3.acm$(DLLEXT) winmm/winenas/__install__: winenas.drv$(DLLEXT) @@ -849,6 +856,7 @@ winmm/midimap: winmm.dll$(DLLEXT) user32.dll$(DLLEXT) advapi32.dll$(DLLEXT) kern winmm/wavemap: msacm32.dll$(DLLEXT) winmm.dll$(DLLEXT) user32.dll$(DLLEXT) kernel32.dll$(DLLEXT) winmm/winealsa: winmm.dll$(DLLEXT) user32.dll$(DLLEXT) kernel32.dll$(DLLEXT) ntdll.dll$(DLLEXT) winmm/winearts: winmm.dll$(DLLEXT) user32.dll$(DLLEXT) kernel32.dll$(DLLEXT) +winmm/wineaudioio: winmm.dll$(DLLEXT) user32.dll$(DLLEXT) kernel32.dll$(DLLEXT) ntdll.dll$(DLLEXT) winmm/winenas: winmm.dll$(DLLEXT) user32.dll$(DLLEXT) kernel32.dll$(DLLEXT) winmm/wineoss: winmm.dll$(DLLEXT) user32.dll$(DLLEXT) kernel32.dll$(DLLEXT) winmm: user32.dll$(DLLEXT) advapi32.dll$(DLLEXT) kernel32.dll$(DLLEXT) diff --git a/dlls/winmm/wineaudioio/.cvsignore b/dlls/winmm/wineaudioio/.cvsignore new file mode 100644 index 00000000000..a744d015a92 --- /dev/null +++ b/dlls/winmm/wineaudioio/.cvsignore @@ -0,0 +1,3 @@ +Makefile +wineaudioio.drv.dbg.c +wineaudioio.drv.spec.c diff --git a/dlls/winmm/wineaudioio/Makefile.in b/dlls/winmm/wineaudioio/Makefile.in new file mode 100644 index 00000000000..a41d5a5ed84 --- /dev/null +++ b/dlls/winmm/wineaudioio/Makefile.in @@ -0,0 +1,18 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = wineaudioio.drv +IMPORTS = winmm user32 kernel32 ntdll +EXTRALIBS = @AUDIOIOLIBS@ + +LDDLLFLAGS = @LDDLLFLAGS@ +SYMBOLFILE = $(MODULE).tmp.o + +C_SRCS = \ + audio.c \ + audioio.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/dlls/winmm/wineaudioio/audio.c b/dlls/winmm/wineaudioio/audio.c new file mode 100644 index 00000000000..1968f7cacba --- /dev/null +++ b/dlls/winmm/wineaudioio/audio.c @@ -0,0 +1,2282 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ +/* + * Wine Driver for Libaudioio + * Derived from the Wine OSS Sample Driver + * Copyright 1994 Martin Ayotte + * 1999 Eric Pouech (async playing in waveOut/waveIn) + * 2000 Eric Pouech (loops in waveOut) + * 2002 Robert Lunnon (Modifications for libaudioio) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Note large hacks done to effectively disable DSound altogether + * Also Input is not yet working (Lots more work to be done there) + * But this does make a reasonable output driver for solaris untill the arts driver comes up to speed + */ + +/* + * FIXME: + * pause in waveOut does not work correctly + * full duplex (in/out) is not working (device is opened twice for Out + * and In) (OSS is known for its poor duplex capabilities, alsa is + * better) + */ + +/*#define EMULATE_SB16*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#ifdef HAVE_LIBAUDIOIO_H +#include +#endif +#include "windef.h" +#include "wingdi.h" +#include "winerror.h" +#include "wine/winuser16.h" +#include "mmddk.h" +#include "dsound.h" +#include "dsdriver.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wave); + +#ifdef HAVE_LIBAUDIOIO + +/* Allow 1% deviation for sample rates (some ES137x cards) */ +#define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0) + +#define SOUND_DEV "/dev/audio" +#define DEFAULT_FRAGMENT_SIZE 4096 + + + +#define MAX_WAVEOUTDRV (1) +#define MAX_WAVEINDRV (1) + +/* state diagram for waveOut writing: + * + * +---------+-------------+---------------+---------------------------------+ + * | state | function | event | new state | + * +---------+-------------+---------------+---------------------------------+ + * | | open() | | STOPPED | + * | PAUSED | write() | | PAUSED | + * | STOPPED | write() | | PLAYING | + * | PLAYING | write() | HEADER | PLAYING | + * | (other) | write() | | | + * | (any) | pause() | PAUSING | PAUSED | + * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) | + * | (any) | reset() | RESETTING | STOPPED | + * | (any) | close() | CLOSING | CLOSED | + * +---------+-------------+---------------+---------------------------------+ + */ + +/* states of the playing device */ +#define WINE_WS_PLAYING 0 +#define WINE_WS_PAUSED 1 +#define WINE_WS_STOPPED 2 +#define WINE_WS_CLOSED 3 + +/* events to be send to device */ +#define WINE_WM_PAUSING (WM_USER + 1) +#define WINE_WM_RESTARTING (WM_USER + 2) +#define WINE_WM_RESETTING (WM_USER + 3) +#define WINE_WM_CLOSING (WM_USER + 4) +#define WINE_WM_HEADER (WM_USER + 5) + +#define WINE_WM_FIRST WINE_WM_PAUSING +#define WINE_WM_LAST WINE_WM_HEADER + +typedef struct { + int msg; + DWORD param; +} WWO_MSG; + +typedef struct { + int unixdev; + volatile int state; /* one of the WINE_WS_ manifest constants */ + DWORD dwFragmentSize; /* size of OSS buffer fragment */ + WAVEOPENDESC waveDesc; + WORD wFlags; + PCMWAVEFORMAT format; + LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */ + LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */ + LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */ + DWORD dwLoops; /* private copy of loop counter */ + + DWORD dwLastFragDone; /* time in ms, when last played fragment will be actually played */ + DWORD dwPlayedTotal; /* number of bytes played since opening */ + + /* info on current lpQueueHdr->lpWaveHdr */ + DWORD dwOffCurrHdr; /* offset in lpPlayPtr->lpData for fragments */ + DWORD dwRemain; /* number of bytes to write to end the current fragment */ + + /* synchronization stuff */ + HANDLE hThread; + DWORD dwThreadID; + HANDLE hEvent; +#define WWO_RING_BUFFER_SIZE 30 + WWO_MSG messages[WWO_RING_BUFFER_SIZE]; + int msg_tosave; + int msg_toget; + HANDLE msg_event; + CRITICAL_SECTION msg_crst; + WAVEOUTCAPSA caps; + + /* DirectSound stuff */ + LPBYTE mapping; + DWORD maplen; +} WINE_WAVEOUT; + +typedef struct { + int unixdev; + volatile int state; + DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */ + WAVEOPENDESC waveDesc; + WORD wFlags; + PCMWAVEFORMAT format; + LPWAVEHDR lpQueuePtr; + DWORD dwTotalRecorded; + WAVEINCAPSA caps; + BOOL bTriggerSupport; + + /* synchronization stuff */ + HANDLE hThread; + DWORD dwThreadID; + HANDLE hEvent; +} WINE_WAVEIN; + +static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV]; +static WINE_WAVEIN WInDev [MAX_WAVEINDRV ]; + +static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv); + +/*======================================================================* + * Low level WAVE implementation * + *======================================================================*/ +SampleSpec_t spec[2]; + + + +LONG LIBAUDIOIO_WaveInit(void) +{ + int audio; + int smplrate; + int samplesize = 16; + int dsp_stereo = 1; + int bytespersmpl; + int caps; + int mask; + int i; + + int audio_fd; + int mode; + TRACE("Init ENTERED rate = %d\n",spec[PLAYBACK].rate); + spec[RECORD].channels=spec[PLAYBACK].channels=2; + spec[RECORD].max_blocks=spec[PLAYBACK].max_blocks=16; + spec[RECORD].rate=spec[PLAYBACK].rate=44100; + spec[RECORD].encoding=spec[PLAYBACK].encoding= ENCODE_PCM; + spec[RECORD].precision=spec[PLAYBACK].precision=16 ; + spec[RECORD].endian=spec[PLAYBACK].endian=ENDIAN_INTEL; + spec[RECORD].disable_threads=spec[PLAYBACK].disable_threads=1; + spec[RECORD].type=spec[PLAYBACK].type=TYPE_SIGNED; // in 16 bit mode this is what typical PC hardware expects + + mode = O_WRONLY|O_NDELAY; + + /* start with output device */ + + /* initialize all device handles to -1 */ + for (i = 0; i < MAX_WAVEOUTDRV; ++i) + { + WOutDev[i].unixdev = -1; + } + + /* FIXME: only one device is supported */ + memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps)); + + + + /* FIXME: some programs compare this string against the content of the registry + * for MM drivers. The names have to match in order for the program to work + * (e.g. MS win9x mplayer.exe) + */ +#ifdef EMULATE_SB16 + WOutDev[0].caps.wMid = 0x0002; + WOutDev[0].caps.wPid = 0x0104; + strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out"); +#else + WOutDev[0].caps.wMid = 0x00FF; /* Manufac ID */ + WOutDev[0].caps.wPid = 0x0001; /* Product ID */ + /* strcpy(WOutDev[0].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/ + strcpy(WOutDev[0].caps.szPname, "CS4236/37/38"); +#endif + WOutDev[0].caps.vDriverVersion = 0x0100; + WOutDev[0].caps.dwFormats = 0x00000000; + WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME; + + /* + * Libaudioio works differently, you tell it what spec audio you want to write and it + * Guarantees to match it by converting to the final format on the fly + *So we dont need to read back and compare + */ + + bytespersmpl = spec[PLAYBACK].precision/8; + + WOutDev[0].caps.wChannels = spec[PLAYBACK].channels; + + +/* Fixme: Libaudioio 0.2 doesn't support balance yet (Libaudioio 0.3 does so this must be fixed later)*/ + +/* if (WOutDev[0].caps.wChannels > 1) WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;*/ + TRACE("Init sammplerate= %d\n",spec[PLAYBACK].rate); + + smplrate = spec[PLAYBACK].rate; +/* + * We have set up the data format to be 16 bit signed in intel format + * For Big Endian machines libaudioio will convert the data to bigendian for us + */ + + WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M16; + if (WOutDev[0].caps.wChannels > 1) + WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S16; + +/* Don't understand this yet, but I dont think this functionality is portable, leave here for future evaluation + * if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) { + * TRACE("OSS dsp out caps=%08X\n", caps); + * if ((caps & DSP_CAP_REALTIME) && !(caps & DSP_CAP_BATCH)) { + * WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE; + * } + */ /* well, might as well use the DirectSound cap flag for something */ +/* if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) && + * !(caps & DSP_CAP_BATCH)) + * WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND; + * } + */ + + +/* + * Note that libaudioio audio capture is not proven yet, in our open call + * set the spec for input audio the same as for output + */ + TRACE("out dwFormats = %08lX, dwSupport = %08lX\n", + WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport); + + for (i = 0; i < MAX_WAVEINDRV; ++i) + { + WInDev[i].unixdev = -1; + } + + memset(&WInDev[0].caps, 0, sizeof(WInDev[0].caps)); + + + +#ifdef EMULATE_SB16 + WInDev[0].caps.wMid = 0x0002; + WInDev[0].caps.wPid = 0x0004; + strcpy(WInDev[0].caps.szPname, "SB16 Wave In"); +#else + WInDev[0].caps.wMid = 0x00FF; /* Manufac ID */ + WInDev[0].caps.wPid = 0x0001; /* Product ID */ + strcpy(WInDev[0].caps.szPname, "OpenSoundSystem WAVIN Driver"); +#endif + WInDev[0].caps.dwFormats = 0x00000000; + WInDev[0].caps.wChannels = spec[RECORD].channels; + + WInDev[0].bTriggerSupport = TRUE; /* Maybe :-) */ + + bytespersmpl = spec[RECORD].precision/8; + smplrate = spec[RECORD].rate; + + WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M16; + if (WInDev[0].caps.wChannels > 1) + WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S16; + + + TRACE("in dwFormats = %08lX\n", WInDev[0].caps.dwFormats); + + return 0; +} + +/************************************************************************** + * LIBAUDIOIO_NotifyClient [internal] + */ +static DWORD LIBAUDIOIO_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwParam2) +{ + TRACE("wDevID = %04X wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2); + + switch (wMsg) { + case WOM_OPEN: + case WOM_CLOSE: + case WOM_DONE: + if (wDevID >= MAX_WAVEOUTDRV) return MCIERR_INTERNAL; + + if (WOutDev[wDevID].wFlags != DCB_NULL && + !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback, + WOutDev[wDevID].wFlags, + WOutDev[wDevID].waveDesc.hWave, + wMsg, + WOutDev[wDevID].waveDesc.dwInstance, + dwParam1, + dwParam2)) { + WARN("can't notify client !\n"); + return MMSYSERR_NOERROR; + } + break; + + case WIM_OPEN: + case WIM_CLOSE: + case WIM_DATA: + if (wDevID >= MAX_WAVEINDRV) return MCIERR_INTERNAL; + + if (WInDev[wDevID].wFlags != DCB_NULL && + !DriverCallback(WInDev[wDevID].waveDesc.dwCallback, + WInDev[wDevID].wFlags, + WInDev[wDevID].waveDesc.hWave, + wMsg, + WInDev[wDevID].waveDesc.dwInstance, + dwParam1, + dwParam2)) { + WARN("can't notify client !\n"); + return MMSYSERR_NOERROR; + } + break; + default: + FIXME("Unknown CB message %u\n", wMsg); + break; + } + return 0; +} + +/*======================================================================* + * Low level WAVE OUT implementation * + *======================================================================*/ + +/************************************************************************** + * wodPlayer_WriteFragments [internal] + * + * wodPlayer helper. Writes as many fragments as it can to unixdev. + * Returns TRUE in case of buffer underrun. + */ +static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo) +{ + LPWAVEHDR lpWaveHdr; + LPBYTE lpData; + int count; + +TRACE("wodPlayer_WriteFragments sammplerate= %d\n",spec[PLAYBACK].rate); + for (;;) { + + +/* + * Libaudioio doesn't buffer the same way as linux, you can write data is any block size + *And libaudioio just tracks the number of blocks in the streams queue to control latency + */ + + if (!AudioIOCheckWriteReady()) return FALSE; /* Returns false if you have execeeded your specified latency (No space left)*/ + + lpWaveHdr = wwo->lpPlayPtr; + if (!lpWaveHdr) { + if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */ + wwo->dwLastFragDone && /* first fragment has been played */ + AudioIOCheckUnderrun()) { /* done with all waveOutWrite()' fragments */ + /* FIXME: should do better handling here */ + WARN("Oooch, buffer underrun !\n"); + return TRUE; /* force resetting of waveOut device */ + } + return FALSE; /* wait a bit */ + } + + if (wwo->dwOffCurrHdr == 0) { + TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength); + if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) { + if (wwo->lpLoopPtr) { + WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr); + } else { + TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr); + wwo->lpLoopPtr = lpWaveHdr; + /* Windows does not touch WAVEHDR.dwLoops, + * so we need to make an internal copy */ + wwo->dwLoops = lpWaveHdr->dwLoops; + } + } + } + + lpData = lpWaveHdr->lpData; + + /* finish current wave hdr ? */ + if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) { + DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr; + + /* write end of current wave hdr */ + count = AudioIOWrite(lpData + wwo->dwOffCurrHdr, toWrite); + TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count); + + if (count > 0 || toWrite == 0) { + DWORD tc = GetTickCount(); + + if (wwo->dwLastFragDone /* + guard time ?? */ < tc) + wwo->dwLastFragDone = tc; + wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec; + + lpWaveHdr->reserved = wwo->dwLastFragDone; + TRACE("Tagging hdr %p with %08lx\n", lpWaveHdr, wwo->dwLastFragDone); + + /* WAVEHDR written, go to next one */ + if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) { + if (--wwo->dwLoops > 0) { + wwo->lpPlayPtr = wwo->lpLoopPtr; + } else { + /* last one played */ + if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) { + FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n"); + /* shall we consider the END flag for the closing loop or for + * the opening one or for both ??? + * code assumes for closing loop only + */ + wwo->lpLoopPtr = lpWaveHdr; + } else { + wwo->lpLoopPtr = NULL; + } + wwo->lpPlayPtr = lpWaveHdr->lpNext; + } + } else { + wwo->lpPlayPtr = lpWaveHdr->lpNext; + } + wwo->dwOffCurrHdr = 0; + if ((wwo->dwRemain -= count) == 0) { + wwo->dwRemain = wwo->dwFragmentSize; + } + } + continue; /* try to go to use next wavehdr */ + } else { + count = AudioIOWrite( lpData + wwo->dwOffCurrHdr, wwo->dwRemain); + TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count); + if (count > 0) { + DWORD tc = GetTickCount(); + + if (wwo->dwLastFragDone /* + guard time ?? */ < tc) + wwo->dwLastFragDone = tc; + wwo->dwLastFragDone += (wwo->dwRemain * 1000) / wwo->format.wf.nAvgBytesPerSec; + + TRACE("Tagging frag with %08lx\n", wwo->dwLastFragDone); + + wwo->dwOffCurrHdr += count; + wwo->dwRemain = wwo->dwFragmentSize; + } + } + } + +} + +int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param) +{ + TRACE("wodPlayerMessage sammplerate= %d msg=%d\n",spec[PLAYBACK].rate,msg); + EnterCriticalSection(&wwo->msg_crst); + if ((wwo->msg_tosave == wwo->msg_toget) /* buffer overflow ? */ + && (wwo->messages[wwo->msg_toget].msg)) + { + ERR("buffer overflow !?\n"); + LeaveCriticalSection(&wwo->msg_crst); + return 0; + } + + wwo->messages[wwo->msg_tosave].msg = msg; + wwo->messages[wwo->msg_tosave].param = param; + wwo->msg_tosave++; + if (wwo->msg_tosave > WWO_RING_BUFFER_SIZE-1) + wwo->msg_tosave = 0; + LeaveCriticalSection(&wwo->msg_crst); + /* signal a new message */ + SetEvent(wwo->msg_event); + return 1; +} + +int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param) +{ + EnterCriticalSection(&wwo->msg_crst); + + if (wwo->msg_toget == wwo->msg_tosave) /* buffer empty ? */ + { + LeaveCriticalSection(&wwo->msg_crst); + return 0; + } + + *msg = wwo->messages[wwo->msg_toget].msg; + wwo->messages[wwo->msg_toget].msg = 0; + *param = wwo->messages[wwo->msg_toget].param; + wwo->msg_toget++; + if (wwo->msg_toget > WWO_RING_BUFFER_SIZE-1) + wwo->msg_toget = 0; + LeaveCriticalSection(&wwo->msg_crst); + return 1; +} + +/************************************************************************** + * wodPlayer_Notify [internal] + * + * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content + * have been played (actually to speaker, not to unixdev fd). + */ +static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force) +{ + LPWAVEHDR lpWaveHdr; + DWORD tc = GetTickCount(); + + while (wwo->lpQueuePtr && + (force || + (wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) { + lpWaveHdr = wwo->lpQueuePtr; + + if (lpWaveHdr->reserved > tc && !force) break; + + wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength; + wwo->lpQueuePtr = lpWaveHdr->lpNext; + + lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; + lpWaveHdr->dwFlags |= WHDR_DONE; + + TRACE("Notifying client with %p\n", lpWaveHdr); + if (LIBAUDIOIO_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) { + WARN("can't notify client !\n"); + } + } +} + +/************************************************************************** + * wodPlayer_Reset [internal] + * + * wodPlayer helper. Resets current output stream. + */ +static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset) +{ + /* updates current notify list */ + wodPlayer_Notify(wwo, uDevID, FALSE); + + /* flush all possible output */ +/* + *FIXME In the original code I think this aborted IO. With Libaudioio you have to wait + * The following function just blocks untill I/O is complete + * There is possibly a way to abort the blocks buffered in streams + * but this approach seems to work OK + */ + AudioIOFlush(); + + + + wwo->dwOffCurrHdr = 0; + wwo->dwRemain = wwo->dwFragmentSize; + if (reset) { + /* empty notify list */ + wodPlayer_Notify(wwo, uDevID, TRUE); + + wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; + wwo->state = WINE_WS_STOPPED; + wwo->dwPlayedTotal = 0; + } else { + /* FIXME: this is not accurate when looping, but can be do better ? */ + wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr; + wwo->state = WINE_WS_PAUSED; + } +} + +/************************************************************************** + * wodPlayer [internal] + */ +static DWORD CALLBACK wodPlayer(LPVOID pmt) +{ + WORD uDevID = (DWORD)pmt; + WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID]; + WAVEHDR* lpWaveHdr; + DWORD dwSleepTime; + int msg; + DWORD param; + DWORD tc; + BOOL had_msg; + + wwo->state = WINE_WS_STOPPED; + + wwo->dwLastFragDone = 0; + wwo->dwOffCurrHdr = 0; + wwo->dwRemain = wwo->dwFragmentSize; + wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL; + wwo->dwPlayedTotal = 0; + + TRACE("imhere[0]\n"); + SetEvent(wwo->hEvent); + + for (;;) { + /* wait for dwSleepTime or an event in thread's queue + * FIXME: + * - is wait time calculation optimal ? + * - these 100 ms parts should be changed, but Eric reports + * that the wodPlayer thread might lock up if we use INFINITE + * (strange !), so I better don't change that now... */ + if (wwo->state != WINE_WS_PLAYING) + dwSleepTime = 100; + else + { + tc = GetTickCount(); + if (tc < wwo->dwLastFragDone) + { + /* calculate sleep time depending on when the last fragment + will be played */ + dwSleepTime = (wwo->dwLastFragDone - tc)*7/10; + if (dwSleepTime > 100) + dwSleepTime = 100; + } + else + dwSleepTime = 0; + } + + TRACE("imhere[1] tc = %08lx\n", GetTickCount()); + if (dwSleepTime) + WaitForSingleObject(wwo->msg_event, dwSleepTime); + TRACE("imhere[2] (q=%p p=%p) tc = %08lx\n", wwo->lpQueuePtr, + wwo->lpPlayPtr, GetTickCount()); + had_msg = FALSE; + while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m)) { + had_msg = TRUE; + switch (msg) { + case WINE_WM_PAUSING: + wodPlayer_Reset(wwo, uDevID, FALSE); + wwo->state = WINE_WS_PAUSED; + SetEvent(wwo->hEvent); + break; + case WINE_WM_RESTARTING: + wwo->state = WINE_WS_PLAYING; + SetEvent(wwo->hEvent); + break; + case WINE_WM_HEADER: + lpWaveHdr = (LPWAVEHDR)param; + + /* insert buffer at the end of queue */ + { + LPWAVEHDR* wh; + for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); + *wh = lpWaveHdr; + } + if (!wwo->lpPlayPtr) wwo->lpPlayPtr = lpWaveHdr; + if (wwo->state == WINE_WS_STOPPED) + wwo->state = WINE_WS_PLAYING; + break; + case WINE_WM_RESETTING: + wodPlayer_Reset(wwo, uDevID, TRUE); + SetEvent(wwo->hEvent); + break; + case WINE_WM_CLOSING: + /* sanity check: this should not happen since the device must have been reset before */ + if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n"); + wwo->hThread = 0; + wwo->state = WINE_WS_CLOSED; + SetEvent(wwo->hEvent); + ExitThread(0); + /* shouldn't go here */ + default: + FIXME("unknown message %d\n", msg); + break; + } + if (wwo->state == WINE_WS_PLAYING) { + wodPlayer_WriteFragments(wwo); + } + wodPlayer_Notify(wwo, uDevID, FALSE); + } + + if (!had_msg) { /* if we've received a msg we've just done this so we + won't repeat it */ + if (wwo->state == WINE_WS_PLAYING) { + wodPlayer_WriteFragments(wwo); + } + wodPlayer_Notify(wwo, uDevID, FALSE); + } + } + ExitThread(0); + /* just for not generating compilation warnings... should never be executed */ + return 0; +} + +/************************************************************************** + * wodGetDevCaps [internal] + */ +static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize) +{ + TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize); + + if (lpCaps == NULL) return MMSYSERR_NOTENABLED; + + if (wDevID >= MAX_WAVEOUTDRV) { + TRACE("MAX_WAVOUTDRV reached !\n"); + return MMSYSERR_BADDEVICEID; + } + + memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps))); + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodOpen [internal] + */ +static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) +{ + int audio; + int format; + int sample_rate; + int dsp_stereo; + int fragment_size; + int audio_fragment; + WINE_WAVEOUT* wwo; + + TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags); + if (lpDesc == NULL) { + WARN("Invalid Parameter !\n"); + return MMSYSERR_INVALPARAM; + } + if (wDevID >= MAX_WAVEOUTDRV) { + TRACE("MAX_WAVOUTDRV reached !\n"); + return MMSYSERR_BADDEVICEID; + } + + /* only PCM format is supported so far... */ + if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM || + lpDesc->lpFormat->nChannels == 0 || + lpDesc->lpFormat->nSamplesPerSec == 0) { + WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", + lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, + lpDesc->lpFormat->nSamplesPerSec); + return WAVERR_BADFORMAT; + } + + if (dwFlags & WAVE_FORMAT_QUERY) { + TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", + lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, + lpDesc->lpFormat->nSamplesPerSec); + return MMSYSERR_NOERROR; + } + + wwo = &WOutDev[wDevID]; + + if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND)) + /* not supported, ignore it */ + dwFlags &= ~WAVE_DIRECTSOUND; + + if (access(SOUND_DEV, 0) != 0) + return MMSYSERR_NOTENABLED; + + audio = AudioIOOpenX( O_WRONLY|O_NDELAY,&spec[PLAYBACK],&spec[PLAYBACK]); + + if (audio == -1) { + WARN("can't open sound device %s (%s)!\n", SOUND_DEV, strerror(errno)); + return MMSYSERR_ALLOCATED; + } + /* fcntl(audio, F_SETFD, 1); *//* set close on exec flag - Dunno about this (RL)*/ + wwo->unixdev = audio; + wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); + + memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC)); + memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT)); + + if (wwo->format.wBitsPerSample == 0) { + WARN("Resetting zeroed wBitsPerSample\n"); + wwo->format.wBitsPerSample = 8 * + (wwo->format.wf.nAvgBytesPerSec / + wwo->format.wf.nSamplesPerSec) / + wwo->format.wf.nChannels; + } + + if (dwFlags & WAVE_DIRECTSOUND) { + if (wwo->caps.dwSupport & WAVECAPS_SAMPLEACCURATE) + /* we have realtime DirectSound, fragments just waste our time, + * but a large buffer is good, so choose 64KB (32 * 2^11) */ + audio_fragment = 0x0020000B; + else + /* to approximate realtime, we must use small fragments, + * let's try to fragment the above 64KB (256 * 2^8) */ + audio_fragment = 0x01000008; + } else { + /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec + * thus leading to 46ms per fragment, and a turnaround time of 185ms + */ + /* 16 fragments max, 2^10=1024 bytes per fragment */ + audio_fragment = 0x000F000A; + } + sample_rate = wwo->format.wf.nSamplesPerSec; + dsp_stereo = (wwo->format.wf.nChannels > 1) ? 1 : 0; + + + + /*Set the sample rate*/ + spec[PLAYBACK].rate=sample_rate; + + /*And the size and signedness*/ + spec[PLAYBACK].precision=(wwo->format.wBitsPerSample ); + if (spec[PLAYBACK].precision==16) spec[PLAYBACK].type=TYPE_SIGNED; else spec[PLAYBACK].type=TYPE_UNSIGNED; + spec[PLAYBACK].channels=(wwo->format.wf.nChannels); + spec[PLAYBACK].encoding=ENCODE_PCM; + spec[PLAYBACK].endian=ENDIAN_INTEL; + spec[PLAYBACK].max_blocks=16; /*FIXME This is the libaudioio equivalent to fragments, it controls latency*/ + + audio = AudioIOOpenX( O_WRONLY|O_NDELAY,&spec[PLAYBACK],&spec[PLAYBACK]); + + if (audio == -1) { + WARN("can't open sound device %s (%s)!\n", SOUND_DEV, strerror(errno)); + return MMSYSERR_ALLOCATED; + } + /* fcntl(audio, F_SETFD, 1); *//* set close on exec flag */ + wwo->unixdev = audio; + + + /* even if we set fragment size above, read it again, just in case */ + + wwo->dwFragmentSize = DEFAULT_FRAGMENT_SIZE; + + wwo->msg_toget = 0; + wwo->msg_tosave = 0; + wwo->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL); + memset(wwo->messages, 0, sizeof(WWO_MSG)*WWO_RING_BUFFER_SIZE); + InitializeCriticalSection(&wwo->msg_crst); + + if (!(dwFlags & WAVE_DIRECTSOUND)) { + wwo->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID)); + WaitForSingleObject(wwo->hEvent, INFINITE); + } else { + wwo->hEvent = INVALID_HANDLE_VALUE; + wwo->hThread = INVALID_HANDLE_VALUE; + wwo->dwThreadID = 0; + } + + TRACE("fd=%d fragmentSize=%ld\n", + wwo->unixdev, wwo->dwFragmentSize); + if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign) + ERR("Fragment doesn't contain an integral number of data blocks\n"); + + TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n", + wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec, + wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels, + wwo->format.wf.nBlockAlign); + + if (LIBAUDIOIO_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { + WARN("can't notify client !\n"); + return MMSYSERR_INVALPARAM; + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodClose [internal] + */ +static DWORD wodClose(WORD wDevID) +{ + DWORD ret = MMSYSERR_NOERROR; + WINE_WAVEOUT* wwo; + + TRACE("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + wwo = &WOutDev[wDevID]; + if (wwo->lpQueuePtr) { + WARN("buffers still playing !\n"); + ret = WAVERR_STILLPLAYING; + } else { + TRACE("imhere[3-close]\n"); + if (wwo->hEvent != INVALID_HANDLE_VALUE) { + wodPlayer_Message(wwo, WINE_WM_CLOSING, 0); + WaitForSingleObject(wwo->hEvent, INFINITE); + CloseHandle(wwo->hEvent); + } + if (wwo->mapping) { + munmap(wwo->mapping, wwo->maplen); + wwo->mapping = NULL; + } + + AudioIOClose(); + wwo->unixdev = -1; + wwo->dwFragmentSize = 0; + if (LIBAUDIOIO_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { + WARN("can't notify client !\n"); + ret = MMSYSERR_INVALPARAM; + } + } + return ret; +} + +/************************************************************************** + * wodWrite [internal] + * + */ +static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); + + /* first, do the sanity checks... */ + if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) { + WARN("bad dev ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) + return WAVERR_UNPREPARED; + + if (lpWaveHdr->dwFlags & WHDR_INQUEUE) + return WAVERR_STILLPLAYING; + + lpWaveHdr->dwFlags &= ~WHDR_DONE; + lpWaveHdr->dwFlags |= WHDR_INQUEUE; + lpWaveHdr->lpNext = 0; + + TRACE("imhere[3-HEADER]\n"); + wodPlayer_Message(&WOutDev[wDevID], WINE_WM_HEADER, (DWORD)lpWaveHdr); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodPrepare [internal] + */ +static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); + + if (wDevID >= MAX_WAVEOUTDRV) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + if (lpWaveHdr->dwFlags & WHDR_INQUEUE) + return WAVERR_STILLPLAYING; + + lpWaveHdr->dwFlags |= WHDR_PREPARED; + lpWaveHdr->dwFlags &= ~WHDR_DONE; + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodUnprepare [internal] + */ +static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); + + if (wDevID >= MAX_WAVEOUTDRV) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + if (lpWaveHdr->dwFlags & WHDR_INQUEUE) + return WAVERR_STILLPLAYING; + + lpWaveHdr->dwFlags &= ~WHDR_PREPARED; + lpWaveHdr->dwFlags |= WHDR_DONE; + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodPause [internal] + */ +static DWORD wodPause(WORD wDevID) +{ + TRACE("(%u);!\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + TRACE("imhere[3-PAUSING]\n"); + wodPlayer_Message(&WOutDev[wDevID], WINE_WM_PAUSING, 0); + WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodRestart [internal] + */ +static DWORD wodRestart(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + if (WOutDev[wDevID].state == WINE_WS_PAUSED) { + TRACE("imhere[3-RESTARTING]\n"); + wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESTARTING, 0); + WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE); + } + + /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */ + /* FIXME: Myst crashes with this ... hmm -MM + if (LIBAUDIOIO_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) { + WARN("can't notify client !\n"); + return MMSYSERR_INVALPARAM; + } + */ + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodReset [internal] + */ +static DWORD wodReset(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + TRACE("imhere[3-RESET]\n"); + wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESETTING, 0); + WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE); + + return MMSYSERR_NOERROR; +} + + +/************************************************************************** + * wodGetPosition [internal] + */ +static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize) +{ + int time; + DWORD val; + WINE_WAVEOUT* wwo; + + TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize); + + if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + if (lpTime == NULL) return MMSYSERR_INVALPARAM; + + wwo = &WOutDev[wDevID]; + val = wwo->dwPlayedTotal; + + TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n", + lpTime->wType, wwo->format.wBitsPerSample, + wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels, + wwo->format.wf.nAvgBytesPerSec); + TRACE("dwTotalPlayed=%lu\n", val); + + switch (lpTime->wType) { + case TIME_BYTES: + lpTime->u.cb = val; + TRACE("TIME_BYTES=%lu\n", lpTime->u.cb); + break; + case TIME_SAMPLES: + lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample; + TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample); + break; + case TIME_SMPTE: + time = val / (wwo->format.wf.nAvgBytesPerSec / 1000); + lpTime->u.smpte.hour = time / 108000; + time -= lpTime->u.smpte.hour * 108000; + lpTime->u.smpte.min = time / 1800; + time -= lpTime->u.smpte.min * 1800; + lpTime->u.smpte.sec = time / 30; + time -= lpTime->u.smpte.sec * 30; + lpTime->u.smpte.frame = time; + lpTime->u.smpte.fps = 30; + TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n", + lpTime->u.smpte.hour, lpTime->u.smpte.min, + lpTime->u.smpte.sec, lpTime->u.smpte.frame); + break; + default: + FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType); + lpTime->wType = TIME_MS; + case TIME_MS: + lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000); + TRACE("TIME_MS=%lu\n", lpTime->u.ms); + break; + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodGetVolume [internal] + */ +static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol) +{ + int mixer; + int vol,bal; + DWORD left, right; + + TRACE("(%u, %p);\n", wDevID, lpdwVol); + + if (lpdwVol == NULL) + return MMSYSERR_NOTENABLED; + + vol=AudioIOGetPlaybackVolume(); + bal=AudioIOGetPlaybackBalance(); + + + if(bal<0) { + left = vol; + right=-(vol*(-100+bal)/100); + } + else + { + right = vol; + left=(vol*(100-bal)/100); + } + + *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16); + return MMSYSERR_NOERROR; +} + + +/************************************************************************** + * wodSetVolume [internal] + */ +static DWORD wodSetVolume(WORD wDevID, DWORD dwParam) +{ + int mixer; + int volume,bal; + DWORD left, right; + + TRACE("(%u, %08lX);\n", wDevID, dwParam); + + left = (LOWORD(dwParam) * 100) / 0xFFFFl; + right = (HIWORD(dwParam) * 100) / 0xFFFFl; + volume = max(left , right ); + bal=min(left,right); + bal=bal*100/volume; + if(right>left) bal=-100+bal; else bal=100-bal; + + AudioIOSetPlaybackVolume(volume); + AudioIOSetPlaybackBalance(bal); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * wodGetNumDevs [internal] + */ +static DWORD wodGetNumDevs(void) +{ + DWORD ret = 1; + + /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */ + int audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0); + + if (audio == -1) { + if (errno != EBUSY) + ret = 0; + } else { + close(audio); + + } + TRACE("NumDrivers = %d\n",ret); + return ret; +} + +/************************************************************************** + * wodMessage (WINEOSS.7) + */ +DWORD WINAPI LIBAUDIOIO_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser, + DWORD dwParam1, DWORD dwParam2) +{ + TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n", + wDevID, wMsg, dwUser, dwParam1, dwParam2); + + switch (wMsg) { + case DRVM_INIT: + case DRVM_EXIT: + case DRVM_ENABLE: + case DRVM_DISABLE: + /* FIXME: Pretend this is supported */ + return 0; + case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2); + case WODM_CLOSE: return wodClose (wDevID); + case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2); + case WODM_PAUSE: return wodPause (wDevID); + case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2); + case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED; + case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); + case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); + case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2); + case WODM_GETNUMDEVS: return wodGetNumDevs (); + case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED; + case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED; + case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED; + case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED; + case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1); + case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1); + case WODM_RESTART: return wodRestart (wDevID); + case WODM_RESET: return wodReset (wDevID); + + case DRV_QUERYDSOUNDIFACE: return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1); + default: + FIXME("unknown message %d!\n", wMsg); + } + return MMSYSERR_NOTSUPPORTED; +} + +/*======================================================================* + * Low level DSOUND implementation * + * While I have tampered somewhat with this code it is wholely unlikely that it works + * Elsewhere the driver returns Not Implemented for DIrectSound + * While it may be possible to map the sound device on Solaris + * Doing so would bypass the libaudioio library and therefore break any conversions + * that the libaudioio sample specification converter is doing + * **** All this is untested so far + *======================================================================*/ + +typedef struct IDsDriverImpl IDsDriverImpl; +typedef struct IDsDriverBufferImpl IDsDriverBufferImpl; + +struct IDsDriverImpl +{ + /* IUnknown fields */ + ICOM_VFIELD(IDsDriver); + DWORD ref; + /* IDsDriverImpl fields */ + UINT wDevID; + IDsDriverBufferImpl*primary; +}; + +struct IDsDriverBufferImpl +{ + /* IUnknown fields */ + ICOM_VFIELD(IDsDriverBuffer); + DWORD ref; + /* IDsDriverBufferImpl fields */ + IDsDriverImpl* drv; + DWORD buflen; +}; + +static HRESULT DSDB_MapPrimary(IDsDriverBufferImpl *dsdb) +{ + WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]); + if (!wwo->mapping) { + wwo->mapping = mmap(NULL, wwo->maplen, PROT_WRITE, MAP_SHARED, + wwo->unixdev, 0); + if (wwo->mapping == (LPBYTE)-1) { + ERR("(%p): Could not map sound device for direct access (errno=%d)\n", dsdb, errno); + return DSERR_GENERIC; + } + TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dsdb, wwo->mapping, wwo->maplen); + + /* for some reason, es1371 and sblive! sometimes have junk in here. */ + memset(wwo->mapping,0,wwo->maplen); /* clear it, or we get junk noise */ + } + return DS_OK; +} + +static HRESULT DSDB_UnmapPrimary(IDsDriverBufferImpl *dsdb) +{ + WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]); + if (wwo->mapping) { + if (munmap(wwo->mapping, wwo->maplen) < 0) { + ERR("(%p): Could not unmap sound device (errno=%d)\n", dsdb, errno); + return DSERR_GENERIC; + } + wwo->mapping = NULL; + TRACE("(%p): sound device unmapped\n", dsdb); + } + return DS_OK; +} + +static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj) +{ + /* ICOM_THIS(IDsDriverBufferImpl,iface); */ + FIXME("(): stub!\n"); + return DSERR_UNSUPPORTED; +} + +static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface) +{ + ICOM_THIS(IDsDriverBufferImpl,iface); + This->ref++; + return This->ref; +} + +static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface) +{ + ICOM_THIS(IDsDriverBufferImpl,iface); + if (--This->ref) + return This->ref; + if (This == This->drv->primary) + This->drv->primary = NULL; + DSDB_UnmapPrimary(This); + HeapFree(GetProcessHeap(),0,This); + return 0; +} + +static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface, + LPVOID*ppvAudio1,LPDWORD pdwLen1, + LPVOID*ppvAudio2,LPDWORD pdwLen2, + DWORD dwWritePosition,DWORD dwWriteLen, + DWORD dwFlags) +{ + /* ICOM_THIS(IDsDriverBufferImpl,iface); */ + /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK, + * and that we don't support secondary buffers, this method will never be called */ + TRACE("(%p): stub\n",iface); + return DSERR_UNSUPPORTED; +} + +static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface, + LPVOID pvAudio1,DWORD dwLen1, + LPVOID pvAudio2,DWORD dwLen2) +{ + /* ICOM_THIS(IDsDriverBufferImpl,iface); */ + TRACE("(%p): stub\n",iface); + return DSERR_UNSUPPORTED; +} + +static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface, + LPWAVEFORMATEX pwfx) +{ + /* ICOM_THIS(IDsDriverBufferImpl,iface); */ + + TRACE("(%p,%p)\n",iface,pwfx); + /* On our request (GetDriverDesc flags), DirectSound has by now used + * waveOutClose/waveOutOpen to set the format... + * unfortunately, this means our mmap() is now gone... + * so we need to somehow signal to our DirectSound implementation + * that it should completely recreate this HW buffer... + * this unexpected error code should do the trick... */ + return DSERR_BUFFERLOST; +} + +static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq) +{ + /* ICOM_THIS(IDsDriverBufferImpl,iface); */ + TRACE("(%p,%ld): stub\n",iface,dwFreq); + return DSERR_UNSUPPORTED; +} + +static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan) +{ + /* ICOM_THIS(IDsDriverBufferImpl,iface); */ + FIXME("(%p,%p): stub!\n",iface,pVolPan); + return DSERR_UNSUPPORTED; +} + +static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos) +{ + /* ICOM_THIS(IDsDriverImpl,iface); */ + TRACE("(%p,%ld): stub\n",iface,dwNewPos); + return DSERR_UNSUPPORTED; +} + +static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface, + LPDWORD lpdwPlay, LPDWORD lpdwWrite) +{ + ICOM_THIS(IDsDriverBufferImpl,iface); +// count_info info; + DWORD ptr; + + TRACE("(%p)\n",iface); + if (WOutDev[This->drv->wDevID].unixdev == -1) { + ERR("device not open, but accessing?\n"); + return DSERR_UNINITIALIZED; + } + /*Libaudioio doesn't support this (Yet anyway)*/ + return DSERR_UNSUPPORTED; + +} + +static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags) +{ + ICOM_THIS(IDsDriverBufferImpl,iface); +#if 0 + int enable = PCM_ENABLE_OUTPUT; + TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags); + if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { + ERR("ioctl failed (%d)\n", errno); + return DSERR_GENERIC; + } +#endif + return DS_OK; +} + +static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface) +{ + ICOM_THIS(IDsDriverBufferImpl,iface); + int enable = 0; +#if 0 + TRACE("(%p)\n",iface); + /* no more playing */ + if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { + ERR("ioctl failed (%d)\n", errno); + return DSERR_GENERIC; + } +#endif +#if 0 + /* the play position must be reset to the beginning of the buffer */ + if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_RESET, 0) < 0) { + ERR("ioctl failed (%d)\n", errno); + return DSERR_GENERIC; + } +#endif + /* Most OSS drivers just can't stop the playback without closing the device... + * so we need to somehow signal to our DirectSound implementation + * that it should completely recreate this HW buffer... + * this unexpected error code should do the trick... */ + return DSERR_BUFFERLOST; +} + +static ICOM_VTABLE(IDsDriverBuffer) dsdbvt = +{ + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IDsDriverBufferImpl_QueryInterface, + IDsDriverBufferImpl_AddRef, + IDsDriverBufferImpl_Release, + IDsDriverBufferImpl_Lock, + IDsDriverBufferImpl_Unlock, + IDsDriverBufferImpl_SetFormat, + IDsDriverBufferImpl_SetFrequency, + IDsDriverBufferImpl_SetVolumePan, + IDsDriverBufferImpl_SetPosition, + IDsDriverBufferImpl_GetPosition, + IDsDriverBufferImpl_Play, + IDsDriverBufferImpl_Stop +}; + +static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj) +{ + /* ICOM_THIS(IDsDriverImpl,iface); */ + FIXME("(%p): stub!\n",iface); + return DSERR_UNSUPPORTED; +} + +static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface) +{ + ICOM_THIS(IDsDriverImpl,iface); + This->ref++; + return This->ref; +} + +static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface) +{ + ICOM_THIS(IDsDriverImpl,iface); + if (--This->ref) + return This->ref; + HeapFree(GetProcessHeap(),0,This); + return 0; +} + +static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc) +{ + ICOM_THIS(IDsDriverImpl,iface); + TRACE("(%p,%p)\n",iface,pDesc); + pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT | + DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK; + strcpy(pDesc->szDesc,"WineOSS DirectSound Driver"); + strcpy(pDesc->szDrvName,"wineoss.drv"); + pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode; + pDesc->wVxdId = 0; + pDesc->wReserved = 0; + pDesc->ulDeviceNum = This->wDevID; + pDesc->dwHeapType = DSDHEAP_NOHEAP; + pDesc->pvDirectDrawHeap = NULL; + pDesc->dwMemStartAddress = 0; + pDesc->dwMemEndAddress = 0; + pDesc->dwMemAllocExtra = 0; + pDesc->pvReserved1 = NULL; + pDesc->pvReserved2 = NULL; + return DS_OK; +} + +static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface) +{ + ICOM_THIS(IDsDriverImpl,iface); + int enable = 0; + + TRACE("(%p)\n",iface); + /* make sure the card doesn't start playing before we want it to */ +#if 0 + if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { + ERR("ioctl failed (%d)\n", errno); + return DSERR_GENERIC; + } +#endif + return DS_OK; +} + +static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface) +{ + ICOM_THIS(IDsDriverImpl,iface); + TRACE("(%p)\n",iface); + if (This->primary) { + ERR("problem with DirectSound: primary not released\n"); + return DSERR_GENERIC; + } + return DS_OK; +} + +static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps) +{ + /* ICOM_THIS(IDsDriverImpl,iface); */ + TRACE("(%p,%p)\n",iface,pCaps); + memset(pCaps, 0, sizeof(*pCaps)); + /* FIXME: need to check actual capabilities */ + pCaps->dwFlags = DSCAPS_PRIMARYMONO | DSCAPS_PRIMARYSTEREO | + DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARY16BIT; + pCaps->dwPrimaryBuffers = 1; + /* the other fields only apply to secondary buffers, which we don't support + * (unless we want to mess with wavetable synthesizers and MIDI) */ + return DS_OK; +} + +static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface, + LPWAVEFORMATEX pwfx, + DWORD dwFlags, DWORD dwCardAddress, + LPDWORD pdwcbBufferSize, + LPBYTE *ppbBuffer, + LPVOID *ppvObj) +{ + ICOM_THIS(IDsDriverImpl,iface); + IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj; + HRESULT err; +// audio_buf_info info; + int enable = 0; + + TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress); + /* we only support primary buffers */ + if (!(dwFlags & DSBCAPS_PRIMARYBUFFER)) + return DSERR_UNSUPPORTED; + if (This->primary) + return DSERR_ALLOCATED; + if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN)) + return DSERR_CONTROLUNAVAIL; + + *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl)); + if (*ippdsdb == NULL) + return DSERR_OUTOFMEMORY; + ICOM_VTBL(*ippdsdb) = &dsdbvt; + (*ippdsdb)->ref = 1; + (*ippdsdb)->drv = This; + + /* check how big the DMA buffer is now */ +#if 0 + if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) { + ERR("ioctl failed (%d)\n", errno); + HeapFree(GetProcessHeap(),0,*ippdsdb); + *ippdsdb = NULL; + return DSERR_GENERIC; + } +#endif + WOutDev[This->wDevID].maplen =64*1024; // Map 64 K at a time + +#if 0 + (*ippdsdb)->buflen = info.fragstotal * info.fragsize; +#endif + /* map the DMA buffer */ + err = DSDB_MapPrimary(*ippdsdb); + if (err != DS_OK) { + HeapFree(GetProcessHeap(),0,*ippdsdb); + *ippdsdb = NULL; + return err; + } + + /* primary buffer is ready to go */ + *pdwcbBufferSize = WOutDev[This->wDevID].maplen; + *ppbBuffer = WOutDev[This->wDevID].mapping; + + /* some drivers need some extra nudging after mapping */ +#if 0 + if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { + ERR("ioctl failed (%d)\n", errno); + return DSERR_GENERIC; + } +#endif + + This->primary = *ippdsdb; + + return DS_OK; +} + +static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface, + PIDSDRIVERBUFFER pBuffer, + LPVOID *ppvObj) +{ + /* ICOM_THIS(IDsDriverImpl,iface); */ + TRACE("(%p,%p): stub\n",iface,pBuffer); + return DSERR_INVALIDCALL; +} + +static ICOM_VTABLE(IDsDriver) dsdvt = +{ + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IDsDriverImpl_QueryInterface, + IDsDriverImpl_AddRef, + IDsDriverImpl_Release, + IDsDriverImpl_GetDriverDesc, + IDsDriverImpl_Open, + IDsDriverImpl_Close, + IDsDriverImpl_GetCaps, + IDsDriverImpl_CreateSoundBuffer, + IDsDriverImpl_DuplicateSoundBuffer +}; + +static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv) +{ + IDsDriverImpl** idrv = (IDsDriverImpl**)drv; + + /* the HAL isn't much better than the HEL if we can't do mmap() */ + if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) { + ERR("DirectSound flag not set\n"); + MESSAGE("This sound card's driver does not support direct access\n"); + MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n"); + return MMSYSERR_NOTSUPPORTED; + } + + *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl)); + if (!*idrv) + return MMSYSERR_NOMEM; + ICOM_VTBL(*idrv) = &dsdvt; + (*idrv)->ref = 1; + + (*idrv)->wDevID = wDevID; + (*idrv)->primary = NULL; + return MMSYSERR_NOERROR; +} + +/*======================================================================* + * Low level WAVE IN implementation * + *======================================================================*/ + +/************************************************************************** + * widGetDevCaps [internal] + */ +static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize) +{ + TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize); + + if (lpCaps == NULL) return MMSYSERR_NOTENABLED; + + if (wDevID >= MAX_WAVEINDRV) { + TRACE("MAX_WAVINDRV reached !\n"); + return MMSYSERR_BADDEVICEID; + } + + memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps))); + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widRecorder [internal] + */ +static DWORD CALLBACK widRecorder(LPVOID pmt) +{ + WORD uDevID = (DWORD)pmt; + WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID]; + WAVEHDR* lpWaveHdr; + DWORD dwSleepTime; + MSG msg; + DWORD bytesRead; + + + int fragments; + int fragsize; + int fragstotal; + int bytes; + + + int xs; + + LPVOID buffer = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + wwi->dwFragmentSize); + + LPVOID pOffset = buffer; + + PeekMessageA(&msg, 0, 0, 0, 0); + wwi->state = WINE_WS_STOPPED; + wwi->dwTotalRecorded = 0; + + SetEvent(wwi->hEvent); + + + /* make sleep time to be # of ms to output a fragment */ + dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec; + TRACE("sleeptime=%ld ms\n", dwSleepTime); + + for (; ; ) { + /* wait for dwSleepTime or an event in thread's queue */ + /* FIXME: could improve wait time depending on queue state, + * ie, number of queued fragments + */ + + if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING) + { + lpWaveHdr = wwi->lpQueuePtr; + + bytes=fragsize=AudioIORecordingAvailable(); + fragments=fragstotal=1; + + TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", fragments, fragsize, fragstotal, bytes); + + + /* read all the fragments accumulated so far */ + while ((fragments > 0) && (wwi->lpQueuePtr)) + { + fragments --; + + if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwFragmentSize) + { + /* directly read fragment in wavehdr */ + bytesRead = AudioIORead( + lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, + wwi->dwFragmentSize); + + TRACE("bytesRead=%ld (direct)\n", bytesRead); + if (bytesRead != (DWORD) -1) + { + /* update number of bytes recorded in current buffer and by this device */ + lpWaveHdr->dwBytesRecorded += bytesRead; + wwi->dwTotalRecorded += bytesRead; + + /* buffer is full. notify client */ + if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) + { + /* must copy the value of next waveHdr, because we have no idea of what + * will be done with the content of lpWaveHdr in callback + */ + LPWAVEHDR lpNext = lpWaveHdr->lpNext; + + lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; + lpWaveHdr->dwFlags |= WHDR_DONE; + + if (LIBAUDIOIO_NotifyClient(uDevID, WIM_DATA, + (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) + { + WARN("can't notify client !\n"); + } + lpWaveHdr = wwi->lpQueuePtr = lpNext; + } + } + } + else + { + /* read the fragment in a local buffer */ + bytesRead = AudioIORead( buffer, wwi->dwFragmentSize); + pOffset = buffer; + + TRACE("bytesRead=%ld (local)\n", bytesRead); + + /* copy data in client buffers */ + while (bytesRead != (DWORD) -1 && bytesRead > 0) + { + DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded); + + memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, + pOffset, + dwToCopy); + + /* update number of bytes recorded in current buffer and by this device */ + lpWaveHdr->dwBytesRecorded += dwToCopy; + wwi->dwTotalRecorded += dwToCopy; + bytesRead -= dwToCopy; + pOffset += dwToCopy; + + /* client buffer is full. notify client */ + if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) + { + /* must copy the value of next waveHdr, because we have no idea of what + * will be done with the content of lpWaveHdr in callback + */ + LPWAVEHDR lpNext = lpWaveHdr->lpNext; + TRACE("lpNext=%p\n", lpNext); + + lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; + lpWaveHdr->dwFlags |= WHDR_DONE; + + if (LIBAUDIOIO_NotifyClient(uDevID, WIM_DATA, + (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) + { + WARN("can't notify client !\n"); + } + + wwi->lpQueuePtr = lpWaveHdr = lpNext; + if (!lpNext && bytesRead) { + /* no more buffer to copy data to, but we did read more. + * what hasn't been copied will be dropped + */ + WARN("buffer under run! %lu bytes dropped.\n", bytesRead); + wwi->lpQueuePtr = NULL; + break; + } + } + } + } + } + } + + MsgWaitForMultipleObjects(0, NULL, FALSE, dwSleepTime, QS_POSTMESSAGE); + + while (PeekMessageA(&msg, 0, WINE_WM_FIRST, WINE_WM_LAST, PM_REMOVE)) { + + TRACE("msg=0x%x wParam=0x%x lParam=0x%lx\n", msg.message, msg.wParam, msg.lParam); + switch (msg.message) { + case WINE_WM_PAUSING: + wwi->state = WINE_WS_PAUSED; + + AudioIORecordingPause(); + SetEvent(wwi->hEvent); + break; + case WINE_WM_RESTARTING: + { + + wwi->state = WINE_WS_PLAYING; + + if (wwi->bTriggerSupport) + { + /* start the recording */ + AudioIORecordingResume(); + } + else + { + unsigned char data[4]; + /* read 4 bytes to start the recording */ + AudioIORead( data, 4); + } + + SetEvent(wwi->hEvent); + break; + } + case WINE_WM_HEADER: + lpWaveHdr = (LPWAVEHDR)msg.lParam; + lpWaveHdr->lpNext = 0; + + /* insert buffer at the end of queue */ + { + LPWAVEHDR* wh; + for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); + *wh = lpWaveHdr; + } + break; + case WINE_WM_RESETTING: + wwi->state = WINE_WS_STOPPED; + /* return all buffers to the app */ + for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) { + TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext); + lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; + lpWaveHdr->dwFlags |= WHDR_DONE; + + if (LIBAUDIOIO_NotifyClient(uDevID, WIM_DATA, + (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) { + WARN("can't notify client !\n"); + } + } + wwi->lpQueuePtr = NULL; + SetEvent(wwi->hEvent); + break; + case WINE_WM_CLOSING: + wwi->hThread = 0; + wwi->state = WINE_WS_CLOSED; + SetEvent(wwi->hEvent); + HeapFree(GetProcessHeap(), 0, buffer); + ExitThread(0); + /* shouldn't go here */ + default: + FIXME("unknown message %d\n", msg.message); + break; + } + } + } + ExitThread(0); + /* just for not generating compilation warnings... should never be executed */ + return 0; +} + + +/************************************************************************** + * widOpen [internal] + */ +static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) +{ + int audio; + int fragment_size; + int sample_rate; + int format; + int dsp_stereo; + WINE_WAVEIN* wwi; + int audio_fragment; + + TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags); + if (lpDesc == NULL) { + WARN("Invalid Parameter !\n"); + return MMSYSERR_INVALPARAM; + } + if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_BADDEVICEID; + + /* only PCM format is supported so far... */ + if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM || + lpDesc->lpFormat->nChannels == 0 || + lpDesc->lpFormat->nSamplesPerSec == 0) { + WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", + lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, + lpDesc->lpFormat->nSamplesPerSec); + return WAVERR_BADFORMAT; + } + + if (dwFlags & WAVE_FORMAT_QUERY) { + TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", + lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, + lpDesc->lpFormat->nSamplesPerSec); + return MMSYSERR_NOERROR; + } + + if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED; + audio = AudioIOOpenX( O_RDONLY|O_NDELAY, &spec[RECORD],&spec[RECORD]); + if (audio == -1) { + WARN("can't open sound device %s (%s)!\n", SOUND_DEV, strerror(errno)); + return MMSYSERR_ALLOCATED; + } + fcntl(audio, F_SETFD, 1); /* set close on exec flag */ + + wwi = &WInDev[wDevID]; + if (wwi->lpQueuePtr) { + WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr); + wwi->lpQueuePtr = NULL; + } + wwi->unixdev = audio; + wwi->dwTotalRecorded = 0; + wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); + + memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC)); + memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT)); + + if (wwi->format.wBitsPerSample == 0) { + WARN("Resetting zeroed wBitsPerSample\n"); + wwi->format.wBitsPerSample = 8 * + (wwi->format.wf.nAvgBytesPerSec / + wwi->format.wf.nSamplesPerSec) / + wwi->format.wf.nChannels; + } + + spec[RECORD].rate=sample_rate = wwi->format.wf.nSamplesPerSec; + dsp_stereo = ((spec[RECORD].channels=wwi->format.wf.nChannels) > 1) ? TRUE : FALSE; + spec[RECORD].precision= wwi->format.wBitsPerSample; + spec[RECORD].type=(spec[RECORD].precision==16)?TYPE_SIGNED:TYPE_UNSIGNED; + + /* This is actually hand tuned to work so that my SB Live: + * - does not skip + * - does not buffer too much + * when sending with the Shoutcast winamp plugin + */ + /* 7 fragments max, 2^10 = 1024 bytes per fragment */ + audio_fragment = 0x0007000A; + fragment_size=4096; + if (fragment_size == -1) { + AudioIOClose(); + wwi->unixdev = -1; + return MMSYSERR_NOTENABLED; + } + wwi->dwFragmentSize = fragment_size; + + TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n", + wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec, + wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels, + wwi->format.wf.nBlockAlign); + + wwi->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID)); + WaitForSingleObject(wwi->hEvent, INFINITE); + + if (LIBAUDIOIO_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { + WARN("can't notify client !\n"); + return MMSYSERR_INVALPARAM; + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widClose [internal] + */ +static DWORD widClose(WORD wDevID) +{ + WINE_WAVEIN* wwi; + + TRACE("(%u);\n", wDevID); + if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) { + WARN("can't close !\n"); + return MMSYSERR_INVALHANDLE; + } + + wwi = &WInDev[wDevID]; + + if (wwi->lpQueuePtr != NULL) { + WARN("still buffers open !\n"); + return WAVERR_STILLPLAYING; + } + + PostThreadMessageA(wwi->dwThreadID, WINE_WM_CLOSING, 0, 0); + WaitForSingleObject(wwi->hEvent, INFINITE); + CloseHandle(wwi->hEvent); + AudioIOClose(); + wwi->unixdev = -1; + wwi->dwFragmentSize = 0; + if (LIBAUDIOIO_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { + WARN("can't notify client !\n"); + return MMSYSERR_INVALPARAM; + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widAddBuffer [internal] + */ +static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); + + if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) { + WARN("can't do it !\n"); + return MMSYSERR_INVALHANDLE; + } + if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) { + TRACE("never been prepared !\n"); + return WAVERR_UNPREPARED; + } + if (lpWaveHdr->dwFlags & WHDR_INQUEUE) { + TRACE("header already in use !\n"); + return WAVERR_STILLPLAYING; + } + + lpWaveHdr->dwFlags |= WHDR_INQUEUE; + lpWaveHdr->dwFlags &= ~WHDR_DONE; + lpWaveHdr->dwBytesRecorded = 0; + lpWaveHdr->lpNext = NULL; + + PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr); + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widPrepare [internal] + */ +static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); + + if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE; + + if (lpWaveHdr->dwFlags & WHDR_INQUEUE) + return WAVERR_STILLPLAYING; + + lpWaveHdr->dwFlags |= WHDR_PREPARED; + lpWaveHdr->dwFlags &= ~(WHDR_INQUEUE|WHDR_DONE); + lpWaveHdr->dwBytesRecorded = 0; + TRACE("header prepared !\n"); + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widUnprepare [internal] + */ +static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); + if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE; + + if (lpWaveHdr->dwFlags & WHDR_INQUEUE) + return WAVERR_STILLPLAYING; + + lpWaveHdr->dwFlags &= ~(WHDR_PREPARED|WHDR_INQUEUE); + lpWaveHdr->dwFlags |= WHDR_DONE; + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widStart [internal] + */ +static DWORD widStart(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) { + WARN("can't start recording !\n"); + return MMSYSERR_INVALHANDLE; + } + + PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0); + WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE); + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widStop [internal] + */ +static DWORD widStop(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) { + WARN("can't stop !\n"); + return MMSYSERR_INVALHANDLE; + } + /* FIXME: reset aint stop */ + PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0); + WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widReset [internal] + */ +static DWORD widReset(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) { + WARN("can't reset !\n"); + return MMSYSERR_INVALHANDLE; + } + PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0); + WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE); + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widGetPosition [internal] + */ +static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize) +{ + int time; + WINE_WAVEIN* wwi; + + TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize); + + if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) { + WARN("can't get pos !\n"); + return MMSYSERR_INVALHANDLE; + } + if (lpTime == NULL) return MMSYSERR_INVALPARAM; + + wwi = &WInDev[wDevID]; + + TRACE("wType=%04X !\n", lpTime->wType); + TRACE("wBitsPerSample=%u\n", wwi->format.wBitsPerSample); + TRACE("nSamplesPerSec=%lu\n", wwi->format.wf.nSamplesPerSec); + TRACE("nChannels=%u\n", wwi->format.wf.nChannels); + TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec); + switch (lpTime->wType) { + case TIME_BYTES: + lpTime->u.cb = wwi->dwTotalRecorded; + TRACE("TIME_BYTES=%lu\n", lpTime->u.cb); + break; + case TIME_SAMPLES: + lpTime->u.sample = wwi->dwTotalRecorded * 8 / + wwi->format.wBitsPerSample; + TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample); + break; + case TIME_SMPTE: + time = wwi->dwTotalRecorded / + (wwi->format.wf.nAvgBytesPerSec / 1000); + lpTime->u.smpte.hour = time / 108000; + time -= lpTime->u.smpte.hour * 108000; + lpTime->u.smpte.min = time / 1800; + time -= lpTime->u.smpte.min * 1800; + lpTime->u.smpte.sec = time / 30; + time -= lpTime->u.smpte.sec * 30; + lpTime->u.smpte.frame = time; + lpTime->u.smpte.fps = 30; + TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n", + lpTime->u.smpte.hour, lpTime->u.smpte.min, + lpTime->u.smpte.sec, lpTime->u.smpte.frame); + break; + case TIME_MS: + lpTime->u.ms = wwi->dwTotalRecorded / + (wwi->format.wf.nAvgBytesPerSec / 1000); + TRACE("TIME_MS=%lu\n", lpTime->u.ms); + break; + default: + FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType); + lpTime->wType = TIME_MS; + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * widMessage (WINEOSS.6) + */ +DWORD WINAPI LIBAUDIOIO_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, + DWORD dwParam1, DWORD dwParam2) +{ + TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n", + wDevID, wMsg, dwUser, dwParam1, dwParam2); + + switch (wMsg) { + case DRVM_INIT: + case DRVM_EXIT: + case DRVM_ENABLE: + case DRVM_DISABLE: + /* FIXME: Pretend this is supported */ + return 0; + case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2); + case WIDM_CLOSE: return widClose (wDevID); + case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2); + case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); + case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); + case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2); + case WIDM_GETNUMDEVS: return wodGetNumDevs (); /* same number of devices in output as in input */ + case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2); + case WIDM_RESET: return widReset (wDevID); + case WIDM_START: return widStart (wDevID); + case WIDM_STOP: return widStop (wDevID); + default: + FIXME("unknown message %u!\n", wMsg); + } + return MMSYSERR_NOTSUPPORTED; +} + +#else /* HAVE_LIBAUDIOIO */ + +/************************************************************************** + * wodMessage (WINEOSS.7) + */ +DWORD WINAPI LIBAUDIOIO_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, + DWORD dwParam1, DWORD dwParam2) +{ + FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); + return MMSYSERR_NOTENABLED; +} + +/************************************************************************** + * widMessage (WINEOSS.6) + */ +DWORD WINAPI LIBAUDIOIO_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, + DWORD dwParam1, DWORD dwParam2) +{ + FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); + return MMSYSERR_NOTENABLED; +} + +#endif /* HAVE_LIBAUDIOIO */ diff --git a/dlls/winmm/wineaudioio/audioio.c b/dlls/winmm/wineaudioio/audioio.c new file mode 100644 index 00000000000..b52bb75487e --- /dev/null +++ b/dlls/winmm/wineaudioio/audioio.c @@ -0,0 +1,95 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ +/* + * Wine Driver for Libaudioio + * Derived From WineOSS + * Copyright 1999 Eric Pouech + * Modifications by Robert Lunnon 2002 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "mmddk.h" + +#ifdef HAVE_LIBAUDIOIO + +static struct WINE_LIBAUDIOIO* audioio = NULL; + +extern LONG LIBAUDIOIO_WaveInit(void); +extern BOOL LIBAUDIOIO_MidiInit(void); + +/************************************************************************** + * LIBAUDIOIO_drvOpen [internal] + */ +static DWORD LIBAUDIOIO_drvOpen(LPSTR str) +{ + if (audioio) + return 0; + + /* I know, this is ugly, but who cares... */ + audioio = (struct WINE_LIBAUDIOIO*)1; + return 1; +} + +/************************************************************************** + * LIBAUDIOIO_drvClose [internal] + */ +static DWORD LIBAUDIOIO_drvClose(DWORD dwDevID) +{ + if (audioio) { + audioio = NULL; + return 1; + } + return 0; +} + +#endif + + +/************************************************************************** + * DriverProc + */ +LONG CALLBACK LIBAUDIOIO_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, + DWORD dwParam1, DWORD dwParam2) +{ +/* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */ +/* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */ + + switch(wMsg) { +#ifdef HAVE_LIBAUDIOIO + case DRV_LOAD: LIBAUDIOIO_WaveInit(); +#ifdef HAVE_LIBAUDIOIO_MIDI + LIBAUDIOIO_MidiInit(); +#endif + return 1; + case DRV_FREE: return 1; + case DRV_OPEN: return LIBAUDIOIO_drvOpen((LPSTR)dwParam1); + case DRV_CLOSE: return LIBAUDIOIO_drvClose(dwDevID); + case DRV_ENABLE: return 1; + case DRV_DISABLE: return 1; + case DRV_QUERYCONFIGURE: return 1; + case DRV_CONFIGURE: MessageBoxA(0, "Libaudioio MultiMedia Driver !", "Libaudioio Driver", MB_OK); return 1; + case DRV_INSTALL: return DRVCNF_RESTART; + case DRV_REMOVE: return DRVCNF_RESTART; +#endif + default: + return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); + } +} diff --git a/dlls/winmm/wineaudioio/wineaudioio.drv.spec b/dlls/winmm/wineaudioio/wineaudioio.drv.spec new file mode 100644 index 00000000000..c28ca6cf73d --- /dev/null +++ b/dlls/winmm/wineaudioio/wineaudioio.drv.spec @@ -0,0 +1,2 @@ +@ stdcall DriverProc(long long long long long) LIBAUDIOIO_DriverProc +@ stdcall wodMessage(long long long long long) LIBAUDIOIO_wodMessage diff --git a/include/config.h.in b/include/config.h.in index 28d7edd1c9d..4a4f258b6c5 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -191,6 +191,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_JPEGLIB_H +/* Define if you have libaudioIO */ +#undef HAVE_LIBAUDIOIO + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBAUDIOIO_H + /* Define if you have the curses library (-lcurses) */ #undef HAVE_LIBCURSES