Symbian QT: Multithreaded audio menggunakan QAudioOutput

Tutorial Qt for Symbian OS

Symbian QT: Multithreaded audio menggunakan QAudioOutput

Saya perhatikan bahwa jika UI QML menjalankan animasi dan pada saat yang sama saya mencoba menggunakan QAudioOutput untuk memutar efek suara, animasinya menjadi tersendat-sendat dengan mudah, setidaknya pada perangkat (Symbian). Saya kira penyebab masalahnya adalah pemrosesan audio terjadi di utas yang sama yang menjalankan loop peristiwa.

Solusi untuk masalah ini cukup jelas – pindahkan hal-hal yang berhubungan dengan audio ke utas yang berbeda. Namun, untuk melakukan ini diperlukan sedikit penyesuaian, jadi saya mendokumentasikan solusinya di sini jika ada orang lain yang menghadapi masalah yang sama. Tentu saja, solusi ini sama sekali tidak eksklusif untuk keluaran audio, solusi ini juga dapat digunakan untuk melakukan pemrosesan apa pun (yang dimulai dengan QML atau C++) di utas terpisah.

Dalam implementasi saya, saya menggunakan dua kelas C++, SfxPlayer dan SfxThread. SfxPlayer diekspos ke sisi QML dan berisi satu slot, play(). Satu-satunya tujuan kelas ini adalah untuk memuat objek SfxThread dan berfungsi sebagai proksi untuk memanggil metode di thread lainnya.


pemutar sfx.h


#ifndef SFXPLAYER_H
#define SFXPLAYER_H

#include 
#include "sfxthread.h"

class SfxPlayer : public QObject
{
    Q_OBJECT
public:
    explicit SfxPlayer(QObject *parent = 0);

signals:

public slots:
    void play();

private:
    SfxThread _sfxThread;
};

#endif // SFXPLAYER_H

pemutar sfx.cpp


#include "sfxplayer.h"

SfxPlayer::SfxPlayer(QObject *parent)
    : QObject(parent)
{
}

void SfxPlayer::play()
{
    // Call playInternal of SfxThread (and invoke it in the other thread)
    QMetaObject::invokeMethod(&_sfxThread, "play", Qt::QueuedConnection);
}

Hal yang sedikit membingungkan adalah bagaimana membuat utas pemain benar-benar melakukan pemrosesan di utas lain, bukan di pemanggil. Solusinya adalah memanggil QObject::moveToThread(this); di konstruktor utas.


sfxthread.h


#ifndef SFXTHREAD_H
#define SFXTHREAD_H

#include 
#include 
#include 
#include 

class SfxThread : public QThread
{
    Q_OBJECT
public:
    explicit SfxThread(QObject *parent = 0);
    ~SfxThread();
    void run();

signals:

public slots:
    void play();
    void playerStateChanged(QAudio::State state);
private:
    void playNext();
    void play(QString& filename);

    QAudioOutput *_ao;
    QAudioFormat _af;
    QBuffer _buf;

    QQueue playlist;
};

#endif // SFXTHREAD_H

sfxthread.cpp


#include 
#include 
#include "sfxthread.h"

SfxThread::SfxThread(QObject *parent) :
    QThread(parent)
{ 
    ...do precaching etc of the sounds here

    start();

    // Move event processing of SfxThread to this thread
    QObject::moveToThread(this);
}

SfxThread::~SfxThread()
{
    quit();
    wait();
}

void SfxThread::play()
{
    playlist.append(...);
    playNext();
}

// Plays the next file from queue
void SfxThread::playNext()
{
    if (playlist.length() == 0 || _ao->state() == QAudio::ActiveState) {
        return;
    }
    _ao->stop();
    _buf.close();
    play(playlist.dequeue());
}

void SfxThread::play(QString& filename)
{
    // load content here
    QByteArray *ptr = ...

    _buf.setBuffer(ptr);
    _buf.open(QIODevice::ReadOnly);

    _ao->setBufferSize(_buf.size());
    _ao->start(&_buf);
}

void SfxThread::playerStateChanged(QAudio::State state)
{
    // Play finished, play next clip
    if (state == QAudio::IdleState)
    {
        playNext();
    }
}

void SfxThread::run()
{
    ...set audio format here (_af.setCodec etc)
    
    _ao = new QAudioOutput(_af);

    connect(_ao, SIGNAL(stateChanged(QAudio::State)), this, SLOT(playerStateChanged(QAudio::State)));

    exec();
}

Selesai! Kini Anda dapat mengekspos objek SfxPlayer ke QML. Memanggil .play(); di QML kini hanya akan memanggil metode play dari SfxThread di thread baru yang menjalankan loop peristiwanya sendiri.

Apa Reaksi Anda?

like

dislike

love

funny

angry

sad

wow