I can't get JUCE to connect to ALSA and PulseAudio

I have an application where I use JUCE for the audio and MIDI and Flutter for the UI.

I am using:

  • Ubuntu 20.04 focal
  • JUCE 6.0.8
  • Flutter 3.13.8
  • Cmake 3.16.3

I have been checking the AudioSettingsDemo as a reference. So far the application can open the audio devices without a problem in other targets: windows, mac and android but not in linux using JUCE_ALSA.

The dart API launches the message thread as follows:

    LinuxMessageThread::getInstance();
    MessageManager::callAsync([] {
        userActionHandler = new UserActionHandler();
        setup = true;
    });

Which is the same thing I have for windows and it works.

The LinuxMessageThread class looks like this:

struct LinuxMessageThread : public Thread {
    LinuxMessageThread() : Thread("LinuxMessageThread") {
        startThread(7);
        juce::Logger::writeToLog("LinuxMessageThread started");
        while (!initialised)
            sleep(1);
    }

    ~LinuxMessageThread() {
        signalThreadShouldExit();
        JUCEApplicationBase::quit();
        waitForThreadToExit(5000);
        clearSingletonInstance();
    }

    void run() override {
        initialiseJuce_GUI();
        initialised = true;

        MessageManager::getInstance()->setCurrentThreadAsMessageThread();

        while ((!threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil(250)) {
        }
    }

    JUCE_DECLARE_SINGLETON(LinuxMessageThread, false)

    bool initialised = false;
};

JUCE_IMPLEMENT_SINGLETON(LinuxMessageThread)

The init code is as follows:

UserActionHandler::UserActionHandler() {
[...]
    audioDeviceManager = std::make_unique<AudioDeviceManager>();
    String ret_msg = audioDeviceManager->initialise(0, 2, nullptr, true, {}, nullptr);
    Logger::writeToLog("Audio init return message: " + ret_msg);
[...]
}

With linux unfortunately I cannot start the audio with ALSA, it does work with Jack, but I want to have the control over the audio device within my application, like with the rest of the targets.

Setting JUCE_ALSA_LOGGING 1 I get:

AudioSettingsDemo:

ALSA: getDeviceProperties(default)
ALSA: getDeviceNumChannels: 1 32
ALSA: getDeviceProperties(default)
ALSA: getDeviceNumChannels: 1 32
[ALSA] Audio device not open
ALSA: snd_pcm_open (default, forInput=1)
ALSA: ALSADevice::setParameters(default, 44100, 2, 512)
ALSA: format: bitDepth=32, isFloat=1, isLittleEndian=1, numChannels=2
ALSA: frames: 512, periods: 4, samplesPerPeriod: 512

and my application:


ALSA: getDeviceProperties(default)

ALSA: getDeviceNumChannels: 0 0

Audio init return message: no channels

I have checked the system trace and for the AudioSettingsDemo looks like this:

openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-13.99.so", O_RDONLY|O_CLOEXEC) = 6
memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 8
openat(AT_FDCWD, "/etc/alsa/conf.d/50-pulseaudio.conf", O_RDONLY) = 6
memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 8
memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 10
memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 10
memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 10
[pid 20515] memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 17
[pid 20515] write(2, "Current audio device: \"Playback/"..., 78Current audio device: "Playback/recording through the PulseAudio sound server") = 78
[pid 20515] memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 10
[pid 20515] memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 17
[pid 20515] write(2, "Current audio device: \"Playback/"..., 78 <unfinished ...>
Current audio device: "Playback/recording through the PulseAudio sound server"[pid 20546] <... write resumed>)        = 1

Where the pulseaudio lib is loaded and you can see the changes I made through the demo but for my application it looks like this:

openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-13.99.so", O_RDONLY|O_CLOEXEC) = 3
[pid 21027] openat(AT_FDCWD, "/etc/alsa/conf.d/50-pulseaudio.conf", O_RDONLY) = 28
[pid 21027] memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 30
[pid 21027] openat(AT_FDCWD, "/usr/share/locale/en_US.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale/en_US.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale/en_US/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale/en.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale/en.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale-langpack/en_US.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale-langpack/en_US.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale-langpack/en_US/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale-langpack/en.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale-langpack/en.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/usr/share/locale-langpack/en/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 21027] openat(AT_FDCWD, "/etc/alsa/conf.d/50-pulseaudio.conf", O_RDONLY) = 28
[pid 21027] memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 30
[pid 21027] memfd_create("pulseaudio", MFD_ALLOW_SEALING) = 32

Both strace outputs are generated within the same terminal, so I don’t really understand the locale problem.
I have even regenerated the locales for “en_US.UTF-8” and reinstalled pulseaudio but with no avail.

I am pretty new to JUCE so please bear with me. What am I missing?
If the AudioSettingsDemo can get the audio to work it may not be a JUCE problem but how Dart sets JUCE up.

If I force the channel numbres in the getDeviceNumChannels func inside juce_linux_ALSA as:

static void getDeviceNumChannels (snd_pcm_t* handle, unsigned int* minChans, unsigned int* maxChans)
{
    snd_pcm_hw_params_t *params;
    snd_pcm_hw_params_alloca (&params);

    if (snd_pcm_hw_params_any (handle, params) >= 0)
    {
        snd_pcm_hw_params_get_channels_min (params, minChans);
        snd_pcm_hw_params_get_channels_max (params, maxChans);

        JUCE_ALSA_LOG ("getDeviceNumChannels: " << (int) *minChans << " " << (int) *maxChans);

        // some virtual devices (dmix for example) report 10000 channels , we have to clamp these values
        *maxChans = jmin (*maxChans, 256u);
        *minChans = jmin (*minChans, *maxChans);
        // FIXME: this is just a debugging hack
        if (*maxChans == 0 && *minChans == 0) {
            JUCE_ALSA_LOG("Channel hack, forced min: 1 max: 32");
            *maxChans = 1;
            *minChans = 32;
        } else {
            JUCE_ALSA_LOG("No channel hack needed");
        }
    }
    else
    {
        JUCE_ALSA_LOG ("getDeviceNumChannels failed");
    }
}

Then the device can be opened and the alsa logs look like this:

ALSA: getDeviceProperties(default)
ALSA: Test output OK
ALSA: getDeviceNumChannels: 0 0
ALSA: Channel hack, forced min: 1 max: 32
ALSA: snd_pcm_open (default, forInput=0)
ALSA: ALSADevice::setParameters(default, 44100, 1, 512)
ALSA: format: bitDepth=32, isFloat=1, isLittleEndian=1, numChannels=1
ALSA: frames: 0, periods: 0, samplesPerPeriod: 512

I get to reproduce the samples although they are chopped, extremely delayed

why does:

        snd_pcm_hw_params_get_channels_min (params, minChans);
        snd_pcm_hw_params_get_channels_max (params, maxChans);

work well for the JUCE example but not in my application?