Skip to content

Instantly share code, notes, and snippets.

@ThirdPartyNinjas
Created August 15, 2019 19:29
Show Gist options
  • Save ThirdPartyNinjas/bfc1c53ec4604d60e8be169b992e3fdb to your computer and use it in GitHub Desktop.
Save ThirdPartyNinjas/bfc1c53ec4604d60e8be169b992e3fdb to your computer and use it in GitHub Desktop.
FNA Vorbis Player
using Microsoft.Xna.Framework.Audio;
using System;
namespace Fenix.Audio
{
public class Vorbis : IDisposable
{
public int Channels { get; private set; }
public int SampleRate { get; private set; }
public float Duration { get; private set; }
public SoundState SoundState { get; private set; }
public Vorbis(string assetPath)
{
stbVorbisData = FAudio.stb_vorbis_open_filename(assetPath, out int error, IntPtr.Zero);
var info = FAudio.stb_vorbis_get_info(stbVorbisData);
Channels = info.channels;
SampleRate = (int)info.sample_rate;
Duration = FAudio.stb_vorbis_stream_length_in_seconds(stbVorbisData);
SoundState = SoundState.Stopped;
dynamicSoundEffectInstance = new DynamicSoundEffectInstance(SampleRate, (Channels == 1) ? AudioChannels.Mono : AudioChannels.Stereo);
sampleReadBuffer = new float[SampleRate * Channels / 10];
}
public void Play(int playCount = 1, Action<Vorbis> playCompletedAction = null)
{
if(SoundState != SoundState.Stopped)
{
// todo: log warning
return;
}
Reset();
FillBuffer();
this.playCount = playCount;
this.playCompletedAction = playCompletedAction;
dynamicSoundEffectInstance.BufferNeeded += DynamicSoundEffectInstance_BufferNeeded;
dynamicSoundEffectInstance.Play();
SoundState = SoundState.Playing;
}
public void Pause()
{
if (SoundState != SoundState.Playing)
{
// todo: log warning
return;
}
dynamicSoundEffectInstance.Pause();
SoundState = SoundState.Paused;
}
public void Resume()
{
if (SoundState != SoundState.Paused)
{
// todo: log warning
return;
}
dynamicSoundEffectInstance.Resume();
SoundState = SoundState.Playing;
}
public void Stop()
{
if (SoundState != SoundState.Playing && SoundState != SoundState.Paused)
{
// todo: log warning
return;
}
dynamicSoundEffectInstance.Stop();
dynamicSoundEffectInstance.BufferNeeded -= DynamicSoundEffectInstance_BufferNeeded;
playCompletedAction = null;
SoundState = SoundState.Stopped;
}
public void Reset()
{
FAudio.stb_vorbis_seek_start(stbVorbisData);
}
public DynamicSoundEffectInstance GetInstance()
{
return dynamicSoundEffectInstance;
}
private void DynamicSoundEffectInstance_BufferNeeded(object sender, EventArgs e)
{
FillBuffer();
}
private void FillBuffer()
{
var framesRead = ReadFrames(sampleReadBuffer, SampleRate / 10);
if (framesRead != 0)
{
dynamicSoundEffectInstance.SubmitFloatBufferEXT(sampleReadBuffer, 0, framesRead * Channels);
}
else if (dynamicSoundEffectInstance.PendingBufferCount == 0)
{
if (playCount > 0)
playCount--;
if (playCount == 0)
{
Action<Vorbis> pca = playCompletedAction;
Stop();
pca?.Invoke(this);
}
else
{
Reset();
FillBuffer();
}
}
}
private int ReadFrames(float[] interleavedSamples, int frameCount)
{
return FAudio.stb_vorbis_get_samples_float_interleaved(stbVorbisData, Channels, interleavedSamples, frameCount * Channels);
}
public void Dispose()
{
playCompletedAction = null;
if (stbVorbisData != IntPtr.Zero)
{
FAudio.stb_vorbis_close(stbVorbisData);
stbVorbisData = IntPtr.Zero;
}
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
Dispose();
disposed = true;
}
private bool disposed = false;
private IntPtr stbVorbisData = IntPtr.Zero;
private int playCount;
private DynamicSoundEffectInstance dynamicSoundEffectInstance;
private float[] sampleReadBuffer;
private Action<Vorbis> playCompletedAction = null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment