Skip to content

[Metronome] Split local/remote lead time for low-latency conductor monitoring #27

Description

@hakaru

問題

指揮者本人が自分のクリック音を 200ms 遅れで聞くため、演奏感が悪い。

現状

`ConductorService.leadTimeNs = 200_000_000` が local/remote 両方に適用される。

  • Local: 指揮者自身が ictus から 200ms 後にクリックを聞く
  • Remote: ネットワーク保険として 200ms は妥当

指揮者にとって 200ms の遅延は明確に長い(人間の知覚限界 ~30ms を大幅に超える)。

改善方針

Lead time を local/remote で分離:

```swift
static let localLeadTimeNs: UInt64 = 30_000_000 // 30ms 程度
static let remoteLeadTimeNs: UInt64 = 200_000_000 // 200ms
```

  • Local self-monitor は 20-40ms(即時モニタ感覚)
  • Remote broadcast は `base + jitter budget + one-way network budget`

`ConductorService.handleDetectedBeat`:

  • Local 再生用 applyAtNs = syncedNow + localLead
  • Broadcast BeatEvent の applyAtNs = syncedNow + remoteLead

ただし、同じ BeatEvent が 2 つの時刻を持つと受信側が混乱するので、broadcast は remote 用の時刻で送信し、local は別パスで即時再生する実装が必要。

実装案

```swift
private func handleDetectedBeat(_ beat: BeatDetected) async {
let now = mach_absolute_time()
let syncedNow = syncedNowProvider()

let localApplyHost = now + nsToMach(Self.localLeadTimeNs)
let remoteApplyAtNs = syncedNow + Self.remoteLeadTimeNs

// Local immediate trigger
await onLocalBeat(localApplyHost, tickType, beatIndex)

// Remote broadcast
let event = BeatEvent(..., applyAtNs: remoteApplyAtNs)
await onBeatEvent(event)

}
```

重要度

High(UX に直接影響)

関連 issue

#22(hostTime 基準の修正), #25(MainActor 分離)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions