private void GenerateVibrato(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel)
        {
            int phaseLength;
            int bendAmplitude;

            switch (note.Vibrato)
            {
            case VibratoType.Slight:
                phaseLength   = _settings.Vibrato.NoteSlightLength;
                bendAmplitude = _settings.Vibrato.NoteSlightAmplitude;
                break;

            case VibratoType.Wide:
                phaseLength   = _settings.Vibrato.NoteWideLength;
                bendAmplitude = _settings.Vibrato.NoteWideAmplitude;
                break;

            default:
                return;
            }

            var track = note.Beat.Voice.Bar.Staff.Track;

            GenerateVibratorWithParams(track, noteStart, noteDuration.NoteOnly, phaseLength, bendAmplitude, channel);
        }
        private void GenerateTremoloPicking(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel)
        {
            var track    = note.Beat.Voice.Bar.Staff.Track;
            var tpLength = note.Beat.TremoloSpeed.Value.ToTicks();
            var tick     = noteStart;
            var end      = noteStart + noteDuration.UntilTieEnd;

            while (tick + 10 < (end))
            {
                // only the rest on last trill play
                if ((tick + tpLength) >= (end))
                {
                    tpLength = (end) - tick;
                }
                _handler.AddNote(track.Index, tick, tpLength, (byte)noteKey, dynamicValue, (byte)channel);
                tick += tpLength;
            }
        }
        private void GenerateTrill(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel)
        {
            var track       = note.Beat.Voice.Bar.Staff.Track;
            var trillKey    = note.StringTuning + note.TrillFret;
            var trillLength = note.TrillSpeed.ToTicks();
            var realKey     = true;
            var tick        = noteStart;
            var end         = noteStart + noteDuration.UntilTieEnd;

            while (tick + 10 < (end))
            {
                // only the rest on last trill play
                if ((tick + trillLength) >= (end))
                {
                    trillLength = (end) - tick;
                }
                _handler.AddNote(track.Index, tick, trillLength, (byte)(realKey ? trillKey : noteKey), dynamicValue, (byte)channel);
                realKey = !realKey;
                tick   += trillLength;
            }
        }
        private void GenerateFadeIn(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue)
        {
            var track        = note.Beat.Voice.Bar.Staff.Track;
            var endVolume    = ToChannelShort(track.PlaybackInfo.Volume);
            var volumeFactor = (float)endVolume / noteDuration.NoteOnly;

            var tickStep = 120;
            int steps    = (noteDuration.NoteOnly / tickStep);

            var endTick = noteStart + noteDuration.NoteOnly;

            for (int i = steps - 1; i >= 0; i--)
            {
                var tick   = endTick - (i * tickStep);
                var volume = (tick - noteStart) * volumeFactor;
                if (i == steps - 1)
                {
                    _handler.AddControlChange(track.Index, noteStart, (byte)track.PlaybackInfo.PrimaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume);
                    _handler.AddControlChange(track.Index, noteStart, (byte)track.PlaybackInfo.SecondaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume);
                }
                _handler.AddControlChange(track.Index, tick, (byte)track.PlaybackInfo.PrimaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume);
                _handler.AddControlChange(track.Index, tick, (byte)track.PlaybackInfo.SecondaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume);
            }
        }
        private void GenerateWhammy(Beat beat, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel)
        {
            FastList <BendPoint> bendPoints = beat.WhammyBarPoints;
            var track = beat.Voice.Bar.Staff.Track;

            double duration = noteDuration.NoteOnly;

            // ensure prebends are slightly before the actual note.
            if (bendPoints[0].Value > 0 && !beat.IsContinuedWhammy)
            {
                noteStart--;
            }

            FastList <BendPoint> playedBendPoints = new FastList <BendPoint>();

            switch (beat.WhammyBarType)
            {
            case WhammyType.Custom:
                playedBendPoints = bendPoints;
                break;

            case WhammyType.Dive:
                switch (beat.WhammyStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, bendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, bendPoints[1].Value));
                    break;

                case BendStyle.Fast:
                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 false, new[] { bendPoints[0].Value, bendPoints[1].Value });
                    return;
                }

                break;

            case WhammyType.Dip:
                switch (beat.WhammyStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, bendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, bendPoints[1].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, bendPoints[2].Value));
                    break;

                case BendStyle.Fast:
                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 true, new[] { bendPoints[0].Value, bendPoints[1].Value, bendPoints[2].Value });
                    return;
                }

                break;

            case WhammyType.Hold:
                playedBendPoints = bendPoints;
                break;

            case WhammyType.Predive:
                playedBendPoints = bendPoints;
                break;

            case WhammyType.PrediveDive:
                switch (beat.WhammyStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, bendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, bendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, bendPoints[1].Value));
                    break;

                case BendStyle.Fast:
                    var preDiveValue = DefaultBend + (bendPoints[0].Value * DefaultBendSemitone);
                    _handler.AddBend(track.Index, noteStart, (byte)channel, (byte)preDiveValue);

                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 false, new[] { bendPoints[0].Value, bendPoints[1].Value });
                    return;
                }

                break;
            }

            GenerateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track);
        }
        private void GenerateBend(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel)
        {
            FastList <BendPoint> bendPoints = note.BendPoints;
            var track = note.Beat.Voice.Bar.Staff.Track;

            // Bends are spread across all tied notes unless they have a bend on their own.
            double duration;

            if (note.IsTieOrigin && (_settings == null || _settings.ExtendBendArrowsOnTiedNotes))
            {
                var endNote = note;
                while (endNote.IsTieOrigin && !endNote.TieDestination.HasBend)
                {
                    endNote = endNote.TieDestination;
                }

                duration = endNote.Beat.AbsolutePlaybackStart - note.Beat.AbsolutePlaybackStart + GetNoteDuration(endNote, endNote.Beat.PlaybackDuration).NoteOnly;
            }
            else
            {
                duration = noteDuration.NoteOnly;
            }

            // ensure prebends are slightly before the actual note.
            if (bendPoints[0].Value > 0 && !note.IsContinuedBend)
            {
                noteStart--;
            }

            FastList <BendPoint> playedBendPoints = new FastList <BendPoint>();

            switch (note.BendType)
            {
            case BendType.Custom:
                playedBendPoints = bendPoints;
                break;

            case BendType.Bend:
            case BendType.Release:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[1].Value));
                    break;

                case BendStyle.Fast:
                    if (note.Beat.GraceType == GraceType.BendGrace)
                    {
                        GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                     true, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value });
                    }
                    else
                    {
                        GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                     false, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value });
                    }
                    return;
                }

                break;

            case BendType.BendRelease:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, note.BendPoints[1].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[2].Value));
                    break;

                case BendStyle.Fast:
                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 false, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value, note.BendPoints[2].Value });
                    return;
                }

                break;

            case BendType.Hold:
                playedBendPoints = bendPoints;
                break;

            case BendType.Prebend:
                playedBendPoints = bendPoints;
                break;

            case BendType.PrebendBend:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[1].Value));
                    break;

                case BendStyle.Fast:
                    var preBendValue = DefaultBend + (note.BendPoints[0].Value * DefaultBendSemitone);
                    _handler.AddBend(track.Index, noteStart, (byte)channel, (byte)preBendValue);

                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 false, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value });
                    return;
                }

                break;

            case BendType.PrebendRelease:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[1].Value));
                    break;

                case BendStyle.Fast:
                    var preBendValue = DefaultBend + (note.BendPoints[0].Value * DefaultBendSemitone);
                    _handler.AddBend(track.Index, noteStart, (byte)channel, (byte)preBendValue);

                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 true, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value });
                    return;
                }

                break;
            }

            GenerateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track);
        }
 private void GenerateSlide(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel)
 {
     // TODO
 }
        private MidiNoteDuration GetNoteDuration(Note note, int duration)
        {
            var durationWithEffects = new MidiNoteDuration();

            durationWithEffects.NoteOnly    = duration;
            durationWithEffects.UntilTieEnd = duration;
            durationWithEffects.LetRingEnd  = duration;

            if (note.IsDead)
            {
                durationWithEffects.NoteOnly    = ApplyStaticDuration(DefaultDurationDead, duration);
                durationWithEffects.UntilTieEnd = durationWithEffects.NoteOnly;
                durationWithEffects.LetRingEnd  = durationWithEffects.NoteOnly;
                return(durationWithEffects);
            }
            if (note.IsPalmMute)
            {
                durationWithEffects.NoteOnly    = ApplyStaticDuration(DefaultDurationPalmMute, duration);
                durationWithEffects.UntilTieEnd = durationWithEffects.NoteOnly;
                durationWithEffects.LetRingEnd  = durationWithEffects.NoteOnly;
                return(durationWithEffects);
            }
            if (note.IsStaccato)
            {
                durationWithEffects.NoteOnly    = (duration / 2);
                durationWithEffects.UntilTieEnd = durationWithEffects.NoteOnly;
                durationWithEffects.LetRingEnd  = durationWithEffects.NoteOnly;
                return(durationWithEffects);
            }

            if (note.IsTieOrigin)
            {
                var endNote = note.TieDestination;

                // for the initial start of the tie calculate absolute duration from start to end note
                if (endNote != null)
                {
                    if (!note.IsTieDestination)
                    {
                        var startTick = note.Beat.AbsolutePlaybackStart;
                        var tieDestinationDuration = GetNoteDuration(endNote, endNote.Beat.PlaybackDuration);
                        var endTick = endNote.Beat.AbsolutePlaybackStart + tieDestinationDuration.UntilTieEnd;
                        durationWithEffects.UntilTieEnd = endTick - startTick;
                    }
                    else
                    {
                        // for continuing ties, take the current duration + the one from the destination
                        // this branch will be entered as part of the recusion of the if branch
                        var tieDestinationDuration = GetNoteDuration(endNote, endNote.Beat.PlaybackDuration);
                        durationWithEffects.UntilTieEnd = duration + tieDestinationDuration.UntilTieEnd;
                    }
                }
            }

            if (note.IsLetRing && _settings.DisplayMode == DisplayMode.GuitarPro)
            {
                // LetRing ends when:
                // - rest
                Beat lastLetRingBeat = note.Beat;
                var  letRingEnd      = 0;
                var  maxDuration     = note.Beat.Voice.Bar.MasterBar.CalculateDuration();
                while (lastLetRingBeat.NextBeat != null)
                {
                    var next = lastLetRingBeat.NextBeat;
                    if (next.IsRest)
                    {
                        break;
                    }

                    // note on the same string
                    if (note.IsStringed && next.HasNoteOnString(note.String))
                    {
                        break;
                    }

                    lastLetRingBeat = lastLetRingBeat.NextBeat;

                    letRingEnd = (lastLetRingBeat.AbsolutePlaybackStart - note.Beat.AbsolutePlaybackStart) +
                                 lastLetRingBeat.PlaybackDuration;
                    if (letRingEnd > maxDuration)
                    {
                        letRingEnd = maxDuration;
                        break;
                    }
                }

                if (lastLetRingBeat == note.Beat)
                {
                    durationWithEffects.LetRingEnd = duration;
                }
                else
                {
                    durationWithEffects.LetRingEnd = letRingEnd;
                }
            }
            else
            {
                durationWithEffects.LetRingEnd = durationWithEffects.UntilTieEnd;
            }
            return(durationWithEffects);
        }
        private void GenerateBend(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel)
        {
            FastList <BendPoint> bendPoints = note.BendPoints;
            var track = note.Beat.Voice.Bar.Staff.Track;

            // if bend is extended on next tied note, we directly bend to the final bend value
            int?finalBendValue = null;

            // Bends are spread across all tied notes unless they have a bend on their own.
            double duration;

            if (note.IsTieOrigin && (_settings == null || _settings.ExtendBendArrowsOnTiedNotes))
            {
                var endNote = note;
                while (endNote.IsTieOrigin && !endNote.TieDestination.HasBend)
                {
                    endNote = endNote.TieDestination;
                }

                duration = endNote.Beat.AbsolutePlaybackStart - note.Beat.AbsolutePlaybackStart + GetNoteDuration(endNote, endNote.Beat.PlaybackDuration).NoteOnly;
            }
            // if current note is a grace note with bend and tie, we can reach into next note
            else if (note.IsTieOrigin && note.Beat.GraceType != GraceType.None)
            {
                switch (note.TieDestination.BendType)
                {
                case BendType.Bend:
                case BendType.BendRelease:
                case BendType.PrebendBend:
                    finalBendValue = note.TieDestination.BendPoints[1].Value;
                    break;

                case BendType.Prebend:
                case BendType.PrebendRelease:
                    finalBendValue = note.TieDestination.BendPoints[0].Value;
                    break;
                }

                if (_settings == null)
                {
                    duration = noteDuration.NoteOnly;
                }
                else
                {
                    duration = Math.Max(noteDuration.NoteOnly, MidiUtils.MillisToTicks(_settings.SongBookBendDuration, _currentTempo));
                }
            }
            else
            {
                duration = noteDuration.NoteOnly;
            }

            // ensure prebends are slightly before the actual note.
            if (bendPoints[0].Value > 0 && !note.IsContinuedBend)
            {
                noteStart--;
            }
            var bendDuration = _settings == null ? duration : Math.Min(duration, MidiUtils.MillisToTicks(_settings.SongBookBendDuration, _currentTempo));

            FastList <BendPoint> playedBendPoints = new FastList <BendPoint>();

            switch (note.BendType)
            {
            case BendType.Custom:
                playedBendPoints = bendPoints;
                break;

            case BendType.Bend:
            case BendType.Release:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    if (finalBendValue == null || finalBendValue.Value < note.BendPoints[1].Value)
                    {
                        finalBendValue = note.BendPoints[1].Value;
                    }
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, finalBendValue.Value));
                    break;

                case BendStyle.Fast:
                    if (finalBendValue == null || finalBendValue.Value < note.BendPoints[1].Value)
                    {
                        finalBendValue = note.BendPoints[1].Value;
                    }

                    if (note.Beat.GraceType == GraceType.BendGrace)
                    {
                        GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                     true, new[] { note.BendPoints[0].Value, finalBendValue.Value }, bendDuration);
                    }
                    else
                    {
                        GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                     false, new[] { note.BendPoints[0].Value, finalBendValue.Value }, bendDuration);
                    }
                    return;
                }

                break;

            case BendType.BendRelease:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, note.BendPoints[1].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[2].Value));
                    break;

                case BendStyle.Fast:
                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 false, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value, note.BendPoints[2].Value }, bendDuration);
                    return;
                }

                break;

            case BendType.Hold:
                playedBendPoints = bendPoints;
                break;

            case BendType.Prebend:
                playedBendPoints = bendPoints;
                break;

            case BendType.PrebendBend:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[1].Value));
                    break;

                case BendStyle.Fast:
                    var preBendValue = DefaultBend + (note.BendPoints[0].Value * DefaultBendSemitone);
                    _handler.AddBend(track.Index, noteStart, (byte)channel, (int)preBendValue);

                    if (finalBendValue == null || finalBendValue.Value < note.BendPoints[1].Value)
                    {
                        finalBendValue = note.BendPoints[1].Value;
                    }

                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 false, new[] { note.BendPoints[0].Value, finalBendValue.Value }, bendDuration);
                    return;
                }

                break;

            case BendType.PrebendRelease:
                switch (note.BendStyle)
                {
                case BendStyle.Default:
                    playedBendPoints = bendPoints;
                    break;

                case BendStyle.Gradual:
                    playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value));
                    playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[1].Value));
                    break;

                case BendStyle.Fast:
                    var preBendValue = DefaultBend + (note.BendPoints[0].Value * DefaultBendSemitone);
                    _handler.AddBend(track.Index, noteStart, (byte)channel, (int)preBendValue);

                    GenerateSongBookWhammyOrBend(noteStart, channel, duration, track,
                                                 false, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value }, bendDuration);
                    return;
                }

                break;
            }

            GenerateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track);
        }