【问题标题】:QByteArray including floatsQByteArray 包括浮点数
【发布时间】:2019-08-10 06:34:40
【问题描述】:

我是 C++ 和 QT 的新手,实际上我正在编写一个声音信号发生器的项目。但我的问题是,

我正在创建要在 qbytearray 中实现的浮点数,我将使用它来填充 qbuffer。但是我不能浮动到 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);
}

【问题讨论】:

  • 如果sinbufQByteArray 对象(如果没有minimal reproducible example 很难分辨),那么&amp;sinbuf 是指向QByteArray 对象的指针,而不是指向它包含的数据的指针。
  • operator[] for QByteArray 返回 char&amp;,因此您仅将来自 qreal 的一个字节存储在 sinbuf[i] = t 中(如果 qreal 为 double,则不会存储来自 double 的 7 个字节)。您需要将 qreal 中的所有字节添加到 QByteArray - 读取 this topic。将 qreal 存储在 QVector 中,然后将其转换为 QByteArray。
  • 谢谢。将尝试制作一个 qvector 并将其转换为 qbytearray
  • 试过了。 qvector 已被真正填充。但是当我尝试将它写入 qbytearray 时,它只是随机放置浮点数。尝试了@rafix07 提供的帖子中的所有方法。

标签: c++ qt qbytearray qbuffer


【解决方案1】:

QByteArray 上的 [] 运算符仅引用单个字节(8 位长),但浮点数是 4 字节(32 位长)。

您应该存储整个浮点数,而不是只存储浮点数的前 8 位的 sinbuf[i] = t;

此模板函数将返回一个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
}

当然,这完全取决于样本大小和字节序...

【讨论】:

    【解决方案2】:

    在为健全的开发编写代码时,重要的是要注意每个样本的大小、将样本存储为二进制数据的字节顺序,以及是否需要向数据写入标头,或者如果它是原始的,标头- 少。

    如果您的目标是填写QBuffer,您可以通过QDataStream 给它写信,如果您愿意,可以将其读回。

    在我的回答中,我将假设 Little Endian,而不是浮点数,我将使用 16 位有符号整数样本、1 个通道和 8000Hz 频率。

    我提供了一个简单的音源示例,请根据您的需要进行调整!

    让我们看下面的控制台示例:

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

    【讨论】:

    • 非常感谢您的回复。我做到了,但现在音频输出只会产生肮脏的噪音。会不会是样本量的问题?
    • int len= m_secondsSAMPLE_RATE; QDataStream write_stream(&sininput); write_stream.setVersion(QDataStream::Qt_5_0); write_stream.setByteOrder(QDataStream::LittleEndian); write_stream.setFloatingPointPrecision(QDataStream::SinglePrecision); sininput.open(QIODevice::WriteOnly); for(int i=0;ii/2.0f; t *= FREQ_CONST; t = t+ m_ph; t = qSin(t); t*= m_amp;写流
    • sininput.open(QIODevice::ReadOnly); sininput.seek(0);音频 = 新 QAudioOutput(格式,此);音频->开始(&sininput);
    • 实际上它同时给出了4个不同的频率
    • 实际上现在它会生成一个方形音频。当我绘制它时。这是一个很好的正弦波数据,但是当我输出流时它变成了一个正方形。怎么会这样?
    猜你喜欢
    • 1970-01-01
    • 2015-01-11
    • 2016-08-19
    • 1970-01-01
    • 2019-09-28
    • 2015-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多