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

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.





  • 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 of QIODevice by calling m_audioOutput->start() and write to it when you get new audio data.
    In pull mode, you need to write a custom class which subclasses QIODevice, create an instance of it and use its pointer as the parameter of m_audioOutput->start(device).
    And in that subclassed QIODevice class, you need to reimplement readData(char *data, qint64 len), in which what you actually do is to write audio to the data pointer.
    So in both push and pull mode, you can just write the data from an QAudioBuffer.



  • 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 like

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


Log in to reply