PDA

View Full Version : Questions about QAudioOutput, QIODevice. Play wav file



Smosia
18th March 2014, 15:47
Hello everybody

I'am writing a program that will play wav files and add some sound effects.
And now I want to ask you some questions about problems, that I faced.

First of all I tried to use write method from QIODevice


QAudioDevice * m_audioOutput;
QIODevice * m_output;
QFile file;
QByteArray buff;

....
file.seek(44);
buff = file.read(44000);

m_audioOutput = new QAudioDevice(format,this);
m_output = m_audioOutput->start();

int size = m_output->bytesToWrite(); //size =0
int bytesWritten = m_output->write(buff.data(),buff.size()); // bytesWritten = 8192


Why was written only 8192 bytes, instead of 44000?

Ok, i sad to myself. It isn't a big problem



QQueue<QByteArray> queue;
QByteArray buff;
QEventLoop loop;

for (int i=0;i<50;i++)
{
queue.enqueue(file.read(8192));
}

QObject::connect(m_output,SIGNAL(bytesWritten(qint 64)),&loop,SLOT(quit()));
while (!queue.isEmpty())
{
buff = queue.dequeue();
m_output->write(buff.data(),buff.size());
loop.exec();
}


I heard a small pop from speakers. It was first 8192 bytes written to QIODevice. Then program freezes in the loop.
Why signal bytesWritten() doesn't stop loop?
Ok, I said to myself, lets try something else.



QQueue<QByteArray> queue;
QByteArray buff;
QEventLoop loop;

for (int i=0;i<50;i++)
{
queue.enqueue(file.read(8192));
}

QObject::connect(m_audioOutput,SIGNAL(stateChanged (QAudio::State)),&loop,SLOT(quit()));
while (!queue.isEmpty())
{
buff = queue.dequeue();
m_output->write(buff.data(),buff.size());
loop.exec();
}


Oh yeah! I hear music, but with lags. It was like 8192 bytes played, then silence for about half of a second, then another 8192 bytes was played.

So I decide to change everything.
I decide to use timer.



#define TIMER_PLAY 30

QQueue<QByteArray> queue;
QByteArray buff;
QEventLoop loop;

int bytesToRead = TIMER_PLAY*44100*4/1000; //bytesToRead - it is how many bytes will play in 30 milliseconds between timerEvent
for (int i=0;i<50;i++)
{
queue.enqueue(file.read(bytesToRead));
}

startTimer(TIMER_PLAY,Qt::PreciseTimer); // class Player : public QObject

void Player::timerEvent(QTimerEvent *)
{
buff = queue.dequeue();
m_output->write(buff.data(),buff.size());
}


I hear music! it works. I was happy :)
My program was growing bigger and bigger. I add some QThreads and some math calculations. And faced new problem.
Timer can't gave me exactly 30 milliseconds and now i can hear some annoying lags (milliseconds).
So i decide to write a post here.

Could you help me please.
1. Why I can write only 8192 bytes to QIODevice?
2. What i'm doing wrong with loops? What should I do to hear music?

Thank you for your attention.

wysota
18th March 2014, 16:50
The problems you are having are because of assuming you can stick an infinite amount of water into a finite pipe in an infinitely short amount of time. If you have 100kB of data and the device you are using only buffers 8kB of data then that's the amount of data you can stick into it without overfilling the buffer. You can easily see that if you check the return value of write(). A proper approach is to monitor this value and repeat the write with the remaining data once the device buffer level drops (e.g. when bytesWritten() is emitted).

Smosia
18th March 2014, 19:22
I tried that approach. I wrote about that in my post. It doesn't work.


QQueue<QByteArray> queue;
QByteArray buff;
QEventLoop loop;

for (int i=0;i<50;i++)
{
queue.enqueue(file.read(8192));
}

QObject::connect(m_output,SIGNAL(bytesWritten(qint 64)),&loop,SLOT(quit())); //bytesWritten doesn't stop the loop;
while (!queue.isEmpty())
{
buff = queue.dequeue();
m_output->write(buff.data(),buff.size());
loop.exec();
}


I tried to use method QIODevice::waitForBytesWritten() but doesn't work, because my audio device has no buffer (m_output->bytesToWrite() returns 0)

Smosia
19th March 2014, 13:15
I find method QAudioOutput::setBufferSize(int). Now i can change buffer size. How I could be so blind :)
But question is still open. How to make bytesWritten() signal work. It doesn't call after write() ends. What i am doing wrong?