documentation + fix some edge cases

pull/50/head
cosmonaut 2023-08-02 18:26:27 -07:00
parent 0500d94930
commit 9a854506f3
13 changed files with 65 additions and 40 deletions

View File

@ -3,6 +3,10 @@ using System.Runtime.InteropServices;
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary>
/// Contains raw audio data in the format specified by Format.
/// Submit this to a SourceVoice to play audio.
/// </summary>
public class AudioBuffer : AudioResource public class AudioBuffer : AudioResource
{ {
IntPtr BufferDataPtr; IntPtr BufferDataPtr;
@ -24,11 +28,22 @@ namespace MoonWorks.Audio
OwnsBufferData = ownsBufferData; OwnsBufferData = ownsBufferData;
} }
/// <summary>
/// Create another AudioBuffer from this audio buffer.
/// It will not own the buffer data.
/// </summary>
/// <param name="offset">Offset in bytes from the top of the original buffer.</param>
/// <param name="length">Length in bytes of the new buffer.</param>
/// <returns></returns>
public AudioBuffer CreateSubBuffer(int offset, uint length) public AudioBuffer CreateSubBuffer(int offset, uint length)
{ {
return new AudioBuffer(Device, Format, BufferDataPtr + offset, length, false); return new AudioBuffer(Device, Format, BufferDataPtr + offset, length, false);
} }
/// <summary>
/// Create an FAudioBuffer struct from this AudioBuffer.
/// </summary>
/// <param name="loop">Whether we should set the FAudioBuffer to loop.</param>
public FAudio.FAudioBuffer ToFAudioBuffer(bool loop = false) public FAudio.FAudioBuffer ToFAudioBuffer(bool loop = false)
{ {
return new FAudio.FAudioBuffer return new FAudio.FAudioBuffer

View File

@ -4,6 +4,9 @@ using System.Runtime.InteropServices;
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary>
/// Streamable audio in Ogg format.
/// </summary>
public class AudioDataOgg : AudioDataStreamable public class AudioDataOgg : AudioDataStreamable
{ {
private IntPtr FileDataPtr = IntPtr.Zero; private IntPtr FileDataPtr = IntPtr.Zero;

View File

@ -4,6 +4,9 @@ using System.Runtime.InteropServices;
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary>
/// Streamable audio in QOA format.
/// </summary>
public class AudioDataQoa : AudioDataStreamable public class AudioDataQoa : AudioDataStreamable
{ {
private IntPtr QoaHandle = IntPtr.Zero; private IntPtr QoaHandle = IntPtr.Zero;

View File

@ -1,5 +1,8 @@
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary>
/// Use this in conjunction with a StreamingVoice to play back streaming audio data.
/// </summary>
public abstract class AudioDataStreamable : AudioResource public abstract class AudioDataStreamable : AudioResource
{ {
public Format Format { get; protected set; } public Format Format { get; protected set; }

View File

@ -6,9 +6,14 @@ namespace MoonWorks.Audio
{ {
public static class AudioDataWav public static class AudioDataWav
{ {
// mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385 /// <summary>
/// Create an AudioBuffer containing all the WAV audio data in a file.
/// </summary>
/// <returns></returns>
public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath) public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath)
{ {
// mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385
// WaveFormatEx data // WaveFormatEx data
ushort wFormatTag; ushort wFormatTag;
ushort nChannels; ushort nChannels;

View File

@ -1,33 +0,0 @@
using System.IO;
namespace MoonWorks.Audio
{
public static class AudioUtils
{
public static Format ReadWaveFormat(string filePath, out int dataLength)
{
var fileInfo = new FileInfo(filePath);
using FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using BinaryReader br = new BinaryReader(fs);
fs.Position = 20;
var formatTag = br.ReadInt16();
fs.Position = 22;
var channels = br.ReadInt16();
fs.Position = 24;
var sampleRate = br.ReadInt32();
fs.Position = 34;
var bitsPerSample = br.ReadInt16();
fs.Position = 40;
dataLength = br.ReadInt32();
return new Format
{
Tag = (FormatTag) formatTag,
Channels = (ushort) channels,
SampleRate = (uint) sampleRate,
BitsPerSample = (ushort) bitsPerSample
};
}
}
}

View File

@ -1,5 +1,8 @@
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary>
/// PersistentVoice should be used when you need to maintain a long-term reference to a source voice.
/// </summary>
public class PersistentVoice : SourceVoice, IPoolable<PersistentVoice> public class PersistentVoice : SourceVoice, IPoolable<PersistentVoice>
{ {
public PersistentVoice(AudioDevice device, Format format) : base(device, format) public PersistentVoice(AudioDevice device, Format format) : base(device, format)

View File

@ -3,7 +3,9 @@ using System.Runtime.InteropServices;
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
// sound instances can send their audio to this voice to add reverb /// <summary>
/// Use this in conjunction with SourceVoice.SetReverbEffectChain to add reverb to a voice.
/// </summary>
public unsafe class ReverbEffect : SubmixVoice public unsafe class ReverbEffect : SubmixVoice
{ {
public ReverbEffect(AudioDevice audioDevice) : base(audioDevice, 1, audioDevice.DeviceDetails.OutputFormat.Format.nSamplesPerSec) public ReverbEffect(AudioDevice audioDevice) : base(audioDevice, 1, audioDevice.DeviceDetails.OutputFormat.Format.nSamplesPerSec)

View File

@ -1,6 +1,8 @@
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
// NOTE: all sounds played with a SoundSequence must have the same audio format! /// <summary>
/// Plays back a series of AudioBuffers in sequence. Set the OnSoundNeeded callback to add AudioBuffers dynamically.
/// </summary>
public class SoundSequence : SourceVoice public class SoundSequence : SourceVoice
{ {
public int NeedSoundThreshold = 0; public int NeedSoundThreshold = 0;

View File

@ -10,6 +10,8 @@ namespace MoonWorks.Audio
private Format format; private Format format;
public Format Format => format; public Format Format => format;
protected bool PlaybackInitiated;
/// <summary> /// <summary>
/// The number of buffers queued in the voice. /// The number of buffers queued in the voice.
/// This includes the currently playing voice! /// This includes the currently playing voice!
@ -170,6 +172,13 @@ namespace MoonWorks.Audio
} }
} }
public override void Reset()
{
Stop();
PlaybackInitiated = false;
base.Reset();
}
protected override unsafe void Destroy() protected override unsafe void Destroy()
{ {
Stop(); Stop();

View File

@ -3,6 +3,9 @@ using System.Runtime.InteropServices;
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary>
/// Use in conjunction with an AudioDataStreamable object to play back streaming audio data.
/// </summary>
public class StreamingVoice : SourceVoice, IPoolable<StreamingVoice> public class StreamingVoice : SourceVoice, IPoolable<StreamingVoice>
{ {
private const int BUFFER_COUNT = 3; private const int BUFFER_COUNT = 3;
@ -54,6 +57,12 @@ namespace MoonWorks.Audio
} }
} }
public override void Reset()
{
Unload();
base.Reset();
}
public override void Update() public override void Update()
{ {
lock (StateLock) lock (StateLock)
@ -67,7 +76,7 @@ namespace MoonWorks.Audio
} }
} }
protected void QueueBuffers() private void QueueBuffers()
{ {
int buffersNeeded = BUFFER_COUNT - (int) BuffersQueued; // don't get got by uint underflow! int buffersNeeded = BUFFER_COUNT - (int) BuffersQueued; // don't get got by uint underflow!
for (int i = 0; i < buffersNeeded; i += 1) for (int i = 0; i < buffersNeeded; i += 1)
@ -76,7 +85,7 @@ namespace MoonWorks.Audio
} }
} }
protected unsafe void AddBuffer() private unsafe void AddBuffer()
{ {
var buffer = buffers[nextBufferIndex]; var buffer = buffers[nextBufferIndex];
nextBufferIndex = (nextBufferIndex + 1) % BUFFER_COUNT; nextBufferIndex = (nextBufferIndex + 1) % BUFFER_COUNT;

View File

@ -2,6 +2,9 @@ using System;
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary>
/// SourceVoices can send audio to a SubmixVoice for convenient effects processing.
/// </summary>
public class SubmixVoice : Voice public class SubmixVoice : Voice
{ {
public SubmixVoice( public SubmixVoice(

View File

@ -1,7 +1,8 @@
namespace MoonWorks.Audio namespace MoonWorks.Audio
{ {
/// <summary> /// <summary>
/// These voices are intended for playing one-off sound effects that don't have a long term reference. /// TransientVoice is intended for playing one-off sound effects that don't have a long term reference.
/// It will be automatically returned to the source voice pool once it is done playing back.
/// </summary> /// </summary>
public class TransientVoice : SourceVoice, IPoolable<TransientVoice> public class TransientVoice : SourceVoice, IPoolable<TransientVoice>
{ {
@ -18,7 +19,7 @@ namespace MoonWorks.Audio
{ {
lock (StateLock) lock (StateLock)
{ {
if (BuffersQueued == 0) if (PlaybackInitiated && BuffersQueued == 0)
{ {
Return(); Return();
} }