基于libzplay 实现window下音乐频谱动态显示

在做多媒体开发中我们经常需要做pcm(频谱)显示,这个例子专门使用libzplay库实现音频频谱动态显示,附加波动动画。看一下频谱的效果!
clipboard.png
clipboard.png
clipboard.png

实现步骤:
首先我们需要去libzplay下载这个库,因为我使用了它来采集音频频谱数据。
然后配置pro文件

clipboard.png

配置好了就开始写功能实现部分,直接上代码吧!

我写了一个专门读取音频数据的类:

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
                    }
                }
            }
        }
    }

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值