采用FMOD引擎实现音频波形图
因为做老师留的大作业时要做音频波形图接触了FMOD引擎,感觉FMOD功能真的是强大,不过大部分较新的学习资源都是外国的,对英语不好的新手来说也有些困难。我在此分享一下自己的学习成果,希望对大家有写帮助和启发。这也是本人第一次写博客,写得不好请见谅。
- 什么是FMOD?
在使用它之前让我们了解一下什么是FMOD
百度百科上说的很简单,FMOD是为游戏开发者准备的革命性音频引擎。到底有多厉害呢?百度百科并没有详细说明,你可以进入他们的官网,查看game一栏,如果你是一名单机游戏玩家的话,相信会认出不少3A大作。它的功能有多强大,相信看到这些不用我多说了吧。 - FMOD的引擎的下载
FMOD是免费的,你可以在FMOD的网站上进行下载
在下载之前你要先注册他们的账户,注册完账户点击下载,他们会给你邮箱发送一封邮件,点击邮件中的链接便可以进行下载。
FMOD是跨平台的,你可以选择你需要的版本。 - 连接FOMD外部库
下载下来后你只需要将lowlevel下的inc文件夹的头文件和lib文件夹下的外部库导入进项目即可,其他的内容本实验用不到。
另外说一下本实验我是在linux下用QT来实现的,如果是在windows下使用的可能会有些问题。 - 运行效果
- 主要代码
MainWindow:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QString>
#include <QFileDialog>
#include <QTimer>
#include "fmod.hpp"
#include "fmod_dsp.h"
#include "fmod_errors.h"
#include "fmod_dsp_effects.h"
#include "curveplot.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
FMOD::System* mSystem=NULL;
FMOD::Channel* mChannel;
FMOD::DSP* mDsp;
FMOD::Sound* mSound;
FMOD_RESULT mResult;
FMOD::ChannelGroup *mChannelGroup;
FMOD_DSP_PARAMETER_FFT *fftparameter;
QTimer timer;
private slots:
void onTimer();
void on_toolButton_clicked();
void on_toolButton_2_clicked();
void on_horizontalSlider_sliderMoved(int position);
private:
Ui::MainWindow *ui;
CurvePlot *_curvePlot=nullptr;
QVector<float>wav;
};
#endif // MAINWINDOW_H
//构造函数:
ui->setupUi(this);
wav.resize(2048);
mResult=FMOD::System_Create(&mSystem);
connect(&timer, SIGNAL(timeout()), SLOT(onTimer()));
if(mResult!=FMOD_OK)
{
qDebug()<<"error:System_Create";
exit(-1);
}
mResult=mSystem->init(32,FMOD_INIT_NORMAL,0);
if(mResult!=FMOD_OK)
{
qDebug()<<"error:System->init";
exit(-1);
}
ui->toolButton->setIcon(QPixmap(":/images/play.png"));
ui->toolButton_2->setIcon(QPixmap(":/images/file.png"));
_curvePlot=new CurvePlot(this);
ui->content->addWidget(_curvePlot);
_curvePlot->setAdjustVal(0.5);
_curvePlot->outPut();
//打开文件按钮
QString filename = QFileDialog::getOpenFileName();
mResult=mSystem->createSound(filename.toLocal8Bit(),FMOD_LOOP_NORMAL|FMOD_2D,0,&mSound);
if(mResult!=FMOD_OK)
{
qDebug()<<"error:System->createSound";
exit(-1);
}
mResult=mSystem->playSound(mSound,0,false,&mChannel);
if(mResult!=FMOD_OK)
{
qDebug()<<"error:System->playSound";
exit(-1);
}
ui->toolButton->setIcon(QPixmap(":/images/pause.png"));
mSound->setMode(FMOD_LOOP_OFF);
mResult=mSystem->createDSPByType(FMOD_DSP_TYPE_FFT,&mDsp);
mResult=mChannel->addDSP(0,mDsp);
uint length;
mSound->getLength(&length,FMOD_TIMEUNIT_MS);
ui->horizontalSlider->setMaximum(length);
length/=1000;
ui->label->setText(QString().sprintf("%02d:%02d ", length/60, length%60));
mSystem->update();
timer.start(400);
//实时刷新void onTimer()
mDsp->getParameterData(FMOD_DSP_FFT_SPECTRUMDATA,(void **)&fftparameter,0,0,0);
for(int j=0;j<fftparameter->length;j++)
{
//一倍不太明显,将其放大十倍
wav[j]=fftparameter->spectrum[0][j]*10;
}
_curvePlot->outPut(wav);
uint pos;
mChannel->getPosition(&pos,FMOD_TIMEUNIT_MS);
ui->horizontalSlider->setValue(pos);
pos/=1000;
ui->label_2->setText(QString().sprintf(" %02d:%02d", pos/60, pos%60));
bool isPlay;
mChannel->isPlaying(&isPlay);
if(!isPlay)
timer.stop();
绘制图形部分:
#ifndef CURVEPLOT_H
#define CURVEPLOT_H
#include <QWidget>
#include <QPainter>
#include <QPaintEvent>
//边缘
class QMargin
{
public:
QMargin(int left,int top,int right,int bottom):
Left(left),Top(top),Right(right),Bottom(bottom){}
QMargin(int h,int v):
Left(h),Top(v),Right(h),Bottom(v){}
QMargin(int d=0):
Left(d),Top(d),Right(d),Bottom(d){}
int Left;
int Top;
int Right;
int Bottom;
};
//波形图
class CurvePlot : public QWidget
{
Q_OBJECT
public:
explicit CurvePlot(QWidget *parent=0);
void outPut();
void outPut(QVector<float> &data);
void setAdjustVal(float val){iAdjustVal = val;}
QSize minimumSizeHint() const;
QMargin Margin;
QPen pen;
QBrush Background;
protected:
void paintEvent(QPaintEvent *paint);
void resizeEvent(QResizeEvent *);
private:
void transformPoints(QVector<float> &data, int w, int h, QVector<QPointF> &points);
QPixmap pixmap;
float iAdjustVal;
};
#endif // CURVEPLOT_H
//构造函数
Margin = QMargin(2);
Background = QBrush(QColor(Qt::black));
pen = QPen(QBrush(QColor(Qt::red)), 3);
iAdjustVal = 0;
//坐标转换
//void transformPoints(QVector<float> &data, int w, int h, QVector<QPointF> &points)
//获取极值
float max=data[0],min=data[0];
for(int i=1;i<data.size();i++)
{
if(max<data[i])
max=data[i];
if(min>data[i])
min=data[i];
}
//转化成当前屏幕的内的坐标大小
max+=iAdjustVal;
min-=iAdjustVal;
float diffVal=max-min;
for(int i=0;i<data.size();i++)
points.append(QPointF(i*w/data.size(),h-(data[i]-min)/diffVal*h));
//void paintEvent()
QPainter painter(this);
int w=width()-Margin.Left-Margin.Right;
int h=height()-Margin.Top-Margin.Bottom;
painter.drawPixmap(Margin.Left,Margin.Top,w,h,pixmap);
//void outPut()
int w = width() - Margin.Left - Margin.Right;
int h = height() - Margin.Top - Margin.Bottom;
pixmap=QPixmap(":/images/timg.jpg");
pixmap.scaled(w,h);
QPainter painter(&pixmap);
painter.setPen(pen);
painter.drawLine(QPoint(Margin.Left,height()/2),QPoint(Margin.Left+w,height()/2));
update();
//void minimumSizeHint()
return QSize(Margin.Left+Margin.Right+40,Margin.Top+Margin.Bottom+40);
//void outPut(QVector<float> &data)
int w = width() - Margin.Left - Margin.Right;
int h = height() - Margin.Top - Margin.Bottom;
pixmap = QPixmap(":/images/timg.jpg");
pixmap.scaled(w,h);
QPainter painter(&pixmap);
painter.setRenderHints(QPainter::SmoothPixmapTransform);
//painter.fillRect(QRectF(0, 0, w, h), Background);
QVector<QPointF> points;
transformPoints(data, w, h+this->height()*0.3, points);
painter.setPen(pen);
for (int i= 0; i< points.size()-1; i++)
painter.drawLine(points[i], points[i+1]);
update();