├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README ├── Xcode ├── FAudio.xcodeproj │ └── project.pbxproj └── README ├── cmake ├── FAudio.pc.in ├── JoinPaths.cmake ├── config.cmake.in └── install_shared_libs.cmake ├── csharp ├── FAudio-CS.Core.csproj ├── FAudio-CS.csproj ├── FAudio-CS.sln ├── FAudio.cs ├── LICENSE ├── Makefile ├── README └── app.config ├── extensions ├── COMConstructEXT.txt ├── CollectorEXT.txt ├── CustomAllocatorEXT.txt ├── DestroyVoiceSafeEXT.txt ├── EngineProcedureEXT.txt └── FilterWetDryMixEXT.txt ├── include ├── F3DAudio.h ├── FACT.h ├── FACT3D.h ├── FAPO.h ├── FAPOBase.h ├── FAPOFX.h ├── FAudio.h └── FAudioFX.h ├── src ├── F3DAudio.c ├── FACT.c ├── FACT3D.c ├── FACT_internal.c ├── FACT_internal.h ├── FAPOBase.c ├── FAPOFX.c ├── FAPOFX_echo.c ├── FAPOFX_eq.c ├── FAPOFX_masteringlimiter.c ├── FAPOFX_reverb.c ├── FAudio.c ├── FAudioFX_collector.c ├── FAudioFX_reverb.c ├── FAudioFX_volumemeter.c ├── FAudio_internal.c ├── FAudio_internal.h ├── FAudio_internal_simd.c ├── FAudio_operationset.c ├── FAudio_platform_sdl2.c ├── FAudio_platform_sdl3.c ├── FAudio_platform_win32.c ├── XNA_Song.c ├── matrix_defaults.inl ├── qoa_decoder.h ├── stb.h └── stb_vorbis.h ├── tests ├── FAudio_compat.h ├── Makefile └── xaudio2.c ├── utils ├── facttool │ └── facttool.cpp ├── showriffheader │ └── showriffheader.cpp ├── testfilter │ ├── audio.cpp │ ├── audio.h │ ├── audio_faudio.cpp │ ├── audio_player.h │ ├── audio_xaudio.cpp │ ├── oscillator.cpp │ ├── oscillator.h │ └── testfilter.cpp ├── testparse │ └── testparse.c ├── testreverb │ ├── audio.cpp │ ├── audio.h │ ├── audio_faudio.cpp │ ├── audio_xaudio.cpp │ └── testreverb.cpp ├── testvolumemeter │ ├── audio.cpp │ ├── audio.h │ ├── audio_faudio.cpp │ └── testvolumemeter.cpp ├── testxwma │ └── testxwma.cpp ├── uicommon │ ├── FAudioUI_main.cpp │ ├── imconfig.h │ ├── imgui.cpp │ ├── imgui.h │ ├── imgui_demo.cpp │ ├── imgui_draw.cpp │ ├── imgui_internal.h │ ├── imgui_widgets.cpp │ ├── imstb_rectpack.h │ ├── imstb_textedit.h │ └── imstb_truetype.h ├── voicepool │ └── voicepool.cpp └── wavcommon │ ├── dr_wav.h │ ├── resources │ ├── origin.txt │ ├── snaredrum_forte.wav │ ├── snaredrum_forte_stereo.wav │ ├── snaredrum_fortissimo.wav │ ├── snaredrum_fortissimo_stereo.wav │ ├── snaredrum_mezzoforte.wav │ └── snaredrum_mezzoforte_stereo.wav │ ├── wavs.cpp │ └── wavs.h ├── visualc-gdk ├── FAudio.sln └── FAudio.vcxproj └── visualc ├── FAudio.sln ├── FAudio.vcxproj └── README /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [flibitijibibo] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.dylib 4 | *.dll 5 | .vs/ 6 | obj/ 7 | Debug/ 8 | Release/ 9 | *.csproj.user 10 | *.vcxproj.user 11 | .DS_Store 12 | xcuserdata/ 13 | *.xcworkspace/ 14 | *.xcscheme -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is FAudio, an XAudio reimplementation that focuses solely on developing 2 | fully accurate DirectX Audio runtime libraries for the FNA project, including 3 | XAudio2, X3DAudio, XAPO, and XACT3. 4 | 5 | Project Website: https://fna-xna.github.io/ 6 | 7 | License 8 | ------- 9 | FAudio is released under the zlib license. See LICENSE for details. 10 | 11 | About FAudio 12 | ------------ 13 | FAudio was written to be used for FNA's Audio/Media namespaces. We access this 14 | library via FAudio#, which you can find in the 'csharp/' directory. 15 | 16 | Dependencies 17 | ------------ 18 | FAudio depends solely on SDL 3.2.0 or newer. 19 | FAudio never explicitly uses the C runtime. 20 | 21 | Building FAudio 22 | --------------- 23 | For *nix platforms, use cmake. 24 | 25 | $ mkdir build/ 26 | $ cd build/ 27 | $ cmake ../ 28 | $ make 29 | 30 | For Windows, see the 'visualc/' directory. 31 | 32 | For Xbox GDK, see the 'visualc-gdk/' directory. 33 | 34 | For iOS/tvOS and macOS universal binaries, see the 'Xcode/' directory. 35 | 36 | Unit tests 37 | ---------- 38 | FAudio includes a set of unit tests which document the behavior of XAudio2 and 39 | are to be run against FAudio to verify it has the same behavior. The tests are 40 | NOT built by default; set BUILD_TESTS=1 to build and then run the output with: 41 | 42 | $ ./faudio_tests 43 | 44 | To build a Windows executable to run the tests against XAudio2, use the 45 | provided Makefile. This requires mingw-w64 to build. 46 | 47 | $ cd tests/ 48 | $ make faudio_tests.exe 49 | # run faudio_tests.exe on a Windows box 50 | 51 | Found an issue? 52 | --------------- 53 | Issues and patches can be reported via GitHub: 54 | 55 | https://github.com/FNA-XNA/FAudio/issues 56 | -------------------------------------------------------------------------------- /Xcode/README: -------------------------------------------------------------------------------- 1 | Building FAudio for macOS/iOS/tvOS 2 | ---------------------------- 3 | FAudio uses Xcode to build on macOS, iOS, and tvOS. 4 | 5 | Dependencies 6 | ------------ 7 | Before building, download SDL2's source code from SDL's website: 8 | 9 | http://libsdl.org/download-2.0.php 10 | 11 | After extracting the zip file, be sure to rename the directory to remove the 12 | version number (for example, 'SDL2-2.0.8' should be 'SDL2'). 13 | 14 | Compiling 15 | --------- 16 | 1. Build SDL2/Xcode/SDL/SDL.xcodeproj 17 | 2. Build FAudio.xcodeproj 18 | 3. Grab libFAudio.a and libSDL2.a (or libFAudio.dylib and libSDL2.dylib on macOS), ship it! 19 | -------------------------------------------------------------------------------- /cmake/FAudio.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@FAUDIO_PKGCONF_LIBDIR@ 4 | includedir=@FAUDIO_PKGCONF_INCLUDEDIR@ 5 | 6 | Name: @PROJECT_NAME@ 7 | URL: https://github.com/FNA-XNA/FAudio 8 | Description: Accuracy-focused XAudio reimplementation for open platforms 9 | Version: @LIB_VERSION@ 10 | @PC_REQUIRES_PRIVATE@ 11 | 12 | Libs: -L${libdir} -l@PROJECT_NAME@ 13 | Cflags: -I${includedir} @PLATFORM_CFLAGS@ 14 | -------------------------------------------------------------------------------- /cmake/JoinPaths.cmake: -------------------------------------------------------------------------------- 1 | # This module provides a function for joining paths 2 | # known from most languages 3 | # 4 | # SPDX-License-Identifier: (MIT OR CC0-1.0) 5 | # Copyright 2020 Jan Tojnar 6 | # https://github.com/jtojnar/cmake-snips 7 | # 8 | # Modelled after Python’s os.path.join 9 | # https://docs.python.org/3.7/library/os.path.html#os.path.join 10 | # Windows not supported 11 | function(join_paths joined_path first_path_segment) 12 | set(temp_path "${first_path_segment}") 13 | foreach(current_segment IN LISTS ARGN) 14 | if(NOT ("${current_segment}" STREQUAL "")) 15 | string(REGEX REPLACE "^${CMAKE_INSTALL_PREFIX}/?" "" new_segment "${current_segment}") 16 | if(NOT ("${new_segment}" STREQUAL "")) 17 | if(NOT IS_ABSOLUTE "${current_segment}" OR NOT "${current_segment}" MATCHES "^${new_segment}$") 18 | set(temp_path "${temp_path}/${new_segment}") 19 | else() 20 | set(temp_path "${current_segment}") 21 | endif() 22 | endif() 23 | endif() 24 | endforeach() 25 | set(${joined_path} "${temp_path}" PARENT_SCOPE) 26 | endfunction() 27 | -------------------------------------------------------------------------------- /cmake/config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | if(NOT "@PLATFORM_WIN32@") 4 | include(CMakeFindDependencyMacro) 5 | find_dependency(SDL2 CONFIG) 6 | endif() 7 | 8 | if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@CMAKE_PROJECT_NAME@-targets-shared.cmake") 9 | include("${CMAKE_CURRENT_LIST_DIR}/@CMAKE_PROJECT_NAME@-targets-shared.cmake") 10 | set(shared_target TRUE) 11 | endif() 12 | 13 | if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@CMAKE_PROJECT_NAME@-targets-static.cmake") 14 | include("${CMAKE_CURRENT_LIST_DIR}/@CMAKE_PROJECT_NAME@-targets-static.cmake") 15 | endif() 16 | 17 | if(shared_target) 18 | add_library(@CMAKE_PROJECT_NAME@::@CMAKE_PROJECT_NAME@ ALIAS @CMAKE_PROJECT_NAME@::@CMAKE_PROJECT_NAME@-shared) 19 | else() 20 | add_library(@CMAKE_PROJECT_NAME@::@CMAKE_PROJECT_NAME@ ALIAS @CMAKE_PROJECT_NAME@::@CMAKE_PROJECT_NAME@-static) 21 | endif() 22 | 23 | check_required_components("@CMAKE_PROJECT_NAME@") 24 | 25 | -------------------------------------------------------------------------------- /csharp/FAudio-CS.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net40;netstandard2.0 4 | x64 5 | 6 | 7 | false 8 | FAudio-CS 9 | FAudio 10 | true 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /csharp/FAudio-CS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 9.0.21022 7 | 2.0 8 | {4FF0E874-ADB4-4AE5-B059-DD6116C4A370} 9 | Library 10 | FAudio 11 | FAudio-CS 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug 18 | DEBUG; 19 | prompt 20 | 4 21 | x86 22 | false 23 | true 24 | 25 | 26 | none 27 | false 28 | bin\Release 29 | prompt 30 | 4 31 | x86 32 | false 33 | true 34 | 35 | 36 | true 37 | full 38 | false 39 | bin\Debug 40 | DEBUG; 41 | prompt 42 | 4 43 | false 44 | true 45 | 46 | 47 | none 48 | false 49 | bin\Release 50 | prompt 51 | 4 52 | false 53 | true 54 | 55 | 56 | true 57 | full 58 | false 59 | bin\Debug 60 | DEBUG; 61 | prompt 62 | 4 63 | x64 64 | false 65 | true 66 | 67 | 68 | none 69 | false 70 | bin\Release 71 | prompt 72 | 4 73 | x64 74 | false 75 | true 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | $(TargetFileName).config 84 | PreserveNewest 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /csharp/FAudio-CS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FAudio-CS", "FAudio-CS.csproj", "{4FF0E874-ADB4-4AE5-B059-DD6116C4A370}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|x86 = Debug|x86 9 | Release|x86 = Release|x86 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {4FF0E874-ADB4-4AE5-B059-DD6116C4A370}.Debug|x86.ActiveCfg = Debug|x86 13 | {4FF0E874-ADB4-4AE5-B059-DD6116C4A370}.Debug|x86.Build.0 = Debug|x86 14 | {4FF0E874-ADB4-4AE5-B059-DD6116C4A370}.Release|x86.ActiveCfg = Release|x86 15 | {4FF0E874-ADB4-4AE5-B059-DD6116C4A370}.Release|x86.Build.0 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(MonoDevelopProperties) = preSolution 18 | StartupItem = FAudio-CS.csproj 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /csharp/LICENSE: -------------------------------------------------------------------------------- 1 | /* FAudio# - C# Wrapper for FAudio 2 | * 3 | * Copyright (c) 2018-2024 Ethan Lee. 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | -------------------------------------------------------------------------------- /csharp/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for FAudio# 2 | # Written by Ethan "flibitijibibo" Lee 3 | 4 | build: clean 5 | mkdir bin 6 | cp app.config bin/FAudio-CS.dll.config 7 | mcs /unsafe -debug -out:bin/FAudio-CS.dll -target:library FAudio.cs 8 | 9 | clean: 10 | rm -rf bin 11 | -------------------------------------------------------------------------------- /csharp/README: -------------------------------------------------------------------------------- 1 | This is FAudio#, a C# wrapper for FAudio, an XAudio reimplementation for FNA. 2 | 3 | Project Website: http://fna-xna.github.io/ 4 | 5 | License 6 | ------- 7 | FAudio# is released under the zlib license. See LICENSE for details. 8 | 9 | Building FAudio# 10 | ---------------- 11 | Just type `make` in the root directory! 12 | -------------------------------------------------------------------------------- /csharp/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /extensions/COMConstructEXT.txt: -------------------------------------------------------------------------------- 1 | COMConstructEXT - Entry point for improved compatibility with COM wrappers 2 | 3 | About 4 | ----- 5 | This entry point is needed for scenarios when you need to create the FAudio 6 | object, but are unable to provide the parameters that FAudioCreate asks for by 7 | default. This is typical in COM wrappers where XAudio2 (<= 2.7) programs will 8 | first construct the object (as requested by CoCreateInstance), then call 9 | IXAudio2_Initialize with the 'Flags' and 'XAudio2Processor' parameters 10 | afterward. 11 | 12 | Dependencies 13 | ------------ 14 | This extension does not interact with any non-standard XAudio features. 15 | 16 | New Procedures and Functions 17 | ---------------------------- 18 | FAUDIOAPI uint32_t FAudioCOMConstructEXT(FAudio **ppFAudio, uint8_t version); 19 | 20 | How to Use 21 | ---------- 22 | Instead of initializing FAudio with the traditional method... 23 | 24 | FAudio *audio; 25 | FAudioCreate(&audio, 0, FAUDIO_PROCESSOR_DEFAULT); 26 | 27 | ... you construct and then initialize as two separate function calls: 28 | 29 | FAudio *audio; 30 | FAudioCOMConstructEXT(&audio, FAUDIO_TARGET_VERSION); 31 | FAudio_Initialize(audio, 0, FAUDIO_PROCESSOR_DEFAULT); 32 | 33 | FAQ: 34 | ---- 35 | Q: Should we create similar functions for other XAudio subsystems (such as XACT, 36 | XAPOFX, and XAudio2FX)? 37 | A: No. While other objects in XAudio 2.7 (or lower) are initialized via COM on 38 | Windows, XAudio2 has a unique situation where the 'XAudio2Create' macro calls 39 | Initialize in addition to CoCreateInstance. All other creation macros simply 40 | call CoCreateInstance with no further activity, so it is easy to support them 41 | with the one exported 'Create' function. 42 | -------------------------------------------------------------------------------- /extensions/CollectorEXT.txt: -------------------------------------------------------------------------------- 1 | CollectorEXT - Allow asynchronously capturing samples via an effect chain 2 | 3 | About 4 | ----- 5 | By default if you want to do sophisticated monitoring of mixed samples in XAudio, you need to author a custom FAPO and insert it into an effect chain. Doing this correctly can be somewhat tricky, and for developers using garbage collected languages this can introduce the potential for a GC pause on the mixer thread (causing audio dropouts). 6 | This extension introduces a new built-in FAPO called "Collector" that captures samples into a user-provided ring buffer. 7 | 8 | Dependencies 9 | ------------ 10 | This extension does not interact with any non-standard XAudio features. 11 | 12 | New Defines 13 | ----------- 14 | 15 | New Types 16 | --------- 17 | typedef struct FAudioFXCollectorState 18 | { 19 | uint32_t WriteOffset; 20 | } FAudioFXCollectorState; 21 | 22 | New Procedures and Functions 23 | ---------------------------- 24 | FAUDIOAPI uint32_t FAudioCreateCollectorEXT( 25 | FAPO** ppApo, 26 | uint32_t Flags, 27 | float* pBuffer, 28 | uint32_t bufferLength 29 | ); 30 | 31 | FAUDIOAPI uint32_t FAudioCreateCollectorWithCustomAllocatorEXT( 32 | FAPO** ppApo, 33 | uint32_t Flags, 34 | float* pBuffer, 35 | uint32_t bufferLength, 36 | FAudioMallocFunc customMalloc, 37 | FAudioFreeFunc customFree, 38 | FAudioReallocFunc customRealloc 39 | ); 40 | 41 | How to Use 42 | ---------- 43 | Allocate a sufficiently large ring buffer and pass it in to FAudioCreateCollectorEXT. Then take the resulting FAPO and add it to an effect chain, i.e. 44 | 45 | var buffer = new float[1024]; 46 | var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 47 | 48 | FAudio.FAudioCreateCollector( 49 | out var pCollector, 50 | 0, handle.AddrOfPinnedObject(), (uint)buffer.Length 51 | ); 52 | 53 | FAudio.FAudioEffectDescriptor effectDescriptor = new() { 54 | OutputChannels = 2, 55 | pEffect = pCollector 56 | }; 57 | FAudio.FAudioEffectChain effectChain = new() { 58 | EffectCount = 1, 59 | pEffectDescriptors = (IntPtr)Unsafe.AsPointer(ref effectDescriptor) 60 | }; 61 | 62 | 63 | FAQ 64 | --- 65 | Q. How do I avoid processing the same samples twice, or missing samples, if my game is running faster/slower than the mixer? 66 | A. Allocate a sufficiently large ring buffer and use GetParameters to determine the current write position of the mixer, then read the N most recent samples before the write position. The write position is updated once per block of processed samples instead of once per sample so it doesn't change that often. i.e. 67 | 68 | FAudio.FAudioFXCollectorState parameters = default; 69 | FAudio.FAudioVoice_GetEffectParameters( 70 | Voice, 0, (IntPtr)(¶meters), (uint)sizeof(FAudio.FAudioFXCollectorState) 71 | ); 72 | -------------------------------------------------------------------------------- /extensions/CustomAllocatorEXT.txt: -------------------------------------------------------------------------------- 1 | CustomAllocatorEXT - Allow using custom memory management functions with FAudio 2 | 3 | About 4 | ----- 5 | These entry points are needed for scenarios where the application needs control 6 | over the memory allocators used by FAudio. This is useful for programs that have 7 | strict memory requirements, and is also useful for COM wrappers that need to use 8 | the same memory allocator as Microsoft's XAudio2 runtime, for accuracy. 9 | 10 | This extension only supports using ONE and ONLY ONE set of custom callbacks. You 11 | are NOT allowed to send different malloc/free/realloc functions to each object, 12 | ALL objects must use the exact same callbacks. Any and all attempts to mix 13 | callbacks will result in an unspecified program crash. 14 | 15 | Dependencies 16 | ------------ 17 | This extension interacts with COMConstructEXT. 18 | 19 | New Types 20 | --------- 21 | typedef void* (FAUDIOCALL * FAudioMallocFunc)(size_t size); 22 | 23 | typedef void (FAUDIOCALL * FAudioFreeFunc)(void* ptr); 24 | 25 | typedef void* (FAUDIOCALL * FAudioReallocFunc)(void* ptr, size_t size); 26 | 27 | New Procedures and Functions 28 | ---------------------------- 29 | FAUDIOAPI uint32_t FAudioCreateWithCustomAllocatorEXT( 30 | FAudio **ppFAudio, 31 | uint32_t Flags, 32 | FAudioProcessor XAudio2Processor, 33 | FAudioMallocFunc customMalloc, 34 | FAudioFreeFunc customFree, 35 | FAudioReallocFunc customRealloc 36 | ); 37 | 38 | FAUDIOAPI uint32_t FAudioCOMConstructWithCustomAllocatorEXT( 39 | FAudio **ppFAudio, 40 | uint8_t version, 41 | FAudioMallocFunc customMalloc, 42 | FAudioFreeFunc customFree, 43 | FAudioReallocFunc customRealloc 44 | ); 45 | 46 | FAUDIOAPI uint32_t FAudioCreateVolumeMeterWithCustomAllocatorEXT( 47 | FAPO** ppApo, 48 | uint32_t Flags, 49 | FAudioMallocFunc customMalloc, 50 | FAudioFreeFunc customFree, 51 | FAudioReallocFunc customRealloc 52 | ); 53 | 54 | FAUDIOAPI uint32_t FAudioCreateReverbWithCustomAllocatorEXT( 55 | FAPO** ppApo, 56 | uint32_t Flags, 57 | FAudioMallocFunc customMalloc, 58 | FAudioFreeFunc customFree, 59 | FAudioReallocFunc customRealloc 60 | ); 61 | 62 | FAUDIOAPI uint32_t FAudioCreateReverb9WithCustomAllocatorEXT( 63 | FAPO** ppApo, 64 | uint32_t Flags, 65 | FAudioMallocFunc customMalloc, 66 | FAudioFreeFunc customFree, 67 | FAudioReallocFunc customRealloc 68 | ); 69 | 70 | FAPOAPI void CreateFAPOBaseWithCustomAllocatorEXT( 71 | FAPOBase *fapo, 72 | const FAPORegistrationProperties *pRegistrationProperties, 73 | uint8_t *pParameterBlocks, 74 | uint32_t uParameterBlockByteSize, 75 | uint8_t fProducer, 76 | FAudioMallocFunc customMalloc, 77 | FAudioFreeFunc customFree, 78 | FAudioReallocFunc customRealloc 79 | ); 80 | 81 | FAPOFXAPI uint32_t FAPOFX_CreateFXWithCustomAllocatorEXT( 82 | const FAudioGUID *clsid, 83 | FAPO **pEffect, 84 | const void *pInitData, 85 | uint32_t InitDataByteSize, 86 | FAudioMallocFunc customMalloc, 87 | FAudioFreeFunc customFree, 88 | FAudioReallocFunc customRealloc 89 | ); 90 | 91 | FACTAPI uint32_t FACTCreateEngineWithCustomAllocatorEXT( 92 | uint32_t dwCreationFlags, 93 | FACTAudioEngine **ppEngine, 94 | FAudioMallocFunc customMalloc, 95 | FAudioFreeFunc customFree, 96 | FAudioReallocFunc customRealloc 97 | ); 98 | 99 | How to Use 100 | ---------- 101 | Initialization of FAudio objects should be the same, only you send three more 102 | parameters when initializing: A malloc, free, and realloc function: 103 | 104 | extern void* MyMalloc(size_t size); 105 | extern void MyFree(void* ptr); 106 | extern void MyRealloc(void* ptr, size_t size); 107 | FAudio *audio; 108 | FAudioCreateWithCustomAllocatorEXT( 109 | &audio, 110 | 0, 111 | FAUDIO_PROCESSOR_DEFAULT, 112 | MyMalloc, 113 | MyFree, 114 | MyRealloc 115 | ); 116 | 117 | If you use a custom allocator with one object, you MUST send the same allocators 118 | to ALL new objects that support custom allocators. 119 | 120 | FAQ: 121 | ---- 122 | Q: Should we allow custom allocators to be mixed between objects? 123 | A: No. While each object asks for callbacks separately, XAudio2 objects all 124 | closely interact with each other, including allocating/freeing memory from 125 | one another. Your program WILL crash if allocator functions are mismatched! 126 | -------------------------------------------------------------------------------- /extensions/DestroyVoiceSafeEXT.txt: -------------------------------------------------------------------------------- 1 | DestroyVoiceSafeEXT - Expose whether or not a voice was actually destroyed 2 | 3 | About 4 | ----- 5 | XAudio's function for exporting voices has the ability to fail - however, the 6 | function declaration does not have a return value, so there is no way to safely 7 | determine on the client side whether or not a voice was actually destroyed. 8 | This extension adds a function that is otherwise identical, but includes a 9 | result code to allow checking for failures. 10 | 11 | Dependencies 12 | ------------ 13 | This extension does not interact with any non-standard XAudio features. 14 | 15 | New Procedures and Functions 16 | ---------------------------- 17 | FAUDIOAPI uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice *voice); 18 | 19 | How to Use 20 | ---------- 21 | This extension acts identially to the existing DestroyVoice, but with and added 22 | return value - it will return 0 on success or non-0 on failure. 23 | -------------------------------------------------------------------------------- /extensions/EngineProcedureEXT.txt: -------------------------------------------------------------------------------- 1 | EngineProcedureEXT - Allow clients to wrap engine procedure 2 | 3 | About 4 | ----- 5 | Clients may be interested in having more control over the engine procedure. In 6 | particular, they may want to modify or access the generated output before 7 | passing it to the underlying platform. In other cases, clients like Wine may 8 | have specific thread requirements that the platform's audio library cannot 9 | satisfy. This extension allows clients to "wrap" the engine procedure with 10 | arbitrary code. 11 | 12 | Dependencies 13 | ------------ 14 | This extension does not interact with any non-standard XAudio features. 15 | 16 | New Types 17 | --------- 18 | typedef void (FAUDIOCALL *FAudioEngineCallEXT)(FAudio *audio, float *output); 19 | 20 | typedef void (FAUDIOCALL *FAudioEngineProcedureEXT)( 21 | FAudioEngineCallEXT defaultEngineProc, 22 | FAudio *audio, 23 | float *output, 24 | void *user 25 | ); 26 | 27 | New Procedures and Functions 28 | ---------------------------- 29 | FAUDIOAPI void FAudio_SetEngineProcedureEXT( 30 | FAudio *audio, 31 | FAudioEngineProcedureEXT clientEngineProc, 32 | void *user 33 | ); 34 | 35 | How to Use 36 | ---------- 37 | At any time, the client may request that FAudio call the given 38 | clientEngineProc function instead of FAudio's default engine procedure 39 | beginning with the next engine tick. It is valid to call 40 | FAudio_SetEngineProcedureEXT before creating the mastering voice. 41 | 42 | clientEngineProc will be invoked with a pointer to FAudio's default engine 43 | procedure, allowing the client to execute the procedure on a specific thread, 44 | or operate on the retrieved audio before returning control back to FAudio. 45 | -------------------------------------------------------------------------------- /extensions/FilterWetDryMixEXT.txt: -------------------------------------------------------------------------------- 1 | FilterWetDryMixEXT - Allow voice/send filters to apply a wet/dry mix 2 | 3 | About 4 | ----- 5 | The default XAudio specification allows for applying filters and effects. While 6 | effects have the capability of applying wet/dry mixes to their processing, 7 | filters do not have this feature. Filters are, for the most part, just another 8 | effect that's very clearly defined, so adding a wet/dry calculation to the 9 | existing filter algorithm is trivial. 10 | 11 | Dependencies 12 | ------------ 13 | This extension does not interact with any non-standard XAudio features. 14 | 15 | New Defines 16 | ----------- 17 | #define FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT 1.0f 18 | 19 | New Types 20 | --------- 21 | typedef struct FAudioFilterParametersEXT 22 | { 23 | FAudioFilterType Type; 24 | float Frequency; /* [0, FAUDIO_MAX_FILTER_FREQUENCY] */ 25 | float OneOverQ; /* [0, FAUDIO_MAX_FILTER_ONEOVERQ] */ 26 | float WetDryMix; /* [0, 1] */ 27 | } FAudioFilterParametersEXT; 28 | 29 | New Procedures and Functions 30 | ---------------------------- 31 | FAUDIOAPI uint32_t FAudioVoice_SetFilterParametersEXT( 32 | FAudioVoice* voice, 33 | const FAudioFilterParametersEXT* pParameters, 34 | uint32_t OperationSet 35 | ); 36 | 37 | FAUDIOAPI void FAudioVoice_GetFilterParametersEXT( 38 | FAudioVoice* voice, 39 | FAudioFilterParametersEXT* pParameters 40 | ); 41 | 42 | FAUDIOAPI uint32_t FAudioVoice_SetOutputFilterParametersEXT( 43 | FAudioVoice* voice, 44 | FAudioVoice* pDestinationVoice, 45 | const FAudioFilterParametersEXT* pParameters, 46 | uint32_t OperationSet 47 | ); 48 | 49 | FAUDIOAPI void FAudioVoice_GetOutputFilterParametersEXT( 50 | FAudioVoice* voice, 51 | FAudioVoice* pDestinationVoice, 52 | FAudioFilterParametersEXT* pParameters 53 | ); 54 | 55 | How to Use 56 | ---------- 57 | This extension acts identially to the existing FAudioFilterParameters, but with 58 | an added WetDryMix structure member - set this value to apply a wet/dry mix. 59 | 60 | FAQ 61 | --- 62 | Q: Does the behavior of the stock filter functions change? 63 | A: No. Calls to the original functions will apply a WetDryMix value of 1, so as 64 | to sound as the function would have originally intended. This is subject to 65 | change in future revisions; in the current implementation, preserving the 66 | current wet/dry value would have required additional mutex activity, which 67 | could degrade performance for users of the stock API. 68 | -------------------------------------------------------------------------------- /include/F3DAudio.h: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | /* This file has no documentation since the MSDN docs are still perfectly fine: 28 | * https://docs.microsoft.com/en-us/windows/desktop/api/x3daudio/ 29 | */ 30 | 31 | #ifndef F3DAUDIO_H 32 | #define F3DAUDIO_H 33 | 34 | #ifdef _WIN32 35 | #define F3DAUDIOAPI __declspec(dllexport) 36 | #else 37 | #define F3DAUDIOAPI 38 | #endif 39 | 40 | #include 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif /* __cplusplus */ 45 | 46 | /* Constants */ 47 | 48 | #ifndef _SPEAKER_POSITIONS_ 49 | #define SPEAKER_FRONT_LEFT 0x00000001 50 | #define SPEAKER_FRONT_RIGHT 0x00000002 51 | #define SPEAKER_FRONT_CENTER 0x00000004 52 | #define SPEAKER_LOW_FREQUENCY 0x00000008 53 | #define SPEAKER_BACK_LEFT 0x00000010 54 | #define SPEAKER_BACK_RIGHT 0x00000020 55 | #define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040 56 | #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080 57 | #define SPEAKER_BACK_CENTER 0x00000100 58 | #define SPEAKER_SIDE_LEFT 0x00000200 59 | #define SPEAKER_SIDE_RIGHT 0x00000400 60 | #define SPEAKER_TOP_CENTER 0x00000800 61 | #define SPEAKER_TOP_FRONT_LEFT 0x00001000 62 | #define SPEAKER_TOP_FRONT_CENTER 0x00002000 63 | #define SPEAKER_TOP_FRONT_RIGHT 0x00004000 64 | #define SPEAKER_TOP_BACK_LEFT 0x00008000 65 | #define SPEAKER_TOP_BACK_CENTER 0x00010000 66 | #define SPEAKER_TOP_BACK_RIGHT 0x00020000 67 | #define _SPEAKER_POSITIONS_ 68 | #endif 69 | 70 | #ifndef SPEAKER_MONO 71 | #define SPEAKER_MONO SPEAKER_FRONT_CENTER 72 | #define SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) 73 | #define SPEAKER_2POINT1 \ 74 | ( SPEAKER_FRONT_LEFT | \ 75 | SPEAKER_FRONT_RIGHT | \ 76 | SPEAKER_LOW_FREQUENCY ) 77 | #define SPEAKER_SURROUND \ 78 | ( SPEAKER_FRONT_LEFT | \ 79 | SPEAKER_FRONT_RIGHT | \ 80 | SPEAKER_FRONT_CENTER | \ 81 | SPEAKER_BACK_CENTER ) 82 | #define SPEAKER_QUAD \ 83 | ( SPEAKER_FRONT_LEFT | \ 84 | SPEAKER_FRONT_RIGHT | \ 85 | SPEAKER_BACK_LEFT | \ 86 | SPEAKER_BACK_RIGHT ) 87 | #define SPEAKER_4POINT1 \ 88 | ( SPEAKER_FRONT_LEFT | \ 89 | SPEAKER_FRONT_RIGHT | \ 90 | SPEAKER_LOW_FREQUENCY | \ 91 | SPEAKER_BACK_LEFT | \ 92 | SPEAKER_BACK_RIGHT ) 93 | #define SPEAKER_5POINT1 \ 94 | ( SPEAKER_FRONT_LEFT | \ 95 | SPEAKER_FRONT_RIGHT | \ 96 | SPEAKER_FRONT_CENTER | \ 97 | SPEAKER_LOW_FREQUENCY | \ 98 | SPEAKER_BACK_LEFT | \ 99 | SPEAKER_BACK_RIGHT ) 100 | #define SPEAKER_7POINT1 \ 101 | ( SPEAKER_FRONT_LEFT | \ 102 | SPEAKER_FRONT_RIGHT | \ 103 | SPEAKER_FRONT_CENTER | \ 104 | SPEAKER_LOW_FREQUENCY | \ 105 | SPEAKER_BACK_LEFT | \ 106 | SPEAKER_BACK_RIGHT | \ 107 | SPEAKER_FRONT_LEFT_OF_CENTER | \ 108 | SPEAKER_FRONT_RIGHT_OF_CENTER ) 109 | #define SPEAKER_5POINT1_SURROUND \ 110 | ( SPEAKER_FRONT_LEFT | \ 111 | SPEAKER_FRONT_RIGHT | \ 112 | SPEAKER_FRONT_CENTER | \ 113 | SPEAKER_LOW_FREQUENCY | \ 114 | SPEAKER_SIDE_LEFT | \ 115 | SPEAKER_SIDE_RIGHT ) 116 | #define SPEAKER_7POINT1_SURROUND \ 117 | ( SPEAKER_FRONT_LEFT | \ 118 | SPEAKER_FRONT_RIGHT | \ 119 | SPEAKER_FRONT_CENTER | \ 120 | SPEAKER_LOW_FREQUENCY | \ 121 | SPEAKER_BACK_LEFT | \ 122 | SPEAKER_BACK_RIGHT | \ 123 | SPEAKER_SIDE_LEFT | \ 124 | SPEAKER_SIDE_RIGHT ) 125 | #define SPEAKER_XBOX SPEAKER_5POINT1 126 | #endif 127 | 128 | #define F3DAUDIO_PI 3.141592654f 129 | #define F3DAUDIO_2PI 6.283185307f 130 | 131 | #define F3DAUDIO_CALCULATE_MATRIX 0x00000001 132 | #define F3DAUDIO_CALCULATE_DELAY 0x00000002 133 | #define F3DAUDIO_CALCULATE_LPF_DIRECT 0x00000004 134 | #define F3DAUDIO_CALCULATE_LPF_REVERB 0x00000008 135 | #define F3DAUDIO_CALCULATE_REVERB 0x00000010 136 | #define F3DAUDIO_CALCULATE_DOPPLER 0x00000020 137 | #define F3DAUDIO_CALCULATE_EMITTER_ANGLE 0x00000040 138 | #define F3DAUDIO_CALCULATE_ZEROCENTER 0x00010000 139 | #define F3DAUDIO_CALCULATE_REDIRECT_TO_LFE 0x00020000 140 | 141 | /* Type Declarations */ 142 | 143 | #define F3DAUDIO_HANDLE_BYTESIZE 20 144 | typedef uint8_t F3DAUDIO_HANDLE[F3DAUDIO_HANDLE_BYTESIZE]; 145 | 146 | /* Structures */ 147 | 148 | #pragma pack(push, 1) 149 | 150 | typedef struct F3DAUDIO_VECTOR 151 | { 152 | float x; 153 | float y; 154 | float z; 155 | } F3DAUDIO_VECTOR; 156 | 157 | typedef struct F3DAUDIO_DISTANCE_CURVE_POINT 158 | { 159 | float Distance; 160 | float DSPSetting; 161 | } F3DAUDIO_DISTANCE_CURVE_POINT; 162 | 163 | typedef struct F3DAUDIO_DISTANCE_CURVE 164 | { 165 | F3DAUDIO_DISTANCE_CURVE_POINT *pPoints; 166 | uint32_t PointCount; 167 | } F3DAUDIO_DISTANCE_CURVE; 168 | 169 | typedef struct F3DAUDIO_CONE 170 | { 171 | float InnerAngle; 172 | float OuterAngle; 173 | float InnerVolume; 174 | float OuterVolume; 175 | float InnerLPF; 176 | float OuterLPF; 177 | float InnerReverb; 178 | float OuterReverb; 179 | } F3DAUDIO_CONE; 180 | 181 | typedef struct F3DAUDIO_LISTENER 182 | { 183 | F3DAUDIO_VECTOR OrientFront; 184 | F3DAUDIO_VECTOR OrientTop; 185 | F3DAUDIO_VECTOR Position; 186 | F3DAUDIO_VECTOR Velocity; 187 | F3DAUDIO_CONE *pCone; 188 | } F3DAUDIO_LISTENER; 189 | 190 | typedef struct F3DAUDIO_EMITTER 191 | { 192 | F3DAUDIO_CONE *pCone; 193 | F3DAUDIO_VECTOR OrientFront; 194 | F3DAUDIO_VECTOR OrientTop; 195 | F3DAUDIO_VECTOR Position; 196 | F3DAUDIO_VECTOR Velocity; 197 | float InnerRadius; 198 | float InnerRadiusAngle; 199 | uint32_t ChannelCount; 200 | float ChannelRadius; 201 | float *pChannelAzimuths; 202 | F3DAUDIO_DISTANCE_CURVE *pVolumeCurve; 203 | F3DAUDIO_DISTANCE_CURVE *pLFECurve; 204 | F3DAUDIO_DISTANCE_CURVE *pLPFDirectCurve; 205 | F3DAUDIO_DISTANCE_CURVE *pLPFReverbCurve; 206 | F3DAUDIO_DISTANCE_CURVE *pReverbCurve; 207 | float CurveDistanceScaler; 208 | float DopplerScaler; 209 | } F3DAUDIO_EMITTER; 210 | 211 | #ifndef F3DAUDIO_DSP_SETTINGS_DECL 212 | #define F3DAUDIO_DSP_SETTINGS_DECL 213 | typedef struct F3DAUDIO_DSP_SETTINGS F3DAUDIO_DSP_SETTINGS; 214 | #endif /* F3DAUDIO_DSP_SETTINGS_DECL */ 215 | 216 | struct F3DAUDIO_DSP_SETTINGS 217 | { 218 | float *pMatrixCoefficients; 219 | float *pDelayTimes; 220 | uint32_t SrcChannelCount; 221 | uint32_t DstChannelCount; 222 | float LPFDirectCoefficient; 223 | float LPFReverbCoefficient; 224 | float ReverbLevel; 225 | float DopplerFactor; 226 | float EmitterToListenerAngle; 227 | float EmitterToListenerDistance; 228 | float EmitterVelocityComponent; 229 | float ListenerVelocityComponent; 230 | }; 231 | 232 | #pragma pack(pop) 233 | 234 | /* Functions */ 235 | 236 | F3DAUDIOAPI void F3DAudioInitialize( 237 | uint32_t SpeakerChannelMask, 238 | float SpeedOfSound, 239 | F3DAUDIO_HANDLE Instance 240 | ); 241 | 242 | F3DAUDIOAPI uint32_t F3DAudioInitialize8( 243 | uint32_t SpeakerChannelMask, 244 | float SpeedOfSound, 245 | F3DAUDIO_HANDLE Instance 246 | ); 247 | 248 | F3DAUDIOAPI void F3DAudioCalculate( 249 | const F3DAUDIO_HANDLE Instance, 250 | const F3DAUDIO_LISTENER *pListener, 251 | const F3DAUDIO_EMITTER *pEmitter, 252 | uint32_t Flags, 253 | F3DAUDIO_DSP_SETTINGS *pDSPSettings 254 | ); 255 | 256 | #ifdef __cplusplus 257 | } 258 | #endif /* __cplusplus */ 259 | 260 | #endif /* F3DAUDIO_H */ 261 | 262 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 263 | -------------------------------------------------------------------------------- /include/FACT3D.h: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | /* This file has no documentation since you are expected to already know how 28 | * XACT works if you are still using these APIs! 29 | */ 30 | 31 | #ifndef FACT3D_H 32 | #define FACT3D_H 33 | 34 | #include "F3DAudio.h" 35 | #include "FACT.h" 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif /* __cplusplus */ 40 | 41 | /* Constants */ 42 | 43 | #define LEFT_AZIMUTH (3.0f * F3DAUDIO_PI / 2.0f) 44 | #define RIGHT_AZIMUTH (F3DAUDIO_PI / 2.0f) 45 | #define FRONT_LEFT_AZIMUTH (7.0f * F3DAUDIO_PI / 4.0f) 46 | #define FRONT_RIGHT_AZIMUTH (F3DAUDIO_PI / 4.0f) 47 | #define FRONT_CENTER_AZIMUTH 0.0f 48 | #define LOW_FREQUENCY_AZIMUTH F3DAUDIO_2PI 49 | #define BACK_LEFT_AZIMUTH (5.0f * F3DAUDIO_PI / 4.0f) 50 | #define BACK_RIGHT_AZIMUTH (3.0f * F3DAUDIO_PI / 4.0f) 51 | #define BACK_CENTER_AZIMUTH F3DAUDIO_PI 52 | #define FRONT_LEFT_OF_CENTER_AZIMUTH (15.0f * F3DAUDIO_PI / 8.0f) 53 | #define FRONT_RIGHT_OF_CENTER_AZIMUTH (F3DAUDIO_PI / 8.0f) 54 | 55 | static const float aStereoLayout[] = 56 | { 57 | LEFT_AZIMUTH, 58 | RIGHT_AZIMUTH 59 | }; 60 | static const float a2Point1Layout[] = 61 | { 62 | LEFT_AZIMUTH, 63 | RIGHT_AZIMUTH, 64 | LOW_FREQUENCY_AZIMUTH 65 | }; 66 | static const float aQuadLayout[] = 67 | { 68 | FRONT_LEFT_AZIMUTH, 69 | FRONT_RIGHT_AZIMUTH, 70 | BACK_LEFT_AZIMUTH, 71 | BACK_RIGHT_AZIMUTH 72 | }; 73 | static const float a4Point1Layout[] = 74 | { 75 | FRONT_LEFT_AZIMUTH, 76 | FRONT_RIGHT_AZIMUTH, 77 | LOW_FREQUENCY_AZIMUTH, 78 | BACK_LEFT_AZIMUTH, 79 | BACK_RIGHT_AZIMUTH 80 | }; 81 | static const float a5Point1Layout[] = 82 | { 83 | FRONT_LEFT_AZIMUTH, 84 | FRONT_RIGHT_AZIMUTH, 85 | FRONT_CENTER_AZIMUTH, 86 | LOW_FREQUENCY_AZIMUTH, 87 | BACK_LEFT_AZIMUTH, 88 | BACK_RIGHT_AZIMUTH 89 | }; 90 | static const float a7Point1Layout[] = 91 | { 92 | FRONT_LEFT_AZIMUTH, 93 | FRONT_RIGHT_AZIMUTH, 94 | FRONT_CENTER_AZIMUTH, 95 | LOW_FREQUENCY_AZIMUTH, 96 | BACK_LEFT_AZIMUTH, 97 | BACK_RIGHT_AZIMUTH, 98 | LEFT_AZIMUTH, 99 | RIGHT_AZIMUTH 100 | }; 101 | 102 | /* Functions */ 103 | 104 | FACTAPI uint32_t FACT3DInitialize( 105 | FACTAudioEngine *pEngine, 106 | F3DAUDIO_HANDLE F3DInstance 107 | ); 108 | 109 | FACTAPI uint32_t FACT3DCalculate( 110 | F3DAUDIO_HANDLE F3DInstance, 111 | const F3DAUDIO_LISTENER *pListener, 112 | F3DAUDIO_EMITTER *pEmitter, 113 | F3DAUDIO_DSP_SETTINGS *pDSPSettings 114 | ); 115 | 116 | FACTAPI uint32_t FACT3DApply( 117 | F3DAUDIO_DSP_SETTINGS *pDSPSettings, 118 | FACTCue *pCue 119 | ); 120 | 121 | #ifdef __cplusplus 122 | } 123 | #endif /* __cplusplus */ 124 | 125 | #endif /* FACT3D_H */ 126 | 127 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 128 | -------------------------------------------------------------------------------- /include/FAPO.h: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | /* This file has no documentation since the MSDN docs are still perfectly fine: 28 | * https://docs.microsoft.com/en-us/windows/desktop/api/xapo/ 29 | * 30 | * Of course, the APIs aren't exactly the same since XAPO is super dependent on 31 | * C++. Instead, we use a struct full of functions to mimic a vtable. 32 | * 33 | * The only serious difference is that our FAPO (yes, really) always has the 34 | * Get/SetParameters function pointers, for simplicity. You can ignore these if 35 | * your effect does not have parameters, as they will never get called unless 36 | * it is explicitly requested by the application. 37 | */ 38 | 39 | #ifndef FAPO_H 40 | #define FAPO_H 41 | 42 | #include "FAudio.h" 43 | 44 | #define FAPOAPI FAUDIOAPI 45 | #define FAPOCALL FAUDIOCALL 46 | 47 | #ifdef __cplusplus 48 | extern "C" { 49 | #endif /* __cplusplus */ 50 | 51 | /* Enumerations */ 52 | 53 | typedef enum FAPOBufferFlags 54 | { 55 | FAPO_BUFFER_SILENT, 56 | FAPO_BUFFER_VALID 57 | } FAPOBufferFlags; 58 | 59 | /* Structures */ 60 | 61 | #pragma pack(push, 1) 62 | 63 | typedef struct FAPORegistrationProperties 64 | { 65 | FAudioGUID clsid; 66 | int16_t FriendlyName[256]; /* Win32 wchar_t */ 67 | int16_t CopyrightInfo[256]; /* Win32 wchar_t */ 68 | uint32_t MajorVersion; 69 | uint32_t MinorVersion; 70 | uint32_t Flags; 71 | uint32_t MinInputBufferCount; 72 | uint32_t MaxInputBufferCount; 73 | uint32_t MinOutputBufferCount; 74 | uint32_t MaxOutputBufferCount; 75 | } FAPORegistrationProperties; 76 | 77 | typedef struct FAPOLockForProcessBufferParameters 78 | { 79 | const FAudioWaveFormatEx *pFormat; 80 | uint32_t MaxFrameCount; 81 | } FAPOLockForProcessBufferParameters; 82 | 83 | typedef struct FAPOProcessBufferParameters 84 | { 85 | void* pBuffer; 86 | FAPOBufferFlags BufferFlags; 87 | uint32_t ValidFrameCount; 88 | } FAPOProcessBufferParameters; 89 | 90 | #pragma pack(pop) 91 | 92 | /* Constants */ 93 | 94 | #define FAPO_MIN_CHANNELS 1 95 | #define FAPO_MAX_CHANNELS 64 96 | 97 | #define FAPO_MIN_FRAMERATE 1000 98 | #define FAPO_MAX_FRAMERATE 200000 99 | 100 | #define FAPO_REGISTRATION_STRING_LENGTH 256 101 | 102 | #define FAPO_FLAG_CHANNELS_MUST_MATCH 0x00000001 103 | #define FAPO_FLAG_FRAMERATE_MUST_MATCH 0x00000002 104 | #define FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH 0x00000004 105 | #define FAPO_FLAG_BUFFERCOUNT_MUST_MATCH 0x00000008 106 | #define FAPO_FLAG_INPLACE_REQUIRED 0x00000020 107 | #define FAPO_FLAG_INPLACE_SUPPORTED 0x00000010 108 | 109 | /* FAPO Interface */ 110 | 111 | #ifndef FAPO_DECL 112 | #define FAPO_DECL 113 | typedef struct FAPO FAPO; 114 | #endif /* FAPO_DECL */ 115 | 116 | typedef int32_t (FAPOCALL * AddRefFunc)( 117 | void *fapo 118 | ); 119 | typedef int32_t (FAPOCALL * ReleaseFunc)( 120 | void *fapo 121 | ); 122 | typedef uint32_t (FAPOCALL * GetRegistrationPropertiesFunc)( 123 | void* fapo, 124 | FAPORegistrationProperties **ppRegistrationProperties 125 | ); 126 | typedef uint32_t (FAPOCALL * IsInputFormatSupportedFunc)( 127 | void* fapo, 128 | const FAudioWaveFormatEx *pOutputFormat, 129 | const FAudioWaveFormatEx *pRequestedInputFormat, 130 | FAudioWaveFormatEx **ppSupportedInputFormat 131 | ); 132 | typedef uint32_t (FAPOCALL * IsOutputFormatSupportedFunc)( 133 | void* fapo, 134 | const FAudioWaveFormatEx *pInputFormat, 135 | const FAudioWaveFormatEx *pRequestedOutputFormat, 136 | FAudioWaveFormatEx **ppSupportedOutputFormat 137 | ); 138 | typedef uint32_t (FAPOCALL * InitializeFunc)( 139 | void* fapo, 140 | const void* pData, 141 | uint32_t DataByteSize 142 | ); 143 | typedef void (FAPOCALL * ResetFunc)( 144 | void* fapo 145 | ); 146 | typedef uint32_t (FAPOCALL * LockForProcessFunc)( 147 | void* fapo, 148 | uint32_t InputLockedParameterCount, 149 | const FAPOLockForProcessBufferParameters *pInputLockedParameters, 150 | uint32_t OutputLockedParameterCount, 151 | const FAPOLockForProcessBufferParameters *pOutputLockedParameters 152 | ); 153 | typedef void (FAPOCALL * UnlockForProcessFunc)( 154 | void* fapo 155 | ); 156 | typedef void (FAPOCALL * ProcessFunc)( 157 | void* fapo, 158 | uint32_t InputProcessParameterCount, 159 | const FAPOProcessBufferParameters* pInputProcessParameters, 160 | uint32_t OutputProcessParameterCount, 161 | FAPOProcessBufferParameters* pOutputProcessParameters, 162 | int32_t IsEnabled 163 | ); 164 | typedef uint32_t (FAPOCALL * CalcInputFramesFunc)( 165 | void* fapo, 166 | uint32_t OutputFrameCount 167 | ); 168 | typedef uint32_t (FAPOCALL * CalcOutputFramesFunc)( 169 | void* fapo, 170 | uint32_t InputFrameCount 171 | ); 172 | typedef void (FAPOCALL * SetParametersFunc)( 173 | void* fapo, 174 | const void* pParameters, 175 | uint32_t ParameterByteSize 176 | ); 177 | typedef void (FAPOCALL * GetParametersFunc)( 178 | void* fapo, 179 | void* pParameters, 180 | uint32_t ParameterByteSize 181 | ); 182 | 183 | struct FAPO 184 | { 185 | AddRefFunc AddRef; 186 | ReleaseFunc Release; 187 | GetRegistrationPropertiesFunc GetRegistrationProperties; 188 | IsInputFormatSupportedFunc IsInputFormatSupported; 189 | IsOutputFormatSupportedFunc IsOutputFormatSupported; 190 | InitializeFunc Initialize; 191 | ResetFunc Reset; 192 | LockForProcessFunc LockForProcess; 193 | UnlockForProcessFunc UnlockForProcess; 194 | ProcessFunc Process; 195 | CalcInputFramesFunc CalcInputFrames; 196 | CalcOutputFramesFunc CalcOutputFrames; 197 | SetParametersFunc SetParameters; 198 | GetParametersFunc GetParameters; 199 | }; 200 | 201 | #ifdef __cplusplus 202 | } 203 | #endif /* __cplusplus */ 204 | 205 | #endif /* FAPO_H */ 206 | 207 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 208 | -------------------------------------------------------------------------------- /include/FAPOBase.h: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | /* This file has no documentation since the MSDN docs are still perfectly fine: 28 | * https://docs.microsoft.com/en-us/windows/desktop/api/xapobase/ 29 | * 30 | * Of course, the APIs aren't exactly the same since XAPO is super dependent on 31 | * C++. Instead, we use a struct full of functions to mimic a vtable. 32 | * 33 | * To mimic the CXAPOParametersBase experience, initialize the object like this: 34 | * 35 | * extern FAPORegistrationProperties MyFAPOProperties; 36 | * extern int32_t producer; 37 | * typedef struct MyFAPOParams 38 | * { 39 | * uint32_t something; 40 | * } MyFAPOParams; 41 | * typedef struct MyFAPO 42 | * { 43 | * FAPOBase base; 44 | * uint32_t somethingElse; 45 | * } MyFAPO; 46 | * void MyFAPO_Free(void* fapo) 47 | * { 48 | * MyFAPO *mine = (MyFAPO*) fapo; 49 | * mine->base.pFree(mine->base.m_pParameterBlocks); 50 | * mine->base.pFree(fapo); 51 | * } 52 | * 53 | * MyFAPO *result = (MyFAPO*) SDL_malloc(sizeof(MyFAPO)); 54 | * uint8_t *params = (uint8_t*) SDL_malloc(sizeof(MyFAPOParams) * 3); 55 | * CreateFAPOBase( 56 | * &result->base, 57 | * &MyFAPOProperties, 58 | * params, 59 | * sizeof(MyFAPOParams), 60 | * producer 61 | * ); 62 | * result->base.base.Initialize = (InitializeFunc) MyFAPO_Initialize; 63 | * result->base.base.Process = (ProcessFunc) MyFAPO_Process; 64 | * result->base.Destructor = MyFAPO_Free; 65 | */ 66 | 67 | #ifndef FAPOBASE_H 68 | #define FAPOBASE_H 69 | 70 | #include "FAPO.h" 71 | 72 | #ifdef __cplusplus 73 | extern "C" { 74 | #endif /* __cplusplus */ 75 | 76 | /* Constants */ 77 | 78 | #define FAPOBASE_DEFAULT_FORMAT_TAG FAUDIO_FORMAT_IEEE_FLOAT 79 | #define FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS FAPO_MIN_CHANNELS 80 | #define FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS FAPO_MAX_CHANNELS 81 | #define FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE FAPO_MIN_FRAMERATE 82 | #define FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE FAPO_MAX_FRAMERATE 83 | #define FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE 32 84 | 85 | #define FAPOBASE_DEFAULT_FLAG ( \ 86 | FAPO_FLAG_CHANNELS_MUST_MATCH | \ 87 | FAPO_FLAG_FRAMERATE_MUST_MATCH | \ 88 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | \ 89 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | \ 90 | FAPO_FLAG_INPLACE_SUPPORTED \ 91 | ) 92 | 93 | #define FAPOBASE_DEFAULT_BUFFER_COUNT 1 94 | 95 | /* FAPOBase Interface */ 96 | 97 | typedef struct FAPOBase FAPOBase; 98 | 99 | typedef void (FAPOCALL * OnSetParametersFunc)( 100 | FAPOBase *fapo, 101 | const void* parameters, 102 | uint32_t parametersSize 103 | ); 104 | 105 | #pragma pack(push, 8) 106 | struct FAPOBase 107 | { 108 | /* Base Classes/Interfaces */ 109 | FAPO base; 110 | void (FAPOCALL *Destructor)(void*); 111 | 112 | /* Public Virtual Functions */ 113 | OnSetParametersFunc OnSetParameters; 114 | 115 | /* Private Variables */ 116 | const FAPORegistrationProperties *m_pRegistrationProperties; 117 | void* m_pfnMatrixMixFunction; 118 | float *m_pfl32MatrixCoefficients; 119 | uint32_t m_nSrcFormatType; 120 | uint8_t m_fIsScalarMatrix; 121 | uint8_t m_fIsLocked; 122 | uint8_t *m_pParameterBlocks; 123 | uint8_t *m_pCurrentParameters; 124 | uint8_t *m_pCurrentParametersInternal; 125 | uint32_t m_uCurrentParametersIndex; 126 | uint32_t m_uParameterBlockByteSize; 127 | uint8_t m_fNewerResultsReady; 128 | uint8_t m_fProducer; 129 | 130 | /* Protected Variables */ 131 | int32_t m_lReferenceCount; /* LONG */ 132 | 133 | /* Allocator callbacks, NOT part of XAPOBase spec! */ 134 | FAudioMallocFunc pMalloc; 135 | FAudioFreeFunc pFree; 136 | FAudioReallocFunc pRealloc; 137 | }; 138 | #pragma pack(pop) 139 | 140 | FAPOAPI void CreateFAPOBase( 141 | FAPOBase *fapo, 142 | const FAPORegistrationProperties *pRegistrationProperties, 143 | uint8_t *pParameterBlocks, 144 | uint32_t uParameterBlockByteSize, 145 | uint8_t fProducer 146 | ); 147 | 148 | /* See "extensions/CustomAllocatorEXT.txt" for more information. */ 149 | FAPOAPI void CreateFAPOBaseWithCustomAllocatorEXT( 150 | FAPOBase *fapo, 151 | const FAPORegistrationProperties *pRegistrationProperties, 152 | uint8_t *pParameterBlocks, 153 | uint32_t uParameterBlockByteSize, 154 | uint8_t fProducer, 155 | FAudioMallocFunc customMalloc, 156 | FAudioFreeFunc customFree, 157 | FAudioReallocFunc customRealloc 158 | ); 159 | 160 | FAPOAPI int32_t FAPOBase_AddRef(FAPOBase *fapo); 161 | 162 | FAPOAPI int32_t FAPOBase_Release(FAPOBase *fapo); 163 | 164 | FAPOAPI uint32_t FAPOBase_GetRegistrationProperties( 165 | FAPOBase *fapo, 166 | FAPORegistrationProperties **ppRegistrationProperties 167 | ); 168 | 169 | FAPOAPI uint32_t FAPOBase_IsInputFormatSupported( 170 | FAPOBase *fapo, 171 | const FAudioWaveFormatEx *pOutputFormat, 172 | const FAudioWaveFormatEx *pRequestedInputFormat, 173 | FAudioWaveFormatEx **ppSupportedInputFormat 174 | ); 175 | 176 | FAPOAPI uint32_t FAPOBase_IsOutputFormatSupported( 177 | FAPOBase *fapo, 178 | const FAudioWaveFormatEx *pInputFormat, 179 | const FAudioWaveFormatEx *pRequestedOutputFormat, 180 | FAudioWaveFormatEx **ppSupportedOutputFormat 181 | ); 182 | 183 | FAPOAPI uint32_t FAPOBase_Initialize( 184 | FAPOBase *fapo, 185 | const void* pData, 186 | uint32_t DataByteSize 187 | ); 188 | 189 | FAPOAPI void FAPOBase_Reset(FAPOBase *fapo); 190 | 191 | FAPOAPI uint32_t FAPOBase_LockForProcess( 192 | FAPOBase *fapo, 193 | uint32_t InputLockedParameterCount, 194 | const FAPOLockForProcessBufferParameters *pInputLockedParameters, 195 | uint32_t OutputLockedParameterCount, 196 | const FAPOLockForProcessBufferParameters *pOutputLockedParameters 197 | ); 198 | 199 | FAPOAPI void FAPOBase_UnlockForProcess(FAPOBase *fapo); 200 | 201 | FAPOAPI uint32_t FAPOBase_CalcInputFrames( 202 | FAPOBase *fapo, 203 | uint32_t OutputFrameCount 204 | ); 205 | 206 | FAPOAPI uint32_t FAPOBase_CalcOutputFrames( 207 | FAPOBase *fapo, 208 | uint32_t InputFrameCount 209 | ); 210 | 211 | FAPOAPI uint32_t FAPOBase_ValidateFormatDefault( 212 | FAPOBase *fapo, 213 | FAudioWaveFormatEx *pFormat, 214 | uint8_t fOverwrite 215 | ); 216 | 217 | FAPOAPI uint32_t FAPOBase_ValidateFormatPair( 218 | FAPOBase *fapo, 219 | const FAudioWaveFormatEx *pSupportedFormat, 220 | FAudioWaveFormatEx *pRequestedFormat, 221 | uint8_t fOverwrite 222 | ); 223 | 224 | FAPOAPI void FAPOBase_ProcessThru( 225 | FAPOBase *fapo, 226 | void* pInputBuffer, 227 | float *pOutputBuffer, 228 | uint32_t FrameCount, 229 | uint16_t InputChannelCount, 230 | uint16_t OutputChannelCount, 231 | uint8_t MixWithOutput 232 | ); 233 | 234 | FAPOAPI void FAPOBase_SetParameters( 235 | FAPOBase *fapo, 236 | const void* pParameters, 237 | uint32_t ParameterByteSize 238 | ); 239 | 240 | FAPOAPI void FAPOBase_GetParameters( 241 | FAPOBase *fapo, 242 | void* pParameters, 243 | uint32_t ParameterByteSize 244 | ); 245 | 246 | FAPOAPI void FAPOBase_OnSetParameters( 247 | FAPOBase *fapo, 248 | const void* parameters, 249 | uint32_t parametersSize 250 | ); 251 | 252 | FAPOAPI uint8_t FAPOBase_ParametersChanged(FAPOBase *fapo); 253 | 254 | FAPOAPI uint8_t* FAPOBase_BeginProcess(FAPOBase *fapo); 255 | 256 | FAPOAPI void FAPOBase_EndProcess(FAPOBase *fapo); 257 | 258 | #ifdef __cplusplus 259 | } 260 | #endif /* __cplusplus */ 261 | 262 | #endif /* FAPOBASE_H */ 263 | 264 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 265 | -------------------------------------------------------------------------------- /include/FAPOFX.h: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #ifndef FAPOFX_H 28 | #define FAPOFX_H 29 | 30 | #include "FAPO.h" 31 | 32 | #define FAPOFXAPI FAUDIOAPI 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif /* __cplusplus */ 37 | 38 | /* GUIDs */ 39 | 40 | /* "Legacy" GUIDs are from XAPOFX <= 1.5. They were removed in XAudio 2.8 and later. */ 41 | extern const FAudioGUID FAPOFX_CLSID_FXEQ, FAPOFX_CLSID_FXEQ_LEGACY; 42 | extern const FAudioGUID FAPOFX_CLSID_FXMasteringLimiter, FAPOFX_CLSID_FXMasteringLimiter_LEGACY; 43 | extern const FAudioGUID FAPOFX_CLSID_FXReverb, FAPOFX_CLSID_FXReverb_LEGACY; 44 | extern const FAudioGUID FAPOFX_CLSID_FXEcho, FAPOFX_CLSID_FXEcho_LEGACY; 45 | 46 | /* Structures */ 47 | 48 | #pragma pack(push, 1) 49 | 50 | /* See FAPOFXEQ_* constants below. 51 | * FrequencyCenter is in Hz, Gain is amplitude ratio, Bandwidth is Q factor. 52 | */ 53 | typedef struct FAPOFXEQParameters 54 | { 55 | float FrequencyCenter0; 56 | float Gain0; 57 | float Bandwidth0; 58 | float FrequencyCenter1; 59 | float Gain1; 60 | float Bandwidth1; 61 | float FrequencyCenter2; 62 | float Gain2; 63 | float Bandwidth2; 64 | float FrequencyCenter3; 65 | float Gain3; 66 | float Bandwidth3; 67 | } FAPOFXEQParameters; 68 | 69 | /* See FAPOFXMASTERINGLIMITER_* constants below. */ 70 | typedef struct FAPOFXMasteringLimiterParameters 71 | { 72 | uint32_t Release; /* In milliseconds */ 73 | uint32_t Loudness; /* In... uh, MSDN doesn't actually say what. */ 74 | } FAPOFXMasteringLimiterParameters; 75 | 76 | /* See FAPOFXREVERB_* constants below. 77 | * Both parameters are arbitrary and should be treated subjectively. 78 | */ 79 | typedef struct FAPOFXReverbParameters 80 | { 81 | float Diffusion; 82 | float RoomSize; 83 | } FAPOFXReverbParameters; 84 | 85 | /* See FAPOFXECHO_* constants below. */ 86 | typedef struct FAPOFXEchoParameters 87 | { 88 | float WetDryMix; /* Percentage of processed signal vs original */ 89 | float Feedback; /* Percentage to feed back into input */ 90 | float Delay; /* In milliseconds */ 91 | } FAPOFXEchoParameters; 92 | 93 | #pragma pack(pop) 94 | 95 | /* Constants */ 96 | 97 | #define FAPOFXEQ_MIN_FRAMERATE 22000 98 | #define FAPOFXEQ_MAX_FRAMERATE 48000 99 | 100 | #define FAPOFXEQ_MIN_FREQUENCY_CENTER 20.0f 101 | #define FAPOFXEQ_MAX_FREQUENCY_CENTER 20000.0f 102 | #define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_0 100.0f 103 | #define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_1 800.0f 104 | #define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_2 2000.0f 105 | #define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_3 10000.0f 106 | 107 | #define FAPOFXEQ_MIN_GAIN 0.126f 108 | #define FAPOFXEQ_MAX_GAIN 7.94f 109 | #define FAPOFXEQ_DEFAULT_GAIN 1.0f 110 | 111 | #define FAPOFXEQ_MIN_BANDWIDTH 0.1f 112 | #define FAPOFXEQ_MAX_BANDWIDTH 2.0f 113 | #define FAPOFXEQ_DEFAULT_BANDWIDTH 1.0f 114 | 115 | #define FAPOFXMASTERINGLIMITER_MIN_RELEASE 1 116 | #define FAPOFXMASTERINGLIMITER_MAX_RELEASE 20 117 | #define FAPOFXMASTERINGLIMITER_DEFAULT_RELEASE 6 118 | 119 | #define FAPOFXMASTERINGLIMITER_MIN_LOUDNESS 1 120 | #define FAPOFXMASTERINGLIMITER_MAX_LOUDNESS 1800 121 | #define FAPOFXMASTERINGLIMITER_DEFAULT_LOUDNESS 1000 122 | 123 | #define FAPOFXREVERB_MIN_DIFFUSION 0.0f 124 | #define FAPOFXREVERB_MAX_DIFFUSION 1.0f 125 | #define FAPOFXREVERB_DEFAULT_DIFFUSION 0.9f 126 | 127 | #define FAPOFXREVERB_MIN_ROOMSIZE 0.0001f 128 | #define FAPOFXREVERB_MAX_ROOMSIZE 1.0f 129 | #define FAPOFXREVERB_DEFAULT_ROOMSIZE 0.6f 130 | 131 | #define FAPOFXECHO_MIN_WETDRYMIX 0.0f 132 | #define FAPOFXECHO_MAX_WETDRYMIX 1.0f 133 | #define FAPOFXECHO_DEFAULT_WETDRYMIX 0.5f 134 | 135 | #define FAPOFXECHO_MIN_FEEDBACK 0.0f 136 | #define FAPOFXECHO_MAX_FEEDBACK 1.0f 137 | #define FAPOFXECHO_DEFAULT_FEEDBACK 0.5f 138 | 139 | #define FAPOFXECHO_MIN_DELAY 1.0f 140 | #define FAPOFXECHO_MAX_DELAY 2000.0f 141 | #define FAPOFXECHO_DEFAULT_DELAY 500.0f 142 | 143 | /* Functions */ 144 | 145 | /* Creates an effect from the pre-made FAPOFX effect library. 146 | * 147 | * clsid: A reference to one of the FAPOFX_CLSID_* GUIDs 148 | * pEffect: Filled with the resulting FAPO object 149 | * pInitData: Starting parameters, pass NULL to use the default values 150 | * InitDataByteSize: Parameter struct size, pass 0 if pInitData is NULL 151 | * 152 | * Returns 0 on success. 153 | */ 154 | FAPOFXAPI uint32_t FAPOFX_CreateFX( 155 | const FAudioGUID *clsid, 156 | FAPO **pEffect, 157 | const void *pInitData, 158 | uint32_t InitDataByteSize 159 | ); 160 | 161 | /* See "extensions/CustomAllocatorEXT.txt" for more details. */ 162 | FAPOFXAPI uint32_t FAPOFX_CreateFXWithCustomAllocatorEXT( 163 | const FAudioGUID *clsid, 164 | FAPO **pEffect, 165 | const void *pInitData, 166 | uint32_t InitDataByteSize, 167 | FAudioMallocFunc customMalloc, 168 | FAudioFreeFunc customFree, 169 | FAudioReallocFunc customRealloc 170 | ); 171 | 172 | #ifdef __cplusplus 173 | } 174 | #endif /* __cplusplus */ 175 | 176 | #endif /* FAPOFX_H */ 177 | 178 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 179 | -------------------------------------------------------------------------------- /src/FACT3D.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #include "FACT3D.h" 28 | 29 | uint32_t FACT3DInitialize( 30 | FACTAudioEngine *pEngine, 31 | F3DAUDIO_HANDLE F3DInstance 32 | ) { 33 | float nSpeedOfSound; 34 | FAudioWaveFormatExtensible wfxFinalMixFormat; 35 | 36 | if (pEngine == NULL) 37 | { 38 | return 0; 39 | } 40 | 41 | FACTAudioEngine_GetGlobalVariable( 42 | pEngine, 43 | FACTAudioEngine_GetGlobalVariableIndex( 44 | pEngine, 45 | "SpeedOfSound" 46 | ), 47 | &nSpeedOfSound 48 | ); 49 | FACTAudioEngine_GetFinalMixFormat( 50 | pEngine, 51 | &wfxFinalMixFormat 52 | ); 53 | F3DAudioInitialize( 54 | wfxFinalMixFormat.dwChannelMask, 55 | nSpeedOfSound, 56 | F3DInstance 57 | ); 58 | return 0; 59 | } 60 | 61 | uint32_t FACT3DCalculate( 62 | F3DAUDIO_HANDLE F3DInstance, 63 | const F3DAUDIO_LISTENER *pListener, 64 | F3DAUDIO_EMITTER *pEmitter, 65 | F3DAUDIO_DSP_SETTINGS *pDSPSettings 66 | ) { 67 | static F3DAUDIO_DISTANCE_CURVE_POINT DefaultCurvePoints[2] = 68 | { 69 | { 0.0f, 1.0f }, 70 | { 1.0f, 1.0f } 71 | }; 72 | static F3DAUDIO_DISTANCE_CURVE DefaultCurve = 73 | { 74 | (F3DAUDIO_DISTANCE_CURVE_POINT*) &DefaultCurvePoints[0], 2 75 | }; 76 | 77 | if (pListener == NULL || pEmitter == NULL || pDSPSettings == NULL) 78 | { 79 | return 0; 80 | } 81 | 82 | if (pEmitter->ChannelCount > 1 && pEmitter->pChannelAzimuths == NULL) 83 | { 84 | pEmitter->ChannelRadius = 1.0f; 85 | 86 | if (pEmitter->ChannelCount == 2) 87 | { 88 | pEmitter->pChannelAzimuths = (float*) &aStereoLayout[0]; 89 | } 90 | else if (pEmitter->ChannelCount == 3) 91 | { 92 | pEmitter->pChannelAzimuths = (float*) &a2Point1Layout[0]; 93 | } 94 | else if (pEmitter->ChannelCount == 4) 95 | { 96 | pEmitter->pChannelAzimuths = (float*) &aQuadLayout[0]; 97 | } 98 | else if (pEmitter->ChannelCount == 5) 99 | { 100 | pEmitter->pChannelAzimuths = (float*) &a4Point1Layout[0]; 101 | } 102 | else if (pEmitter->ChannelCount == 6) 103 | { 104 | pEmitter->pChannelAzimuths = (float*) &a5Point1Layout[0]; 105 | } 106 | else if (pEmitter->ChannelCount == 8) 107 | { 108 | pEmitter->pChannelAzimuths = (float*) &a7Point1Layout[0]; 109 | } 110 | else 111 | { 112 | return 0; 113 | } 114 | } 115 | 116 | if (pEmitter->pVolumeCurve == NULL) 117 | { 118 | pEmitter->pVolumeCurve = &DefaultCurve; 119 | } 120 | if (pEmitter->pLFECurve == NULL) 121 | { 122 | pEmitter->pLFECurve = &DefaultCurve; 123 | } 124 | 125 | F3DAudioCalculate( 126 | F3DInstance, 127 | pListener, 128 | pEmitter, 129 | ( 130 | F3DAUDIO_CALCULATE_MATRIX | 131 | F3DAUDIO_CALCULATE_DOPPLER | 132 | F3DAUDIO_CALCULATE_EMITTER_ANGLE 133 | ), 134 | pDSPSettings 135 | ); 136 | return 0; 137 | } 138 | 139 | uint32_t FACT3DApply( 140 | F3DAUDIO_DSP_SETTINGS *pDSPSettings, 141 | FACTCue *pCue 142 | ) { 143 | if (pDSPSettings == NULL || pCue == NULL) 144 | { 145 | return 0; 146 | } 147 | 148 | FACTCue_SetMatrixCoefficients( 149 | pCue, 150 | pDSPSettings->SrcChannelCount, 151 | pDSPSettings->DstChannelCount, 152 | pDSPSettings->pMatrixCoefficients 153 | ); 154 | FACTCue_SetVariable( 155 | pCue, 156 | FACTCue_GetVariableIndex(pCue, "Distance"), 157 | pDSPSettings->EmitterToListenerDistance 158 | ); 159 | FACTCue_SetVariable( 160 | pCue, 161 | FACTCue_GetVariableIndex(pCue, "DopplerPitchScalar"), 162 | pDSPSettings->DopplerFactor 163 | ); 164 | FACTCue_SetVariable( 165 | pCue, 166 | FACTCue_GetVariableIndex(pCue, "OrientationAngle"), 167 | pDSPSettings->EmitterToListenerAngle * (180.0f / F3DAUDIO_PI) 168 | ); 169 | return 0; 170 | } 171 | 172 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 173 | -------------------------------------------------------------------------------- /src/FAPOFX.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #include "FAPOFX.h" 28 | #include "FAudio_internal.h" 29 | 30 | uint32_t FAPOFX_CreateFX( 31 | const FAudioGUID *clsid, 32 | FAPO **pEffect, 33 | const void *pInitData, 34 | uint32_t InitDataByteSize 35 | ) { 36 | return FAPOFX_CreateFXWithCustomAllocatorEXT( 37 | clsid, 38 | pEffect, 39 | pInitData, 40 | InitDataByteSize, 41 | FAudio_malloc, 42 | FAudio_free, 43 | FAudio_realloc 44 | ); 45 | } 46 | 47 | uint32_t FAPOFX_CreateFXWithCustomAllocatorEXT( 48 | const FAudioGUID *clsid, 49 | FAPO **pEffect, 50 | const void *pInitData, 51 | uint32_t InitDataByteSize, 52 | FAudioMallocFunc customMalloc, 53 | FAudioFreeFunc customFree, 54 | FAudioReallocFunc customRealloc 55 | ) { 56 | #define CHECK_AND_RETURN(effect) \ 57 | if (FAudio_memcmp(clsid, &FAPOFX_CLSID_FX##effect, sizeof(FAudioGUID)) == 0) \ 58 | { \ 59 | return FAPOFXCreate##effect( \ 60 | pEffect, \ 61 | pInitData, \ 62 | InitDataByteSize, \ 63 | customMalloc, \ 64 | customFree, \ 65 | customRealloc, \ 66 | 0 \ 67 | ); \ 68 | } \ 69 | else if (FAudio_memcmp(clsid, &FAPOFX_CLSID_FX##effect##_LEGACY, sizeof(FAudioGUID)) == 0) \ 70 | { \ 71 | return FAPOFXCreate##effect( \ 72 | pEffect, \ 73 | pInitData, \ 74 | InitDataByteSize, \ 75 | customMalloc, \ 76 | customFree, \ 77 | customRealloc, \ 78 | 1 \ 79 | ); \ 80 | } 81 | CHECK_AND_RETURN(EQ) 82 | CHECK_AND_RETURN(MasteringLimiter) 83 | CHECK_AND_RETURN(Reverb) 84 | CHECK_AND_RETURN(Echo) 85 | #undef CHECK_AND_RETURN 86 | return -1; 87 | } 88 | 89 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 90 | -------------------------------------------------------------------------------- /src/FAPOFX_echo.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #include "FAPOFX.h" 28 | #include "FAudio_internal.h" 29 | 30 | /* FXEcho FAPO Implementation */ 31 | 32 | const FAudioGUID FAPOFX_CLSID_FXEcho = 33 | { 34 | 0x5039D740, 35 | 0xF736, 36 | 0x449A, 37 | { 38 | 0x84, 39 | 0xD3, 40 | 0xA5, 41 | 0x62, 42 | 0x02, 43 | 0x55, 44 | 0x7B, 45 | 0x87 46 | } 47 | }; 48 | 49 | static FAPORegistrationProperties FXEchoProperties = 50 | { 51 | /* .clsid = */ {0}, 52 | /* .FriendlyName = */ 53 | { 54 | 'F', 'X', 'E', 'c', 'h', 'o', '\0' 55 | }, 56 | /*.CopyrightInfo = */ 57 | { 58 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 59 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 60 | }, 61 | /*.MajorVersion = */ 0, 62 | /*.MinorVersion = */ 0, 63 | /*.Flags = */( 64 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 65 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 66 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 67 | FAPO_FLAG_INPLACE_SUPPORTED | 68 | FAPO_FLAG_INPLACE_REQUIRED 69 | ), 70 | /*.MinInputBufferCount = */ 1, 71 | /*.MaxInputBufferCount = */ 1, 72 | /*.MinOutputBufferCount = */ 1, 73 | /*.MaxOutputBufferCount =*/ 1 74 | }; 75 | 76 | const FAudioGUID FAPOFX_CLSID_FXEcho_LEGACY = 77 | { 78 | 0xA90BC001, 79 | 0xE897, 80 | 0xE897, 81 | { 82 | 0x74, 83 | 0x39, 84 | 0x43, 85 | 0x55, 86 | 0x00, 87 | 0x00, 88 | 0x00, 89 | 0x03 90 | } 91 | }; 92 | 93 | static FAPORegistrationProperties FXEchoProperties_LEGACY = 94 | { 95 | /* .clsid = */ {0}, 96 | /* .FriendlyName = */ 97 | { 98 | 'F', 'X', 'E', 'c', 'h', 'o', '\0' 99 | }, 100 | /*.CopyrightInfo = */ 101 | { 102 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 103 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 104 | }, 105 | /*.MajorVersion = */ 0, 106 | /*.MinorVersion = */ 0, 107 | /*.Flags = */( 108 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 109 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 110 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 111 | FAPO_FLAG_INPLACE_SUPPORTED | 112 | FAPO_FLAG_INPLACE_REQUIRED 113 | ), 114 | /*.MinInputBufferCount = */ 1, 115 | /*.MaxInputBufferCount = */ 1, 116 | /*.MinOutputBufferCount = */ 1, 117 | /*.MaxOutputBufferCount =*/ 1 118 | }; 119 | 120 | typedef struct FAPOFXEcho 121 | { 122 | FAPOBase base; 123 | 124 | /* TODO */ 125 | } FAPOFXEcho; 126 | 127 | uint32_t FAPOFXEcho_Initialize( 128 | FAPOFXEcho *fapo, 129 | const void* pData, 130 | uint32_t DataByteSize 131 | ) { 132 | #define INITPARAMS(offset) \ 133 | FAudio_memcpy( \ 134 | fapo->base.m_pParameterBlocks + DataByteSize * offset, \ 135 | pData, \ 136 | DataByteSize \ 137 | ); 138 | INITPARAMS(0) 139 | INITPARAMS(1) 140 | INITPARAMS(2) 141 | #undef INITPARAMS 142 | return 0; 143 | } 144 | 145 | void FAPOFXEcho_Process( 146 | FAPOFXEcho *fapo, 147 | uint32_t InputProcessParameterCount, 148 | const FAPOProcessBufferParameters* pInputProcessParameters, 149 | uint32_t OutputProcessParameterCount, 150 | FAPOProcessBufferParameters* pOutputProcessParameters, 151 | int32_t IsEnabled 152 | ) { 153 | FAPOBase_BeginProcess(&fapo->base); 154 | 155 | /* TODO */ 156 | 157 | FAPOBase_EndProcess(&fapo->base); 158 | } 159 | 160 | void FAPOFXEcho_Free(void* fapo) 161 | { 162 | FAPOFXEcho *echo = (FAPOFXEcho*) fapo; 163 | echo->base.pFree(echo->base.m_pParameterBlocks); 164 | echo->base.pFree(fapo); 165 | } 166 | 167 | /* Public API */ 168 | 169 | uint32_t FAPOFXCreateEcho( 170 | FAPO **pEffect, 171 | const void *pInitData, 172 | uint32_t InitDataByteSize, 173 | FAudioMallocFunc customMalloc, 174 | FAudioFreeFunc customFree, 175 | FAudioReallocFunc customRealloc, 176 | uint8_t legacy 177 | ) { 178 | const FAPOFXEchoParameters fxdefault = 179 | { 180 | FAPOFXECHO_DEFAULT_WETDRYMIX, 181 | FAPOFXECHO_DEFAULT_FEEDBACK, 182 | FAPOFXECHO_DEFAULT_DELAY 183 | }; 184 | 185 | /* Allocate... */ 186 | FAPOFXEcho *result = (FAPOFXEcho*) customMalloc( 187 | sizeof(FAPOFXEcho) 188 | ); 189 | uint8_t *params = (uint8_t*) customMalloc( 190 | sizeof(FAPOFXEchoParameters) * 3 191 | ); 192 | if (pInitData == NULL) 193 | { 194 | FAudio_zero(params, sizeof(FAPOFXEchoParameters) * 3); 195 | #define INITPARAMS(offset) \ 196 | FAudio_memcpy( \ 197 | params + sizeof(FAPOFXEchoParameters) * offset, \ 198 | &fxdefault, \ 199 | sizeof(FAPOFXEchoParameters) \ 200 | ); 201 | INITPARAMS(0) 202 | INITPARAMS(1) 203 | INITPARAMS(2) 204 | #undef INITPARAMS 205 | } 206 | else 207 | { 208 | FAudio_assert(InitDataByteSize == sizeof(FAPOFXEchoParameters)); 209 | FAudio_memcpy(params, pInitData, InitDataByteSize); 210 | FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize); 211 | FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize); 212 | } 213 | 214 | /* Initialize... */ 215 | FAudio_memcpy( 216 | &FXEchoProperties_LEGACY.clsid, 217 | &FAPOFX_CLSID_FXEcho_LEGACY, 218 | sizeof(FAudioGUID) 219 | ); 220 | FAudio_memcpy( 221 | &FXEchoProperties.clsid, 222 | &FAPOFX_CLSID_FXEcho, 223 | sizeof(FAudioGUID) 224 | ); 225 | CreateFAPOBaseWithCustomAllocatorEXT( 226 | &result->base, 227 | legacy ? &FXEchoProperties_LEGACY : &FXEchoProperties, 228 | params, 229 | sizeof(FAPOFXEchoParameters), 230 | 0, 231 | customMalloc, 232 | customFree, 233 | customRealloc 234 | ); 235 | 236 | /* Function table... */ 237 | result->base.base.Initialize = (InitializeFunc) 238 | FAPOFXEcho_Initialize; 239 | result->base.base.Process = (ProcessFunc) 240 | FAPOFXEcho_Process; 241 | result->base.Destructor = FAPOFXEcho_Free; 242 | 243 | /* Finally. */ 244 | *pEffect = &result->base.base; 245 | return 0; 246 | } 247 | 248 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 249 | -------------------------------------------------------------------------------- /src/FAPOFX_eq.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #include "FAPOFX.h" 28 | #include "FAudio_internal.h" 29 | 30 | /* FXEQ FAPO Implementation */ 31 | 32 | const FAudioGUID FAPOFX_CLSID_FXEQ = 33 | { 34 | 0xF5E01117, 35 | 0xD6C4, 36 | 0x485A, 37 | { 38 | 0xA3, 39 | 0xF5, 40 | 0x69, 41 | 0x51, 42 | 0x96, 43 | 0xF3, 44 | 0xDB, 45 | 0xFA 46 | } 47 | }; 48 | 49 | static FAPORegistrationProperties FXEQProperties = 50 | { 51 | /* .clsid = */ {0}, 52 | /* .FriendlyName = */ 53 | { 54 | 'F', 'X', 'E', 'Q', '\0' 55 | }, 56 | /*.CopyrightInfo = */ 57 | { 58 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 59 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 60 | }, 61 | /*.MajorVersion = */ 0, 62 | /*.MinorVersion = */ 0, 63 | /*.Flags = */( 64 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 65 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 66 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 67 | FAPO_FLAG_INPLACE_SUPPORTED | 68 | FAPO_FLAG_INPLACE_REQUIRED 69 | ), 70 | /*.MinInputBufferCount = */ 1, 71 | /*.MaxInputBufferCount = */ 1, 72 | /*.MinOutputBufferCount = */ 1, 73 | /*.MaxOutputBufferCount =*/ 1 74 | }; 75 | 76 | const FAudioGUID FAPOFX_CLSID_FXEQ_LEGACY = 77 | { 78 | 0xA90BC001, 79 | 0xE897, 80 | 0xE897, 81 | { 82 | 0x74, 83 | 0x39, 84 | 0x43, 85 | 0x55, 86 | 0x00, 87 | 0x00, 88 | 0x00, 89 | 0x00 90 | } 91 | }; 92 | 93 | static FAPORegistrationProperties FXEQProperties_LEGACY = 94 | { 95 | /* .clsid = */ {0}, 96 | /* .FriendlyName = */ 97 | { 98 | 'F', 'X', 'E', 'Q', '\0' 99 | }, 100 | /*.CopyrightInfo = */ 101 | { 102 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 103 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 104 | }, 105 | /*.MajorVersion = */ 0, 106 | /*.MinorVersion = */ 0, 107 | /*.Flags = */( 108 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 109 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 110 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 111 | FAPO_FLAG_INPLACE_SUPPORTED | 112 | FAPO_FLAG_INPLACE_REQUIRED 113 | ), 114 | /*.MinInputBufferCount = */ 1, 115 | /*.MaxInputBufferCount = */ 1, 116 | /*.MinOutputBufferCount = */ 1, 117 | /*.MaxOutputBufferCount =*/ 1 118 | }; 119 | 120 | typedef struct FAPOFXEQ 121 | { 122 | FAPOBase base; 123 | 124 | /* TODO */ 125 | } FAPOFXEQ; 126 | 127 | uint32_t FAPOFXEQ_Initialize( 128 | FAPOFXEQ *fapo, 129 | const void* pData, 130 | uint32_t DataByteSize 131 | ) { 132 | #define INITPARAMS(offset) \ 133 | FAudio_memcpy( \ 134 | fapo->base.m_pParameterBlocks + DataByteSize * offset, \ 135 | pData, \ 136 | DataByteSize \ 137 | ); 138 | INITPARAMS(0) 139 | INITPARAMS(1) 140 | INITPARAMS(2) 141 | #undef INITPARAMS 142 | return 0; 143 | } 144 | 145 | void FAPOFXEQ_Process( 146 | FAPOFXEQ *fapo, 147 | uint32_t InputProcessParameterCount, 148 | const FAPOProcessBufferParameters* pInputProcessParameters, 149 | uint32_t OutputProcessParameterCount, 150 | FAPOProcessBufferParameters* pOutputProcessParameters, 151 | int32_t IsEnabled 152 | ) { 153 | FAPOBase_BeginProcess(&fapo->base); 154 | 155 | /* TODO */ 156 | 157 | FAPOBase_EndProcess(&fapo->base); 158 | } 159 | 160 | void FAPOFXEQ_Free(void* fapo) 161 | { 162 | FAPOFXEQ *eq = (FAPOFXEQ*) fapo; 163 | eq->base.pFree(eq->base.m_pParameterBlocks); 164 | eq->base.pFree(fapo); 165 | } 166 | 167 | /* Public API */ 168 | 169 | uint32_t FAPOFXCreateEQ( 170 | FAPO **pEffect, 171 | const void *pInitData, 172 | uint32_t InitDataByteSize, 173 | FAudioMallocFunc customMalloc, 174 | FAudioFreeFunc customFree, 175 | FAudioReallocFunc customRealloc, 176 | uint8_t legacy 177 | ) { 178 | const FAPOFXEQParameters fxdefault = 179 | { 180 | FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_0, 181 | FAPOFXEQ_DEFAULT_GAIN, 182 | FAPOFXEQ_DEFAULT_BANDWIDTH, 183 | FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_1, 184 | FAPOFXEQ_DEFAULT_GAIN, 185 | FAPOFXEQ_DEFAULT_BANDWIDTH, 186 | FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_2, 187 | FAPOFXEQ_DEFAULT_GAIN, 188 | FAPOFXEQ_DEFAULT_BANDWIDTH, 189 | FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_3, 190 | FAPOFXEQ_DEFAULT_GAIN, 191 | FAPOFXEQ_DEFAULT_BANDWIDTH 192 | }; 193 | 194 | /* Allocate... */ 195 | FAPOFXEQ *result = (FAPOFXEQ*) customMalloc( 196 | sizeof(FAPOFXEQ) 197 | ); 198 | uint8_t *params = (uint8_t*) customMalloc( 199 | sizeof(FAPOFXEQParameters) * 3 200 | ); 201 | if (pInitData == NULL) 202 | { 203 | FAudio_zero(params, sizeof(FAPOFXEQParameters) * 3); 204 | #define INITPARAMS(offset) \ 205 | FAudio_memcpy( \ 206 | params + sizeof(FAPOFXEQParameters) * offset, \ 207 | &fxdefault, \ 208 | sizeof(FAPOFXEQParameters) \ 209 | ); 210 | INITPARAMS(0) 211 | INITPARAMS(1) 212 | INITPARAMS(2) 213 | #undef INITPARAMS 214 | } 215 | else 216 | { 217 | FAudio_assert(InitDataByteSize == sizeof(FAPOFXEQParameters)); 218 | FAudio_memcpy(params, pInitData, InitDataByteSize); 219 | FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize); 220 | FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize); 221 | } 222 | 223 | /* Initialize... */ 224 | FAudio_memcpy( 225 | &FXEQProperties_LEGACY.clsid, 226 | &FAPOFX_CLSID_FXEQ_LEGACY, 227 | sizeof(FAudioGUID) 228 | ); 229 | FAudio_memcpy( 230 | &FXEQProperties.clsid, 231 | &FAPOFX_CLSID_FXEQ, 232 | sizeof(FAudioGUID) 233 | ); 234 | CreateFAPOBaseWithCustomAllocatorEXT( 235 | &result->base, 236 | legacy ? &FXEQProperties_LEGACY : &FXEQProperties, 237 | params, 238 | sizeof(FAPOFXEQParameters), 239 | 0, 240 | customMalloc, 241 | customFree, 242 | customRealloc 243 | ); 244 | 245 | /* Function table... */ 246 | result->base.base.Initialize = (InitializeFunc) 247 | FAPOFXEQ_Initialize; 248 | result->base.base.Process = (ProcessFunc) 249 | FAPOFXEQ_Process; 250 | result->base.Destructor = FAPOFXEQ_Free; 251 | 252 | /* Finally. */ 253 | *pEffect = &result->base.base; 254 | return 0; 255 | } 256 | 257 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 258 | -------------------------------------------------------------------------------- /src/FAPOFX_masteringlimiter.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #include "FAPOFX.h" 28 | #include "FAudio_internal.h" 29 | 30 | /* FXMasteringLimiter FAPO Implementation */ 31 | 32 | const FAudioGUID FAPOFX_CLSID_FXMasteringLimiter = 33 | { 34 | 0xC4137916, 35 | 0x2BE1, 36 | 0x46FD, 37 | { 38 | 0x85, 39 | 0x99, 40 | 0x44, 41 | 0x15, 42 | 0x36, 43 | 0xF4, 44 | 0x98, 45 | 0x56 46 | } 47 | }; 48 | 49 | static FAPORegistrationProperties FXMasteringLimiterProperties = 50 | { 51 | /* .clsid = */ {0}, 52 | /* .FriendlyName = */ 53 | { 54 | 'F', 'X', 'M', 'a', 's', 't', 'e', 'r', 'i', 'n', 'g', 'L', 'i', 'm', 'i', 't', 'e', 'r', '\0' 55 | }, 56 | /*.CopyrightInfo = */ 57 | { 58 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 59 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 60 | }, 61 | /*.MajorVersion = */ 0, 62 | /*.MinorVersion = */ 0, 63 | /*.Flags = */( 64 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 65 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 66 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 67 | FAPO_FLAG_INPLACE_SUPPORTED | 68 | FAPO_FLAG_INPLACE_REQUIRED 69 | ), 70 | /*.MinInputBufferCount = */ 1, 71 | /*.MaxInputBufferCount = */ 1, 72 | /*.MinOutputBufferCount = */ 1, 73 | /*.MaxOutputBufferCount =*/ 1 74 | }; 75 | 76 | const FAudioGUID FAPOFX_CLSID_FXMasteringLimiter_LEGACY = 77 | { 78 | 0xA90BC001, 79 | 0xE897, 80 | 0xE897, 81 | { 82 | 0x74, 83 | 0x39, 84 | 0x43, 85 | 0x55, 86 | 0x00, 87 | 0x00, 88 | 0x00, 89 | 0x01 90 | } 91 | }; 92 | 93 | static FAPORegistrationProperties FXMasteringLimiterProperties_LEGACY = 94 | { 95 | /* .clsid = */ {0}, 96 | /* .FriendlyName = */ 97 | { 98 | 'F', 'X', 'M', 'a', 's', 't', 'e', 'r', 'i', 'n', 'g', 'L', 'i', 'm', 'i', 't', 'e', 'r', '\0' 99 | }, 100 | /*.CopyrightInfo = */ 101 | { 102 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 103 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 104 | }, 105 | /*.MajorVersion = */ 0, 106 | /*.MinorVersion = */ 0, 107 | /*.Flags = */( 108 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 109 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 110 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 111 | FAPO_FLAG_INPLACE_SUPPORTED | 112 | FAPO_FLAG_INPLACE_REQUIRED 113 | ), 114 | /*.MinInputBufferCount = */ 1, 115 | /*.MaxInputBufferCount = */ 1, 116 | /*.MinOutputBufferCount = */ 1, 117 | /*.MaxOutputBufferCount =*/ 1 118 | }; 119 | 120 | typedef struct FAPOFXMasteringLimiter 121 | { 122 | FAPOBase base; 123 | 124 | /* TODO */ 125 | } FAPOFXMasteringLimiter; 126 | 127 | uint32_t FAPOFXMasteringLimiter_Initialize( 128 | FAPOFXMasteringLimiter *fapo, 129 | const void* pData, 130 | uint32_t DataByteSize 131 | ) { 132 | #define INITPARAMS(offset) \ 133 | FAudio_memcpy( \ 134 | fapo->base.m_pParameterBlocks + DataByteSize * offset, \ 135 | pData, \ 136 | DataByteSize \ 137 | ); 138 | INITPARAMS(0) 139 | INITPARAMS(1) 140 | INITPARAMS(2) 141 | #undef INITPARAMS 142 | return 0; 143 | } 144 | 145 | void FAPOFXMasteringLimiter_Process( 146 | FAPOFXMasteringLimiter *fapo, 147 | uint32_t InputProcessParameterCount, 148 | const FAPOProcessBufferParameters* pInputProcessParameters, 149 | uint32_t OutputProcessParameterCount, 150 | FAPOProcessBufferParameters* pOutputProcessParameters, 151 | int32_t IsEnabled 152 | ) { 153 | FAPOBase_BeginProcess(&fapo->base); 154 | 155 | /* TODO */ 156 | 157 | FAPOBase_EndProcess(&fapo->base); 158 | } 159 | 160 | void FAPOFXMasteringLimiter_Free(void* fapo) 161 | { 162 | FAPOFXMasteringLimiter *limiter = (FAPOFXMasteringLimiter*) fapo; 163 | limiter->base.pFree(limiter->base.m_pParameterBlocks); 164 | limiter->base.pFree(fapo); 165 | } 166 | 167 | /* Public API */ 168 | 169 | uint32_t FAPOFXCreateMasteringLimiter( 170 | FAPO **pEffect, 171 | const void *pInitData, 172 | uint32_t InitDataByteSize, 173 | FAudioMallocFunc customMalloc, 174 | FAudioFreeFunc customFree, 175 | FAudioReallocFunc customRealloc, 176 | uint8_t legacy 177 | ) { 178 | const FAPOFXMasteringLimiterParameters fxdefault = 179 | { 180 | FAPOFXMASTERINGLIMITER_DEFAULT_RELEASE, 181 | FAPOFXMASTERINGLIMITER_DEFAULT_LOUDNESS 182 | }; 183 | 184 | /* Allocate... */ 185 | FAPOFXMasteringLimiter *result = (FAPOFXMasteringLimiter*) customMalloc( 186 | sizeof(FAPOFXMasteringLimiter) 187 | ); 188 | uint8_t *params = (uint8_t*) customMalloc( 189 | sizeof(FAPOFXMasteringLimiterParameters) * 3 190 | ); 191 | if (pInitData == NULL) 192 | { 193 | FAudio_zero(params, sizeof(FAPOFXMasteringLimiterParameters) * 3); 194 | #define INITPARAMS(offset) \ 195 | FAudio_memcpy( \ 196 | params + sizeof(FAPOFXMasteringLimiterParameters) * offset, \ 197 | &fxdefault, \ 198 | sizeof(FAPOFXMasteringLimiterParameters) \ 199 | ); 200 | INITPARAMS(0) 201 | INITPARAMS(1) 202 | INITPARAMS(2) 203 | #undef INITPARAMS 204 | } 205 | else 206 | { 207 | FAudio_assert(InitDataByteSize == sizeof(FAPOFXMasteringLimiterParameters)); 208 | FAudio_memcpy(params, pInitData, InitDataByteSize); 209 | FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize); 210 | FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize); 211 | } 212 | 213 | /* Initialize... */ 214 | FAudio_memcpy( 215 | &FXMasteringLimiterProperties_LEGACY.clsid, 216 | &FAPOFX_CLSID_FXMasteringLimiter_LEGACY, 217 | sizeof(FAudioGUID) 218 | ); 219 | FAudio_memcpy( 220 | &FXMasteringLimiterProperties.clsid, 221 | &FAPOFX_CLSID_FXMasteringLimiter, 222 | sizeof(FAudioGUID) 223 | ); 224 | CreateFAPOBaseWithCustomAllocatorEXT( 225 | &result->base, 226 | legacy ? &FXMasteringLimiterProperties_LEGACY : &FXMasteringLimiterProperties, 227 | params, 228 | sizeof(FAPOFXMasteringLimiterParameters), 229 | 0, 230 | customMalloc, 231 | customFree, 232 | customRealloc 233 | ); 234 | 235 | /* Function table... */ 236 | result->base.base.Initialize = (InitializeFunc) 237 | FAPOFXMasteringLimiter_Initialize; 238 | result->base.base.Process = (ProcessFunc) 239 | FAPOFXMasteringLimiter_Process; 240 | result->base.Destructor = FAPOFXMasteringLimiter_Free; 241 | 242 | /* Finally. */ 243 | *pEffect = &result->base.base; 244 | return 0; 245 | } 246 | 247 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 248 | -------------------------------------------------------------------------------- /src/FAPOFX_reverb.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #include "FAPOFX.h" 28 | #include "FAudio_internal.h" 29 | 30 | /* FXReverb FAPO Implementation */ 31 | 32 | const FAudioGUID FAPOFX_CLSID_FXReverb = 33 | { 34 | 0x7D9ACA56, 35 | 0xCB68, 36 | 0x4807, 37 | { 38 | 0xB6, 39 | 0x32, 40 | 0xB1, 41 | 0x37, 42 | 0x35, 43 | 0x2E, 44 | 0x85, 45 | 0x96 46 | } 47 | }; 48 | 49 | static FAPORegistrationProperties FXReverbProperties = 50 | { 51 | /* .clsid = */ {0}, 52 | /* .FriendlyName = */ 53 | { 54 | 'F', 'X', 'R', 'e', 'v', 'e', 'r', 'b', '\0' 55 | }, 56 | /*.CopyrightInfo = */ 57 | { 58 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 59 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 60 | }, 61 | /*.MajorVersion = */ 0, 62 | /*.MinorVersion = */ 0, 63 | /*.Flags = */( 64 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 65 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 66 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 67 | FAPO_FLAG_INPLACE_SUPPORTED | 68 | FAPO_FLAG_INPLACE_REQUIRED 69 | ), 70 | /*.MinInputBufferCount = */ 1, 71 | /*.MaxInputBufferCount = */ 1, 72 | /*.MinOutputBufferCount = */ 1, 73 | /*.MaxOutputBufferCount =*/ 1 74 | }; 75 | 76 | const FAudioGUID FAPOFX_CLSID_FXReverb_LEGACY = 77 | { 78 | 0xA90BC001, 79 | 0xE897, 80 | 0xE897, 81 | { 82 | 0x74, 83 | 0x39, 84 | 0x43, 85 | 0x55, 86 | 0x00, 87 | 0x00, 88 | 0x00, 89 | 0x02 90 | } 91 | }; 92 | 93 | static FAPORegistrationProperties FXReverbProperties_LEGACY = 94 | { 95 | /* .clsid = */ {0}, 96 | /* .FriendlyName = */ 97 | { 98 | 'F', 'X', 'R', 'e', 'v', 'e', 'r', 'b', '\0' 99 | }, 100 | /*.CopyrightInfo = */ 101 | { 102 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 103 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 104 | }, 105 | /*.MajorVersion = */ 0, 106 | /*.MinorVersion = */ 0, 107 | /*.Flags = */( 108 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 109 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 110 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 111 | FAPO_FLAG_INPLACE_SUPPORTED | 112 | FAPO_FLAG_INPLACE_REQUIRED 113 | ), 114 | /*.MinInputBufferCount = */ 1, 115 | /*.MaxInputBufferCount = */ 1, 116 | /*.MinOutputBufferCount = */ 1, 117 | /*.MaxOutputBufferCount =*/ 1 118 | }; 119 | 120 | typedef struct FAPOFXReverb 121 | { 122 | FAPOBase base; 123 | 124 | /* TODO */ 125 | } FAPOFXReverb; 126 | 127 | uint32_t FAPOFXReverb_Initialize( 128 | FAPOFXReverb *fapo, 129 | const void* pData, 130 | uint32_t DataByteSize 131 | ) { 132 | #define INITPARAMS(offset) \ 133 | FAudio_memcpy( \ 134 | fapo->base.m_pParameterBlocks + DataByteSize * offset, \ 135 | pData, \ 136 | DataByteSize \ 137 | ); 138 | INITPARAMS(0) 139 | INITPARAMS(1) 140 | INITPARAMS(2) 141 | #undef INITPARAMS 142 | return 0; 143 | } 144 | 145 | void FAPOFXReverb_Process( 146 | FAPOFXReverb *fapo, 147 | uint32_t InputProcessParameterCount, 148 | const FAPOProcessBufferParameters* pInputProcessParameters, 149 | uint32_t OutputProcessParameterCount, 150 | FAPOProcessBufferParameters* pOutputProcessParameters, 151 | int32_t IsEnabled 152 | ) { 153 | FAPOBase_BeginProcess(&fapo->base); 154 | 155 | /* TODO */ 156 | 157 | FAPOBase_EndProcess(&fapo->base); 158 | } 159 | 160 | void FAPOFXReverb_Free(void* fapo) 161 | { 162 | FAPOFXReverb *reverb = (FAPOFXReverb*) fapo; 163 | reverb->base.pFree(reverb->base.m_pParameterBlocks); 164 | reverb->base.pFree(fapo); 165 | } 166 | 167 | /* Public API */ 168 | 169 | uint32_t FAPOFXCreateReverb( 170 | FAPO **pEffect, 171 | const void *pInitData, 172 | uint32_t InitDataByteSize, 173 | FAudioMallocFunc customMalloc, 174 | FAudioFreeFunc customFree, 175 | FAudioReallocFunc customRealloc, 176 | uint8_t legacy 177 | ) { 178 | const FAPOFXReverbParameters fxdefault = 179 | { 180 | FAPOFXREVERB_DEFAULT_DIFFUSION, 181 | FAPOFXREVERB_DEFAULT_ROOMSIZE, 182 | }; 183 | 184 | /* Allocate... */ 185 | FAPOFXReverb *result = (FAPOFXReverb*) customMalloc( 186 | sizeof(FAPOFXReverb) 187 | ); 188 | uint8_t *params = (uint8_t*) customMalloc( 189 | sizeof(FAPOFXReverbParameters) * 3 190 | ); 191 | if (pInitData == NULL) 192 | { 193 | FAudio_zero(params, sizeof(FAPOFXReverbParameters) * 3); 194 | #define INITPARAMS(offset) \ 195 | FAudio_memcpy( \ 196 | params + sizeof(FAPOFXReverbParameters) * offset, \ 197 | &fxdefault, \ 198 | sizeof(FAPOFXReverbParameters) \ 199 | ); 200 | INITPARAMS(0) 201 | INITPARAMS(1) 202 | INITPARAMS(2) 203 | #undef INITPARAMS 204 | } 205 | else 206 | { 207 | FAudio_assert(InitDataByteSize == sizeof(FAPOFXReverbParameters)); 208 | FAudio_memcpy(params, pInitData, InitDataByteSize); 209 | FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize); 210 | FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize); 211 | } 212 | 213 | /* Initialize... */ 214 | FAudio_memcpy( 215 | &FXReverbProperties_LEGACY.clsid, 216 | &FAPOFX_CLSID_FXReverb_LEGACY, 217 | sizeof(FAudioGUID) 218 | ); 219 | FAudio_memcpy( 220 | &FXReverbProperties.clsid, 221 | &FAPOFX_CLSID_FXReverb, 222 | sizeof(FAudioGUID) 223 | ); 224 | CreateFAPOBaseWithCustomAllocatorEXT( 225 | &result->base, 226 | legacy ? &FXReverbProperties_LEGACY : &FXReverbProperties, 227 | params, 228 | sizeof(FAPOFXReverbParameters), 229 | 0, 230 | customMalloc, 231 | customFree, 232 | customRealloc 233 | ); 234 | 235 | /* Function table... */ 236 | result->base.base.Initialize = (InitializeFunc) 237 | FAPOFXReverb_Initialize; 238 | result->base.base.Process = (ProcessFunc) 239 | FAPOFXReverb_Process; 240 | result->base.Destructor = FAPOFXReverb_Free; 241 | 242 | /* Finally. */ 243 | *pEffect = &result->base.base; 244 | return 0; 245 | } 246 | 247 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 248 | -------------------------------------------------------------------------------- /src/FAudioFX_collector.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2025 Ethan Lee and the FNA team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Katelyn Gadd 24 | * 25 | */ 26 | 27 | #include "FAudioFX.h" 28 | #include "FAudio_internal.h" 29 | 30 | /* Sample Collector FAPO Implementation */ 31 | 32 | const FAudioGUID FAudioFX_CLSID_Collector = /* 2.7 */ 33 | { 34 | 0xCAC1105F, 35 | 0x619B, 36 | 0x4D04, 37 | { 38 | 0x83, 39 | 0x1A, 40 | 0x44, 41 | 0xE1, 42 | 0xCB, 43 | 0xF1, 44 | 0x2D, 45 | 0x58 46 | } 47 | }; 48 | 49 | static FAPORegistrationProperties CollectorProperties = 50 | { 51 | /* .clsid = */ {0}, 52 | /* .FriendlyName = */ 53 | { 54 | 'C', 'o', 'l', 'l', 'e', 'c', 't', 'o', 'r', '\0' 55 | }, 56 | /*.CopyrightInfo = */ 57 | { 58 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 59 | 'K', 'a', 't', 'e', 'l', 'y', 'n', ' ', 'G', 'a', 'd', 'd', '\0' 60 | }, 61 | /*.MajorVersion = */ 0, 62 | /*.MinorVersion = */ 0, 63 | /*.Flags = */( 64 | FAPO_FLAG_CHANNELS_MUST_MATCH | 65 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 66 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 67 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 68 | FAPO_FLAG_INPLACE_SUPPORTED | 69 | FAPO_FLAG_INPLACE_REQUIRED 70 | ), 71 | /*.MinInputBufferCount = */ 1, 72 | /*.MaxInputBufferCount = */ 1, 73 | /*.MinOutputBufferCount = */ 1, 74 | /*.MaxOutputBufferCount =*/ 1 75 | }; 76 | 77 | typedef struct FAudioFXCollector 78 | { 79 | FAPOBase base; 80 | uint16_t channels; 81 | float* pBuffer; 82 | uint32_t bufferLength; 83 | uint32_t writeOffset; 84 | } FAudioFXCollector; 85 | 86 | uint32_t FAudioFXCollector_LockForProcess( 87 | FAudioFXCollector*fapo, 88 | uint32_t InputLockedParameterCount, 89 | const FAPOLockForProcessBufferParameters *pInputLockedParameters, 90 | uint32_t OutputLockedParameterCount, 91 | const FAPOLockForProcessBufferParameters *pOutputLockedParameters 92 | ) { 93 | /* Verify parameter counts... */ 94 | if ( InputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinInputBufferCount || 95 | InputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxInputBufferCount || 96 | OutputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinOutputBufferCount || 97 | OutputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxOutputBufferCount ) 98 | { 99 | return FAUDIO_E_INVALID_ARG; 100 | } 101 | 102 | 103 | /* Validate input/output formats */ 104 | #define VERIFY_FORMAT_FLAG(flag, prop) \ 105 | if ( (fapo->base.m_pRegistrationProperties->Flags & flag) && \ 106 | (pInputLockedParameters->pFormat->prop != pOutputLockedParameters->pFormat->prop) ) \ 107 | { \ 108 | return FAUDIO_E_INVALID_ARG; \ 109 | } 110 | VERIFY_FORMAT_FLAG(FAPO_FLAG_CHANNELS_MUST_MATCH, nChannels) 111 | VERIFY_FORMAT_FLAG(FAPO_FLAG_FRAMERATE_MUST_MATCH, nSamplesPerSec) 112 | VERIFY_FORMAT_FLAG(FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH, wBitsPerSample) 113 | #undef VERIFY_FORMAT_FLAG 114 | if ( (fapo->base.m_pRegistrationProperties->Flags & FAPO_FLAG_BUFFERCOUNT_MUST_MATCH) && 115 | (InputLockedParameterCount != OutputLockedParameterCount) ) 116 | { 117 | return FAUDIO_E_INVALID_ARG; 118 | } 119 | 120 | fapo->channels = pInputLockedParameters->pFormat->nChannels; 121 | fapo->base.m_fIsLocked = 1; 122 | return 0; 123 | } 124 | 125 | void FAudioFXCollector_UnlockForProcess(FAudioFXCollector* fapo) 126 | { 127 | fapo->base.m_fIsLocked = 0; 128 | } 129 | 130 | void FAudioFXCollector_Process( 131 | FAudioFXCollector* fapo, 132 | uint32_t InputProcessParameterCount, 133 | const FAPOProcessBufferParameters* pInputProcessParameters, 134 | uint32_t OutputProcessParameterCount, 135 | FAPOProcessBufferParameters* pOutputProcessParameters, 136 | int32_t IsEnabled 137 | ) { 138 | uint32_t channels = fapo->channels, 139 | bufferLength = fapo->bufferLength, 140 | sampleCount = pInputProcessParameters->ValidFrameCount; 141 | uint32_t writeOffset = fapo->writeOffset; 142 | float* pBuffer = (float*)pInputProcessParameters->pBuffer; 143 | float* pOutput = fapo->pBuffer; 144 | float multiplier = 1.0f / channels; 145 | 146 | for (uint32_t i = 0; i < sampleCount; i++) { 147 | // Accumulate all channels and average them 148 | float accumulator = 0; 149 | for (uint32_t j = 0; j < channels; j++) 150 | accumulator += pBuffer[j]; 151 | accumulator *= multiplier; 152 | 153 | // Advance to next set of samples 154 | pBuffer += channels; 155 | 156 | // Write to output buffer 157 | pOutput[writeOffset++] = accumulator; 158 | // Wrap write offset 159 | if (writeOffset >= bufferLength) 160 | writeOffset = 0; 161 | } 162 | 163 | fapo->writeOffset = writeOffset; 164 | FAPOBase_EndProcess(&fapo->base); 165 | } 166 | 167 | void FAudioFXCollector_Free(void* fapo) 168 | { 169 | FAudioFXCollector *collector = (FAudioFXCollector*) fapo; 170 | collector->base.pFree(fapo); 171 | } 172 | 173 | void FAudioFXCollector_GetParameters( 174 | FAudioFXCollector* fapo, 175 | FAudioFXCollectorState* pParameters, 176 | uint32_t ParameterByteSize 177 | ) { 178 | FAudio_assert(pParameters); 179 | FAudio_assert(ParameterByteSize == sizeof(FAudioFXCollectorState)); 180 | pParameters->WriteOffset = fapo->writeOffset; 181 | } 182 | 183 | /* Public API */ 184 | 185 | uint32_t FAudioCreateCollectorEXT(FAPO** ppApo, uint32_t Flags, float* pBuffer, uint32_t bufferLength) 186 | { 187 | return FAudioCreateCollectorWithCustomAllocatorEXT( 188 | ppApo, 189 | Flags, 190 | pBuffer, 191 | bufferLength, 192 | FAudio_malloc, 193 | FAudio_free, 194 | FAudio_realloc 195 | ); 196 | } 197 | 198 | FAUDIOAPI uint32_t FAudioCreateCollectorWithCustomAllocatorEXT( 199 | FAPO** ppApo, 200 | uint32_t Flags, 201 | float* pBuffer, 202 | uint32_t bufferLength, 203 | FAudioMallocFunc customMalloc, 204 | FAudioFreeFunc customFree, 205 | FAudioReallocFunc customRealloc 206 | ) { 207 | FAudio_assert(ppApo); 208 | FAudio_assert(pBuffer); 209 | FAudio_assert(bufferLength); 210 | 211 | /* Allocate... */ 212 | FAudioFXCollector *result = (FAudioFXCollector*) customMalloc( 213 | sizeof(FAudioFXCollector) 214 | ); 215 | 216 | /* Initialize... */ 217 | FAudio_memcpy( 218 | &CollectorProperties.clsid, 219 | &FAudioFX_CLSID_Collector, 220 | sizeof(FAudioGUID) 221 | ); 222 | CreateFAPOBaseWithCustomAllocatorEXT( 223 | &result->base, 224 | &CollectorProperties, 225 | NULL, 226 | 0, 227 | 1, 228 | customMalloc, 229 | customFree, 230 | customRealloc 231 | ); 232 | 233 | /* Function table... */ 234 | result->base.base.LockForProcess = (LockForProcessFunc) 235 | FAudioFXCollector_LockForProcess; 236 | result->base.base.UnlockForProcess = (UnlockForProcessFunc) 237 | FAudioFXCollector_UnlockForProcess; 238 | result->base.base.Process = (ProcessFunc) 239 | FAudioFXCollector_Process; 240 | result->base.base.GetParameters = (GetParametersFunc) 241 | FAudioFXCollector_GetParameters; 242 | result->base.Destructor = FAudioFXCollector_Free; 243 | 244 | result->pBuffer = pBuffer; 245 | result->writeOffset = 0; 246 | result->bufferLength = bufferLength; 247 | 248 | /* Finally. */ 249 | *ppApo = &result->base.base; 250 | return 0; 251 | } 252 | 253 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 254 | -------------------------------------------------------------------------------- /src/FAudioFX_volumemeter.c: -------------------------------------------------------------------------------- 1 | /* FAudio - XAudio Reimplementation for FNA 2 | * 3 | * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team 4 | * 5 | * This software is provided 'as-is', without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in a 15 | * product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | * Ethan "flibitijibibo" Lee 24 | * 25 | */ 26 | 27 | #include "FAudioFX.h" 28 | #include "FAudio_internal.h" 29 | 30 | /* Volume Meter FAPO Implementation */ 31 | 32 | const FAudioGUID FAudioFX_CLSID_AudioVolumeMeter = /* 2.7 */ 33 | { 34 | 0xCAC1105F, 35 | 0x619B, 36 | 0x4D04, 37 | { 38 | 0x83, 39 | 0x1A, 40 | 0x44, 41 | 0xE1, 42 | 0xCB, 43 | 0xF1, 44 | 0x2D, 45 | 0x57 46 | } 47 | }; 48 | 49 | static FAPORegistrationProperties VolumeMeterProperties = 50 | { 51 | /* .clsid = */ {0}, 52 | /* .FriendlyName = */ 53 | { 54 | 'V', 'o', 'l', 'u', 'm', 'e', 'M', 'e', 't', 'e', 'r', '\0' 55 | }, 56 | /*.CopyrightInfo = */ 57 | { 58 | 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', 59 | 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0' 60 | }, 61 | /*.MajorVersion = */ 0, 62 | /*.MinorVersion = */ 0, 63 | /*.Flags = */( 64 | FAPO_FLAG_CHANNELS_MUST_MATCH | 65 | FAPO_FLAG_FRAMERATE_MUST_MATCH | 66 | FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | 67 | FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | 68 | FAPO_FLAG_INPLACE_SUPPORTED | 69 | FAPO_FLAG_INPLACE_REQUIRED 70 | ), 71 | /*.MinInputBufferCount = */ 1, 72 | /*.MaxInputBufferCount = */ 1, 73 | /*.MinOutputBufferCount = */ 1, 74 | /*.MaxOutputBufferCount =*/ 1 75 | }; 76 | 77 | typedef struct FAudioFXVolumeMeter 78 | { 79 | FAPOBase base; 80 | uint16_t channels; 81 | } FAudioFXVolumeMeter; 82 | 83 | uint32_t FAudioFXVolumeMeter_LockForProcess( 84 | FAudioFXVolumeMeter *fapo, 85 | uint32_t InputLockedParameterCount, 86 | const FAPOLockForProcessBufferParameters *pInputLockedParameters, 87 | uint32_t OutputLockedParameterCount, 88 | const FAPOLockForProcessBufferParameters *pOutputLockedParameters 89 | ) { 90 | FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*) 91 | fapo->base.m_pParameterBlocks; 92 | 93 | /* Verify parameter counts... */ 94 | if ( InputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinInputBufferCount || 95 | InputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxInputBufferCount || 96 | OutputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinOutputBufferCount || 97 | OutputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxOutputBufferCount ) 98 | { 99 | return FAUDIO_E_INVALID_ARG; 100 | } 101 | 102 | 103 | /* Validate input/output formats */ 104 | #define VERIFY_FORMAT_FLAG(flag, prop) \ 105 | if ( (fapo->base.m_pRegistrationProperties->Flags & flag) && \ 106 | (pInputLockedParameters->pFormat->prop != pOutputLockedParameters->pFormat->prop) ) \ 107 | { \ 108 | return FAUDIO_E_INVALID_ARG; \ 109 | } 110 | VERIFY_FORMAT_FLAG(FAPO_FLAG_CHANNELS_MUST_MATCH, nChannels) 111 | VERIFY_FORMAT_FLAG(FAPO_FLAG_FRAMERATE_MUST_MATCH, nSamplesPerSec) 112 | VERIFY_FORMAT_FLAG(FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH, wBitsPerSample) 113 | #undef VERIFY_FORMAT_FLAG 114 | if ( (fapo->base.m_pRegistrationProperties->Flags & FAPO_FLAG_BUFFERCOUNT_MUST_MATCH) && 115 | (InputLockedParameterCount != OutputLockedParameterCount) ) 116 | { 117 | return FAUDIO_E_INVALID_ARG; 118 | } 119 | 120 | /* Allocate volume meter arrays */ 121 | fapo->channels = pInputLockedParameters->pFormat->nChannels; 122 | levels[0].pPeakLevels = (float*) fapo->base.pMalloc( 123 | fapo->channels * sizeof(float) * 6 124 | ); 125 | FAudio_zero(levels[0].pPeakLevels, fapo->channels * sizeof(float) * 6); 126 | levels[0].pRMSLevels = levels[0].pPeakLevels + fapo->channels; 127 | levels[1].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 2); 128 | levels[1].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 3); 129 | levels[2].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 4); 130 | levels[2].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 5); 131 | 132 | fapo->base.m_fIsLocked = 1; 133 | return 0; 134 | } 135 | 136 | void FAudioFXVolumeMeter_UnlockForProcess(FAudioFXVolumeMeter *fapo) 137 | { 138 | FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*) 139 | fapo->base.m_pParameterBlocks; 140 | fapo->base.pFree(levels[0].pPeakLevels); 141 | fapo->base.m_fIsLocked = 0; 142 | } 143 | 144 | void FAudioFXVolumeMeter_Process( 145 | FAudioFXVolumeMeter *fapo, 146 | uint32_t InputProcessParameterCount, 147 | const FAPOProcessBufferParameters* pInputProcessParameters, 148 | uint32_t OutputProcessParameterCount, 149 | FAPOProcessBufferParameters* pOutputProcessParameters, 150 | int32_t IsEnabled 151 | ) { 152 | float peak; 153 | float total; 154 | float *buffer; 155 | uint32_t i, j; 156 | FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*) 157 | FAPOBase_BeginProcess(&fapo->base); 158 | 159 | /* TODO: This could probably be SIMD-ified... */ 160 | for (i = 0; i < fapo->channels; i += 1) 161 | { 162 | peak = 0.0f; 163 | total = 0.0f; 164 | buffer = ((float*) pInputProcessParameters->pBuffer) + i; 165 | for (j = 0; j < pInputProcessParameters->ValidFrameCount; j += 1, buffer += fapo->channels) 166 | { 167 | const float sampleAbs = FAudio_fabsf(*buffer); 168 | if (sampleAbs > peak) 169 | { 170 | peak = sampleAbs; 171 | } 172 | total += (*buffer) * (*buffer); 173 | } 174 | levels->pPeakLevels[i] = peak; 175 | levels->pRMSLevels[i] = FAudio_sqrtf( 176 | total / pInputProcessParameters->ValidFrameCount 177 | ); 178 | } 179 | 180 | FAPOBase_EndProcess(&fapo->base); 181 | } 182 | 183 | void FAudioFXVolumeMeter_GetParameters( 184 | FAudioFXVolumeMeter *fapo, 185 | FAudioFXVolumeMeterLevels *pParameters, 186 | uint32_t ParameterByteSize 187 | ) { 188 | FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*) 189 | fapo->base.m_pCurrentParameters; 190 | FAudio_assert(ParameterByteSize == sizeof(FAudioFXVolumeMeterLevels)); 191 | FAudio_assert(pParameters->ChannelCount == fapo->channels); 192 | 193 | /* Copy what's current as of the last Process */ 194 | if (pParameters->pPeakLevels != NULL) 195 | { 196 | FAudio_memcpy( 197 | pParameters->pPeakLevels, 198 | levels->pPeakLevels, 199 | fapo->channels * sizeof(float) 200 | ); 201 | } 202 | if (pParameters->pRMSLevels != NULL) 203 | { 204 | FAudio_memcpy( 205 | pParameters->pRMSLevels, 206 | levels->pRMSLevels, 207 | fapo->channels * sizeof(float) 208 | ); 209 | } 210 | } 211 | 212 | void FAudioFXVolumeMeter_Free(void* fapo) 213 | { 214 | FAudioFXVolumeMeter *volumemeter = (FAudioFXVolumeMeter*) fapo; 215 | volumemeter->base.pFree(volumemeter->base.m_pParameterBlocks); 216 | volumemeter->base.pFree(fapo); 217 | } 218 | 219 | /* Public API */ 220 | 221 | uint32_t FAudioCreateVolumeMeter(FAPO** ppApo, uint32_t Flags) 222 | { 223 | return FAudioCreateVolumeMeterWithCustomAllocatorEXT( 224 | ppApo, 225 | Flags, 226 | FAudio_malloc, 227 | FAudio_free, 228 | FAudio_realloc 229 | ); 230 | } 231 | 232 | uint32_t FAudioCreateVolumeMeterWithCustomAllocatorEXT( 233 | FAPO** ppApo, 234 | uint32_t Flags, 235 | FAudioMallocFunc customMalloc, 236 | FAudioFreeFunc customFree, 237 | FAudioReallocFunc customRealloc 238 | ) { 239 | /* Allocate... */ 240 | FAudioFXVolumeMeter *result = (FAudioFXVolumeMeter*) customMalloc( 241 | sizeof(FAudioFXVolumeMeter) 242 | ); 243 | uint8_t *params = (uint8_t*) customMalloc( 244 | sizeof(FAudioFXVolumeMeterLevels) * 3 245 | ); 246 | FAudio_zero(params, sizeof(FAudioFXVolumeMeterLevels) * 3); 247 | 248 | /* Initialize... */ 249 | FAudio_memcpy( 250 | &VolumeMeterProperties.clsid, 251 | &FAudioFX_CLSID_AudioVolumeMeter, 252 | sizeof(FAudioGUID) 253 | ); 254 | CreateFAPOBaseWithCustomAllocatorEXT( 255 | &result->base, 256 | &VolumeMeterProperties, 257 | params, 258 | sizeof(FAudioFXVolumeMeterLevels), 259 | 1, 260 | customMalloc, 261 | customFree, 262 | customRealloc 263 | ); 264 | 265 | /* Function table... */ 266 | result->base.base.LockForProcess = (LockForProcessFunc) 267 | FAudioFXVolumeMeter_LockForProcess; 268 | result->base.base.UnlockForProcess = (UnlockForProcessFunc) 269 | FAudioFXVolumeMeter_UnlockForProcess; 270 | result->base.base.Process = (ProcessFunc) 271 | FAudioFXVolumeMeter_Process; 272 | result->base.base.GetParameters = (GetParametersFunc) 273 | FAudioFXVolumeMeter_GetParameters; 274 | result->base.Destructor = FAudioFXVolumeMeter_Free; 275 | 276 | /* Finally. */ 277 | *ppApo = &result->base.base; 278 | return 0; 279 | } 280 | 281 | /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ 282 | -------------------------------------------------------------------------------- /tests/FAudio_compat.h: -------------------------------------------------------------------------------- 1 | /* map xaudio2 API to faudio API */ 2 | typedef uint32_t HRESULT; 3 | typedef uint32_t UINT32; 4 | typedef uint32_t DWORD; 5 | typedef uint8_t BOOL; 6 | 7 | #define WINAPI FAUDIOCALL 8 | 9 | #define TRUE 1 10 | #define FALSE 0 11 | 12 | #define S_OK 0 13 | #define XAUDIO2_E_INVALID_CALL FAUDIO_E_INVALID_CALL 14 | 15 | #define XAUDIO2_ANY_PROCESSOR FAUDIO_DEFAULT_PROCESSOR 16 | #define XAUDIO2_COMMIT_NOW FAUDIO_COMMIT_NOW 17 | #define XAUDIO2_END_OF_STREAM FAUDIO_END_OF_STREAM 18 | 19 | #define WAVE_FORMAT_IEEE_FLOAT FAUDIO_FORMAT_IEEE_FLOAT 20 | 21 | #define AudioCategory_GameEffects FAudioStreamCategory_GameEffects 22 | 23 | #define GlobalDefaultDevice FAudioGlobalDefaultDevice 24 | #define NotDefaultDevice FAudioNotDefaultDevice 25 | 26 | typedef FAudioBuffer XAUDIO2_BUFFER; 27 | typedef FAudioDeviceDetails XAUDIO2_DEVICE_DETAILS; 28 | typedef FAudioEffectChain XAUDIO2_EFFECT_CHAIN; 29 | typedef FAudioEffectDescriptor XAUDIO2_EFFECT_DESCRIPTOR; 30 | typedef FAudioVoiceDetails XAUDIO2_VOICE_DETAILS; 31 | typedef FAudioVoiceDetails XAUDIO27_VOICE_DETAILS; 32 | typedef FAudioVoiceState XAUDIO2_VOICE_STATE; 33 | typedef FAudioWaveFormatEx WAVEFORMATEX; 34 | typedef FAudioPerformanceData XAUDIO2_PERFORMANCE_DATA; 35 | 36 | typedef FAudioEngineCallback IXAudio2EngineCallback; 37 | typedef FAudioVoiceCallback IXAudio2VoiceCallback; 38 | 39 | typedef FAPO IXAPO; 40 | 41 | typedef FAudio IXAudio27; 42 | #define IXAudio27_CreateMasteringVoice FAudio_CreateMasteringVoice 43 | #define IXAudio27_CreateSourceVoice FAudio_CreateSourceVoice 44 | #define IXAudio27_CreateSubmixVoice FAudio_CreateSubmixVoice 45 | #define IXAudio27_GetDeviceCount FAudio_GetDeviceCount 46 | #define IXAudio27_GetDeviceDetails FAudio_GetDeviceDetails 47 | #define IXAudio27_GetPerformanceData FAudio_GetPerformanceData 48 | #define IXAudio27_Initialize FAudio_Initialize 49 | #define IXAudio27_RegisterForCallbacks FAudio_RegisterForCallbacks 50 | #define IXAudio27_Release FAudio_Release 51 | #define IXAudio27_StartEngine FAudio_StartEngine 52 | #define IXAudio27_StopEngine FAudio_StopEngine 53 | #define IXAudio27_UnregisterForCallbacks FAudio_UnregisterForCallbacks 54 | 55 | typedef FAudio IXAudio2; 56 | #define IXAudio2_CreateMasteringVoice FAudio_CreateMasteringVoice 57 | #define IXAudio2_CreateSourceVoice FAudio_CreateSourceVoice 58 | #define IXAudio2_CreateSubmixVoice FAudio_CreateSubmixVoice 59 | #define IXAudio2_GetPerformanceData FAudio_GetPerformanceData 60 | #define IXAudio2_RegisterForCallbacks FAudio_RegisterForCallbacks 61 | #define IXAudio2_Release FAudio_Release 62 | #define IXAudio2_StartEngine FAudio_StartEngine 63 | #define IXAudio2_StopEngine FAudio_StopEngine 64 | #define IXAudio2_UnregisterForCallbacks FAudio_UnregisterForCallbacks 65 | 66 | typedef FAudioMasteringVoice IXAudio2MasteringVoice; 67 | #define IXAudio2MasteringVoice_DestroyVoice FAudioVoice_DestroyVoice 68 | #define IXAudio2MasteringVoice_GetChannelMask FAudioMasteringVoice_GetChannelMask 69 | #define IXAudio2MasteringVoice_SetEffectChain FAudioVoice_SetEffectChain 70 | 71 | typedef FAudioSourceVoice IXAudio27SourceVoice; 72 | #define IXAudio27SourceVoice_DestroyVoice FAudioVoice_DestroyVoice 73 | #define IXAudio27SourceVoice_ExitLoop FAudioSourceVoice_ExitLoop 74 | #define IXAudio27SourceVoice_FlushSourceBuffers FAudioSourceVoice_FlushSourceBuffers 75 | #define IXAudio27SourceVoice_GetState(a,b) FAudioSourceVoice_GetState(a,b,0) 76 | #define IXAudio27SourceVoice_GetVoiceDetails FAudioVoice_GetVoiceDetails 77 | #define IXAudio27SourceVoice_SetChannelVolumes FAudioVoice_SetChannelVolumes 78 | #define IXAudio27SourceVoice_SetSourceSampleRate FAudioSourceVoice_SetSourceSampleRate 79 | #define IXAudio27SourceVoice_Start FAudioSourceVoice_Start 80 | #define IXAudio27SourceVoice_Stop FAudioSourceVoice_Stop 81 | #define IXAudio27SourceVoice_SubmitSourceBuffer FAudioSourceVoice_SubmitSourceBuffer 82 | 83 | typedef FAudioSourceVoice IXAudio2SourceVoice; 84 | #define IXAudio2SourceVoice_DestroyVoice FAudioVoice_DestroyVoice 85 | #define IXAudio2SourceVoice_ExitLoop FAudioSourceVoice_ExitLoop 86 | #define IXAudio2SourceVoice_FlushSourceBuffers FAudioSourceVoice_FlushSourceBuffers 87 | #define IXAudio2SourceVoice_GetState FAudioSourceVoice_GetState 88 | #define IXAudio2SourceVoice_GetVoiceDetails FAudioVoice_GetVoiceDetails 89 | #define IXAudio2SourceVoice_SetChannelVolumes FAudioVoice_SetChannelVolumes 90 | #define IXAudio2SourceVoice_SetSourceSampleRate FAudioSourceVoice_SetSourceSampleRate 91 | #define IXAudio2SourceVoice_Start FAudioSourceVoice_Start 92 | #define IXAudio2SourceVoice_Stop FAudioSourceVoice_Stop 93 | #define IXAudio2SourceVoice_SubmitSourceBuffer FAudioSourceVoice_SubmitSourceBuffer 94 | 95 | typedef FAudioSubmixVoice IXAudio27SubmixVoice; 96 | #define IXAudio27SubmixVoice_GetVoiceDetails FAudioVoice_GetVoiceDetails 97 | #define IXAudio27SubmixVoice_DestroyVoice FAudioVoice_DestroyVoice 98 | 99 | typedef FAudioSubmixVoice IXAudio2SubmixVoice; 100 | #define IXAudio2SubmixVoice_GetVoiceDetails FAudioVoice_GetVoiceDetails 101 | #define IXAudio2SubmixVoice_DestroyVoice FAudioVoice_DestroyVoice 102 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | # Build win32 executable to verify unit tests pass when run 2 | # against XAudio2 on Windows. 3 | 4 | WINEROOT = /usr/include/wine 5 | 6 | CROSSCC = x86_64-w64-mingw32-gcc 7 | 8 | CFLAGS += -g -Wall 9 | 10 | all: faudio_tests.exe 11 | 12 | faudio_tests.exe: xaudio2.c 13 | $(CROSSCC) $(CFLAGS) -Wall -I$(WINEROOT)/windows/ -o $@ $< -lole32 14 | -------------------------------------------------------------------------------- /utils/testfilter/audio.cpp: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | PFN_AUDIO_DESTROY_CONTEXT audio_destroy_context = NULL; 4 | PFN_AUDIO_CREATE_VOICE audio_create_voice = NULL; 5 | PFN_AUDIO_VOICE_DESTROY audio_voice_destroy = NULL; 6 | PFN_AUDIO_VOICE_SET_VOLUME audio_voice_set_volume = NULL; 7 | PFN_AUDIO_VOICE_SET_FREQUENCY audio_voice_set_frequency = NULL; 8 | 9 | PFN_AUDIO_CREATE_FILTER audio_create_filter = NULL; 10 | PFN_AUDIO_FILTER_UPDATE audio_filter_update = NULL; 11 | PFN_AUDIO_FILTER_APPLY audio_filter_apply = NULL; 12 | PFN_AUDIO_FILTER_APPLY audio_output_filter_apply = NULL; 13 | 14 | extern AudioContext *xaudio_create_context(); 15 | extern AudioContext *faudio_create_context(); 16 | 17 | AudioContext *audio_create_context(AudioEngine p_engine) 18 | { 19 | switch (p_engine) 20 | { 21 | #ifdef HAVE_XAUDIO2 22 | case AudioEngine_XAudio2: 23 | return xaudio_create_context(); 24 | #endif 25 | 26 | case AudioEngine_FAudio: 27 | return faudio_create_context(); 28 | 29 | default: 30 | return NULL; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /utils/testfilter/audio.h: -------------------------------------------------------------------------------- 1 | #ifndef FAUDIOFILTERDEMO_AUDIO_H 2 | #define FAUDIOFILTERDEMO_AUDIO_H 3 | 4 | #include 5 | 6 | #ifdef _MSC_VER 7 | #define HAVE_XAUDIO2 8 | #endif 9 | 10 | const float PI = 3.14159265358979323846f; 11 | 12 | // types 13 | struct AudioContext; 14 | struct AudioVoice; 15 | struct AudioFilter; 16 | 17 | enum AudioEngine { 18 | AudioEngine_XAudio2, 19 | AudioEngine_FAudio 20 | }; 21 | 22 | typedef void(*PFN_AUDIO_DESTROY_CONTEXT)(AudioContext *p_context); 23 | 24 | typedef AudioVoice *(*PFN_AUDIO_CREATE_VOICE)(AudioContext *p_context, float *p_buffer, size_t p_buffer_size, int p_sample_rate, int p_num_channels); 25 | typedef void (*PFN_AUDIO_VOICE_DESTROY)(AudioVoice *p_voice); 26 | typedef void (*PFN_AUDIO_VOICE_SET_VOLUME)(AudioVoice *p_voice, float p_volume); 27 | typedef void (*PFN_AUDIO_VOICE_SET_FREQUENCY)(AudioVoice *p_vioce, float p_frequency); 28 | 29 | typedef AudioFilter *(*PFN_AUDIO_CREATE_FILTER)(AudioContext *p_context); 30 | typedef void (*PFN_AUDIO_FILTER_UPDATE)(AudioFilter *p_filter, int p_type, float p_cutoff_frequency, float p_q); 31 | typedef void (*PFN_AUDIO_FILTER_APPLY)(AudioFilter *p_filter, AudioVoice *p_voice); 32 | 33 | // API 34 | AudioContext *audio_create_context(AudioEngine p_engine); 35 | 36 | extern PFN_AUDIO_DESTROY_CONTEXT audio_destroy_context; 37 | extern PFN_AUDIO_CREATE_VOICE audio_create_voice; 38 | extern PFN_AUDIO_VOICE_DESTROY audio_voice_destroy; 39 | extern PFN_AUDIO_VOICE_SET_VOLUME audio_voice_set_volume; 40 | extern PFN_AUDIO_VOICE_SET_FREQUENCY audio_voice_set_frequency; 41 | 42 | extern PFN_AUDIO_CREATE_FILTER audio_create_filter; 43 | extern PFN_AUDIO_FILTER_UPDATE audio_filter_update; 44 | extern PFN_AUDIO_FILTER_APPLY audio_filter_apply; 45 | extern PFN_AUDIO_FILTER_APPLY audio_output_filter_apply; 46 | 47 | #endif // FAUDIOFILTERDEMO_AUDIO_H 48 | -------------------------------------------------------------------------------- /utils/testfilter/audio_faudio.cpp: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct AudioContext 8 | { 9 | FAudio *faudio; 10 | FAudioMasteringVoice *mastering_voice; 11 | }; 12 | 13 | struct AudioVoice 14 | { 15 | AudioContext *context; 16 | FAudioSourceVoice *voice; 17 | }; 18 | 19 | struct AudioFilter 20 | { 21 | AudioContext *context; 22 | FAudioFilterParameters params; 23 | }; 24 | 25 | void faudio_destroy_context(AudioContext *p_context) 26 | { 27 | FAudioVoice_DestroyVoice(p_context->mastering_voice); 28 | FAudio_Release(p_context->faudio); 29 | delete p_context; 30 | } 31 | 32 | AudioVoice *faudio_create_voice(AudioContext *p_context, float *p_buffer, size_t p_buffer_size, int p_sample_rate, int p_num_channels) 33 | { 34 | // create a source voice 35 | FAudioWaveFormatEx waveFormat; 36 | waveFormat.wFormatTag = 3; 37 | waveFormat.nChannels = p_num_channels; 38 | waveFormat.nSamplesPerSec = p_sample_rate; 39 | waveFormat.nAvgBytesPerSec = p_sample_rate * 4; 40 | waveFormat.nBlockAlign = p_num_channels * 4; 41 | waveFormat.wBitsPerSample = 32; 42 | waveFormat.cbSize = 0; 43 | 44 | FAudioSourceVoice *voice; 45 | FAudioSendDescriptor send; 46 | FAudioVoiceSends sends; 47 | sends.SendCount = 1; 48 | sends.pSends = &send; 49 | send.Flags = FAUDIO_SEND_USEFILTER; 50 | send.pOutputVoice = p_context->mastering_voice; 51 | uint32_t hr = FAudio_CreateSourceVoice(p_context->faudio, &voice, &waveFormat, FAUDIO_VOICE_USEFILTER, FAUDIO_DEFAULT_FREQ_RATIO, NULL, &sends, NULL); 52 | 53 | if (hr != 0) { 54 | return NULL; 55 | } 56 | 57 | FAudioVoice_SetVolume(voice, 0.0f, FAUDIO_COMMIT_NOW); 58 | 59 | // submit the array 60 | FAudioBuffer buffer = { 0 }; 61 | buffer.AudioBytes = 4 * p_buffer_size * p_num_channels; 62 | buffer.pAudioData = (uint8_t *)p_buffer; 63 | buffer.Flags = FAUDIO_END_OF_STREAM; 64 | buffer.PlayBegin = 0; 65 | buffer.PlayLength = p_buffer_size; 66 | buffer.LoopBegin = 0; 67 | buffer.LoopLength = p_buffer_size; 68 | buffer.LoopCount = FAUDIO_LOOP_INFINITE; 69 | 70 | FAudioSourceVoice_SubmitSourceBuffer(voice, &buffer, NULL); 71 | 72 | // start the voice playing 73 | FAudioSourceVoice_Start(voice, 0, FAUDIO_COMMIT_NOW); 74 | 75 | // return a voice struct 76 | AudioVoice *result = new AudioVoice(); 77 | result->context = p_context; 78 | result->voice = voice; 79 | return result; 80 | } 81 | 82 | void faudio_voice_destroy(AudioVoice *p_voice) 83 | { 84 | FAudioVoice_DestroyVoice(p_voice->voice); 85 | } 86 | 87 | void faudio_voice_set_volume(AudioVoice *p_voice, float p_volume) 88 | { 89 | FAudioVoice_SetVolume(p_voice->voice, p_volume, FAUDIO_COMMIT_NOW); 90 | } 91 | 92 | void faudio_voice_set_frequency(AudioVoice *p_voice, float p_frequency) 93 | { 94 | FAudioSourceVoice_SetFrequencyRatio(p_voice->voice, p_frequency, FAUDIO_COMMIT_NOW); 95 | } 96 | 97 | AudioFilter *faudio_create_filter(AudioContext *p_context) 98 | { 99 | AudioFilter *filter = new AudioFilter(); 100 | filter->context = p_context; 101 | return filter; 102 | } 103 | 104 | void faudio_filter_update(AudioFilter *p_filter, int p_type, float p_cutoff_frequency, float p_q) 105 | { 106 | if (p_type != -1) 107 | { 108 | p_filter->params.Type = FAudioFilterType(p_type); 109 | p_filter->params.Frequency = (float) (2 * sin(PI * p_cutoff_frequency / 44100)); 110 | p_filter->params.OneOverQ = (float)(1.0 / p_q); 111 | } 112 | else 113 | { 114 | // documentation of XAUDIO2_FILTER_PARAMETERS: 115 | // Setting XAUDIO2_FILTER_PARAMETERS with the following values is acoustically equivalent to the filter being fully bypassed. 116 | p_filter->params.Type = FAudioLowPassFilter; 117 | p_filter->params.Frequency = 1.0f; 118 | p_filter->params.OneOverQ = 1.0f; 119 | } 120 | } 121 | 122 | void faudio_filter_apply(AudioFilter *p_filter, AudioVoice *p_voice) 123 | { 124 | FAudioVoice_SetFilterParameters(p_voice->voice, &p_filter->params, FAUDIO_COMMIT_NOW); 125 | } 126 | 127 | void faudio_output_filter_apply(AudioFilter *p_filter, AudioVoice *p_voice) 128 | { 129 | FAudioVoice_SetOutputFilterParameters(p_voice->voice, p_voice->context->mastering_voice, &p_filter->params, FAUDIO_COMMIT_NOW); 130 | } 131 | 132 | AudioContext *faudio_create_context() 133 | { 134 | // setup function pointers 135 | audio_destroy_context = faudio_destroy_context; 136 | audio_create_voice = faudio_create_voice; 137 | audio_voice_destroy = faudio_voice_destroy; 138 | audio_voice_set_volume = faudio_voice_set_volume; 139 | audio_voice_set_frequency = faudio_voice_set_frequency; 140 | audio_create_filter = faudio_create_filter; 141 | audio_filter_update = faudio_filter_update; 142 | audio_filter_apply = faudio_filter_apply; 143 | audio_output_filter_apply = faudio_output_filter_apply; 144 | 145 | // create Faudio object 146 | FAudio *faudio; 147 | 148 | uint32_t hr = FAudioCreate(&faudio, 0, FAUDIO_DEFAULT_PROCESSOR); 149 | if (hr != 0) 150 | return NULL; 151 | 152 | // create a mastering voice 153 | FAudioMasteringVoice *mastering_voice; 154 | 155 | hr = FAudio_CreateMasteringVoice(faudio, &mastering_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, 0, NULL); 156 | if (hr != 0) 157 | return NULL; 158 | 159 | // return a context object 160 | AudioContext *context = new AudioContext(); 161 | context->faudio = faudio; 162 | context->mastering_voice = mastering_voice; 163 | 164 | return context; 165 | } 166 | -------------------------------------------------------------------------------- /utils/testfilter/audio_player.h: -------------------------------------------------------------------------------- 1 | #ifndef FAUDIOFILTERDEMO_AUDIO_PLAYER_H 2 | #define FAUDIOFILTERDEMO_AUDIO_PLAYER_H 3 | 4 | #include "oscillator.h" 5 | #include "audio.h" 6 | #include 7 | 8 | class AudioPlayer 9 | { 10 | public : 11 | enum OscillatorType { 12 | SineWave = 0, 13 | SquareWave, 14 | SawTooth, 15 | Oscillator_Count 16 | }; 17 | 18 | AudioPlayer() 19 | { 20 | SDL_zero(m_oscillators); 21 | setup(AudioEngine_FAudio, 1); 22 | } 23 | 24 | void setup(AudioEngine p_engine, int p_channels) 25 | { 26 | oscillator_sine_wave(&m_oscillators[SineWave], p_channels); 27 | oscillator_square_wave(&m_oscillators[SquareWave], p_channels); 28 | oscillator_saw_tooth(&m_oscillators[SawTooth], p_channels); 29 | 30 | m_context = audio_create_context(p_engine); 31 | for (int idx = 0; idx < Oscillator_Count; ++idx) 32 | { 33 | m_voices[idx] = audio_create_voice(m_context, m_oscillators[idx].buffer, Oscillator::CHANNEL_BUFFER_LENGTH, Oscillator::SAMPLE_RATE, p_channels); 34 | } 35 | m_filter = audio_create_filter(m_context); 36 | m_output_filter = audio_create_filter(m_context); 37 | } 38 | 39 | void shutdown() 40 | { 41 | if (m_context == NULL) 42 | return; 43 | 44 | for (int idx = 0; idx < Oscillator_Count; ++idx) 45 | { 46 | audio_voice_destroy(m_voices[idx]); 47 | m_voices[idx] = NULL; 48 | } 49 | 50 | audio_destroy_context(m_context); 51 | m_context = NULL; 52 | } 53 | 54 | void oscillator_change(OscillatorType p_type, float p_frequency, float p_volume) 55 | { 56 | audio_voice_set_frequency(m_voices[p_type], p_frequency / Oscillator::BASE_FREQ); 57 | audio_voice_set_volume(m_voices[p_type], p_volume); 58 | } 59 | 60 | void filter_change(int p_type, float p_cutoff_frequency, float p_q) 61 | { 62 | audio_filter_update(m_filter, p_type, p_cutoff_frequency, p_q); 63 | for (int idx = 0; idx < Oscillator_Count; ++idx) 64 | { 65 | audio_filter_apply(m_filter, m_voices[idx]); 66 | } 67 | } 68 | 69 | void output_filter_change(int p_type, float p_cutoff_frequency, float p_q) 70 | { 71 | audio_filter_update(m_output_filter, p_type, p_cutoff_frequency, p_q); 72 | for (int idx = 0; idx < Oscillator_Count; ++idx) 73 | { 74 | audio_output_filter_apply(m_output_filter, m_voices[idx]); 75 | } 76 | } 77 | 78 | private : 79 | Oscillator m_oscillators[Oscillator_Count]; 80 | AudioContext * m_context; 81 | AudioVoice * m_voices[Oscillator_Count]; 82 | AudioFilter * m_filter; 83 | AudioFilter * m_output_filter; 84 | }; 85 | 86 | #endif // FAUDIOFILTERDEMO_AUDIO_PLAYER_H 87 | -------------------------------------------------------------------------------- /utils/testfilter/audio_xaudio.cpp: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | #ifdef HAVE_XAUDIO2 4 | 5 | #include 6 | #include 7 | 8 | struct AudioContext 9 | { 10 | IXAudio2 *xaudio2; 11 | IXAudio2MasteringVoice *mastering_voice; 12 | }; 13 | 14 | struct AudioVoice 15 | { 16 | AudioContext *context; 17 | IXAudio2SourceVoice *voice; 18 | }; 19 | 20 | struct AudioFilter 21 | { 22 | AudioContext *context; 23 | XAUDIO2_FILTER_PARAMETERS params; 24 | }; 25 | 26 | void xaudio_destroy_context(AudioContext *p_context) 27 | { 28 | p_context->mastering_voice->DestroyVoice(); 29 | p_context->xaudio2->Release(); 30 | delete p_context; 31 | } 32 | 33 | AudioVoice *xaudio_create_voice(AudioContext *p_context, float *p_buffer, size_t p_buffer_size, int p_sample_rate, int p_num_channels) 34 | { 35 | // create a source voice 36 | WAVEFORMATEX waveFormat; 37 | waveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; 38 | waveFormat.nChannels = p_num_channels; 39 | waveFormat.nSamplesPerSec = p_sample_rate; 40 | waveFormat.nBlockAlign = p_num_channels * 4; 41 | waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; 42 | waveFormat.wBitsPerSample = 32; 43 | waveFormat.cbSize = 0; 44 | 45 | IXAudio2SourceVoice *voice; 46 | XAUDIO2_SEND_DESCRIPTOR send; 47 | XAUDIO2_VOICE_SENDS sends; 48 | sends.SendCount = 1; 49 | sends.pSends = &send; 50 | send.Flags = XAUDIO2_SEND_USEFILTER; 51 | send.pOutputVoice = p_context->mastering_voice; 52 | HRESULT hr = p_context->xaudio2->CreateSourceVoice(&voice, &waveFormat, XAUDIO2_VOICE_USEFILTER, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, &sends); 53 | 54 | if (FAILED(hr)) { 55 | return NULL; 56 | } 57 | 58 | voice->SetVolume(0.0f); 59 | 60 | // submit the array 61 | XAUDIO2_BUFFER buffer = { 0 }; 62 | buffer.AudioBytes = 4 * p_buffer_size * p_num_channels; 63 | buffer.pAudioData = (byte *)p_buffer; 64 | buffer.Flags = XAUDIO2_END_OF_STREAM; 65 | buffer.PlayBegin = 0; 66 | buffer.PlayLength = 0; 67 | buffer.LoopBegin = 0; 68 | buffer.LoopLength = 0; 69 | buffer.LoopCount = XAUDIO2_LOOP_INFINITE; 70 | 71 | hr = voice->SubmitSourceBuffer(&buffer); 72 | 73 | if (FAILED(hr)) { 74 | return NULL; 75 | } 76 | 77 | // start the voice playing 78 | voice->Start(); 79 | 80 | // return a voice struct 81 | AudioVoice *result = new AudioVoice(); 82 | result->context = p_context; 83 | result->voice = voice; 84 | return result; 85 | } 86 | 87 | void xaudio_voice_destroy(AudioVoice *p_voice) 88 | { 89 | 90 | } 91 | 92 | void xaudio_voice_set_volume(AudioVoice *p_voice, float p_volume) 93 | { 94 | p_voice->voice->SetVolume(p_volume); 95 | } 96 | 97 | void xaudio_voice_set_frequency(AudioVoice *p_voice, float p_frequency) 98 | { 99 | p_voice->voice->SetFrequencyRatio(p_frequency); 100 | } 101 | 102 | AudioFilter *xaudio_create_filter(AudioContext *p_context) 103 | { 104 | AudioFilter *filter = new AudioFilter(); 105 | filter->context = p_context; 106 | return filter; 107 | } 108 | 109 | void xaudio_filter_update(AudioFilter *p_filter, int p_type, float p_cutoff_frequency, float p_q) 110 | { 111 | if (p_type != -1) 112 | { 113 | p_filter->params.Type = XAUDIO2_FILTER_TYPE(p_type); 114 | p_filter->params.Frequency = (float) (2 * sin(PI * p_cutoff_frequency / 44100)); 115 | p_filter->params.OneOverQ = (float)(1.0 / p_q); 116 | } 117 | else 118 | { 119 | // documentation of XAUDIO2_FILTER_PARAMETERS: 120 | // Setting XAUDIO2_FILTER_PARAMETERS with the following values is acoustically equivalent to the filter being fully bypassed. 121 | p_filter->params.Type = LowPassFilter; 122 | p_filter->params.Frequency = 1.0f; 123 | p_filter->params.OneOverQ = 1.0f; 124 | } 125 | } 126 | 127 | void xaudio_filter_apply(AudioFilter *p_filter, AudioVoice *p_voice) 128 | { 129 | p_voice->voice->SetFilterParameters(&p_filter->params, XAUDIO2_COMMIT_ALL); 130 | } 131 | 132 | void xaudio_output_filter_apply(AudioFilter *p_filter, AudioVoice *p_voice) 133 | { 134 | p_voice->voice->SetOutputFilterParameters(p_voice->context->mastering_voice, &p_filter->params, XAUDIO2_COMMIT_ALL); 135 | } 136 | 137 | AudioContext *xaudio_create_context() 138 | { 139 | // setup function pointers 140 | audio_destroy_context = xaudio_destroy_context; 141 | audio_create_voice = xaudio_create_voice; 142 | audio_voice_destroy = xaudio_voice_destroy; 143 | audio_voice_set_volume = xaudio_voice_set_volume; 144 | audio_voice_set_frequency = xaudio_voice_set_frequency; 145 | audio_create_filter = xaudio_create_filter; 146 | audio_filter_update = xaudio_filter_update; 147 | audio_filter_apply = xaudio_filter_apply; 148 | audio_output_filter_apply = xaudio_output_filter_apply; 149 | 150 | // create XAudio object 151 | IXAudio2 *xaudio2; 152 | 153 | HRESULT hr = XAudio2Create(&xaudio2); 154 | if (FAILED(hr)) 155 | return NULL; 156 | 157 | // create a mastering voice 158 | IXAudio2MasteringVoice *mastering_voice; 159 | 160 | hr = xaudio2->CreateMasteringVoice(&mastering_voice); 161 | if (FAILED(hr)) 162 | return NULL; 163 | 164 | // return a context object 165 | AudioContext *context = new AudioContext(); 166 | context->xaudio2 = xaudio2; 167 | context->mastering_voice = mastering_voice; 168 | 169 | return context; 170 | } 171 | 172 | #endif // HAVE_XAUDIO2 173 | -------------------------------------------------------------------------------- /utils/testfilter/oscillator.cpp: -------------------------------------------------------------------------------- 1 | #include "oscillator.h" 2 | #include 3 | 4 | #define _USE_MATH_DEFINES 5 | #include 6 | 7 | void oscillator_sine_wave(Oscillator *p_oscillator, int p_num_channels) 8 | { 9 | if (p_oscillator->buffer != NULL) 10 | delete[] p_oscillator->buffer; 11 | 12 | p_oscillator->buffer = new float[Oscillator::CHANNEL_BUFFER_LENGTH * p_num_channels]; 13 | 14 | for (int sample = 0; sample < Oscillator::CHANNEL_BUFFER_LENGTH; ++sample) 15 | { 16 | float value = (float) sin(2 * M_PI * sample / Oscillator::CHANNEL_BUFFER_LENGTH); 17 | 18 | for (int channel = 0; channel < p_num_channels; ++channel) 19 | { 20 | p_oscillator->buffer[sample * p_num_channels + channel] = value; 21 | } 22 | } 23 | } 24 | 25 | void oscillator_square_wave(Oscillator *p_oscillator, int p_num_channels) 26 | { 27 | if (p_oscillator->buffer != NULL) 28 | delete[] p_oscillator->buffer; 29 | 30 | p_oscillator->buffer = new float[Oscillator::CHANNEL_BUFFER_LENGTH * p_num_channels]; 31 | 32 | for (int sample = 0; sample < Oscillator::CHANNEL_BUFFER_LENGTH / 2; ++sample) 33 | for (int channel = 0; channel < p_num_channels; ++channel) 34 | { 35 | p_oscillator->buffer[sample * p_num_channels + channel] = 1.0f; 36 | } 37 | 38 | for (int sample = Oscillator::CHANNEL_BUFFER_LENGTH / 2; sample < Oscillator::CHANNEL_BUFFER_LENGTH; ++sample) 39 | for (int channel = 0; channel < p_num_channels; ++channel) 40 | { 41 | p_oscillator->buffer[sample * p_num_channels + channel] = -1.0f; 42 | } 43 | } 44 | 45 | void oscillator_saw_tooth(Oscillator *p_oscillator, int p_num_channels) 46 | { 47 | if (p_oscillator->buffer != NULL) 48 | delete[] p_oscillator->buffer; 49 | 50 | p_oscillator->buffer = new float[Oscillator::CHANNEL_BUFFER_LENGTH * p_num_channels]; 51 | 52 | for (int sample = 0; sample < Oscillator::CHANNEL_BUFFER_LENGTH; ++sample) 53 | { 54 | float value = (2.0f * sample / Oscillator::CHANNEL_BUFFER_LENGTH) - 1.0f; 55 | 56 | for (int channel = 0; channel < p_num_channels; ++channel) 57 | { 58 | p_oscillator->buffer[sample * p_num_channels + channel] = value; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /utils/testfilter/oscillator.h: -------------------------------------------------------------------------------- 1 | #ifndef FACTFILTERDEMO_OSCILLATOR_H 2 | #define FACTFILTERDEMO_OSCILLATOR_H 3 | 4 | struct Oscillator 5 | { 6 | static const int BASE_FREQ = 20; 7 | static const int SAMPLE_RATE = 48000; 8 | static const int CHANNEL_BUFFER_LENGTH = SAMPLE_RATE / BASE_FREQ; 9 | 10 | float *buffer; 11 | }; 12 | 13 | void oscillator_sine_wave(Oscillator *p_oscillator, int p_num_channels); 14 | void oscillator_square_wave(Oscillator *p_oscillator, int p_num_channels); 15 | void oscillator_saw_tooth(Oscillator *p_oscillator, int p_num_channels); 16 | 17 | #endif // FACTFILTERDEMO_OSCILLATOR_H -------------------------------------------------------------------------------- /utils/testfilter/testfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "../uicommon/imgui.h" 2 | 3 | #include "audio_player.h" 4 | #include 5 | 6 | const char* TOOL_NAME = "Filter Test Tool"; 7 | int TOOL_WIDTH = 640; 8 | int TOOL_HEIGHT = 580; 9 | 10 | static const int NOTE_MIN = 24; 11 | static const int NOTE_MAX = 96; 12 | 13 | float note_to_frequency(int p_note) 14 | { 15 | return (float)(440.0f * pow(2.0, (p_note - 69.0) / 12.0)); 16 | } 17 | 18 | int next_window_dims(int y_pos, int height) 19 | { 20 | ImGui::SetNextWindowPos(ImVec2(0, static_cast(y_pos))); 21 | ImGui::SetNextWindowSize(ImVec2(640, static_cast(height))); 22 | 23 | return y_pos + height; 24 | } 25 | 26 | void FAudioTool_Quit() 27 | { 28 | /* Nothing to do... */ 29 | } 30 | 31 | void FAudioTool_Update() 32 | { 33 | bool update_engine = false; 34 | bool update_sine = false; 35 | bool update_square = false; 36 | bool update_saw = false; 37 | bool update_filter = false; 38 | bool update_output_filter = false; 39 | 40 | // gui 41 | int window_y = next_window_dims(0, 80); 42 | ImGui::Begin("Output Audio Engine"); 43 | 44 | static int audio_engine = (int)AudioEngine_FAudio; 45 | update_engine |= ImGui::RadioButton("FAudio", &audio_engine, (int)AudioEngine_FAudio); 46 | #ifdef HAVE_XAUDIO2 47 | ImGui::SameLine(); 48 | update_engine |= ImGui::RadioButton("XAudio2", &audio_engine, (int)AudioEngine_XAudio2); 49 | #endif 50 | 51 | static int num_channels = 1; 52 | update_engine |= ImGui::RadioButton("1 Channel", &num_channels, 1); ImGui::SameLine(); 53 | update_engine |= ImGui::RadioButton("2 Channels", &num_channels, 2); 54 | 55 | ImGui::End(); 56 | 57 | window_y = next_window_dims(window_y, 80); 58 | ImGui::Begin("Sine Wave Generator"); 59 | 60 | static int sine_note = 60; 61 | update_sine |= ImGui::SliderInt("Frequency", &sine_note, NOTE_MIN, NOTE_MAX); ImGui::SameLine(); 62 | ImGui::Text(" (%.2f Hz)", note_to_frequency(sine_note)); 63 | 64 | static float sine_volume = 0.0f; 65 | update_sine |= ImGui::SliderFloat("Volume", &sine_volume, 0.0f, 1.0f); 66 | 67 | ImGui::End(); 68 | 69 | window_y = next_window_dims(window_y, 80); 70 | ImGui::Begin("Square Wave Generator"); 71 | 72 | static int square_note = 60; 73 | update_square |= ImGui::SliderInt("Frequency", &square_note, NOTE_MIN, NOTE_MAX); ImGui::SameLine(); 74 | ImGui::Text(" (%.2f Hz)", note_to_frequency(square_note)); 75 | 76 | static float square_volume = 0.0f; 77 | update_square |= ImGui::SliderFloat("Volume", &square_volume, 0.0f, 1.0f); 78 | 79 | ImGui::End(); 80 | 81 | window_y = next_window_dims(window_y, 80); 82 | ImGui::Begin("Saw Tooth Generator"); 83 | 84 | static int saw_note = 60; 85 | update_saw |= ImGui::SliderInt("Frequency", &saw_note, NOTE_MIN, NOTE_MAX); ImGui::SameLine(); 86 | ImGui::Text(" (%.2f Hz)", note_to_frequency(saw_note)); 87 | 88 | static float saw_volume = 0.0f; 89 | update_saw |= ImGui::SliderFloat("Volume", &saw_volume, 0.0f, 1.0f); 90 | 91 | ImGui::End(); 92 | 93 | window_y = next_window_dims(window_y, 100); 94 | ImGui::Begin("Filter"); 95 | 96 | static int filter_type = -1; 97 | static int filter_cutoff_note = 60; 98 | static float filter_q = 0.7f; 99 | 100 | update_filter |= ImGui::RadioButton("None", &filter_type, -1); ImGui::SameLine(); 101 | update_filter |= ImGui::RadioButton("Low-Pass", &filter_type, 0); ImGui::SameLine(); 102 | update_filter |= ImGui::RadioButton("Band-Pass", &filter_type, 1); ImGui::SameLine(); 103 | update_filter |= ImGui::RadioButton("High-Pass", &filter_type, 2); ImGui::SameLine(); 104 | update_filter |= ImGui::RadioButton("Notch", &filter_type, 3); 105 | 106 | update_filter |= ImGui::SliderInt("Cutoff Frequency", &filter_cutoff_note, 12, 108); ImGui::SameLine(); 107 | ImGui::Text(" (%.2f Hz)", note_to_frequency(filter_cutoff_note)); 108 | update_filter |= ImGui::SliderFloat("Q", &filter_q, 0.7f, 100.0f, "%.1f"); 109 | 110 | ImGui::End(); 111 | 112 | window_y = next_window_dims(window_y, 100); 113 | ImGui::Begin("Output Filter"); 114 | 115 | static int output_filter_type = -1; 116 | static int output_filter_cutoff_note = 60; 117 | static float output_filter_q = 0.7f; 118 | 119 | update_output_filter |= ImGui::RadioButton("None", &output_filter_type, -1); ImGui::SameLine(); 120 | update_output_filter |= ImGui::RadioButton("Low-Pass", &output_filter_type, 0); ImGui::SameLine(); 121 | update_output_filter |= ImGui::RadioButton("Band-Pass", &output_filter_type, 1); ImGui::SameLine(); 122 | update_output_filter |= ImGui::RadioButton("High-Pass", &output_filter_type, 2); ImGui::SameLine(); 123 | update_output_filter |= ImGui::RadioButton("Notch", &output_filter_type, 3); 124 | 125 | update_output_filter |= ImGui::SliderInt("Cutoff Frequency", &output_filter_cutoff_note, 12, 108); ImGui::SameLine(); 126 | ImGui::Text(" (%.2f Hz)", note_to_frequency(output_filter_cutoff_note)); 127 | update_output_filter |= ImGui::SliderFloat("Q", &output_filter_q, 0.7f, 100.0f, "%.1f"); 128 | 129 | ImGui::End(); 130 | 131 | // audio control 132 | static AudioPlayer player; 133 | 134 | if (update_engine) 135 | { 136 | player.shutdown(); 137 | player.setup((AudioEngine)audio_engine, num_channels); 138 | } 139 | 140 | if (update_sine | update_engine) 141 | { 142 | player.oscillator_change(AudioPlayer::SineWave, note_to_frequency(sine_note), sine_volume); 143 | } 144 | 145 | if (update_square | update_engine) 146 | { 147 | player.oscillator_change(AudioPlayer::SquareWave, note_to_frequency(square_note), square_volume); 148 | } 149 | 150 | if (update_saw | update_engine) 151 | { 152 | player.oscillator_change(AudioPlayer::SawTooth, note_to_frequency(saw_note), saw_volume); 153 | } 154 | 155 | if (update_filter | update_engine) 156 | { 157 | player.filter_change(filter_type, note_to_frequency(filter_cutoff_note), filter_q); 158 | } 159 | if (update_output_filter | update_engine) 160 | { 161 | player.output_filter_change(output_filter_type, note_to_frequency(output_filter_cutoff_note), output_filter_q); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /utils/testreverb/audio.cpp: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | #include 4 | 5 | const char *audio_voice_type_names[3] = 6 | { 7 | "Source Voice", 8 | "Submix Voice", 9 | "Mastering Voice" 10 | }; 11 | 12 | const char *audio_reverb_preset_names[] = 13 | { 14 | "Generic", 15 | "Padded Cell", 16 | "Room ", 17 | "Bathroom", 18 | "Livingroom", 19 | "Stone Room", 20 | "Auditorium", 21 | "Concert Hall", 22 | "Cave", 23 | "Arena", 24 | "Hangar", 25 | "Carpeted Hallway", 26 | "Hallway", 27 | "Stone Corridor", 28 | "Alley", 29 | "Forest", 30 | "City", 31 | "Mountains", 32 | "Quarry", 33 | "Plain", 34 | "Parking Lot", 35 | "Sewerpipe", 36 | "Underwater", 37 | "Small Room", 38 | "Medium Room", 39 | "Large Room", 40 | "Medium Hall", 41 | "Large Hall", 42 | "Plate", 43 | }; 44 | 45 | // see xaudio2fx.h 46 | const ReverbI3DL2Parameters audio_reverb_presets_i3dl2[] = 47 | { 48 | {100, -1000, -100,0.0f, 1.49f,0.83f, -2602,0.007f, 200,0.011f,100.0f,100.0f,5000.0f}, 49 | {100, -1000,-6000,0.0f, 0.17f,0.10f, -1204,0.001f, 207,0.002f,100.0f,100.0f,5000.0f}, 50 | {100, -1000, -454,0.0f, 0.40f,0.83f, -1646,0.002f, 53,0.003f,100.0f,100.0f,5000.0f}, 51 | {100, -1000,-1200,0.0f, 1.49f,0.54f, -370,0.007f, 1030,0.011f,100.0f, 60.0f,5000.0f}, 52 | {100, -1000,-6000,0.0f, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f,5000.0f}, 53 | {100, -1000, -300,0.0f, 2.31f,0.64f, -711,0.012f, 83,0.017f,100.0f,100.0f,5000.0f}, 54 | {100, -1000, -476,0.0f, 4.32f,0.59f, -789,0.020f, -289,0.030f,100.0f,100.0f,5000.0f}, 55 | {100, -1000, -500,0.0f, 3.92f,0.70f, -1230,0.020f, -2,0.029f,100.0f,100.0f,5000.0f}, 56 | {100, -1000, 0,0.0f, 2.91f,1.30f, -602,0.015f, -302,0.022f,100.0f,100.0f,5000.0f}, 57 | {100, -1000, -698,0.0f, 7.24f,0.33f, -1166,0.020f, 16,0.030f,100.0f,100.0f,5000.0f}, 58 | {100, -1000,-1000,0.0f,10.05f,0.23f, -602,0.020f, 198,0.030f,100.0f,100.0f,5000.0f}, 59 | {100, -1000,-4000,0.0f, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f,5000.0f}, 60 | {100, -1000, -300,0.0f, 1.49f,0.59f, -1219,0.007f, 441,0.011f,100.0f,100.0f,5000.0f}, 61 | {100, -1000, -237,0.0f, 2.70f,0.79f, -1214,0.013f, 395,0.020f,100.0f,100.0f,5000.0f}, 62 | {100, -1000, -270,0.0f, 1.49f,0.86f, -1204,0.007f, -4,0.011f,100.0f,100.0f,5000.0f}, 63 | {100, -1000,-3300,0.0f, 1.49f,0.54f, -2560,0.162f, -613,0.088f, 79.0f,100.0f,5000.0f}, 64 | {100, -1000, -800,0.0f, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f,5000.0f}, 65 | {100, -1000,-2500,0.0f, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f,5000.0f}, 66 | {100, -1000,-1000,0.0f, 1.49f,0.83f,-10000,0.061f, 500,0.025f,100.0f,100.0f,5000.0f}, 67 | {100, -1000,-2000,0.0f, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f,5000.0f}, 68 | {100, -1000, 0,0.0f, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f,5000.0f}, 69 | {100, -1000,-1000,0.0f, 2.81f,0.14f, 429,0.014f, 648,0.021f, 80.0f, 60.0f,5000.0f}, 70 | {100, -1000,-4000,0.0f, 1.49f,0.10f, -449,0.007f, 1700,0.011f,100.0f,100.0f,5000.0f}, 71 | {100, -1000, -600,0.0f, 1.10f,0.83f, -400,0.005f, 500,0.010f,100.0f,100.0f,5000.0f}, 72 | {100, -1000, -600,0.0f, 1.30f,0.83f, -1000,0.010f, -200,0.020f,100.0f,100.0f,5000.0f}, 73 | {100, -1000, -600,0.0f, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f,5000.0f}, 74 | {100, -1000, -600,0.0f, 1.80f,0.70f, -1300,0.015f, -800,0.030f,100.0f,100.0f,5000.0f}, 75 | {100, -1000, -600,0.0f, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f,5000.0f}, 76 | {100, -1000, -200,0.0f, 1.30f,0.90f, 0,0.002f, 0,0.010f,100.0f, 75.0f,5000.0f} 77 | }; 78 | const ReverbParameters *audio_reverb_presets = NULL; 79 | const size_t audio_reverb_preset_count = sizeof(audio_reverb_preset_names) / sizeof(audio_reverb_preset_names[0]); 80 | 81 | PFN_AUDIO_DESTROY_CONTEXT audio_destroy_context = NULL; 82 | PFN_AUDIO_CREATE_VOICE audio_create_voice = NULL; 83 | PFN_AUDIO_WAVE_LOAD audio_wave_load = NULL; 84 | PFN_AUDIO_WAVE_PLAY audio_wave_play = NULL; 85 | PFN_AUDIO_WAVE_STOP audio_wave_stop = NULL; 86 | PFN_AUDIO_EFFECT_CHANGE audio_effect_change = NULL; 87 | 88 | extern AudioContext *xaudio_create_context(bool output_5p1, AudioVoiceType effect_on_voice); 89 | extern AudioContext *faudio_create_context(bool output_5p1, AudioVoiceType effect_on_voice); 90 | 91 | AudioContext *audio_create_context(AudioEngine p_engine, bool output_5p1, AudioVoiceType effect_on_voice) 92 | { 93 | if (audio_reverb_presets == NULL) 94 | { 95 | audio_reverb_presets = new ReverbParameters[audio_reverb_preset_count]; 96 | 97 | for (size_t idx = 0; idx < audio_reverb_preset_count; ++idx) 98 | { 99 | ReverbConvertI3DL2ToNative( 100 | (const FAudioFXReverbI3DL2Parameters *) &audio_reverb_presets_i3dl2[idx], 101 | (FAudioFXReverbParameters *) &audio_reverb_presets[idx]); 102 | } 103 | } 104 | 105 | switch (p_engine) 106 | { 107 | #ifdef HAVE_XAUDIO2 108 | case AudioEngine_XAudio2: 109 | return xaudio_create_context(output_5p1, effect_on_voice); 110 | #endif 111 | 112 | case AudioEngine_FAudio: 113 | return faudio_create_context(output_5p1, effect_on_voice); 114 | 115 | default: 116 | return NULL; 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /utils/testreverb/audio.h: -------------------------------------------------------------------------------- 1 | #ifndef FAUDIOFILTERDEMO_AUDIO_H 2 | #define FAUDIOFILTERDEMO_AUDIO_H 3 | 4 | #include 5 | #include 6 | #include "../wavcommon/wavs.h" 7 | 8 | #ifdef _MSC_VER 9 | #define HAVE_XAUDIO2 10 | #endif 11 | 12 | const uint32_t SAMPLERATE = 44100; 13 | 14 | // types 15 | struct AudioContext; 16 | 17 | enum AudioEngine { 18 | AudioEngine_XAudio2, 19 | AudioEngine_FAudio 20 | }; 21 | 22 | enum AudioVoiceType { 23 | AudioVoiceType_Source = 0, 24 | AudioVoiceType_Submix, 25 | AudioVoiceType_Master 26 | }; 27 | 28 | #pragma pack(push, 1) 29 | 30 | struct ReverbI3DL2Parameters 31 | { 32 | float WetDryMix; 33 | int Room; 34 | int RoomHF; 35 | float RoomRolloffFactor; 36 | float DecayTime; 37 | float DecayHFRatio; 38 | int Reflections; 39 | float ReflectionsDelay; 40 | int Reverb; 41 | float ReverbDelay; 42 | float Diffusion; 43 | float Density; 44 | float HFReference; 45 | }; 46 | 47 | struct ReverbParameters 48 | { 49 | float WetDryMix; 50 | uint32_t ReflectionsDelay; 51 | uint8_t ReverbDelay; 52 | uint8_t RearDelay; 53 | uint8_t PositionLeft; 54 | uint8_t PositionRight; 55 | uint8_t PositionMatrixLeft; 56 | uint8_t PositionMatrixRight; 57 | uint8_t EarlyDiffusion; 58 | uint8_t LateDiffusion; 59 | uint8_t LowEQGain; 60 | uint8_t LowEQCutoff; 61 | uint8_t HighEQGain; 62 | uint8_t HighEQCutoff; 63 | float RoomFilterFreq; 64 | float RoomFilterMain; 65 | float RoomFilterHF; 66 | float ReflectionsGain; 67 | float ReverbGain; 68 | float DecayTime; 69 | float Density; 70 | float RoomSize; 71 | }; 72 | 73 | #pragma pack(pop) 74 | 75 | extern const char *audio_voice_type_names[3]; 76 | 77 | extern const char *audio_reverb_preset_names[]; 78 | extern const ReverbI3DL2Parameters audio_reverb_presets_i3dl2[]; 79 | extern const ReverbParameters *audio_reverb_presets; 80 | extern const size_t audio_reverb_preset_count; 81 | 82 | typedef void (*PFN_AUDIO_DESTROY_CONTEXT)(AudioContext *p_context); 83 | typedef void (*PFN_AUDIO_CREATE_VOICE)(AudioContext *p_context, float *p_buffer, size_t p_buffer_size, int p_sample_rate, int p_num_channels); 84 | 85 | typedef void (*PFN_AUDIO_WAVE_LOAD)(AudioContext *p_context, AudioSampleWave sample, bool stereo); 86 | typedef void (*PFN_AUDIO_WAVE_PLAY)(AudioContext *p_context); 87 | typedef void (*PFN_AUDIO_WAVE_STOP)(AudioContext *p_context); 88 | 89 | typedef void(*PFN_AUDIO_EFFECT_CHANGE)(AudioContext *p_context, bool p_enabled, ReverbParameters *p_params); 90 | 91 | // API 92 | AudioContext *audio_create_context(AudioEngine p_engine, bool output_5p1, AudioVoiceType effect_on_voice); 93 | 94 | extern PFN_AUDIO_DESTROY_CONTEXT audio_destroy_context; 95 | extern PFN_AUDIO_CREATE_VOICE audio_create_voice; 96 | 97 | extern PFN_AUDIO_WAVE_LOAD audio_wave_load; 98 | extern PFN_AUDIO_WAVE_PLAY audio_wave_play; 99 | extern PFN_AUDIO_WAVE_STOP audio_wave_stop; 100 | 101 | extern PFN_AUDIO_EFFECT_CHANGE audio_effect_change; 102 | 103 | #endif // FAUDIOFILTERDEMO_AUDIO_H 104 | -------------------------------------------------------------------------------- /utils/testreverb/audio_faudio.cpp: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct AudioContext 8 | { 9 | FAudio *faudio; 10 | bool output_5p1; 11 | AudioVoiceType effect_on_voice; 12 | 13 | FAudioMasteringVoice *mastering_voice; 14 | FAudioSubmixVoice *submix_voice; 15 | FAudioSourceVoice *source_voice; 16 | FAudioVoice *voices[3]; 17 | 18 | unsigned int wav_channels; 19 | unsigned int wav_samplerate; 20 | drwav_uint64 wav_sample_count; 21 | float * wav_samples; 22 | 23 | FAudioBuffer buffer; 24 | 25 | FAudioEffectDescriptor reverb_effect; 26 | FAudioEffectChain effect_chain; 27 | ReverbParameters reverb_params; 28 | bool reverb_enabled; 29 | }; 30 | 31 | void faudio_destroy_context(AudioContext *context) 32 | { 33 | if (context != NULL) 34 | { 35 | FAudioVoice_DestroyVoice(context->source_voice); 36 | FAudioVoice_DestroyVoice(context->submix_voice); 37 | FAudioVoice_DestroyVoice(context->mastering_voice); 38 | FAudio_Release(context->faudio); 39 | delete context; 40 | } 41 | } 42 | 43 | void faudio_create_voice(AudioContext *context, float *buffer, size_t buffer_size, int sample_rate, int num_channels) 44 | { 45 | // create reverb effect 46 | FAPO *fapo = NULL; 47 | uint32_t hr = FAudioCreateReverb(&fapo, 0); 48 | 49 | if (hr != 0) 50 | { 51 | return; 52 | } 53 | 54 | // create effect chain 55 | context->reverb_effect.InitialState = context->reverb_enabled; 56 | context->reverb_effect.OutputChannels = (context->output_5p1) ? 6 : context->wav_channels; 57 | context->reverb_effect.pEffect = fapo; 58 | 59 | context->effect_chain.EffectCount = 1; 60 | context->effect_chain.pEffectDescriptors = &context->reverb_effect; 61 | 62 | FAudioEffectChain *voice_effect[3] = {NULL, NULL, NULL}; 63 | voice_effect[context->effect_on_voice] = &context->effect_chain; 64 | 65 | // create a mastering voice 66 | uint32_t inChannels = context->wav_channels; 67 | if (context->output_5p1 && context->effect_on_voice != AudioVoiceType_Master) 68 | { 69 | inChannels = 6; 70 | } 71 | 72 | hr = FAudio_CreateMasteringVoice( 73 | context->faudio, 74 | &context->mastering_voice, 75 | inChannels, 76 | FAUDIO_DEFAULT_SAMPLERATE, 77 | 0, 78 | 0, 79 | voice_effect[AudioVoiceType_Master] 80 | ); 81 | if (hr != 0) 82 | { 83 | return; 84 | } 85 | context->voices[AudioVoiceType_Master] = context->mastering_voice; 86 | 87 | // create a submix voice 88 | inChannels = context->wav_channels; 89 | if (context->output_5p1 && context->effect_on_voice == AudioVoiceType_Source) 90 | { 91 | inChannels = 6; 92 | } 93 | 94 | hr = FAudio_CreateSubmixVoice( 95 | context->faudio, 96 | &context->submix_voice, 97 | inChannels, 98 | SAMPLERATE, 99 | 0, 100 | 0, 101 | NULL, 102 | voice_effect[AudioVoiceType_Submix] 103 | ); 104 | context->voices[AudioVoiceType_Submix] = context->submix_voice; 105 | 106 | FAudioVoice_SetVolume(context->submix_voice, 1.0f, FAUDIO_COMMIT_NOW); 107 | 108 | // create a source voice 109 | FAudioSendDescriptor desc = {0, context->submix_voice}; 110 | FAudioVoiceSends sends = {1, &desc}; 111 | 112 | FAudioWaveFormatEx waveFormat; 113 | waveFormat.wFormatTag = 3; 114 | waveFormat.nChannels = num_channels; 115 | waveFormat.nSamplesPerSec = sample_rate; 116 | waveFormat.nAvgBytesPerSec = sample_rate * 4; 117 | waveFormat.nBlockAlign = num_channels * 4; 118 | waveFormat.wBitsPerSample = 32; 119 | waveFormat.cbSize = 0; 120 | 121 | hr = FAudio_CreateSourceVoice( 122 | context->faudio, 123 | &context->source_voice, 124 | &waveFormat, 125 | 0, 126 | FAUDIO_DEFAULT_FREQ_RATIO, 127 | NULL, 128 | &sends, 129 | voice_effect[AudioVoiceType_Source] 130 | ); 131 | 132 | if (hr != 0) 133 | { 134 | return; 135 | } 136 | context->voices[AudioVoiceType_Source] = context->source_voice; 137 | 138 | FAudioVoice_SetVolume(context->source_voice, 1.0f, FAUDIO_COMMIT_NOW); 139 | 140 | // submit the array 141 | SDL_zero(context->buffer); 142 | context->buffer.AudioBytes = 4 * buffer_size * num_channels; 143 | context->buffer.pAudioData = (uint8_t *)buffer; 144 | context->buffer.Flags = FAUDIO_END_OF_STREAM; 145 | context->buffer.PlayBegin = 0; 146 | context->buffer.PlayLength = buffer_size; 147 | context->buffer.LoopBegin = 0; 148 | context->buffer.LoopLength = 0; 149 | context->buffer.LoopCount = 0; 150 | } 151 | 152 | void faudio_reverb_set_params(AudioContext *context) 153 | { 154 | FAudioVoice_SetEffectParameters( 155 | context->voices[context->effect_on_voice], 156 | 0, 157 | &context->reverb_params, 158 | sizeof(context->reverb_params), 159 | FAUDIO_COMMIT_NOW 160 | ); 161 | } 162 | 163 | void faudio_wave_load(AudioContext *context, AudioSampleWave sample, bool stereo) 164 | { 165 | if (context->source_voice) 166 | { 167 | FAudioVoice_DestroyVoice(context->source_voice); 168 | FAudioVoice_DestroyVoice(context->submix_voice); 169 | FAudioVoice_DestroyVoice(context->mastering_voice); 170 | } 171 | 172 | context->wav_samples = WAVS_Open( 173 | sample, 174 | stereo, 175 | &context->wav_channels, 176 | &context->wav_samplerate, 177 | &context->wav_sample_count 178 | ); 179 | 180 | context->wav_sample_count /= context->wav_channels; 181 | 182 | audio_create_voice(context, context->wav_samples, context->wav_sample_count, context->wav_samplerate, context->wav_channels); 183 | faudio_reverb_set_params(context); 184 | } 185 | 186 | void faudio_wave_play(AudioContext *context) 187 | { 188 | FAudioSourceVoice_Stop(context->source_voice, 0, FAUDIO_COMMIT_NOW); 189 | FAudioSourceVoice_FlushSourceBuffers(context->source_voice); 190 | 191 | FAudioSourceVoice_SubmitSourceBuffer(context->source_voice, &context->buffer, NULL); 192 | FAudioSourceVoice_Start(context->source_voice, 0, FAUDIO_COMMIT_NOW); 193 | } 194 | 195 | void faudio_wave_stop(AudioContext *context) 196 | { 197 | FAudioSourceVoice_Stop(context->source_voice, FAUDIO_PLAY_TAILS, FAUDIO_COMMIT_NOW); 198 | } 199 | 200 | void faudio_effect_change(AudioContext *context, bool enabled, ReverbParameters *params) 201 | { 202 | if (context->reverb_enabled && !enabled) 203 | { 204 | FAudioVoice_DisableEffect( 205 | context->voices[context->effect_on_voice], 206 | 0, 207 | FAUDIO_COMMIT_NOW 208 | ); 209 | context->reverb_enabled = enabled; 210 | } 211 | else if (!context->reverb_enabled && enabled) 212 | { 213 | FAudioVoice_EnableEffect( 214 | context->voices[context->effect_on_voice], 215 | 0, 216 | FAUDIO_COMMIT_NOW 217 | ); 218 | context->reverb_enabled = enabled; 219 | } 220 | 221 | context->reverb_params = *params; 222 | faudio_reverb_set_params(context); 223 | } 224 | 225 | AudioContext *faudio_create_context(bool output_5p1, AudioVoiceType effect_on_voice) 226 | { 227 | // setup function pointers 228 | audio_destroy_context = faudio_destroy_context; 229 | audio_create_voice = faudio_create_voice; 230 | audio_wave_load = faudio_wave_load; 231 | audio_wave_play = faudio_wave_play; 232 | audio_wave_stop = faudio_wave_stop; 233 | audio_effect_change = faudio_effect_change; 234 | 235 | // create Faudio object 236 | FAudio *faudio; 237 | 238 | uint32_t hr = FAudioCreate(&faudio, 0, FAUDIO_DEFAULT_PROCESSOR); 239 | if (hr != 0) 240 | { 241 | return NULL; 242 | } 243 | 244 | // return a context object 245 | AudioContext *context = new AudioContext(); 246 | context->faudio = faudio; 247 | context->output_5p1 = output_5p1; 248 | context->effect_on_voice = effect_on_voice; 249 | 250 | context->source_voice = NULL; 251 | context->submix_voice = NULL; 252 | context->mastering_voice = NULL; 253 | context->wav_samples = NULL; 254 | SDL_zero(context->reverb_params); 255 | context->reverb_enabled = false; 256 | 257 | // load the first wave 258 | audio_wave_load(context, (AudioSampleWave) 0, false); 259 | 260 | return context; 261 | } 262 | -------------------------------------------------------------------------------- /utils/testreverb/audio_xaudio.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "audio.h" 3 | 4 | #ifdef HAVE_XAUDIO2 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct AudioContext 11 | { 12 | IXAudio2 *xaudio2; 13 | uint32_t output_5p1; 14 | AudioVoiceType effect_on_voice; 15 | 16 | IXAudio2MasteringVoice *mastering_voice; 17 | IXAudio2SubmixVoice *submix_voice; 18 | IXAudio2SourceVoice *source_voice; 19 | IXAudio2Voice *voices[3]; 20 | 21 | unsigned int wav_channels; 22 | unsigned int wav_samplerate; 23 | drwav_uint64 wav_sample_count; 24 | float * wav_samples; 25 | 26 | XAUDIO2_BUFFER buffer; 27 | 28 | XAUDIO2_EFFECT_DESCRIPTOR reverb_effect; 29 | XAUDIO2_EFFECT_CHAIN effect_chain; 30 | ReverbParameters reverb_params; 31 | bool reverb_enabled; 32 | }; 33 | 34 | void xaudio_destroy_context(AudioContext *context) 35 | { 36 | if (context != NULL) 37 | { 38 | context->source_voice->DestroyVoice(); 39 | context->submix_voice->DestroyVoice(); 40 | context->mastering_voice->DestroyVoice(); 41 | context->xaudio2->Release(); 42 | delete context; 43 | } 44 | } 45 | 46 | void xaudio_create_voice(AudioContext *context, float *buffer, size_t buffer_size, int sample_rate, int num_channels) 47 | { 48 | // create reverb effect 49 | IUnknown *xapo = NULL; 50 | HRESULT hr = XAudio2CreateReverb(&xapo); 51 | 52 | if (FAILED(hr)) 53 | { 54 | return; 55 | } 56 | 57 | // create effect chain 58 | context->reverb_effect.InitialState = context->reverb_enabled; 59 | context->reverb_effect.OutputChannels = (context->output_5p1) ? 6 : context->wav_channels; 60 | context->reverb_effect.pEffect = xapo; 61 | 62 | context->effect_chain.EffectCount = 1; 63 | context->effect_chain.pEffectDescriptors = &context->reverb_effect; 64 | 65 | XAUDIO2_EFFECT_CHAIN *voice_effect[3] = {NULL, NULL, NULL}; 66 | voice_effect[context->effect_on_voice] = &context->effect_chain; 67 | 68 | // create a mastering voice 69 | uint32_t inChannels = context->wav_channels; 70 | if (context->output_5p1 && context->effect_on_voice != AudioVoiceType_Master) 71 | { 72 | inChannels = 6; 73 | } 74 | 75 | hr = context->xaudio2->CreateMasteringVoice( 76 | &context->mastering_voice, 77 | inChannels, 78 | XAUDIO2_DEFAULT_SAMPLERATE, 79 | 0, 80 | 0, 81 | voice_effect[AudioVoiceType_Master] 82 | ); 83 | if (FAILED(hr)) 84 | { 85 | return; 86 | } 87 | context->voices[AudioVoiceType_Master] = context->mastering_voice; 88 | 89 | // create a submix voice 90 | inChannels = context->wav_channels; 91 | if (context->output_5p1 && context->effect_on_voice == AudioVoiceType_Source) 92 | { 93 | inChannels = 6; 94 | } 95 | 96 | hr = context->xaudio2->CreateSubmixVoice( 97 | &context->submix_voice, 98 | inChannels, 99 | SAMPLERATE, 100 | 0, 101 | 0, 102 | NULL, 103 | voice_effect[AudioVoiceType_Submix] 104 | ); 105 | if (FAILED(hr)) 106 | { 107 | return; 108 | } 109 | context->voices[AudioVoiceType_Submix] = context->submix_voice; 110 | context->submix_voice->SetVolume(1.0f); 111 | 112 | // create a source voice 113 | XAUDIO2_SEND_DESCRIPTOR desc = {0, context->submix_voice}; 114 | XAUDIO2_VOICE_SENDS sends = {1, &desc}; 115 | 116 | WAVEFORMATEX waveFormat; 117 | waveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; 118 | waveFormat.nChannels = num_channels; 119 | waveFormat.nSamplesPerSec = sample_rate; 120 | waveFormat.nBlockAlign = num_channels * 4; 121 | waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; 122 | waveFormat.wBitsPerSample = 32; 123 | waveFormat.cbSize = 0; 124 | 125 | hr = context->xaudio2->CreateSourceVoice( 126 | &context->source_voice, 127 | &waveFormat, 128 | 0, 129 | XAUDIO2_DEFAULT_FREQ_RATIO, 130 | NULL, 131 | &sends, 132 | voice_effect[AudioVoiceType_Source] 133 | ); 134 | if (FAILED(hr)) 135 | { 136 | return; 137 | } 138 | context->voices[AudioVoiceType_Source] = context->source_voice; 139 | 140 | context->source_voice->SetVolume(1.0f); 141 | 142 | xapo->Release(); 143 | 144 | // submit the array 145 | SDL_zero(context->buffer); 146 | context->buffer.AudioBytes = 4 * buffer_size * num_channels; 147 | context->buffer.pAudioData = (byte *)buffer; 148 | context->buffer.Flags = XAUDIO2_END_OF_STREAM; 149 | context->buffer.PlayBegin = 0; 150 | context->buffer.PlayLength = buffer_size; 151 | context->buffer.LoopBegin = 0; 152 | context->buffer.LoopLength = 0; 153 | context->buffer.LoopCount = 0; 154 | } 155 | 156 | void xaudio_reverb_set_params(AudioContext *context) 157 | { 158 | XAUDIO2FX_REVERB_PARAMETERS native_params = { 0 }; 159 | 160 | native_params.WetDryMix = context->reverb_params.WetDryMix; 161 | native_params.ReflectionsDelay = context->reverb_params.ReflectionsDelay; 162 | native_params.ReverbDelay = context->reverb_params.ReverbDelay; 163 | native_params.RearDelay = context->reverb_params.RearDelay; 164 | native_params.PositionLeft = context->reverb_params.PositionLeft; 165 | native_params.PositionRight = context->reverb_params.PositionRight; 166 | native_params.PositionMatrixLeft = context->reverb_params.PositionMatrixLeft; 167 | native_params.PositionMatrixRight = context->reverb_params.PositionMatrixRight; 168 | native_params.EarlyDiffusion = context->reverb_params.EarlyDiffusion; 169 | native_params.LateDiffusion = context->reverb_params.LateDiffusion; 170 | native_params.LowEQGain = context->reverb_params.LowEQGain; 171 | native_params.LowEQCutoff = context->reverb_params.LowEQCutoff; 172 | native_params.HighEQGain = context->reverb_params.HighEQGain; 173 | native_params.HighEQCutoff = context->reverb_params.HighEQCutoff; 174 | native_params.RoomFilterFreq = context->reverb_params.RoomFilterFreq; 175 | native_params.RoomFilterMain = context->reverb_params.RoomFilterMain; 176 | native_params.RoomFilterHF = context->reverb_params.RoomFilterHF; 177 | native_params.ReflectionsGain = context->reverb_params.ReflectionsGain; 178 | native_params.ReverbGain = context->reverb_params.ReverbGain; 179 | native_params.DecayTime = context->reverb_params.DecayTime; 180 | native_params.Density = context->reverb_params.Density; 181 | native_params.RoomSize = context->reverb_params.RoomSize; 182 | 183 | /* 2.8+ only but zero-initialization catches this 184 | native_params.DisableLateField = 0; */ 185 | 186 | HRESULT hr = context->voices[context->effect_on_voice]->SetEffectParameters( 187 | 0, 188 | &native_params, 189 | sizeof(XAUDIO2FX_REVERB_PARAMETERS) 190 | ); 191 | } 192 | 193 | void xaudio_wave_load(AudioContext *context, AudioSampleWave sample, bool stereo) 194 | { 195 | if (context->source_voice) 196 | { 197 | context->source_voice->DestroyVoice(); 198 | context->submix_voice->DestroyVoice(); 199 | context->mastering_voice->DestroyVoice(); 200 | } 201 | 202 | context->wav_samples = WAVS_Open( 203 | sample, 204 | stereo, 205 | &context->wav_channels, 206 | &context->wav_samplerate, 207 | &context->wav_sample_count); 208 | 209 | context->wav_sample_count /= context->wav_channels; 210 | 211 | audio_create_voice(context, context->wav_samples, context->wav_sample_count, context->wav_samplerate, context->wav_channels); 212 | xaudio_reverb_set_params(context); 213 | } 214 | 215 | void xaudio_wave_play(AudioContext *context) 216 | { 217 | context->source_voice->Stop(); 218 | context->source_voice->FlushSourceBuffers(); 219 | 220 | HRESULT hr = context->source_voice->SubmitSourceBuffer(&context->buffer); 221 | 222 | if (FAILED(hr)) 223 | { 224 | return; 225 | } 226 | 227 | context->source_voice->Start(); 228 | } 229 | 230 | void xaudio_wave_stop(AudioContext *context) 231 | { 232 | context->source_voice->Stop(XAUDIO2_PLAY_TAILS); 233 | } 234 | 235 | void xaudio_effect_change(AudioContext *context, bool enabled, ReverbParameters *params) 236 | { 237 | HRESULT hr; 238 | 239 | if (context->reverb_enabled && !enabled) 240 | { 241 | hr = context->voices[context->effect_on_voice]->DisableEffect(0); 242 | context->reverb_enabled = enabled; 243 | } 244 | else if (!context->reverb_enabled && enabled) 245 | { 246 | hr = context->voices[context->effect_on_voice]->EnableEffect(0); 247 | context->reverb_enabled = enabled; 248 | } 249 | 250 | memcpy(&context->reverb_params, params, sizeof(ReverbParameters)); 251 | xaudio_reverb_set_params(context); 252 | } 253 | 254 | AudioContext *xaudio_create_context(bool output_5p1, AudioVoiceType effect_on_voice) 255 | { 256 | // setup function pointers 257 | audio_destroy_context = xaudio_destroy_context; 258 | audio_create_voice = xaudio_create_voice; 259 | audio_wave_load = xaudio_wave_load; 260 | audio_wave_play = xaudio_wave_play; 261 | audio_wave_stop = xaudio_wave_stop; 262 | audio_effect_change = xaudio_effect_change; 263 | 264 | // create XAudio object 265 | IXAudio2 *xaudio2; 266 | 267 | HRESULT hr = XAudio2Create(&xaudio2); 268 | if (FAILED(hr)) 269 | { 270 | return NULL; 271 | } 272 | 273 | // return a context object 274 | AudioContext *context = new AudioContext(); 275 | context->xaudio2 = xaudio2; 276 | context->output_5p1 = output_5p1; 277 | context->effect_on_voice = effect_on_voice; 278 | context->mastering_voice = NULL; 279 | context->submix_voice = NULL; 280 | context->source_voice = NULL; 281 | context->wav_samples = NULL; 282 | context->reverb_params = audio_reverb_presets[0]; 283 | context->reverb_enabled = false; 284 | 285 | // load the first wave 286 | audio_wave_load(context, (AudioSampleWave) 0, false); 287 | 288 | return context; 289 | } 290 | 291 | #endif // HAVE_XAUDIO2 292 | -------------------------------------------------------------------------------- /utils/testreverb/testreverb.cpp: -------------------------------------------------------------------------------- 1 | #include "../uicommon/imgui.h" 2 | 3 | #include "audio.h" 4 | #include 5 | 6 | const char* TOOL_NAME = "Reverb Test Tool"; 7 | int TOOL_WIDTH = 640; 8 | int TOOL_HEIGHT = 850; 9 | 10 | int next_window_dims(int y_pos, int height) 11 | { 12 | ImGui::SetNextWindowPos(ImVec2(0, static_cast(y_pos))); 13 | ImGui::SetNextWindowSize(ImVec2(640, static_cast(height))); 14 | 15 | return y_pos + height; 16 | } 17 | 18 | void FAudioTool_Quit() 19 | { 20 | /* Nothing to do... */ 21 | } 22 | 23 | void FAudioTool_Update() 24 | { 25 | bool update_engine = false; 26 | bool update_wave = false; 27 | bool play_wave = false; 28 | bool stop_wave = false; 29 | bool update_effect = false; 30 | 31 | // gui 32 | int window_y = next_window_dims(0, 80); 33 | ImGui::Begin("Output Audio Engine"); 34 | 35 | static int audio_engine = (int)AudioEngine_FAudio; 36 | update_engine |= ImGui::RadioButton("FAudio", &audio_engine, (int)AudioEngine_FAudio); ImGui::SameLine(); 37 | #ifdef HAVE_XAUDIO2 38 | update_engine |= ImGui::RadioButton("XAudio2", &audio_engine, (int)AudioEngine_XAudio2); ImGui::SameLine(); 39 | #endif 40 | 41 | static bool output_5p1 = false; 42 | update_engine |= ImGui::Checkbox("5.1 channel output", &output_5p1); 43 | 44 | static int32_t voice_index = (int32_t) AudioVoiceType_Submix; 45 | update_engine |= ImGui::Combo("Apply effect to", &voice_index, audio_voice_type_names, 3); 46 | 47 | ImGui::End(); 48 | 49 | window_y = next_window_dims(window_y, 80); 50 | ImGui::Begin("Wave file to play"); 51 | 52 | static int wave_index = (int)AudioWave_SnareDrum01; 53 | static bool wave_stereo = false; 54 | 55 | update_wave |= ImGui::RadioButton("Snare Drum (Forte)", &wave_index, (int)AudioWave_SnareDrum01); ImGui::SameLine(); 56 | update_wave |= ImGui::RadioButton("Snare Drum (Fortissimo)", &wave_index, (int)AudioWave_SnareDrum02); ImGui::SameLine(); 57 | update_wave |= ImGui::RadioButton("Snare Drum (Mezzo-Forte)", &wave_index, (int)AudioWave_SnareDrum03); 58 | 59 | play_wave = ImGui::Button("Play"); ImGui::SameLine(); 60 | stop_wave = ImGui::Button("Stop"); ImGui::SameLine(); 61 | update_wave |= ImGui::Checkbox("Stereo", &wave_stereo); 62 | 63 | ImGui::End(); 64 | 65 | window_y = next_window_dims(window_y, 80); 66 | ImGui::Begin("Reverb effect"); 67 | 68 | static bool effect_enabled = false; 69 | update_effect |= ImGui::Checkbox("Enabled", &effect_enabled); 70 | 71 | static int32_t preset_index = -1; 72 | static ReverbParameters reverb_params = { 73 | 100.0f, 74 | 40, 75 | 60, 76 | 0, 0, 0, 0, 0, 77 | 3, 78 | 3, 79 | 0, 80 | 0, 81 | 0, 82 | 0, 83 | 5000.0f, 84 | -5.0f, 85 | -5.0f, 86 | -5.0f, 87 | -5.0f, 88 | 2000.0f, 89 | 50.0f, 90 | 100.0f, 91 | }; 92 | 93 | if (ImGui::Combo("Preset", &preset_index, audio_reverb_preset_names, audio_reverb_preset_count)) { 94 | memcpy(&reverb_params, &audio_reverb_presets[preset_index], sizeof(ReverbParameters)); 95 | update_effect = true; 96 | } 97 | 98 | ImGui::End(); 99 | 100 | window_y = next_window_dims(window_y, 600); 101 | ImGui::Begin("FAudio Tune Detail"); 102 | 103 | int ReverbDelay = reverb_params.ReverbDelay; 104 | int RearDelay = reverb_params.RearDelay; 105 | int PositionLeft = reverb_params.PositionLeft; 106 | int PositionRight = reverb_params.PositionRight; 107 | int PositionMatrixLeft = reverb_params.PositionMatrixLeft; 108 | int PositionMatrixRight = reverb_params.PositionMatrixRight; 109 | int EarlyDiffusion = reverb_params.EarlyDiffusion; 110 | int LateDiffusion = reverb_params.LateDiffusion; 111 | int LowEQGain = reverb_params.LowEQGain; 112 | int LowEQCutoff = reverb_params.LowEQCutoff; 113 | int HighEQGain = reverb_params.HighEQGain; 114 | int HighEQCutoff = reverb_params.HighEQCutoff; 115 | 116 | update_effect |= ImGui::SliderFloat("WetDryMix (%)", &reverb_params.WetDryMix, 0, 100); 117 | update_effect |= ImGui::SliderInt("ReflectionsDelay (ms)", (int *)&reverb_params.ReflectionsDelay, 0, 300); 118 | update_effect |= ImGui::SliderInt("ReverbDelay (ms)", &ReverbDelay, 0, 85); 119 | update_effect |= ImGui::SliderInt("RearDelay (ms)", &RearDelay, 0, 5); 120 | 121 | update_effect |= ImGui::SliderInt("PositionLeft", &PositionLeft, 0, 30); 122 | update_effect |= ImGui::SliderInt("PositionRight", &PositionRight, 0, 30); 123 | update_effect |= ImGui::SliderInt("PositionMatrixLeft", &PositionMatrixLeft, 0, 30); 124 | update_effect |= ImGui::SliderInt("PositionMatrixRight", &PositionMatrixRight, 0, 30); 125 | 126 | update_effect |= ImGui::SliderInt("Early Diffusion", &EarlyDiffusion, 0, 15); 127 | update_effect |= ImGui::SliderInt("Late Diffusion", &LateDiffusion, 0, 15); 128 | 129 | update_effect |= ImGui::SliderInt("LowEqGain", &LowEQGain, 0, 12); 130 | update_effect |= ImGui::SliderInt("LowEqCuttoff", &LowEQCutoff, 0, 9); 131 | update_effect |= ImGui::SliderInt("HighEqGain", &HighEQGain, 0, 8); 132 | update_effect |= ImGui::SliderInt("HighEqCuttoff", &HighEQCutoff, 0, 14); 133 | 134 | update_effect |= ImGui::SliderFloat("RoomFreq (Hz)", &reverb_params.RoomFilterFreq, 20, 20000); 135 | update_effect |= ImGui::SliderFloat("RoomGain (dB)", &reverb_params.RoomFilterMain, -100, 0); 136 | update_effect |= ImGui::SliderFloat("RoomHFGain (dB)", &reverb_params.RoomFilterHF, -100, 0); 137 | 138 | update_effect |= ImGui::SliderFloat("ReflectionsGain (dB)", &reverb_params.ReflectionsGain, -100, 20); 139 | update_effect |= ImGui::SliderFloat("ReverbGain (dB)", &reverb_params.ReverbGain, -100, 20); 140 | 141 | update_effect |= ImGui::SliderFloat("DecayTime (s)", &reverb_params.DecayTime, 0.1f, 15.0f); 142 | 143 | update_effect |= ImGui::SliderFloat("Density (%)", &reverb_params.Density, 0, 100); 144 | update_effect |= ImGui::SliderFloat("RoomSize (feet)", &reverb_params.RoomSize, 1, 100); 145 | 146 | reverb_params.ReverbDelay = ReverbDelay; 147 | reverb_params.RearDelay = RearDelay; 148 | reverb_params.PositionLeft = PositionLeft; 149 | reverb_params.PositionRight = PositionRight; 150 | reverb_params.PositionMatrixLeft = PositionMatrixLeft; 151 | reverb_params.PositionMatrixRight = PositionMatrixRight; 152 | reverb_params.EarlyDiffusion = EarlyDiffusion; 153 | reverb_params.LateDiffusion = LateDiffusion; 154 | reverb_params.LowEQGain = LowEQGain; 155 | reverb_params.LowEQCutoff = LowEQCutoff; 156 | reverb_params.HighEQGain = HighEQGain; 157 | reverb_params.HighEQCutoff = HighEQCutoff; 158 | 159 | ImGui::End(); 160 | 161 | // audio control 162 | static AudioContext *player = NULL; 163 | 164 | if (player == NULL || update_engine) 165 | { 166 | if (player != NULL) 167 | { 168 | audio_destroy_context(player); 169 | } 170 | player = audio_create_context((AudioEngine) audio_engine, output_5p1, (AudioVoiceType) voice_index); 171 | } 172 | 173 | if (update_wave | update_engine) 174 | { 175 | audio_wave_load(player, (AudioSampleWave) wave_index, wave_stereo); 176 | } 177 | 178 | if (play_wave) { 179 | audio_wave_play(player); 180 | } 181 | 182 | if (stop_wave) 183 | { 184 | audio_wave_stop(player); 185 | } 186 | 187 | if ((update_engine || update_effect)) 188 | { 189 | audio_effect_change(player, effect_enabled, &reverb_params); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /utils/testvolumemeter/audio.cpp: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | PFN_AUDIO_DESTROY_CONTEXT audio_destroy_context = NULL; 4 | PFN_AUDIO_WAVE_LOAD audio_wave_load = NULL; 5 | PFN_AUDIO_WAVE_PLAY audio_wave_play = NULL; 6 | PFN_AUDIO_UPDATE_VOLUMEMETER audio_update_volumemeter = NULL; 7 | 8 | extern AudioContext* xaudio_create_context(); 9 | extern AudioContext* faudio_create_context(); 10 | 11 | AudioContext* audio_create_context(AudioEngine p_engine) 12 | { 13 | switch (p_engine) 14 | { 15 | #ifdef HAVE_XAUDIO2 16 | case AudioEngine_XAudio2: 17 | return xaudio_create_context(); 18 | #endif 19 | 20 | case AudioEngine_FAudio: 21 | return faudio_create_context(); 22 | 23 | default: 24 | return NULL; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /utils/testvolumemeter/audio.h: -------------------------------------------------------------------------------- 1 | #ifndef FAUDIOTESTVOLUMEMETER_AUDIO_H 2 | #define FAUDIOTESTVOLUMEMETER_AUDIO_H 3 | 4 | #include 5 | #include 6 | #include "../wavcommon/wavs.h" 7 | 8 | #ifdef _MSC_VER 9 | #define HAVE_XAUDIO2 10 | #endif 11 | 12 | typedef struct AudioContext AudioContext; 13 | 14 | typedef enum 15 | { 16 | AudioEngine_XAudio2, 17 | AudioEngine_FAudio 18 | } AudioEngine; 19 | 20 | typedef void (*PFN_AUDIO_DESTROY_CONTEXT)(AudioContext *p_context); 21 | typedef void (*PFN_AUDIO_WAVE_LOAD)(AudioContext *p_context, AudioSampleWave sample, bool stereo); 22 | typedef void (*PFN_AUDIO_WAVE_PLAY)(AudioContext *p_context); 23 | typedef void (*PFN_AUDIO_UPDATE_VOLUMEMETER)(AudioContext *p_context, float *peak, float *rms); 24 | 25 | AudioContext *audio_create_context(AudioEngine p_engine); 26 | 27 | extern PFN_AUDIO_DESTROY_CONTEXT audio_destroy_context; 28 | extern PFN_AUDIO_WAVE_LOAD audio_wave_load; 29 | extern PFN_AUDIO_WAVE_PLAY audio_wave_play; 30 | extern PFN_AUDIO_UPDATE_VOLUMEMETER audio_update_volumemeter; 31 | 32 | #endif /* FAUDIOTESTVOLUMEMETER_AUDIO_H */ 33 | -------------------------------------------------------------------------------- /utils/testvolumemeter/audio_faudio.cpp: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct AudioContext 8 | { 9 | FAudio *faudio; 10 | FAudioMasteringVoice *mastering_voice; 11 | FAudioSourceVoice *source_voice; 12 | 13 | unsigned int wav_channels; 14 | unsigned int wav_samplerate; 15 | drwav_uint64 wav_sample_count; 16 | float *wav_samples; 17 | FAudioBuffer buffer; 18 | }; 19 | 20 | void faudio_destroy_context(AudioContext *context) 21 | { 22 | if (context != NULL) 23 | { 24 | FAudioVoice_DestroyVoice(context->source_voice); 25 | FAudioVoice_DestroyVoice(context->mastering_voice); 26 | FAudio_Release(context->faudio); 27 | delete context; 28 | } 29 | } 30 | 31 | void faudio_wave_load(AudioContext *context, AudioSampleWave sample, bool stereo) 32 | { 33 | if (context->source_voice) 34 | { 35 | FAudioVoice_DestroyVoice(context->source_voice); 36 | } 37 | 38 | /* Buffer data... */ 39 | context->wav_samples = WAVS_Open( 40 | sample, 41 | stereo, 42 | &context->wav_channels, 43 | &context->wav_samplerate, 44 | &context->wav_sample_count 45 | ); 46 | context->wav_sample_count /= context->wav_channels; 47 | context->buffer.Flags = FAUDIO_END_OF_STREAM; 48 | context->buffer.AudioBytes = 4 * context->wav_sample_count * context->wav_channels; 49 | context->buffer.pAudioData = (uint8_t*) context->wav_samples; 50 | context->buffer.PlayBegin = 0; 51 | context->buffer.PlayLength = context->wav_sample_count; 52 | context->buffer.LoopBegin = 0; 53 | context->buffer.LoopLength = 0; 54 | context->buffer.LoopCount = 0; 55 | context->buffer.pContext = NULL; 56 | 57 | /* Volume meter... */ 58 | FAPO *fapo = NULL; 59 | uint32_t hr = FAudioCreateVolumeMeter(&fapo, 0); 60 | if (hr != 0) 61 | { 62 | return; 63 | } 64 | 65 | /* Effect chain... */ 66 | FAudioEffectDescriptor vmDesc; 67 | vmDesc.InitialState = 1; 68 | vmDesc.OutputChannels = context->wav_channels; 69 | vmDesc.pEffect = fapo; 70 | FAudioEffectChain vmChain; 71 | vmChain.EffectCount = 1; 72 | vmChain.pEffectDescriptors = &vmDesc; 73 | 74 | /* Wave format... */ 75 | FAudioWaveFormatEx waveFormat; 76 | waveFormat.wFormatTag = 3; 77 | waveFormat.nChannels = context->wav_channels; 78 | waveFormat.nSamplesPerSec = context->wav_samplerate; 79 | waveFormat.nAvgBytesPerSec = context->wav_samplerate * 4; 80 | waveFormat.nBlockAlign = context->wav_channels * 4; 81 | waveFormat.wBitsPerSample = 32; 82 | waveFormat.cbSize = 0; 83 | 84 | /*... Source voice, finally. */ 85 | hr = FAudio_CreateSourceVoice( 86 | context->faudio, 87 | &context->source_voice, 88 | &waveFormat, 89 | 0, 90 | FAUDIO_DEFAULT_FREQ_RATIO, 91 | NULL, 92 | NULL, 93 | &vmChain 94 | ); 95 | if (hr != 0) 96 | { 97 | return; 98 | } 99 | fapo->Release(fapo); 100 | } 101 | 102 | void faudio_wave_play(AudioContext *context) 103 | { 104 | FAudioSourceVoice_Stop(context->source_voice, 0, FAUDIO_COMMIT_NOW); 105 | FAudioSourceVoice_FlushSourceBuffers(context->source_voice); 106 | 107 | FAudioSourceVoice_SubmitSourceBuffer(context->source_voice, &context->buffer, NULL); 108 | FAudioSourceVoice_Start(context->source_voice, 0, FAUDIO_COMMIT_NOW); 109 | } 110 | 111 | void faudio_update_volumemeter(AudioContext *context, float *peak, float *rms) 112 | { 113 | FAudioFXVolumeMeterLevels levels; 114 | levels.pPeakLevels = peak; 115 | levels.pRMSLevels = rms; 116 | levels.ChannelCount = context->wav_channels; 117 | 118 | if (context->source_voice != NULL) 119 | { 120 | FAudioVoice_GetEffectParameters( 121 | context->source_voice, 122 | 0, 123 | &levels, 124 | sizeof(levels) 125 | ); 126 | } 127 | } 128 | 129 | AudioContext* faudio_create_context() 130 | { 131 | // setup function pointers 132 | audio_destroy_context = faudio_destroy_context; 133 | audio_wave_load = faudio_wave_load; 134 | audio_wave_play = faudio_wave_play; 135 | audio_update_volumemeter = faudio_update_volumemeter; 136 | 137 | // create FAudio objects 138 | FAudio *faudio; 139 | FAudioMasteringVoice *master; 140 | uint32_t hr = FAudioCreate(&faudio, 0, FAUDIO_DEFAULT_PROCESSOR); 141 | if (hr != 0) 142 | { 143 | return NULL; 144 | } 145 | hr = FAudio_CreateMasteringVoice( 146 | faudio, 147 | &master, 148 | FAUDIO_DEFAULT_CHANNELS, 149 | FAUDIO_DEFAULT_SAMPLERATE, 150 | 0, 151 | 0, 152 | NULL 153 | ); 154 | if (hr != 0) 155 | { 156 | return NULL; 157 | } 158 | 159 | // return a context object 160 | AudioContext *context = new AudioContext(); 161 | context->faudio = faudio; 162 | context->mastering_voice = master; 163 | context->source_voice = NULL; 164 | context->wav_samples = NULL; 165 | 166 | // load the first wave 167 | audio_wave_load(context, (AudioSampleWave) 0, false); 168 | 169 | return context; 170 | } 171 | -------------------------------------------------------------------------------- /utils/testvolumemeter/testvolumemeter.cpp: -------------------------------------------------------------------------------- 1 | #include "../uicommon/imgui.h" 2 | 3 | #include "audio.h" 4 | #include 5 | 6 | const char* TOOL_NAME = "Volume Meter Test Tool"; 7 | int TOOL_WIDTH = 640; 8 | int TOOL_HEIGHT = 510; 9 | 10 | int next_window_dims(int y_pos, int height) 11 | { 12 | ImGui::SetNextWindowPos(ImVec2(0, static_cast(y_pos))); 13 | ImGui::SetNextWindowSize(ImVec2(640, static_cast(height))); 14 | 15 | return y_pos + height; 16 | } 17 | 18 | void FAudioTool_Quit() 19 | { 20 | /* Nothing to do... */ 21 | } 22 | 23 | void FAudioTool_Update() 24 | { 25 | bool update_engine = false; 26 | bool update_wave = false; 27 | bool play_wave = false; 28 | 29 | #define MAX_TIMELINE 120 30 | static float peaksL[MAX_TIMELINE] = { 0 }; 31 | static float peaksR[MAX_TIMELINE] = { 0 }; 32 | static float rmsL[MAX_TIMELINE] = { 0 }; 33 | static float rmsR[MAX_TIMELINE] = { 0 }; 34 | static int vmOffset = 0; 35 | 36 | // GUI 37 | int window_y = next_window_dims(0, 50); 38 | ImGui::Begin("Output Audio Engine"); 39 | 40 | static int audio_engine = (int) AudioEngine_FAudio; 41 | update_engine |= ImGui::RadioButton( 42 | "FAudio", 43 | &audio_engine, 44 | (int) AudioEngine_FAudio); 45 | ImGui::SameLine(); 46 | #ifdef HAVE_XAUDIO2 47 | update_engine |= ImGui::RadioButton( 48 | "XAudio2", 49 | &audio_engine, 50 | (int) AudioEngine_XAudio2 51 | ); 52 | #endif 53 | 54 | ImGui::End(); 55 | 56 | window_y = next_window_dims(window_y, 80); 57 | ImGui::Begin("Wave file to play"); 58 | 59 | static int wave_index = (int) AudioWave_SnareDrum01; 60 | static bool wave_stereo = false; 61 | 62 | update_wave |= ImGui::RadioButton( 63 | "Snare Drum (Forte)", 64 | &wave_index, 65 | (int) AudioWave_SnareDrum01 66 | ); 67 | ImGui::SameLine(); 68 | update_wave |= ImGui::RadioButton( 69 | "Snare Drum (Fortissimo)", 70 | &wave_index, 71 | (int) AudioWave_SnareDrum02 72 | ); 73 | ImGui::SameLine(); 74 | update_wave |= ImGui::RadioButton( 75 | "Snare Drum (Mezzo-Forte)", 76 | &wave_index, 77 | (int) AudioWave_SnareDrum03 78 | ); 79 | 80 | play_wave = ImGui::Button("Play"); ImGui::SameLine(); 81 | update_wave |= ImGui::Checkbox("Stereo", &wave_stereo); 82 | 83 | ImGui::End(); 84 | 85 | window_y = next_window_dims(window_y, 370); 86 | ImGui::Begin("Volume Meter"); 87 | 88 | ImGui::PlotLines( 89 | "Peaks Channel 1", 90 | peaksL, 91 | MAX_TIMELINE, 92 | vmOffset, 93 | NULL, 94 | 0.0f, 95 | 1.0f, 96 | ImVec2(0, 80) 97 | ); 98 | ImGui::PlotLines( 99 | "Peaks Channel 2", 100 | peaksR, 101 | MAX_TIMELINE, 102 | vmOffset, 103 | NULL, 104 | 0.0f, 105 | 1.0f, 106 | ImVec2(0, 80) 107 | ); 108 | ImGui::PlotLines( 109 | "RMS Channel 1", 110 | rmsL, 111 | MAX_TIMELINE, 112 | vmOffset, 113 | NULL, 114 | 0.0f, 115 | 1.0f, 116 | ImVec2(0, 80) 117 | ); 118 | ImGui::PlotLines( 119 | "RMS Channel 2", 120 | rmsR, 121 | MAX_TIMELINE, 122 | vmOffset, 123 | NULL, 124 | 0.0f, 125 | 1.0f, 126 | ImVec2(0, 80) 127 | ); 128 | 129 | ImGui::End(); 130 | 131 | // Audio context 132 | static AudioContext *player = NULL; 133 | if (player == NULL || update_engine) 134 | { 135 | if (player != NULL) 136 | { 137 | audio_destroy_context(player); 138 | } 139 | player = audio_create_context((AudioEngine) audio_engine); 140 | } 141 | if (update_wave | update_engine) 142 | { 143 | audio_wave_load(player, (AudioSampleWave) wave_index, wave_stereo); 144 | } 145 | if (play_wave) 146 | { 147 | audio_wave_play(player); 148 | } 149 | float peaks[2] = { 0.0f, 0.0f }; 150 | float rms[2] = { 0.0f, 0.0f }; 151 | audio_update_volumemeter(player, peaks, rms); 152 | peaksL[vmOffset] = peaks[0]; 153 | peaksR[vmOffset] = peaks[1]; 154 | rmsL[vmOffset] = rms[0]; 155 | rmsR[vmOffset] = rms[1]; 156 | vmOffset = (vmOffset + 1) % MAX_TIMELINE; 157 | } 158 | -------------------------------------------------------------------------------- /utils/testxwma/testxwma.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include /* malloc */ 4 | #include /* FILE* */ 5 | 6 | #include 7 | 8 | float argPlayBegin = 0.0f; 9 | float argPlayLength = 0.0f; 10 | float argLoopBegin = 0.0f; 11 | float argLoopLength = 0.0f; 12 | uint32_t argLoopCount = 0; 13 | 14 | FAudio *faudio = NULL; 15 | FAudioMasteringVoice *mastering_voice = NULL; 16 | FAudioSourceVoice *source_voice = NULL; 17 | 18 | FAudioWaveFormatExtensible *wfx = NULL; 19 | FAudioBuffer buffer = {0}; 20 | FAudioBufferWMA buffer_wma = {0}; 21 | 22 | /* based on https://docs.microsoft.com/en-us/windows/desktop/xaudio2/how-to--load-audio-data-files-in-xaudio2 */ 23 | #define fourccRIFF *((uint32_t *) "RIFF") 24 | #define fourccDATA *((uint32_t *) "data") 25 | #define fourccFMT *((uint32_t *) "fmt ") 26 | #define fourccWAVE *((uint32_t *) "WAVE") 27 | #define fourccXWMA *((uint32_t *) "XWMA") 28 | #define fourccDPDS *((uint32_t *) "dpds") 29 | 30 | uint32_t FindChunk(FILE *hFile, uint32_t fourcc, uint32_t *dwChunkSize, uint32_t *dwChunkDataPosition) 31 | { 32 | uint32_t hr = 0; 33 | 34 | if (fseek(hFile, 0, SEEK_SET) != 0) 35 | { 36 | return -1; 37 | } 38 | 39 | uint32_t dwChunkType; 40 | uint32_t dwChunkDataSize; 41 | uint32_t dwRIFFDataSize = 0; 42 | uint32_t dwFileType; 43 | uint32_t bytesRead = 0; 44 | uint32_t dwOffset = 0; 45 | 46 | while (hr == 0) 47 | { 48 | if (fread(&dwChunkType, sizeof(uint32_t), 1, hFile) < 1) 49 | hr = 1; 50 | 51 | if (fread(&dwChunkDataSize, sizeof(uint32_t), 1, hFile) < 1) 52 | hr = 1; 53 | 54 | if (dwChunkType == fourccRIFF) 55 | { 56 | dwRIFFDataSize = dwChunkDataSize; 57 | dwChunkDataSize = 4; 58 | 59 | if (fread(&dwFileType, sizeof(uint32_t), 1, hFile) < 1) 60 | hr = 1; 61 | } 62 | else 63 | { 64 | if (fseek(hFile, dwChunkDataSize, SEEK_CUR) != 0) 65 | return 1; 66 | } 67 | 68 | dwOffset += sizeof(uint32_t) * 2; 69 | 70 | if (dwChunkType == fourcc) 71 | { 72 | *dwChunkSize = dwChunkDataSize; 73 | *dwChunkDataPosition = dwOffset; 74 | return 0; 75 | } 76 | 77 | dwOffset += dwChunkDataSize; 78 | 79 | if (bytesRead >= dwRIFFDataSize) 80 | return 1; 81 | 82 | } 83 | 84 | return 1; 85 | } 86 | 87 | uint32_t ReadChunkData(FILE *hFile, void * buffer, uint32_t buffersize, uint32_t bufferoffset) 88 | { 89 | uint32_t hr = 0; 90 | 91 | if (fseek(hFile, bufferoffset, SEEK_SET) != 0) 92 | return 1; 93 | 94 | if (fread(buffer, buffersize, 1, hFile) < 1) 95 | hr = 1; 96 | 97 | return hr; 98 | } 99 | 100 | uint32_t load_data(const char *filename) 101 | { 102 | /* open the audio file */ 103 | FILE *hFile = fopen(filename, "rb"); 104 | if (!hFile) 105 | return 1; 106 | 107 | fseek(hFile, 0, SEEK_SET); 108 | 109 | /* Locate the 'RIFF' chunk in the audio file, and check the file type. */ 110 | uint32_t dwChunkSize; 111 | uint32_t dwChunkPosition; 112 | uint32_t filetype; 113 | 114 | FindChunk(hFile,fourccRIFF, &dwChunkSize, &dwChunkPosition); 115 | ReadChunkData(hFile, &filetype, sizeof(uint32_t), dwChunkPosition); 116 | 117 | if (filetype != fourccWAVE && filetype != fourccXWMA) 118 | return 1; 119 | 120 | /* Locate the 'fmt ' chunk, and copy its contents into a WAVEFORMATEXTENSIBLE structure. */ 121 | FindChunk(hFile,fourccFMT, &dwChunkSize, &dwChunkPosition ); 122 | if (dwChunkSize > sizeof(FAudioWaveFormatExtensible)) 123 | { 124 | wfx = (FAudioWaveFormatExtensible *) malloc(dwChunkSize); 125 | printf("chunk-size exceeds wfx size, allocating more: %u > %u\n", dwChunkSize, sizeof(FAudioWaveFormatExtensible)); 126 | } 127 | else 128 | { 129 | wfx = (FAudioWaveFormatExtensible *) malloc(sizeof(FAudioWaveFormatExtensible)); 130 | printf("chunk-size equal or less than wfx size, capping: %u <= %u\n", dwChunkSize, sizeof(FAudioWaveFormatExtensible)); 131 | } 132 | ReadChunkData(hFile, wfx, dwChunkSize, dwChunkPosition ); 133 | 134 | /* Locate the 'data' chunk, and read its contents into a buffer. */ 135 | FindChunk(hFile, fourccDATA, &dwChunkSize, &dwChunkPosition); 136 | uint8_t *pDataBuffer = (uint8_t *) malloc(dwChunkSize); 137 | ReadChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition); 138 | 139 | printf("data chunk size: %u\n", dwChunkSize); 140 | buffer.AudioBytes = dwChunkSize; //buffer containing audio data 141 | buffer.pAudioData = pDataBuffer; //size of the audio buffer in bytes 142 | buffer.Flags = FAUDIO_END_OF_STREAM; // tell the source voice not to expect any data after this buffer 143 | 144 | /* Locate the 'dpds' chunk, and read its contents into a buffer. */ 145 | if (FindChunk(hFile, fourccDPDS, &dwChunkSize, &dwChunkPosition) == 0) 146 | { 147 | uint32_t *cumulBytes = (uint32_t *) malloc(dwChunkSize); 148 | ReadChunkData(hFile, cumulBytes, dwChunkSize, dwChunkPosition); 149 | 150 | buffer_wma.pDecodedPacketCumulativeBytes = cumulBytes; 151 | buffer_wma.PacketCount = dwChunkSize / sizeof(uint32_t); 152 | } 153 | 154 | fclose(hFile); 155 | 156 | return 0; 157 | } 158 | 159 | void faudio_setup() { 160 | uint32_t hr = FAudioCreate(&faudio, 0, FAUDIO_DEFAULT_PROCESSOR); 161 | if (hr != 0) { 162 | return; 163 | } 164 | 165 | hr = FAudio_CreateMasteringVoice(faudio, &mastering_voice, 2, 44100, 0, 0, NULL); 166 | if (hr != 0) { 167 | return; 168 | } 169 | 170 | hr = FAudio_CreateSourceVoice( 171 | faudio, 172 | &source_voice, 173 | (FAudioWaveFormatEx *) wfx, 174 | FAUDIO_VOICE_USEFILTER, 175 | FAUDIO_MAX_FREQ_RATIO, 176 | NULL, NULL, NULL 177 | ); 178 | } 179 | 180 | void play(void) { 181 | 182 | buffer.PlayBegin = argPlayBegin * wfx->Format.nSamplesPerSec; 183 | buffer.PlayLength = argPlayLength * wfx->Format.nSamplesPerSec; 184 | 185 | buffer.LoopBegin = argLoopBegin * wfx->Format.nSamplesPerSec; 186 | buffer.LoopLength = argLoopLength * wfx->Format.nSamplesPerSec; 187 | buffer.LoopCount = argLoopCount; 188 | 189 | if (buffer_wma.pDecodedPacketCumulativeBytes != NULL) 190 | FAudioSourceVoice_SubmitSourceBuffer(source_voice, &buffer, &buffer_wma); 191 | else 192 | FAudioSourceVoice_SubmitSourceBuffer(source_voice, &buffer, NULL); 193 | 194 | uint32_t hr = FAudioSourceVoice_Start(source_voice, 0, FAUDIO_COMMIT_NOW); 195 | 196 | int is_running = 1; 197 | while (hr == 0 && is_running) { 198 | FAudioVoiceState state; 199 | FAudioSourceVoice_GetState(source_voice, &state, 0); 200 | is_running = (state.BuffersQueued > 0) != 0; 201 | SDL_Delay(10); 202 | } 203 | 204 | FAudioVoice_DestroyVoice(source_voice); 205 | 206 | /* free allocated space for FAudioWafeFormatExtensible */ 207 | free(wfx); 208 | } 209 | 210 | int main(int argc, char *argv[]) { 211 | 212 | /* process command line arguments. This is just a test program, didn't go too nuts with validation. */ 213 | if (argc < 2 || (argc > 4 && argc != 7)) { 214 | printf("Usage: %s filename [PlayBegin] [PlayLength] [LoopBegin LoopLength LoopCount]\n", argv[0]); 215 | printf(" - filename (required): can be either a WAV or xWMA audio file.\n"); 216 | printf(" - PlayBegin (optional): start playing at this offset. (in seconds)\n"); 217 | printf(" - PlayLength (optional): duration of the region to be played. (in seconds)\n"); 218 | printf(" - LoopBegin (optional): start looping at this offset. (in seconds)\n"); 219 | printf(" - LoopLength (optional): duration of the loop (in seconds)\n"); 220 | printf(" - LoopCount (optional): number of times to loop\n"); 221 | return -1; 222 | } 223 | 224 | switch (argc) 225 | { 226 | case 7: 227 | sscanf(argv[6], "%d", &argLoopCount); 228 | sscanf(argv[5], "%f", &argLoopLength); 229 | sscanf(argv[4], "%f", &argLoopBegin); 230 | 231 | case 4: 232 | sscanf(argv[3], "%f", &argPlayLength); 233 | 234 | case 3: 235 | sscanf(argv[2], "%f", &argPlayBegin); 236 | } 237 | 238 | if (load_data(argv[1]) != 0) 239 | { 240 | printf("Error loading data\n"); 241 | return -1; 242 | } 243 | 244 | faudio_setup(); 245 | play(); 246 | 247 | return 0; 248 | } 249 | -------------------------------------------------------------------------------- /utils/uicommon/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h) 7 | // B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h" 8 | // If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include 9 | // the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 10 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 11 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 12 | //----------------------------------------------------------------------------- 13 | 14 | #pragma once 15 | 16 | //---- Define assertion handler. Defaults to calling assert(). 17 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 18 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 19 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 20 | 21 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 22 | // Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 23 | //#define IMGUI_API __declspec( dllexport ) 24 | //#define IMGUI_API __declspec( dllimport ) 25 | 26 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 27 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 28 | 29 | //---- Disable all of Dear ImGui or don't implement standard windows. 30 | // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. 31 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 32 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. 33 | //#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty. 34 | 35 | //---- Don't implement some functions to reduce linkage requirements. 36 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. 37 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. 38 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). 39 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 40 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 41 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 42 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 43 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 44 | 45 | //---- Include imgui_user.h at the end of imgui.h as a convenience 46 | //#define IMGUI_INCLUDE_IMGUI_USER_H 47 | 48 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 49 | //#define IMGUI_USE_BGRA_PACKED_COLOR 50 | 51 | //---- Use 32-bit for ImWchar (default is 16-bit) to support full unicode code points. 52 | //#define IMGUI_USE_WCHAR32 53 | 54 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 55 | // By default the embedded implementations are declared static and not available outside of imgui cpp files. 56 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 57 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 58 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 59 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 60 | 61 | //---- Unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined, use the much faster STB sprintf library implementation of vsnprintf instead of the one from the default C library. 62 | // Note that stb_sprintf.h is meant to be provided by the user and available in the include path at compile time. Also, the compatibility checks of the arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. 63 | // #define IMGUI_USE_STB_SPRINTF 64 | 65 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 66 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 67 | /* 68 | #define IM_VEC2_CLASS_EXTRA \ 69 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 70 | operator MyVec2() const { return MyVec2(x,y); } 71 | 72 | #define IM_VEC4_CLASS_EXTRA \ 73 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 74 | operator MyVec4() const { return MyVec4(x,y,z,w); } 75 | */ 76 | 77 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 78 | // Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bit indices). 79 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 80 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 81 | //#define ImDrawIdx unsigned int 82 | 83 | //---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly) 84 | //struct ImDrawList; 85 | //struct ImDrawCmd; 86 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 87 | //#define ImDrawCallback MyImDrawCallback 88 | 89 | //---- Debug Tools: Macro to break in Debugger 90 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 91 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 92 | //#define IM_DEBUG_BREAK __debugbreak() 93 | 94 | //---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), 95 | // (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) 96 | // This adds a small runtime cost which is why it is not enabled by default. 97 | //#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 98 | 99 | //---- Debug Tools: Enable slower asserts 100 | //#define IMGUI_DEBUG_PARANOID 101 | 102 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 103 | /* 104 | namespace ImGui 105 | { 106 | void MyFunction(const char* name, const MyMatrix44& v); 107 | } 108 | */ 109 | -------------------------------------------------------------------------------- /utils/voicepool/voicepool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../uicommon/imgui.h" 4 | #include "../wavcommon/wavs.h" 5 | #include 6 | #include /* free */ 7 | 8 | /* UI Vars */ 9 | const char *TOOL_NAME = "Voice Pool"; 10 | int TOOL_WIDTH = 320; 11 | int TOOL_HEIGHT = 190; 12 | 13 | /* Engine Vars */ 14 | static bool loaded = false; 15 | static FAudio *faudio = NULL; 16 | static FAudioMasteringVoice *master = NULL; 17 | static std::vector monoPool; 18 | static std::vector stereoPool; 19 | static std::vector monoPoolRate; 20 | static std::vector stereoPoolRate; 21 | static float *sounds[6]; 22 | static drwav_uint64 soundLength[6]; 23 | static unsigned int soundRate[6]; 24 | 25 | void FAudioTool_Quit() 26 | { 27 | if (loaded) 28 | { 29 | for (size_t i = 0; i < monoPool.size(); i += 1) 30 | { 31 | FAudioVoice_DestroyVoice(monoPool[i]); 32 | } 33 | monoPool.clear(); 34 | monoPoolRate.clear(); 35 | for (size_t i = 0; i < stereoPool.size(); i += 1) 36 | { 37 | FAudioVoice_DestroyVoice(stereoPool[i]); 38 | } 39 | stereoPool.clear(); 40 | stereoPoolRate.clear(); 41 | 42 | FAudioVoice_DestroyVoice(master); 43 | FAudio_Release(faudio); 44 | 45 | for (size_t i = 0; i < 6; i += 1) 46 | { 47 | free(sounds[i]); 48 | } 49 | 50 | loaded = false; 51 | } 52 | } 53 | 54 | void FAudioTool_Update() 55 | { 56 | bool play_wave = false; 57 | 58 | static int wave_index = (int) AudioWave_SnareDrum01; 59 | static bool wave_stereo = false; 60 | 61 | /* UI Work */ 62 | 63 | ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); 64 | ImGui::SetNextWindowSize(ImVec2(640.0f, 125.0f)); 65 | ImGui::Begin("Wave file to play"); 66 | ImGui::RadioButton("Snare Drum (Forte)", &wave_index, (int) AudioWave_SnareDrum01); 67 | ImGui::RadioButton("Snare Drum (Fortissimo)", &wave_index, (int) AudioWave_SnareDrum02); 68 | ImGui::RadioButton("Snare Drum (Mezzo-Forte)", &wave_index, (int) AudioWave_SnareDrum03); 69 | 70 | play_wave = ImGui::Button("Play"); 71 | ImGui::SameLine(); 72 | ImGui::Checkbox("Stereo", &wave_stereo); 73 | ImGui::End(); 74 | 75 | ImGui::SetNextWindowPos(ImVec2(0.0f, 125.0f)); 76 | ImGui::SetNextWindowSize(ImVec2(640.0f, 65.0f)); 77 | ImGui::Begin("Current pool stats"); 78 | ImGui::Text("Mono Voices: %d", monoPool.size()); 79 | ImGui::Text("Stereo Voices: %d", stereoPool.size()); 80 | ImGui::End(); 81 | 82 | /* Engine Work */ 83 | 84 | if (!loaded) 85 | { 86 | uint32_t hr = FAudioCreate(&faudio, 0, FAUDIO_DEFAULT_PROCESSOR); 87 | SDL_assert(hr == 0); 88 | hr = FAudio_CreateMasteringVoice( 89 | faudio, 90 | &master, 91 | FAUDIO_DEFAULT_CHANNELS, 92 | FAUDIO_DEFAULT_SAMPLERATE, 93 | 0, 94 | 0, 95 | NULL 96 | ); 97 | SDL_assert(hr == 0); 98 | 99 | unsigned int channels; 100 | sounds[0] = WAVS_Open(AudioWave_SnareDrum01, false, &channels, &soundRate[0], &soundLength[0]); 101 | SDL_assert(sounds[0] != NULL); 102 | SDL_assert(channels == 1); 103 | sounds[1] = WAVS_Open(AudioWave_SnareDrum02, false, &channels, &soundRate[1], &soundLength[1]); 104 | SDL_assert(sounds[1] != NULL); 105 | SDL_assert(channels == 1); 106 | sounds[2] = WAVS_Open(AudioWave_SnareDrum03, false, &channels, &soundRate[2], &soundLength[2]); 107 | SDL_assert(sounds[2] != NULL); 108 | SDL_assert(channels == 1); 109 | sounds[3] = WAVS_Open(AudioWave_SnareDrum01, true, &channels, &soundRate[3], &soundLength[3]); 110 | SDL_assert(sounds[3] != NULL); 111 | SDL_assert(channels == 2); 112 | sounds[4] = WAVS_Open(AudioWave_SnareDrum02, true, &channels, &soundRate[4], &soundLength[4]); 113 | SDL_assert(sounds[4] != NULL); 114 | SDL_assert(channels == 2); 115 | sounds[5] = WAVS_Open(AudioWave_SnareDrum03, true, &channels, &soundRate[5], &soundLength[5]); 116 | SDL_assert(sounds[5] != NULL); 117 | SDL_assert(channels == 2); 118 | 119 | loaded = true; 120 | } 121 | 122 | if (play_wave) 123 | { 124 | FAudioSourceVoice *src; 125 | unsigned int rate; 126 | size_t index = wave_index + (wave_stereo ? 3 : 0); 127 | 128 | bool found = false; 129 | const std::vector& pool = wave_stereo ? stereoPool : monoPool; 130 | const std::vector& poolRate = wave_stereo ? stereoPoolRate : monoPoolRate; 131 | for (size_t i = 0; i < pool.size(); i += 1) 132 | { 133 | FAudioVoiceState state; 134 | FAudioSourceVoice_GetState(pool[i], &state, FAUDIO_VOICE_NOSAMPLESPLAYED); 135 | if (state.BuffersQueued == 0) 136 | { 137 | /* Nothing is queued, reuse this voice! */ 138 | src = pool[i]; 139 | rate = poolRate[i]; 140 | found = true; 141 | break; 142 | } 143 | } 144 | if (found) 145 | { 146 | /* At most we can change the sample rate, can't change channel count */ 147 | if (soundRate[index] != rate) 148 | { 149 | FAudioSourceVoice_SetSourceSampleRate(src, soundRate[index]); 150 | } 151 | } 152 | else 153 | { 154 | /* Pool didn't have anything left, make a new voice */ 155 | FAudioWaveFormatEx waveFormat; 156 | waveFormat.wFormatTag = 3; 157 | waveFormat.nChannels = wave_stereo ? 2 : 1; 158 | waveFormat.nSamplesPerSec = soundRate[index]; 159 | waveFormat.nBlockAlign = waveFormat.nChannels * sizeof(float); 160 | waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; 161 | waveFormat.wBitsPerSample = sizeof(float) * 8; 162 | waveFormat.cbSize = 0; 163 | 164 | uint32_t hr = FAudio_CreateSourceVoice( 165 | faudio, 166 | &src, 167 | &waveFormat, 168 | 0, 169 | FAUDIO_DEFAULT_FREQ_RATIO, 170 | NULL, 171 | NULL, 172 | NULL 173 | ); 174 | 175 | /* Just start this now, let the buffer call "play" instead */ 176 | FAudioSourceVoice_Start(src, 0, FAUDIO_COMMIT_NOW); 177 | 178 | if (wave_stereo) 179 | { 180 | stereoPool.push_back(src); 181 | stereoPoolRate.push_back(soundRate[index]); 182 | } 183 | else 184 | { 185 | monoPool.push_back(src); 186 | monoPoolRate.push_back(soundRate[index]); 187 | } 188 | } 189 | 190 | /* Buffer and play, finally */ 191 | FAudioBuffer buffer; 192 | buffer.Flags = FAUDIO_END_OF_STREAM; 193 | buffer.AudioBytes = soundLength[index] * sizeof(float); 194 | buffer.pAudioData = (uint8_t*) sounds[index]; 195 | buffer.PlayBegin = 0; 196 | buffer.PlayLength = soundLength[index] / (wave_stereo ? 2 : 1); 197 | buffer.LoopBegin = 0; 198 | buffer.LoopLength = 0; 199 | buffer.LoopCount = 0; 200 | buffer.pContext = NULL; 201 | FAudioSourceVoice_SubmitSourceBuffer(src, &buffer, NULL); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /utils/wavcommon/resources/origin.txt: -------------------------------------------------------------------------------- 1 | All music instrument samples were downloaded from http://www.philharmonia.co.uk/explore/sound_samples 2 | -------------------------------------------------------------------------------- /utils/wavcommon/resources/snaredrum_forte.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FNA-XNA/FAudio/75b30a6dfaf28ea74b585e2180d82ef993050159/utils/wavcommon/resources/snaredrum_forte.wav -------------------------------------------------------------------------------- /utils/wavcommon/resources/snaredrum_forte_stereo.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FNA-XNA/FAudio/75b30a6dfaf28ea74b585e2180d82ef993050159/utils/wavcommon/resources/snaredrum_forte_stereo.wav -------------------------------------------------------------------------------- /utils/wavcommon/resources/snaredrum_fortissimo.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FNA-XNA/FAudio/75b30a6dfaf28ea74b585e2180d82ef993050159/utils/wavcommon/resources/snaredrum_fortissimo.wav -------------------------------------------------------------------------------- /utils/wavcommon/resources/snaredrum_fortissimo_stereo.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FNA-XNA/FAudio/75b30a6dfaf28ea74b585e2180d82ef993050159/utils/wavcommon/resources/snaredrum_fortissimo_stereo.wav -------------------------------------------------------------------------------- /utils/wavcommon/resources/snaredrum_mezzoforte.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FNA-XNA/FAudio/75b30a6dfaf28ea74b585e2180d82ef993050159/utils/wavcommon/resources/snaredrum_mezzoforte.wav -------------------------------------------------------------------------------- /utils/wavcommon/resources/snaredrum_mezzoforte_stereo.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FNA-XNA/FAudio/75b30a6dfaf28ea74b585e2180d82ef993050159/utils/wavcommon/resources/snaredrum_mezzoforte_stereo.wav -------------------------------------------------------------------------------- /utils/wavcommon/wavs.cpp: -------------------------------------------------------------------------------- 1 | #define DR_WAV_IMPLEMENTATION 2 | #include "wavs.h" 3 | 4 | #ifndef RESOURCE_PATH 5 | #ifdef _MSC_VER 6 | #define RESOURCE_PATH "../../utils/wavcommon/resources" 7 | #else 8 | #define RESOURCE_PATH "utils/wavcommon/resources" 9 | #endif 10 | #endif 11 | 12 | const char *audio_sample_filenames[] = 13 | { 14 | RESOURCE_PATH"/snaredrum_forte.wav", 15 | RESOURCE_PATH"/snaredrum_fortissimo.wav", 16 | RESOURCE_PATH"/snaredrum_mezzoforte.wav", 17 | }; 18 | 19 | const char *audio_stereo_filenames[] = 20 | { 21 | RESOURCE_PATH"/snaredrum_forte_stereo.wav", 22 | RESOURCE_PATH"/snaredrum_fortissimo_stereo.wav", 23 | RESOURCE_PATH"/snaredrum_mezzoforte_stereo.wav", 24 | }; 25 | 26 | float* WAVS_Open( 27 | AudioSampleWave sample, 28 | bool stereo, 29 | unsigned int *wav_channels, 30 | unsigned int *wav_samplerate, 31 | drwav_uint64 *wav_sample_count 32 | ) { 33 | return drwav_open_file_and_read_pcm_frames_f32( 34 | (!stereo) ? 35 | audio_sample_filenames[sample] : 36 | audio_stereo_filenames[sample], 37 | wav_channels, 38 | wav_samplerate, 39 | wav_sample_count, 40 | NULL 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /utils/wavcommon/wavs.h: -------------------------------------------------------------------------------- 1 | #ifndef WAVS_H 2 | #define WAVS_H 3 | 4 | #include 5 | #include "dr_wav.h" 6 | 7 | typedef enum 8 | { 9 | AudioWave_SnareDrum01 = 0, 10 | AudioWave_SnareDrum02, 11 | AudioWave_SnareDrum03, 12 | } AudioSampleWave; 13 | 14 | float* WAVS_Open( 15 | AudioSampleWave sample, 16 | bool stereo, 17 | unsigned int *wav_channels, 18 | unsigned int *wav_samplerate, 19 | drwav_uint64 *wav_sample_count 20 | ); 21 | 22 | #endif /* WAVS_H */ 23 | -------------------------------------------------------------------------------- /visualc-gdk/FAudio.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32630.194 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAudio", "FAudio.vcxproj", "{35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL3", "..\..\SDL\VisualC-GDK\SDL\SDL.vcxproj", "{81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Gaming.Xbox.Scarlett.x64 = Debug|Gaming.Xbox.Scarlett.x64 13 | Debug|Gaming.Xbox.XboxOne.x64 = Debug|Gaming.Xbox.XboxOne.x64 14 | Profile|Gaming.Xbox.Scarlett.x64 = Profile|Gaming.Xbox.Scarlett.x64 15 | Profile|Gaming.Xbox.XboxOne.x64 = Profile|Gaming.Xbox.XboxOne.x64 16 | Release|Gaming.Xbox.Scarlett.x64 = Release|Gaming.Xbox.Scarlett.x64 17 | Release|Gaming.Xbox.XboxOne.x64 = Release|Gaming.Xbox.XboxOne.x64 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 21 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Xbox.Scarlett.x64 22 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 23 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Xbox.XboxOne.x64 24 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Profile|Gaming.Xbox.Scarlett.x64.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 25 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Profile|Gaming.Xbox.Scarlett.x64.Build.0 = Profile|Gaming.Xbox.Scarlett.x64 26 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Profile|Gaming.Xbox.XboxOne.x64.ActiveCfg = Profile|Gaming.Xbox.XboxOne.x64 27 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Profile|Gaming.Xbox.XboxOne.x64.Build.0 = Profile|Gaming.Xbox.XboxOne.x64 28 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 29 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Xbox.Scarlett.x64 30 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 31 | {35B0FB4B-F0C2-4D70-9B2D-136C8A3F68D1}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Xbox.XboxOne.x64 32 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 33 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Xbox.Scarlett.x64 34 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 35 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Xbox.XboxOne.x64 36 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Profile|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 37 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Profile|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Xbox.Scarlett.x64 38 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Profile|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 39 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Profile|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Xbox.XboxOne.x64 40 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 41 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Xbox.Scarlett.x64 42 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 43 | {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Xbox.XboxOne.x64 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {7B59FC6A-2675-4ADB-BA67-7B6F0E01A600} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /visualc/FAudio.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C++ Express 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAudio", "FAudio.vcxproj", "{90A103EF-E403-47D4-BBBB-0F206B9FA7F2}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Debug|Win32.Build.0 = Debug|Win32 16 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Release|Win32.ActiveCfg = Release|Win32 17 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Release|Win32.Build.0 = Release|Win32 18 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Debug|x64.ActiveCfg = Debug|x64 19 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Debug|x64.Build.0 = Debug|x64 20 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Release|x64.ActiveCfg = Release|x64 21 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /visualc/FAudio.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {90A103EF-E403-47D4-BBBB-0F206B9FA7F2} 23 | FAudio 24 | 25 | 26 | 27 | DynamicLibrary 28 | true 29 | MultiByte 30 | 31 | 32 | DynamicLibrary 33 | false 34 | true 35 | MultiByte 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ..\..\SDL2\include;..\include;$(IncludePath) 46 | ..\..\SDL2\lib\$(PlatformShortName);$(LibraryPath) 47 | 48 | 49 | 50 | Level3 51 | Disabled 52 | 53 | 54 | true 55 | SDL2.lib;%(AdditionalDependencies) 56 | 57 | 58 | 59 | 60 | Level3 61 | MaxSpeed 62 | FAUDIO_DISABLE_DEBUGCONFIGURATION;%(PreprocessorDefinitions) 63 | true 64 | true 65 | 66 | 67 | true 68 | true 69 | true 70 | SDL2.lib;%(AdditionalDependencies) 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /visualc/README: -------------------------------------------------------------------------------- 1 | Building FAudio for Windows 2 | --------------------------- 3 | FAudio uses Visual Studio 2010 to build on Windows. 4 | 5 | TODO: REMOVE C RUNTIME DEPENDENCY! 6 | 7 | Dependencies 8 | ------------ 9 | Before building, download SDL2's VC development libraries from SDL's website: 10 | 11 | http://libsdl.org/download-2.0.php 12 | 13 | Extract the ZIP file's SDL2 directory (called something like 'SDL2-2.0.8') as 14 | a sibling to your FAudio checkout and rename it to 'SDL2', so that you have 15 | directories named 'FAudio' and 'SDL2' next to each other. 16 | 17 | Compiling 18 | --------- 19 | 1. Build FAudio.sln 20 | 2. Grab the output DLL along with SDL2.dll, ship it! 21 | --------------------------------------------------------------------------------