mysimulation/server/tso.common/Audio/MP3Player.cs
Tony Bark 22191ce648 Removed NioTSO client and server
- NioTSO client isn't needed because we're using RayLib
- Added FreeSO's API server to handle most backend operations
2024-05-01 02:55:43 -04:00

288 lines
7.9 KiB
C#
Executable file

using Mp3Sharp;
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Audio;
using System.Threading;
using System.Threading.Tasks;
namespace FSO.Common.Audio
{
public class MP3Player : ISFXInstanceLike
{
public static bool NewMode = true;
private Mp3Stream Stream;
public DynamicSoundEffectInstance Inst;
private int LastChunkSize = 1; //don't die immediately..
private Thread DecoderThread;
private List<byte[]> NextBuffers = new List<byte[]>();
private List<int> NextSizes = new List<int>();
private int Requests;
private AutoResetEvent DecodeNext;
private AutoResetEvent BufferDone;
private bool EndOfStream;
private bool Active = true;
private Thread MainThread; //keep track of this, terminate when it closes.
private SoundState _State = SoundState.Stopped;
private bool Disposed = false;
private float _Volume = 1f;
private float _Pan;
private object ControlLock = new object();
private string Path;
public int SendExtra = 2;
private static byte[] Blank = new byte[65536];
public MP3Player(string path)
{
Path = path;
// //let's get started...
DecodeNext = new AutoResetEvent(true);
BufferDone = new AutoResetEvent(false);
MainThread = Thread.CurrentThread;
Task.Run((Action)Start);
}
public void Start()
{
Stream = new Mp3Stream(Path);
Stream.DecodeFrames(1);
var freq = Stream.Frequency;
lock (ControlLock)
{
if (Disposed) return;
Inst = new DynamicSoundEffectInstance(freq, AudioChannels.Stereo);
Inst.IsLooped = false;
Inst.BufferNeeded += SubmitBufferAsync;
if (_State == SoundState.Playing) Inst.Play();
else if (_State == SoundState.Paused)
{
Inst.Play();
Inst.Pause();
}
Inst.Volume = _Volume;
Inst.Pan = _Pan;
Requests = 2;
}
//SubmitBuffer(null, null);
//SubmitBuffer(null, null);
DecoderThread = new Thread(() =>
{
try
{
while (Active && MainThread.IsAlive)
{
DecodeNext.WaitOne(128);
bool go;
lock (this) go = Requests > 0;
while (go)
{
var buf = new byte[262144];// 524288];
var read = Stream.Read(buf, 0, buf.Length);
lock (this)
{
Requests--;
NextBuffers.Add(buf);
NextSizes.Add(read);
if (read == 0)
{
EndOfStream = true;
BufferDone.Set();
return;
}
BufferDone.Set();
}
lock (this) go = Requests > 0;
}
}
}
catch (Exception e) { }
});
DecoderThread.Start();
DecodeNext.Set();
}
public void Play()
{
lock (ControlLock)
{
_State = SoundState.Playing;
Inst?.Play();
}
}
public void Stop()
{
lock (ControlLock)
{
_State = SoundState.Stopped;
Inst?.Stop();
}
}
public void Pause()
{
lock (ControlLock)
{
_State = SoundState.Paused;
Inst?.Pause();
}
}
public void Resume()
{
lock (ControlLock)
{
_State = SoundState.Playing;
Inst?.Resume();
}
}
public void Dispose()
{
lock (ControlLock)
{
Disposed = true;
Inst?.Dispose();
Stream?.Dispose();
Active = false;
DecodeNext.Set(); //end the mp3 thread immediately
EndOfStream = true;
}
}
public bool IsEnded()
{
return EndOfStream && Inst.PendingBufferCount == 0;
}
public float Volume
{
get
{
lock (ControlLock)
{
if (Inst != null) return Inst.Volume;
else return _Volume;
}
}
set
{
lock (ControlLock)
{
_Volume = value;
if (Inst != null) Inst.Volume = value;
}
}
}
public float Pan
{
get
{
lock (ControlLock)
{
if (Inst != null) return Inst.Pan;
else return _Pan;
}
}
set
{
lock (ControlLock)
{
_Pan = value;
if (Inst != null) Inst.Pan = value;
}
}
}
public SoundState State
{
get
{
lock (ControlLock)
{
if (Inst != null) return Inst.State;
else return _State;
}
}
}
public bool IsLooped { get; set; }
private void SubmitBuffer(object sender, EventArgs e)
{
byte[] buffer = new byte[524288];
lock (this)
{
var read = Stream.Read(buffer, 0, buffer.Length);
LastChunkSize = read;
if (read == 0)
{
return;
}
Inst.SubmitBuffer(buffer, 0, read);
}
}
private void SubmitBufferAsync(object sender, EventArgs e)
{
while (true)
{
if (EndOfStream) return;
var gotData = false;
lock (this)
{
if (NextBuffers.Count > 0)
{
if (NextSizes[0] > 0) Inst.SubmitBuffer(NextBuffers[0], 0, NextSizes[0]);
gotData = true;
NextBuffers.RemoveAt(0);
NextSizes.RemoveAt(0);
Requests++;
DecodeNext.Set();
if (SendExtra > 0)
{
SendExtra--;
continue;
}
return;
}
if (EndOfStream) return;
}
if (!gotData)
{
Inst.SubmitBuffer(Blank, 0, Blank.Length);
Requests++;
DecodeNext.Set();
return;
//if (NewMode) BufferDone.WaitOne(128);
}
}
}
}
public interface ISFXInstanceLike
{
float Volume { get; set; }
float Pan { get; set; }
SoundState State { get; }
bool IsLooped { get; set; }
void Play();
void Stop();
void Pause();
void Resume();
void Dispose();
}
}