UltimaSerial
Windaq Add-ons
UltimaWaterfall
XChart
FFT1024
Lessons
|
|
This is the in-depth study of the VB source codes of our
original UltimaSound project. Due to copyright issue, the source of
the latest version cannot be made public. If you wish to see the original
version of UltimaSound, here it is
This project uses
XChart, UltimaWaterfall, and FFT1024 controls,
please check out XChart
reference, UltimaWaterfall reference, and FFT1024 reference for further info
It demonstrates how to use VB to call Windows Wave APIs to interface to any microphone on your
PC to acquire raw data stream, perform a FFT operation and display a
spectrogram and waterfall spectrogram
This VB project contains two files, Sound.bas and
Ultimasound.frm.
Sound.bas
Sound.bas defines the prototypes of Windows Wave APIs and
structures to be used in UltimaSound.frm.
You may find difference
in our definition and the one created by Visual BASIC's API viewer, but watch
out, Visual BASIC's API viewer actually generates wrong definition for some of
the APIs!
Public Const MAXPNAMELEN = 32 ' max product name length (including NULL)
Public Type WAVEHDR
lpData As Long
dwBufferLength As Long
dwBytesRecorded As Long
dwUser As Long
dwFlags As Long
dwLoops As Long
lpNext As Long
Reserved As Long
End Type
Public Type WAVEINCAPS
wMid As Integer
wPid As Integer
vDriverVersion As Long
szPname As String * MAXPNAMELEN
dwFormats As Long
wChannels As Integer
dwSupport As Long
End Type
Public Type WAVEFORMAT
wFormatTag As Integer
nChannels As Integer
nSamplesPerSec As Long
nAvgBytesPerSec As Long
nBlockAlign As Integer
wBitsPerSample As Integer
cbSize As Integer
End Type
Public Declare Function waveInPrepareHeader Lib "winmm.dll" (ByVal hWaveIn As Long, lpWaveInHdr As WAVEHDR, ByVal uSize As Long) As Long
Public Declare Function waveInReset Lib "winmm.dll" (ByVal hWaveIn As Long) As Long
Public Declare Function waveInStart Lib "winmm.dll" (ByVal hWaveIn As Long) As Long
Public Declare Function waveInStop Lib "winmm.dll" (ByVal hWaveIn As Long) As Long
Public Declare Function waveInUnprepareHeader Lib "winmm.dll" (ByVal hWaveIn As Long, lpWaveInHdr As WAVEHDR, ByVal uSize As Long) As Long
Public Declare Function waveInAddBuffer Lib "winmm.dll" (ByVal hWaveIn As Long, lpWaveInHdr As WAVEHDR, ByVal uSize As Long) As Long
Public Declare Function waveInClose Lib "winmm.dll" (ByVal hWaveIn As Long) As Long
Public Declare Function waveInGetDevCaps Lib "winmm.dll" Alias "waveInGetDevCapsA"
(ByVal uDeviceID As Long, lpCaps As WAVEINCAPS, ByVal uSize As Long) As Long
Public Declare Function waveInGetID Lib "winmm.dll" (ByVal hWaveIn As Long, lpuDeviceID As Long) As Long
Public Declare Function waveInGetNumDevs Lib "winmm.dll" () As Long
Public Declare Function waveInOpen Lib "winmm.dll" (lphWaveIn As Long, ByVal uDeviceID As Long, lpFormat As WAVEFORMAT, ByVal dwCallback As Long, ByVal dwInstance As Long, ByVal dwFlags As Long) As Long
Public Declare Function HeapAlloc Lib "kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal dwBytes As Long) As Long
Public Declare Function HeapFree Lib "kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal lpMem As Long) As Long
Public Declare Function HeapLock Lib "kernel32" (ByVal hHeap As Long) As Long
Public Declare Function HeapUnlock Lib "kernel32" (ByVal hHeap As Long) As Long
Public Declare Function GetProcessHeap Lib "kernel32" () As Long
Public Declare Sub CopyMemoryRead Lib "kernel32" Alias
"RtlMoveMemory" (Destination As Any, ByVal Source As Long,
ByVal Length As Long)
Const WAVERR_BADFORMAT = 32 'unsupported wave format
Const WAVERR_STILLPLAYING = 33 'still something playing
Const WAVERR_UNPREPARED = 34 'header not prepared
Const WAVERR_SYNC = 35 'device is synchronous
Const WAVERR_LASTERROR = 36 'last error in range
Const MMSYSERR_NOERROR = 0
Ultimasound.frm.
Besides interfacing to the microphone on your PC via Windows multimedia APIs,
Ultimasound.frm also contains controls like XChart, UltimaWaterfall
and FFT1024 to complete the software. Watch out for
Windows' Wave APIs in bold red.
Here are the steps to acquire the sound
bytes directly from the microphone:
-
Call waveInGetNumDevs to determine the number of
the sound input devices
-
CallwaveInGetDevCaps so that the user can select a
sound input device from the pool
-
CallwaveInOpen
to open the selected device and allocate user buffer
using HeapAlloc
-
Call waveInStart to start the
device. At this point, the sound bytes will not reach your
program
-
CallwaveInPrepareHeader
and waveInAddBuffer to allow Windows to
stream the sound bytes to your own buffer
-
Copy the data to your user array
usingCopyMemoryRead
-
Repeat 5) and 6) if
necessary
-
CallwaveInStop and waveInClose when you are
done
-
CallHeapFree
to release the buffer allocated in 3)
'Copyright 2008 www.ultimaserial.com
Option Explicit
Const WAVE_FORMAT_PCM = 1
Const CALLBACK_NULL = 0
Dim NbSmpl As Integer
Dim DevHandle As Long
Dim myWaveFormat As WAVEFORMAT
Dim DataPtr As Long
Dim myWaveinCaps As WAVEINCAPS
Dim waveform(0 To 1023) As Integer
Const MMSYSERR_NOERROR = 0 '/* no error */
Const WHDR_DONE = &H1 '/* done bit */
Dim Wave1 As WAVEHDR
Dim hHeap As Long
Private Sub Check1_Click()
If Check1.Value = 1 Then
UltimaWaterfall1.ChartType =
uw3D
Option4.Enabled = False
Else
UltimaWaterfall1.ChartType = uw2D
Option4.Enabled = True
End If
End Sub
Private Sub Command1_Click()
Dim i As Integer
myWaveFormat.wFormatTag = WAVE_FORMAT_PCM
myWaveFormat.nChannels = 1
myWaveFormat.nSamplesPerSec = Val(Combo1.Text)
myWaveFormat.wBitsPerSample = 16
myWaveFormat.nBlockAlign = myWaveFormat.nChannels *
(myWaveFormat.wBitsPerSample / 8)
myWaveFormat.nAvgBytesPerSec =
myWaveFormat.nSamplesPerSec *
myWaveFormat.nBlockAlign
myWaveFormat.cbSize = 0
Label4.Caption = Format$(Val(Combo1.Text) / 2) + " Hz"
i = waveInOpen(DevHandle, Val(List1.Text), myWaveFormat, 0, 0, CALLBACK_NULL)
If i <> MMSYSERR_NOERROR Then
MsgBox "Failed to open sound input device!"
Exit Sub
End If
i = waveInStart(DevHandle)
If i <> MMSYSERR_NOERROR Then
MsgBox "Failed to start!"
Exit Sub
End If
Timer1.Enabled = True
Wave1.lpData = DataPtr
Wave1.dwBufferLength = NbSmpl
Wave1.dwFlags = 0
i = waveInPrepareHeader(DevHandle, Wave1, Len(Wave1))
If i <> MMSYSERR_NOERROR Then
MsgBox "Failed to prepare buffer!"
Exit Sub
End If
i = waveInAddBuffer(DevHandle, Wave1, Len(Wave1))
If i <> MMSYSERR_NOERROR Then
MsgBox "Failed to add buffer!"
Exit Sub
End If
End Sub
Private Sub Command2_Click()
Timer1.Enabled = False
Dim i As Integer
i =
waveInStop(DevHandle)
i = waveInClose(DevHandle)
Call waveInUnprepareHeader(DevHandle, Wave1, Len(Wave1))
If i <> 0 Then
MsgBox "Error stopping wav input device " + Format$(i)
End If
End Sub
Private Sub Command3_Click()
XChart1.CopyToClipboard
End Sub
Private Sub Command4_Click()
UltimaWaterfall1.Copy2Clipboard
End Sub
Private Sub Form_Load()
Dim i As Integer
Dim j As Integer
XChart1.Ymin(0) =
0
XChart1.SumWaveforms = False
XChart1.Lock = True
XChart1.DoubleActiveTrace = False
XChart1.ScrollMode = False
NbSmpl = 1024
i = waveInGetNumDevs()
If i <= 0 Then
MsgBox "No microphone detected!"
End If
For j = 1 To i
Call waveInGetDevCaps(j - 1, myWaveinCaps, Len(myWaveinCaps))
If j = 1 Then
List1.Text =
Format$(j - 1) + " " + myWaveinCaps.szPname
End If
List1.AddItem Format$(j - 1) + " " + myWaveinCaps.szPname
Next
hHeap = GetProcessHeap()
DataPtr = HeapAlloc(hHeap, 0, NbSmpl * 2)
If DataPtr = 0 Then
MsgBox "Failed to allocate memory for sound input device!"
Exit Sub
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
HeapFree GetProcessHeap(), 0, DataPtr
End Sub
Private Sub Option1_Click()
UltimaWaterfall1.ChartDirection =
uwB2T
End Sub
Private Sub Option2_Click()
UltimaWaterfall1.ChartDirection = uwNSlope
End Sub
Private Sub Option3_Click()
UltimaWaterfall1.ChartDirection = uwSlope
End Sub
Private Sub Option4_Click()
UltimaWaterfall1.ChartDirection = uwR2L
End Sub
Private Sub Option5_Click()
UltimaWaterfall1.ChartDirection = uwT2B
End Sub
Private Sub Timer1_Timer()
Dim i As Integer
Do
Loop Until ((Wave1.dwFlags And WHDR_DONE) = WHDR_DONE) Or DevHandle = 0
CopyMemoryRead waveform(0), DataPtr, 2048
'Add buffer for next cycle
Wave1.lpData = DataPtr
Wave1.dwBufferLength = NbSmpl
Wave1.dwFlags = 0
i = waveInPrepareHeader(DevHandle, Wave1, Len(Wave1))
If i <> MMSYSERR_NOERROR Then
MsgBox "Failed to prepare header!"
Exit Sub
End If
i = waveInAddBuffer(DevHandle, Wave1, Len(Wave1))
If i <> MMSYSERR_NOERROR Then
MsgBox "Failed to add buffer!"
Exit Sub
End If
'Perform FFT on current waveform
FFT10241.waveform waveform
XChart1.Chart FFT10241.Power
UltimaWaterfall1.Chart FFT10241.Power
End Sub
Private Sub XChart1_CrossHair(ByVal Offset As Long)
Label5.Caption = Format$(Val(Combo1.Text) * Offset / 1024#, "0")
End Sub
.
Last update: 02/29/12
Copyright: 2000-2008 www.UltimaSerial.com
|