package org.jaudiotagger.audio.flac;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.logging.ErrorMessage;
import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
import org.jaudiotagger.tag.id3.ID3v22Tag;
import org.jaudiotagger.tag.id3.ID3v23Tag;
import org.jaudiotagger.tag.id3.ID3v24Tag;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
/**
* Flac Stream
* <p/>
* Reader files and identifies if this is in fact a flac stream
*/
public class FlacStreamReader {
public static final int FLAC_STREAM_IDENTIFIER_LENGTH = 4;
public static final String FLAC_STREAM_IDENTIFIER = "fLaC";
private RandomAccessFile raf;
private int startOfFlacInFile;
/**
* Create instance for holding stream info
*
* @param raf
*/
public FlacStreamReader(RandomAccessFile raf) {
this.raf = raf;
}
/**
* Reads the stream block to ensure it is a flac file
*
* @throws IOException
* @throws CannotReadException
*/
public void findStream() throws IOException, CannotReadException {
//Begins tag parsing
if (raf.length() == 0) {
//Empty File
throw new CannotReadException("Error: File empty");
}
raf.seek(0);
//FLAC Stream at start
if (isFlacHeader()) {
startOfFlacInFile = 0;
return;
}
//Ok maybe there is an ID3v24tag first
if (isId3v24Tag()) {
startOfFlacInFile = (int) (raf.getFilePointer() - FLAC_STREAM_IDENTIFIER_LENGTH);
return;
}
//Ok maybe there is an ID3v23tag first
if (isId3v23Tag()) {
startOfFlacInFile = (int) (raf.getFilePointer() - FLAC_STREAM_IDENTIFIER_LENGTH);
return;
}
//Ok maybe there is an ID3v22tag first
if (isId3v22Tag()) {
startOfFlacInFile = (int) (raf.getFilePointer() - FLAC_STREAM_IDENTIFIER_LENGTH);
return;
}
throw new CannotReadException(ErrorMessage.FLAC_NO_FLAC_HEADER_FOUND.getMsg());
}
private boolean isId3v24Tag() throws IOException {
int id3tagsize;
ID3v24Tag id3tag = new ID3v24Tag();
ByteBuffer bb = ByteBuffer.allocate(AbstractID3v2Tag.TAG_HEADER_LENGTH);
raf.seek(0);
raf.getChannel().read(bb);
if (id3tag.seek(bb)) {
id3tagsize = id3tag.readSize(bb);
raf.seek(id3tagsize);
//FLAC Stream immediately after end of id3 tag
if (isFlacHeader()) {
return true;
}
}
return false;
}
private boolean isId3v23Tag() throws IOException {
int id3tagsize;
ID3v23Tag id3tag = new ID3v23Tag();
ByteBuffer bb = ByteBuffer.allocate(AbstractID3v2Tag.TAG_HEADER_LENGTH);
raf.seek(0);
raf.getChannel().read(bb);
if (id3tag.seek(bb)) {
id3tagsize = id3tag.readSize(bb);
raf.seek(id3tagsize);
//FLAC Stream immediately after end of id3 tag
if (isFlacHeader()) {
return true;
}
}
return false;
}
private boolean isId3v22Tag() throws IOException {
int id3tagsize;
ID3v22Tag id3tag = new ID3v22Tag();
ByteBuffer bb = ByteBuffer.allocate(AbstractID3v2Tag.TAG_HEADER_LENGTH);
raf.seek(0);
raf.getChannel().read(bb);
if (id3tag.seek(bb)) {
id3tagsize = id3tag.readSize(bb);
raf.seek(id3tagsize);
//FLAC Stream immediately after end of id3 tag
if (isFlacHeader()) {
return true;
}
}
return false;
}
private boolean isFlacHeader() throws IOException {
//FLAC Stream at start
byte[] b = new byte[FLAC_STREAM_IDENTIFIER_LENGTH];
raf.read(b);
String flac = new String(b);
return flac.equals(FLAC_STREAM_IDENTIFIER);
}
/**
* Usually flac header is at start of file, but unofficially and ID3 tag is allowed at the start of the file.
*
* @return the start of the Flac within file
*/
public int getStartOfFlacInFile() {
return startOfFlacInFile;
}
}