/* * Created on 13-dic-2005 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package org.herac.tuxguitar.player.base; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.herac.tuxguitar.song.managers.TGSongManager; import org.herac.tuxguitar.song.models.TGBeat; import org.herac.tuxguitar.song.models.TGChannel; import org.herac.tuxguitar.song.models.TGDuration; import org.herac.tuxguitar.song.models.TGMeasure; import org.herac.tuxguitar.song.models.TGMeasureHeader; import org.herac.tuxguitar.song.models.TGNote; import org.herac.tuxguitar.song.models.TGSong; import org.herac.tuxguitar.song.models.TGString; import org.herac.tuxguitar.song.models.TGStroke; import org.herac.tuxguitar.song.models.TGTempo; import org.herac.tuxguitar.song.models.TGTrack; import org.herac.tuxguitar.song.models.TGVelocities; import org.herac.tuxguitar.song.models.TGVoice; import org.herac.tuxguitar.song.models.effects.TGEffectBend; import org.herac.tuxguitar.song.models.effects.TGEffectHarmonic; import org.herac.tuxguitar.song.models.effects.TGEffectTremoloBar; /** * @author julian * * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code Templates */ public class MidiSequenceParser { private static final int DEFAULT_METRONOME_KEY = 37; private static final int DEFAULT_DURATION_PM = 60; private static final int DEFAULT_DURATION_DEAD = 30; private static final int DEFAULT_BEND = 64; private static final float DEFAULT_BEND_SEMI_TONE = 2.75f; /** * flag para agregar los controles por defecto, * no se recomienda usar este flag si el reproductor asigna estos controles en tiempo real. */ public static final int ADD_DEFAULT_CONTROLS = 0x01; /** * flag para agregar los valores del mixer (volumen, balance, instrumento), * no se recomienda usar este flag si el reproductor asigna estos valores en tiempo real. */ public static final int ADD_MIXER_MESSAGES = 0x02; /** * flag para agregar la pista del metronomo, * en casos como la exportacion de midi, este flag no sera necesario */ public static final int ADD_METRONOME = 0x04; /** * tuxguitar usa como primer tick el valor de la constante Duration.QUARTER_TIME * asignando este flag, es posible crear el primer tick en cero. */ public static final int ADD_FIRST_TICK_MOVE = 0x08; public static final int DEFAULT_PLAY_FLAGS = (ADD_METRONOME); public static final int DEFAULT_EXPORT_FLAGS = (ADD_FIRST_TICK_MOVE | ADD_DEFAULT_CONTROLS | ADD_MIXER_MESSAGES); /** * Song Manager */ private TGSongManager manager; /** * flags */ private int flags; /** * Index of info track */ private int infoTrack; /** * Index of metronome track */ private int metronomeTrack; private int metronomeChannelId; private int firstTickMove; private int tempoPercent; private int transpose; private int sHeader; private int eHeader; public MidiSequenceParser(TGSongManager manager,int flags) { this.manager = manager; this.flags = flags; this.tempoPercent = 100; this.transpose = 0; this.sHeader = -1; this.eHeader = -1; this.firstTickMove = (int)(((flags & ADD_FIRST_TICK_MOVE) != 0)?(-TGDuration.QUARTER_TIME):0); } public int getInfoTrack(){ return this.infoTrack; } public int getMetronomeTrack(){ return this.metronomeTrack; } private long getTick(long tick){ return (tick + this.firstTickMove); } public void setSHeader(int header) { this.sHeader = header; } public void setEHeader(int header) { this.eHeader = header; } public void setMetronomeChannelId(int metronomeChannelId) { this.metronomeChannelId = metronomeChannelId; } public void setTempoPercent(int tempoPercent) { this.tempoPercent = tempoPercent; } public void setTranspose(int transpose) { this.transpose = transpose; } private int fix( int value ){ return ( value >= 0 ? value <= 127 ? value : 127 : 0 ); } /** * Crea la cancion */ public void parse(MidiSequenceHandler sequence) { this.infoTrack = 0; this.metronomeTrack = (sequence.getTracks() - 1); MidiSequenceHelper helper = new MidiSequenceHelper(sequence); MidiRepeatController controller = new MidiRepeatController(this.manager.getSong(),this.sHeader,this.eHeader); while(!controller.finished()){ int index = controller.getIndex(); long move = controller.getRepeatMove(); controller.process(); if(controller.shouldPlay()){ helper.addMeasureHelper( new MidiMeasureHelper(index,move) ); } } this.addDefaultMessages(helper, this.manager.getSong()); for (int i = 0; i < this.manager.getSong().countTracks(); i++) { TGTrack songTrack = this.manager.getSong().getTrack(i); createTrack(helper, songTrack); } sequence.notifyFinish(); } /** * Crea las pistas de la cancion */ private void createTrack(MidiSequenceHelper sh, TGTrack track) { TGChannel tgChannel = this.manager.getChannel( track.getChannelId() ); if( tgChannel != null ){ TGMeasure previous = null; this.addBend(sh,track.getNumber(),TGDuration.QUARTER_TIME,DEFAULT_BEND, tgChannel.getChannelId(), -1, false); this.makeChannel(sh, tgChannel, track.getNumber()); int mCount = sh.getMeasureHelpers().size(); for( int mIndex = 0 ; mIndex < mCount ; mIndex++ ){ MidiMeasureHelper mh = sh.getMeasureHelper( mIndex ); TGMeasure measure = track.getMeasure(mh.getIndex()); if(track.getNumber() == 1){ addTimeSignature(sh,measure, previous, mh.getMove()); addTempo(sh,measure, previous, mh.getMove()); addMetronome(sh,measure.getHeader(), mh.getMove() ); } //agrego los pulsos makeBeats( sh, tgChannel, track, measure, mIndex, mh.getMove() ); previous = measure; } } } private void makeBeats(MidiSequenceHelper sh, TGChannel channel, TGTrack track, TGMeasure measure, int mIndex, long startMove) { int[] stroke = new int[track.stringCount()]; TGBeat previous = null; for (int bIndex = 0; bIndex < measure.countBeats(); bIndex++) { TGBeat beat = measure.getBeat(bIndex); makeNotes( sh, channel, track, beat, measure.getTempo(), mIndex, bIndex, startMove, getStroke(beat, previous, stroke) ); previous = beat; } } /** * Crea las notas del compas */ private void makeNotes( MidiSequenceHelper sh, TGChannel tgChannel, TGTrack track, TGBeat beat, TGTempo tempo, int mIndex,int bIndex, long startMove, int[] stroke) { for( int vIndex = 0; vIndex < beat.countVoices(); vIndex ++ ){ TGVoice voice = beat.getVoice(vIndex); MidiTickHelper th = checkTripletFeel(voice,bIndex); for (int noteIdx = 0; noteIdx < voice.countNotes(); noteIdx++) { TGNote note = voice.getNote(noteIdx); if (!note.isTiedNote()) { int key = (this.transpose + track.getOffset() + note.getValue() + ((TGString)track.getStrings().get(note.getString() - 1)).getValue()); long start = applyStrokeStart(note, (th.getStart() + startMove) , stroke); long duration = applyStrokeDuration(note, getRealNoteDuration(sh, track, note, tempo, th.getDuration(), mIndex,bIndex), stroke); int velocity = getRealVelocity(sh, note, track, tgChannel, mIndex, bIndex); int channel = tgChannel.getChannelId(); int midiVoice = note.getString(); boolean bendMode = false; boolean percussionChannel = tgChannel.isPercussionChannel(); //---Fade In--- if(note.getEffect().isFadeIn()){ makeFadeIn(sh,track.getNumber(), start, duration, tgChannel.getVolume(), channel); } //---Grace--- if(note.getEffect().isGrace() && !percussionChannel ){ bendMode = true; int graceKey = track.getOffset() + note.getEffect().getGrace().getFret() + ((TGString)track.getStrings().get(note.getString() - 1)).getValue(); int graceLength = note.getEffect().getGrace().getDurationTime(); int graceVelocity = note.getEffect().getGrace().getDynamic(); long graceDuration = ((note.getEffect().getGrace().isDead())?applyStaticDuration(tempo, DEFAULT_DURATION_DEAD, graceLength):graceLength); if(note.getEffect().getGrace().isOnBeat() || (start - graceLength) < TGDuration.QUARTER_TIME){ start += graceLength; duration -= graceLength; } makeNote(sh,track.getNumber(), graceKey,start - graceLength,graceDuration,graceVelocity,channel,midiVoice, bendMode); } //---Trill--- if(note.getEffect().isTrill() && !percussionChannel ){ int trillKey = track.getOffset() + note.getEffect().getTrill().getFret() + ((TGString)track.getStrings().get(note.getString() - 1)).getValue(); long trillLength = note.getEffect().getTrill().getDuration().getTime(); boolean realKey = true; long tick = start; while(true){ if(tick + 10 >= (start + duration)){ break ; }else if( (tick + trillLength) >= (start + duration)){ trillLength = (((start + duration) - tick) - 1); } makeNote(sh,track.getNumber(),((realKey)?key:trillKey),tick,trillLength,velocity,channel,midiVoice,bendMode); realKey = (!realKey); tick += trillLength; } continue; } //---Tremolo Picking--- if(note.getEffect().isTremoloPicking()){ long tpLength = note.getEffect().getTremoloPicking().getDuration().getTime(); long tick = start; while(true){ if(tick + 10 >= (start + duration)){ break ; }else if( (tick + tpLength) >= (start + duration)){ tpLength = (((start + duration) - tick) - 1); } makeNote(sh,track.getNumber(),key,tick,tpLength,velocity,channel,midiVoice,bendMode); tick += tpLength; } continue; } //---Bend--- if( note.getEffect().isBend() && !percussionChannel ){ bendMode = true; makeBend(sh,track.getNumber(),start,duration,note.getEffect().getBend(),channel,midiVoice,bendMode); } //---TremoloBar--- else if( note.getEffect().isTremoloBar() && !percussionChannel ){ bendMode = true; makeTremoloBar(sh,track.getNumber(),start,duration,note.getEffect().getTremoloBar(),channel,midiVoice,bendMode); } //---Slide--- else if( note.getEffect().isSlide() && !percussionChannel){ bendMode = true; makeSlide(sh, note, track, mIndex, bIndex, startMove, channel,midiVoice,bendMode); } //---Vibrato--- else if( note.getEffect().isVibrato() && !percussionChannel){ bendMode = true; makeVibrato(sh,track.getNumber(),start,duration,channel,midiVoice,bendMode); } //---Harmonic--- if( note.getEffect().isHarmonic() && !percussionChannel){ int orig = key; //Natural if(note.getEffect().getHarmonic().isNatural()){ for(int i = 0;i < TGEffectHarmonic.NATURAL_FREQUENCIES.length;i ++){ if((note.getValue() % 12) == (TGEffectHarmonic.NATURAL_FREQUENCIES[i][0] % 12) ){ key = ((orig + TGEffectHarmonic.NATURAL_FREQUENCIES[i][1]) - note.getValue()); break; } } } //Artifical/Tapped/Pinch/Semi else{ if( note.getEffect().getHarmonic().isSemi() && !percussionChannel ){ makeNote(sh,track.getNumber(),Math.min(127,orig), start, duration,Math.max(TGVelocities.MIN_VELOCITY,velocity - (TGVelocities.VELOCITY_INCREMENT * 3)),channel,midiVoice,bendMode); } key = (orig + TGEffectHarmonic.NATURAL_FREQUENCIES[note.getEffect().getHarmonic().getData()][1]); } if( (key - 12) > 0 ){ int hVelocity = Math.max(TGVelocities.MIN_VELOCITY,velocity - (TGVelocities.VELOCITY_INCREMENT * 4)); makeNote(sh,track.getNumber(),(key - 12), start, duration,hVelocity,channel,midiVoice,bendMode); } } //---Normal Note--- makeNote(sh,track.getNumber(), Math.min(127,key), start, duration, velocity,channel,midiVoice,bendMode); } } } } /** * Crea una nota en la posicion start */ private void makeNote(MidiSequenceHelper sh,int track, int key, long start, long duration, int velocity, int channel, int midiVoice, boolean bendMode) { sh.getSequence().addNoteOn(getTick(start),track,channel,fix(key),fix(velocity), midiVoice, bendMode); if( duration > 0 ){ sh.getSequence().addNoteOff(getTick(start + duration),track,channel,fix(key),fix(velocity), midiVoice, bendMode); } } private void makeChannel(MidiSequenceHelper sh,TGChannel channel,int track) { if((this.flags & ADD_MIXER_MESSAGES) != 0){ int channelId = channel.getChannelId(); long tick = getTick(TGDuration.QUARTER_TIME); sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.VOLUME,fix(channel.getVolume())); sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.BALANCE,fix(channel.getBalance())); sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.CHORUS,fix(channel.getChorus())); sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.REVERB,fix(channel.getReverb())); sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.PHASER,fix(channel.getPhaser())); sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.TREMOLO,fix(channel.getTremolo())); sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.EXPRESSION, 127); if(!channel.isPercussionChannel()){ sh.getSequence().addControlChange(tick,track,channelId,MidiControllers.BANK_SELECT, fix(channel.getBank())); } sh.getSequence().addProgramChange(tick,track,channelId,fix(channel.getProgram())); } } /** * Agrega un Time Signature si es distinto al anterior */ private void addTimeSignature(MidiSequenceHelper sh,TGMeasure currMeasure, TGMeasure prevMeasure,long startMove){ boolean addTimeSignature = false; if (prevMeasure == null) { addTimeSignature = true; } else { int currNumerator = currMeasure.getTimeSignature().getNumerator(); int currValue = currMeasure.getTimeSignature().getDenominator().getValue(); int prevNumerator = prevMeasure.getTimeSignature().getNumerator(); int prevValue = prevMeasure.getTimeSignature().getDenominator().getValue(); if (currNumerator != prevNumerator || currValue != prevValue) { addTimeSignature = true; } } if (addTimeSignature) { sh.getSequence().addTimeSignature(getTick(currMeasure.getStart() + startMove), getInfoTrack(), currMeasure.getTimeSignature()); } } /** * Agrega un Tempo si es distinto al anterior */ private void addTempo(MidiSequenceHelper sh,TGMeasure currMeasure, TGMeasure prevMeasure,long startMove){ boolean addTempo = false; if (prevMeasure == null) { addTempo = true; } else { if (currMeasure.getTempo().getInUSQ() != prevMeasure.getTempo().getInUSQ()) { addTempo = true; } } if (addTempo) { int usq = (int)(currMeasure.getTempo().getInUSQ() * 100.00 / this.tempoPercent ); sh.getSequence().addTempoInUSQ(getTick(currMeasure.getStart() + startMove), getInfoTrack(), usq); } } /** * Retorna la Duracion real de una nota, verificando si tiene otras ligadas */ private long getRealNoteDuration(MidiSequenceHelper sh, TGTrack track, TGNote note, TGTempo tempo, long duration,int mIndex, int bIndex) { boolean letRing = (note.getEffect().isLetRing()); boolean letRingBeatChanged = false; long lastEnd = (note.getVoice().getBeat().getStart() + note.getVoice().getDuration().getTime() + sh.getMeasureHelper(mIndex).getMove()); long realDuration = duration; int nextBIndex = (bIndex + 1); int mCount = sh.getMeasureHelpers().size(); for (int m = mIndex; m < mCount; m++) { MidiMeasureHelper mh = sh.getMeasureHelper( m ); TGMeasure measure = track.getMeasure( mh.getIndex() ); int beatCount = measure.countBeats(); for (int b = nextBIndex; b < beatCount; b++) { TGBeat beat = measure.getBeat(b); TGVoice voice = beat.getVoice(note.getVoice().getIndex()); if(!voice.isEmpty()){ if(voice.isRestVoice()){ return applyDurationEffects(note, tempo, realDuration); } int noteCount = voice.countNotes(); for (int n = 0; n < noteCount; n++) { TGNote nextNote = voice.getNote( n ); if (!nextNote.equals(note) || mIndex != m ) { if (nextNote.getString() == note.getString()) { if (nextNote.isTiedNote()) { realDuration += (mh.getMove() + beat.getStart() - lastEnd) + (nextNote.getVoice().getDuration().getTime()); lastEnd = (mh.getMove() + beat.getStart() + voice.getDuration().getTime()); letRing = (nextNote.getEffect().isLetRing()); letRingBeatChanged = true; } else { return applyDurationEffects(note, tempo, realDuration); } } } } if(letRing && !letRingBeatChanged){ realDuration += ( voice.getDuration().getTime() ); } letRingBeatChanged = false; } } nextBIndex = 0; } return applyDurationEffects(note, tempo, realDuration); } private long applyDurationEffects(TGNote note, TGTempo tempo, long duration){ //dead note if(note.getEffect().isDeadNote()){ return applyStaticDuration(tempo, DEFAULT_DURATION_DEAD, duration); } //palm mute if(note.getEffect().isPalmMute()){ return applyStaticDuration(tempo, DEFAULT_DURATION_PM, duration); } //staccato if(note.getEffect().isStaccato()){ return (long)(duration * 50.00 / 100.00); } return duration; } private long applyStaticDuration(TGTempo tempo, long duration, long maximum ){ long value = ( tempo.getValue() * duration / 60 ); return (value < maximum ? value : maximum ); } private int getRealVelocity(MidiSequenceHelper sh, TGNote note, TGTrack tgTrack, TGChannel tgChannel, int mIndex,int bIndex){ int velocity = note.getVelocity(); //Check for Hammer effect if(!tgChannel.isPercussionChannel()){ MidiNoteHelper previousNote = getPreviousNote(sh, note,tgTrack,mIndex,bIndex,false); if(previousNote != null && previousNote.getNote().getEffect().isHammer()){ velocity = Math.max(TGVelocities.MIN_VELOCITY,(velocity - 25)); } } //Check for GhostNote effect if(note.getEffect().isGhostNote()){ velocity = Math.max(TGVelocities.MIN_VELOCITY,(velocity - TGVelocities.VELOCITY_INCREMENT)); }else if(note.getEffect().isAccentuatedNote()){ velocity = Math.max(TGVelocities.MIN_VELOCITY,(velocity + TGVelocities.VELOCITY_INCREMENT)); }else if(note.getEffect().isHeavyAccentuatedNote()){ velocity = Math.max(TGVelocities.MIN_VELOCITY,(velocity + (TGVelocities.VELOCITY_INCREMENT * 2))); } return ((velocity > 127)?127:velocity); } public void addMetronome(MidiSequenceHelper sh,TGMeasureHeader header, long startMove){ if( (this.flags & ADD_METRONOME) != 0) { if( this.metronomeChannelId >= 0 ){ long start = (startMove + header.getStart()); long length = header.getTimeSignature().getDenominator().getTime(); for(int i = 1; i <= header.getTimeSignature().getNumerator();i ++){ makeNote(sh,getMetronomeTrack(),DEFAULT_METRONOME_KEY,start,length,TGVelocities.DEFAULT,this.metronomeChannelId,-1,false); start += length; } } } } public void addDefaultMessages(MidiSequenceHelper sh, TGSong tgSong) { if( (this.flags & ADD_DEFAULT_CONTROLS) != 0) { Iterator it = tgSong.getChannels(); while ( it.hasNext() ){ int channelId = ((TGChannel) it.next()).getChannelId(); sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),getInfoTrack(),channelId,MidiControllers.RPN_MSB,0); sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),getInfoTrack(),channelId,MidiControllers.RPN_LSB,0); sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),getInfoTrack(),channelId,MidiControllers.DATA_ENTRY_MSB,12); sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),getInfoTrack(),channelId,MidiControllers.DATA_ENTRY_LSB, 0); } } } private void addBend(MidiSequenceHelper sh,int track, long tick,int bend, int channel, int midiVoice, boolean bendMode) { sh.getSequence().addPitchBend(getTick(tick),track,channel,fix(bend), midiVoice, bendMode); } public void makeVibrato(MidiSequenceHelper sh,int track,long start, long duration,int channel, int midiVoice, boolean bendMode){ long nextStart = start; long end = nextStart + duration; while(nextStart < end){ nextStart = ((nextStart + 160 > end)?end:nextStart + 160); addBend(sh, track, nextStart, DEFAULT_BEND, channel, midiVoice, bendMode); nextStart = ((nextStart + 160 > end)?end:nextStart + 160); addBend(sh, track, nextStart, DEFAULT_BEND + (int)(DEFAULT_BEND_SEMI_TONE / 2.0f), channel, midiVoice, bendMode); } addBend(sh, track, nextStart, DEFAULT_BEND, channel, midiVoice, bendMode); } public void makeBend(MidiSequenceHelper sh,int track,long start, long duration, TGEffectBend bend, int channel, int midiVoice, boolean bendMode){ List points = bend.getPoints(); for(int i=0;i<points.size();i++){ TGEffectBend.BendPoint point = (TGEffectBend.BendPoint)points.get(i); long bendStart = start + point.getTime(duration); int value = DEFAULT_BEND + (int)(point.getValue() * DEFAULT_BEND_SEMI_TONE / TGEffectBend.SEMITONE_LENGTH); value = ((value <= 127)?value:127); value = ((value >= 0)?value:0); addBend(sh, track, bendStart, value, channel, midiVoice, bendMode); if(points.size() > i + 1){ TGEffectBend.BendPoint nextPoint = (TGEffectBend.BendPoint)points.get(i + 1); int nextValue = DEFAULT_BEND + (int)(nextPoint.getValue() * DEFAULT_BEND_SEMI_TONE / TGEffectBend.SEMITONE_LENGTH); long nextBendStart = start + nextPoint.getTime(duration); if(nextValue != value){ double width = ( (nextBendStart - bendStart) / Math.abs( (nextValue - value) ) ); //ascendente if(value < nextValue){ while(value < nextValue){ value ++; bendStart +=width; addBend(sh, track, bendStart,((value <= 127) ? value : 127), channel, midiVoice, bendMode); } //descendente }else if(value > nextValue){ while(value > nextValue){ value --; bendStart +=width; addBend(sh, track, bendStart,((value >= 0) ? value : 0), channel, midiVoice, bendMode); } } } } } addBend(sh, track, start + duration, DEFAULT_BEND, channel, midiVoice, bendMode); } public void makeTremoloBar(MidiSequenceHelper sh,int track,long start, long duration, TGEffectTremoloBar effect, int channel, int midiVoice, boolean bendMode){ List points = effect.getPoints(); for(int i=0;i<points.size();i++){ TGEffectTremoloBar.TremoloBarPoint point = (TGEffectTremoloBar.TremoloBarPoint)points.get(i); long pointStart = start + point.getTime(duration); int value = DEFAULT_BEND + (int)(point.getValue() * (DEFAULT_BEND_SEMI_TONE * 2) ); value = ((value <= 127)?value:127); value = ((value >= 0)?value:0); addBend(sh, track, pointStart, value, channel, midiVoice, bendMode); if(points.size() > i + 1){ TGEffectTremoloBar.TremoloBarPoint nextPoint = (TGEffectTremoloBar.TremoloBarPoint)points.get(i + 1); int nextValue = DEFAULT_BEND + (int)(nextPoint.getValue() * (DEFAULT_BEND_SEMI_TONE * 2)); long nextPointStart = start + nextPoint.getTime(duration); if(nextValue != value){ double width = ( (nextPointStart - pointStart) / Math.abs( (nextValue - value) ) ); //ascendente if(value < nextValue){ while(value < nextValue){ value ++; pointStart +=width; addBend(sh, track, pointStart,((value <= 127) ? value : 127), channel, midiVoice, bendMode); } //descendente }else if(value > nextValue){ while(value > nextValue){ value --; pointStart += width; addBend(sh, track, pointStart,((value >= 0) ? value : 0), channel, midiVoice, bendMode); } } } } } addBend(sh, track, start + duration, DEFAULT_BEND, channel, midiVoice, bendMode); } private void makeSlide(MidiSequenceHelper sh, TGNote note,TGTrack track, int mIndex, int bIndex, long startMove,int channel,int midiVoice,boolean bendMode){ MidiNoteHelper nextNote = this.getNextNote(sh, note, track, mIndex, bIndex, true); if( nextNote != null ){ int value1 = note.getValue(); int value2 = nextNote.getNote().getValue(); long tick1 = note.getVoice().getBeat().getStart() + startMove; long tick2 = nextNote.getNote().getVoice().getBeat().getStart() + nextNote.getMeasure().getMove(); // Make the Slide this.makeSlide(sh, track.getNumber(), tick1, value1, tick2, value2, channel, midiVoice, bendMode); // Normalize the Bend this.addBend(sh,track.getNumber(), tick2 ,DEFAULT_BEND, channel, midiVoice, bendMode); } } public void makeSlide(MidiSequenceHelper sh,int track,long tick1,int value1,long tick2,int value2,int channel, int midiVoice, boolean bendMode){ long distance = (value2 - value1); long length = (tick2 - tick1); int points = (int)(length / (TGDuration.QUARTER_TIME / 8)); for(int i = 1;i <= points; i ++){ float tone = ((((length / points) * (float)i) * distance) / length); int bend = (DEFAULT_BEND + (int)(tone * (DEFAULT_BEND_SEMI_TONE * 2))); addBend(sh, track, tick1 + ( (length / points) * i), bend, channel, midiVoice, bendMode); } } private void makeFadeIn(MidiSequenceHelper sh,int track,long start,long duration,int volume3,int channel){ int expression = 31; int expressionIncrement = 1; long tick = start; long tickIncrement = (duration / ((127 - expression) / expressionIncrement)); while( tick < (start + duration) && expression < 127 ) { sh.getSequence().addControlChange(getTick(tick),track,channel,MidiControllers.EXPRESSION, fix(expression)); tick += tickIncrement; expression += expressionIncrement; } sh.getSequence().addControlChange(getTick((start + duration)),track,channel, MidiControllers.EXPRESSION, 127); } private int[] getStroke(TGBeat beat, TGBeat previous, int[] stroke){ int direction = beat.getStroke().getDirection(); if( previous == null || !(direction == TGStroke.STROKE_NONE && previous.getStroke().getDirection() == TGStroke.STROKE_NONE)){ if( direction == TGStroke.STROKE_NONE ){ for( int i = 0 ; i < stroke.length ; i ++ ){ stroke[ i ] = 0; } }else{ int stringUseds = 0; int stringCount = 0; for( int vIndex = 0; vIndex < beat.countVoices(); vIndex ++ ){ TGVoice voice = beat.getVoice(vIndex); for (int nIndex = 0; nIndex < voice.countNotes(); nIndex++) { TGNote note = voice.getNote(nIndex); if( !note.isTiedNote() ){ stringUseds |= 0x01 << ( note.getString() - 1 ); stringCount ++; } } } if( stringCount > 0 ){ int strokeMove = 0; int strokeIncrement = beat.getStroke().getIncrementTime(beat); for( int i = 0 ; i < stroke.length ; i ++ ){ int index = ( direction == TGStroke.STROKE_DOWN ? (stroke.length - 1) - i : i ); if( (stringUseds & ( 0x01 << index ) ) != 0 ){ stroke[ index ] = strokeMove; strokeMove += strokeIncrement; } } } } } return stroke; } private long applyStrokeStart( TGNote note, long start , int[] stroke){ return (start + stroke[ note.getString() - 1 ]); } private long applyStrokeDuration( TGNote note, long duration , int[] stroke){ return (duration > stroke[note.getString() - 1] ? (duration - stroke[ note.getString() - 1 ]) : duration ); } private MidiTickHelper checkTripletFeel(TGVoice voice,int bIndex){ long bStart = voice.getBeat().getStart(); long bDuration = voice.getDuration().getTime(); if(voice.getBeat().getMeasure().getTripletFeel() == TGMeasureHeader.TRIPLET_FEEL_EIGHTH){ if(voice.getDuration().isEqual(newDuration(TGDuration.EIGHTH))){ //first time if( (bStart % TGDuration.QUARTER_TIME) == 0){ TGVoice v = getNextBeat(voice,bIndex); if(v == null || ( v.getBeat().getStart() > (bStart + voice.getDuration().getTime()) || v.getDuration().isEqual(newDuration(TGDuration.EIGHTH))) ){ TGDuration duration = newDuration(TGDuration.EIGHTH); duration.getDivision().setEnters(3); duration.getDivision().setTimes(2); bDuration = (duration.getTime() * 2); } } //second time else if( (bStart % (TGDuration.QUARTER_TIME / 2)) == 0){ TGVoice v = getPreviousBeat(voice,bIndex); if(v == null || ( v.getBeat().getStart() < (bStart - voice.getDuration().getTime()) || v.getDuration().isEqual(newDuration(TGDuration.EIGHTH)) )){ TGDuration duration = newDuration(TGDuration.EIGHTH); duration.getDivision().setEnters(3); duration.getDivision().setTimes(2); bStart = ( (bStart - voice.getDuration().getTime()) + (duration.getTime() * 2)); bDuration = duration.getTime(); } } } }else if(voice.getBeat().getMeasure().getTripletFeel() == TGMeasureHeader.TRIPLET_FEEL_SIXTEENTH){ if(voice.getDuration().isEqual(newDuration(TGDuration.SIXTEENTH))){ //first time if( (bStart % (TGDuration.QUARTER_TIME / 2)) == 0){ TGVoice v = getNextBeat(voice,bIndex); if(v == null || ( v.getBeat().getStart() > (bStart + voice.getDuration().getTime()) || v.getDuration().isEqual(newDuration(TGDuration.SIXTEENTH))) ){ TGDuration duration = newDuration(TGDuration.SIXTEENTH); duration.getDivision().setEnters(3); duration.getDivision().setTimes(2); bDuration = (duration.getTime() * 2); } } //second time else if( (bStart % (TGDuration.QUARTER_TIME / 4)) == 0){ TGVoice v = getPreviousBeat(voice,bIndex); if(v == null || ( v.getBeat().getStart() < (bStart - voice.getDuration().getTime()) || v.getDuration().isEqual(newDuration(TGDuration.SIXTEENTH)) )){ TGDuration duration = newDuration(TGDuration.SIXTEENTH); duration.getDivision().setEnters(3); duration.getDivision().setTimes(2); bStart = ( (bStart - voice.getDuration().getTime()) + (duration.getTime() * 2)); bDuration = duration.getTime(); } } } } return new MidiTickHelper(bStart, bDuration); } private TGDuration newDuration(int value){ TGDuration duration = this.manager.getFactory().newDuration(); duration.setValue(value); return duration; } private TGVoice getPreviousBeat(TGVoice beat,int bIndex){ TGVoice previous = null; for (int b = bIndex - 1; b >= 0; b--) { TGBeat current = beat.getBeat().getMeasure().getBeat( b ); if(current.getStart() < beat.getBeat().getStart() && !current.getVoice(beat.getIndex()).isEmpty()){ if(previous == null || current.getStart() > previous.getBeat().getStart()){ previous = current.getVoice(beat.getIndex()); } } } return previous; } private TGVoice getNextBeat(TGVoice beat,int bIndex){ TGVoice next = null; for (int b = bIndex + 1; b < beat.getBeat().getMeasure().countBeats(); b++) { TGBeat current = beat.getBeat().getMeasure().getBeat( b ); if(current.getStart() > beat.getBeat().getStart() && !current.getVoice(beat.getIndex()).isEmpty()){ if(next == null || current.getStart() < next.getBeat().getStart()){ next = current.getVoice(beat.getIndex()); } } } return next; } private MidiNoteHelper getNextNote(MidiSequenceHelper sh, TGNote note,TGTrack track, int mIndex, int bIndex, boolean breakAtRest){ int nextBIndex = (bIndex + 1); int measureCount = sh.getMeasureHelpers().size(); for (int m = mIndex; m < measureCount; m++) { MidiMeasureHelper mh = sh.getMeasureHelper( m ); TGMeasure measure = track.getMeasure( mh.getIndex() ); int beatCount = measure.countBeats(); for (int b = nextBIndex; b < beatCount; b++) { TGBeat beat = measure.getBeat( b ); TGVoice voice = beat.getVoice( note.getVoice().getIndex() ); if( !voice.isEmpty() ){ int noteCount = voice.countNotes(); for (int n = 0; n < noteCount; n++) { TGNote nextNote = voice.getNote( n ); if(nextNote.getString() == note.getString()){ return new MidiNoteHelper(mh,nextNote); } } if( breakAtRest ){ return null; } } } nextBIndex = 0; } return null; } private MidiNoteHelper getPreviousNote(MidiSequenceHelper pHelper, TGNote note,TGTrack track, int mIndex, int bIndex, boolean breakAtRest){ int nextBIndex = bIndex; for (int m = mIndex; m >= 0; m--) { MidiMeasureHelper mh = pHelper.getMeasureHelper( m ); TGMeasure measure = track.getMeasure( mh.getIndex() ); if( this.sHeader == -1 || this.sHeader <= measure.getNumber() ){ nextBIndex = (nextBIndex < 0 ? measure.countBeats() : nextBIndex); for (int b = (nextBIndex - 1); b >= 0; b--) { TGBeat beat = measure.getBeat( b ); TGVoice voice = beat.getVoice( note.getVoice().getIndex() ); if( !voice.isEmpty() ){ int noteCount = voice.countNotes(); for (int n = 0; n < noteCount; n ++) { TGNote current = voice.getNote( n ); if(current.getString() == note.getString()){ return new MidiNoteHelper(mh,current); } } if( breakAtRest ){ return null; } } } } nextBIndex = -1; } return null; } private class MidiTickHelper{ private long start; private long duration; public MidiTickHelper(long start,long duration){ this.start = start; this.duration = duration; } public long getDuration() { return this.duration; } public long getStart() { return this.start; } } private class MidiNoteHelper { private MidiMeasureHelper measure; private TGNote note; public MidiNoteHelper(MidiMeasureHelper measure, TGNote note){ this.measure = measure; this.note = note; } public MidiMeasureHelper getMeasure() { return this.measure; } public TGNote getNote() { return this.note; } } private class MidiMeasureHelper { private int index; private long move; public MidiMeasureHelper(int index,long move){ this.index = index; this.move = move; } public int getIndex() { return this.index; } public long getMove() { return this.move; } } private class MidiSequenceHelper { private List measureHeaderHelpers; private MidiSequenceHandler sequence; public MidiSequenceHelper(MidiSequenceHandler sequence){ this.sequence = sequence; this.measureHeaderHelpers = new ArrayList(); } public MidiSequenceHandler getSequence(){ return this.sequence; } public void addMeasureHelper( MidiMeasureHelper helper ){ this.measureHeaderHelpers.add( helper ); } public List getMeasureHelpers(){ return this.measureHeaderHelpers; } public MidiMeasureHelper getMeasureHelper( int index ){ return (MidiMeasureHelper)this.measureHeaderHelpers.get( index ); } } }