Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / system

Logarithmic PPM sound meters

4.80/5 (5 votes)
9 Aug 2010CPOL2 min read 79.7K   8.6K  
A simple stereo sound volume meter with audio capture card selection.

VUMeter.jpg

Introduction

This a simple logarithmic stereo peak sound volume meter. Measurement is in decibels (dB). It would be useful to use in sound recording or mixing applications.

Background

This is part of another project where I am attempting to create a Morse code control panel to transmit and receive through the sound card. I originally used Jacob Klint's VolumeMeter (Managed DirectX), but found it crashed, and did not track the peak sound fast enough for my application. Morse code is measured in tens of milliseconds.

I have reused some of Jacob's code for the audio capture.

Prerequisite

It may be necessary to install the DirectX 9.0 SDK first.

Using the Code

On form load, the audio capture devices are listed into a combo box and the first in the list selected. The audio buffer reading and progress bars update start automatically using a threaded routine.

The meter response can be adjusted by altering the FrameDelay value. A value between 10 and 30 gives a good result.

The sound capture is two channels of 16 bit samples, giving values +/-32768. Eight samples are taken each time. The capture code is described by Jacob here: http://www.codeproject.com/KB/directx/volumemeter.aspx.

I use threading to read the capture buffer and update the progress bars in the background.

To convert values to dBs, use Volume (dB) = 20*(log10 (average sample / 32768)). The average sample has to be the RMS (Root/Mean/Square) value, and will result in values between 0 and -90dB. Since the progress bars only accept positive values, an offset is added of 100. By limiting the progress bar values to 74-100, only the top 26dBs are viewed.

VB
// Imports required
Imports Microsoft.DirectX.DirectSound
Imports System.Threading
Imports System.Collections.Specialized

// Detecting the Sound Cards
Private Sub VU_Sound_Form_Load(ByVal sender As Object,_
        ByVal e As System.EventArgs) Handles Me.Load
    Dim audioDevices As New CaptureDevicesCollection
    Dim x As Integer = 0

    While x < audioDevices.Count
        ComboBox1.Items.Add(audioDevices.Item(x).Description)
        x = x + 1
    End While
    ComboBox1.SelectedIndex = 0
    Start()
End Sub

// Reading the capture buffer and convert to decibels
Dim samples__1 As Array = buffer.Read(0, GetType(Int16), _
                          LockFlag.FromWriteCursor, SAMPLE_FORMAT_ARRAY)
            
Dim leftGoal As Integer = 0
Dim rightGoal As Integer = 0

// Convert the 8 samples to positive values and sum them togather 
For i As Integer = 0 To SAMPLES - 1
    If CType(samples__1.GetValue(i, 0, 0), Int16) < 0 Then
        leftGoal -= CType(samples__1.GetValue(i, 0, 0), Int16)
    Else
        leftGoal += CType(samples__1.GetValue(i, 0, 0), Int16)
    End If
    If CType(samples__1.GetValue(i, 1, 0), Int16) < 0 Then
        rightGoal -= CType(samples__1.GetValue(i, 1, 0), Int16)
    Else
        rightGoal += CType(samples__1.GetValue(i, 1, 0), Int16)
    End If
Next

//Calculate the average of the 8 samples
leftGoal = CInt(Math.Abs(leftGoal \ SAMPLES))
rightGoal = CInt(Math.Abs(rightGoal \ SAMPLES))

//Convert values to deecibels
If leftGoal = 0 Then leftGoal = 1
If rightGoal = 0 Then rightGoal = 1
leftGoal = (100 + (20 * Math.Log10(leftGoal / 32768)))
rightGoal = (100 + (20 * Math.Log10(rightGoal / 32768)))

// Updating the progress bars so

// If the measured value is larger than the 
// progress bar value then it is the peak

If ProgressBar2.Value < rightGoal Then 
   ProgressBar2.Value = rightGoal
End If

// If the measured value is smaller than the progress bar value
// step reduce the bar

If ProgressBar2.Value > rightGoal Then ' decrement the value
    If absStepSize2 < Math.Abs(rightGoal - ProgressBar2.Value) Then
        exactValue2 += stepSize2
        ProgressBar2.Value = Math.Truncate(exactValue2)
    Else
        ProgressBar2.Value = rightGoal
    End If
End If
Thread.Sleep(m_frameDelay)

Points of Interest

This is my first time using threading. I found that if you did not add the line Control.CheckForIllegalCrossThreadCalls = False before starting the thread, the form does not update.

History

  • Version 1.0.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)