< Summary

Information
Class: MusicTheory.Theory.Time.NoteValueZoom
Assembly: MusicTheory
File(s): /home/runner/work/MusicTheory/MusicTheory/Theory/Time/NoteValueZoom.cs
Line coverage
97%
Covered lines: 33
Uncovered lines: 1
Coverable lines: 34
Total lines: 65
Line coverage: 97%
Branch coverage
87%
Covered branches: 7
Total branches: 8
Branch coverage: 87.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Label()100%11100%
Create()100%11100%
get_FactorToQuarter()100%210%
get_Ticks()100%11100%
.cctor()100%11100%
get_Entries()100%11100%
FindNearestIndex(...)87.5%88100%
Zoom(...)100%11100%
ZoomIn(...)100%11100%
ZoomOut(...)100%11100%

File(s)

/home/runner/work/MusicTheory/MusicTheory/Theory/Time/NoteValueZoom.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4
 5namespace MusicTheory.Theory.Time;
 6
 7/// <summary>
 8/// 音価虫眼鏡ツール: 四分音符[*1] を基準に、定義済みの代表音価ステップに沿って拡大(+) / 縮小(-) する。
 9/// 例: 8分三連符[*0.33] → 付点16分[*0.375] → 8分[*0.5] → 4分三連符[*0.66] → 付点8分[*0.75] → 4分[*1] → ...
 10/// </summary>
 11public static class NoteValueZoom
 12{
 13413    public readonly record struct Entry(string Label, Func<Duration> Factory)
 14    {
 10615        public Duration Create() => Factory();
 016        public double FactorToQuarter => Create().Ticks / (double)Duration.TicksPerQuarter; // 四分音符=1.0
 9817        public int Ticks => Create().Ticks;
 18    }
 19
 20    // 昇順(小→大)。四分音符[*1] を中心に、画像で示されたリストに準拠。
 121    private static readonly Entry[] _entries = new[]
 122    {
 923        new Entry("32分音符[*0.125]", () => DurationFactory.ThirtySecond()),
 724        new Entry("16分三連符[*0.165]", () => DurationFactory.Tuplet(BaseNoteValue.Sixteenth, new Tuplet(3,2))),
 825        new Entry("16分音符[*0.25]", () => DurationFactory.Sixteenth()),
 726        new Entry("8分三連符[*0.33]", () => DurationFactory.Tuplet(BaseNoteValue.Eighth, new Tuplet(3,2))),
 727        new Entry("付点16分音符[*0.375]", () => DurationFactory.Sixteenth(1)),
 728        new Entry("8分音符[*0.5]", () => DurationFactory.Eighth()),
 729        new Entry("4分三連符[*0.66]", () => DurationFactory.Tuplet(BaseNoteValue.Quarter, new Tuplet(3,2))),
 830        new Entry("付点8分音符[*0.75]", () => DurationFactory.Eighth(1)),
 831        new Entry("4分音符[*1]", () => DurationFactory.Quarter()),
 832        new Entry("2分三連符[*1.32]", () => DurationFactory.Tuplet(BaseNoteValue.Half, new Tuplet(3,2))),
 733        new Entry("付点4分音符[*1.5]", () => DurationFactory.Quarter(1)),
 734        new Entry("2分音符[*2]", () => DurationFactory.Half()),
 735        new Entry("付点2分音符[*3]", () => DurationFactory.Half(1)),
 936        new Entry("全音符[*4]", () => DurationFactory.Whole()),
 137    };
 38
 839    public static IReadOnlyList<Entry> Entries => _entries;
 40
 41    /// <summary>四分音符[*1]基準の倍率(ticks/480)で現在位置に最も近いインデックスを返す(同距離は「大きい方」を優先)。</summary>
 42    public static int FindNearestIndex(Duration current)
 43    {
 644        var ticks = current.Ticks;
 1245        int bestIdx = 0; int bestDiff = Math.Abs(_entries[0].Ticks - ticks);
 16846        for (int i=1;i<_entries.Length;i++)
 47        {
 7848            int diff = Math.Abs(_entries[i].Ticks - ticks);
 7849            if (diff < bestDiff || (diff==bestDiff && _entries[i].Ticks > _entries[bestIdx].Ticks))
 3950                (bestIdx, bestDiff) = (i, diff);
 51        }
 652        return bestIdx;
 53    }
 54
 55    /// <summary>ズーム(delta>0:大きく, delta<0:小さく)。範囲外は端にクランプ。</summary>
 56    public static Duration Zoom(Duration current, int delta)
 57    {
 458        int idx = FindNearestIndex(current);
 459        int next = Math.Clamp(idx + delta, 0, _entries.Length - 1);
 460        return _entries[next].Create();
 61    }
 62
 263    public static Duration ZoomIn(Duration current) => Zoom(current, +1);
 264    public static Duration ZoomOut(Duration current) => Zoom(current, -1);
 65}