Results 1 to 16 of 16

Thread: [VB6] - Circular spectrum visualizer.

Threaded View

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2015

    [VB6] - Circular spectrum visualizer.

    Hello everyone! I wrote the source code of the graphical audio spectrum visualizer. The sound is analyzed through a standard recording device, i.e. you can select the microphone and view its spectrum, or you can select stereo mixer and view a spectrum of a playback sound.
    This visualizer allows to adjust the number of displayed octaves, transparency of background and amplification.
    You can also load a palette from an external PNG file with 32ARGB format. It also supports the following effects: "blur" and "burning". You can view a spectrum of a signal represented in the two modes: arcs (rings) and sectors (pies). If you use the ring view an octave is mapped to radial coordinate and a semitone to angle. The separated harmonics are placed along the same line; color represents an intensity. The sectors view maps the amount of signal to the radial coordinate, the frequency in octaves to the color, the frequency in semitones to the angular coordinate.
    This idea was suggested to me by Vladislav Petrovky (aka Hacker). His idea was a little different.


    Initially it creates the buffers for sound and buffer bitmaps. Further it starts the sound capture process and waits when a buffer will be filled. When a buffer has been filled it begins processing. Firstly it performs the Fast Fourier Transform in order to transform a signal to the frequency domain form. Before performing it applies the Hamming window in order to reduce distortions because a signal has discontinuity at the edges of a buffer. When a signal has been translated to the frequency domain the buffer contains complex value that represent the vectors. The module (length) of a vector implies the energy of signal in that frequency and the argument (angle) implies phase of a harmonic in that frequency:
    We need the energy of frequency although the phase information allows to determine the frequency more accurately considering the phase difference. I don't use phase information in this project. The drawing method is different for each appearance mode. In order to boost the work it uses the precalculated coordinates named MapData. This array contains the angles of arcs and sectors for the current appearance mode. When coordinates has been calculated it calculates the amount of frequency for each FFT bin figuring out the length of a vector. This value is uses as the index in the color palette after converting the value to a range from 0 to 255. Further GDI+ draws the necessary primitives depending on the appearance mode. Note that all drawing occur onto the buffer bitmap not on window. I specially have not mentioned about the Release procedure that animates the background. This procedure applies an effect to the buffer bitmap before signal processing. It uses the Fade property that determines the speed of the disappearance of previous drawing bitmap. It just decrease the alpha value of the entire bitmap. When you use an effect it also works with the bits of the buffer bitmap and decreases the alpha value. For instance, if the blur effect has been selected it averages the near pixels (analog of low-pass filtering) then it decreases the alpha value for all pixels depending on Fade property. Eventually it draws buffer bitmap onto the main window. Thus it draws the energy of the spectrum of signal in the polar coordinates. It can be used as the start point for the notes or chord recognition. Thank for attention!
    Кривоус Анатолий (The trick).

    modAudio.bas module:
    ' // modAudio.bas - module for audio capture
    ' //  Krivous Anatoly Anatolevich (The trick), 2014
    Option Explicit
    Public Type WAVEFORMATEX       ' // Audio format structure
        wFormatTag      As Integer ' // Audio type (WAVE/MP3/etc.)
        nChannels       As Integer ' // Number of channels (mono/stereo)
        nSamplesPerSec  As Long    ' // Sample rate
        nAvgBytesPerSec As Long    ' // Number of bytes per second
        nBlockAlign     As Integer ' // One block (all channels) align in bytes
        wBitsPerSample  As Integer ' // Number of bits per sample
        cbSize          As Integer ' // Number of extra bytes
    End Type
    Public Type WAVEHDR            ' // Buffer header structure
        lpData          As Long    ' // Pointer to buffer data
        dwBufferLength  As Long    ' // Size of buffer in bytes
        dwBytesRecorded As Long    ' // Number of written bytes
        dwUser          As Long    ' // User data
        dwFlags         As Long    ' // Flags
        dwLoops         As Long    ' // Number of playings
        lpNext          As Long
        Reserved        As Long
    End Type
    Public Type BUFFER             ' // Buffer structure
        Data()          As Integer ' // Data
        Header          As WAVEHDR ' // Header
    End Type
    Public Declare Function waveInOpen Lib "winmm.dll" ( _
                            ByRef lphWaveIn As Long, _
                            ByVal uDeviceID As Long, _
                            ByRef lpFormat As WAVEFORMATEX, _
                            ByVal dwCallback As Long, _
                            ByVal dwInstance As Long, _
                            ByVal dwFlags As Long) As Long
    Public Declare Function waveInPrepareHeader Lib "winmm.dll" ( _
                            ByVal hWaveIn As Long, _
                            ByRef 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, _
                            ByRef 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 waveInGetErrorText Lib "winmm.dll" _
                            Alias "waveInGetErrorTextA" ( _
                            ByVal err As Long, _
                            ByVal lpText As String, _
                            ByVal uSize As Long) As Long
    Public Declare Function waveInAddBuffer Lib "winmm.dll" ( _
                            ByVal hWaveIn As Long, _
                            ByRef lpWaveInHdr As WAVEHDR, _
                            ByVal uSize As Long) As Long
    Public Const mSampleRate     As Long = 44100    ' // Default sample rate
    Public Const BufSizeMS       As Single = 0.03   ' // Size of buffer in ms
    Public Const WAVE_MAPPER     As Long = -1&
    Public Const CALLBACK_WINDOW As Long = &H10000
    Public Const WAVE_FORMAT_PCM As Long = 1
    Public Const MM_WIM_DATA     As Long = &H3C0
    Dim hWave       As Long         ' // Handle of wave input device
    Dim Fmt         As WAVEFORMATEX ' // Current format of capture
    Dim Buffers()   As BUFFER       ' // Buffers
    ' // This function initializes capture
    Public Function InitCapture() As Boolean
        Dim ret     As Long
        Dim msg     As String
        Dim i       As Long
        Dim count   As Long
        ' // Set format of capture
        With Fmt
            .cbSize = 0
            .wFormatTag = WAVE_FORMAT_PCM
            .wBitsPerSample = 16
            .nSamplesPerSec = mSampleRate
            .nChannels = 2
            .nBlockAlign = .nChannels * .wBitsPerSample / 8
            .nAvgBytesPerSec = .nSamplesPerSec * .nBlockAlign
        End With
        ' // Calculate size of buffer in samples
        count = Fmt.nAvgBytesPerSec * BufSizeMS
        count = count - (count Mod Fmt.nBlockAlign)
        ' // Open default recording device
        ret = waveInOpen(hWave, WAVE_MAPPER, Fmt, frmMain.hwnd, 0, CALLBACK_WINDOW)
        If ret Then ShowMessage ret: Exit Function
        ' // Create 4 buffers
        ReDim Buffers(3)
        ' // Prepare buffers
        For i = 0 To UBound(Buffers)
             With Buffers(i)
                 ReDim .Data(count - 1)
                 .Header.lpData = VarPtr(.Data(0))
                 .Header.dwBufferLength = count * 2
                 .Header.dwFlags = 0
                 .Header.dwLoops = 0
                 ret = waveInPrepareHeader(hWave, .Header, Len(.Header))
                 If ret Then ShowMessage ret: Exit Function
             End With
        Next i
        ' // Send buffers to device
        For i = 0 To UBound(Buffers)
            ret = waveInAddBuffer(hWave, Buffers(i).Header, Len(Buffers(i).Header))
            If ret Then ShowMessage ret: Exit Function
        Next i
        ' // Begin recording (capture)
        ret = waveInStart(hWave)
        If ret Then ShowMessage ret: Exit Function
        ' // Success
        InitCapture = True
    End Function
    ' // Stop capture
    Public Sub EndCapture()
        Dim i As Long
        ' // Reset device and return all the buffers to application
        waveInReset hWave
        ' // Stop capture
        waveInStop hWave
        ' // Release buffers headers
        For i = 0 To UBound(Buffers)
            waveInUnprepareHeader hWave, Buffers(i).Header, Len(Buffers(i).Header)
        ' // Close capture device
        waveInClose hWave
    End Sub
    ' // This function is called when a buffer has been filled
    Public Function OnCapture( _
                    ByRef Hdr As WAVEHDR) As Boolean
        Dim i As Long
        ' // Get buffer index
        i = modAudio.GetBufferIndex(Hdr.lpData)
        If i = -1 Then Exit Function
        ' // Redraw
        modMain.Draw modAudio.Buffers(i).Data
        ' // Send buffer to device
        waveInAddBuffer hWave, Buffers(i).Header, Len(Buffers(i).Header)
    End Function
    ' // This function returns buffer index by pointer
    Private Function GetBufferIndex( _
                     ByVal Ptr As Long) As Long
        Dim i As Long
        For i = 0 To UBound(Buffers)
            If Buffers(i).Header.lpData = Ptr Then GetBufferIndex = i: Exit Function
        GetBufferIndex = -1
    End Function
    ' // This procedure shows an error message
    Private Sub ShowMessage( _
                ByVal Code As Long)
        Dim msg As String
        msg = Space(255)
        waveInGetErrorText Code, msg, Len(msg)
        MsgBox "Error capture." & vbNewLine & msg
    End Sub

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts


Click Here to Expand Forum to Full Width