2015-07-10 36 views
1

我试图执行以下设置,并且在使用实际输出时它工作正常。我试着用一个定时器,它工作了一段时间,但后来失败了,因为它漂移了一下,我得到一个缓冲区完全例外。我不知道什么是正确的方法是做这件事,我试图使用一个定时器,用于读取NAudio缓冲区的虚拟输出

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事件,但因为有两个输入,将它们混合,我不能说要么工作。

回答

2

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

追踪流逝时间的最常用方法是使用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 ...仍然非常易于管理,并且没有溢出。在这两种情况下,数据都是在刚刚低于最大缓冲区大小一半的范围内到达的 - 在60秒的运行中,每DataAvailable事件对76968字节的最大缓冲区长度为35280字节,平均每100ms触发DataAvailable

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

+0

我工作,谢谢:) –