QByteArray, включая float
Я новичок в C++ и QT, и на самом деле я работаю над проектом создания генератора звукового сигнала. Но моя проблема в том,
Я создаю float для реализации в qbytearray, который я буду использовать для заполнения qbuffer. Но я не могу получить float в qbytearray, он выдает предупреждение "теряю точность". И qbytearray состоит только из целочисленных значений от -100 до 100. Мне нужно с плавающей точкой с желаемой точностью.
Вы можете мне помочь?
void MainWindow::toneGenerate(){
int len= m_seconds*SAMPLE_RATE;
sinbuf.resize(len);
for(int i=0;i<len;i++){
qreal t = m_freq*i;
t *= FREQ_CONST;
t = t+ m_ph;
t = qSin(t);
t*= m_amp;
sinbuf[i] = t;
}
sininput.setBuffer(&sinbuf);
sininput.open(QIODevice::ReadWrite);
}
2 ответа
При написании кода для разработки звука важно позаботиться о размере каждого семпла, порядке байтов для хранения семплов в виде двоичных данных и, если необходимо, написать заголовок данных или, если он необработанный, без заголовка.
И если ваша цель заполнить QBuffer
Вы можете написать в него через QDataStream
и прочитайте его обратно, если хотите.
В своем ответе я буду использовать Little Endian и вместо числа с плавающей запятой я буду использовать 16-битные целочисленные выборки со знаком, 1 канал и частоту 8000 Гц.
Я привожу простой пример генератора тона, пожалуйста, адаптируйте его под свои нужды!
Давайте посмотрим на следующий пример консоли:
#include <QtCore>
#include <QtMultimedia>
static QBuffer m_float_buffer;
void toneGenerator()
{
QDataStream write_stream(&m_float_buffer);
write_stream.setVersion(QDataStream::Qt_5_0); //Protocol for version 5.0
write_stream.setByteOrder(QDataStream::LittleEndian);
//Tone generator from http://www.cplusplus.com/forum/general/129827/
const unsigned int samplerate = 8000;
const unsigned short channels = 1;
const double pi = M_PI;
const qint16 amplitude = qint16(INT16_MAX * 0.5);
const unsigned short n_frequencies = 8;
const unsigned short n_seconds_each = 1;
float frequencies[n_frequencies] = {55.0, 110.0, 220.0, 440.0, 880.0, 1760.0, 3520.0, 7040.0};
const int n_samples = channels * samplerate * n_frequencies * n_seconds_each;
int index = n_samples / n_frequencies;
for (unsigned short i = 0; i < n_frequencies; i++)
{
float freq = frequencies[i];
float d = (samplerate / freq);
int c = 0;
for (int j = index * i; j < index * (i + 1); j++)
{
float deg = 360.0f / d;
write_stream << qint16(qSin((c++ * double(deg)) * pi / 180.0) * amplitude);
}
}
}
void dataPlay()
{
QAudioFormat format;
format.setCodec("audio/pcm");
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(format))
{
qDebug() << "Raw audio format not supported by backend, cannot play audio.";
return;
}
QAudioOutput audio(format);
QEventLoop loop;
QObject::connect(&audio, &QAudioOutput::stateChanged, &audio, [&](const QAudio::State state){
if (state != QAudio::ActiveState)
loop.quit();
});
audio.start(&m_float_buffer);
loop.exec();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Opening buffer...";
m_float_buffer.open(QBuffer::ReadWrite);
qDebug() << "\nGenerating...";
toneGenerator();
//Back to beginning, now for reading
m_float_buffer.seek(0);
qDebug() << "\nPlaying...";
dataPlay();
qDebug() << "\nQBuffer size:" << m_float_buffer.size() << "bytes";
return a.exec();
}
[]
оператор на QByteArray
ссылается только на один байт (длина 8 бит), однако число с плавающей запятой составляет 4 байта (длина 32 бита).
Вместо sinbuf[i] = t;
который будет хранить только первые 8 битов с плавающей точкой, вы должны хранить весь с плавающей точкой, которая будет хранить все 32 бита.
Эта функция шаблона будет возвращать QByteArray
что вы можете добавить к sinbuf
template<typename T>
static QByteArray numToByteArray(T num, bool isLE = false)
{
QByteArray ba("");
if(isLE){
ba.resize(sizeof(T));
memcpy(ba.data(), &num, sizeof(T));
}
else{
for(int i=sizeof(T)-1; i>=0; i--)
ba.append(quint8(num>>(i*8)));
}
return ba;
}
Использование:
void MainWindow::toneGenerate(){
int len= m_seconds*SAMPLE_RATE;
//sinbuf.resize(len); calls to append will resize for you
for(int i=0;i<len;i++){
qreal t = m_freq*i;
t *= FREQ_CONST;
t = t+ m_ph;
t = qSin(t);
t*= m_amp;
//You will have to account for endianness
//Pass true as a second argument here if it's Little Endian
sinbuf.append(numToByteArray<float>(t));
}
sininput.setBuffer(&sinbuf);
sininput.open(QIODevice::ReadWrite);
//You will want to write directly to the device stream
//because sinbuf will store everything in memory
}
Конечно, это полностью зависит от размера выборки и порядка байтов...