MoonWorks/src/Audio/SourceVoice.cs

193 lines
4.6 KiB
C#

using System;
namespace MoonWorks.Audio
{
/// <summary>
/// Emits audio from submitted audio buffers.
/// </summary>
public class SourceVoice : Voice
{
private Format format;
public Format Format => format;
protected object StateLock = new object();
public uint BuffersQueued
{
get
{
FAudio.FAudioSourceVoice_GetState(
Handle,
out var state,
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
);
return state.BuffersQueued;
}
}
private SoundState state;
public SoundState State
{
get
{
if (BuffersQueued == 0)
{
Stop();
}
return state;
}
internal set
{
state = value;
}
}
public delegate void OnUpdateFunc();
public OnUpdateFunc OnUpdate; // called by AudioDevice thread
public SourceVoice(
AudioDevice device,
Format format
) : base(device, format.Channels, device.DeviceDetails.OutputFormat.Format.nChannels)
{
this.format = format;
var fAudioFormat = format.ToFAudioFormat();
FAudio.FAudio_CreateSourceVoice(
device.Handle,
out handle,
ref fAudioFormat,
FAudio.FAUDIO_VOICE_USEFILTER,
FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
IntPtr.Zero,
IntPtr.Zero, // default sends to mastering voice!
IntPtr.Zero
);
}
/// <summary>
/// Starts consumption and processing of audio by the voice.
/// Delivers the result to any connected submix or mastering voice.
/// </summary>
/// <param name="syncGroup">Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called.</param>
public void Play(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
{
lock (StateLock)
{
FAudio.FAudioSourceVoice_Start(Handle, 0, syncGroup);
State = SoundState.Playing;
}
}
/// <summary>
/// Pauses playback.
/// All source buffers that are queued on the voice and the current cursor position are preserved.
/// </summary>
/// <param name="syncGroup">Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called.</param>
public void Pause(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
{
lock (StateLock)
{
FAudio.FAudioSourceVoice_Stop(Handle, 0, syncGroup);
State = SoundState.Paused;
}
}
/// <summary>
/// Stops looping the voice when it reaches the end of the current loop region.
/// If the cursor for the voice is not in a loop region, ExitLoop does nothing.
/// </summary>
/// <param name="syncGroup">Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called.</param>
public void ExitLoop(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
{
lock (StateLock)
{
FAudio.FAudioSourceVoice_ExitLoop(Handle, syncGroup);
}
}
/// <summary>
/// Stops playback and removes all pending audio buffers from the voice queue.
/// </summary>
/// <param name="syncGroup">Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called.</param>
public void Stop(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
{
lock (StateLock)
{
FAudio.FAudioSourceVoice_Stop(Handle, 0, syncGroup);
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
State = SoundState.Stopped;
}
}
/// <summary>
/// Adds a static sound to the voice queue.
/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
/// </summary>
/// <param name="sound">The sound to submit to the voice.</param>
/// <param name="loop">Designates that the voice will loop the submitted buffer.</param>
public void Submit(StaticSound sound, bool loop = false)
{
if (loop)
{
sound.Buffer.LoopCount = FAudio.FAUDIO_LOOP_INFINITE;
}
else
{
sound.Buffer.LoopCount = 0;
}
Submit(sound.Buffer);
}
/// <summary>
/// Adds an FAudio buffer to the voice queue.
/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
/// </summary>
/// <param name="buffer">The buffer to submit to the voice.</param>
public void Submit(FAudio.FAudioBuffer buffer)
{
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
Handle,
ref buffer,
IntPtr.Zero
);
}
public void Submit(StreamingSound streamingSound)
{
}
/// <summary>
/// Designates that this source voice will return to the voice pool once all its buffers are exhausted.
/// </summary>
public void ReturnWhenIdle()
{
Device.ReturnWhenIdle(this);
}
/// <summary>
/// Returns this source voice to the voice pool.
/// </summary>
public void Return()
{
Stop();
Reset();
Device.Return(this);
}
protected override unsafe void Destroy()
{
Stop();
base.Destroy();
}
}
}