【问题标题】:Dummy output for reading NAudio buffers用于读取 NAudio 缓冲区的虚拟输出
【发布时间】:2015-07-10 07:44:53
【问题描述】:

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

我不确定这样做的正确方法是什么,我尝试使用 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 事件,但由于有两个输入并且它们是混合的,我也无法正常工作。

【问题讨论】:

    标签: c# naudio


    【解决方案1】:

    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 毫秒触发一次。

    试一试,看看它对你的效果如何。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 2013-10-18
      • 1970-01-01
      • 1970-01-01
      • 2018-02-14
      • 1970-01-01
      相关资源
      最近更新 更多