openal-soft icon indicating copy to clipboard operation
openal-soft copied to clipboard

Can openal detect a change in default output device?

Open albfan opened this issue 3 years ago • 10 comments

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?

albfan avatar Apr 15 '21 07:04 albfan

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.

kcat avatar Apr 15 '21 22:04 kcat

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:

Captura de pantalla de 2021-04-16 12-16-52

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

albfan avatar Apr 16 '21 10:04 albfan

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)

albfan avatar Apr 16 '21 10:04 albfan

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.

kcat avatar Apr 16 '21 23:04 kcat

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.

Macil avatar Aug 20 '23 11:08 Macil

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.

kcat avatar Aug 20 '23 12:08 kcat

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!

frozax avatar Aug 31 '23 00:08 frozax

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?

SeanTolstoyevski avatar Jun 07 '24 05:06 SeanTolstoyevski

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 ??     

Try avatar Jun 23 '24 22:06 Try

On current master combining alcEventCallbackSOFT + 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.

kcat avatar Jun 23 '24 22:06 kcat