#region License
#endregion
#region Using Statements
using
System;
using
System.IO;
using
System.Collections.Generic;
#endregion
namespace
Microsoft.Xna.Framework.Audio
{
public
sealed
class
SoundEffect : IDisposable
{
#region Public Properties
public
TimeSpan Duration
{
get
{
return
INTERNAL_buffer.Duration;
}
}
public
bool
IsDisposed
{
get
;
private
set
;
}
public
string
Name
{
get
;
set
;
}
#endregion
#region Public Static Properties
private
static
float
INTERNAL_masterVolume = 1.0f;
public
static
float
MasterVolume
{
get
{
return
INTERNAL_masterVolume;
}
set
{
INTERNAL_masterVolume = value;
}
}
private
static
float
INTERNAL_distanceScale = 1.0f;
public
static
float
DistanceScale
{
get
{
return
INTERNAL_distanceScale;
}
set
{
if
(value <= 0.0f)
{
throw
new
ArgumentOutOfRangeException(
"value of DistanceScale"
);
}
INTERNAL_distanceScale = value;
}
}
private
static
float
INTERNAL_dopplerScale = 1.0f;
public
static
float
DopplerScale
{
get
{
return
INTERNAL_dopplerScale;
}
set
{
if
(value <= 0.0f)
{
throw
new
ArgumentOutOfRangeException(
"value of DopplerScale"
);
}
INTERNAL_dopplerScale = value;
}
}
private
static
float
INTERNAL_speedOfSound = 343.5f;
public
static
float
SpeedOfSound
{
get
{
return
INTERNAL_speedOfSound;
}
set
{
INTERNAL_speedOfSound = value;
}
}
#endregion
#region Internal Variables
internal
List<WeakReference> Instances =
new
List<WeakReference>();
internal
IALBuffer INTERNAL_buffer;
#endregion
#region Public Constructors
public
SoundEffect(
byte
[] buffer,
int
sampleRate,
AudioChannels channels
) {
INTERNAL_buffer = AudioDevice.GenBuffer(
buffer,
(
uint
) sampleRate,
(
uint
) channels,
0,
0,
false
,
1
);
}
public
SoundEffect(
byte
[] buffer,
int
offset,
int
count,
int
sampleRate,
AudioChannels channels,
int
loopStart,
int
loopLength
) {
byte
[] sendBuf;
if
(offset != 0 || count != buffer.Length)
{
sendBuf =
new
byte
[count];
Array.Copy(buffer, offset, sendBuf, 0, count);
}
else
{
sendBuf = buffer;
}
INTERNAL_buffer = AudioDevice.GenBuffer(
sendBuf,
(
uint
) sampleRate,
(
uint
) channels,
(
uint
) loopStart,
(
uint
) (loopStart + loopLength),
false
,
1
);
}
#endregion
#region Internal Constructors
internal
SoundEffect(Stream s)
{
INTERNAL_loadAudioStream(s);
}
internal
SoundEffect(
string
name,
byte
[] buffer,
uint
sampleRate,
uint
channels,
uint
loopStart,
uint
loopLength,
bool
isADPCM,
uint
formatParameter
) {
Name = name;
INTERNAL_buffer = AudioDevice.GenBuffer(
buffer,
sampleRate,
channels,
loopStart,
loopStart + loopLength,
isADPCM,
formatParameter
);
}
#endregion
#region Destructor
~SoundEffect()
{
Dispose();
}
#endregion
#region Public Dispose Method
public
void
Dispose()
{
if
(!IsDisposed)
{
foreach
(WeakReference instance
in
Instances.ToArray())
{
object
target = instance.Target;
if
(target !=
null
)
{
(target
as
IDisposable).Dispose();
}
}
Instances.Clear();
if
(INTERNAL_buffer !=
null
)
{
AudioDevice.ALDevice.DeleteBuffer(INTERNAL_buffer);
}
IsDisposed =
true
;
}
}
#endregion
#region Additional SoundEffect/SoundEffectInstance Creation Methods
public
SoundEffectInstance CreateInstance()
{
return
new
SoundEffectInstance(
this
);
}
public
static
SoundEffect FromStream(Stream stream)
{
return
new
SoundEffect(stream);
}
#endregion
#region Public Play Methods
public
bool
Play()
{
return
Play(MasterVolume, 0.0f, 0.0f);
}
public
bool
Play(
float
volume,
float
pitch,
float
pan)
{
SoundEffectInstance instance = CreateInstance();
instance.Volume = volume;
instance.Pitch = pitch;
instance.Pan = pan;
instance.Play();
if
(instance.State != SoundState.Playing)
{
instance.Dispose();
return
false
;
}
AudioDevice.InstancePool.Add(instance);
return
true
;
}
#endregion
#region Private WAV Loading Method
private
void
INTERNAL_loadAudioStream(Stream s)
{
byte
[] data;
uint
sampleRate = 0;
uint
numChannels = 0;
bool
isADPCM =
false
;
uint
formatParameter = 0;
using
(BinaryReader reader =
new
BinaryReader(s))
{
string
signature =
new
string
(reader.ReadChars(4));
if
(signature !=
"RIFF"
)
{
throw
new
NotSupportedException(
"Specified stream is not a wave file."
);
}
reader.ReadUInt32();
string
wformat =
new
string
(reader.ReadChars(4));
if
(wformat !=
"WAVE"
)
{
throw
new
NotSupportedException(
"Specified stream is not a wave file."
);
}
string
format_signature =
new
string
(reader.ReadChars(4));
while
(format_signature !=
"fmt "
)
{
reader.ReadBytes(reader.ReadInt32());
format_signature =
new
string
(reader.ReadChars(4));
}
int
format_chunk_size = reader.ReadInt32();
uint
audio_format = reader.ReadUInt16();
numChannels = reader.ReadUInt16();
sampleRate = reader.ReadUInt32();
reader.ReadUInt32();
ushort
blockAlign = reader.ReadUInt16();
ushort
bitDepth = reader.ReadUInt16();
if
(audio_format == 1)
{
System.Diagnostics.Debug.Assert(bitDepth == 8 || bitDepth == 16);
formatParameter = (
uint
) (bitDepth / 16);
}
else
if
(audio_format != 2)
{
isADPCM =
true
;
formatParameter = (((blockAlign / numChannels) - 6) * 2);
}
else
{
throw
new
NotSupportedException(
"Wave format is not supported."
);
}
if
(format_chunk_size > 16)
{
reader.ReadBytes(format_chunk_size - 16);
}
string
data_signature =
new
string
(reader.ReadChars(4));
while
(data_signature.ToLowerInvariant() !=
"data"
)
{
reader.ReadBytes(reader.ReadInt32());
data_signature =
new
string
(reader.ReadChars(4));
}
if
(data_signature !=
"data"
)
{
throw
new
NotSupportedException(
"Specified wave file is not supported."
);
}
int
waveDataLength = reader.ReadInt32();
data = reader.ReadBytes(waveDataLength);
}
INTERNAL_buffer = AudioDevice.GenBuffer(
data,
sampleRate,
numChannels,
0,
0,
isADPCM,
formatParameter
);
}
#endregion
#region Public Static Methods
public
static
TimeSpan GetSampleDuration(
int
sizeInBytes,
int
sampleRate,
AudioChannels channels
) {
sizeInBytes /= 2;
int
ms = (
int
) (
(sizeInBytes / (
int
) channels) /
(sampleRate / 1000.0f)
);
return
new
TimeSpan(0, 0, 0, 0, ms);
}
public
static
int
GetSampleSizeInBytes(
TimeSpan duration,
int
sampleRate,
AudioChannels channels
) {
return
(
int
) (
duration.TotalSeconds *
sampleRate *
(
int
) channels *
2
);
}
#endregion
}
}