/* * * 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.v10; 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.TGFileFormatException; import org.herac.tuxguitar.io.base.TGLocalFileExporter; import org.herac.tuxguitar.song.models.TGBeat; 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.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 TGLocalFileExporter { /** The Logger for this class. */ public static final transient Logger LOG = Logger .getLogger(TGOutputStream.class); public class TGVoiceJoiner { // private TGFactory factory; private TGMeasure measure; public TGVoiceJoiner(TGMeasure measure) { // this.factory = factory; this.measure = measure.clone(measure.getHeader()); this.measure.setTrack(measure.getTrack()); } public void joinBeats() { TGBeat previous = null; boolean finish = true; long measureStart = this.measure.getStart(); long measureEnd = (measureStart + this.measure.getLength()); for (int i = 0; i < this.measure.countBeats(); i++) { TGBeat beat = this.measure.getBeat(i); TGVoice voice = beat.getVoice(0); for (int v = 1; v < beat.countVoices(); v++) { TGVoice currentVoice = beat.getVoice(v); if (!currentVoice.isEmpty()) { for (int n = 0; n < currentVoice.getNotes().size(); n++) { TGNote note = currentVoice.getNote(n); voice.addNote(note); } } } if (voice.isEmpty()) { this.measure.removeBeat(beat); finish = false; break; } long beatStart = beat.getStart(); if (previous != null) { long previousStart = previous.getStart(); TGDuration previousBestDuration = null; for (int v = /* 1 */0; v < previous.countVoices(); v++) { TGVoice previousVoice = previous.getVoice(v); if (!previousVoice.isEmpty()) { long length = previousVoice.getDuration().getTime(); if ((previousStart + length) <= beatStart) { if (previousBestDuration == null || length > previousBestDuration.getTime()) { previousBestDuration = previousVoice.getDuration(); } } } } if (previousBestDuration != null) { previous.getVoice(0).setDuration(previousBestDuration.clone()); } else { if (voice.isRestVoice()) { this.measure.removeBeat(beat); finish = false; break; } TGDuration duration = TGDuration .fromTime((beatStart - previousStart)); previous.getVoice(0).setDuration(duration.clone()); } } TGDuration beatBestDuration = null; for (int v = /* 1 */0; v < beat.countVoices(); v++) { TGVoice currentVoice = beat.getVoice(v); if (!currentVoice.isEmpty()) { long length = currentVoice.getDuration().getTime(); if ((beatStart + length) <= measureEnd) { if (beatBestDuration == null || length > beatBestDuration.getTime()) { beatBestDuration = currentVoice.getDuration(); } } } } if (beatBestDuration == null) { if (voice.isRestVoice()) { this.measure.removeBeat(beat); finish = false; break; } TGDuration duration = TGDuration.fromTime((measureEnd - beatStart)); voice.setDuration(duration.clone()); } previous = beat; } if (!finish) { joinBeats(); } } public void orderBeats() { for (int i = 0; i < this.measure.countBeats(); i++) { TGBeat minBeat = null; for (int j = i; j < this.measure.countBeats(); j++) { TGBeat beat = this.measure.getBeat(j); if (minBeat == null || beat.getStart() < minBeat.getStart()) { minBeat = beat; } } this.measure.moveBeat(i, minBeat); } } public TGMeasure process() { this.orderBeats(); this.joinBeats(); return this.measure; } } private DataOutputStream dataOutputStream; public boolean configure(boolean setDefaults) { return true; } public void exportSong(TGSong song) throws TGFileFormatException { try { this.writeVersion(); this.write(song); this.dataOutputStream.flush(); this.dataOutputStream.close(); } catch (Throwable throwable) { throw new TGFileFormatException(throwable); } } public String getExportName() { return "TuxGuitar 1.0"; } 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 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) { TGVoice voice = beat.getVoice(0); int header = hasNext ? BEAT_HAS_NEXT : 0; // Berifico si hay cambio de duracion if (!voice.getDuration().isEqual(data.getDuration())) { header |= BEAT_NEXT_DURATION; data.setDuration(voice.getDuration()); } // Berifico si tiene notas if (!beat.isRestBeat()) { header |= BEAT_HAS_NOTES; } // 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 la duracion if (((header & BEAT_NEXT_DURATION) != 0)) { writeDuration(voice.getDuration()); } // escribo las notas if (((header & BEAT_HAS_NOTES) != 0)) { writeNotes(voice, data); } // 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(TGTrack track) { int header = 0; header = (track.isSolo()) ? header |= CHANNEL_SOLO : header; header = (track.isMute()) ? header |= CHANNEL_MUTE : header; writeHeader(header); // escribo el canal writeByte(track.getChannel().getChannel()); // escribo el canal de efectos writeByte(track.getChannel().getEffectChannel()); // escribo el instrumento writeByte(track.getChannel().getInstrument()); // escribo el volumen writeByte(track.getChannel().getVolume()); // escribo el balance writeByte(track.getChannel().getBalance()); // escribo el chorus writeByte(track.getChannel().getChorus()); // escribo el reverb writeByte(track.getChannel().getReverb()); // escribo el phaser writeByte(track.getChannel().getPhaser()); // escribo el tremolo writeByte(track.getChannel().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_TUPLE : header; writeHeader(header); // escribo el valor writeByte(duration.getValue()); // escribo el tipo de divisiones if (((header & DURATION_NO_TUPLE) != 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 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 srcMeasure, TGMeasure lastMeasure) { TGMeasure measure = new TGVoiceJoiner(srcMeasure).process(); 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; 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.getVelocity()) { data.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); } } 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.getLyrics().getLyrics().isEmpty()) { header |= TRACK_LYRICS; } writeHeader(header); // escribo el nombre writeUnsignedByteString(track.getName()); // escribo el canal writeChannel(track); // 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); } }