WAV static sounds + static sound instance pool
parent
08a3c01f66
commit
b1b6b84809
|
@ -1 +1 @@
|
||||||
Subproject commit de0c1f833c12a992af5c7daebe1705cd2c72f743
|
Subproject commit 0b6d5dabbf428633482fe3a956fbdb53228fcf35
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4e9088b49de46ea8b4285948cfe69875ac4c2290
|
Subproject commit b35aaa494e44d08242788ff0ba2cb7a508f4d8f0
|
|
@ -6,8 +6,8 @@ namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public abstract class SoundInstance : AudioResource
|
public abstract class SoundInstance : AudioResource
|
||||||
{
|
{
|
||||||
internal IntPtr Handle { get; }
|
internal IntPtr Handle;
|
||||||
internal FAudio.FAudioWaveFormatEx Format { get; }
|
internal FAudio.FAudioWaveFormatEx Format;
|
||||||
public bool Loop { get; }
|
public bool Loop { get; }
|
||||||
|
|
||||||
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
||||||
|
@ -163,17 +163,19 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
public SoundInstance(
|
public SoundInstance(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
|
ushort formatTag,
|
||||||
|
ushort bitsPerSample,
|
||||||
|
ushort blockAlign,
|
||||||
ushort channels,
|
ushort channels,
|
||||||
uint samplesPerSecond,
|
uint samplesPerSecond,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
var blockAlign = (ushort) (4 * channels);
|
|
||||||
var format = new FAudio.FAudioWaveFormatEx
|
var format = new FAudio.FAudioWaveFormatEx
|
||||||
{
|
{
|
||||||
wFormatTag = 3,
|
wFormatTag = formatTag,
|
||||||
wBitsPerSample = 32,
|
wBitsPerSample = bitsPerSample,
|
||||||
nChannels = channels,
|
nChannels = channels,
|
||||||
nBlockAlign = blockAlign,
|
nBlockAlign = blockAlign,
|
||||||
nSamplesPerSec = samplesPerSecond,
|
nSamplesPerSec = samplesPerSecond,
|
||||||
|
@ -184,8 +186,8 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
FAudio.FAudio_CreateSourceVoice(
|
FAudio.FAudio_CreateSourceVoice(
|
||||||
Device.Handle,
|
Device.Handle,
|
||||||
out var handle,
|
out Handle,
|
||||||
ref format,
|
ref Format,
|
||||||
FAudio.FAUDIO_VOICE_USEFILTER,
|
FAudio.FAUDIO_VOICE_USEFILTER,
|
||||||
FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
|
FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
|
||||||
IntPtr.Zero,
|
IntPtr.Zero,
|
||||||
|
@ -193,20 +195,22 @@ namespace MoonWorks.Audio
|
||||||
IntPtr.Zero
|
IntPtr.Zero
|
||||||
);
|
);
|
||||||
|
|
||||||
if (handle == IntPtr.Zero)
|
if (Handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Logger.LogError("SoundInstance failed to initialize!");
|
Logger.LogError("SoundInstance failed to initialize!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle = handle;
|
|
||||||
this.is3D = is3D;
|
this.is3D = is3D;
|
||||||
InitDSPSettings(Format.nChannels);
|
InitDSPSettings(Format.nChannels);
|
||||||
|
|
||||||
|
// FIXME: not everything should be running through reverb...
|
||||||
|
/*
|
||||||
FAudio.FAudioVoice_SetOutputVoices(
|
FAudio.FAudioVoice_SetOutputVoices(
|
||||||
handle,
|
Handle,
|
||||||
ref Device.ReverbSends
|
ref Device.ReverbSends
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
Loop = loop;
|
Loop = loop;
|
||||||
State = SoundState.Stopped;
|
State = SoundState.Stopped;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
|
@ -6,12 +8,17 @@ namespace MoonWorks.Audio
|
||||||
public class StaticSound : AudioResource
|
public class StaticSound : AudioResource
|
||||||
{
|
{
|
||||||
internal FAudio.FAudioBuffer Handle;
|
internal FAudio.FAudioBuffer Handle;
|
||||||
|
public ushort FormatTag { get; }
|
||||||
|
public ushort BitsPerSample { get; }
|
||||||
public ushort Channels { get; }
|
public ushort Channels { get; }
|
||||||
public uint SamplesPerSecond { get; }
|
public uint SamplesPerSecond { get; }
|
||||||
|
public ushort BlockAlign { get; }
|
||||||
|
|
||||||
public uint LoopStart { get; set; } = 0;
|
public uint LoopStart { get; set; } = 0;
|
||||||
public uint LoopLength { get; set; } = 0;
|
public uint LoopLength { get; set; } = 0;
|
||||||
|
|
||||||
|
private Stack<StaticSoundInstance> Instances = new Stack<StaticSoundInstance>();
|
||||||
|
|
||||||
public static StaticSound LoadOgg(AudioDevice device, string filePath)
|
public static StaticSound LoadOgg(AudioDevice device, string filePath)
|
||||||
{
|
{
|
||||||
var filePointer = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
|
var filePointer = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
|
||||||
|
@ -43,6 +50,194 @@ namespace MoonWorks.Audio
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385
|
||||||
|
public static StaticSound LoadWav(AudioDevice device, string filePath)
|
||||||
|
{
|
||||||
|
// Sample data
|
||||||
|
byte[] data;
|
||||||
|
|
||||||
|
// WaveFormatEx data
|
||||||
|
ushort wFormatTag;
|
||||||
|
ushort nChannels;
|
||||||
|
uint nSamplesPerSec;
|
||||||
|
uint nAvgBytesPerSec;
|
||||||
|
ushort nBlockAlign;
|
||||||
|
ushort wBitsPerSample;
|
||||||
|
int samplerLoopStart = 0;
|
||||||
|
int samplerLoopEnd = 0;
|
||||||
|
|
||||||
|
using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath)))
|
||||||
|
{
|
||||||
|
// RIFF Signature
|
||||||
|
string signature = new string(reader.ReadChars(4));
|
||||||
|
if (signature != "RIFF")
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Specified stream is not a wave file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.ReadUInt32(); // Riff Chunk Size
|
||||||
|
|
||||||
|
string wformat = new string(reader.ReadChars(4));
|
||||||
|
if (wformat != "WAVE")
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Specified stream is not a wave file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// WAVE Header
|
||||||
|
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();
|
||||||
|
|
||||||
|
wFormatTag = reader.ReadUInt16();
|
||||||
|
nChannels = reader.ReadUInt16();
|
||||||
|
nSamplesPerSec = reader.ReadUInt32();
|
||||||
|
nAvgBytesPerSec = reader.ReadUInt32();
|
||||||
|
nBlockAlign = reader.ReadUInt16();
|
||||||
|
wBitsPerSample = reader.ReadUInt16();
|
||||||
|
|
||||||
|
// Reads residual bytes
|
||||||
|
if (format_chunk_size > 16)
|
||||||
|
{
|
||||||
|
reader.ReadBytes(format_chunk_size - 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// data Signature
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Scan for other chunks
|
||||||
|
while (reader.PeekChar() != -1)
|
||||||
|
{
|
||||||
|
char[] chunkIDChars = reader.ReadChars(4);
|
||||||
|
if (chunkIDChars.Length < 4)
|
||||||
|
{
|
||||||
|
break; // EOL!
|
||||||
|
}
|
||||||
|
byte[] chunkSizeBytes = reader.ReadBytes(4);
|
||||||
|
if (chunkSizeBytes.Length < 4)
|
||||||
|
{
|
||||||
|
break; // EOL!
|
||||||
|
}
|
||||||
|
string chunk_signature = new string(chunkIDChars);
|
||||||
|
int chunkDataSize = BitConverter.ToInt32(chunkSizeBytes, 0);
|
||||||
|
if (chunk_signature == "smpl") // "smpl", Sampler Chunk Found
|
||||||
|
{
|
||||||
|
reader.ReadUInt32(); // Manufacturer
|
||||||
|
reader.ReadUInt32(); // Product
|
||||||
|
reader.ReadUInt32(); // Sample Period
|
||||||
|
reader.ReadUInt32(); // MIDI Unity Note
|
||||||
|
reader.ReadUInt32(); // MIDI Pitch Fraction
|
||||||
|
reader.ReadUInt32(); // SMPTE Format
|
||||||
|
reader.ReadUInt32(); // SMPTE Offset
|
||||||
|
uint numSampleLoops = reader.ReadUInt32();
|
||||||
|
int samplerData = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < numSampleLoops; i += 1)
|
||||||
|
{
|
||||||
|
reader.ReadUInt32(); // Cue Point ID
|
||||||
|
reader.ReadUInt32(); // Type
|
||||||
|
int start = reader.ReadInt32();
|
||||||
|
int end = reader.ReadInt32();
|
||||||
|
reader.ReadUInt32(); // Fraction
|
||||||
|
reader.ReadUInt32(); // Play Count
|
||||||
|
|
||||||
|
if (i == 0) // Grab loopStart and loopEnd from first sample loop
|
||||||
|
{
|
||||||
|
samplerLoopStart = start;
|
||||||
|
samplerLoopEnd = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (samplerData != 0) // Read Sampler Data if it exists
|
||||||
|
{
|
||||||
|
reader.ReadBytes(samplerData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Read unwanted chunk data and try again
|
||||||
|
{
|
||||||
|
reader.ReadBytes(chunkDataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End scan
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StaticSound(
|
||||||
|
device,
|
||||||
|
wFormatTag,
|
||||||
|
wBitsPerSample,
|
||||||
|
nBlockAlign,
|
||||||
|
nChannels,
|
||||||
|
nSamplesPerSec,
|
||||||
|
data,
|
||||||
|
0,
|
||||||
|
(uint) data.Length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StaticSound(
|
||||||
|
AudioDevice device,
|
||||||
|
ushort formatTag,
|
||||||
|
ushort bitsPerSample,
|
||||||
|
ushort blockAlign,
|
||||||
|
ushort channels,
|
||||||
|
uint samplesPerSecond,
|
||||||
|
byte[] buffer,
|
||||||
|
uint bufferOffset, /* number of bytes */
|
||||||
|
uint bufferLength /* number of bytes */
|
||||||
|
) : base(device)
|
||||||
|
{
|
||||||
|
FormatTag = formatTag;
|
||||||
|
BitsPerSample = bitsPerSample;
|
||||||
|
BlockAlign = blockAlign;
|
||||||
|
Channels = channels;
|
||||||
|
SamplesPerSecond = samplesPerSecond;
|
||||||
|
|
||||||
|
Handle = new FAudio.FAudioBuffer();
|
||||||
|
Handle.Flags = FAudio.FAUDIO_END_OF_STREAM;
|
||||||
|
Handle.pContext = IntPtr.Zero;
|
||||||
|
Handle.AudioBytes = bufferLength;
|
||||||
|
Handle.pAudioData = Marshal.AllocHGlobal((int) bufferLength);
|
||||||
|
Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, (int) bufferLength);
|
||||||
|
Handle.PlayBegin = 0;
|
||||||
|
Handle.PlayLength = 0;
|
||||||
|
|
||||||
|
if (formatTag == 1)
|
||||||
|
{
|
||||||
|
Handle.PlayLength = (uint) (
|
||||||
|
bufferLength /
|
||||||
|
channels /
|
||||||
|
(bitsPerSample / 8)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (formatTag == 2)
|
||||||
|
{
|
||||||
|
Handle.PlayLength = (uint) (
|
||||||
|
bufferLength /
|
||||||
|
blockAlign *
|
||||||
|
(((blockAlign / channels) - 6) * 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoopStart = 0;
|
||||||
|
LoopLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public StaticSound(
|
public StaticSound(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
ushort channels,
|
ushort channels,
|
||||||
|
@ -52,6 +247,9 @@ namespace MoonWorks.Audio
|
||||||
uint bufferLength /* in floats */
|
uint bufferLength /* in floats */
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
|
FormatTag = 3;
|
||||||
|
BitsPerSample = 32;
|
||||||
|
BlockAlign = (ushort) (4 * channels);
|
||||||
Channels = channels;
|
Channels = channels;
|
||||||
SamplesPerSecond = samplesPerSecond;
|
SamplesPerSecond = samplesPerSecond;
|
||||||
|
|
||||||
|
@ -69,9 +267,23 @@ namespace MoonWorks.Audio
|
||||||
LoopLength = 0;
|
LoopLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StaticSoundInstance CreateInstance(bool loop = false)
|
/// <summary>
|
||||||
|
/// Gets a sound instance from the pool.
|
||||||
|
/// NOTE: If you lose track of instances, you will create garbage collection pressure!
|
||||||
|
/// </summary>
|
||||||
|
public StaticSoundInstance GetInstance(bool loop = false)
|
||||||
{
|
{
|
||||||
return new StaticSoundInstance(Device, this, false, loop);
|
if (Instances.Count == 0)
|
||||||
|
{
|
||||||
|
Instances.Push(new StaticSoundInstance(Device, this, false, loop));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instances.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void FreeInstance(StaticSoundInstance instance)
|
||||||
|
{
|
||||||
|
Instances.Push(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace MoonWorks.Audio
|
||||||
StaticSound parent,
|
StaticSound parent,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device, parent.Channels, parent.SamplesPerSecond, is3D, loop)
|
) : base(device, parent.FormatTag, parent.BitsPerSample, parent.BlockAlign, parent.Channels, parent.SamplesPerSecond, is3D, loop)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
}
|
}
|
||||||
|
@ -92,5 +92,10 @@ namespace MoonWorks.Audio
|
||||||
FAudio.FAudioSourceVoice_ExitLoop(Handle, 0);
|
FAudio.FAudioSourceVoice_ExitLoop(Handle, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Free()
|
||||||
|
{
|
||||||
|
Parent.FreeInstance(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,14 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
public StreamingSound(
|
public StreamingSound(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
|
ushort formatTag,
|
||||||
|
ushort bitsPerSample,
|
||||||
|
ushort blockAlign,
|
||||||
ushort channels,
|
ushort channels,
|
||||||
uint samplesPerSecond,
|
uint samplesPerSecond,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device, channels, samplesPerSecond, is3D, loop) { }
|
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond, is3D, loop) { }
|
||||||
|
|
||||||
public override void Play()
|
public override void Play()
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,7 +46,16 @@ namespace MoonWorks.Audio
|
||||||
FAudio.stb_vorbis_info info,
|
FAudio.stb_vorbis_info info,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device, (ushort) info.channels, info.sample_rate, is3D, loop)
|
) : base(
|
||||||
|
device,
|
||||||
|
3, /* float type */
|
||||||
|
32, /* size of float */
|
||||||
|
(ushort) (4 * info.channels),
|
||||||
|
(ushort) info.channels,
|
||||||
|
info.sample_rate,
|
||||||
|
is3D,
|
||||||
|
loop
|
||||||
|
)
|
||||||
{
|
{
|
||||||
FileHandle = fileHandle;
|
FileHandle = fileHandle;
|
||||||
Info = info;
|
Info = info;
|
||||||
|
|
Loading…
Reference in New Issue