【问题标题】:NAudio - wave split and combine wave in realtimeNAudio - 实时分波和合并波
【发布时间】:2017-08-31 14:21:46
【问题描述】:

我正在使用多输入声卡,我想实现多输入的现场混音。所有输入都是立体声的,所以我需要先将它们分开,混合选择的通道并将它们作为单声道流提供。

目标是这样的混合 Channel1[left] + Channel3[right] + Channel4[right] -> 单声道流。

我已经实现了这样的流程链:

1) WaveIn -> 为每个通道创建 BufferedWaveProvider -> 使用 wavein.DataAvailable 向每个 BufferedWaveProvider 添加样本(仅用于当前通道的样本) += { buffwavprovider[channel].AddSamples(...)... 这给了我一个很好的多个 BufferdWaveProvider 列表。这里的分割音频部分实现正确。

2) 选择多个 BufferedWaveProviders 并将它们交给 MixingWaveProvider32。然后创建一个 WaveStream(使用WaveMixerStream32 and IWaveProvider)。

3) MultiChannelToMonoStream 采用 WaveStream 并生成混音。这也有效。

但结果是,音频被截断了。就像缓冲区出了点问题......

这是处理此问题的正确方法,还是有更好的解决方案?

编辑 - 添加代码:

public class AudioSplitter
   {
      public List<NamedBufferedWaveProvider> WaveProviders { private set; get; }
      public string Name { private set; get; }
      private WaveIn _wavIn;
      private int bytes_per_sample = 4;

      /// <summary>
      /// Splits up one WaveIn into one BufferedWaveProvider for each channel
      /// </summary>
      /// <param name="wavein"></param>
      /// <returns></returns>
      public AudioSplitter(WaveIn wavein, string name)
      {
         if (wavein.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
            throw new Exception("Format must be IEEE float");


         WaveProviders = new List<NamedBufferedWaveProvider>(wavein.WaveFormat.Channels);

         Name = name;
         _wavIn = wavein;
         _wavIn.StartRecording();
         var outFormat = NAudio.Wave.WaveFormat.CreateIeeeFloatWaveFormat(wavein.WaveFormat.SampleRate, 1);

         for (int i = 0; i < wavein.WaveFormat.Channels; i++)
         {
            WaveProviders.Add(new NamedBufferedWaveProvider(outFormat) { DiscardOnBufferOverflow = true, Name = Name + "_" + i });
         }

         bytes_per_sample = _wavIn.WaveFormat.BitsPerSample / 8;
         wavein.DataAvailable += Wavein_DataAvailable;
      }


      /// <summary>
      /// add samples for each channel to bufferedwaveprovider
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Wavein_DataAvailable(object sender, WaveInEventArgs e)
      {
         int channel = 0;
         byte[] buffer = e.Buffer;
         for (int i = 0; i < e.BytesRecorded - bytes_per_sample; i = i + bytes_per_sample)
         {
            byte[] channel_buffer = new byte[bytes_per_sample];

            for (int j = 0; j < bytes_per_sample; j++)
            {
               channel_buffer[j] = buffer[i + j];
            }

            WaveProviders[channel].AddSamples(channel_buffer, 0, channel_buffer.Length);

            channel++;

            if (channel >= _wavIn.WaveFormat.Channels)
               channel = 0;

         }

      }
   }

对每个通道使用 Audiosplitter 会给出一个缓冲波形提供程序列表(单声道 32 位浮点数)。

 var mix = new MixingWaveProvider32(_waveProviders);
 var wps = new WaveProviderToWaveStream(mix);
 MultiChannelToMonoStream mms = new MultiChannelToMonoStream(wps);

 new Thread(() =>
  {
     byte[] buffer = new byte[4096];

     while (mms.Read(buffer, 0, buffer.Length) > 0 && isrunning)
     {


        using (FileStream fs = new FileStream("C:\\temp\\audio\\mono_32.wav", FileMode.Append, FileAccess.Write))
        {
           fs.Write(buffer, 0, buffer.Length);
        }

     }
  }).Start();

【问题讨论】:

  • 你能发布代码吗?
  • 一些快速的 cmets:`DataAvailable 中的外部循环应该只运行到“e.BytesRecorded”。此外,您不需要每次通过循环重新分配“channel_buffer”(只需在循环外执行一次,然后重用它)。
  • @BobC,这正是我昨天所做的,它提高了性能。很快就会提供我的解决方案。

标签: c# .net naudio mixing


【解决方案1】:

还有一些优化空间,但基本上这可以完成工作:

  private void Wavein_DataAvailable(object sender, WaveInEventArgs e)
      {
         int channel = 0;
         byte[] buffer = e.Buffer;

         List<List<byte>> channelbuffers = new List<List<byte>>();
         for (int c = 0; c < _wavIn.WaveFormat.Channels; c++)
         {
            channelbuffers.Add(new List<byte>());
         }

         for (int i = 0; i < e.BytesRecorded; i++)
         {
            var byteList = channelbuffers[channel];

            byteList.Add(buffer[i]);

            if (i % bytes_per_sample == bytes_per_sample - 1)
               channel++;

            if (channel >= _wavIn.WaveFormat.Channels)
               channel = 0;
         }

         for (int j = 0; j < channelbuffers.Count; j++)
         {
            WaveProviders[j].AddSamples(channelbuffers[j].ToArray(), 0, channelbuffers[j].Count());
         }

      }

我们需要为每个频道提供一个 WaveProvider (WaveProviders[j])。

【讨论】:

  • Arno,你解决了“音频被切碎”的问题吗?我在想线程读取数据的速度太快了。没有“刹车”让它等待新样本到达,因此当调用“mms.Read()”并且没有新数据时,Streams 链可能返回 0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-18
  • 2012-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多