C# NAudio未播放,但正在调用WaveProvider32 Read
我正在尝试将NAudio与反应式扩展相结合,但在让NAudio播放音频时遇到了问题 以下是我目前的代码: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
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同意,并且当前版本的代码没有使用任何同步,我添加这些代码是为了排除线程问题是当时的原因。