Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

What's the correct/efficient way of converting char* array to float array in QIODevice::writeData?



  • In my app I am receiving audio data from the selected QIODevice. The audio signal is 32bit values between -1 and 1.

    In my QIODevice::writeData function, I still first convert the signal into integer, as the qFromLittleEndian and qFromBigEndian only works with int types (I get values between -1624492672 and 1624492672). And then divide the value by 2^31 to convert them into float. Is there a more correct/efficient way to directly convert the received data into float, instead of first int and then float value?

    Here is a simplified version of my QIODevice::writeData function:

    MyIODevice::writeData(const char *data, qint64 len)
    {
        int channelCount = m_audioFormat.channelCount();
        int sampleSize   = m_audioFormat.sampleSize();
    
        int channelBytes = sampleSize / 8;
        int sampleBytes = channelCount * channelBytes;
    
        int numSamples = len / sampleBytes;
        const bool isLittleEndian = m_audioFormat.byteOrder() == QAudioFormat::LittleEndian;
        QVector<qint32> sampleValues(channelCount);
    
        for (int sample = 0; sample < numSamples; sample++)
        { 
               isLittleEndian ? qFromLittleEndian<qint32>(data, channelCount, sampleValues.data()) :
                                 qFromBigEndian<qint32>(data, channelCount, sampleValues.data());
               // then before using the sampleValues, sampleValue[index] will be divided by 2^31 to convert the value into a float value between -1, 1
               // ex.  sampleValues[0] / 2147483648.0f 
               // the approach only sort of works because the values are between -0.756463 and 0.756463
               // instead of -1 and 1
      
                // send the received value to the ring buffer to store
               // emit receiveSample(sampleValues); 
        }
    
        return len;
    }
    


  • Are you using QAudioInput?
    If the audio format is float, there's no such big/little endian thing, you should just cast the pointer to a float*.

    const float* floatPtr = reinterpret_cast<const float*>(data);
    

    How are you planning to use the vector then? Acctually I don't think there is any need to use a vector.
    It is already a float array if you cast the pointer.



  • @Bonnie thanks for the reply! Yes I use QAudioInput.
    When I print the audio format used for QAudioInput, it shows:
    QAudioFormat(44100Hz, 32bit, channelCount=64, sampleType=SignedInt, byteOrder=BigEndian, codec="audio/pcm"). (It's confusing as the audio signal is indeed a float value between -1, 1, but it says the sample type is SignedInt)

    Do you think it could be directly casted to float nevertheless?

    I plan to store the float array into a ring buffer (I've updated my question to show that each individual sample will be sent to the ringBuffer through a singal). There is another object that will fetch the float arrays from the ring buffer and convert them into information for drawing (x, y, red, green blue. etc). Do you think QVector is indeed not needed in this case?

    Also I just tried what you suggested

    const float* floatPtr = reinterpret_cast<const float*>(data)
    

    When I print floatPtr[0] floatPtr[1] ... the value doesn't seem to be right (when I send signal value 1, the float value is -1.86266e-38)



  • @Thinium
    I'm guessing that you write you code referring to the audio input example?
    The casting is also in it.

    ...
    else if (m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::Float) {
        value = qAbs(*reinterpret_cast<const float*>(ptr) * 0x7fffffff); // assumes 0-1.0
    }
    ...
    

    So if the condition is m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::Float, then that's the right way.
    [EDITED]
    But as you said the format is signed int but actually it is a float, then I'm confusing, too. How would that happen?
    The only approach I would think of is, reading with qFromBigEndian<qint32>, but also casting the value to float.
    According the code in your previous post, something like

    QVector<float> destination(channelCount);
    qFromBigEndian<qint32>(data, channelCount, destination.data());
    

    If this still cannot get the correct value, then I'm out of ideas.

    About the vector / array...It seems that you are drawing some kind of spectrum chart?
    Hmm...I was assuming the calculating is also here, which would be no need to use the vector ([ADDED] if there's no big/little endian conversion).
    I didn't expect that you need to send a signal for every sample. Yes, you may need the QVector in that case.
    But I'm worrying about the performance. It may be too heavy to copy and send every sample like that.



  • @Bonnie said in What's the correct/efficient way of converting char* array to float array in QIODevice::writeData?:

    QVector<float> destination(channelCount);
    qFromBigEndian<qint32>(data, channelCount, destination.data());

    Thanks for the suggestions! Yes, I am visualizing the received data (but not exactly audio spectrum as they could also be interpreted as other visual data, like color, etc.) I tried the above approach you suggested, and it unfortunately doesn't give the expected values. So I think I'll still need to convert the integers to floats manually.

    And you are totally right about the performance issue. That is why I am trying to optimize it. Maybe I need to consider threading, or avoid copying if possible.

    Thanks again for your help! @Bonnie!


Log in to reply