Search code examples
c#naudio

Playing waveform with NAudio lower for each turn


I am trying to play a byte buffer with NAudio. I created a winforms application for the task.

enter image description here

But when I play the sound (created with Math.sin) subsequently, the volume goes lower and lower for every playback. Why?

using System;
using System.Windows.Forms;
using NAudio.Wave;

namespace beeper
{
    public partial class Form1 : Form
    {
        byte[] bytes;
        const int sampling_freq = 44100;

        public Form1()
        {
            InitializeComponent();

            double freq = 1000;
            var seconds = 0.5;
            
            var length = (int)(seconds * sampling_freq);
            bytes = new byte[length];

            for (var i = 0; i < length; i++)
            {
                var offset = 80 * Math.Sin(i * freq / sampling_freq * Math.PI * 2);

                bytes[i] = (byte)(128 + offset);
            }
        }

        private void btnBeep_Click(object sender, EventArgs e)
        {
            var format = new WaveFormat(sampling_freq, 8, 1);
            var provider = new BufferedWaveProvider(format);
            provider.AddSamples(bytes, 0, bytes.Length);

            var wo = new WaveOut();
            wo.Init(provider);
            wo.Play();
        }
    }
}

Recording in audacity

enter image description here

Not wonder it sounds like it does when looking at this waveform thats both offsetted and distorted in some strange way. I search for a pattern, but I cannot really find one.

https://github.com/naudio/NAudio/issues/729


Solution

  • You're adding a whole new playback channel each time you click the play button. If you don't close those channels then they will just continue to play silence, so you end up with multiple silent channels mixed with one active sound, giving you the result you're seeing. The more times you push the button the quieter the output will be.

    Either you need to close your channels after the tone completes (using a timer or similar) or create one channel and reuse it:

        private BufferedWaveProvider provider;
        private WaveOut waveOut;
        
        private void btnBeep_Click(object sender, EventArgs e)
        {
            if (waveOut == null)
            {
                var format = new WaveFormat(sampling_freq, 8, 1);
                provider = new BufferedWaveProvider(format);
                waveOut = new WaveOut();
                waveOut.Init(provider);
                waveOut.Play();
            }
            
            provider.ClearBuffer();
            provider.AddSamples(bytes, 0, bytes.Length);
        }
    

    Calling ClearBuffer removes any remaining buffered samples so hitting the button repeatedly doesn't just queue up a whole stack of output. If you want to play multiple overlapping tones you'll need to do multiple channels or handle mixing yourself.

    If you do want overlaps I suggest you check out MixingWaveProvider32. As it stands it's a bit constrained, but you should be able to either use it as is (with the appropriate conversion sample provider).