在QThread中播放QSoundEffect

时间:2014-05-21 08:03:31

标签: multithreading qt audio qt5 qt5.2

我无法让QSoundEffect在单独的帖子中播放。你能否告诉我为什么声音仅由第一个代码片段而不是第二个代码片段播放?

//main.cpp

#include <QCoreApplication>
#include "SoundThread.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // #1
    QSoundEffect alarmSound;
    alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
    alarmSound.play();

    /* #2
    SoundThread thread;
    thread.start();
    thread.wait();
    */

    return a.exec();
}

//SoundThread.h

#ifndef SOUNDTHREAD_H
#define SOUNDTHREAD_H

#include <QThread>
#include <QtMultimedia/QSoundEffect>

class SoundThread : public QThread
{
    Q_OBJECT
    private:
        void run()
        {
            QSoundEffect alarmSound;
            alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
            alarmSound.play();
            while(true){}
        }
};

#endif // SOUNDTHREAD_H

3 个答案:

答案 0 :(得分:2)

来自Qt documentation on QThread: -

  

默认情况下,run()通过调用exec()

来启动事件循环

由于你继承自QThread,你现在有一个不调用exec()的run函数。因此,事件循环没有运行,很可能是播放音效所必需的。

调用exec()应该替换while(true){},因为exec()将一直等到调用exit()。

根据"How to Really Truly Use QThreads..."

将对象移动到线程中,正确执行此操作
class Worker : public QObject 
{
    Q_OBJECT

public:
    Worker();
    ~Worker();

public slots:
    void PlaySoundEffect();

signals:
    void finished();
    void error(QString err);

private:
    // store the sound effect, so we can reuse it multiple times
    QSoundEffect* m_pAlarmSound;

private slots:

};


Worker::Worker() 
{
    m_pAlarmSound = new QSoundEffect;
    m_pAlarmSound.setSource(QUrl::fromLocalFile(":/sound"));       
}

Worker::~Worker() 
{
    delete m_pAlarmSound;
    m_pAlarmSound = nullptr; // C++ 11
}

void Worker::PlaySoundEffect()
{
    m_pAlarmSound->play();
}

// setup the worker and move it to another thread...
MainWindow::MainWindow
{
    QThread* thread = new QThread;
    Worker* worker = new Worker();
    worker->moveToThread(thread);
    connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
    connect(thread, SIGNAL(started()), worker, SLOT(PlaySoundEffect()));
    connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    thread->start();

   // We can also connect a signal of an object in the main thread to the PlaySoundEffect slot

   // Assuming MainWindow has declared a signal void Alert();
   connect(this, &MainWindow::Alert, worker, &Worker::PlaySoundEffect);

   // Then play the sound when we want: -

   emit Alert();
}

虽然这看起来很费劲,但这样做有很多好处。例如,如果您有很多声音效果,继承QThread的方法意味着您要为每个声音效果创建一个线程,这是不理想的。

我们可以通过将枚举传递到PlaySoundEffect插槽,轻松扩展上面的Worker对象以保存声音效果列表并播放我们想要的声音效果。由于这个线程不断运行,播放声音会减少延迟;在运行时创建一个线程需要时间和资源。

答案 1 :(得分:1)

在运行函数结束时进入无限循环会导致阻塞线程,从而导致QSoundEffect不起作用。应该是这样的:

    void run()
    {
        QSoundEffect alarmSound;
        alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
        alarmSound.play();
        exec();
    }

答案 2 :(得分:1)

while(true){}替换为QThread::run();,这将启动内部事件循环并等待(休眠)事件,例如定时器事件和调用插槽的异步信号,这可能是内部发生的事情QSoundEffect:当您调用QSoundEffect::play()某些事件(可能是信号/插槽)在QThread内的事件队列中排队,但没有任何事件处理事件队列。请记住:您已覆盖virtual void run(),原始实现正在为您调用QThread::exec()。只要它们不是纯虚拟的,每当你覆盖它们时总是调用你的超类虚函数总是一个好主意。

    void run()
    {
        QSoundEffect alarmSound;
        alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
        alarmSound.play();
        QThread::run();
    }

有人建议调用QThread::exec()就行了。他们可能是对的。我必须检查QThread的实现,以确认它是QThread::run()实现中唯一调用的内容。我个人认为(根据经验)调用超级类的虚函数总是更安全,以防有其他事情被调用(对于这个特殊情况除了QThread::exec()之外)。

另一种选择是将QSoundEffect实例移动到线程上,并使用信号和插槽默认的自动连接行为类型来切换线程。

SoundPlayer.h:

#ifndef SOUNDPLAYER_H_
#define SOUNDPLAYER_H_

#include <QObject>
#include <QThread>
#include <QSoundEffect>

class SoundPlayer : public QObject
{
    Q_OBJECT

public:

    SoundPlayer();

signals:

    void play();

private:

    QThread      m_thread;
    QSoundEffect m_alarmSound;

};

#endif

SoundPlayer.cpp:

#include "SoundPlayer.h"

SoundPlayer()
{
    m_alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
    m_alarmSound.moveToThread(&m_thread);
    connect(this, SIGNAL(play()), &m_alarmSound, SLOT(play()));
    m_thread.start(); // QThread::exec() will be called for you, making the thread wait for events
}

然后调用play()信号将开始在正确的线程中播放。

SoundPlayer player;
emit player.play(); // m_alarmSound.play() will be called by m_thread