Codebase list faudio / f8c1f4a
New upstream release Stephen Kitt 2 years ago
20 changed file(s) with 2427 addition(s) and 296 deletion(s). Raw diff Collapse all Expand all
88 option(BUILD_TESTS "Build tests/ folder for unit tests to be executed on the host against FAudio" OFF)
99 if(WIN32)
1010 option(BUILD_CPP "Build cpp/ folder (COM wrapper for XAudio2)" OFF)
11 option(PLATFORM_WIN32 "Enable native Win32 platform instead of SDL2" OFF)
1112 endif()
1213 option(XNASONG "Build with XNA_Song.c" ON)
1314 option(LOG_ASSERTIONS "Bind FAudio_assert to log, instead of platform's assert" OFF)
2930 # Version
3031 SET(LIB_MAJOR_VERSION "0")
3132 SET(LIB_MINOR_VERSION "21")
32 SET(LIB_REVISION "02")
33 SET(LIB_REVISION "07")
3334 SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
3435
3536 # Build Type
9798 src/FAudio_internal_simd.c
9899 src/FAudio_operationset.c
99100 src/FAudio_platform_sdl2.c
101 src/FAudio_platform_win32.c
100102 # Optional source files
101103 src/XNA_Song.c
102104 src/FAudio_gstreamer.c
103105 )
106
107 if(PLATFORM_WIN32)
108 target_link_libraries(FAudio PRIVATE -ldxguid -luuid -lwinmm -lole32 -ladvapi32 -luser32 -lmfplat -lmfreadwrite -lmfuuid -lpropsys)
109 target_compile_definitions(FAudio PUBLIC FAUDIO_WIN32_PLATFORM)
110 target_compile_definitions(FAudio PRIVATE HAVE_WMADEC=1)
111 set(PLATFORM_CFLAGS "-DFAUDIO_WIN32_PLATFORM")
112 set(GSTREAMER OFF)
113 set(XNASONG OFF)
114 else()
115 set(PLATFORM_CFLAGS)
116 endif()
104117
105118 # Only disable DebugConfiguration in release builds
106119 if(NOT FORCE_ENABLE_DEBUGCONFIGURATION)
136149
137150 # GStreamer Support
138151 if(GSTREAMER)
152 # Requires for gstreamer in the pkgconfig file
153 set(FAUDIO_GSTREAMER_DEPS " gstreamer-app-1.0 gstreamer-audio-1.0")
154
139155 # Add the extra definition...
140 target_compile_definitions(FAudio PRIVATE HAVE_GSTREAMER=1)
156 target_compile_definitions(FAudio PRIVATE HAVE_WMADEC=1 HAVE_GSTREAMER=1)
141157
142158 # Find GStreamer
143159 find_package(PkgConfig)
156172 ${GSTAUDIO_LDFLAGS}
157173 ${GSTAPP_LDFLAGS}
158174 )
175 else()
176 set(FAUDIO_GSTREAMER_DEPS "")
159177 endif(GSTREAMER)
160178
161179 # Dump source voices to RIFF WAVE files
164182 endif()
165183
166184 # SDL2 Dependency
167 if (DEFINED SDL2_INCLUDE_DIRS AND DEFINED SDL2_LIBRARIES)
185 if (PLATFORM_WIN32)
186 message(STATUS "not using SDL2")
187 elseif (DEFINED SDL2_INCLUDE_DIRS AND DEFINED SDL2_LIBRARIES)
168188 message(STATUS "using pre-defined SDL2 variables SDL2_INCLUDE_DIRS and SDL2_LIBRARIES")
169189 target_include_directories(FAudio PUBLIC "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
170190 target_link_libraries(FAudio PUBLIC ${SDL2_LIBRARIES})
88 Version: @LIB_VERSION@
99
1010 Libs: -L${libdir} -l@PROJECT_NAME@
11 Cflags: -I${includedir}
11 Requires:@FAUDIO_GSTREAMER_DEPS@
12 Cflags: -I${includedir} @PLATFORM_CFLAGS@
7878
7979 public const uint FAUDIO_ABI_VERSION = 0;
8080 public const uint FAUDIO_MAJOR_VERSION = 21;
81 public const uint FAUDIO_MINOR_VERSION = 2;
81 public const uint FAUDIO_MINOR_VERSION = 7;
8282 public const uint FAUDIO_PATCH_VERSION = 0;
8383
8484 public const uint FAUDIO_COMPILED_VERSION = (
175175 */
176176 }
177177
178 public struct FAudioXMA2WaveFormatEx
179 {
180 public FAudioWaveFormatEx wfx;
181 public ushort wNumStreams;
182 public uint dwChannelMask;
183 public uint dwSamplesEncoded;
184 public uint dwBytesPerBlock;
185 public uint dwPlayBegin;
186 public uint dwPlayLength;
187 public uint dwLoopBegin;
188 public uint dwLoopLength;
189 public byte bLoopCount;
190 public byte bEncoderVersion;
191 public ushort wBlockCount;
192 };
193
178194 [StructLayout(LayoutKind.Sequential, Pack = 1)]
179195 public unsafe struct FAudioDeviceDetails
180196 {
405421 );
406422
407423 [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
424 public static extern uint FAudio_CreateSourceVoice(
425 IntPtr audio, /* FAudio* */
426 out IntPtr ppSourceVoice, /* FAudioSourceVoice** */
427 IntPtr pSourceFormat, /* FAudioWaveFormatEx* */
428 uint Flags,
429 float MaxFrequencyRatio,
430 IntPtr pCallback, /* FAudioVoiceCallback* */
431 IntPtr pSendList, /* FAudioVoiceSends* */
432 IntPtr pEffectChain /* FAudioEffectChain* */
433 );
434
435 [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
408436 public static extern uint FAudio_CreateSubmixVoice(
409437 IntPtr audio, /* FAudio* */
410438 out IntPtr ppSubmixVoice, /* FAudioSubmixVoice** */
622650 IntPtr voice, /* FAudioSourceVoice* */
623651 ref FAudioBuffer pBuffer,
624652 IntPtr pBufferWMA /* const FAudioBufferWMA* */
653 );
654
655 [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
656 public static extern uint FAudioSourceVoice_SubmitSourceBuffer(
657 IntPtr voice, /* FAudioSourceVoice* */
658 ref FAudioBuffer pBuffer,
659 ref FAudioBufferWMA pBufferWMA
625660 );
626661
627662 [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
0 faudio (21.02-2) UNRELEASED; urgency=medium
0 faudio (21.07-1) UNRELEASED; urgency=medium
11
2 [ Jens Reyer ]
23 * Remove myself from Uploaders.
34
4 -- Jens Reyer <jre.winesim@gmail.com> Sun, 07 Feb 2021 18:23:26 +0100
5 [ Stephen Kitt ]
6 * New upstream release.
7
8 -- Stephen Kitt <skitt@debian.org> Wed, 07 Jul 2021 21:36:36 +0200
59
610 faudio (21.02-1) unstable; urgency=medium
711
189189 FAudio_CreateMasteringVoice@Base 19.02
190190 FAudio_CreateSourceVoice@Base 19.02
191191 FAudio_CreateSubmixVoice@Base 19.02
192 FAudio_GSTREAMER_end_buffer@Base 20.11
193 FAudio_GSTREAMER_free@Base 20.11
194 FAudio_GSTREAMER_init@Base 20.11
195192 FAudio_GetDeviceCount@Base 19.02
196193 FAudio_GetDeviceDetails@Base 19.02
197194 FAudio_GetPerformanceData@Base 19.02
236233 FAudio_StopEngine@Base 19.02
237234 FAudio_UTF8_To_UTF16@Base 19.02
238235 FAudio_UnregisterForCallbacks@Base 19.02
236 FAudio_WMADEC_end_buffer@Base 21.07
237 FAudio_WMADEC_free@Base 21.07
238 FAudio_WMADEC_init@Base 21.07
239239 FAudio_close@Base 19.02
240240 FAudio_fopen@Base 19.02
241241 FAudio_memopen@Base 19.02
22
33 --- a/CMakeLists.txt
44 +++ b/CMakeLists.txt
5 @@ -462,12 +462,3 @@
5 @@ -482,12 +482,3 @@
66 ${CMAKE_CURRENT_BINARY_DIR}/generated/${PROJECT_NAME}-config.cmake
77 INSTALL_DESTINATION ${FAudio_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
88 )
22
33 --- a/CMakeLists.txt
44 +++ b/CMakeLists.txt
5 @@ -102,6 +102,7 @@
5 @@ -104,6 +104,7 @@
66 src/XNA_Song.c
77 src/FAudio_gstreamer.c
88 )
99 +target_link_libraries(FAudio PRIVATE m)
1010
11 # Only disable DebugConfiguration in release builds
12 if(NOT FORCE_ENABLE_DEBUGCONFIGURATION)
11 if(PLATFORM_WIN32)
12 target_link_libraries(FAudio PRIVATE -ldxguid -luuid -lwinmm -lole32 -ladvapi32 -luser32 -lmfplat -lmfreadwrite -lmfuuid -lpropsys)
22
33 --- a/CMakeLists.txt
44 +++ b/CMakeLists.txt
5 @@ -78,7 +78,6 @@
5 @@ -79,7 +79,6 @@
66 # Internal Headers
77 src/FACT_internal.h
88 src/FAudio_internal.h
1010 src/stb_vorbis.h
1111 # Source Files
1212 src/F3DAudio.c
13 @@ -104,6 +103,9 @@
14 )
15 target_link_libraries(FAudio PRIVATE m)
13 @@ -117,6 +116,9 @@
14 set(PLATFORM_CFLAGS)
15 endif()
1616
1717 +add_definitions(-DSTB_VORBIS_HEADER_ONLY -D_DEFAULT_SOURCE)
1818 +target_link_libraries(FAudio PRIVATE stb)
22
33 --- a/CMakeLists.txt
44 +++ b/CMakeLists.txt
5 @@ -423,6 +423,7 @@
5 @@ -443,6 +443,7 @@
66 if(BUILD_TESTS)
77 add_executable(faudio_tests tests/xaudio2.c)
88 target_link_libraries(faudio_tests PRIVATE FAudio)
310310 } FAudioDebugConfiguration;
311311
312312 #pragma pack(pop)
313
314 /* This ISN'T packed. Strictly speaking it wouldn't have mattered anyway but eh.
315 * See https://github.com/microsoft/DirectXTK/issues/256
316 */
317 typedef struct FAudioXMA2WaveFormatEx
318 {
319 FAudioWaveFormatEx wfx;
320 uint16_t wNumStreams;
321 uint32_t dwChannelMask;
322 uint32_t dwSamplesEncoded;
323 uint32_t dwBytesPerBlock;
324 uint32_t dwPlayBegin;
325 uint32_t dwPlayLength;
326 uint32_t dwLoopBegin;
327 uint32_t dwLoopLength;
328 uint8_t bLoopCount;
329 uint8_t bEncoderVersion;
330 uint16_t wBlockCount;
331 } FAudioXMA2WaveFormat;
313332
314333 /* Constants */
315334
465484
466485 #define FAUDIO_ABI_VERSION 0
467486 #define FAUDIO_MAJOR_VERSION 21
468 #define FAUDIO_MINOR_VERSION 2
487 #define FAUDIO_MINOR_VERSION 7
469488 #define FAUDIO_PATCH_VERSION 0
470489
471490 #define FAUDIO_COMPILED_VERSION ( \
8888 FAudio_PlatformDestroyMutex(pEngine->wbLock);
8989 FAudio_PlatformUnlockMutex(pEngine->apiLock);
9090 FAudio_PlatformDestroyMutex(pEngine->apiLock);
91 if (pEngine->settings != NULL)
92 {
93 pEngine->pFree(pEngine->settings);
94 }
9195 pEngine->pFree(pEngine);
9296 FAudio_PlatformRelease();
9397 return 0;
16351639 ((entry->Format.wBlockAlign + 16) * 2)
16361640 );
16371641 }
1642 else
1643 {
1644 FAudio_assert(0 && "Unrecognized wFormatTag!");
1645 }
16381646
16391647 pWaveProperties->loopRegion = entry->LoopRegion;
16401648 pWaveProperties->streaming = pWaveBank->streaming;
16551663 FAudioBufferWMA bufferWMA;
16561664 FAudioVoiceSends sends;
16571665 FAudioSendDescriptor send;
1658 FAudioADPCMWaveFormat format;
1666 union
1667 {
1668 FAudioWaveFormatEx pcm;
1669 FAudioADPCMWaveFormat adpcm;
1670 FAudioXMA2WaveFormat xma2;
1671 } format;
16591672 FACTWaveBankEntry *entry;
1673 FACTSeekTable *seek;
16601674 if (pWaveBank == NULL)
16611675 {
16621676 *ppWave = NULL;
17011715 send.pOutputVoice = pWaveBank->parentEngine->master;
17021716 sends.SendCount = 1;
17031717 sends.pSends = &send;
1704 format.wfx.nChannels = entry->Format.nChannels;
1705 format.wfx.nSamplesPerSec = entry->Format.nSamplesPerSec;
1718 format.pcm.nChannels = entry->Format.nChannels;
1719 format.pcm.nSamplesPerSec = entry->Format.nSamplesPerSec;
17061720 if (entry->Format.wFormatTag == 0x0)
17071721 {
1708 format.wfx.wFormatTag = FAUDIO_FORMAT_PCM;
1709 format.wfx.wBitsPerSample = 8 << entry->Format.wBitsPerSample;
1710 format.wfx.nBlockAlign = format.wfx.nChannels * format.wfx.wBitsPerSample / 8;
1711 format.wfx.nAvgBytesPerSec = format.wfx.nBlockAlign * format.wfx.nSamplesPerSec;
1712 format.wfx.cbSize = 0;
1722 format.pcm.wFormatTag = FAUDIO_FORMAT_PCM;
1723 format.pcm.wBitsPerSample = 8 << entry->Format.wBitsPerSample;
1724 format.pcm.nBlockAlign = format.pcm.nChannels * format.pcm.wBitsPerSample / 8;
1725 format.pcm.nAvgBytesPerSec = format.pcm.nBlockAlign * format.pcm.nSamplesPerSec;
1726 format.pcm.cbSize = 0;
17131727 }
17141728 else if (entry->Format.wFormatTag == 0x1)
17151729 {
1716 /* XMA2 is quite similar to WMA Pro. */
1730 /* XMA2 is quite similar to WMA Pro... is what everyone thought.
1731 * What a great way to start this comment.
1732 *
1733 * Let's reconstruct the extra data because who knows what decoder we're dealing with in <present year>.
1734 * It's also a good exercise in understanding XMA2 metadata and feeding blocks into the decoder properly.
1735 * At the time of writing this patch, it's FFmpeg via gstreamer which doesn't even respect most of this.
1736 * ... which means: good luck to whoever ends up finding inaccuracies here in the future!
1737 *
1738 * dwLoopLength seems to match dwPlayLength in everything I've seen that had bLoopCount == 0.
1739 * dwLoopBegin can be > 0 even with bLoopCount == 0 because why not. Let's ignore that.
1740 *
1741 * dwSamplesEncoded is usually close to dwPlayLength but not always (if ever?) equal. Let's assume equality.
1742 * The XMA2 seek table uses sample indices as opposed to WMA's byte index seek table.
1743 *
1744 * nBlockAlign uses aWMABlockAlign given the entire WMA Pro thing BUT it's expected to be the block size for decoding.
1745 * The XMA2 block size MUST be a multiple of 2048 BUT entry->PlayRegion.dwLength / seek->entryCount doesn't respect that.
1746 * And even when correctly guesstimating the block size, we sometimes end up with block sizes >= 64k BYTES. nBlockAlign IS 16-BIT!
1747 * Scrap nBlockAlign. I've given up and made all FAudio gstreamer functions use dwBytesPerBlock if available.
1748 * Still though, if we don't want FAudio_INTERNAL_DecodeGSTREAMER to hang, the total data length must match (see SoundEffect.cs in FNA).
1749 * As such, we round up when guessing the block size, feed GStreamer with zeroes^Wundersized blocks and hope for the best.
1750 *
1751 * This is FUN.
1752 * -ade
1753 */
17171754 FAudio_assert(entry->Format.wBitsPerSample != 0);
17181755
1719 format.wfx.wFormatTag = FAUDIO_FORMAT_XMAUDIO2;
1720 format.wfx.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1721 format.wfx.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1722 format.wfx.wBitsPerSample = 16;
1723 format.wfx.cbSize = 0;
1756 seek = &pWaveBank->seekTables[nWaveIndex];
1757 format.pcm.wFormatTag = FAUDIO_FORMAT_XMAUDIO2;
1758 format.pcm.wBitsPerSample = 16;
1759 format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1760 format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1761 format.pcm.cbSize = (
1762 sizeof(FAudioXMA2WaveFormat) -
1763 sizeof(FAudioWaveFormatEx)
1764 );
1765 format.xma2.wNumStreams = (format.pcm.nChannels + 1) / 2;
1766 format.xma2.dwChannelMask = format.pcm.nChannels > 1 ? 0xFFFFFFFF >> (32 - format.pcm.nChannels) : 0;
1767 format.xma2.dwSamplesEncoded = seek->entries[seek->entryCount - 1];
1768 format.xma2.dwBytesPerBlock = (uint16_t) FAudio_ceil(
1769 (double) entry->PlayRegion.dwLength /
1770 (double) seek->entryCount /
1771 2048.0
1772 ) * 2048;
1773 format.xma2.dwPlayBegin = format.xma2.dwLoopBegin = 0;
1774 format.xma2.dwPlayLength = format.xma2.dwLoopLength = format.xma2.dwSamplesEncoded;
1775 format.xma2.bLoopCount = 0;
1776 format.xma2.bEncoderVersion = 4;
1777 format.xma2.wBlockCount = seek->entryCount;
17241778 }
17251779 else if (entry->Format.wFormatTag == 0x2)
17261780 {
1727 format.wfx.wFormatTag = FAUDIO_FORMAT_MSADPCM;
1728 format.wfx.nBlockAlign = (entry->Format.wBlockAlign + 22) * format.wfx.nChannels;
1729 format.wfx.wBitsPerSample = 16;
1730 format.wfx.cbSize = (
1781 format.pcm.wFormatTag = FAUDIO_FORMAT_MSADPCM;
1782 format.pcm.nBlockAlign = (entry->Format.wBlockAlign + 22) * format.pcm.nChannels;
1783 format.pcm.wBitsPerSample = 16;
1784 format.pcm.cbSize = (
17311785 sizeof(FAudioADPCMWaveFormat) -
17321786 sizeof(FAudioWaveFormatEx)
17331787 );
1734 format.wSamplesPerBlock = (
1735 ((format.wfx.nBlockAlign / format.wfx.nChannels) - 6) * 2
1788 format.adpcm.wSamplesPerBlock = (
1789 ((format.pcm.nBlockAlign / format.pcm.nChannels) - 6) * 2
17361790 );
17371791 }
17381792 else if (entry->Format.wFormatTag == 0x3)
17401794 /* Apparently this is used to detect WMA Pro...? */
17411795 FAudio_assert(entry->Format.wBitsPerSample == 0);
17421796
1743 format.wfx.wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
1744 format.wfx.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1745 format.wfx.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1746 format.wfx.wBitsPerSample = 16;
1747 format.wfx.cbSize = 0;
1797 format.pcm.wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
1798 format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1799 format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1800 format.pcm.wBitsPerSample = 16;
1801 format.pcm.cbSize = 0;
17481802 }
17491803 else
17501804 {
17601814 (*ppWave)->callback.callback.OnVoiceProcessingPassEnd = NULL;
17611815 (*ppWave)->callback.callback.OnVoiceProcessingPassStart = NULL;
17621816 (*ppWave)->callback.wave = *ppWave;
1763 (*ppWave)->srcChannels = format.wfx.nChannels;
1817 (*ppWave)->srcChannels = format.pcm.nChannels;
17641818 FAudio_CreateSourceVoice(
17651819 pWaveBank->parentEngine->audio,
17661820 &(*ppWave)->voice,
1767 &format.wfx,
1821 &format.pcm,
17681822 FAUDIO_VOICE_USEFILTER, /* FIXME: Can this be optional? */
17691823 4.0f,
17701824 (FAudioVoiceCallback*) &(*ppWave)->callback,
17741828 if (pWaveBank->streaming)
17751829 {
17761830 /* Init stream cache info */
1777 if (format.wfx.wFormatTag == FAUDIO_FORMAT_PCM)
1831 if (format.pcm.wFormatTag == FAUDIO_FORMAT_PCM)
17781832 {
17791833 (*ppWave)->streamSize = (
1780 format.wfx.nSamplesPerSec *
1781 format.wfx.nBlockAlign
1834 format.pcm.nSamplesPerSec *
1835 format.pcm.nBlockAlign
17821836 );
17831837 }
1784 else if (format.wfx.wFormatTag == FAUDIO_FORMAT_MSADPCM)
1838 else if (format.pcm.wFormatTag == FAUDIO_FORMAT_MSADPCM)
17851839 {
17861840 (*ppWave)->streamSize = (
1787 format.wfx.nSamplesPerSec /
1788 format.wSamplesPerBlock *
1789 format.wfx.nBlockAlign
1841 format.pcm.nSamplesPerSec /
1842 format.adpcm.wSamplesPerBlock *
1843 format.pcm.nBlockAlign
17901844 );
17911845 }
17921846 else
17961850
17971851 /* XACT does NOT support loop subregions for these formats */
17981852 FAudio_assert(entry->LoopRegion.dwStartSample == 0);
1799 FAudio_assert(entry->LoopRegion.dwTotalSamples == entry->Duration);
1853 FAudio_assert(entry->LoopRegion.dwTotalSamples == 0 || entry->LoopRegion.dwTotalSamples == entry->Duration);
18001854 }
18011855 (*ppWave)->streamCache = (uint8_t*) pWaveBank->parentEngine->pMalloc(
18021856 (*ppWave)->streamSize
18311885 buffer.LoopCount = nLoopCount;
18321886 }
18331887 buffer.pContext = NULL;
1834 if ( format.wfx.wFormatTag == FAUDIO_FORMAT_WMAUDIO2 ||
1835 format.wfx.wFormatTag == FAUDIO_FORMAT_XMAUDIO2 )
1888 if (format.pcm.wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
18361889 {
18371890 bufferWMA.pDecodedPacketCumulativeBytes =
18381891 pWaveBank->seekTables[nWaveIndex].entries;
23152368 FACT_STATE_STOPPING |
23162369 FACT_STATE_PAUSED
23172370 );
2371
2372 FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
2373
23182374 FAudio_PlatformUnlockMutex(
23192375 pCue->parentBank->parentEngine->apiLock
23202376 );
24132469 FACT_STATE_STOPPED |
24142470 FACT_STATE_PREPARED
24152471 );
2472
2473 FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUEPLAY, FACTNOTIFICATIONTYPE_CUEPLAY);
2474
24162475 pCue->start = FAudio_timems();
24172476
24182477 /* If it's a simple wave, just play it! */
25112570 pCue->maxRpcReleaseTime
25122571 );
25132572 }
2514
2515 pCue->state |= FACT_STATE_STOPPING;
2573 else
2574 {
2575 /* Pretty sure this doesn't happen, but just in case? */
2576 pCue->state |= FACT_STATE_STOPPING;
2577 }
25162578 }
25172579
25182580 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
3737
3838 #define FACT_CONTENT_VERSION_3_4 45
3939 #define FACT_CONTENT_VERSION_3_1 44
40 #define FACT_CONTENT_VERSION_3_0 43
4041
4142 static inline int FACT_INTERNAL_SupportedContent(uint16_t version)
4243 {
4344 return ( version == FACT_CONTENT_VERSION ||
4445 version == FACT_CONTENT_VERSION_3_4 ||
45 version == FACT_CONTENT_VERSION_3_1 );
46 version == FACT_CONTENT_VERSION_3_1 ||
47 version == FACT_CONTENT_VERSION_3_0 );
4648 }
4749
4850 #define WAVEBANK_HEADER_VERSION 44
6163 static inline float FACT_INTERNAL_CalculateAmplitudeRatio(float decibel)
6264 {
6365 return (float) FAudio_pow(10.0, decibel / 2000.0);
66 }
67
68 static inline float FACT_INTERNAL_CalculateFilterFrequency(
69 float desiredFrequency,
70 uint32_t sampleRate
71 ) {
72 /* This is needed to convert linear frequencies to the value
73 * FAudio_INTERNAL_FilterVoice expects, in order for it to actually
74 * filter at the correct frequency.
75 *
76 * The formula is...
77 *
78 * (2 * sin(pi * (desired filter cutoff frequency) / sampleRate))
79 *
80 * ... but it behaves badly as the filter frequency gets too high as a
81 * fraction of the sample rate, hence the mins.
82 *
83 * -@Woflox
84 */
85 float freq = 2 * FAudio_sin(
86 F3DAUDIO_PI *
87 FAudio_min(desiredFrequency / sampleRate, 0.5f)
88 );
89 return FAudio_min(freq, 1.0f);
6490 }
6591
6692 static inline void FACT_INTERNAL_ReadFile(
387413 {
388414 const float rngQFactor = 1.0f / (
389415 FACT_INTERNAL_rng() *
390 (evt->wave.maxQFactor - evt->wave.minQFactor)
416 (evt->wave.maxQFactor - evt->wave.minQFactor) +
417 evt->wave.minQFactor
391418 );
392 const float rngFrequency = (
393 FACT_INTERNAL_rng() *
394 (evt->wave.maxFrequency - evt->wave.minFrequency)
395 ) / 20000.0f;
419 const float rngFrequency = FACT_INTERNAL_CalculateFilterFrequency(
420 (
421 FACT_INTERNAL_rng() *
422 (evt->wave.maxFrequency - evt->wave.minFrequency) +
423 evt->wave.minFrequency
424 ),
425 cue->parentBank->parentEngine->audio->master->master.inputSampleRate
426 );
396427 if (trackInst->activeWave.wave != NULL)
397428 {
398429 /* Variation on Loop */
426457 }
427458 else
428459 {
429 trackInst->upcomingWave.baseQFactor = 1.0f / (float) track->qfactor;
430 trackInst->upcomingWave.baseFrequency = track->frequency / 20000.0f;
460 trackInst->upcomingWave.baseQFactor = 1.0f / (track->qfactor / 3.0f);
461 trackInst->upcomingWave.baseFrequency = FACT_INTERNAL_CalculateFilterFrequency(
462 track->frequency,
463 cue->parentBank->parentEngine->audio->master->master.inputSampleRate
464 );
431465 }
432466
433467 /* Try to change loop counter at the very end */
834868 return 1;
835869 }
836870
871 void FACT_INTERNAL_SendCueNotification(FACTCue *cue, FACTNoticationsFlags flag, uint8_t type)
872 {
873 if (cue->parentBank->parentEngine->notifications & flag)
874 {
875 FACTNotification note;
876
877 note.type = type;
878 note.pvContext = cue->parentBank->parentEngine->cue_context;
879 note.cue.cueIndex = cue->index;
880 note.cue.pSoundBank = cue->parentBank;
881 note.cue.pCue = cue;
882
883 cue->parentBank->parentEngine->notificationCallback(&note);
884 }
885 }
886
837887 void FACT_INTERNAL_DestroySound(FACTSoundInstance *sound)
838888 {
839889 uint8_t i;
871921 sound->parentCue->state |= FACT_STATE_STOPPED;
872922 sound->parentCue->state &= ~(FACT_STATE_PLAYING | FACT_STATE_STOPPING);
873923 sound->parentCue->data->instanceCount -= 1;
924
925 FACT_INTERNAL_SendCueNotification(sound->parentCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
874926 }
875927 sound->parentCue->parentBank->parentEngine->pFree(sound);
876928 }
887939 sound->fadeType = 2; /* Out */
888940 sound->fadeStart = FAudio_timems();
889941 sound->fadeTarget = fadeOutMS;
942
943 sound->parentCue->state |= FACT_STATE_STOPPING;
890944 }
891945
892946 void FACT_INTERNAL_BeginReleaseRPC(FACTSoundInstance *sound, uint16_t releaseMS)
901955 sound->fadeType = 3; /* Release RPC */
902956 sound->fadeStart = FAudio_timems();
903957 sound->fadeTarget = releaseMS;
958
959 sound->parentCue->state |= FACT_STATE_STOPPING;
904960 }
905961
906962 /* RPC Helper Functions */
9491005 result = rpc->points[i].y;
9501006 if (var >= rpc->points[i].x && var <= rpc->points[i + 1].x)
9511007 {
952 /* TODO: Non-linear curves! Check rpc->points[i].type!
953 * 0 - Linear
954 * 1 - "Fast", a logarithmic curve?
955 * 2 - "Slow", an exponential curve?
956 * 3 - "SinCos", looks like log if higher/exp if lower?
957 * flibit absolutely does not know math, he is useless!
958 */
959
960 /* y += mx */
961 result +=
962 ((rpc->points[i + 1].y - rpc->points[i].y) /
963 (rpc->points[i + 1].x - rpc->points[i].x)) *
964 (var - rpc->points[i].x);
965
966 /* Pre-algebra, rockin'! */
1008 const float maxX = rpc->points[i + 1].x - rpc->points[i].x;
1009 const float maxY = rpc->points[i + 1].y - rpc->points[i].y;
1010 const float deltaX = (var - rpc->points[i].x);
1011 const float deltaXNormalized = deltaX / maxX;
1012
1013 if (rpc->points[i].type == 0) /* Linear */
1014 {
1015 result += maxY * deltaXNormalized;
1016 }
1017 else if (rpc->points[i].type == 1) /* Fast */
1018 {
1019 result += maxY * (1.0f - FAudio_pow(1.0f - FAudio_pow(deltaXNormalized, 1.0f / 1.5f), 1.5f));
1020 }
1021 else if (rpc->points[i].type == 2) /* Slow */
1022 {
1023 result += maxY * (1.0f - FAudio_pow(1.0f - FAudio_pow(deltaXNormalized, 1.5f), 1.0f / 1.5f));
1024 }
1025 else if (rpc->points[i].type == 3) /* SinCos */
1026 {
1027 if (maxY > 0.0f)
1028 {
1029 result += maxY * (1.0f - FAudio_pow(1.0f - FAudio_sqrtf(deltaXNormalized), 2.0f));
1030 }
1031 else
1032 {
1033 result += maxY * (1.0f - FAudio_sqrtf(1.0f - FAudio_pow(deltaXNormalized, 2.0f)));
1034 }
1035 }
1036 else
1037 {
1038 FAudio_assert(0 && "Unrecognized curve type!");
1039 }
1040
9671041 break;
9681042 }
9691043 }
9861060
9871061 if (codeCount > 0)
9881062 {
989 /* Do NOT overwrite Frequency! */
1063 /* Do NOT overwrite Frequency/QFactor! */
9901064 data->rpcVolume = 0.0f;
9911065 data->rpcPitch = 0.0f;
9921066 data->rpcReverbSend = 0.0f;
993 data->rpcFilterQFactor = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
9941067 for (i = 0; i < codeCount; i += 1)
9951068 {
9961069 rpc = FACT_INTERNAL_GetRPC(
10501123 else if (rpc->parameter == RPC_PARAMETER_FILTERFREQUENCY)
10511124 {
10521125 /* Yes, just overwrite... */
1053 data->rpcFilterFreq = rpcResult / 20000.0f;
1126 data->rpcFilterFreq = FACT_INTERNAL_CalculateFilterFrequency(
1127 rpcResult,
1128 engine->audio->master->master.inputSampleRate
1129 );
10541130 }
10551131 else if (rpc->parameter == RPC_PARAMETER_FILTERQFACTOR)
10561132 {
1057 /* TODO: How do we combine these? */
1058 data->rpcFilterQFactor += 1.0f / rpcResult;
1133 /* Yes, just overwrite... */
1134 data->rpcFilterQFactor = 1.0f / rpcResult;
10591135 }
10601136 else
10611137 {
12151291 sound->parentCue->maxRpcReleaseTime
12161292 );
12171293 }
1218
1219 sound->parentCue->state |= FACT_STATE_STOPPING;
1294 else
1295 {
1296 /* Pretty sure this doesn't happen, but just in case? */
1297 sound->parentCue->state |= FACT_STATE_STOPPING;
1298 }
12201299 }
12211300 }
12221301
14291508
14301509 /* RPC updates */
14311510 sound->rpcData.rpcFilterFreq = -1.0f;
1511 sound->rpcData.rpcFilterQFactor = -1.0f;
14321512 FACT_INTERNAL_UpdateRPCs(
14331513 sound->parentCue,
14341514 sound->sound->rpcCodeCount,
14401520 for (i = 0; i < sound->sound->trackCount; i += 1)
14411521 {
14421522 sound->tracks[i].rpcData.rpcFilterFreq = sound->rpcData.rpcFilterFreq;
1523 sound->tracks[i].rpcData.rpcFilterQFactor = sound->rpcData.rpcFilterQFactor;
14431524 FACT_INTERNAL_UpdateRPCs(
14441525 sound->parentCue,
14451526 sound->sound->tracks[i].rpcCodeCount,
15411622 {
15421623 filterParams.Frequency = sound->tracks[i].activeWave.baseFrequency;
15431624 }
1544 filterParams.OneOverQ = (
1545 sound->tracks[i].activeWave.baseQFactor +
1546 sound->rpcData.rpcFilterQFactor +
1547 sound->tracks[i].rpcData.rpcFilterQFactor
1548 ) / 3.0f; /* FIXME: How do we combine QFactor params? */
1625 if (sound->tracks[i].rpcData.rpcFilterQFactor >= 0.0f)
1626 {
1627 filterParams.OneOverQ = sound->tracks[i].rpcData.rpcFilterQFactor;
1628 }
1629 else
1630 {
1631 filterParams.OneOverQ = sound->tracks[i].activeWave.baseQFactor;
1632 }
15491633 FAudioVoice_SetFilterParameters(
15501634 sound->tracks[i].activeWave.wave->voice,
15511635 &filterParams,
18381922 buffer.pContext = NULL;
18391923
18401924 /* Submit, finally. */
1841 if ( entry->Format.wFormatTag == 0x1 ||
1842 entry->Format.wFormatTag == 0x3 )
1925 if (entry->Format.wFormatTag == 0x3)
18431926 {
18441927 bufferWMA.pDecodedPacketCumulativeBytes =
18451928 c->wave->parentBank->seekTables[c->wave->index].entries;
21302213 {
21312214 pEngine->dspPresetCodes[i] = (uint32_t) (ptr - start);
21322215 pEngine->dspPresets[i].accessibility = read_u8(&ptr);
2133 pEngine->dspPresets[i].parameterCount = read_u32(&ptr, se);
2216 pEngine->dspPresets[i].parameterCount = read_u16(&ptr, se);
2217 ptr += 2; /* Unknown value */
21342218 pEngine->dspPresets[i].parameters = (FACTDSPParameter*) pEngine->pMalloc(
21352219 sizeof(FACTDSPParameter) *
21362220 pEngine->dspPresets[i].parameterCount
22022286 pEngine->sb_context = NULL;
22032287 pEngine->wb_context = NULL;
22042288 pEngine->wave_context = NULL;
2289
2290 /* Store this pointer in case we're asked to free it */
2291 if (pParams->globalSettingsFlags & FACT_FLAG_MANAGEDATA)
2292 {
2293 pEngine->settings = pParams->pGlobalSettingsBuffer;
2294 }
22052295
22062296 /* Finally. */
22072297 FAudio_assert((ptr - start) == pParams->globalSettingsBufferSize);
22652355 track->events[i].wave.angle = read_u16(ptr, se);
22662356
22672357 /* Track Variation */
2268 track->events[i].wave.complex.trackCount = read_u16(ptr, se);
2269 track->events[i].wave.complex.variation = read_u16(ptr, se);
2358 evtInfo = read_u32(ptr, se);
2359 track->events[i].wave.complex.trackCount = evtInfo & 0xFFFF;
2360 track->events[i].wave.complex.variation = (evtInfo >> 16) & 0xFFFF;
22702361 *ptr += 4; /* Unknown values */
22712362 track->events[i].wave.complex.tracks = (uint16_t*) pMalloc(
22722363 sizeof(uint16_t) *
23372428 track->events[i].wave.variationFlags = read_u16(ptr, se);
23382429
23392430 /* Track Variation */
2340 track->events[i].wave.complex.trackCount = read_u16(ptr, se);
2341 track->events[i].wave.complex.variation = read_u16(ptr, se);
2431 evtInfo = read_u32(ptr, se);
2432 track->events[i].wave.complex.trackCount = evtInfo & 0xFFFF;
2433 track->events[i].wave.complex.variation = (evtInfo >> 16) & 0xFFFF;
23422434 *ptr += 4; /* Unknown values */
23432435 track->events[i].wave.complex.tracks = (uint16_t*) pMalloc(
23442436 sizeof(uint16_t) *
24282520 FACTSoundBank **ppSoundBank
24292521 ) {
24302522 FACTSoundBank *sb;
2431 uint16_t cueSimpleCount,
2523 uint16_t contentVersion,
2524 cueSimpleCount,
24322525 cueComplexCount,
24332526 cueTotalAlign;
24342527 int32_t cueSimpleOffset,
24402533 cueHashOffset,
24412534 cueNameIndexOffset,
24422535 soundOffset;
2536 uint32_t entryCountAndFlags;
2537 uint16_t filterData;
24432538 uint8_t platform;
24442539 size_t memsize;
2445 uint16_t i, j, cur, tool;
2540 uint16_t i, j, k, cur, tool;
24462541 uint8_t *ptrBookmark;
24472542
24482543 uint8_t *ptr = (uint8_t*) pvBuffer;
24562551 return -1; /* TODO: NOT XACT FILE */
24572552 }
24582553
2459 if (!FACT_INTERNAL_SupportedContent(read_u16(&ptr, se)))
2554 contentVersion = read_u16(&ptr, se);
2555 if (!FACT_INTERNAL_SupportedContent(contentVersion))
24602556 {
24612557 return -2;
24622558 }
25992695 loc.rpcCodeCount = read_u8(&ptr); \
26002696 memsize = sizeof(uint32_t) * loc.rpcCodeCount; \
26012697 loc.rpcCodes = (uint32_t*) pEngine->pMalloc(memsize); \
2602 FAudio_memcpy(loc.rpcCodes, ptr, memsize); \
2603 ptr += memsize;
2698 for (k = 0; k < loc.rpcCodeCount; k += 1) \
2699 { \
2700 loc.rpcCodes[k] = read_u32(&ptr, se); \
2701 } \
26042702
26052703 /* Sound has attached RPCs */
26062704 if (sb->sounds[i].flags & 0x02)
26552753 sb->sounds[i].dspCodeCount = read_u8(&ptr);
26562754 memsize = sizeof(uint32_t) * sb->sounds[i].dspCodeCount;
26572755 sb->sounds[i].dspCodes = (uint32_t*) pEngine->pMalloc(memsize);
2658 FAudio_memcpy(sb->sounds[i].dspCodes, ptr, memsize);
2659 ptr += memsize;
2756 for (j = 0; j < sb->sounds[i].dspCodeCount; j += 1)
2757 {
2758 sb->sounds[i].dspCodes[j] = read_u32(&ptr, se);
2759 }
26602760 }
26612761 else
26622762 {
26732773
26742774 sb->sounds[i].tracks[j].code = read_u32(&ptr, se);
26752775
2676 sb->sounds[i].tracks[j].filter = read_u8(&ptr);
2677 if (sb->sounds[i].tracks[j].filter & 0x01)
2776 if (contentVersion == FACT_CONTENT_VERSION_3_0)
2777 {
2778 /* 3.0 doesn't have track filter data */
2779 sb->sounds[i].tracks[j].filter = 0xFF;
2780 sb->sounds[i].tracks[j].qfactor = 0;
2781 sb->sounds[i].tracks[j].frequency = 0;
2782 continue;
2783 }
2784
2785 filterData = read_u16(&ptr, se);
2786 if (filterData & 0x0001)
26782787 {
26792788 sb->sounds[i].tracks[j].filter =
2680 (sb->sounds[i].tracks[j].filter >> 1) & 0x02;
2789 (filterData >> 1) & 0x02;
26812790 }
26822791 else
26832792 {
26842793 /* Huh...? */
26852794 sb->sounds[i].tracks[j].filter = 0xFF;
26862795 }
2687
2688 sb->sounds[i].tracks[j].qfactor = read_u8(&ptr);
2796 sb->sounds[i].tracks[j].qfactor = (filterData >> 8) & 0xFF;
26892797 sb->sounds[i].tracks[j].frequency = read_u16(&ptr, se);
26902798 }
26912799
27772885 for (i = 0; i < sb->variationCount; i += 1)
27782886 {
27792887 sb->variationCodes[i] = (uint32_t) (ptr - start);
2780 sb->variations[i].entryCount = read_u16(&ptr, se);
2781 sb->variations[i].flags = (read_u16(&ptr, se) >> 3) & 0x07;
2888 entryCountAndFlags = read_u32(&ptr, se);
2889 sb->variations[i].entryCount = entryCountAndFlags & 0xFFFF;
2890 sb->variations[i].flags = (entryCountAndFlags >> (16 + 3)) & 0x07;
27822891 ptr += 2; /* Unknown value */
27832892 sb->variations[i].variable = read_s16(&ptr, se);
27842893 memsize = sizeof(FACTVariation) * sb->variations[i].entryCount;
29593068 uint32_t fileOffset;
29603069 uint8_t *packetBuffer = NULL;
29613070 uint32_t packetBufferLen = 0;
3071 uint16_t *pcm;
29623072
29633073 #define SEEKSET(loc) \
29643074 fileOffset = offset + loc;
31163226 }
31173227 wb->entries[i].PlayRegion.dwOffset +=
31183228 header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYWAVEDATA].dwOffset;
3229
3230 /* If it's in-memory big-endian PCM, swap! */
3231 if ( se &&
3232 !wb->streaming &&
3233 wb->entries[i].Format.wFormatTag == 0x0 &&
3234 wb->entries[i].Format.wBitsPerSample == 1 )
3235 {
3236 pcm = (uint16_t*) FAudio_memptr(
3237 (FAudioIOStream*) wb->io,
3238 wb->entries[i].PlayRegion.dwOffset
3239 );
3240 for (j = 0; j < wb->entries[i].PlayRegion.dwLength; j += 2, pcm += 1)
3241 {
3242 DOSWAP_16(*pcm);
3243 }
3244 }
31193245 }
31203246
31213247 /* FIXME: This is a bit hacky. */
8888 typedef struct FACTDSPPreset
8989 {
9090 uint8_t accessibility;
91 uint32_t parameterCount;
91 uint16_t parameterCount;
9292 FACTDSPParameter *parameters;
9393 } FACTDSPPreset;
9494
439439 void *sb_context;
440440 void *wb_context;
441441 void *wave_context;
442
443 /* Settings handle */
444 void *settings;
442445 };
443446
444447 struct FACTSoundBank
580583 void FACT_INTERNAL_DestroySound(FACTSoundInstance *sound);
581584 void FACT_INTERNAL_BeginFadeOut(FACTSoundInstance *sound, uint16_t fadeOutMS);
582585 void FACT_INTERNAL_BeginReleaseRPC(FACTSoundInstance *sound, uint16_t releaseMS);
586
587 void FACT_INTERNAL_SendCueNotification(FACTCue *cue, FACTNoticationsFlags flag, uint8_t type);
583588
584589 /* RPC Helper Functions */
585590
292292
293293 if ( pSourceFormat->wFormatTag == FAUDIO_FORMAT_PCM ||
294294 pSourceFormat->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT ||
295 pSourceFormat->wFormatTag == FAUDIO_FORMAT_XMAUDIO2 ||
296295 pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2 )
297296 {
298297 FAudioWaveFormatExtensible *fmtex = (FAudioWaveFormatExtensible*) audio->pMalloc(
316315 {
317316 FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID));
318317 }
319 else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
320 {
321 FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_XMAUDIO2, sizeof(FAudioGUID));
322 }
323318 else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
324319 {
325320 FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_WMAUDIO2, sizeof(FAudioGUID));
352347 fmtex->wSamplesPerBlock = ((
353348 fmtex->wfx.nBlockAlign / fmtex->wfx.nChannels
354349 ) - 6) * 2;
350 (*ppSourceVoice)->src.format = &fmtex->wfx;
351 }
352 else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
353 {
354 FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat*) audio->pMalloc(
355 sizeof(FAudioXMA2WaveFormat)
356 );
357
358 /* Copy what we can, ideally the sizes match! */
359 size_t cbSize = sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize;
360 FAudio_memcpy(
361 fmtex,
362 pSourceFormat,
363 FAudio_min(cbSize, sizeof(FAudioXMA2WaveFormat))
364 );
365 if (cbSize < sizeof(FAudioXMA2WaveFormat))
366 {
367 FAudio_zero(
368 ((uint8_t*) fmtex) + cbSize,
369 sizeof(FAudioADPCMWaveFormat) - cbSize
370 );
371 }
372
373 /* Does XAudio2 validate this input?! */
374 fmtex->wfx.cbSize = sizeof(FAudioXMA2WaveFormat) - sizeof(FAudioWaveFormatEx);
355375 (*ppSourceVoice)->src.format = &fmtex->wfx;
356376 }
357377 else
428448 }
429449 else if ( COMPARE_GUID(WMAUDIO2) ||
430450 COMPARE_GUID(WMAUDIO3) ||
431 COMPARE_GUID(WMAUDIO_LOSSLESS) ||
432 COMPARE_GUID(XMAUDIO2) )
433 {
434 #ifdef HAVE_GSTREAMER
435 if (FAudio_GSTREAMER_init(*ppSourceVoice, fmtex->SubFormat.Data1) != 0)
451 COMPARE_GUID(WMAUDIO_LOSSLESS) )
452 {
453 #ifdef HAVE_WMADEC
454 if (FAudio_WMADEC_init(*ppSourceVoice, fmtex->SubFormat.Data1) != 0)
436455 {
437456 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
438457 }
439458 #else
440459 FAudio_assert(0 && "xWMA is not supported!");
441460 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
442 #endif /* HAVE_GSTREAMER */
461 #endif /* HAVE_WMADEC */
443462 }
444463 else
445464 {
446465 FAudio_assert(0 && "Unsupported WAVEFORMATEXTENSIBLE subtype!");
447466 }
448467 #undef COMPARE_GUID
468 }
469 else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
470 {
471 #ifdef HAVE_WMADEC
472 if (FAudio_WMADEC_init(*ppSourceVoice, FAUDIO_FORMAT_XMAUDIO2) != 0)
473 {
474 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
475 }
476 #else
477 FAudio_assert(0 && "XMA2 is not supported!");
478 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
479 #endif /* HAVE_WMADEC */
449480 }
450481 else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
451482 {
22072238 voice->audio->pFree(voice->src.format);
22082239 LOG_MUTEX_DESTROY(voice->audio, voice->src.bufferLock)
22092240 FAudio_PlatformDestroyMutex(voice->src.bufferLock);
2210 #ifdef HAVE_GSTREAMER
2211 if (voice->src.gstreamer)
2212 {
2213 FAudio_GSTREAMER_free(voice);
2214 }
2215 #endif /* HAVE_GSTREAMER */
2241 #ifdef HAVE_WMADEC
2242 if (voice->src.wmadec)
2243 {
2244 FAudio_WMADEC_free(voice);
2245 }
2246 #endif /* HAVE_WMADEC */
22162247 }
22172248 else if (voice->type == FAUDIO_VOICE_SUBMIX)
22182249 {
24202451 )
24212452
24222453 FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2423 #ifdef HAVE_GSTREAMER
2424 FAudio_assert( (voice->src.gstreamer != NULL && pBufferWMA != NULL) ||
2425 (voice->src.gstreamer == NULL && pBufferWMA == NULL) );
2426 #endif /* HAVE_GSTREAMER */
2454 #ifdef HAVE_WMADEC
2455 FAudio_assert( (voice->src.wmadec != NULL && (pBufferWMA != NULL || voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)) ||
2456 (voice->src.wmadec == NULL && (pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)) );
2457 #endif /* HAVE_WMADEC */
24272458
24282459 /* Start off with whatever they just sent us... */
24292460 playBegin = pBuffer->PlayBegin;
24502481 fmtex->wSamplesPerBlock
24512482 ) - playBegin;
24522483 }
2484 else if (voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
2485 {
2486 FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat*) voice->src.format;
2487 playLength = fmtex->dwSamplesEncoded - playBegin;
2488 }
24532489 else if (pBufferWMA != NULL)
24542490 {
24552491 playLength = (
24662502 }
24672503 }
24682504
2469 if (pBuffer->LoopCount > 0 && pBufferWMA == NULL)
2505 if (pBuffer->LoopCount > 0 && pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)
24702506 {
24712507 /* "The value of LoopBegin must be less than PlayBegin + PlayLength" */
24722508 if (loopBegin >= (playBegin + playLength))
25082544 pBuffer->AudioBytes / voice->src.format->nBlockAlign
25092545 ) * voice->src.format->nBlockAlign;
25102546 }
2511 else if (pBufferWMA != NULL)
2547 else if (pBufferWMA != NULL || voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
25122548 {
25132549 /* WMA only supports looping the whole buffer */
25142550 loopBegin = 0;
3232 #include <gst/audio/audio.h>
3333 #include <gst/app/gstappsink.h>
3434
35 typedef struct FAudioGSTREAMER
35 typedef struct FAudioWMADEC
3636 {
3737 GstPad *srcpad;
3838 GstElement *pipeline;
4343 size_t convertCacheLen, prevConvertCacheLen;
4444 uint32_t curBlock, prevBlock;
4545 size_t *blockSizes;
46 } FAudioGSTREAMER;
46 uint32_t blockAlign;
47 uint32_t blockCount;
48 size_t maxBytes;
49 } FAudioWMADEC;
4750
4851 #define SIZE_FROM_DST(sample) \
4952 ((sample) * voice->src.format->nChannels * sizeof(float))
6063 #define SIZE_DST_TO_SRC(len) \
6164 ((len) / (sizeof(float) / (voice->src.format->wBitsPerSample / 8)))
6265
63 static int FAudio_GSTREAMER_RestartDecoder(FAudioGSTREAMER *gstreamer)
66 static int FAudio_GSTREAMER_RestartDecoder(FAudioWMADEC *wmadec)
6467 {
6568 GstEvent *event;
6669
6770 event = gst_event_new_flush_start();
68 gst_pad_push_event(gstreamer->srcpad, event);
71 gst_pad_push_event(wmadec->srcpad, event);
6972
7073 event = gst_event_new_flush_stop(TRUE);
71 gst_pad_push_event(gstreamer->srcpad, event);
72
73 event = gst_event_new_segment(&gstreamer->segment);
74 gst_pad_push_event(gstreamer->srcpad, event);
75
76 gstreamer->curBlock = ~0u;
77 gstreamer->prevBlock = ~0u;
78
79 if (gst_element_set_state(gstreamer->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
74 gst_pad_push_event(wmadec->srcpad, event);
75
76 event = gst_event_new_segment(&wmadec->segment);
77 gst_pad_push_event(wmadec->srcpad, event);
78
79 wmadec->curBlock = ~0u;
80 wmadec->prevBlock = ~0u;
81
82 if (gst_element_set_state(wmadec->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
8083 {
8184 return 0;
8285 }
9295 gchar *debug_info = NULL;
9396 int ret = 0;
9497
95 bus = gst_pipeline_get_bus(GST_PIPELINE(voice->src.gstreamer->pipeline));
98 bus = gst_pipeline_get_bus(GST_PIPELINE(voice->src.wmadec->pipeline));
9699
97100 while((msg = gst_bus_pop_filtered(bus, GST_MESSAGE_ERROR)))
98101 {
126129 static size_t FAudio_GSTREAMER_FillConvertCache(
127130 FAudioVoice *voice,
128131 FAudioBuffer *buffer,
129 size_t maxBytes
132 size_t availableBytes
130133 ) {
131134 GstBuffer *src, *dst;
132135 GstSample *sample;
138141 LOG_FUNC_ENTER(voice->audio)
139142
140143 /* Write current block to input buffer, push to pipeline */
144
145 clipStartBytes = (
146 (size_t) voice->src.wmadec->blockAlign *
147 voice->src.wmadec->curBlock
148 );
149 toCopyBytes = voice->src.wmadec->blockAlign;
150 clipEndBytes = clipStartBytes + toCopyBytes;
151 if (buffer->AudioBytes < clipEndBytes)
152 {
153 /* XMA2 assets can ship with undersized ends.
154 * If your first instinct is to "pad with 0xFF, that's what game data trails with,"
155 * sorry to disappoint but it inserts extra silence in loops.
156 * Instead, push an undersized buffer and pray that the decoder handles it properly.
157 * At the time of writing, it's FFmpeg, and it's handling it well.
158 * For everything else, uh, let's assume the same. If you're reading this: sorry.
159 * -ade
160 */
161 toCopyBytes = buffer->AudioBytes - clipStartBytes;
162 }
163 clipStartBytes += (size_t) buffer->pAudioData;
164
141165 src = gst_buffer_new_allocate(
142166 NULL,
143 voice->src.format->nBlockAlign,
167 toCopyBytes,
144168 NULL
145169 );
146170
147171 if ( gst_buffer_fill(
148172 src,
149173 0,
150 buffer->pAudioData + (
151 voice->src.format->nBlockAlign *
152 voice->src.gstreamer->curBlock
153 ),
154 voice->src.format->nBlockAlign
155 ) != voice->src.format->nBlockAlign )
174 (gconstpointer*) clipStartBytes,
175 toCopyBytes
176 ) != toCopyBytes )
156177 {
157178 LOG_ERROR(
158179 voice->audio,
163184 return (size_t) -1;
164185 }
165186
166 push_ret = gst_pad_push(voice->src.gstreamer->srcpad, src);
187 push_ret = gst_pad_push(voice->src.wmadec->srcpad, src);
167188 if( push_ret != GST_FLOW_OK &&
168189 push_ret != GST_FLOW_EOS )
169190 {
181202 {
182203 /* Pull frames one into cache */
183204 sample = gst_app_sink_try_pull_sample(
184 GST_APP_SINK(voice->src.gstreamer->dst),
205 GST_APP_SINK(voice->src.wmadec->dst),
185206 0
186207 );
187208
206227 clipEndBytes = 0;
207228 }
208229
209 toCopyBytes = FAudio_min(info.size - (clipStartBytes + clipEndBytes), maxBytes - pulled);
210
211 if (voice->src.gstreamer->convertCacheLen < pulled + toCopyBytes)
212 {
213 voice->src.gstreamer->convertCacheLen = pulled + toCopyBytes;
214 voice->src.gstreamer->convertCache = (uint8_t*) voice->audio->pRealloc(
215 voice->src.gstreamer->convertCache,
230 toCopyBytes = FAudio_min(info.size - (clipStartBytes + clipEndBytes), availableBytes - pulled);
231
232 if (voice->src.wmadec->convertCacheLen < pulled + toCopyBytes)
233 {
234 voice->src.wmadec->convertCacheLen = pulled + toCopyBytes;
235 voice->src.wmadec->convertCache = (uint8_t*) voice->audio->pRealloc(
236 voice->src.wmadec->convertCache,
216237 pulled + toCopyBytes
217238 );
218239 }
219240
220241
221 FAudio_memcpy(voice->src.gstreamer->convertCache + pulled,
242 FAudio_memcpy(voice->src.wmadec->convertCache + pulled,
222243 info.data + clipStartBytes,
223244 toCopyBytes
224245 );
234255 return pulled;
235256 }
236257
237 static int FAudio_GSTREAMER_DecodeBlock(FAudioVoice *voice, FAudioBuffer *buffer, uint32_t block, size_t maxBytes)
258 static int FAudio_WMADEC_DecodeBlock(FAudioVoice *voice, FAudioBuffer *buffer, uint32_t block, size_t availableBytes)
238259 {
239 FAudioGSTREAMER *gstreamer = voice->src.gstreamer;
260 FAudioWMADEC *wmadec = voice->src.wmadec;
240261 uint8_t *tmpBuf;
241262 size_t tmpLen;
242263
243 if (gstreamer->curBlock != ~0u && block != gstreamer->curBlock + 1)
264 if (wmadec->curBlock != ~0u && block != wmadec->curBlock + 1)
244265 {
245266 /* XAudio2 allows looping back to start of XMA buffers, but nothing else */
246267 if (block != 0)
250271 "for voice %p, out of sequence block: %u (cur: %d)\n",
251272 voice,
252273 block,
253 gstreamer->curBlock
274 wmadec->curBlock
254275 );
255276 }
256277 FAudio_assert(block == 0);
257 if (!FAudio_GSTREAMER_RestartDecoder(gstreamer))
278 if (!FAudio_GSTREAMER_RestartDecoder(wmadec))
258279 {
259280 LOG_WARNING(
260281 voice->audio,
265286 }
266287
267288 /* store previous block to allow for minor rewinds (FAudio quirk) */
268 tmpBuf = gstreamer->prevConvertCache;
269 tmpLen = gstreamer->prevConvertCacheLen;
270 gstreamer->prevConvertCache = gstreamer->convertCache;
271 gstreamer->prevConvertCacheLen = gstreamer->convertCacheLen;
272 gstreamer->convertCache = tmpBuf;
273 gstreamer->convertCacheLen = tmpLen;
274
275 gstreamer->prevBlock = gstreamer->curBlock;
276 gstreamer->curBlock = block;
277
278 gstreamer->blockSizes[block] = FAudio_GSTREAMER_FillConvertCache(
289 tmpBuf = wmadec->prevConvertCache;
290 tmpLen = wmadec->prevConvertCacheLen;
291 wmadec->prevConvertCache = wmadec->convertCache;
292 wmadec->prevConvertCacheLen = wmadec->convertCacheLen;
293 wmadec->convertCache = tmpBuf;
294 wmadec->convertCacheLen = tmpLen;
295
296 wmadec->prevBlock = wmadec->curBlock;
297 wmadec->curBlock = block;
298
299 wmadec->blockSizes[block] = FAudio_GSTREAMER_FillConvertCache(
279300 voice,
280301 buffer,
281 maxBytes
302 availableBytes
282303 );
283304
284 return gstreamer->blockSizes[block] != (size_t) -1;
305 return wmadec->blockSizes[block] != (size_t) -1;
285306 }
286307
287308 static void FAudio_INTERNAL_DecodeGSTREAMER(
290311 float *decodeCache,
291312 uint32_t samples
292313 ) {
293 size_t byteOffset, siz, maxBytes;
314 size_t byteOffset, siz, availableBytes, blockCount, maxBytes;
294315 uint32_t curBlock, curBufferOffset;
295316 uint8_t *convertCache;
296317 int error = 0;
297 FAudioGSTREAMER *gstreamer = voice->src.gstreamer;
318 FAudioWMADEC *wmadec = voice->src.wmadec;
319
320 if (wmadec->blockCount != 0)
321 {
322 blockCount = wmadec->blockCount;
323 maxBytes = wmadec->maxBytes;
324 }
325 else
326 {
327 blockCount = voice->src.bufferList->bufferWMA.PacketCount;
328 maxBytes = voice->src.bufferList->bufferWMA.pDecodedPacketCumulativeBytes[blockCount - 1];
329 }
298330
299331 LOG_FUNC_ENTER(voice->audio)
300332
301 if (!gstreamer->blockSizes)
302 {
303 size_t sz = voice->src.bufferList->bufferWMA.PacketCount * sizeof(*gstreamer->blockSizes);
304 gstreamer->blockSizes = (size_t *) voice->audio->pMalloc(sz);
305 memset(gstreamer->blockSizes, 0xff, sz);
333 if (!wmadec->blockSizes)
334 {
335 size_t sz = blockCount * sizeof(*wmadec->blockSizes);
336 wmadec->blockSizes = (size_t *) voice->audio->pMalloc(sz);
337 memset(wmadec->blockSizes, 0xff, sz);
306338 }
307339
308340 curBufferOffset = voice->src.curBufferOffset;
310342 byteOffset = SIZE_FROM_DST(curBufferOffset);
311343
312344 /* the last block size can truncate the length of the buffer */
313 maxBytes = SIZE_SRC_TO_DST(voice->src.bufferList->bufferWMA.pDecodedPacketCumulativeBytes[voice->src.bufferList->bufferWMA.PacketCount - 1]);
314 for (curBlock = 0; curBlock < voice->src.bufferList->bufferWMA.PacketCount; curBlock += 1)
345 availableBytes = SIZE_SRC_TO_DST(maxBytes);
346 for (curBlock = 0; curBlock < blockCount; curBlock += 1)
315347 {
316348 /* decode to get real size */
317 if (gstreamer->blockSizes[curBlock] == (size_t)-1)
318 {
319 if (!FAudio_GSTREAMER_DecodeBlock(voice, buffer, curBlock, maxBytes))
349 if (wmadec->blockSizes[curBlock] == (size_t)-1)
350 {
351 if (!FAudio_WMADEC_DecodeBlock(voice, buffer, curBlock, availableBytes))
320352 {
321353 error = 1;
322354 goto done;
323355 }
324356 }
325357
326 if (gstreamer->blockSizes[curBlock] > byteOffset)
358 if (wmadec->blockSizes[curBlock] > byteOffset)
327359 {
328360 /* ensure curBlock is decoded in either slot */
329 if (gstreamer->curBlock != curBlock && gstreamer->prevBlock != curBlock)
361 if (wmadec->curBlock != curBlock && wmadec->prevBlock != curBlock)
330362 {
331 if (!FAudio_GSTREAMER_DecodeBlock(voice, buffer, curBlock, maxBytes))
363 if (!FAudio_WMADEC_DecodeBlock(voice, buffer, curBlock, availableBytes))
332364 {
333365 error = 1;
334366 goto done;
337369 break;
338370 }
339371
340 byteOffset -= gstreamer->blockSizes[curBlock];
341 maxBytes -= gstreamer->blockSizes[curBlock];
342 if(maxBytes == 0)
372 byteOffset -= wmadec->blockSizes[curBlock];
373 availableBytes -= wmadec->blockSizes[curBlock];
374 if (availableBytes == 0)
343375 break;
344376 }
345377
346 if (curBlock >= voice->src.bufferList->bufferWMA.PacketCount || maxBytes == 0)
378 if (curBlock >= blockCount || availableBytes == 0)
347379 {
348380 goto done;
349381 }
350382
351383 /* If we're in a different block from last time, decode! */
352 if (curBlock == gstreamer->curBlock)
353 {
354 convertCache = gstreamer->convertCache;
355 }
356 else if (curBlock == gstreamer->prevBlock)
357 {
358 convertCache = gstreamer->prevConvertCache;
384 if (curBlock == wmadec->curBlock)
385 {
386 convertCache = wmadec->convertCache;
387 }
388 else if (curBlock == wmadec->prevBlock)
389 {
390 convertCache = wmadec->prevConvertCache;
359391 }
360392 else
361393 {
364396 }
365397
366398 /* Copy to decodeCache, finally. */
367 siz = FAudio_min(SIZE_FROM_DST(samples), gstreamer->blockSizes[curBlock] - byteOffset);
399 siz = FAudio_min(SIZE_FROM_DST(samples), wmadec->blockSizes[curBlock] - byteOffset);
368400 if (convertCache)
369401 {
370402 FAudio_memcpy(
402434 /* If the cache isn't filled yet, keep decoding! */
403435 if (samples > 0)
404436 {
405 if ( !error &&
406 curBlock < voice->src.bufferList->bufferWMA.PacketCount - 1 )
437 if (!error && curBlock < blockCount - 1)
407438 {
408439 goto decode;
409440 }
415446 LOG_FUNC_EXIT(voice->audio)
416447 }
417448
418 void FAudio_GSTREAMER_end_buffer(FAudioSourceVoice *pSourceVoice)
449 void FAudio_WMADEC_end_buffer(FAudioSourceVoice *pSourceVoice)
419450 {
420 FAudioGSTREAMER *gstreamer = pSourceVoice->src.gstreamer;
451 FAudioWMADEC *wmadec = pSourceVoice->src.wmadec;
421452
422453 LOG_FUNC_ENTER(pSourceVoice->audio)
423454
424 pSourceVoice->audio->pFree(gstreamer->blockSizes);
425 gstreamer->blockSizes = NULL;
426
427 gstreamer->curBlock = ~0u;
428 gstreamer->prevBlock = ~0u;
455 pSourceVoice->audio->pFree(wmadec->blockSizes);
456 wmadec->blockSizes = NULL;
457
458 wmadec->curBlock = ~0u;
459 wmadec->prevBlock = ~0u;
429460
430461 LOG_FUNC_EXIT(pSourceVoice->audio)
431462 }
432463
433 static void FAudio_GSTREAMER_NewDecodebinPad(GstElement *decodebin,
464 static void FAudio_WMADEC_NewDecodebinPad(GstElement *decodebin,
434465 GstPad *pad, gpointer user)
435466 {
436 FAudioGSTREAMER *gstreamer = user;
467 FAudioWMADEC *wmadec = user;
437468 GstPad *ac_sink;
438469
439 ac_sink = gst_element_get_static_pad(gstreamer->resampler, "sink");
470 ac_sink = gst_element_get_static_pad(wmadec->resampler, "sink");
440471 if (GST_PAD_IS_LINKED(ac_sink))
441472 {
442473 gst_object_unref(ac_sink);
448479 gst_object_unref(ac_sink);
449480 }
450481
451 uint32_t FAudio_GSTREAMER_init(FAudioSourceVoice *pSourceVoice, uint32_t type)
482 /* XMA2 doesn't have a recognized mimetype and so far only libav's avdec_xma2 exists for XMA2.
483 * Things can change and as of writing this patch, things are actively changing.
484 * As for this being a possible leak: using gstreamer is a static endeavour elsewhere already~
485 * -ade
486 */
487 /* #define FAUDIO_GST_LIBAV_EXPOSES_XMA2_CAPS_IN_CURRENT_YEAR */
488 #ifndef FAUDIO_GST_LIBAV_EXPOSES_XMA2_CAPS_IN_CURRENT_YEAR
489 static const char *FAudio_WMADEC_XMA2_Mimetype = NULL;
490 static GstElementFactory *FAudio_WMADEC_XMA2_DecoderFactory = NULL;
491 static GValueArray *FAudio_WMADEC_XMA2_DecodebinAutoplugFactories(
492 GstElement *decodebin,
493 GstPad *pad,
494 GstCaps *caps,
495 gpointer user
496 ) {
497 FAudio *audio = user;
498 GValueArray *result;
499 gchar *capsText;
500
501 if (!gst_structure_has_name(gst_caps_get_structure(caps, 0), FAudio_WMADEC_XMA2_Mimetype))
502 {
503 capsText = gst_caps_to_string(caps);
504 LOG_ERROR(
505 audio,
506 "Expected %s (XMA2) caps not %s",
507 FAudio_WMADEC_XMA2_Mimetype,
508 capsText
509 )
510 g_free(capsText);
511 /*
512 * From the docs:
513 *
514 * If this function returns NULL, @pad will be exposed as a final caps.
515 *
516 * If this function returns an empty array, the pad will be considered as
517 * having an unhandled type media type.
518 */
519 return g_value_array_new(0);
520 }
521
522 result = g_value_array_new(1);
523 GValue val = G_VALUE_INIT;
524 g_value_init(&val, G_TYPE_OBJECT);
525 g_value_set_object(&val, FAudio_WMADEC_XMA2_DecoderFactory);
526 g_value_array_append(result, &val);
527 g_value_unset(&val);
528 return result;
529 }
530 #endif
531
532
533 uint32_t FAudio_WMADEC_init(FAudioSourceVoice *pSourceVoice, uint32_t type)
452534 {
453 FAudioGSTREAMER *result;
535 FAudioWMADEC *result;
454536 GstElement *decoder = NULL, *converter = NULL;
537 const GList *tmpls;
538 GstStaticPadTemplate *tmpl;
455539 GstCaps *caps;
540 const char *mimetype;
541 const char *versiontype;
456542 int version;
457543 GstBuffer *codec_data;
458544 size_t codec_data_size;
472558 gst_init(NULL, NULL);
473559 }
474560
561 #ifndef FAUDIO_GST_LIBAV_EXPOSES_XMA2_CAPS_IN_CURRENT_YEAR
562 if (type == FAUDIO_FORMAT_XMAUDIO2 && !FAudio_WMADEC_XMA2_Mimetype)
563 {
564 /* Old versions ship with unknown/unknown as the sink caps mimetype.
565 * A patch has been submitted (and merged!) to expose avdec_xma2 as audio/x-xma with xmaversion 2:
566 * https://gitlab.freedesktop.org/gstreamer/gst-libav/-/merge_requests/124
567 * For now, try to find avdec_xma2 if it's found, match the mimetype on the fly.
568 * This should also take future alternative XMA2 decoders into account if avdec_xma2 is missing.
569 * -ade
570 */
571 FAudio_WMADEC_XMA2_Mimetype = "audio/x-xma";
572 FAudio_WMADEC_XMA2_DecoderFactory = gst_element_factory_find("avdec_xma2");
573 if (FAudio_WMADEC_XMA2_DecoderFactory)
574 {
575 for (tmpls = gst_element_factory_get_static_pad_templates(FAudio_WMADEC_XMA2_DecoderFactory); tmpls; tmpls = tmpls->next)
576 {
577 tmpl = (GstStaticPadTemplate*) tmpls->data;
578 if (tmpl->direction == GST_PAD_SINK)
579 {
580 caps = gst_static_pad_template_get_caps(tmpl);
581 FAudio_WMADEC_XMA2_Mimetype = gst_structure_get_name(gst_caps_get_structure(caps, 0));
582 gst_caps_unref(caps);
583 break;
584 }
585 }
586 }
587 }
588 #endif
589
475590 /* Match the format with the codec */
476591 switch (type)
477592 {
478 #define GSTTYPE(fmt, ver) \
479 case FAUDIO_FORMAT_##fmt: version = ver; break;
480 GSTTYPE(WMAUDIO2, 2)
481 GSTTYPE(WMAUDIO3, 3)
482 GSTTYPE(WMAUDIO_LOSSLESS, 4)
483 /* FIXME: XMA2 */
593 #define GSTTYPE(fmt, mt, vt, ver) \
594 case FAUDIO_FORMAT_##fmt: mimetype = mt; versiontype = vt; version = ver; break;
595 GSTTYPE(WMAUDIO2, "audio/x-wma", "wmaversion", 2)
596 GSTTYPE(WMAUDIO3, "audio/x-wma", "wmaversion", 3)
597 GSTTYPE(WMAUDIO_LOSSLESS, "audio/x-wma", "wmaversion", 4)
598 #ifndef FAUDIO_GST_LIBAV_EXPOSES_XMA2_CAPS_IN_CURRENT_YEAR
599 GSTTYPE(XMAUDIO2, FAudio_WMADEC_XMA2_Mimetype, "xmaversion", 2)
600 #else
601 GSTTYPE(XMAUDIO2, "audio/x-xma", "xmaversion", 2)
602 #endif
484603 #undef GSTTYPE
485604 default:
486605 LOG_ERROR(
496615 * Note that we're not assigning names, since many pipelines will exist
497616 * at the same time (one per source voice).
498617 */
499 result = (FAudioGSTREAMER*) pSourceVoice->audio->pMalloc(sizeof(FAudioGSTREAMER));
500 FAudio_zero(result, sizeof(FAudioGSTREAMER));
618 result = (FAudioWMADEC*) pSourceVoice->audio->pMalloc(sizeof(FAudioWMADEC));
619 FAudio_zero(result, sizeof(FAudioWMADEC));
501620
502621 result->pipeline = gst_pipeline_new(NULL);
503622
512631 goto free_without_bin;
513632 }
514633
515 g_signal_connect(decoder, "pad-added", G_CALLBACK(FAudio_GSTREAMER_NewDecodebinPad), result);
634 #ifndef FAUDIO_GST_LIBAV_EXPOSES_XMA2_CAPS_IN_CURRENT_YEAR
635 if (type == FAUDIO_FORMAT_XMAUDIO2 && FAudio_WMADEC_XMA2_DecoderFactory)
636 {
637 g_signal_connect(decoder, "autoplug-factories", G_CALLBACK(FAudio_WMADEC_XMA2_DecodebinAutoplugFactories), pSourceVoice->audio);
638 }
639 #endif
640 g_signal_connect(decoder, "pad-added", G_CALLBACK(FAudio_WMADEC_NewDecodebinPad), result);
516641
517642 result->srcpad = gst_pad_new(NULL, GST_PAD_SRC);
518643
606731 gst_pad_push_event(result->srcpad, event);
607732
608733 /* Prepare the input format */
734 result->blockAlign = (uint32_t) pSourceVoice->src.format->nBlockAlign;
609735 if (type == FAUDIO_FORMAT_WMAUDIO3)
610736 {
611737 const FAudioWaveFormatExtensible *wfx =
612738 (FAudioWaveFormatExtensible*) pSourceVoice->src.format;
613739 extradata = (uint8_t*) &wfx->Samples;
614740 codec_data_size = pSourceVoice->src.format->cbSize;
741 /* bufferList (and thus bufferWMA) can't be accessed yet. */
615742 }
616743 else if (type == FAUDIO_FORMAT_WMAUDIO2)
617744 {
620747
621748 extradata = fakeextradata;
622749 codec_data_size = sizeof(fakeextradata);
750 /* bufferList (and thus bufferWMA) can't be accessed yet. */
751 }
752 else if (type == FAUDIO_FORMAT_XMAUDIO2)
753 {
754 const FAudioXMA2WaveFormat *wfx =
755 (FAudioXMA2WaveFormat*) pSourceVoice->src.format;
756 extradata = (uint8_t*) &wfx->wNumStreams;
757 codec_data_size = pSourceVoice->src.format->cbSize;
758 /* XMA2 block size >= 16-bit limit and it doesn't come with bufferWMA. */
759 result->blockAlign = wfx->dwBytesPerBlock;
760 result->blockCount = wfx->wBlockCount;
761 result->maxBytes = (
762 (size_t) wfx->dwSamplesEncoded *
763 pSourceVoice->src.format->nChannels *
764 (pSourceVoice->src.format->wBitsPerSample / 8)
765 );
623766 }
624767 else
625768 {
626769 extradata = NULL;
770 codec_data_size = 0;
627771 FAudio_assert(0 && "Unrecognized WMA format!");
628772 }
629773 codec_data = gst_buffer_new_and_alloc(codec_data_size);
630774 gst_buffer_fill(codec_data, 0, extradata, codec_data_size);
631775 caps = gst_caps_new_simple(
632 "audio/x-wma",
633 "wmaversion", G_TYPE_INT, version,
776 mimetype,
777 versiontype, G_TYPE_INT, version,
634778 "bitrate", G_TYPE_INT, pSourceVoice->src.format->nAvgBytesPerSec * 8,
635779 "channels", G_TYPE_INT, pSourceVoice->src.format->nChannels,
636780 "rate", G_TYPE_INT, pSourceVoice->src.format->nSamplesPerSec,
637 "block_align", G_TYPE_INT, pSourceVoice->src.format->nBlockAlign,
781 "block_align", G_TYPE_INT, result->blockAlign,
638782 "depth", G_TYPE_INT, pSourceVoice->src.format->wBitsPerSample,
639783 "codec_data", GST_TYPE_BUFFER, codec_data,
640784 NULL
669813 goto free_with_bin;
670814 }
671815
672 pSourceVoice->src.gstreamer = result;
816 pSourceVoice->src.wmadec = result;
673817 pSourceVoice->src.decode = FAudio_INTERNAL_DecodeGSTREAMER;
674818
675819 if (FAudio_GSTREAMER_CheckForBusErrors(pSourceVoice))
680824 "Got a bus error after creation!"
681825 )
682826
683 pSourceVoice->src.gstreamer = NULL;
827 pSourceVoice->src.wmadec = NULL;
684828 pSourceVoice->src.decode = NULL;
685829
686830 goto free_with_bin;
726870 return FAUDIO_E_UNSUPPORTED_FORMAT;
727871 }
728872
729 void FAudio_GSTREAMER_free(FAudioSourceVoice *voice)
873 void FAudio_WMADEC_free(FAudioSourceVoice *voice)
730874 {
731875 LOG_FUNC_ENTER(voice->audio)
732 gst_element_set_state(voice->src.gstreamer->pipeline, GST_STATE_NULL);
733 gst_object_unref(voice->src.gstreamer->pipeline);
734 gst_object_unref(voice->src.gstreamer->srcpad);
735 voice->audio->pFree(voice->src.gstreamer->convertCache);
736 voice->audio->pFree(voice->src.gstreamer->prevConvertCache);
737 voice->audio->pFree(voice->src.gstreamer->blockSizes);
738 voice->audio->pFree(voice->src.gstreamer);
739 voice->src.gstreamer = NULL;
876 gst_element_set_state(voice->src.wmadec->pipeline, GST_STATE_NULL);
877 gst_object_unref(voice->src.wmadec->pipeline);
878 gst_object_unref(voice->src.wmadec->srcpad);
879 voice->audio->pFree(voice->src.wmadec->convertCache);
880 voice->audio->pFree(voice->src.wmadec->prevConvertCache);
881 voice->audio->pFree(voice->src.wmadec->blockSizes);
882 voice->audio->pFree(voice->src.wmadec);
883 voice->src.wmadec = NULL;
740884 LOG_FUNC_EXIT(voice->audio)
741885 }
742886
304304
305305 LOG_FUNC_ENTER(voice->audio)
306306
307 #ifdef HAVE_GSTREAMER
308 if (voice->src.gstreamer != NULL)
307 #ifdef HAVE_WMADEC
308 if (voice->src.wmadec != NULL)
309309 {
310310 /* Always 0, per the spec */
311311 LOG_FUNC_EXIT(voice->audio)
312312 return 0;
313313 }
314 #endif /* HAVE_GSTREAMER */
314 #endif /* HAVE_WMADEC */
315315 while (list != NULL && decoding > 0)
316316 {
317317 buffer = &list->buffer;
454454 }
455455 else
456456 {
457 #ifdef HAVE_GSTREAMER
458 if (voice->src.gstreamer != NULL)
457 #ifdef HAVE_WMADEC
458 if (voice->src.wmadec != NULL)
459459 {
460 FAudio_GSTREAMER_end_buffer(voice);
460 FAudio_WMADEC_end_buffer(voice);
461461 }
462 #endif /* HAVE_GSTREAMER */
462 #endif /* HAVE_WMADEC */
463463 /* For EOS we can stop storing fraction offsets */
464464 if (buffer->Flags & FAUDIO_END_OF_STREAM)
465465 {
998998
999999 sendwork:
10001000
1001 /* Nowhere to send it? Just skip the rest...*/
1002 if (voice->sends.SendCount == 0)
1003 {
1004 FAudio_PlatformUnlockMutex(voice->sendLock);
1005 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1006 LOG_FUNC_EXIT(voice->audio)
1007 return;
1008 }
1009
10101001 /* Filters */
10111002 if (voice->flags & FAUDIO_VOICE_USEFILTER)
10121003 {
10491040 FAudio_PlatformUnlockMutex(voice->effectLock);
10501041 LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
10511042
1043 /* Nowhere to send it? Just skip the rest...*/
1044 if (voice->sends.SendCount == 0)
1045 {
1046 FAudio_PlatformUnlockMutex(voice->sendLock);
1047 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1048 LOG_FUNC_EXIT(voice->audio)
1049 return;
1050 }
1051
10521052 /* Send float cache to sends */
10531053 FAudio_PlatformLockMutex(voice->volumeLock);
10541054 LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
11091109 FAudio_PlatformLockMutex(voice->sendLock);
11101110 LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
11111111
1112 /* Nothing to do? */
1113 if (voice->sends.SendCount == 0)
1114 {
1115 goto end;
1116 }
1117
11181112 /* Resample */
11191113 if (voice->mix.resampleStep == FIXED_ONE)
11201114 {
11801174 }
11811175 FAudio_PlatformUnlockMutex(voice->effectLock);
11821176 LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1177
1178 /* Nothing more to do? */
1179 if (voice->sends.SendCount == 0)
1180 {
1181 goto end;
1182 }
11831183
11841184 /* Send float cache to sends */
11851185 FAudio_PlatformLockMutex(voice->volumeLock);
2727 #include "FAPOBase.h"
2828 #include <stdarg.h>
2929
30 #ifdef FAUDIO_UNKNOWN_PLATFORM
30 #ifdef FAUDIO_WIN32_PLATFORM
3131 #include <stdio.h>
3232 #include <stdlib.h>
3333 #include <string.h>
3535 #include <assert.h>
3636 #include <inttypes.h>
3737
38 #include <windef.h>
39 #include <winbase.h>
40
3841 #define FAudio_malloc malloc
3942 #define FAudio_realloc realloc
4043 #define FAudio_free free
41 #define FAudio_alloca(x) alloca(uint8_t, x)
42 #define FAudio_dealloca(x) dealloca(x)
44 #define FAudio_alloca(x) alloca(x)
45 #define FAudio_dealloca(x) (void)(x)
4346 #define FAudio_zero(ptr, size) memset(ptr, '\0', size)
4447 #define FAudio_memset(ptr, val, size) memset(ptr, val, size)
4548 #define FAudio_memcpy(dst, src, size) memcpy(dst, src, size)
4851
4952 #define FAudio_strlen(ptr) strlen(ptr)
5053 #define FAudio_strcmp(str1, str2) strcmp(str1, str2)
51 #define FAudio_strlcpy(ptr1, ptr2, size) strlcpy(ptr1, ptr2, size)
54 #define FAudio_strlcpy(ptr1, ptr2, size) lstrcpynA(ptr1, ptr2, size)
5255
5356 #define FAudio_pow(x, y) pow(x, y)
5457 #define FAudio_log(x) log(x)
7578 #define FAudio_assert assert
7679 #define FAudio_snprintf snprintf
7780 #define FAudio_vsnprintf vsnprintf
78 #define FAudio_Log(msg) fprintf(stderr, "%s\n", msg)
7981 #define FAudio_getenv getenv
8082 #define FAudio_PRIu64 PRIu64
8183 #define FAudio_PRIx64 PRIx64
84
85 extern void FAudio_Log(char const *msg);
8286
8387 /* FIXME: Assuming little-endian! */
8488 #define FAudio_swap16LE(x) (x)
463467 uint64_t curBufferOffsetDec;
464468 uint32_t curBufferOffset;
465469
466 /* GStreamer */
467 #ifdef HAVE_GSTREAMER
468 struct FAudioGSTREAMER *gstreamer;
469 #endif /* HAVE_GSTREAMER*/
470 /* WMA decoding */
471 #ifdef HAVE_WMADEC
472 struct FAudioWMADEC *wmadec;
473 #endif /* HAVE_WMADEC*/
470474
471475 /* Read-only */
472476 float maxFreqRatio;
731735 DECODE_FUNC(WMAERROR)
732736 #undef DECODE_FUNC
733737
734 /* GStreamer */
735
736 #ifdef HAVE_GSTREAMER
737 uint32_t FAudio_GSTREAMER_init(FAudioSourceVoice *pSourceVoice, uint32_t type);
738 void FAudio_GSTREAMER_free(FAudioSourceVoice *voice);
739 void FAudio_GSTREAMER_end_buffer(FAudioSourceVoice *voice);
740 #endif /* HAVE_GSTREAMER */
738 /* WMA decoding */
739
740 #ifdef HAVE_WMADEC
741 uint32_t FAudio_WMADEC_init(FAudioSourceVoice *pSourceVoice, uint32_t type);
742 void FAudio_WMADEC_free(FAudioSourceVoice *voice);
743 void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice);
744 #endif /* HAVE_WMADEC */
741745
742746 /* Platform Functions */
743747
2323 *
2424 */
2525
26 #ifndef FAUDIO_WIN32_PLATFORM
27
2628 #include "FAudio_internal.h"
2729
2830 #include <SDL.h>
220222 ) {
221223 const char *name, *envvar;
222224 int channels, rate;
225 SDL_AudioSpec spec;
226 uint32_t devcount, i;
223227
224228 FAudio_zero(details, sizeof(FAudioDeviceDetails));
225 if (index >= FAudio_PlatformGetDeviceCount())
229
230 devcount = FAudio_PlatformGetDeviceCount();
231 if (index >= devcount)
226232 {
227233 return FAUDIO_E_INVALID_CALL;
228234 }
257263 sizeof(details->DisplayName)
258264 );
259265
260 /* TODO: SDL_GetAudioDeviceSpec! */
266 /* Environment variables take precedence over all possible values */
261267 envvar = SDL_getenv("SDL_AUDIO_FREQUENCY");
262 if (!envvar || ((rate = SDL_atoi(envvar)) == 0))
268 if (envvar != NULL)
269 {
270 rate = SDL_atoi(envvar);
271 }
272 else
273 {
274 rate = 0;
275 }
276 envvar = SDL_getenv("SDL_AUDIO_CHANNELS");
277 if (envvar != NULL)
278 {
279 channels = SDL_atoi(envvar);
280 }
281 else
282 {
283 channels = 0;
284 }
285
286 #if SDL_VERSION_ATLEAST(2, 0, 15)
287 if (index == 0)
288 {
289 /* Okay, so go grab something from the liquor cabinet and get
290 * ready, because this loop is a bit of a trip:
291 *
292 * We can't get the spec for the default device, because in
293 * audio land a "default device" is a completely foreign idea,
294 * some APIs support it but in reality you just have to pass
295 * NULL as a driver string and the sound server figures out the
296 * rest. In some psychotic universe the device can even be a
297 * network address. No, seriously.
298 *
299 * So what do we do? Well, at least in my experience shipping
300 * for the PC, the easiest thing to do is assume that the
301 * highest spec in the list is what you should target, even if
302 * it turns out that's not the default at the time you create
303 * your device.
304 *
305 * Consider a laptop that has built-in stereo speakers, but is
306 * connected to a home theater system with 5.1 audio. It may be
307 * the case that the stereo audio is active, but the user may
308 * at some point move audio to 5.1, at which point the server
309 * will simply move the endpoint from underneath us and move our
310 * output stream to the new device. At that point, you _really_
311 * want to already be pushing out 5.1, because if not the user
312 * will be stuck recreating the whole program, which on many
313 * platforms is an instant cert failure. The tradeoff is that
314 * you're potentially downmixing a 5.1 stream to stereo, which
315 * is a bit wasteful, but presumably the hardware can handle it
316 * if they were able to use a 5.1 system to begin with.
317 *
318 * So, we just aim for the highest channel count on the system.
319 * We also do this with sample rate to a lesser degree; we try
320 * to use a single device spec at a time, so it may be that
321 * the sample rate you get isn't the highest from the list if
322 * another device had a higher channel count.
323 *
324 * Lastly, if you set SDL_AUDIO_CHANNELS but not
325 * SDL_AUDIO_FREQUENCY, we don't bother checking for a sample
326 * rate, we fall through to the hardcoded value at the bottom of
327 * this function.
328 *
329 * I'm so tired.
330 *
331 * -flibit
332 */
333 if (channels <= 0)
334 {
335 const uint8_t setRate = (rate <= 0);
336 devcount -= 1; /* Subtracting the default index */
337 for (i = 0; i < devcount; i += 1)
338 {
339 SDL_GetAudioDeviceSpec(i, 0, &spec);
340 if (spec.channels > channels)
341 {
342 channels = spec.channels;
343 if (setRate)
344 {
345 /* May be 0! That's okay! */
346 rate = spec.freq;
347 }
348 }
349 }
350 }
351 }
352 else
353 {
354 SDL_GetAudioDeviceSpec(index - 1, 0, &spec);
355 if ((spec.freq > 0) && (rate <= 0))
356 {
357 rate = spec.freq;
358 }
359 if ((spec.channels > 0) && (channels <= 0))
360 {
361 channels = spec.channels;
362 }
363 }
364 #endif /* SDL >= 2.0.15 */
365
366 /* If we make it all the way here with no format, hardcode a sane one */
367 if (rate <= 0)
263368 {
264369 rate = 48000;
265370 }
266 envvar = SDL_getenv("SDL_AUDIO_CHANNELS");
267 if (!envvar || ((channels = SDL_atoi(envvar)) == 0))
371 if (channels <= 0)
268372 {
269373 channels = 2;
270374 }
375
376 /* Write the format, finally. */
271377 WriteWaveFormatExtensible(
272378 &details->OutputFormat,
273379 channels,
599705 }
600706
601707 /* vim: set noexpandtab shiftwidth=8 tabstop=8: */
708
709 #else
710
711 extern int this_tu_is_empty;
712
713 #endif /* FAUDIO_WIN32_PLATFORM */
0 /* FAudio - XAudio Reimplementation for FNA
1 *
2 * Copyright (c) 2011-2020 Ethan Lee, Luigi Auriemma, and the MonoGame Team
3 *
4 * This software is provided 'as-is', without any express or implied warranty.
5 * In no event will the authors be held liable for any damages arising from
6 * the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software in a
14 * product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 *
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 *
20 * 3. This notice may not be removed or altered from any source distribution.
21 *
22 * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
23 *
24 */
25
26 #ifdef FAUDIO_WIN32_PLATFORM
27
28 #include "FAudio_internal.h"
29
30 #include <stddef.h>
31
32 #define COBJMACROS
33 #include <windows.h>
34 #include <mfidl.h>
35 #include <mfapi.h>
36 #include <mferror.h>
37 #include <mfreadwrite.h>
38 #include <propvarutil.h>
39
40 #include <initguid.h>
41 #include <audioclient.h>
42 #include <mmdeviceapi.h>
43
44 DEFINE_GUID(CLSID_CWMADecMediaObject, 0x2eeb4adf, 0x4578, 0x4d10, 0xbc, 0xa7, 0xbb, 0x95, 0x5f, 0x56, 0x32, 0x0a);
45 DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2, FAUDIO_FORMAT_XMAUDIO2);
46
47 static CRITICAL_SECTION faudio_cs = { NULL, -1, 0, 0, 0, 0 };
48 static IMMDeviceEnumerator *device_enumerator;
49 static HRESULT init_hr;
50
51 struct FAudioWin32PlatformData
52 {
53 IAudioClient *client;
54 HANDLE audioThread;
55 HANDLE stopEvent;
56 };
57
58 struct FAudioAudioClientThreadArgs
59 {
60 WAVEFORMATEXTENSIBLE format;
61 IAudioClient *client;
62 HANDLE events[2];
63 FAudio *audio;
64 UINT updateSize;
65 };
66
67 void FAudio_Log(char const *msg)
68 {
69 OutputDebugStringA(msg);
70 }
71
72 static HRESULT FAudio_FillAudioClientBuffer(
73 struct FAudioAudioClientThreadArgs *args,
74 IAudioRenderClient *client,
75 UINT frames,
76 UINT padding
77 ) {
78 HRESULT hr = S_OK;
79 BYTE *buffer;
80
81 while (padding + args->updateSize <= frames)
82 {
83 hr = IAudioRenderClient_GetBuffer(
84 client,
85 frames - padding,
86 &buffer
87 );
88 if (FAILED(hr)) return hr;
89
90 FAudio_zero(
91 buffer,
92 args->updateSize * args->format.Format.nBlockAlign
93 );
94
95 if (args->audio->active)
96 {
97 FAudio_INTERNAL_UpdateEngine(
98 args->audio,
99 (float*) buffer
100 );
101 }
102
103 hr = IAudioRenderClient_ReleaseBuffer(
104 client,
105 args->updateSize,
106 0
107 );
108 if (FAILED(hr)) return hr;
109
110 padding += args->updateSize;
111 }
112
113 return hr;
114 }
115
116 static DWORD WINAPI FAudio_AudioClientThread(void *user)
117 {
118 struct FAudioAudioClientThreadArgs *args = user;
119 IAudioRenderClient *render_client;
120 HRESULT hr = S_OK;
121 UINT frames, padding = 0;
122
123 hr = IAudioClient_GetService(
124 args->client,
125 &IID_IAudioRenderClient,
126 (void **)&render_client
127 );
128 FAudio_assert(!FAILED(hr) && "Failed to get IAudioRenderClient service!");
129
130 hr = IAudioClient_GetBufferSize(args->client, &frames);
131 FAudio_assert(!FAILED(hr) && "Failed to get IAudioClient buffer size!");
132
133 hr = FAudio_FillAudioClientBuffer(args, render_client, frames, 0);
134 FAudio_assert(!FAILED(hr) && "Failed to initialize IAudioClient buffer!");
135
136 hr = IAudioClient_Start(args->client);
137 FAudio_assert(!FAILED(hr) && "Failed to start IAudioClient!");
138
139 while (WaitForMultipleObjects(2, args->events, FALSE, INFINITE) == WAIT_OBJECT_0)
140 {
141 hr = IAudioClient_GetCurrentPadding(args->client, &padding);
142 FAudio_assert(!FAILED(hr) && "Failed to get IAudioClient current padding!");
143
144 hr = FAudio_FillAudioClientBuffer(args, render_client, frames, padding);
145 FAudio_assert(!FAILED(hr) && "Failed to fill IAudioClient buffer!");
146 }
147
148 hr = IAudioClient_Stop(args->client);
149 FAudio_assert(!FAILED(hr) && "Failed to stop IAudioClient!");
150
151 IAudioRenderClient_Release(render_client);
152 FAudio_free(args);
153 return 0;
154 }
155
156 void FAudio_PlatformInit(
157 FAudio *audio,
158 uint32_t flags,
159 uint32_t deviceIndex,
160 FAudioWaveFormatExtensible *mixFormat,
161 uint32_t *updateSize,
162 void** platformDevice
163 ) {
164 struct FAudioAudioClientThreadArgs *args;
165 struct FAudioWin32PlatformData *data;
166 REFERENCE_TIME duration;
167 WAVEFORMATEX *closest;
168 IMMDevice *device = NULL;
169 HRESULT hr;
170 HANDLE audioEvent = NULL;
171 BOOL has_sse2 = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
172
173 FAudio_INTERNAL_InitSIMDFunctions(has_sse2, FALSE);
174
175 FAudio_PlatformAddRef();
176
177 *platformDevice = NULL;
178 if (deviceIndex > 0) return;
179
180 args = FAudio_malloc(sizeof(*args));
181 FAudio_assert(!!args && "Failed to allocate FAudio thread args!");
182
183 data = FAudio_malloc(sizeof(*data));
184 FAudio_assert(!!data && "Failed to allocate FAudio platform data!");
185 FAudio_zero(data, sizeof(*data));
186
187 args->format.Format.wFormatTag = mixFormat->Format.wFormatTag;
188 args->format.Format.nChannels = mixFormat->Format.nChannels;
189 args->format.Format.nSamplesPerSec = mixFormat->Format.nSamplesPerSec;
190 args->format.Format.nAvgBytesPerSec = mixFormat->Format.nAvgBytesPerSec;
191 args->format.Format.nBlockAlign = mixFormat->Format.nBlockAlign;
192 args->format.Format.wBitsPerSample = mixFormat->Format.wBitsPerSample;
193 args->format.Format.cbSize = mixFormat->Format.cbSize;
194
195 if (args->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
196 {
197 args->format.Samples.wValidBitsPerSample = mixFormat->Samples.wValidBitsPerSample;
198 args->format.dwChannelMask = mixFormat->dwChannelMask;
199 FAudio_memcpy(
200 &args->format.SubFormat,
201 &mixFormat->SubFormat,
202 sizeof(GUID)
203 );
204 }
205
206 audioEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
207 FAudio_assert(!!audioEvent && "Failed to create FAudio thread buffer event!");
208
209 data->stopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
210 FAudio_assert(!!data->stopEvent && "Failed to create FAudio thread stop event!");
211
212 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
213 device_enumerator,
214 eRender,
215 eConsole,
216 &device
217 );
218 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
219
220 hr = IMMDevice_Activate(
221 device,
222 &IID_IAudioClient,
223 CLSCTX_ALL,
224 NULL,
225 (void **)&data->client
226 );
227 FAudio_assert(!FAILED(hr) && "Failed to create audio client!");
228 IMMDevice_Release(device);
229
230 if (flags & FAUDIO_1024_QUANTUM) duration = 21330;
231 else duration = 30000;
232
233 hr = IAudioClient_IsFormatSupported(
234 data->client,
235 AUDCLNT_SHAREMODE_SHARED,
236 &args->format.Format,
237 &closest
238 );
239 FAudio_assert(!FAILED(hr) && "Failed to find supported audio format!");
240
241 if (closest)
242 {
243 if (closest->wFormatTag != WAVE_FORMAT_EXTENSIBLE) args->format.Format = *closest;
244 else args->format = *(WAVEFORMATEXTENSIBLE *)closest;
245 CoTaskMemFree(closest);
246 }
247
248 hr = IAudioClient_Initialize(
249 data->client,
250 AUDCLNT_SHAREMODE_SHARED,
251 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
252 duration,
253 0,
254 &args->format.Format,
255 &GUID_NULL
256 );
257 FAudio_assert(!FAILED(hr) && "Failed to initialize audio client!");
258
259 hr = IAudioClient_SetEventHandle(data->client, audioEvent);
260 FAudio_assert(!FAILED(hr) && "Failed to set audio client event!");
261
262 mixFormat->Format.wFormatTag = args->format.Format.wFormatTag;
263 mixFormat->Format.nChannels = args->format.Format.nChannels;
264 mixFormat->Format.nSamplesPerSec = args->format.Format.nSamplesPerSec;
265 mixFormat->Format.nAvgBytesPerSec = args->format.Format.nAvgBytesPerSec;
266 mixFormat->Format.nBlockAlign = args->format.Format.nBlockAlign;
267 mixFormat->Format.wBitsPerSample = args->format.Format.wBitsPerSample;
268
269 if (args->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
270 {
271 mixFormat->Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
272 mixFormat->Samples.wValidBitsPerSample = args->format.Samples.wValidBitsPerSample;
273 mixFormat->dwChannelMask = args->format.dwChannelMask;
274 FAudio_memcpy(
275 &mixFormat->SubFormat,
276 &args->format.SubFormat,
277 sizeof(GUID)
278 );
279 }
280 else
281 {
282 mixFormat->Format.cbSize = sizeof(FAudioWaveFormatEx);
283 }
284
285 args->client = data->client;
286 args->events[0] = audioEvent;
287 args->events[1] = data->stopEvent;
288 args->audio = audio;
289 args->updateSize = args->format.Format.nSamplesPerSec / 100;
290
291 data->audioThread = CreateThread(NULL, 0, &FAudio_AudioClientThread, args, 0, NULL);
292 FAudio_assert(!!data->audioThread && "Failed to create audio client thread!");
293
294 *updateSize = args->updateSize;
295 *platformDevice = data;
296 return;
297 }
298
299 void FAudio_PlatformQuit(void* platformDevice)
300 {
301 struct FAudioWin32PlatformData *data = platformDevice;
302
303 SetEvent(data->stopEvent);
304 WaitForSingleObject(data->audioThread, INFINITE);
305 if (data->client) IAudioClient_Release(data->client);
306 FAudio_PlatformRelease();
307 }
308
309 void FAudio_PlatformAddRef()
310 {
311 HRESULT hr;
312 EnterCriticalSection(&faudio_cs);
313 if (!device_enumerator)
314 {
315 init_hr = CoInitialize(NULL);
316 hr = CoCreateInstance(
317 &CLSID_MMDeviceEnumerator,
318 NULL,
319 CLSCTX_INPROC_SERVER,
320 &IID_IMMDeviceEnumerator,
321 (void**)&device_enumerator
322 );
323 FAudio_assert(!FAILED(hr) && "CoCreateInstance failed!");
324 }
325 else IMMDeviceEnumerator_AddRef(device_enumerator);
326 LeaveCriticalSection(&faudio_cs);
327 }
328
329 void FAudio_PlatformRelease()
330 {
331 EnterCriticalSection(&faudio_cs);
332 if (!IMMDeviceEnumerator_Release(device_enumerator))
333 {
334 device_enumerator = NULL;
335 if (SUCCEEDED(init_hr)) CoUninitialize();
336 }
337 LeaveCriticalSection(&faudio_cs);
338 }
339
340 uint32_t FAudio_PlatformGetDeviceCount(void)
341 {
342 IMMDevice *device;
343 uint32_t count;
344 HRESULT hr;
345
346 FAudio_PlatformAddRef();
347 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
348 device_enumerator,
349 eRender,
350 eConsole,
351 &device
352 );
353 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
354
355 IMMDevice_Release(device);
356 FAudio_PlatformRelease();
357
358 return 1;
359 }
360
361 uint32_t FAudio_PlatformGetDeviceDetails(
362 uint32_t index,
363 FAudioDeviceDetails *details
364 ) {
365 WAVEFORMATEXTENSIBLE *ext;
366 WAVEFORMATEX *format;
367 IAudioClient *client;
368 IMMDevice *device;
369 uint32_t ret = 0;
370 HRESULT hr;
371 WCHAR *str;
372
373 FAudio_memset(details, 0, sizeof(FAudioDeviceDetails));
374 if (index > 0) return FAUDIO_E_INVALID_CALL;
375
376 FAudio_PlatformAddRef();
377
378 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
379 device_enumerator,
380 eRender,
381 eConsole,
382 &device
383 );
384 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
385
386 details->Role = FAudioGlobalDefaultDevice;
387
388 hr = IMMDevice_GetId(device, &str);
389 FAudio_assert(!FAILED(hr) && "Failed to get audio endpoint id!");
390
391 lstrcpynW(details->DeviceID, str, ARRAYSIZE(details->DeviceID) - 1);
392 lstrcpynW(details->DisplayName, str, ARRAYSIZE(details->DisplayName) - 1);
393 CoTaskMemFree(str);
394
395 hr = IMMDevice_Activate(
396 device,
397 &IID_IAudioClient,
398 CLSCTX_ALL,
399 NULL,
400 (void **)&client
401 );
402 FAudio_assert(!FAILED(hr) && "Failed to activate audio client!");
403
404 hr = IAudioClient_GetMixFormat(client, &format);
405 FAudio_assert(!FAILED(hr) && "Failed to get audio client mix format!");
406
407 details->OutputFormat.Format.wFormatTag = format->wFormatTag;
408 details->OutputFormat.Format.nChannels = format->nChannels;
409 details->OutputFormat.Format.nSamplesPerSec = format->nSamplesPerSec;
410 details->OutputFormat.Format.nAvgBytesPerSec = format->nAvgBytesPerSec;
411 details->OutputFormat.Format.nBlockAlign = format->nBlockAlign;
412 details->OutputFormat.Format.wBitsPerSample = format->wBitsPerSample;
413 details->OutputFormat.Format.cbSize = format->cbSize;
414
415 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
416 {
417 ext = (WAVEFORMATEXTENSIBLE *)format;
418 details->OutputFormat.Samples.wValidBitsPerSample = ext->Samples.wValidBitsPerSample;
419 details->OutputFormat.dwChannelMask = ext->dwChannelMask;
420 FAudio_memcpy(
421 &details->OutputFormat.SubFormat,
422 &ext->SubFormat,
423 sizeof(GUID)
424 );
425 }
426
427 IAudioClient_Release(client);
428
429 IMMDevice_Release(device);
430
431 FAudio_PlatformRelease();
432
433 return ret;
434 }
435
436 FAudioMutex FAudio_PlatformCreateMutex(void)
437 {
438 CRITICAL_SECTION *cs;
439
440 cs = FAudio_malloc(sizeof(CRITICAL_SECTION));
441 if (!cs) return NULL;
442
443 InitializeCriticalSection(cs);
444
445 return cs;
446 }
447
448 void FAudio_PlatformLockMutex(FAudioMutex mutex)
449 {
450 if (mutex) EnterCriticalSection(mutex);
451 }
452
453 void FAudio_PlatformUnlockMutex(FAudioMutex mutex)
454 {
455 if (mutex) LeaveCriticalSection(mutex);
456 }
457
458 void FAudio_PlatformDestroyMutex(FAudioMutex mutex)
459 {
460 if (mutex) DeleteCriticalSection(mutex);
461 FAudio_free(mutex);
462 }
463
464 struct FAudioThreadArgs
465 {
466 FAudioThreadFunc func;
467 const char *name;
468 void* data;
469 };
470
471 static DWORD WINAPI FaudioThreadWrapper(void *user)
472 {
473 struct FAudioThreadArgs *args = user;
474 DWORD ret;
475
476 ret = args->func(args->data);
477
478 FAudio_free(args);
479 return ret;
480 }
481
482 FAudioThread FAudio_PlatformCreateThread(
483 FAudioThreadFunc func,
484 const char *name,
485 void* data
486 ) {
487 struct FAudioThreadArgs *args;
488
489 if (!(args = FAudio_malloc(sizeof(*args)))) return NULL;
490 args->func = func;
491 args->name = name;
492 args->data = data;
493
494 return CreateThread(NULL, 0, &FaudioThreadWrapper, args, 0, NULL);
495 }
496
497 void FAudio_PlatformWaitThread(FAudioThread thread, int32_t *retval)
498 {
499 WaitForSingleObject(thread, INFINITE);
500 GetExitCodeThread(thread, (DWORD *)retval);
501 }
502
503 void FAudio_PlatformThreadPriority(FAudioThreadPriority priority)
504 {
505 /* FIXME */
506 }
507
508 uint64_t FAudio_PlatformGetThreadID(void)
509 {
510 return GetCurrentThreadId();
511 }
512
513 void FAudio_sleep(uint32_t ms)
514 {
515 Sleep(ms);
516 }
517
518 uint32_t FAudio_timems()
519 {
520 return GetTickCount();
521 }
522
523 /* FAudio I/O */
524
525 static size_t FAUDIOCALL FAudio_FILE_read(
526 void *data,
527 void *dst,
528 size_t size,
529 size_t count
530 ) {
531 if (!data) return 0;
532 return fread(dst, size, count, data);
533 }
534
535 static int64_t FAUDIOCALL FAudio_FILE_seek(
536 void *data,
537 int64_t offset,
538 int whence
539 ) {
540 if (!data) return -1;
541 fseek(data, offset, whence);
542 return ftell(data);
543 }
544
545 static int FAUDIOCALL FAudio_FILE_close(void *data)
546 {
547 if (!data) return 0;
548 fclose(data);
549 return 0;
550 }
551
552 FAudioIOStream* FAudio_fopen(const char *path)
553 {
554 FAudioIOStream *io;
555
556 io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
557 if (!io) return NULL;
558
559 io->data = fopen(path, "rb");
560 io->read = FAudio_FILE_read;
561 io->seek = FAudio_FILE_seek;
562 io->close = FAudio_FILE_close;
563 io->lock = FAudio_PlatformCreateMutex();
564
565 return io;
566 }
567
568 struct FAudio_mem
569 {
570 char *mem;
571 int64_t len;
572 int64_t pos;
573 };
574
575 static size_t FAUDIOCALL FAudio_mem_read(
576 void *data,
577 void *dst,
578 size_t size,
579 size_t count
580 ) {
581 struct FAudio_mem *io = data;
582 size_t len = size * count;
583
584 if (!data) return 0;
585
586 while (len && len > (io->len - io->pos)) len -= size;
587 FAudio_memcpy(dst, io->mem + io->pos, len);
588 io->pos += len;
589
590 return len;
591 }
592
593 static int64_t FAUDIOCALL FAudio_mem_seek(
594 void *data,
595 int64_t offset,
596 int whence
597 ) {
598 struct FAudio_mem *io = data;
599 if (!data) return -1;
600
601 if (whence == SEEK_SET)
602 {
603 if (io->len > offset) io->pos = offset;
604 else io->pos = io->len;
605 }
606 if (whence == SEEK_CUR)
607 {
608 if (io->len > io->pos + offset) io->pos += offset;
609 else io->pos = io->len;
610 }
611 if (whence == SEEK_END)
612 {
613 if (io->len > offset) io->pos = io->len - offset;
614 else io->pos = 0;
615 }
616
617 return io->pos;
618 }
619
620 static int FAUDIOCALL FAudio_mem_close(void *data)
621 {
622 if (!data) return 0;
623 FAudio_free(data);
624 }
625
626 FAudioIOStream* FAudio_memopen(void *mem, int len)
627 {
628 struct FAudio_mem *data;
629 FAudioIOStream *io;
630
631 io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
632 if (!io) return NULL;
633
634 data = FAudio_malloc(sizeof(struct FAudio_mem));
635 if (!data)
636 {
637 FAudio_free(io);
638 return NULL;
639 }
640
641 data->mem = mem;
642 data->len = len;
643 data->pos = 0;
644
645 io->data = data;
646 io->read = FAudio_mem_read;
647 io->seek = FAudio_mem_seek;
648 io->close = FAudio_mem_close;
649 io->lock = FAudio_PlatformCreateMutex();
650 return io;
651 }
652
653 uint8_t* FAudio_memptr(FAudioIOStream *io, size_t offset)
654 {
655 struct FAudio_mem *memio = io->data;
656 return memio->mem + offset;
657 }
658
659 void FAudio_close(FAudioIOStream *io)
660 {
661 io->close(io->data);
662 FAudio_PlatformDestroyMutex((FAudioMutex) io->lock);
663 FAudio_free(io);
664 }
665
666 /* XNA Song implementation over Win32 MF */
667
668 static FAudioWaveFormatEx activeSongFormat;
669 IMFSourceReader *activeSong;
670 static uint8_t *songBuffer;
671 static SIZE_T songBufferSize;
672
673 static float songVolume = 1.0f;
674 static FAudio *songAudio = NULL;
675 static FAudioMasteringVoice *songMaster = NULL;
676
677 static FAudioSourceVoice *songVoice = NULL;
678 static FAudioVoiceCallback callbacks;
679
680 /* Internal Functions */
681
682 static void XNA_SongSubmitBuffer(FAudioVoiceCallback *callback, void *pBufferContext)
683 {
684 IMFMediaBuffer *media_buffer;
685 FAudioBuffer buffer;
686 IMFSample *sample;
687 HRESULT hr;
688 DWORD flags, buffer_size = 0;
689 BYTE *buffer_ptr;
690
691 LOG_FUNC_ENTER(songAudio);
692
693 FAudio_memset(&buffer, 0, sizeof(buffer));
694
695 hr = IMFSourceReader_ReadSample(
696 activeSong,
697 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
698 0,
699 NULL,
700 &flags,
701 NULL,
702 &sample
703 );
704 FAudio_assert(!FAILED(hr) && "Failed to read audio sample!");
705
706 if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
707 {
708 buffer.Flags = FAUDIO_END_OF_STREAM;
709 }
710 else
711 {
712 hr = IMFSample_ConvertToContiguousBuffer(
713 sample,
714 &media_buffer
715 );
716 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
717
718 hr = IMFMediaBuffer_Lock(
719 media_buffer,
720 &buffer_ptr,
721 NULL,
722 &buffer_size
723 );
724 FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
725
726 if (songBufferSize < buffer_size)
727 {
728 songBufferSize = buffer_size;
729 songBuffer = FAudio_realloc(songBuffer, songBufferSize);
730 FAudio_assert(songBuffer != NULL && "Failed to allocate song buffer!");
731 }
732 FAudio_memcpy(songBuffer, buffer_ptr, buffer_size);
733
734 hr = IMFMediaBuffer_Unlock(media_buffer);
735 FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
736
737 IMFMediaBuffer_Release(media_buffer);
738 IMFSample_Release(sample);
739 }
740
741 if (buffer_size > 0)
742 {
743 buffer.AudioBytes = buffer_size;
744 buffer.pAudioData = songBuffer;
745 buffer.PlayBegin = 0;
746 buffer.PlayLength = buffer_size / activeSongFormat.nBlockAlign;
747 buffer.LoopBegin = 0;
748 buffer.LoopLength = 0;
749 buffer.LoopCount = 0;
750 buffer.pContext = NULL;
751 FAudioSourceVoice_SubmitSourceBuffer(
752 songVoice,
753 &buffer,
754 NULL
755 );
756 }
757
758 LOG_FUNC_EXIT(songAudio);
759 }
760
761 static void XNA_SongKill()
762 {
763 if (songVoice != NULL)
764 {
765 FAudioSourceVoice_Stop(songVoice, 0, 0);
766 FAudioVoice_DestroyVoice(songVoice);
767 songVoice = NULL;
768 }
769 if (activeSong)
770 {
771 IMFSourceReader_Release(activeSong);
772 activeSong = NULL;
773 }
774 FAudio_free(songBuffer);
775 songBuffer = NULL;
776 songBufferSize = 0;
777 }
778
779 /* "Public" API */
780
781 FAUDIOAPI void XNA_SongInit()
782 {
783 HRESULT hr;
784
785 hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
786 FAudio_assert(!FAILED(hr) && "Failed to initialize Media Foundation!");
787
788 FAudioCreate(&songAudio, 0, FAUDIO_DEFAULT_PROCESSOR);
789 FAudio_CreateMasteringVoice(
790 songAudio,
791 &songMaster,
792 FAUDIO_DEFAULT_CHANNELS,
793 FAUDIO_DEFAULT_SAMPLERATE,
794 0,
795 0,
796 NULL
797 );
798 }
799
800 FAUDIOAPI void XNA_SongQuit()
801 {
802 XNA_SongKill();
803 FAudioVoice_DestroyVoice(songMaster);
804 FAudio_Release(songAudio);
805 MFShutdown();
806 }
807
808 FAUDIOAPI float XNA_PlaySong(const char *name)
809 {
810 IMFAttributes *attributes = NULL;
811 IMFMediaType *media_type = NULL;
812 UINT32 channels, samplerate;
813 UINT64 duration;
814 PROPVARIANT var;
815 HRESULT hr;
816 WCHAR filename_w[MAX_PATH];
817
818 LOG_FUNC_ENTER(songAudio);
819 LOG_INFO(songAudio, "name %s\n", name);
820 XNA_SongKill();
821
822 MultiByteToWideChar(CP_UTF8, 0, name, -1, filename_w, MAX_PATH);
823
824 hr = MFCreateAttributes(&attributes, 1);
825 FAudio_assert(!FAILED(hr) && "Failed to create attributes!");
826 hr = MFCreateSourceReaderFromURL(
827 filename_w,
828 attributes,
829 &activeSong
830 );
831 FAudio_assert(!FAILED(hr) && "Failed to create source reader!");
832 IMFAttributes_Release(attributes);
833
834 hr = MFCreateMediaType(&media_type);
835 FAudio_assert(!FAILED(hr) && "Failed to create media type!");
836 hr = IMFMediaType_SetGUID(
837 media_type,
838 &MF_MT_MAJOR_TYPE,
839 &MFMediaType_Audio
840 );
841 FAudio_assert(!FAILED(hr) && "Failed to set major type!");
842 hr = IMFMediaType_SetGUID(
843 media_type,
844 &MF_MT_SUBTYPE,
845 &MFAudioFormat_Float
846 );
847 FAudio_assert(!FAILED(hr) && "Failed to set sub type!");
848 hr = IMFSourceReader_SetCurrentMediaType(
849 activeSong,
850 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
851 NULL,
852 media_type
853 );
854 FAudio_assert(!FAILED(hr) && "Failed to set source media type!");
855 hr = IMFSourceReader_SetStreamSelection(
856 activeSong,
857 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
858 TRUE
859 );
860 FAudio_assert(!FAILED(hr) && "Failed to select source stream!");
861 IMFMediaType_Release(media_type);
862
863 hr = IMFSourceReader_GetCurrentMediaType(
864 activeSong,
865 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
866 &media_type
867 );
868 FAudio_assert(!FAILED(hr) && "Failed to get current media type!");
869 hr = IMFMediaType_GetUINT32(
870 media_type,
871 &MF_MT_AUDIO_NUM_CHANNELS,
872 &channels
873 );
874 FAudio_assert(!FAILED(hr) && "Failed to get channel count!");
875 hr = IMFMediaType_GetUINT32(
876 media_type,
877 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
878 &samplerate
879 );
880 FAudio_assert(!FAILED(hr) && "Failed to get sample rate!");
881 IMFMediaType_Release(media_type);
882
883 hr = IMFSourceReader_GetPresentationAttribute(
884 activeSong,
885 MF_SOURCE_READER_MEDIASOURCE,
886 &MF_PD_DURATION,
887 &var
888 );
889 FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
890 hr = PropVariantToInt64(&var, &duration);
891 FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
892 PropVariantClear(&var);
893
894 activeSongFormat.wFormatTag = FAUDIO_FORMAT_IEEE_FLOAT;
895 activeSongFormat.nChannels = channels;
896 activeSongFormat.nSamplesPerSec = samplerate;
897 activeSongFormat.wBitsPerSample = sizeof(float) * 8;
898 activeSongFormat.nBlockAlign = activeSongFormat.nChannels * activeSongFormat.wBitsPerSample / 8;
899 activeSongFormat.nAvgBytesPerSec = activeSongFormat.nSamplesPerSec * activeSongFormat.nBlockAlign;
900 activeSongFormat.cbSize = 0;
901
902 /* Init voice */
903 FAudio_zero(&callbacks, sizeof(FAudioVoiceCallback));
904 callbacks.OnBufferEnd = XNA_SongSubmitBuffer;
905 FAudio_CreateSourceVoice(
906 songAudio,
907 &songVoice,
908 &activeSongFormat,
909 0,
910 1.0f, /* No pitch shifting here! */
911 &callbacks,
912 NULL,
913 NULL
914 );
915 FAudioVoice_SetVolume(songVoice, songVolume, 0);
916 XNA_SongSubmitBuffer(NULL, NULL);
917
918 /* Finally. */
919 FAudioSourceVoice_Start(songVoice, 0, 0);
920 LOG_FUNC_EXIT(songAudio);
921 return duration / 10000000.;
922 }
923
924 FAUDIOAPI void XNA_PauseSong()
925 {
926 if (songVoice == NULL)
927 {
928 return;
929 }
930 FAudioSourceVoice_Stop(songVoice, 0, 0);
931 }
932
933 FAUDIOAPI void XNA_ResumeSong()
934 {
935 if (songVoice == NULL)
936 {
937 return;
938 }
939 FAudioSourceVoice_Start(songVoice, 0, 0);
940 }
941
942 FAUDIOAPI void XNA_StopSong()
943 {
944 XNA_SongKill();
945 }
946
947 FAUDIOAPI void XNA_SetSongVolume(float volume)
948 {
949 songVolume = volume;
950 if (songVoice != NULL)
951 {
952 FAudioVoice_SetVolume(songVoice, songVolume, 0);
953 }
954 }
955
956 FAUDIOAPI uint32_t XNA_GetSongEnded()
957 {
958 FAudioVoiceState state;
959 if (songVoice == NULL || activeSong == NULL)
960 {
961 return 1;
962 }
963 FAudioSourceVoice_GetState(songVoice, &state, 0);
964 return state.BuffersQueued == 0;
965 }
966
967 FAUDIOAPI void XNA_EnableVisualization(uint32_t enable)
968 {
969 /* TODO: Enable/Disable FAPO effect */
970 }
971
972 FAUDIOAPI uint32_t XNA_VisualizationEnabled()
973 {
974 /* TODO: Query FAPO effect enabled */
975 return 0;
976 }
977
978 FAUDIOAPI void XNA_GetSongVisualizationData(
979 float *frequencies,
980 float *samples,
981 uint32_t count
982 ) {
983 /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
984 }
985
986 /* FAudio WMADEC implementation over Win32 MF */
987
988 struct FAudioWMADEC
989 {
990 IMFTransform *decoder;
991 IMFSample *output_sample;
992
993 char *output_buf;
994 size_t output_pos;
995 size_t output_size;
996 size_t input_pos;
997 size_t input_size;
998 };
999
1000 static BOOL FAudio_WMAMF_ProcessInput(
1001 FAudioVoice *voice,
1002 FAudioBuffer *buffer
1003 ) {
1004 struct FAudioWMADEC *impl = voice->src.wmadec;
1005 IMFMediaBuffer *media_buffer;
1006 IMFSample *sample;
1007 DWORD copy_size;
1008 BYTE *copy_buf;
1009 HRESULT hr;
1010
1011 copy_size = min(buffer->AudioBytes - impl->input_pos, impl->input_size);
1012 if (!copy_size) return FALSE;
1013 LOG_INFO(voice->audio, "pushing %x bytes at %x", copy_size, impl->input_pos);
1014
1015 hr = MFCreateSample(&sample);
1016 FAudio_assert(!FAILED(hr) && "Failed to create sample!");
1017 hr = MFCreateMemoryBuffer(copy_size, &media_buffer);
1018 FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
1019 hr = IMFMediaBuffer_SetCurrentLength(media_buffer, copy_size);
1020 FAudio_assert(!FAILED(hr) && "Failed to set buffer length!");
1021 hr = IMFMediaBuffer_Lock(
1022 media_buffer,
1023 &copy_buf,
1024 NULL,
1025 &copy_size
1026 );
1027 FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
1028 FAudio_memcpy(copy_buf, buffer->pAudioData + impl->input_pos, copy_size);
1029 hr = IMFMediaBuffer_Unlock(media_buffer);
1030 FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
1031
1032 hr = IMFSample_AddBuffer(sample, media_buffer);
1033 FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
1034 IMFMediaBuffer_Release(media_buffer);
1035
1036 hr = IMFTransform_ProcessInput(impl->decoder, 0, sample, 0);
1037 IMFSample_Release(sample);
1038 if (hr == MF_E_NOTACCEPTING) return TRUE;
1039 if (FAILED(hr)) LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#x", hr);
1040 FAudio_assert(!FAILED(hr) || !"Failed to process input sample!");
1041
1042 impl->input_pos += copy_size;
1043 return TRUE;
1044 };
1045
1046 static BOOL FAudio_WMAMF_ProcessOutput(
1047 FAudioVoice *voice,
1048 FAudioBuffer *buffer
1049 ) {
1050 struct FAudioWMADEC *impl = voice->src.wmadec;
1051 MFT_OUTPUT_DATA_BUFFER output;
1052 IMFMediaBuffer *media_buffer;
1053 DWORD status, copy_size;
1054 BYTE *copy_buf;
1055 HRESULT hr;
1056
1057 while (1)
1058 {
1059 FAudio_memset(&output, 0, sizeof(output));
1060 output.pSample = impl->output_sample;
1061 hr = IMFTransform_ProcessOutput(impl->decoder, 0, 1, &output, &status);
1062 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) return FALSE;
1063 if (FAILED(hr)) LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#x", hr);
1064 FAudio_assert(!FAILED(hr) && "Failed to process output sample!");
1065
1066 if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) continue;
1067
1068 hr = IMFSample_ConvertToContiguousBuffer(
1069 output.pSample,
1070 &media_buffer
1071 );
1072 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
1073 hr = IMFMediaBuffer_Lock(
1074 media_buffer,
1075 &copy_buf,
1076 NULL,
1077 &copy_size
1078 );
1079 FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
1080 if (impl->output_pos + copy_size > impl->output_size)
1081 {
1082 impl->output_size = max(
1083 impl->output_pos + copy_size,
1084 impl->output_size * 3 / 2
1085 );
1086 impl->output_buf = voice->audio->pRealloc(
1087 impl->output_buf,
1088 impl->output_size
1089 );
1090 FAudio_assert(impl->output_buf && "Failed to resize output buffer!");
1091 }
1092 FAudio_memcpy(impl->output_buf + impl->output_pos, copy_buf, copy_size);
1093 impl->output_pos += copy_size;
1094 LOG_INFO(voice->audio, "pulled %x bytes at %x", copy_size, impl->output_pos);
1095 hr = IMFMediaBuffer_Unlock(media_buffer);
1096 FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
1097
1098 IMFMediaBuffer_Release(media_buffer);
1099 if (!impl->output_sample) IMFSample_Release(output.pSample);
1100 }
1101
1102 return TRUE;
1103 };
1104
1105 static void FAudio_INTERNAL_DecodeWMAMF(
1106 FAudioVoice *voice,
1107 FAudioBuffer *buffer,
1108 float *decodeCache,
1109 uint32_t samples
1110 ) {
1111 const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
1112 struct FAudioWMADEC *impl = voice->src.wmadec;
1113 size_t samples_pos, samples_size, copy_size;
1114 HRESULT hr;
1115
1116 LOG_FUNC_ENTER(voice->audio)
1117
1118 if (!impl->output_pos)
1119 {
1120 if (wfx->Format.wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
1121 {
1122 const FAudioBufferWMA *wma = &voice->src.bufferList->bufferWMA;
1123 const UINT32 *output_sizes = wma->pDecodedPacketCumulativeBytes;
1124
1125 impl->input_size = wfx->Format.nBlockAlign;
1126 impl->output_size = max(
1127 impl->output_size,
1128 output_sizes[wma->PacketCount - 1]
1129 );
1130 }
1131 else
1132 {
1133 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1134
1135 impl->input_size = xwf->dwBytesPerBlock;
1136 impl->output_size = max(
1137 impl->output_size,
1138 (size_t) xwf->dwSamplesEncoded *
1139 voice->src.format->nChannels *
1140 (voice->src.format->wBitsPerSample / 8)
1141 );
1142 }
1143
1144 impl->output_buf = voice->audio->pRealloc(
1145 impl->output_buf,
1146 impl->output_size
1147 );
1148 FAudio_assert(impl->output_buf && "Failed to allocate output buffer!");
1149
1150 LOG_INFO(voice->audio, "sending BOS to %p", impl->decoder);
1151 hr = IMFTransform_ProcessMessage(
1152 impl->decoder,
1153 MFT_MESSAGE_NOTIFY_START_OF_STREAM,
1154 0
1155 );
1156 FAudio_assert(!FAILED(hr) && "Failed to notify decoder stream start!");
1157 FAudio_WMAMF_ProcessInput(voice, buffer);
1158 }
1159
1160 samples_pos = voice->src.curBufferOffset * voice->src.format->nChannels * sizeof(float);
1161 samples_size = samples * voice->src.format->nChannels * sizeof(float);
1162
1163 while (impl->output_pos < samples_pos + samples_size)
1164 {
1165 if (FAudio_WMAMF_ProcessOutput(voice, buffer)) continue;
1166 if (FAudio_WMAMF_ProcessInput(voice, buffer)) continue;
1167 if (!impl->input_size) break;
1168
1169 LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
1170 hr = IMFTransform_ProcessMessage(
1171 impl->decoder,
1172 MFT_MESSAGE_NOTIFY_END_OF_STREAM,
1173 0
1174 );
1175 FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
1176 impl->input_size = 0;
1177 }
1178
1179 copy_size = FAudio_clamp(impl->output_pos - samples_pos, 0, samples_size);
1180 FAudio_memcpy(decodeCache, impl->output_buf + samples_pos, copy_size);
1181 LOG_INFO(
1182 voice->audio,
1183 "decoded %x / %x bytes, copied %x / %x bytes",
1184 impl->output_pos,
1185 impl->output_size,
1186 copy_size,
1187 samples_size
1188 );
1189
1190 LOG_FUNC_EXIT(voice->audio)
1191 }
1192
1193 uint32_t FAudio_WMADEC_init(FAudioSourceVoice *voice, uint32_t type)
1194 {
1195 static const uint8_t fake_codec_data[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1196 const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
1197 struct FAudioWMADEC *impl;
1198 MFT_OUTPUT_STREAM_INFO info = {0};
1199 IMFMediaBuffer *media_buffer;
1200 IMFMediaType *media_type;
1201 IMFTransform *decoder;
1202 HRESULT hr;
1203 UINT32 i, value;
1204 GUID guid;
1205
1206 LOG_FUNC_ENTER(voice->audio)
1207
1208 if (!(impl = voice->audio->pMalloc(sizeof(*impl)))) return 0;
1209 FAudio_memset(impl, 0, sizeof(*impl));
1210
1211 hr = CoCreateInstance(
1212 &CLSID_CWMADecMediaObject,
1213 0,
1214 CLSCTX_INPROC_SERVER,
1215 &IID_IMFTransform,
1216 (void **)&decoder
1217 );
1218 FAudio_assert(!FAILED(hr) && "Failed to create decoder!");
1219
1220 hr = MFCreateMediaType(&media_type);
1221 FAudio_assert(!FAILED(hr) && "Failed create media type!");
1222 hr = IMFMediaType_SetGUID(
1223 media_type,
1224 &MF_MT_MAJOR_TYPE,
1225 &MFMediaType_Audio
1226 );
1227 FAudio_assert(!FAILED(hr) && "Failed set media major type!");
1228
1229 switch (type)
1230 {
1231 case FAUDIO_FORMAT_WMAUDIO2:
1232 hr = IMFMediaType_SetBlob(
1233 media_type,
1234 &MF_MT_USER_DATA,
1235 (void *)fake_codec_data,
1236 sizeof(fake_codec_data)
1237 );
1238 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1239 hr = IMFMediaType_SetGUID(
1240 media_type,
1241 &MF_MT_SUBTYPE,
1242 &MFAudioFormat_WMAudioV8
1243 );
1244 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1245 hr = IMFMediaType_SetUINT32(
1246 media_type,
1247 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1248 wfx->Format.nBlockAlign
1249 );
1250 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1251 break;
1252 case FAUDIO_FORMAT_WMAUDIO3:
1253 hr = IMFMediaType_SetBlob(
1254 media_type,
1255 &MF_MT_USER_DATA,
1256 (void *)&wfx->Samples,
1257 wfx->Format.cbSize
1258 );
1259 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1260 hr = IMFMediaType_SetGUID(
1261 media_type,
1262 &MF_MT_SUBTYPE,
1263 &MFAudioFormat_WMAudioV9
1264 );
1265 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1266 hr = IMFMediaType_SetUINT32(
1267 media_type,
1268 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1269 wfx->Format.nBlockAlign
1270 );
1271 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1272 break;
1273 case FAUDIO_FORMAT_WMAUDIO_LOSSLESS:
1274 hr = IMFMediaType_SetBlob(
1275 media_type,
1276 &MF_MT_USER_DATA,
1277 (void *)&wfx->Samples,
1278 wfx->Format.cbSize
1279 );
1280 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1281 hr = IMFMediaType_SetGUID(
1282 media_type,
1283 &MF_MT_SUBTYPE,
1284 &MFAudioFormat_WMAudio_Lossless
1285 );
1286 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1287 hr = IMFMediaType_SetUINT32(
1288 media_type,
1289 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1290 wfx->Format.nBlockAlign
1291 );
1292 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1293 break;
1294 case FAUDIO_FORMAT_XMAUDIO2:
1295 {
1296 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1297 hr = IMFMediaType_SetBlob(
1298 media_type,
1299 &MF_MT_USER_DATA,
1300 (void *)&wfx->Samples,
1301 wfx->Format.cbSize
1302 );
1303 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1304 hr = IMFMediaType_SetGUID(
1305 media_type,
1306 &MF_MT_SUBTYPE,
1307 &MFAudioFormat_XMAudio2
1308 );
1309 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1310 hr = IMFMediaType_SetUINT32(
1311 media_type,
1312 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1313 xwf->dwBytesPerBlock
1314 );
1315 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1316 break;
1317 }
1318 default:
1319 FAudio_assert(0 && "Unsupported type!");
1320 break;
1321 }
1322
1323 hr = IMFMediaType_SetUINT32(
1324 media_type,
1325 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1326 wfx->Format.wBitsPerSample
1327 );
1328 FAudio_assert(!FAILED(hr) && "Failed set input bits per sample!");
1329 hr = IMFMediaType_SetUINT32(
1330 media_type,
1331 &MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
1332 wfx->Format.nAvgBytesPerSec
1333 );
1334 FAudio_assert(!FAILED(hr) && "Failed set input bytes per sample!");
1335 hr = IMFMediaType_SetUINT32(
1336 media_type,
1337 &MF_MT_AUDIO_NUM_CHANNELS,
1338 wfx->Format.nChannels
1339 );
1340 FAudio_assert(!FAILED(hr) && "Failed set input channel count!");
1341 hr = IMFMediaType_SetUINT32(
1342 media_type,
1343 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1344 wfx->Format.nSamplesPerSec
1345 );
1346 FAudio_assert(!FAILED(hr) && "Failed set input sample rate!");
1347
1348 hr = IMFTransform_SetInputType(
1349 decoder,
1350 0,
1351 media_type,
1352 0
1353 );
1354 FAudio_assert(!FAILED(hr) && "Failed set decoder input type!");
1355 IMFMediaType_Release(media_type);
1356
1357 i = 0;
1358 while (SUCCEEDED(hr))
1359 {
1360 hr = IMFTransform_GetOutputAvailableType(
1361 decoder,
1362 0,
1363 i++,
1364 &media_type
1365 );
1366 FAudio_assert(!FAILED(hr) && "Failed get output media type!");
1367
1368 hr = IMFMediaType_GetGUID(
1369 media_type,
1370 &MF_MT_MAJOR_TYPE,
1371 &guid
1372 );
1373 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1374 if (!IsEqualGUID(&MFMediaType_Audio, &guid)) goto next;
1375
1376 hr = IMFMediaType_GetGUID(
1377 media_type,
1378 &MF_MT_SUBTYPE,
1379 &guid
1380 );
1381 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1382 if (!IsEqualGUID(&MFAudioFormat_Float, &guid)) goto next;
1383
1384 hr = IMFMediaType_GetUINT32(
1385 media_type,
1386 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1387 &value
1388 );
1389 if (FAILED(hr))
1390 {
1391 value = 32;
1392 hr = IMFMediaType_SetUINT32(
1393 media_type,
1394 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1395 value
1396 );
1397 }
1398 FAudio_assert(!FAILED(hr) && "Failed get bits per sample!");
1399 if (value != 32) goto next;
1400
1401 hr = IMFMediaType_GetUINT32(
1402 media_type,
1403 &MF_MT_AUDIO_NUM_CHANNELS,
1404 &value
1405 );
1406 if (FAILED(hr))
1407 {
1408 value = wfx->Format.nChannels;
1409 hr = IMFMediaType_SetUINT32(
1410 media_type,
1411 &MF_MT_AUDIO_NUM_CHANNELS,
1412 value
1413 );
1414 }
1415 FAudio_assert(!FAILED(hr) && "Failed get channel count!");
1416 if (value != wfx->Format.nChannels) goto next;
1417
1418 hr = IMFMediaType_GetUINT32(
1419 media_type,
1420 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1421 &value
1422 );
1423 if (FAILED(hr))
1424 {
1425 value = wfx->Format.nSamplesPerSec;
1426 hr = IMFMediaType_SetUINT32(
1427 media_type,
1428 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1429 value
1430 );
1431 }
1432 FAudio_assert(!FAILED(hr) && "Failed get sample rate!");
1433 if (value != wfx->Format.nSamplesPerSec) goto next;
1434
1435 hr = IMFMediaType_GetUINT32(
1436 media_type,
1437 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1438 &value
1439 );
1440 if (FAILED(hr))
1441 {
1442 value = wfx->Format.nChannels * sizeof(float);
1443 hr = IMFMediaType_SetUINT32(
1444 media_type,
1445 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1446 value
1447 );
1448 }
1449 FAudio_assert(!FAILED(hr) && "Failed get block align!");
1450 if (value == wfx->Format.nChannels * sizeof(float)) break;
1451
1452 next:
1453 IMFMediaType_Release(media_type);
1454 }
1455 FAudio_assert(!FAILED(hr) && "Failed to find output media type!");
1456 hr = IMFTransform_SetOutputType(decoder, 0, media_type, 0);
1457 FAudio_assert(!FAILED(hr) && "Failed set decoder output type!");
1458 IMFMediaType_Release(media_type);
1459
1460 hr = IMFTransform_GetOutputStreamInfo(decoder, 0, &info);
1461 FAudio_assert(!FAILED(hr) && "Failed to get output stream info!");
1462
1463 impl->decoder = decoder;
1464 if (!(info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES))
1465 {
1466 hr = MFCreateSample(&impl->output_sample);
1467 FAudio_assert(!FAILED(hr) && "Failed to create sample!");
1468 hr = MFCreateMemoryBuffer(info.cbSize, &media_buffer);
1469 FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
1470 hr = IMFSample_AddBuffer(impl->output_sample, media_buffer);
1471 FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
1472 IMFMediaBuffer_Release(media_buffer);
1473 }
1474
1475 hr = IMFTransform_ProcessMessage(
1476 decoder,
1477 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
1478 0
1479 );
1480 FAudio_assert(!FAILED(hr) && "Failed to start decoder stream!");
1481
1482 voice->src.wmadec = impl;
1483 voice->src.decode = FAudio_INTERNAL_DecodeWMAMF;
1484
1485 LOG_FUNC_EXIT(voice->audio);
1486 return 0;
1487 }
1488
1489 void FAudio_WMADEC_free(FAudioSourceVoice *voice)
1490 {
1491 struct FAudioWMADEC *impl = voice->src.wmadec;
1492 HRESULT hr;
1493
1494 LOG_FUNC_ENTER(voice->audio)
1495 FAudio_PlatformLockMutex(voice->audio->sourceLock);
1496 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
1497
1498 if (impl->input_size)
1499 {
1500 LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
1501 hr = IMFTransform_ProcessMessage(
1502 impl->decoder,
1503 MFT_MESSAGE_NOTIFY_END_OF_STREAM,
1504 0
1505 );
1506 FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
1507 impl->input_size = 0;
1508 }
1509 if (impl->output_pos)
1510 {
1511 LOG_INFO(voice->audio, "sending DRAIN to %p", impl->decoder);
1512 hr = IMFTransform_ProcessMessage(
1513 impl->decoder,
1514 MFT_MESSAGE_COMMAND_DRAIN,
1515 0
1516 );
1517 FAudio_assert(!FAILED(hr) && "Failed to send DRAIN!");
1518 impl->output_pos = 0;
1519 }
1520
1521 if (impl->output_sample) IMFSample_Release(impl->output_sample);
1522 IMFTransform_Release(impl->decoder);
1523 voice->audio->pFree(impl->output_buf);
1524 voice->audio->pFree(voice->src.wmadec);
1525 voice->src.wmadec = NULL;
1526 voice->src.decode = NULL;
1527
1528 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
1529 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
1530 LOG_FUNC_EXIT(voice->audio)
1531 }
1532
1533 void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice)
1534 {
1535 struct FAudioWMADEC *impl = voice->src.wmadec;
1536 HRESULT hr;
1537
1538 LOG_FUNC_ENTER(voice->audio)
1539
1540 if (impl->input_size)
1541 {
1542 LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
1543 hr = IMFTransform_ProcessMessage(
1544 impl->decoder,
1545 MFT_MESSAGE_NOTIFY_END_OF_STREAM,
1546 0
1547 );
1548 FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
1549 impl->input_size = 0;
1550 }
1551 impl->output_pos = 0;
1552 impl->input_pos = 0;
1553
1554 LOG_FUNC_EXIT(voice->audio)
1555 }
1556
1557 #else
1558
1559 extern int this_tu_is_empty;
1560
1561 #endif /* FAUDIO_WIN32_PLATFORM */
6969 #define SEEK_END FAUDIO_SEEK_END
7070 #define EOF FAUDIO_EOF
7171 #define fopen(path, mode) FAudio_fopen(path)
72 #define fopen_s(io, path, mode) (!(*io = FAudio_fopen(path)))
7273 #define fclose(io) FAudio_close(io)
7374 #define fread(dst, size, count, io) io->read(io->data, dst, size, count)
7475 #define fseek(io, offset, whence) io->seek(io->data, offset, whence)