在做多媒体开发中我们经常需要做pcm(频谱)显示,这个例子专门使用libzplay库实现音频频谱动态显示,附加波动动画。看一下频谱的效果!
实现步骤:
首先我们需要去libzplay下载这个库,因为我使用了它来采集音频频谱数据。
然后配置pro文件
配置好了就开始写功能实现部分,直接上代码吧!
我写了一个专门读取音频数据的类:
AudioData.h
#ifndef AUDIODATA_H
#define AUDIODATA_H
#include <QObject>
//#include <QAudioDecoder>
//#include <QAudioBuffer>
#include <QDebug>
//#include <QAudioDecoderControl>
#include <QFile>
#include "libzplay.h"
#include <QTimer>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
using namespace libZPlay;
class AudioData : public QObject
{
Q_OBJECT
public:
explicit AudioData(QObject *parent = 0);
Q_INVOKABLE void setSource(QString source);
Q_INVOKABLE void playMusic();
Q_INVOKABLE void stopMusic();
Q_INVOKABLE void pauseMusic();
Q_INVOKABLE void seekPosition(int ms);
Q_PROPERTY(int amplitude READ amplitude NOTIFY amplitudeChanged)
Q_PROPERTY(int position READ position NOTIFY positionChanged)
Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY isPlayingChanged)
//music pcm data
Q_PROPERTY(QString pcmData READ pcmData NOTIFY pcmDataChanged)
QString pcmData(){
QJsonObject root;
QJsonArray hn;
for(int i =0;i<257;i++){
hn.append (pnLeftAmplitude[i]);
}
root.insert ("data",hn);
QJsonDocument doc;
doc.setObject (root);
return doc.toJson ();
}
signals:
void amplitudeChanged();
void positionChanged();
void isPlayingChanged();
void pcmDataChanged();
public slots:
void refreshDatas();
private:
ZPlay *player;
QTimer timer;
int pnHarmonicNumber[512];
int pnHarmonicFreq[257];
int pnLeftAmplitude[257];
int pnRightAmplitude[257];
int pnLeftPhase[257];
int pnRightPhase[257];
int mposition=0;
bool isMusicPlaying;
int amplitude(){
return pnRightAmplitude[171];
}
int position(){
return mposition;
}
bool isPlaying(){
return isMusicPlaying;
}
};
#endif // AUDIODATA_H
AudioData.cpp
#include "audiodata.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <QImage>
AudioData::AudioData(QObject *parent) : QObject(parent)
{
player=CreateZPlay();
// set graph type to AREA, left channel on top
player->SetFFTGraphParam(gpGraphType, gtAreaLeftOnTop);
// set linear scale
player->SetFFTGraphParam(gpHorizontalScale, gsLinear);
timer.setInterval(1);
connect(&timer,SIGNAL(timeout()),this,SLOT(refreshDatas()));
}
//music source
void AudioData::setSource(QString source){
const char* msource=source.toLatin1().data();
// open file
int result = player->OpenFile(msource, sfAutodetect);
if(result == 0)
{
// display error message
qDebug()<<"file open failed"<<player->GetError()<<endl;
return;
}
}
/**
* @brief AudioData::playMusic
*/
void AudioData::playMusic(){
player->Play();
timer.start();
isMusicPlaying=true;
isPlayingChanged();
}
/**
* @brief AudioData::stopMusic
*/
void AudioData::stopMusic(){
player->Stop();
timer.stop();
isMusicPlaying=false;
isPlayingChanged();
}
/**
* @brief AudioData::pauseMusic
*/
void AudioData::pauseMusic(){
player->Pause();
timer.stop();
isMusicPlaying=false;
isPlayingChanged();
}
/**
* @brief AudioData::refreshDatas
*/
void AudioData::refreshDatas(){
// get song length
TStreamInfo info;
player->GetStreamInfo(&info);
// check key press
if(kbhit())
{
int a = getch();
if(a == 'p' || a == 'P')
player->Pause();
else if(a=='q'||a=='Q'){
player->Stop();
}
}
// get stream status to check if song is still playing
TStreamStatus status;
player->GetStatus(&status);
if(status.fPlay == 0)
return;
// get current position
TStreamTime pos;
player->GetPosition(&pos);
this->mposition=pos.ms;
positionChanged();
int FFTPoints = player->GetFFTGraphParam(gpFFTPoints);
player->GetFFTData(FFTPoints,fwTriangular,
pnHarmonicNumber,
pnHarmonicFreq,
pnLeftAmplitude,
pnRightAmplitude,
pnLeftPhase,
pnRightPhase);
amplitudeChanged();
pcmDataChanged();
// draw FFT graph on desktop window
// player->DrawFFTGraphOnHWND(0, 0, 0, 300, 200);
// qDebug()<<pnLeftAmplitude[100]<<endl;
}
/**
* @brief AudioData::seekPosition
* @param ms
*/
void AudioData::seekPosition(int ms){
TStreamTime time;
time.ms=ms;
player->Seek(tfMillisecond,&time,smFromBeginning);
}
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <audiodata.h>
#include <QtQml>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
AudioData ad;
engine.rootContext()->setContextProperty("AudioData",&ad);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property url soundUrl;
// property var pcmDataM:new Array();
property var pcmDatastr:AudioData.pcmData
ListModel{
id:dataModel
}
onPcmDatastrChanged: {
try{
if(dataModel.count===0)
return;
var jdata=JSON.parse(pcmDatastr).data;
for(var a=0;a<jdata.length;a++){
dataModel.setProperty(a,"pcmdata",jdata[a]);
}
}
catch(e){
console.log(e);
}
}
id:mainwin
Component.onCompleted: {
for(var a=0;a<257;a++){
dataModel.append({"pcmdata":0});
}
}
menuBar: MenuBar {
Menu {
title: qsTr("文件")
MenuItem {
text: qsTr("&打开MP3文件")
onTriggered: {
mdialog.open();
}
}
}
}
FileDialog{
id:mdialog
folder: shortcuts.home
nameFilters: [ "Sound files (*.mp3)" ]
onAccepted:{
soundUrl=mdialog.fileUrl;
AudioData.setSource(soundUrl.toString().substring(8,soundUrl.toString().length));
AudioData.playMusic();
}
}
Row{
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
Repeater{
model: dataModel
delegate: Rectangle{
width: mainwin.width/dataModel.count
height: pcmdata*mainwin.height/255
color:Qt.rgba(index/257,Math.abs(257-index)/257,Math.abs(257-index)/257,1)
anchors.bottom: parent.bottom
// radius: width/2
// border.width:1
// border.color:"#ffff12"
Rectangle{
width:parent.width
height:1
color:"#454545"
}
Behavior on height{
PropertyAnimation{
properties: "height"
duration: 70
}
}
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。