package org.herac.tuxguitar.io.ptb;
import java.io.IOException;
import java.io.InputStream;
import org.apache.log4j.Logger;
import org.herac.tuxguitar.gui.editors.chord.ChordSelector;
import org.herac.tuxguitar.io.base.TGFileFormat;
import org.herac.tuxguitar.io.base.TGInputStreamBase;
import org.herac.tuxguitar.io.ptb.base.PTBar;
import org.herac.tuxguitar.io.ptb.base.PTBeat;
import org.herac.tuxguitar.io.ptb.base.PTDirection;
import org.herac.tuxguitar.io.ptb.base.PTGuitarIn;
import org.herac.tuxguitar.io.ptb.base.PTNote;
import org.herac.tuxguitar.io.ptb.base.PTSection;
import org.herac.tuxguitar.io.ptb.base.PTSong;
import org.herac.tuxguitar.io.ptb.base.PTSymbol;
import org.herac.tuxguitar.io.ptb.base.PTTempo;
import org.herac.tuxguitar.io.ptb.base.PTTrack;
import org.herac.tuxguitar.io.ptb.base.PTTrackInfo;
import org.herac.tuxguitar.song.models.TGMeasureHeader;
import org.herac.tuxguitar.song.models.TGSong;
public class PTInputStream implements TGInputStreamBase {
/** The Logger for this class. */
public static final transient Logger LOG = Logger
.getLogger(PTInputStream.class);
private static final String PTB_VERSION = "ptab-4";
private PTSongParser parser;
private PTSong song;
private InputStream stream;
private String version;
public PTInputStream() {
super();
}
private void close() {
try {
this.stream.close();
} catch (IOException e) {
LOG.error(e);
}
}
public TGFileFormat getFileFormat() {
return new TGFileFormat("PowerTab", "*.ptb");
}
public void init(InputStream stream) {
this.version = null;
this.stream = stream;
this.parser = new PTSongParser();
}
public boolean isSupportedVersion() {
try {
readVersion();
return isSupportedVersion(this.version);
} catch (Exception e) {
return false;
} catch (Error e) {
return false;
}
}
public boolean isSupportedVersion(String version) {
return (version.equals(PTB_VERSION));
}
private void readBarLine(PTSection section) {
PTBar bar = new PTBar();
int position = readByte();
int type = readByte();
// repeat start
bar.setRepeatStart(((type >>> 5) == 3));
// repeat end
bar.setRepeatClose((((type >>> 5) == 4) ? (type - 128) : 0));
readKeySignature();
readTimeSignature(bar);
readRehearsalSign();
section.getPosition(position).addComponent(bar);
}
private boolean readBoolean() {
try {
return (this.stream.read() > 0);
} catch (IOException e) {
LOG.error(e);
}
return false;
}
private int readByte() {
try {
return this.stream.read();
} catch (IOException e) {
LOG.error(e);
}
return 0;
}
private void readChord() {
readShort(); // chordKey
readByte();
readShort(); // chordModification
readByte();
readByte();
int stringCount = readByte();
for (int j = 0; j < stringCount; j++) {
readByte(); // fret
}
}
private void readChordText() {
readByte();
readShort();
readByte();
readShort();
readByte();
}
private void readDataInstruments(PTTrack track) {
// Guitar section
int itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readTrackInfo(track);
if (j < itemCount - 1) {
readShort();
}
}
// ChordDiagram section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readChord();
if (j < itemCount - 1) {
readShort();
}
}
// FloatingText section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readFloattingText();
if (j < itemCount - 1) {
readShort();
}
}
// GuitarIn section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readGuitarIn(track);
if (j < itemCount - 1) {
readShort();
}
}
// TempoMarker
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readTempoMarker(track);
if (j < itemCount - 1) {
readShort();
}
}
// Dynamic section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readDynamic();
if (j < itemCount - 1) {
readShort();
}
}
// SectionSymbol section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readSectionSymbol(track);
if (j < itemCount - 1) {
readShort();
}
}
// Section section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readSection(track.getSection(j));
if (j < itemCount - 1) {
readShort();
}
}
}
private void readDirection(PTSection section) {
int position = readByte();
int symboleCount = readByte();
for (int i = 0; i < symboleCount; i++) {
int data = readShort();
section.getPosition(position).addComponent(
new PTDirection((data >> 8), ((data & 0xc0) >> 6), (data & 0x1f)));
}
}
private void readDynamic() {
readShort();
readByte();
readByte();
readShort();
}
private void readFloattingText() {
// Floating text
readString();
// Read mfc rect
readInt();// left
readInt();// top
readInt();// right
readInt();// bottom
readByte();
readFontSetting();
}
private void readFontSetting() {
readString();// fontName
readInt();// pointSize
readInt();// weight
readBoolean();// italic
readBoolean();// underline
readBoolean();// strikeout
readInt();// color
}
private void readGuitarIn(PTTrack track) {
int section = readShort();
int staff = readByte();
int position = readByte();
skip(1);
int info = (readByte() & 0xff);
track.getSection(section).getPosition(position).addComponent(
new PTGuitarIn(staff, info));
}
private int readHeaderItems() {
int nbItems = readShort();
if (nbItems != 0) {
int header = readShort();
if (header == 0xffff) {
if (readShort() != 1) {
return -1;
}
readString(readShort());
}
}
return nbItems;
}
private int readInt() {
try {
byte[] b = new byte[4];
this.stream.read(b);
return ((b[3] & 0xff) << 24) | ((b[2] & 0xff) << 16)
| ((b[1] & 0xff) << 8) | (b[0] & 0xff);
} catch (IOException e) {
LOG.error(e);
}
return 0;
}
private void readKeySignature() {
readByte();
}
private void readNote(PTBeat beat) {
PTNote note = new PTNote();
int position = readByte();
int simpleData = readShort();
int symbolCount = readByte();
for (int i = 0; i < symbolCount; i++) {
readByte();
readByte();
int data3 = readByte();
int data4 = readByte();
note.setBend((data4 == 101) ? ((data3 / 16) + 1) : 0);
note.setSlide((data4 == 100));
}
note.setValue(position & 0x1f);
note.setString(((position & 0xe0) >> 5) + 1);
note.setTied((simpleData & 0x01) != 0);
note.setDead((simpleData & 0x02) != 0);
beat.addNote(note);
}
private void readPosition(int staff, int voice, PTSection section) {
PTBeat beat = new PTBeat(staff, voice);
int position = readByte();
int beaming = readByte();
beaming = ((beaming - 128 < 0) ? beaming : beaming - 128);
readByte();
int data1 = readByte();
readByte();
int data3 = readByte();
int durationValue = readByte();
int multiBarRest = 1;
int complexCount = readByte();
for (int i = 0; i < complexCount; i++) {
int count = readShort();
readByte();
int type = readByte();
if ((type & 0x08) != 0) {
multiBarRest = count;
}
}
int itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readNote(beat);
if (j < itemCount - 1) {
readShort();
}
}
beat.setMultiBarRest((itemCount == 0) ? multiBarRest : 1);
beat.setVibrato(((data1 & 0x08) != 0) || ((data1 & 0x10) != 0));
beat.setGrace((data3 & 0x01) != 0);
// Set the duration
beat.setDuration(durationValue);
beat.setDotted((data1 & 0x01) != 0);
beat.setDoubleDotted((data1 & 0x02) != 0);
beat.setArpeggioUp((data1 & 0x20) != 0);
beat.setArpeggioDown((data1 & 0x40) != 0);
beat.setEnters(((beaming - (beaming % 8)) / 8) + 1);
beat.setTimes((beaming % 8) + 1);
section.getPosition(position).addComponent(beat);
}
private void readRehearsalSign() {
readByte();
readString();
}
private void readRhythmSlash() {
readByte();
readByte();
readInt();
}
private void readSection(PTSection section) {
readInt();// left
readInt();// top
readInt();// right
readInt();// bottom
int lastBarData = readByte();
readByte();
readByte();
readByte();
readByte();
// BarLine
readBarLine(section);
// Direction section
int itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readDirection(section);
if (j < itemCount - 1) {
readShort();
}
}
// ChordText section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readChordText();
if (j < itemCount - 1) {
readShort();
}
}
// RhythmSlash section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readRhythmSlash();
if (j < itemCount - 1) {
readShort();
}
}
// Staff
section.setStaffs(readHeaderItems());
for (int staff = 0; staff < section.getStaffs(); staff++) {
readStaff(staff, section);
if (staff < section.getStaffs() - 1) {
readShort();
}
}
// MusicBar section
itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readBarLine(section);
if (j < itemCount - 1) {
readShort();
}
}
PTBar bar = new PTBar();
bar.setRepeatClose(((lastBarData >>> 5) == 4) ? (lastBarData - 128) : 0);
section.getPosition(section.getNextPositionNumber()).addComponent(bar);
}
private void readSectionSymbol(PTTrack track) {
int section = readShort();
int position = readByte();
int data = readInt();
PTSymbol symbol = new PTSymbol();
symbol.setEndNumber((data >> 16));
track.getSection(section).getPosition(position).addComponent(symbol);
}
private int readShort() {
try {
byte[] b = { 0, 0 };
this.stream.read(b);
return ((b[1] & 0xff) << 8) | (b[0] & 0xff);
} catch (IOException e) {
LOG.error(e);
}
return 0;
}
public TGSong readSong() throws IOException {
this.readVersion();
if (!isSupportedVersion(this.version)) {
throw new IOException("Unsupported Version");
}
this.song = new PTSong();
this.readSongInfo();
this.readDataInstruments(this.song.getTrack1());
this.readDataInstruments(this.song.getTrack2());
this.close();
return this.parser.parseSong(this.song);
}
private void readSongInfo() {
this.song.getInfo().setClassification(readByte());
if (this.song.getInfo().getClassification() == 0) {
skip(1);
this.song.getInfo().setName(readString());
this.song.getInfo().setInterpret(readString());
this.song.getInfo().setReleaseType(readByte());
if (this.song.getInfo().getReleaseType() == 0) {
this.song.getInfo().setAlbumType(readByte());
this.song.getInfo().setAlbum(readString());
this.song.getInfo().setYear(readShort());
this.song.getInfo().setLiveRecording(readBoolean());
} else if (this.song.getInfo().getReleaseType() == 1) {
this.song.getInfo().setAlbum(readString());
this.song.getInfo().setLiveRecording(readBoolean());
} else if (this.song.getInfo().getReleaseType() == 2) {
this.song.getInfo().setAlbum(readString());
this.song.getInfo().setDay(readShort());
this.song.getInfo().setMonth(readShort());
this.song.getInfo().setYear(readShort());
}
if (readByte() == 0) {
this.song.getInfo().setAuthor(readString());
this.song.getInfo().setLyricist(readString());
}
this.song.getInfo().setArrenger(readString());
this.song.getInfo().setGuitarTranscriber(readString());
this.song.getInfo().setBassTranscriber(readString());
this.song.getInfo().setCopyright(readString());
this.song.getInfo().setLyrics(readString());
this.song.getInfo().setGuitarInstructions(readString());
this.song.getInfo().setBassInstructions(readString());
} else if (this.song.getInfo().getClassification() == 1) {
this.song.getInfo().setName(readString());
this.song.getInfo().setAlbum(readString());
this.song.getInfo().setStyle(readShort());
this.song.getInfo().setLevel(readByte());
this.song.getInfo().setAuthor(readString());
this.song.getInfo().setInstructions(readString());
this.song.getInfo().setCopyright(readString());
}
}
private void readStaff(int staff, PTSection section) {
readByte();
readByte();
readByte();
readByte();
readByte();
for (int voice = 0; voice < 2; voice++) {
int itemCount = readHeaderItems();
for (int j = 0; j < itemCount; j++) {
readPosition(staff, voice, section);
if (j < itemCount - 1) {
readShort();
}
}
}
}
private String readString() {
try {
int length = (this.stream.read() & 0xff);
return this.readString(((length < 0xff) ? length : readShort()));
} catch (IOException e) {
LOG.error(e);
}
return null;
}
private String readString(int length) {
try {
byte[] bytes = new byte[length];
this.stream.read(bytes);
return new String(bytes);
} catch (IOException e) {
LOG.error(e);
}
return null;
}
private void readTempoMarker(PTTrack track) {
int section = readShort();
int position = readByte();
int tempo = readShort();
int data = readShort();
readString();// description
int tripletFeel = TGMeasureHeader.TRIPLET_FEEL_NONE;
if ((data & 0x01) != 0) {
tripletFeel = TGMeasureHeader.TRIPLET_FEEL_EIGHTH;
} else if ((data & 0x02) != 0) {
tripletFeel = TGMeasureHeader.TRIPLET_FEEL_SIXTEENTH;
}
if (tempo > 0) {
track.getSection(section).getPosition(position).addComponent(
new PTTempo(tempo, tripletFeel));
}
}
private void readTimeSignature(PTBar bar) {
int data = readInt();
readByte(); // measurePulses
bar.setNumerator(((((data >> 24) - ((data >> 24) % 8)) / 8) + 1));
bar.setDenominator((int) Math.pow(2, (data >> 24) % 8));
}
private void readTrackInfo(PTTrack track) {
PTTrackInfo info = new PTTrackInfo();
info.setNumber(readByte());
info.setName(readString());
info.setInstrument((short) readByte());
info.setVolume((short) readByte());
info.setBalance((short) readByte());
info.setReverb((short) readByte());
info.setChorus((short) readByte());
info.setTremolo((short) readByte());
info.setPhaser((short) readByte());
readByte();// capo
// Tuning
readString();// tunningName
// bit 7 = Music notation offset sign, bits 6 to 1 = Music notation offset
// value, bit 0 = display sharps or flats;
readByte(); // offset
int[] strings = new int[(readByte() & 0xff)];
for (int i = 0; i < strings.length; i++) {
strings[i] = readByte();
}
info.setStrings(strings);
track.getInfos().add(info);
}
private void readVersion() {
if (this.version == null) {
this.version = (readString(4) + "-" + readShort());
}
}
private void skip(int bytes) {
try {
this.stream.read(new byte[bytes]);
} catch (IOException e) {
LOG.error(e);
}
}
}