Integrating FAudio, a reimplementation of XAudio2

Ethan Lee flibitijibibo at flibitijibibo.com
Fri Oct 19 12:39:44 CDT 2018


Actually, shoot, I just realized the XAPO exposure actually breaks the exported function idea too; the implementation itself calls that function…

https://github.com/FNA-XNA/FAudio/blob/1149f712b181076437c5918200d0406aba066779/src/FAudio.c#L835

So what would happen is a Windows application sends a custom effect, that gets sent to FAudio, FAudio gets the registration properties and then frees it with SDL_free. Well, crap… the global variable might be the only fix for that, in a dynamically linked situation.

-Ethan

> On Oct 19, 2018, at 1:31 PM, Ethan Lee <flibitijibibo at flibitijibibo.com> wrote:
> 
> That’s another possibility. For XACT I ended up doing something just like it for streaming WaveBank support, called the FAudioIOStream. The difference is that the XACT API had room for that - for WaveBank creation the stream handle is just a void*, so I was able to sell the compatibility issue as “if you use CreateFile, call FAudio_fopen, otherwise implement FAudioIOStream.” XAudio doesn’t quite have that, so we either have to break the API (for reference, FAudio is meant to be a 1:1 exact mapping of the spec) or add a new function that’s called before everything else (and then disallow changing it after the first call).
> 
> In either case, it actually gets even more annoying from there - part of the memory exposure is also in the XAPO interface, so in addition to FAudio returning allocated memory, custom effects from the application are expected to return allocated memory too! And whatever system we’d come up with internally, sadly there’s no way to pass off a callback context to existing XAPO implementations (and even if there was, that breaks the XAPO spec anyway). Even worse, XACT may or may not create its own engine internally, and so that API would also need to have an additional parameter as well, if we decided to go with the API change (and for all I know, someone will want different allocators for the low-level XAudio portion and high-level XACT stuff, I’d have to think about that before marking it as stable). So we either have a global variable for custom memory management that may or may not be set at all (meaning we may have to static init a default…?) or we add a handful of extra parameters whose locations vary and may conflict between multiple contexts between both XAudio and XACT. It’s definitely messy, for sure… I’m inclined to just expose a malloc/free, even though in just about every other case imaginable it’s inferior to a callback system.
> 
> -Ethan
> 
>> On Oct 19, 2018, at 12:53 PM, Henri Verbeet <hverbeet at gmail.com> wrote:
>> 
>> On Fri, 19 Oct 2018 at 20:12, Ethan Lee <flibitijibibo at flibitijibibo.com> wrote:
>>> 
>>> Ideally that’d be the goal, unfortunately there’s some friction that XAudio2 throws at us.
>>> 
>>> In the 100% ideal situation we could link to a native FAudio, but the main thing that blocks it is how XAudio2 (and XACT) expose memory to applications. There are a handful of calls that throw memory at you that you’re expected to free with CoTaskMemFree, which a native FAudio would not be able to line up with, so you would have to duplicate the data structure in the XAudio2 built-in and then free it… but then the `free` implementation can vary (by default it’s SDL_free), so you’d have to be sync’d with FAudio at the source level anyhow.
>>> 
>>> That said, I was thinking about it again this morning (we’ve been throwing around ideas for a couple days) and realized that even XAudio doesn’t _necessarily_ ask for CoTaskMemFree, it asks for “XAPOFree” for example, which macros to either CoTaskMemFree or XMemFree. So a possible fix on my end could be to expose FAudio_malloc/FAudio_free as exported functions (not just internal macros, which is how it works now), and then projects like Wine can call FAudio_free after duplicating the memory. It’s still a duplication,  and accuracywise it’s not entirely the same as the official macro, but it sounds like that’d be preferred to statically linking the whole implementation!
>>> 
>> The way you typically solve that kind of thing is by passing an "ops"
>> structure with callbacks for e.g. memory allocation, thread creation,
>> etc. on library initialisation, which the library then uses
>> internally.
>> 
> 




More information about the wine-devel mailing list