mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-29 22:19:39 +08:00
2b6bd5a532
* [audio] added sdl audio backend and in-tree atrac9 decoder * [input] replaced per-platform pad readers with sdl gamepad input * [video] added sdl window and host display plumbing * [gui] added host display options and per-game render settings * [bink] synced host movie playback to the guest audio clock * [cpu] hooked windows write faults into guest image tracking * [perf] added guest and render profiling, reserved host cpu lanes * [kernel] fixed stale pthread mutex handle alias * [host] wired the sdl session, save-data paths and project references * [audio] hoisted ajm trace stackalloc out of its loop * [video] Add guest image sync setting * [build] Strip native symbols * reuse
70 lines
2.3 KiB
C#
70 lines
2.3 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Diagnostics;
|
|
|
|
namespace SharpEmu.HLE.Host;
|
|
|
|
/// <summary>
|
|
/// How much guest audio the host device has actually played, in seconds.
|
|
///
|
|
/// This is the only clock in the emulator that advances at the rate the player
|
|
/// hears. Wall clock runs ahead of it whenever the guest cannot feed the device
|
|
/// (the stream underruns and the missing time is never played), so anything
|
|
/// that has to stay in step with the guest's audio — host-decoded video being
|
|
/// the case that matters — has to follow this rather than <see cref="Stopwatch"/>.
|
|
///
|
|
/// Reported per stream and kept as the furthest-along value: the guest's ports
|
|
/// all carry one mix, and the leading port is the one whose position the
|
|
/// listener perceives.
|
|
/// </summary>
|
|
public static class GuestAudioClock
|
|
{
|
|
private static long _playedMicroseconds;
|
|
private static long _lastAdvanceTimestamp;
|
|
|
|
/// <summary>Seconds of guest audio the device has played. Monotonic.</summary>
|
|
public static double PlayedSeconds =>
|
|
Interlocked.Read(ref _playedMicroseconds) / 1_000_000.0;
|
|
|
|
/// <summary>
|
|
/// True while a stream has reported progress recently. False means no guest
|
|
/// audio is playing, and callers must fall back to wall clock rather than
|
|
/// stalling on a clock that will never advance.
|
|
/// </summary>
|
|
public static bool IsRunning
|
|
{
|
|
get
|
|
{
|
|
var last = Interlocked.Read(ref _lastAdvanceTimestamp);
|
|
return last != 0 &&
|
|
Stopwatch.GetElapsedTime(last) < TimeSpan.FromMilliseconds(250);
|
|
}
|
|
}
|
|
|
|
public static void Report(double playedSeconds)
|
|
{
|
|
if (double.IsNaN(playedSeconds) || playedSeconds < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var microseconds = (long)(playedSeconds * 1_000_000.0);
|
|
var current = Interlocked.Read(ref _playedMicroseconds);
|
|
while (microseconds > current)
|
|
{
|
|
var seen = Interlocked.CompareExchange(
|
|
ref _playedMicroseconds,
|
|
microseconds,
|
|
current);
|
|
if (seen == current)
|
|
{
|
|
Interlocked.Exchange(ref _lastAdvanceTimestamp, Stopwatch.GetTimestamp());
|
|
return;
|
|
}
|
|
|
|
current = seen;
|
|
}
|
|
}
|
|
}
|