Files
osu-framework/osu.Framework/Audio/EzPlaybackModule.cs
LA 0c98f9c0a8 修改架构\过滤规则\调整记录结构\修改单位
增加开关机制
增强延迟检测框架
2026-01-15 23:27:22 +08:00

131 lines
5.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Logging;
using osu.Framework.Threading;
namespace osu.Framework.Audio
{
/// <summary>
/// EzPlaybackModule 负责处理播放事件的触发和时间戳记录。
/// 主要职责包括监听播放事件,记录播放触发时间,并测量从触发到实际发声的延迟。
/// </summary>
public class EzPlaybackModule
{
/// <summary>
/// EzOsuLatency 总开关 - 控制是否启用延迟测量
/// </summary>
public bool Enabled { get; set; }
private readonly System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
// 存储播放时间
internal double PlaybackTime;
// AudioThread 引用,用于访问其他模块
private readonly AudioThread audioThread;
public EzPlaybackModule(AudioThread audioThread)
{
this.audioThread = audioThread;
stopwatch.Start();
}
/// <summary>
/// 记录播放事件的时间戳T_call
/// </summary>
/// <param name="timestamp">播放事件的时间戳</param>
public void RecordTimestamp(DateTime timestamp)
{
if (Enabled)
{
PlaybackTime = stopwatch.Elapsed.TotalMilliseconds;
// 只有在有有效的输入数据时才打印日志避免长按note的情况
if (EzInputModule.InputTime > 0)
{
// build local low-level structs for richer diagnostics (do not mutate AudioThread fields)
osu.Framework.Threading.EzLatencyInputData inputData = default;
osu.Framework.Threading.EzLatencyHardwareData hardwareData = default;
try
{
inputData.InputTime = EzInputModule.InputTime;
inputData.KeyValue = EzInputModule.KeyValue ?? "";
inputData.JudgeTime = EzJudgeModule.JudgeTime;
inputData.PlaybackTime = PlaybackTime;
hardwareData.DriverTime = audioThread.DriverModule.DriverTime;
hardwareData.OutputHardwareTime = audioThread.HardwareModule.OutputHardwareTime;
hardwareData.InputHardwareTime = audioThread.HardwareModule.InputHardwareTime;
hardwareData.LatencyDifference = audioThread.HardwareModule.LatencyDifference;
}
catch { }
// 生成最终 EzLatencyRecord 并直接推送到 EzLatencyService
var record = new osu.Framework.Audio.EzLatency.EzLatencyRecord
{
Timestamp = DateTimeOffset.Now,
MeasuredMs = PlaybackTime - EzInputModule.InputTime,
Note = "playback-record",
InputTime = EzInputModule.InputTime,
JudgeTime = EzJudgeModule.JudgeTime,
PlaybackTime = PlaybackTime,
DriverTime = audioThread.DriverModule.DriverTime,
OutputHardwareTime = audioThread.HardwareModule.OutputHardwareTime,
InputHardwareTime = audioThread.HardwareModule.InputHardwareTime,
LatencyDifference = audioThread.HardwareModule.LatencyDifference,
InputData = inputData,
HardwareData = hardwareData
};
// push to central service for osu layer to consume
try
{
osu.Framework.Audio.EzLatency.EzLatencyService.Instance.PushRecord(record);
}
catch { }
// 兼容:仍然把事件记录到 EzLogModule 的内部统计(可选)
try
{
if (EzLogModule.Instance != null && EzLogModule.Instance.Enabled)
EzLogModule.Instance.RecordLatencyEventAndGet(record.InputTime, record.JudgeTime, record.PlaybackTime, record.DriverTime, record.OutputHardwareTime, record.InputHardwareTime, record.LatencyDifference, inputData, hardwareData);
}
catch { }
}
}
}
/// <summary>
/// 运行播放延迟测试
/// </summary>
public void RunTest()
{
if (Enabled)
Logger.Log("[EzPlayback] Run playback latency test", name: "audio", level: LogLevel.Debug);
}
/// <summary>
/// 设置播放缓冲区大小
/// </summary>
/// <param name="bufferSize">缓冲区大小</param>
public void SetBufferSize(int bufferSize)
{
if (Enabled)
Logger.Log($"[EzPlayback] Set buffer size: {bufferSize}", name: "audio", level: LogLevel.Debug);
}
/// <summary>
/// 设置采样率
/// </summary>
/// <param name="sampleRate">采样率</param>
public void SetSampleRate(int sampleRate)
{
if (Enabled)
Logger.Log($"[EzPlayback] Set sample rate: {sampleRate}", name: "audio", level: LogLevel.Debug);
}
}
}