Is there any memory leak with the QAudioOutput code? - c++

I am playing an audio stream in a QThread like this:
// Setup
QAudioFormat format;
format.setFrequency(44100);
format.setChannels(2);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
format = info.nearestFormat(format);
this->m_AudioOutput = new QAudioOutput(format, this);
DECLARE_ALIGNED(16,uint8_t,audio_buffer)[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
// Playback
QIODevice *iodevice = this->m_AudioOutput->start();
for(;;) {
// Routine that fetches audio data from network
// data_size is length of the buffer
fetch_packet(&audio_buffer, data_size);
qint64 dataRemaining = data_size;
const char *b2 = (const char *)audio_buffer;
while (dataRemaining) {
qint64 bytesWritten = iodevice->write((const char *)b2, dataRemaining);
dataRemaining -= bytesWritten;
b2 = b2 + bytesWritten ;
}
msleep(10);
}
The audio plays just fine but the app's memory consumption seems to increase over time (around 2MB per minute). I was wondering if I have done something wrong. I suppose QAudioOutput should be responsible for deleting the QIODevice's buffer after it has been read and used for playback?

I don't think so, the docs says:
Starting to play an audio stream is simply a matter of calling start() with a QIODevice. QAudioOutput will then fetch the data it needs from the io device.
It it just reading data. The QIODevice should manage the buffer. To be sure, you can check the size of your buffer using QIODevice::size() and see if it is growing.

Related

Sending hex data via serial communication with QT

I am finding way to send hex data via serial communication
i searched it several times and followed some ways but it didn't work.
i checked that protocol is working with using other software that sending hex data to device
below is my code
const char data[]={0xAA,0xAA,0x01,0x00,0x00,0x0E,0x00,0x01,0x00,0x00,0x00,0x2D,0x37,0x1D,0xAA,0xAA,0x01,0x00,0x00,0x0E,0x00,0x0C,0x10,0x00,0x00,0x01,0x76,0x13};
serial->setPortName(("COM8"));
initSerialPort(); // baud rate and etc
if(serial->open(QIODevice::ReadWrite))
{
qDebug()<<"Port is open!";
if(serial->isWritable())
{
qDebug()<<"Yes, i can write to port!";
int size = sizeof(data);
serial->write(data,size);
}
}
and when i use other declare like uint16_t, uchar, write function cannot convert argument 1 from uint16_t (or uchar) to const char *
i did try also this form
QByteArray hex("AAAA0100000E00010000002D371DAAAA0100000E000C100000017613");
QByteArray data = QByteArray::fromHex(hex);
and it also didnt work
You can do this using only QByteArray like:
connect(serial, &QSerialPort::readyRead, this, &YourClass::doSomeStuff);
QByteArray arr;
arr += static_cast<char>(0xAA);
arr += static_cast<char>(0x01);
<...>
serial->setPortName("COM8");
initSerialPort(); // baud rate and etc
if(serial->open(QIODevice::ReadWrite) && serial->isWritable())
serial->write(arr);

Set up a QBuffer as a FIFO with a restricted size ? QT

I have a continuous audio stream flow from which I'd like to keep only the last 50 seconds.
I do it like so for now but I have issues:
The buffer grows beyond 50 seconds. I tried to use resize on a QByteArray to get closer from a FIFO , but it doesn't seem to care much about it and continue to grow so I guess I misunderstood the doc.
The sound get suddenly ugly when we pass the 15 sec -> I have no clue why the sound suddenly rips (if I load a 5min local sound in the same buffer, everything works great).
Last, but not least of course, the buffer doesn't behave like a FIFO and this is what I'm looking for to keep only the last 50sec
Here is my code: (Windows10 - Qt5)
buffer = new QBuffer;
arr = new QByteArray;
arr->resize(200000);
buffer->setData(*arr); // buffer->setBuffer(*arr);
buffer->open(QIODevice::ReadWrite);
dataStream.setDevice(buffer); `
m_player = new QMediaPlayer(this);
m_player->setMedia(QMediaContent(), buffer);
[...]
connect(m_player, &QMediaPlayer::durationChanged, this, &MainWindow::durationChanged);
connect(m_player, &QMediaPlayer::positionChanged, this, &MainWindow::positionChanged);
I refresh the buffer duration in the latest slot.
So the buffer gets bigger than 50sec of course and the sound get chopped after 15 sec.
Does anyone know how to set up a FIFO buffer with a restricted size with Qt?
---------------------------------------------------------- EDIT ------------------------------------------------------------------
I found this to get closer but I miss something in it :
//.cpp :
#define SAMPLE_RATE 22050
#define CHANNELS 1
#define SAMPLE_SIZE 16
#define SAMPLE_TYPE SignedInt
myAudio::myAudio()
{
formatIn.setSampleRate(SAMPLE_RATE);
formatIn.setChannelCount(CHANNELS);
formatIn.setSampleSize(SAMPLE_SIZE);
[...]
formatIn.setByteOrder(QAudioFormat::LittleEndian);
formatIn.setSampleType(QAudioFormat::SAMPLE_TYPE);
formatOut. //same than formatIn
[...]
//configure device
audioOut = new QAudioOutput(deviceOut,formatOut,0);
audioIn = new QAudioInput (deviceIn, formatIn,0);
buff.resize(0x10000); //create a rx buffer
pbuff=buff.data(); //get the buff address;
RXbuff=0; //set RX buffer pointer
qDebug()<<"File open"<<open(QIODevice::ReadWrite);
qDebug()<<"is device Sequential="<<isSequential();
audioIn->start(this); //start reading device
audioOut->setVolume(0.5); //volume 0 to 1.0
audioOut->start(this); //start writing to device
}
//QIODevice Class (Protected Functions)This function is called by QIODevice.
//send to output(Speaker)
qint64 myAudio::readData(char *data, qint64 len)
{
static quint64 TXbuff=0;
qint64 total = 0;
while (len > total && RXbuff>TXbuff)//write and synchonise buffers
{
//write data to speaker
memcpy(&data[total],&pbuff[TXbuff%0x10000], 2); //copy 2 Bytes
TXbuff+=2; //point to next buffer 16 bit location
total+=2;
}
return total; //the reset interval
}
//audio input (from Microphone)
qint64 myAudio::writeData(const char *data, qint64 len)
{
int total=0;
while (len > total)
{
memcpy(&pbuff[RXbuff%0x10000],&data[total], 2); //write 2Bytes into
circular buffer(64K)
RXbuff+=2; //next 16bit buffer location
total+=2; //next data location
}
return (total); //return total number of bytes received
}
qint64 myAudio::bytesAvailable() const{return 0;}
What I miss is pretty basic... does anyone know When/How are the methods called ?!

Sending images over TCP from labVIEW to QT

I am trying to capture images taken from a camera connected to a myRIO and send them over a TCP/IP connection from labVIEW to a QT GUI application.
My problem is that QT keeps throwing a heap pointer exception and crashing when I read the data.
Expression: is_block_type_valid(header->_block_use)
I believe this could be because the data being sent is over 35k bytes, so I tried to read the data in separate chunks, but alas am still getting the error.
Below is my function that gets called on readyRead() being emitted:
void TCPHandler::onRead() {
QByteArray byteArray;
QByteArray buffer;
QByteArray dataSize = mainSocket->read(5); //read the expected amount of bytes incoming (about 35000)
while (buffer.size() < dataSize.toInt()) {
int bytesLeft = dataSize.toInt() - buffer.size();
if (bytesLeft < 1024) {
byteArray = mainSocket->read(bytesLeft);
}
else {
byteArray = mainSocket->read(1024);
}
buffer.append(byteArray);
}
QBuffer imageBuffer(&buffer);
imageBuffer.open(QIODevice::ReadOnly);
QImageReader reader(&imageBuffer, "JPEG");
QImage image;
if(reader.canRead())
image = reader.read();
else {
emit read("Cannot read image data");
}
if (!image.isNull())
{
image.save("C:/temp");
}
else
{
emit read(reader.errorString());
}}
In the LabVIEW code I send the size of the bytes being sent first, then the raw image data:
EDIT: Connect for the slot. Also should have mentioned this is running in a separate thread to the Main GUI.
TCPHandler::TCPHandler(QObject *parent)
: QObject(parent),
bytesExpected(0)
{
mainSocket = new QTcpSocket(this);
connect(mainSocket, SIGNAL(readyRead()), this, SLOT(onRead()));
connect(mainSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TCPHandler::displayError);
}
You are sending your length as a decimal string. Then followed by the string.
I would expect that the length would be binary value. So instead of an 'I32 to String' function use a typecast with a string as the type.

Playing audio without freezing draw loop in openGL

I'm working on a project in openGL and it needs to be able to play simple sounds (mp3) from file while not interrupting the draw loop.
I've been playing around with a few different libraries (openAL, portaudio) and eventually settled on mpg123 (to load the mp3) and libao to play the mp3 back.
The current playsound function works but it blocks the openGL draw loop (ie. freezes the game) until the audio has completed playing. I have tried messing around with std::thread but it still blocked the draw loop.
Here is the audio playback function I've been testing with:
void playSound() {
mpg123_handle *mh;
unsigned char *buffer;
size_t buffer_size;
size_t done;
int err;
int driver;
ao_device *dev;
ao_sample_format format;
int channels, encoding;
long rate;
/* initializations */
ao_initialize();
driver = ao_default_driver_id();
mpg123_init();
mh = mpg123_new(NULL, &err);
buffer_size = mpg123_outblock(mh);
buffer = (unsigned char*) malloc(buffer_size * sizeof(unsigned char));
/* open the file and get the decoding format */
mpg123_open(mh, "sounds/door.mp3");
mpg123_getformat(mh, &rate, &channels, &encoding);
/* set the output format and open the output device */
format.bits = mpg123_encsize(encoding) * 8;
format.rate = rate;
format.channels = channels;
format.byte_format = AO_FMT_NATIVE;
format.matrix = 0;
dev = ao_open_live(driver, &format, NULL);
/* decode and play */
while (mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK)
ao_play(dev, (char*)buffer, done);
/* clean up */
free(buffer);
ao_close(dev);
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
ao_shutdown();
}
How would I go about fixing this so that my game continues to run smoothly and the audio plays in the background?
You should unpack small amount of audio data and feed it to an audio device every frame.
The main trick is to find out how many samples was played by device already. I'm not sure how you can do this with libao, but it pretty simple with OpenAL.
You can check details here Play stream in OpenAL library
Also, you always can use additional thread. It'll be overkill, but very simple to do and can work fine for a small/demo project.

controlling the number of bytes ready to read from Audio card in QT

In QT I can define an Audio input as:
m_audioInput = new QAudioInput(m_Inputdevice, m_format, this);
m_input = m_audioInput->start();
In my application I would like to use a mic and read from audio card.
Now if I want to see how many bytes are ready to read from Audio buffer I use:
qint64 len = m_audioInput->bytesReady();
It looks like the len is a function of sampling rate and number of bits per sample.
My question is that is there a way to control len, without changing the sampling rate? In other words I would like to control the audio card such that it reads data in shorter blocks and emits the ready signal.
You can control the audio card by setting up proper format parameters e.g., frequency, sample size. For this you need to use QAudioFormat class.
Other than this, there is no other way to control audio card from Qt.
Class Reference.
Example from the reference:
QFile outputFile; // class member.
QAudioInput* audio; // class member.
outputFile.setFileName("/tmp/test.raw");
outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
QAudioFormat format;
// set up the format you want, eg.
format.setFrequency(8000);
format.setChannels(1);
format.setSampleSize(8);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if (!info.isFormatSupported(format)) {
qWarning()<<"default format not supported try to use nearest";
format = info.nearestFormat(format);
}
audio = new QAudioInput(format, this);
QTimer::singleShot(3000, this, SLOT(stopRecording()));
audio->start(&outputFile);
// Records audio for 3000ms