1

我无法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
4

3 回答 3

2

来自QThread 上的 Qt 文档:-

默认情况下,run() 通过调用 exec() 启动事件循环

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

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

基于“如何真正真正使用 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 对象以保存声音效果列表并播放我们想要的效果。由于该线程一直在运行,因此播放声音会产生较少的延迟;在运行时创建线程需要时间和资源。

于 2014-05-21T08:29:56.093 回答
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 实例移动到线程上,并使用信号和插槽默认的自动连接行为类型来切换线程。

声音播放器.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

声音播放器.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
于 2014-11-25T14:38:49.820 回答
1

您在 run 函数结束时进入无限循环,这会导致线程阻塞,因此 QSoundEffect 无法正常工作。它应该是这样的:

    void run()
    {
        QSoundEffect alarmSound;
        alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
        alarmSound.play();
        exec();
    }
于 2014-05-21T08:21:53.920 回答