WIP: Music callback

- 头几拍会抖动一下,导致对不上拍子
This commit is contained in:
2026-04-07 10:06:02 +08:00
parent 0f204aa794
commit 49a502e647
21 changed files with 353 additions and 61 deletions
@@ -0,0 +1,129 @@
using System;
using System.Collections;
using UnityEngine;
namespace OCES.Audio
{
public class BeatClock
{
int m_beatsPerBar; // 从 TimeSig 解析
int m_barsPerGrid; // 从 Grid 字段读取
double m_startDspTime, m_secondsPerBeat; // 本次计数周期的起点
Coroutine m_beatCoroutine, m_barCoroutine, m_gridCoroutine;
readonly Action<uint> m_onBeat, m_onBar, m_onGrid;
readonly MonoBehaviour m_host;
uint m_containerId;
bool m_stopped, m_blendError; // 检测到 Blend 多 BPM 情况时置为 true
internal BeatClock(MonoBehaviour host, Action<uint> onBeat, Action<uint> onBar, Action<uint> onGrid)
{
this.m_host = host;
this.m_onBeat = onBeat;
this.m_onBar = onBar;
this.m_onGrid = onGrid;
}
public void Restart(MusicContainer container, float inheritedBpm, double dspTime)
{
Debug.Log($"[BeatClock] Restart called, container={container.Id}, bpm={container.Bpm}, inherited={inheritedBpm}");
StopAll();
this.m_blendError = this.m_stopped = false;
this.m_containerId = container.Id;
this.m_startDspTime = dspTime;
// BPM:优先用自己的,为 0 则用传入的继承值,最终回退 120
float bpm = container.Bpm > 0f ? container.Bpm : inheritedBpm;
if (bpm <= 30f) bpm = 120f; // 没见过速度小于30bpm的音乐,小于30要么是数填错了,要么是有奇怪的情况。要是真有这么慢的音乐再处理吧。
this.m_secondsPerBeat = 60 / bpm;
this.m_beatsPerBar = MusicContainerConfig.GetBeatsPerBar(container.TimeSig); // 沿用现有的解析逻辑
this.m_barsPerGrid = container.Grid > 0 ? container.Grid : 4;
this.m_beatCoroutine = this.m_host.StartCoroutine(BeatCoroutine());
this.m_barCoroutine = this.m_host.StartCoroutine(BarCoroutine());
this.m_gridCoroutine = this.m_host.StartCoroutine(GridCoroutine());
}
internal void OnBlendError(MusicContainer container)
{
this.m_blendError = true;
Debug.LogWarning($"[Music System] Blend Container {container.Id} 包含多个不同 BPM 的子 Container,节拍回调已停止。");
}
internal void StopAll()
{
this.m_stopped = true;
if (this.m_beatCoroutine != null) this.m_host.StopCoroutine(this.m_beatCoroutine);
if (this.m_barCoroutine != null) this.m_host.StopCoroutine(this.m_barCoroutine);
if (this.m_gridCoroutine != null) this.m_host.StopCoroutine(this.m_gridCoroutine);
this.m_beatCoroutine = this.m_barCoroutine = this.m_gridCoroutine = null;
}
IEnumerator BeatCoroutine()
{
long index = 0; // 从第 1 拍开始,第 0 拍是起始点本身
while (true)
{
double nextTime = this.m_startDspTime + index * this.m_secondsPerBeat;
if (index == 0)
{
Debug.Log($"[BeatClock] BeatCoroutine waiting, nextTime={nextTime}, now={AudioSettings.dspTime}");
}
yield return new WaitUntil(() => AudioSettings.dspTime >= nextTime - 0.02);
while (AudioSettings.dspTime < nextTime)
yield return null;
if (this.m_blendError || this.m_stopped){
Debug.Log($"[BeatClock] Coroutine exiting early, blendError={m_blendError}, stopped={m_stopped}");
yield break;
}
this.m_onBeat?.Invoke(this.m_containerId);
Debug.Log($"[Beat] index={index}, nextTime={nextTime:F4}, actualDspTime={AudioSettings.dspTime:F4}, diff={(AudioSettings.dspTime - nextTime)*1000:F1}ms");
index++;
}
}
IEnumerator BarCoroutine()
{
long index = 0;
while (true)
{
double nextTime = this.m_startDspTime + index * this.m_secondsPerBeat * this.m_beatsPerBar;
yield return new WaitUntil(() => AudioSettings.dspTime >= nextTime - 0.02);
while (AudioSettings.dspTime < nextTime)
yield return null;
if (this.m_blendError || this.m_stopped){
Debug.Log($"[BeatClock] Coroutine exiting early, blendError={m_blendError}, stopped={m_stopped}");
yield break;
}
this.m_onBar?.Invoke(this.m_containerId);
index++;
}
}
IEnumerator GridCoroutine()
{
long index = 0;
while (true)
{
double nextTime = this.m_startDspTime + index * this.m_secondsPerBeat * this.m_beatsPerBar * this.m_barsPerGrid;
yield return new WaitUntil(() => AudioSettings.dspTime >= nextTime - 0.02);
while (AudioSettings.dspTime < nextTime)
yield return null;
if (this.m_blendError || this.m_stopped){
Debug.Log($"[BeatClock] Coroutine exiting early, blendError={m_blendError}, stopped={m_stopped}");
yield break;
}
this.m_onGrid?.Invoke(this.m_containerId);
index++;
}
}
}
}