c++audioopenal

OpenAL: How to reuse audio buffer created on other OpenAL's device?


According to OpenAL programming guide:

Device and Context are keywords in OpenAL. Device is not a sound card in OpenAL's terminology but a single output for example a bluetooth headset.

Currently in my application I load multiple audio files at the beginning and play them from the buffer when I need them. It is the most optimal way to do it because I don't need to load the file the moment I need to play it.

Now all the things get complicated when I change the audio device (audio output) on the fly. This happens when the user connects the Bluetooth headset. Because OpenAL device is connected with the output sink I have to close the old device and open the current Bluetooth output sink. When I do that all the buffers loaded in the initialization are no more valid, they can not be played. It looks like that I have to reload all the application sound when the device is changed. This looks very non optimal to do. Of course I can store only the audio buffers and during the device switch just initialize the OpenAL's internal buffers but still that looks ugly to do.

What is the best way to do this?


Solution

  • There are two ways to solve this except writing own code which would reload all the buffers on the new device.

    Use reopen extension

    This problem has been already solved using ALC_SOFT_reopen_device extension. It was introduced around 2021.03 and finalized in version 1.20. That is how it works:

    int reopenDevice(const char* deviceName)
    {
        auto ctx = alcGetCurrentContext();
        auto device = alcGetContextsDevice(ctx);
        if(nullptr == device)
        {
            return -2;
        }
    
        if(alcIsExtensionPresent(device, "ALC_SOFT_reopen_device"))
        {
            ALCboolean (ALC_APIENTRY*alcReopenDeviceSOFT)(ALCdevice *device, const ALCchar *name, const ALCint *attribs);
            alcReopenDeviceSOFT = reinterpret_cast<ALCboolean (ALC_APIENTRY*)(ALCdevice *device, const ALCchar *name, const ALCint *attribs)>(alcGetProcAddress(device, "alcReopenDeviceSOFT"));
    
            if(alcReopenDeviceSOFT(device, deviceName, NULL))
            {
                return 0;
            }
    
            return -3;
        }
    
        return -1;
    }
    

    It will open a new device and ensure that everything is working as in the old one.

    Use OSS backend

    Starting from v1.20 the backend was switched to OSS. With the new backend the OpenAL's device is always pointed to /dev/dsp (in Linux) and changing the output sink does not change the OpenAL's device. So it was a matter of upgrading to minimum v1.20 to fix this and installing the osspd package for Linux.