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.
-
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.
@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 thenm_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 achar*
pointer to its byte array (e.g. via itsconstData()
ordata()
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).
-
@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 thenm_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 achar*
pointer to its byte array (e.g. via itsconstData()
ordata()
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).
@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 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? -
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 thenm_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 achar*
pointer to its byte array (e.g. via itsconstData()
ordata()
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).
@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 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 getIdleState
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.
-
@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 : how did you solve the underrun issue ? i get it on the embedded side, on PC there is no underruns ?