package org.herac.tuxguitar.io.gtp; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.herac.tuxguitar.io.base.TGFileFormat; 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.TGColor; 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.TGVelocities; import org.herac.tuxguitar.song.models.TGVoice; import org.herac.tuxguitar.song.models.effects.TGEffectBend; import org.herac.tuxguitar.song.models.effects.TGEffectGrace; import org.herac.tuxguitar.song.models.effects.TGEffectHarmonic; import org.herac.tuxguitar.song.models.effects.TGEffectTremoloBar; import org.herac.tuxguitar.song.models.effects.TGEffectTremoloPicking; import org.herac.tuxguitar.song.models.effects.TGEffectTrill; public class GP5InputStream extends GTPInputStream { private static final String supportedVersions[] = { "FICHIER GUITAR PRO v5.00","FICHIER GUITAR PRO v5.10"}; private static final float GP_BEND_SEMITONE = 25f; private static final float GP_BEND_POSITION = 60f; public GP5InputStream(GTPSettings settings) { super(settings, supportedVersions); } public TGFileFormat getFileFormat(){ return new TGFileFormat("Guitar Pro 5","*.gp5"); } public TGSong readSong() throws IOException, GTPFormatException { readVersion(); if (!isSupportedVersion(getVersion())) { this.close(); throw new GTPFormatException("Unsupported Version"); } TGSong song = getFactory().newSong(); readInfo(song); //lyrics int lyricTrack = readInt(); TGLyric lyric = readLyrics(); readPageSetup(); int tempoValue = readInt(); if(getVersionIndex() > 0){ skip(1); } readInt(); //key readByte(); //octave List channels = readChannels(); skip(42); int measures = readInt(); int tracks = readInt(); readMeasureHeaders(song, measures); readTracks(song, tracks, channels, lyric, lyricTrack); readMeasures(song, measures, tracks, tempoValue); this.close(); return song; } private void readInfo(TGSong song) throws IOException{ song.setName(readStringByteSizeOfInteger()); readStringByteSizeOfInteger(); song.setArtist(readStringByteSizeOfInteger()); song.setAlbum(readStringByteSizeOfInteger()); song.setAuthor(readStringByteSizeOfInteger()); readStringByteSizeOfInteger(); song.setCopyright(readStringByteSizeOfInteger()); song.setWriter(readStringByteSizeOfInteger()); readStringByteSizeOfInteger(); int comments = readInt(); for (int i = 0; i < comments; i++) { song.setComments( song.getComments() + readStringByteSizeOfInteger() ); } } private void readPageSetup() throws IOException{ skip( (getVersionIndex() > 0 ?49 : 30 ) ); for (int i = 0; i < 11; i++) { skip(4); readStringByte(0); } } private void readMeasureHeaders(TGSong song, int count) throws IOException{ TGTimeSignature timeSignature = getFactory().newTimeSignature(); for (int i = 0; i < count; i++) { if(i > 0 ){ skip(1); } song.addMeasureHeader(readMeasureHeader(i,timeSignature)); } } private void readTracks(TGSong song, int count, List channels,TGLyric lyric, int lyricTrack) throws IOException{ for (int number = 1; number <= count; number++) { song.addTrack(readTrack(number, channels,(number == lyricTrack)?lyric:getFactory().newLyric())); } skip( (getVersionIndex() == 0 ? 2 : 1) ); } private void readMeasures(TGSong song, int measures, int tracks, int tempoValue) throws IOException{ TGTempo tempo = getFactory().newTempo(); tempo.setValue(tempoValue); long start = TGDuration.QUARTER_TIME; for (int i = 0; i < measures; i++) { TGMeasureHeader header = song.getMeasureHeader(i); header.setStart(start); for (int j = 0; j < tracks; j++) { TGTrack track = song.getTrack(j); TGMeasure measure = getFactory().newMeasure(header); track.addMeasure(measure); readMeasure(measure, track, tempo); skip(1); } tempo.copy(header.getTempo()); start += header.getLength(); } } private TGLyric readLyrics() throws IOException{ TGLyric lyric = getFactory().newLyric(); lyric.setFrom(readInt()); lyric.setLyrics(readStringInteger()); for (int i = 0; i < 4; i++) { readInt(); readStringInteger(); } return lyric; } private long readBeat(long start, TGMeasure measure, TGTrack track, TGTempo tempo, int voiceIndex) throws IOException{ int flags = readUnsignedByte(); TGBeat beat = getBeat(measure, start); TGVoice voice = beat.getVoice(voiceIndex); if((flags & 0x40) != 0){ int beatType = readUnsignedByte(); voice.setEmpty( ( beatType & 0x02 ) == 0 ); } TGDuration duration = readDuration(flags); TGNoteEffect effect = getFactory().newEffect(); if ((flags & 0x02) != 0) { readChord(track.stringCount(), beat); } if ((flags & 0x04) != 0) { readText(beat); } if ((flags & 0x08) != 0) { readBeatEffects(beat, effect); } if ((flags & 0x10) != 0) { readMixChange(tempo); } int stringFlags = readUnsignedByte(); for (int i = 6; i >= 0; i--) { if ((stringFlags & (1 << i)) != 0 && (6 - i) < track.stringCount()) { TGString string = track.getString( (6 - i) + 1 ).clone(getFactory()); TGNote note = readNote(string,track,effect.clone(getFactory())); voice.addNote(note); } duration.copy(voice.getDuration()); } skip(1); int read = readByte(); //if (read == 8 || read == 10 || read == 24 ) { if( (read & 0x08) != 0 ){ skip(1); } return (!voice.isEmpty() ? duration.getTime() : 0 ); } private List readChannels() throws IOException{ List channels = new ArrayList(); for (int i = 0; i < 64; i++) { TGChannel channel = getFactory().newChannel(); channel.setChannel((short)i); channel.setEffectChannel((short)i); channel.setInstrument((short)readInt()); channel.setVolume(toChannelShort(readByte())); channel.setBalance(toChannelShort(readByte())); channel.setChorus(toChannelShort(readByte())); channel.setReverb(toChannelShort(readByte())); channel.setPhaser(toChannelShort(readByte())); channel.setTremolo(toChannelShort(readByte())); channels.add(channel); skip(2); } return channels; } private void readText(TGBeat beat) throws IOException{ TGText text = getFactory().newText(); text.setValue(readStringByteSizeOfInteger()); beat.setText(text); } private TGDuration readDuration(int flags) throws IOException { TGDuration duration = getFactory().newDuration(); duration.setValue( (int) (Math.pow( 2 , (readByte() + 4) ) / 4 ) ); duration.setDotted(((flags & 0x01) != 0)); if ((flags & 0x20) != 0) { int divisionType = readInt(); switch (divisionType) { case 3: duration.getDivision().setEnters(3); duration.getDivision().setTimes(2); break; case 5: duration.getDivision().setEnters(5); duration.getDivision().setTimes(4); break; case 6: duration.getDivision().setEnters(6); duration.getDivision().setTimes(4); break; case 7: duration.getDivision().setEnters(7); duration.getDivision().setTimes(4); break; case 9: duration.getDivision().setEnters(9); duration.getDivision().setTimes(8); break; case 10: duration.getDivision().setEnters(10); duration.getDivision().setTimes(8); break; case 11: duration.getDivision().setEnters(11); duration.getDivision().setTimes(8); break; case 12: duration.getDivision().setEnters(12); duration.getDivision().setTimes(8); break; } } return duration; } private int getTiedNoteValue(int string, TGTrack track) { int measureCount = track.countMeasures(); if (measureCount > 0) { for (int m = measureCount - 1; m >= 0; m--) { TGMeasure measure = track.getMeasure( m ); for (int b = measure.countBeats() - 1; b >= 0; b--) { TGBeat beat = measure.getBeat( b ); for (int v = 0; v < beat.countVoices(); v ++) { TGVoice voice = beat.getVoice(v); if(!voice.isEmpty()){ for (int n = 0; n < voice.countNotes(); n ++) { TGNote note = voice.getNote( n ); if (note.getString() == string) { return note.getValue(); } } } } } } } return -1; } private void readColor(TGColor color) throws IOException { color.setR(readUnsignedByte()); color.setG(readUnsignedByte()); color.setB(readUnsignedByte()); skip(1); } private TGMarker readMarker(int measure) throws IOException { TGMarker marker = getFactory().newMarker(); marker.setMeasure(measure); marker.setTitle(readStringByteSizeOfInteger()); readColor(marker.getColor()); return marker; } private TGMeasureHeader readMeasureHeader(int index,TGTimeSignature timeSignature) throws IOException { int flags = readUnsignedByte(); TGMeasureHeader header = getFactory().newHeader(); header.setNumber( (index + 1) ); header.setStart(0); header.getTempo().setValue(120); header.setRepeatOpen( ((flags & 0x04) != 0) ); if ((flags & 0x01) != 0) { timeSignature.setNumerator(readByte()); } if ((flags & 0x02) != 0) { timeSignature.getDenominator().setValue(readByte()); } timeSignature.copy(header.getTimeSignature()); if ((flags & 0x08) != 0) { header.setRepeatClose( ( (readByte() & 0xff) - 1) ); } if ((flags & 0x20) != 0) { header.setMarker(readMarker(header.getNumber())); } if ((flags & 0x10) != 0) { header.setRepeatAlternative(readUnsignedByte()); } if ((flags & 0x40) != 0) { readByte(); readByte(); } if ((flags & 0x01) != 0) { skip(4); } if ((flags & 0x10) == 0) { skip(1); } int tripletFeel = readByte(); if(tripletFeel == 1){ header.setTripletFeel(TGMeasureHeader.TRIPLET_FEEL_EIGHTH); }else if(tripletFeel == 2){ header.setTripletFeel(TGMeasureHeader.TRIPLET_FEEL_SIXTEENTH); }else{ header.setTripletFeel(TGMeasureHeader.TRIPLET_FEEL_NONE); } return header; } private void readMeasure(TGMeasure measure, TGTrack track, TGTempo tempo) throws IOException { for( int voice = 0 ; voice < 2 ; voice ++ ){ long start = measure.getStart(); int beats = readInt(); for (int i = 0; i < beats; i++) { start += readBeat(start, measure, track, tempo, voice); } } List emptyBeats = new ArrayList(); for( int i = 0 ; i < measure.countBeats() ; i ++ ){ TGBeat beat = measure.getBeat( i ); boolean empty = true; for( int v = 0 ; v < beat.countVoices() ; v ++ ){ if( !beat.getVoice( v ).isEmpty() ){ empty = false; } } if( empty ){ emptyBeats.add( beat ); } } Iterator it = emptyBeats.iterator(); while( it.hasNext() ){ TGBeat beat = (TGBeat)it.next(); measure.removeBeat( beat ); } measure.setClef( getClef(track) ); } private TGNote readNote(TGString string,TGTrack track,TGNoteEffect effect)throws IOException { int flags = readUnsignedByte(); TGNote note = getFactory().newNote(); note.setString(string.getNumber()); note.setEffect(effect); note.getEffect().setAccentuatedNote(((flags & 0x40) != 0)); note.getEffect().setHeavyAccentuatedNote(((flags & 0x02) != 0)); note.getEffect().setGhostNote(((flags & 0x04) != 0)); if ((flags & 0x20) != 0) { int noteType = readUnsignedByte(); note.setTiedNote( (noteType == 0x02) ); note.getEffect().setDeadNote((noteType == 0x03)); } if ((flags & 0x10) != 0) { note.setVelocity((TGVelocities.MIN_VELOCITY + (TGVelocities.VELOCITY_INCREMENT * readByte())) - TGVelocities.VELOCITY_INCREMENT); } if ((flags & 0x20) != 0) { int fret = readByte(); int value = ( note.isTiedNote() ? getTiedNoteValue(string.getNumber(), track) : fret ); note.setValue( value >= 0 && value < 100 ? value : 0 ); } if ((flags & 0x80) != 0) { skip(2); } if ((flags & 0x01) != 0) { skip(8); } skip(1); if ((flags & 0x08) != 0) { readNoteEffects(note.getEffect()); } return note; } private TGTrack readTrack(int number, List channels,TGLyric lyrics) throws IOException { readUnsignedByte(); if(number == 1 || getVersionIndex() == 0){ skip(1); } TGTrack track = getFactory().newTrack(); track.setNumber(number); track.setLyrics(lyrics); track.setName(readStringByte(40)); int stringCount = readInt(); for (int i = 0; i < 7; i++) { int tuning = readInt(); if (stringCount > i) { TGString string = getFactory().newString(); string.setNumber(i + 1); string.setValue(tuning); track.getStrings().add(string); } } readInt(); readChannel(track.getChannel(), channels); readInt(); track.setOffset(readInt()); readColor(track.getColor()); skip( (getVersionIndex() > 0)? 49 : 44); if(getVersionIndex() > 0){ readStringByteSizeOfInteger(); readStringByteSizeOfInteger(); } return track; } private void readChannel(TGChannel channel,List channels) throws IOException { int index = (readInt() - 1); int effectChannel = (readInt() - 1); if(index >= 0 && index < channels.size()){ ((TGChannel) channels.get(index)).copy(channel); if (channel.getInstrument() < 0) { channel.setInstrument((short)0); } if(!channel.isPercussionChannel()){ channel.setEffectChannel((short)effectChannel); } } } private void readChord(int strings,TGBeat beat) throws IOException{ TGChord chord = getFactory().newChord(strings); this.skip(17); chord.setName(readStringByte(21)); this.skip(4); chord.setFirstFret(readInt()); for (int i = 0; i < 7; i++) { int fret = readInt(); if(i < chord.countStrings()){ chord.addFretValue(i,fret); } } this.skip(32); if(chord.countNotes() > 0){ beat.setChord(chord); } } private void readBeatEffects(TGBeat beat, TGNoteEffect noteEffect) throws IOException { int flags1 = readUnsignedByte(); int flags2 = readUnsignedByte(); noteEffect.setFadeIn(((flags1 & 0x10) != 0)); noteEffect.setVibrato(((flags1 & 0x02) != 0)); if ((flags1 & 0x20) != 0) { int effect = readUnsignedByte(); noteEffect.setTapping(effect == 1); noteEffect.setSlapping(effect == 2); noteEffect.setPopping(effect == 3); } if ((flags2 & 0x04) != 0) { readTremoloBar(noteEffect); } if ((flags1 & 0x40) != 0) { int strokeUp = readByte(); int strokeDown = readByte(); if( strokeUp > 0 ){ beat.getStroke().setDirection( TGStroke.STROKE_UP ); beat.getStroke().setValue( toStrokeValue(strokeUp) ); }else if( strokeDown > 0 ){ beat.getStroke().setDirection( TGStroke.STROKE_DOWN ); beat.getStroke().setValue( toStrokeValue(strokeDown) ); } } if ((flags2 & 0x02) != 0) { readByte(); } } private void readNoteEffects(TGNoteEffect noteEffect) throws IOException { int flags1 = readUnsignedByte(); int flags2 = readUnsignedByte(); if ((flags1 & 0x01) != 0) { readBend(noteEffect); } if ((flags1 & 0x10) != 0) { readGrace(noteEffect); } if ((flags2 & 0x04) != 0) { readTremoloPicking(noteEffect); } if ((flags2 & 0x08) != 0) { noteEffect.setSlide(true); readByte(); } if ((flags2 & 0x10) != 0) { readArtificialHarmonic(noteEffect); } if ((flags2 & 0x20) != 0) { readTrill(noteEffect); } noteEffect.setHammer(((flags1 & 0x02) != 0)); noteEffect.setVibrato(((flags2 & 0x40) != 0) || noteEffect.isVibrato()); noteEffect.setPalmMute(((flags2 & 0x02) != 0)); noteEffect.setStaccato(((flags2 & 0x01) != 0)); } private void readGrace(TGNoteEffect effect) throws IOException { int fret = readUnsignedByte(); int dynamic = readUnsignedByte(); int transition = readByte(); int duration = readUnsignedByte(); int flags = readUnsignedByte(); TGEffectGrace grace = getFactory().newEffectGrace(); grace.setFret( fret ); grace.setDynamic( (TGVelocities.MIN_VELOCITY + (TGVelocities.VELOCITY_INCREMENT * dynamic)) - TGVelocities.VELOCITY_INCREMENT ); grace.setDuration(duration); grace.setDead( (flags & 0x01) != 0 ); grace.setOnBeat( (flags & 0x02) != 0 ); if(transition == 0){ grace.setTransition(TGEffectGrace.TRANSITION_NONE); } else if(transition == 1){ grace.setTransition(TGEffectGrace.TRANSITION_SLIDE); } else if(transition == 2){ grace.setTransition(TGEffectGrace.TRANSITION_BEND); } else if(transition == 3){ grace.setTransition(TGEffectGrace.TRANSITION_HAMMER); } effect.setGrace(grace); } private void readBend(TGNoteEffect effect) throws IOException { skip(5); TGEffectBend bend = getFactory().newEffectBend(); int numPoints = readInt(); for (int i = 0; i < numPoints; i++) { int bendPosition = readInt(); int bendValue = readInt(); readByte(); int pointPosition = Math.round(bendPosition * TGEffectBend.MAX_POSITION_LENGTH / GP_BEND_POSITION); int pointValue = Math.round(bendValue * TGEffectBend.SEMITONE_LENGTH / GP_BEND_SEMITONE); bend.addPoint(pointPosition,pointValue); } if(!bend.getPoints().isEmpty()){ effect.setBend(bend); } } private void readTremoloBar(TGNoteEffect effect) throws IOException { skip(5); TGEffectTremoloBar tremoloBar = getFactory().newEffectTremoloBar(); int numPoints = readInt(); for (int i = 0; i < numPoints; i++) { int position = readInt(); int value = readInt(); readByte(); int pointPosition = Math.round(position * TGEffectTremoloBar.MAX_POSITION_LENGTH / GP_BEND_POSITION); int pointValue = Math.round(value / (GP_BEND_SEMITONE * 2f)); tremoloBar.addPoint(pointPosition,pointValue); } if(!tremoloBar.getPoints().isEmpty()){ effect.setTremoloBar(tremoloBar); } } private void readTrill(TGNoteEffect effect) throws IOException{ byte fret = readByte(); byte period = readByte(); TGEffectTrill trill = getFactory().newEffectTrill(); trill.setFret(fret); if(period == 1){ trill.getDuration().setValue(TGDuration.SIXTEENTH); effect.setTrill(trill); }else if(period == 2){ trill.getDuration().setValue(TGDuration.THIRTY_SECOND); effect.setTrill(trill); }else if(period == 3){ trill.getDuration().setValue(TGDuration.SIXTY_FOURTH); effect.setTrill(trill); } } private void readArtificialHarmonic(TGNoteEffect effect) throws IOException{ int type = readByte(); TGEffectHarmonic harmonic = getFactory().newEffectHarmonic(); harmonic.setData(0); if(type == 1){ harmonic.setType(TGEffectHarmonic.TYPE_NATURAL); effect.setHarmonic(harmonic); }else if(type == 2){ skip(3); harmonic.setType(TGEffectHarmonic.TYPE_ARTIFICIAL); effect.setHarmonic(harmonic); }else if(type == 3){ skip(1); harmonic.setType(TGEffectHarmonic.TYPE_TAPPED); effect.setHarmonic(harmonic); }else if(type == 4){ harmonic.setType(TGEffectHarmonic.TYPE_PINCH); effect.setHarmonic(harmonic); }else if(type == 5){ harmonic.setType(TGEffectHarmonic.TYPE_SEMI); effect.setHarmonic(harmonic); } } public void readTremoloPicking(TGNoteEffect effect) throws IOException{ int value = readUnsignedByte(); TGEffectTremoloPicking tp = getFactory().newEffectTremoloPicking(); if(value == 1){ tp.getDuration().setValue(TGDuration.EIGHTH); effect.setTremoloPicking(tp); }else if(value == 2){ tp.getDuration().setValue(TGDuration.SIXTEENTH); effect.setTremoloPicking(tp); }else if(value == 3){ tp.getDuration().setValue(TGDuration.THIRTY_SECOND); effect.setTremoloPicking(tp); } } private void readMixChange(TGTempo tempo) throws IOException { readByte(); //instrument skip(16); int volume = readByte(); int pan = readByte(); int chorus = readByte(); int reverb = readByte(); int phaser = readByte(); int tremolo = readByte(); readStringByteSizeOfInteger(); //tempoName int tempoValue = readInt(); if(volume >= 0){ readByte(); } if(pan >= 0){ readByte(); } if(chorus >= 0){ readByte(); } if(reverb >= 0){ readByte(); } if(phaser >= 0){ readByte(); } if(tremolo >= 0){ readByte(); } if(tempoValue >= 0){ tempo.setValue(tempoValue); skip(1); if(getVersionIndex() > 0){ skip(1); } } readByte(); skip(1); if(getVersionIndex() > 0){ readStringByteSizeOfInteger(); readStringByteSizeOfInteger(); } } private int toStrokeValue( int value ){ if( value == 1 || value == 2){ return TGDuration.SIXTY_FOURTH; } if( value == 3){ return TGDuration.THIRTY_SECOND; } if( value == 4){ return TGDuration.SIXTEENTH; } if( value == 5){ return TGDuration.EIGHTH; } if( value == 6){ return TGDuration.QUARTER; } return TGDuration.SIXTY_FOURTH; } private short toChannelShort(byte b){ short value = (short)(( b * 8 ) - 1); return (short)Math.max(value,0); } private int getClef( TGTrack track ){ if( !track.isPercussionTrack() ){ Iterator it = track.getStrings().iterator(); while( it.hasNext() ){ TGString string = (TGString) it.next(); if( string.getValue() <= 34 ){ return TGMeasure.CLEF_BASS; } } } return TGMeasure.CLEF_TREBLE; } private TGBeat getBeat(TGMeasure measure, long start){ int count = measure.countBeats(); for(int i = 0 ; i < count ; i ++ ){ TGBeat beat = measure.getBeat( i ); if( beat.getStart() == start ){ return beat; } } TGBeat beat = getFactory().newBeat(); beat.setStart(start); measure.addBeat(beat); return beat; } }