用于读取 NAudio 缓冲区的虚拟输出
Dummy output for reading NAudio buffers
我尝试进行以下设置,当我使用真实输出时它工作正常。
我不确定这样做的正确方法是什么,我尝试使用计时器并且它工作了一段时间,但后来失败了,因为它有点漂移并且我得到了缓冲区已满的异常。
var mixSampleProvider = new MixingSampleProvider(resampleWaveFormat);
mixSampleProvider.AddMixerInput(inputAResampler);
mixSampleProvider.AddMixerInput(inputBResampler);
var mixWaveProvider = new SampleToWaveProvider(mixSampleProvider);
savingWaveProvider = new SavingWaveProvider(mixWaveProvider);
System.Timers.Timer timer = new System.Timers.Timer(98);
timer.Elapsed += (sender, args) =>
{
var count = resampleWaveFormat.AverageBytesPerSecond / 10;
var dummy = new byte[count];
savingWaveProvider.Read(dummy, 0, count);
};
timer.Start();
我已经尝试计算每次报价时我应该读多少,例如
var readCount = Math.Min(inputABufferedWaveProvider.BufferedBytes, inputBBufferedWaveProvider.BufferedBytes);
但无法使其工作,我尝试使用 DataAvailable 事件,但由于有两个输入并且它们是混合的,我也无法工作。
System.Timer.Timer
的分辨率约为 15.6 毫秒,基于 Windows 时钟时间。您需要使用更准确的机制来跟踪时间,并根据真实时间而不是计时器滴答的速率调整您的读取速率。
最流行的跟踪经过时间的方法是使用 System.Diagnostics.Stopwatch
来确定自您的进程开始以来实际经过了多少时间,然后您可以使用它来计算要保留的样本数同步。
这是一个 IWaveOutput
实现,它使用计时器和秒表来确定要从其输入中读取多少样本:
public class SyncedNullOutput : IWavePlayer
{
// where to read data from
private IWaveProvider _source;
// time measurement
Stopwatch _stopwatch = null;
double _lastTime = 0;
// timer to fire our read method
System.Timers.Timer _timer = null;
PlaybackState _state = PlaybackState.Stopped;
public PlaybackState PlaybackState { get { return _state; } }
public SuncedNullOutput()
{ }
public SyncedNullOutput(IWaveProvider source)
{
Init(source);
}
public void Dispose()
{
Stop();
}
void _timer_Elapsed(object sender, ElapsedEventArgs args)
{
// get total elapsed time, compare to last time
double elapsed = _stopwatch.Elapsed.TotalSeconds;
double deltaTime = elapsed - _lastTime;
_lastTime = elapsed;
// work out number of samples we need to read...
int nSamples = (int)(deltaTime * _source.WaveFormat.SampleRate);
// ...and how many bytes those samples occupy
int nBytes = nSamples * _source.WaveFormat.BlockAlign;
// Read samples from the source
byte[] buffer = new byte[nBytes];
_source.Read(buffer, 0, nBytes);
}
public void Play()
{
if (_state == PlaybackState.Stopped)
{
// create timer
_timer = new System.Timers.Timer(90);
_timer.AutoReset = true;
_timer.Elapsed += _timer_Elapsed;
_timer.Start();
// create stopwatch
_stopwatch = Stopwatch.StartNew();
_lastTime = 0;
}
else if (_state == PlaybackState.Paused)
{
// reset stopwatch
_stopwatch.Reset();
_lastTime = 0;
// restart timer
_timer.Start();
}
_state = PlaybackState.Playing;
}
public void Stop()
{
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
_timer = null;
}
if (_stopwatch != null)
{
_stopwatch.Stop();
_stopwatch = null;
}
_lastTime = 0;
_state = PlaybackState.Stopped;
}
public void Pause()
{
_timer.Stop();
_state = PlaybackState.Paused;
}
public void Init(IWaveProvider waveProvider)
{
Stop();
_source = waveProvider;
}
public event EventHandler<StoppedEventArgs> PlaybackStopped;
protected void OnPlaybackStopped(Exception exception = null)
{
if (PlaybackStopped != null)
PlaybackStopped(this, new StoppedEventArgs(exception));
}
public float Volume {get;set;}
}
我用这个连接到 BufferedWaveProvider
进行了一些测试,该 BufferedWaveProvider
从默认 WaveInEvent
实例(8kHz PCM 16 位单声道)提供样本。根据总 运行 时间与读取次数的对比判断,计时器在 93 毫秒左右滴答作响,而不是请求的 90 毫秒,并且输入缓冲区的长度始终保持在 3800 字节以下。更改为 44.1kHz 立体声 IeeeFloat 格式将缓冲区大小增加到略低于 80kB……仍然非常易于管理,并且没有溢出。在这两种情况下,数据到达的块都不到最大缓冲区大小的一半 - 每个 DataAvailable
事件 35280 字节,而 60 秒 运行 内的最大缓冲区长度为 76968 字节,DataAvailable
每次触发平均 100 毫秒。
试一试,看看效果如何。
我尝试进行以下设置,当我使用真实输出时它工作正常。
我不确定这样做的正确方法是什么,我尝试使用计时器并且它工作了一段时间,但后来失败了,因为它有点漂移并且我得到了缓冲区已满的异常。
var mixSampleProvider = new MixingSampleProvider(resampleWaveFormat);
mixSampleProvider.AddMixerInput(inputAResampler);
mixSampleProvider.AddMixerInput(inputBResampler);
var mixWaveProvider = new SampleToWaveProvider(mixSampleProvider);
savingWaveProvider = new SavingWaveProvider(mixWaveProvider);
System.Timers.Timer timer = new System.Timers.Timer(98);
timer.Elapsed += (sender, args) =>
{
var count = resampleWaveFormat.AverageBytesPerSecond / 10;
var dummy = new byte[count];
savingWaveProvider.Read(dummy, 0, count);
};
timer.Start();
我已经尝试计算每次报价时我应该读多少,例如
var readCount = Math.Min(inputABufferedWaveProvider.BufferedBytes, inputBBufferedWaveProvider.BufferedBytes);
但无法使其工作,我尝试使用 DataAvailable 事件,但由于有两个输入并且它们是混合的,我也无法工作。
System.Timer.Timer
的分辨率约为 15.6 毫秒,基于 Windows 时钟时间。您需要使用更准确的机制来跟踪时间,并根据真实时间而不是计时器滴答的速率调整您的读取速率。
最流行的跟踪经过时间的方法是使用 System.Diagnostics.Stopwatch
来确定自您的进程开始以来实际经过了多少时间,然后您可以使用它来计算要保留的样本数同步。
这是一个 IWaveOutput
实现,它使用计时器和秒表来确定要从其输入中读取多少样本:
public class SyncedNullOutput : IWavePlayer
{
// where to read data from
private IWaveProvider _source;
// time measurement
Stopwatch _stopwatch = null;
double _lastTime = 0;
// timer to fire our read method
System.Timers.Timer _timer = null;
PlaybackState _state = PlaybackState.Stopped;
public PlaybackState PlaybackState { get { return _state; } }
public SuncedNullOutput()
{ }
public SyncedNullOutput(IWaveProvider source)
{
Init(source);
}
public void Dispose()
{
Stop();
}
void _timer_Elapsed(object sender, ElapsedEventArgs args)
{
// get total elapsed time, compare to last time
double elapsed = _stopwatch.Elapsed.TotalSeconds;
double deltaTime = elapsed - _lastTime;
_lastTime = elapsed;
// work out number of samples we need to read...
int nSamples = (int)(deltaTime * _source.WaveFormat.SampleRate);
// ...and how many bytes those samples occupy
int nBytes = nSamples * _source.WaveFormat.BlockAlign;
// Read samples from the source
byte[] buffer = new byte[nBytes];
_source.Read(buffer, 0, nBytes);
}
public void Play()
{
if (_state == PlaybackState.Stopped)
{
// create timer
_timer = new System.Timers.Timer(90);
_timer.AutoReset = true;
_timer.Elapsed += _timer_Elapsed;
_timer.Start();
// create stopwatch
_stopwatch = Stopwatch.StartNew();
_lastTime = 0;
}
else if (_state == PlaybackState.Paused)
{
// reset stopwatch
_stopwatch.Reset();
_lastTime = 0;
// restart timer
_timer.Start();
}
_state = PlaybackState.Playing;
}
public void Stop()
{
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
_timer = null;
}
if (_stopwatch != null)
{
_stopwatch.Stop();
_stopwatch = null;
}
_lastTime = 0;
_state = PlaybackState.Stopped;
}
public void Pause()
{
_timer.Stop();
_state = PlaybackState.Paused;
}
public void Init(IWaveProvider waveProvider)
{
Stop();
_source = waveProvider;
}
public event EventHandler<StoppedEventArgs> PlaybackStopped;
protected void OnPlaybackStopped(Exception exception = null)
{
if (PlaybackStopped != null)
PlaybackStopped(this, new StoppedEventArgs(exception));
}
public float Volume {get;set;}
}
我用这个连接到 BufferedWaveProvider
进行了一些测试,该 BufferedWaveProvider
从默认 WaveInEvent
实例(8kHz PCM 16 位单声道)提供样本。根据总 运行 时间与读取次数的对比判断,计时器在 93 毫秒左右滴答作响,而不是请求的 90 毫秒,并且输入缓冲区的长度始终保持在 3800 字节以下。更改为 44.1kHz 立体声 IeeeFloat 格式将缓冲区大小增加到略低于 80kB……仍然非常易于管理,并且没有溢出。在这两种情况下,数据到达的块都不到最大缓冲区大小的一半 - 每个 DataAvailable
事件 35280 字节,而 60 秒 运行 内的最大缓冲区长度为 76968 字节,DataAvailable
每次触发平均 100 毫秒。
试一试,看看效果如何。