| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- using OpenTK.Audio;
- using OpenTK.Audio.OpenAL;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- namespace Ryujinx.Audio.OpenAL
- {
- public class OpenALAudioOut : IAalOutput
- {
- private const int MaxTracks = 256;
- private AudioContext Context;
- private class Track : IDisposable
- {
- public int SourceId { get; private set; }
- public int SampleRate { get; private set; }
-
- public ALFormat Format { get; private set; }
- public PlaybackState State { get; set; }
- private ConcurrentDictionary<long, int> Buffers;
- private Queue<long> QueuedTagsQueue;
- private bool Disposed;
- public Track(int SampleRate, ALFormat Format)
- {
- this.SampleRate = SampleRate;
- this.Format = Format;
- State = PlaybackState.Stopped;
- SourceId = AL.GenSource();
- Buffers = new ConcurrentDictionary<long, int>();
- QueuedTagsQueue = new Queue<long>();
- }
- public int GetBufferId(long Tag)
- {
- if (Disposed)
- {
- throw new ObjectDisposedException(nameof(Track));
- }
- int Id = AL.GenBuffer();
- Buffers.AddOrUpdate(Tag, Id, (Key, OldId) =>
- {
- AL.DeleteBuffer(OldId);
- return Id;
- });
- QueuedTagsQueue.Enqueue(Tag);
- return Id;
- }
- public long[] GetReleasedBuffers()
- {
- ClearReleased();
- List<long> Tags = new List<long>();
- foreach (long Tag in Buffers.Keys)
- {
- if (!ContainsBuffer(Tag))
- {
- Tags.Add(Tag);
- }
- }
- return Tags.ToArray();
- }
- public void ClearReleased()
- {
- SyncQueuedTags();
- AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount);
- if (ReleasedCount > 0)
- {
- AL.SourceUnqueueBuffers(SourceId, ReleasedCount);
- }
- }
- public bool ContainsBuffer(long Tag)
- {
- SyncQueuedTags();
- foreach (long QueuedTag in QueuedTagsQueue)
- {
- if (QueuedTag == Tag)
- {
- return true;
- }
- }
- return false;
- }
-
- private void SyncQueuedTags()
- {
- AL.GetSource(SourceId, ALGetSourcei.BuffersQueued, out int QueuedCount);
- AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount);
- QueuedCount -= ReleasedCount;
- while (QueuedTagsQueue.Count > QueuedCount)
- {
- QueuedTagsQueue.Dequeue();
- }
- }
- public void Dispose()
- {
- Dispose(true);
- }
- protected virtual void Dispose(bool Disposing)
- {
- if (Disposing && !Disposed)
- {
- Disposed = true;
- AL.DeleteSource(SourceId);
- foreach (int Id in Buffers.Values)
- {
- AL.DeleteBuffer(Id);
- }
- }
- }
- }
- private ConcurrentDictionary<int, Track> Tracks;
- public OpenALAudioOut()
- {
- Context = new AudioContext();
- Tracks = new ConcurrentDictionary<int, Track>();
- }
- public int OpenTrack(int SampleRate, int Channels, out AudioFormat Format)
- {
- Format = AudioFormat.PcmInt16;
- Track Td = new Track(SampleRate, GetALFormat(Channels, Format));
- for (int Id = 0; Id < MaxTracks; Id++)
- {
- if (Tracks.TryAdd(Id, Td))
- {
- return Id;
- }
- }
- return -1;
- }
- private ALFormat GetALFormat(int Channels, AudioFormat Format)
- {
- if (Channels < 1 || Channels > 2)
- {
- throw new ArgumentOutOfRangeException(nameof(Channels));
- }
- if (Channels == 1)
- {
- switch (Format)
- {
- case AudioFormat.PcmInt8: return ALFormat.Mono8;
- case AudioFormat.PcmInt16: return ALFormat.Mono16;
- }
- }
- else /* if (Channels == 2) */
- {
- switch (Format)
- {
- case AudioFormat.PcmInt8: return ALFormat.Stereo8;
- case AudioFormat.PcmInt16: return ALFormat.Stereo16;
- }
- }
- throw new ArgumentException(nameof(Format));
- }
- public void CloseTrack(int Track)
- {
- if (Tracks.TryRemove(Track, out Track Td))
- {
- Td.Dispose();
- }
- }
- public void AppendBuffer(int Track, long Tag, byte[] Buffer)
- {
- if (Tracks.TryGetValue(Track, out Track Td))
- {
- int BufferId = Td.GetBufferId(Tag);
- AL.BufferData(BufferId, Td.Format, Buffer, Buffer.Length, Td.SampleRate);
- AL.SourceQueueBuffer(Td.SourceId, BufferId);
- StartPlaybackIfNeeded(Td);
- }
- }
- public bool ContainsBuffer(int Track, long Tag)
- {
- if (Tracks.TryGetValue(Track, out Track Td))
- {
- return Td.ContainsBuffer(Tag);
- }
-
- return false;
- }
- public long[] GetReleasedBuffers(int Track)
- {
- if (Tracks.TryGetValue(Track, out Track Td))
- {
- return Td.GetReleasedBuffers();
- }
-
- return null;
- }
- public void Start(int Track)
- {
- if (Tracks.TryGetValue(Track, out Track Td))
- {
- Td.State = PlaybackState.Playing;
- StartPlaybackIfNeeded(Td);
- }
- }
- private void StartPlaybackIfNeeded(Track Td)
- {
- AL.GetSource(Td.SourceId, ALGetSourcei.SourceState, out int StateInt);
- ALSourceState State = (ALSourceState)StateInt;
- if (State != ALSourceState.Playing && Td.State == PlaybackState.Playing)
- {
- Td.ClearReleased();
- AL.SourcePlay(Td.SourceId);
- }
- }
- public void Stop(int Track)
- {
- if (Tracks.TryGetValue(Track, out Track Td))
- {
- Td.State = PlaybackState.Stopped;
- AL.SourceStop(Td.SourceId);
- }
- }
- public PlaybackState GetState(int Track)
- {
- if (Tracks.TryGetValue(Track, out Track Td))
- {
- return Td.State;
- }
- return PlaybackState.Stopped;
- }
- }
- }
|