czwartek, 13 sierpnia 2015

Android, QAudioOutput and callback in separate thread.


QAudio (both, either output and input) was something I've been fed up very quickly on desktop. I prefer RtAudio and Nootka works with it pretty good.
But I gave a once more try of it under Android, to keep hands away from Jni.
The start was painful.
QIODevice *outDev = audioOutput-<start();

Returned QIODvice outDev was very "unresponsive". audioOutput->bytesFree() returned only declared buffer size but not real amount of free data. outDev device didn't emit any signals to inform about need of a new audio data,
even audioOutput device didn't emit notify() signal.
The only way to feed device with data was a QTimer event loop and invoking outDev->write().

Another way is tricky.
Instead of starting output with QIODevice *outDev = audioOutput->start ();
we may start it like this:
audioOutput->start(ourDevice);
where ourDevice is an instance of tricky QIODevice subclass.
When "real device" will want a data, it will call
ourDevice->read(char* data, qint64 maxLen);
As Qt doc says: http://doc.qt.io/qt-5/qiodevice.html
by re-implementation of readData(char* data, qint64 maxLen) we get access to it.
Moreover, this call is performed in different thread - "the real device" one.
We may feed char* data just in readData() method, but I prefer to emit signal
and write data in more suitable place.

Important thing! Such signal has to be connected with any slot with
Qt::DirectConnection flag:
connect(ourDevice, &ProxyDevice::feedAudio, someObject, &SomeObject::feedAudioSlot, Qt::DirectConnection);


class ProxyDevice : public QIODevice
{

Q_OBJECT

public:
/** Constructor does almost nothing, just initializes size of fake buffer. */
ProxyDevice(QObject* parent = 0) : QIODevice(parent), m_bufferSize(2048) {}

/** In fact, there is no any buffer!
* That value controls size of data to get by feedAudio()
* instead of maxlen value sending in readData(data, maxlen) */
void setBufferSize(qint64 s) { m_bufferSize = s; }
qint64 bufferSize() const { return m_bufferSize; }

signals:
/** char* is pointer to data, qint64 is its size 
* and qint64& is reference where connected slot will return size of data has been written.  */
void feedAudio(char*, qint64, qint64&);

protected:

/** This is heart of the class:
* feedAudio(data, m_bufferSize, wasRead) signal is emitted
* to get m_bufferSize bytes into data
* and into reference of wasRead connected slot
* returns amout of bytes were put. */
virtual qint64 readData(char *data, qint64 maxlen) {
Q_UNUSED(maxlen)
qint64 wasRead = 0;
emit feedAudio(data, m_bufferSize, wasRead);
return wasRead;
}

/** Dummy - does nothing */
virtual qint64 writeData(const char *data, qint64 len) { return 0; }

private:
qint64          m_bufferSize;

};

When connected slot will not write any data and returns 0 in reference - device will go into Idle state.


Brak komentarzy:

Prześlij komentarz