Audio Device Selection
Hello.
I'm working on audio 3D renderer with Python and PyOpenAL.
I have problems :
- I can't list all audio device from my windos PC
- So I can't select specific audio device for rendering audio
- Will 3D audio spatialisation work with not 7.1 card (example : 10 channels pro audio board) ?
the 7.1 integrated soundboard work when selected by default on windows but I have to select another specific audio device and use PRO Audio USB Board
Many thanks for your answer and help :)
I had a similar issue with the audio device selection not functioning correctly. It does seem that PyOpenAL's current implementation doesn't support device listing properly.
Normally you'd use alc.alcIsExtensionPresent(None, b'ALC_ENUMERATION_EXT') and alc.alcGetString(None, alc.ALC_ALL_DEVICES_SPECIFIER) to get the list of playback devices. However, ALC_ALL_DEVICES_SPECIFIER is undefined in PyOpenAL. The correct constant is 0x1013, which you can pass directly.
In theory, you should be able to do:
if alc.alcIsExtensionPresent(None, b'ALC_ENUMERATION_EXT'):
devices = alc.alcGetString(None, 0x1013)
print(devices)
But OpenAL's documentation indicates that the strings returned for ALC_ALL_DEVICES_SPECIFIER are null character delimited with the end being marked by a double null character. Since alc.alcGetString() was bound to the CDLL with the return type ctypes.c_char_p, I'm guessing everything after the first null character gets dropped.
Here's the hack I came up with for reading these weird string lists:
import ctypes
from openal import alc
alc.ALC_ALL_DEVICES_SPECIFIER = 0x1013
def alcGetStringList(*args):
alc.alcGetString.restype = ctypes.POINTER(ctypes.c_char)
str_p = alc.alcGetString(*args)
alc.alcGetString.restype = ctypes.c_char_p
items = []
item = b''
for char in str_p:
if char == b'\0':
if not len(item):
break
items.append(item.decode('utf-8', errors='replace'))
item = b''
else:
item += char
return items
def list_audio_devices():
device_list = []
if alc.alcIsExtensionPresent(None, b'ALC_ENUMERATION_EXT'):
return alcGetStringList(None, alc.ALC_ALL_DEVICES_SPECIFIER)
else:
print('WARNING: OpenAL device enumeration extension is not available.')
return device_list
print(list_audio_devices())
Which gives me:
['OpenAL Soft on Headphones (3- High Definition Audio Device)', 'OpenAL Soft on Headphones (Oculus Virtual Audio Device)', 'OpenAL Soft on Digital Audio (S/PDIF) (3- High Definition Audio Device)']
THIS SOLUTION IS NOT THREAD SAFE! It temporarily overrides the default return type for alc.alcGetString() so that it can get what it needs. I don't work with DLL's much, so I'm not sure how to copy the DLL function pointer so I can have a separate function alias for the different return type.
I may look into making a PR to address the issue at some point, but looking into what the correct implementation would be would take a bit of time since the expected use case for alcGetString() based on the OpenAL documentation depends on some pointer shenanigans, which isn't very pythonic.