2012-07-10 21 views
2

我正在嘗試將NAudio與Reactive Extentions相結合,並且我正在收聽NAudio播放音頻的問題。NAudio沒有播放,但WaveProvider32讀取正在被呼叫

以下是到目前爲止我的代碼:

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之間,還是支持全範圍的浮動?

感謝

+0

GregC,同意代碼確實有問題,但是我更感興趣的是在當時獲得任何聲音,而不是獲得乾淨的聲音!請參閱更新的代碼。 – stevenrcfox 2012-07-11 15:09:48

回答

3

它更建議使用n音訊的BufferedWaveProvider而不是整個復位事件/隊列。它是爲這樣的場景而設計的。

當播放器的緩衝區突然用完時,您將遇到中斷 - 數據流式傳輸速度不夠快。

第二,讓我明確了函數簽名:

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

你將與一個緩衝區來提供,在緩衝區中的偏移和塊大小。您返回的值表示您可以爲此讀取操作提供的項目數量。 你是不需要提供與請求的樣本相對應的確切字節數。

所以,如果你想提供沉默,不要構造和返回一個零緩衝區,而是簡單地返回0作爲讀取長度。最後,如果不想突然切斷,可以慢慢減小最後一組採樣的幅度 - 它可以像乘以(N - n)/N(nε[0,N])一樣簡單。由於緩衝區溢出而切斷是一個完全不同的問題。

N.B.

讓我補充一點,你現在的解決方案不是在生產中這樣做的好方法。在垃圾收集碰撞時,使用它們之間的很多對象最終會導致很多延遲問題。 NAudio的作者Mark Heath使用了很多技巧來減少垃圾回收,比如重複使用緩衝區,EventArgs等,所以如果浪費了就會是不公正的。

+0

我對nAudio不太瞭解,但是我同意Asti的評論,並且希望補充說,使用Rx的AutoResetEvents(或任何WaitHandle)是一種代碼異味。 – 2012-07-26 09:17:50

+0

@asti,1)謝謝,我不知道BufferedWaveProvider。 2)我明白從Read返回0,就像說'流結束'一樣。關於你的N.B,我同意,這不是生產質量代碼,它是一個簡單易讀的信號處理管道,學生可以玩。 – stevenrcfox 2012-10-02 14:27:27

+0

@LeeCampbell同意,並且當前版本的代碼不使用任何同步,我添加這些以排除線程問題是當時的原因。 – stevenrcfox 2012-10-02 14:36:22