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.
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