我正在尝试将 NAudio 与响应式扩展结合起来,但在让 NAudio 播放音频时遇到问题。
以下是到目前为止我的代码:
public class WaveOutPlayer : IDisposable
{
WaveOut wavOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
public WaveOutPlayer(int device, int sampleRate, int channels, IStereoSource source)
{
var provider = new WavProv(source, sampleRate, channels);
provider.SetWaveFormat(sampleRate,channels);
wavOut.Init(provider);
}
private class WavProv : WaveProvider32
{
AutoResetEvent are = new AutoResetEvent(false);
ConcurrentQueue<float> queue = new ConcurrentQueue<float>();
public WavProv(IStereoSource source, int sampleRate, int channels)
{
source.ChannelLeft
.Zip(source.ChannelRight, (ls, rs) => new double[] { ls, rs }) //one sample from each channel
.SelectMany(samps => samps) //convert to samples array l,r,l,r,l
.Buffer(sampleRate * channels * 1) //buffer samplerate*channels*2 seconds
.Select(x => x.ToArray()) // to observable of chunks
.Do(x => { are.Set(); })
.SubscribeOn(NewThreadScheduler.Default)
.Subscribe(data =>
{
//queue.Enqueue((float)data);
data.ToList().ForEach((x) => queue.Enqueue((float)x));
});
}
public override int Read(float[] buffer, int offset, int sampleCount)
{
int itemsRead;
if (!queue.Any()) //No data in the queue
{
//are.WaitOne();
buffer = Enumerable.Repeat(0.0f, sampleCount).ToArray(); //Wait for some data
itemsRead = sampleCount;
}
else
{
//number of items to read is lower of samplecount or items in queue
int itemsToRead = (queue.Count() > sampleCount) ? sampleCount : queue.Count();
for (itemsRead = 0; itemsRead < itemsToRead; itemsRead++)
{
float res;
if(queue.TryDequeue(out res))
buffer[itemsRead + offset] = res; //add items from queue to buffer
}
}
Console.WriteLine("Requested:{0}, Read: {1}",sampleCount, itemsRead);
return itemsRead;
}
}
public void Play()
{
wavOut.Play();
}
public void Dispose()
{
wavOut.Dispose();
}
}
正在调用 Read 方法,并且 Console.WriteLine 显示我始终提供足够的数据。顺便说一句,如果我减慢信号的产生,以至于我需要偶尔提供全零缓冲区(当前不存在代码),那么我会听到“咔哒”声。
还有其他我遗漏的问题/陷阱吗?
例如幅度范围是只在0-1之间,还是支持全范围的float?
谢谢
最佳答案
建议使用 NAudio 的 BufferedWaveProvider 而不是整个重置事件/队列。它是为此类场景而设计的。
当播放器的缓冲区突然耗尽时,您将遇到中断 - 数据传输速度不够快。
其次,让我清理一下函数签名:
int Read(float[] buffer, int offset, int SampleCount)
您将获得一个缓冲区、缓冲区中的偏移量和 block 大小。您返回的值表示您可以为此读取操作提供的项目数。 您不需要提供与请求的示例相对应的确切字节数。
因此,如果您想提供静音,请不要构造并返回零的缓冲区,而只需返回 0 作为读取长度。
最后,如果您不想突然截止,可以慢慢减小最后一组样本的幅度 - 它可以像乘以(N - n)/N
一样简单> (n ϵ [0, N])。由于缓冲区不足而导致的中断是一个完全不同的问题。
注意
让我补充一下,您目前的解决方案并不是在生产中执行此操作的好方法。当垃圾收集出现时,在中间使用大量对象最终会导致大量延迟问题。 NAudio 的作者 Mark Heath 使用了很多技巧来最小化垃圾收集,比如重用缓冲区、EventArgs 等,所以如果浪费了那就不公平了。
关于c# - NAudio 未播放,但正在调用 WaveProvider32 Read,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11412766/