100 % of Processor when playing audio.
-
I've got a thread where I'm reading data from one buffer and put it to QAudioOutput buffer to play data. I'm using forever loop without sleep. And it loads my CPU on 100% and when I'm doing enithing else on PC(like open files) sound is clipping. So here as look my thread:
@#ifndef SOUNDTHREAD_H
#define SOUNDTHREAD_H#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QAudioOutput>#include "AudioBuffer.h"
class AudioBuffer;
class QAudioOutput;class SoundThread : public QThread
{
Q_OBJECT//----------------------------------------------------------------------------------------------
public: SoundThread(QAudioOutput * out_, AudioBuffer * buf): QThread(){
m_buf = buf;
m_out = out_;
m_buf2 = (AudioBuffer*)(m_out->start());
}
//----------------------------------------------------------------------------------------------
~SoundThread(){
m_mutex.lock();delete m_buf; delete m_buf2; m_condition.wakeOne(); m_mutex.unlock(); wait(); }
//----------------------------------------------------------------------------------------------
public: void start(){
QThread::start(QThread::HighPriority);
}
//----------------------------------------------------------------------------------------------
public: void stop(){
QThread::terminate();
}
//----------------------------------------------------------------------------------------------
public: void run(){
char* data;
quint64 len;forever { m_mutex.lock(); len = m_buf->bytesAvailable(); data = new char[len]; len = m_buf->readData(data, len); m_buf2->writeData(data, len); delete data; m_mutex.unlock(); }
}
//----------------------------------------------------------------------------------------------
private:
AudioBuffer * m_buf;
AudioBuffer * m_buf2;
QAudioOutput * m_out;QMutex m_mutex; QWaitCondition m_condition;
};
#endif//SOUNDTHREAD_H@
-
try using QThread::sleep() at the forever loop
-
You're leaking memory!
Call
@
delete[] data;
@on the data pointer! See "Section 16.11ff of the C++ FAQ":http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.11 for some details.
Rules: Always use together the following
- new and delete
- new[] and delete[]
- malloc (or friends) and free()
and never ever mix any of them.
To your actual problem:
as QAudioOutput::start() returns an [[Doc:QIODevice]], I assume that class AudioBuffer is an IO device. In that case, run an event loop in your run method and connect to the readyRead() signal. This way you're notified as soon as there's more data to read and you don't need to sleep in your thread.
Being a QIODevice, you should consider kicking away the char buffer alltogether, but use readAll(), which returns a QByteArray, and write that bytearray to the out buffer.
Best effort would be to put everything into a QObject based class and use QThread only as a lightweight thread manager instead of subclassing as a worker. See the excellent wiki article on "Threads, Events and QObjects":/wiki/Threads_Events_QObjects about this topic.
-
About readyRead() it seems like it's better then sleep. But on which slot I need to connect readyRead() signal to make forever loop ? Now I'm just using sleep:
@public: void run(){
char* data;
quint64 len = 0;forever { m_mutex.lock(); len = m_buf->bytesAvailable(); if(len != 0) { data = new char[len]; len = m_buf->readData(data, len); m_buf2->write(data, len); delete[] data; } m_mutex.unlock(); QThread::usleep(1); }
}@
-
I made a private slot in QThread:
@private slots:
void readMore()
{
char* data;
quint64 len = 0;m_mutex.lock(); len = m_buf->bytesAvailable(); if(len != 0) { data = new char[len]; len = m_buf->readData(data, len); m_buf2->write(data, len); delete[] data; } m_mutex.unlock(); }@
and replace everything in run() method by this:
@connect(m_buf,SIGNAL(readyRead()),this,SLOT(readMore()));@
But I never get inside a slot. -
Using signal/slot you do not need a foreach loop at all.
Some outline for your worker class (brain to terminal, not tested):
soundworker.h (header)
@
class SoundWorker : public QObject
{
Q_OBJECTpublic:
// it's good practice to have an optional parent pointer
// for all QObject based classes!
SoundWorker(QAudioOutput *out_, AudioBuffer * buf, QObject *parent = 0);protected slots:
void copyInput();private:
AudioBuffer *m_buf;
AudioBuffer *m_buf2;
QAudioOutput *m_out;
};
@soundworker.cpp (implementation)
@
SoundWorker::SoundWorker(QAudioOutput *out_, AudioBuffer * buf, QObject *parent)
: QObject(parent),
m_buf(buf),
m_out(out_)
{
// never use C style casts in C++!
// for QObject based classes use qobject_cast
// otherwise use dynamic_cast or static_cast
m_buf2 = qobject_cast<AudioBuffer *>(m_out->start());connect(m_buf, SIGNAL(readyRead()), this, SLOT(copyInput()));
}
void SoundWorker::copyInput()
{
while(m_buf->bytesAvailable() > 0) {
QByteArray data = m_buf->readAll();
m_buf2->write(data);
// short version:
// m_buf2->write(m_buf->readAll());
}
}
@main.cpp (using it in your other code)
@
// put it into a thread like this:
QThread *soundThread = new QThread(this);
SoundWorker *worker = new SoundWorker;
worker->moveToThread(soundThread);
soundThread->start();
@I've omitted the mutex and some cleanup code (not sure whether that's needed at all!)
Please do read "Threads, Events and QObjects":/wiki/Threads_Events_QObjects wiki article.
And after this think hard if your really need a thread at all. The QIODevice classes are asynchronous at all. The SoundWorker object works without a separate thread too.
-
OK I made QObject based class:
@#ifndef SOUNDCONTROL_H
#define SOUNDCONTROL_H#include <QObject>
#include <QAudioOutput>
#include <QMutex>
#include <QWaitCondition>#include "AudioBuffer.h"
class AUdioBuffer;
class SoundControl : public QObject
{
Q_OBJECTpublic:
//---------------------------------------------------------------------------------------------
SoundControl(QAudioOutput * out_, AudioBuffer * buf): QObject(){
m_buf = buf;
m_out = out_;
m_buf->start();
m_buf2 = m_out->start();
}
//---------------------------------------------------------------------------------------------
~SoundControl(){
m_mutex.lock();delete m_buf; delete m_buf2; m_condition.wakeOne(); m_mutex.unlock(); }
//---------------------------------------------------------------------------------------------
public slots:
void doWork() {
char* data;
quint64 len = 0;m_mutex.lock(); len = m_buf->bytesAvailable(); if(len != 0) { data = new char[len]; len = m_buf->readData(data, len); m_buf2->write(data, len); delete[] data; } m_mutex.unlock(); }
//---------------------------------------------------------------------------------------------
private:
AudioBuffer * m_buf;
QIODevice * m_buf2;
QAudioOutput * m_out;QMutex m_mutex; QWaitCondition m_condition;
};
#endif//SOUNDCONTROL_H@
And do this in main thread:
@ m_th = new QThread;
SoundControl * worker = new SoundControl(m_output, m_buf);
QObject::connect(m_buf, SIGNAL(readyRead()), worker, SLOT(doWork()));
worker->moveToThread(m_th);
m_th->start();@It work's but now the sound "clicks" frequently as when I use QThread::usleep(1);
-
AudioBuffer class described here :http://developer.qt.nokia.com/forums/viewthread/13323 . I see no difference between using qint64 readData(char * data, qint64 len) or realizing readAll method and using it.