Functional sound equalization live!
#22
I've decided to release this (quite fiddly to use) source code before making any more deep and I mean major changes to it, in case anyone ever wants to try their hand at making their own equalizer. So - compared to the previous version, I added a mix of medium frequencies. It is literally a mix, in the next source code I will divide this field. In the future, I will keep the bass frequencies in the form of a triangle only up to 150 Hz. I get the mixed mids, as I would call it, by taking the original (original) signal and the triangle signal (used for the bass) and subtracting these signals from each other. This gives me a band of signals without the lowest notes. From this signal, in the continuation of this fun work, I will get the 300Hz, 500Hz and so on signals in the same way as I get the bass signal (in next release).

To try it out (don't expect dolby digital 7.2 + enhanced stereo, of course), I recommend the longest possible song - about 5 minutes long. The < and > buttons work only if the main sound stream is amplified by the V button. The Q and W buttons change the frequency of the generated triangle signal, I recommend setting it to 150 Hz. Use the + and - buttons to change the volume of the bass signal. Use the N and M keys to add/remove treble, A, C to add/remove MIX center frequencies, C, V to change the volume of the original sound. In this version, the visualization is done like this:

Black curve - course of original sound (changes according to volume)
White curve - triangle signal used for bass
Yellow-green curve - height signal
Purple curve - mix of medium frequencies


But I remind you again that what I'm doing here is just my personal guess as to how it could be - I'm literally just guessing here.

Anyway, I have to say I'm excited. I'm impressed with the amount of work you've been to do with the QB64PE with the sound. It's something absolutely amazing and I really enjoy it a lot. SndRaw that works as it should is literally my dream come true. Qb64PE has huge potential.


Code: (Select All)
$NoPrefix


_Title "Give me more BASS!"
s$ = "e.mp3" 'PLECE HERE CORRECT MUSIC FILE NAME!
s = SndOpen(s$)
Dim As MEM LS, RS, L, R, O 'arrays with default original MP3 values
Dim As Integer L1, R1, L2, R2, L3, R3, L4, R4, NL, NR 'L1, R1 - integers contains original signal values from MEMSOUND arrays, L2, R2 - the same as L1 and R1 but shifted right by KROK value, so if is created 100 Hz signal,
'                                                                        so L2 and R2 are shifted by 441 records in MEMSOUND array to right, NL, NR - for mixing both - new and original signal to new signal
'LS = MemSound(s, 1)
'RS = MemSound(s, 2)

O = _MemSound(s, 0)
BackCompatible O, L, R
_MemFree O

SOR1 = _SndOpenRaw 'original audio stream
SOR2 = _SndOpenRaw 'triangle (bass) stream
SOR3 = _SndOpenRaw 'high stream
SOR4 = _SndOpenRaw 'middle stream

'-------------------- downsampling block begin -----------------------
Proposal = 44100 'my proposal for minimal soundrate - but this is not dividible by my SndRate 192 Khz
Do Until _SndRate Mod Proposal = 0
    Proposal = Proposal + 2 '       why + 2: sound output is WAV 16 bit, 16 bit = 2 bytes, INTEGER has lenght 2 bytes and INTEGER is used in WAV data block for saving sound information (just in 16 bit WAV)
Loop
Ratio = _SndRate \ Proposal 'downsampling to 48000 Hz, my sndrate 192000 is dividible by 48000, not by 44100 (SaveSound16S is also upgraded, but it is still for saving without _SndNew)

LS = _MemNew(L.SIZE \ Ratio)
RS = _MemNew(R.SIZE \ Ratio)


Done& = 0
Do Until Done& >= L.SIZE - Ratio * 4
    L1 = _MemGet(L, L.OFFSET + Done&, Integer)
    _MemPut LS, LS.OFFSET + i&, L1

    R1 = _MemGet(R, R.OFFSET + Done&, Integer)
    _MemPut RS, RS.OFFSET + i&, R1

    i& = i& + 2
    Done& = Done& + Ratio * 2
Loop
'-------------------- downsampling block end -----------------------

_MemFree L
_MemFree R
L1 = 0: R1 = 0


'ensure that the LS and RS fields are dividible by the KROK value
Dim As Offset MaxKrok, stp, krok, oldkrok 'varibles for reading MEMSOUND array
Dim As _Float PropocetL, PropocetR ' variables for calculating TRIANGLE signal
krok = 250 'Default is program set to creating 100 Hz (Bass) signal


MaxKrok = LS.SIZE 'maximal steps value for MEM functions

Screen NewImage(1200, 768, 256) 'graphis screen for visualising output signal

Do Until MaxKrok Mod krok * 2 = 0 'this loop ensures that when reading the field MEMSOUND to create a new signal, the field will not overflow and Memory out of range not occur.
    MaxKrok = MaxKrok - 1
Loop
'Default settings
mix = .25 'Original signal MIX level to output signal (is not volume level)
Volume = 1 'Created Signal (Created Signal) volume level


Locate 1: Print "Original Signal:"; Int(mix * 100); "%"; " Created Signal:"; Int((1 - mix) * 100); "%"; "           "
Locate 1, 90: Print "Created Signal Volume Level:"; Int(Volume * 100); "  "
Locate 2, 1: Print "Use the < and > keys to set the ratio between the original and generated signal, Q and W for freq. change, + and - for generated signal volume level. New: Use N,n and M,m for high freq, C,c and V,v for volume original stream A,a; S,s for middle."

Bmix = 1 '100 percent volume for original stream
mix = .3 '30 percrent volume for triangle (bass) audio stream
HighVol = .2 'percent for 22 KHz stream

Do Until stp >= MaxKrok - krok * 4 'read MEMSOUND array in full range
    MemGet LS, LS.OFFSET + stp, L1 'read left original signal
    MemGet RS, RS.OFFSET + stp, R1 'read right original signal

    MemGet LS, LS.OFFSET + stp + krok * 2, L2 'read left original signal shifted to right by krok's value
    MemGet RS, RS.OFFSET + stp + krok * 2, R2 'read right original signal shifted to right by krok's value

    stp = stp + krok * 2 '* 2 - values in MEMSOUND array are INTEGERS, 2 Bytes long

    PropocetL = (L2 - L1) / ConvertOffset(krok) 'calculation of the size of the increase or decrease of the signal to create a triangular signal [LEFT]
    PropocetR = (R2 - R1) / ConvertOffset(krok) 'calculation of the size of the increase or decrease of the signal to create a triangular signal [RIGHT]

    PrL = PropocetL
    PrR = PropocetR
    NL = L1 'reset this value (NL) to start value before create new triangle signal for left channel
    NR = R1 'reset this value (NR) to start value before create new triangle signal for right channel

    es = 0
    If _SndRate < 48000 Then snr = 44100 Else snr = 48000
    Locate 1, 60: Print "Frequency curve: [Hz] "; snr / krok

    Locked = 0 'for the possibility of changing the frequency during playback
    Do Until es = krok 'it only reads the slice of the memsound field in which the new signal is formed
        'keyborad program setup
        k$ = InKey$


        If k$ = "*" Then '                                             work not as expected
            If UP < 0 Then k$ = "W" Else k$ = "Q"
            UP = Sin(t)
            t = t + .01
        End If



        Select Case k$

            Case ",", "<": mix = mix + .01: Locate 1: Print "Original Signal:"; Int(mix * 100); "%"
            Case ".", ">": mix = mix - .01: Locate 1: Print "Original Signal:"; Int(mix * 100); "%"

                'Triangle signal volue control (bass)
            Case "+": Volume = Volume + .1: Locate 1, 90: Print "Created Signal Volume Level:"; Int(Volume * 100); "  " '            Volume - is created signal volume level
            Case "-": Volume = Volume - .1: Locate 1, 90: Print "Created Signal Volume Level:"; Int(Volume * 100); "  "
            Case "Q", "q"
                If Locked = 0 Then
                    Locked = 1
                    krok = krok + 10
                    If krok > 550 Then krok = 550
                    MaxKrok = LS.SIZE
                    Do Until MaxKrok Mod krok * 2 = 0
                        MaxKrok = MaxKrok - 1
                    Loop
                    If _SndRate < 48000 Then snr = 44100 Else snr = 48000
                    Locate 1, 60: Print "Frequency curve: [Hz] "; snr / krok / 2 'we working with downsamples source
                    Exit Do
                End If
            Case "W", "w"
                If Locked = 0 Then
                    Locked = 1
                    krok = krok - 10
                    If krok < 1 Then krok = 1
                    MaxKrok = LS.SIZE
                    Do Until MaxKrok Mod krok * 2 = 0
                        MaxKrok = MaxKrok - 1
                    Loop
                    If _SndRate < 48000 Then snr = 44100 Else snr = 48000
                    Locate 1, 60: Print "Frequency curve: [Hz] "; snr / krok / 2
                    Exit Do
                End If
            Case "N", "n": HighVol = HighVol + .1: Locate 4: Print "High:"; Str$(HighVol): If HighVol > 5 Then HighVol = 5
            Case "M", "m": HighVol = HighVol - .1: Locate 4: Print "High:"; Str$(HighVol): If HighVol < 0 Then HighVol = 0
            Case "C", "c": Bmix = Bmix + .1
            Case "V", "v": Bmix = Bmix - .1
            Case "A", "a": MidVol = MidVol + .1
            Case "S", "s": MidVol = MidVol - .1
        End Select

        mix = MINMAX(mix, 0, 4)
        Bmix = MINMAX(Bmix, 0, 4)
        MidVol = MINMAX(MidVol, 0, 4)
        Volume = MINMAX(Volume, 0, 5)

        posuvX = posuvX + 1 'variable for shift curve on the screen, just for graphic, not for own sound function
        If posuvX = Width Then posuvX = 1
        Line (posuvX, 70)-(posuvX, Height), 40, BF

        MemGet LS, LS.OFFSET + (stp - krok * 2) + es * 2, L1 'krok and es varibles must be multiplied by two, because MEMGET reads INTEGER values.
        MemGet RS, RS.OFFSET + (stp - krok * 2) + es * 2, R1


        'get 22 Khz frequency in this 4 rows (for sample rate 44100!, this samples are all downsampled to 44100!)
        MemGet LS, LS.OFFSET + (stp + es * 2), L3
        MemGet RS, RS.OFFSET + (stp + es * 2), R3 'normalni originalni hodnota zvuku brana z originalu pri vytvareni triangle signalu
        MemGet LS, LS.OFFSET + (stp + es * 2) - 2, L4
        MemGet RS, RS.OFFSET + (stp + es * 2) - 2, R4
        '--------------------------------------------

        'get middle frequency
        MidL = (L3 / 32768 - NL / 32768) * MidVol
        MidR = (R3 / 32768 - NR / 32768) * MidVol
        MidL = MINMAX(MidL, -1, 1)
        MidR = MINMAX(MidR, -1, 1)
        'middle frequency: when creating a bass signal, a triangle is created - for example, every fifth value is taken from the MemSound field
        'and this is calculated between them as a gradual increase or as a gradual decrease. I then call the middle frequency in this program
        'the difference between the value that I calculate in the triangular signal of the bass signal and the value of the original signal.
        '--------------------------------------------

        hL = (L3 / -32768 + L4 / 32768) * HighVol
        hR = (R3 / -32768 + R4 / 32768) * HighVol


        SL = L1 * mix / 32768 * Bmix '+ (NL / 32768 * bmix * Volume) 'the same as for R1 but for left channel.
        sR = R1 * mix / 32768 * Bmix '+ (NR / 32768 * bmix * Volume) 'R1 is original signal, mix is R1 percentage level in new signal, NR is created signal, bmix is percentage level for NR and volume is NR volume level.

        sL2 = NL / 32768 * Volume
        sR2 = NR / 32768 * Volume

        hL = MINMAX(hL, -1, 1)
        hR = MINMAX(hR, -1, 1)
        SL = MINMAX(SL, -1, 1)
        sR = MINMAX(sR, -1, 1)
        sL2 = MINMAX(sL2, -1, 1)
        sR2 = MINMAX(sR2, -1, 1)



        For UpSampling = 1 To Ratio
            SndRaw SL, sR, SOR1 '    original audio stream
            SndRaw sL2, sR2, SOR2 '  triangle created stream
            SndRaw hL, hR, SOR3 '   high signal 22KHz
            SndRaw MidL, MidR, SOR4 'middle
        Next '

        NL = NL + PrL 'signal calculation - fall or rise - formation of a triangular waveform  [LEFT]
        NR = NR + PrR 'signal calculation - fall or rise - formation of a triangular waveform [RIGHT]
        es = es + 1


        Line (posuvX, (Height / 2 - SL * 100))-(posuvX, (Height / 2 + SL * 100)), 0, BF 'draw original audio stream output to screen
        Line (posuvX, (Height / 2 - MidL * 100))-(posuvX, (Height / 2 + MidL * 100)), 5, BF 'draw middle audio stream output to screen
        Line (posuvX, (Height / 2 - sL2 * 100))-(posuvX, (Height / 2 + sL2 * 100)), 30, BF 'draw triangle audio stream output to screen
        Line (posuvX, (Height / 2 - hL * 100))-(posuvX, (Height / 2 + hL * 100)), 70, BF 'draw 22Khz audio stream output to screen


        Do Until SndRawLen(SOR1) < 0.1 'wait until all music samples are playing
            _Limit 30
        Loop
    Loop
Loop
_SndClose s
_MemFree LS
_MemFree RS
End

Function MINMAX (Value, MinVal, MaxVal)
    MINMAX = Value
    If Value > MaxVal Then MINMAX = MaxVal
    If Value < MinVal Then MINMAX = MinVal
End Function



Function ConvertOffset&& (value As _Offset)
    $Checking:Off
    Dim m As _MEM 'Define a memblock
    m = _Mem(value) 'Point it to use value
    $If 64BIT Then
            'On 64 bit OSes, an OFFSET is 8 bytes in size.  We can put it directly into an Integer64
            _MEMGET m, m.OFFSET, ConvertOffset&& 'Get the contents of the memblock and put the values there directly into ConvertOffset&&
    $Else
        'However, on 32 bit OSes, an OFFSET is only 4 bytes.  We need to put it into a LONG variable first
        _MemGet m, m.OFFSET, temp& 'Like this
        ConvertOffset&& = temp& 'And then assign that long value to ConvertOffset&&
    $End If
    _MemFree m 'Free the memblock
    $Checking:On
End Function

Sub BackCompatible (Snd As _MEM, Left As _MEM, Right As _MEM)
    If Snd.SIZE = 0 Then
        Print "Original sample data array is empty."
        Exit Sub
    End If
    Dim SndChannels As Long, ChannelLenght As _Offset
    Select Case Snd.TYPE
        Case 260 ' 32-bit floating point
            ChannelLenght = Snd.SIZE \ 4 'return size in INTEGERS
            If Snd.ELEMENTSIZE = 4 Then
                SndChannels = 1
            ElseIf Snd.ELEMENTSIZE = 8 Then
                SndChannels = 2
            End If
        Case 132 ' 32-bit integer
            ChannelLenght = Snd.SIZE \ 4 'return size in INTEGERS
            If Snd.ELEMENTSIZE = 4 Then
                SndChannels = 1
            ElseIf Snd.ELEMENTSIZE = 8 Then
                SndChannels = 2
            End If
        Case 130: ' 16-bit integer
            ChannelLenght = Snd.SIZE \ 2 'return size in INTEGERS
            If Snd.ELEMENTSIZE = 2 Then
                SndChannels = 1
            ElseIf Snd.ELEMENTSIZE = 4 Then
                SndChannels = 2
            End If
        Case 1153: ' 8-bit unsigned integer
            ChannelLenght = Snd.SIZE 'return size in INTEGERS
            If Snd.ELEMENTSIZE = 1 Then
                SndChannels = 1
            ElseIf Snd.ELEMENTSIZE = 2 Then
                SndChannels = 2
            End If
    End Select


    Left = _MemNew(ChannelLenght)
    Right = _MemNew(ChannelLenght)
    Dim As Integer LI, RI
    Dim As Long Oi
    Dim i As _Offset

    Do Until i = Snd.SIZE - Snd.ELEMENTSIZE 'Read Phoenix MEMSOUND and convert it as back-compatible as QB64 2.02 MEMSOUND's output.
        Select Case SndChannels
            Case 1
                Select Case Snd.TYPE
                    Case 260: sampL = _MemGet(Snd, Snd.OFFSET + i, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(Snd, Snd.OFFSET + i, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(Snd, Snd.OFFSET + i, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(Snd, Snd.OFFSET + i, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
            Case 2
                Select Case Snd.TYPE
                    Case 260: sampL = _MemGet(Snd, Snd.OFFSET + i, Single): sampR = _MemGet(Snd, Snd.OFFSET + i + Snd.ELEMENTSIZE \ 2, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(Snd, Snd.OFFSET + i, Long) / 2147483648: sampR = _MemGet(Snd, Snd.OFFSET + i + Snd.ELEMENTSIZE \ 2, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(Snd, Snd.OFFSET + i, Integer) / 32768: sampR = _MemGet(Snd, Snd.OFFSET + i + Snd.ELEMENTSIZE \ 2, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(Snd, Snd.OFFSET + i, _Unsigned _Byte) - 128) / 128: sampR = (_MemGet(Snd, Snd.OFFSET + i + Snd.ELEMENTSIZE \ 2, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
        End Select
        If SndChannels Mod 2 = 0 Then
            LI = sampL * 32767
            RI = sampR * 32767
            _MemPut Left, Left.OFFSET + Oi, LI
            _MemPut Right, Right.OFFSET + Oi, RI
        Else
            LI = sampL * 32767
            _MemPut Left, Left.OFFSET + Oi, LI
            _MemPut Right, Right.OFFSET + Oi, LI
        End If
        i = i + Snd.ELEMENTSIZE
        Oi = Oi + 2
    Loop
End Sub


Reply


Messages In This Thread
Functional sound equalization live! - by Petr - 08-03-2022, 03:56 PM
RE: Functional sound equalization live! - by Jack - 08-03-2022, 04:12 PM
RE: Functional sound equalization live! - by Petr - 08-03-2022, 04:19 PM
RE: Functional sound equalization live! - by Petr - 08-03-2022, 04:23 PM
RE: Functional sound equalization live! - by Petr - 08-06-2022, 01:23 PM
RE: Functional sound equalization live! - by Pete - 08-06-2022, 03:27 PM
RE: Functional sound equalization live! - by Pete - 08-06-2022, 05:37 PM
RE: Functional sound equalization live! - by Petr - 02-18-2023, 03:38 PM
RE: Functional sound equalization live! - by Petr - 02-18-2023, 08:05 PM
RE: Functional sound equalization live! - by Petr - 02-18-2023, 09:52 PM
RE: Functional sound equalization live! - by Petr - 02-19-2023, 12:40 PM
RE: Functional sound equalization live! - by Petr - 02-19-2023, 03:39 PM
RE: Functional sound equalization live! - by Petr - 02-22-2023, 07:25 PM
RE: Functional sound equalization live! - by Petr - 03-03-2023, 11:53 PM
RE: Functional sound equalization live! - by Petr - 03-04-2023, 04:21 PM
RE: Functional sound equalization live! - by Petr - 03-24-2023, 08:35 PM



Users browsing this thread: 4 Guest(s)