This Time Self-Hosted
dark mode light mode Search

The mystery of the a52dec decoded samples

I’ve been working a lot on the audio conversion code for xine-lib, with results that are always more satisfying, although Amarok fails to build on this new version, audio playback improved a lot, and with the code being all self-contained, the option of replacing the functions with more optimised code, using MMX, SSE, AltiVec or whatever other SIMD extension is more feasible.

Unfortunately I’ve hit one issue: beside A52 and DTS having a channel order different than most of other formats, I have an issue with the type of the decoded samples. There’s a function called float_to_int() that I supposed truncated the floating-point values to get 16-bit signed integer samples (that were the only format supported by xine before), but the floats themselves don’t play at all, while floating-point data plays fine for most other formats. I had to look up a lot of documentation to find for sure that floating-point samples, like I supposed before, varies in the interval [–1.0,1.0], so I was doing it alright up to now.

Now, if someone has any idea on what the following code does, I’d be glad to know it. The blah() function (with the same name) is present in xine, MPlayer, FFmpeg, and renamed in Ogle and other software too.


static inline int16_t blah (int32_t i) {
  if (i > 0x43c07fff)
    return 32767;
  else if (i < 0x43bf8000)
    return -32768;
    return i - 0x43c00000;

int main() {
  uint8_t val_1[4] = { 0xFF, 0xFF, 0xBF, 0x43 };

  return printf("%fn%dn", *(float*)(&val_1), blah(*(int32_t*)(&val_1)));

The output (on Little-Endian, as val_1 is the byte representation of a float number in Little-Endian) is: 383.999969 and –1; it makes no sense to me, but maybe someone else has a clue..

And for who’s wondering, the byte-value I chosen there is one of the samples decoded by a52dec, so it’s a proper value I’m trying to understand; and for who’s wondering, the proper correspondence for –1 (int16) in float32 format is (approximated) –3.0517e-05.

Edit: thanks Nathan Smith who made me see the spelling error in the title (I still confuse the two).

Comments 12
  1. Maybe I fail to understand the question??However the function blah itself seems trivial. It takes a specific range out of the int_32 and gives that back as a signed 16_bit int. Just convert the hexidecimal numbers to decimals and it is obvious. The difference between 0x43c07fff and 0x43bf8000 is 65535 in decimal notation. And 0x43c00000 lies exactly in between the other two hexidecimal numbers, so that corresponds to the new zero.So bottomline is that it does not rescale, but just takes a specific range only. Don’t ask me why it is that specific range out of the int_32 as I would have no clue….but that is probably the real question you are asking??

  2. Yes, the problem is to find what the function is supposed to do; I can see it does take a range from the general float value (but then it’s also rescaling, if you consider it being either mantissa or exponent, I’m not sure of that, I fail at IEEE floating point), but I can’t understand the reasoning behind it.The reason why I need to understand the logic behind it is that I want to output 32-bit floating point data from a52 plugin, but the raw output is not good for that.

  3. Sorry for asking stupid questions, but shouldn’t you be thinking instead on what exactly is the meaning of*(int32_t*)(&val_1)))That piece of code is obscure to me.It seems like “casting” the start-adres of the array to a 32bit pointer and then dereferencing again. (WARNING: I’ve never been good at understanding pointers beyond the beyond the basics, so this is probably not what it means).Note that it does gives hexadecimal numbers in the given range that are subsequently substracted in Blah()

  4. Mmmm… the blog-engine drops the *’s in the code.I tried to post the piece of code that is send to blah() in the printf statement on the last line of your example code.

  5. An alternative to see it would have been to use an union, in this way:<typo:code>union { uint8_t memory[4]; float as_float; int32_t as_int;} val;</typo:code>Basically it accesses the four bytes of the float value as it were an unsigned integer value; it’s a dirty trick that only works when the floating-point data is managed in IEEE format.

  6. Thanks for the eye-opener….that’s indeed a dirty trick. Each of those constants in val_1 is simply one byte from a 32-bit number, with the last one the most significant byte.Things made more sense for me by plugging these constants instead in val_1.0x01, 0x00, 0xc0, 0x43 –> 10×00, 0x00, 0xC0, 0x43 –> 00xff, 0xff, 0xbf, 0x43 –> -1The middle one is the zero used in blah(). That gives 0 from blah() as expected, but 384.00 as a floating point value. It seems that substracting 384 from the float-value gives numbers in the range [-1,1].Unfortunately the logic of why this is done still evades me.

  7. To add a bit more. Just play with the hexadecimal calculator on this webpage.…(use: 43c00000, 43bfffff, 43c0ffff as input)That shows that substracting 384 does the trick to get from a 32-bit binary number to a number in the [-1,1] range. And that is what the function blah() is doing.

  8. I’d strongly suggest to convert the code to the usage of a union, otherwise you’ll need to use -fno-strict-aliasing unless I’m mistaken. Recent GCC versions have optimizations which don’t play well with invalid code like accessing memory through a different type.This may happen to work on a concrete machine but C is defined on an abstract machine and optimizations may be done with those constraints, not the real world situation…

  9. I know perfectly well how strict-aliasing works, but this code is just a testcase, so there’s no need to worry about it.

  10. That stuff about strict-aliasing is even harder to understand that the pointer-casting. :-(@Flameeyes: Did you understand what I was trying to say??All that the function blah() does is converting from an unsigned int_32 to a signed int by subtracting of the 384 (=0x43c00000) and taking care of the boundaries.

  11. Yeah thanks I did get that, although I still haven’t found to get what I need for xine to work with a52dec properly, but at least it’s a start 🙂

  12. The source code for the AC3 decoder (which this routine comes from) appears to always set an output bias value to ‘384’. This biases values that would otherwise be in the range [+1, -1] up to [385, 383].It therefore looks like the code is trying to take numbers in the range 385 to 383 and map them to the PCM range of 32768 to -32767.I guess this routine is very specific to that codebase and cannot be used in other contexts.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.