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