thestk / rtaudio

A set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound, ASIO, and WASAPI) operating systems.
Other
1.49k stars 318 forks source link

Can't open `RtAudio` object from a different thread #374

Open Foaly opened 1 year ago

Foaly commented 1 year ago

Hello!

I think I found a bug in RtAudio, although I am not sure and it might just be undocumented behaviour.

If I instanciate a RtAudio object in one thread and then call openStream on that object from another thread, the connection to the sound card can not be established. I did not read read anywhere that this is not possible, so I am assuming it might be bug. The output I am getting with the following minimal example is:

RtApiAsio::getDeviceInfo: unable to load driver (ASIO MADIface USB).

RtApiAsio::getDeviceInfo: unable to load driver (Realtek ASIO).

RtApiAsio::probeDeviceOpen: unable to load driver (ASIO MADIface USB).

Does anybody have an idea how to fix this? Thank you some much in advance!

Minimal example:

#include <RtAudio.h>

#include <chrono>
#include <thread>

RtAudio audioOutputStream( RtAudio::WINDOWS_ASIO );

int audioCallback(void* /*outputBuffer*/, void* /*inputBuffer*/, unsigned int /*frameCount*/, double /*streamTime*/, RtAudioStreamStatus /*status*/, void* /*userData*/)
{
    return 0;
}

void threadFunction()
{
    constexpr std::size_t sampleRate = 48000;
    unsigned int bufferSize = 512;
    constexpr unsigned int channelCount = 2;
    constexpr RtAudioFormat format = RTAUDIO_FLOAT32;

    RtAudio::StreamParameters outputStreamParams;
    outputStreamParams.deviceId = 0;
    outputStreamParams.nChannels = channelCount;

    try
    {
        audioOutputStream.openStream(&outputStreamParams,
                                     nullptr,
                                     format,
                                     static_cast<unsigned int>(sampleRate),
                                     &bufferSize,
                                     audioCallback);
        audioOutputStream.startStream();
    }
    catch (RtAudioError& e)
    {
        e.printMessage();
    }
}

int main()
{
    using namespace std::chrono_literals;

    std::thread t (threadFunction);

    std::this_thread::sleep_for(3s);

    t.join();
}
garyscavone commented 1 year ago

I tried this on OS-X and it worked fine. I don't know if it may be an issue with the ASIO API or the configuration on your particular computer.

Foaly commented 1 year ago

Ah forgot to add I am not of macos but on windows 11.

sonoro1234 commented 1 year ago

Does it work from same thread?

Foaly commented 1 year ago

@sonoro1234 Yes it does indeed. If my main function in the minimal example above looks like the following everything works as expected and no errors are printed.

int main()
{
    using namespace std::chrono_literals;

    threadFunction();

    std::this_thread::sleep_for(3s);

    audioOutputStream.closeStream();
}
garyscavone commented 1 year ago

The first example worked for me in Mac OS-X but not in Windows ASIO. I see in the RtApiAsio constructor that it says: // ASIO cannot run on a multi-threaded apartment. You can call // CoInitialize beforehand, but it must be for apartment threading // (in which case, CoInitilialize will return S_FALSE here). So, this seems to be a limitation of the ASIO API.

Foaly commented 1 year ago

Alright, if it is a limitation of the windows ASIO API, then it would be great if you could add a description of the behaviour to the documentation. I couldn't find anything about it anywhere here: https://www.music.mcgill.ca/~gary/rtaudio/ That would be fantastic! Thank you so much for investigating :)