QB64 Phoenix Edition
Functional sound equalization live! - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Programs (https://qb64phoenix.com/forum/forumdisplay.php?fid=7)
+---- Thread: Functional sound equalization live! (/showthread.php?tid=720)

Pages: 1 2 3


RE: Functional sound equalization live! - Petr - 03-03-2023

Small upgrade

Thanks to @DSMan195276 opening my eyes, I've used SndOpenRaw mixing (makes it a lot easier) and added the option I've already posted here separately, so now you can control the treble with N and M + the same as in previous). I'm assuming it's 22KHz because it's made up of two adjacent samples at a refresh rate of 44100Hz (here this program downsamples it to 44100 if the real _SndRate is higher). The middle frequencies will still be a challenge, I will also play with the bass curve, because there is no need to strictly calculate the entire course if the direction of the oscillation does not change during the period (that is, if we take, for example, 400 samples and the whole time they have falling or rising trend, just copy them, but if not, i think its best try deforming the wave just minimal to next period)

Code: (Select All)
$NoPrefix
_Title "Give me more BASS!"
s$ = "Alkehol - dejvice.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
SOR2 = _SndOpenRaw
SOR3 = _SndOpenRaw
'-------------------- 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 divisible by the KROK value
Dim As Offset MaxKrok, stp, krok 'varibles for reading MEMSOUND array
Dim As _Float PropocetL, PropocetR ' variables for calculating TRIANGLE signal
krok = 220 '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."
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$
        Select Case k$
            Case ",", "<": mix = mix + .01: Locate 1: Print "Original Signal:"; Int(mix * 100); "%"; " Created Signal:"; Int(bmix * 100); "%"; "           "
            Case ".", ">": mix = mix - .01: Locate 1: Print "Original Signal:"; Int(mix * 100); "%"; " Created Signal:"; Int(bmix * 100); "%"; "           "
            Case "+": Volume = Volume + .1
                If Volume > 2 Then Volume = 2
                Locate 1, 90: Print "Created Signal Volume Level:"; Int(Volume * 100); "  " '            Volume - is created signal volume level
            Case "-": Volume = Volume - .1
                If Volume < 0 Then Volume = 0
                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

        End Select


        If mix < 0 Then mix = 0
        If mix > 1 Then mix = 1
        bmix = 1 - mix

        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), 0, 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
        MemGet LS, LS.OFFSET + (stp + es * 2) - 2, L4
        MemGet RS, RS.OFFSET + (stp + es * 2) - 2, R4
        '--------------------------------------------



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


        SL = L1 * mix / 32768 '+ (NL / 32768 * bmix * Volume) 'the same as for R1 but for left channel.
        sR = R1 * mix / 32768 '+ (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 * bmix * Volume
        sR2 = NR / 32768 * bmix * Volume

        For UpSampling = 1 To Ratio
            SndRaw SL, sR, SOR1 '    original audio stream
            SndRaw sL2, sR2, SOR2 '  triangle created stream
            SndRaw hL, hR, SOR3
        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)), , BF 'draw output signal to screen

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

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



RE: Functional sound equalization live! - Petr - 03-04-2023

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



RE: Functional sound equalization live! - Petr - 03-24-2023

Here is the first change - some sound waves are counted as Sine. The principle remained the same. Control is now fully mouse-based. Wave rendering is now for each freqency range. The appearance and principle of creating some waves have changed since the last version. I definitely have a lot more modifications planned.

This version uses direntry.h, so you don't need to write the name of the audio file anymore, the program will play all the audio files in the current folder (QB64PE compatible) one by one. The bass frequency is very deep, it's more like a subwoofer band. Of course, an adjustment is needed. It is necessary to use resonance. Next time.
Enjoy it.


Code: (Select All)
$NoPrefix
Declare CustomType Library ".\direntry"
    Function load_dir& (s As String)
    Function has_next_entry& ()
    Sub close_dir ()
    Sub get_next_entry (s As String, flags As Long, file_size As Long)
End Declare

ReDim Dire(0) As String
ReDim File(0) As String
ReDim Music(0) As String

GetFileList _CWD$, Dire(), File()
FilterMusicFiles File(), Music()


Type Tahlo
    typ As _Byte
    Xpos As Integer
    Ypos As Integer
    MinVal As _Float
    MaxVAL As _Float
    Lenght As Integer

    CurrVal As _Float
    DVal As Integer
End Type

ReDim Shared Pull(0) As Tahlo

Type RG
    position As Integer
    SO As Long
    Recs As Long
End Type

ReDim Shared RG(0) As RG
ReDim Shared RG_Helper(0) As Single


_Title "preEQ-Alpha test"

SOR1 = _SndOpenRaw 'original audio stream
SOR2 = _SndOpenRaw 'triangle (bass) stream (recomended 160 Hz)
SOR3 = _SndOpenRaw 'high stream            (11 KHz)
SOR4 = _SndOpenRaw 'middle stream
SOR5 = _SndOpenRaw 'Bass middle 800 Hz

mix = .25 'Original signal MIX level to output signal (is not volume level)
Volume = .3 'Created Signal (Created Signal) volume level


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

Screen NewImage(1200, 768, 256) 'graphis screen for visualising output signal
_ScreenHide
F1 = NewPull(30, 213, 0, 5, 1, 120, 0)
F2 = NewPull(300, 213, 0, 5, .2, 120, 0)
F3 = NewPull(550, 213, 0, 5, .3, 120, 0)
F4 = NewPull(800, 213, 0, 5, .3, 120, 0)
F5 = NewPull(1050, 213, 0, 5, .5, 120, 0)
F6 = NewPull(240, 583, -1, 1, 0, 720, 2)

InitPull F1
InitPull F2
InitPull F3
InitPull F4
InitPull F5
InitPull F6

Do Until SoundIndex = UBound(Music) + 1
    n2:
    s$ = Music(SoundIndex) 'this version plays all files in folder automaticaly - use direntry.h for it
    s = SndOpen(s$)
    ReDim As MEM LS, RS, LS800, RS800, L, R, O, f1KL, f1KR '                                        arrays with default original MP3 values
    ReDim As Integer L1, R1, L2, R2, L3, R3, L4, R4, NL, NR, Mid800L, Mid800R, Mid800Lb, Mid800Rb '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)
    If O.SIZE = 0 Then SoundIndex = SoundIndex + 1: GoTo n2
    BackCompatible O, L, R
    _MemFree O

    OriginalStream = NewRG(0, 100)
    SinusBass = NewRG(0, 100)
    HighStream = NewRG(0, 100)
    MiddleStream = NewRG(0, 100)
    MiddleBass = NewRG(0, 100)
    LSPK = NewRG(0, 700)
    RSPK = NewRG(0, 700)


    Proposal = 0
    '-------------------- 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 And 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)
    LS800 = _MemNew(L.SIZE \ Ratio)
    RS800 = _MemNew(R.SIZE \ Ratio)

    f1KL = _MemNew(L.SIZE \ Ratio)
    f1KR = _MemNew(L.SIZE \ Ratio)
    i& = 0
    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

    Make800 LS, RS, LS800, RS800, 8

    '                                           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 = 400 '                                Default is program set to creating 110 Hz (Bass) signal (44100 / 400)


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



    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

    ScreenShow
    stp = 0
    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


        If L1 > L2 Then NL = L1 Else NL = L2 '      bass signal L default value
        If R2 > R2 Then NR = R1 Else NR = R2 '      bass signal R default value

        es = 0
        If _SndRate < 48000 Then snr = 44100 Else snr = 48000


        Locked = 0


        StartL = L1 / 32768 * _Pi(.5)
        EndL = L2 / 32768 * _Pi(.5)
        StartR = R1 / 32768 * _Pi(.5)
        EndR = R2 / 32768 * _Pi(.5)

        If EndL < StartL Then ssgn = -1 Else ssgn = 1
        If EndR < StartR Then rsgn = -1 Else rsgn = 1

        Okrok = ConvertOffset(krok)
        ReDim As _Float NarustL, NarustR

        'pro vypocet uhlu sinusovky calculate sinus angle step
        NarustL = Abs(StartL - EndL) * ssgn / Okrok
        NarustR = Abs(StartR - EndR) * rsgn / Okrok
        SinL = StartL 'prvni hodnoty uhlu sinusovky
        SinR = StartR
        ' -------------------


        If stpUPDT Mod 5 = 0 Then
            UpdateRG OriginalStream, SL
            UpdateRG SinusBass, sl2
            UpdateRG HighStream, hl
            UpdateRG MiddleStream, midl
            UpdateRG MiddleBass, NL800
        End If
        stpUPDT = stpUPDT + 1

        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$
            Select Case k$
                Case "N", "n": GoTo nextsong
            End Select

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

            MemGet f1KL, f1KL.OFFSET + stp + es * 2, f1kLs
            MemGet f1KR, f1KR.OFFSET + stp + es * 2, f1kRs

            MemGet LS800, LS800.OFFSET + stp + 2 + es * 2, Mid800L
            MemGet RS800, RS800.OFFSET + stp + 2 + es * 2, Mid800R

            MemGet LS, LS.OFFSET + (stp - krok * 2) + es * 2, L1
            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
            MemGet LS, LS.OFFSET + (stp + es * 2) - 2, L4
            MemGet RS, RS.OFFSET + (stp + es * 2) - 2, R4
            '--------------------------------------------

            'get middle frequency  800 Hz
            NL800 = Mid800L / 32768 * MidVol800
            NR800 = Mid800R / 32768 * MidVol800

            'Other middle
            midl = (L3 / 32768 - (hl - midl800)) * MidVol
            midR = (R3 / 32768 - (hr - MidR800)) * MidVol

            s1KL = f1kLs / 32768 * Vol1K
            s1KR = f1kRs / 32768 * Vol1K

            midl = MINMAX(midl, -1, 1)
            midR = MINMAX(midR, -1, 1)

            s1KL = MINMAX(s1KL, -1, 1)
            s1KR = MINMAX(s1KR, -1, 1)


            '22KHz
            hl = (L3 / -32768 + L4 / 32768) * HighVol
            hr = (R3 / -32768 + R4 / 32768) * HighVol


            'Original stream
            SL = L3 * mix / 32768 * Bmix
            sR = R3 * mix / 32768 * Bmix


            'bass
            sl2 = (Sin(SinL)) * Volume
            sr2 = (Sin(SinR)) * 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)
            NL800 = MINMAX(NL800, -1, 1)
            NR800 = MINMAX(NR800, -1, 1)

            For UpSampling = 1 To Ratio
                SndRaw SL * LeftBalance, sR * RightBalance, SOR1 '    original audio stream
                SndRaw sl2 * LeftBalance, sr2 * RightBalance, SOR2 '  triangle created stream
                SndRaw hl * LeftBalance, hr * RightBalance, SOR3 '   high signal 22KHz
                SndRaw midl * LeftBalance, midR * RightBalance, SOR4 'middle
                SndRaw NL800 * LeftBalance, NR800 * RightBalance, SOR5 'MiddleBass800
            Next

            es = es + 1
            'my loop counter

            'bass sinus get up or down
            SinL = SinL + NarustL
            SinR = SinR + NarustR

            'MiddleBass800Hz
            SinL800 = SinL800 + NarustL800
            SinR800 = SinR800 + NarustR800

            Do Until SndRawLen(SOR1) < 0.1 'wait until all music samples are playing
                _Limit 300
            Loop
        Loop
        SinL = 0
        SinR = 0
        If stp Mod 1200 = 0 Then
            UpdateRG OriginalStream, SL
            UpdateRG SinusBass, sl2
            UpdateRG HighStream, hl
            UpdateRG MiddleStream, midl
            UpdateRG MiddleBass, NL800
            LSOut = SL + sl2 + hl + midl + NL800
            RSOut = sR + sr2 + hr + midR + NR800
            LSOut = MINMAX(LSOut, -1, 1)
            RSOut = MINMAX(RSOut, -1, 1)
            UpdateRG LSPK, LSOut
            UpdateRG RSPK, RSOut


            ShowRG 40, 150, OriginalStream, "Original"
            ShowRG 310, 150, SinusBass, "Bass signal"
            ShowRG 560, 150, HighStream, "High - 22 KHz"
            ShowRG 810, 150, MiddleStream, "Middle signal"
            ShowRG 1060, 150, MiddleBass, "Middle bass"

            ShowRG 250, 350, LSPK, "Left Speaker Out"
            ShowRG 250, 520, RSPK, "Right Speaker Out"

            'Vol1K je ponekud plonkovy
            Bmix = GetPull(F1)
            Volume = GetPull(F2)
            HighVol = GetPull(F3)
            MidVol = GetPull(F4)
            MidVol800 = GetPull(F5)
            Balance = GetPull(F6)

            LeftBalance = 1
            RightBalance = 1

            If Balance > 0 Then LeftBalance = -1 * Balance + LeftBalance
            If Balance < 0 Then RightBalance = RightBalance + Balance


            If SoundIndex < UBound(Music) - 1 Then _PrintString (50, 5), "Press N for next audio track: " + Music(SoundIndex + 1)
            _PrintString (50, 25), "Now playing: " + Music(SoundIndex)
            _PrintString (50, 45), "This track lenght:" + Str$(Int(LS.SIZE / 2 / Proposal)) + " [sec], track time:" + Str$(stp \ Proposal \ 2) + " [sec]      "
            _Display
        End If
    Loop
    nextsong:
    es = 0
    MemFree LS
    _MemFree RS
    _MemFree LS800
    _MemFree RS800
    _MemFree f1KL
    _MemFree f1KR
    SndClose s

    SoundIndex = SoundIndex + 1
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, temp&&
            ConvertOffset&& = temp&& '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 * 2 '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


Sub Make800 (LS As _MEM, RS As _MEM, LS800 As _MEM, RS800 As _MEM, StepMem)

    Dim As Integer Mid800L, MID800R, MID800Lb, MID800Rb
    Dim Stp As Long

    Do Until Stp >= LS.SIZE - StepMem * 4
        MemGet LS, LS.OFFSET + Stp, MID800Lb
        MemGet RS, RS.OFFSET + Stp, MID800Rb

        StL = MID800Lb / 32768 * _Pi(.5)
        StR = MID800Rb / 32768 * _Pi(.5)

        MemGet LS, LS.OFFSET + Stp + StepMem, Mid800L
        MemGet RS, RS.OFFSET + Stp + StepMem, MID800R 'stredobasova frekvence SIN

        If MID800Lb > Mid800L Then NL800 = MID800Lb Else NL800 = Mid800L 'middle bass signal default value Left channel
        If MID800Rb > MID800R Then NR800 = MID800Rb Else NR800 = MID800R

        EndL800 = Mid800L / 32768 * _Pi(.5)
        EndR800 = MID800R / 32768 * _Pi(.5)

        If EndL800 < StL Then ssgn800 = -1 Else ssgn800 = 1
        If EndR800 < StR Then rsgn800 = -1 Else rsgn800 = 1


        NarustL800 = Abs(StL - EndL800) * ssgn800 / (StepMem / 2) 'sinus 800 Hz step LEFT channel
        NarustR800 = Abs(StR - EndR800) * rsgn800 / (StepMem / 2) '                  RIGHT


        sl = MID800Lb / 32768
        sr = MID800Rb / 32768

        For WriteAll = Stp To Stp + StepMem Step 2
            NL800 = Sin(sl) * 32768
            NR800 = Sin(sr) * 32768
            _MemPut LS800, LS800.OFFSET + WriteAll, NL800 As INTEGER
            _MemPut RS800, RS800.OFFSET + WriteAll, NR800 As INTEGER
            sl = sl + NarustL800
            sr = sr + NarustR800
        Next
        Stp = Stp + StepMem
    Loop
End Sub


Function NewRG (value, records) 'create new graph handle, reserve place in RG_Helper, write to RG_Helper array first value and this value position in RG_Helper array
    u = records
    u2 = UBound(RG_Helper)
    u3 = UBound(RG)
    RG(u3).SO = u2
    RG(u3).Recs = u
    RG(u3).position = 1
    NewRG = u3
    RG_Helper(u2) = value
    ReDim _Preserve RG_Helper(u2 + u + 1) As Single
    ReDim _Preserve RG(u3 + 1) As RG
End Function

Sub UpdateRG (identity, value) ' update and shift values in RG_Helper array using RG array (identity is RG array index)
    Id = identity
    V = value
    If RG(Id).position < RG(Id).Recs Then
        RG(Id).position = RG(Id).position + 1
        i2 = RG(Id).position
        u = RG(Id).SO
        RG_Helper(u + i2) = value
        Exit Sub
    Else
        shift = RG(Id).SO
        Do Until shift = RG(Id).SO + RG(Id).Recs
            RG_Helper(shift) = RG_Helper(shift + 1)
            shift = shift + 1
        Loop
        RG_Helper(RG(Id).SO + RG(Id).Recs) = value
    End If
End Sub

Sub ShowRG (x, y, id, index$) ' Draw graph to screen
    xx = x
    s2 = RG(id).Recs
    s = RG(id).SO
    _PrintMode _KeepBackground

    p = xx - 10 + s2 / 2 - _PrintWidth(index$) / 2 'printstring X
    Line (xx - 20, y - 70)-(xx + 20 + s2, y + 50), 30, BF
    Line (xx - 17, y - 67)-(xx + 17 + s2, y + 47), , B
    C = _DefaultColor
    Color 0
    _PrintString (p, y - 64), index$
    Color C
    _PrintMode _FillBackground
    Line (xx - 20, y - 70)-(xx + 20 + s2, y + 50), , B
    Line (xx - 17, y - 47)-(xx + 17 + s2, y + 47), , B

    ss = s
    Do Until ss = s2 + s - 1
        v = RG_Helper(ss)
        v2 = RG_Helper(ss + 1)
        GoTo notthis
        If Abs(v) > 1 Then
            Do Until Abs(v) <= 1
                v = v / 2
            Loop
        End If
        notthis:
        xx = xx + 1
        Line (xx, y + v * 15)-(xx + 1, y + v2 * 15), 0
        ss = ss + 1
    Loop
    xx = 0
End Sub

Function NewPull (x, y, ValAtMin, ValAtMax, DefaultValue, PullLen, KindOfPull)
    u = UBound(Pull)
    Pull(u).Xpos = x
    Pull(u).Ypos = y
    Pull(u).MinVal = ValAtMin
    Pull(u).MaxVAL = ValAtMax
    Pull(u).Lenght = PullLen
    Pull(u).typ = KindOfPull
    Pull(u).CurrVal = DefaultValue 'designed for range 0 to 1 - value at start
    NewPull = u
    ReDim _Preserve Pull(u + 1) As Tahlo
End Function

Sub GetMouse (Mx, My, Lb, Rb, Mw)
    While _MouseInput
        Mw = Mw + _MouseWheel
    Wend
    Mx = _MouseX
    My = _MouseY
    Lb = _MouseButton(1)
    Rb = _MouseButton(2)
End Sub

Function QDetect (x1, y1, x2, y2, Xd, Yd)
    If x1 > x2 Then Swap x2, x1
    If y1 > y2 Then Swap y2, y1
    QDetect = 0
    If Xd > x1 And Xd < x2 Then
        If Yd > y1 And Yd < y2 Then
            QDetect = -1
        End If
    End If
End Function

Function CircleDetect (X&, Y&, CX&, CY&, R&)
    xy& = ((X& - CX&) ^ 2) + ((Y& - CY&) ^ 2) 'Pythagorean theorem
    If R& ^ 2 >= xy& Then CircleDetect = -1 Else CircleDetect = 0
End Function

Sub InitPull (P)
    GetMouse mx, my, lb, rb, mw
    Static GrX, GrY
    X = Pull(P).Xpos
    Y = Pull(P).Ypos
    XX = X + Pull(P).Lenght
    YY = Y + Pull(P).Lenght
    If lb = -1 Then
        stat = QDetect(X, Y, XX, YY, mx, my)
    End If
    stat = 0
    Dim As Integer GrX, GrY
    Select Case Pull(P).typ
        Case 0
            Line (X - 10, Y - 10)-(XX + 10, Y + 10), 30, BF
            Line (X - 7, Y - 7)-(XX + 7, Y + 7), , B
            Line (X, Y)-(XX, Y), 31

            Xp = X + Pull(P).Lenght * (Pull(P).CurrVal / Pull(P).MaxVAL)

            If stat Then GrX = mx Else GrX = Int(Xp)

            Line (X, Y)-(GrX, Y), 22

            CircleFill GrX, Y, 6, 16
            CircleFill GrX, Y, 5, 28
            Pull(P).DVal = Xp
        Case 1

            Line (X - 10, Y - 10)-(X + 10, YY + 10), 30, BF
            Line (X - 7, Y - 7)-(X + 7, YY + 7), , B
            Line (X, Y)-(X, YY), 31

            Yp = YY - Pull(P).Lenght * (Pull(P).CurrVal / Pull(P).MaxVAL)

            If stat Then GrY = my Else GrY = Int(Yp)
            Line (X, YY)-(X, GrY), 22

            CircleFill X, GrY, 6, 16
            CircleFill X, GrY, 5, 28
            Pull(P).DVal = Yp
        Case 2

            Line (X - 10, Y - 10)-(XX + 10, Y + 10), 30, BF
            Line (X - 7, Y - 7)-(XX + 7, Y + 7), , B
            Line (X, Y)-(XX, Y), 31

            Xp = X + Pull(P).Lenght / 2 + (Pull(P).Lenght / 2 * (Pull(P).CurrVal / Pull(P).MaxVAL))

            If stat Then GrX = mx Else GrX = Int(Xp)

            Line (X + Pull(P).Lenght / 2, Y)-(GrX, Y), 22

            CircleFill GrX, Y, 6, 16
            CircleFill GrX, Y, 5, 28
            Pull(P).DVal = Xp
        Case 3

            Line (X - 10, Y - 10)-(X + 10, YY + 10), 30, BF
            Line (X - 7, Y - 7)-(X + 7, YY + 7), , B
            Line (X, Y)-(X, YY), 31

            Yp = YY - Pull(P).Lenght / 2 - (Pull(P).Lenght / 2 * (Pull(P).CurrVal / Pull(P).MaxVAL))

            If stat Then GrY = my Else GrY = Int(Yp)
            Line (X, YY - Pull(P).Lenght / 2)-(X, GrY), 22

            CircleFill X, GrY, 6, 16
            CircleFill X, GrY, 5, 28
            Pull(P).DVal = Yp
    End Select
End Sub

Function GetPull (p)
    GetMouse mx, my, lb, rb, mw
    Select Case Pull(p).typ
        Case 0

            If QDetect(Pull(p).Xpos - 20, Pull(p).Ypos - 20, Pull(p).Xpos + 20 + Pull(p).Lenght, Pull(p).Ypos + 20, mx, my) Then
                status = CircleDetect(mx, my, Pull(p).DVal, Pull(p).Ypos, 40)
                If lb = -1 Then
                    If status Then
                        GetMouse mx, my, lb, rb, mw
                        Pull(p).CurrVal = (mx - Pull(p).Xpos) / Pull(p).Lenght * Pull(p).MaxVAL
                        If Pull(p).CurrVal > Pull(p).MaxVAL Then Pull(p).CurrVal = Pull(p).MaxVAL
                        If Pull(p).CurrVal < Pull(p).MinVal Then Pull(p).CurrVal = Pull(p).MinVal
                        InitPull p
                    End If
                End If
            End If

        Case 2
            'asi ok
            If QDetect(Pull(p).Xpos - 20, Pull(p).Ypos - 20, Pull(p).Xpos + 20 + Pull(p).Lenght, Pull(p).Ypos + 20, mx, my) Then
                status = CircleDetect(mx, my, Pull(p).DVal, Pull(p).Ypos, 40)
                If lb = -1 Then
                    If status Then
                        GetMouse mx, my, lb, rb, mw
                        If mx > Pull(p).Xpos And mx < Pull(p).Xpos + Pull(p).Lenght / 2 Then
                            Pull(p).CurrVal = ((mx - Pull(p).Lenght / 2) - Pull(p).Xpos) / (Pull(p).Lenght / 2) * Pull(p).MinVal * -1
                            If Pull(p).CurrVal < Pull(p).MinVal Then Pull(p).CurrVal = Pull(p).MinVal
                        End If
                        If mx > Pull(p).Xpos + Pull(p).Lenght / 2 And mx < Pull(p).Lenght + Pull(p).Xpos Then
                            Pull(p).CurrVal = (mx - Pull(p).Xpos - (Pull(p).Lenght / 2)) / (Pull(p).Lenght / 2) * Pull(p).MaxVAL
                            If Pull(p).CurrVal > Pull(p).MaxVAL Then Pull(p).CurrVal = Pull(p).MaxVAL
                        End If
                        InitPull p
                    End If
                End If
            End If

        Case 1
            x = Pull(p).Xpos
            Y = Pull(p).Ypos
            XX = x + Pull(p).Lenght
            YY = Y + Pull(p).Lenght

            If QDetect(x - 20, Y - 20, x + 20, YY, mx, my) Then
                status = CircleDetect(mx, my, Pull(p).Xpos, Pull(p).DVal, 40)
                If lb = -1 Then
                    If status Then
                        GetMouse mx, my, lb, rb, mw
                        Pull(p).CurrVal = (YY - my) / Pull(p).Lenght * Pull(p).MaxVAL
                        If Pull(p).CurrVal > Pull(p).MaxVAL Then Pull(p).CurrVal = Pull(p).MaxVAL
                        If Pull(p).CurrVal < Pull(p).MinVal Then Pull(p).CurrVal = Pull(p).MinVal
                        InitPull p
                    End If
                End If
            End If

        Case 3
            If QDetect(Pull(p).Xpos - 20, Pull(p).Ypos - 20, Pull(p).Xpos + 20, Pull(p).Ypos + 20 + Pull(p).Lenght, mx, my) Then
                status = CircleDetect(mx, my, Pull(p).Xpos, Pull(p).DVal, 40)
                If lb = -1 Then
                    If status Then

                        GetMouse mx, my, lb, rb, mw
                        If my > Pull(p).Ypos + Pull(p).Lenght / 2 Then
                            Pull(p).CurrVal = ((my - Pull(p).Lenght / 2) - Pull(p).Ypos) / (Pull(p).Lenght / 2) * Pull(p).MinVal
                            If Pull(p).CurrVal < Pull(p).MinVal Then Pull(p).CurrVal = Pull(p).MinVal
                        End If

                        If my < Pull(p).Ypos + Pull(p).Lenght / 2 Then
                            Pull(p).CurrVal = ((Pull(p).Lenght / 2) - (my - Pull(p).Ypos)) / (Pull(p).Lenght / 2) * Pull(p).MaxVAL
                            If Pull(p).CurrVal > Pull(p).MaxVAL Then Pull(p).CurrVal = Pull(p).MinVal
                        End If
                        InitPull p
                    End If
                End If
            End If
    End Select
    GetPull = Pull(p).CurrVal
End Function


Sub ResetMouse
    While _MouseInput
    Wend
    Do Until _MouseButton(1) = 0
        While _MouseInput
        Wend
    Loop
End Sub

Sub CircleFill (CX As Long, CY As Long, R As Long, C As Long)
    Dim Radius As Long, RadiusError As Long
    Dim X As Long, Y As Long

    Radius = Abs(R)
    RadiusError = -Radius
    X = Radius
    Y = 0

    If Radius = 0 Then PSet (CX, CY), C: Exit Sub
    Line (CX - X, CY)-(CX + X, CY), C, BF

    While X > Y
        RadiusError = RadiusError + Y * 2 + 1
        If RadiusError >= 0 Then
            If X <> Y + 1 Then
                Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
                Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
            End If
            X = X - 1
            RadiusError = RadiusError - X * 2
        End If
        Y = Y + 1
        Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
        Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
    Wend
End Sub

Sub GetFileList (SearchDirectory As String, DirList() As String, FileList() As String)
    Const IS_DIR = 1
    Const IS_FILE = 2
    Dim flags As Long, file_size As Long

    ReDim _Preserve DirList(100), FileList(100)
    DirCount = 0: FileCount = 0

    If load_dir(SearchDirectory) Then
        Do
            length = has_next_entry
            If length > -1 Then
                nam$ = Space$(length)
                get_next_entry nam$, flags, file_size
                If flags And IS_DIR Then
                    DirCount = DirCount + 1
                    If DirCount > UBound(DirList) Then ReDim _Preserve DirList(UBound(DirList) + 100)
                    DirList(DirCount) = nam$
                ElseIf flags And IS_FILE Then
                    FileCount = FileCount + 1
                    If FileCount > UBound(FileList) Then ReDim _Preserve FileList(UBound(FileList) + 100)
                    FileList(FileCount) = nam$
                End If
            End If
        Loop Until length = -1
        close_dir
    Else
    End If
    ReDim _Preserve DirList(DirCount)
    ReDim _Preserve FileList(FileCount)
End Sub

Sub FilterMusicFiles (AllFiles() As String, Music() As String)
    For f = LBound(AllFiles) To UBound(AllFiles)
        coma = InStrRev(AllFiles(f), ".") + 1
        ext$ = Mid$(AllFiles(f), coma, 4)
        If Len(ext$) < 4 Then ext$ = ext$ + Space$(4 - Len(ext$))
        Select Case LCase$(ext$)
            Case "mp3 ", "wav ", "flac", "s3m ", "xm  ", "mod ", "it  ", "ogg ", "rad ", "mid "
                Music(i) = AllFiles(f)
                i = i + 1
                ReDim _Preserve Music(i) As String
        End Select
    Next f
End Sub

Program need direntry.h library.


.h   direntry.h (Size: 1.15 KB / Downloads: 30)