< Summary

Information
Class: MusicTheory.Theory.Time.GrooveHumanize
Assembly: MusicTheory
File(s): /home/runner/work/MusicTheory/MusicTheory/Theory/Time/GrooveHumanize.cs
Line coverage
100%
Covered lines: 15
Uncovered lines: 0
Coverable lines: 15
Total lines: 57
Line coverage: 100%
Branch coverage
87%
Covered branches: 14
Total branches: 16
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
ApplyGroove(...)100%11100%
ProcessOne(...)100%88100%
AddTimingJitter(...)75%44100%
HumanizeVelocity(...)75%44100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4
 5namespace MusicTheory.Theory.Time;
 6
 7/// <summary>
 8/// グルーヴ / ヒューマナイズ適用ユーティリティ。
 9/// </summary>
 10public static class GrooveHumanize
 11{
 12    /// <summary>位置配列にグルーヴテンプレート (tick オフセット) を適用。</summary>
 13    public static IEnumerable<long> ApplyGroove(IEnumerable<long> ticks, int gridTicks, IReadOnlyList<int> pattern)
 414        => ticks.Select(t => Quantize.ApplyGroove(t, gridTicks, pattern));
 15
 16    /// <summary>スイング + グルーヴ + 強拍バイアスをまとめて適用。</summary>
 17    public static long ProcessOne(long tick, int gridTicks, double swingRatio, int swingSubdivisionTicks, int ticksPerBa
 18    {
 319        var q = Quantize.ToGrid(tick, gridTicks);
 320        if (swingRatio>0 && swingSubdivisionTicks>0)
 121            q = Quantize.ApplySwing(q, swingSubdivisionTicks, swingRatio);
 322        if (strongBeats.Count>0)
 123            q = Quantize.ToGridStrongBeat(q, gridTicks, ticksPerBar, strongBeats, strongBias);
 324        if (groovePattern.Count>0)
 125            q = Quantize.ApplyGroove(q, gridTicks, groovePattern);
 326        return q;
 27    }
 28
 29    /// <summary>タイミングジッター (±maxJitterTicks の整数一様) を付与。</summary>
 30    public static IEnumerable<long> AddTimingJitter(IEnumerable<long> ticks, int maxJitterTicks, int? seed=null)
 31    {
 332        if (maxJitterTicks<=0) return ticks;
 133        var rnd = seed.HasValue? new Random(seed.Value) : new Random();
 434        return ticks.Select(t => t + rnd.Next(-maxJitterTicks, maxJitterTicks+1));
 35    }
 36
 37    /// <summary>ベロシティヒューマナイズ (割合 ±percent / clamp 0..127)。</summary>
 38    public static IEnumerable<int> HumanizeVelocity(IEnumerable<int> velocities, double percent, int? seed=null)
 39    {
 440        if (percent<=0) return velocities;
 241        var rnd = seed.HasValue? new Random(seed.Value) : new Random();
 742        return velocities.Select(v => (int)Math.Clamp(v + v * (rnd.NextDouble()*2-1)*percent, 0, 127));
 43    }
 44}
 45
 46/// <summary>量子化プリセット (スイングとグルーヴ合成) を提供。</summary>
 47public static class QuantizePresets
 48{
 49    public record Preset(string Name, int GridTicks, double SwingRatio, int SwingSubdivisionTicks, IReadOnlyList<int> Gr
 50
 51    public static Preset Straight16 => new("Straight16", Duration.TicksPerQuarter/4, 0, 0, Array.Empty<int>(), Array.Emp
 52    public static Preset Swing8 => new("Swing8", Duration.TicksPerQuarter/2, 0.5, Duration.TicksPerQuarter/2, Array.Empt
 53    public static Preset Funk16 => new("Funk16", Duration.TicksPerQuarter/4, 0.0, 0, new[]{0,5,0,-5}, new[]{0, Duration.
 54
 55    public static long Apply(Preset p, long tick, int ticksPerBar)
 56        => GrooveHumanize.ProcessOne(tick, p.GridTicks, p.SwingRatio, p.SwingSubdivisionTicks, ticksPerBar, p.StrongBeat
 57}