A game framework written with osu! in mind.
at master 4.8 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using osu.Framework.Statistics; 5using System; 6using System.Collections.Generic; 7using System.Diagnostics; 8using System.Linq; 9using ManagedBass; 10using osu.Framework.Audio; 11using osu.Framework.Development; 12using osu.Framework.Platform.Linux.Native; 13 14namespace osu.Framework.Threading 15{ 16 public class AudioThread : GameThread 17 { 18 public AudioThread() 19 : base(name: "Audio") 20 { 21 OnNewFrame += onNewFrame; 22 PreloadBass(); 23 } 24 25 public override bool IsCurrent => ThreadSafety.IsAudioThread; 26 27 internal sealed override void MakeCurrent() 28 { 29 base.MakeCurrent(); 30 31 ThreadSafety.IsAudioThread = true; 32 } 33 34 internal override IEnumerable<StatisticsCounterType> StatisticsCounters => new[] 35 { 36 StatisticsCounterType.TasksRun, 37 StatisticsCounterType.Tracks, 38 StatisticsCounterType.Samples, 39 StatisticsCounterType.SChannels, 40 StatisticsCounterType.Components, 41 StatisticsCounterType.MixChannels, 42 }; 43 44 private readonly List<AudioManager> managers = new List<AudioManager>(); 45 46 private static readonly HashSet<int> initialised_devices = new HashSet<int>(); 47 48 private static readonly GlobalStatistic<double> cpu_usage = GlobalStatistics.Get<double>("Audio", "Bass CPU%"); 49 50 private void onNewFrame() 51 { 52 cpu_usage.Value = Bass.CPUUsage; 53 54 lock (managers) 55 { 56 for (var i = 0; i < managers.Count; i++) 57 { 58 var m = managers[i]; 59 m.Update(); 60 } 61 } 62 } 63 64 internal void RegisterManager(AudioManager manager) 65 { 66 lock (managers) 67 { 68 if (managers.Contains(manager)) 69 throw new InvalidOperationException($"{manager} was already registered"); 70 71 managers.Add(manager); 72 } 73 } 74 75 internal void UnregisterManager(AudioManager manager) 76 { 77 lock (managers) 78 managers.Remove(manager); 79 } 80 81 internal void RegisterInitialisedDevice(int deviceId) 82 { 83 Debug.Assert(ThreadSafety.IsAudioThread); 84 85 initialised_devices.Add(deviceId); 86 } 87 88 protected override void OnExit() 89 { 90 base.OnExit(); 91 92 lock (managers) 93 { 94 // AudioManagers are iterated over backwards since disposal will unregister and remove them from the list. 95 for (int i = managers.Count - 1; i >= 0; i--) 96 { 97 var m = managers[i]; 98 99 m.Dispose(); 100 101 // Audio component disposal (including the AudioManager itself) is scheduled and only runs when the AudioThread updates. 102 // But the AudioThread won't run another update since it's exiting, so an update must be performed manually in order to finish the disposal. 103 m.Update(); 104 } 105 106 managers.Clear(); 107 } 108 109 // Safety net to ensure we have freed all devices before exiting. 110 // This is mainly required for device-lost scenarios. 111 // See https://github.com/ppy/osu-framework/pull/3378 for further discussion. 112 foreach (var d in initialised_devices.ToArray()) 113 FreeDevice(d); 114 } 115 116 internal static void FreeDevice(int deviceId) 117 { 118 Debug.Assert(ThreadSafety.IsAudioThread); 119 120 int lastDevice = Bass.CurrentDevice; 121 122 // Freeing the 0 device on linux can cause deadlocks. This doesn't always happen immediately. 123 // Todo: Reproduce in native code and report to BASS at some point. 124 if (deviceId != 0 || RuntimeInfo.OS != RuntimeInfo.Platform.Linux) 125 { 126 Bass.CurrentDevice = deviceId; 127 Bass.Free(); 128 } 129 130 if (lastDevice != deviceId) 131 Bass.CurrentDevice = lastDevice; 132 133 initialised_devices.Remove(deviceId); 134 } 135 136 /// <summary> 137 /// Makes BASS available to be consumed. 138 /// </summary> 139 internal static void PreloadBass() 140 { 141 if (RuntimeInfo.OS == RuntimeInfo.Platform.Linux) 142 { 143 // required for the time being to address libbass_fx.so load failures (see https://github.com/ppy/osu/issues/2852) 144 Library.Load("libbass.so", Library.LoadFlags.RTLD_LAZY | Library.LoadFlags.RTLD_GLOBAL); 145 } 146 } 147 } 148}