简体   繁体   English

QByteArray包括花车

[英]QByteArray including floats

I am a newbie of C++ and QT and i am actually on a project of writing a sound signal generator. 我是C ++和QT的新手,我实际上是在写一个声音信号发生器的项目。 But my problem is, 但是我的问题是

I am creating the floats to be implemented in qbytearray which i will use to fill qbuffer. 我正在创建要在qbytearray中实现的浮点数,我将用它来填充qbuffer。 But i can not get float into the qbytearray, it gives a warning saying "losing precision". 但我不能浮入qbytearray,它会发出警告说“失去精确度”。 And qbytearray consists of only integer values from -100 to 100. i need the floats with desired precision. 并且qbytearray只包含-100到100之间的整数值。我需要具有所需精度的浮点数。

Can you help me ? 你能帮助我吗 ?

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);
}

When writing code for sound development, is important take care about the size of each sample, the byte order for storing the samples as binary data, and if is necessary to write a header to the data, or if it's raw, header-less. 在为声音开发编写代码时,重要的是要注意每个样本的大小,将样本存储为二进制数据的字节顺序,以及是否需要将数据写入数据,或者如果它是原始的,无标题。

And if your goal is fill a QBuffer you can write to it trough QDataStream , and read it back if you will. 如果您的目标是填充QBuffer您可以通过QDataStream写入它,如果愿意,可以将其读回。

In my answer, I'll assume Little Endian, and instead of float, I'll use 16 bits signed integer samples, 1 channel and 8000Hz frequency. 在我的回答中,我将假设Little Endian,而不是浮点数,我将使用16位有符号整数样本,1通道和8000Hz频率。

I'm providing a simple example of tone generator, please adapt it to your needs! 我提供了一个简单的音调发生器示例,请根据您的需求进行调整!

Let's see the following console example: 让我们看看以下控制台示例:

#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();
}

The [] operator on a QByteArray only references a single byte (8 bits long) however a float is 4 bytes (32 bits long). QByteArray上的[]运算符仅引用单个字节(8位长),但浮点数为4个字节(32位长)。

Instead of sinbuf[i] = t; 而不是sinbuf[i] = t; which will only store the first 8 bits of the float, you should store the entire float which will store all 32 bits. 它只存储浮点数的前8位,你应该存储整个浮点数,它将存储所有32位。

This template function will return a QByteArray that you can append to sinbuf 此模板函数将返回一个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;
}

Usage: 用法:

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
}

Of course this is entirely dependent on the sample size and endianness... 当然这完全取决于样本大小和字节顺序......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM