NAudio - разделение и объединение волн в реальном времени

Я работаю со звуковой картой с несколькими входами и хочу добиться живого микширования нескольких входов. Все входы стерео, поэтому мне нужно сначала разделить их, смешать выбранные каналы и предоставить их как монопоток.

Целью будет что-то вроде этого микса Channel1[левый] + Channel3[правый] + Channel4[правый] -> монопоток.

Я уже реализовал такую ​​цепочку процессов:

1) WaveIn -> создать BufferedWaveProvider для каждого канала -> добавить образцы (только для текущего канала) в каждый BufferedWaveProvider с помощью wavein.DataAvailable += { buffwavprovider[channel].AddSamples(...)... Это дает мне хороший список нескольких BufferdWaveProvider. Разделение звуковой части здесь реализовано корректно.

2) Выберите несколько BufferedWaveProviders и передайте их MixingWaveProvider32. Затем создайте WaveStream (используя WaveMixerStream32 и 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();

person Arno    schedule 31.08.2017    source источник
comment
Не могли бы вы опубликовать код?   -  person Flexabust Bergson    schedule 31.08.2017
comment
Несколько быстрых комментариев: внешний цикл в `DataAvailable должен просто запускаться в e.BytesRecorded. Кроме того, вам не нужно перераспределять channel_buffer каждый раз в цикле (просто сделайте это один раз вне цикла и используйте его повторно).   -  person Bob C    schedule 07.09.2017
comment
@BobC, это именно то, что я сделал вчера, и это значительно повысило производительность. Предоставлю свое решение в ближайшее время.   -  person Arno    schedule 11.09.2017


Ответы (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]) для каждого канала.

person Arno    schedule 12.09.2017
comment
Арно, ты решил проблему с обрывом звука? Я думал, что поток считывает данные слишком быстро. Там нет тормоза, чтобы заставить его ждать поступления новых выборок, поэтому цепочка потоков, вероятно, возвращает 0, когда вызывается mms.Read() и нет новых данных. - person Bob C; 12.09.2017