Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How can I get real-time level data while playing audio using QAudioSink?
Forum Updated to NodeBB v4.3 + New Features

How can I get real-time level data while playing audio using QAudioSink?

Scheduled Pinned Locked Moved Solved General and Desktop
7 Posts 3 Posters 706 Views
  • 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.
  • V Offline
    V Offline
    VentureLee
    wrote on last edited by
    #1

    I'm currently migrating a project from Qt 5.14.2 to Qt 6.8.0. I've run into an issue: when using QAudioSink to replace QAudioOutput, I found that QAudioSink doesn't have a notify() signal. This means I can't get real-time data from QAudioSink like I did with QAudioOutput in my original code, as shown below. What's the best way to achieve this in Qt 6.8.0?

    Original code was like:

    connect(audioOutput, &QAudioOutput::notify, this, [this, memoryDevice]() {
        // Get the duration of processed audio data
        qint64 processedUSecs = audioOutput->processedUSecs();
    
        // Calculate the current position of the playing audio data in memoryDevice
        qint64 currentPosition = (processedUSecs * format.sampleRate() * format.channelCount() * format.sampleSize()) / (1000000 * 8);
    
        // Read audio data at the corresponding position
        memoryDevice->seek(currentPosition);
        QByteArray audioData = memoryDevice->read(1024); // Read 1024 bytes of audio data
    
        // Calculate audio level
        qreal level = calculateAudioLevel(audioData);
        // Normalize dB value to the range of 0-100
        qreal normalizedLevel = normalizeAudioLevel(level);
        // Emit signal or update UI to display the level value
        emit Sig_VolumePlayed(normalizedLevel);
    });
    

    Well,how about now in Qt6.8.0 since the QAudioOutput was nearly removed.

    JoeCFDJ 1 Reply Last reply
    1
    • B Offline
      B Offline
      Bonnie
      wrote on last edited by
      #2

      Well maybe like in Audio Output Example, use a QTimer. Actually even in the example of Qt5, "push mode" was also using QTimer instead of notify().

      V 2 Replies Last reply
      0
      • B Bonnie

        Well maybe like in Audio Output Example, use a QTimer. Actually even in the example of Qt5, "push mode" was also using QTimer instead of notify().

        V Offline
        V Offline
        VentureLee
        wrote on last edited by
        #3

        @Bonnie wow,much appriciate!I will try it.

        1 Reply Last reply
        0
        • V VentureLee

          I'm currently migrating a project from Qt 5.14.2 to Qt 6.8.0. I've run into an issue: when using QAudioSink to replace QAudioOutput, I found that QAudioSink doesn't have a notify() signal. This means I can't get real-time data from QAudioSink like I did with QAudioOutput in my original code, as shown below. What's the best way to achieve this in Qt 6.8.0?

          Original code was like:

          connect(audioOutput, &QAudioOutput::notify, this, [this, memoryDevice]() {
              // Get the duration of processed audio data
              qint64 processedUSecs = audioOutput->processedUSecs();
          
              // Calculate the current position of the playing audio data in memoryDevice
              qint64 currentPosition = (processedUSecs * format.sampleRate() * format.channelCount() * format.sampleSize()) / (1000000 * 8);
          
              // Read audio data at the corresponding position
              memoryDevice->seek(currentPosition);
              QByteArray audioData = memoryDevice->read(1024); // Read 1024 bytes of audio data
          
              // Calculate audio level
              qreal level = calculateAudioLevel(audioData);
              // Normalize dB value to the range of 0-100
              qreal normalizedLevel = normalizeAudioLevel(level);
              // Emit signal or update UI to display the level value
              emit Sig_VolumePlayed(normalizedLevel);
          });
          

          Well,how about now in Qt6.8.0 since the QAudioOutput was nearly removed.

          JoeCFDJ Offline
          JoeCFDJ Offline
          JoeCFD
          wrote on last edited by
          #4

          @VentureLee Qt Multimedia module was rewritten in Qt 6. I guess you have to use the API of Qt6 after upgrade.

          1 Reply Last reply
          0
          • B Bonnie

            Well maybe like in Audio Output Example, use a QTimer. Actually even in the example of Qt5, "push mode" was also using QTimer instead of notify().

            V Offline
            V Offline
            VentureLee
            wrote on last edited by
            #5

            @Bonnie Well, after some attempts, I found that the method in the example didn't seem to meet my requirements. In the end, I chose to write a class myself that inherits from the QBuffer class, and then I overrode the writeData and readData methods to obtain real-time PCM data. I also wrote my own normalization scheme to normalize the PCM level data to the range of 0-100.

            B 1 Reply Last reply
            1
            • V VentureLee

              @Bonnie Well, after some attempts, I found that the method in the example didn't seem to meet my requirements. In the end, I chose to write a class myself that inherits from the QBuffer class, and then I overrode the writeData and readData methods to obtain real-time PCM data. I also wrote my own normalization scheme to normalize the PCM level data to the range of 0-100.

              B Offline
              B Offline
              Bonnie
              wrote on last edited by
              #6

              @VentureLee Right, that's the pull mode in the example. I prefer this mode too. You can also check the Audio Source Example (in Qt5 Audio Input Example), there's also some code to calculate the audio level.

              V 1 Reply Last reply
              0
              • B Bonnie

                @VentureLee Right, that's the pull mode in the example. I prefer this mode too. You can also check the Audio Source Example (in Qt5 Audio Input Example), there's also some code to calculate the audio level.

                V Offline
                V Offline
                VentureLee
                wrote on last edited by
                #7

                @Bonnie Well,I made a simple method to normalize the level from 0 to 100,override function writeData from QBuffer

                qint64 AudioIODevice::writeData(const char* data, qint64 len)
                {
                    QMutexLocker locker(&mutex);
                
                    //recordedData->append(data, len);
                
                    // Use QAudioFormat to handle audio data
                    // int sampleSize = this->format.sampleFormat();
                    int sampleSize = format.sampleFormat();
                    QAudioFormat::SampleFormat sampleType = format.sampleFormat();
                    int channelCount = format.channelCount();
                    int bytesPerSample = sampleSize;
                
                    double rmsValue = 0;
                    int sampleCount = len / bytesPerSample;
                
                    if (sampleType == QAudioFormat::Int16) {
                        // Assume 16-bit signed integer (may need to adjust based on actual situation)
                        const qint16* samples = reinterpret_cast<const qint16*>(data);
                        qint64 sum = 0;
                        for (int i = 0; i < sampleCount; ++i) {
                            qint16 sampleValue = qFromLittleEndian<qint16>(samples[i]);
                            sum += sampleValue * sampleValue;
                        }
                        rmsValue = std::sqrt(static_cast<double>(sum) / sampleCount) / 32768.0;
                    }
                    else if (sampleType == QAudioFormat::Int32) {
                        // Assume 16-bit unsigned integer 
                        const quint16* samples = reinterpret_cast<const quint16*>(data);
                        qint64 sum = 0;
                        for (int i = 0; i < sampleCount; ++i) {
                            quint16 sampleValue = qFromLittleEndian<quint16>(samples[i]);
                            // Shift the sample value to center it at 0
                            int value = static_cast<int>(sampleValue) - 32768;
                            sum += value * value;
                        }
                        rmsValue = std::sqrt(static_cast<double>(sum) / sampleCount) / 32768.0;
                    }
                    else if (sampleType == QAudioFormat::Float) {
                        // Assume 32-bit floating point
                        const float* samples = reinterpret_cast<const float*>(data);
                        double sum = 0;
                        for (int i = 0; i < sampleCount; ++i) {
                            float sampleValue = samples[i];
                            sum += sampleValue * sampleValue;
                        }
                        rmsValue = std::sqrt(sum / sampleCount);
                    }
                    else {
                        // Handle other data types
                        return len;
                    }
                
                    // Prevent negative infinity in logarithmic calculation
                    if (rmsValue < 1e-10) {
                        rmsValue = 1e-10;
                    }
                
                    // Convert RMS value to decibels
                    double dbValue = 20 * std::log10(rmsValue);
                
                    // Define minimum and maximum decibel values for normalization
                    constexpr double minDb = -60.0;
                    constexpr double maxDb = 0.0;
                
                    // Normalize decibel value to 0 - 1 range
                    double normalizedVolume = (dbValue - minDb) / (maxDb - minDb);
                    // Limit to 0 - 1 range
                    normalizedVolume = clamp(normalizedVolume, 0.0, 1.0);
                
                    // Convert to 0 - 100 range
                    double volume = normalizedVolume * 100;
                
                    // Emit volume signal
                    emit sig_volumeChanged(volume);
                    
                    return QBuffer::writeData(data, len);;
                }
                
                1 Reply Last reply
                0
                • V VentureLee has marked this topic as solved on

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved