【问题标题】:Qt audio file to wave like audacityQt 音频文件像大胆一样挥动
【发布时间】:2018-10-20 23:23:01
【问题描述】:

我必须管理电影和音频文件,并且我需要像 audacity 那样渲染声音的波形。但我只是找到了实时渲染的例子。 我想在不播放的情况下渲染所有文件。
预期结果: 我的实际结果: 使用 Qt,我尝试使用QAudioDecoder 打开我的文件并获得QAudioBuffer,但我没有找到将所有数据转换为波形的算法。我也尝试使用Qt Spectrum Example 进行查看,但这并不容易理解,而且它仍然是实时的。

我的track.h

#ifndef TRACK_H
#define TRACK_H

#include <QWidget>
#include <QAudioBuffer>

class QAudioDecoder;

class Track : public QWidget
{
    Q_OBJECT    
public:
    Track(QWidget *parent = Q_NULLPTR);
    ~Track();
    void setSource(const QString &fileName);

public slots:
    void setBuffer();

protected:
    void paintEvent(QPaintEvent *e) override;

private:
    int pointDistance(const QPoint& a, const QPoint& b);
    QAudioDecoder *decoder;
    QAudioBuffer buffer;
    QByteArray byteArr;    
};    
#endif // TRACK_H

我的track.cpp

#include "track.h"

#include <QPaintEvent>
#include <QPainter>
#include <QAudioDecoder>

Track::Track(QWidget *parent)
    : QWidget(parent)
    , decoder(new QAudioDecoder(this))
{
    setMinimumHeight(50);

    connect(decoder, SIGNAL(bufferReady()), this, SLOT(setBuffer()));
    connect(decoder, SIGNAL(finished()), this, SLOT(update()));
}

Track::~Track()
{
    delete decoder;
}

void Track::setSource(const QString &fileName)
{
    byteArr.clear();
    decoder->setSourceFilename(fileName);
    decoder->start();
}

void Track::setBuffer()
{
    buffer = decoder->read();
    byteArr.append(buffer.constData<char>(), buffer.byteCount());
}

void Track::paintEvent(QPaintEvent *e)
{
    QWidget::paintEvent(e);

    int w = width(), h = height();
    QBrush backgroundBrush(Qt::white);
    QPainter painter(this);
    painter.fillRect(0, 0, w, h, backgroundBrush);

    painter.drawLine(0, h/2, w, h/2);
    if (!byteArr.isEmpty()){
        QPen pen(QColor(Qt::blue));
        painter.setPen(pen);
        int length = byteArr.size();
        int samplesPerPixel = length/w;
        int idx=0;
        for (int i=0; i<w; i++){
            QLine line;
            int higher = 0;
            for (int j=0; j<samplesPerPixel && idx+1<length; j++){
                const QPoint a(i, byteArr.at(idx)+(h/2));
                const QPoint b(i, byteArr.at(idx+1)+(h/2));
                if (higher < pointDistance(a, b))
                    line = QLine(a, b);
                idx++;
            }
            painter.drawLine(line);
        }
    }
}

int Track::pointDistance(const QPoint &a, const QPoint &b)
{
    int ret = 0;
    ret = sqrt(pow(b.x()-a.x(), 2) + pow(b.y()-a.y(), 2));
    return ret;
}

【问题讨论】:

  • 您不是在看波/光谱。您正在查看(平均)幅度。与audiobuffer.data() 成员中的数据相同。
  • @CaptainGiraffe,谢谢,这是我第一次处理声音处理,我的词汇量不好。目的是获取第一个数据并绘制一条线到下一个直到结束?
  • 想象数据成员中的每个值对应于 X 轴上一条垂直线的高度。然后你只需要压缩/平均它以使它在窗口中看起来不错。所以是的,前几个数据值构成了第一条垂直线。
  • @CaptainGiraffe,所以如果我很好理解,如果我有 500 个数据要放置在 100px 的小部件中,我必须在 100px 的每个数据下画一条线?
  • 是的,差不多。每个像素 5 个数据值。尝试平均和着色,你应该没问题。

标签: c++ qt waveform audio-processing


【解决方案1】:

我终于通过使用QCustomPlot 小部件找到了解决方案(通过阅读此post):

我的结果:

我的track.h

#ifndef TRACK_H
#define TRACK_H

#include "qcustomplot.h"
#include <QAudioBuffer>

class QAudioDecoder;

class Track : public QCustomPlot
{
    Q_OBJECT

public:
    Track(TrackType type, QWidget *parent = Q_NULLPTR);
    ~Track();
    void setSource(const QString &fileName);

public slots:
    void setBuffer();
    void plot();

private:
    qreal getPeakValue(const QAudioFormat& format);

    QAudioDecoder *decoder;
    QAudioBuffer buffer;
    QVector<double> samples;
    QCPGraph *wavePlot;    
};    
#endif // TRACK_H

我的track.cpp

#include "track.h"

#include <QAudioDecoder>

Track::Track(Track::TrackType type, QWidget *parent)
    : QCustomPlot(parent)
    , decoder(new QAudioDecoder(this))
{
    this->type = type;    
    wavePlot = addGraph();    
    setMinimumHeight(50);    
    connect(decoder, SIGNAL(bufferReady()), this, SLOT(setBuffer()));
    connect(decoder, SIGNAL(finished()), this, SLOT(plot()));
}

Track::~Track()
{
    delete decoder;
    // wavePlot delete auto ?
}

void Track::setSource(const QString &fileName)
{
    samples.clear();
    decoder->setSourceFilename(fileName);
    decoder->start();
}

void Track::setBuffer()
{
    buffer = decoder->read();
    qreal peak = getPeakValue(buffer.format());
    const qint16 *data = buffer.constData<qint16>();
    int count = buffer.sampleCount() / 2;
    for (int i=0; i<count; i++){
        double val = data[i]/peak;
        samples.append(val);
    }
}

void Track::plot()
{
    QVector<double> x(samples.size());
    for (int i=0; i<x.size(); i++)
        x[i] = i;
    wavePlot->addData(x, samples);
    yAxis->setRange(QCPRange(-1, 1));
    xAxis->setRange(QCPRange(0, samples.size()));
    replot();
}

/**
 * https://stackoverflow.com/questions/46947668/draw-waveform-from-raw-data-using-qaudioprobe
 * @brief Track::getPeakValue
 * @param format
 * @return The peak value
 */
qreal Track::getPeakValue(const QAudioFormat &format)
{
    qreal ret(0);
    if (format.isValid()){
        switch (format.sampleType()) {
            case QAudioFormat::Unknown:
            break;
            case QAudioFormat::Float:
                if (format.sampleSize() != 32) // other sample formats are not supported
                    ret = 0;
                else
                    ret = 1.00003;
            break;
            case QAudioFormat::SignedInt:
                if (format.sampleSize() == 32)
#ifdef Q_OS_WIN
                    ret = INT_MAX;
#endif
#ifdef Q_OS_UNIX
                    ret = SHRT_MAX;
#endif
                else if (format.sampleSize() == 16)
                    ret = SHRT_MAX;
                else if (format.sampleSize() == 8)
                    ret = CHAR_MAX;
                break;
            case QAudioFormat::UnSignedInt:
                if (format.sampleSize() == 32)
                    ret = UINT_MAX;
                else if (format.sampleSize() == 16)
                    ret = USHRT_MAX;
                else if (format.sampleSize() == 8)
                    ret = UCHAR_MAX;
            break;
        default:
            break;
        }
    }
    return ret;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 2013-12-29
    • 1970-01-01
    • 2021-04-20
    • 2015-08-17
    • 1970-01-01
    相关资源
    最近更新 更多