QSound、QSoundEffect播放WAV音频
QSound、QSoundEffect播放WAV音频
本文旨在介绍QSound、QSoundEffect的简单播放音频的方法以及对这两个类的一些基本介绍
最近,接受了一个同事写的代码,发现在播放音乐的地方,会有内存泄露,代码如下:
if (type == "HELLO") {
QSound *sound = new QSound("./radio/hello.wav");
sound->play();
// 执行其他操作
}
这里明显会出现内存泄露,但是如果使用局部变量的话,就会在这个if结束之后销毁掉,那有可能音乐没有播放完就停了的结果。
QSound#
因为是第一次接触这个类,秉着不懂的就学的原则,所以我转头去看了帮助文档。
简介&使用方法#
QSound类提供了一种播放**.wav格式音乐的方法。
你可以使用QSound**类的静态函数来播放音乐,之前的代码就可以写成下面这样:
QSound::play("./radio/hello.wav");
但是这种方法只会播放一次,没有办法循环播放。你也可以实例化对象,来设置播放的循环次数。
基本函数#
-
fileName()
这个函数,用于获取设置的资源文件的文件名。
QString fileName() const;
-
loops()
获取音频循环的次数
int loops() const;
-
setLoops()
设置循环的次数
void setLoops(int number);
-
loopsRemaining()
返回音频剩余的循环次数,如果返回的是QSound::Infinite代表循环播放
int loosRemaining() const;
-
isFinished()
判断当前音频是否播放完成。
bool isFinished() const;
-
play()与stop()
play()和stop()函数分别代表开始和结束当前音频的播放。
void play(); void stop();
但是在使用这个类的时候,我有点困惑,都找不到一个信号用于标志音频播放结束了,那怎么去判断是否结束播放,进而释放资源呢?
然后在文档中找到了一句话:
大致的意思就是,如果你想要更多对播放的音频的控制,请去看QSoundEffect和QAudioOutput这两个类。下面介绍一下这两个类。
QSoundEffect#
简介&基本使用#
这个类是用于低延迟的播放未经过压缩的音频(比如WAV文件)。这个类的比较常见的应用就是播放一个提示音;
基本的使用方法如下:
QSoundEffect *soundEffect = new QSoundEffect();
// 设置声音源文件的路径
soundEffect->setSource(QUrl::fromLoaclFile("./radio/hello.wav"));
// 音频循环的次数
soundEffect->setLoopCount(1);
// 音量
soundEffect->setVolume(1);
soundEffect->play();
// 连接信号,当播放完毕时,自动销毁对象。
connect(soundEffect, &QSoundEffect::playingChanged, [soundEffect] () {
if (effect->isPlaying()) {
effect->deleteLater();
}
});
使用这个类,就可以连接信号playingChanged,来实现在音乐播放完之后自动销毁。
属性及对应的函数#
这个类拥有的属性有:
-
category(种类):QString
这个属性包含当前QSoundEffect的种类,不同的平台,可以根据不同的category来设置执行不同的音频通道或者允许用户设置不同的音量等级。这个应该类似于Windows下的音量合成器
设置、读取函数和值发生改变时发射的信号分别为:// 读取属性函数 QString category() const // 设置属性函数 void setCategory(const QString &category); // 属性改变时发射的信号 void categoryChanged();
-
loops(循环次数):int
这个属性保存了音频的循环次数,当这个的值为0或者1时,音频都只会播放一次。当这个属性的值为QSoundEffect::Infinite为无限循环。
这个属性,可以在音频正在播放的时候进行修改,修改其实就是改变remaining loops的值。
设置、读取函数和值发生改变时发射的信号分别为:// 读取属性函数 int loopCount() const // 设置属性函数 void setLoopCount(int loops); // 属性改变时发射的信号 void loopCountChanged();
-
loopsRemaining(剩余循环次数):const int
当前音频剩余的播放次数。
读取的函数和发生改变时发射的信号为:// 读取属性函数 int loopsRemaining() const; // 属性改变时发射的信号 void loopsRemainingChanged();
-
muted(静音):bool
这个属性提供了一种控制静音的方法。
设置、读取以及发生改变时发射的信号为:// 读取属性函数 bool isMuted() const; // 设置属性函数 void setMuted(int loops); // 属性改变时发射的信号 void mutedChanged();
-
playing(播放状态):bool
这个属性标识了当前音频是不是处于播放状态。
对应的函数为:// 读取属性函数 bool isPlaying() const; // 属性改变时发射的信号 void playingChanged();
-
source(资源文件):QUrl
这个属性保存了声音文件的url。如果想要加载资源文件,这个URL必须存在,并且应用必须有访问指定目录的权限。
对应的函数为:// 读取属性函数 QUrl source() const; // 设置属性函数 void setSource(const QUrl &url); // 属性改变时发射的信号 void sourceChanged();
-
status(状态): const Status
这个属性指示了当前QSoundEffect的状态。
对应的函数为:// 读取属性函数 QSoundEffect::Status status() const; // 属性改变时发射的信号 void statusChanged();
-
volume(音量):qreal
这个属性保存了当前播放的音量,范围是[0.0,1.0]。
对应的函数为:// 读取属性函数 qreal volume() const; // 设置属性函数 void setVolume(qreal volume); // 属性改变时发射的信号 void volumeChanged();
源码探秘#
再次回到QSound这个类,由于在帮助文档里对QSound::play这个函数的介绍里,没有说会不会导致内存泄漏,同时也没有一个信号来标识音频播放完成。但是如果使用一个局部变量,在变量销毁时,音频就会停止播放,就会导致问题,于是,带着这些问题,我去看了QSound的源码。
首先,关于play这个静态函数的定义:
void QSound::play(const QString &filename)
{
// Object destruction is generally handled via deleteOnComplete
// Unexpected cases will be handled via parenting of QSound objects to qApp
QSound *sound = new QSound(filename, qApp);
sound->connect(sound->m_soundEffect, &QSoundEffect::playingChanged,
sound, &QSound::deleteOnComplete);
sound->play();
}
发现,它连接了一个槽函数,那就是deleteOnComplte
/*!
\internal
*/
void QSound::deleteOnComplete()
{
if (!m_soundEffect->isPlaying())
deleteLater();
}
在这里,就可以得出一个结论,play这个静态函数可以放心用,他会在播放结束之后,自动释放。
QSound其实也是在QSoundEffect上封装了一层,播放音频还是用的QSoundEffect,后面,在看了QSoundEffect之后,发现其底层用的是QAudioOutput,关于QAudioOutput和QAudioInput,挖个坑,我在后面的文章里,结合一个局域网实时语音通话的例子,来进行介绍。
作者:师从名剑山
出处:https://www.cnblogs.com/codegb/p/16653433.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理