Проблема опустошения QAudioOutput при воспроизведении в реальном времени с микрофона с QAudioInput - PullRequest
0 голосов
/ 23 октября 2018

Иногда я получаю "недогрузку" из библиотеки ALSA, и это означает, что аудиовход не получает значения во время воспроизведения.Затем Alsa повторяет старые значения буфера на динамике.

Как можно избежать опустошений в QAudioOuput?Я использую Qt5.9.1 и процессор на базе ARM, работающий на Debian 8.

Я пытался изменить размер буфера:

audioOutput->setBufferSize(144000);
qDebug()<<"buffersize "<<audioOutput->bufferSize()<<" period size" . 
<<audioOutput->periodSize();

Я получаю: размер буфера 144000 размер периода 0

и после audiOutput->start() я получаю: размер буфера 19200, размер периода 3840

Вот что я делаю:

audioOutput->setBufferSize(144000);
qDebug()<<"buffersize "<<audioOutput->bufferSize()<<" period size" . 
<<audioOutput->periodSize();
m_audioInput = audioInput->start();
m_audioOutput = audioOutput->start();

qDebug()<<"buffersize "<<audioOutput->bufferSize()<<" period size"< 
<<audioOutput->periodSize();
connect(m_audioInput, SIGNAL(readyRead()), SLOT(readBufferSlot()));

После записи аудиоданных я пишу вQIODevice m_audioOutput значения из QIODevice m_audioInput.

Так что я думаю, что иногда у меня возникают проблемы с синхронизацией, и аудио интервал для обоих составляет 1000 мс до и после start ().Почему я не могу увеличить размер буфера?И как я могу избежать поражения?

1 Ответ

0 голосов
/ 24 октября 2018

Исходя из моего опыта работы с QAudioOutput, его буфер предназначен только для воспроизведения в реальном времени, например, вы не можете, например, сбросить 1 минуту звука прямо на QIODevice, ожидая, что он буферизуется и воспроизводится последовательно, ноэто не означает, что вы не можете сохранить звук, просто означает, что вам нужно сделать это самостоятельно.

Я сделал следующий пример в «C-Style», чтобы создать решение «все в одном», оно буферизует 1000 миллисекунд (1 секунду) ввода перед его воспроизведением.

Цикл событий должен быть доступен для обработки Qt SIGNAL s.

В моих тестах достаточно 1-секундной буферизации, чтобы избежать недопустимых запусков.

#include <QtCore>
#include <QtMultimedia>

#define MAX_BUFFERED_TIME 1000

static inline int timeToSize(int ms, const QAudioFormat &format)
{
    return ((format.channelCount() * (format.sampleSize() / 8) * format.sampleRate()) * ms / 1000);
}

struct AudioContext
{
    QAudioInput *m_audio_input;
    QIODevice *m_input_device;

    QAudioOutput *m_audio_output;
    QIODevice *m_output_device;

    QByteArray m_buffer;

    QAudioDeviceInfo m_input_device_info;
    QAudioDeviceInfo m_output_device_info;
    QAudioFormat m_format;

    int m_time_to_buffer;
    int m_max_size_to_buffer;

    int m_size_to_buffer;

    bool m_buffer_requested = true; //Needed
    bool m_play_called = false;
};

void play(AudioContext *ctx)
{
    //Set that last async call was triggered
    ctx->m_play_called = false;

    if (ctx->m_buffer.isEmpty())
    {
        //If data is empty set that nothing should be played
        //until the buffer has at least the minimum buffered size already set
        ctx->m_buffer_requested = true;
        return;
    }
    else if (ctx->m_buffer.size() < ctx->m_size_to_buffer)
    {
        //If buffer doesn't contains enough data,
        //check if exists a already flag telling that the buffer comes
        //from a empty state and should not play anything until have the minimum data size
        if (ctx->m_buffer_requested)
            return;
    }
    else
    {
        //Buffer is ready and data can be played
        ctx->m_buffer_requested = false;
    }

    int readlen = ctx->m_audio_output->periodSize();

    int chunks = ctx->m_audio_output->bytesFree() / readlen;

    //Play data while it's available in the output device
    while (chunks)
    {
        //Get chunk from the buffer
        QByteArray samples = ctx->m_buffer.mid(0, readlen);
        int len = samples.size();
        ctx->m_buffer.remove(0, len);

        //Write data to the output device after the volume was applied
        if (len)
        {
            ctx->m_output_device->write(samples);
        }

        //If chunk is smaller than the output chunk size, exit loop
        if (len != readlen)
            break;

        //Decrease the available number of chunks
        chunks--;
    }
}

void preplay(AudioContext *ctx)
{
    //Verify if exists a pending call to play function
    //If not, call the play function async
    if (!ctx->m_play_called)
    {
        ctx->m_play_called = true;
        QTimer::singleShot(0, [=]{play(ctx);});
    }
}

void init(AudioContext *ctx)
{
    /***** INITIALIZE INPUT *****/

    //Check if format is supported by the choosen input device
    if (!ctx->m_input_device_info.isFormatSupported(ctx->m_format))
    {
        qDebug() << "Format not supported by the input device";
        return;
    }

    //Initialize the audio input device
    ctx->m_audio_input = new QAudioInput(ctx->m_input_device_info, ctx->m_format, qApp);

    ctx->m_input_device = ctx->m_audio_input->start();

    if (!ctx->m_input_device)
    {
        qDebug() << "Failed to open input audio device";
        return;
    }

    //Call the readyReadPrivate function when data are available in the input device
    QObject::connect(ctx->m_input_device, &QIODevice::readyRead, [=]{
        //Read sound samples from input device to buffer
        ctx->m_buffer.append(ctx->m_input_device->readAll());
        preplay(ctx);
    });

    /***** INITIALIZE INPUT *****/

    /***** INITIALIZE OUTPUT *****/

    //Check if format is supported by the choosen output device
    if (!ctx->m_output_device_info.isFormatSupported(ctx->m_format))
    {
        qDebug() << "Format not supported by the output device";
        return;
    }

    int internal_buffer_size;

    //Adjust internal buffer size
    if (ctx->m_format.sampleRate() >= 44100)
        internal_buffer_size = (1024 * 10) * ctx->m_format.channelCount();
    else if (ctx->m_format.sampleRate() >= 24000)
        internal_buffer_size = (1024 * 6) * ctx->m_format.channelCount();
    else
        internal_buffer_size = (1024 * 4) * ctx->m_format.channelCount();

    //Initialize the audio output device
    ctx->m_audio_output = new QAudioOutput(ctx->m_output_device_info, ctx->m_format, qApp);
    //Increase the buffer size to enable higher sample rates
    ctx->m_audio_output->setBufferSize(internal_buffer_size);

    //Compute the size in bytes to be buffered based on the current format
    ctx->m_size_to_buffer = int(timeToSize(ctx->m_time_to_buffer, ctx->m_format));
    //Define a highest size that the buffer are allowed to have in the given time
    //This value is used to discard too old buffered data
    ctx->m_max_size_to_buffer = ctx->m_size_to_buffer + int(timeToSize(MAX_BUFFERED_TIME, ctx->m_format));

    ctx->m_output_device = ctx->m_audio_output->start();

    if (!ctx->m_output_device)
    {
        qDebug() << "Failed to open output audio device";
        return;
    }

    //Timer that helps to keep playing data while it's available on the internal buffer
    QTimer *timer_play = new QTimer(qApp);
    timer_play->setTimerType(Qt::PreciseTimer);
    QObject::connect(timer_play, &QTimer::timeout, [=]{
        preplay(ctx);
    });
    timer_play->start(10);

    //Timer that checks for too old data in the buffer
    QTimer *timer_verifier = new QTimer(qApp);
    QObject::connect(timer_verifier, &QTimer::timeout, [=]{
        if (ctx->m_buffer.size() >= ctx->m_max_size_to_buffer)
            ctx->m_buffer.clear();
    });
    timer_verifier->start(qMax(ctx->m_time_to_buffer, 10));

    /***** INITIALIZE OUTPUT *****/

    qDebug() << "Playing...";
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    AudioContext ctx;

    QAudioFormat format;
    format.setCodec("audio/pcm");
    format.setSampleRate(44100);
    format.setChannelCount(1);
    format.setSampleSize(16);
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    ctx.m_format = format;

    ctx.m_input_device_info = QAudioDeviceInfo::defaultInputDevice();
    ctx.m_output_device_info = QAudioDeviceInfo::defaultOutputDevice();

    ctx.m_time_to_buffer = 1000;

    init(&ctx);

    return a.exec();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...