How can I get real-time level data while playing audio using QAudioSink?
-
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.
-
Well maybe like in Audio Output Example, use a
QTimer
. Actually even in the example of Qt5, "push mode" was also usingQTimer
instead ofnotify()
. -
Well maybe like in Audio Output Example, use a
QTimer
. Actually even in the example of Qt5, "push mode" was also usingQTimer
instead ofnotify()
.@Bonnie wow,much appriciate!I will try it.
-
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.
@VentureLee Qt Multimedia module was rewritten in Qt 6. I guess you have to use the API of Qt6 after upgrade.
-
Well maybe like in Audio Output Example, use a
QTimer
. Actually even in the example of Qt5, "push mode" was also usingQTimer
instead ofnotify()
.@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.
-
@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.
@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.
-
@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.
@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);; }
-