Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Call for Presentations - Qt World Summit

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

    General and Desktop
    4
    9
    1847
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • F
      fpartl last edited by fpartl

      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.

      jsulm 1 Reply Last reply Reply Quote 0
      • jsulm
        jsulm Lifetime Qt Champion @fpartl last edited by

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

        foreach (sample i, samples) {

        What type is sample?

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        F 1 Reply Last reply Reply Quote 1
        • Diracsbracket
          Diracsbracket last edited by Diracsbracket

          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).

          F 2 Replies Last reply Reply Quote 1
          • F
            fpartl @jsulm last edited by

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

            1 Reply Last reply Reply Quote 0
            • F
              fpartl @Diracsbracket last edited by

              @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.

              Diracsbracket 1 Reply Last reply Reply Quote 0
              • Diracsbracket
                Diracsbracket @fpartl last edited by Diracsbracket

                @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?

                1 Reply Last reply Reply Quote 0
                • F
                  fpartl @Diracsbracket last edited by fpartl

                  @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);
                  
                  Diracsbracket S 2 Replies Last reply Reply Quote 0
                  • Diracsbracket
                    Diracsbracket @fpartl last edited by Diracsbracket

                    @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.

                    1 Reply Last reply Reply Quote 0
                    • S
                      SherifOmran @fpartl last edited by

                      @fpartl : how did you solve the underrun issue ? i get it on the embedded side, on PC there is no underruns ?

                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post