2021-04-14 11:28:43 +01:00
using OpenTK.Audio.OpenAL ;
2021-02-26 00:11:56 +00:00
using Ryujinx.Audio.Common ;
using Ryujinx.Audio.Integration ;
using Ryujinx.Memory ;
using System ;
using System.Collections.Generic ;
2021-04-14 11:28:43 +01:00
using System.Linq ;
2021-02-26 00:11:56 +00:00
using System.Threading ;
using static Ryujinx . Audio . Integration . IHardwareDeviceDriver ;
namespace Ryujinx.Audio.Backends.OpenAL
{
public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
{
private object _lock = new object ( ) ;
2021-04-14 11:28:43 +01:00
private ALDevice _device ;
private ALContext _context ;
2021-02-26 00:11:56 +00:00
private ManualResetEvent _updateRequiredEvent ;
private List < OpenALHardwareDeviceSession > _sessions ;
private bool _stillRunning ;
private Thread _updaterThread ;
public OpenALHardwareDeviceDriver ( )
{
2021-04-14 11:28:43 +01:00
_device = ALC . OpenDevice ( "" ) ;
_context = ALC . CreateContext ( _device , new ALContextAttributes ( ) ) ;
2021-02-26 00:11:56 +00:00
_updateRequiredEvent = new ManualResetEvent ( false ) ;
_sessions = new List < OpenALHardwareDeviceSession > ( ) ;
_stillRunning = true ;
_updaterThread = new Thread ( Update )
{
Name = "HardwareDeviceDriver.OpenAL"
} ;
_updaterThread . Start ( ) ;
}
public static bool IsSupported
{
get
{
try
{
2021-04-14 11:28:43 +01:00
return ALC . GetStringList ( GetEnumerationStringList . DeviceSpecifier ) . Any ( ) ;
2021-02-26 00:11:56 +00:00
}
catch
{
return false ;
}
}
}
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 ArgumentException ( $"{direction}" ) ;
}
else if ( ! SupportsChannelCount ( channelCount ) )
{
throw new ArgumentException ( $"{channelCount}" ) ;
}
lock ( _lock )
{
OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession ( this , memoryManager , sampleFormat , sampleRate , channelCount ) ;
_sessions . Add ( session ) ;
return session ;
}
}
internal void Unregister ( OpenALHardwareDeviceSession session )
{
lock ( _lock )
{
_sessions . Remove ( session ) ;
}
}
public ManualResetEvent GetUpdateRequiredEvent ( )
{
return _updateRequiredEvent ;
}
private void Update ( )
{
2021-04-14 11:28:43 +01:00
ALC . MakeContextCurrent ( _context ) ;
2021-02-26 00:11:56 +00:00
while ( _stillRunning )
{
bool updateRequired = false ;
lock ( _lock )
{
foreach ( OpenALHardwareDeviceSession session in _sessions )
{
if ( session . Update ( ) )
{
updateRequired = true ;
}
}
}
if ( updateRequired )
{
_updateRequiredEvent . Set ( ) ;
}
// If it's not slept it will waste cycles.
Thread . Sleep ( 10 ) ;
}
}
public void Dispose ( )
{
Dispose ( true ) ;
}
protected virtual void Dispose ( bool disposing )
{
if ( disposing )
{
2021-06-29 18:37:13 +01:00
_stillRunning = false ;
2021-02-26 00:11:56 +00:00
2021-06-29 18:37:13 +01:00
int sessionCount = 0 ;
// NOTE: This is done in a way to avoid possible situations when the OpenALHardwareDeviceSession is already being dispose in another thread but doesn't hold the lock and tries to Unregister.
do
{
lock ( _lock )
2021-02-26 00:11:56 +00:00
{
2021-06-29 18:37:13 +01:00
if ( _sessions . Count = = 0 )
{
break ;
}
OpenALHardwareDeviceSession session = _sessions [ _sessions . Count - 1 ] ;
2021-02-26 00:11:56 +00:00
session . Dispose ( ) ;
2021-06-29 18:37:13 +01:00
sessionCount = _sessions . Count ;
2021-02-26 00:11:56 +00:00
}
}
2021-06-29 18:37:13 +01:00
while ( sessionCount > 0 ) ;
2021-02-26 00:11:56 +00:00
2021-04-14 11:28:43 +01:00
ALC . DestroyContext ( _context ) ;
ALC . CloseDevice ( _device ) ;
2021-02-26 00:11:56 +00:00
}
}
public bool SupportsSampleRate ( uint sampleRate )
{
return true ;
}
public bool SupportsSampleFormat ( SampleFormat sampleFormat )
{
return true ;
}
public bool SupportsChannelCount ( uint channelCount )
{
return channelCount = = 1 | | channelCount = = 2 | | channelCount = = 6 ;
}
public bool SupportsDirection ( Direction direction )
{
return direction = = Direction . Output ;
}
}
}