Audio output with TorizonCore

I’m trying to get headphone audio output working and i’ve tried following this guide. It seems like TorizonCore does not comes with ALSA utilities installed. Currently I’m testing my qt application in a docker container. What is the preferable way to get headphone audio output working with Qt inside docker? As i understood, I need to have Pulseaudio running to get Qt audio output.

Thank you.

Hardware
Apalis iMX8QM 4GB WB IT v1.1 B
Ixora V1.1A
with TorizonCore 5.7

Greetings @imeshsps,

You probably want to look at this article here: How to play audio on TorizonCore using Alsa and C/C++ | Toradex Developer Center

The article uses our extension as a example, but the idea is more or less the same. You’ll want to install whatever audio related packages/libraries you need in the container alongside your Qt application.

Best Regards,
Jeremias

Hey @jeremias.tx,

Thanks for your response. I have ALSA and pulseaudio installed in my docker container but as I understood, we need to have a pulseaudio server running on host and we can connect to that from container over a socket which I think we don’t have in torizoncore. So is the idea here is to have pulseaudio server on docker container?

Do you strictly need PulseAudio? You just need a single audio plugin of some kind correct? The example I linked we used ALSA instead of PulseAudio since it’s simpler to integrate and doesn’t require a whole daemon and server.

Researching a bit it seems Qt should be fine just using ALSA as the audio plugin: QAudioSystemPlugin Class | Qt Multimedia 5.15.12

Otherwise, if you strictly require PulseAudio on the host, you’d need to rebuild the entire TorizonCore OS image and customize it to add PulseAudio.

Best Regards,
Jeremias

Hi @imeshsps,

Have you managed to find a workaround to solve the lack of PulseAudio?

I’m also encountering this issue where my QSound, QSoundEffect and QMediaPlayer sounds do not output since, by default, they rely on a PulseAudio server running instead of directly through ALSA.

Currently looking into QAudioSystemPlugin as suggested by @jeremias.tx

Best,
Anthony

Just to be clear you can try to utilize PulseAudio though we don’t have any examples or articles on this. Also doing some quick research it seems trying to utilize PulseAudio in a container is quite a bit more complex than just using ALSA. Most methods I’ve seen described online also still require a PulseAudio server running on the host outside of the container. Which goes back to my comment about having to re-build TorizonCore to include PulseAudio.

This was my reasoning for suggesting ALSA. Especially since we already have documented examples of using ALSA in a container. Though I say this without having too deep an understanding of Qt and how it handles audio.

Best Regards,
Jeremias

1 Like

hey @anthonyabboud ,
I checked the Qt config summery as I cross compiled Qt build and I noticed that Low level Audio backend for ALSA was not included in the build. I’m going to give it a try again and give you an update.

1 Like

Good morning @imeshsps and @jeremias.tx ,

I’ve attempted playing around with other Qt classes and seem to have something almost working.

Instead of using the primitive classes, such as QSound, QSoundEffect and QMediaPlayer, I went on the use QAudioFormat, QAudioOutput and QAudioDeviceInfo.

See below my code snippet of the class I drafted up real quick:

SoundManager.h

#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H

#include <QObject>
#include <QAudioOutput>
#include <QAudioFormat>
#include <QFile>

class SoundManager : public QObject
{
    Q_OBJECT
public:   
    static SoundManager* Instance();
    void playAudio();
    
public slots:
    void handleStateChanged(QAudio::State newState);

private:
    explicit SoundManager(QObject *parent = nullptr);
    static SoundManager* m_instance;
    QFile* m_sourceFile;
    QAudioOutput* audio;
};

#endif // SOUNDMANAGER_H

SoundManager.cpp

#include "soundmanager.h"
#include <QDebug>

SoundManager* SoundManager::m_instance = nullptr;

SoundManager::SoundManager(QObject *parent)
    : QObject{parent}
{

}

SoundManager* SoundManager::Instance()
{
    if(!m_instance){
        m_instance = new SoundManager;
    }
    return m_instance;
}

void SoundManager::playAudio()
{
    QAudioFormat format;
    // Set up the format, eg.
    format.setSampleRate(44100);
    format.setChannelCount(2);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);
    
    // Fetch Audio Device List
    QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
    foreach (QAudioDeviceInfo i, devices){
        // Find desired Audio device
        if(i.deviceName() == "sysdefault:CARD=imx6qapalissgtl")
        {

            qDebug() << "Attempting to play on device: " << i.deviceName() << Qt::endl;
            QAudioDeviceInfo info(i);
            // Print out supported format by device
            qDebug() << "Supported Byte Orders: " << info.supportedByteOrders() 
            << "Supported Channel Counts: " << info.supportedChannelCounts() 
            << "Supported Codes: " << info.supportedCodecs()
            << "Supported Sample Rates: " << info.supportedSampleRates() 
            << "Supported Sample Sizes: " << info.supportedSampleSizes()
            << "Supported Sample Types: " << info.supportedSampleTypes()
            << Qt::endl;

            // Format support check
            if (!info.isFormatSupported(format)) {
                qDebug() << "Raw audio format not supported by backend, cannot play audio." << Qt::endl;
                return;
            }
            else{
                qDebug() << "Format supported" << Qt::endl;
                // Load source file and play audio
                m_sourceFile = new QFile("/eclipse_qml/startup-sound.wav");
                audio = new QAudioOutput(i, format, this);
                connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
                m_sourceFile->open(QIODevice::ReadOnly);
                audio->start(m_sourceFile);
                break;
            }
        }
    }
}

void SoundManager::handleStateChanged(QAudio::State newState)
{
    switch (newState) {
        case QAudio::IdleState:
            // Finished playing (no more data)
            audio->stop();
            m_sourceFile->close();
            delete audio;
            break;

        case QAudio::StoppedState:
            if(audio->error() != QAudio::NoError){
                // Error handling
                qDebug() << "Reached Stopped state: " << QString(audio->error()) << Qt::endl;
            }
            break;

        default:       
            break;
    }
}

To figure out the parameters set in QAudioFormat format, I executed the ALSA command line aplay to print out the information and matched them in the code.

Then I iterated through all of my audio devices until I reach the one we’re looking for, in our case it’s sysdefault:CARD=imx6qapalissgtl as @jeremias.tx showed me in one of my previous topic: TorizonCore Setting Audio Levels: No Simple Control detected by Amixer - Technical Support - Toradex Community. For code optimization, I’ll probably move up this device detection on application startup to avoid doing that everytime I want to play a sound.

From there I can simply call SoundManager::Instance()->playAudio(string audioFileName) from anywhere in the code.

I’ve tested with my app startup sound. It works, but there’s a very small amount of pops and cracks I’m hearing, so the quality isn’t as great as playing on command line. I’ll check around on Qt Forums to see if there’s a way to optimize sound quality.

Let me know if you found another method.

Thanks,
Anthony

Glad you were able to come up with something for Qt.

Turns out you have to add -DQT_FEATURE_alsa=ON to make experimental feature ON when you build Qt 6.5 from source.

Interesting that it’s an experimental feature in Qt. Well thank you for coming back and sharing your findings here!