openal-soft
openal-soft copied to clipboard
Can openal detect a change in default output device?
Many apps detect change in audio output device and reconnect, but not sure if openal can do that.
I tried alplay from examples and it always plays correctly on defined output device, but if I change during play it do not reconnect to new output device
Is that possible?
OpenAL Soft won't automatically switch devices. But as long as the underlying audio system supports changing the default audio device in running apps, OpenAL Soft will show the change when querying after re-enumerating devices. This may not be very practical in most apps since enumeration can take a relatively long amount of time. You could set up a background thread that periodically checks for changes in the default device, which would avoid stalling the main thread, though it won't be the most efficient thing. Alternatively, you can use the OS's functionality to get notifications of the default device changing, and only check with OpenAL after being notified that way.
In either case, when you determine that the default output changed and you want to move with the default device (if the user didn't request a specific device, for instance), current Git has an in-progress reopen_device extension, with the alcReopenDeviceSOFT
function that can change the output of an active ALCdevice
handle. So after being notified that the default changed, you can call that to move output to the new default. Without that extension, you'll need to destroy the current context and close the existing device, then open the new device you want and recreate the context and its objects.
Thanks for clarify, I probably describe incorrectly terms, maybe card profile change is more accurate:
I made this test:
build alplay.c from examples:
$ g++ $(pkg-config openal --cflags --libs) -lsndfile -I ./common/ -fpermissive alplay.c common/alhelpers.c -o alplay
or
$ gcc $(pkg-config openal --cflags --libs) -lsndfile -I ./common/ alplay.c common/alhelpers.c -o alplay
get a free sample (of some seconds duration) to reproduce:
$ wget https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_10MG.wav
Play it with differente card profiles (notice the open device):
$ ./alplay file_example_WAV_10MG.wav
Opened "Audio Interno Digital Stereo (HDMI 2)"
$ ./alplay file_example_WAV_10MG.wav
Opened "Audio Interno Estéreo analógico"
Then play same sample and change during playback card profile:
pacmd set-card-profile alsa_card.pci-0000_00_1f.3 output:analog-stereo
pacmd set-card-profile alsa_card.pci-0000_00_1f.3 output:hdmi-stereo-extra1+input:analog-stereo
sound is missed for a second but it continues playing on new profile, so is this change transparent to openal?
Sometimes change card profile do not work, in that case I open sound control, choose hdmi and press test sound buttons for a while (after I hear sound on hdmi) then openal alplay is able to change on the fly from one profile to the other:
I'm debugging a problem with sound where pavucontrol shows audio on an input sink (from openal) but sound is only played on internal speakers.
probably openal is behaving correctly but pulseaudio is not routing sound correctly, not really sure how to triage this
I can upload a screencast if that clarifies it, and this looks related to openal (if this looks like pulseaudio related sorry for the noise)
sound is missed for a second but it continues playing on new profile, so is this change transparent to openal?
Yeah. OpenAL Soft just opens the device the app asks it to, so whether or not it changes afterward is not a result of OpenAL doing anything itself. It's the audio system enacting those changes separate from OpenAL or the app.
Looks like OpenAL may support exposing the default output device change events soon, under the ALC_SOFTX_system_events
extension: https://github.com/kcat/openal-soft/commit/517bb94c0953de768f6aacca45456fd63bb2c96d.
I'm excited for this because I was just starting to consider using each platform-specific API to watch for default-device changes so I could use alcReopenDeviceSOFT
to switch to the new default device whenever it changes.
It can report changes now with commit bfb79945d0ed7450ad3e93941f6576ce1cc4058b, depending on the backend. The PulseAudio backend, notably, doesn't report default device changes, only when devices are added or removed. The PulseAudio API doesn't seem to have a notification method for the default device changing. But PipeWire and WASAPI should work. With that and the ALC_SOFT_reopen_device
extension, you can detect default device change notifications from the system, then reopen a given ALCdevice
using the new default as desired.
Wow, crazy how I went digging into this issue just now, and you made the commit last week ;)
So I tested it, on Win32, using alcReopenDeviceSOFT
, alcEventControlSOFT
and alcEventCallbackSOFT
and it's working perfectly: when the default device changes, I reopen the device and it moves properly to the new default device.
Thanks and keep up the great work!
hi @kcat and @frozax
Can we handle the device removal event used by OpenAL-Soft with alcEventCallbackSOFT
?
@kcat, can you show a small example of this?
On current master
combining alcEventCallbackSOFT
+ alcReopenDeviceSOFT
causes soft dead-lock (or something similar).
Callstack:
1 ntdll!ZwWaitForMultipleObjects
2 WaitForMultipleObjectsEx
3 WaitForMultipleObjects
4 libwinpthread-1!_pthread_rel_time_in_ms
5 pthread_cond_init
6 pthread_cond_init
7 pthread_cond_wait
8 libstdc++-6!_ZNSt18condition_variable4waitERSt11unique_lockISt5mutexE
9 std::__atomic_futex_unsigned<2147483648u>::_M_load_when_equal
10 std::__future_base::_State_baseV2::wait
11 std::__basic_future<long>::wait
12 (anonymous namespace)::WasapiPlayback::~WasapiPlayback
13 (anonymous namespace)::WasapiPlayback::~WasapiPlayback
14 std::default_delete<BackendBase>::operator()
15 std::__uniq_ptr_impl<BackendBase, std::default_delete<BackendBase>>::reset
16 std::__uniq_ptr_impl<BackendBase, std::default_delete<BackendBase>>::operator=
17 std::__uniq_ptr_data<BackendBase, std::default_delete<BackendBase>, true, true>::operator=
18 std::unique_ptr<BackendBase, std::default_delete<BackendBase>>::operator=
19 alcReopenDeviceSOFT
20 operator() // my callback here
21 _FUN
22 alc::Event
23 alc::Event
24 (anonymous namespace)::DeviceHelper::OnDefaultDeviceChanged
25 MMDevAPI!DllUnregisterServer
26 MMDevAPI!DllCanUnloadNow
27 ??
28 ntdll!RtlDeactivateActivationContext
29 ntdll!TpReleaseCleanupGroupMembers
30 KERNEL32!BaseThreadInitThunk
31 ntdll!RtlUserThreadStart
32 ??
On current
master
combiningalcEventCallbackSOFT
+alcReopenDeviceSOFT
causes soft dead-lock (or something similar).
You can't make OpenAL calls in the event callback (or wait in the callback for an OpenAL call to finish). The OpenAL event callback is called from the system's notification callback, which will have restrictions on what you can do with the device. It's best to only do basic things in the callback, and use a flag or something to do more complicated work later in your main/worker threads.