jFLAC woes
May. 20th, 2017 10:02 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
In today's instance of "it's all terrible", I'm trying to decode a bunch of FLAC audio from Java (I've got a half-baked idea for trying to determine if a sequence of files should be gapless or not by comparing the end of one with the start of the next - and no, I can't just check the original CD because these are online albums). A quick search threw up jFLAC which is a port of libflac (the reference decoder) to Java. Unfortunately there's not much documentation - well, the classes are somewhat JavaDoc'd but there's no overall "how do I use it" documentation - so I'm left puzzling through the source and the demos to try and work out how it all fits together.
The answer is, not well.
FLAC files start with a bunch of metadata records. jFLAC has a bunch of corresponding classes that implement the different metadata records, along with a method in FlacDecoder to read them. Now, there's several standard ways to implement polymorphic record classes but they generally involve having a superclass with an overloaded method to work out what your subclass is. Sadly the jFLAC Metadata class has no such method so I'm left doing a bunch of sequential "if (record instanceof ThisThing) {} else if (record instanceof ThatThing) {}..." tests. That's annoying but I can cope with that. Then I discover that half the metadata classes provide no way to read the values from them. Sigh.
The next puzzle is seeking. Now, what I want is the first second and the last second of a file and preferably without decoding everything inbetween. Fortunately there's a handy decode() overload that takes a pair of SeekPoint objects. Unfortunately this requires me to know the stream byte offset to seek to. Fortunately I can guess and it'll search for the next valid frame so I could do my own binary search through the stream to find the frame I want. I then just have to handle potentially decoding multiple frames and joining them together, trimming off unwanted leading/trailing data to get the audio samples I want (and recombining bytes into samples and de-interleaving the audio, because jFLAC just gives me a stream of bytes rather than samples...).
I'm seriously tempted to just write my own FLAC decoder...
The answer is, not well.
FLAC files start with a bunch of metadata records. jFLAC has a bunch of corresponding classes that implement the different metadata records, along with a method in FlacDecoder to read them. Now, there's several standard ways to implement polymorphic record classes but they generally involve having a superclass with an overloaded method to work out what your subclass is. Sadly the jFLAC Metadata class has no such method so I'm left doing a bunch of sequential "if (record instanceof ThisThing) {} else if (record instanceof ThatThing) {}..." tests. That's annoying but I can cope with that. Then I discover that half the metadata classes provide no way to read the values from them. Sigh.
The next puzzle is seeking. Now, what I want is the first second and the last second of a file and preferably without decoding everything inbetween. Fortunately there's a handy decode() overload that takes a pair of SeekPoint objects. Unfortunately this requires me to know the stream byte offset to seek to. Fortunately I can guess and it'll search for the next valid frame so I could do my own binary search through the stream to find the frame I want. I then just have to handle potentially decoding multiple frames and joining them together, trimming off unwanted leading/trailing data to get the audio samples I want (and recombining bytes into samples and de-interleaving the audio, because jFLAC just gives me a stream of bytes rather than samples...).
I'm seriously tempted to just write my own FLAC decoder...