D Dob
D Dob

Reputation: 1

convert 24 bit binary char array to double (reading binary wave data)

I'm trying to read 24 bit binary data which is stored in a wave file in 2's Complement. It's stored as little endian. I'm trying to reinterpret the 3 char arrays as int32_t und then convert it to double. I'm getting very close but it's not working properly somehow.

if (wavHead.getBits_per_Sample() == 24) {
        unsigned char int24[3];
        while (!is.eof()) {
            int32_t val = 0;
            is.read((char *) &int24, sizeof(char) * 3); //ifstream
            if(int24[2] & 0x80) {
                val = 0xff;         //Checking for sign (MSB) and inverting it to bitshift to int32 MSB
                int24[2] &= ~(0x80);
            }
            val = (val << 8) | ((u_int32_t)int24[2]);
            val = (val << 8) | ((u_int32_t)int24[1]);
            val = (val << 8) | ((u_int32_t)int24[0]);

            cout << val / 8388608.0 << endl;
            data.push_back(val / 8388608.0);
        }
    }

Here is a plot of the signal. It should be a simple 440 Hz Sine tone but it's not centered and looking a bit dubious : plot of the wave file

Upvotes: 0

Views: 139

Answers (1)

user555045
user555045

Reputation: 64913

This is wrong:

int24[2] &= ~(0x80);

Consider the number -1, encoded into 3 bytes as FF FF FF. The result we want in val is 0xFFFFFFFF, there is no unset bit in it, and that applies in general: when sign-extending, the bit that you're extending from is not changed itself. Resetting it adds an offset of -8388608 to negative vals, and therefore an offset of -1.0 to negative results (which matches the plot). Simply removing that line should do the trick. Here is an alternative way to sign-extend as well (and there are others):

// note: nothing before this, the sign extension is applied *after* rebuilding
val = (u_int32_t)int24[2];
val = (val << 8) | ((u_int32_t)int24[1]);
val = (val << 8) | ((u_int32_t)int24[0]);
val = (val ^ (1 << 23)) - (1 << 23);

Upvotes: 0

Related Questions