前言
本項目實現了使用QT QML創建一個音頻波形圖進度條的功能。用戶可以在界面上看到音頻波形圖,并且可以點擊進度條上的位置進行定位,也可以拖動進度條來調整播放進度。可以讓用戶更方便地控制音頻的播放進度,并且通過音頻波形圖可以直觀地了解音頻的節奏和節奏變化,為音頻播放功能增添了更多的交互性和用戶體驗。
效果圖
正文
本文使用QAudioDecoder
進行音頻解碼,將解碼數據計算后存儲到數組中,解碼完成后統一在QML中進行繪制。
關鍵代碼:
#include "audiowaveform.h"
#include <QDebug>
#include <QUrl>
#include <QTime>AudioWaveform::AudioWaveform(QObject *parent): QObject(parent), m_decoder(new QAudioDecoder(this)), m_sampleCount(0)
{connect(m_decoder, &QAudioDecoder::bufferReady,this, &AudioWaveform::handleBufferReady);connect(m_decoder, &QAudioDecoder::finished,this, &AudioWaveform::handleFinished);connect(m_decoder, QOverload<QAudioDecoder::Error>::of(&QAudioDecoder::error),this, &AudioWaveform::handleError);
}AudioWaveform::~AudioWaveform()
{m_decoder->stop();
}QString AudioWaveform::source() const
{return m_source;
}void AudioWaveform::setSource(const QString &source)
{if (m_source != source) {m_source = source;emit sourceChanged();processAudioFile();}
}QVector<qreal> AudioWaveform::waveformData() const
{return m_waveformData;
}void AudioWaveform::processAudioFile()
{clearWaveformData();if (m_source.isEmpty()) {return;}m_decoder->setSourceFilename(m_source);QAudioFormat desiredFormat;desiredFormat.setChannelCount(1);desiredFormat.setCodec("audio/pcm");desiredFormat.setSampleRate(SAMPLE_RATE);desiredFormat.setSampleSize(16);desiredFormat.setSampleType(QAudioFormat::SignedInt);m_decoder->setAudioFormat(desiredFormat);m_decoder->start();qDebug() <<__FUNCTION__<< __LINE__<< QTime::currentTime().toString("hh:mm:ss.zzz");
}void AudioWaveform::handleBufferReady()
{QAudioBuffer buffer = m_decoder->read();if (!buffer.isValid())return;const qint16 *data = buffer.constData<qint16>();int sampleCount = buffer.sampleCount();// 計算這個緩沖區的最大振幅qreal maxAmplitude = 0;for (int i = 0; i < sampleCount; ++i) {qreal amplitude = qAbs(data[i]) / 32768.0; // 將16位整數轉換為0-1范圍maxAmplitude = qMax(maxAmplitude, amplitude);}m_waveformData.append(maxAmplitude);
}void AudioWaveform::handleFinished()
{qDebug() <<__FUNCTION__<< __LINE__ << QTime::currentTime().toString("hh:mm:ss.zzz");// 對波形數據進行重采樣,使其具有固定的點數
// if (m_waveformData.size() > WAVEFORM_POINTS) {
// QVector<qreal> resampledData;
// resampledData.reserve(WAVEFORM_POINTS);// qreal step = m_waveformData.size() / static_cast<qreal>(WAVEFORM_POINTS);
// for (int i = 0; i < WAVEFORM_POINTS; ++i) {
// int index = static_cast<int>(i * step);
// resampledData.append(m_waveformData.at(index));
// }
// m_waveformData = resampledData;
// }emit waveformDataChanged();emit waveformProcessingFinished();
}void AudioWaveform::handleError(QAudioDecoder::Error error)
{QString errorMessage;switch (error) {case QAudioDecoder::NoError:return;case QAudioDecoder::ResourceError:errorMessage = "Resource error";break;case QAudioDecoder::FormatError:errorMessage = "Format error";break;case QAudioDecoder::AccessDeniedError:errorMessage = "Access denied error";break;case QAudioDecoder::ServiceMissingError:errorMessage = "Service missing error";break;default:errorMessage = "Unknown error";}emit this->error(errorMessage);
}void AudioWaveform::clearWaveformData()
{m_waveformData.clear();m_sampleCount = 0;emit waveformDataChanged();
}
波形繪制部分:
Canvas {id: waveformCanvasanchors.fill: parentanchors.margins: 2onPaint: {var ctx = getContext("2d");var width = waveformCanvas.width;var height = waveformCanvas.height;// 清除畫布ctx.clearRect(0, 0, width, height);// 如果沒有波形數據,直接返回if (!waveformModel || waveformModel.length === 0) return;// 設置波形樣式ctx.strokeStyle = "#4a90e2";ctx.lineWidth = 2;// 計算每個數據點的寬度var pointWidth = width / waveformModel.length;// 繪制波形ctx.beginPath();waveformModel.forEach(function(amplitude, index) {var x = index * pointWidth;var centerY = height / 2;var waveHeight = amplitude * (height * 0.8);ctx.moveTo(x, centerY - waveHeight / 2);ctx.lineTo(x, centerY + waveHeight / 2);});ctx.stroke();// 繪制已播放部分的遮罩if (duration > 0) {var progress = currentPosition / duration;ctx.fillStyle = "rgba(74, 144, 226, 0.3)";ctx.fillRect(0, 0, width * progress, height);}}
}
本文Demo下載