QDataStream uses sometimes 32 bit and sometimes 40

2019-07-31 17:49发布

问题:

I am writing an application that is supposed to write an array of floats to a WAVE file. I am using a QDataStream for this, but this results in a very improbable output that I can't explain. It seems like the QDataStream sometimes chooses 32 bit floats and sometimes 40 bit floats. This messes up the entire output file, since it has to obey a strict format.

My code roughly looks like this:

float* array;
unsigned int nSamples;

void saveWAV(const QString& fileName) const 
{
    QFile outFile(fileName);
    if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) 
    {
        QDataStream dataStream(&outFile);
        dataStream.setByteOrder(QDataStream::LittleEndian);
        dataStream.setFloatingPointPrecision(QDataStream::SinglePrecision);

        // ... do all the WAV file header stuff ...

        for(int ii = 0; ii < nSamples; ++ii) 
            dataStream << array[ii];
    }
}

I can think of no reason of how this code could have such a side-effect. So I made a minimal example to find out what was going on. I replaced the for-loop by this:

float temp1 = 1.63006e-33f;
float temp2 = 1.55949e-32f;

dataStream << temp1;
dataStream << temp1;
dataStream << temp2;
dataStream << temp1;
dataStream << temp2;

Then I opened the output file using Matlab and had a look at the bytes written the file. Those were:

8b 6b 07 09      // this is indeed 1.63006e-33f (notice it's Little Endian)
8b 6b 07 09 
5b f2 a1 0d 0a    // I don't know what this is but it's a byte to long
8b 6b 07 09 
5b f2 a1 0d 0a

I chose the values pretty arbitrarily, they just happened to have this effect. Some values are exported as 4-byte and other ones as 5-byte numbers. Does anyone have any idea what may be the cause of this?

Edit: When checking the size of both floats, they do seem to be 4 chars long, though:

    qDebug() << sizeof(temp1); // prints '4'
    qDebug() << sizeof(temp2); // prints '4'

回答1:

The answer lies in the opening of the output file: the QIODevice::Text flag should have been left out, since it is a binary file. If the text-flag is included, a 0d character is inserted before each 0a. So each float that contains an 0a character seems a char longer because of this.

All credits for this answer go to the answers given in: Length of float changes between 32 and 40 bit



回答2:

Note: I'm not 100% sure I'm right below, and would love to hear I'm wrong, but this is how I think it is:

QDataStream has it's own serialization format, and while I did not check, that is probably related to that. Point is, it's not meant for what you are trying to do with it: write just any binary format. You can use the class, but I believe you need to use only writeRawData() method, and take care of byte order etc yourself.



回答3:

I had a similar issue even though I was not using the IODevice::Text flag. I found that adding a line

dataStream.device()->setTextModeEnabled(false);

solved the problem to make sure you are in the binary mode.



标签: c++ wav qt4.8