NAudio : 保留最后一个 ex: 5s 录制的音频并随时保存

Posted

技术标签:

【中文标题】NAudio : 保留最后一个 ex: 5s 录制的音频并随时保存【英文标题】:NAudio : Keeping last ex: 5s of recorded audio and save it anytime 【发布时间】:2019-06-15 05:34:04 【问题描述】:

我正在编写一个音板,使用 NAudio 作为库来管理我的音频。我想要实现的功能之一是能够连续记录一个(或多个)音频输入并能够随时保存它们。

我认为这可能的方式是保持最后一个循环缓冲区:音频输入拾取的 5 秒样本。

我还想避免将所有数据保持在启动时的状态,因为我不想过度使用内存。

我已经尝试了很多方法来解决这个问题:

    使用循环缓冲区并向其提供来自“DataAvailable”事件的数据; 使用“Queue”和“BufferedWaveProvider”添加缓冲区 数据并将其转换为样本。 我尝试使用 2 个“BufferedWaveProvider”,结果交替出现 填充取决于哪个已满。 我还尝试使用 2 个波形输入和计时器来交替使用 录音。

我尝试使用字节数组并将其用作循环缓冲区。我使用“WaveInEvent”中的“DataAvailable”事件填充了缓冲区。 “WaveInEventArgs”有一个缓冲区,所以我将其中的数据添加到循环缓冲区中。

private int _start = 0, _end = 0;
private bool _filled = false;
private byte[] _buffer; // the size was set in the constructor
                        // its an equation to figure out how many samples
                        // a certain time needs.

private void _dataAvailable(object sender, WaveInEventArgs e)


    for (int i = 0; i < e.BytesRecorded; i++)
    
        if (_filled)
        
            _start = _end + 1 > _buffer.Length - 1 ? _end + 1 : 0;
        
        if (_end > _buffer.Length - 1 && !_filled) _filled = true;
        _end = _end > _buffer.Length - 1 ? _end + 1 : 0;

        _buffer[_end] = e.Buffer[i];
    

我所做的一些尝试有些奏效,但大多数时候,它们会在前 5 秒内起作用(我知道使用“BufferredWaveProvider”可能会导致该问题。我认为问题的一部分是缓冲区的开头需要一定数量的数据,一旦缓冲区开始覆盖该数据,音频播放器就不会

问题的另一个非常可能的原因是我刚刚开始使用 NAudio,还没有完全理解它。

我已经被这个问题困扰了一段时间,感谢任何人可以给我的所有帮助。

我还有一些可以添加的代码,但我认为这个问题已经很长了。

提前谢谢你!

【问题讨论】:

仍然存在只保留必要数据的问题。我不想将“WaveIn”数据保持在它开始的位置。我认为它需要时不时地重新启动,除非它最终停止并清除其缓冲区。那么,“WaveIn”不断录制是一个问题还是它自己管理? 我不认为WaveInEvent 保留任何数据。检查类和/或文档的描述。还要确保处理波进出的物体(如果它们是一次性的)。除此之外……没关系。 【参考方案1】:

如果其他人想做类似的事情,我将离开全班。随意使用。

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();
    


    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();
        
    

【讨论】:

【参考方案2】:

_dataAvailable 方法中的代码对我来说很奇怪。我会简单地从头到尾写入字节,然后再从头开始等等。然后,当您想要获取实际字节来保存它们时,创建一个从当前位置到结尾以及从开始到当前位置的新数组。在下面检查我的代码。

private int _pos = 0;
private bool _isFull = false;
private byte[] _buffer; // intialized in the constructor with the correct length

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;

【讨论】:

以上是关于NAudio : 保留最后一个 ex: 5s 录制的音频并随时保存的主要内容,如果未能解决你的问题,请参考以下文章

用 NAudio 录制音频让我无法接受

无法播放用 naudio 录制的音频流

C# NAudio:录制的文件无法播放

naudio 从麦克风录制声音然后保存

在 WPF C# 中使用 NAudio 录制音频

使用 naudio 录制特定的应用程序音频