16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-17 22:39:17 +02:00

feat: follow audio output device changes (reopen on disconnect)

OpenAL does not re-route on its own, so unplugging the active output device
(e.g. headphones) mid-game left the sim silent until restart. Poll ALC_CONNECTED
(ALC_EXT_disconnect) at ~1 Hz and, when the device is lost, reopen playback on
the current default output via alcReopenDeviceSOFT, preserving the context and
all sources. Tokens are declared locally since the bundled AL headers ship no
alext.h; the entry point is resolved at runtime, so this is a no-op (with a log
warning) on OpenAL Soft builds too old to provide ALC_SOFT_reopen_device.
This commit is contained in:
maj00r
2026-07-03 22:12:55 +02:00
parent 2ffff9193f
commit 7fd98374e9
2 changed files with 45 additions and 0 deletions

View File

@@ -17,6 +17,12 @@ http://mozilla.org/MPL/2.0/.
#include "simulation/simulation.h"
#include "vehicle/Train.h"
// ALC_EXT_disconnect / ALC_SOFT_reopen_device tokens; the bundled AL headers ship no alext.h,
// but OpenAL Soft provides these at runtime (resolved via alcGetProcAddress).
#ifndef ALC_CONNECTED
#define ALC_CONNECTED 0x313
#endif
namespace audio {
openal_renderer renderer;
@@ -363,6 +369,32 @@ openal_renderer::update( double const Deltatime ) {
if (alcDeviceResumeSOFT)
alcDeviceResumeSOFT(m_device);
// follow audio output device changes (OpenAL won't on its own): if the active device is
// gone (e.g. headphones unplugged) reopen playback on the current default output. Polled at
// ~1 Hz to avoid per-frame ALC round-trips.
if( m_candetectdisconnect ) {
m_devicechecktime += Deltatime;
if( m_devicechecktime >= 1.0 ) {
m_devicechecktime = 0.0;
ALCint connected{ ALC_TRUE };
::alcGetIntegerv( m_device, ALC_CONNECTED, 1, &connected );
if( connected == ALC_FALSE ) {
if( alcReopenDeviceSOFT != nullptr ) {
// NULL device name selects the current default output; context and sources are preserved
if( alcReopenDeviceSOFT( m_device, nullptr, m_contextattributes ) == ALC_TRUE ) {
WriteLog( "sound: active audio device lost, reopened on the current default output" );
}
else {
ErrorLog( "sound: active audio device lost, reopening on the default output failed (retrying)" );
}
}
else {
ErrorLog( "sound: active audio device lost and ALC_SOFT_reopen_device is unavailable; cannot recover" );
}
}
}
}
// update listener
// gain
::alListenerf( AL_GAIN, Global.AudioVolume );
@@ -544,6 +576,7 @@ openal_renderer::init_caps() {
WriteLog( "Supported extensions: " + std::string{ (char *)::alcGetString( m_device, ALC_EXTENSIONS ) } );
ALCint attr[3] = { ALC_MONO_SOURCES, Global.audio_max_sources, 0 }; // request more sounds
std::copy( std::begin( attr ), std::end( attr ), std::begin( m_contextattributes ) ); // cached for device reopen
m_context = ::alcCreateContext( m_device, attr );
if( m_context == nullptr ) {
@@ -573,6 +606,12 @@ openal_renderer::init_caps() {
if (!alcDevicePauseSOFT || !alcDeviceResumeSOFT)
WriteLog("sound: warning: extension ALC_SOFT_pause_device not found");
m_candetectdisconnect = ( alcIsExtensionPresent( m_device, "ALC_EXT_disconnect" ) == ALC_TRUE );
if( alcIsExtensionPresent( m_device, "ALC_SOFT_reopen_device" ) == ALC_TRUE )
alcReopenDeviceSOFT = (ALCboolean(*)(ALCdevice*, ALCchar const*, ALCint const*))alcGetProcAddress( m_device, "alcReopenDeviceSOFT" );
if( !alcReopenDeviceSOFT )
WriteLog( "sound: warning: extension ALC_SOFT_reopen_device not found; audio output device changes won't be followed" );
return true;
}

View File

@@ -176,6 +176,12 @@ private:
void (*alProcessUpdatesSOFT)() = nullptr;
void (*alcDevicePauseSOFT)(ALCdevice*) = nullptr;
void (*alcDeviceResumeSOFT)(ALCdevice*) = nullptr;
// ALC_SOFT_reopen_device: move playback to the current default output when the active
// device disappears (e.g. headphones unplugged mid-game), keeping context and sources
ALCboolean (*alcReopenDeviceSOFT)(ALCdevice*, ALCchar const*, ALCint const*) = nullptr;
bool m_candetectdisconnect{ false }; // ALC_EXT_disconnect present, ALC_CONNECTED is queryable
ALCint m_contextattributes[3]{ 0, 0, 0 }; // cached context attribs, reused when reopening the device
double m_devicechecktime{ 0.0 }; // accumulates dt to throttle the ALC_CONNECTED poll to ~1 Hz
};
extern openal_renderer renderer;