如何在线程循环中运行QSoundEffect或QMediaPlayer?

时间:2019-08-08 11:57:56

标签: c++ audio qthread qmediaplayer

问题是我无法使用QSoundEffectQMediaPlayer从线程播放声音。我在程序每次启动时播放声音的唯一方法是添加以下内容:

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();
}

1 个答案:

答案 0 :(得分:1)

由于QThread::run()的默认实现会为您完成事件循环,因此您不必自己处理事件循环。

我举了一个简单的示例,该示例在另一个线程中使用QSoundEffect播放声音。

由于我不确定您到底想做什么,所以我假设以下陈述:

  • 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,以在单独的线程中发出声音。启动后,该按钮将被禁用。

希望对您有帮助。