SND\AstrorEnales_cp ef734ddcd3 Implemented the MediaPlayer and MediaQueue classes as preparation for native Song playback.
Also added the FrameworkDispatcher calls in the Game class.
Checking for possible fire and forget sound instances to be disposed in the FrameworkDispatcher update chain.
2015-03-15 01:11:17 +01:00

242 lines
5.8 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using ANX.Framework.NonXNA;
using ANX.Framework.NonXNA.Development;
using ANX.Framework.NonXNA.SoundSystem;
// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license
namespace ANX.Framework.Audio
{
[PercentageComplete(100)]
[TestState(TestStateAttribute.TestState.InProgress)]
[Developer("AstrorEnales")]
public sealed class SoundEffect : IDisposable
{
#region Static
public static float DistanceScale
{
get { return GetCreator().DistanceScale; }
set { GetCreator().DistanceScale = value; }
}
public static float DopplerScale
{
get { return GetCreator().DopplerScale; }
set { GetCreator().DopplerScale = value; }
}
public static float MasterVolume
{
get { return GetCreator().MasterVolume; }
set { GetCreator().MasterVolume = value; }
}
public static float SpeedOfSound
{
get { return GetCreator().SpeedOfSound; }
set { GetCreator().SpeedOfSound = value; }
}
#endregion
#region Private
private static readonly List<SoundEffectInstance> fireAndForgetInstances;
private readonly List<WeakReference> children;
internal ISoundEffect NativeSoundEffect;
#endregion
#region Public
public TimeSpan Duration
{
get
{
return NativeSoundEffect.Duration;
}
}
public bool IsDisposed
{
get;
private set;
}
public string Name
{
get;
set;
}
#endregion
#region Constructor
static SoundEffect()
{
fireAndForgetInstances = new List<SoundEffectInstance>();
MasterVolume = 1f;
SpeedOfSound = 343.5f;
DopplerScale = 1f;
DistanceScale = 1f;
}
private SoundEffect()
{
children = new List<WeakReference>();
IsDisposed = false;
}
private SoundEffect(Stream stream)
: this()
{
var creator = GetCreator();
NativeSoundEffect = creator.CreateSoundEffect(this, stream);
}
public SoundEffect(byte[] buffer, int sampleRate, AudioChannels channels)
: this(buffer, 0, buffer.Length, sampleRate, channels, 0, 0)
{
}
public SoundEffect(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart,
int loopLength)
: this()
{
var creator = GetCreator();
NativeSoundEffect = creator.CreateSoundEffect(this, buffer, offset, count, sampleRate, channels, loopStart,
loopLength);
}
~SoundEffect()
{
Dispose();
}
#endregion
#region GetCreator
private static ISoundSystemCreator GetCreator()
{
return AddInSystemFactory.Instance.GetDefaultCreator<ISoundSystemCreator>();
}
#endregion
#region CreateInstance
public SoundEffectInstance CreateInstance()
{
return new SoundEffectInstance(this, false);
}
#endregion
#region FromStream
public static SoundEffect FromStream(Stream stream)
{
return new SoundEffect(stream);
}
#endregion
#region GetSampleDuration
public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate, AudioChannels channels)
{
float sizeMulBlockAlign = (float)sizeInBytes / ((int)channels * 2);
return TimeSpan.FromMilliseconds(sizeMulBlockAlign * 1000f / sampleRate);
}
#endregion
#region GetSampleSizeInBytes
public static int GetSampleSizeInBytes(TimeSpan duration, int sampleRate, AudioChannels channels)
{
int timeMulSamples = (int)(duration.TotalMilliseconds * (sampleRate / 1000f));
return (timeMulSamples + timeMulSamples % (int)channels) * ((int)channels * 2);
}
#endregion
#region Play
public bool Play()
{
return Play(1f, 1f, 0f);
}
public bool Play(float volume, float pitch, float pan)
{
if (IsDisposed)
return false;
try
{
var newInstance = new SoundEffectInstance(this, true)
{
Volume = volume,
Pitch = pitch,
Pan = pan,
};
children.Add(new WeakReference(newInstance));
lock (fireAndForgetInstances)
fireAndForgetInstances.Add(newInstance);
newInstance.Play();
}
catch (Exception ex)
{
Logger.Warning("Failed to play sound effect cause of: " + ex);
return false;
}
return true;
}
#endregion
#region RecycleStoppedFireAndForgetInstances
internal static void RecycleStoppedFireAndForgetInstances()
{
lock (fireAndForgetInstances)
{
var instancesToDispose = new List<SoundEffectInstance>();
foreach (SoundEffectInstance current in fireAndForgetInstances)
if (current.State == SoundState.Stopped)
instancesToDispose.Add(current);
foreach (SoundEffectInstance current in instancesToDispose)
{
current.Dispose();
fireAndForgetInstances.Remove(current);
}
}
}
#endregion
#region Dispose
public void Dispose()
{
if (IsDisposed)
return;
IsDisposed = true;
NativeSoundEffect.Dispose();
NativeSoundEffect = null;
var weakRefs = new List<WeakReference>(children);
lock (fireAndForgetInstances)
{
foreach (WeakReference current in weakRefs)
{
var soundInstance = current.Target as SoundEffectInstance;
if (soundInstance == null)
continue;
if (soundInstance.IsFireAndForget)
fireAndForgetInstances.Remove(soundInstance);
soundInstance.Dispose();
}
}
weakRefs.Clear();
children.Clear();
}
#endregion
}
}