/*
* 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.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.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 fisrtTickMove;
private int tempoPercent;
private int transpose;
public MidiSequenceParser(TGSongManager manager,int flags,int tempoPercent,int transpose) {
this.manager = manager;
this.flags = flags;
this.transpose = transpose;
this.tempoPercent = tempoPercent;
this.fisrtTickMove = (int)(((flags & ADD_FIRST_TICK_MOVE) != 0)?(-TGDuration.QUARTER_TIME):0);
}
public MidiSequenceParser(TGSongManager manager,int flags) {
this(manager,flags,100,0);
}
/**
* Crea la cancion
*/
public void parse(MidiSequenceHandler sequence) {
this.infoTrack = sequence.getInfoTrack();
this.metronomeTrack = sequence.getMetronomeTrack();
addDefaultMessages(sequence);
for (int i = 0; i < this.manager.getSong().countTracks(); i++) {
TGTrack songTrack = this.manager.getSong().getTrack(i);
createTrack(sequence,songTrack);
}
sequence.notifyFinish();
}
private int infoIndex(){
return this.infoTrack;
}
private int metronomeIndex(){
return this.metronomeTrack;
}
private long getTick(long tick){
return (tick + this.fisrtTickMove);
}
/**
* Crea las pistas de la cancion
*/
private void createTrack(MidiSequenceHandler sequence,TGTrack track) {
TGMeasure previous = null;
MidiRepeatController controller = new MidiRepeatController(track.getSong());
addBend(sequence,track.getNumber(),TGDuration.QUARTER_TIME,DEFAULT_BEND,track.getChannel().getChannel());
makeChannel(sequence, track.getChannel(), track.getNumber());
while(!controller.finished()){
TGMeasure measure = track.getMeasure(controller.getIndex());
int index = controller.getIndex();
long move = controller.getRepeatMove();
controller.process();
if(controller.shouldPlay()){
if(track.getNumber() == 1){
addTimeSignature(sequence,measure, previous,move);
addTempo(sequence,measure, previous,move);
addMetronome(sequence,measure.getHeader(),move);
}
//agrego los pulsos
makeBeats(sequence, track, measure,index, move);
}
previous = measure;
}
}
private void makeBeats(MidiSequenceHandler sequence, TGTrack track, TGMeasure measure, int measureIdx, 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(sequence, track, beat, measure.getTempo(), measureIdx, bIndex, startMove, getStroke(beat, previous, stroke) );
previous = beat;
}
}
/**
* Crea las notas del compas
*/
private void makeNotes(MidiSequenceHandler sequence, TGTrack track, TGBeat beat, TGTempo tempo, int measureIdx,int bIndex, long startMove, int[] stroke) {
int trackId = track.getNumber();
for( int vIndex = 0; vIndex < beat.countVoices(); vIndex ++ ){
TGVoice voice = beat.getVoice(vIndex);
BeatData data = 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 = data.getStart() + startMove;
//long duration = getRealNoteDuration(note,data.getDuration(), songTrack, measureIdx,bIndex);
long start = applyStrokeStart(note, (data.getStart() + startMove) , stroke);
long duration = applyStrokeDuration(note, getRealNoteDuration(track, note, tempo, data.getDuration(), measureIdx,bIndex), stroke);
int velocity = getRealVelocity(note, track, measureIdx, bIndex);
int channel = track.getChannel().getChannel();
int effectChannel = track.getChannel().getEffectChannel();
boolean percussionTrack = track.isPercussionTrack();
//---Fade In---
if(note.getEffect().isFadeIn()){
channel = effectChannel;
makeFadeIn(sequence,trackId, start, duration, track.getChannel().getVolume(), channel);
}
//---Grace---
if(note.getEffect().isGrace() && effectChannel >= 0 && !percussionTrack ){
channel = effectChannel;
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(sequence,trackId, graceKey,start - graceLength,graceDuration,graceVelocity,channel);
}
//---Trill---
if(note.getEffect().isTrill() && effectChannel >= 0 && !percussionTrack ){
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(sequence,trackId,((realKey)?key:trillKey),tick,trillLength,velocity,channel);
realKey = (!realKey);
tick += trillLength;
}
continue;
}
//---Tremolo Picking---
if(note.getEffect().isTremoloPicking() && effectChannel >= 0){
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(sequence,trackId,key,tick,tpLength,velocity,channel);
tick += tpLength;
}
continue;
}
//---Bend---
if(note.getEffect().isBend() && effectChannel >= 0 && !percussionTrack ){
channel = effectChannel;
makeBend(sequence,trackId,start,duration,note.getEffect().getBend(),channel);
}
//---TremoloBar---
else if(note.getEffect().isTremoloBar() && effectChannel >= 0 && !percussionTrack ){
channel = effectChannel;
makeTremoloBar(sequence,trackId,start,duration,note.getEffect().getTremoloBar(),channel);
}
//---Slide---
else if(note.getEffect().isSlide() && effectChannel >= 0 && !percussionTrack){
channel = effectChannel;
TGNote nextNote = getNextNote(note,track,measureIdx,bIndex);
makeSlide(sequence,trackId,note,nextNote,startMove,channel);
}
//---Vibrato---
else if(note.getEffect().isVibrato() && effectChannel >= 0 && !percussionTrack){
channel = effectChannel;
makeVibrato(sequence,trackId,start,duration,channel);
}
//---Harmonic---
if(note.getEffect().isHarmonic() && !percussionTrack){
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() && !percussionTrack){
makeNote(sequence,trackId,Math.min(127,orig), start, duration,Math.max(TGVelocities.MIN_VELOCITY,velocity - (TGVelocities.VELOCITY_INCREMENT * 3)),channel);
}
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(sequence,trackId,(key - 12), start, duration,hVelocity,channel);
}
}
//---Normal Note---
makeNote(sequence,trackId, Math.min(127,key), start, duration, velocity,channel);
}
}
}
}
/**
* Crea una nota en la posicion start
*/
private void makeNote(MidiSequenceHandler sequence,int track, int key, long start, long duration, int velocity, int channel) {
sequence.addNoteOn(getTick(start),track,channel,key,velocity);
sequence.addNoteOff(getTick(start + duration),track,channel,key,velocity);
}
private void makeChannel(MidiSequenceHandler sequence,TGChannel channel,int track) {
if( (this.flags & ADD_MIXER_MESSAGES) != 0){
makeChannel(sequence, channel, track,true);
if(channel.getChannel() != channel.getEffectChannel()){
makeChannel(sequence, channel, track,false);
}
}
}
private void makeChannel(MidiSequenceHandler sequence,TGChannel channel,int track,boolean primary) {
int number = (primary?channel.getChannel():channel.getEffectChannel());
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),track,number,MidiControllers.VOLUME,channel.getVolume());
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),track,number,MidiControllers.BALANCE,channel.getBalance());
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),track,number,MidiControllers.CHORUS,channel.getChorus());
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),track,number,MidiControllers.REVERB,channel.getReverb());
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),track,number,MidiControllers.PHASER,channel.getPhaser());
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),track,number,MidiControllers.TREMOLO,channel.getTremolo());
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),track,number,MidiControllers.EXPRESSION, 127);
sequence.addProgramChange(getTick(TGDuration.QUARTER_TIME),track,number,channel.getInstrument());
}
/**
* Agrega un Time Signature si es distinto al anterior
*/
private void addTimeSignature(MidiSequenceHandler sequence,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) {
sequence.addTimeSignature(getTick(currMeasure.getStart() + startMove), infoIndex(), currMeasure.getTimeSignature());
}
}
/**
* Agrega un Tempo si es distinto al anterior
*/
private void addTempo(MidiSequenceHandler sequence,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 );
sequence.addTempoInUSQ(getTick(currMeasure.getStart() + startMove), infoIndex(), usq);
}
}
/**
* Retorna la Duracion real de una nota, verificando si tiene otras ligadas
*/
private long getRealNoteDuration(TGTrack track, TGNote note, TGTempo tempo, long duration,int mIndex, int bIndex) {
long lastEnd = (note.getVoice().getBeat().getStart() + note.getVoice().getDuration().getTime());
long realDuration = duration;
int nextBIndex = (bIndex + 1);
int measureCount = track.countMeasures();
for (int m = mIndex; m < measureCount; m++) {
TGMeasure measure = track.getMeasure( m );
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.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)) {
if (nextNote.getString() == note.getString()) {
if (nextNote.isTiedNote()) {
realDuration += (beat.getStart() - lastEnd) + (nextNote.getVoice().getDuration().getTime());
lastEnd = (beat.getStart() + voice.getDuration().getTime());
} else {
return applyDurationEffects(note, tempo, realDuration);
}
}
}
}
}
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(TGNote note, TGTrack songTrack, int mIndex,int bIndex){
int velocity = note.getVelocity();
//Check for Hammer effect
if(!songTrack.isPercussionTrack()){
TGNote prevNote = getPreviousNote(note,songTrack,mIndex,bIndex);
if(prevNote != null && prevNote.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(MidiSequenceHandler sequence,TGMeasureHeader header, long startMove){
if( (this.flags & ADD_METRONOME) != 0) {
long start = (startMove + header.getStart());
long length = header.getTimeSignature().getDenominator().getTime();
for(int i = 1; i <= header.getTimeSignature().getNumerator();i ++){
makeNote(sequence,metronomeIndex(),DEFAULT_METRONOME_KEY,start,length,TGVelocities.DEFAULT,9);
start += length;
}
}
}
public void addDefaultMessages(MidiSequenceHandler sequence) {
if( (this.flags & ADD_DEFAULT_CONTROLS) != 0) {
for(int i = 0; i < 16; i ++){
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),infoIndex(),i,MidiControllers.RPN_MSB,0);
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),infoIndex(),i,MidiControllers.RPN_LSB,0);
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),infoIndex(),i,MidiControllers.DATA_ENTRY_MSB,12);
sequence.addControlChange(getTick(TGDuration.QUARTER_TIME),infoIndex(),i,MidiControllers.DATA_ENTRY_LSB, 0);
}
}
}
private void addBend(MidiSequenceHandler sequence,int track, long tick,int bend, int channel) {
sequence.addPitchBend(getTick(tick),track,channel,bend);
}
public void makeVibrato(MidiSequenceHandler sequence,int track,long start, long duration,int channel){
long nextStart = start;
long end = nextStart + duration;
while(nextStart < end){
nextStart = ((nextStart + 160 > end)?end:nextStart + 160);
addBend(sequence,track,nextStart,DEFAULT_BEND,channel);
nextStart = ((nextStart + 160 > end)?end:nextStart + 160);
addBend(sequence,track,nextStart,DEFAULT_BEND + (int)(DEFAULT_BEND_SEMI_TONE / 2.0f),channel);
}
addBend(sequence,track,nextStart,DEFAULT_BEND,channel);
}
public void makeBend(MidiSequenceHandler sequence,int track,long start, long duration, TGEffectBend bend, int channel){
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(sequence,track,bendStart,value,channel);
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(sequence,track,bendStart,((value <= 127)?value:127),channel);
}
//descendente
}else if(value > nextValue){
while(value > nextValue){
value --;
bendStart +=width;
addBend(sequence,track,bendStart,((value >= 0)?value:0),channel);
}
}
}
}
}
addBend(sequence,track,start + duration,DEFAULT_BEND,channel);
}
public void makeTremoloBar(MidiSequenceHandler sequence,int track,long start, long duration, TGEffectTremoloBar effect, int channel){
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(sequence,track,pointStart,value,channel);
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(sequence,track,pointStart,((value <= 127)?value:127),channel);
}
//descendente
}else if(value > nextValue){
while(value > nextValue){
value --;
pointStart += width;
addBend(sequence,track,pointStart,((value >= 0)?value:0),channel);
}
}
}
}
}
addBend(sequence,track,start + duration,DEFAULT_BEND,channel);
}
public void makeSlide(MidiSequenceHandler sequence,int track,TGNote note,TGNote nextNote,long startMove,int channel){
if(nextNote != null){
makeSlide(sequence,track,note.getVoice().getBeat().getStart()+startMove,note.getValue(),nextNote.getVoice().getBeat().getStart() + startMove,nextNote.getValue(),channel);
addBend(sequence,track,nextNote.getVoice().getBeat().getStart() + startMove,DEFAULT_BEND,channel);
}
}
public void makeSlide(MidiSequenceHandler sequence,int track,long tick1,int value1,long tick2,int value2,int channel){
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(sequence,track,tick1 + ( (length / points) * i),bend,channel);
}
}
private void makeFadeIn(MidiSequenceHandler sequence,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 ) {
sequence.addControlChange(getTick(tick),track,channel,MidiControllers.EXPRESSION, expression);
tick += tickIncrement;
expression += expressionIncrement;
}
sequence.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 ]);
}
private BeatData 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 BeatData(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 TGNote getNextNote(TGNote note,TGTrack track, int mIndex, int bIndex){
int nextBIndex = (bIndex + 1);
int measureCount = track.countMeasures();
for (int m = mIndex; m < measureCount; m++) {
TGMeasure measure = track.getMeasure( m );
int beatCount = measure.countBeats();
for (int b = nextBIndex; b < beatCount; b++) {
TGBeat beat = measure.getBeat( b );
TGVoice voice = beat.getVoice( note.getVoice().getIndex() );
int noteCount = voice.countNotes();
for (int n = 0; n < noteCount; n++) {
TGNote currNote = voice.getNote( n );
if(currNote.getString() == note.getString()){
return currNote;
}
}
return null;
}
nextBIndex = 0;
}
return null;
}
private TGNote getPreviousNote(TGNote note,TGTrack track, int mIndex, int bIndex){
int nextBIndex = bIndex;
for (int m = mIndex; m >= 0; m--) {
TGMeasure measure = track.getMeasure( m );
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() );
int noteCount = voice.countNotes();
for (int n = 0; n < noteCount; n ++) {
TGNote current = voice.getNote( n );
if(current.getString() == note.getString()){
return current;
}
}
}
nextBIndex = -1;
}
return null;
}
private class BeatData{
private long start;
private long duration;
public BeatData(long start,long duration){
this.start = start;
this.duration = duration;
}
public long getDuration() {
return this.duration;
}
public long getStart() {
return this.start;
}
}
}