时域波形:直观展示声音的音量变化 (如声波的震动幅度),默认情况下显示的是时域信号。,而不是频率。数据来源是原始PCM样本。
在 Qt 中为音频播放器增加频谱显示功能,可以通过 QAudioProbe 或 QMediaPlayer 结合QLinearGradient可视化时域波形(音频振幅)可以创建更丰富的视觉效果,比如渐变颜色表示音量强度。
其中Qt音频播放器源码来自正点原子源码:
正点原子IMX6U仓库/正点原子I.MX6U 嵌入式Qt开发指南例程源码
本文主要目标是在原播放器基础上增加可视化时域波形(音频振幅)功能。
以下是完整实现方法:
1. 核心思路
- 时域波形 :直接绘制音频 PCM 样本的振幅值。
- 渐变效果 :用
QLinearGradient 填充波形,例如:
2. 实现步骤
步骤1:创建自定义绘图控件
继承 QWidget 并重写 paintEvent 方法:
#include
#include
#include
class WaveformWidget : public QWidget {
Q_OBJECT
public:
explicit WaveformWidget(QWidget *parent = nullptr) : QWidget(parent) {}
void setSamples(const QVector &samples) {
m_samples = samples;
update();
}
protected:
void paintEvent(QPaintEvent *) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QLinearGradient gradient(0, 0, width(), 0);
gradient.setColorAt(0, Qt::blue);
gradient.setColorAt(0.5, Qt::green);
gradient.setColorAt(1, Qt::red);
QPen pen;
pen.setBrush(gradient);
pen.setWidth(2);
painter.setPen(pen);
QPainterPath path;
if (m_samples.isEmpty()) {
painter.drawText(rect(), Qt::AlignCenter, "No audio data");
return;
}
float yScale = height() / 2.0f / 32768.0f;
float xStep = static_cast(width()) / m_samples.size();
path.moveTo(0, height() / 2);
for (int i = 0; i < m_samples.size(); ++i) {
float x = i * xStep;
float y = height() / 2 - m_samples[i] * yScale;
path.lineTo(x, y);
}
painter.drawPath(path);
}
private:
QVector m_samples;
};
步骤2:在播放器中连接音频数据
void MainWindow::processAudioBuffer(const QAudioBuffer& buffer)
{
const qint16 *samples = buffer.constData();
QVector sampleVector(samples, samples + buffer.sampleCount());
m_waveformWidget->setSamples(sampleVector);
}
3、完整的WaveformWidget类
waveformwidget.h
#ifndef WAVEFORMWIDGET_H
#define WAVEFORMWIDGET_H
#include <QWidget>
#include <QPainter>
#include <QLinearGradient>
#include <QPainterPath>
class WaveformWidget : public QWidget
{
Q_OBJECT
public:
explicit WaveformWidget(QWidget *parent = nullptr);
void setSamples(const QVector<qint16> &samples);
protected:
void paintEvent(QPaintEvent *) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QLinearGradient gradient(0, 0, width(), 0);
gradient.setColorAt(0, Qt::blue);
gradient.setColorAt(0.5, Qt::green);
gradient.setColorAt(1, Qt::red);
QPen pen;
pen.setBrush(gradient);
pen.setWidth(2);
painter.setPen(pen);
QPainterPath path;
if (m_samples.isEmpty()) {
painter.drawText(rect(), Qt::AlignCenter, "No audio data");
return;
}
float yScale = height() / 2.0f / 32768.0f;
float xStep = static_cast<float>(width()) / m_samples.size();
path.moveTo(0, height() / 2);
for (int i = 0; i < m_samples.size(); ++i) {
float x = i * xStep;
float y = height() / 2 - m_samples[i] * yScale;
path.lineTo(x, y);
}
painter.drawPath(path);
}
private:
QVector<qint16> m_samples;
signals:
};
#endif
waveformwidget.c
#include "waveformwidget.h"
WaveformWidget::WaveformWidget(QWidget *parent)
: QWidget{parent}
{}
void WaveformWidget::setSamples(const QVector<qint16> &samples) {
m_samples = samples;
update();
}
4、MainWindow修改
增加2个成员
private:
QAudioProbe *m_audioProbe;
WaveformWidget *m_waveformWidget;
增加探针
connect(m_audioProbe, &QAudioProbe::audioBufferProbed,
this, &MainWindow::processAudioBuffer);