一、环境介绍
操作系统: 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\单片机教程,欢迎关注: