3
votes

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;
    }
3
I haven't checked that project yet, but it might not be an actual peak meter. There are several "VU"-like algorithms.Brad
What is a "VU"-like algorithm?Shivam Sarodia
A VU meter (transitchicago.com/farechanges) measures signal levels. The way the needle tracks the level isn't instantaneous... it lags behind a bit, and averages out some of the peaks and valleys. There are algorithms out there that try to emulate this behavior. There are also other algorithms for metering a signal. A peak meter always shows you the highest level for the set of samples. The project you linked to may use some sort of delta algorithm, which is why it drops when nothing changes. It's just a guess though, I haven't looked at the code.Brad
@Brad excuse me but how is the link to Chicago's transport fare changes page relevant to VU meter? ;)J Pollack
@JPollack, Ha, sorry, I must have pasted a link without copying. I don't remember what I was going to link to... maybe this? en.wikipedia.org/wiki/File:VU-meter-reponse-graph.svgBrad

3 Answers

6
votes

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)

3
votes

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));
        }
    }
}
0
votes

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.)