Skip to content

Instantly share code, notes, and snippets.

@markheath
Created February 3, 2014 13:42
Show Gist options
  • Select an option

  • Save markheath/8783999 to your computer and use it in GitHub Desktop.

Select an option

Save markheath/8783999 to your computer and use it in GitHub Desktop.

Revisions

  1. markheath created this gist Feb 3, 2014.
    57 changes: 57 additions & 0 deletions AudioPlaybackEngine.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    using System;
    using NAudio.Wave;
    using NAudio.Wave.SampleProviders;

    namespace FireAndForgetAudioSample
    {
    class AudioPlaybackEngine : IDisposable
    {
    private readonly IWavePlayer outputDevice;
    private readonly MixingSampleProvider mixer;

    public AudioPlaybackEngine(int sampleRate = 44100, int channelCount = 2)
    {
    outputDevice = new WaveOutEvent();
    mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channelCount));
    mixer.ReadFully = true;
    outputDevice.Init(mixer);
    outputDevice.Play();
    }

    public void PlaySound(string fileName)
    {
    var input = new AudioFileReader(fileName);
    AddMixerInput(new AutoDisposeFileReader(input));
    }

    private ISampleProvider ConvertToRightChannelCount(ISampleProvider input)
    {
    if (input.WaveFormat.Channels == mixer.WaveFormat.Channels)
    {
    return input;
    }
    if (input.WaveFormat.Channels == 1 && mixer.WaveFormat.Channels == 2)
    {
    return new MonoToStereoSampleProvider(input);
    }
    throw new NotImplementedException("Not yet implemented this channel count conversion");
    }

    public void PlaySound(CachedSound sound)
    {
    AddMixerInput(new CachedSoundSampleProvider(sound));
    }

    private void AddMixerInput(ISampleProvider input)
    {
    mixer.AddMixerInput(ConvertToRightChannelCount(input));
    }

    public void Dispose()
    {
    outputDevice.Dispose();
    }

    public static readonly AudioPlaybackEngine Instance = new AudioPlaybackEngine(44100, 2);
    }
    }
    30 changes: 30 additions & 0 deletions AutoDisposeFileReader
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    using NAudio.Wave;

    namespace FireAndForgetAudioSample
    {
    class AutoDisposeFileReader : ISampleProvider
    {
    private readonly AudioFileReader reader;
    private bool isDisposed;
    public AutoDisposeFileReader(AudioFileReader reader)
    {
    this.reader = reader;
    this.WaveFormat = reader.WaveFormat;
    }

    public int Read(float[] buffer, int offset, int count)
    {
    if (isDisposed)
    return 0;
    int read = reader.Read(buffer, offset, count);
    if (read == 0)
    {
    reader.Dispose();
    isDisposed = true;
    }
    return read;
    }

    public WaveFormat WaveFormat { get; private set; }
    }
    }
    28 changes: 28 additions & 0 deletions CachedSound.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    using System.Collections.Generic;
    using System.Linq;
    using NAudio.Wave;

    namespace FireAndForgetAudioSample
    {
    class CachedSound
    {
    public float[] AudioData { get; private set; }
    public WaveFormat WaveFormat { get; private set; }
    public CachedSound(string audioFileName)
    {
    using (var audioFileReader = new AudioFileReader(audioFileName))
    {
    // TODO: could add resampling in here if required
    WaveFormat = audioFileReader.WaveFormat;
    var wholeFile = new List<float>((int)(audioFileReader.Length / 4));
    var readBuffer= new float[audioFileReader.WaveFormat.SampleRate * audioFileReader.WaveFormat.Channels];
    int samplesRead;
    while((samplesRead = audioFileReader.Read(readBuffer,0,readBuffer.Length)) > 0)
    {
    wholeFile.AddRange(readBuffer.Take(samplesRead));
    }
    AudioData = wholeFile.ToArray();
    }
    }
    }
    }
    27 changes: 27 additions & 0 deletions CachedSoundSampleProvider.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    using System;
    using NAudio.Wave;

    namespace FireAndForgetAudioSample
    {
    class CachedSoundSampleProvider : ISampleProvider
    {
    private readonly CachedSound cachedSound;
    private long position;

    public CachedSoundSampleProvider(CachedSound cachedSound)
    {
    this.cachedSound = cachedSound;
    }

    public int Read(float[] buffer, int offset, int count)
    {
    var availableSamples = cachedSound.AudioData.Length - position;
    var samplesToCopy = Math.Min(availableSamples, count);
    Array.Copy(cachedSound.AudioData, position, buffer, offset, samplesToCopy);
    position += samplesToCopy;
    return (int)samplesToCopy;
    }

    public WaveFormat WaveFormat { get { return cachedSound.WaveFormat; } }
    }
    }
    12 changes: 12 additions & 0 deletions UsageSnippet.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    // on startup:
    var zap = new CachedSound("zap.wav");
    var boom = new CachedSound("boom.wav");


    // later in the app...
    AudioFileEngine.Instance.PlaySound(zap);
    AudioFileEngine.Instance.PlaySound(boom);
    AudioFileEngine.Instance.PlaySound("crash.wav");

    // on shutdown
    AudioFileEngine.Instance.Dispose();