用于读取NAudio缓冲区的虚拟输出

时间:2015-07-10 07:44:53

标签: c# naudio

我尝试进行以下设置,当我使用实际输出时它工作正常。 Setup

我不确定这样做的正确方法是什么,我试图使用一个Timer并且它可以工作一段时间,但后来失败了,因为它漂移了一点,我得到了一个缓冲区完全异常。

  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 事件,但由于有两个输入并且它们是混合的,我也无法使用它。

1 个答案:

答案 0 :(得分:2)

基于Windows时钟时间,System.Timer.Timer的分辨率约为15.6毫秒。您需要使用更准确的机制跟踪时间,并根据实际时间而不是计时器滴答率来调整读取速率。

最流行的跟踪已用时间的方法是使用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正在从默认WaveInEvent实例(8kHz PCM 16位单声道)提供样本。定时器在大约93ms而不是请求的90ms时滴答,由总运行时间与读取次数判断,输入缓冲器的长度始终保持在3800字节以下。更改为44.1kHz立体声IeeeFloat格式将缓冲区大小提升到80kB以下...仍然非常易于管理,并且没有溢出。在这两种情况下,数据都以最大缓冲区大小的一半到达块 - 每DataAvailable个事件35280个字节,而60秒运行时最大缓冲区长度为76968个字节,平均每100毫秒DataAvailable发射一次。

尝试一下,看看它对你有多好。