Qt SDL2播放Wav音频

这里介绍两种方法来实现Qt播放Wav音频数据。

方法一:使用QAudioOutput

pro文件中加入multimedia模块。

#include <QApplication>
#include <QFile>
#include <QAudioFormat>
#include <QAudioOutput>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;
// 传递给回调函数的userdata
AudioBuffer 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 <= 0) return;if (buffer->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 <QWidget>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 <QTime>
#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 <QThread>
#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 <= 0) return;if (buffer->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);
}AudioPlayThread::AudioPlayThread(QObject *parent): QThread(parent)
{
}int AudioPlayThread::open(const QString &fileName, TimeFunc timeFunc)
{m_timeFunc = timeFunc;// 加载 WAV 文件if (!SDL_LoadWAV(fileName.toStdString().c_str(), &m_spec, &m_data, &m_len)){printf("load wav error \n");return -1;}//计算1s钟字节大小m_perByte = (SDL_AUDIO_BITSIZE(m_spec.format) * m_spec.channels / 8 * m_spec.freq);m_totalTime = m_len / m_perByte;m_timeFunc(0, m_totalTime);printf("===============Wav params=============\n");printf("======size = %d\n", m_len);printf("======channels = %d\n", m_spec.channels);printf("======samples = %d\n", m_spec.samples);printf("======freq = %d\n", m_spec.freq);printf("======time = %d\n", m_totalTime);printf("===============Wav params=============\n");m_buffer.data = m_data;m_buffer.len = m_len;m_buffer.thread = this;m_spec.userdata = &m_buffer;m_spec.callback = fill_audio;// 打开音频设备if (SDL_OpenAudio(&m_spec, nullptr)){printf("SDL_OpenAudio error \n");SDL_FreeWAV(m_data);return  -1;}return 0;
}void AudioPlayThread::stop()
{m_isExit = true;
}void AudioPlayThread::pause()
{m_isPause = true;
}void AudioPlayThread::resume()
{m_isPause = false;
}void AudioPlayThread::run()
{SDL_PauseAudio(0);while(!m_isExit){if (m_buffer.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");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/412740.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《如何制作类mnist的金融数据集》——1.数据集制作思路

1&#xff0e;数据集制作思路&#xff08;生成用于拟合金融趋势图像的分段线性函数&#xff09; 那么如何去制作这样的一个类minist的金融趋势曲线数据集呢&#xff1f; 还是如上图所示&#xff0c;为了使类别平均分布&#xff0c;因此可以选取三种“buy”的曲线、三种“sell”…

Spring+SpringMVC+Mybatis进行项目的整合

Spring SpringMVCM Mybatis 整合 一、 通过idea创建maven工程 二、 引入依赖项以及导入mybatis逆向工程的插件 将如下的文件替换所在工程的pom文件 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4…

LabVIEW图像识别检测机械零件故障

项目背景&#xff1a; 在工业生产中&#xff0c;零件尺寸的准确检测对保证产品质量至关重要。传统的人工测量方法不仅耗时费力&#xff0c;精度低&#xff0c;还容易导致零件的接触磨损。为了解决这些问题&#xff0c;开发了一套基于LabVIEW和机器视觉的机械零件检测系统。该系…

Kubernetes增加master节点

一. 新增节点 无论是node节点还是master节点&#xff0c;kubelet、kubeadm、kubectl、CRI需要部署好&#xff0c; ### 新增node, 重新生成token, 复制加入即可, 前提是需要装上面的 kubectl kubeadm kubelet containerd 等 kubeadm token create --print-join-command### 新增 …

5-微信小程序语法参考

1. 数据绑定 官网传送门 WXML 中的动态数据均来自对应 Page 的 data。 数据绑定使用 Mustache 语法&#xff08;双大括号&#xff09;将变量包起来 ts Page({data: {info: hello wechart!,msgList: [{ msg: hello }, { msg: wechart }]}, })WXML <view class"vie…

MySQL之视图索引

学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;SC (Sno, Cno, Score)…

HDFS WebHDFS 读写文件分析及HTTP Chunk Transfer Encoding相关问题探究

文章目录 前言需要回答的首要问题DataNode端基于Netty的WebHDFS Service的实现基于重定向的文件写入流程写入一个大文件时WebHDFS和Hadoop Native的块分布差异 基于重定向的数据读取流程尝试读取一个小文件尝试读取一个大文件 读写过程中的Chunk Transfer-Encoding支持写文件使…

【Docker】Linux中使用Docker安装Nginx部署前后端分离项目应用

目录 一、概述 1. Nginx介绍 2. Nginx优势 3. Nginx的工作原理 二、容器创建 1. Mysql容器 2. Tomcat容器 3. Nginx容器 每篇一获 一、概述 1. Nginx介绍 Nginx&#xff08;发音为 "engine x"&#xff09;是一个开源的、高性能的 HTTP 服务器和反向代理服务…

rviz可视化机械臂(python)

一、准备的东西 一个机械臂的urdf 规划的路径点 二、launch文件的撰写 1.初始化 <?xml version"1.0" encoding"utf-8"?> <launch><param name"robot_description" textfile"机械臂.urdf" /><node name&qu…

抽象类(没有对象)之引用对象失败之谜

&#x1f468;‍&#x1f4bb;作者简介&#xff1a;&#x1f468;&#x1f3fb;‍&#x1f393;告别&#xff0c;今天 &#x1f4d4;高质量专栏 &#xff1a;☕java趣味之旅 欢迎&#x1f64f;点赞&#x1f5e3;️评论&#x1f4e5;收藏&#x1f493;关注 &#x1f496;衷心的希…

LLM(十)| Tiny-Vicuna-1B:Tiny Models轻量化系列Top One

在过去的一年里&#xff0c;见证了LLM的蓬勃发展&#xff0c;而模型的参数量也不断刷新记录&#xff0c;在2023年下半年&#xff0c;外界传言GPT-4是一个专家混合模型。因此&#xff0c;如果你想用人工智能做点什么&#xff0c;你需要IBM或NASA类似的计算能力&#xff1a;你怎么…

GoZero微服务个人探索之路(三)Go-Zero官方rpc demo示例探究

官方网址&#xff1a;https://go-zero.dev/docs/tasks/cli/grpc-demo 项目结构 demo包 两个文件均为protoc-gen-go-grpc自动生成构成一个完整的 gRPC 服务的定义和实现 democlient包 demo.go goctl生成的客户端代码 Request 和 Response 别名&#xff1a; 定义了 Request 和…