QtMultimedia and spectrum analyzer
-
Hi,
I have a media player with spectrum analyzer visualization. I want to update it to Qt 6. In Qt 5 I was using QtAV to decode and play audio, and it also has support for reading of decoded audio data, which I can run through FFT and get the spectrum. Since QtAV is a dead project and most likely won't be updated to work with Qt 6, I need to switch to QtMultimedia.
But I don't know, how to convert my code. In Qt 5 there was QAudioProbe, which I could have used. But it is not available in Qt 6 anymore. I discovered there is a QAudioDecoder class. I can use it and get the decoded audio as QAudioBuffer. But what is next? How do I play the raw data in the buffer, and how can I get the current audio data for FFT processing? I was looking for some more documentation or examples, but I couldn't find anything useful.
Is my approach correct, or should I find a different solution? Where could I find some more information? Thank you.
-
@vlada The module underwent significant changes.
https://doc.qt.io/qt-6/qtmultimedia-multimedia-spectrum-example.html
-
Thank you for the answer. I tried to look at documentation and the example, unfortunately the example doesn't show how to play QAudioBuffer and use it for FFT analysis. I found some code examples on StackOverflow, but they were for Qt 5 and are not compatible with Qt 6.
It is true that QtMultimedia was changed significantly. And now I lack some more documentation and examples on how to use it. The spectrum example (which I have already reimplemented in my media player since Qt 5) shows how to play a QBuffer. I do believe the is some easy way on how to play a QAudioBuffer too. I could probably find some workarounds, but I don't want to reinvent a wheel.
This is from the example:
m_audioOutputIODevice.close(); m_audioOutputIODevice.setBuffer(&m_buffer); m_audioOutputIODevice.open(QIODevice::ReadOnly); m_audioOutput->start(&m_audioOutputIODevice);
The m_audioOutputIODevice is an instance of QBuffer and m_audioOutput is an instance of QAudioSink. How can I change this part of code to use QAudioBuffer instead of QBuffer? Thank you.
-
I'm not familiar with Qt6, but I think the audio output part doesn't change that much from Qt5.
You can learn how to play raw data from Audio Output Example.
There are two modes, push mode and pull mode.
In push mode, you get a pointer ofQIODevice
by callingm_audioOutput->start()
and write to it when you get new audio data.
In pull mode, you need to write a custom class which subclassesQIODevice
, create an instance of it and use its pointer as the parameter ofm_audioOutput->start(device)
.
And in that subclassedQIODevice
class, you need to reimplementreadData(char *data, qint64 len)
, in which what you actually do is to write audio to thedata
pointer.
So in both push and pull mode, you can just write the data from anQAudioBuffer
. -
Thank you, I got a little further by checking the Audio Output Example. I decided to try the pull mode by subclassing QIODevice.
I implemented the tone generator into my code, and it works now. But I still can't find the correct way for reading the data from QAudioBuffer. I tried this code:
const quint16 *data = m_audioBuffer.constData<quint16>(); *musicDataBuffer = musicDataBuffer->fromRawData((char *)data, m_audioBuffer.byteCount());
But it crashes at the second line. The object musicDataBuffer is of type QByteArray.
What is the correct way to access the data contained in QAudioBuffer and write them to a QByteArray buffer? Thank you.
P.S. I'm sorry for my stupid questions, but I'm not very familiar with pointers and low level programming in general.
-
@vlada I don't quite understand your code since I don't know what's that
QByteArray
for.
My imaginary code (not tested) should be something likeqint64 SomeIO::readData(char *data, qint64 len) { if(!mCurrentBuffer.isValid() || mBufferPos >= mCurrentBuffer.byteCount()) { //TODO: save a new QAudioBuffer to mCurrentBuffer mBufferPos = 0; } int byteToCopy = mCurrentBuffer.byteCount() - mBufferPos; if(byteToCopy > len) byteToCopy = len; memcpy(data, mCurrentBuffer.constData<char>() + mBufferPos, byteToCopy); mBufferPos += byteToCopy; return byteToCopy; }
This might not work. It's just my idea about how to use a
QAudioBuffer
.