242 lines
5.8 KiB
C#
Raw Normal View History

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
}
}