using System; namespace MoonWorks.Audio { /// /// Emits audio from submitted audio buffers. /// 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 ); } /// /// Starts consumption and processing of audio by the voice. /// Delivers the result to any connected submix or mastering voice. /// /// Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called. public void Play(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW) { lock (StateLock) { FAudio.FAudioSourceVoice_Start(Handle, 0, syncGroup); State = SoundState.Playing; } } /// /// Pauses playback. /// All source buffers that are queued on the voice and the current cursor position are preserved. /// /// Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called. public void Pause(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW) { lock (StateLock) { FAudio.FAudioSourceVoice_Stop(Handle, 0, syncGroup); State = SoundState.Paused; } } /// /// 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. /// /// Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called. public void ExitLoop(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW) { lock (StateLock) { FAudio.FAudioSourceVoice_ExitLoop(Handle, syncGroup); } } /// /// Stops playback and removes all pending audio buffers from the voice queue. /// /// Optional. Denotes that the operation will be pending until AudioDevice.TriggerSyncGroup is called. public void Stop(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW) { lock (StateLock) { FAudio.FAudioSourceVoice_Stop(Handle, 0, syncGroup); FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle); State = SoundState.Stopped; } } /// /// 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. /// /// The sound to submit to the voice. /// Designates that the voice will loop the submitted buffer. 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); } /// /// 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. /// /// The buffer to submit to the voice. public void Submit(FAudio.FAudioBuffer buffer) { FAudio.FAudioSourceVoice_SubmitSourceBuffer( Handle, ref buffer, IntPtr.Zero ); } public void Submit(StreamingSound streamingSound) { } /// /// Designates that this source voice will return to the voice pool once all its buffers are exhausted. /// public void ReturnWhenIdle() { Device.ReturnWhenIdle(this); } /// /// Returns this source voice to the voice pool. /// public void Return() { Stop(); Reset(); Device.Return(this); } protected override unsafe void Destroy() { Stop(); base.Destroy(); } } }