QT5音频开发:使用QAudioOutput播放WAV文件,可使用指定的声卡进行播放

一、环境介绍

操作系统:  windows10 64位

QT版本:5.12.6

二、功能介绍

封装了一个播放WAV文件的类,可以播放指定的WAV文件、并且可以指定放声音的扬声器设备,在有多个声卡的系统上非常实用。

代码里主要注意的地方:在其他系统上运行,需要注意结构体的字节对齐问题,WAV头结构体正常字节大小是44字节。如果在Linux系统下运行如果无法播放WAV,要注意打印下WAV头结构体大小是否是44字节。

(不同位数的系统下, unsigned long 类型占用的字节不一样,WAV头结构体里存在 unsigned long类型,在其他系统运行需要注意下,为了兼容可以修改成 unsigned int)

三、核心代码

3.1  widget.cpp文件代码

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QSound>
#include <QMediaPlayer>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
          qDebug() << "当前系统可用的声卡设备: " << deviceInfo.deviceName();

}

Widget::~Widget()
{
    delete ui;
}

void Widget::wav_file_play(QString file)
{
    WAV_PLAY *wav_play;
    //这里可使用指定的声卡进行播放这里默认使用系统的默认声卡
    //要使用指定声卡,替换第一个参数即可,可看构造函数里打印的可用设备
    wav_play=new WAV_PLAY(QAudioDeviceInfo::defaultOutputDevice(),file);
    wav_play->play();
}

void Widget::on_pushButton_clicked()
{
    wav_file_play("D:/linux-share-dir/12804.wav");
}

3.2 widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "wav_play.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void wav_file_play(QString file);
private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

3.3  wav_play.cpp文件代码

#include "wav_play.h"
WAV_PLAY::WAV_PLAY(QAudioDeviceInfo info,QString wav_file)
{
    audio=nullptr;
    inputFile=nullptr;
    thread=nullptr;

    inputFile=new QFile;
    inputFile->setFileName(wav_file);
    if(inputFile->open(QIODevice::ReadOnly)==false)
    {
        qDebug()<<"文件打开失败.";
        QObject::deleteLater(); //释放资源
        return;
    }

    // 开始读取WAV的文件头
    WAVFILEHEADER WavFileHeader;
    if(inputFile->read((char*)&WavFileHeader,sizeof (WavFileHeader))!=sizeof (WavFileHeader))
    {
        qDebug()<<"读取WAV文件头失败.";
        QObject::deleteLater(); //释放资源
        return;
    }

    //打印读取到的信息
    qDebug()<<"RiffName:"<<WavFileHeader.RiffName;
    qDebug()<<"WavName:"<<WavFileHeader.WavName;
    qDebug()<<"FmtName:"<<WavFileHeader.FmtName;
    qDebug()<<"DATANAME:"<<WavFileHeader.DATANAME;
    qDebug()<<"FMT块 的长度:"<<WavFileHeader.nFmtLength;
    qDebug()<<"按照PCM 编码:"<<WavFileHeader.nAudioFormat;
    qDebug()<<"声道数目:"<<WavFileHeader.nChannleNumber;
    qDebug()<<"采样频率:"<<WavFileHeader.nSampleRate;
    qDebug()<<"数据块对齐单位:"<<WavFileHeader.nBytesPerSample;
    qDebug()<<"每秒平均字节数:"<<WavFileHeader.nBytesPerSecond;
    qDebug()<<"每次采样得到的样本数据位数:"<<WavFileHeader.nBitsPerSample;

    QAudioFormat format;

    //设置录音的格式
    format.setSampleRate(WavFileHeader.nSampleRate); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.
    format.setChannelCount(WavFileHeader.nChannleNumber);   //将通道数设置为通道。
    format.setSampleSize(WavFileHeader.nBitsPerSample);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/
    format.setCodec("audio/pcm"); //设置编码格式
    format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序
    format.setSampleType(QAudioFormat::SignedInt); //样本类型

    audio = new QAudioOutput(info,format);

    thread=new QThread;
    //销毁线程
    QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
    this->moveToThread(thread); //移动到子线程运行
    //播放状态
    connect(audio,SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged_input(QAudio::State)),Qt::QueuedConnection);
    thread->start();
}


WAV_PLAY::~WAV_PLAY()
{
    qDebug()<<"开始资源释放";
    if(audio)
    {
        audio->stop();
        delete audio;
    }
    if(inputFile)
    {
        inputFile->close();
        delete inputFile;
    }
    qDebug()<<"资源释放成功";
}

void WAV_PLAY::play()
{
    if(audio)audio->start(inputFile);
}

//播放状态
void WAV_PLAY::handleStateChanged_input(QAudio::State newState)
{
    //开始
    if(newState==QAudio::ActiveState)
    {

    }
    //播放完成
    /*
    传入的QIODevice没有数据,并且音频系统的缓冲区为空,
    此状态在调用start()之后并且没有音频数据可用于处理时设置。
    */
    if(newState==QAudio::IdleState)
    {
        QObject::deleteLater();
        thread->exit();
    }
}

3.4 wav_play.h代码

#ifndef WAV_PLAY_H
#define WAV_PLAY_H
#include <QSound>
#include <QThread>
#include <QAudioOutput>
#include <QFile>
#include <QAudioFormat>
#include <QDebug>

//WAV文件结构体信息
struct WAVFILEHEADER
{
    // RIFF 头
    char RiffName[4];
    unsigned long nRiffLength;

    // 数据类型标识符
    char WavName[4];

    // 格式块中的块头
    char FmtName[4];
    unsigned long nFmtLength;

    // 格式块中的块数据
    unsigned short nAudioFormat;
    unsigned short nChannleNumber;
    unsigned long nSampleRate;
    unsigned long nBytesPerSecond;
    unsigned short nBytesPerSample;
    unsigned short nBitsPerSample;

    // 数据块中的块头
    char    DATANAME[4];
    unsigned long   nDataLength;
};


class WAV_PLAY:public QObject
{
    Q_OBJECT
public:
    WAV_PLAY(QAudioDeviceInfo info, QString wav_file);
    ~WAV_PLAY();
    QFile *inputFile;
    QAudioOutput *audio;
    QThread *thread;
    void play();
public slots:
    void handleStateChanged_input(QAudio::State newState);
};
#endif // WAV_PLAY_H

四、运行效果

界面比较简陋,只是为了测试代码。

下面公众号里有全套的QT\C++\C\单片机教程,欢迎关注:

猜你喜欢

转载自blog.csdn.net/xiaolong1126626497/article/details/105816254