Reputation: 1
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
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