/ src / Ryujinx.Audio.Backends.OpenAL / OpenALHardwareDeviceDriver.cs
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  }