Getting the name of an audio device from it’s GUID : Using the Oculus Rift selected audio device with OpenAL

So, while working on my game engine, I was curious about looking at the technical requirement for submitting an application to the Oculus Store.

One of the things required is that you need to target the audio output (and input) devices selected by the user in the Oculus app

So, how does the Oculus SDK tells you what is the selected device?

Well, take a look at the OVR_CAPI_Audio.h header in the OculusSDK (/OculusSDK/LibOVR/include/)

GUID is a type declared by the Win32 libraries.

As far as I know, there isn’t any way with OpenAL to select an audio device by anything but it’s name. You can enumerate them by name using an extension (that every platform has available), but that’s about it/

After some research (and frustration) looking at the core audio libraries pages on the MSDN (Microsoft’s documentation for developers), I discovered that theses GUID are actually used by a higher level API from the DirectX package: DirectSound.

At this point, know that you’ll need to link against dsound.lib, I’m sorry… ^^”

And, there isn’t a direct way to get the name of the device from it’s GUID. But, you can enumerate ALL the present devices, and, in a callback function, you can get at the same time, a pointer to the GUID and the name of the device. This can be done with the DirectSoundEnumerate function.

As most function that uses text in the Windows API, the actual name is a macro that points to either a wide-char version of the funciton, or an ASCII version. Here we’ll force it being ASCII for the sake of simplicity.

So, here’s the prototype of DirectSoundEnumerateA from the dsound.h header

Okay, so, at that point, if you never ever saw code from Microsoft’s C library, you are probably thinking “WHAT THE HELL IS GOING ON WHIT THIS?!??!”, and you have some write to feel like that, don’t worry.

The Windows library redefine every single type that way, and has a very heavy usage of pre-processor macros.

An HRESULT is a long, a VOID is a void and each time you see LP in the begining of a type, it means pointer.

LPDSENUMCALLBACKA is a typedefed function pointer, that will point to a callback funciton you’ll have to write yourself. LP means pointer, and the A at the end means ASCII.

Here’s the typedef that shows how your callback function will be defined :

Since there isn’t the names of these arguments here, it’s not that much helpfull, so we have to turn to the MSDN documentation to see how it works : https://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.reference.dsenumcallback(v=vs.85).aspx

So, the idea is, you define a funciton that follow this signature, you give it to DirectSoundEnumerate, and it will call it with the GUID and the name of the sound device as the 2 parameters. great… Problem, you can’t get the out of the function without some work.

Both of theses functions takes a LPVOID as their last argument, they are just void* and permit you to give an “user defined context”.

It means that you can pass the address of anything you want to it, like… I don’t know, a data structure that the function will insert the guid and name together so that we can get the correspondence. Well, you can do what ever you want.

The thing is, you need to define a new function for that, and it’s annoying. thankfully, (and this is the quite clever bit IMO) C++11 lambda “decay” as function pointers. More or less in the same way a C array of int will “decay” as an int* pointing to it.

So, you can totally do this instead of declaring a plain old C function :

Here, descriptor is a std::vector of “stupidAudioDescriptor” that has a constructor that will take the LPCSTR and the LPGUID, figure out if the GUID is not null, and store them side by side.

Then I just std::find_if my way in to get the one with a corresponding GUID and get the name. There’s probably a cleverer way of doing it by giving the GUID as context and using a lambda capture but I did not took the time to look into that, as this is just called once during my engine initialization.

And, That’s a bit convoluted, I agree, but it works.The hacked together version of this can be seen here on Annwvyn’s github repo.

Leave a Reply

Your email address will not be published / Required fields are marked *

*