/*
* Created on 16-dic-2005
*
* TODO To change the template for this generated file go to Window -
* Preferences - Java - Code Style - Code Templates
*/
package org.herac.tuxguitar.io.tg;
import java.awt.Color;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.log4j.Logger;
import org.herac.tuxguitar.io.base.TGFileFormat;
import org.herac.tuxguitar.io.base.TGOutputStreamBase;
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;
/**
* @author julian
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class TGOutputStream extends TGStream implements TGOutputStreamBase {
private DataOutputStream dataOutputStream;
public TGFileFormat getFileFormat() {
return new TGFileFormat("TuxGuitar", "*.tg");
}
public void init(OutputStream stream) {
this.dataOutputStream = new DataOutputStream(stream);
}
public boolean isSupportedExtension(String extension) {
return (extension.toLowerCase().equals(TG_FORMAT_EXTENSION));
}
private void write(TGSong song) {
// escribo el nombre
writeUnsignedByteString(song.getName());
// escribo el artista
writeUnsignedByteString(song.getArtist());
// escribo el album
writeUnsignedByteString(song.getAlbum());
// escribo el autor
writeUnsignedByteString(song.getAuthor());
// escribo la fecha
writeUnsignedByteString(song.getDate());
// escribo el copyright
writeUnsignedByteString(song.getCopyright());
// escribo el creador
writeUnsignedByteString(song.getWriter());
// escribo el transcriptor
writeUnsignedByteString(song.getTranscriber());
// escribo los comentarios
writeIntegerString(song.getComments());
// escribo la cantidad de measure headers
writeShort((short) song.countMeasureHeaders());
// escribo las pistas
TGMeasureHeader lastHeader = null;
for (final TGMeasureHeader header : song.getMeasureHeaders()) {
writeMeasureHeader(header, lastHeader);
lastHeader = header;
}
// escribo la cantidad de pistas
writeByte(song.countTracks());
// escribo las pistas
for (int i = 0; i < song.countTracks(); i++) {
TGTrack track = song.getTrack(i);
writeTrack(track);
}
}
private void writeBeat(TGBeat beat, TGBeatData data, boolean hasNext) {
int header = hasNext ? BEAT_HAS_NEXT : 0;
// Berifico si hay cambios en las voces
for (int i = 0; i < TGBeat.MAX_VOICES; i++) {
int shift = (i * 2);
if (!beat.getVoice(i).isEmpty()) {
header |= (BEAT_HAS_VOICE << shift);
int flags = (beat.getVoice(i).isRestVoice() ? 0 : VOICE_HAS_NOTES);
if (!beat.getVoice(i).getDuration().isEqual(
data.getVoice(i).getDuration())) {
flags |= VOICE_NEXT_DURATION;
data.getVoice(i).setDuration(beat.getVoice(i).getDuration());
}
if (!beat.getVoice(i).getDirection().equals(Direction.NONE)) {
if (beat.getVoice(i).getDirection().equals(Direction.UP)) {
flags |= VOICE_DIRECTION_UP;
} else if (beat.getVoice(i).getDirection().equals(Direction.DOWN)) {
flags |= VOICE_DIRECTION_DOWN;
}
}
if (data.getVoice(i).getFlags() != flags) {
header |= (BEAT_HAS_VOICE_CHANGES << shift);
data.getVoice(i).setFlags(flags);
}
}
}
// Berifico si tiene stroke
if (!beat.getStroke().getDirection().equals(Direction.NONE)) {
header |= BEAT_HAS_STROKE;
}
// Berifico si tiene acorde
if (beat.getChord() != null) {
header |= BEAT_HAS_CHORD;
}
// Berifico si tiene texto
if (beat.getText() != null) {
header |= BEAT_HAS_TEXT;
}
// escribo la cabecera
writeHeader(header);
// escribo las voces
writeVoices(header, beat, data);
// escribo el stroke
if (((header & BEAT_HAS_STROKE) != 0)) {
writeStroke(beat.getStroke());
}
// escribo el acorde
if (((header & BEAT_HAS_CHORD) != 0)) {
writeChord(beat.getChord());
}
// escribo el texto
if (((header & BEAT_HAS_TEXT) != 0)) {
writeText(beat.getText());
}
}
private void writeBeats(TGMeasure measure, TGBeatData data) {
int count = measure.countBeats();
for (int i = 0; i < count; i++) {
TGBeat beat = measure.getBeat(i);
writeBeat(beat, data, (i + 1 < count));
}
}
private void writeBendEffect(BendingEffect effect) {
// escribo la cantidad de puntos
writeByte(effect.getPoints().size());
for (final EffectPoint point : effect.getPoints()) {
// escribo la posicion
writeByte(point.getPosition());
// escribo el valor
writeByte(point.getValue());
}
}
public void writeByte(int v) {
try {
this.dataOutputStream.write(v);
} catch (IOException e) {
LOG.error(e);
}
}
private void writeChannel(TGChannel channel) {
// escribo el canal
writeByte(channel.getChannel());
// escribo el canal de efectos
writeByte(channel.getEffectChannel());
// escribo el instrumento
writeByte(channel.getInstrument());
// escribo el volumen
writeByte(channel.getVolume());
// escribo el balance
writeByte(channel.getBalance());
// escribo el chorus
writeByte(channel.getChorus());
// escribo el reverb
writeByte(channel.getReverb());
// escribo el phaser
writeByte(channel.getPhaser());
// escribo el tremolo
writeByte(channel.getTremolo());
}
private void writeChord(TGChord chord) {
// escribo la cantidad de cuerdas
writeByte(chord.countStrings());
// escribo el nombre
writeUnsignedByteString(chord.getName());
// escribo el primer fret
writeByte(chord.getFirstFret());
// escribo el valor de cada cuerda
for (int string = 0; string < chord.countStrings(); string++) {
writeByte(chord.getFretValue(string));
}
}
private void writeDivisionType(TGDivisionType divisionType) {
// escribo los enters
writeByte(divisionType.getEnters());
// escribo los tiempos
writeByte(divisionType.getTimes());
}
private void writeDuration(TGDuration duration) {
int header = 0;
header = (duration.isDotted()) ? header |= DURATION_DOTTED : header;
header = (duration.isDoubleDotted()) ? header |= DURATION_DOUBLE_DOTTED
: header;
header = (!duration.getDivision().isEqual(TGDivisionType.NORMAL)) ? header |= DURATION_NO_TUPLET
: header;
writeHeader(header);
// escribo el valor
writeByte(duration.getValue());
// escribo el tipo de divisiones
if (((header & DURATION_NO_TUPLET) != 0)) {
writeDivisionType(duration.getDivision());
}
}
private void writeGraceEffect(TGEffectGrace effect) {
int header = 0;
header = (effect.isDead()) ? header |= GRACE_FLAG_DEAD : header;
header = (effect.isOnBeat()) ? header |= GRACE_FLAG_ON_BEAT : header;
// excribo el header
writeHeader(header);
// excribo el fret
writeByte(effect.getFret());
// excribo la duracion
writeByte(effect.getDuration());
// excribo el velocity
writeByte(effect.getDynamic());
// excribo la transicion
writeByte(effect.getTransition().getId());
}
private void writeHarmonicEffect(HarmonicEffect effect) {
// excribo el tipo
writeByte(effect.getId());
// excribo la data
if (!effect.equals(HarmonicEffect.NATURAL)) {
writeByte(effect.getData());
}
}
public void writeHeader(int v) {
try {
this.dataOutputStream.write(v);
} catch (IOException e) {
LOG.error(e);
}
}
public static final transient Logger LOG = Logger
.getLogger(TGOutputStream.class);
public void writeHeader(int v, int bCount) {
for (int i = bCount; i > 0; i--) {
writeHeader((v >>> ((8 * i) - 8)) & 0xFF);
}
}
private void writeInstrumentString(TGString string) {
// escribo el valor
writeByte(string.getValue());
}
private void writeIntegerString(String v) {
try {
this.dataOutputStream.writeInt(v.length());
this.dataOutputStream.writeChars(v);
} catch (IOException e) {
LOG.error(e);
}
}
private void writeLyrics(TGLyric lyrics) {
// escribo el compas de comienzo
writeShort((short) lyrics.getFrom());
// escribo el texto
writeIntegerString(lyrics.getLyrics());
}
private void writeMarker(TGMarker marker) {
// escribo el titulo
writeUnsignedByteString(marker.getTitle());
// escribo el color
writeRGBColor(marker.getColor());
}
private void writeMeasure(TGMeasure measure, TGMeasure lastMeasure) {
int header = 0;
if (lastMeasure == null) {
header |= MEASURE_CLEF;
header |= MEASURE_KEYSIGNATURE;
} else {
// Clef
if (measure.getClef() != lastMeasure.getClef()) {
header |= MEASURE_CLEF;
}
// KeySignature
if (measure.getKeySignature() != lastMeasure.getKeySignature()) {
header |= MEASURE_KEYSIGNATURE;
}
}
// escribo la cabecera
writeHeader(header);
// escribo los beats
TGBeatData data = new TGBeatData(measure);
writeBeats(measure, data);
// escribo la clave
if (((header & MEASURE_CLEF) != 0)) {
switch (measure.getClef()) {
case ALTO:
writeByte(4);
break;
case BASS:
writeByte(2);
break;
case TENOR:
writeByte(3);
break;
case TREBLE:
writeByte(1);
break;
}
}
// escribo el key signature
if (((header & MEASURE_KEYSIGNATURE) != 0)) {
writeByte(measure.getKeySignature());
}
}
private void writeMeasureHeader(TGMeasureHeader measureheader,
TGMeasureHeader lastMeasureHeader) {
int header = 0;
if (lastMeasureHeader == null) {
header |= MEASURE_HEADER_TIMESIGNATURE;
header |= MEASURE_HEADER_TEMPO;
if (measureheader.getTripletFeel() != TGMeasureHeader.TRIPLET_FEEL_NONE) {
header |= MEASURE_HEADER_TRIPLET_FEEL;
}
} else {
// Time Signature
int numerator = measureheader.getTimeSignature().getNumerator();
int value = measureheader.getTimeSignature().getDenominator().getValue();
int prevNumerator = lastMeasureHeader.getTimeSignature().getNumerator();
int prevValue = lastMeasureHeader.getTimeSignature().getDenominator()
.getValue();
if (numerator != prevNumerator || value != prevValue) {
header |= MEASURE_HEADER_TIMESIGNATURE;
}
// Tempo
if (measureheader.getTempo().getValue() != lastMeasureHeader.getTempo()
.getValue()) {
header |= MEASURE_HEADER_TEMPO;
}
// Triplet Feel
if (measureheader.getTripletFeel() != lastMeasureHeader.getTripletFeel()) {
header |= MEASURE_HEADER_TRIPLET_FEEL;
}
}
header = (measureheader.isRepeatOpen()) ? header |= MEASURE_HEADER_REPEAT_OPEN
: header;
header = (measureheader.getRepeatClose() > 0) ? header |= MEASURE_HEADER_REPEAT_CLOSE
: header;
header = (measureheader.getRepeatAlternative() > 0) ? header |= MEASURE_HEADER_REPEAT_ALTERNATIVE
: header;
header = (measureheader.hasMarker()) ? header |= MEASURE_HEADER_MARKER
: header;
writeHeader(header);
// escribo el timeSignature
if (((header & MEASURE_HEADER_TIMESIGNATURE) != 0)) {
writeTimeSignature(measureheader.getTimeSignature());
}
// escribo el tempo
if (((header & MEASURE_HEADER_TEMPO) != 0)) {
writeTempo(measureheader.getTempo());
}
// escribo el numero de repeticiones
if (((header & MEASURE_HEADER_REPEAT_CLOSE) != 0)) {
writeShort((short) measureheader.getRepeatClose());
}
// escribo los finales alternativos
if (((header & MEASURE_HEADER_REPEAT_ALTERNATIVE) != 0)) {
writeByte(measureheader.getRepeatAlternative());
}
// escribo el marker
if (((header & MEASURE_HEADER_MARKER) != 0)) {
writeMarker(measureheader.getMarker());
}
// escribo el triplet feel
if (((header & MEASURE_HEADER_TRIPLET_FEEL) != 0)) {
writeByte(measureheader.getTripletFeel());
}
}
private void writeNote(int header, TGNote note) {
// escribo el valor
writeByte(note.getValue());
// escribo la cuerda
writeByte(note.getString());
// escribo el velocity
if (((header & NOTE_VELOCITY) != 0)) {
writeByte(note.getVelocity());
}
// escribo los efectos
if (((header & NOTE_EFFECT) != 0)) {
writeNoteEffect(note.getEffect());
}
}
private void writeNoteEffect(TGNoteEffect effect) {
int header = 0;
header = (effect.isBend()) ? header |= EFFECT_BEND : header;
header = (effect.isTremoloBar()) ? header |= EFFECT_TREMOLO_BAR : header;
header = (effect.isHarmonic()) ? header |= EFFECT_HARMONIC : header;
header = (effect.isGrace()) ? header |= EFFECT_GRACE : header;
header = (effect.isTrill()) ? header |= EFFECT_TRILL : header;
header = (effect.isTremoloPicking()) ? header |= EFFECT_TREMOLO_PICKING
: header;
header = (effect.isVibrato()) ? header |= EFFECT_VIBRATO : header;
header = (effect.isDeadNote()) ? header |= EFFECT_DEAD : header;
header = (effect.isSlide()) ? header |= EFFECT_SLIDE : header;
header = (effect.isHammer()) ? header |= EFFECT_HAMMER : header;
header = (effect.isGhostNote()) ? header |= EFFECT_GHOST : header;
header = (effect.isAccentuatedNote()) ? header |= EFFECT_ACCENTUATED
: header;
header = (effect.isHeavyAccentuatedNote()) ? header |= EFFECT_HEAVY_ACCENTUATED
: header;
header = (effect.isPalmMute()) ? header |= EFFECT_PALM_MUTE : header;
header = (effect.isStaccato()) ? header |= EFFECT_STACCATO : header;
header = (effect.isTapping()) ? header |= EFFECT_TAPPING : header;
header = (effect.isSlapping()) ? header |= EFFECT_SLAPPING : header;
header = (effect.isPopping()) ? header |= EFFECT_POPPING : header;
header = (effect.isFadeIn()) ? header |= EFFECT_FADE_IN : header;
header = (effect.isLetRing()) ? header |= EFFECT_LET_RING : header;
writeHeader(header, 3);
// escribo el bend
if (((header & EFFECT_BEND) != 0)) {
writeBendEffect(effect.getBend());
}
// leo el tremolo bar
if (((header & EFFECT_TREMOLO_BAR) != 0)) {
writeTremoloBarEffect(effect.getTremoloBar());
}
// leo el harmonic
if (((header & EFFECT_HARMONIC) != 0)) {
writeHarmonicEffect(effect.getHarmonic());
}
// leo el grace
if (((header & EFFECT_GRACE) != 0)) {
writeGraceEffect(effect.getGrace());
}
// leo el trill
if (((header & EFFECT_TRILL) != 0)) {
writeTrillEffect(effect.getTrill());
}
// leo el tremolo picking
if (((header & EFFECT_TREMOLO_PICKING) != 0)) {
writeTremoloPickingEffect(effect.getTremoloPicking());
}
}
private void writeNotes(TGVoice voice, TGBeatData data) {
for (int i = 0; i < voice.getNotes().size(); i++) {
TGNote note = voice.getNote(i);
int header = (i + 1 < voice.getNotes().size() ? NOTE_HAS_NEXT : 0);
header = (note.isTiedNote()) ? header |= NOTE_TIED : header;
if (note.getVelocity() != data.getVoice(voice.getIndex()).getVelocity()) {
data.getVoice(voice.getIndex()).setVelocity(note.getVelocity());
header |= NOTE_VELOCITY;
}
header = (note.getEffect().hasAnyEffect()) ? header |= NOTE_EFFECT
: header;
writeHeader(header);
writeNote(header, note);
}
}
private void writeRGBColor(Color color) {
// escribo el RGB
writeByte(color.getRed());
writeByte(color.getGreen());
writeByte(color.getBlue());
}
public void writeShort(short v) {
try {
this.dataOutputStream.writeShort(v);
} catch (IOException e) {
LOG.error(e);
}
}
public void writeSong(TGSong song) throws IOException {
this.writeVersion();
this.write(song);
this.dataOutputStream.flush();
this.dataOutputStream.close();
}
private void writeStroke(TGStroke stroke) {
// escribo la direccion
writeByte(stroke.getDirection().getId());
// escribo el valor
writeByte(stroke.getValue());
}
private void writeTempo(TGTempo tempo) {
// escribo el valor
writeShort((short) tempo.getValue());
}
private void writeText(TGText text) {
// escribo el texto
writeUnsignedByteString(text.getValue());
}
private void writeTimeSignature(TGTimeSignature timeSignature) {
// escribo el numerador
writeByte(timeSignature.getNumerator());
// escribo el denominador
writeDuration(timeSignature.getDenominator());
}
private void writeTrack(TGTrack track) {
// header
int header = 0;
if (track.isSolo()) {
header |= TRACK_SOLO;
}
if (track.isMute()) {
header |= TRACK_MUTE;
}
if (!track.getLyrics().getLyrics().isEmpty()) {
header |= TRACK_LYRICS;
}
writeHeader(header);
// escribo el nombre
writeUnsignedByteString(track.getName());
// escribo el canal
writeChannel(track.getChannel());
// escribo los compases
TGMeasure lastMeasure = null;
for (final TGMeasure measure : track.getMeasures()) {
writeMeasure(measure, lastMeasure);
lastMeasure = measure;
}
// escribo la cantidad de cuerdas
writeByte(track.getStrings().size());
// escribo las cuerdas
for (final TGString string : track.getStrings()) {
writeInstrumentString(string);
}
// escribo el offset
writeByte(track.getOffset() - TGTrack.MIN_OFFSET);
// escribo el color
writeRGBColor(track.getColor());
// escribo el lyrics
if (((header & TRACK_LYRICS) != 0)) {
writeLyrics(track.getLyrics());
}
}
private void writeTremoloBarEffect(BendingEffect effect) {
// escribo la cantidad de puntos
writeByte(effect.getPoints().size());
for (final EffectPoint point : effect.getPoints()) {
// escribo la posicion
writeByte(point.getPosition());
// escribo el valor
writeByte((point.getValue() + EffectPoint.MAX_VALUE_LENGTH));
}
}
private void writeTremoloPickingEffect(TGEffectTremoloPicking effect) {
// excribo la duracion
writeByte(effect.getDuration().getValue());
}
private void writeTrillEffect(TGEffectTrill effect) {
// excribo el fret
writeByte(effect.getFret());
// excribo la duracion
writeByte(effect.getDuration().getValue());
}
private void writeUnsignedByteString(String v) {
try {
String byteString = (v == null ? new String() : ((v.length() > 0xFF) ? v
.substring(0, 0xFF) : v));
this.dataOutputStream.write(byteString.length());
this.dataOutputStream.writeChars(byteString);
} catch (IOException e) {
LOG.error(e);
}
}
private void writeVersion() {
writeUnsignedByteString(TG_FORMAT_VERSION);
}
private void writeVoices(int header, TGBeat beat, TGBeatData data) {
for (int i = 0; i < TGBeat.MAX_VOICES; i++) {
int shift = (i * 2);
if (((header & (BEAT_HAS_VOICE << shift)) != 0)) {
if (((header & (BEAT_HAS_VOICE_CHANGES << shift)) != 0)) {
writeHeader(data.getVoice(i).getFlags());
}
// escribo la duracion
if (((data.getVoice(i).getFlags() & VOICE_NEXT_DURATION) != 0)) {
writeDuration(beat.getVoice(i).getDuration());
}
// escribo las notas
if (((data.getVoice(i).getFlags() & VOICE_HAS_NOTES) != 0)) {
writeNotes(beat.getVoice(i), data);
}
}
}
}
}