WAV static sounds + static sound instance pool
parent
26dc361d31
commit
7c97cf8bdd
|
@ -6,8 +6,8 @@ namespace MoonWorks.Audio
|
|||
{
|
||||
public abstract class SoundInstance : AudioResource
|
||||
{
|
||||
internal IntPtr Handle { get; }
|
||||
internal FAudio.FAudioWaveFormatEx Format { get; }
|
||||
internal IntPtr Handle;
|
||||
internal FAudio.FAudioWaveFormatEx Format;
|
||||
public bool Loop { get; }
|
||||
|
||||
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
||||
|
@ -163,17 +163,19 @@ namespace MoonWorks.Audio
|
|||
|
||||
public SoundInstance(
|
||||
AudioDevice device,
|
||||
ushort formatTag,
|
||||
ushort bitsPerSample,
|
||||
ushort blockAlign,
|
||||
ushort channels,
|
||||
uint samplesPerSecond,
|
||||
bool is3D,
|
||||
bool loop
|
||||
) : base(device)
|
||||
{
|
||||
var blockAlign = (ushort) (4 * channels);
|
||||
var format = new FAudio.FAudioWaveFormatEx
|
||||
{
|
||||
wFormatTag = 3,
|
||||
wBitsPerSample = 32,
|
||||
wFormatTag = formatTag,
|
||||
wBitsPerSample = bitsPerSample,
|
||||
nChannels = channels,
|
||||
nBlockAlign = blockAlign,
|
||||
nSamplesPerSec = samplesPerSecond,
|
||||
|
@ -184,8 +186,8 @@ namespace MoonWorks.Audio
|
|||
|
||||
FAudio.FAudio_CreateSourceVoice(
|
||||
Device.Handle,
|
||||
out var handle,
|
||||
ref format,
|
||||
out Handle,
|
||||
ref Format,
|
||||
FAudio.FAUDIO_VOICE_USEFILTER,
|
||||
FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
|
||||
IntPtr.Zero,
|
||||
|
@ -193,20 +195,22 @@ namespace MoonWorks.Audio
|
|||
IntPtr.Zero
|
||||
);
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
if (Handle == IntPtr.Zero)
|
||||
{
|
||||
Logger.LogError("SoundInstance failed to initialize!");
|
||||
return;
|
||||
}
|
||||
|
||||
Handle = handle;
|
||||
this.is3D = is3D;
|
||||
InitDSPSettings(Format.nChannels);
|
||||
|
||||
// FIXME: not everything should be running through reverb...
|
||||
/*
|
||||
FAudio.FAudioVoice_SetOutputVoices(
|
||||
handle,
|
||||
Handle,
|
||||
ref Device.ReverbSends
|
||||
);
|
||||
*/
|
||||
|
||||
Loop = loop;
|
||||
State = SoundState.Stopped;
|
||||
|
|
218
StaticSound.cs
218
StaticSound.cs
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MoonWorks.Audio
|
||||
|
@ -6,12 +8,17 @@ namespace MoonWorks.Audio
|
|||
public class StaticSound : AudioResource
|
||||
{
|
||||
internal FAudio.FAudioBuffer Handle;
|
||||
public ushort FormatTag { get; }
|
||||
public ushort BitsPerSample { get; }
|
||||
public ushort Channels { get; }
|
||||
public uint SamplesPerSecond { get; }
|
||||
public ushort BlockAlign { get; }
|
||||
|
||||
public uint LoopStart { get; set; } = 0;
|
||||
public uint LoopLength { get; set; } = 0;
|
||||
|
||||
private Stack<StaticSoundInstance> Instances = new Stack<StaticSoundInstance>();
|
||||
|
||||
public static StaticSound LoadOgg(AudioDevice device, string filePath)
|
||||
{
|
||||
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(
|
||||
AudioDevice device,
|
||||
ushort channels,
|
||||
|
@ -52,6 +247,9 @@ namespace MoonWorks.Audio
|
|||
uint bufferLength /* in floats */
|
||||
) : base(device)
|
||||
{
|
||||
FormatTag = 3;
|
||||
BitsPerSample = 32;
|
||||
BlockAlign = (ushort) (4 * channels);
|
||||
Channels = channels;
|
||||
SamplesPerSecond = samplesPerSecond;
|
||||
|
||||
|
@ -69,9 +267,23 @@ namespace MoonWorks.Audio
|
|||
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()
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace MoonWorks.Audio
|
|||
StaticSound parent,
|
||||
bool is3D,
|
||||
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;
|
||||
}
|
||||
|
@ -92,5 +92,10 @@ namespace MoonWorks.Audio
|
|||
FAudio.FAudioSourceVoice_ExitLoop(Handle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
Parent.FreeInstance(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,14 @@ namespace MoonWorks.Audio
|
|||
|
||||
public StreamingSound(
|
||||
AudioDevice device,
|
||||
ushort formatTag,
|
||||
ushort bitsPerSample,
|
||||
ushort blockAlign,
|
||||
ushort channels,
|
||||
uint samplesPerSecond,
|
||||
bool is3D,
|
||||
bool loop
|
||||
) : base(device, channels, samplesPerSecond, is3D, loop) { }
|
||||
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond, is3D, loop) { }
|
||||
|
||||
public override void Play()
|
||||
{
|
||||
|
|
|
@ -46,7 +46,16 @@ namespace MoonWorks.Audio
|
|||
FAudio.stb_vorbis_info info,
|
||||
bool is3D,
|
||||
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;
|
||||
Info = info;
|
||||
|
|
Loading…
Reference in New Issue