Skip to content

[Metronome] Internal fallback phase inheritance produces past nextBeatHostTime #26

Description

@hakaru

問題

指揮者タイムアウト後の Internal モード復帰で、位相継承が正しく機能しない。

現状

`MetronomeViewModel.followerTimedOut()` → `MetronomeEngine.setInternalFromExternal(lastBeatHostTime:beatIndex:bpm:)` が呼ばれる。

`setInternalFromExternal` は:

```swift
nextBeatHostTime = lastBeatHostTime + subIntervalMach * UInt64(subsPerBeat)
```

つまり「最終ビート + 1拍」を次のビートとして設定する。

バグ

タイムアウトは 1.5拍後 に発火する。
発火時点では `lastBeatHostTime + 1拍` は 必ず過去時刻

その直後 `scheduleUpcoming()` が走り:

```swift
if nextBeatHostTime < now {
if syncedNowProvider != nil {
calculateNextBeatFromSyncedClock() // ← 外部位相を捨てて再量子化
}
}
```

結果、指揮者の位相は継承されず、grid 上の次のタイミングにジャンプする。

改善方針

復帰時点で `lastBeatHostTime + n * beatInterval >= now` となる最初の未来ビートを計算:

```swift
var next = lastBeatHostTime + subIntervalMach * UInt64(subsPerBeat)
let now = mach_absolute_time()
while next < now {
next += subIntervalMach * UInt64(subsPerBeat)
currentSubBeat = (currentSubBeat + subsPerBeat) % totalSubs
}
nextBeatHostTime = next
```

加えて、この経路では `calculateNextBeatFromSyncedClock()` に潰されないように scheduleUpcoming のガード条件を見直す必要あり。

重要度

High

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions