0

I'm trying to write into buffer for QAudioOutput every 20ms. When I try to execute this code I can see the size of process increases about 4-8 kB per second. I was trying to find some function to clear internal buffer of QIODevice or DAudioOuptut but had no luck.

I'm using Qt 5.2.1

In the example below is written only silence(zeros) but it has the same effect:

#include <QLibraryInfo>
#include <QtCore/QCoreApplication>
#include <windows.h> // for Sleep
#include <QAudioOutput>
#include <QAudioDeviceInfo>
#include <QAudioFormat>
#include <array>

class QAudioOutput;

int main(int argc, char *argv[])
{
    // Create QApplication
    QCoreApplication app(argc, argv);
    app.setApplicationName("Audiotest");
    //Initialize device
    QIODevice * _output;
    QAudioDeviceInfo _device = QAudioDeviceInfo::defaultOutputDevice();
    QAudioFormat _format;
    _format.setSampleRate(44100);
    _format.setChannelCount(2);
    _format.setSampleSize(16);
    _format.setCodec("audio/pcm");  // This codec should be supported on all platforms and plugin implementation
    _format.setByteOrder(QAudioFormat::LittleEndian);
    _format.setSampleType(QAudioFormat::SignedInt);
    if (!_device.isFormatSupported(_format)) {
        printf("Default format not supported - trying to use nearest.\n");
        _format = _device.nearestFormat(_format);
    }
    QAudioOutput * _audioOutput = new QAudioOutput(_device, _format);
    _output = _audioOutput->start();
    std::array<char, 32768> _buffer;
    _buffer.fill(0);


    for (;;) {
        const int periodSize = _audioOutput->periodSize();
        const int chunks = _audioOutput->bytesFree() / periodSize;
        for (int i = 0; i < chunks; ++i) {
            const qint64 len = periodSize;
            if (len && _output) {
                _output->write(_buffer.data(), len);
            }
            if (len != periodSize) {
                break;
            }
        }
        Sleep(20);
    }
    return 0;
}
Filip
  • 3
  • 2

2 Answers2

3

When your loop runs, nothing else does. Your code should be asynchronous, and you should invert the control flow. React to a notification by the audio output device that it has processed a certain interval of samples.

To receive the first notification, you need to prime the device with some data.

// https://github.com/KubaO/stackoverflown/tree/master/questions/audio-37993427
#include <QtMultimedia>
#include <array>

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   auto device = QAudioDeviceInfo::defaultOutputDevice();
   QAudioFormat format;
   format.setSampleRate(44100);
   format.setChannelCount(2);
   format.setSampleSize(16);
   format.setCodec("audio/pcm");
   format.setByteOrder(QAudioFormat::LittleEndian);
   format.setSampleType(QAudioFormat::SignedInt);
   if (!device.isFormatSupported(format))
      qFatal("Default format not supported");
   QAudioOutput audioOutput{device, format};
   auto output = audioOutput.start();
   qDebug() << audioOutput.state();
   std::array<char, 32768> buffer;
   buffer.fill(0);

   auto write = [&]{
      qDebug() << "notify";
      auto periodSize = audioOutput.periodSize();
      auto chunks = audioOutput.bytesFree() / periodSize;
      for (int i = 0; i < chunks; ++i) {
         if (periodSize && output) {
            auto len = output->write(buffer.data(), periodSize);
            if (len != periodSize)
               break;
         }
      }
   };

   audioOutput.setNotifyInterval(20);
   QObject::connect(&audioOutput, &QAudioOutput::notify, write);
   write();
   return app.exec();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

Don't run your own event loop; instead, connect the QAudioOutput's notify signal to a slot in one of your QObjects, and have that slot call write() one time. The notify signal will be emitted whenever the QAudioOutput needs some more audio data to play.

All of this will happen inside QApplication::exec(), which you should call (near the end of main()) to run the Qt event loop for you, rather than your own for-loop.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234