~ubuntu-branches/ubuntu/oneiric/tuxguitar/oneiric

« back to all changes in this revision

Viewing changes to src/org/herac/tuxguitar/io/gp/GP4OutputStream.java

  • Committer: Bazaar Package Importer
  • Author(s): Philippe Coval
  • Date: 2008-06-19 00:30:30 UTC
  • mto: (5.1.2 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20080619003030-h719szrhsngou7c6
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Created on 09-ene-2006
3
 
 *
4
 
 * TODO To change the template for this generated file go to
5
 
 * Window - Preferences - Java - Code Style - Code Templates
6
 
 */
7
 
package org.herac.tuxguitar.io.gp;
8
 
 
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;
14
 
 
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;
36
 
 
37
 
/**
38
 
 * @author julian
39
 
 * 
40
 
 * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code Templates
41
 
 */
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;
46
 
    
47
 
    private OutputStream outputStream;
48
 
 
49
 
    public GP4OutputStream(OutputStream outputStream) {
50
 
        this.outputStream = outputStream;
51
 
    }
52
 
 
53
 
    public void writeSong(Song song) throws IOException {
54
 
        try {
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);
60
 
            }
61
 
 
62
 
            int numberOfMeasures = 0;
63
 
            if (firstTrack != null) {
64
 
                numberOfMeasures = firstTrack.getMeasures().size();
65
 
            }
66
 
            if (numberOfMeasures > 0) {
67
 
                firstMeasure = (Measure) firstTrack.getMeasures().get(0);
68
 
            }
69
 
 
70
 
            //version
71
 
            writeStringByte(GP4_VERSION, 30);
72
 
            //title
73
 
            writeStringIntegerPlusOne(song.getName());
74
 
            //subtitle
75
 
            writeStringIntegerPlusOne("");
76
 
            //interpret
77
 
            writeStringIntegerPlusOne(song.getInterpret());
78
 
            //album
79
 
            writeStringIntegerPlusOne(song.getAlbum());
80
 
            //songAuthor
81
 
            writeStringIntegerPlusOne(song.getAuthor());
82
 
            //copyright
83
 
            writeStringIntegerPlusOne("");
84
 
            //pieceAuthor
85
 
            writeStringIntegerPlusOne("");
86
 
            //instructions
87
 
            writeStringIntegerPlusOne("");
88
 
 
89
 
            //notes
90
 
            writeInt(0);
91
 
            //tripletFeel
92
 
            writeBoolean(false);
93
 
            
94
 
            //lyrics
95
 
            writeLyrics(song);
96
 
 
97
 
            //tempo
98
 
            Tempo tempo = new Tempo(120);
99
 
            if (firstMeasure != null) {
100
 
                tempo = (Tempo) firstMeasure.getTempo().clone();
101
 
            }
102
 
            writeInt(tempo.getValue());
103
 
            //key
104
 
            writeByte((byte)0);
105
 
 
106
 
            //octave
107
 
            writeInt(0);
108
 
            
109
 
            
110
 
            SongChannel[] channels = makeChannels(song);
111
 
            for (int i = 0; i < channels.length; i++) {
112
 
                //instrument
113
 
                writeInt(channels[i].getInstrument());
114
 
                //volume
115
 
                writeByte(toChannelByte(channels[i].getVolume()));
116
 
                //balance
117
 
                writeByte(toChannelByte(channels[i].getBalance()));
118
 
                //chorus
119
 
                writeByte(toChannelByte(channels[i].getChorus()));
120
 
                //reverb
121
 
                writeByte(toChannelByte(channels[i].getReverb()));
122
 
                //phaser
123
 
                writeByte(toChannelByte(channels[i].getPhaser()));
124
 
                //tremolo
125
 
                writeByte(toChannelByte(channels[i].getTremolo()));
126
 
 
127
 
                byte[] b = { 0, 0 };
128
 
                this.outputStream.write(b);
129
 
            }
130
 
 
131
 
            //numberOfMeasures
132
 
            writeInt(numberOfMeasures);
133
 
            //numberOfTracks
134
 
            writeInt(numberOfTracks);
135
 
 
136
 
            createMeasures(song.getMeasureHeaders());
137
 
            
138
 
            createTracks(song.getTracks());
139
 
 
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);
144
 
 
145
 
                    addMeasureComponents(track.getStrings().size(), measure, tempo);
146
 
                }
147
 
            }
148
 
 
149
 
            this.outputStream.flush();
150
 
            this.outputStream.close();
151
 
        } catch (Exception e) {
152
 
            e.printStackTrace();
153
 
        }
154
 
    }
155
 
    
156
 
    private void writeLyrics(Song song) throws IOException{
157
 
        SongTrack lyricTrack = null;
158
 
        Iterator it = song.getTracks().iterator();
159
 
        while(it.hasNext()){
160
 
                SongTrack track = (SongTrack)it.next();
161
 
                if(!track.getLyrics().isEmpty()){
162
 
                        lyricTrack = track;
163
 
                        break;
164
 
                }
165
 
        }
166
 
        
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++) {
171
 
            writeInt(0);
172
 
            writeStringInteger("");
173
 
        }
174
 
    }
175
 
 
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);
182
 
 
183
 
                timeSignature.setNumerator(measure.getTimeSignature().getNumerator());
184
 
                timeSignature.getDenominator().setValue(measure.getTimeSignature().getDenominator().getValue());
185
 
            }
186
 
        }
187
 
    }
188
 
 
189
 
    private void createMeasureHeader(MeasureHeader measure, TimeSignature currTimeSignature) throws IOException {
190
 
        //-----------------------
191
 
        int header = 0;
192
 
        //numerator
193
 
        if (measure.getTimeSignature().getNumerator() != currTimeSignature.getNumerator()) {
194
 
            header |= 0x01;
195
 
        }
196
 
        //denominator
197
 
        if (measure.getTimeSignature().getDenominator().getValue() != currTimeSignature.getDenominator().getValue()) {
198
 
            header |= 0x02;
199
 
        }
200
 
        //repeatStart
201
 
        if (measure.isRepeatStart()) {
202
 
            header |= 0x04;
203
 
        }
204
 
 
205
 
        //numberOfRepetitions
206
 
        if (measure.getNumberOfRepetitions() > 0) {
207
 
            header |= 0x08;
208
 
        }
209
 
        
210
 
        //marker
211
 
        if (measure.hasMarker()) {
212
 
            header |= 0x20;
213
 
        }        
214
 
        
215
 
        writeUnsignedByte(header);
216
 
        //---------------------------------------------
217
 
        if ((header & 0x01) != 0) {
218
 
            //numerator
219
 
            writeByte((byte) measure.getTimeSignature().getNumerator());
220
 
        }
221
 
 
222
 
        if ((header & 0x02) != 0) {
223
 
            //denominator
224
 
            writeByte((byte) measure.getTimeSignature().getDenominator().getValue());
225
 
        }
226
 
 
227
 
        if ((header & 0x08) != 0) {
228
 
            //numberOfRepetitions
229
 
            writeByte((byte) measure.getNumberOfRepetitions());
230
 
        }
231
 
 
232
 
        if ((header & 0x20) != 0) {
233
 
                //marker
234
 
                writeMarker(measure.getMarker());
235
 
        }        
236
 
    }
237
 
 
238
 
    private void createTracks(List tracks) throws IOException {
239
 
        for (int i = 0; i < tracks.size(); i++) {
240
 
            SongTrack track = (SongTrack) tracks.get(i);
241
 
            createTrack(track);
242
 
        }
243
 
    }
244
 
 
245
 
    private void createTrack(SongTrack track) throws IOException {
246
 
        //---------------------------------------------
247
 
        int header = 0;
248
 
 
249
 
        //numberOfRepetitions
250
 
        if (track.isPercussionTrack()) {
251
 
            header |= 0x01;
252
 
        }
253
 
 
254
 
        writeUnsignedByte(header);
255
 
        //---------------------------------------------
256
 
 
257
 
        //name
258
 
        writeStringByte(track.getName(), 40);
259
 
 
260
 
        //numberOfStrings
261
 
        writeInt(track.getStrings().size());
262
 
 
263
 
        for (int i = 0; i < 7; i++) {
264
 
            int value = 0;
265
 
            if (track.getStrings().size() > i) {
266
 
                InstrumentString string = (InstrumentString) track.getStrings().get(i);
267
 
                value = string.getValue();
268
 
            }
269
 
            writeInt(value);
270
 
        }
271
 
 
272
 
        //port
273
 
        writeInt(1);
274
 
 
275
 
        //channel
276
 
        writeInt(track.getChannel().getChannel() + 1);
277
 
 
278
 
        //effects
279
 
        writeInt(track.getChannel().getEffectChannel() + 1);
280
 
 
281
 
        //numberOfFrets
282
 
        writeInt(24);
283
 
 
284
 
        //capo
285
 
        writeInt(Math.min(Math.max(track.getOffset(),0),12));
286
 
 
287
 
        //color
288
 
        writeColor(track.getColor());
289
 
 
290
 
    }
291
 
 
292
 
    private void addMeasureComponents(int strings, Measure measure, Tempo tempo) throws IOException, GPFormatException {
293
 
        List beats = getBeats(measure);
294
 
        //numberOfBeats
295
 
        writeInt(beats.size());
296
 
 
297
 
        for (int i = 0; i < beats.size(); i++) {
298
 
            MeasureBeat beat = (MeasureBeat) beats.get(i);
299
 
            addNotes(beat, strings, measure, tempo);
300
 
        }
301
 
 
302
 
    }
303
 
 
304
 
    private void addNotes(MeasureBeat beat, int strings, Measure measure, Tempo songTempo) throws IOException, GPFormatException {
305
 
        Duration duration = beat.getDuration();
306
 
        //---------------------------------------------
307
 
        int header = 0;
308
 
 
309
 
        if (duration.isDotted()) {
310
 
            header |= 0x01;
311
 
        }
312
 
        if (beat.getEffects().isTremoloBar() || 
313
 
                beat.getEffects().isTapping() || 
314
 
                beat.getEffects().isSlapping() || 
315
 
                beat.getEffects().isPopping() ||
316
 
            beat.getEffects().isFadeIn()) {
317
 
            header |= 0x08;
318
 
        }
319
 
                
320
 
                
321
 
        if (!duration.getTupleto().isEqual(Duration.NO_TUPLETO)) {
322
 
            header |= 0x20;
323
 
        }
324
 
        if (measure.getTempo().getValue() != songTempo.getValue()) {
325
 
            header |= 0x10;
326
 
        }
327
 
        NoteEffect effect = null;
328
 
        if (beat.isSilence()) {
329
 
            header |= 0x40;
330
 
        }
331
 
        writeUnsignedByte(header);
332
 
        //---------------------------------------------
333
 
 
334
 
        if ((header & 0x40) != 0) {
335
 
            writeUnsignedByte(0x02);
336
 
        }
337
 
 
338
 
        //duration value
339
 
        writeByte(parseDuration(duration));
340
 
 
341
 
        if ((header & 0x20) != 0) {
342
 
            //tupleto
343
 
            writeInt(duration.getTupleto().getEnters());
344
 
        }
345
 
 
346
 
        if ((header & 0x08) != 0) {
347
 
                writeBeatEffects(beat.getEffects());
348
 
        }
349
 
        
350
 
        if ((header & 0x10) != 0) {
351
 
            writeMixChange(measure.getTempo());
352
 
        }
353
 
 
354
 
        int stringHeader = 0;
355
 
        if (!beat.isSilence()) {
356
 
 
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);
361
 
            }
362
 
        }
363
 
 
364
 
        writeUnsignedByte(stringHeader);
365
 
 
366
 
        for (int i = 0; i < beat.getNotes().size(); i++) {
367
 
            Note playedNote = (Note) beat.getNotes().get(i);
368
 
            writeNote(playedNote);
369
 
        }
370
 
 
371
 
    }
372
 
 
373
 
    private void writeNote(Note note) throws IOException {
374
 
        int header = 0x20;
375
 
        
376
 
        //Velocity
377
 
        header |= 0x10;        
378
 
        
379
 
        //ghost note
380
 
        if(note.getEffect().isGhostNote()){
381
 
                header |= 0x04;
382
 
        }
383
 
        //accentuated
384
 
        if(note.getEffect().isAccentuatedNote()){
385
 
                header |= 0x40;
386
 
        }
387
 
        
388
 
        //effects 
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()) {
402
 
            header |= 0x08;
403
 
        }
404
 
        
405
 
        writeUnsignedByte(header);
406
 
 
407
 
        if ((header & 0x20) != 0) {
408
 
            int typeHeader = 0x01;
409
 
            if (note.isTiedNote()) {
410
 
                typeHeader = 0x02;
411
 
            }else if(note.getEffect().isDeadNote()){
412
 
                typeHeader = 0x03;
413
 
            }
414
 
            writeUnsignedByte(typeHeader);
415
 
        }
416
 
 
417
 
        if ((header & 0x10) != 0) {
418
 
                writeByte((byte)(((note.getVelocity() - VelocityValues.MIN_VELOCITY) / VelocityValues.VELOCITY_INCREMENT) + 1));            
419
 
        }          
420
 
        
421
 
        if ((header & 0x20) != 0) {
422
 
            writeByte((byte) note.getValue());
423
 
        }
424
 
 
425
 
        if ((header & 0x08) != 0) {
426
 
            writeNoteEffects(note.getEffect());
427
 
        }
428
 
 
429
 
    }
430
 
 
431
 
    private byte parseDuration(Duration duration) {
432
 
        byte value = 0;
433
 
        switch (duration.getValue()) {
434
 
        case Duration.WHOLE:
435
 
            value = -2;
436
 
            break;
437
 
        case Duration.HALF:
438
 
            value = -1;
439
 
            break;
440
 
        case Duration.QUARTER:
441
 
            value = 0;
442
 
            break;
443
 
        case Duration.EIGHTH:
444
 
            value = 1;
445
 
            break;
446
 
        case Duration.SIXTEENTH:
447
 
            value = 2;
448
 
            break;
449
 
        case Duration.THIRTY_SECOND:
450
 
            value = 3;
451
 
            break;
452
 
        case Duration.SIXTY_FOURTH:
453
 
            value = 4;
454
 
            break;
455
 
        }
456
 
        return value;
457
 
    }
458
 
 
459
 
    private void writeBeatEffects(NoteEffect effect) throws IOException{                        
460
 
        int header[] = new int[2];
461
 
        header[0] = 0;
462
 
        header[1] = 0;
463
 
        
464
 
        if(effect.isFadeIn()){
465
 
                header[0] |= 0x10;
466
 
        }        
467
 
        if(effect.isTapping() || effect.isSlapping() || effect.isPopping()){
468
 
                header[0] |= 0x20;
469
 
        }
470
 
        if(effect.isTremoloBar()){
471
 
                header[1] |= 0x04;
472
 
        }        
473
 
 
474
 
        writeUnsignedByte(header[0]);
475
 
        writeUnsignedByte(header[1]);
476
 
                
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);
484
 
                }                                           
485
 
        }
486
 
 
487
 
        if ((header[1] & 0x04) != 0) {          
488
 
            writeTremoloBar(effect.getTremoloBar());
489
 
        }
490
 
 
491
 
        
492
 
    }
493
 
    
494
 
    private void writeNoteEffects(NoteEffect effect) throws IOException {
495
 
        int header1 = 0;
496
 
        int header2 = 0;
497
 
        if (effect.isBend()) {
498
 
            header1 |= 0x01;
499
 
        }
500
 
        if (effect.isHammer()) {
501
 
            header1 |= 0x02;
502
 
        }
503
 
        if (effect.isGrace()) {
504
 
            header1 |= 0x10;
505
 
        }          
506
 
        if(effect.isStaccato()){
507
 
                header2 |= 0x01;
508
 
        }                
509
 
        if(effect.isPalmMute()){
510
 
                header2 |= 0x02;
511
 
        }
512
 
        if(effect.isTremoloPicking()){
513
 
                header2 |= 0x04;
514
 
        }        
515
 
        if (effect.isSlide()) {
516
 
            header2 |= 0x08;            
517
 
        }
518
 
        if (effect.isVibrato()) {
519
 
            header2 |= 0x40;
520
 
        }       
521
 
        if(effect.isHarmonic()){
522
 
                header2 |= 0x10;
523
 
        }
524
 
        if(effect.isTrill()){
525
 
                header2 |= 0x20;
526
 
        }        
527
 
 
528
 
 
529
 
        writeUnsignedByte(header1);
530
 
        writeUnsignedByte(header2);
531
 
        
532
 
        if ((header1 & 0x01) != 0) {
533
 
            writeBend(effect.getBend());
534
 
        }
535
 
        if ((header1 & 0x10) != 0) {
536
 
            writeGrace(effect.getGrace());
537
 
        }  
538
 
        if ((header2 & 0x04) != 0) {
539
 
                writeTremoloPicking(effect.getTremoloPicking());
540
 
        }
541
 
        if ((header2 & 0x08) != 0) {
542
 
            writeByte((byte)1);
543
 
        }
544
 
        
545
 
        if ((header2 & 0x10) != 0) {   
546
 
                if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_NATURAL){
547
 
                        writeByte((byte)1);
548
 
                }else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_TAPPED){
549
 
                        writeByte((byte)3);
550
 
                }else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_PINCH){
551
 
                        writeByte((byte)4);
552
 
                }else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_SEMI){
553
 
                        writeByte((byte)5);
554
 
                }else if(effect.getHarmonic().getType() == HarmonicEffect.TYPE_ARTIFICIAL){
555
 
                        writeByte((byte)15);
556
 
                }               
557
 
        }
558
 
        
559
 
        //trill
560
 
        if ((header2 & 0x20) != 0) {
561
 
                writeByte((byte)effect.getTrill().getFret());           
562
 
                if(effect.getTrill().getDuration().getValue() ==  Duration.SIXTEENTH){
563
 
                        writeByte((byte)1);             
564
 
                }else if(effect.getTrill().getDuration().getValue() ==  Duration.THIRTY_SECOND){
565
 
                        writeByte((byte)2);     
566
 
                }else if(effect.getTrill().getDuration().getValue() ==  Duration.SIXTY_FOURTH){
567
 
                        writeByte((byte)3);     
568
 
                }                   
569
 
        }        
570
 
    }
571
 
 
572
 
    private void writeBend(BendEffect bend) throws IOException {
573
 
        //type
574
 
        writeByte((byte) 1);
575
 
        //value
576
 
        writeInt(0);
577
 
 
578
 
        int numPoints = bend.getPoints().size();
579
 
        writeInt(numPoints);
580
 
 
581
 
        for (int i = 0; i < numPoints; i++) {
582
 
            BendEffect.BendPoint point = (BendEffect.BendPoint) bend.getPoints().get(i);
583
 
 
584
 
            int bendPosition = (int) (point.getPosition() * GP_BEND_POSITION / BendEffect.MAX_POSITION_LENGTH);
585
 
            int bendValue = (point.getValue() * GP_BEND_SEMITONE / BendEffect.SEMITONE_LENGTH);            
586
 
 
587
 
            //bendPosition
588
 
            writeInt(bendPosition);
589
 
            //bendValue
590
 
            writeInt(bendValue);
591
 
            //bendVibrato
592
 
            writeByte((byte) 0);
593
 
        }
594
 
 
595
 
    }    
596
 
    
597
 
    private void writeTremoloBar(TremoloBarEffect effect) throws IOException {
598
 
        //type
599
 
        writeByte((byte) 6);
600
 
        //value
601
 
        writeInt(0);
602
 
 
603
 
        int numPoints = effect.getPoints().size();
604
 
        writeInt(numPoints);
605
 
 
606
 
        for (int i = 0; i < numPoints; i++) {
607
 
                TremoloBarEffect.TremoloBarPoint point = (TremoloBarEffect.TremoloBarPoint) effect.getPoints().get(i);
608
 
 
609
 
            int position = (int) (point.getPosition() * GP_BEND_POSITION / TremoloBarEffect.MAX_POSITION_LENGTH);
610
 
            int value = (point.getValue() * GP_BEND_SEMITONE / TremoloBarEffect.SEMITONE_LENGTH);            
611
 
 
612
 
            //position
613
 
            writeInt(position);
614
 
            //value
615
 
            writeInt(value);
616
 
            //vibrato
617
 
            writeByte((byte) 0);
618
 
        }
619
 
 
620
 
    }
621
 
 
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);
629
 
                }    
630
 
        }
631
 
    
632
 
    private void writeGrace(GraceEffect grace) throws IOException {             
633
 
        //fret || dead
634
 
        if(grace.isDead()){
635
 
                writeUnsignedByte(255);
636
 
        }else{
637
 
                writeUnsignedByte(grace.getFret());
638
 
        }
639
 
        
640
 
        //velocity      
641
 
        writeUnsignedByte(((grace.getDynamic() - VelocityValues.MIN_VELOCITY) / VelocityValues.VELOCITY_INCREMENT) + 1);
642
 
        
643
 
        //transition
644
 
        if(grace.getTransition() == GraceEffect.TRANSITION_NONE){
645
 
                writeUnsignedByte(0);
646
 
        }
647
 
        else if(grace.getTransition() == GraceEffect.TRANSITION_SLIDE){
648
 
                writeUnsignedByte(1);
649
 
        }
650
 
        else if(grace.getTransition() == GraceEffect.TRANSITION_BEND){
651
 
                writeUnsignedByte(2);
652
 
        }
653
 
        else if(grace.getTransition() == GraceEffect.TRANSITION_HAMMER){
654
 
                writeUnsignedByte(3);
655
 
        }
656
 
        
657
 
        //duration
658
 
        writeUnsignedByte(grace.getDuration());
659
 
    }    
660
 
    
661
 
    private void writeMixChange(Tempo tempo) throws IOException {
662
 
        for (int i = 0; i < 7; i++) {
663
 
            writeByte((byte) -1);
664
 
            
665
 
        }
666
 
        writeInt(tempo.getValue());
667
 
        
668
 
        writeByte((byte) 0);   
669
 
        
670
 
        //apply to all tracks
671
 
        writeUnsignedByte(1);
672
 
    }
673
 
 
674
 
 
675
 
    private void writeMarker(Marker marker) throws IOException {
676
 
        writeStringIntegerPlusOne(marker.getTitle());           
677
 
        writeColor(marker.getColor());
678
 
    }
679
 
    
680
 
    private List getBeats(Measure measure) {
681
 
        boolean hasSilences = (!measure.getSilences().isEmpty());
682
 
        orderNotes(measure);
683
 
        List beats = new ArrayList();
684
 
        MeasureBeat beat = null;
685
 
        Iterator it = null;
686
 
        //-----notas---------------------------------------
687
 
        it = measure.getNotes().iterator();
688
 
        while (it.hasNext()) {
689
 
            Note note = (Note) it.next();
690
 
            if (beat == null) {
691
 
                beat = new MeasureBeat(note.getDuration(), note.getStart());
692
 
            }
693
 
 
694
 
            //verifico si tengo que autocompletar silencios
695
 
            if (!hasSilences) {
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);
700
 
                    }
701
 
                }
702
 
            }
703
 
 
704
 
            //verifico si termino el beat
705
 
            if (note.getStart() != beat.getStart()) {
706
 
                beats.add(beat);
707
 
 
708
 
                //verifico si tengo que autocompletar silencios
709
 
                if (!hasSilences) {
710
 
                    long silenceLength = (note.getStart() - (beat.getStart() + beat.getDuration().getTime()));
711
 
                    if (silenceLength > 10) {
712
 
                        createSilenceBeats(beats, (beat.getStart() + beat.getDuration().getTime()), silenceLength);
713
 
                    }
714
 
                }
715
 
                //creo el siguiente beat
716
 
                beat = new MeasureBeat(note.getDuration(), note.getStart());
717
 
            }
718
 
            beat.addNote(note);
719
 
        }
720
 
        if (beat != null) {
721
 
            beats.add(beat);
722
 
        }
723
 
 
724
 
        //agrego los silencios
725
 
        if (hasSilences) {
726
 
            it = measure.getSilences().iterator();
727
 
            while (it.hasNext()) {
728
 
                Silence silence = (Silence) it.next();
729
 
                beats.add(new MeasureBeat(silence.getDuration(), silence.getStart()));
730
 
            }
731
 
        }
732
 
 
733
 
        //ordeno los beats
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()) {
739
 
                    minBeat = currBeat;
740
 
                }
741
 
            }
742
 
            beats.remove(minBeat);
743
 
            beats.add(i, minBeat);
744
 
        }
745
 
 
746
 
        return beats;
747
 
    }
748
 
 
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();
756
 
        }
757
 
    }
758
 
 
759
 
    private void orderNotes(Measure measure) {
760
 
        for (int i = 0; i < measure.getNotes().size(); i++) {
761
 
            Note minNote = null;
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()))) {
766
 
                    minNote = note;
767
 
                }
768
 
            }
769
 
            measure.getNotes().remove(minNote);
770
 
            measure.getNotes().add(i, minNote);
771
 
        }
772
 
    }
773
 
    
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);
778
 
        }
779
 
 
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());
789
 
        }
790
 
 
791
 
        return channels;
792
 
    }
793
 
 
794
 
    private void writeColor(RGBColor color) throws IOException {
795
 
        //red
796
 
        writeUnsignedByte(color.getR());
797
 
        //green
798
 
        writeUnsignedByte(color.getG());
799
 
        //blue
800
 
        writeUnsignedByte(color.getB());
801
 
 
802
 
        this.outputStream.write(0);
803
 
    }
804
 
 
805
 
    //-----------------------------------------------------------------------------------
806
 
    private void writeBoolean(boolean v) throws IOException {
807
 
        this.outputStream.write(v ? 1 : 0);
808
 
    }
809
 
 
810
 
    private void writeByte(byte v) throws IOException {
811
 
        this.outputStream.write(v);
812
 
    }
813
 
 
814
 
    private void writeUnsignedByte(int v) throws IOException {
815
 
        this.outputStream.write(v);
816
 
    }
817
 
 
818
 
    private void writeStringByte(String v, int expectedLength) throws IOException {
819
 
        byte[] bytes = v.getBytes();
820
 
 
821
 
        this.writeUnsignedByte(bytes.length);
822
 
 
823
 
        if (expectedLength != 0) {
824
 
            byte[] tempBytes = new byte[expectedLength];
825
 
            for (int i = 0; i < bytes.length; i++) {
826
 
                tempBytes[i] = bytes[i];
827
 
            }
828
 
            bytes = tempBytes;
829
 
        }
830
 
        this.outputStream.write(bytes);
831
 
    }
832
 
 
833
 
    private void writeStringIntegerPlusOne(String v) throws IOException {
834
 
        byte[] b = v.getBytes();
835
 
 
836
 
        this.writeInt(b.length + 1);
837
 
        this.outputStream.write(b.length);
838
 
        this.outputStream.write(b);
839
 
    }
840
 
 
841
 
    private void writeStringInteger(String v) throws IOException {
842
 
        byte[] bytes = v.getBytes();
843
 
        this.writeInt(bytes.length);
844
 
        this.outputStream.write(bytes);
845
 
    }
846
 
 
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);
853
 
 
854
 
        this.outputStream.write(bytes);
855
 
    }
856
 
 
857
 
    //-----------------------------------------------------------------------------------
858
 
 
859
 
    private class MeasureBeat {         
860
 
        private Duration duration;
861
 
        private List notes;
862
 
        private boolean silence;
863
 
        private long start;
864
 
        private NoteEffect beatEffects;
865
 
        
866
 
        public MeasureBeat(Duration duration, long start) {
867
 
            this.duration = duration;
868
 
            this.start = start;
869
 
            this.notes = new ArrayList();
870
 
            this.silence = true;
871
 
            this.beatEffects = new NoteEffect();
872
 
        }
873
 
 
874
 
        public long getStart() {
875
 
            return start;
876
 
        }
877
 
 
878
 
        public void setStart(long start) {
879
 
            this.start = start;
880
 
        }
881
 
 
882
 
        public Duration getDuration() {
883
 
            return duration;
884
 
        }
885
 
 
886
 
        public void addNote(Note note) {
887
 
            this.getNotes().add(note);
888
 
            this.checkEffects(note.getEffect());
889
 
            this.silence = false;            
890
 
        }
891
 
 
892
 
        public List getNotes() {
893
 
            return notes;
894
 
        }
895
 
 
896
 
        private boolean isSilence() {
897
 
            return silence;
898
 
        }
899
 
        
900
 
        private NoteEffect getEffects(){
901
 
                return this.beatEffects;
902
 
        }
903
 
        
904
 
        private void checkEffects(NoteEffect effect){
905
 
                if(effect.isFadeIn()){
906
 
                        this.beatEffects.setFadeIn(true);
907
 
                }
908
 
                if(effect.isTremoloBar()){
909
 
                        this.beatEffects.setTremoloBar((TremoloBarEffect)effect.getTremoloBar().clone());
910
 
                }               
911
 
                if(effect.isTapping()){
912
 
                        this.beatEffects.setTapping(true);
913
 
                }
914
 
                if(effect.isSlapping()){
915
 
                        this.beatEffects.setSlapping(true);
916
 
                }
917
 
                if(effect.isPopping()){
918
 
                        this.beatEffects.setPopping(true);
919
 
                }               
920
 
        }
921
 
 
922
 
    }
923
 
 
924
 
 
925
 
    private byte toChannelByte(short s){
926
 
        s = (short)((s * (short)16) / (short)127);
927
 
        s = (s <= 16)?s:16;
928
 
        return (byte)s;
929
 
    }
930
 
}
 
 
b'\\ No newline at end of file'