«« An Intro to jid3rL The id3v2 Frames Hierarchy »»
blog header image
Java Signed Bytes and Decoding Flags

The id3v2 spec has bit flags in it, which are either 1/0 (true/false). These flags are jammed into bytes and each byte can contain up to eight flags.

For example, here's an excerpt from the 2.3.0 spec for the tag header flags:

ID3v2 flags %abc00000

The % notation is shorthand for bits. a,b and c are bit flags that can be turned off/on and the rest are expected to be zeros. Altogether these 8 bits make up a single unsigned byte.

Unsigned byte values range from 0 to 255. The most significant bit is on the left (a, worth 128 if on) and the least significant bit is on the right. If you don't know how to decode bits from an unsigned byte, you can read up on it.

The problem is that Java represents bytes as signed bytes, which range from -128 to 127. I'm reading signed bytes from the files, so I have to deal with them. You might say "OK, just take the signed byte and add 128 to it to get an unsigned byte". At first glance that might seem correct, but it's not. To understand why you have to get under the hood of the Java signed byte.

Java stores its signed bytes in two's complement notation. If only the a flag is on from the example above, the signed byte value would be -128. Adding 128 to that gives you %0000 0000 in bits: no flags on. This is obviously incorrect, we know the first flag should be on.

The trick is to use a bitwise operator in Java that doesn't care about sign: the unsigned right shift operator >>>. Here's the method I wrote to determine if a flag in a byte is on:

protected static boolean parseFlag(byte flags, int position)
{
   int shifted = flags >>> position;
   return (0 != (shifted & 0x01));
}

The >>> operator shifts the bits of flags to the right so that the bit in the desired position ends up being the least significant (right-most) bit. Zeroes are shifted into the left side. Then I bitwise AND %0000 0001 to shifted so that all of the other bits are zeroed except the least significant bit that I'm concerned about. If the resulting value isn't 0 then the bit in question was set and the method returns true.

How did I verify my code works? Unit testing, of course. Here are the tests I do:

bits       byte description
%0000 0000    0 none set
%1000 0000 -128 a set
%1100 0000  -64 a and b set
%1110 0000  -32 a, b and c set
%1010 0000  -96 a and c set
%0100 0000   64 b set
%0110 0000   96 b and c set
%0010 0000   32 c set
%0001 0000   16 bit 4 set
%0000 1000    8 bit 3 set
%0000 0100    4 bit 2 set
%0000 0010    2 bit 1 set
%0000 0001    1 bit 0 set

I could be much more comprehensive, especially on the negative side. But this seems good enough for now.

Posted at August 09, 2004 at 08:30 AM EST
Last updated August 09, 2004 at 08:30 AM EST
Comments

I ran into this exact same problem when doing the code for id3v1 for track number.

It was the boundary cases that always show the problems since I only ran into the problem when I tried to give a mp3 a track number greater than 128 which isn't too common.

» Posted by: Jim at August 9, 2004 12:15 PM


Would you consider something like fitnesse for acceptance testing (aka showing off your unit tests in .xls or .html)?

[Once I am done with this honeymoon stuff, I was going to jump on the .net fitnesse bandwagon - i'll let you know how it goes]

» Posted by: dru at August 25, 2004 10:41 PM

Very cool to be here. Hi all!

» Posted by: at December 1, 2004 04:51 AM

Very interesting. This info helped me a lot.
Thanks

» Posted by: Wolf at March 24, 2005 04:59 AM
Google
 
Search scope: Web ryanlowe.ca