diff --git a/configure b/configure index 81754baeff6..fda555cd26d 100755 --- a/configure +++ b/configure @@ -746,6 +746,7 @@ LDEXERPATH LDDLLRPATH COREFOUNDATIONLIB IOKITLIB +COREAUDIO CROSSTEST CROSSCC CROSSWINDRES @@ -7768,10 +7769,14 @@ done + + for ac_header in \ + AudioUnit/AudioUnit.h \ + CoreAudio/CoreAudio.h \ IOKit/IOKitLib.h \ alsa/asoundlib.h \ arpa/inet.h \ @@ -15074,6 +15079,11 @@ echo "${ECHO_T}$ac_cv_c_dll_macho" >&6; } LDEXECFLAGS="-image_base 0x7bf00000 -Wl,-segaddr,WINE_DOS,0x00000000,-segaddr,WINE_SHARED_HEAP,0x7ffe0000" + if test "$ac_cv_header_CoreAudio_CoreAudio_h" = "yes" -a "$ac_cv_header_AudioUnit_AudioUnit_h" = "yes" + then + COREAUDIO="-framework CoreAudio -framework AudioUnit -framework CoreServices" + + fi case $host_cpu in *powerpc*) CFLAGS="$CFLAGS -ffixed-r13" @@ -21085,7 +21095,7 @@ MAKE_LIB_RULES=libs/Makelib.rules MAKE_PROG_RULES=programs/Makeprog.rules -ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Makeimplib.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/activeds/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/advpack/Makefile dlls/advpack/tests/Makefile dlls/amstream/Makefile dlls/atl/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/cabinet/tests/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/comdlg32/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/crypt32/tests/Makefile dlls/cryptdll/Makefile dlls/ctl3d32/Makefile dlls/d3d8/Makefile dlls/d3d8/tests/Makefile dlls/d3d9/Makefile dlls/d3d9/tests/Makefile dlls/d3dim/Makefile dlls/d3drm/Makefile dlls/d3dx8/Makefile dlls/d3dxof/Makefile dlls/dbghelp/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput/tests/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dnsapi/Makefile dlls/dnsapi/tests/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnet/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxdiagn/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/gphoto2.ds/Makefile dlls/hhctrl.ocx/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imaadp32.acm/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/itss/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lz32/Makefile dlls/lz32/tests/Makefile dlls/mapi32/Makefile dlls/mapi32/tests/Makefile dlls/mciavi32/Makefile dlls/mcicda/Makefile dlls/mciseq/Makefile dlls/mciwave/Makefile dlls/midimap/Makefile dlls/mlang/Makefile dlls/mlang/tests/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/mprapi/Makefile dlls/msacm32.drv/Makefile dlls/msacm32/Makefile dlls/msacm32/tests/Makefile dlls/msadp32.acm/Makefile dlls/mscms/Makefile dlls/mscms/tests/Makefile dlls/msdmo/Makefile dlls/msftedit/Makefile dlls/msg711.acm/Makefile dlls/mshtml/Makefile dlls/mshtml/tests/Makefile dlls/msi/Makefile dlls/msi/tests/Makefile dlls/msimg32/Makefile dlls/msisys.ocx/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvcrtd/tests/Makefile dlls/msvfw32/Makefile dlls/msvidc32/Makefile dlls/mswsock/Makefile dlls/msxml3/Makefile dlls/msxml3/tests/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/ntdsapi/Makefile dlls/objsel/Makefile dlls/odbc32/Makefile dlls/odbccp32/Makefile dlls/ole32/Makefile dlls/ole32/tests/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli32/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr32/Makefile dlls/opengl32/Makefile dlls/powrprof/Makefile dlls/psapi/Makefile dlls/psapi/tests/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/quartz/tests/Makefile dlls/rasapi32/Makefile dlls/riched20/Makefile dlls/riched20/tests/Makefile dlls/riched32/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/rsabase/tests/Makefile dlls/rsaenh/Makefile dlls/rsaenh/tests/Makefile dlls/sane.ds/Makefile dlls/secur32/Makefile dlls/secur32/tests/Makefile dlls/security/Makefile dlls/sensapi/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/setupapi/tests/Makefile dlls/sfc/Makefile dlls/shdocvw/Makefile dlls/shdocvw/tests/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/spoolss/Makefile dlls/stdole2.tlb/Makefile dlls/stdole32.tlb/Makefile dlls/sti/Makefile dlls/strmiids/Makefile dlls/tapi32/Makefile dlls/twain_32/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/usp10/Makefile dlls/usp10/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/vdmdbg/Makefile dlls/version/Makefile dlls/version/tests/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/w32skrnl/Makefile dlls/winecrt0/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/winemp3.acm/Makefile dlls/wineps.drv/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/tests/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/wineesd/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls32/Makefile dlls/winspool.drv/Makefile dlls/winspool.drv/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wldap32/Makefile dlls/wnaspi32/Makefile dlls/wow32/Makefile dlls/ws2_32/Makefile dlls/ws2_32/tests/Makefile dlls/wsock32/Makefile dlls/wtsapi32/Makefile dlls/x11drv/Makefile documentation/Makefile fonts/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/eject/Makefile programs/expand/Makefile programs/explorer/Makefile programs/hh/Makefile programs/icinfo/Makefile programs/iexplore/Makefile programs/msiexec/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile programs/wordpad/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile" +ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Makeimplib.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/activeds/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/advpack/Makefile dlls/advpack/tests/Makefile dlls/amstream/Makefile dlls/atl/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/cabinet/tests/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/comdlg32/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/crypt32/tests/Makefile dlls/cryptdll/Makefile dlls/ctl3d32/Makefile dlls/d3d8/Makefile dlls/d3d8/tests/Makefile dlls/d3d9/Makefile dlls/d3d9/tests/Makefile dlls/d3dim/Makefile dlls/d3drm/Makefile dlls/d3dx8/Makefile dlls/d3dxof/Makefile dlls/dbghelp/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput/tests/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dnsapi/Makefile dlls/dnsapi/tests/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnet/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxdiagn/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/gphoto2.ds/Makefile dlls/hhctrl.ocx/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imaadp32.acm/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/itss/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lz32/Makefile dlls/lz32/tests/Makefile dlls/mapi32/Makefile dlls/mapi32/tests/Makefile dlls/mciavi32/Makefile dlls/mcicda/Makefile dlls/mciseq/Makefile dlls/mciwave/Makefile dlls/midimap/Makefile dlls/mlang/Makefile dlls/mlang/tests/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/mprapi/Makefile dlls/msacm32.drv/Makefile dlls/msacm32/Makefile dlls/msacm32/tests/Makefile dlls/msadp32.acm/Makefile dlls/mscms/Makefile dlls/mscms/tests/Makefile dlls/msdmo/Makefile dlls/msftedit/Makefile dlls/msg711.acm/Makefile dlls/mshtml/Makefile dlls/mshtml/tests/Makefile dlls/msi/Makefile dlls/msi/tests/Makefile dlls/msimg32/Makefile dlls/msisys.ocx/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvcrtd/tests/Makefile dlls/msvfw32/Makefile dlls/msvidc32/Makefile dlls/mswsock/Makefile dlls/msxml3/Makefile dlls/msxml3/tests/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/ntdsapi/Makefile dlls/objsel/Makefile dlls/odbc32/Makefile dlls/odbccp32/Makefile dlls/ole32/Makefile dlls/ole32/tests/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli32/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr32/Makefile dlls/opengl32/Makefile dlls/powrprof/Makefile dlls/psapi/Makefile dlls/psapi/tests/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/quartz/tests/Makefile dlls/rasapi32/Makefile dlls/riched20/Makefile dlls/riched20/tests/Makefile dlls/riched32/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/rsabase/tests/Makefile dlls/rsaenh/Makefile dlls/rsaenh/tests/Makefile dlls/sane.ds/Makefile dlls/secur32/Makefile dlls/secur32/tests/Makefile dlls/security/Makefile dlls/sensapi/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/setupapi/tests/Makefile dlls/sfc/Makefile dlls/shdocvw/Makefile dlls/shdocvw/tests/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/spoolss/Makefile dlls/stdole2.tlb/Makefile dlls/stdole32.tlb/Makefile dlls/sti/Makefile dlls/strmiids/Makefile dlls/tapi32/Makefile dlls/twain_32/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/usp10/Makefile dlls/usp10/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/vdmdbg/Makefile dlls/version/Makefile dlls/version/tests/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/w32skrnl/Makefile dlls/winecrt0/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/winemp3.acm/Makefile dlls/wineps.drv/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/tests/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winecoreaudio/Makefile dlls/winmm/wineesd/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls32/Makefile dlls/winspool.drv/Makefile dlls/winspool.drv/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wldap32/Makefile dlls/wnaspi32/Makefile dlls/wow32/Makefile dlls/ws2_32/Makefile dlls/ws2_32/tests/Makefile dlls/wsock32/Makefile dlls/wtsapi32/Makefile dlls/x11drv/Makefile documentation/Makefile fonts/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/eject/Makefile programs/expand/Makefile programs/explorer/Makefile programs/hh/Makefile programs/icinfo/Makefile programs/iexplore/Makefile programs/msiexec/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile programs/wordpad/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile" cat >confcache <<\_ACEOF @@ -21860,6 +21870,7 @@ do "dlls/winmm/winealsa/Makefile") CONFIG_FILES="$CONFIG_FILES dlls/winmm/winealsa/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/winecoreaudio/Makefile") CONFIG_FILES="$CONFIG_FILES dlls/winmm/winecoreaudio/Makefile" ;; "dlls/winmm/wineesd/Makefile") CONFIG_FILES="$CONFIG_FILES dlls/winmm/wineesd/Makefile" ;; "dlls/winmm/winejack/Makefile") CONFIG_FILES="$CONFIG_FILES dlls/winmm/winejack/Makefile" ;; "dlls/winmm/winenas/Makefile") CONFIG_FILES="$CONFIG_FILES dlls/winmm/winenas/Makefile" ;; @@ -22209,6 +22220,7 @@ LDEXERPATH!$LDEXERPATH$ac_delim LDDLLRPATH!$LDDLLRPATH$ac_delim COREFOUNDATIONLIB!$COREFOUNDATIONLIB$ac_delim IOKITLIB!$IOKITLIB$ac_delim +COREAUDIO!$COREAUDIO$ac_delim CROSSTEST!$CROSSTEST$ac_delim CROSSCC!$CROSSCC$ac_delim CROSSWINDRES!$CROSSWINDRES$ac_delim @@ -22222,7 +22234,7 @@ LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `grep -c "$ac_delim\$" conf$$subs.sed` = 78; then + if test `grep -c "$ac_delim\$" conf$$subs.sed` = 79; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 diff --git a/configure.ac b/configure.ac index f9d49d9b0c5..d60099db64c 100644 --- a/configure.ac +++ b/configure.ac @@ -160,6 +160,8 @@ AC_SUBST(OPENGL_LIBS,"") dnl **** Check for header files **** AC_CHECK_HEADERS(\ + AudioUnit/AudioUnit.h \ + CoreAudio/CoreAudio.h \ IOKit/IOKitLib.h \ alsa/asoundlib.h \ arpa/inet.h \ @@ -1133,6 +1135,11 @@ case $host_os in AC_SUBST(COREFOUNDATIONLIB,"-framework CoreFoundation") AC_SUBST(IOKITLIB,"-framework IOKit -framework CoreFoundation") AC_SUBST(LDEXECFLAGS,["-image_base 0x7bf00000 -Wl,-segaddr,WINE_DOS,0x00000000,-segaddr,WINE_SHARED_HEAP,0x7ffe0000"]) + if test "$ac_cv_header_CoreAudio_CoreAudio_h" = "yes" -a "$ac_cv_header_AudioUnit_AudioUnit_h" = "yes" + then + dnl CoreServices needed by AudioUnit + AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices") + fi case $host_cpu in *powerpc*) CFLAGS="$CFLAGS -ffixed-r13" @@ -1729,6 +1736,7 @@ dlls/winmm/tests/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile +dlls/winmm/winecoreaudio/Makefile dlls/winmm/wineesd/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile diff --git a/dlls/Makefile.in b/dlls/Makefile.in index a3cda109781..3b044549f1a 100644 --- a/dlls/Makefile.in +++ b/dlls/Makefile.in @@ -170,6 +170,7 @@ BASEDIRS = \ winmm/winealsa \ winmm/winearts \ winmm/wineaudioio \ + winmm/winecoreaudio \ winmm/wineesd \ winmm/winejack \ winmm/winenas \ @@ -275,6 +276,7 @@ SYMLINKS_SO = \ winealsa.drv.so \ winearts.drv.so \ wineaudioio.drv.so \ + winecoreaudio.drv.so \ wineesd.drv.so \ winejack.drv.so \ winenas.drv.so \ @@ -319,6 +321,9 @@ winearts.drv.so: winmm/winearts/winearts.drv.so wineaudioio.drv.so: winmm/wineaudioio/wineaudioio.drv.so $(RM) $@ && $(LN_S) winmm/wineaudioio/wineaudioio.drv.so $@ +winecoreaudio.drv.so: winmm/winecoreaudio/winecoreaudio.drv.so + $(RM) $@ && $(LN_S) winmm/winecoreaudio/winecoreaudio.drv.so $@ + wineesd.drv.so: winmm/wineesd/wineesd.drv.so $(RM) $@ && $(LN_S) winmm/wineesd/wineesd.drv.so $@ @@ -919,6 +924,7 @@ user/user32.dll.so: user winmm/winealsa/winealsa.drv.so: winmm/winealsa winmm/winearts/winearts.drv.so: winmm/winearts winmm/wineaudioio/wineaudioio.drv.so: winmm/wineaudioio +winmm/winecoreaudio/winecoreaudio.drv.so: winmm/winecoreaudio winmm/wineesd/wineesd.drv.so: winmm/wineesd winmm/winejack/winejack.drv.so: winmm/winejack winmm/winenas/winenas.drv.so: winmm/winenas diff --git a/dlls/winmm/winecoreaudio/.gitignore b/dlls/winmm/winecoreaudio/.gitignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/dlls/winmm/winecoreaudio/.gitignore @@ -0,0 +1 @@ +Makefile diff --git a/dlls/winmm/winecoreaudio/Makefile.in b/dlls/winmm/winecoreaudio/Makefile.in new file mode 100644 index 00000000000..591457db55a --- /dev/null +++ b/dlls/winmm/winecoreaudio/Makefile.in @@ -0,0 +1,16 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = winecoreaudio.drv +IMPORTS = winmm user32 kernel32 +EXTRALIBS = $(LIBUUID) @COREAUDIO@ + +C_SRCS = \ + audio.c \ + audiounit.c \ + coreaudio.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/dlls/winmm/winecoreaudio/audio.c b/dlls/winmm/winecoreaudio/audio.c new file mode 100644 index 00000000000..52c880bd2d5 --- /dev/null +++ b/dlls/winmm/winecoreaudio/audio.c @@ -0,0 +1,1433 @@ +/* + * Wine Driver for CoreAudio based on Jack Driver + * + * Copyright 1994 Martin Ayotte + * Copyright 1999 Eric Pouech (async playing in waveOut/waveIn) + * Copyright 2000 Eric Pouech (loops in waveOut) + * Copyright 2002 Chris Morgan (jack version of this file) + * Copyright 2005, 2006 Emmanuel Maillard + * + * 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 +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "wingdi.h" +#include "winerror.h" +#include "mmddk.h" +#include "dsound.h" +#include "dsdriver.h" +#include "coreaudio.h" +#include "wine/unicode.h" +#include "wine/library.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wave); + + +#if defined(HAVE_COREAUDIO_COREAUDIO_H) && defined(HAVE_AUDIOUNIT_AUDIOUNIT_H) +#include +#include + +/* + Due to AudioUnit headers conflict define some needed types. +*/ + +typedef void *AudioUnit; + +/* From AudioUnit/AUComponents.h */ +typedef UInt32 AudioUnitRenderActionFlags; + +/* only allow 10 output devices through this driver, this ought to be adequate */ +#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 + +typedef struct tagCoreAudio_Device { + char dev_name[32]; + char mixer_name[32]; + unsigned open_count; + char* interface_name; + + WAVEOUTCAPSA out_caps; + WAVEINCAPSA in_caps; + DWORD in_caps_support; + int sample_rate; + int stereo; + int format; + unsigned audio_fragment; + BOOL full_duplex; + BOOL bTriggerSupport; + BOOL bOutputEnabled; + BOOL bInputEnabled; + DSDRIVERDESC ds_desc; + DSDRIVERCAPS ds_caps; + DSCDRIVERCAPS dsc_caps; + GUID ds_guid; + GUID dsc_guid; + + AudioDeviceID outputDeviceID; + AudioDeviceID inputDeviceID; + AudioStreamBasicDescription streamDescription; +} CoreAudio_Device; + +/* for now use the default device */ +static CoreAudio_Device CoreAudio_DefaultDevice; + +typedef struct { + volatile int state; /* one of the WINE_WS_ manifest constants */ + CoreAudio_Device *cadev; + WAVEOPENDESC waveDesc; + WORD wFlags; + PCMWAVEFORMAT format; + DWORD woID; + AudioUnit audioUnit; + AudioStreamBasicDescription streamDescription; + + WAVEOUTCAPSW caps; + char interface_name[32]; + LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */ + LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */ + DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */ + + LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */ + DWORD dwLoops; /* private copy of loop counter */ + + DWORD dwPlayedTotal; /* number of bytes actually played since opening */ + DWORD dwWrittenTotal; /* number of bytes written to OSS buffer since opening */ + + DWORD tickCountMS; /* time in MS of last AudioUnit callback */ + + pthread_mutex_t lock; /* synchronization stuff */ +} WINE_WAVEOUT; + +typedef struct { + volatile int state; + CoreAudio_Device *cadev; + WAVEOPENDESC waveDesc; + WORD wFlags; + PCMWAVEFORMAT format; + LPWAVEHDR lpQueuePtr; + DWORD dwTotalRecorded; + WAVEINCAPSW caps; + + AudioUnit audioUnit; + AudioStreamBasicDescription streamDescription; + + /* BOOL bTriggerSupport; + WORD wDevID; + char interface_name[32];*/ + + /* synchronization stuff */ + pthread_mutex_t lock; +} WINE_WAVEIN; + +static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV]; +static WINE_WAVEIN WInDev [MAX_WAVEINDRV ]; + +static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv); +static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc); + +static LPWAVEHDR wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo); +static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force); + +extern int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au); +extern int AudioUnit_CloseAudioUnit(AudioUnit au); +extern int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription streamFormat); + +extern OSStatus AudioOutputUnitStart(AudioUnit au); +extern OSStatus AudioOutputUnitStop(AudioUnit au); +extern OSStatus AudioUnitUninitialize(AudioUnit au); + +extern int AudioUnit_SetVolume(AudioUnit au, float left, float right); +extern int AudioUnit_GetVolume(AudioUnit au, float *left, float *right); + +OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); + +/* These strings used only for tracing */ + +static const char * getMessage(UINT msg) +{ + static char unknown[32]; +#define MSG_TO_STR(x) case x: return #x + switch(msg) { + MSG_TO_STR(DRVM_INIT); + MSG_TO_STR(DRVM_EXIT); + MSG_TO_STR(DRVM_ENABLE); + MSG_TO_STR(DRVM_DISABLE); + MSG_TO_STR(WIDM_OPEN); + MSG_TO_STR(WIDM_CLOSE); + MSG_TO_STR(WIDM_ADDBUFFER); + MSG_TO_STR(WIDM_PREPARE); + MSG_TO_STR(WIDM_UNPREPARE); + MSG_TO_STR(WIDM_GETDEVCAPS); + MSG_TO_STR(WIDM_GETNUMDEVS); + MSG_TO_STR(WIDM_GETPOS); + MSG_TO_STR(WIDM_RESET); + MSG_TO_STR(WIDM_START); + MSG_TO_STR(WIDM_STOP); + MSG_TO_STR(WODM_OPEN); + MSG_TO_STR(WODM_CLOSE); + MSG_TO_STR(WODM_WRITE); + MSG_TO_STR(WODM_PAUSE); + MSG_TO_STR(WODM_GETPOS); + MSG_TO_STR(WODM_BREAKLOOP); + MSG_TO_STR(WODM_PREPARE); + MSG_TO_STR(WODM_UNPREPARE); + MSG_TO_STR(WODM_GETDEVCAPS); + MSG_TO_STR(WODM_GETNUMDEVS); + MSG_TO_STR(WODM_GETPITCH); + MSG_TO_STR(WODM_SETPITCH); + MSG_TO_STR(WODM_GETPLAYBACKRATE); + MSG_TO_STR(WODM_SETPLAYBACKRATE); + MSG_TO_STR(WODM_GETVOLUME); + MSG_TO_STR(WODM_SETVOLUME); + MSG_TO_STR(WODM_RESTART); + MSG_TO_STR(WODM_RESET); + MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE); + MSG_TO_STR(DRV_QUERYDEVICEINTERFACE); + MSG_TO_STR(DRV_QUERYDSOUNDIFACE); + MSG_TO_STR(DRV_QUERYDSOUNDDESC); + } +#undef MSG_TO_STR + sprintf(unknown, "UNKNOWN(0x%04x)", msg); + return unknown; +} + +#define kStopLoopMessage 0 +#define kWaveOutCallbackMessage 1 +#define kWaveInCallbackMessage 2 + +/* Mach Message Handling */ +static CFDataRef wodMessageHandler(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) +{ + UInt32 *buffer = NULL; + WINE_WAVEOUT* wwo = NULL; + + switch (msgid) + { + case kWaveOutCallbackMessage: + buffer = (UInt32 *) CFDataGetBytePtr(data); + wwo = &WOutDev[buffer[0]]; + + pthread_mutex_lock(&wwo->lock); + DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, + (HDRVR)wwo->waveDesc.hWave, (WORD)buffer[1], wwo->waveDesc.dwInstance, + (DWORD)buffer[2], (DWORD)buffer[3]); + pthread_mutex_unlock(&wwo->lock); + break; + case kWaveInCallbackMessage: + default: + CFRunLoopStop(CFRunLoopGetCurrent()); + break; + } + + return NULL; +} + +static DWORD messageThread(LPVOID p) +{ + CFMessagePortRef local; + CFRunLoopSourceRef source; + Boolean info; + + local = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("WaveMessagePort"), + &wodMessageHandler, NULL, &info); + + source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, (CFIndex)0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + + CFRunLoopRun(); + + return 0; +} + +static DWORD wodSendDriverCallbackMessage(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2) +{ + CFDataRef data; + UInt32 *buffer; + SInt32 ret; + + CFMessagePortRef messagePort; + messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, CFSTR("WaveMessagePort")); + + buffer = CFAllocatorAllocate(NULL, sizeof(UInt32) * 4, 0); + if (!buffer) + return 0; + + buffer[0] = (UInt32) wwo->woID; + buffer[1] = (UInt32) wMsg; + buffer[2] = (UInt32) dwParam1; + buffer[3] = (UInt32) dwParam2; + + data = CFDataCreate(kCFAllocatorDefault, buffer, sizeof(UInt32) * 4); + if (!data) + { + CFAllocatorDeallocate(NULL, buffer); + return 0; + } + + ret = CFMessagePortSendRequest(messagePort, kWaveOutCallbackMessage, data, 0.0, 0.0, NULL, NULL); + CFRelease(data); + CFAllocatorDeallocate(NULL, buffer); + + return (ret == kCFMessagePortSuccess)?1:0; +} + +static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position, + PCMWAVEFORMAT* format) +{ + TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n", + lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec, + format->wf.nChannels, format->wf.nAvgBytesPerSec); + TRACE("Position in bytes=%lu\n", position); + + switch (lpTime->wType) { + case TIME_SAMPLES: + lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels); + TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample); + break; + case TIME_MS: + lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec); + TRACE("TIME_MS=%lu\n", lpTime->u.ms); + break; + case TIME_SMPTE: + lpTime->u.smpte.fps = 30; + position = position / (format->wBitsPerSample / 8 * format->wf.nChannels); + position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */ + lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec; + position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec; + lpTime->u.smpte.min = lpTime->u.smpte.sec / 60; + lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min; + lpTime->u.smpte.hour = lpTime->u.smpte.min / 60; + lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour; + lpTime->u.smpte.fps = 30; + lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec; + 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: + WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType); + lpTime->wType = TIME_BYTES; + /* fall through */ + case TIME_BYTES: + lpTime->u.cb = position; + TRACE("TIME_BYTES=%lu\n", lpTime->u.cb); + break; + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* CoreAudio_GetDevCaps [internal] +*/ +BOOL CoreAudio_GetDevCaps (void) +{ + OSStatus status; + UInt32 propertySize; + AudioDeviceID devId = CoreAudio_DefaultDevice.outputDeviceID; + + char name[MAXPNAMELEN]; + + propertySize = MAXPNAMELEN; + status = AudioDeviceGetProperty(devId, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name); + if (status) { + ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24), + (char) (status >> 16), + (char) (status >> 8), + (char) status); + return FALSE; + } + + memcpy(CoreAudio_DefaultDevice.ds_desc.szDesc, name, sizeof(name)); + strcpy(CoreAudio_DefaultDevice.ds_desc.szDrvname, "winecoreaudio.drv"); + MultiByteToWideChar(CP_ACP, 0, name, sizeof(name), + CoreAudio_DefaultDevice.out_caps.szPname, + sizeof(CoreAudio_DefaultDevice.out_caps.szPname) / sizeof(WCHAR)); + memcpy(CoreAudio_DefaultDevice.dev_name, name, 32); + + propertySize = sizeof(CoreAudio_DefaultDevice.streamDescription); + status = AudioDeviceGetProperty(devId, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &CoreAudio_DefaultDevice.streamDescription); + if (status != noErr) { + ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24), + (char) (status >> 16), + (char) (status >> 8), + (char) status); + return FALSE; + } + + TRACE("Device Stream Description mSampleRate : %f\n mFormatID : %c%c%c%c\n" + "mFormatFlags : %lX\n mBytesPerPacket : %u\n mFramesPerPacket : %u\n" + "mBytesPerFrame : %u\n mChannelsPerFrame : %u\n mBitsPerChannel : %u\n", + CoreAudio_DefaultDevice.streamDescription.mSampleRate, + (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 24), + (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 16), + (char) (CoreAudio_DefaultDevice.streamDescription.mFormatID >> 8), + (char) CoreAudio_DefaultDevice.streamDescription.mFormatID, + CoreAudio_DefaultDevice.streamDescription.mFormatFlags, + CoreAudio_DefaultDevice.streamDescription.mBytesPerPacket, + CoreAudio_DefaultDevice.streamDescription.mFramesPerPacket, + CoreAudio_DefaultDevice.streamDescription.mBytesPerFrame, + CoreAudio_DefaultDevice.streamDescription.mChannelsPerFrame, + CoreAudio_DefaultDevice.streamDescription.mBitsPerChannel); + + CoreAudio_DefaultDevice.out_caps.wMid = 0xcafe; + CoreAudio_DefaultDevice.out_caps.wPid = 0x0001; + + CoreAudio_DefaultDevice.out_caps.vDriverVersion = 0x0001; + CoreAudio_DefaultDevice.out_caps.dwFormats = 0x00000000; + CoreAudio_DefaultDevice.out_caps.wReserved1 = 0; + CoreAudio_DefaultDevice.out_caps.dwSupport = WAVECAPS_VOLUME; + CoreAudio_DefaultDevice.out_caps.dwSupport |= WAVECAPS_LRVOLUME; + + CoreAudio_DefaultDevice.out_caps.wChannels = 2; + CoreAudio_DefaultDevice.out_caps.dwFormats|= WAVE_FORMAT_4S16; + + return TRUE; +} + +/****************************************************************** +* CoreAudio_WaveInit +* +* Initialize CoreAudio_DefaultDevice +*/ +LONG CoreAudio_WaveInit(void) +{ + OSStatus status; + UInt32 propertySize; + CHAR szPname[MAXPNAMELEN]; + int i; + HANDLE hThread; + + TRACE("()\n"); + + /* number of sound cards */ + AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL); + propertySize /= sizeof(AudioDeviceID); + TRACE("sound cards : %u\n", propertySize); + + /* Get the output device */ + propertySize = sizeof(CoreAudio_DefaultDevice.outputDeviceID); + status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &CoreAudio_DefaultDevice.outputDeviceID); + if (status) { + ERR("AudioHardwareGetProperty return %c%c%c%c for kAudioHardwarePropertyDefaultOutputDevice\n", (char) (status >> 24), + (char) (status >> 16), + (char) (status >> 8), + (char) status); + return 1; + } + if (CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown) { + ERR("AudioHardwareGetProperty: CoreAudio_DefaultDevice.outputDeviceID == kAudioDeviceUnknown\n"); + return 1; + } + + if ( ! CoreAudio_GetDevCaps() ) + return 1; + + CoreAudio_DefaultDevice.interface_name=HeapAlloc(GetProcessHeap(),0,strlen(CoreAudio_DefaultDevice.dev_name)+1); + sprintf(CoreAudio_DefaultDevice.interface_name, "%s", CoreAudio_DefaultDevice.dev_name); + + for (i = 0; i < MAX_WAVEOUTDRV; ++i) + { + WOutDev[i].state = WINE_WS_CLOSED; + WOutDev[i].cadev = &CoreAudio_DefaultDevice; + WOutDev[i].woID = i; + + memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); + + WOutDev[i].caps.wMid = 0xcafe; /* Manufac ID */ + WOutDev[i].caps.wPid = 0x0001; /* Product ID */ + snprintf(szPname, sizeof(szPname), "CoreAudio WaveOut %d", i); + MultiByteToWideChar(CP_ACP, 0, szPname, -1, WOutDev[i].caps.szPname, sizeof(WOutDev[i].caps.szPname)/sizeof(WCHAR)); + snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winecoreaudio: %d", i); + + WOutDev[i].caps.vDriverVersion = 0x0001; + WOutDev[i].caps.dwFormats = 0x00000000; + WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME; + + WOutDev[i].caps.wChannels = 2; + /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */ /* FIXME */ + + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16; + WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16; + } + + /* create mach messages handler */ + hThread = CreateThread(NULL, 0, messageThread, NULL, 0, NULL); + if ( !hThread ) + { + ERR("Can't create message thread\n"); + return 1; + } + + return 0; +} + +void CoreAudio_WaveRelease(void) +{ + TRACE("()\n"); + + /* Stop CFRunLoop in messageThread */ + CFMessagePortRef messagePort; + + messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, CFSTR("WaveMessagePort")); + CFMessagePortSendRequest(messagePort, kStopLoopMessage, NULL, 0.0, 0.0, NULL, NULL); + CFRelease(messagePort); +} + +/*======================================================================* +* Low level WAVE OUT implementation * +*======================================================================*/ + +/************************************************************************** +* wodNotifyClient [internal] +* Call from AudioUnit IO thread can't use Wine debug channels. +*/ +static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2) +{ + switch (wMsg) { + case WOM_OPEN: + case WOM_CLOSE: + if (wwo->wFlags != DCB_NULL && + !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, + (HDRVR)wwo->waveDesc.hWave, wMsg, wwo->waveDesc.dwInstance, + dwParam1, dwParam2)) + { + return MMSYSERR_ERROR; + } + break; + case WOM_DONE: + if (wwo->wFlags != DCB_NULL && + ! wodSendDriverCallbackMessage(wwo, wMsg, dwParam1, dwParam2)) + { + return MMSYSERR_ERROR; + } + break; + default: + return MMSYSERR_INVALPARAM; + } + return MMSYSERR_NOERROR; +} + + +/************************************************************************** +* wodGetDevCaps [internal] +*/ +static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW 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; + } + + TRACE("dwSupport=(0x%lx), dwFormats=(0x%lx)\n", WOutDev[wDevID].caps.dwSupport, WOutDev[wDevID].caps.dwFormats); + memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps))); + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodOpen [internal] +* +* NOTE: doesn't it seem like there is a race condition if you try to open +* the same device twice? +*/ +static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) +{ + WINE_WAVEOUT* wwo; + DWORD retval; + DWORD ret; + int audio_fragment; + + 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; + } + + TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%ld wBitsPerSample=%d !\n", + lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, + lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample); + + if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM || + lpDesc->lpFormat->nChannels == 0 || + lpDesc->lpFormat->nSamplesPerSec == 0 + ) + { + WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld wBitsPerSample=%d !\n", + lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, + lpDesc->lpFormat->nSamplesPerSec, lpDesc->lpFormat->wBitsPerSample); + 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 (!AudioUnit_CreateDefaultAudioUnit((void *) wwo, &wwo->audioUnit)) + { + ERR("CoreAudio_CreateDefaultAudioUnit(%p) failed\n", wwo); + return MMSYSERR_ERROR; + } + + if ((dwFlags & WAVE_DIRECTSOUND) && + !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND)) + /* not supported, ignore it */ + dwFlags &= ~WAVE_DIRECTSOUND; + + if (wwo->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED; + + pthread_mutex_init(&wwo->lock, NULL); /* initialize the mutex */ + pthread_mutex_lock(&wwo->lock); + + AudioStreamBasicDescription streamFormat; + + streamFormat.mFormatID = kAudioFormatLinearPCM; + + /* FIXME check for 32bits float -> kLinearPCMFormatFlagIsFloat */ + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger +# ifdef __powerpc__ + | kLinearPCMFormatFlagIsBigEndian /* FIXME Wave format is little endian */ +# endif + | kLinearPCMFormatFlagIsPacked; + + streamFormat.mSampleRate = lpDesc->lpFormat->nSamplesPerSec; + streamFormat.mChannelsPerFrame = lpDesc->lpFormat->nChannels; + streamFormat.mFramesPerPacket = 1; + streamFormat.mBitsPerChannel = lpDesc->lpFormat->wBitsPerSample; + streamFormat.mBytesPerFrame = streamFormat.mBitsPerChannel * streamFormat.mChannelsPerFrame / 8; + streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket; + + ret = AudioUnit_InitializeWithStreamDescription(wwo->audioUnit, streamFormat); + if (!ret) + { + pthread_mutex_unlock(&wwo->lock); + return WAVERR_BADFORMAT; /* FIXME return an error based on the OSStatus */ + } + wwo->streamDescription = streamFormat; + wwo->state = WINE_WS_STOPPED; + + pthread_mutex_unlock(&wwo->lock); + + 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; + } + + wwo->dwPlayedTotal = 0; + wwo->dwWrittenTotal = 0; + + retval = wodNotifyClient(wwo, WOM_OPEN, 0L, 0L); + + return retval; +} + +/************************************************************************** +* wodClose [internal] +*/ +static DWORD wodClose(WORD wDevID) +{ + DWORD ret = MMSYSERR_NOERROR; + WINE_WAVEOUT* wwo; + + TRACE("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + wwo = &WOutDev[wDevID]; + if (wwo->lpQueuePtr) + { + WARN("buffers still playing !\n"); + ret = WAVERR_STILLPLAYING; + } else + { + OSStatus err; + /* 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->state = WINE_WS_CLOSED; /* mark the device as closed */ + + err = AudioUnitUninitialize(wwo->audioUnit); + if (err) { + ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24), + (char) (err >> 16), + (char) (err >> 8), + (char) err); + pthread_mutex_destroy(&wwo->lock); + return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */ + } + + if ( !AudioUnit_CloseAudioUnit(wwo->audioUnit) ) + { + ERR("Can't close AudioUnit\n"); + pthread_mutex_destroy(&wwo->lock); + return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */ + } + pthread_mutex_destroy(&wwo->lock); + + ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L); + } + + return ret; +} + +/************************************************************************** +* wodPrepare [internal] +*/ +static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + OSStatus status; + + 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; + + WOutDev[wDevID].state = WINE_WS_STOPPED; + + status = AudioOutputUnitStart(WOutDev[wDevID].audioUnit); + if (status) { + ERR("AudioOutputUnitStart return %c%c%c%c\n", (char) (status >> 24), + (char) (status >> 16), + (char) (status >> 8), + (char) status); + return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */ + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodUnprepare [internal] +*/ +static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + OSStatus status; + + 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; + + status = AudioOutputUnitStop(WOutDev[wDevID].audioUnit); + if (status) { + ERR("AudioOutputUnitStop return %c%c%c%c\n", (char) (status >> 24), (char) (status >> 16), (char) (status >> 8), (char) status); + return MMSYSERR_ERROR; /* FIXME return an error based on the OSStatus */ + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodHelper_BeginWaveHdr [internal] +* +* Makes the specified lpWaveHdr the currently playing wave header. +* If the specified wave header is a begin loop and we're not already in +* a loop, setup the loop. +* Call from AudioUnit IO thread can't use Wine debug channels. +*/ +static void wodHelper_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr) +{ + wwo->lpPlayPtr = lpWaveHdr; + + if (!lpWaveHdr) + { + return; + } + + if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) + { + if (wwo->lpLoopPtr) + { + fprintf(stderr, "trace:winecoreaudio:wodHelper_BeginWaveHdr Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr); + } else + { + fprintf(stderr, "trace:winecoreaudio:wodHelper_BeginWaveHdr 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; + } + } + wwo->dwPartialOffset = 0; +} + + +/************************************************************************** +* wodHelper_PlayPtrNext [internal] +* +* Advance the play pointer to the next waveheader, looping if required. +* Call from AudioUnit IO thread can't use Wine debug channels. +*/ +static LPWAVEHDR wodHelper_PlayPtrNext(WINE_WAVEOUT* wwo) +{ + LPWAVEHDR lpWaveHdr; + + pthread_mutex_lock(&wwo->lock); + + lpWaveHdr = wwo->lpPlayPtr; + + wwo->dwPartialOffset = 0; + if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) + { + /* We're at the end of a loop, loop if required */ + if (--wwo->dwLoops > 0) + { + wwo->lpPlayPtr = wwo->lpLoopPtr; + } else + { + /* Handle overlapping loops correctly */ + if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) { + fprintf(stderr, "trace:winecoreaudio:wodHelper_PlayPtrNext 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 + */ + } else + { + lpWaveHdr = lpWaveHdr->lpNext; + } + wwo->lpLoopPtr = NULL; + wodHelper_BeginWaveHdr(wwo, lpWaveHdr); + } + } else + { + /* We're not in a loop. Advance to the next wave header */ + wodHelper_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext); + } + + pthread_mutex_unlock(&wwo->lock); + + return lpWaveHdr; +} + +/* if force is TRUE then notify the client that all the headers were completed + * Call from AudioUnit IO thread can't use Wine debug channels. + */ +static DWORD wodHelper_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force) +{ + LPWAVEHDR lpWaveHdr; + DWORD retval; + + pthread_mutex_lock(&wwo->lock); + + /* Start from lpQueuePtr and keep notifying until: + * - we hit an unwritten wavehdr + * - we hit the beginning of a running loop + * - we hit a wavehdr which hasn't finished playing + */ + while ((lpWaveHdr = wwo->lpQueuePtr) && + (force || + (lpWaveHdr != wwo->lpPlayPtr && + lpWaveHdr != wwo->lpLoopPtr))) + { + wwo->lpQueuePtr = lpWaveHdr->lpNext; + + lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; + lpWaveHdr->dwFlags |= WHDR_DONE; + + wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0); + } + + retval = (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != + wwo->lpLoopPtr) ? 0 : INFINITE; + + pthread_mutex_unlock(&wwo->lock); + + return retval; +} + +/************************************************************************** +* wodHelper_Reset [internal] +* +* Resets current output stream. +*/ +static void wodHelper_Reset(WINE_WAVEOUT* wwo, BOOL reset) +{ + FIXME("\n"); + + /* updates current notify list */ + wodHelper_NotifyCompletions(wwo, FALSE); + + if (reset) + { + /* remove all wave headers and notify client that all headers were completed */ + wodHelper_NotifyCompletions(wwo, TRUE); + + pthread_mutex_lock(&wwo->lock); + + wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; + wwo->state = WINE_WS_STOPPED; + wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0; + + wwo->dwPartialOffset = 0; /* Clear partial wavehdr */ + + pthread_mutex_unlock(&wwo->lock); + } + else + { + pthread_mutex_lock(&wwo->lock); + if (wwo->lpLoopPtr) + { + /* complicated case, not handled yet (could imply modifying the loop counter) */ + FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n"); + wwo->lpPlayPtr = wwo->lpLoopPtr; + wwo->dwPartialOffset = 0; + wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */ + } else + { + LPWAVEHDR ptr; + DWORD sz = wwo->dwPartialOffset; + + /* reset all the data as if we had written only up to lpPlayedTotal bytes */ + /* compute the max size playable from lpQueuePtr */ + for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) + { + sz += ptr->dwBufferLength; + } + + /* because the reset lpPlayPtr will be lpQueuePtr */ + if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("doh\n"); + wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal); + wwo->dwWrittenTotal = wwo->dwPlayedTotal; + wwo->lpPlayPtr = wwo->lpQueuePtr; + } + + wwo->state = WINE_WS_PAUSED; + pthread_mutex_unlock(&wwo->lock); + } +} + + +/************************************************************************** +* wodWrite [internal] +* +*/ +static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize) +{ + LPWAVEHDR*wh; + WINE_WAVEOUT *wwo; + + TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize); + + /* first, do the sanity checks... */ + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad dev ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + wwo = &WOutDev[wDevID]; + + if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) + { + TRACE("unprepared\n"); + return WAVERR_UNPREPARED; + } + + if (lpWaveHdr->dwFlags & WHDR_INQUEUE) + { + TRACE("still playing\n"); + return WAVERR_STILLPLAYING; + } + + lpWaveHdr->dwFlags &= ~WHDR_DONE; + lpWaveHdr->dwFlags |= WHDR_INQUEUE; + lpWaveHdr->lpNext = 0; + + pthread_mutex_lock(&wwo->lock); + /* insert buffer at the end of queue */ + for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); + *wh = lpWaveHdr; + + if (!wwo->lpPlayPtr) + wodHelper_BeginWaveHdr(wwo,lpWaveHdr); + if (wwo->state == WINE_WS_STOPPED) + wwo->state = WINE_WS_PLAYING; + pthread_mutex_unlock(&wwo->lock); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodPause [internal] +*/ +static DWORD wodPause(WORD wDevID) +{ + TRACE("(%u);!\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + if (WOutDev[wDevID].state == WINE_WS_PLAYING) + { + pthread_mutex_lock(&WOutDev[wDevID].lock); + WOutDev[wDevID].state = WINE_WS_PAUSED; + pthread_mutex_unlock(&WOutDev[wDevID].lock); + } + + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodRestart [internal] +*/ +static DWORD wodRestart(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV ) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + if (WOutDev[wDevID].state == WINE_WS_PAUSED) + { + pthread_mutex_lock(&WOutDev[wDevID].lock); + WOutDev[wDevID].state = WINE_WS_PLAYING; + pthread_mutex_unlock(&WOutDev[wDevID].lock); + } + + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodReset [internal] +*/ +static DWORD wodReset(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + wodHelper_Reset(&WOutDev[wDevID], TRUE); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodGetPosition [internal] +*/ +static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize) +{ + DWORD val; + WINE_WAVEOUT* wwo; + DWORD elapsedMS; + + TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize); + + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + /* if null pointer to time structure return error */ + if (lpTime == NULL) return MMSYSERR_INVALPARAM; + + wwo = &WOutDev[wDevID]; + + pthread_mutex_lock(&WOutDev[wDevID].lock); + val = wwo->dwPlayedTotal; + pthread_mutex_unlock(&WOutDev[wDevID].lock); + + return bytes_to_mmtime(lpTime, val, &wwo->format); +} + +/************************************************************************** +* wodBreakLoop [internal] +*/ +static DWORD wodBreakLoop(WORD wDevID) +{ + FIXME("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + pthread_mutex_lock(&WOutDev[wDevID].lock); + + if (WOutDev[wDevID].state == WINE_WS_PLAYING && WOutDev[wDevID].lpLoopPtr != NULL) + { + /* ensure exit at end of current loop */ + WOutDev[wDevID].dwLoops = 1; + } + + pthread_mutex_unlock(&WOutDev[wDevID].lock); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodGetVolume [internal] +*/ +static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol) +{ + float left; + float right; + + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + TRACE("(%u, %08lX);\n", wDevID, lpdwVol); + + pthread_mutex_lock(&WOutDev[wDevID].lock); + + AudioUnit_GetVolume(WOutDev[wDevID].audioUnit, &left, &right); + + pthread_mutex_unlock(&WOutDev[wDevID].lock); + + *lpdwVol = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodSetVolume [internal] +*/ +static DWORD wodSetVolume(WORD wDevID, DWORD dwParam) +{ + float left; + float right; + + if (wDevID >= MAX_WAVEOUTDRV) + { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + + left = LOWORD(dwParam) / 65535.0f; + right = HIWORD(dwParam) / 65535.0f; + + TRACE("(%u, %08lX);\n", wDevID, dwParam); + + pthread_mutex_lock(&WOutDev[wDevID].lock); + + AudioUnit_SetVolume(WOutDev[wDevID].audioUnit, left, right); + + pthread_mutex_unlock(&WOutDev[wDevID].lock); + + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodGetNumDevs [internal] +*/ +static DWORD wodGetNumDevs(void) +{ + TRACE("\n"); + return MAX_WAVEOUTDRV; +} + +/************************************************************************** +* wodDevInterfaceSize [internal] +*/ +static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) +{ + TRACE("(%u, %p)\n", wDevID, dwParam1); + + *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1, + NULL, 0 ) * sizeof(WCHAR); + return MMSYSERR_NOERROR; +} + +/************************************************************************** +* wodDevInterface [internal] +*/ +static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2) +{ + TRACE("\n"); + if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1, + NULL, 0 ) * sizeof(WCHAR)) + { + MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].cadev->interface_name, -1, + dwParam1, dwParam2 / sizeof(WCHAR)); + return MMSYSERR_NOERROR; + } + return MMSYSERR_INVALPARAM; +} + +/************************************************************************** +* wodMessage (WINEJACK.7) +*/ +DWORD WINAPI CoreAudio_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser, + DWORD dwParam1, DWORD dwParam2) +{ + TRACE("(%u, %s, %08lX, %08lX, %08lX);\n", + wDevID, getMessage(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, (LPWAVEOUTCAPSW) dwParam1, dwParam2); + case WODM_GETNUMDEVS: return wodGetNumDevs(); + + case WODM_GETPITCH: + case WODM_SETPITCH: + case WODM_GETPLAYBACKRATE: + 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_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1); + case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2); + case DRV_QUERYDSOUNDIFACE: + case DRV_QUERYDSOUNDDESC: + return MMSYSERR_NOTSUPPORTED; + + default: + FIXME("unknown message %d!\n", wMsg); + } + + return MMSYSERR_NOTSUPPORTED; +} + +/*======================================================================* +* Low level DSOUND implementation * +*======================================================================*/ + +typedef struct IDsDriverImpl IDsDriverImpl; +typedef struct IDsDriverBufferImpl IDsDriverBufferImpl; + +struct IDsDriverImpl +{ + /* IUnknown fields */ + const IDsDriverVtbl *lpVtbl; + DWORD ref; + /* IDsDriverImpl fields */ + UINT wDevID; + IDsDriverBufferImpl*primary; +}; + +struct IDsDriverBufferImpl +{ + /* IUnknown fields */ + const IDsDriverBufferVtbl *lpVtbl; + DWORD ref; + /* IDsDriverBufferImpl fields */ + IDsDriverImpl* drv; + DWORD buflen; +}; + +static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv) +{ + /* we can't perform memory mapping as we don't have a file stream + interface with jack like we do with oss */ + 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; +} + +static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc) +{ + memset(desc, 0, sizeof(*desc)); + strcpy(desc->szDesc, "Wine CoreAudio DirectSound Driver"); + strcpy(desc->szDrvname, "winecoreaudio.drv"); + return MMSYSERR_NOERROR; +} + + +/* + CoreAudio IO threaded callback, + we can't call Wine debug channels, critical section or anything using NtCurrentTeb here. +*/ +OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + UInt32 channel; + WINE_WAVEOUT *wwo = (WINE_WAVEOUT *) inRefCon; + int nextPtr = 0; + int needNotify = 0; + + pthread_mutex_lock(&wwo->lock); + if(wwo->state == WINE_WS_PLAYING) + { + unsigned int available; + unsigned int count = ioData->mBuffers[0].mDataByteSize; + + available = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset; + + for (channel = 0; channel < ioData->mNumberBuffers; channel++) + { + if ( available >= count) + { + memcpy(ioData->mBuffers[channel].mData, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, count); + wwo->dwPartialOffset += count; + wwo->dwPlayedTotal += count; + } + else + { + int s; + memcpy(ioData->mBuffers[channel].mData, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, available); + wwo->dwPartialOffset += available; + wwo->dwPlayedTotal += available; + + /* Fill with silence */ + for (s = available; s < count; s++) + ((int *)ioData->mBuffers[channel].mData)[s] = 0; + + nextPtr = 1; + } + } + needNotify = 1; + } + else + { + for (channel = 0; channel < ioData->mNumberBuffers; channel++) + memset(ioData->mBuffers[channel].mData, 0, ioData->mBuffers[channel].mDataByteSize); + } + pthread_mutex_unlock(&wwo->lock); + + if (nextPtr) wodHelper_PlayPtrNext(wwo); + if (needNotify) wodHelper_NotifyCompletions(wwo, FALSE); + return noErr; +} +#else + +/************************************************************************** +* wodMessage (WINECOREAUDIO.7) +*/ +DWORD WINAPI CoreAudio_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, + DWORD dwParam1, DWORD dwParam2) +{ + FIXME("(%u, %04X, %08lX, %08lX, %08lX): CoreAudio support not compiled into wine\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); + return MMSYSERR_NOTENABLED; +} + +#endif diff --git a/dlls/winmm/winecoreaudio/audiounit.c b/dlls/winmm/winecoreaudio/audiounit.c new file mode 100644 index 00000000000..b9ac57cb780 --- /dev/null +++ b/dlls/winmm/winecoreaudio/audiounit.c @@ -0,0 +1,141 @@ +/* + * Wine Driver for CoreAudio / AudioUnit + * + * Copyright 2005, 2006 Emmanuel Maillard + * + * 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 "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wave); + +#ifdef HAVE_AUDIOUNIT_AUDIOUNIT_H +#include + +static char streamStr[512] = {0}; +static char *streamDescription(AudioStreamBasicDescription stream) +{ + sprintf(streamStr, "\n mSampleRate : %f\n mFormatID : %c%c%c%c\n mFormatFlags : %lX\n mBytesPerPacket : %u\n mFramesPerPacket : %u\n mBytesPerFrame : %u\n mChannelsPerFrame : %u\n mBitsPerChannel : %u\n", + stream.mSampleRate, + (char) (stream.mFormatID >> 24), + (char) (stream.mFormatID >> 16), + (char) (stream.mFormatID >> 8), + (char) stream.mFormatID, + stream.mFormatFlags, + stream.mBytesPerPacket, + stream.mFramesPerPacket, + stream.mBytesPerFrame, + stream.mChannelsPerFrame, + stream.mBitsPerChannel); + return streamStr; +} + +extern OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); + +int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au) +{ + OSStatus err; + ComponentDescription desc; + AURenderCallbackStruct callbackStruct; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + Component comp = FindNextComponent(NULL, &desc); + if (comp == NULL) + return 0; + + err = OpenAComponent(comp, au); + if (comp == NULL) + return 0; + + callbackStruct.inputProc = CoreAudio_woAudioUnitIOProc; + callbackStruct.inputProcRefCon = wwo; + + err = AudioUnitSetProperty( *au, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &callbackStruct, + sizeof(callbackStruct)); + return (err == noErr); +} + +int AudioUnit_CloseAudioUnit(AudioUnit au) +{ + OSStatus err = CloseComponent(au); + return (err == noErr); +} + +int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription stream) +{ + OSStatus err = noErr; + + err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 0, &stream, sizeof(AudioStreamBasicDescription)); + + if (err != noErr) + { + ERR("AudioUnitSetProperty return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + err = AudioUnitInitialize(au); + if (err != noErr) + { + ERR("AudioUnitInitialize return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + return 1; +} + +int AudioUnit_SetVolume(AudioUnit au, float left, float right) +{ + OSStatus err = noErr; + + err = AudioUnitSetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left, 0); + + if (err != noErr) + { + ERR("AudioUnitSetParameter return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + return 1; +} + +int AudioUnit_GetVolume(AudioUnit au, float *left, float *right) +{ + OSStatus err = noErr; + + err = AudioUnitGetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left); + if (err != noErr) + { + ERR("AudioUnitGetParameter return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + *right = *left; + return 1; +} +#endif diff --git a/dlls/winmm/winecoreaudio/coreaudio.c b/dlls/winmm/winecoreaudio/coreaudio.c new file mode 100644 index 00000000000..63a9acacb8b --- /dev/null +++ b/dlls/winmm/winecoreaudio/coreaudio.c @@ -0,0 +1,114 @@ +/* + * Wine Driver for CoreAudio + * + * Copyright 2005 Emmanuel Maillard + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "mmddk.h" +#include "coreaudio.h" +#include "wine/library.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(coreaudio); + +#ifdef HAVE_COREAUDIO_COREAUDIO_H + +/************************************************************************** + * CoreAudio_drvLoad [internal] + */ +static LRESULT CoreAudio_drvLoad(void) +{ + TRACE("()\n"); + CoreAudio_WaveInit(); + return 1; +} + +/************************************************************************** + * CoreAudio_drvFree [internal] + */ +static LRESULT CoreAudio_drvFree(void) +{ + TRACE("()\n"); + CoreAudio_WaveRelease(); + return 1; +} + +/************************************************************************** + * CoreAudio_drvOpen [internal] + */ +static LRESULT CoreAudio_drvOpen(LPSTR str) +{ + TRACE("(%s)\n", str); + return 1; +} + +/************************************************************************** + * CoreAudio_drvClose [internal] + */ +static DWORD CoreAudio_drvClose(DWORD dwDevID) +{ + TRACE("(%08lx)\n", dwDevID); + return 1; +} +#endif /* HAVE_COREAUDIO_COREAUDIO_H */ + +/************************************************************************** + * DriverProc (WINECOREAUDIO.1) + */ +LRESULT CALLBACK CoreAudio_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, + LPARAM dwParam1, LPARAM dwParam2) +{ + TRACE("(%08lX, %p, %s (%08X), %08lX, %08lX)\n", + dwDevID, hDriv, wMsg == DRV_LOAD ? "DRV_LOAD" : + wMsg == DRV_FREE ? "DRV_FREE" : + wMsg == DRV_OPEN ? "DRV_OPEN" : + wMsg == DRV_CLOSE ? "DRV_CLOSE" : + wMsg == DRV_ENABLE ? "DRV_ENABLE" : + wMsg == DRV_DISABLE ? "DRV_DISABLE" : + wMsg == DRV_QUERYCONFIGURE ? "DRV_QUERYCONFIGURE" : + wMsg == DRV_CONFIGURE ? "DRV_CONFIGURE" : + wMsg == DRV_INSTALL ? "DRV_INSTALL" : + wMsg == DRV_REMOVE ? "DRV_REMOVE" : "UNKNOWN", + wMsg, dwParam1, dwParam2); + + switch(wMsg) { +#ifdef HAVE_COREAUDIO_COREAUDIO_H + case DRV_LOAD: return CoreAudio_drvLoad(); + case DRV_FREE: return CoreAudio_drvFree(); + case DRV_OPEN: return CoreAudio_drvOpen((LPSTR)dwParam1); + case DRV_CLOSE: return CoreAudio_drvClose(dwDevID); + case DRV_ENABLE: return 1; + case DRV_DISABLE: return 1; + case DRV_QUERYCONFIGURE: return 1; + case DRV_CONFIGURE: MessageBoxA(0, "CoreAudio driver!", "CoreAudio 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/winecoreaudio/coreaudio.h b/dlls/winmm/winecoreaudio/coreaudio.h new file mode 100644 index 00000000000..5c93235e736 --- /dev/null +++ b/dlls/winmm/winecoreaudio/coreaudio.h @@ -0,0 +1,28 @@ +/* Definition for CoreAudio drivers : wine multimedia system + * + * Copyright 2005 Emmanuel Maillard + * + * 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 + */ + +#ifndef __WINE_COREAUDIO_H +#define __WINE_COREAUDIO_H + +extern LONG CoreAudio_WaveInit(void); +extern void CoreAudio_WaveRelease(void); + +/* extern BOOL CoreAudio_MidiInit(void); */ + +#endif /* __WINE_COREAUDIO_H */ diff --git a/dlls/winmm/winecoreaudio/winecoreaudio.drv.spec b/dlls/winmm/winecoreaudio/winecoreaudio.drv.spec new file mode 100644 index 00000000000..4445838e548 --- /dev/null +++ b/dlls/winmm/winecoreaudio/winecoreaudio.drv.spec @@ -0,0 +1,2 @@ +@ stdcall -private DriverProc(long long long long long) CoreAudio_DriverProc +@ stdcall -private wodMessage(long long long long long) CoreAudio_wodMessage diff --git a/include/config.h.in b/include/config.h.in index 87bc361a70a..c3752c66f54 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -26,6 +26,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_ASM_TYPES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_AUDIOUNIT_AUDIOUNIT_H + /* Define to 1 if you have the