Shivam Sarodia
Shivam Sarodia

Reputation: 417

NAudio Peak Volume Meter

I'm attempting to write a peak volume meter using NAudio. My code is very similar to http://channel9.msdn.com/coding4fun/articles/NET-Voice-Recorder, but both my code and the linked Voice Recorder project suffer from an issue.

When playing a sound of constant frequency and volume, the volume meter initially begins at a reasonable level, but then decays to a very small value. I'm not sure why this is the case, for the peak volume meter in the NAudioDemo does not do this. I attempted to replicate the code from NAudioDemo in my program, but I was unable to find the code file containing the peak volume meter code.

Can somebody guide me to an alternative solution for creating a peak volume meter or help me determine why my solution (and the one provided at the link) both don't work?

public MainWindow()
    {
        int waveInDevices = WaveIn.DeviceCount;
        for (int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
        {
            WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
            Console.WriteLine("Device {0}: {1}, {2} channels",
                waveInDevice, deviceInfo.ProductName, deviceInfo.Channels);

            WaveIn waveIn = new WaveIn();
            waveIn.DeviceNumber = 0; //TODO: Let the user choose which device, this comes from the device numbers above
            waveIn.DataAvailable += waveIn_DataAvailable;
            int sampleRate = SAMPLE_RATE; // 8 kHz
            int channels = 1; // mono
            waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
            waveIn.StartRecording();
        }
    }

    void waveIn_DataAvailable(object sender, WaveInEventArgs e)
    {
        for (int index = 0; index < e.BytesRecorded; index += 2)
        {
            short sample = (short)((e.Buffer[index + 1] << 8) |
                                    e.Buffer[index + 0]);
            float sample32 = sample / 32768f;
            ProcessSample(sample32);
        }           

    }

    void ProcessSample(float sample1)
    {
        samplenumber += 1;

        if (sample1 > maxval)
        {
            maxval = sample1;
        }

        if (sample1 < minval)
        {
            minval = sample1;
        }


        //Run updateView every few loops

        if (samplenumber > (double)SAMPLE_RATE / DISPLAY_UPDATE_RATE)
        {
            samplenumber = 0;
            updateView(); //needs to be fast!
        }
    }

    void updateView()
    {
        Console.WriteLine(maxval);
        Console.WriteLine(minval);
        progressBar1.Value = (maxval - minval)*50;

        maxval = 0;
        minval = 0;
    }

Upvotes: 3

Views: 14524

Answers (3)

Mark
Mark

Reputation: 617

Trying to get the levels with MasterPeakValue appears to be more complicated than just calling the method, which defeats its simplicity.

I accidentally realized that you have to open the device for recording, even if you don't use with the incoming data. Since you are starting a WaveIn, MasterPeakValue should return a non-0 value.

A simple alternative, just for testing, is to open the properties of system's recording devices (right-click on system volume icon and choose "Recording devices").

(Tested on 2 different computers.)

Upvotes: 0

Muno
Muno

Reputation: 769

This was my little solution for getting peak from output device. I'm using NAudio version 1.7.0.15

 public partial class Form1 : Form
{


    public Form1()
    {
        InitializeComponent();

        MMDeviceEnumerator enumerator = new MMDeviceEnumerator();

        var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
        comboboxDevices.Items.AddRange(devices.ToArray());
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        if (comboboxDevices.SelectedItem != null)
        {
            var device = (MMDevice)comboboxDevices.SelectedItem;
            progressBar1.Value = (int)(Math.Round(device.AudioMeterInformation.MasterPeakValue * 100));
        }
    }
}

Upvotes: 3

Mark Heath
Mark Heath

Reputation: 49522

All that is happening in that article is that it is finding the maximum audio peak over a small interval (e.g. 20ms) and then plotting that on a decibel scale. To find the peak, examine the value of each sample in the interval and select the max value (It's what the SampleAggregator class is doing). To convert to decibels, take the log base 10 of the maximum value, and multiply by 10. So 0dB is the loudest, and anything below say -96dB is effectively silence. (actually, looking back at the article, I don't think I even bothered to convert to a decibel scale, which I probably should have done)

Upvotes: 6

Related Questions