| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- using Ryujinx.Audio.Common;
- using Ryujinx.Audio.Integration;
- using Ryujinx.Memory;
- using SoundIOSharp;
- using System;
- using System.Collections.Generic;
- using System.Threading;
- using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
- namespace Ryujinx.Audio.Backends.SoundIo
- {
- public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
- {
- private object _lock = new object();
- private SoundIO _audioContext;
- private SoundIODevice _audioDevice;
- private ManualResetEvent _updateRequiredEvent;
- private List<SoundIoHardwareDeviceSession> _sessions;
- private int _disposeState;
- public SoundIoHardwareDeviceDriver()
- {
- _audioContext = new SoundIO();
- _updateRequiredEvent = new ManualResetEvent(false);
- _sessions = new List<SoundIoHardwareDeviceSession>();
- _audioContext.Connect();
- _audioContext.FlushEvents();
- _audioDevice = FindNonRawDefaultAudioDevice(_audioContext, true);
- }
- public static bool IsSupported => IsSupportedInternal();
- private static bool IsSupportedInternal()
- {
- SoundIO context = null;
- SoundIODevice device = null;
- SoundIOOutStream stream = null;
- bool backendDisconnected = false;
- try
- {
- context = new SoundIO();
- context.OnBackendDisconnect = (i) =>
- {
- backendDisconnected = true;
- };
- context.Connect();
- context.FlushEvents();
- if (backendDisconnected)
- {
- return false;
- }
- if (context.OutputDeviceCount == 0)
- {
- return false;
- }
- device = FindNonRawDefaultAudioDevice(context);
- if (device == null || backendDisconnected)
- {
- return false;
- }
- stream = device.CreateOutStream();
- if (stream == null || backendDisconnected)
- {
- return false;
- }
- return true;
- }
- catch
- {
- return false;
- }
- finally
- {
- if (stream != null)
- {
- stream.Dispose();
- }
- if (context != null)
- {
- context.Dispose();
- }
- }
- }
- private static SoundIODevice FindNonRawDefaultAudioDevice(SoundIO audioContext, bool fallback = false)
- {
- SoundIODevice defaultAudioDevice = audioContext.GetOutputDevice(audioContext.DefaultOutputDeviceIndex);
- if (!defaultAudioDevice.IsRaw)
- {
- return defaultAudioDevice;
- }
- for (int i = 0; i < audioContext.BackendCount; i++)
- {
- SoundIODevice audioDevice = audioContext.GetOutputDevice(i);
- if (audioDevice.Id == defaultAudioDevice.Id && !audioDevice.IsRaw)
- {
- return audioDevice;
- }
- }
- return fallback ? defaultAudioDevice : null;
- }
- public ManualResetEvent GetUpdateRequiredEvent()
- {
- return _updateRequiredEvent;
- }
- public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
- {
- if (channelCount == 0)
- {
- channelCount = 2;
- }
- if (sampleRate == 0)
- {
- sampleRate = Constants.TargetSampleRate;
- }
- if (direction != Direction.Output)
- {
- throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
- }
- lock (_lock)
- {
- SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
- _sessions.Add(session);
- return session;
- }
- }
- internal void Unregister(SoundIoHardwareDeviceSession session)
- {
- lock (_lock)
- {
- _sessions.Remove(session);
- }
- }
- public static SoundIOFormat GetSoundIoFormat(SampleFormat format)
- {
- return format switch
- {
- SampleFormat.PcmInt8 => SoundIOFormat.S8,
- SampleFormat.PcmInt16 => SoundIOFormat.S16LE,
- SampleFormat.PcmInt24 => SoundIOFormat.S24LE,
- SampleFormat.PcmInt32 => SoundIOFormat.S32LE,
- SampleFormat.PcmFloat => SoundIOFormat.Float32LE,
- _ => throw new ArgumentException ($"Unsupported sample format {format}"),
- };
- }
- internal SoundIOOutStream OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
- {
- SoundIOFormat driverSampleFormat = GetSoundIoFormat(requestedSampleFormat);
- if (!_audioDevice.SupportsSampleRate((int)requestedSampleRate))
- {
- throw new ArgumentException($"This sound device does not support a sample rate of {requestedSampleRate}Hz");
- }
- if (!_audioDevice.SupportsFormat(driverSampleFormat))
- {
- throw new ArgumentException($"This sound device does not support {requestedSampleFormat}");
- }
- if (!_audioDevice.SupportsChannelCount((int)requestedChannelCount))
- {
- throw new ArgumentException($"This sound device does not support channel count {requestedChannelCount}");
- }
- SoundIOOutStream result = _audioDevice.CreateOutStream();
- result.Name = "Ryujinx";
- result.Layout = SoundIOChannelLayout.GetDefault((int)requestedChannelCount);
- result.Format = driverSampleFormat;
- result.SampleRate = (int)requestedSampleRate;
- return result;
- }
- internal void FlushContextEvents()
- {
- _audioContext.FlushEvents();
- }
- public void Dispose()
- {
- if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
- {
- Dispose(true);
- }
- }
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- int sessionCount = 0;
- // NOTE: This is done in a way to avoid possible situations when the SoundIoHardwareDeviceSession is already being dispose in another thread but doesn't hold the lock and tries to Unregister.
- do
- {
- lock (_lock)
- {
- if (_sessions.Count == 0)
- {
- break;
- }
- SoundIoHardwareDeviceSession session = _sessions[_sessions.Count - 1];
- session.Dispose();
- sessionCount = _sessions.Count;
- }
- }
- while (sessionCount > 0);
- _audioContext.Disconnect();
- _audioContext.Dispose();
- }
- }
- public bool SupportsSampleRate(uint sampleRate)
- {
- return _audioDevice.SupportsSampleRate((int)sampleRate);
- }
- public bool SupportsSampleFormat(SampleFormat sampleFormat)
- {
- return _audioDevice.SupportsFormat(GetSoundIoFormat(sampleFormat));
- }
- public bool SupportsChannelCount(uint channelCount)
- {
- return _audioDevice.SupportsChannelCount((int)channelCount);
- }
- public bool SupportsDirection(Direction direction)
- {
- // TODO: add direction input when supported.
- return direction == Direction.Output;
- }
- }
- }
|