我尝试进行以下设置,当我使用实际输出时它工作正常。
我不确定这样做的正确方法是什么,我试图使用一个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 事件,但由于有两个输入并且它们是混合的,我也无法使用它。
答案 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
发射一次。
尝试一下,看看它对你有多好。