/* * Created on 09-ene-2006 * * TODO To change the template for this generated file go to Window - * Preferences - Java - Code Style - Code Templates */ package org.herac.tuxguitar.io.gtp; import java.awt.Color; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.herac.tuxguitar.io.base.TGFileFormat; import org.herac.tuxguitar.io.base.TGFileFormatException; 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.TGDivisionType; import org.herac.tuxguitar.song.models.TGDuration; 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.TGVelocities; 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.Transition; /** * @author julian * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public class GP3OutputStream extends GTPOutputStream { private static final int GP_BEND_POSITION = 60; private static final int GP_BEND_SEMITONE = 25; private static final String GP3_FORMAT_EXTENSION = ".gp3"; private static final String GP3_VERSION = "FICHIER GUITAR PRO v3.00"; /** The Logger for this class. */ public static final transient Logger LOG = Logger .getLogger(GP3OutputStream.class); public GP3OutputStream(GTPSettings settings) { super(settings); } private void createTrack(TGTrack track) throws IOException { int flags = 0; if (track.isPercussionTrack()) { flags |= 0x01; } writeUnsignedByte(flags); writeStringByte(track.getName(), 40); writeInt(track.getStrings().size()); for (int i = 0; i < 7; i++) { int value = 0; if (track.getStrings().size() > i) { TGString string = (TGString) track.getStrings().get(i); value = string.getValue(); } writeInt(value); } writeInt(1); writeInt(track.getChannel().getChannel() + 1); writeInt(track.getChannel().getEffectChannel() + 1); writeInt(24); writeInt(Math.min(Math.max(track.getOffset(), 0), 12)); writeColor(track.getColor()); } public TGFileFormat getFileFormat() { return new TGFileFormat("Guitar Pro 3", ("*" + GP3_FORMAT_EXTENSION)); } public boolean isSupportedExtension(String extension) { return (extension.toLowerCase().equals(GP3_FORMAT_EXTENSION)); } private TGChannel[] makeChannels(TGSong song) { TGChannel[] channels = new TGChannel[64]; for (int i = 0; i < channels.length; i++) { channels[i] = new TGChannel(); channels[i].setChannel((short) i); channels[i].setEffectChannel((short) i); channels[i].setInstrument((short) 24); channels[i].setVolume((short) 13); channels[i].setBalance((short) 8); channels[i].setChorus((short) 0); channels[i].setReverb((short) 0); channels[i].setPhaser((short) 0); channels[i].setTremolo((short) 0); } for (final TGTrack track : song.getTracks()) { channels[track.getChannel().getChannel()].setInstrument(track .getChannel().getInstrument()); channels[track.getChannel().getChannel()].setVolume(track.getChannel() .getVolume()); channels[track.getChannel().getChannel()].setBalance(track.getChannel() .getBalance()); channels[track.getChannel().getEffectChannel()].setInstrument(track .getChannel().getInstrument()); channels[track.getChannel().getEffectChannel()].setVolume(track .getChannel().getVolume()); channels[track.getChannel().getEffectChannel()].setBalance(track .getChannel().getBalance()); } return channels; } private byte parseDuration(TGDuration duration) { byte value = 0; switch (duration.getValue()) { case TGDuration.WHOLE: value = -2; break; case TGDuration.HALF: value = -1; break; case TGDuration.QUARTER: value = 0; break; case TGDuration.EIGHTH: value = 1; break; case TGDuration.SIXTEENTH: value = 2; break; case TGDuration.THIRTY_SECOND: value = 3; break; case TGDuration.SIXTY_FOURTH: value = 4; break; } return value; } private byte toChannelByte(short s) { return (byte) ((s + 1) / 8); } private List<String> toCommentLines(String comments) { List<String> lines = new ArrayList<String>(); String line = comments; while (line.length() > Byte.MAX_VALUE) { String subline = line.substring(0, Byte.MAX_VALUE); lines.add(subline); line = line.substring(Byte.MAX_VALUE); } lines.add(line); return lines; } private int toStrokeValue(TGStroke stroke) { if (stroke.getValue() == TGDuration.SIXTY_FOURTH) { return 2; } if (stroke.getValue() == TGDuration.THIRTY_SECOND) { return 3; } if (stroke.getValue() == TGDuration.SIXTEENTH) { return 4; } if (stroke.getValue() == TGDuration.EIGHTH) { return 5; } if (stroke.getValue() == TGDuration.QUARTER) { return 6; } return 2; } private void writeBeat(TGBeat beat, TGMeasure measure, boolean changeTempo) throws IOException { TGVoice voice = beat.getVoice(0); TGDuration duration = voice.getDuration(); int flags = 0; if (duration.isDotted() || duration.isDoubleDotted()) { flags |= 0x01; } if (!duration.getDivision().isEqual(TGDivisionType.NORMAL)) { flags |= 0x20; } if (beat.isTextBeat()) { flags |= 0x04; } if (changeTempo) { flags |= 0x10; } TGNoteEffect effect = null; if (voice.isRestVoice()) { flags |= 0x40; } else if (voice.getNotes().size() > 0) { TGNote note = voice.getNote(0); effect = note.getEffect(); if (effect.isVibrato() || effect.isTremoloBar() || effect.isTapping() || effect.isSlapping() || effect.isPopping() || effect.isHarmonic() || effect.isFadeIn() || !beat.getStroke().getDirection().equals(Direction.NONE)) { flags |= 0x08; } } writeUnsignedByte(flags); if ((flags & 0x40) != 0) { writeUnsignedByte(2); } writeByte(parseDuration(duration)); if ((flags & 0x20) != 0) { writeInt(duration.getDivision().getEnters()); } if ((flags & 0x04) != 0) { writeText(beat.getText()); } if ((flags & 0x08) != 0) { writeBeatEffects(beat, effect); } if ((flags & 0x10) != 0) { writeMixChange(measure.getTempo()); } int stringFlags = 0; if (!voice.isRestVoice()) { for (int i = 0; i < voice.getNotes().size(); i++) { TGNote playedNote = voice.getNote(i); int string = (7 - playedNote.getString()); stringFlags |= (1 << string); } } writeUnsignedByte(stringFlags); for (int i = 6; i >= 0; i--) { if ((stringFlags & (1 << i)) != 0) { for (int n = 0; n < voice.getNotes().size(); n++) { TGNote playedNote = voice.getNote(n); if (playedNote.getString() == (6 - i + 1)) { writeNote(playedNote); break; } } } } } private void writeBeatEffects(TGBeat beat, TGNoteEffect noteEffect) throws IOException { int flags = 0; if (noteEffect.isVibrato()) { flags += 0x01; } if (noteEffect.isTremoloBar() || noteEffect.isTapping() || noteEffect.isSlapping() || noteEffect.isPopping()) { flags += 0x20; } if (!beat.getStroke().getDirection().equals(Direction.NONE)) { flags |= 0x40; } if (noteEffect.isHarmonic()) { if (noteEffect.getHarmonic().equals(HarmonicEffect.NATURAL)) { flags += 0x04; } else { flags += 0x08; } } if (noteEffect.isFadeIn()) { flags += 0x10; } writeUnsignedByte(flags); if ((flags & 0x20) != 0) { if (noteEffect.isTremoloBar()) { writeUnsignedByte(0); writeInt(100); } else if (noteEffect.isTapping()) { writeUnsignedByte(1); writeInt(0); } else if (noteEffect.isSlapping()) { writeUnsignedByte(2); writeInt(0); } else if (noteEffect.isPopping()) { writeUnsignedByte(3); writeInt(0); } } if ((flags & 0x40) != 0) { writeUnsignedByte((beat.getStroke().getDirection().equals(Direction.DOWN) ? toStrokeValue(beat .getStroke()) : 0)); writeUnsignedByte((beat.getStroke().getDirection().equals(Direction.UP) ? toStrokeValue(beat .getStroke()) : 0)); } } private void writeBend(BendingEffect bend) throws IOException { int points = bend.getPoints().size(); writeByte((byte) 1); writeInt(0); writeInt(points); for (final EffectPoint point : bend.getPoints()) { writeInt((point.getPosition() * GP_BEND_POSITION / EffectPoint.MAX_POSITION_LENGTH)); writeInt((point.getValue() * GP_BEND_SEMITONE / EffectPoint.SEMITONE_LENGTH)); writeByte((byte) 0); } } private void writeChannels(TGSong song) throws IOException { TGChannel[] channels = makeChannels(song); for (int i = 0; i < channels.length; i++) { writeInt(channels[i].getInstrument()); writeByte(toChannelByte(channels[i].getVolume())); writeByte(toChannelByte(channels[i].getBalance())); writeByte(toChannelByte(channels[i].getChorus())); writeByte(toChannelByte(channels[i].getReverb())); writeByte(toChannelByte(channels[i].getPhaser())); writeByte(toChannelByte(channels[i].getTremolo())); writeBytes(new byte[] { 0, 0 }); } } private void writeColor(Color color) throws IOException { writeUnsignedByte(color.getRed()); writeUnsignedByte(color.getGreen()); writeUnsignedByte(color.getBlue()); writeByte((byte) 0); } private void writeGrace(TGEffectGrace grace) throws IOException { if (grace.isDead()) { writeUnsignedByte(0xff); } else { writeUnsignedByte(grace.getFret()); } writeUnsignedByte(((grace.getDynamic() - TGVelocities.MIN_VELOCITY) / TGVelocities.VELOCITY_INCREMENT) + 1); if (grace.getTransition() == Transition.NONE) { writeUnsignedByte(0); } else if (grace.getTransition() == Transition.SLIDE) { writeUnsignedByte(1); } else if (grace.getTransition() == Transition.BEND) { writeUnsignedByte(2); } else if (grace.getTransition() == Transition.HAMMER) { writeUnsignedByte(3); } writeUnsignedByte(grace.getDuration()); } private void writeInfo(TGSong song) throws IOException { List<String> comments = toCommentLines(song.getComments()); writeStringByteSizeOfInteger(song.getName()); writeStringByteSizeOfInteger(""); writeStringByteSizeOfInteger(song.getArtist()); writeStringByteSizeOfInteger(song.getAlbum()); writeStringByteSizeOfInteger(song.getAuthor()); writeStringByteSizeOfInteger(song.getCopyright()); writeStringByteSizeOfInteger(song.getWriter()); writeStringByteSizeOfInteger(""); writeInt(comments.size()); for (int i = 0; i < comments.size(); i++) { writeStringByteSizeOfInteger((String) comments.get(i)); } } private void writeMarker(TGMarker marker) throws IOException { writeStringByteSizeOfInteger(marker.getTitle()); writeColor(marker.getColor()); } private void writeMeasure(TGMeasure srcMeasure, boolean changeTempo) throws IOException { TGMeasure measure = new GTPVoiceJoiner(srcMeasure).process(); int beatCount = measure.countBeats(); writeInt(beatCount); for (int i = 0; i < beatCount; i++) { TGBeat beat = measure.getBeat(i); writeBeat(beat, measure, (changeTempo && i == 0)); } } private void writeMeasureHeader(TGMeasureHeader measure, TGTimeSignature timeSignature) throws IOException { int flags = 0; if (measure.getNumber() == 1 || measure.getTimeSignature().getNumerator() != timeSignature .getNumerator()) { flags |= 0x01; } if (measure.getNumber() == 1 || measure.getTimeSignature().getDenominator().getValue() != timeSignature .getDenominator().getValue()) { flags |= 0x02; } if (measure.isRepeatOpen()) { flags |= 0x04; } if (measure.getRepeatClose() > 0) { flags |= 0x08; } if (measure.hasMarker()) { flags |= 0x20; } writeUnsignedByte(flags); if ((flags & 0x01) != 0) { writeByte((byte) measure.getTimeSignature().getNumerator()); } if ((flags & 0x02) != 0) { writeByte((byte) measure.getTimeSignature().getDenominator().getValue()); } if ((flags & 0x08) != 0) { writeByte((byte) measure.getRepeatClose()); } if ((flags & 0x20) != 0) { writeMarker(measure.getMarker()); } } private void writeMeasureHeaders(TGSong song) throws IOException { TGTimeSignature timeSignature = new TGTimeSignature(); if (song.countMeasureHeaders() > 0) { for (int i = 0; i < song.countMeasureHeaders(); i++) { TGMeasureHeader measure = song.getMeasureHeader(i); writeMeasureHeader(measure, timeSignature); timeSignature.setNumerator(measure.getTimeSignature().getNumerator()); timeSignature.getDenominator().setValue( measure.getTimeSignature().getDenominator().getValue()); } } } private void writeMeasures(TGSong song, TGTempo tempo) throws IOException { for (int i = 0; i < song.countMeasureHeaders(); i++) { TGMeasureHeader header = song.getMeasureHeader(i); for (int j = 0; j < song.countTracks(); j++) { TGTrack track = song.getTrack(j); TGMeasure measure = track.getMeasure(i); writeMeasure(measure, (header.getTempo().getValue() != tempo.getValue())); } tempo = header.getTempo().clone(); } } private void writeMixChange(TGTempo tempo) throws IOException { for (int i = 0; i < 7; i++) { writeByte((byte) -1); } writeInt(tempo.getValue()); writeByte((byte) 0); } private void writeNote(TGNote note) throws IOException { int flags = (0x20 | 0x10); if (note.getEffect().isGhostNote()) { flags |= 0x04; } if (note.getEffect().isBend() || note.getEffect().isGrace() || note.getEffect().isSlide() || note.getEffect().isHammer() || note.getEffect().isLetRing()) { flags |= 0x08; } writeUnsignedByte(flags); if ((flags & 0x20) != 0) { int typeHeader = 0x01; if (note.isTiedNote()) { typeHeader = 0x02; } else if (note.getEffect().isDeadNote()) { typeHeader = 0x03; } writeUnsignedByte(typeHeader); } if ((flags & 0x10) != 0) { writeByte((byte) (((note.getVelocity() - TGVelocities.MIN_VELOCITY) / TGVelocities.VELOCITY_INCREMENT) + 1)); } if ((flags & 0x20) != 0) { writeByte((byte) note.getValue()); } if ((flags & 0x08) != 0) { writeNoteEffects(note.getEffect()); } } private void writeNoteEffects(TGNoteEffect effect) throws IOException { int flags = 0; if (effect.isBend()) { flags |= 0x01; } if (effect.isHammer()) { flags |= 0x02; } if (effect.isSlide()) { flags |= 0x04; } if (effect.isLetRing()) { flags |= 0x08; } if (effect.isGrace()) { flags |= 0x10; } writeUnsignedByte(flags); if ((flags & 0x01) != 0) { writeBend(effect.getBend()); } if ((flags & 0x10) != 0) { writeGrace(effect.getGrace()); } } public void writeSong(TGSong song) { try { if (song.isEmpty()) { throw new TGFileFormatException("Empty Song!!!"); } TGMeasureHeader header = song.getMeasureHeader(0); writeStringByte(GP3_VERSION, 30, DEFAULT_VERSION_CHARSET); writeInfo(song); writeBoolean((header.getTripletFeel() == TGMeasureHeader.TRIPLET_FEEL_EIGHTH)); writeInt(header.getTempo().getValue()); writeInt(0); writeChannels(song); writeInt(song.countMeasureHeaders()); writeInt(song.countTracks()); writeMeasureHeaders(song); writeTracks(song); writeMeasures(song, header.getTempo().clone()); close(); } catch (Exception e) { LOG.error(e); } } private void writeText(TGText text) throws IOException { writeStringByteSizeOfInteger(text.getValue()); } private void writeTracks(TGSong song) throws IOException { for (int i = 0; i < song.countTracks(); i++) { TGTrack track = song.getTrack(i); createTrack(track); } } }