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 NextBuffers = new List(); private List NextSizes = new List(); 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(); } }