QByteArray, включая float - PullRequest
       56

QByteArray, включая float

0 голосов
/ 19 марта 2019

Я новичок в C ++ и QT, и на самом деле я работаю над проектом создания генератора звукового сигнала.Но моя проблема в том, что

Я создаю плавающие объекты для реализации в 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 ]

0 голосов
/ 28 марта 2019

Оператор [] в 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
}

Конечно, это полностью зависит от размера выборки и порядкового номера ...

0 голосов
/ 20 марта 2019

При написании кода для разработки звука важно позаботиться о размере каждого семпла, порядке байтов для хранения семплов в виде двоичных данных и, если необходимо, записать заголовок в данные или, если он необработанный, заголовок-less.

И если ваша цель заполнить QBuffer, вы можете написать в него через QDataStream и прочитать его обратно, если хотите.

В моем ответе япредположим Little Endian, и вместо float я буду использовать 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();
}
...