簡體   English   中英

使用QtAudioOutput處理大型QByteArray會導致std :: bad_alloc

[英]Handeling large QByteArray with QtAudioOutput causes std::bad_alloc

使用QAudioOut Im嘗試按順序播放存儲在QByteArray中的數據...這在附加少量數據時起作用,但是當數據過多時,可以說是從不同組合附加2〜3小時的RAW PCM由於堆的大小不足以同時容納所有數據,因此將這些數據一次全部存儲到QByteArray將導致std::bad_alloc

我知道問題出在哪里,我想我有一個可能的解決方案,只是我不知道如何實現它。

下面是一個轉換后的函數,它以1800000毫秒的速度獲取列表中第一個440Hz的值,並創建了RAW PCM方波。 然后工作,將其附加到QByteArray然后播放它。

如果沒有來自多個已添加序列的大量附加數據,這將起作用。

我正在尋找一種方法,可以一次從列表中進行一次操作,然后創建wave,將其播放x毫秒,然后轉到MySeq列表中的下一個條目。 該列表可以包含3分鍾頻率的大型序列,持續數小時。

QStringList MySeq;

MySeq << "1:440:180000";
MySeq << "1:20:180000";
MySeq << "1:2120:180000";
MySeq << "1:240:180000";
MySeq << "1:570:180000";

foreach(QString seq, MySeq)
{
    QStringList Assits = seq.split(":");

    qDebug() << "Now At: " << seq;

    QString A = Assits.at(0);
    QString B = Assits.at(1);
    QString C = Assits.at(2);

    qreal amplitude = A.toInt();
    float frequency = B.toFloat();
    int msecs = C.toInt();

    qreal singleWaveTime = amplitude / frequency;

    qreal samplesPerWave = qCeil(format->sampleRate() * singleWaveTime);

    quint32 waveCount = qCeil(msecs / (singleWaveTime * 1000.0));

    quint32 sampleSize = static_cast<quint32>(format->sampleSize() / 8.0);

    QByteArray data(waveCount * samplesPerWave * sampleSize * format->channelCount(), '\0');

    unsigned char* dataPointer = reinterpret_cast<unsigned char*>(data.data());

    for (quint32 currentWave = 0; currentWave < waveCount; currentWave++)
    {
        for (int currentSample = 0; currentSample < samplesPerWave; currentSample++)
        {
            double nextRadStep = (currentSample / static_cast<double>(samplesPerWave)) * (2 * M_PI);

            quint16 sampleValue = static_cast<quint16>((qSin(nextRadStep) > 0 ? 1 : -1) * 32767);

            for (int channel = 0; channel < format->channelCount(); channel++)
            {
                qToLittleEndian(sampleValue, dataPointer);
                dataPointer += sampleSize;
            }
        }
    }

    soundBuffer->append(data); // HERE IS THE Std::Bad_Alloc
    output->start(outputBuffer); 
    qDebug() << data.size()
}

我希望一次只用一個序列填充QByteArray ,然后用QAudioOutput播放它,然后清除ByteArray然后加載下一個序列重復,直到在QStringList中完成所有序列。

現在,這種方法的問題在於QAudioOutput是異步的,並且不等待第一個序列結束。如果如上所述循環顯示列表,則它們將依次加載,而實際上僅播放最后一個頻率。 就像循環一樣,繼續覆蓋之前的序列。

我不確定這里是否需要QEventLoop (我還沒有使用過的東西)或線程。 我嘗試了幾種不同的方法,但均未成功。 任何建議將不勝感激。 這是我對以下有關波形文件以及數據和頻率生成的以下問題的延續: Qt C ++創建方音頻波。 播放並保存

mainWindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtCore>
#include <QtMultimedia/QAudioOutput>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:

    void playbackFinished();

private slots:
    void appendSound();

    void on_pushButton_Run_clicked();

    void on_pushButton_Stop_clicked();

private:
    Ui::MainWindow *ui;

    QByteArray   *soundBuffer;
    QBuffer      *outputBuffer;
    QAudioFormat *format;
    QAudioOutput *output;
};

#endif // MAINWINDOW_H

mainWindows.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>

int sampleRate = 44100;
int channelCount = 2;
int sampleSize = 16;
const QString codec = "audio/pcm";

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    soundBuffer = new QByteArray();

    format = new QAudioFormat();
    format->setSampleRate(sampleRate);
    format->setChannelCount(channelCount);
    format->setSampleSize(sampleSize);
    format->setCodec(codec);
    format->setByteOrder(QAudioFormat::LittleEndian);
    format->setSampleType(QAudioFormat::UnSignedInt);

    output = new QAudioOutput(*format, this);

    connect(output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(playbackFinished()));

    outputBuffer = new QBuffer(soundBuffer);
    if (outputBuffer->open(QIODevice::ReadOnly) == false) {
        qCritical() << "Invalid operation while opening QBuffer. audio/pcm";
        return;
    }
}

MainWindow::~MainWindow()
{
    delete ui;
    delete soundBuffer;
    delete format;
    delete output;
    delete outputBuffer;
}

void MainWindow::playbackFinished()
{
    if (output->state() == QAudio::IdleState)
    {
        qDebug() << "Playback finished";
    }
}

void MainWindow::appendSound()
{

    QStringList MySq;

    MySq << "1:440:180000";
    MySq << "1:20:180000";
    MySq << "1:2120:180000";
    MySq << "1:240:180000";
    MySq << "1:570:180000";
    MySq << "1:570:180000";
    MySq << "1:570:180000";
    MySq << "1:850:180000";
    MySq << "1:1570:180000";
    MySq << "1:200:180000";
    MySq << "1:50:180000";
    MySq << "1:85:180000";
    MySq << "1:59:180000";
    MySq << "1:20:180000";

    foreach(QString seq, MySq)
    {
        QStringList Assits = seq.split(":");

        qDebug() << "Now At: " << seq;

        QString A = Assits.at(0);
        QString B = Assits.at(1);
        QString C = Assits.at(2);

        qreal amplitude = A.toInt();
        float frequency = B.toFloat();
        int msecs = C.toInt();

        msecs = (msecs < 50) ? 50 : msecs;

        qreal singleWaveTime = amplitude / frequency;

        qreal samplesPerWave = qCeil(format->sampleRate() * singleWaveTime);

        quint32 waveCount = qCeil(msecs / (singleWaveTime * 1000.0));

        quint32 sampleSize = static_cast<quint32>(format->sampleSize() / 8.0);

        QByteArray data(waveCount * samplesPerWave * sampleSize * format->channelCount(), '\0');

        unsigned char* dataPointer = reinterpret_cast<unsigned char*>(data.data());

        for (quint32 currentWave = 0; currentWave < waveCount; currentWave++)
        {
            for (int currentSample = 0; currentSample < samplesPerWave; currentSample++)
            {
                double nextRadStep = (currentSample / static_cast<double>(samplesPerWave)) * (2 * M_PI);

                quint16 sampleValue = static_cast<quint16>((qSin(nextRadStep) > 0 ? 1 : -1) * 32767);

                for (int channel = 0; channel < format->channelCount(); channel++)
                {
                    qToLittleEndian(sampleValue, dataPointer);
                    dataPointer += sampleSize;
                }
            }
        }

        soundBuffer->append(data); // <-- STD::Bad_alloc Not enough memory on heap for appending all the frequencies at once in buffer 
        output->start(outputBuffer);
        qDebug() << data.size();
    }
}

void MainWindow::on_pushButton_Run_clicked()
{
    appendSound();
}

void MainWindow::on_pushButton_Stop_clicked()
{
    output->stop();
    soundBuffer->clear();
    output->reset();
    qDebug() << "Playback Stopped";
}

我想出了一個使用QThread和解釋器的解決方案,讓該線程在繼續下一個序列之前,等待更多事件。

這樣,我不需要將全部3到4個小時的PCM數據全部同時加載到QBufferArray中,而是將其分解。 運行一個較小的序列,然后等待線程完成,然后在行中加載下一個序列,依此類推,直到所有序列都播放完畢。

不再需要std :: bad_alloc了,因為在任何給定時間,線程僅在堆上使用大約80mb。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM