/*
* 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.List;
import org.herac.tuxguitar.song.managers.TGSongManager;
import org.herac.tuxguitar.song.models.Direction;
import org.herac.tuxguitar.song.models.TGBeat;
import org.herac.tuxguitar.song.models.TGChannel;
import org.herac.tuxguitar.song.models.TGDivisionType;
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.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.BendingEffect;
import org.herac.tuxguitar.song.models.effects.EffectPoint;
import org.herac.tuxguitar.song.models.effects.HarmonicEffect;
/**
* @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 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 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 MidiSequenceHelper {
private List<MidiMeasureHelper> measureHeaderHelpers;
private MidiSequenceHandler sequence;
public MidiSequenceHelper(MidiSequenceHandler sequence) {
this.sequence = sequence;
this.measureHeaderHelpers = new ArrayList<MidiMeasureHelper>();
}
public void addMeasureHelper(MidiMeasureHelper helper) {
this.measureHeaderHelpers.add(helper);
}
public MidiMeasureHelper getMeasureHelper(int index) {
return (MidiMeasureHelper) this.measureHeaderHelpers.get(index);
}
public List<MidiMeasureHelper> getMeasureHelpers() {
return this.measureHeaderHelpers;
}
public MidiSequenceHandler getSequence() {
return this.sequence;
}
}
private class MidiTickHelper {
private long duration;
private long start;
public MidiTickHelper(long start, long duration) {
this.start = start;
this.duration = duration;
}
public long getDuration() {
return this.duration;
}
public long getStart() {
return this.start;
}
}
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
private static final int DEFAULT_BEND = 64;
private static final float DEFAULT_BEND_SEMI_TONE = 2.75f;
private static final int DEFAULT_DURATION_DEAD = 30;
private static final int DEFAULT_DURATION_PM = 60;
public static final int DEFAULT_EXPORT_FLAGS = (ADD_FIRST_TICK_MOVE
| ADD_DEFAULT_CONTROLS | ADD_MIXER_MESSAGES);
private static final int DEFAULT_METRONOME_KEY = 37;
public static final int DEFAULT_PLAY_FLAGS = (ADD_METRONOME);
private int eHeader;
private int firstTickMove;
/**
* flags
*/
private int flags;
/**
* Index of info track
*/
private int infoTrack;
/**
* Song Manager
*/
private TGSongManager manager;
/**
* Index of metronome track
*/
private int metronomeTrack;
private int sHeader;
private int tempoPercent;
private int transpose;
public MidiSequenceParser(TGSongManager manager, int flags) {
this(manager, flags, 100, 0);
}
public MidiSequenceParser(TGSongManager manager, int flags, int tempoPercent,
int transpose) {
this.manager = manager;
this.flags = flags;
this.transpose = transpose;
this.tempoPercent = tempoPercent;
this.firstTickMove = (int) (((flags & ADD_FIRST_TICK_MOVE) != 0) ? (-TGDuration.QUARTER_TIME)
: 0);
this.sHeader = -1;
this.eHeader = -1;
}
private void addBend(MidiSequenceHelper sh, int track, long tick, int bend,
int channel) {
sh.getSequence().addPitchBend(getTick(tick), track, channel, fix(bend));
}
public void addDefaultMessages(MidiSequenceHelper sh) {
if ((this.flags & ADD_DEFAULT_CONTROLS) != 0) {
for (int i = 0; i < 16; i++) {
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),
getInfoTrack(), i, MidiControllers.RPN_MSB, 0);
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),
getInfoTrack(), i, MidiControllers.RPN_LSB, 0);
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),
getInfoTrack(), i, MidiControllers.DATA_ENTRY_MSB, 12);
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME),
getInfoTrack(), i, MidiControllers.DATA_ENTRY_LSB, 0);
}
}
}
public void addMetronome(MidiSequenceHelper sh, 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(sh, getMetronomeTrack(), DEFAULT_METRONOME_KEY, start, length,
TGVelocities.DEFAULT, 9);
start += length;
}
}
}
/**
* 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);
}
}
/**
* 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());
}
}
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 long applyStrokeDuration(TGNote note, long duration, int[] stroke) {
return (duration > stroke[note.getString() - 1] ? (duration - stroke[note
.getString() - 1]) : duration);
}
private long applyStrokeStart(TGNote note, long start, int[] stroke) {
return (start + stroke[note.getString() - 1]);
}
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.setDivision(TGDivisionType.DEFAULT);
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.setDivision(TGDivisionType.DEFAULT);
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.setDivision(TGDivisionType.DEFAULT);
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.setDivision(TGDivisionType.DEFAULT);
bStart = ((bStart - voice.getDuration().getTime()) + (duration
.getTime() * 2));
bDuration = duration.getTime();
}
}
}
}
return new MidiTickHelper(bStart, bDuration);
}
/**
* Crea las pistas de la cancion
*/
private void createTrack(MidiSequenceHelper sh, TGTrack track) {
TGMeasure previous = null;
this.addBend(sh, track.getNumber(), TGDuration.QUARTER_TIME, DEFAULT_BEND,
track.getChannel().getChannel());
this.makeChannel(sh, track.getChannel(), 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, track, measure, mIndex, mh.getMove());
previous = measure;
}
}
private int fix(int value) {
return (value >= 0 ? value <= 127 ? value : 127 : 0);
}
public int getInfoTrack() {
return this.infoTrack;
}
public int getMetronomeTrack() {
return this.metronomeTrack;
}
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.getNotes().size();
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 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 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.getNotes().size();
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;
}
/**
* 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.getNotes().size();
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 int getRealVelocity(MidiSequenceHelper sh, TGNote note,
TGTrack songTrack, int mIndex, int bIndex) {
int velocity = note.getVelocity();
// Check for Hammer effect
if (!songTrack.isPercussionTrack()) {
MidiNoteHelper previousNote = getPreviousNote(sh, note, songTrack,
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);
}
private int[] getStroke(TGBeat beat, TGBeat previous, int[] stroke) {
Direction direction = beat.getStroke().getDirection();
if (previous == null
|| !(direction.equals(Direction.NONE) && previous.getStroke()
.getDirection().equals(Direction.NONE))) {
if (direction.equals(Direction.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.getNotes().size(); 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.equals(Direction.DOWN) ? (stroke.length - 1)
- i : i);
if ((stringUseds & (0x01 << index)) != 0) {
stroke[index] = strokeMove;
strokeMove += strokeIncrement;
}
}
}
}
}
return stroke;
}
private long getTick(long tick) {
return (tick + this.firstTickMove);
}
private void makeBeats(MidiSequenceHelper sh, 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, track, beat, measure.getTempo(), mIndex, bIndex, startMove,
getStroke(beat, previous, stroke));
previous = beat;
}
}
public void makeBend(MidiSequenceHelper sh, int track, long start,
long duration, BendingEffect bend, int channel) {
final List<EffectPoint> points = bend.getPoints();
for (int i = 0; i < points.size(); i++) {
EffectPoint point = points.get(i);
long bendStart = start + point.getTime(duration);
int value = DEFAULT_BEND
+ (int) (point.getValue() * DEFAULT_BEND_SEMI_TONE / EffectPoint.SEMITONE_LENGTH);
// value between 0 and (2^7)-1
value = Math.max(0, Math.min(value, 127));
addBend(sh, track, bendStart, value, channel);
if (points.size() > i + 1) {
EffectPoint nextPoint = points.get(i + 1);
int nextValue = DEFAULT_BEND
+ (int) (nextPoint.getValue() * DEFAULT_BEND_SEMI_TONE / EffectPoint.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);
}
// descendente
} else if (value > nextValue) {
while (value > nextValue) {
value--;
bendStart += width;
addBend(sh, track, bendStart, ((value >= 0) ? value : 0), channel);
}
}
}
}
}
addBend(sh, track, start + duration, DEFAULT_BEND, channel);
}
private void makeChannel(MidiSequenceHelper sh, TGChannel channel, int track) {
if ((this.flags & ADD_MIXER_MESSAGES) != 0) {
makeChannel(sh, channel, track, true);
if (channel.getChannel() != channel.getEffectChannel()) {
makeChannel(sh, channel, track, false);
}
}
}
private void makeChannel(MidiSequenceHelper sh, TGChannel channel, int track,
boolean primary) {
int number = (primary ? channel.getChannel() : channel.getEffectChannel());
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME), track,
number, MidiControllers.VOLUME, fix(channel.getVolume()));
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME), track,
number, MidiControllers.BALANCE, fix(channel.getBalance()));
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME), track,
number, MidiControllers.CHORUS, fix(channel.getChorus()));
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME), track,
number, MidiControllers.REVERB, fix(channel.getReverb()));
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME), track,
number, MidiControllers.PHASER, fix(channel.getPhaser()));
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME), track,
number, MidiControllers.TREMOLO, fix(channel.getTremolo()));
sh.getSequence().addControlChange(getTick(TGDuration.QUARTER_TIME), track,
number, MidiControllers.EXPRESSION, 127);
sh.getSequence().addProgramChange(getTick(TGDuration.QUARTER_TIME), track,
number, fix(channel.getInstrument()));
}
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);
}
/**
* Crea una nota en la posicion start
*/
private void makeNote(MidiSequenceHelper sh, int track, int key, long start,
long duration, int velocity, int channel) {
sh.getSequence().addNoteOn(getTick(start), track, channel, fix(key),
fix(velocity));
if (duration > 0) {
sh.getSequence().addNoteOff(getTick(start + duration), track, channel,
fix(key), fix(velocity));
}
}
/**
* Crea las notas del compas
*/
private void makeNotes(MidiSequenceHelper sh, 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.getNotes().size(); noteIdx++) {
TGNote note = voice.getNote(noteIdx);
if (!note.isTiedNote()) {
int key = this.transpose + track.getOffset() + note.getValue()
+ 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, mIndex, bIndex);
int channel = track.getChannel().getChannel();
int effectChannel = track.getChannel().getEffectChannel();
boolean percussionTrack = track.isPercussionTrack();
// ---Fade In---
if (note.getEffect().isFadeIn()) {
channel = effectChannel;
makeFadeIn(sh, track.getNumber(), 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(sh, track.getNumber(), 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(sh, track.getNumber(), ((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(sh, track.getNumber(), key, tick, tpLength, velocity,
channel);
tick += tpLength;
}
continue;
}
// ---Bend---
if (note.getEffect().isBend() && effectChannel >= 0
&& !percussionTrack) {
channel = effectChannel;
makeBend(sh, track.getNumber(), start, duration, note.getEffect()
.getBend(), channel);
}
// ---TremoloBar---
else if (note.getEffect().isTremoloBar() && effectChannel >= 0
&& !percussionTrack) {
channel = effectChannel;
makeTremoloBar(sh, track.getNumber(), start, duration, note
.getEffect().getTremoloBar(), channel);
}
// ---Slide---
else if (note.getEffect().isSlide() && effectChannel >= 0
&& !percussionTrack) {
channel = effectChannel;
makeSlide(sh, note, track, mIndex, bIndex, startMove, channel);
}
// ---Vibrato---
else if (note.getEffect().isVibrato() && effectChannel >= 0
&& !percussionTrack) {
channel = effectChannel;
makeVibrato(sh, track.getNumber(), start, duration, channel);
}
// ---Harmonic---
if (note.getEffect().isHarmonic() && !percussionTrack) {
int orig = key;
// Natural
if (note.getEffect().getHarmonic().equals(HarmonicEffect.NATURAL)) {
for (int i = 0; i < HarmonicEffect.NATURAL_FREQUENCIES.length; i++) {
if ((note.getValue() % 12) == (HarmonicEffect.NATURAL_FREQUENCIES[i][0] % 12)) {
key = ((orig + HarmonicEffect.NATURAL_FREQUENCIES[i][1]) - note
.getValue());
break;
}
}
}
// Artifical/Tapped/Pinch/Semi
else {
if (note.getEffect().getHarmonic().equals(HarmonicEffect.SEMI)
&& !percussionTrack) {
makeNote(sh, track.getNumber(), Math.min(127, orig), start,
duration, Math.max(TGVelocities.MIN_VELOCITY, velocity
- (TGVelocities.VELOCITY_INCREMENT * 3)), channel);
}
key = (orig + HarmonicEffect.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);
}
}
// ---Normal Note---
makeNote(sh, track.getNumber(), Math.min(127, key), start, duration,
velocity, channel);
}
}
}
}
public void makeSlide(MidiSequenceHelper sh, 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(sh, track, tick1 + ((length / points) * i), bend, channel);
}
}
private void makeSlide(MidiSequenceHelper sh, TGNote note, TGTrack track,
int mIndex, int bIndex, long startMove, int channel) {
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);
// Normalize the Bend
this.addBend(sh, track.getNumber(), tick2, DEFAULT_BEND, channel);
}
}
public void makeTremoloBar(MidiSequenceHelper sh, int track, long start,
long duration, BendingEffect effect, int channel) {
final List<EffectPoint> points = effect.getPoints();
for (int i = 0; i < points.size(); i++) {
final EffectPoint point = 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);
if (points.size() > i + 1) {
final EffectPoint nextPoint = 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);
}
// descendente
} else if (value > nextValue) {
while (value > nextValue) {
value--;
pointStart += width;
addBend(sh, track, pointStart, ((value >= 0) ? value : 0),
channel);
}
}
}
}
}
addBend(sh, track, start + duration, DEFAULT_BEND, channel);
}
public void makeVibrato(MidiSequenceHelper sh, 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(sh, track, nextStart, DEFAULT_BEND, channel);
nextStart = ((nextStart + 160 > end) ? end : nextStart + 160);
addBend(sh, track, nextStart, DEFAULT_BEND
+ (int) (DEFAULT_BEND_SEMI_TONE / 2.0f), channel);
}
addBend(sh, track, nextStart, DEFAULT_BEND, channel);
}
private TGDuration newDuration(int value) {
TGDuration duration = new TGDuration();
duration.setValue(value);
return duration;
}
/**
* 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);
for (int i = 0; i < this.manager.getSong().countTracks(); i++) {
TGTrack songTrack = this.manager.getSong().getTrack(i);
createTrack(helper, songTrack);
}
sequence.notifyFinish();
}
public void setEHeader(int header) {
this.eHeader = header;
}
public void setSHeader(int header) {
this.sHeader = header;
}
}