这里介绍两种方法来实现Qt播放Wav音频数据。
方法一:使用QAudioOutput
pro文件中加入multimedia模块。
#include #include #include #include int main(int argc, char *argv[]){QApplication a(argc, argv);QFile inputFile;inputFile.setFileName("test.wav");inputFile.open(QIODevice::ReadOnly);//设置采样格式QAudioFormat audioFormat;//设置采样率audioFormat.setSampleRate(44100);//设置通道数audioFormat.setChannelCount(2);//设置采样大小,一般为8位或16位audioFormat.setSampleSize(16);//设置编码方式audioFormat.setCodec("audio/pcm");//设置字节序audioFormat.setByteOrder(QAudioFormat::LittleEndian);//设置样本数据类型audioFormat.setSampleType(QAudioFormat::UnSignedInt);QAudioOutput *audio = new QAudioOutput( audioFormat, 0);audio->start(&inputFile);return a.exec();}
注意这里采样率、通道数和采样大小的设置,本例只能用来播放无损的WAV。
方法二:使用SDL2来播放
接下来演示一下如何使用SDL播放WAV文件。
初始化子系统:
// 初始化Audio子系统if (SDL_Init(SDL_INIT_AUDIO)) {qDebug() << "SDL_Init error:" << SDL_GetError();return;}
加载WAV文件:
// 存放WAV的PCM数据和数据长度typedef struct {Uint32 len = 0;int pullLen = 0;Uint8 *data = nullptr;} AudioBuffer; // WAV中的PCM数据Uint8 *data;// WAV中的PCM数据大小(字节)Uint32 len;// 音频参数SDL_AudioSpec spec; // 加载wav文件if (!SDL_LoadWAV(FILENAME, &spec, &data, &len)) {qDebug() << "SDL_LoadWAV error:" << SDL_GetError();// 清除所有的子系统SDL_Quit();return;} // 回调spec.callback = pull_audio_data;// 传递给回调函数的userdataAudioBuffer buffer;buffer.len = len;buffer.data = data;spec.userdata = &buffer;
打开音频设备:
// 打开设备if (SDL_OpenAudio(&spec, nullptr)) {qDebug() << "SDL_OpenAudio error:" << SDL_GetError();// 释放文件数据SDL_FreeWAV(data);// 清除所有的子系统SDL_Quit();return;}
开始播放:
// 每一个样本大小int size = SDL_AUDIO_BITSIZE(m_spec.format) * m_spec.channels / 8;// 最后一次播放的样本数量int leftSample = m_buffer.pullLen / size;// 最后一次播放的时长msint ms = leftSample * 1000 / m_spec.freq;SDL_Delay(ms);
回调:
static void fill_audio(void *userdata, Uint8 *stream, int len){AudioPlayThread::AudioBuffer *buffer = (AudioPlayThread::AudioBuffer *)userdata;SDL_memset(stream, 0, len);if (buffer->len thread->m_isPause)return;buffer->pullLen = buffer->len > len ? len : buffer->len;SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME);buffer->data += buffer->pullLen;buffer->len -= buffer->pullLen;//未播放的时间int unPlayTime = (buffer->thread->m_totalTime) - (buffer->len / buffer->thread->m_perByte);buffer->thread->m_timeFunc(unPlayTime,buffer->thread->m_totalTime);}
释放资源:
// 释放WAV文件数据SDL_FreeWAV(data); // 关闭设备SDL_CloseAudio(); // 清除所有的子系统SDL_Quit();
运行效果图:
音频界面构造:
音频播放界面:AudioPlayWidget类。
#ifndef AUDIOPLAYWIDGET_H#define AUDIOPLAYWIDGET_H#include namespace Ui {class AudioPlayWidget;}class AudioPlayThread;class AudioPlayWidget : public QWidget{Q_OBJECTpublic:explicit AudioPlayWidget(const QString &name, const QString &url, QWidget *parent = 0);~AudioPlayWidget();private slots:void on_btnPlay_clicked();void slotFinished();void slotShowTime(int playTime, int totalTime);private:Ui::AudioPlayWidget *ui;private:bool m_play = false;AudioPlayThread *m_thread = nullptr;bool m_isExistPlay = false;QString m_url;};#endif // AUDIOPLAYWIDGET_H#include "AudioPlayWidget.h"#include "ui_AudioPlayWidget.h"#include "BaseHelper.h"#include "AudioPlayThread.h"#include #include "mymessagebox.h"const QString playStyle = "QPushButton#btnPlay\{\border-image: url(\":/image/audioPlay.png\");\}";const QString pauseStyle = "QPushButton#btnPlay\{\border-image: url(\":/image/audioPause.png\");\}";AudioPlayWidget::AudioPlayWidget(const QString &name, const QString &url,QWidget *parent) :QWidget(parent),ui(new Ui::AudioPlayWidget),m_url(url){ui->setupUi(this);m_thread = new AudioPlayThread(this);connect(m_thread,&AudioPlayThread::finished,this,&AudioPlayWidget::slotFinished);BaseHelper::load(":/qss/AudioPlayWidget.qss",this);}AudioPlayWidget::~AudioPlayWidget(){delete ui;if(m_thread){m_thread->stop();m_thread->wait();}}void AudioPlayWidget::on_btnPlay_clicked(){if (!BaseHelper::fileExist(m_url)){MyMessageBox::showMyMessageBox(this, QString("文件丢失"),QString("打开的音频文件丢失?"), MESSAGE_WARNNING, BUTTON_OK, true);return;}m_play = !m_play;if(m_play){ui->btnPlay->setStyleSheet(pauseStyle);if (!m_isExistPlay){TimeFunc totalTimeFunc = std::bind(&AudioPlayWidget::slotShowTime, this,std::placeholders::_1, std::placeholders::_2);m_thread->open(m_url, totalTimeFunc);m_thread->start();m_isExistPlay = true;}else{m_thread->resume();}}else{ui->btnPlay->setStyleSheet(playStyle);m_thread->pause();}}void AudioPlayWidget::slotFinished(){if (m_isExistPlay)m_isExistPlay = false;m_play = !m_play;ui->lbPlay->setText("00:00:00");ui->lbTotal->setText("00:00:00");ui->horizontalSlider->setValue(0);ui->btnPlay->setStyleSheet(playStyle);}void AudioPlayWidget::slotShowTime(int playTime, int totalTime){QString strPlayTime = QTime::fromMSecsSinceStartOfDay(playTime* 1000).toString("hh:mm:ss");QString strTotalTime = QTime::fromMSecsSinceStartOfDay(totalTime*1000).toString("hh:mm:ss");ui->lbPlay->setText(strPlayTime);ui->lbTotal->setText(strTotalTime);ui->horizontalSlider->setMaximum(totalTime);ui->horizontalSlider->setMinimum(0);ui->horizontalSlider->setValue(playTime);}
音频播放线程:
#ifndef AUDIOPLAYTHREAD_H#define AUDIOPLAYTHREAD_H#include #include "global.h"class AudioPlayThread : public QThread{public:AudioPlayThread(QObject *parent = nullptr);typedef struct AudioBuffer {int len = 0;int pullLen = 0;uint8_t *data = nullptr;AudioPlayThread *thread = nullptr;} AudioBuffer;public:int open(const QString &fileName, TimeFunc timeFunc);void stop();void pause();void resume();protected:void run();public:TimeFunc m_timeFunc;int m_totalTime = 0; //单位sint m_perByte = 0;//1s字节数bool m_isPause = false;private:bool m_isExit = false; Uint8 *m_data = nullptr;Uint32 m_len = 0;AudioBuffer m_buffer;SDL_AudioSpec m_spec;};#endif // AUDIOPLAYTHREAD_H#include "AudioPlayThread.h"static void fill_audio(void *userdata, Uint8 *stream, int len){AudioPlayThread::AudioBuffer *buffer = (AudioPlayThread::AudioBuffer *)userdata;SDL_memset(stream, 0, len);if (buffer->len thread->m_isPause)return;buffer->pullLen = buffer->len > len " /> 0) continue;// 每一个样本大小int size = SDL_AUDIO_BITSIZE(m_spec.format) * m_spec.channels / 8;// 最后一次播放的样本数量int leftSample = m_buffer.pullLen / size;// 最后一次播放的时长msint ms = leftSample * 1000 / m_spec.freq;SDL_Delay(ms);break;}// 释放 WAV 数据SDL_FreeWAV(m_data);SDL_CloseAudio();printf("=======Play Wav thread exit===\n");}