/ src / Ryujinx.Audio.Backends.OpenAL / OpenALHardwareDeviceSession.cs
OpenALHardwareDeviceSession.cs
  1  using OpenTK.Audio.OpenAL;
  2  using Ryujinx.Audio.Backends.Common;
  3  using Ryujinx.Audio.Common;
  4  using Ryujinx.Memory;
  5  using System;
  6  using System.Collections.Generic;
  7  using System.Diagnostics;
  8  
  9  namespace Ryujinx.Audio.Backends.OpenAL
 10  {
 11      class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase
 12      {
 13          private readonly OpenALHardwareDeviceDriver _driver;
 14          private readonly int _sourceId;
 15          private readonly ALFormat _targetFormat;
 16          private bool _isActive;
 17          private readonly Queue<OpenALAudioBuffer> _queuedBuffers;
 18          private ulong _playedSampleCount;
 19          private float _volume;
 20  
 21          private readonly object _lock = new();
 22  
 23          public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
 24          {
 25              _driver = driver;
 26              _queuedBuffers = new Queue<OpenALAudioBuffer>();
 27              _sourceId = AL.GenSource();
 28              _targetFormat = GetALFormat();
 29              _isActive = false;
 30              _playedSampleCount = 0;
 31              SetVolume(1f);
 32          }
 33  
 34          private ALFormat GetALFormat()
 35          {
 36              return RequestedSampleFormat switch
 37              {
 38                  SampleFormat.PcmInt16 => RequestedChannelCount switch
 39                  {
 40                      1 => ALFormat.Mono16,
 41                      2 => ALFormat.Stereo16,
 42                      6 => ALFormat.Multi51Chn16Ext,
 43                      _ => throw new NotImplementedException($"Unsupported channel config {RequestedChannelCount}"),
 44                  },
 45                  _ => throw new NotImplementedException($"Unsupported sample format {RequestedSampleFormat}"),
 46              };
 47          }
 48  
 49          public override void PrepareToClose() { }
 50  
 51          private void StartIfNotPlaying()
 52          {
 53              AL.GetSource(_sourceId, ALGetSourcei.SourceState, out int stateInt);
 54  
 55              ALSourceState State = (ALSourceState)stateInt;
 56  
 57              if (State != ALSourceState.Playing)
 58              {
 59                  AL.SourcePlay(_sourceId);
 60              }
 61          }
 62  
 63          public override void QueueBuffer(AudioBuffer buffer)
 64          {
 65              lock (_lock)
 66              {
 67                  OpenALAudioBuffer driverBuffer = new()
 68                  {
 69                      DriverIdentifier = buffer.DataPointer,
 70                      BufferId = AL.GenBuffer(),
 71                      SampleCount = GetSampleCount(buffer),
 72                  };
 73  
 74                  AL.BufferData(driverBuffer.BufferId, _targetFormat, buffer.Data, (int)RequestedSampleRate);
 75  
 76                  _queuedBuffers.Enqueue(driverBuffer);
 77  
 78                  AL.SourceQueueBuffer(_sourceId, driverBuffer.BufferId);
 79  
 80                  if (_isActive)
 81                  {
 82                      StartIfNotPlaying();
 83                  }
 84              }
 85          }
 86  
 87          public override void SetVolume(float volume)
 88          {
 89              _volume = volume;
 90  
 91              UpdateMasterVolume(_driver.Volume);
 92          }
 93  
 94          public override float GetVolume()
 95          {
 96              return _volume;
 97          }
 98  
 99          public void UpdateMasterVolume(float newVolume)
100          {
101              lock (_lock)
102              {
103                  AL.Source(_sourceId, ALSourcef.Gain, newVolume * _volume);
104              }
105          }
106  
107          public override void Start()
108          {
109              lock (_lock)
110              {
111                  _isActive = true;
112  
113                  StartIfNotPlaying();
114              }
115          }
116  
117          public override void Stop()
118          {
119              lock (_lock)
120              {
121                  SetVolume(0.0f);
122  
123                  AL.SourceStop(_sourceId);
124  
125                  _isActive = false;
126              }
127          }
128  
129          public override void UnregisterBuffer(AudioBuffer buffer) { }
130  
131          public override bool WasBufferFullyConsumed(AudioBuffer buffer)
132          {
133              lock (_lock)
134              {
135                  if (!_queuedBuffers.TryPeek(out OpenALAudioBuffer driverBuffer))
136                  {
137                      return true;
138                  }
139  
140                  return driverBuffer.DriverIdentifier != buffer.DataPointer;
141              }
142          }
143  
144          public override ulong GetPlayedSampleCount()
145          {
146              lock (_lock)
147              {
148                  return _playedSampleCount;
149              }
150          }
151  
152          public bool Update()
153          {
154              lock (_lock)
155              {
156                  if (_isActive)
157                  {
158                      AL.GetSource(_sourceId, ALGetSourcei.BuffersProcessed, out int releasedCount);
159  
160                      if (releasedCount > 0)
161                      {
162                          int[] bufferIds = new int[releasedCount];
163  
164                          AL.SourceUnqueueBuffers(_sourceId, releasedCount, bufferIds);
165  
166                          int i = 0;
167  
168                          while (_queuedBuffers.TryPeek(out OpenALAudioBuffer buffer) && i < bufferIds.Length)
169                          {
170                              if (buffer.BufferId == bufferIds[i])
171                              {
172                                  _playedSampleCount += buffer.SampleCount;
173  
174                                  _queuedBuffers.TryDequeue(out _);
175  
176                                  i++;
177                              }
178                          }
179  
180                          Debug.Assert(i == bufferIds.Length, "Unknown buffer ids found!");
181  
182                          AL.DeleteBuffers(bufferIds);
183                      }
184  
185                      return releasedCount > 0;
186                  }
187  
188                  return false;
189              }
190          }
191  
192          protected virtual void Dispose(bool disposing)
193          {
194              if (disposing && _driver.Unregister(this))
195              {
196                  lock (_lock)
197                  {
198                      PrepareToClose();
199                      Stop();
200  
201                      AL.DeleteSource(_sourceId);
202                  }
203              }
204          }
205  
206          public override void Dispose()
207          {
208              Dispose(true);
209              GC.SuppressFinalize(this);
210          }
211      }
212  }