Integrating FAudio, a reimplementation of XAudio2

Ethan Lee flibitijibibo at flibitijibibo.com
Fri Oct 19 20:05:30 CDT 2018


The biggest problem is that there are _loads_ of entry points that 
aren't necessarily coupled with one another - the main ones are 
XAudio2Create, XACT3CreateEngine, CreateReverb, CreateVolumeMeter, and 
CreateFX, all of which are technically supposed to be decoupled from one 
another even though they all directly interact with each other and 
nothing outside of this subsystem of DirectX. The famous "we're totally 
decoupled except for the part where we're not" scenario. So in reality, 
you're not setting 1 allocator, you're setting up to 5:

https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b241103457#diff-d343797575e4549662dd2fbe738a6d91R75
https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b241103457#diff-d343797575e4549662dd2fbe738a6d91R87
https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b241103457#diff-7cfa2f99a6e263b4e2900151c7fb87d8R701
https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b241103457#diff-4eb1f5065e9064881be7b560be991332R1502

Not pictured is XAPO CreateFX, which I haven't started yet. In a typical 
application you would only set this once at startup, but since the COM 
wrapper (and Wine) don't know the call stream's order ahead of time, we 
have to shotgun blast it. Not the worst thing, just looks kind of goofy.

You might be thinking we could add the callback system to every 
function, but keep in mind that the allocators in some parts should be 
the same as in others, or malloc/free mismatches can occur. Admittedly 
it's "some" and not "all", but the fact that it's ambiguous is what 
makes it kind of dangerous to wield (IMO). Even worse, there's 
situations like AudioEngine_Initialize that takes in an optional XAudio2 
parameter:

https://github.com/FNA-XNA/FAudio/blob/2fdfc193940a1aa9a89f84ce46bf7ff84280ae1b/src/FACT.h#L109

So then some questions come up along the lines of "which allocator do we 
use, the XACT3 allocator all the time, or the XAudio2 allocator if we 
provide that (since the engine may have already allocated some stuff), 
or do we allow an XACT3 with allocator A and a custom XAudio2 with 
allocator B, and try to support both at the same time," and that's just 
the interaction between XACT and XAudio; there are potential conflicts 
with XAPO as well because the APO could be using allocator A but XAudio 
expects allocator B when reading RegistrationProprties, and that's not 
factoring in what could happen with custom XAudio2 voices you can set on 
XACT3 voices with custom XAPOs attached... you get the idea.

The global allocator definitely feels primitive and clunky, but I don't 
know if I have it in me to figure out the above...

-Ethan

On 10/19/18 18:35, Henri Verbeet wrote:
> On Fri, 19 Oct 2018 at 22:26, Ethan Lee <flibitijibibo at flibitijibibo.com> wrote:
>> To test the waters, I added the global custom allocator func and tried
>> it out with our existing COM wrapper, and aside from making sure that
>> it's absolutely called before any API call, it's kind of nice! The
>> commit is in a separate branch for now:
>>
>> https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b241103457
>>
>> So that's pretty much what it would look like on the Wine side too; any
>> function that initializes any portion of the XAudio/XACT APIs would need
>> to set the functions before doing anything else. There's definitely
>> redundancy and I probably shouldn't be using static functions in our COM
>> wrapper, for example, but it works and manages to fix things in a way
>> that allows our COM wrapper to work with the stock FAudio.dll on
>> Windows, so that's nice!
>>
>> This doesn't have to be the official API right away, of course, so I'm
>> open to feedback on this.
>>
> I should note that I'm not all that familiar with the specifics of the
> XAudio API, so I may be missing the obvious here, but it's not clear
> to me why the allocation callbacks would need to be globals. (Reasons
> you don't want them to be globals include e.g. different parts of the
> same process loading the library, potentially with different
> allocators.)
>
> For what it's worth, the kind of API I had in mind would look roughly like this:
>
>      HRESULT faudio_create(const struct faudio_create_info
> *create_info, void **xaudio2)
>      {
>         ...
>      }
>
>      HRESULT XAudio2Create(IXAudio2 **xaudio2, UINT32 flags,
> XAUDIO2_PROCESSOR proc)
>      {
>         struct faudio_create_info create_info;
>
>         create_info.type = FAUDIO_STRUCTURE_TYPE_CREATE_INFO;
>         create_info.next = NULL;
>
>         create_info.pfn_malloc = SDL_malloc;
>         create_info.pfn_realloc = SDL_realloc;
>         create_info.pfn_free = SDL_free;
>
>         create_info.iid = &IID_IXAudio28;
>         create_info.flags = flags;
>         create_info.proc = proc;
>
>         return faudio_create(&create_info, (void **)xaudio2);
>      }
>
> Which (again, unsurprisingly) is fairly similar to the vkd3d API.
>




More information about the wine-devel mailing list