WIP: Music callback
- 头几拍会抖动一下,导致对不上拍子
This commit is contained in:
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user