[NotNull] private static MemoryStream NormalizeAudioData(byte[] data)
        {
            #if NCRUNCH
            return(new MemoryStream(data));
            #endif

            //Construct reader which can read the audio (whatever format it is in)
            var reader         = new StreamMediaFoundationReader(new MemoryStream(data));
            var sampleProvider = reader.ToSampleProvider();

            // find the max peak
            float max    = 0;
            var   buffer = new float[reader.WaveFormat.SampleRate];
            int   read;
            do
            {
                read = sampleProvider.Read(buffer, 0, buffer.Length);
                if (read > 0)
                {
                    max = Math.Max(max, Enumerable.Range(0, read).Select(i => Math.Abs(buffer[i])).Max());
                }
            } while (read > 0);

            if (Math.Abs(max) < float.Epsilon || max > 1.0f)
            {
                throw new InvalidOperationException("Audio normalization failed to find a reasonable peak volume");
            }

            //Write (as wav) with soft clipping and peak volume normalization
            var output = new MemoryStream((int)(reader.Length * 4));
            var input  = new SoftClipSampleProvider(new VolumeSampleProvider(sampleProvider)
            {
                Volume = 1 / max - 0.05f
            });
            reader.Position = 0;
            WaveFileWriter.WriteWavFileToStream(output, input.ToWaveProvider16());

            return(output);
        }
        public MultichannelAudioPlayer([NotNull] IVoiceChannel voiceChannel, [NotNull] IEnumerable <IChannel> sources)
        {
            _voiceChannel = voiceChannel;
            _threadEvent  = new AutoResetEvent(true);
            _thread       = Task.Run(ThreadEntry);

            //Sample provider which mixes together several sample providers
            _mixerInput = new MixingSampleProvider(MixingFormat)
            {
                ReadFully = true
            };

            //Soft clip using opus
            var clipper = new SoftClipSampleProvider(_mixerInput.ToMono());

            //resample mix format to output format
            _mixerOutput = new WdlResamplingSampleProvider(clipper, OutputFormat.SampleRate).ToStereo().ToWaveProvider16();

            //Add all initial channels to the mixer
            foreach (var source in sources)
            {
                Add(source);
            }
        }