使用QAudioOut
,我试图按顺序播放存储在QByteArray
中的数据...当添加的数据很少时,此方法有效,但是当数据过多时,可以说如果将2到3小时的RAW PCM从不同的组合追加到QByteArray
一次,则由于堆的大小不足以同时容纳所有数据,因此将一次生成std::bad_alloc
。
我知道问题出在哪里,我想我有一个可能的解决方案,只是我不知道如何实施。
下面是一个转换后的函数,它采用列表中的值
第一个440Hz,持续1800000毫秒,并创建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++ Creating a square audio tone wave. Play and saving it
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";
}
答案 0 :(得分:0)
我想出了一个使用QThread和一个解释器的解决方案,让该线程在继续下一个序列之前等待更多事件。
通过这样做,我不需要同时将全部3到4个小时的PCM数据全部加载到QBufferArray中,而是将其分解。运行一个较小的序列,然后等待线程完成,然后在行中加载下一个序列,依此类推,直到所有序列都播放完毕。
不再需要std :: bad_alloc了,因为线程在任何给定时间仅在堆上使用约80mb。