[openal] Device removing, moving, and invalidation
Chris Robinson
chris.kcat at gmail.com
Sun Mar 20 20:08:33 EDT 2016
Hello.
I'm looking to get some feedback and advice for an extension to handle
the aforementioned device events. The playback device being removed,
playback being moved to a different device than the one that was
originally opened, and the playback device being invalidated.
Currently, OpenAL has the ALC_EXT_disconnect extension that defines the
behavior, and a query, for a device being removed/disconnected. I
remember discussion had touched upon the idea of automatically switching
to another device before being marked as disconnected, but at the time
it was decided to let it be implementation-defined about how to handle
devices being lost, with the disconnect state simply being the point
when playback/capture was unrecoverable.
Moving devices in a more general capacity has some conceptual
difficulties that I'd like to tackle, but I'm looking for ideas. The
problems as I see them are:
1) The device specifier can change. It's one thing if OpenAL reports a
device as simply "PulseAudio" or something, in which case it can more
freely move between other PulseAudio devices and keep the same name.
However, if it reports the real name, like "Built-in Audio Analog
Surround 5.1", it couldn't then decide to switch to "GK107 HDMI Audio
Controller Digital Stereo (HDMI)" without some issues. In particular,
the pointer returned by previous string queries for the device's
ALC_ALL_DEVICES_SPECIFIER may no longer be valid if the name string was
allocated dynamically, or the supposedly const string would change even
if the pointer remained the same. So there needs to be some rules for
when (or if?) the device specifier can change that won't break existing
apps. Would querying a device's ALC_ALL_DEVICES_SPECIFIER be a safe
place to change the reported name, invalidating old pointers, similar to
changing the enumerated list? I'm not sure.
2) The playback format may change. Not as big of a deal since the app
doesn't deal with the channel configuration or bit-depth in the first
place, but it can have an effect on the sample rate, refresh rate, and
HRTF status, which I think the app should be able to assume will be
relatively constant between resets (either via alcCreateContext or
alcResetDeviceSOFT).
3) Some moves may be reactive, and others proactive. For instance, if
the app opens the default AL device with alcOpenDevice(NULL), and the
user then plugs in another system device that takes over as (or
otherwise changes) the default, the app could receive a message that the
default device was changed and could decide to move the AL device over
to it on their own time. In another example, a user could use an
external app to force OpenAL output to play on a different system
device, in which case the AL device is moved and the app merely gets a
notification after the fact. Should requested moves for default devices
automatically happen, or should the app receive some kind of change
request for ones that aren't forced?
In the same vein, on some systems it's possible for the system device to
be invalidated such that OpenAL can't keep sending audio to it as-is,
and needs to reset it to continue. For example I have a report that
switching a video device to fullscreen on Windows causes an HDMI audio
device to either temporarily unplug or reconfigure. OpenAL Soft sees
this error and reports that device was disconnected because it's not
accepting audio anymore. Currently, the disconnect extension specifies
that once a device is disconnected, it can't become connected again and
the app needs to close it down and open a device again. Transparently
resetting the device, before marking it disconnected, could be difficult
since it could be changing app-visible attributes.
My thinking is we could add a few new functions and queries.
ALCboolean alcReopenDevice(ALCdevice *device, const ALCchar *name);
This would allow moving a pre-existing device to another named device.
The ALCdevice handle itself remains valid, as does all contexts and
other AL objects and state. It's simply moving output over to the new
device, and may cause the device specifier, playback format/metrics, etc
to change. This way also allows OpenAL to cleanly fail if it can't be
moved (e.g. a hardware implementation may allow moving between outputs
on the same device, but moving may fail when switching between devices
or drivers that don't share state).
For trying to reconnect a lost device, we could have
ALCboolean alcResetDevice(ALCdevice *device, const ALCint *attribs);
OpenAL Soft basically has this already (alcResetDeviceSOFT), but this
additional behavior would allow a disconnected device to attempt to
reconnect to the output it was using. It may also specify attributes
like you'd specify for context creation, updating things like the
playback frequency, max source sends, etc.
In addition, there could be integer queries that return a queue of
events that have happened, either on a specific device or the audio
system in general. So calling alcGetIntegerv with
ALC_STATUS_UPDATE_SIZE
would return the number of status updates queued, which can be anywhere
between 0 and whatever. With
ALC_STATUS_UPDATE
it would pop off and return the given number of queued items, which you
can then respond to. So for example
ALCint count;
alcGetIntegerv(device, ALC_STATUS_UPDATE_SIZE, 1, &count);
for(int i = 0;i < count;i++)
{
ALCint status;
alcGetIntegerv(device, ALC_STATUS_UPDATE, 1, &status);
if(status == ALC_DISCONNECTED)
{
// Disconnected (ALC_CONNECTED would be false too), try to
// reconnect
if(!alcResetDevice(device, NULL))
{
// Reconnect failed, try to switch to the default
alcReopenDevice(device, NULL);
}
}
else if(status == ALC_OUTPUT_MOVED)
{
// We've been moved to a different output.
const ALCchar *newname = alcGetString(status,
ALC_ALL_DEVICES_SPECIFIER
);
...
}
}
or alternatively, you can get them all at once:
ALCint status[count];
alcGetIntegerv(device, ALC_STATUS_UPDATE, count, status);
But this idea could be expanded upon in the future, like to add status
updates for device suspension/resume (which don't exactly disconnect the
device, but do stop updates for a time).
So I'm curious about your thoughts. Do you see any problems with these
ideas, do you have alternative ideas, or ways to improve them? Any and
all feedback is appreciated. Thanks!
More information about the openal
mailing list