Qt音频文件像大胆一样挥舞
Qt audio file to wave like audacity
我必须管理电影和音频文件,并且我需要像 audacity 一样渲染声波。但我只是找到实时渲染的例子。
我想渲染所有文件而不播放它。
预期结果:
我的实际结果:
对于 Qt,我尝试使用 QAudioDecoder
to open my file and get a QAudioBuffer
but I don't find an algorithm to transform all the data into wave. I also try to see with the 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;
}
我终于找到了解决方法 QCustomPlot
widget (by reading this 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();
}
/**
*
* @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;
}
我必须管理电影和音频文件,并且我需要像 audacity 一样渲染声波。但我只是找到实时渲染的例子。
我想渲染所有文件而不播放它。
预期结果:QAudioDecoder
to open my file and get a QAudioBuffer
but I don't find an algorithm to transform all the data into wave. I also try to see with the 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;
}
我终于找到了解决方法 QCustomPlot
widget (by reading this 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();
}
/**
*
* @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;
}