NAudio : 保留最后的 ex: 5s 录制的音频并随时保存
NAudio : Keeping last ex: 5s of recorded audio and save it anytime
我正在使用 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,还没有完全理解它。
我已经被这个问题困扰了一段时间,非常感谢任何人能给我的帮助。
我还有一些代码可以添加,但我认为这个问题已经很长了。
提前致谢!
您 _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;
}
如果其他人想做类似的事情,我将离开整个 class。想怎么用就怎么用。
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();
}
}
}
我正在使用 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,还没有完全理解它。
我已经被这个问题困扰了一段时间,非常感谢任何人能给我的帮助。
我还有一些代码可以添加,但我认为这个问题已经很长了。
提前致谢!
您 _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;
}
如果其他人想做类似的事情,我将离开整个 class。想怎么用就怎么用。
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();
}
}
}