OpenALHardwareDeviceDriver.cs
1 using OpenTK.Audio.OpenAL; 2 using Ryujinx.Audio.Common; 3 using Ryujinx.Audio.Integration; 4 using Ryujinx.Memory; 5 using System; 6 using System.Collections.Concurrent; 7 using System.Linq; 8 using System.Threading; 9 using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; 10 11 namespace Ryujinx.Audio.Backends.OpenAL 12 { 13 public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver 14 { 15 private readonly ALDevice _device; 16 private readonly ALContext _context; 17 private readonly ManualResetEvent _updateRequiredEvent; 18 private readonly ManualResetEvent _pauseEvent; 19 private readonly ConcurrentDictionary<OpenALHardwareDeviceSession, byte> _sessions; 20 private bool _stillRunning; 21 private readonly Thread _updaterThread; 22 23 private float _volume; 24 25 public float Volume 26 { 27 get 28 { 29 return _volume; 30 } 31 set 32 { 33 _volume = value; 34 35 foreach (OpenALHardwareDeviceSession session in _sessions.Keys) 36 { 37 session.UpdateMasterVolume(value); 38 } 39 } 40 } 41 42 public OpenALHardwareDeviceDriver() 43 { 44 _device = ALC.OpenDevice(""); 45 _context = ALC.CreateContext(_device, new ALContextAttributes()); 46 _updateRequiredEvent = new ManualResetEvent(false); 47 _pauseEvent = new ManualResetEvent(true); 48 _sessions = new ConcurrentDictionary<OpenALHardwareDeviceSession, byte>(); 49 50 _stillRunning = true; 51 _updaterThread = new Thread(Update) 52 { 53 Name = "HardwareDeviceDriver.OpenAL", 54 }; 55 56 _volume = 1f; 57 58 _updaterThread.Start(); 59 } 60 61 public static bool IsSupported 62 { 63 get 64 { 65 try 66 { 67 return ALC.GetStringList(GetEnumerationStringList.DeviceSpecifier).Any(); 68 } 69 catch 70 { 71 return false; 72 } 73 } 74 } 75 76 public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) 77 { 78 if (channelCount == 0) 79 { 80 channelCount = 2; 81 } 82 83 if (sampleRate == 0) 84 { 85 sampleRate = Constants.TargetSampleRate; 86 } 87 88 if (direction != Direction.Output) 89 { 90 throw new ArgumentException($"{direction}"); 91 } 92 else if (!SupportsChannelCount(channelCount)) 93 { 94 throw new ArgumentException($"{channelCount}"); 95 } 96 97 OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount); 98 99 _sessions.TryAdd(session, 0); 100 101 return session; 102 } 103 104 internal bool Unregister(OpenALHardwareDeviceSession session) 105 { 106 return _sessions.TryRemove(session, out _); 107 } 108 109 public ManualResetEvent GetUpdateRequiredEvent() 110 { 111 return _updateRequiredEvent; 112 } 113 114 public ManualResetEvent GetPauseEvent() 115 { 116 return _pauseEvent; 117 } 118 119 private void Update() 120 { 121 ALC.MakeContextCurrent(_context); 122 123 while (_stillRunning) 124 { 125 bool updateRequired = false; 126 127 foreach (OpenALHardwareDeviceSession session in _sessions.Keys) 128 { 129 if (session.Update()) 130 { 131 updateRequired = true; 132 } 133 } 134 135 if (updateRequired) 136 { 137 _updateRequiredEvent.Set(); 138 } 139 140 // If it's not slept it will waste cycles. 141 Thread.Sleep(10); 142 } 143 } 144 145 public void Dispose() 146 { 147 GC.SuppressFinalize(this); 148 Dispose(true); 149 } 150 151 protected virtual void Dispose(bool disposing) 152 { 153 if (disposing) 154 { 155 _stillRunning = false; 156 157 foreach (OpenALHardwareDeviceSession session in _sessions.Keys) 158 { 159 session.Dispose(); 160 } 161 162 ALC.DestroyContext(_context); 163 ALC.CloseDevice(_device); 164 165 _pauseEvent.Dispose(); 166 } 167 } 168 169 public bool SupportsSampleRate(uint sampleRate) 170 { 171 return true; 172 } 173 174 public bool SupportsSampleFormat(SampleFormat sampleFormat) 175 { 176 return true; 177 } 178 179 public bool SupportsChannelCount(uint channelCount) 180 { 181 return channelCount == 1 || channelCount == 2 || channelCount == 6; 182 } 183 184 public bool SupportsDirection(Direction direction) 185 { 186 return direction == Direction.Output; 187 } 188 } 189 }