2
* Created on 09-ene-2006
4
* TODO To change the template for this generated file go to
5
* Window - Preferences - Java - Code Style - Code Templates
7
package org.herac.tuxguitar.io.gp;
9
import java.io.IOException;
10
import java.io.OutputStream;
11
import java.util.ArrayList;
12
import java.util.Iterator;
13
import java.util.List;
15
import org.herac.tuxguitar.gui.editors.tab.TablatureUtil;
16
import org.herac.tuxguitar.song.models.Duration;
17
import org.herac.tuxguitar.song.models.InstrumentString;
18
import org.herac.tuxguitar.song.models.Marker;
19
import org.herac.tuxguitar.song.models.Measure;
20
import org.herac.tuxguitar.song.models.MeasureHeader;
21
import org.herac.tuxguitar.song.models.Note;
22
import org.herac.tuxguitar.song.models.NoteEffect;
23
import org.herac.tuxguitar.song.models.RGBColor;
24
import org.herac.tuxguitar.song.models.Silence;
25
import org.herac.tuxguitar.song.models.Song;
26
import org.herac.tuxguitar.song.models.SongChannel;
27
import org.herac.tuxguitar.song.models.SongTrack;
28
import org.herac.tuxguitar.song.models.Tempo;
29
import org.herac.tuxguitar.song.models.TimeSignature;
30
import org.herac.tuxguitar.song.models.VelocityValues;
31
import org.herac.tuxguitar.song.models.effects.BendEffect;
32
import org.herac.tuxguitar.song.models.effects.GraceEffect;
33
import org.herac.tuxguitar.song.models.effects.HarmonicEffect;
34
import org.herac.tuxguitar.song.models.effects.TremoloBarEffect;
35
import org.herac.tuxguitar.song.models.effects.TremoloPickingEffect;
40
* TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code Templates
42
public class GP4OutputStream {
43
private static final String GP4_VERSION = "FICHIER GUITAR PRO v4.00";
44
private static final int GP_BEND_SEMITONE = 50;
45
private static final int GP_BEND_POSITION = 60;
47
private OutputStream outputStream;
49
public GP4OutputStream(OutputStream outputStream) {
50
this.outputStream = outputStream;
53
public void writeSong(Song song) throws IOException {
55
SongTrack firstTrack = null;
56
Measure firstMeasure = null;
57
int numberOfTracks = song.getTracks().size();
58
if (numberOfTracks > 0) {
59
firstTrack = (SongTrack) song.getTracks().get(0);
62
int numberOfMeasures = 0;
63
if (firstTrack != null) {
64
numberOfMeasures = firstTrack.getMeasures().size();
66
if (numberOfMeasures > 0) {
67
firstMeasure = (Measure) firstTrack.getMeasures().get(0);
71
writeStringByte(GP4_VERSION, 30);
73
writeStringIntegerPlusOne(song.getName());
75
writeStringIntegerPlusOne("");
77
writeStringIntegerPlusOne(song.getInterpret());
79
writeStringIntegerPlusOne(song.getAlbum());
81
writeStringIntegerPlusOne(song.getAuthor());
83
writeStringIntegerPlusOne("");
85
writeStringIntegerPlusOne("");
87
writeStringIntegerPlusOne("");
98
Tempo tempo = new Tempo(120);
99
if (firstMeasure != null) {
100
tempo = (Tempo) firstMeasure.getTempo().clone();
102
writeInt(tempo.getValue());
110
SongChannel[] channels = makeChannels(song);
111
for (int i = 0; i < channels.length; i++) {
113
writeInt(channels[i].getInstrument());
115
writeByte(toChannelByte(channels[i].getVolume()));
117
writeByte(toChannelByte(channels[i].getBalance()));
119
writeByte(toChannelByte(channels[i].getChorus()));
121
writeByte(toChannelByte(channels[i].getReverb()));
123
writeByte(toChannelByte(channels[i].getPhaser()));
125
writeByte(toChannelByte(channels[i].getTremolo()));
128
this.outputStream.write(b);
132
writeInt(numberOfMeasures);
134
writeInt(numberOfTracks);
136
createMeasures(song.getMeasureHeaders());
138
createTracks(song.getTracks());
140
for (int i = 0; i < numberOfMeasures; i++) {
141
for (int j = 0; j < numberOfTracks; j++) {
142
SongTrack track = (SongTrack) song.getTracks().get(j);
143
Measure measure = (Measure) track.getMeasures().get(i);
145
addMeasureComponents(track.getStrings().size(), measure, tempo);
149
this.outputStream.flush();
150
this.outputStream.close();
151
} catch (Exception e) {
156
private void writeLyrics(Song song) throws IOException{
157
SongTrack lyricTrack = null;
158
Iterator it = song.getTracks().iterator();
160
SongTrack track = (SongTrack)it.next();
161
if(!track.getLyrics().isEmpty()){
167
writeInt( (lyricTrack == null)?0:lyricTrack.getNumber() );
168
writeInt((lyricTrack == null)?0:lyricTrack.getLyrics().getFrom());
169
writeStringInteger((lyricTrack == null)?"":lyricTrack.getLyrics().getLyrics());
170
for (int i = 0; i < 4; i++) {
172
writeStringInteger("");
176
private void createMeasures(List measures) throws IOException {
177
TimeSignature timeSignature = new TimeSignature(0, new Duration(0));
178
if (measures.size() > 0) {
179
for (int i = 0; i < measures.size(); i++) {
180
MeasureHeader measure = (MeasureHeader) measures.get(i);
181
createMeasureHeader(measure, timeSignature);
183
timeSignature.setNumerator(measure.getTimeSignature().getNumerator());
184
timeSignature.getDenominator().setValue(measure.getTimeSignature().getDenominator().getValue());
189
private void createMeasureHeader(MeasureHeader measure, TimeSignature currTimeSignature) throws IOException {
190
//-----------------------
193
if (measure.getTimeSignature().getNumerator() != currTimeSignature.getNumerator()) {
197
if (measure.getTimeSignature().getDenominator().getValue() != currTimeSignature.getDenominator().getValue()) {
201
if (measure.isRepeatStart()) {
205
//numberOfRepetitions
206
if (measure.getNumberOfRepetitions() > 0) {
211
if (measure.hasMarker()) {
215
writeUnsignedByte(header);
216
//---------------------------------------------
217
if ((header & 0x01) != 0) {
219
writeByte((byte) measure.getTimeSignature().getNumerator());
222
if ((header & 0x02) != 0) {
224
writeByte((byte) measure.getTimeSignature().getDenominator().getValue());
227
if ((header & 0x08) != 0) {
228
//numberOfRepetitions
229
writeByte((byte) measure.getNumberOfRepetitions());
232
if ((header & 0x20) != 0) {
234
writeMarker(measure.getMarker());
238
private void createTracks(List tracks) throws IOException {
239
for (int i = 0; i < tracks.size(); i++) {
240
SongTrack track = (SongTrack) tracks.get(i);
245
private void createTrack(SongTrack track) throws IOException {
246
//---------------------------------------------
249
//numberOfRepetitions
250
if (track.isPercussionTrack()) {
254
writeUnsignedByte(header);
255
//---------------------------------------------
258
writeStringByte(track.getName(), 40);
261
writeInt(track.getStrings().size());
263
for (int i = 0; i < 7; i++) {
265
if (track.getStrings().size() > i) {
266
InstrumentString string = (InstrumentString) track.getStrings().get(i);
267
value = string.getValue();
276
writeInt(track.getChannel().getChannel() + 1);
279
writeInt(track.getChannel().getEffectChannel() + 1);
285
writeInt(Math.min(Math.max(track.getOffset(),0),12));
288
writeColor(track.getColor());
292
private void addMeasureComponents(int strings, Measure measure, Tempo tempo) throws IOException, GPFormatException {
293
List beats = getBeats(measure);
295
writeInt(beats.size());
297
for (int i = 0; i < beats.size(); i++) {
298
MeasureBeat beat = (MeasureBeat) beats.get(i);
299
addNotes(beat, strings, measure, tempo);
304
private void addNotes(MeasureBeat beat, int strings, Measure measure, Tempo songTempo) throws IOException, GPFormatException {
305
Duration duration = beat.getDuration();
306
//---------------------------------------------
309
if (duration.isDotted()) {
312
if (beat.getEffects().isTremoloBar() ||
313
beat.getEffects().isTapping() ||
314
beat.getEffects().isSlapping() ||
315
beat.getEffects().isPopping() ||
316
beat.getEffects().isFadeIn()) {
321
if (!duration.getTupleto().isEqual(Duration.NO_TUPLETO)) {
324
if (measure.getTempo().getValue() != songTempo.getValue()) {
327
NoteEffect effect = null;
328
if (beat.isSilence()) {
331
writeUnsignedByte(header);
332
//---------------------------------------------
334
if ((header & 0x40) != 0) {
335
writeUnsignedByte(0x02);
339
writeByte(parseDuration(duration));
341
if ((header & 0x20) != 0) {
343
writeInt(duration.getTupleto().getEnters());
346
if ((header & 0x08) != 0) {
347
writeBeatEffects(beat.getEffects());
350
if ((header & 0x10) != 0) {
351
writeMixChange(measure.getTempo());
354
int stringHeader = 0;
355
if (!beat.isSilence()) {
357
for (int i = 0; i < beat.getNotes().size(); i++) {
358
Note playedNote = (Note) beat.getNotes().get(i);
359
int string = (7 - playedNote.getString());
360
stringHeader |= (1 << string);
364
writeUnsignedByte(stringHeader);
366
for (int i = 0; i < beat.getNotes().size(); i++) {
367
Note playedNote = (Note) beat.getNotes().get(i);
368
writeNote(playedNote);
373
private void writeNote(Note note) throws IOException {
380
if(note.getEffect().isGhostNote()){
384
if(note.getEffect().isAccentuatedNote()){
389
if (note.getEffect().isVibrato() ||
390
note.getEffect().isBend() ||
391
note.getEffect().isGrace() ||
392
note.getEffect().isSlide() ||
393
note.getEffect().isHammer() ||
394
note.getEffect().isPalmMute() ||
395
note.getEffect().isStaccato() ||
396
note.getEffect().isTapping() ||
397
note.getEffect().isSlapping() ||
398
note.getEffect().isPopping() ||
399
note.getEffect().isHarmonic() ||
400
note.getEffect().isTremoloPicking() ||
401
note.getEffect().isTrill()) {
405
writeUnsignedByte(header);
407
if ((header & 0x20) != 0) {
408
int typeHeader = 0x01;
409
if (note.isTiedNote()) {
411
}else if(note.getEffect().isDeadNote()){
414
writeUnsignedByte(typeHeader);
417
if ((header & 0x10) != 0) {
418
writeByte((byte)(((note.getVelocity() - VelocityValues.MIN_VELOCITY) / VelocityValues.VELOCITY_INCREMENT) + 1));
421
if ((header & 0x20) != 0) {
422
writeByte((byte) note.getValue());
425
if ((header & 0x08) != 0) {
426
writeNoteEffects(note.getEffect());
431
private byte parseDuration(Duration duration) {
433
switch (duration.getValue()) {
440
case Duration.QUARTER:
443
case Duration.EIGHTH:
446
case Duration.SIXTEENTH:
449
case Duration.THIRTY_SECOND:
452
case Duration.SIXTY_FOURTH:
459
private void writeBeatEffects(NoteEffect effect) throws IOException{
460
int header[] = new int[2];
464
if(effect.isFadeIn()){
467
if(effect.isTapping() || effect.isSlapping() || effect.isPopping()){
470
if(effect.isTremoloBar()){
474
writeUnsignedByte(header[0]);
475
writeUnsignedByte(header[1]);
477
if ((header[0] & 0x20) != 0) {
478
if(effect.isTapping()){
479
writeUnsignedByte(1);
480
}else if(effect.isSlapping()){
481
writeUnsignedByte(2);
482
}else if(effect.isPopping()){
483
writeUnsignedByte(3);
487
if ((header[1] & 0x04) != 0) {
488
writeTremoloBar(effect.getTremoloBar());
494
private void writeNoteEffects(NoteEffect effect) throws IOException {
497
if (effect.isBend()) {
500
if (effect.isHammer()) {
503
if (effect.isGrace()) {
506
if(effect.isStaccato()){
509
if(effect.isPalmMute()){
512
if(effect.isTremoloPicking()){
515
if (effect.isSlide()) {
518
if (effect.isVibrato()) {
521
if(effect.isHarmonic()){
524
if(effect.isTrill()){
529
writeUnsignedByte(header1);
530
writeUnsignedByte(header2);
532
if ((header1 & 0x01) != 0) {
533
writeBend(effect.getBend());
535
if ((header1 & 0x10) != 0) {
536
writeGrace(effect.getGrace());
538
if ((header2 & 0x04) != 0) {
539
writeTremoloPicking(effect.getTremoloPicking());
541
if ((header2 & 0x08) != 0) {
545
if ((header2 & 0x10) != 0) {
546
if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_NATURAL){
548
}else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_TAPPED){
550
}else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_PINCH){
552
}else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_SEMI){
554
}else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_ARTIFICIAL){
560
if ((header2 & 0x20) != 0) {
561
writeByte((byte)effect.getTrill().getFret());
562
if(effect.getTrill().getDuration().getValue() == Duration.SIXTEENTH){
564
}else if(effect.getTrill().getDuration().getValue() == Duration.THIRTY_SECOND){
566
}else if(effect.getTrill().getDuration().getValue() == Duration.SIXTY_FOURTH){
572
private void writeBend(BendEffect bend) throws IOException {
578
int numPoints = bend.getPoints().size();
581
for (int i = 0; i < numPoints; i++) {
582
BendEffect.BendPoint point = (BendEffect.BendPoint) bend.getPoints().get(i);
584
int bendPosition = (int) (point.getPosition() * GP_BEND_POSITION / BendEffect.MAX_POSITION_LENGTH);
585
int bendValue = (point.getValue() * GP_BEND_SEMITONE / BendEffect.SEMITONE_LENGTH);
588
writeInt(bendPosition);
597
private void writeTremoloBar(TremoloBarEffect effect) throws IOException {
603
int numPoints = effect.getPoints().size();
606
for (int i = 0; i < numPoints; i++) {
607
TremoloBarEffect.TremoloBarPoint point = (TremoloBarEffect.TremoloBarPoint) effect.getPoints().get(i);
609
int position = (int) (point.getPosition() * GP_BEND_POSITION / TremoloBarEffect.MAX_POSITION_LENGTH);
610
int value = (point.getValue() * GP_BEND_SEMITONE / TremoloBarEffect.SEMITONE_LENGTH);
622
public void writeTremoloPicking(TremoloPickingEffect effect) throws IOException{
623
if(effect.getDuration().getValue() == Duration.EIGHTH){
624
writeUnsignedByte(1);
625
}else if(effect.getDuration().getValue() == Duration.SIXTEENTH){
626
writeUnsignedByte(2);
627
}else if(effect.getDuration().getValue() == Duration.THIRTY_SECOND){
628
writeUnsignedByte(3);
632
private void writeGrace(GraceEffect grace) throws IOException {
635
writeUnsignedByte(255);
637
writeUnsignedByte(grace.getFret());
641
writeUnsignedByte(((grace.getDynamic() - VelocityValues.MIN_VELOCITY) / VelocityValues.VELOCITY_INCREMENT) + 1);
644
if(grace.getTransition() == GraceEffect.TRANSITION_NONE){
645
writeUnsignedByte(0);
647
else if(grace.getTransition() == GraceEffect.TRANSITION_SLIDE){
648
writeUnsignedByte(1);
650
else if(grace.getTransition() == GraceEffect.TRANSITION_BEND){
651
writeUnsignedByte(2);
653
else if(grace.getTransition() == GraceEffect.TRANSITION_HAMMER){
654
writeUnsignedByte(3);
658
writeUnsignedByte(grace.getDuration());
661
private void writeMixChange(Tempo tempo) throws IOException {
662
for (int i = 0; i < 7; i++) {
663
writeByte((byte) -1);
666
writeInt(tempo.getValue());
670
//apply to all tracks
671
writeUnsignedByte(1);
675
private void writeMarker(Marker marker) throws IOException {
676
writeStringIntegerPlusOne(marker.getTitle());
677
writeColor(marker.getColor());
680
private List getBeats(Measure measure) {
681
boolean hasSilences = (!measure.getSilences().isEmpty());
683
List beats = new ArrayList();
684
MeasureBeat beat = null;
686
//-----notas---------------------------------------
687
it = measure.getNotes().iterator();
688
while (it.hasNext()) {
689
Note note = (Note) it.next();
691
beat = new MeasureBeat(note.getDuration(), note.getStart());
694
//verifico si tengo que autocompletar silencios
696
if (beats.isEmpty() && beat.getStart() != measure.getStart()) {
697
long silenceLength = (beat.getStart() - measure.getStart());
698
if (silenceLength > 10) {
699
createSilenceBeats(beats, (beat.getStart() + beat.getDuration().getTime()), silenceLength);
704
//verifico si termino el beat
705
if (note.getStart() != beat.getStart()) {
708
//verifico si tengo que autocompletar silencios
710
long silenceLength = (note.getStart() - (beat.getStart() + beat.getDuration().getTime()));
711
if (silenceLength > 10) {
712
createSilenceBeats(beats, (beat.getStart() + beat.getDuration().getTime()), silenceLength);
715
//creo el siguiente beat
716
beat = new MeasureBeat(note.getDuration(), note.getStart());
724
//agrego los silencios
726
it = measure.getSilences().iterator();
727
while (it.hasNext()) {
728
Silence silence = (Silence) it.next();
729
beats.add(new MeasureBeat(silence.getDuration(), silence.getStart()));
734
for (int i = 0; i < beats.size(); i++) {
735
MeasureBeat minBeat = null;
736
for (int beatIdx = i; beatIdx < beats.size(); beatIdx++) {
737
MeasureBeat currBeat = (MeasureBeat) beats.get(beatIdx);
738
if (minBeat == null || currBeat.getStart() < minBeat.getStart()) {
742
beats.remove(minBeat);
743
beats.add(i, minBeat);
749
private void createSilenceBeats(List beats, long start, long length) {
750
List durations = TablatureUtil.createDurations(length);
751
Iterator it = durations.iterator();
752
while (it.hasNext()) {
753
Duration duration = (Duration) it.next();
754
beats.add(new MeasureBeat(duration, start));
755
start += duration.getTime();
759
private void orderNotes(Measure measure) {
760
for (int i = 0; i < measure.getNotes().size(); i++) {
762
for (int noteIdx = i; noteIdx < measure.getNotes().size(); noteIdx++) {
763
Note note = (Note) measure.getNotes().get(noteIdx);
764
if (minNote == null || (note.getStart() < minNote.getStart())
765
|| (note.getStart() == minNote.getStart() && (note.getString() < minNote.getString()))) {
769
measure.getNotes().remove(minNote);
770
measure.getNotes().add(i, minNote);
774
private SongChannel[] makeChannels(Song song) {
775
SongChannel[] channels = new SongChannel[64];
776
for (int i = 0; i < channels.length; i++) {
777
channels[i] = new SongChannel((short)i,(short)i, (short) 24, (short) 13, (short) 8, (short) 0, (short) 0, (short) 0, (short) 0,false,false);
780
Iterator it = song.getTracks().iterator();
781
while (it.hasNext()) {
782
SongTrack track = (SongTrack) it.next();
783
channels[track.getChannel().getChannel()].setInstrument(track.getChannel().getInstrument());
784
channels[track.getChannel().getChannel()].setVolume(track.getChannel().getVolume());
785
channels[track.getChannel().getChannel()].setBalance(track.getChannel().getBalance());
786
channels[track.getChannel().getEffectChannel()].setInstrument(track.getChannel().getInstrument());
787
channels[track.getChannel().getEffectChannel()].setVolume(track.getChannel().getVolume());
788
channels[track.getChannel().getEffectChannel()].setBalance(track.getChannel().getBalance());
794
private void writeColor(RGBColor color) throws IOException {
796
writeUnsignedByte(color.getR());
798
writeUnsignedByte(color.getG());
800
writeUnsignedByte(color.getB());
802
this.outputStream.write(0);
805
//-----------------------------------------------------------------------------------
806
private void writeBoolean(boolean v) throws IOException {
807
this.outputStream.write(v ? 1 : 0);
810
private void writeByte(byte v) throws IOException {
811
this.outputStream.write(v);
814
private void writeUnsignedByte(int v) throws IOException {
815
this.outputStream.write(v);
818
private void writeStringByte(String v, int expectedLength) throws IOException {
819
byte[] bytes = v.getBytes();
821
this.writeUnsignedByte(bytes.length);
823
if (expectedLength != 0) {
824
byte[] tempBytes = new byte[expectedLength];
825
for (int i = 0; i < bytes.length; i++) {
826
tempBytes[i] = bytes[i];
830
this.outputStream.write(bytes);
833
private void writeStringIntegerPlusOne(String v) throws IOException {
834
byte[] b = v.getBytes();
836
this.writeInt(b.length + 1);
837
this.outputStream.write(b.length);
838
this.outputStream.write(b);
841
private void writeStringInteger(String v) throws IOException {
842
byte[] bytes = v.getBytes();
843
this.writeInt(bytes.length);
844
this.outputStream.write(bytes);
847
private void writeInt(int v) throws IOException {
848
byte[] bytes = new byte[4];
849
bytes[0] = (byte) (v & 0x00FF);
850
bytes[1] = (byte) ((v >> 8) & 0x000000FF);
851
bytes[2] = (byte) ((v >> 16) & 0x000000FF);
852
bytes[3] = (byte) ((v >> 24) & 0x000000FF);
854
this.outputStream.write(bytes);
857
//-----------------------------------------------------------------------------------
859
private class MeasureBeat {
860
private Duration duration;
862
private boolean silence;
864
private NoteEffect beatEffects;
866
public MeasureBeat(Duration duration, long start) {
867
this.duration = duration;
869
this.notes = new ArrayList();
871
this.beatEffects = new NoteEffect();
874
public long getStart() {
878
public void setStart(long start) {
882
public Duration getDuration() {
886
public void addNote(Note note) {
887
this.getNotes().add(note);
888
this.checkEffects(note.getEffect());
889
this.silence = false;
892
public List getNotes() {
896
private boolean isSilence() {
900
private NoteEffect getEffects(){
901
return this.beatEffects;
904
private void checkEffects(NoteEffect effect){
905
if(effect.isFadeIn()){
906
this.beatEffects.setFadeIn(true);
908
if(effect.isTremoloBar()){
909
this.beatEffects.setTremoloBar((TremoloBarEffect)effect.getTremoloBar().clone());
911
if(effect.isTapping()){
912
this.beatEffects.setTapping(true);
914
if(effect.isSlapping()){
915
this.beatEffects.setSlapping(true);
917
if(effect.isPopping()){
918
this.beatEffects.setPopping(true);
925
private byte toChannelByte(short s){
926
s = (short)((s * (short)16) / (short)127);
b'\\ No newline at end of file'