2

我正在尝试将 NAudio 与 Reactive Extensions 结合使用,但在让 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 之间,还是支持全范围的浮点?

谢谢

4

1 回答 1

3

BufferedWaveProvider使用 NAudio而不是整个重置事件/队列更为可取。它是为这样的场景而设计的。

当播放器的缓冲区突然用完时,您会遇到中断 - 数据流的速度不够快。

其次,让我清理一下函数签名:

int Read(float[] buffer, int offset, int sampleCount)

您将获得一个缓冲区、缓冲区中的偏移量和块大小。您返回的值表示您可以为此读取操作提供的项目数。您不需要提供与请求的样本相对应的确切字节数。

因此,如果您想提供静音,请不要构造并返回零缓冲区,而只需返回 0 作为读取长度。

最后,如果您不想突然中断,您可以慢慢减小最后一组样本的幅度 - 它可以像乘以(N - n) / N(n ϵ [0, N]) 一样简单。由于缓冲区不足而中断是一个完全不同的问题。

注意

让我补充一点,您目前的解决方案不是在生产中执行此操作的好方法。当垃圾收集来敲门时,在两者之间使用大量对象最终会导致很多延迟问题。NAudio 的作者 Mark Heath 使用了很多技巧来最小化 Garbage 收集,比如重用缓冲区、EventArgs 之类的,所以如果浪费掉就很不公平了。

于 2012-07-11T21:49:00.517 回答