From 9c762c7f5b077072cf5388c836bdbab519ff8241 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 31 May 2018 10:58:48 -0500
Subject: [PATCH] xaudio2: Use ffmpeg to convert WMA formats

---
 configure.ac                    |  33 +++
 dlls/x3daudio1_0/Makefile.in    |   2 +
 dlls/x3daudio1_1/Makefile.in    |   2 +
 dlls/x3daudio1_2/Makefile.in    |   2 +
 dlls/x3daudio1_3/Makefile.in    |   2 +
 dlls/x3daudio1_4/Makefile.in    |   2 +
 dlls/x3daudio1_5/Makefile.in    |   2 +
 dlls/x3daudio1_6/Makefile.in    |   2 +
 dlls/x3daudio1_7/Makefile.in    |   2 +
 dlls/xapofx1_1/Makefile.in      |   2 +
 dlls/xapofx1_2/Makefile.in      |   2 +
 dlls/xapofx1_3/Makefile.in      |   2 +
 dlls/xapofx1_4/Makefile.in      |   2 +
 dlls/xapofx1_5/Makefile.in      |   2 +
 dlls/xaudio2_0/Makefile.in      |   3 +-
 dlls/xaudio2_1/Makefile.in      |   3 +-
 dlls/xaudio2_2/Makefile.in      |   3 +-
 dlls/xaudio2_3/Makefile.in      |   3 +-
 dlls/xaudio2_4/Makefile.in      |   3 +-
 dlls/xaudio2_5/Makefile.in      |   3 +-
 dlls/xaudio2_6/Makefile.in      |   3 +-
 dlls/xaudio2_7/Makefile.in      |   3 +-
 dlls/xaudio2_7/xaudio_dll.c     | 370 +++++++++++++++++++++++++++++++-
 dlls/xaudio2_7/xaudio_private.h |   9 +
 dlls/xaudio2_8/Makefile.in      |   3 +-
 dlls/xaudio2_9/Makefile.in      |   3 +-
 include/config.h.in             |   9 +
 27 files changed, 457 insertions(+), 20 deletions(-)

diff --git a/configure.ac b/configure.ac
index ffb3ca523..0fc260487 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_ARG_WITH(cups,      AS_HELP_STRING([--without-cups],[do not use CUPS]))
 AC_ARG_WITH(curses,    AS_HELP_STRING([--without-curses],[do not use (n)curses]),
             [if test "x$withval" = "xno"; then ac_cv_header_ncurses_h=no; ac_cv_header_curses_h=no; fi])
 AC_ARG_WITH(dbus,      AS_HELP_STRING([--without-dbus],[do not use DBus (dynamic device support)]))
+AC_ARG_WITH(ffmpeg,    AS_HELP_STRING([--without-ffmpeg],[do not use the FFmpeg library]))
 AC_ARG_WITH(float-abi, AS_HELP_STRING([--with-float-abi=abi],[specify the ABI (soft|softfp|hard) for ARM platforms]))
 AC_ARG_WITH(fontconfig,AS_HELP_STRING([--without-fontconfig],[do not use fontconfig]))
 AC_ARG_WITH(freetype,  AS_HELP_STRING([--without-freetype],[do not use the FreeType library]))
@@ -1799,6 +1800,38 @@ WINE_NOTICE_WITH(mpg123,[test "x$ac_cv_lib_mpg123_mpg123_feed" != xyes -a x"$ac_
                  [enable_l3codeca_acm])
 test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes || enable_mp3dmod=${enable_mp3dmod:-no}
 
+dnl **** Check for FFmpeg's libavutil ****
+if test "x$with_ffmpeg" != "xno"
+then
+    WINE_PACKAGE_FLAGS(LIBAVUTIL,[libavutil],[-llibavutil],,,
+        [AC_CHECK_HEADERS([libavutil/avutil.h])
+        if test "$ac_cv_header_libavutil_avutil_h" = "yes"
+        then
+            AC_CHECK_LIB(avutil,av_frame_alloc,[:],[LIBAVUTIL_LIBS=""],[$LIBAVUTIL_LIBS])
+        else
+            LIBAVUTIL_CFLAGS=""
+            LIBAVUTIL_LIBS=""
+        fi])
+fi
+
+dnl **** Check for FFmpeg's libavcodec ****
+if test "x$with_ffmpeg" != "xno" -a "x$ac_cv_lib_avutil_av_frame_alloc" = "xyes"
+then
+    WINE_PACKAGE_FLAGS(LIBAVCODEC,[libavcodec],[-llibavcodec],,,
+        [AC_CHECK_HEADERS([libavcodec/avcodec.h])
+        if test "$ac_cv_header_libavcodec_avcodec_h" = "yes"
+        then
+            AC_CHECK_LIB(avcodec,avcodec_send_packet,
+                         [AC_DEFINE(HAVE_FFMPEG, 1, [Define to 1 if you have libavutil and libavcodec from ffmpeg.])],
+                         [LIBAVCODEC_LIBS=""], [$LIBAVUTIL_LIBS $LIBAVCODEC_LIBS])
+        else
+            LIBAVCODEC_CFLAGS=""
+            LIBAVCODEC_LIBS=""
+        fi])
+fi
+WINE_NOTICE_WITH(ffmpeg,[test "x$ac_cv_lib_avcodec_avcodec_send_packet" != xyes],
+                 [libavcodec ${notice_platform}development files not found (or too old), XAudio2 WMA conversion won't be supported.])
+
 dnl **** Check for OpenAL 1.1 ****
 if test "$ac_cv_header_AL_al_h" = "yes"
 then
diff --git a/dlls/x3daudio1_0/Makefile.in b/dlls/x3daudio1_0/Makefile.in
index 9b4ce2189..6c2b0058b 100644
--- a/dlls/x3daudio1_0/Makefile.in
+++ b/dlls/x3daudio1_0/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=0 -DXAUDIO2_VER=0
 MODULE    = x3daudio1_0.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/x3daudio1_1/Makefile.in b/dlls/x3daudio1_1/Makefile.in
index d866dc3dd..e268e3c5e 100644
--- a/dlls/x3daudio1_1/Makefile.in
+++ b/dlls/x3daudio1_1/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=1 -DXAUDIO2_VER=1
 MODULE    = x3daudio1_1.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/x3daudio1_2/Makefile.in b/dlls/x3daudio1_2/Makefile.in
index d5de11b25..758412305 100644
--- a/dlls/x3daudio1_2/Makefile.in
+++ b/dlls/x3daudio1_2/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=2 -DXAUDIO2_VER=2
 MODULE    = x3daudio1_2.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/x3daudio1_3/Makefile.in b/dlls/x3daudio1_3/Makefile.in
index cfd2c6c6c..084336d46 100644
--- a/dlls/x3daudio1_3/Makefile.in
+++ b/dlls/x3daudio1_3/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=3 -DXAUDIO2_VER=3
 MODULE    = x3daudio1_3.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/x3daudio1_4/Makefile.in b/dlls/x3daudio1_4/Makefile.in
index bd0eac7fc..691a71ad2 100644
--- a/dlls/x3daudio1_4/Makefile.in
+++ b/dlls/x3daudio1_4/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=4 -DXAUDIO2_VER=4
 MODULE    = x3daudio1_4.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/x3daudio1_5/Makefile.in b/dlls/x3daudio1_5/Makefile.in
index dac67aef7..cee7b4b28 100644
--- a/dlls/x3daudio1_5/Makefile.in
+++ b/dlls/x3daudio1_5/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=5 -DXAUDIO2_VER=5
 MODULE    = x3daudio1_5.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/x3daudio1_6/Makefile.in b/dlls/x3daudio1_6/Makefile.in
index 177fb8d34..23876bd8a 100644
--- a/dlls/x3daudio1_6/Makefile.in
+++ b/dlls/x3daudio1_6/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=6 -DXAUDIO2_VER=6
 MODULE    = x3daudio1_6.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/x3daudio1_7/Makefile.in b/dlls/x3daudio1_7/Makefile.in
index 019588dce..448a54867 100644
--- a/dlls/x3daudio1_7/Makefile.in
+++ b/dlls/x3daudio1_7/Makefile.in
@@ -1,5 +1,7 @@
 EXTRADEFS = -DX3DAUDIO1_VER=7 -DXAUDIO2_VER=7
 MODULE    = x3daudio1_7.dll
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xapofx1_1/Makefile.in b/dlls/xapofx1_1/Makefile.in
index f69ff86bd..e43cfcf0a 100644
--- a/dlls/xapofx1_1/Makefile.in
+++ b/dlls/xapofx1_1/Makefile.in
@@ -1,6 +1,8 @@
 EXTRADEFS = -DXAPOFX1_VER=1 -DXAUDIO2_VER=2
 MODULE    = xapofx1_1.dll
 IMPORTS   = ole32
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xapofx1_2/Makefile.in b/dlls/xapofx1_2/Makefile.in
index d56a2be4b..bf4df8b09 100644
--- a/dlls/xapofx1_2/Makefile.in
+++ b/dlls/xapofx1_2/Makefile.in
@@ -1,6 +1,8 @@
 EXTRADEFS = -DXAPOFX1_VER=2 -DXAUDIO2_VER=3
 MODULE    = xapofx1_2.dll
 IMPORTS   = ole32
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xapofx1_3/Makefile.in b/dlls/xapofx1_3/Makefile.in
index 1139520b7..4a3056d8d 100644
--- a/dlls/xapofx1_3/Makefile.in
+++ b/dlls/xapofx1_3/Makefile.in
@@ -1,6 +1,8 @@
 EXTRADEFS = -DXAPOFX1_VER=3 -DXAUDIO2_VER=4
 MODULE    = xapofx1_3.dll
 IMPORTS   = ole32
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xapofx1_4/Makefile.in b/dlls/xapofx1_4/Makefile.in
index b49e4643a..36672e776 100644
--- a/dlls/xapofx1_4/Makefile.in
+++ b/dlls/xapofx1_4/Makefile.in
@@ -1,6 +1,8 @@
 EXTRADEFS = -DXAPOFX1_VER=4 -DXAUDIO2_VER=6
 MODULE    = xapofx1_4.dll
 IMPORTS   = ole32
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xapofx1_5/Makefile.in b/dlls/xapofx1_5/Makefile.in
index 5055a16fd..f9244f593 100644
--- a/dlls/xapofx1_5/Makefile.in
+++ b/dlls/xapofx1_5/Makefile.in
@@ -1,6 +1,8 @@
 EXTRADEFS = -DXAPOFX1_VER=5 -DXAUDIO2_VER=7
 MODULE    = xapofx1_5.dll
 IMPORTS   = ole32
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_0/Makefile.in b/dlls/xaudio2_0/Makefile.in
index cf15c7bf0..d8d282bcd 100644
--- a/dlls/xaudio2_0/Makefile.in
+++ b/dlls/xaudio2_0/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=0
 MODULE    = xaudio2_0.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_1/Makefile.in b/dlls/xaudio2_1/Makefile.in
index 32a5a62a2..a0d7bfc7c 100644
--- a/dlls/xaudio2_1/Makefile.in
+++ b/dlls/xaudio2_1/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=1
 MODULE    = xaudio2_1.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_2/Makefile.in b/dlls/xaudio2_2/Makefile.in
index f20de2d5f..4ba7e6c34 100644
--- a/dlls/xaudio2_2/Makefile.in
+++ b/dlls/xaudio2_2/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=2
 MODULE    = xaudio2_2.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_3/Makefile.in b/dlls/xaudio2_3/Makefile.in
index ca749f247..62c644f6e 100644
--- a/dlls/xaudio2_3/Makefile.in
+++ b/dlls/xaudio2_3/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=3
 MODULE    = xaudio2_3.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_4/Makefile.in b/dlls/xaudio2_4/Makefile.in
index 0b74f68fd..b77753fb0 100644
--- a/dlls/xaudio2_4/Makefile.in
+++ b/dlls/xaudio2_4/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=4
 MODULE    = xaudio2_4.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_5/Makefile.in b/dlls/xaudio2_5/Makefile.in
index 09356c94a..bd6873489 100644
--- a/dlls/xaudio2_5/Makefile.in
+++ b/dlls/xaudio2_5/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=5
 MODULE    = xaudio2_5.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_6/Makefile.in b/dlls/xaudio2_6/Makefile.in
index e0ef58815..17295098f 100644
--- a/dlls/xaudio2_6/Makefile.in
+++ b/dlls/xaudio2_6/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=6
 MODULE    = xaudio2_6.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_7/Makefile.in b/dlls/xaudio2_7/Makefile.in
index 2f2e23200..41d307dca 100644
--- a/dlls/xaudio2_7/Makefile.in
+++ b/dlls/xaudio2_7/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=7
 MODULE    = xaudio2_7.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 
 C_SRCS = \
 	compat.c \
diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c
index 0bbe943f1..2e65f47ea 100644
--- a/dlls/xaudio2_7/xaudio_dll.c
+++ b/dlls/xaudio2_7/xaudio_dll.c
@@ -82,6 +82,11 @@ __ASM_GLOBAL_FUNC( call_on_voice_processing_pass_start,
                    "ret" )
 #endif
 
+#define IS_WMA(tag) (tag == WAVE_FORMAT_MSAUDIO1 || \
+        tag == WAVE_FORMAT_WMAUDIO2 || \
+        tag == WAVE_FORMAT_WMAUDIO3 || \
+        tag == WAVE_FORMAT_WMAUDIO_LOSSLESS)
+
 static void dump_fmt(const WAVEFORMATEX *fmt)
 {
     TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
@@ -90,6 +95,11 @@ static void dump_fmt(const WAVEFORMATEX *fmt)
     DOCASE(WAVE_FORMAT_PCM)
     DOCASE(WAVE_FORMAT_IEEE_FLOAT)
     DOCASE(WAVE_FORMAT_EXTENSIBLE)
+    DOCASE(WAVE_FORMAT_ADPCM)
+    DOCASE(WAVE_FORMAT_MSAUDIO1)
+    DOCASE(WAVE_FORMAT_WMAUDIO2)
+    DOCASE(WAVE_FORMAT_WMAUDIO3)
+    DOCASE(WAVE_FORMAT_WMAUDIO_LOSSLESS)
 #undef DOCASE
     default:
         TRACE("Unknown");
@@ -462,6 +472,10 @@ static void WINAPI XA2SRC_DestroyVoice(IXAudio2SourceVoice *iface)
     }
 
     HeapFree(GetProcessHeap(), 0, This->fmt);
+    HeapFree(GetProcessHeap(), 0, This->scratch_buf);
+    This->scratch_buf = NULL;
+    HeapFree(GetProcessHeap(), 0, This->convert_buf);
+    This->convert_buf = NULL;
 
     alDeleteBuffers(XAUDIO2_MAX_QUEUED_BUFFERS, This->al_bufs);
     alDeleteSources(1, &This->al_src);
@@ -474,6 +488,17 @@ static void WINAPI XA2SRC_DestroyVoice(IXAudio2SourceVoice *iface)
     This->cur_buf = 0;
     This->abandoned_albufs = 0;
 
+#if HAVE_FFMPEG
+    if(This->conv_ctx){
+        HeapFree(GetProcessHeap(), 0, This->conv_ctx->extradata);
+        av_frame_free(&This->conv_frame);
+        This->conv_frame = NULL;
+        avcodec_close(This->conv_ctx);
+        av_free(This->conv_ctx);
+        This->conv_ctx = NULL;
+    }
+#endif
+
     LeaveCriticalSection(&This->lock);
 }
 
@@ -579,6 +604,97 @@ static ALenum get_al_format(const WAVEFORMATEX *fmt)
     return 0;
 }
 
+#if HAVE_FFMPEG
+static enum AVCodecID get_ffmpeg_format(const WAVEFORMATEX *pSourceFormat)
+{
+    switch(pSourceFormat->wFormatTag){
+    case WAVE_FORMAT_MSAUDIO1:
+        return AV_CODEC_ID_WMAV1;
+    case WAVE_FORMAT_WMAUDIO2:
+        return AV_CODEC_ID_WMAV2;
+    case WAVE_FORMAT_WMAUDIO3:
+        return AV_CODEC_ID_WMAPRO;
+    case WAVE_FORMAT_WMAUDIO_LOSSLESS:
+        return AV_CODEC_ID_WMALOSSLESS;
+    case WAVE_FORMAT_ADPCM:
+        return AV_CODEC_ID_ADPCM_MS;
+    }
+    return 0;
+}
+
+static ALenum ffmpeg_to_al_fmt(enum AVSampleFormat fmt, int channels)
+{
+    switch(fmt){
+    case AV_SAMPLE_FMT_U8:
+    case AV_SAMPLE_FMT_U8P:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO8;
+        case 2:
+            return AL_FORMAT_STEREO8;
+        case 4:
+            return AL_FORMAT_QUAD8;
+        case 6:
+            return AL_FORMAT_51CHN8;
+        case 7:
+            return AL_FORMAT_61CHN8;
+        case 8:
+            return AL_FORMAT_71CHN8;
+        }
+        break;
+    case AV_SAMPLE_FMT_S16:
+    case AV_SAMPLE_FMT_S16P:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO16;
+        case 2:
+            return AL_FORMAT_STEREO16;
+        case 4:
+            return AL_FORMAT_QUAD16;
+        case 6:
+            return AL_FORMAT_51CHN16;
+        case 7:
+            return AL_FORMAT_61CHN16;
+        case 8:
+            return AL_FORMAT_71CHN16;
+        }
+        break;
+    case AV_SAMPLE_FMT_S32:
+    case AV_SAMPLE_FMT_S32P:
+        switch(channels){
+        /* TODO: mono/stereo? */
+        case 4:
+            return AL_FORMAT_QUAD32;
+        case 6:
+            return AL_FORMAT_51CHN32;
+        case 7:
+            return AL_FORMAT_61CHN32;
+        case 8:
+            return AL_FORMAT_71CHN32;
+        }
+    case AV_SAMPLE_FMT_FLT:
+    case AV_SAMPLE_FMT_FLTP:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO_FLOAT32;
+        case 2:
+            return AL_FORMAT_STEREO_FLOAT32;
+        }
+    case AV_SAMPLE_FMT_DBL:
+    case AV_SAMPLE_FMT_DBLP:
+        switch(channels){
+        case 1:
+            return AL_FORMAT_MONO_DOUBLE_EXT;
+        case 2:
+            return AL_FORMAT_STEREO_DOUBLE_EXT;
+        }
+    default:
+        break;
+    }
+    return 0;
+}
+#endif
+
 static HRESULT WINAPI XA2SRC_SubmitSourceBuffer(IXAudio2SourceVoice *iface,
         const XAUDIO2_BUFFER *pBuffer, const XAUDIO2_BUFFER_WMA *pBufferWMA)
 {
@@ -622,7 +738,10 @@ static HRESULT WINAPI XA2SRC_SubmitSourceBuffer(IXAudio2SourceVoice *iface,
 #endif
 
     /* convert samples offsets to bytes */
-    if(This->fmt->wFormatTag == WAVE_FORMAT_ADPCM){
+    if(IS_WMA(This->fmt->wFormatTag)){
+        /* Offsets for WMA appear to just be byte offsets, since it doesn't
+         * have the concept of "samples". */
+    }else if(This->fmt->wFormatTag == WAVE_FORMAT_ADPCM){
         /* ADPCM gives us a number of samples per block, so round down to
          * nearest block and convert to bytes */
         buf->xa2buffer.PlayBegin = buf->xa2buffer.PlayBegin / ((ADPCMWAVEFORMAT*)This->fmt)->wSamplesPerBlock * This->fmt->nBlockAlign;
@@ -1515,19 +1634,119 @@ static HRESULT WINAPI IXAudio2Impl_CreateSourceVoice(IXAudio2 *iface,
 
     src->al_fmt = get_al_format(pSourceFormat);
     if(!src->al_fmt){
-        src->in_use = FALSE;
-        LeaveCriticalSection(&src->lock);
-        WARN("OpenAL can't convert this format!\n");
-        return AUDCLNT_E_UNSUPPORTED_FORMAT;
-    }
+#if HAVE_FFMPEG
+        enum AVCodecID cid;
+        AVCodec *codec;
+
+        TRACE("OpenAL can't use this format, so using FFmpeg\n");
+
+        cid = get_ffmpeg_format(pSourceFormat);
+        if(!cid){
+            WARN("Don't know how to convert this format to an FFmpeg codec\n");
+            src->in_use = FALSE;
+            LeaveCriticalSection(&src->lock);
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        codec = avcodec_find_decoder(cid);
+        if(!codec){
+            WARN("FFmpeg can't convert this format (0x%x), so failing\n", cid);
+            src->in_use = FALSE;
+            LeaveCriticalSection(&src->lock);
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->conv_ctx = avcodec_alloc_context3(codec);
+        if(!src->conv_ctx){
+            WARN("avcodec_alloc_context3 failed\n");
+            src->in_use = FALSE;
+            LeaveCriticalSection(&src->lock);
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->conv_ctx->bit_rate = pSourceFormat->nAvgBytesPerSec * 8;
+        src->conv_ctx->channels = pSourceFormat->nChannels;
+        src->conv_ctx->sample_rate = pSourceFormat->nSamplesPerSec;
+        src->conv_ctx->block_align = pSourceFormat->nBlockAlign;
+        src->conv_ctx->bits_per_coded_sample = pSourceFormat->wBitsPerSample;
+        src->conv_ctx->extradata_size = pSourceFormat->cbSize;
+        if(pSourceFormat->cbSize){
+            src->conv_ctx->extradata = HeapAlloc(GetProcessHeap(), 0, pSourceFormat->cbSize + AV_INPUT_BUFFER_PADDING_SIZE);
+            memcpy(src->conv_ctx->extradata, (&pSourceFormat->cbSize) + 1, pSourceFormat->cbSize);
+        }else if(IS_WMA(pSourceFormat->wFormatTag)){
+            /* xWMA doesn't provide the extradata info that FFmpeg needs to
+             * decode WMA data, so we create some fake extradata. This is taken
+             * from <ffmpeg/libavformat/xwma.c>. */
+            TRACE("synthesizing extradata for xWMA\n");
+            src->conv_ctx->extradata_size = 6;
+            src->conv_ctx->extradata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AV_INPUT_BUFFER_PADDING_SIZE);
+            src->conv_ctx->extradata[4] = 31;
+        }
 
-    src->submit_blocksize = pSourceFormat->nBlockAlign;
+        if(avcodec_open2(src->conv_ctx, codec, NULL) < 0){
+            WARN("avcodec_open2 failed\n");
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+            src->in_use = FALSE;
+            LeaveCriticalSection(&src->lock);
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->conv_frame = av_frame_alloc();
+        if(!src->conv_ctx){
+            WARN("av_frame_alloc failed\n");
+            avcodec_close(src->conv_ctx);
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+            src->in_use = FALSE;
+            LeaveCriticalSection(&src->lock);
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->al_fmt = ffmpeg_to_al_fmt(src->conv_ctx->sample_fmt, pSourceFormat->nChannels);
+        if(!src->al_fmt){
+            WARN("OpenAL can't use FFmpeg output format\n");
+            av_frame_free(&src->conv_frame);
+            src->conv_frame = NULL;
+            avcodec_close(src->conv_ctx);
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+            src->in_use = FALSE;
+            LeaveCriticalSection(&src->lock);
+            return AUDCLNT_E_UNSUPPORTED_FORMAT;
+        }
+
+        src->submit_blocksize = av_get_bytes_per_sample(src->conv_ctx->sample_fmt);
+
+        src->scratch_bytes = This->period_frames * 1.5 * src->submit_blocksize;
+        src->scratch_buf = HeapAlloc(GetProcessHeap(), 0, src->scratch_bytes);
+
+        src->convert_bytes = pSourceFormat->nBlockAlign + AV_INPUT_BUFFER_PADDING_SIZE;
+        src->convert_buf = HeapAlloc(GetProcessHeap(), 0, src->convert_bytes);
+#else
+        WARN("OpenAL can't use this format and no FFmpeg, so giving up\n");
+#endif
+    }else
+        src->submit_blocksize = pSourceFormat->nBlockAlign;
 
     src->fmt = copy_waveformat(pSourceFormat);
 
     hr = XA2SRC_SetOutputVoices(&src->IXAudio2SourceVoice_iface, pSendList);
     if(FAILED(hr)){
         HeapFree(GetProcessHeap(), 0, src->fmt);
+#if HAVE_FFMPEG
+        if(src->conv_ctx){
+            av_frame_free(&src->conv_frame);
+            src->conv_frame = NULL;
+            avcodec_close(src->conv_ctx);
+            HeapFree(GetProcessHeap(), 0, src->conv_ctx->extradata);
+            av_free(src->conv_ctx);
+            src->conv_ctx = NULL;
+        }
+#endif
         src->in_use = FALSE;
         LeaveCriticalSection(&src->lock);
         return hr;
@@ -2257,6 +2476,9 @@ HRESULT WINAPI XAudio2Create(IXAudio2 **ppxa2, UINT32 flags, XAUDIO2_PROCESSOR p
  * buffer's data has all been queued */
 static BOOL xa2buffer_queue_period(XA2SourceImpl *src, XA2Buffer *buf, ALuint al_buf)
 {
+#if HAVE_FFMPEG
+    int averr;
+#endif
     UINT32 submit_bytes;
     const BYTE *submit_buf = NULL;
 
@@ -2265,9 +2487,133 @@ static BOOL xa2buffer_queue_period(XA2SourceImpl *src, XA2Buffer *buf, ALuint al
         return FALSE;
     }
 
-    submit_bytes = min(src->xa2->period_frames * src->submit_blocksize, buf->cur_end_bytes - buf->offs_bytes);
-    submit_buf = buf->xa2buffer.pAudioData + buf->offs_bytes;
-    buf->offs_bytes += submit_bytes;
+#if HAVE_FFMPEG
+    if(src->conv_ctx){
+        DWORD scratch_offs_bytes = 0;
+        AVPacket avpkt = {0};
+
+        avpkt.size = src->fmt->nBlockAlign;
+        avpkt.data = (unsigned char*)buf->xa2buffer.pAudioData + buf->offs_bytes;
+
+        /* convert at least a period into scratch_buf */
+        while(scratch_offs_bytes < src->xa2->period_frames * src->submit_blocksize){
+            DWORD to_copy_bytes;
+
+            averr = avcodec_receive_frame(src->conv_ctx, src->conv_frame);
+            if(averr == AVERROR(EAGAIN)){
+                /* ffmpeg needs more data to decode */
+                avpkt.pts = avpkt.dts = AV_NOPTS_VALUE;
+
+                if(buf->offs_bytes >= buf->cur_end_bytes)
+                    /* no more data in this buffer */
+                    break;
+
+                if(buf->offs_bytes + avpkt.size + AV_INPUT_BUFFER_PADDING_SIZE > buf->cur_end_bytes){
+                    UINT32 remain = buf->cur_end_bytes - buf->offs_bytes;
+                    /* Unfortunately, the FFmpeg API requires that a number of
+                     * extra bytes must be available past the end of the buffer.
+                     * The xaudio2 client probably hasn't done this, so we have to
+                     * perform a copy near the end of the buffer. */
+                    TRACE("hitting end of buffer. copying %u + %u bytes into %u buffer\n",
+                            remain, AV_INPUT_BUFFER_PADDING_SIZE, src->convert_bytes);
+                    if(src->convert_bytes < remain + AV_INPUT_BUFFER_PADDING_SIZE){
+                        src->convert_bytes = remain + AV_INPUT_BUFFER_PADDING_SIZE;
+                        TRACE("buffer too small, expanding to %u\n", src->convert_bytes);
+                        src->convert_buf = HeapReAlloc(GetProcessHeap(), 0, src->convert_buf, src->convert_bytes);
+                    }
+                    memcpy(src->convert_buf, buf->xa2buffer.pAudioData + buf->offs_bytes, remain);
+                    memset(src->convert_buf + remain, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+                    avpkt.data = src->convert_buf;
+                }
+
+                averr = avcodec_send_packet(src->conv_ctx, &avpkt);
+                if(averr){
+                    WARN("avcodec_send_packet failed: %s\n", av_err2str(averr));
+                    break;
+                }
+
+                buf->offs_bytes += avpkt.size;
+                avpkt.data += avpkt.size;
+
+                /* data sent, try receive again */
+                continue;
+            }
+
+            if(averr){
+                WARN("avcodec_receive_frame failed: %s\n", av_err2str(averr));
+                return TRUE;
+            }
+
+            to_copy_bytes = src->conv_frame->nb_samples * src->conv_ctx->channels * src->submit_blocksize;
+
+            while(scratch_offs_bytes + to_copy_bytes >= src->scratch_bytes){
+                src->scratch_bytes *= 2;
+                src->scratch_buf = HeapReAlloc(GetProcessHeap(), 0, src->scratch_buf, src->scratch_bytes);
+            }
+
+            if(av_sample_fmt_is_planar(src->conv_ctx->sample_fmt)){
+                int s, c;
+                uint8_t **source, *dst;
+                uint16_t *dst16;
+                uint32_t *dst32;
+                uint64_t *dst64;
+
+                /* one buffer per channel, but openal needs interleaved, so
+                 * interleave samples into scratch buf */
+                dst = src->scratch_buf + scratch_offs_bytes;
+                source = src->conv_frame->data;
+
+                switch(src->submit_blocksize){
+                case 1:
+                    for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                        for(c = 0; c < src->conv_ctx->channels; ++c)
+                            *(dst++) = source[c][s];
+                    break;
+                case 2:
+                    dst16 = (uint16_t*)dst;
+                    for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                        for(c = 0; c < src->conv_ctx->channels; ++c)
+                            *(dst16++) = ((uint16_t*)(source[c]))[s];
+                    break;
+                case 4:
+                    dst32 = (uint32_t*)dst;
+                    for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                        for(c = 0; c < src->conv_ctx->channels; ++c)
+                            *(dst32++) = ((uint32_t*)(source[c]))[s];
+                    break;
+                case 8:
+                    dst64 = (uint64_t*)dst;
+                    for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                        for(c = 0; c < src->conv_ctx->channels; ++c)
+                            *(dst64++) = ((uint64_t*)(source[c]))[s];
+                    break;
+                default:
+                    for(s = 0; s < src->conv_frame->nb_samples; ++s)
+                        for(c = 0; c < src->conv_ctx->channels; ++c){
+                            memcpy(dst, &source[c][src->submit_blocksize * s], src->submit_blocksize);
+                            dst += src->submit_blocksize;
+                        }
+                    break;
+                }
+
+                scratch_offs_bytes += to_copy_bytes;
+            }else{
+                /* copy into scratch buf */
+                memcpy(src->scratch_buf + scratch_offs_bytes, src->conv_frame->data[0], to_copy_bytes);
+                scratch_offs_bytes += to_copy_bytes;
+            }
+        }
+
+        submit_bytes = scratch_offs_bytes;
+        submit_buf = src->scratch_buf;
+    }else{
+#endif
+        submit_bytes = min(src->xa2->period_frames * src->submit_blocksize, buf->cur_end_bytes - buf->offs_bytes);
+        submit_buf = buf->xa2buffer.pAudioData + buf->offs_bytes;
+        buf->offs_bytes += submit_bytes;
+#if HAVE_FFMPEG
+    }
+#endif
 
     alBufferData(al_buf, src->al_fmt, submit_buf, submit_bytes,
             src->fmt->nSamplesPerSec);
@@ -2290,6 +2636,10 @@ static UINT32 get_underrun_warning(XA2SourceImpl *src)
     UINT32 period_bytes = src->xa2->period_frames * src->submit_blocksize;
     UINT32 total = 0, i;
 
+    if(IS_WMA(src->fmt->wFormatTag))
+        /* PCM only */
+        return 0;
+
     for(i = 0; i < src->nbufs && total < IN_AL_PERIODS * period_bytes; ++i){
         XA2Buffer *buf = &src->buffers[(src->first_buf + i) % XAUDIO2_MAX_QUEUED_BUFFERS];
         total += buf->cur_end_bytes - buf->offs_bytes;
diff --git a/dlls/xaudio2_7/xaudio_private.h b/dlls/xaudio2_7/xaudio_private.h
index 5d3814fb9..9cf7f003c 100644
--- a/dlls/xaudio2_7/xaudio_private.h
+++ b/dlls/xaudio2_7/xaudio_private.h
@@ -29,6 +29,10 @@
 #include "mmdeviceapi.h"
 #include "audioclient.h"
 
+#if HAVE_FFMPEG
+#include <libavcodec/avcodec.h>
+#endif
+
 #include <AL/al.h>
 #include <AL/alc.h>
 #include <AL/alext.h>
@@ -83,6 +87,11 @@ typedef struct _XA2SourceImpl {
     ALuint al_bufs[XAUDIO2_MAX_QUEUED_BUFFERS];
     DWORD first_al_buf, al_bufs_used, abandoned_albufs;
 
+#if HAVE_FFMPEG
+    AVCodecContext *conv_ctx;
+    AVFrame *conv_frame;
+#endif
+
     struct list entry;
 } XA2SourceImpl;
 
diff --git a/dlls/xaudio2_8/Makefile.in b/dlls/xaudio2_8/Makefile.in
index d4efc41d5..8ad071f48 100644
--- a/dlls/xaudio2_8/Makefile.in
+++ b/dlls/xaudio2_8/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=8
 MODULE    = xaudio2_8.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/dlls/xaudio2_9/Makefile.in b/dlls/xaudio2_9/Makefile.in
index ceb2216f5..0243ed674 100644
--- a/dlls/xaudio2_9/Makefile.in
+++ b/dlls/xaudio2_9/Makefile.in
@@ -1,7 +1,8 @@
 EXTRADEFS = -DXAUDIO2_VER=9
 MODULE    = xaudio2_9.dll
 IMPORTS   = advapi32 ole32 user32 uuid
-EXTRALIBS = $(OPENAL_LIBS)
+EXTRALIBS = $(OPENAL_LIBS) $(LIBAVCODEC_LIBS) $(LIBAVUTIL_LIBS)
+EXTRAINCL = $(LIBAVCODEC_CFLAGS) $(LIBAVUTIL_CFLAGS)
 PARENTSRC = ../xaudio2_7
 
 C_SRCS = \
diff --git a/include/config.h.in b/include/config.h.in
index a4cc23111..5c26873f2 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -381,6 +381,15 @@
 /* Define to 1 if you have the `lgammaf' function. */
 #undef HAVE_LGAMMAF
 
+/* Define to 1 if you have the <libavcodec/avcodec.h> header file. */
+#undef HAVE_LIBAVCODEC_AVCODEC_H
+
+/* Define to 1 if you have the <libavutil/avutil.h> header file. */
+#undef HAVE_LIBAVUTIL_AVUTIL_H
+
+/* Define to 1 if you have libavutil and libavcodec from ffmpeg. */
+#undef HAVE_FFMPEG
+
 /* Define to 1 if you have the `gettextpo' library (-lgettextpo). */
 #undef HAVE_LIBGETTEXTPO
 
-- 
2.20.1

