Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QAudioOutput makes huge amount of noise and Underrun error...



  • Hi all,
    I am trying to play audio samples stored in QVector. I have created a Player class witch using QAudioOutput and QBuffer to play it. When I start playing audio there are no sound at all. I found on stackoverlow.com I have to call seek after write to my buffer. When I have added this line of code, my program becomes pretty noisy and ends with UnderrunError. Input raw data in QVector is ok - I have checked it with audacity. Can you please tell me if I am completely wrong about how this thiunstableng works or I am doing some stupid mistake.
    Here is my header file:

    #ifndef AUDIOPLAYER_H
    #define AUDIOPLAYER_H
    
    #include <QObject>
    #include <QVector>
    #include <QAudioOutput>
    #include <QByteArray>
    #include <QBuffer>
    #include <QDebug>
    #include <QDataStream>
    
    #include "config.h"
    
    class AudioPlayer : public QObject {
        Q_OBJECT
    
    public:
        explicit AudioPlayer(int channelCount, QString codec, int sampleRate, QObject *parent = nullptr);
        void play(QVector<sample> samples);
    
    private:
        int m_sampleRate;
        QString m_codec;
        int m_channelCount;
        QAudioOutput *m_output;
        QBuffer *m_test;
    
        void initAudio();
        QAudioFormat initOutputFormat();
    
    private slots:
        void stateChaned(QAudio::State newState);
    };
    
    #endif
    
    

    And here is cpp file:

    #include "audioplayer.h"
    
    AudioPlayer::AudioPlayer(int channelCount, QString codec, int sampleRate, QObject *parent) : QObject(parent) {
        m_channelCount = channelCount; // 1
        m_codec = codec;               // "audio/pcm"
        m_sampleRate = sampleRate;     // 44100 Hz
    
        initAudio();
    }
    
    void AudioPlayer::play(QVector<sample> samples) {
        m_test = new QBuffer(this);
        m_test->open(QIODevice::ReadWrite);
    
        QDataStream writeStream(m_test);
        foreach (sample i, samples) {
            writeStream << i;
        }
    
        m_test->seek(0); // with this line program produce huge amount of noise and underrun error...
    
        m_output->start(m_test);
    }
    
    void AudioPlayer::initAudio() {
        QAudioDeviceInfo device = QAudioDeviceInfo::defaultOutputDevice();
        qDebug() << device.deviceName();
    
        QAudioFormat format = initOutputFormat();
    
        if (!device.isFormatSupported(format)) {
            qWarning() << "Default format not supported -> trying to use nearest."; // I have never saw this text in debug console...
            format = device.nearestFormat(format);
        }
    
        m_output = new QAudioOutput(device, format, this);
        connect(m_output, &QAudioOutput::stateChanged, this, &AudioPlayer::stateChaned);
    }
    
    QAudioFormat AudioPlayer::initOutputFormat() {
        QAudioFormat formatAudio;
    
        formatAudio.setSampleRate(m_sampleRate);
        formatAudio.setChannelCount(m_channelCount);
        formatAudio.setSampleSize(sizeof(sample) * 8); // (typedef short sample;)
        formatAudio.setCodec(m_codec);
        formatAudio.setByteOrder(QAudioFormat::LittleEndian);
        formatAudio.setSampleType(QAudioFormat::SignedInt);
    
        return formatAudio;
    }
    
    void AudioPlayer::stateChaned(QAudio::State newState) {
        qDebug() << newState;
        qDebug() << m_output->error();
    
        if (newState == QAudio::ActiveState && m_output->error() == QAudio::NoError) {
            qDebug() << "It is running!";
        }
    
        if (newState == QAudio::IdleState) {
            qDebug() << "Finished...";
            delete m_test;
        }
    }
    
    

    Thank you for help.


  • Qt Champions 2019

    @fpartl said in QAudioOutput makes huge amount of noise and Underrun error...:

    foreach (sample i, samples) {

    What type is sample?



  • Hi
    Why don't you put all your samples in a QByteArray, which can then be directly passed to the device (via a buffer)? Writing the samples one by one may be too slow for some reason, and would result in the underruns you experience.

    For example,

    QBuffer             m_buffer;
    QByteArray*         m_data;  
    QAudioOutput*       m_audioOutput
    

    Fill the QByteArray in the expected format (byte order, bytes per sample, number format, number of channels) and then

    m_buffer.setBuffer(m_data);
    m_buffer.open(QIODevice::ReadOnly);
    m_audioOutput->start(&m_buffer)
    

    If you really need the QVector for some reason, you can get a char* pointer to its byte array (e.g. via its constData() or data() methods) and then use that to get a QByteArray:
    QByteArray::fromRawData(const char*, int size)

    As for the noise problems, maybe the sample format you provide does not match the device's expected format (e.g. signed vs. unsigned integers, byte ordering or bytes/samples).



  • @jsulm Sample is short. There is a typedef in cpp in comment.



  • @Diracsbracket How it can be slow if I write all samples before even output is started? The same QAudioFormat I am using to capture audio using QAudioInput. Thank you for help I will try solution with QByteArray.



  • @fpartl said in QAudioOutput makes huge amount of noise and Underrun error...:

    How it can be slow if I write all samples before even output is started

    Yes, you're right. I didn't see the start() at the end :0
    By the way, do the Qt Examples using QAudioOutput work?



  • @Diracsbracket It is working now! With you idea about QByteArray. Thank you for that! The only weird thing is the UnderrunError is still appears but on the end of the play. Program works... so I will not deal with this error. Thank you again. :-)

    here is my code:

        QAudioOutput *m_output;
        QByteArray m_data;
        QBuffer m_buffer;
    

    and cpp file is:

        m_data = QByteArray::fromRawData((const char *)samples.constData(), samples.size() * sizeof(sample));
    
        m_buffer.setBuffer(&m_data);
        m_buffer.open(QIODevice::ReadOnly);
    
        m_output->start(&m_buffer);
    


  • @fpartl said in QAudioOutput makes huge amount of noise and Underrun error...:

    he only weird thing is the UnderrunError is still appears

    I'm glad it works!
    I've not seen underruns at the end of playback, but I may get IdleState notifications at the end.

    CHECKED_CONNECT(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(notifySetOutputState(QAudio::State)));
    

    In my StateChanged handler, I do something like this:

    void AudioEngine::notifySetOutputState(QAudio::State state)
    {
        ...
        if (state == QAudio::IdleState)
              m_audioOutput->stop();
       ...
    }
    

    But I don't know if that is needed at all, nor if it would help avoid the underrun messages in your case.


Log in to reply