C# NAudio未播放,但正在调用WaveProvider32 Read

C# NAudio未播放,但正在调用WaveProvider32 Read,c#,.net,system.reactive,naudio,C#,.net,System.reactive,Naudio,我正在尝试将NAudio与反应式扩展相结合,但在让NAudio播放音频时遇到了问题 以下是我目前的代码: public class WaveOutPlayer : IDisposable { WaveOut wavOut = new WaveOut(WaveCallbackInfo.FunctionCallback()); public WaveOutPlayer(int device, int sampleRate, int channels, IStereoSource

我正在尝试将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();
    }
}
公共类WaveOutPlayer:IDisposable
{
WaveOut wavOut=新的WaveOut(WaveCallbackInfo.FunctionCallback());
公共波输出层(int设备、int采样器、int通道、IStereoSource源)
{
var provider=新的WavProv(源、采样器、通道);
设置波形格式(采样器、通道);
wavOut.Init(提供者);
}
私有类WavProv:WaveProvider32
{
AutoResetEvent are=新的AutoResetEvent(假);
ConcurrentQueue=新ConcurrentQueue();
公共WavProv(IStereoSource源、int采样器、int通道)
{
source.ChannelLeft
.Zip(source.ChannelRight,(ls,rs)=>newdouble[]{ls,rs})//每个通道一个样本
.SelectMany(samps=>samps)//转换为样本数组l,r,l,r,l
.Buffer(sampleRate*通道*1)//Buffer sampleRate*通道*2秒
.Select(x=>x.ToArray())//以观察数据块
.Do(x=>{are.Set();})
.SubscribeOn(NewThreadScheduler.Default)
.订阅(数据=>
{
//排队((浮动)数据);
data.ToList().ForEach((x)=>queue.Enqueue((float)x));
});
}
公共覆盖整型读取(浮点[]缓冲区、整型偏移量、整型采样计数)
{
int itemsRead;
if(!queue.Any())//队列中没有数据
{
//是。WaitOne();
buffer=Enumerable.Repeat(0.0f,sampleCount).ToArray();//等待一些数据
itemsRead=样本计数;
}
其他的
{
//要读取的项目数低于samplecount或队列中的项目数
int itemsToRead=(queue.Count()>sampleCount)?sampleCount:queue.Count();
对于(itemsRead=0;itemsRead
正在调用Read方法,Console.WriteLine显示我总是提供足够的数据。顺便说一句,如果我减慢了信号的产生,以至于我需要偶尔提供一个全零缓冲区(代码当前不存在),那么我会听到“咔哒”声

我还遗漏了其他问题/陷阱吗

e、 g.振幅范围仅在0-1之间,还是支持浮动的全范围


谢谢

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

当播放器的缓冲区突然耗尽时,您会遇到中断-数据流传输速度不够快

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

int读取(浮点[]缓冲区、int偏移量、int采样计数)

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

因此,如果要提供静默,请不要构造并返回一个零缓冲区,而只返回0作为读取长度

最后,如果您不希望突然中断,可以缓慢降低最后一组样本的振幅-可以简单地乘以
(N-N)/N
(Nϵ[0,N])。由于缓冲区不足而导致的中断是一个完全不同的问题

N.B.


让我补充一下,您目前的解决方案并不是在生产中实现这一点的好方法。当垃圾收集到来时,在两者之间使用大量对象最终将导致大量延迟问题。《NAudio》的作者马克·希思(Mark Heath)使用了很多技巧来最小化垃圾收集,比如重用缓冲区、事件参数等,因此如果垃圾被浪费,那将是不公平的。

GregC同意代码确实存在问题,但我当时更感兴趣的是获得任何声音,而不是干净的声音!请查看更新的代码。我对nAudio了解不多,但我同意Asti的意见,并想补充一点,在Rx中使用AutoResetEvents(或任何WaitHandle)是一种代码味道。@Asti,1)谢谢,我不知道BufferedWaveProvider。2) 我理解从读取返回0与说“流结束”是一样的。关于你的N.B,我同意,这不是为了产品质量代码,而是为了一个简单易读的信号处理管道,学生可以使用。@LeeCampbell同意,并且当前版本的代码没有使用任何同步,我添加这些代码是为了排除线程问题是当时的原因。