如何在线程中的循环中 运行 QSoundEffect 或 QMediaPlayer?
How to run QSoundEffect or QMediaPlayer in a loop that is in a thread?
问题是我无法从使用 QSoundEffect
或 QMediaPlayer
的线程播放声音。我让它在每次程序启动时播放一次声音的唯一方法是添加:
QEventLoop loop;
loop.exec();
但是这种方法不符合我的需要,因为我需要声音能够播放不止一次。无限循环计数不是我需要的,但是当重复播放使用过的声音直到我结束程序时。我的两种方法都没有错误。那么,我遗漏了什么或使用方法不正确?
顺便说一下 QSound 的工作方式,但我无法用它控制循环次数和音量,所以我试图让 QSoundEffect 或 QMediaPlayer 工作,因为它们具有这种能力。
// main.cpp
QThread sound_thread;
QTimer sound_loop_timer;
Sound sound;
QObject::connect(&sound_loop_timer, SIGNAL(timeout()), &sound, SLOT(exec()));
sound_loop_timer.start(500);
sound_loop_timer.moveToThread(&sound_thread);
sound.moveToThread(&sound_thread);
sound_thread.start();
// Sound.h
class Sound : public QObject
{
Q_OBJECT
public:
Sound();
private slots:
void exec();
private:
void playSound();
QSoundEffect *sound_effect;
};
// Sound.cpp
Sound::Sound()
{
}
void Sound::exec(){
//...
playSound();
}
void Sound::playSound(){
sound_effect = new QSoundEffect;
sound_effect->setSource(QUrl("qrc:/sounds/audio/test.wav"));
// sound_effect->setLoopCount(QSoundEffect::Infinite);
sound_effect->setVolume(0.9);
sound_effect->play();
// QEventLoop loop;
// loop.exec();
QMediaPlayer player;// = new QMediaPlayer;
player.setMedia(QUrl::fromLocalFile("/home/path/audio/test.wav"));
player.setVolume(50);
player.play();
QEventLoop loop;
// loop.exec();
// loop.exit();
}
您不必自己处理事件循环,因为 QThread::run()
的默认实现会为您完成。
我做了一个简单的例子,在另一个线程中使用 QSoundEffect
播放声音。
由于我不确定你到底想做什么,我假设了以下陈述:
- A
SoundHandler
将处理 QSoundEffect
对象。
- 单击开始按钮时,声音会在单独的线程中播放。
- 声音无限循环播放,并在定时器超时后停止。
下面的代码只是为了向您展示如何在单独的线程(您要求的)中播放声音。如果上述规范不符合您的用例要求,我认为您可以轻松调整代码以使其适合您的需要。
test.h:
#ifndef TEST_H
#define TEST_H
#include <QMainWindow>
#include <QPushButton>
#include <QSoundEffect>
#include <QTimer>
#include <QThread>
class SoundHandler final : public QSoundEffect
{
Q_OBJECT
private:
QTimer * life_time_handler;
public:
SoundHandler(const QUrl & sound_path, int life_time_ms, QObject * parent = nullptr);
public slots:
void playSound();
void stopSound();
signals:
void hasFinished();
};
class TestWindow : public QMainWindow
{
Q_OBJECT
protected:
QPushButton * start_sound_thread;
QThread sound_thread;
SoundHandler * sound_effect;
public:
TestWindow();
~TestWindow();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QApplication>
SoundHandler::SoundHandler(const QUrl & sound_path, int life_time_ms, QObject * parent) : QSoundEffect(parent)
{
setSource(sound_path);
setVolume(0.5);
setLoopCount(QSoundEffect::Infinite);
life_time_handler = new QTimer(this);
life_time_handler->setInterval(life_time_ms);
life_time_handler->setSingleShot(true);
connect(life_time_handler, &QTimer::timeout, this, &SoundHandler::stopSound);
}
void SoundHandler::playSound()
{
life_time_handler->start();
play();
}
void SoundHandler::stopSound()
{
stop();
emit hasFinished();
}
TestWindow::TestWindow()
{
start_sound_thread = new QPushButton("Start");
this->setCentralWidget(start_sound_thread);
sound_effect = new SoundHandler(QUrl::fromLocalFile("../test/audio/test.wav"), 4000);
sound_effect->moveToThread(&sound_thread);
connect(&sound_thread, &QThread::finished, [&](){sound_effect->deleteLater();});
connect(&sound_thread, &QThread::started, sound_effect, &SoundHandler::playSound);
// Handle the thread termination
connect(sound_effect, &SoundHandler::hasFinished, [&](){
sound_thread.quit();
sound_thread.wait();
});
// Handle the thread launch
connect(start_sound_thread, &QPushButton::clicked, [&](){
sound_thread.start();
start_sound_thread->setEnabled(false);
});
}
TestWindow::~TestWindow()
{
if(sound_thread.isRunning())
{
sound_thread.quit();
sound_thread.wait();
}
}
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
TestWindow tw;
tw.show();
return app.exec();
}
我已经测试过了,效果很好。
备注:
- 为了方便起见,我在这里选择将
SoundHandler
作为 QSoundEffect
的子类,但这不是必需的(它可以有一个 QSoundEffect
对象作为成员而不是子类化它).
TestWindow
仅包含一个 QPushButton
以在单独的线程中启动声音。启动后,该按钮将被禁用。
希望对您有所帮助。
问题是我无法从使用 QSoundEffect
或 QMediaPlayer
的线程播放声音。我让它在每次程序启动时播放一次声音的唯一方法是添加:
QEventLoop loop;
loop.exec();
但是这种方法不符合我的需要,因为我需要声音能够播放不止一次。无限循环计数不是我需要的,但是当重复播放使用过的声音直到我结束程序时。我的两种方法都没有错误。那么,我遗漏了什么或使用方法不正确?
顺便说一下 QSound 的工作方式,但我无法用它控制循环次数和音量,所以我试图让 QSoundEffect 或 QMediaPlayer 工作,因为它们具有这种能力。
// main.cpp
QThread sound_thread;
QTimer sound_loop_timer;
Sound sound;
QObject::connect(&sound_loop_timer, SIGNAL(timeout()), &sound, SLOT(exec()));
sound_loop_timer.start(500);
sound_loop_timer.moveToThread(&sound_thread);
sound.moveToThread(&sound_thread);
sound_thread.start();
// Sound.h
class Sound : public QObject
{
Q_OBJECT
public:
Sound();
private slots:
void exec();
private:
void playSound();
QSoundEffect *sound_effect;
};
// Sound.cpp
Sound::Sound()
{
}
void Sound::exec(){
//...
playSound();
}
void Sound::playSound(){
sound_effect = new QSoundEffect;
sound_effect->setSource(QUrl("qrc:/sounds/audio/test.wav"));
// sound_effect->setLoopCount(QSoundEffect::Infinite);
sound_effect->setVolume(0.9);
sound_effect->play();
// QEventLoop loop;
// loop.exec();
QMediaPlayer player;// = new QMediaPlayer;
player.setMedia(QUrl::fromLocalFile("/home/path/audio/test.wav"));
player.setVolume(50);
player.play();
QEventLoop loop;
// loop.exec();
// loop.exit();
}
您不必自己处理事件循环,因为 QThread::run()
的默认实现会为您完成。
我做了一个简单的例子,在另一个线程中使用 QSoundEffect
播放声音。
由于我不确定你到底想做什么,我假设了以下陈述:
- A
SoundHandler
将处理QSoundEffect
对象。 - 单击开始按钮时,声音会在单独的线程中播放。
- 声音无限循环播放,并在定时器超时后停止。
下面的代码只是为了向您展示如何在单独的线程(您要求的)中播放声音。如果上述规范不符合您的用例要求,我认为您可以轻松调整代码以使其适合您的需要。
test.h:
#ifndef TEST_H
#define TEST_H
#include <QMainWindow>
#include <QPushButton>
#include <QSoundEffect>
#include <QTimer>
#include <QThread>
class SoundHandler final : public QSoundEffect
{
Q_OBJECT
private:
QTimer * life_time_handler;
public:
SoundHandler(const QUrl & sound_path, int life_time_ms, QObject * parent = nullptr);
public slots:
void playSound();
void stopSound();
signals:
void hasFinished();
};
class TestWindow : public QMainWindow
{
Q_OBJECT
protected:
QPushButton * start_sound_thread;
QThread sound_thread;
SoundHandler * sound_effect;
public:
TestWindow();
~TestWindow();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QApplication>
SoundHandler::SoundHandler(const QUrl & sound_path, int life_time_ms, QObject * parent) : QSoundEffect(parent)
{
setSource(sound_path);
setVolume(0.5);
setLoopCount(QSoundEffect::Infinite);
life_time_handler = new QTimer(this);
life_time_handler->setInterval(life_time_ms);
life_time_handler->setSingleShot(true);
connect(life_time_handler, &QTimer::timeout, this, &SoundHandler::stopSound);
}
void SoundHandler::playSound()
{
life_time_handler->start();
play();
}
void SoundHandler::stopSound()
{
stop();
emit hasFinished();
}
TestWindow::TestWindow()
{
start_sound_thread = new QPushButton("Start");
this->setCentralWidget(start_sound_thread);
sound_effect = new SoundHandler(QUrl::fromLocalFile("../test/audio/test.wav"), 4000);
sound_effect->moveToThread(&sound_thread);
connect(&sound_thread, &QThread::finished, [&](){sound_effect->deleteLater();});
connect(&sound_thread, &QThread::started, sound_effect, &SoundHandler::playSound);
// Handle the thread termination
connect(sound_effect, &SoundHandler::hasFinished, [&](){
sound_thread.quit();
sound_thread.wait();
});
// Handle the thread launch
connect(start_sound_thread, &QPushButton::clicked, [&](){
sound_thread.start();
start_sound_thread->setEnabled(false);
});
}
TestWindow::~TestWindow()
{
if(sound_thread.isRunning())
{
sound_thread.quit();
sound_thread.wait();
}
}
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
TestWindow tw;
tw.show();
return app.exec();
}
我已经测试过了,效果很好。
备注:
- 为了方便起见,我在这里选择将
SoundHandler
作为QSoundEffect
的子类,但这不是必需的(它可以有一个QSoundEffect
对象作为成员而不是子类化它). TestWindow
仅包含一个QPushButton
以在单独的线程中启动声音。启动后,该按钮将被禁用。
希望对您有所帮助。