1 package org.herac.tuxguitar.io.gtp;
2 
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.Iterator;
6 import java.util.List;
7 
8 import org.herac.tuxguitar.io.base.TGFileFormat;
9 import org.herac.tuxguitar.song.models.TGBeat;
10 import org.herac.tuxguitar.song.models.TGChannel;
11 import org.herac.tuxguitar.song.models.TGChord;
12 import org.herac.tuxguitar.song.models.TGColor;
13 import org.herac.tuxguitar.song.models.TGDuration;
14 import org.herac.tuxguitar.song.models.TGLyric;
15 import org.herac.tuxguitar.song.models.TGMarker;
16 import org.herac.tuxguitar.song.models.TGMeasure;
17 import org.herac.tuxguitar.song.models.TGMeasureHeader;
18 import org.herac.tuxguitar.song.models.TGNote;
19 import org.herac.tuxguitar.song.models.TGNoteEffect;
20 import org.herac.tuxguitar.song.models.TGSong;
21 import org.herac.tuxguitar.song.models.TGString;
22 import org.herac.tuxguitar.song.models.TGStroke;
23 import org.herac.tuxguitar.song.models.TGTempo;
24 import org.herac.tuxguitar.song.models.TGText;
25 import org.herac.tuxguitar.song.models.TGTimeSignature;
26 import org.herac.tuxguitar.song.models.TGTrack;
27 import org.herac.tuxguitar.song.models.TGVelocities;
28 import org.herac.tuxguitar.song.models.TGVoice;
29 import org.herac.tuxguitar.song.models.effects.TGEffectBend;
30 import org.herac.tuxguitar.song.models.effects.TGEffectGrace;
31 import org.herac.tuxguitar.song.models.effects.TGEffectHarmonic;
32 import org.herac.tuxguitar.song.models.effects.TGEffectTremoloBar;
33 import org.herac.tuxguitar.song.models.effects.TGEffectTremoloPicking;
34 import org.herac.tuxguitar.song.models.effects.TGEffectTrill;
35 
36 public class GP4InputStream extends GTPInputStream {
37 	private static final String SUPPORTED_VERSIONS[] = { "FICHIER GUITAR PRO v4.00", "FICHIER GUITAR PRO v4.06", "FICHIER GUITAR PRO L4.06" };
38 	private static final float GP_BEND_SEMITONE = 25f;
39 	private static final float GP_BEND_POSITION = 60f;
40 
41 	private int tripletFeel;
42 
43 	public GP4InputStream(GTPSettings settings){
44 		super(settings, SUPPORTED_VERSIONS);
GP3InputStream(GTPSettings settings)45 	}
46 
47 	public TGFileFormat getFileFormat(){
48 		return new TGFileFormat("Guitar Pro 4","*.gp4");
49 	}
50 
51 	public TGSong readSong() throws IOException, GTPFormatException {
52 		readVersion();
readSong()53 		if (!isSupportedVersion(getVersion())) {
54 			this.close();
55 			throw new GTPFormatException("Unsupported Version");
56 		}
57 		TGSong song = getFactory().newSong();
58 
59 		readInfo(song);
60 
61 		this.tripletFeel = ((readBoolean())?TGMeasureHeader.TRIPLET_FEEL_EIGHTH:TGMeasureHeader.TRIPLET_FEEL_NONE);
62 
63 		int lyricTrack = readInt();
64 		TGLyric lyric = readLyrics();
65 
66 		int tempoValue = readInt();
67 
68 		readInt(); //key
69 
70 		readByte(); //octave
71 
72 		List channels = readChannels();
73 
74 		int measures = readInt();
75 		int tracks = readInt();
76 
77 		readMeasureHeaders(song, measures);
78 		readTracks(song, tracks, channels, lyric, lyricTrack);
79 		readMeasures(song, measures, tracks, tempoValue);
80 
81 		this.close();
82 
readChannels()83 		return song;
84 	}
85 
86 	private void readInfo(TGSong song) throws IOException{
87 		song.setName(readStringByteSizeOfInteger());
88 		readStringByteSizeOfInteger();
89 		song.setArtist(readStringByteSizeOfInteger());
90 		song.setAlbum(readStringByteSizeOfInteger());
91 		song.setAuthor(readStringByteSizeOfInteger());
92 		song.setCopyright(readStringByteSizeOfInteger());
93 		song.setWriter(readStringByteSizeOfInteger());
94 		readStringByteSizeOfInteger();
95 		int comments = readInt();
96 		for (int i = 0; i < comments; i++) {
97 			song.setComments( song.getComments() + readStringByteSizeOfInteger() );
98 		}
99 	}
100 
101 	private void readMeasureHeaders(TGSong song, int count) throws IOException{
readInfo(TGSong song)102 		TGTimeSignature timeSignature = getFactory().newTimeSignature();
103 		for (int i = 0; i < count; i++) {
104 			song.addMeasureHeader(readMeasureHeader((i + 1),song,timeSignature));
105 		}
106 	}
107 
108 	private void readTracks(TGSong song, int count, List channels,TGLyric lyric, int lyricTrack) throws IOException{
109 		for (int number = 1; number <= count; number++) {
110 			song.addTrack(readTrack(number, channels,(number == lyricTrack)?lyric:getFactory().newLyric()));
111 		}
112 	}
113 
114 	private void readMeasures(TGSong song, int measures, int tracks, int tempoValue) throws IOException{
115 		TGTempo tempo = getFactory().newTempo();
116 		tempo.setValue(tempoValue);
readMeasureHeaders(TGSong song, int count)117 		long start = TGDuration.QUARTER_TIME;
118 		for (int i = 0; i < measures; i++) {
119 			TGMeasureHeader header = song.getMeasureHeader(i);
120 			header.setStart(start);
121 			for (int j = 0; j < tracks; j++) {
122 				TGTrack track = song.getTrack(j);
123 				TGMeasure measure = getFactory().newMeasure(header);
readTracks(TGSong song, int count, List channels)124 				track.addMeasure(measure);
125 				readMeasure(measure, track, tempo);
126 			}
127 			tempo.copy(header.getTempo());
128 			start += header.getLength();
129 		}
readMeasures(TGSong song, int measures, int tracks, int tempoValue)130 	}
131 
132 	private TGLyric readLyrics() throws IOException{
133 		TGLyric lyric = getFactory().newLyric();
134 		lyric.setFrom(readInt());
135 		lyric.setLyrics(readStringInteger());
136 		for (int i = 0; i < 4; i++) {
137 			readInt();
138 			readStringInteger();
139 		}
140 		return lyric;
141 	}
142 
143 	private List readChannels() throws IOException{
144 		List channels = new ArrayList();
145 		for (int i = 0; i < 64; i++) {
146 			TGChannel channel = getFactory().newChannel();
147 			channel.setChannel((short)i);
readDuration(int flags)148 			channel.setEffectChannel((short)i);
149 			channel.setInstrument((short)readInt());
150 			channel.setVolume(toChannelShort(readByte()));
151 			channel.setBalance(toChannelShort(readByte()));
152 			channel.setChorus(toChannelShort(readByte()));
153 			channel.setReverb(toChannelShort(readByte()));
154 			channel.setPhaser(toChannelShort(readByte()));
155 			channel.setTremolo(toChannelShort(readByte()));
156 			channels.add(channel);
157 			skip(2);
158 		}
159 		return channels;
160 	}
161 
162 	private long readBeat(long start, TGMeasure measure,TGTrack track, TGTempo tempo) throws IOException {
163 		int flags = readUnsignedByte();
164 		if((flags & 0x40) != 0){
165 			readUnsignedByte();
166 		}
167 
168 		TGBeat beat = getFactory().newBeat();
169 		TGVoice voice = beat.getVoice(0);
170 		TGDuration duration = readDuration(flags);
171 		TGNoteEffect effect = getFactory().newEffect();
172 		if ((flags & 0x02) != 0) {
173 			readChord(track.stringCount(), beat);
174 		}
175 		if ((flags & 0x04) != 0) {
176 			readText(beat);
177 		}
178 		if ((flags & 0x08) != 0) {
179 			readBeatEffects(beat,effect);
180 		}
181 		if ((flags & 0x10) != 0) {
182 			readMixChange(tempo);
183 		}
184 		int stringFlags = readUnsignedByte();
185 		for (int i = 6; i >= 0; i--) {
186 			if ((stringFlags & (1 << i)) != 0 && (6 - i) < track.stringCount()) {
187 				TGString string = track.getString( (6 - i) + 1 ).clone(getFactory());
188 				TGNote note = readNote(string, track,effect.clone(getFactory()));
189 				voice.addNote(note);
190 			}
191 		}
getTiedNoteValue(int string, TGTrack track)192 		beat.setStart(start);
193 		voice.setEmpty(false);
194 		duration.copy(voice.getDuration());
195 		measure.addBeat(beat);
196 
197 		return duration.getTime();
198 	}
199 
200 	private void readText(TGBeat beat) throws IOException{
201 		TGText text = getFactory().newText();
202 		text.setValue(readStringByteSizeOfInteger());
203 		beat.setText(text);
204 	}
205 
206 	private TGDuration readDuration(int flags) throws IOException {
207 		TGDuration duration = getFactory().newDuration();
208 		duration.setValue( (int) (Math.pow( 2 , (readByte() + 4) ) / 4 ) );
209 		duration.setDotted(((flags & 0x01) != 0));
210 		if ((flags & 0x20) != 0) {
211 			int divisionType = readInt();
readColor(TGColor color)212 			switch (divisionType) {
213 			case 3:
214 				duration.getDivision().setEnters(3);
215 				duration.getDivision().setTimes(2);
216 				break;
217 			case 5:
218 				duration.getDivision().setEnters(5);
219 				duration.getDivision().setTimes(4);
220 				break;
221 			case 6:
222 				duration.getDivision().setEnters(6);
223 				duration.getDivision().setTimes(4);
224 				break;
225 			case 7:
226 				duration.getDivision().setEnters(7);
227 				duration.getDivision().setTimes(4);
228 				break;
229 			case 9:
230 				duration.getDivision().setEnters(9);
231 				duration.getDivision().setTimes(8);
232 				break;
233 			case 10:
234 				duration.getDivision().setEnters(10);
235 				duration.getDivision().setTimes(8);
236 				break;
237 			case 11:
238 				duration.getDivision().setEnters(11);
239 				duration.getDivision().setTimes(8);
240 				break;
241 			case 12:
242 				duration.getDivision().setEnters(12);
243 				duration.getDivision().setTimes(8);
244 				break;
245 			}
246 		}
247 		return duration;
248 	}
249 
250 	private int getTiedNoteValue(int string, TGTrack track) {
251 		int measureCount = track.countMeasures();
252 		if (measureCount > 0) {
253 			for (int m = measureCount - 1; m >= 0; m--) {
254 				TGMeasure measure = track.getMeasure( m );
255 				for (int b = measure.countBeats() - 1; b >= 0; b--) {
256 					TGBeat beat = measure.getBeat( b );
257 					TGVoice voice = beat.getVoice(0);
258 					for (int n = 0; n < voice.countNotes(); n ++) {
readMeasure(TGMeasure measure, TGTrack track, TGTempo tempo)259 						TGNote note = voice.getNote( n );
260 						if (note.getString() == string) {
261 							return note.getValue();
262 						}
263 					}
264 				}
265 			}
266 		}
267 		return -1;
readBeat(long start, TGMeasure measure,TGTrack track, TGTempo tempo)268 	}
269 
270 	private void readColor(TGColor color) throws IOException {
271 		color.setR(readUnsignedByte());
272 		color.setG(readUnsignedByte());
273 		color.setB(readUnsignedByte());
274 		read();
275 	}
276 
277 	private TGMarker readMarker(int measure) throws IOException {
278 		TGMarker marker = getFactory().newMarker();
279 		marker.setMeasure(measure);
280 		marker.setTitle(readStringByteSizeOfInteger());
281 		readColor(marker.getColor());
282 		return marker;
283 	}
284 
285 	private TGMeasureHeader readMeasureHeader(int number,TGSong song,TGTimeSignature timeSignature) throws IOException {
286 		int flags = readUnsignedByte();
287 		TGMeasureHeader header = getFactory().newHeader();
288 		header.setNumber(number);
289 		header.setStart(0);
290 		header.getTempo().setValue(120);
291 		header.setTripletFeel(this.tripletFeel);
292 		header.setRepeatOpen( ((flags & 0x04) != 0) );
293 		if ((flags & 0x01) != 0) {
294 			timeSignature.setNumerator(readByte());
295 		}
296 		if ((flags & 0x02) != 0) {
297 			timeSignature.getDenominator().setValue(readByte());
298 		}
299 		timeSignature.copy(header.getTimeSignature());
300 		if ((flags & 0x08) != 0) {
301 			header.setRepeatClose(readByte());
302 		}
303 		if ((flags & 0x10) != 0) {
304 			header.setRepeatAlternative(parseRepeatAlternative(song, number, readUnsignedByte()));
305 		}
readText(TGBeat beat)306 		if ((flags & 0x20) != 0) {
307 			header.setMarker(readMarker(number));
308 		}
309 		if ((flags & 0x40) != 0) {
310 			readByte();
311 			readByte();
readNote(TGString string,TGTrack track,TGNoteEffect effect)312 		}
313 		return header;
314 	}
315 
316 	private void readMeasure(TGMeasure measure,TGTrack track, TGTempo tempo) throws IOException{
317 		long nextNoteStart = measure.getStart();
318 		int numberOfBeats = readInt();
319 		for (int i = 0; i < numberOfBeats; i++) {
320 			nextNoteStart += readBeat(nextNoteStart, measure, track, tempo);
321 		}
322 		measure.setClef( getClef(track) );
323 	}
324 
325 	private TGNote readNote(TGString string, TGTrack track,TGNoteEffect effect)throws IOException {
326 		int flags = readUnsignedByte();
327 		TGNote note = getFactory().newNote();
328 		note.setString(string.getNumber());
329 		note.setEffect(effect);
330 		note.getEffect().setAccentuatedNote(((flags & 0x40) != 0));
331 		note.getEffect().setGhostNote(((flags & 0x04) != 0));
332 		if ((flags & 0x20) != 0) {
333 			int noteType = readUnsignedByte();
334 			note.setTiedNote( (noteType == 0x02) );
335 			note.getEffect().setDeadNote((noteType == 0x03));
336 		}
337 		if ((flags & 0x01) != 0) {
338 			skip(2);
339 		}
340 		if ((flags & 0x10) != 0) {
341 			note.setVelocity((TGVelocities.MIN_VELOCITY + (TGVelocities.VELOCITY_INCREMENT * readByte())) - TGVelocities.VELOCITY_INCREMENT);
342 		}
readTrack(int number, List channels)343 		if ((flags & 0x20) != 0) {
344 			int fret = readByte();
345 			int value = ( note.isTiedNote() ? getTiedNoteValue(string.getNumber(), track) : fret );
346 			note.setValue( value >= 0 && value < 100 ? value : 0 );
347 		}
348 		if ((flags & 0x80) != 0) {
349 			skip(2);
350 		}
351 		if ((flags & 0x08) != 0) {
352 			readNoteEffects(note.getEffect());
353 		}
354 		return note;
355 	}
356 
357 	private TGTrack readTrack(int number, List channels,TGLyric lyrics) throws IOException {
358 		TGTrack track = getFactory().newTrack();
359 		track.setNumber(number);
360 		track.setLyrics(lyrics);
361 		readUnsignedByte();
362 		track.setName(readStringByte(40));
363 		int stringCount = readInt();
364 		for (int i = 0; i < 7; i++) {
365 			int tuning = readInt();
readChannel(TGChannel channel,List channels)366 			if (stringCount > i) {
367 				TGString string = getFactory().newString();
368 				string.setNumber(i + 1);
369 				string.setValue(tuning);
370 				track.getStrings().add(string);
371 			}
372 		}
373 		readInt();
374 		readChannel(track.getChannel(), channels);
375 		readInt();
376 		track.setOffset(readInt());
377 		readColor(track.getColor());
378 		return track;
379 	}
parseRepeatAlternative(TGSong song,int measure,int value)380 
381 	private void readChannel(TGChannel channel,List channels) throws IOException {
382 		int index = (readInt() - 1);
383 		int effectChannel = (readInt() - 1);
384 		if(index >= 0 && index < channels.size()){
385 			((TGChannel) channels.get(index)).copy(channel);
386 			if (channel.getInstrument() < 0) {
387 				channel.setInstrument((short)0);
388 			}
389 			if(!channel.isPercussionChannel()){
390 				channel.setEffectChannel((short)effectChannel);
391 			}
392 		}
393 	}
394 
395 	private int parseRepeatAlternative(TGSong song,int measure,int value){
396 		int repeatAlternative = 0;
397 		int existentAlternatives = 0;
398 		Iterator it = song.getMeasureHeaders();
399 		while(it.hasNext()){
400 			TGMeasureHeader header = (TGMeasureHeader)it.next();
401 			if(header.getNumber() == measure){
402 				break;
readChord(int strings, TGBeat beat)403 			}
404 			if(header.isRepeatOpen()){
405 				existentAlternatives = 0;
406 			}
407 			existentAlternatives |= header.getRepeatAlternative();
408 		}
409 
410 		for(int i = 0; i < 8; i ++){
411 			if(value > i && (existentAlternatives & (1 << i)) == 0){
412 				repeatAlternative |= (1 << i);
413 			}
414 		}
415 		return repeatAlternative;
416 	}
417 
418 	private void readChord(int strings,TGBeat beat) throws IOException {
419 		TGChord chord = getFactory().newChord(strings);
420 		if ((readUnsignedByte() & 0x01) == 0) {
421 			chord.setName( readStringByteSizeOfInteger() );
422 			chord.setFirstFret(readInt());
423 			if(chord.getFirstFret() != 0){
424 				for (int i = 0; i < 6; i++) {
425 					int fret = readInt();
426 					if(i < chord.countStrings()){
427 						chord.addFretValue(i,fret);
428 					}
429 				}
430 			}
431 		}
432 		else{
433 			skip(16);
434 			chord.setName(readStringByte(21));
readGrace(TGNoteEffect effect)435 			skip(4);
436 			chord.setFirstFret(readInt());
437 			for (int i = 0; i < 7; i++) {
438 				int fret = readInt();
439 				if(i < chord.countStrings()){
440 					chord.addFretValue(i,fret);
441 				}
442 			}
443 			skip(32);
444 		}
445 		if(chord.countNotes() > 0){
446 			beat.setChord(chord);
447 		}
448 	}
449 
450 	private void readGrace(TGNoteEffect effect) throws IOException {
451 		int fret = readUnsignedByte();
452 		TGEffectGrace grace = getFactory().newEffectGrace();
453 		grace.setOnBeat(false);
454 		grace.setDead( (fret == 255) );
455 		grace.setFret( ((!grace.isDead())?fret:0) );
456 		grace.setDynamic( (TGVelocities.MIN_VELOCITY + (TGVelocities.VELOCITY_INCREMENT * readUnsignedByte())) - TGVelocities.VELOCITY_INCREMENT );
457 		int transition = readUnsignedByte();
458 		if(transition == 0){
readBend(TGNoteEffect effect)459 			grace.setTransition( TGEffectGrace.TRANSITION_NONE );
460 		}
461 		else if(transition == 1){
462 			grace.setTransition(  TGEffectGrace.TRANSITION_SLIDE );
463 		}
464 		else if(transition == 2){
465 			grace.setTransition(  TGEffectGrace.TRANSITION_BEND );
466 		}
467 		else if(transition == 3){
468 			grace.setTransition(  TGEffectGrace.TRANSITION_HAMMER );
469 		}
470 		grace.setDuration(readUnsignedByte());
471 		effect.setGrace(grace);
472 	}
473 
474 	private void readBend(TGNoteEffect effect) throws IOException {
475 		TGEffectBend bend = getFactory().newEffectBend();
476 		skip(5);
readTremoloBar(TGNoteEffect noteEffect)477 		int points = readInt();
478 		for (int i = 0; i < points; i++) {
479 			int position = readInt();
480 			int value = readInt();
481 			readByte();
482 
483 			int pointPosition = Math.round(position * TGEffectBend.MAX_POSITION_LENGTH / GP_BEND_POSITION);
484 			int pointValue = Math.round(value * TGEffectBend.SEMITONE_LENGTH / GP_BEND_SEMITONE);
485 			bend.addPoint(pointPosition,pointValue);
readNoteEffects(TGNoteEffect effect)486 		}
487 		if(!bend.getPoints().isEmpty()){
488 			effect.setBend(bend);
489 		}
490 	}
491 
492 	private void readTremoloBar(TGNoteEffect effect) throws IOException {
493 		TGEffectTremoloBar tremoloBar = getFactory().newEffectTremoloBar();
494 		skip(5);
495 		int points = readInt();
496 		for (int i = 0; i < points; i++) {
497 			int position = readInt();
readBeatEffects(TGBeat beat,TGNoteEffect effect)498 			int value = readInt();
499 			readByte();
500 
501 			int pointPosition = Math.round(position * TGEffectTremoloBar.MAX_POSITION_LENGTH / GP_BEND_POSITION);
502 			int pointValue = Math.round(value / (GP_BEND_SEMITONE * 2f));
503 			tremoloBar.addPoint(pointPosition,pointValue);
504 		}
505 		if(!tremoloBar.getPoints().isEmpty()){
506 			effect.setTremoloBar(tremoloBar);
507 		}
508 	}
509 
510 	public void readTremoloPicking(TGNoteEffect effect) throws IOException{
511 		int value = readUnsignedByte();
512 		TGEffectTremoloPicking tp = getFactory().newEffectTremoloPicking();
513 		if(value == 1){
514 			tp.getDuration().setValue(TGDuration.EIGHTH);
515 			effect.setTremoloPicking(tp);
516 		}else if(value == 2){
517 			tp.getDuration().setValue(TGDuration.SIXTEENTH);
518 			effect.setTremoloPicking(tp);
519 		}else if(value == 3){
520 			tp.getDuration().setValue(TGDuration.THIRTY_SECOND);
521 			effect.setTremoloPicking(tp);
522 		}
523 	}
524 
525 	private void readNoteEffects(TGNoteEffect noteEffect) throws IOException {
526 		int flags1 = readUnsignedByte();
527 		int flags2 = readUnsignedByte();
528 		noteEffect.setHammer(((flags1 & 0x02) != 0));
529 		noteEffect.setVibrato(((flags2 & 0x40) != 0) || noteEffect.isVibrato());
530 		noteEffect.setPalmMute(((flags2 & 0x02) != 0));
531 		noteEffect.setStaccato(((flags2 & 0x01) != 0));
532 		if ((flags1 & 0x01) != 0) {
533 			readBend(noteEffect);
534 		}
535 		if ((flags1 & 0x10) != 0) {
536 			readGrace(noteEffect);
readMixChange(TGTempo tempo)537 		}
538 		if ((flags2 & 0x04) != 0) {
539 			readTremoloPicking(noteEffect);
540 		}
541 		if ((flags2 & 0x08) != 0) {
542 			noteEffect.setSlide(true);
543 			readByte();
544 		}
545 		if ((flags2 & 0x10) != 0) {
546 			TGEffectHarmonic harmonic = getFactory().newEffectHarmonic();
547 			int type = readByte();
548 			if(type == 1){
549 				harmonic.setType(TGEffectHarmonic.TYPE_NATURAL);
550 			}else if(type == 3){
551 				harmonic.setType(TGEffectHarmonic.TYPE_TAPPED);
552 			}else if(type == 4){
553 				harmonic.setType(TGEffectHarmonic.TYPE_PINCH);
554 			}else if(type == 5){
555 				harmonic.setType(TGEffectHarmonic.TYPE_SEMI);
556 			}else if(type == 15){
557 				harmonic.setType(TGEffectHarmonic.TYPE_ARTIFICIAL);
558 				harmonic.setData(2);
559 			}else if(type == 17){
560 				harmonic.setType(TGEffectHarmonic.TYPE_ARTIFICIAL);
561 				harmonic.setData(3);
562 			}else if(type == 22){
563 				harmonic.setType(TGEffectHarmonic.TYPE_ARTIFICIAL);
564 				harmonic.setData(0);
565 			}
566 			noteEffect.setHarmonic(harmonic);
567 		}
568 		if ((flags2 & 0x20) != 0) {
569 			byte fret = readByte();
toStrokeValue( int value )570 			byte period = readByte();
571 			TGEffectTrill trill = getFactory().newEffectTrill();
572 			trill.setFret(fret);
573 			if(period == 1){
574 				trill.getDuration().setValue(TGDuration.SIXTEENTH);
575 				noteEffect.setTrill(trill);
576 			}else if(period == 2){
577 				trill.getDuration().setValue(TGDuration.THIRTY_SECOND);
578 				noteEffect.setTrill(trill);
579 			}else if(period == 3){
580 				trill.getDuration().setValue(TGDuration.SIXTY_FOURTH);
581 				noteEffect.setTrill(trill);
582 			}
583 		}
584 	}
585 
586 	private void readBeatEffects(TGBeat beat,TGNoteEffect noteEffect) throws IOException {
587 		int flags1 = readUnsignedByte();
588 		int flags2 = readUnsignedByte();
toChannelShort(byte b)589 		noteEffect.setFadeIn(((flags1 & 0x10) != 0));
590 		noteEffect.setVibrato(((flags1  & 0x02) != 0));
591 		if ((flags1 & 0x20) != 0) {
592 			int effect = readUnsignedByte();
593 			noteEffect.setTapping(effect == 1);
getClef( TGTrack track )594 			noteEffect.setSlapping(effect == 2);
595 			noteEffect.setPopping(effect == 3);
596 		}
597 		if ((flags2 & 0x04) != 0) {
598 			readTremoloBar(noteEffect);
599 		}
600 		if ((flags1 & 0x40) != 0) {
601 			int strokeDown = readByte();
602 			int strokeUp = readByte();
603 			if( strokeDown > 0 ){
604 				beat.getStroke().setDirection( TGStroke.STROKE_DOWN );
605 				beat.getStroke().setValue( toStrokeValue(strokeDown) );
606 			}else if( strokeUp > 0 ){
607 				beat.getStroke().setDirection( TGStroke.STROKE_UP );
608 				beat.getStroke().setValue( toStrokeValue(strokeUp) );
609 			}
610 		}
611 		if ((flags2 & 0x02) != 0) {
612 			readByte();
613 		}
614 	}
615 
616 	private void readMixChange(TGTempo tempo) throws IOException {
617 		readByte();
618 		int volume = readByte();
619 		int pan = readByte();
620 		int chorus = readByte();
621 		int reverb = readByte();
622 		int phaser = readByte();
623 		int tremolo = readByte();
624 		int tempoValue = readInt();
625 		if(volume >= 0){
626 			readByte();
627 		}
628 		if(pan >= 0){
629 			readByte();
630 		}
631 		if(chorus >= 0){
632 			readByte();
633 		}
634 		if(reverb >= 0){
635 			readByte();
636 		}
637 		if(phaser >= 0){
638 			readByte();
639 		}
640 		if(tremolo >= 0){
641 			readByte();
642 		}
643 		if(tempoValue >= 0){
644 			tempo.setValue(tempoValue);
645 			readByte();
646 		}
647 		readByte();
648 	}
649 
650 	private int toStrokeValue( int value ){
651 		if( value == 1 || value == 2){
652 			return TGDuration.SIXTY_FOURTH;
653 		}
654 		if( value == 3){
655 			return TGDuration.THIRTY_SECOND;
656 		}
657 		if( value == 4){
658 			return TGDuration.SIXTEENTH;
659 		}
660 		if( value == 5){
661 			return TGDuration.EIGHTH;
662 		}
663 		if( value == 6){
664 			return TGDuration.QUARTER;
665 		}
666 		return TGDuration.SIXTY_FOURTH;
667 	}
668 
669 	private short toChannelShort(byte b){
670 		short value = (short)(( b * 8 ) - 1);
671 		return (short)Math.max(value,0);
672 	}
673 
674 	private int getClef( TGTrack track ){
675 		if( !track.isPercussionTrack() ){
676 			Iterator it = track.getStrings().iterator();
677 			while( it.hasNext() ){
678 				TGString string = (TGString) it.next();
679 				if( string.getValue() <= 34 ){
680 					return TGMeasure.CLEF_BASS;
681 				}
682 			}
683 		}
684 		return TGMeasure.CLEF_TREBLE;
685 	}
686 }
687