Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions osu.Framework/Audio/AudioManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ public class AudioManager : AudioCollectionManager<AudioComponent>
/// </summary>
public readonly Bindable<string> AudioDevice = new Bindable<string>();

/// <summary>
/// Whether to use experimental WASAPI initialisation on windows.
/// This generally results in lower audio latency, but also changes the audio synchronisation from
/// historical expectations, meaning users / application will have to account for different offsets.
/// </summary>
public readonly BindableBool UseExperimentalWasapi = new BindableBool();

/// <summary>
/// Volume of all samples played game-wide.
/// </summary>
Expand Down Expand Up @@ -176,6 +183,7 @@ public AudioManager(AudioThread audioThread, ResourceStore<byte[]> trackStore, R
thread.RegisterManager(this);

AudioDevice.ValueChanged += _ => onDeviceChanged();
UseExperimentalWasapi.ValueChanged += _ => onDeviceChanged();
GlobalMixerHandle.ValueChanged += handle =>
{
onDeviceChanged();
Expand Down Expand Up @@ -430,10 +438,16 @@ protected virtual bool InitBass(int device)
// See https://www.un4seen.com/forum/?topic=19601 for more information.
Bass.Configure((ManagedBass.Configuration)70, false);

if (!thread.InitDevice(device))
return false;
if (UseExperimentalWasapi.Value)
{
if (thread.InitDevice(device, true))
return true;

return true;
Logger.Log($"BASS device {device} failed to initialise with experimental WASAPI, disabling", level: LogLevel.Error);
UseExperimentalWasapi.Value = false;
}

return thread.InitDevice(device, false);
}

private void syncAudioDevices()
Expand Down
2 changes: 2 additions & 0 deletions osu.Framework/Configuration/FrameworkConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ protected override void InitialiseDefaults()
SetDefault(FrameworkSetting.WindowedPositionY, 0.5, -0.5, 1.5);
SetDefault(FrameworkSetting.LastDisplayDevice, DisplayIndex.Default);
SetDefault(FrameworkSetting.AudioDevice, string.Empty);
SetDefault(FrameworkSetting.AudioUseExperimentalWasapi, false);
SetDefault(FrameworkSetting.VolumeUniversal, 1.0, 0.0, 1.0, 0.01);
SetDefault(FrameworkSetting.VolumeMusic, 1.0, 0.0, 1.0, 0.01);
SetDefault(FrameworkSetting.VolumeEffect, 1.0, 0.0, 1.0, 0.01);
Expand Down Expand Up @@ -79,6 +80,7 @@ public enum FrameworkSetting
ShowLogOverlay,

AudioDevice,
AudioUseExperimentalWasapi,
VolumeUniversal,
VolumeEffect,
VolumeMusic,
Expand Down
3 changes: 0 additions & 3 deletions osu.Framework/FrameworkEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public static class FrameworkEnvironment
public static bool NoStructuredBuffers { get; }
public static string? DeferredRendererEventsOutputPath { get; }
public static bool UseSDL3 { get; }
public static bool UseWasapi { get; }

/// <summary>
/// Whether non-SSL requests should be allowed. Debug only. Defaults to disabled.
Expand Down Expand Up @@ -56,8 +55,6 @@ static FrameworkEnvironment()

// Desktop has many issues, see https://github.com/ppy/osu-framework/issues/6540.
UseSDL3 = RuntimeInfo.IsMobile || (parseBool(Environment.GetEnvironmentVariable("OSU_SDL3")) ?? false);

UseWasapi = parseBool(Environment.GetEnvironmentVariable("OSU_AUDIO_WASAPI_EXPERIMENTAL")) ?? false;
}

private static bool? parseBool(string? value)
Expand Down
1 change: 1 addition & 0 deletions osu.Framework/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ private void load(FrameworkConfigManager config)

// attach our bindables to the audio subsystem.
config.BindWith(FrameworkSetting.AudioDevice, Audio.AudioDevice);
config.BindWith(FrameworkSetting.AudioUseExperimentalWasapi, Audio.UseExperimentalWasapi);
config.BindWith(FrameworkSetting.VolumeUniversal, Audio.Volume);
config.BindWith(FrameworkSetting.VolumeEffect, Audio.VolumeSample);
config.BindWith(FrameworkSetting.VolumeMusic, Audio.VolumeTrack);
Expand Down
17 changes: 8 additions & 9 deletions osu.Framework/Threading/AudioThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ protected override void OnExit()
/// </summary>
private readonly Bindable<int?> globalMixerHandle = new Bindable<int?>();

internal bool InitDevice(int deviceId)
internal bool InitDevice(int deviceId, bool useExperimentalWasapi)
{
Debug.Assert(ThreadSafety.IsAudioThread);
Trace.Assert(deviceId != -1); // The real device ID should always be used, as the -1 device has special cases which are hard to work with.
Expand All @@ -139,9 +139,7 @@ internal bool InitDevice(int deviceId)
if (!Bass.Init(deviceId, Flags: (DeviceInitFlags)128)) // 128 == BASS_DEVICE_REINIT
return false;

// That this has not been mass-tested since https://github.com/ppy/osu-framework/pull/6651 and probably needs to be.
// Currently envvar gated for users to test at their own discretion.
if (FrameworkEnvironment.UseWasapi)
if (useExperimentalWasapi)
attemptWasapiInitialisation();

initialised_devices.Add(deviceId);
Expand Down Expand Up @@ -182,10 +180,10 @@ internal static void PreloadBass()
}
}

private void attemptWasapiInitialisation()
private bool attemptWasapiInitialisation()
{
if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
return;
return false;

Logger.Log("Attempting local BassWasapi initialisation");

Expand Down Expand Up @@ -219,10 +217,10 @@ private void attemptWasapiInitialisation()

// To keep things in a sane state let's only keep one device initialised via wasapi.
freeWasapi();
initWasapi(wasapiDevice);
return initWasapi(wasapiDevice);
}

private void initWasapi(int wasapiDevice)
private bool initWasapi(int wasapiDevice)
{
// This is intentionally initialised inline and stored to a field.
// If we don't do this, it gets GC'd away.
Expand All @@ -246,13 +244,14 @@ private void initWasapi(int wasapiDevice)
Logger.Log($"Initialising BassWasapi for device {wasapiDevice}...{(initialised ? "success!" : "FAILED")}");

if (!initialised)
return;
return false;

BassWasapi.GetInfo(out var wasapiInfo);
globalMixerHandle.Value = BassMix.CreateMixerStream(wasapiInfo.Frequency, wasapiInfo.Channels, BassFlags.MixerNonStop | BassFlags.Decode | BassFlags.Float);
BassWasapi.Start();

BassWasapi.SetNotify(wasapiNotifyProcedure);
return true;
}

private void freeWasapi()
Expand Down
Loading