Playing .WAV files using QBuffer, QAudioOutput and QAudioFormat: Segmentation Fault
-
Hi Qt Community,
I'm currently working on a Sound Manager for my Embedded System application and initially wrote the following code:
QFile* m_sourceFile; QAudioOutput* audio; 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; } }
The code successfully outputs sounds to my correct sound device. However, the quality isn't perfect; I hear a few pops and cracks that I do not hear when using the ALSA aplay command in terminal, so the quality isn't matching.
From my understanding, these imperfections are because I'm not handling a buffer. Upon some research, I modified my code to looks like this:
void SoundManager::playAudio() { m_audioFile = new QFile("/eclipse_qml/startup-sound.wav"); if(m_audioFile->open(QIODevice::ReadOnly)) { m_audioFile->seek(44); // skip wav header QByteArray audio_data = m_audioFile->readAll(); m_audioFile->close(); qDebug() << "Audio_Data ByteArray content: " << audio_data << Qt::endl; QBuffer* audio_buffer = new QBuffer(&audio_data); audio_buffer->open(QIODevice::ReadOnly); qDebug() << "Audio Buffer Size: " << audio_buffer->size(); 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; // Format support check if (!i.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 audio = new QAudioOutput(i, format, this); connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State))); audio->start(audio_buffer); qDebug() << "Sound Player Started" << Qt::endl; break; } } } } } void SoundManager::handleStateChanged(QAudio::State newState) { switch (newState) { case QAudio::IdleState: // Finished playing (no more data) qDebug() << "Finished playing, no more data" << Qt::endl; audio->stop(); qDebug() << "Sound Player Stopped" << Qt::endl; delete m_audioFile; delete audio; delete audio_buffer; qDebug() << "Freed data" << Qt::endl; break; case QAudio::StoppedState: if(audio->error() != QAudio::NoError){ // Error handling qDebug() << "Reached Stopped state: " << QString(audio->error()) << Qt::endl; } break; default: break; } }
This last code outputs the following:
Audio_Data ByteArray content: <Some Hex data followed by a TON of \0x00> Audio Buffer Size: 1310780 Attempting to play on device: "sysdefault:CARD=imx6qapalissgtl" Format supported Sound Player Started Segmentation fault
In this case, I do not hear anything, and the program seems to crash before the stateChanged signal is triggered.
It's my first time using QBuffer and QAudioOutput so I'm not too sure what triggered this issue, although I'm thinking it has to do with a memory leak I am not seeing.
Any help on this is appreciated.
Thanks in advance,
Anthony -
@Anthony-Abboud said in Playing .WAV files using QBuffer, QAudioOutput and QAudioFormat: Segmentation Fault:
QBuffer* audio_buffer = new QBuffer(&audio_data);
Please read the documentation of the QBuffer ctor you're using.
-
Hi @Christian-Ehrlicher ,
Thanks for the reply!
Looking at the documentation, I see that the QByteArray data was no longer valid when exiting the function, therefore creating a SegFault from the QBuffer.
The code now looks like the following:
QFile* m_audioFile; QAudioOutput* audio; QBuffer audio_buffer; QByteArray audio_data; void SoundManager::playAudio() { m_audioFile = new QFile("/eclipse_qml/startup-sound.wav"); if(m_audioFile->open(QIODevice::ReadOnly)) { // Build QByteArray m_audioFile->seek(44); // skip wav header audio_data = m_audioFile->readAll(); m_audioFile->close(); // Set QBuffer with data audio_buffer.setBuffer(&audio_data); audio_buffer.open(QIODevice::ReadOnly); qDebug() << "Audio Buffer Size: " << audio_buffer.size(); 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; // Format support check if (!i.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 audio = new QAudioOutput(i, format, this); connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State))); audio->start(&audio_buffer); qDebug() << "Sound Player Started" << Qt::endl; break; } } } } } void SoundManager::handleStateChanged(QAudio::State newState) { switch (newState) { case QAudio::IdleState: // Finished playing (no more data) qDebug() << "Finished playing, no more data" << Qt::endl; audio->stop(); qDebug() << "Sound Player Stopped" << Qt::endl; delete m_audioFile; delete audio; qDebug() << "Freed data" << Qt::endl; break; case QAudio::StoppedState: if(audio->error() != QAudio::NoError){ // Error handling qDebug() << "Reached Stopped state: " << QString(audio->error()) << Qt::endl; } break; default: break; } }
And the sound now outputs. However, I'm still hearing a few pops in the audio file, and the outputs shows the following lines upon playing the sound:
Audio Buffer Size: 1310780 Attempting to play on device: "sysdefault:CARD=imx6qapalissgtl" Format supported Sound Player Started ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred Finished playing, no more data Sound Player Stopped Freed data
-
And the setBuffer() function you use has exact the same problem.
-