package org.farng.mp3.id3;
import org.farng.mp3.InvalidTagException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* <h3>4.30. Audio seek point index</h3>
* <p/>
* <p> Audio files with variable bit rates are intrinsically difficult to<br> deal with in the
* case of seeking within the file. The ASPI frame<br> makes seeking easier by providing a list a seek
* points within the<br> audio file. The seek points are a fractional offset within the audio<br>
* data, providing a starting point from which to find an appropriate<br>
* <p/>
* point to start decoding. The presence of an ASPI frame requires the<br> existence of a TLEN
* frame, indicating the duration of the file in<br> milliseconds. There may only be one 'audio seek point
* index' frame in<br> a tag.</p>
* <p/>
* <p> <Header for 'Seek Point Index', ID: "ASPI"><br>
* Indexed data start (S) $xx xx xx xx<br>
* Indexed data length (L) $xx xx xx xx<br> Number of
* index points (N) $xx xx<br>
* <p/>
* Bits per index point (b) $xx</p>
* <p/>
* <p> Then for every index point the following data is included;</p>
* <p/>
* <p> Fraction at index (Fi) $xx
* (xx)</p>
* <p/>
* <p> 'Indexed data start' is a byte offset from the beginning of the file.<br> 'Indexed data
* length' is the byte length of the audio data being<br> indexed. 'Number of index points' is the number
* of index points, as<br> the name implies. The recommended number is 100. 'Bits per index<br>
* point' is 8 or 16, depending on the chosen precision. 8 bits works<br>
* <p/>
* well for short files (less than 5 minutes of audio), while 16 bits is<br> advantageous for
* long files. 'Fraction at index' is the numerator of<br> the fraction representing a relative position in
* the data. The<br> denominator is 2 to the power of b.</p>
* <p/>
* <p> Here are the algorithms to be used in the calculation. The known data<br> must be the
* offset of the start of the indexed data (S), the offset<br> of the end of the indexed data (E), the
* number of index points (N),<br> the offset at index i (Oi). We calculate the fraction at index i<br>
* (Fi).</p>
* <p/>
* <p> Oi is the offset of the frame whose start is soonest after the point<br> for which the
* time offset is (i/N * duration).</p>
* <p/>
* <p> The frame data should be calculated as follows:</p>
* <p/>
* <p> Fi = Oi/L * 2^b (rounded down to the nearest integer)</p>
* <p/>
* <p> Offset calculation should be calculated as follows from data in the<br> frame:</p>
* <p/>
* <p> Oi = (Fi/2^b)*L (rounded up to the nearest integer)<br> </p>
*
* @author Eric Farng
* @version $Revision: 1.5 $
*/
public class FrameBodyASPI extends AbstractID3v2FrameBody {
private short[] fraction = null;
private int bitsPerPoint = 0;
private int dataLength = 0;
private int dataStart = 0;
private int indexPoints = 0;
/**
* Creates a new FrameBodyASPI object.
*/
public FrameBodyASPI() {
super();
}
/**
* Creates a new FrameBodyASPI object.
*/
public FrameBodyASPI(final FrameBodyASPI copyObject) {
super(copyObject);
fraction = (short[]) copyObject.fraction.clone();
bitsPerPoint = copyObject.bitsPerPoint;
dataLength = copyObject.dataLength;
dataStart = copyObject.dataStart;
indexPoints = copyObject.indexPoints;
}
/**
* Creates a new FrameBodyASPI object.
*/
public FrameBodyASPI(final int dataStart,
final int dataLength,
final int indexPoints,
final int bitsPerPoint,
final short[] fraction) {
super();
this.dataStart = dataStart;
this.dataLength = dataLength;
this.indexPoints = indexPoints;
this.bitsPerPoint = bitsPerPoint;
this.fraction = new short[fraction.length];
System.arraycopy(fraction, 0, this.fraction, 0, fraction.length);
}
/**
* Creates a new FrameBodyASPI object.
*/
public FrameBodyASPI(final RandomAccessFile file) throws IOException, InvalidTagException {
super();
read(file);
}
public String getIdentifier() {
return "ASPI";
}
public int getSize() {
return 4 + 4 + 2 + 1 + fraction.length << 1;
}
/**
* This method is not yet supported.
*
* @throws UnsupportedOperationException This method is not yet supported
*/
public void equals() {
// todo Implement this java.lang.Object method
throw new UnsupportedOperationException("Method equals() not yet implemented.");
}
protected void setupObjectList() {
// throw new UnsupportedOperationException();
}
public void read(final RandomAccessFile file) throws IOException, InvalidTagException {
final int size = readHeader(file);
if (size == 0) {
throw new InvalidTagException("Empty Frame");
}
dataStart = file.readInt();
dataLength = file.readInt();
indexPoints = (int) file.readShort();
bitsPerPoint = (int) file.readByte();
fraction = new short[indexPoints];
for (int i = 0; i < indexPoints; i++) {
if (bitsPerPoint == 8) {
fraction[i] = (short) file.readByte();
} else if (bitsPerPoint == 16) {
fraction[i] = file.readShort();
} else {
throw new InvalidTagException("ASPI bits per point wasn't 8 or 16");
}
}
}
public String toString() {
return getIdentifier() + ' ' + this
.dataStart + ' ' + this
.dataLength + ' ' + this
.indexPoints + ' ' + this
.bitsPerPoint + ' ' + this.fraction
.toString();
}
public void write(final RandomAccessFile file) throws IOException {
writeHeader(file, getSize());
file.writeInt(dataStart);
file.writeInt(dataLength);
file.writeShort(indexPoints);
file.writeByte(16);
for (int i = 0; i < indexPoints; i++) {
file.writeShort((int) fraction[i]);
}
}
}