/// <summary>
 /// 歌唱をやめてスタートに戻る確率
 /// </summary>
 public static IEnumerable <ProbabilityGenerationResult> StopSinging(ProbabilityGenerationContext context)
 {
     if (context.TargetNoteNode?.Next == null)
     {
         yield break;                                       // 最後のノートからスタートへの移動は後で
     }
     yield return(ProbabilityGenerationResult.CreateToStartState(0.05, false));
 }
 /// <summary>
 /// 自己ループの確率(音程変化の判定が過剰に反応してしまった場合)
 /// </summary>
 public static IEnumerable <ProbabilityGenerationResult> SelfLoop(ProbabilityGenerationContext context)
 {
     if (context.TargetNoteNode == null)
     {
         // スタート状態
         yield return(ProbabilityGenerationResult.CreateToStartState(0.8, false));
     }
     else
     {
         yield return(new ProbabilityGenerationResult(context.TargetNoteNode.Value.Index, 0.33, false));
     }
 }
        /// <summary>
        /// 先のノートに移動する確率
        /// </summary>
        public static IEnumerable <ProbabilityGenerationResult> MoveToForwardNotes(ProbabilityGenerationContext context)
        {
            var next = context.TargetNoteNode == null ? context.Notes.First : context.TargetNoteNode.Next;

            if (next == null)
            {
                yield break;
            }

            // 2分音符の長さまでの範囲のノートに移動する
            const int maxSkipLength   = 960;
            var       targetNote      = context.TargetNoteNode?.Value;
            var       maxSkipPosition = targetNote != null
                ? targetNote.Position + targetNote.Length + maxSkipLength
                : next.Value.Position + maxSkipLength; // スタート状態からの場合は最初のノートの開始位置から

            var sentinel = next.Next;

            while (sentinel != null && sentinel.Value.Position < maxSkipPosition)
            {
                sentinel = sentinel.Next;
            }

            var totalLength = 0.0;
            var node        = next;

            while (node != sentinel)
            {
                if (!node.Value.IsRestNote)
                {
                    totalLength += VirtualLength(node);
                }
                node = node.Next;
            }

            node = next;
            while (node != sentinel)
            {
                var n = node.Value;

                if (!n.IsRestNote)
                {
                    var p = context.RemainingProbability * VirtualLength(node) / totalLength;

                    if (context.TargetNoteNode == null)
                    {
                        // スタート状態からの遷移なら、無音状態を経由することはない
                        yield return(new ProbabilityGenerationResult(n.Index, p, false));
                    }
                    else
                    {
                        var viaNoSoundProbability = node.Previous?.Value is UtauNote prevNote && prevNote.IsRestNote
                            ? ProbabilityOfNoSoundWhenRestNote(prevNote)
                            : ProbabilityOfNoSoundAfter(n);

                        yield return(new ProbabilityGenerationResult(n.Index, (1.0 - viaNoSoundProbability) * p, false));

                        yield return(new ProbabilityGenerationResult(n.Index, viaNoSoundProbability * p, true));
                    }
                }

                node = node.Next;
            }

            double VirtualLength(LinkedListNode <UtauNote> noteNode)
            {
                double l = Math.Min(noteNode.Value.Position + noteNode.Value.Length, maxSkipPosition) - noteNode.Value.Position;

                // 前のノートと同じ高さなら、認識しにくいので、本来よりも短いものとして認識する
                if (noteNode.Previous?.Value.NoteNumber == noteNode.Value.NoteNumber)
                {
                    l *= 0.8;
                }

                // next から離れているほど確率を下げる
                var n = next;

                while (n != noteNode)
                {
                    l *= 0.5;
                    n  = n.Next;
                }

                return(l);
            }
        }
        /// <summary>
        /// 1 小節前の最初に戻る確率
        /// </summary>
        public static IEnumerable <ProbabilityGenerationResult> MoveToFirstNoteInPreviousMeasure(ProbabilityGenerationContext context)
        {
            if (context.TargetNoteNode?.Next == null)
            {
                yield break;                                       // スタート状態または最後のノートならいらない
            }
            var note = context.TargetNoteNode.Value;
            var measureStartPosition         = note.Position - note.Position % 1920;
            var previousMeasureStartPosition = measureStartPosition - 1920;

            var n = context.TargetNoteNode;
            var firstStateInPreviousMeasure = n;

            while ((n = n.Previous)?.Value.Position >= measureStartPosition)
            {
                if (!n.Value.IsRestNote)
                {
                    firstStateInPreviousMeasure = n;
                }
            }

            if (firstStateInPreviousMeasure.Value.Position < measureStartPosition)
            {
                yield return(new ProbabilityGenerationResult(firstStateInPreviousMeasure.Value.Index, 0.05, true));
            }
        }
 public static IEnumerable <ProbabilityGenerationResult> MoveToStartFromLastNote(ProbabilityGenerationContext context)
 {
     if (context.TargetNoteNode == null || context.TargetNoteNode.Next != null)
     {
         yield break;
     }
     yield return(ProbabilityGenerationResult.CreateToStartState(context.RemainingProbability, false));
 }