bk999
bk999

Reputation: 35

Using NAudio to save last X seconds in temporary .wav file

I'm not sure if this is possible, but what I'd like to do is be able to constantly be saving the last X (let's just say 5) seconds of audio being recorded using NAudio as a temporary .Wav file (temp.wav) or buffer of some kind. There is a similar question here (C# NAudio - Keep Last X Seconds Of Audio), but it looks like it was only partially resolved.

It's easy to get NAudio recording, and I could take a given 5 seconds of audio, then stop and save that, but I'm not making any headway in constantly updating a temporary .Wav file so at any given time it always represents the last 5 seconds of recorded audio. It looks like it might be possible by used a BufferedWaveProvider (https://github.com/SjB/NAudio/blob/master/NAudio/Wave/WaveProviders/BufferedWaveProvider.cs), but I have been unsuccessful thus far. Right now I have it set up to record from a WaveInEvent starting when the app starts, and it is successfully recording audio for as long as I run the app, but I can't figure out how to continually save out to that temporary wav file storing the last 5 seconds.

Would appreciate any guidance or nudges in the right direction.

Upvotes: 0

Views: 717

Answers (1)

bk999
bk999

Reputation: 35

I found the answer here (NAudio : Keeping last ex: 5s of recorded audio and save it anytime), I had missed it before. It is Cevelier's answer to his own question. I tested his AudioRecorder class, and it works exactly as expected. The only change I made was disposing of the file writer once a file was saved:

using System;
using NAudio.Wave;
using System.Diagnostics;

public class AudioRecorder
{
    public WaveInEvent MyWaveIn;
    public readonly double RecordTime;

    private WaveOutEvent _wav = new WaveOutEvent();
    private bool _isFull = false;
    private int _pos = 0;
    private byte[] _buffer;
    private bool _isRecording = false;

    /// <summary>
    /// Creates a new recorder with a buffer
    /// </summary>
    /// <param name="recordTime">Time to keep in buffer (in seconds)</param>
    public AudioRecorder(double recordTime)
    {
        RecordTime = recordTime;
        MyWaveIn = new WaveInEvent();
        MyWaveIn.DataAvailable += DataAvailable;
        _buffer = new byte[(int)(MyWaveIn.WaveFormat.AverageBytesPerSecond * RecordTime)];
    }

    /// <summary>
    /// Starts recording
    /// </summary>
    public void StartRecording()
    {
        if (!_isRecording)
        {
            try
            {
                MyWaveIn.StartRecording();
            }
            catch (InvalidOperationException)
            {
                Debug.WriteLine("Already recording!");
            }
        }

        _isRecording = true;
    }

    /// <summary>
    /// Stops recording
    /// </summary>
    public void StopRecording()
    {
        MyWaveIn.StopRecording();
        _isRecording = false;
    }

    /// <summary>
    /// Play currently recorded data
    /// </summary>
    public void PlayRecorded() 
    {
        if (_wav.PlaybackState == PlaybackState.Stopped)
        {
            var buff = new BufferedWaveProvider(MyWaveIn.WaveFormat);
            var bytes = GetBytesToSave();
            buff.AddSamples(bytes, 0, bytes.Length);
            _wav.Init(buff);
            _wav.Play();
        }

    }

    /// <summary>
    /// Stops replay
    /// </summary>
    public void StopReplay()
    {
        if (_wav != null) _wav.Stop();
    }

    /// <summary>
    /// Save to disk
    /// </summary>
    /// <param name="fileName"></param>
    public void Save(string fileName)
    {
        var writer = new WaveFileWriter(fileName, MyWaveIn.WaveFormat);
        var buff = GetBytesToSave();
        writer.Write(buff, 0 , buff.Length);
        writer.Flush();
        writer.Dispose();
    }


    private void DataAvailable(object sender, WaveInEventArgs e)
    {

        for (int i = 0; i < e.BytesRecorded; ++i)
        {
            // save the data
            _buffer[_pos] = e.Buffer[i];
            // move the current position (advances by 1 OR resets to zero if the length of the buffer was reached)
            _pos = (_pos + 1) % _buffer.Length;
            // flag if the buffer is full (will only set it from false to true the first time that it reaches the full length of the buffer)
            _isFull |= (_pos == 0);
        }
    }

    public byte[] GetBytesToSave()
    {
        int length = _isFull ? _buffer.Length : _pos;
        var bytesToSave = new byte[length];
        int byteCountToEnd = _isFull ? (_buffer.Length - _pos) : 0;
        if (byteCountToEnd > 0)
        {
            // bytes from the current position to the end
            Array.Copy(_buffer, _pos, bytesToSave, 0, byteCountToEnd);
        }
        if (_pos > 0)
        {
            // bytes from the start to the current position
            Array.Copy(_buffer, 0, bytesToSave, byteCountToEnd, _pos);
        }
        return bytesToSave;
    }

    /// <summary>
    /// Starts recording if WaveIn stopped
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Stopped(object sender, StoppedEventArgs e)
    {
        Debug.WriteLine("Recording stopped!");
        if (e.Exception != null) Debug.WriteLine(e.Exception.Message);
        if (_isRecording)
        {
            MyWaveIn.StartRecording();
        }
    }
}

Upvotes: 1

Related Questions