1
package org.herac.tuxguitar.io.importer;
4
import java.io.IOException;
5
import java.util.ArrayList;
6
import java.util.Iterator;
9
import javax.sound.midi.MetaMessage;
10
import javax.sound.midi.MidiEvent;
11
import javax.sound.midi.MidiSystem;
12
import javax.sound.midi.Sequence;
13
import javax.sound.midi.ShortMessage;
14
import javax.sound.midi.Track;
16
import org.herac.tuxguitar.player.base.MidiControllers;
17
import org.herac.tuxguitar.song.managers.SongManager;
18
import org.herac.tuxguitar.song.models.Duration;
19
import org.herac.tuxguitar.song.models.InstrumentString;
20
import org.herac.tuxguitar.song.models.Measure;
21
import org.herac.tuxguitar.song.models.MeasureHeader;
22
import org.herac.tuxguitar.song.models.Note;
23
import org.herac.tuxguitar.song.models.NoteEffect;
24
import org.herac.tuxguitar.song.models.RGBColor;
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;
31
public class MidiImporter implements SongImporter{
33
private static final long MIN_START = 1000;
35
private static final Duration MIN_DURATION = new Duration(Duration.SIXTY_FOURTH);
37
private float divisionType;
38
private int resolution;
39
private boolean moveTick;
42
private List tempNotes;
43
private List tempChannels;
44
private List trackTunningHelpers;
46
public MidiImporter(){
49
public Song importSong(String fileName) throws IOException, Exception, Error{
50
Sequence sequence = MidiSystem.getSequence(new File(fileName));
53
Track[] tracks = sequence.getTracks();
54
for(int i = 0; i < tracks.length; i++){
55
Track track = tracks[i];
56
int trackNumber = getNextTrackNumber();
57
int events = track.size();
58
for(int j = 0;j < events;j ++){
59
MidiEvent event = track.get(j);
60
parseMessage(trackNumber,event.getTick(),event.getMessage().getMessage());
65
return new SongAdjuster(new Song(this.tracks,this.headers)).adjustSong();
66
//return new Song(this.tracks,this.headers);
69
private void initFields(Sequence sequence){
70
this.divisionType = sequence.getDivisionType();
71
this.resolution = sequence.getResolution();
72
this.headers = new ArrayList();
73
this.tracks = new ArrayList();
74
this.tempNotes = new ArrayList();
75
this.tempChannels = new ArrayList();
76
this.trackTunningHelpers = new ArrayList();
79
private int getNextTrackNumber(){
80
return (tracks.size() + 1);
83
private void parseMessage(int trackNumber,long tick,byte[] data){
84
int length = data.length;
85
tick = parseTick(tick + resolution);
88
if((((length > 0)?(data[0] & 0xFF):0) & 0xF0) == ShortMessage.NOTE_ON){
89
parseNoteOn(trackNumber,tick,data);
92
else if((((length > 0)?(data[0] & 0xFF):0) & 0xF0) == ShortMessage.NOTE_OFF){
93
parseNoteOff(trackNumber,tick,data);
96
else if((((length > 0)?(data[0] & 0xFF):0) & 0xF0) == ShortMessage.PROGRAM_CHANGE){
97
parseProgramChange(data);
100
else if((((length > 0)?(data[0] & 0xFF):0) & 0xF0) == ShortMessage.CONTROL_CHANGE){
101
parseControlChange(data);
104
else if(((length > 0)?(data[0] & 0xFF):0) == MetaMessage.META && (data[1] == 0x58)){
105
parseTimeSignature(tick,data);
108
else if(((length > 0)?(data[0] & 0xFF):0) == MetaMessage.META && (data[1] == 0x51)){
109
parseTempo(tick,data);
113
private long parseTick(long tick){
114
return Math.abs(Duration.QUARTER_TIME * tick / this.resolution);
117
private void parseNoteOn(int track,long tick,byte[] data){
118
int length = data.length;
119
int channel = (length > 0)?((data[0] & 0xFF) & 0x0F):0;
120
int value = (length > 1)?(data[1] & 0xFF):0;
121
int velocity = (length > 2)?(data[2] & 0xFF):0;
123
parseNoteOff(track,tick,data);
125
makeTempNotesBefore(tick,track);
126
getTempChannel(channel).track = track;
127
getTrackTunningHelper(track).checkValue(value);
128
tempNotes.add(new TempNote(track,channel,value,tick));
132
private void parseNoteOff(int track,long tick,byte[] data){
133
int length = data.length;
135
int channel = (length > 0)?((data[0] & 0xFF) & 0x0F):0;
136
int value = (length > 1)?(data[1] & 0xFF):0;
137
int velocity = (length > 2)?(data[2] & 0xFF):0;
139
makeNote(tick,track,channel,value);
142
private void parseProgramChange(byte[] data){
143
int length = data.length;
144
int channel = (length > 0)?((data[0] & 0xFF) & 0x0F):-1;
145
int instrument = (length > 1)?(data[1] & 0xFF):-1;
146
if(channel != -1 && instrument != -1){
147
getTempChannel(channel).instrument = instrument;
151
private void parseControlChange(byte[] data){
152
int length = data.length;
153
int channel = (length > 0)?((data[0] & 0xFF) & 0x0F):-1;
154
int control = (length > 1)?(data[1] & 0xFF):-1;
155
int value = (length > 2)?(data[2] & 0xFF):-1;
156
if(channel != -1 && control != -1 && value != -1){
157
if(control == MidiControllers.VOLUME){
158
getTempChannel(channel).volume = value;
160
else if(control == MidiControllers.BALANCE){
161
getTempChannel(channel).balance = value;
167
private void parseTimeSignature(long tick,byte[] data){
168
TimeSignature timeSignature = new TimeSignature(data[3],new Duration(Duration.QUARTER));
170
timeSignature.getDenominator().setValue(Duration.WHOLE);
171
} else if (data[4] == 1) {
172
timeSignature.getDenominator().setValue(Duration.HALF);
173
} else if (data[4] == 2) {
174
timeSignature.getDenominator().setValue(Duration.QUARTER);
175
} else if (data[4] == 3) {
176
timeSignature.getDenominator().setValue(Duration.EIGHTH);
177
} else if (data[4] == 4) {
178
timeSignature.getDenominator().setValue(Duration.SIXTEENTH);
179
} else if (data[4] == 5) {
180
timeSignature.getDenominator().setValue(Duration.THIRTY_SECOND);
182
//timeSignature.getDenominator().setValue((96 / data[5]));
183
getHeader(tick).setTimeSignature(timeSignature);
186
private void parseTempo(long tick,byte[] data){
187
Tempo tempo = Tempo.fromUSQ((data[5] & 0x00ff) | ((data[4] & 0x00ff) << 8) | ((data[3] & 0x00ff) << 16));
188
getHeader(tick).setTempo(tempo);
191
private SongTrack getTrack(int number){
192
Iterator it = tracks.iterator();
194
SongTrack track = (SongTrack)it.next();
195
if(track.getNumber() == number){
199
SongChannel channel = new SongChannel((short)-1,(short)-1,(short)0);
200
SongTrack track = new SongTrack(number,"",channel,new ArrayList(),new ArrayList(),0,(RGBColor)RGBColor.RED.clone());
201
//SongTrack track = new SongTrack(number,"",channel,new ArrayList(),SongManager.createDefaultInstrumentStrings(),0,(RGBColor)RGBColor.RED.clone());
206
private MeasureHeader getHeader(long tick){
207
tick = (tick >= MIN_START)?tick:MIN_START;
209
Iterator it = headers.iterator();
211
MeasureHeader header = (MeasureHeader)it.next();
212
if(tick >= header.getStart() && tick < header.getStart() + header.getLength()){
216
MeasureHeader last = getLastHeader();
217
int number = (last != null)?last.getNumber() + 1:1;
218
long start = (last != null)?(last.getStart() + last.getLength()):1000;
219
int tripletFeel = MeasureHeader.TRIPLET_FEEL_NONE;
220
TimeSignature timeSignature = (last != null)?(TimeSignature)last.getTimeSignature().clone():new TimeSignature(4,new Duration(Duration.QUARTER));
221
Tempo tempo = (last != null)?(Tempo)last.getTempo().clone():new Tempo(120);
222
MeasureHeader header = new MeasureHeader(number,start,timeSignature,tempo,null,tripletFeel,false,0);
224
this.headers.add(header);
226
if(header.getStart() + header.getLength() <= tick){
229
return getHeader(tick);
232
private MeasureHeader getLastHeader(){
233
if(!this.headers.isEmpty()){
234
return (MeasureHeader)this.headers.get(this.headers.size() - 1);
239
private Measure getMeasure(SongTrack track,long tick){
240
tick = (tick >= MIN_START)?tick:MIN_START;
241
Iterator it = track.getMeasures().iterator();
243
Measure measure = (Measure)it.next();
244
if(tick >= measure.getStart() && tick < measure.getStart() + measure.getLength()){
249
for(int i = 0;i < headers.size();i++){
250
boolean exist = false;
251
MeasureHeader header = (MeasureHeader)headers.get(i);
252
for(int j = 0;j < track.getMeasures().size();j++){
253
Measure measure = (Measure)track.getMeasures().get(j);
254
if(measure.getHeader().equals(header)){
259
track.getMeasures().add(new Measure(header,new ArrayList(),new ArrayList(),1,0));
262
return getMeasure(track,tick);
265
private TempNote getTempNote(int track,int channel,int value,boolean purge){
266
for(int i = 0;i < tempNotes.size();i ++){
267
TempNote note = (TempNote)tempNotes.get(i);
268
if(note.track == track && note.channel == channel && note.value == value){
278
private TrackTunningHelper getTrackTunningHelper(int track){
279
Iterator it = this.trackTunningHelpers.iterator();
281
TrackTunningHelper helper = (TrackTunningHelper)it.next();
282
if(helper.getTrack() == track){
286
TrackTunningHelper helper = new TrackTunningHelper(track);
287
this.trackTunningHelpers.add(helper);
292
private void makeTempNotesBefore(long tick,int track){
293
boolean check = true;
296
for(int i = 0;i < tempNotes.size();i ++){
297
TempNote note = (TempNote)tempNotes.get(i);
298
if(note.tick < tick && note.track == track){
300
tick = note.tick + 5000;
301
makeNote(tick,track,note.channel,note.value);
309
private void makeNote(long tick,int track,int channel,int value){
310
TempNote tempNote = getTempNote(track,channel,value,true);
311
if(tempNote != null){
313
int nValue = tempNote.value;
315
long nStart = tempNote.tick;
316
Duration nDuration = Duration.fromTime(tick - tempNote.tick,MIN_DURATION);
317
NoteEffect effect = new NoteEffect();
319
Measure measure = getMeasure(getTrack(track),tempNote.tick);
320
Note note = new Note(nValue,nStart,nDuration,nVelocity,nString,false,effect);
321
measure.addNote(note);
326
public TempChannel getTempChannel(int channel){
327
Iterator it = tempChannels.iterator();
329
TempChannel tempChannel = (TempChannel)it.next();
330
if(tempChannel.channel == channel){
334
TempChannel tempChannel = new TempChannel(channel);
335
tempChannels.add(tempChannel);
343
private void checkAll()throws Exception{
346
int headerCount = headers.size();
347
for(int i = 0;i < tracks.size();i ++){
348
SongTrack track = (SongTrack)tracks.get(i);
350
while(track.getMeasures().size() < headerCount){
351
long start = Duration.QUARTER_TIME;
352
Measure lastMeasure = (Measure)((!track.getMeasures().isEmpty())?track.getMeasures().get(track.getMeasures().size() - 1) :null);
353
if(lastMeasure != null){
354
start = (lastMeasure.getStart() + lastMeasure.getLength());
356
track.getMeasures().add(new Measure(getHeader(start),new ArrayList(),new ArrayList(),1,0));
360
if(headers.isEmpty() || tracks.isEmpty()){
361
throw new Exception("Empty Song");
365
private void checkTracks(){
366
Iterator it = tracks.iterator();
368
SongTrack track = (SongTrack)it.next();
371
Iterator tcIt = tempChannels.iterator();
372
while(tcIt.hasNext()){
373
TempChannel tempChannel = (TempChannel)tcIt.next();
374
if(tempChannel.track == track.getNumber()){
375
if(track.getChannel().getChannel() < 0){
376
track.getChannel().setChannel((short)tempChannel.channel);
377
track.getChannel().setInstrument((short)tempChannel.instrument);
378
track.getChannel().setVolume((short)tempChannel.volume);
379
track.getChannel().setBalance((short)tempChannel.balance);
380
}else if(track.getChannel().getEffectChannel() < 0){
381
track.getChannel().setEffectChannel((short)tempChannel.channel);
385
if(track.getChannel().getChannel() < 0){
386
track.getChannel().setChannel((short)(SongManager.MAX_CHANNELS - 1));
387
track.getChannel().setInstrument((short)0);
388
track.getChannel().setVolume((short)127);
389
track.getChannel().setBalance((short)64);
391
if(track.getChannel().getEffectChannel() < 0){
392
track.getChannel().setEffectChannel(track.getChannel().getChannel());
395
if(!track.isPercussionTrack()){
396
track.setStrings(getTrackTunningHelper((int)track.getNumber()).getStrings());
398
track.setStrings(SongManager.createPercusionStrings(6));
404
private class TempNote{
410
public TempNote(int track, int channel, int value,long tick) {
412
this.channel = channel;
418
private class TempChannel{
420
private int instrument;
425
public TempChannel(int channel) {
426
this.channel = channel;
435
private class TrackTunningHelper{
437
private int maxValue;
438
private int minValue;
440
public TrackTunningHelper(int track){
446
public void checkValue(int value){
447
if(this.minValue < 0 || value < this.minValue){
448
this.minValue = value;
450
if(this.maxValue < 0 || value > this.maxValue){
451
this.maxValue = value;
456
private List getStrings() {
457
List strings = new ArrayList();
461
if(minValue >= 40 && maxValue <= 64 + maxFret){
462
strings.add(new InstrumentString(1, 64));
463
strings.add(new InstrumentString(2, 59));
464
strings.add(new InstrumentString(3, 55));
465
strings.add(new InstrumentString(4, 50));
466
strings.add(new InstrumentString(5, 45));
467
strings.add(new InstrumentString(6, 40));
469
else if(minValue >= 38 && maxValue <= 64 + maxFret){
470
strings.add(new InstrumentString(1, 64));
471
strings.add(new InstrumentString(2, 59));
472
strings.add(new InstrumentString(3, 55));
473
strings.add(new InstrumentString(4, 50));
474
strings.add(new InstrumentString(5, 45));
475
strings.add(new InstrumentString(6, 38));
477
else if(minValue >= 35 && maxValue <= 64 + maxFret){
478
strings.add(new InstrumentString(1, 64));
479
strings.add(new InstrumentString(2, 59));
480
strings.add(new InstrumentString(3, 55));
481
strings.add(new InstrumentString(4, 50));
482
strings.add(new InstrumentString(5, 45));
483
strings.add(new InstrumentString(6, 40));
484
strings.add(new InstrumentString(7, 35));
486
else if(minValue >= 28 && maxValue <= 43 + maxFret){
487
strings.add(new InstrumentString(1, 43));
488
strings.add(new InstrumentString(2, 38));
489
strings.add(new InstrumentString(3, 33));
490
strings.add(new InstrumentString(4, 28));
492
else if(minValue >= 23 && maxValue <= 43 + maxFret){
493
strings.add(new InstrumentString(1, 43));
494
strings.add(new InstrumentString(2, 38));
495
strings.add(new InstrumentString(3, 33));
496
strings.add(new InstrumentString(4, 28));
497
strings.add(new InstrumentString(5, 23));
500
int stringSpacing = ((maxValue - (maxFret - 4) - minValue) / stringCount);
501
if(stringSpacing > 5){
503
stringSpacing = ((maxValue - (maxFret - 4) - minValue) / stringCount);
506
int maxStringValue = (minValue + (stringCount * stringSpacing));
507
while(strings.size() < stringCount){
508
maxStringValue -= stringSpacing;
509
strings.add(new InstrumentString(strings.size() + 1,maxStringValue));
516
public int getMaxValue() {
520
public int getMinValue() {
524
public int getTrack() {
530
private class SongAdjuster{
532
private long minDurationTime;
534
public SongAdjuster(Song song){
536
this.minDurationTime = 60;
539
public Song adjustSong(){
540
Iterator it = song.getTracks().iterator();
543
SongTrack track = (SongTrack)it.next();
549
private void adjustTrack(SongTrack track){
550
Iterator it = track.getMeasures().iterator();
552
Measure measure = (Measure)it.next();
553
adjustMeasure(measure);
554
adjustStrings(track,measure);
559
private void adjustMeasure(Measure measure){
561
for(int i = 0; i < measure.getNotes().size();i++){
562
Note note = (Note)measure.getNotes().get(i);
563
long start = note.getStart();
564
long time = note.getDuration().getTime();
565
if(i == 0 && measure.getStart() + minDurationTime > start){
566
note.setStart(measure.getStart());
568
int nextIndex = (i + 1);
569
for(int j = nextIndex; j < measure.getNotes().size();j++){
570
Note next = (Note)measure.getNotes().get(j);
571
long nextTime = next.getDuration().getTime();
573
if(start + minDurationTime > next.getStart()){
574
start = (next.getStart() > start)?next.getStart():start;
575
next.setStart(note.getStart());
577
next.setDuration((Duration)note.getDuration().clone());
578
time = next.getDuration().getTime();
579
}else if(time < nextTime){
580
note.setDuration((Duration)next.getDuration().clone());
583
if(note.getStart() + time + minDurationTime + diff > next.getStart()){
584
diff = (next.getStart() - (note.getStart() + time));
585
next.setStart(note.getStart() + time);
594
private void adjustMeasure(Measure measure){
595
List beatNotes = new ArrayList();
596
long nextStart = measure.getStart();
598
for(int i = 0; i < measure.getNotes().size();i++){
599
Note note = (Note)measure.getNotes().get(i);
600
long start = note.getStart();
601
int nextIndex = (i + 1);
604
if((nextStart < start && nextStart + minDurationTime > start) || (nextStart > start && nextStart - minDurationTime < start)){
605
note.setStart(nextStart);
608
long end = measure.getStart() + measure.getLength();
609
for(int j = nextIndex; j < measure.getNotes().size();j++){
610
Note next = (Note)measure.getNotes().get(j);
611
long nextTime = next.getDuration().getTime();
613
if(start + minDurationTime > next.getStart()){
614
start = (next.getStart() > start)?next.getStart():start;
618
end= next.getStart();
622
nextStart = nextStart + adjustBeat(beatNotes,note.getStart(),end);
627
private long adjustBeat(List beatNotes,long start,long nextBeatStart){
628
Duration lengthDuration = Duration.fromTime(nextBeatStart - start,MIN_DURATION);
629
long length = lengthDuration.getTime();
631
Duration maxDuration = null;
632
for(int i = 0; i < beatNotes.size();i++){
633
Note note = (Note)beatNotes.get(i);
634
note.setStart(start);
635
if(maxDuration == null || note.getDuration().getTime() > maxDuration.getTime()){
636
maxDuration = note.getDuration();
639
if(maxDuration != null){
640
if(maxDuration.getTime() > length){
641
maxDuration = lengthDuration;
643
for(int i = 0; i < beatNotes.size();i++){
644
Note note = (Note)beatNotes.get(i);
645
note.setStart(start);
646
note.setDuration((Duration)maxDuration.clone());
654
private void adjustStrings(SongTrack track,Measure measure){
655
List tempStrings = new ArrayList();
659
Iterator it = measure.getNotes().iterator();
661
Note note = (Note)it.next();
663
//checkeo las cuerdas
664
if(note.getStart() != start){
666
tempStrings.addAll(track.getStrings());
668
int string = getStringForValue(tempStrings,note.getValue());
669
for(int j = 0;j < tempStrings.size();j ++){
670
InstrumentString tempString = (InstrumentString)tempStrings.get(j);
671
if(tempString.getNumber() == string){
672
note.setValue(note.getValue() - tempString.getValue());
673
note.setString(tempString.getNumber());
674
tempStrings.remove(j);
678
if(note.getString() < 1){
679
string = getStringForValue(track.getStrings(),note.getValue());
680
note.setValue(note.getValue() - ((InstrumentString)track.getStrings().get(string - 1)).getValue());
681
note.setString(string);
683
start = note.getStart();
689
private int getStringForValue(List strings,int value){
691
int stringForValue = 0;
692
for(int i = 0;i < strings.size();i++){
693
InstrumentString string = (InstrumentString)strings.get(i);
694
int fret = value - string.getValue();
695
if(minFret < 0 || (fret >= 0 && fret < minFret)){
696
stringForValue = string.getNumber();
700
return stringForValue;