Skip to content

🔬Research how to handle auto device change when device is lost #344

@CalvinWilkinson

Description

@CalvinWilkinson

Complete The Item Below

  • I have updated the title without removing the 🔬 emoji.

What To Research

Perform some research around how we can handle when an audio device has been disconnected and a new device needs to be picked up.

Checking if a device is connected is an OpenAL extension. The name of the extension is ALC_EXT_disconnect and getting the list of extensions for an audio device done by using the this.alInvoker.GetString(this.device, AlcGetString.Extensions) call.

Not all devices support the same extensions and some support extensions that others do not. This is of course all done at the device level.

Possible Solution 1 (With Extension Support):
The idea here is to first see if the device supports the ALC_EXT_disconnect extension. If it does, then we can use the ALC API to be able to check if the device is or is not connected.

The ALC OpenAL function to do this would be alcGetIntegerv and is documented in the OpenAL guide.

This would require adding a new function API to the ALC class for function interop. The delegate would be called ALCGetInteger and the delegate pointer creation would be delegateFactory.CreateDelegate<ALCGetInteger>(libraryPointer, "alcGetIntegerv").

The function signature would be:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void ALCGetInteger(ALDevice device, AlcGetInteger param, int size, ref int data);`

This function could then be used to find out if the device is connected. This would be done like:

public bool IsDeviceConnected(ALDevice device)
{
    var result = this.alc.GetInteger(device, AlcGetInteger.Connected, 1);

    return result > 0;
}

Possible Solution 2 (Without Extension Support):

The only thing we could do in this scenario is get the list of devices in the system at a particular interval and then inspect the list to see what has been removed so we can know if the current device in use was disconnected. One problem with this is that the function returns an array of strings. This would cause GC collections in scenarios where it is not ideal, such as a game engine. Research would have to be done to find a way to greatly reduce or remove the GC collections. We will be limited by the OpenAL API with this.

One possible way is to reuse the class array that holds all of the device names and overwrite the items with the new ones without creating a temporary array, which would cause an allocation.


Possible useful way to watch for device changes

This could be injected into the device manager and used to watch for device changes, and then react appropriately.
This implementation would work only for devices that support the disconnection extension but could be adapted to solution 2 mentioned above.

internal class DeviceWatcher : IDeviceWatcher
{
    private readonly CancellationTokenSource tokenSrc = new ();
    private readonly IOpenALInvoker alInvoker;
    private Task internalTask;
    private bool isDisposed;
    private ALDevice device;

    public DeviceWatcher(IOpenALInvoker alInvoker)
    {
        ArgumentNullException.ThrowIfNull(alInvoker);
        this.alInvoker = alInvoker;

        this.internalTask = new Task(CheckDevice, this.tokenSrc.Token);
    }

    public event Func<ALDevice>? DeviceDisconnected;

    public void StartWatchingDevice(ALDevice device)
    {
        this.device = device;
        this.internalTask.Start();
    }

    public void StopWatchingDevice() => this.tokenSrc.Cancel();

    private void CheckDevice()
    {
        while (!this.tokenSrc.IsCancellationRequested)
        {
            Thread.Sleep(100);

            if (!this.alInvoker.IsDeviceConnected(this.device))
            {
                if (this.DeviceDisconnected is not null)
                {
                    this.device = this.DeviceDisconnected.Invoke();
                }
            }
        }
    }
}

Research Results

No response

Acceptance Criteria

### The items to complete to satisfy the Definition of Done.
- [ ] Research complete and issues created _(if needed)_.
- [ ] If any issues were created, they have been added to the _**Related Work**_ section below.

ToDo Items

### The items to complete to satisfy the Definition of Done.
- [ ] Priority label added to this issue.  Refer to the _**Priority Type Labels**_ section below.

Issue Dependencies

No response

Related Work

No response

Additional Information:

Priority Type Labels

Priority Type Label
Low Priority low priority
Medium Priority medium priority
High Priority high priority

Code of Conduct

  • I agree to follow this project's Code of Conduct.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status

    ⚪Not Set

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions