PDA

View Full Version : using threads to eliminate a never ending loop



duma
12th August 2011, 15:18
Hi guys,
I have a program in Qt Creator which outputs a sine wave with the click of a pushButton. However, due to a never ending while loop in my pushButton function, the application freezes when I click the pushButton. So I am using threads for my function and ending the while loop by eliminating the thread with the click of another pushButton(pushButton2).
I build my code with no errors. however, when I run it, the pushButton does not generate a sine wave and I get the following message:
@QMetaObject::connectSlotsByName: No matching signal for on_write_loop(snd_pcm_t*,signed short*,snd_pcm_channel_area_t*)@

Any help would be greatly appreciated.

My code snippets are below:

wave.cpp:

void wave::on_write_loop(snd_pcm_t *handles, signed short *samples , snd_pcm_channel_area_t *areas)
{
double freq = ui->frequency->text().toDouble();
double ampl = ui->amplitude->text().toDouble();
double phase = 0;
signed short *ptr;
int err, cptr;
while (1) {
generate_sine(0, period_size, &phase, freq, ampl);
ptr = samples;
cptr = period_size;
while (cptr > 0) {
err = snd_pcm_writei(hspdif, ptr, cptr);
if (err == -EAGAIN)
continue;
if (err < 0) {
if (xrun_recovery(hspdif, err) < 0) {
printf("Write error: %s ", snd_strerror(err));
exit(EXIT_FAILURE);
}
break; /* skip one period */
}
ptr += err * channels;
cptr -= err;
}
}

}


wave::wave(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::wave)
{
ui->setupUi(this);
//setup();
mThread = new MyThread(this);
connect(mThread, SIGNAL(write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)), this, SLOT(on_write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)));
}

wave::~wave()
{
delete ui;

}


void wave::on_pushButton_clicked()
{

//Generate
mThread->start();

}


void wave::on_pushButton_2_clicked()
{
//Terminate
mThread->Stop = true;

}


wave.h:

#ifndef WAVE_H
#define WAVE_H
#include "ui_wave.h"
#include <alsa/asoundlib.h>
#include <QMainWindow>
#include <QObject>
#include "mythread.h"

namespace Ui {
class wave;
}

class wave : public QMainWindow
{
Q_OBJECT

public:
explicit wave(QWidget *parent = 0);
~wave();
MyThread *mThread;

private slots:
void on_pushButton_clicked();

void on_pushButton_2_clicked();

void setup();

private:
Ui::wave *ui;

public slots:
void on_write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*);
;

#endif // WAVE_H

MyThread.cpp:

void MyThread::run()
{
QMutex mutex;
mutex.lock();
if (this->Stop)
mutex.unlock();

const char *device = "plughw:0,0";

snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);

int err = snd_output_stdio_attach(&output, stdout, 0);
printf( "snd_output_stdio_attach err=%d\n", err);
err = snd_pcm_open(&hspdif, device, SND_PCM_STREAM_PLAYBACK, 0);
printf( "snd_pcm_open err=%d\n", err);
err = set_hwparams(hspdif, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
printf( "set_hwparams err=%d\n", err);
err = set_swparams(hspdif, swparams);
printf( "set_swparams err=%d\n", err);

samples = new signed short [period_size * channels * snd_pcm_format_physical_width(format)];
printf( "samples array_size=%d\n", int( period_size * channels * snd_pcm_format_physical_width(format)) );

areas = new snd_pcm_channel_area_t [channels];
printf( "areas channels=%d\n", channels);
for (unsigned int chn = 0; chn < channels; chn++) {
areas[chn].addr = samples;
areas[chn].first = chn * snd_pcm_format_physical_width(format);
areas[chn].step = channels * snd_pcm_format_physical_width(format);
}
emit write_loop(hspdif, samples, areas);
}

MyThread.h:

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <alsa/asoundlib.h>
#include <QThread>

class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void run();
bool Stop;

signals:
void write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*);

public slots:
};

#endif // MYTHREAD_H

nix
12th August 2011, 15:54
Your source is a bit complicated...

You use QThread in a stange way.

Try this :


Put the QThread::run() content in your wave constructor.
Remove your class MyThread and any reference to QThread
In your UI class add :

signals:
void startWave(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*);
private:
QThread *thread;.
Add the following code your constructor :

thread=new QThread();
thread.start();
Wave *wave = new Wave();
wave->moveToThread(thread);
connect(this, SIGNAL(startWave(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)), wave, SLOT(on_write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)));


And emit your signal when you want to start playing.



That should work without subclassing QThread.

Code not tested i have to leave fast.

Good luck

duma
12th August 2011, 18:35
Your source is a bit complicated...

You use QThread in a stange way.

Try this :


Put the QThread::run() content in your wave constructor.
Remove your class MyThread and any reference to QThread
In your UI class add :

signals:
void startWave(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*);
private:
QThread *thread;.
Add the following code your constructor :

thread=new QThread();
thread.start();
Wave *wave = new Wave();
wave->moveToThread(thread);
connect(this, SIGNAL(startWave(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)), wave, SLOT(on_write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)));


And emit your signal when you want to start playing.



That should work without subclassing QThread.

Code not tested i have to leave fast.

Good luck

Hey thanks alot. I did as you said and I'm running into the error:
error: request for member ‘start’ in ‘((wave*)this)->wave::thread’, which is of non-class type ‘QThread*’

nix
12th August 2011, 18:47
I made a mistake in the code I give you try
thread->start() of course.

duma
12th August 2011, 19:43
I made a mistake in the code I give you try
thread->start() of course.

I am now getting this error:

QMetaObject::connectSlotsByName: No matching signal for on_write_loop(snd_pcm_t*,signed short*,snd_pcm_channel_area_t*)
Wave: pcm_params.c:2286: snd_pcm_hw_refine: Assertion `pcm && params' failed.
The program has unexpectedly finished.

also, I think you mean:

thread = new QThread();
thread->start();
wave *Wave = new wave();
Wave->moveToThread(thread);
connect(this, SIGNAL(startWave(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)), Wave, SLOT(on_write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)));

Added after 5 minutes:


I am now getting this error:

QMetaObject::connectSlotsByName: No matching signal for on_write_loop(snd_pcm_t*,signed short*,snd_pcm_channel_area_t*)
Wave: pcm_params.c:2286: snd_pcm_hw_refine: Assertion `pcm && params' failed.
The program has unexpectedly finished.

also, I think you mean:

thread = new QThread();
thread->start();
wave *Wave = new wave();
Wave->moveToThread(thread);
connect(this, SIGNAL(startWave(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)), Wave, SLOT(on_write_loop(snd_pcm_t*, signed short*, snd_pcm_channel_area_t*)));


Got rid of the first error:QMetaObject::connectSlotsByName: No matching signal for on_write_loop(snd_pcm_t*,signed short*,snd_pcm_channel_area_t*)
I changed on_write_loop to onWriteLoop. Because a function with format on_Foo_Bar look for a children named Foo and connect that slot to Foo’s Bar signal.

Now working on the second error. Any help would be appreciated. Thanks

nix
12th August 2011, 19:59
My mistake.

The initialization of your pcm is on GUI thread and the use in the new thread that's bad, it should be the same. So you have to put the init in the Wave object and do it after the moveToThread.
So create a new slot used as on_write_loop or put the init in on_write_loop.

duma
12th August 2011, 20:52
My mistake.

The initialization of your pcm is on GUI thread and the use in the new thread that's bad, it should be the same. So you have to put the init in the Wave object and do it after the moveToThread.
So create a new slot used as on_write_loop or put the init in on_write_loop.

I am getting the following errors:
../Wave/wave.cpp:310:9: error: expected unqualified-id before ‘->’ token
../Wave/wave.cpp:311:5: error: ‘Wave’ was not declared in this scope
../Wave/wave.cpp:311:16: error: expected primary-expression before ‘=’ token
../Wave/wave.cpp:311:22: error: expected type-specifier before ‘Wave’
../Wave/wave.cpp:311:22: error: expected ‘;’ before ‘Wave’

Also, I don't believe we can create a “wave” object inside of a “wave” and, and then move that second object into a thread. Excuse me if I am wrong.

wysota
12th August 2011, 22:46
Get rid of the thread completely, read the Keeping the GUI Responsive article and employ the "step by step" pattern to your problem. Also consider making your code more "C++-ish" and less "C-ish".

duma
15th August 2011, 14:57
Get rid of the thread completely, read the Keeping the GUI Responsive article and employ the "step by step" pattern to your problem. Also consider making your code more "C++-ish" and less "C-ish".

Thanks for the reply. this seems like exactly what i need. Although, if I apply the step by step method, a time limit will be placed on my event loop. But i want my loop to remain infinite so that it doesn't stop running and only stops when I click the STOP pushButton.

wysota
15th August 2011, 15:13
Read the article again, I think you didn't understand every part of it. If something is unclear, ask. In short, there is no time limit on your whole operation, only on a single iteration of it.

duma
15th August 2011, 18:33
Read the article again, I think you didn't understand every part of it. If something is unclear, ask. In short, there is no time limit on your whole operation, only on a single iteration of it.

Got it working. thanks alot :)