Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Next small EQ step - EQ DONE in last example!
#13
Among other things, I worked on removing the clicks. I succeeded. Three times and each time differently. Source codes as they are: First version - uses Hanning window. But there is audible sound floating, which is of course an undesirable effect, but I'm putting it here anyway. It's simply the first development branch. In the source code, find: factor = 0.85 - 0.15 * Cos(2 * 3.14159265359 * i / (N - 1)) and here, by adjusting the ratio (rewriting the numbers 0.85 and 0.15), adjust the strength of the Hanning window. Too small a window means less sound floating and more audible pops, if they occur, but on the other hand, with the opposite ratio, the sound will float without pops. Here it's a difficult compromise. So I continued on, to the second source code: It's the same thing, but with the addition of smooth frequency filtering:
In the transition zones between bands (e.g. between 64 and 65 Hz, 125 and 126 Hz, etc.), a linear interpolation is used to calculate the gain, which makes the sudden changes that caused the clicks appear. Then there's RMS normalization with exponential smoothing:
After the IFFT, the RMS value of the original (unprocessed) block and the processed block is calculated. The new normalization factor is smoothed using the previous value (smoothing factor set to 0.8) to make the sudden changes in volume appear.
This version has the problem that it can still cause clicks at really extreme values, and above all, that when using only the bass band, the higher frequency band is also heard. Then I found out what was not clear to me (there is much more):
In order to really filter without popping, the Overlap-Add method was added. This solution uses a block transformation with overlap (usually 50% overlap, i.e. for a block of 2048 samples, each block is shifted by 1024 samples). After processing each block, the resulting (inverse FFT) block is added to the synthesis buffer. The first 1024 samples are then sent from this buffer and the rest are shifted to the beginning of the buffer. Next, the second half of the previous block is added to the input block (for further processing) and supplemented with new samples from the sound memory. This solution eliminated the floating sound even with a sharp Hannig window (50 by 50). Then, in the third version, the sound equalization had to be adjusted so that it does not equalize somewhere from 2Hz but from 40 Hz. The reason was that when this band was boosted to maximum and the other bands were turned to minimum, a sound at another higher frequency was also heard (necessary for remove the pops). Since this behavior was undesirable, I set the lowest frequency from 40 Hz and then added a filter that removes higher frequencies when boosting the bass band. All three versions are available here so that everyone can adjust it to their own liking.


Source code 1:

Code: (Select All)

'==============================================
' QB64PE Sound Equalizer with RMS Normalization & Exponential Smoothing
'==============================================
_Title "QB64PE Sound Equalizer with RMS Smoothing"

' Open the sound file (the file "b.mp3" must be available)
Zdroj = _SndOpen("b.mp3")
Dim Zvuk As _MEM

Dim A As Long
UU = 2047 ' We work with 2048 samples (indices 0 to 2047)
Dim Blok(UU) As Single ' Original left channel samples for RMS measurement
Dim BlokR(UU) As Single ' Original right channel samples (can also be used for measurement)
Dim RealPart(UU) As Single, ImagPart(UU) As Single ' FFT data – left channel
Dim RealPartR(UU) As Single, ImagPartR(UU) As Single ' FFT data – right channel
Dim Eq(9) As Single

Dim Shared N As Long
Dim VzorkovaciFrekvence As Single ' Audio sampling frequency
Dim VisualEq(9) As Single
Dim Colors(9) As _Unsigned Long
Colors(0) = _RGB32(255, 0, 0)
Colors(1) = _RGB32(255, 127, 0)
Colors(2) = _RGB32(255, 255, 0)
Colors(3) = _RGB32(127, 255, 0)
Colors(4) = _RGB32(0, 255, 0)
Colors(5) = _RGB32(0, 255, 127)
Colors(6) = _RGB32(0, 255, 255)
Colors(7) = _RGB32(0, 127, 255)
Colors(8) = _RGB32(0, 0, 255)
Colors(9) = _RGB32(127, 0, 255)

' Correction factors (Fletcher-Munson) – boost or attenuate specific frequency bands
Dim Korekce(9) As Single
Korekce(0) = 2.0 ' 2 to 64 Hz
Korekce(1) = 1.8 ' 65 to 125 Hz
Korekce(2) = 1.5 ' 126 to 250 Hz
Korekce(3) = 1.2 ' 251 to 500 Hz
Korekce(4) = 1.0 ' 501 to 1000 Hz
Korekce(5) = 0.8 ' 1001 to 2000 Hz
Korekce(6) = 0.7 ' 2001 to 4000 Hz
Korekce(7) = 0.6 ' 4001 to 8000 Hz
Korekce(8) = 0.5 ' 8001 to 16000 Hz
Korekce(9) = 0.4 ' 16001 to 22000 Hz

N = UU + 1 ' The FFT block has 2048 samples

Zvuk = _MemSound(Zdroj, 0)
VzorkovaciFrekvence = _SndRate
Screen _NewImage(800, 600, 32)

' Initialize equalizer values (default value = 2)
For FillEq = 0 To 9
    Eq(FillEq) = 2
Next

' Global variable for the smoothed normalization factor
Dim prevScaleFactor As Single
prevScaleFactor = 1

'==============================================
' Main processing loop
Do Until A >= Zvuk.SIZE

    ' Reset the visualization array
    For EqBand = 0 To 9
        VisualEq(EqBand) = 0
    Next EqBand

    ' Load a block of samples
    For i = 0 To N - 1
        If A >= Zvuk.SIZE Then Exit For
        Dim LevaStopa As Single, PravaStopa As Single
        LevaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A, Single)
        PravaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A + 4, Single)
        ' Save the original signal for later RMS normalization
        Blok(i) = LevaStopa
        BlokR(i) = PravaStopa
        ' Prepare data for FFT – set the imaginary part to 0
        RealPart(i) = LevaStopa
        RealPartR(i) = PravaStopa
        ImagPart(i) = 0
        ImagPartR(i) = 0
        A = A + 8 ' 8 bytes = 2*4 (left and right channel)
    Next i

    '-------------------------------
    ' 1. Apply the Hanning window to the time-domain block
    '    (reduces leakage and improves continuity)
    For i = 0 To N - 1
        factor = 0.75 - 0.25 * Cos(2 * 3.14159265359 * i / (N - 1)) 'check ratio 0.75 / 0.25 (total must be 1) for setup clicking / sound swimming
        RealPart(i) = RealPart(i) * factor
        RealPartR(i) = RealPartR(i) * factor
    Next i
    '-------------------------------

    ' 2. Perform FFT on both channels (convert signal to the frequency domain)
    Call FFT(RealPart(), ImagPart(), N)
    Call FFT(RealPartR(), ImagPartR(), N)

    ' 3. Frequency filtering with smooth transitions between bands
    For k = 0 To N - 1
        Frekvence = k * VzorkovaciFrekvence / N
        Dim Vol As Single, t As Single
        ' Determine the gain for the current frequency band using linear interpolation
        ' in the transition zones between bands
        If Frekvence < 2 Or Frekvence > 22000 Then
            Vol = 1
        ElseIf Frekvence < 64 Then
            Vol = Eq(0) * Korekce(0)
        ElseIf Frekvence < 65 Then
            t = (Frekvence - 64) / 1
            Vol = (1 - t) * (Eq(0) * Korekce(0)) + t * (Eq(1) * Korekce(1))
        ElseIf Frekvence < 125 Then
            Vol = Eq(1) * Korekce(1)
        ElseIf Frekvence < 126 Then
            t = (Frekvence - 125) / 1
            Vol = (1 - t) * (Eq(1) * Korekce(1)) + t * (Eq(2) * Korekce(2))
        ElseIf Frekvence < 250 Then
            Vol = Eq(2) * Korekce(2)
        ElseIf Frekvence < 251 Then
            t = (Frekvence - 250) / 1
            Vol = (1 - t) * (Eq(2) * Korekce(2)) + t * (Eq(3) * Korekce(3))
        ElseIf Frekvence < 500 Then
            Vol = Eq(3) * Korekce(3)
        ElseIf Frekvence < 501 Then
            t = (Frekvence - 500) / 1
            Vol = (1 - t) * (Eq(3) * Korekce(3)) + t * (Eq(4) * Korekce(4))
        ElseIf Frekvence < 1000 Then
            Vol = Eq(4) * Korekce(4)
        ElseIf Frekvence < 1001 Then
            t = (Frekvence - 1000) / 1
            Vol = (1 - t) * (Eq(4) * Korekce(4)) + t * (Eq(5) * Korekce(5))
        ElseIf Frekvence < 2000 Then
            Vol = Eq(5) * Korekce(5)
        ElseIf Frekvence < 2001 Then
            t = (Frekvence - 2000) / 1
            Vol = (1 - t) * (Eq(5) * Korekce(5)) + t * (Eq(6) * Korekce(6))
        ElseIf Frekvence < 4000 Then
            Vol = Eq(6) * Korekce(6)
        ElseIf Frekvence < 4001 Then
            t = (Frekvence - 4000) / 1
            Vol = (1 - t) * (Eq(6) * Korekce(6)) + t * (Eq(7) * Korekce(7))
        ElseIf Frekvence < 8000 Then
            Vol = Eq(7) * Korekce(7)
        ElseIf Frekvence < 8001 Then
            t = (Frekvence - 8000) / 1
            Vol = (1 - t) * (Eq(7) * Korekce(7)) + t * (Eq(8) * Korekce(8))
        ElseIf Frekvence < 16000 Then
            Vol = Eq(8) * Korekce(8)
        ElseIf Frekvence < 16001 Then
            t = (Frekvence - 16000) / 1
            Vol = (1 - t) * (Eq(8) * Korekce(8)) + t * (Eq(9) * Korekce(9))
        Else
            Vol = Eq(9) * Korekce(9)
        End If

        ' Update visualization – accumulate the amplitude for each frequency band
        Select Case Frekvence
            Case 2 To 64: VisualEq(0) = VisualEq(0) + Abs(RealPart(k)) * Vol
            Case 65 To 125: VisualEq(1) = VisualEq(1) + Abs(RealPart(k)) * Vol
            Case 126 To 250: VisualEq(2) = VisualEq(2) + Abs(RealPart(k)) * Vol
            Case 251 To 500: VisualEq(3) = VisualEq(3) + Abs(RealPart(k)) * Vol
            Case 501 To 1000: VisualEq(4) = VisualEq(4) + Abs(RealPart(k)) * Vol
            Case 1001 To 2000: VisualEq(5) = VisualEq(5) + Abs(RealPart(k)) * Vol
            Case 2001 To 4000: VisualEq(6) = VisualEq(6) + Abs(RealPart(k)) * Vol
            Case 4001 To 8000: VisualEq(7) = VisualEq(7) + Abs(RealPart(k)) * Vol
            Case 8001 To 16000: VisualEq(8) = VisualEq(8) + Abs(RealPart(k)) * Vol
            Case 16001 To 22000: VisualEq(9) = VisualEq(9) + Abs(RealPart(k)) * Vol
        End Select

        ' Apply the calculated gain to the frequency components of both channels
        RealPart(k) = RealPart(k) * Vol
        ImagPart(k) = ImagPart(k) * Vol
        RealPartR(k) = RealPartR(k) * Vol
        ImagPartR(k) = ImagPartR(k) * Vol
    Next k

    ' 4. Inverse FFT – reconstruct the signal in the time domain
    Call IFFT(RealPart(), ImagPart(), N)
    Call IFFT(RealPartR(), ImagPartR(), N)

    ' 5. RMS normalization with exponential smoothing
    '    Compute the RMS of the original (unprocessed) block and the processed block,
    '    then calculate a new scaling factor that is smoothed with the previous factor.
    Dim origRMS As Single, newRMS As Single
    origRMS = 0: newRMS = 0
    For i = 0 To N - 1
        origRMS = origRMS + (Blok(i) * Blok(i))
        newRMS = newRMS + (RealPart(i) * RealPart(i))
    Next i
    origRMS = Sqr(origRMS / N)
    newRMS = Sqr(newRMS / N)
    Dim newScale As Single
    If newRMS > 0 Then
        newScale = origRMS / newRMS
    Else
        newScale = 1
    End If
    Dim smoothing As Single
    smoothing = 0.3 ' Sets the smoothness of changes (closer to 1 means slower changes)
    Dim scaleFactor As Single
    scaleFactor = smoothing * prevScaleFactor + (1 - smoothing) * newScale
    prevScaleFactor = scaleFactor
    For i = 0 To N - 1
        RealPart(i) = RealPart(i) * scaleFactor
        RealPartR(i) = RealPartR(i) * scaleFactor
    Next i

    ' 6. Playback of the processed block (limit the amplitude to avoid clipping)
    For i = 0 To N - 1
        If RealPart(i) > 0.95 Then RealPart(i) = 0.95
        If RealPart(i) < -0.95 Then RealPart(i) = -0.95
        If RealPartR(i) > 0.95 Then RealPartR(i) = 0.95
        If RealPartR(i) < -0.95 Then RealPartR(i) = -0.95
        _SndRaw RealPart(i), RealPartR(i)
    Next i

    ' 7. Equalizer visualization – draw bars based on the amplitude of each frequency band
    Cls
    For EqBand = 0 To 9
        VisualEq(EqBand) = VisualEq(EqBand) / N
        BarHeight = VisualEq(EqBand) * 400
        Line (EqBand * 80 + 20, 600)-(EqBand * 80 + 60, 600 - BarHeight), Colors(EqBand), BF
    Next EqBand

    ' 8. Keyboard control for adjusting the equalizer settings
    Do Until _SndRawLen < 0.05
        i$ = InKey$
        Select Case LCase$(i$)
            Case "q": Eq(0) = Eq(0) + 0.1
            Case "a": Eq(0) = Eq(0) - 0.1
            Case "w": Eq(1) = Eq(1) + 0.1
            Case "s": Eq(1) = Eq(1) - 0.1
            Case "e": Eq(2) = Eq(2) + 0.1
            Case "d": Eq(2) = Eq(2) - 0.1
            Case "r": Eq(3) = Eq(3) + 0.1
            Case "f": Eq(3) = Eq(3) - 0.1
            Case "t": Eq(4) = Eq(4) + 0.1
            Case "g": Eq(4) = Eq(4) - 0.1
            Case "y": Eq(5) = Eq(5) + 0.1
            Case "h": Eq(5) = Eq(5) - 0.1
            Case "u": Eq(6) = Eq(6) + 0.1
            Case "j": Eq(6) = Eq(6) - 0.1
            Case "i": Eq(7) = Eq(7) + 0.1
            Case "k": Eq(7) = Eq(7) - 0.1
            Case "o": Eq(8) = Eq(8) + 0.1
            Case "l": Eq(8) = Eq(8) - 0.1
            Case "p": Eq(9) = Eq(9) + 0.1
            Case ";": Eq(9) = Eq(9) - 0.1
        End Select

        Locate 4
        Print " Use keys for equalize:"
        Print
        Print " q/a [2-64 Hz] "; Int((Eq(0) - 2) * 100); "% "
        Print " w/s [65-125 Hz] "; Int((Eq(1) - 2) * 100); "% "
        Print " e/d [126-250 Hz] "; Int((Eq(2) - 2) * 100); "% "
        Print " r/f [251-500 Hz] "; Int((Eq(3) - 2) * 100); "% "
        Print " t/g [501-1000 Hz] "; Int((Eq(4) - 2) * 100); "% "
        Print " y/h [1001-2000 Hz] "; Int((Eq(5) - 2) * 100); "% "
        Print " u/j [2001-4000 Hz] "; Int((Eq(6) - 2) * 100); "% "
        Print " i/k [4001-8000 Hz] "; Int((Eq(7) - 2) * 100); "% "
        Print " o/l [8001-16000 Hz] "; Int((Eq(8) - 2) * 100); "% "
        Print " p/; [16001-22000 Hz] "; Int((Eq(9) - 2) * 100); "% "
        _Display

        For EqVolsTest = 0 To 9
            If Eq(EqVolsTest) > 6 Then Eq(EqVolsTest) = 6
            If Eq(EqVolsTest) < 0 Then Eq(EqVolsTest) = 0
        Next
    Loop

Loop

_MemFree Zvuk
End
'==============================================
' FFT subroutine (classical implementation)
Sub FFT (RealPart() As Single, ImagPart() As Single, N As Long)
    Dim i As Long, j As Long, k As Long, m As Long, stp As Long
    Dim angle As Double
    Dim tReal As Double, tImag As Double, uReal As Double, uImag As Double

    ' Bit-reverse permutation
    j = 0
    For i = 0 To N - 1
        If i < j Then
            Swap RealPart(i), RealPart(j)
            Swap ImagPart(i), ImagPart(j)
        End If
        k = N \ 2
        Do While (k >= 1 And j >= k)
            j = j - k
            k = k \ 2
        Loop
        j = j + k
    Next i

    m = 1
    Do While m < N
        stp = m * 2
        angle = -3.14159265359 / m
        For k = 0 To m - 1
            uReal = Cos(k * angle)
            uImag = Sin(k * angle)
            For i = k To N - 1 Step stp
                j = i + m
                tReal = uReal * RealPart(j) - uImag * ImagPart(j)
                tImag = uReal * ImagPart(j) + uImag * RealPart(j)
                RealPart(j) = RealPart(i) - tReal
                ImagPart(j) = ImagPart(i) - tImag
                RealPart(i) = RealPart(i) + tReal
                ImagPart(i) = ImagPart(i) + tImag
            Next i
        Next k
        m = stp
    Loop
End Sub

'==============================================
' IFFT subroutine (inverse FFT)
Sub IFFT (RealPart() As Single, ImagPart() As Single, N As Long)
    Dim i As Long
    ' Invert the signs of the imaginary components
    For i = 0 To N - 1
        ImagPart(i) = -ImagPart(i)
    Next i
    ' Call FFT (this performs the inverse transformation)
    Call FFT(RealPart(), ImagPart(), N)
    ' Normalize and restore the signs of the imaginary components
    For i = 0 To N - 1
        RealPart(i) = RealPart(i) / N
        ImagPart(i) = -ImagPart(i) / N
    Next i
End Sub


Version 2 - Better, but not best

Code: (Select All)

'==============================================
' QB64PE Sound Equalizer with RMS Normalization, Exponential Smoothing, and Overlap-Add
'==============================================
_Title "QB64PE Sound Equalizer with Overlap-Add"

' Open the sound file (the file "b.mp3" must be available)
Zdroj = _SndOpen("b.mp3")
Dim Zvuk As _MEM
Zvuk = _MemSound(Zdroj, 0)

' Define constants for Overlap-Add
Const blockSize = 2048 ' FFT block size
Const hopSize = blockSize / 2 ' Hop size (50% overlap)

Dim A As Long ' Position in the sound memory (in bytes)

' Arrays for input block (for overlap-add processing)
Dim InL(blockSize - 1) As Single ' Left channel input block
Dim InR(blockSize - 1) As Single ' Right channel input block

' Synthesis (overlap-add) buffers for output
Dim synthesisL(blockSize - 1) As Single ' Synthesis buffer for left channel
Dim synthesisR(blockSize - 1) As Single ' Synthesis buffer for right channel

' FFT processing arrays (blockSize = 2048)
Dim RealPart(blockSize - 1) As Single, ImagPart(blockSize - 1) As Single ' Left channel FFT data
Dim RealPartR(blockSize - 1) As Single, ImagPartR(blockSize - 1) As Single ' Right channel FFT data

' Equalizer settings (10 bands)
Dim Eq(9) As Single
For FillEq = 0 To 9
    Eq(FillEq) = 2
Next

' Get audio sampling rate
Dim VzorkovaciFrekvence As Single
VzorkovaciFrekvence = _SndRate

' Visualization arrays and colors
Dim VisualEq(9) As Single
Dim Colors(9) As _Unsigned Long
Colors(0) = _RGB32(255, 0, 0)
Colors(1) = _RGB32(255, 127, 0)
Colors(2) = _RGB32(255, 255, 0)
Colors(3) = _RGB32(127, 255, 0)
Colors(4) = _RGB32(0, 255, 0)
Colors(5) = _RGB32(0, 255, 127)
Colors(6) = _RGB32(0, 255, 255)
Colors(7) = _RGB32(0, 127, 255)
Colors(8) = _RGB32(0, 0, 255)
Colors(9) = _RGB32(127, 0, 255)

' Correction factors (Fletcher-Munson) for each frequency band
Dim Korekce(9) As Single
Korekce(0) = 2.0 ' 2 to 64 Hz
Korekce(1) = 1.8 ' 65 to 125 Hz
Korekce(2) = 1.5 ' 126 to 250 Hz
Korekce(3) = 1.2 ' 251 to 500 Hz
Korekce(4) = 1.0 ' 501 to 1000 Hz
Korekce(5) = 0.8 ' 1001 to 2000 Hz
Korekce(6) = 0.7 ' 2001 to 4000 Hz
Korekce(7) = 0.6 ' 4001 to 8000 Hz
Korekce(8) = 0.5 ' 8001 to 16000 Hz
Korekce(9) = 0.4 ' 16001 to 22000 Hz

Screen _NewImage(800, 600, 32)

' Global variable for smoothed normalization factor (exponential smoothing)
Dim prevScaleFactor As Single
prevScaleFactor = 1

'===================================================
' Initialization: read the first full input block from the sound memory
For i = 0 To blockSize - 1
    If A >= Zvuk.SIZE Then Exit For
    Dim LevaStopa As Single, PravaStopa As Single
    LevaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A, Single)
    PravaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A + 4, Single)
    InL(i) = LevaStopa
    InR(i) = PravaStopa
    A = A + 8 ' 8 bytes = 2*4 (left and right channel)
Next i

' Initialize the synthesis buffers to 0
For i = 0 To blockSize - 1
    synthesisL(i) = 0
    synthesisR(i) = 0
Next i

'===================================================
' Main processing loop with Overlap-Add
Do While A < Zvuk.SIZE
    ' Reset the visualization accumulation array for each block
    For EqBand = 0 To 9
        VisualEq(EqBand) = 0
    Next EqBand

    ' ------------------------------
    ' Step 1: Prepare the processing block.
    ' For each iteration, the input block consists of the previous block's second half (overlap)
    ' and new samples read from the sound memory.
    ' ------------------------------
    ' (The current input block is already in arrays InL() and InR())

    ' Copy the input block into the FFT processing arrays
    For i = 0 To blockSize - 1
        RealPart(i) = InL(i)
        RealPartR(i) = InR(i)
        ImagPart(i) = 0
        ImagPartR(i) = 0
    Next i

    ' ------------------------------
    ' Step 2: Apply a Hanning window to the time-domain block.
    ' This reduces spectral leakage and improves block continuity.
    ' (Note: here the window has been modified; you can adjust the parameters.)
    ' ------------------------------
    For i = 0 To blockSize - 1
        factor = 0.5 - 0.5 * Cos(2 * 3.14159265359 * i / (blockSize - 1))
        RealPart(i) = RealPart(i) * factor
        RealPartR(i) = RealPartR(i) * factor
    Next i

    ' ------------------------------
    ' Step 3: Perform FFT on both channels (convert time-domain signal to frequency domain).
    ' ------------------------------
    Call FFT(RealPart(), ImagPart(), blockSize)
    Call FFT(RealPartR(), ImagPartR(), blockSize)

    ' ------------------------------
    ' Step 4: Frequency-domain processing (equalizer filtering).
    ' Here, we calculate a gain (Vol) for each frequency bin using linear interpolation
    ' between adjacent bands for smooth transitions.
    ' ------------------------------
    For k = 0 To blockSize - 1
        Frekvence = k * VzorkovaciFrekvence / blockSize
        Dim Vol As Single, t As Single
        If Frekvence < 2 Or Frekvence > 22000 Then
            Vol = 1
        ElseIf Frekvence < 64 Then
            Vol = Eq(0) * Korekce(0)
        ElseIf Frekvence < 65 Then
            t = (Frekvence - 64) / 1
            Vol = (1 - t) * (Eq(0) * Korekce(0)) + t * (Eq(1) * Korekce(1))
        ElseIf Frekvence < 125 Then
            Vol = Eq(1) * Korekce(1)
        ElseIf Frekvence < 126 Then
            t = (Frekvence - 125) / 1
            Vol = (1 - t) * (Eq(1) * Korekce(1)) + t * (Eq(2) * Korekce(2))
        ElseIf Frekvence < 250 Then
            Vol = Eq(2) * Korekce(2)
        ElseIf Frekvence < 251 Then
            t = (Frekvence - 250) / 1
            Vol = (1 - t) * (Eq(2) * Korekce(2)) + t * (Eq(3) * Korekce(3))
        ElseIf Frekvence < 500 Then
            Vol = Eq(3) * Korekce(3)
        ElseIf Frekvence < 501 Then
            t = (Frekvence - 500) / 1
            Vol = (1 - t) * (Eq(3) * Korekce(3)) + t * (Eq(4) * Korekce(4))
        ElseIf Frekvence < 1000 Then
            Vol = Eq(4) * Korekce(4)
        ElseIf Frekvence < 1001 Then
            t = (Frekvence - 1000) / 1
            Vol = (1 - t) * (Eq(4) * Korekce(4)) + t * (Eq(5) * Korekce(5))
        ElseIf Frekvence < 2000 Then
            Vol = Eq(5) * Korekce(5)
        ElseIf Frekvence < 2001 Then
            t = (Frekvence - 2000) / 1
            Vol = (1 - t) * (Eq(5) * Korekce(5)) + t * (Eq(6) * Korekce(6))
        ElseIf Frekvence < 4000 Then
            Vol = Eq(6) * Korekce(6)
        ElseIf Frekvence < 4001 Then
            t = (Frekvence - 4000) / 1
            Vol = (1 - t) * (Eq(6) * Korekce(6)) + t * (Eq(7) * Korekce(7))
        ElseIf Frekvence < 8000 Then
            Vol = Eq(7) * Korekce(7)
        ElseIf Frekvence < 8001 Then
            t = (Frekvence - 8000) / 1
            Vol = (1 - t) * (Eq(7) * Korekce(7)) + t * (Eq(8) * Korekce(8))
        ElseIf Frekvence < 16000 Then
            Vol = Eq(8) * Korekce(8)
        ElseIf Frekvence < 16001 Then
            t = (Frekvence - 16000) / 1
            Vol = (1 - t) * (Eq(8) * Korekce(8)) + t * (Eq(9) * Korekce(9))
        Else
            Vol = Eq(9) * Korekce(9)
        End If

        ' Accumulate amplitude per frequency band for visualization
        Select Case Frekvence
            Case 2 To 64: VisualEq(0) = VisualEq(0) + Abs(RealPart(k)) * Vol
            Case 65 To 125: VisualEq(1) = VisualEq(1) + Abs(RealPart(k)) * Vol
            Case 126 To 250: VisualEq(2) = VisualEq(2) + Abs(RealPart(k)) * Vol
            Case 251 To 500: VisualEq(3) = VisualEq(3) + Abs(RealPart(k)) * Vol
            Case 501 To 1000: VisualEq(4) = VisualEq(4) + Abs(RealPart(k)) * Vol
            Case 1001 To 2000: VisualEq(5) = VisualEq(5) + Abs(RealPart(k)) * Vol
            Case 2001 To 4000: VisualEq(6) = VisualEq(6) + Abs(RealPart(k)) * Vol
            Case 4001 To 8000: VisualEq(7) = VisualEq(7) + Abs(RealPart(k)) * Vol
            Case 8001 To 16000: VisualEq(8) = VisualEq(8) + Abs(RealPart(k)) * Vol
            Case 16001 To 22000: VisualEq(9) = VisualEq(9) + Abs(RealPart(k)) * Vol
        End Select

        ' Apply the calculated gain to both channels' frequency components
        RealPart(k) = RealPart(k) * Vol
        ImagPart(k) = ImagPart(k) * Vol
        RealPartR(k) = RealPartR(k) * Vol
        ImagPartR(k) = ImagPartR(k) * Vol
    Next k

    ' ------------------------------
    ' Step 5: Perform the inverse FFT to reconstruct the time-domain signal.
    ' ------------------------------
    Call IFFT(RealPart(), ImagPart(), blockSize)
    Call IFFT(RealPartR(), ImagPartR(), blockSize)

    ' ------------------------------
    ' Step 6: RMS normalization with exponential smoothing.
    ' Here we compare the RMS of the input block (InL) with the processed block (RealPart)
    ' and adjust the gain smoothly.
    ' ------------------------------
    Dim origRMS As Single, newRMS As Single
    origRMS = 0: newRMS = 0
    For i = 0 To blockSize - 1
        origRMS = origRMS + (InL(i) * InL(i)) ' Reference RMS from input block
        newRMS = newRMS + (RealPart(i) * RealPart(i))
    Next i
    origRMS = Sqr(origRMS / blockSize)
    newRMS = Sqr(newRMS / blockSize)
    Dim newScale As Single
    If newRMS > 0 Then
        newScale = origRMS / newRMS
    Else
        newScale = 1
    End If
    Dim smoothing As Single
    smoothing = 0.3 ' Smoothing factor (closer to 1 means slower changes)
    Dim scaleFactor As Single
    scaleFactor = smoothing * prevScaleFactor + (1 - smoothing) * newScale
    prevScaleFactor = scaleFactor
    For i = 0 To blockSize - 1
        RealPart(i) = RealPart(i) * scaleFactor
        RealPartR(i) = RealPartR(i) * scaleFactor
    Next i

    ' ------------------------------
    ' Step 7: Overlap-Add synthesis.
    ' Add the processed block into the synthesis buffers.
    ' ------------------------------
    For i = 0 To blockSize - 1
        synthesisL(i) = synthesisL(i) + RealPart(i)
        synthesisR(i) = synthesisR(i) + RealPartR(i)
    Next i

    ' Output the first hopSize samples from the synthesis buffers.
    For i = 0 To hopSize - 1
        Dim outL As Single, outR As Single
        outL = synthesisL(i)
        outR = synthesisR(i)
        ' Limit amplitude to avoid clipping
        If outL > 0.95 Then outL = 0.95
        If outL < -0.95 Then outL = -0.95
        If outR > 0.95 Then outR = 0.95
        If outR < -0.95 Then outR = -0.95
        _SndRaw outL, outR
    Next i

    ' Shift the synthesis buffers: remove the first hopSize samples and shift the remaining samples to the beginning.
    For i = 0 To blockSize - hopSize - 1
        synthesisL(i) = synthesisL(i + hopSize)
        synthesisR(i) = synthesisR(i + hopSize)
    Next i
    For i = blockSize - hopSize To blockSize - 1
        synthesisL(i) = 0
        synthesisR(i) = 0
    Next i

    ' ------------------------------
    ' Step 8: Prepare the next input block.
    ' Shift the second half of the current input block into the first half,
    ' then fill the remainder with new samples from the sound memory.
    ' ------------------------------
    For i = 0 To hopSize - 1
        InL(i) = InL(i + hopSize)
        InR(i) = InR(i + hopSize)
    Next i
    For i = hopSize To blockSize - 1
        If A >= Zvuk.SIZE Then
            InL(i) = 0
            InR(i) = 0
        Else
            LevaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A, Single)
            PravaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A + 4, Single)
            InL(i) = LevaStopa
            InR(i) = PravaStopa
            A = A + 8
        End If
    Next i

    ' ------------------------------
    ' Step 9: Visualization and keyboard control.
    ' The accumulated visualization data (VisualEq) is scaled and drawn as bars.
    ' The user can adjust equalizer settings via the keyboard.
    ' ------------------------------
    Cls
    For EqBand = 0 To 9
        VisualEq(EqBand) = VisualEq(EqBand) / blockSize
        BarHeight = VisualEq(EqBand) * 400
        Line (EqBand * 80 + 20, 600)-(EqBand * 80 + 60, 600 - BarHeight), Colors(EqBand), BF
    Next EqBand

    Do Until _SndRawLen < 0.05
        Dim i As String
        i$ = InKey$
        Select Case LCase$(i$)
            Case "q": Eq(0) = Eq(0) + 0.1
            Case "a": Eq(0) = Eq(0) - 0.1
            Case "w": Eq(1) = Eq(1) + 0.1
            Case "s": Eq(1) = Eq(1) - 0.1
            Case "e": Eq(2) = Eq(2) + 0.1
            Case "d": Eq(2) = Eq(2) - 0.1
            Case "r": Eq(3) = Eq(3) + 0.1
            Case "f": Eq(3) = Eq(3) - 0.1
            Case "t": Eq(4) = Eq(4) + 0.1
            Case "g": Eq(4) = Eq(4) - 0.1
            Case "y": Eq(5) = Eq(5) + 0.1
            Case "h": Eq(5) = Eq(5) - 0.1
            Case "u": Eq(6) = Eq(6) + 0.1
            Case "j": Eq(6) = Eq(6) - 0.1
            Case "i": Eq(7) = Eq(7) + 0.1
            Case "k": Eq(7) = Eq(7) - 0.1
            Case "o": Eq(8) = Eq(8) + 0.1
            Case "l": Eq(8) = Eq(8) - 0.1
            Case "p": Eq(9) = Eq(9) + 0.1
            Case ";": Eq(9) = Eq(9) - 0.1
        End Select

        Locate 4
        Print " Use keys for equalize:"
        Print
        Print " q/a [2-64 Hz] "; Int((Eq(0) - 2) * 100); "% "
        Print " w/s [65-125 Hz] "; Int((Eq(1) - 2) * 100); "% "
        Print " e/d [126-250 Hz] "; Int((Eq(2) - 2) * 100); "% "
        Print " r/f [251-500 Hz] "; Int((Eq(3) - 2) * 100); "% "
        Print " t/g [501-1000 Hz] "; Int((Eq(4) - 2) * 100); "% "
        Print " y/h [1001-2000 Hz] "; Int((Eq(5) - 2) * 100); "% "
        Print " u/j [2001-4000 Hz] "; Int((Eq(6) - 2) * 100); "% "
        Print " i/k [4001-8000 Hz] "; Int((Eq(7) - 2) * 100); "% "
        Print " o/l [8001-16000 Hz] "; Int((Eq(8) - 2) * 100); "% "
        Print " p/; [16001-22000 Hz] "; Int((Eq(9) - 2) * 100); "% "
        _Display

        For EqVolsTest = 0 To 9
            If Eq(EqVolsTest) > 6 Then Eq(EqVolsTest) = 6
            If Eq(EqVolsTest) < 0 Then Eq(EqVolsTest) = 0
        Next
    Loop

Loop

_MemFree Zvuk
End

'==============================================
' FFT subroutine (classical implementation)
Sub FFT (RealPart() As Single, ImagPart() As Single, N As Long)
    Dim i As Long, j As Long, k As Long, m As Long, stp As Long
    Dim angle As Double
    Dim tReal As Double, tImag As Double, uReal As Double, uImag As Double

    ' Bit-reverse permutation
    j = 0
    For i = 0 To N - 1
        If i < j Then
            Swap RealPart(i), RealPart(j)
            Swap ImagPart(i), ImagPart(j)
        End If
        k = N \ 2
        Do While (k >= 1 And j >= k)
            j = j - k
            k = k \ 2
        Loop
        j = j + k
    Next i

    m = 1
    Do While m < N
        stp = m * 2
        angle = -3.14159265359 / m
        For k = 0 To m - 1
            uReal = Cos(k * angle)
            uImag = Sin(k * angle)
            For i = k To N - 1 Step stp
                j = i + m
                tReal = uReal * RealPart(j) - uImag * ImagPart(j)
                tImag = uReal * ImagPart(j) + uImag * RealPart(j)
                RealPart(j) = RealPart(i) - tReal
                ImagPart(j) = ImagPart(i) - tImag
                RealPart(i) = RealPart(i) + tReal
                ImagPart(i) = ImagPart(i) + tImag
            Next i
        Next k
        m = stp
    Loop
End Sub

'==============================================
' IFFT subroutine (inverse FFT)
Sub IFFT (RealPart() As Single, ImagPart() As Single, N As Long)
    Dim i As Long
    ' Invert the signs of the imaginary components
    For i = 0 To N - 1
        ImagPart(i) = -ImagPart(i)
    Next i
    ' Call FFT (this performs the inverse transformation)
    Call FFT(RealPart(), ImagPart(), N)
    ' Normalize and restore the signs of the imaginary components
    For i = 0 To N - 1
        RealPart(i) = RealPart(i) / N
        ImagPart(i) = -ImagPart(i) / N
    Next i
End Sub

Version three - best. No clicking!

Code: (Select All)

'==============================================
' QB64PE Sound Equalizer with RMS Normalization, Exponential Smoothing, and Overlap-Add
'==============================================
_Title "QB64PE Sound Equalizer with Overlap-Add"

' Open the sound file (the file "b.mp3" must be available)
Zdroj = _SndOpen("b.mp3")
Dim Zvuk As _MEM
Zvuk = _MemSound(Zdroj, 0)

' Define constants for Overlap-Add
Const blockSize = 4096 ' FFT block size
Const hopSize = blockSize / 2 ' Hop size (50% overlap)

Dim A As Long ' Position in the sound memory (in bytes)

' Arrays for input block (for overlap-add processing)
Dim InL(blockSize - 1) As Single ' Left channel input block
Dim InR(blockSize - 1) As Single ' Right channel input block

' Synthesis (overlap-add) buffers for output
Dim synthesisL(blockSize - 1) As Single ' Synthesis buffer for left channel
Dim synthesisR(blockSize - 1) As Single ' Synthesis buffer for right channel

' FFT processing arrays (blockSize = 2048)
Dim RealPart(blockSize - 1) As Single, ImagPart(blockSize - 1) As Single ' Left channel FFT data
Dim RealPartR(blockSize - 1) As Single, ImagPartR(blockSize - 1) As Single ' Right channel FFT data

' Equalizer settings (10 bands)
Dim Eq(9) As Single
For FillEq = 0 To 9
    Eq(FillEq) = 3
Next

' Get audio sampling rate
Dim VzorkovaciFrekvence As Single
VzorkovaciFrekvence = _SndRate

' Visualization arrays and colors
Dim VisualEq(9) As Single
Dim Colors(9) As _Unsigned Long
Colors(0) = _RGB32(255, 0, 0)
Colors(1) = _RGB32(255, 127, 0)
Colors(2) = _RGB32(255, 255, 0)
Colors(3) = _RGB32(127, 255, 0)
Colors(4) = _RGB32(0, 255, 0)
Colors(5) = _RGB32(0, 255, 127)
Colors(6) = _RGB32(0, 255, 255)
Colors(7) = _RGB32(0, 127, 255)
Colors(8) = _RGB32(0, 0, 255)
Colors(9) = _RGB32(127, 0, 255)

' Correction factors (Fletcher-Munson) for each frequency band
Dim Korekce(9) As Single
Korekce(0) = 2.0 ' 2 to 64 Hz
Korekce(1) = 1.8 ' 65 to 125 Hz
Korekce(2) = 1.5 ' 126 to 250 Hz
Korekce(3) = 1.2 ' 251 to 500 Hz
Korekce(4) = 1.0 ' 501 to 1000 Hz
Korekce(5) = 0.8 ' 1001 to 2000 Hz
Korekce(6) = 0.7 ' 2001 to 4000 Hz
Korekce(7) = 0.6 ' 4001 to 8000 Hz
Korekce(8) = 0.5 ' 8001 to 16000 Hz
Korekce(9) = 0.4 ' 16001 to 22000 Hz

Screen _NewImage(800, 600, 32)

' Global variable for smoothed normalization factor (exponential smoothing)
Dim prevScaleFactor As Single
prevScaleFactor = 1

'===================================================
' Initialization: read the first full input block from the sound memory
For i = 0 To blockSize - 1
    If A >= Zvuk.SIZE Then Exit For
    Dim LevaStopa As Single, PravaStopa As Single
    LevaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A, Single)
    PravaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A + 4, Single)
    InL(i) = LevaStopa
    InR(i) = PravaStopa
    A = A + 8 ' 8 bytes = 2*4 (left and right channel)
Next i

' Initialize the synthesis buffers to 0
For i = 0 To blockSize - 1
    synthesisL(i) = 0
    synthesisR(i) = 0
Next i

'===================================================
' Main processing loop with Overlap-Add
Do While A < Zvuk.SIZE
    ' Reset the visualization accumulation array for each block
    For EqBand = 0 To 9
        VisualEq(EqBand) = 0
    Next EqBand

    ' ------------------------------
    ' Step 1: Prepare the processing block.
    ' For each iteration, the input block consists of the previous block's second half (overlap)
    ' and new samples read from the sound memory.
    ' ------------------------------
    ' (The current input block is already in arrays InL() and InR())

    ' Copy the input block into the FFT processing arrays
    For i = 0 To blockSize - 1
        RealPart(i) = InL(i)
        RealPartR(i) = InR(i)
        ImagPart(i) = 0
        ImagPartR(i) = 0
    Next i

    ' ------------------------------
    ' Step 2: Apply a Hanning window to the time-domain block.
    ' This reduces spectral leakage and improves block continuity.
    ' (Note: here the window has been modified; you can adjust the parameters.)
    ' ------------------------------
    For i = 0 To blockSize - 1
        factor = 0.5 - 0.5 * Cos(2 * 3.14159265359 * i / (blockSize - 1))
        RealPart(i) = RealPart(i) * factor
        RealPartR(i) = RealPartR(i) * factor
    Next i

    ' ------------------------------
    ' Step 3: Perform FFT on both channels (convert time-domain signal to frequency domain).
    ' ------------------------------
    Call FFT(RealPart(), ImagPart(), blockSize)
    Call FFT(RealPartR(), ImagPartR(), blockSize)

    ' ------------------------------
    ' Step 4: Frequency-domain processing (equalizer filtering).
    ' Here, we calculate a gain (Vol) for each frequency bin using linear interpolation
    ' between adjacent bands for smooth transitions.
    ' ------------------------------
    For k = 0 To blockSize - 1
        Frekvence = k * VzorkovaciFrekvence / blockSize
        Dim Vol As Single, t As Single

        ' Instead of starting the first band at 2 Hz, we now define it to cover frequencies below 40 Hz.
        ' This implements a low-pass filter for the bass boost.
        If Frekvence < 40 Then
            Vol = Eq(0) * Korekce(0)
        ElseIf Frekvence < 41 Then
            ' Interpolate between the bass band (up to 40 Hz) and the second band (starting at 41 Hz)
            t = (Frekvence - 40) / 1
            Vol = (1 - t) * (Eq(0) * Korekce(0)) + t * (Eq(1) * Korekce(1))
        ElseIf Frekvence < 125 Then
            Vol = Eq(1) * Korekce(1)
        ElseIf Frekvence < 126 Then
            t = (Frekvence - 125) / 1
            Vol = (1 - t) * (Eq(1) * Korekce(1)) + t * (Eq(2) * Korekce(2))
        ElseIf Frekvence < 250 Then
            Vol = Eq(2) * Korekce(2)
        ElseIf Frekvence < 251 Then
            t = (Frekvence - 250) / 1
            Vol = (1 - t) * (Eq(2) * Korekce(2)) + t * (Eq(3) * Korekce(3))
        ElseIf Frekvence < 500 Then
            Vol = Eq(3) * Korekce(3)
        ElseIf Frekvence < 501 Then
            t = (Frekvence - 500) / 1
            Vol = (1 - t) * (Eq(3) * Korekce(3)) + t * (Eq(4) * Korekce(4))
        ElseIf Frekvence < 1000 Then
            Vol = Eq(4) * Korekce(4)
        ElseIf Frekvence < 1001 Then
            t = (Frekvence - 1000) / 1
            Vol = (1 - t) * (Eq(4) * Korekce(4)) + t * (Eq(5) * Korekce(5))
        ElseIf Frekvence < 2000 Then
            Vol = Eq(5) * Korekce(5)
        ElseIf Frekvence < 2001 Then
            t = (Frekvence - 2000) / 1
            Vol = (1 - t) * (Eq(5) * Korekce(5)) + t * (Eq(6) * Korekce(6))
        ElseIf Frekvence < 4000 Then
            Vol = Eq(6) * Korekce(6)
        ElseIf Frekvence < 4001 Then
            t = (Frekvence - 4000) / 1
            Vol = (1 - t) * (Eq(6) * Korekce(6)) + t * (Eq(7) * Korekce(7))
        ElseIf Frekvence < 8000 Then
            Vol = Eq(7) * Korekce(7)
        ElseIf Frekvence < 8001 Then
            t = (Frekvence - 8000) / 1
            Vol = (1 - t) * (Eq(7) * Korekce(7)) + t * (Eq(8) * Korekce(8))
        ElseIf Frekvence < 16000 Then
            Vol = Eq(8) * Korekce(8)
        ElseIf Frekvence < 16001 Then
            t = (Frekvence - 16000) / 1
            Vol = (1 - t) * (Eq(8) * Korekce(8)) + t * (Eq(9) * Korekce(9))
        Else
            Vol = Eq(9) * Korekce(9)
        End If

        ' Update visualization and apply the gain to the FFT data for both channels:
        Select Case Frekvence
            '  Case 40 To 64: VisualEq(0) = VisualEq(0) + Abs(RealPart(k)) * Vol ' Visualization for the bass band
            Case 65 To 125: VisualEq(1) = VisualEq(1) + Abs(RealPart(k)) * Vol
            Case 126 To 250: VisualEq(2) = VisualEq(2) + Abs(RealPart(k)) * Vol
            Case 251 To 500: VisualEq(3) = VisualEq(3) + Abs(RealPart(k)) * Vol
            Case 501 To 1000: VisualEq(4) = VisualEq(4) + Abs(RealPart(k)) * Vol
            Case 1001 To 2000: VisualEq(5) = VisualEq(5) + Abs(RealPart(k)) * Vol
            Case 2001 To 4000: VisualEq(6) = VisualEq(6) + Abs(RealPart(k)) * Vol
            Case 4001 To 8000: VisualEq(7) = VisualEq(7) + Abs(RealPart(k)) * Vol
            Case 8001 To 16000: VisualEq(8) = VisualEq(8) + Abs(RealPart(k)) * Vol
            Case 16001 To 22000: VisualEq(9) = VisualEq(9) + Abs(RealPart(k)) * Vol
        End Select

        RealPart(k) = RealPart(k) * Vol
        ImagPart(k) = ImagPart(k) * Vol
        RealPartR(k) = RealPartR(k) * Vol
        ImagPartR(k) = ImagPartR(k) * Vol
    Next k
    ' ------------------------------
    ' Step 5: Perform the inverse FFT to reconstruct the time-domain signal.
    ' ------------------------------
    Call IFFT(RealPart(), ImagPart(), blockSize)
    Call IFFT(RealPartR(), ImagPartR(), blockSize)

    ' ------------------------------
    ' Step 6: RMS normalization with exponential smoothing.
    ' Here we compare the RMS of the input block (InL) with the processed block (RealPart)
    ' and adjust the gain smoothly.
    ' ------------------------------
    Dim origRMS As Single, newRMS As Single
    origRMS = 0: newRMS = 0
    For i = 0 To blockSize - 1
        origRMS = origRMS + (InL(i) * InL(i)) ' Reference RMS from input block
        newRMS = newRMS + (RealPart(i) * RealPart(i))
    Next i
    origRMS = Sqr(origRMS / blockSize)
    newRMS = Sqr(newRMS / blockSize)
    Dim newScale As Single
    If newRMS > 0 Then
        newScale = origRMS / newRMS
    Else
        newScale = 1
    End If
    Dim smoothing As Single
    smoothing = 0.3 ' Smoothing factor (closer to 1 means slower changes)
    Dim scaleFactor As Single
    scaleFactor = smoothing * prevScaleFactor + (1 - smoothing) * newScale
    prevScaleFactor = scaleFactor
    For i = 0 To blockSize - 1
        RealPart(i) = RealPart(i) * scaleFactor
        RealPartR(i) = RealPartR(i) * scaleFactor
    Next i

    ' ------------------------------
    ' Step 7: Overlap-Add synthesis.
    ' Add the processed block into the synthesis buffers.
    ' ------------------------------
    For i = 0 To blockSize - 1
        synthesisL(i) = synthesisL(i) + RealPart(i)
        synthesisR(i) = synthesisR(i) + RealPartR(i)
    Next i

    ' Output the first hopSize samples from the synthesis buffers.
    For i = 0 To hopSize - 1
        Dim outL As Single, outR As Single
        outL = synthesisL(i)
        outR = synthesisR(i)
        ' Limit amplitude to avoid clipping
        If outL > 0.95 Then outL = 0.95
        If outL < -0.95 Then outL = -0.95
        If outR > 0.95 Then outR = 0.95
        If outR < -0.95 Then outR = -0.95
        _SndRaw outL, outR
    Next i

    ' Shift the synthesis buffers: remove the first hopSize samples and shift the remaining samples to the beginning.
    For i = 0 To blockSize - hopSize - 1
        synthesisL(i) = synthesisL(i + hopSize)
        synthesisR(i) = synthesisR(i + hopSize)
    Next i
    For i = blockSize - hopSize To blockSize - 1
        synthesisL(i) = 0
        synthesisR(i) = 0
    Next i

    ' ------------------------------
    ' Step 8: Prepare the next input block.
    ' Shift the second half of the current input block into the first half,
    ' then fill the remainder with new samples from the sound memory.
    ' ------------------------------
    For i = 0 To hopSize - 1
        InL(i) = InL(i + hopSize)
        InR(i) = InR(i + hopSize)
    Next i
    For i = hopSize To blockSize - 1
        If A >= Zvuk.SIZE Then
            InL(i) = 0
            InR(i) = 0
        Else
            LevaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A, Single)
            PravaStopa = _MemGet(Zvuk, Zvuk.OFFSET + A + 4, Single)
            InL(i) = LevaStopa
            InR(i) = PravaStopa
            A = A + 8
        End If
    Next i

    ' ------------------------------
    ' Step 9: Visualization and keyboard control.
    ' The accumulated visualization data (VisualEq) is scaled and drawn as bars.
    ' The user can adjust equalizer settings via the keyboard.
    ' ------------------------------
    Cls
    For EqBand = 1 To 9
        VisualEq(EqBand) = VisualEq(EqBand) / blockSize
        BarHeight = VisualEq(EqBand) * 400
        Line (EqBand * 80 + 20, 600)-(EqBand * 80 + 60, 600 - BarHeight), Colors(EqBand), BF
    Next EqBand

    Do Until _SndRawLen < 0.05
        Dim i As String
        i$ = InKey$
        Select Case LCase$(i$)
            '  Case "q": Eq(0) = Eq(0) + 0.1
            '  Case "a": Eq(0) = Eq(0) - 0.1
            Case "w": Eq(1) = Eq(1) + 0.1
            Case "s": Eq(1) = Eq(1) - 0.1
            Case "e": Eq(2) = Eq(2) + 0.1
            Case "d": Eq(2) = Eq(2) - 0.1
            Case "r": Eq(3) = Eq(3) + 0.1
            Case "f": Eq(3) = Eq(3) - 0.1
            Case "t": Eq(4) = Eq(4) + 0.1
            Case "g": Eq(4) = Eq(4) - 0.1
            Case "y": Eq(5) = Eq(5) + 0.1
            Case "h": Eq(5) = Eq(5) - 0.1
            Case "u": Eq(6) = Eq(6) + 0.1
            Case "j": Eq(6) = Eq(6) - 0.1
            Case "i": Eq(7) = Eq(7) + 0.1
            Case "k": Eq(7) = Eq(7) - 0.1
            Case "o": Eq(8) = Eq(8) + 0.1
            Case "l": Eq(8) = Eq(8) - 0.1
            Case "p": Eq(9) = Eq(9) + 0.1
            Case ";": Eq(9) = Eq(9) - 0.1
        End Select

        Locate 4
        Print " Use keys for equalize:"
        Print
        ' Print " q/a [2-64 Hz] "; Int((Eq(0) - 2) * 100); "% "
        Print " w/s [65-125 Hz] "; Int((Eq(1) - 3) * 100); "% "
        Print " e/d [126-250 Hz] "; Int((Eq(2) - 3) * 100); "% "
        Print " r/f [251-500 Hz] "; Int((Eq(3) - 3) * 100); "% "
        Print " t/g [501-1000 Hz] "; Int((Eq(4) - 3) * 100); "% "
        Print " y/h [1001-2000 Hz] "; Int((Eq(5) - 3) * 100); "% "
        Print " u/j [2001-4000 Hz] "; Int((Eq(6) - 3) * 100); "% "
        Print " i/k [4001-8000 Hz] "; Int((Eq(7) - 3) * 100); "% "
        Print " o/l [8001-16000 Hz] "; Int((Eq(8) - 3) * 100); "% "
        Print " p/; [16001-22000 Hz] "; Int((Eq(9) - 3) * 100); "% "
        _Display

        For EqVolsTest = 1 To 9
            If Eq(EqVolsTest) > 6 Then Eq(EqVolsTest) = 6
            If Eq(EqVolsTest) < 0 Then Eq(EqVolsTest) = 0
        Next
    Loop

Loop

_MemFree Zvuk
End

'==============================================
' FFT subroutine (classical implementation)
Sub FFT (RealPart() As Single, ImagPart() As Single, N As Long)
    Dim i As Long, j As Long, k As Long, m As Long, stp As Long
    Dim angle As Double
    Dim tReal As Double, tImag As Double, uReal As Double, uImag As Double

    ' Bit-reverse permutation
    j = 0
    For i = 0 To N - 1
        If i < j Then
            Swap RealPart(i), RealPart(j)
            Swap ImagPart(i), ImagPart(j)
        End If
        k = N \ 2
        Do While (k >= 1 And j >= k)
            j = j - k
            k = k \ 2
        Loop
        j = j + k
    Next i

    m = 1
    Do While m < N
        stp = m * 2
        angle = -3.14159265359 / m
        For k = 0 To m - 1
            uReal = Cos(k * angle)
            uImag = Sin(k * angle)
            For i = k To N - 1 Step stp
                j = i + m
                tReal = uReal * RealPart(j) - uImag * ImagPart(j)
                tImag = uReal * ImagPart(j) + uImag * RealPart(j)
                RealPart(j) = RealPart(i) - tReal
                ImagPart(j) = ImagPart(i) - tImag
                RealPart(i) = RealPart(i) + tReal
                ImagPart(i) = ImagPart(i) + tImag
            Next i
        Next k
        m = stp
    Loop
End Sub

'==============================================
' IFFT subroutine (inverse FFT)
Sub IFFT (RealPart() As Single, ImagPart() As Single, N As Long)
    Dim i As Long
    ' Invert the signs of the imaginary components
    For i = 0 To N - 1
        ImagPart(i) = -ImagPart(i)
    Next i
    ' Call FFT (this performs the inverse transformation)
    Call FFT(RealPart(), ImagPart(), N)
    ' Normalize and restore the signs of the imaginary components
    For i = 0 To N - 1
        RealPart(i) = RealPart(i) / N
        ImagPart(i) = -ImagPart(i) / N
    Next i
End Sub


Reply


Messages In This Thread
RE: Next small EQ step - by Petr - 03-31-2024, 10:39 AM
RE: Next small EQ step - by Petr - 04-07-2024, 09:07 AM
RE: Next small EQ step - by Petr - 04-07-2024, 06:35 PM
RE: Next small EQ step - by Petr - 01-06-2025, 05:19 PM
RE: Next small EQ step - EQ DONE in last example! - by Petr - 02-07-2025, 09:19 PM



Users browsing this thread: 2 Guest(s)