package org.herac.tuxguitar.io.tg;
import java.awt.Color;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.log4j.Logger;
import org.herac.tuxguitar.gui.editors.tab.TGBeatImpl;
import org.herac.tuxguitar.gui.editors.tab.TGChordImpl;
import org.herac.tuxguitar.gui.editors.tab.TGMeasureHeaderImpl;
import org.herac.tuxguitar.gui.editors.tab.TGMeasureImpl;
import org.herac.tuxguitar.gui.editors.tab.TGNoteImpl;
import org.herac.tuxguitar.gui.editors.tab.TGTextImpl;
import org.herac.tuxguitar.gui.editors.tab.TGTrackImpl;
import org.herac.tuxguitar.io.base.TGFileFormat;
import org.herac.tuxguitar.io.base.TGFileFormatException;
import org.herac.tuxguitar.io.base.TGInputStreamBase;
import org.herac.tuxguitar.song.models.Clef;
import org.herac.tuxguitar.song.models.Direction;
import org.herac.tuxguitar.song.models.TGBeat;
import org.herac.tuxguitar.song.models.TGChannel;
import org.herac.tuxguitar.song.models.TGChord;
import org.herac.tuxguitar.song.models.TGDivisionType;
import org.herac.tuxguitar.song.models.TGDuration;
import org.herac.tuxguitar.song.models.TGLyric;
import org.herac.tuxguitar.song.models.TGMarker;
import org.herac.tuxguitar.song.models.TGMeasure;
import org.herac.tuxguitar.song.models.TGMeasureHeader;
import org.herac.tuxguitar.song.models.TGNote;
import org.herac.tuxguitar.song.models.TGNoteEffect;
import org.herac.tuxguitar.song.models.TGSong;
import org.herac.tuxguitar.song.models.TGString;
import org.herac.tuxguitar.song.models.TGStroke;
import org.herac.tuxguitar.song.models.TGTempo;
import org.herac.tuxguitar.song.models.TGText;
import org.herac.tuxguitar.song.models.TGTimeSignature;
import org.herac.tuxguitar.song.models.TGTrack;
import org.herac.tuxguitar.song.models.TGVoice;
import org.herac.tuxguitar.song.models.effects.BendingEffect;
import org.herac.tuxguitar.song.models.effects.EffectPoint;
import org.herac.tuxguitar.song.models.effects.HarmonicEffect;
import org.herac.tuxguitar.song.models.effects.TGEffectGrace;
import org.herac.tuxguitar.song.models.effects.TGEffectTremoloPicking;
import org.herac.tuxguitar.song.models.effects.TGEffectTrill;
import org.herac.tuxguitar.song.models.effects.Transition;
public abstract class AbstractTuxGuitarInputStream extends TGStream implements
TGInputStreamBase {
/** The Logger for this class. */
public static final transient Logger LOG = Logger
.getLogger(AbstractTuxGuitarInputStream.class);
private DataInputStream dataInputStream;
private String version;
@Override
public TGFileFormat getFileFormat() {
return new TGFileFormat("TuxGuitar", "*.tg");
}
@Override
public void init(InputStream stream) {
this.dataInputStream = new DataInputStream(stream);
this.version = null;
}
@Override
public boolean isSupportedVersion() {
try {
readVersion();
return isSupportedVersion(this.version);
} catch (Throwable throwable) {
return false;
}
}
public boolean isSupportedVersion(String version) {
return (version.equals(TG_FORMAT_VERSION));
}
protected abstract TGSong read();
private void readBeat(int header, TGMeasure measure, TGBeatData data) {
TGBeat beat = new TGBeatImpl();
beat.setStart(data.getCurrentStart());
readVoices(header, beat, data);
// leo el stroke
if (((header & BEAT_HAS_STROKE) != 0)) {
beat.setStroke(readStroke());
}
// leo el acorde
if (((header & BEAT_HAS_CHORD) != 0)) {
readChord(beat);
}
// leo el texto
if (((header & BEAT_HAS_TEXT) != 0)) {
readText(beat);
}
measure.addBeat(beat);
}
private void readBeats(TGMeasure measure, TGBeatData data) {
int header = BEAT_HAS_NEXT;
while (((header & BEAT_HAS_NEXT) != 0)) {
header = readHeader();
readBeat(header, measure, data);
}
}
protected BendingEffect readBendEffect() {
BendingEffect bend = new BendingEffect();
// leo la cantidad de puntos
int count = readByte();
for (int i = 0; i < count; i++) {
// leo la posicion
int position = readByte();
// leo el valor
int value = readByte();
// agrego el punto
bend.addPoint(position, value);
}
return bend;
}
protected byte readByte() {
try {
return (byte) this.dataInputStream.read();
} catch (IOException e) {
LOG.error(e);
}
return 0;
}
private void readChannel(TGChannel channel) {
// leo el canal
channel.setChannel(readByte());
// leo el canal de efectos
channel.setEffectChannel(readByte());
// leo el instrumento
channel.setInstrument(readByte());
// leo el volumen
channel.setVolume(readByte());
// leo el balance
channel.setBalance(readByte());
// leo el chorus
channel.setChorus(readByte());
// leo el reverb
channel.setReverb(readByte());
// leo el phaser
channel.setPhaser(readByte());
// leo el tremolo
channel.setTremolo(readByte());
}
private void readChord(TGBeat beat) {
TGChord chord = new TGChordImpl(readByte());
// leo el nombre
chord.setName(readUnsignedByteString());
// leo el primer fret
chord.setFirstFret(readByte());
// leo las cuerdas
for (int string = 0; string < chord.countStrings(); string++) {
chord.addFretValue(string, readByte());
}
beat.setChord(chord);
}
private TGDivisionType readDivisionType() {
// leo los enters
final int enters = readByte();
// leo los tiempos
final int times = readByte();
return new TGDivisionType(enters, times);
}
private void readDuration(TGDuration duration) {
int header = readHeader();
// leo el puntillo
duration.setDotted((header & DURATION_DOTTED) != 0);
// leo el doble puntillo
duration.setDoubleDotted((header & DURATION_DOUBLE_DOTTED) != 0);
// leo el valor
duration.setValue(readByte());
// leo el tipo de divisiones
if (((header & DURATION_NO_TUPLET) != 0)) {
duration.setDivision(readDivisionType());
} else {
duration.setDivision(TGDivisionType.NORMAL);
}
}
protected TGEffectGrace readGraceEffect() {
int header = readHeader();
TGEffectGrace effect = new TGEffectGrace();
effect.setDead((header & GRACE_FLAG_DEAD) != 0);
effect.setOnBeat((header & GRACE_FLAG_ON_BEAT) != 0);
// leo el fret
effect.setFret(readByte());
// leo la duracion
effect.setDuration(readByte());
// leo el velocity
effect.setDynamic(readByte());
// leo la transicion
final int transitionId = readByte();
Transition transition = null;
if (transitionId == Transition.NONE.getId()) {
transition = Transition.NONE;
} else if (transitionId == Transition.BEND.getId()) {
transition = Transition.BEND;
} else if (transitionId == Transition.HAMMER.getId()) {
transition = Transition.HAMMER;
} else if (transitionId == Transition.SLIDE.getId()) {
transition = Transition.SLIDE;
}
effect.setTransition(transition);
return effect;
}
protected HarmonicEffect readHarmonicEffect() {
// leo el tipo
final int id = readByte();
HarmonicEffect harmonic = null;
if (id == HarmonicEffect.ARTIFICIAL.getId()) {
harmonic = HarmonicEffect.ARTIFICIAL;
} else if (id == HarmonicEffect.NATURAL.getId()) {
harmonic = HarmonicEffect.NATURAL;
} else if (id == HarmonicEffect.PINCH.getId()) {
harmonic = HarmonicEffect.PINCH;
} else if (id == HarmonicEffect.SEMI.getId()) {
harmonic = HarmonicEffect.SEMI;
} else if (id == HarmonicEffect.TAPPED.getId()) {
harmonic = HarmonicEffect.TAPPED;
} else {
LOG.debug("Unknown type of HarmonicEffect, with id " + id);
}
// leo la data
if (harmonic != null && !harmonic.equals(HarmonicEffect.NATURAL)) {
harmonic.setData(readByte());
}
return harmonic;
}
private int readHeader() {
try {
return this.dataInputStream.read();
} catch (IOException e) {
LOG.error(e);
}
return 0;
}
protected int readHeader(int bCount) {
int header = 0;
for (int i = bCount; i > 0; i--) {
header += (readHeader() << ((8 * i) - 8));
}
return header;
}
private TGString readInstrumentString(int number) {
return new TGString(number, readByte());
}
protected String readIntegerString() {
try {
return readString(this.dataInputStream.readInt());
} catch (IOException e) {
LOG.error(e);
}
return null;
}
private void readLyrics(TGLyric lyrics) {
// leo el compas de comienzo
lyrics.setFrom(readShort());
// leo el texto
lyrics.setLyrics(readIntegerString());
}
private TGMarker readMarker(int measure) {
TGMarker marker = new TGMarker();
marker.setMeasure(measure);
// leo el titulo
marker.setTitle(readUnsignedByteString());
// leo el color
marker.setColor(readRGBColor());
return marker;
}
private TGMeasure readMeasure(TGMeasureHeader measureHeader,
TGMeasure lastMeasure) {
int header = readHeader();
TGMeasure measure = new TGMeasureImpl(measureHeader);
TGBeatData data = new TGBeatData(measure);
// leo la los beats
readBeats(measure, data);
// leo la clave
measure
.setClef((lastMeasure == null) ? Clef.TREBLE : lastMeasure.getClef());
if (((header & MEASURE_CLEF) != 0)) {
final int clefCode = readByte();
Clef clef = null;
switch (clefCode) {
case 1:
clef = Clef.TREBLE;
break;
case 2:
clef = Clef.BASS;
break;
case 3:
clef = Clef.TENOR;
break;
case 4:
clef = Clef.ALTO;
break;
}
measure.setClef(clef);
}
// leo el key signature
measure.setKeySignature((lastMeasure == null) ? 0 : lastMeasure
.getKeySignature());
if (((header & MEASURE_KEYSIGNATURE) != 0)) {
measure.setKeySignature(readByte());
}
return measure;
}
protected TGMeasureHeader readMeasureHeader(int number, long start,
TGMeasureHeader lastMeasureHeader) {
int header = readHeader();
TGMeasureHeader measureHeader = new TGMeasureHeaderImpl();
measureHeader.setNumber(number);
measureHeader.setStart(start);
// leo el time signature
if (((header & MEASURE_HEADER_TIMESIGNATURE) != 0)) {
readTimeSignature(measureHeader.getTimeSignature());
} else if (lastMeasureHeader != null) {
measureHeader.setTimeSignature(lastMeasureHeader.getTimeSignature()
.clone());
}
// leo el tempo
if (((header & MEASURE_HEADER_TEMPO) != 0)) {
readTempo(measureHeader.getTempo());
} else if (lastMeasureHeader != null) {
measureHeader.setTempo(lastMeasureHeader.getTempo().clone());
}
// leo el comienzo de la repeticion
measureHeader.setRepeatOpen((header & MEASURE_HEADER_REPEAT_OPEN) != 0);
// leo el numero de repeticiones
if (((header & MEASURE_HEADER_REPEAT_CLOSE) != 0)) {
measureHeader.setRepeatClose(readShort());
}
// leo los finales alternativos
if (((header & MEASURE_HEADER_REPEAT_ALTERNATIVE) != 0)) {
measureHeader.setRepeatAlternative(readByte());
}
// leo el marker
if (((header & MEASURE_HEADER_MARKER) != 0)) {
measureHeader.setMarker(readMarker(number));
}
measureHeader
.setTripletFeel((lastMeasureHeader != null) ? lastMeasureHeader
.getTripletFeel() : TGMeasureHeader.TRIPLET_FEEL_NONE);
if (((header & MEASURE_HEADER_TRIPLET_FEEL) != 0)) {
measureHeader.setTripletFeel(readByte());
}
return measureHeader;
}
private void readNote(int header, TGVoice voice, TGBeatData data) {
TGNote note = new TGNoteImpl();
// leo el valor
note.setValue(readByte());
// leo la cuerda
note.setString(readByte());
// leo la ligadura
note.setTiedNote((header & NOTE_TIED) != 0);
// leo el velocity
if (((header & NOTE_VELOCITY) != 0)) {
data.getVoice(voice.getIndex()).setVelocity(readByte());
}
note.setVelocity(data.getVoice(voice.getIndex()).getVelocity());
// leo los efectos
if (((header & NOTE_EFFECT) != 0)) {
readNoteEffect(note.getEffect());
}
voice.addNote(note);
}
protected abstract void readNoteEffect(final TGNoteEffect effect);
private void readNotes(TGVoice voice, TGBeatData data) {
int header = NOTE_HAS_NEXT;
while (((header & NOTE_HAS_NEXT) != 0)) {
header = readHeader();
readNote(header, voice, data);
}
}
private Color readRGBColor() {
// leo el RGB
final int red = readByte() & 0xff;
final int green = readByte() & 0xff;
final int blue = readByte() & 0xff;
return new Color(red, green, blue);
}
protected short readShort() {
try {
return this.dataInputStream.readShort();
} catch (IOException e) {
LOG.error(e);
}
return 0;
}
public TGSong readSong() throws TGFileFormatException {
try {
if (this.isSupportedVersion()) {
TGSong song = this.read();
this.dataInputStream.close();
return song;
}
throw new TGFileFormatException("Unsopported Version");
} catch (Throwable throwable) {
throw new TGFileFormatException(throwable);
}
}
private String readString(int length) {
try {
char[] chars = new char[length];
for (int i = 0; i < chars.length; i++) {
chars[i] = this.dataInputStream.readChar();
}
return String.copyValueOf(chars);
} catch (IOException e) {
LOG.error(e);
}
return null;
}
private TGStroke readStroke() {
final int directionId = readByte();
final int value = readByte();
Direction direction = null;
if (directionId == Direction.NONE.getId()) {
direction = Direction.NONE;
} else if (directionId == Direction.UP.getId()) {
direction = Direction.UP;
} else if (directionId == Direction.DOWN.getId()) {
direction = Direction.DOWN;
} else {
LOG.error("Unknown direction ID: " + directionId);
}
return new TGStroke(direction, value);
}
private void readTempo(TGTempo tempo) {
// leo el valor
tempo.setValue(readShort());
}
private void readText(TGBeat beat) {
TGText text = new TGTextImpl();
// leo el texto
text.setValue(readUnsignedByteString());
beat.setText(text);
}
private void readTimeSignature(TGTimeSignature timeSignature) {
// leo el numerador
timeSignature.setNumerator(readByte());
// leo el denominador
readDuration(timeSignature.getDenominator());
}
protected TGTrack readTrack(int number, TGSong song) {
// header
int header = readHeader();
TGTrack track = new TGTrackImpl();
track.setNumber(number);
// leo el nombre
track.setName(readUnsignedByteString());
// leo el solo
track.setSolo((header & TRACK_SOLO) != 0);
// leo el mute
track.setMute((header & TRACK_MUTE) != 0);
// leo el canal
readChannel(track.getChannel());
// leo la cantidad de compases
int measureCount = song.countMeasureHeaders();
// leo los compases
TGMeasure lastMeasure = null;
for (int i = 0; i < measureCount; i++) {
TGMeasure measure = readMeasure(song.getMeasureHeader(i), lastMeasure);
track.addMeasure(measure);
lastMeasure = measure;
}
// leo la cantidad de cuerdas
int stringCount = readByte();
// leo las cuerdas
for (int i = 0; i < stringCount; i++) {
track.getStrings().add(readInstrumentString(i + 1));
}
// leo el offset
track.setOffset(TGTrack.MIN_OFFSET + readByte());
// leo el color
track.setColor(this.readRGBColor());
// leo el lyrics
if (((header & TRACK_LYRICS) != 0)) {
readLyrics(track.getLyrics());
}
return track;
}
protected BendingEffect readTremoloBarEffect() {
BendingEffect tremoloBar = new BendingEffect();
// leo la cantidad de puntos
int count = readByte();
for (int i = 0; i < count; i++) {
// leo la posicion
int position = readByte();
// leo el valor
int value = (readByte() - EffectPoint.MAX_VALUE_LENGTH);
// agrego el punto
tremoloBar.addPoint(position, value);
}
return tremoloBar;
}
protected TGEffectTremoloPicking readTremoloPickingEffect() {
TGEffectTremoloPicking effect = new TGEffectTremoloPicking();
// leo la duracion
effect.getDuration().setValue(readByte());
return effect;
}
protected TGEffectTrill readTrillEffect() {
TGEffectTrill effect = new TGEffectTrill();
// leo el fret
effect.setFret(readByte());
// leo la duracion
effect.getDuration().setValue(readByte());
return effect;
}
protected String readUnsignedByteString() {
try {
return readString((this.dataInputStream.read() & 0xFF));
} catch (IOException e) {
LOG.error(e);
}
return null;
}
private void readVersion() {
if (this.version == null) {
this.version = readUnsignedByteString();
}
}
private void readVoices(int header, TGBeat beat, TGBeatData data) {
for (int i = 0; i < TGBeat.MAX_VOICES; i++) {
int shift = (i * 2);
beat.getVoice(i).setEmpty(true);
if (((header & (BEAT_HAS_VOICE << shift)) != 0)) {
if (((header & (BEAT_HAS_VOICE_CHANGES << shift)) != 0)) {
data.getVoice(i).setFlags(readHeader());
}
int flags = data.getVoice(i).getFlags();
// leo la duracion
if (((flags & VOICE_NEXT_DURATION) != 0)) {
readDuration(data.getVoice(i).getDuration());
}
// leo las notas
if (((flags & VOICE_HAS_NOTES) != 0)) {
readNotes(beat.getVoice(i), data);
}
// leo la direccion
if (((flags & VOICE_DIRECTION_UP) != 0)) {
beat.getVoice(i).setDirection(Direction.UP);
} else if (((flags & VOICE_DIRECTION_DOWN) != 0)) {
beat.getVoice(i).setDirection(Direction.DOWN);
}
beat.getVoice(i).setDuration(data.getVoice(i).getDuration().clone());
data.getVoice(i).setStart(
data.getVoice(i).getStart()
+ beat.getVoice(i).getDuration().getTime());
beat.getVoice(i).setEmpty(false);
}
}
}
}