02-18-2023, 03:38 PM
(This post was last modified: 02-22-2023, 06:07 PM by Petr.
Edit Reason: Clarification of the QB64 version for this source and announcement of the start of work on the update for Phoenix 3.5.0
)
Hello everyone in the forum. Attached is a functional program, it is a cornerstone on a possible path to an equalizer. This one is mainly focused on deep frequencies, but you can try to influence frequencies up to 5KHz with it.
I'm not educated in this field, I don't even have basic knowledge about music mixing procedures, everything I do here is logically derived and I read something somewhere.
I built it on the principle of signal subsampling. Example. You want to mix music and boost the frequency of 100 Hz. Good. So here's how I deduced it:
The basic signal has 44100 samples per second and the maximum captured frequency in such a signal is 22050 Hz. This means that for a frequency of 100 Hz we need 200 samples. So the program creates a signal by taking sample number 1 and sample number 201, looking at how much higher or lower the level of the samples is between them and calculating the missing 200 samples between them as a gradual increase or gradual difference of the signals. This creates a signal with a frequency of 100 Hz, which respects the original audio signal at its beginning and at its end.
The equalization itself is further implemented by choosing the mix ratio between this triangular signal and the original signal. These are added up and the result is then replayed. Basically, I had it in front of me in the past when I was creating WAVSAVE, but it didn't even occur to me at the time.
Before actually trying this program, I will first warn you about 4 things.
1) I tested the program only in a 32-bit IDE
2) I do not guarantee the correctness of this procedure, although it returns a fairly decent signal at deep frequencies
3) Turn down your speakers before trying, seriously loud production this time can destroy them.
4) Place correct music file name on row 2 in source code before compiling.
Program control is described in the program:
It's fully open, so you can even listen to the triangle signal itself and change the mixing level of the original signal. An equalizer should be made in a similar way, when the individual frequency curves will be mixed together with the original curve.
If someone comes up with a better solution, let them know.
Signal mixing with keys < and >
triangle signal volume (default ist 100%) can be set with keys + and -
frequency setting with keys q,w or Q,W
This source code is for QB64 2.02, I'm starting to convert it to Phoenix 3.5.0 today, 3/22/2023, I'll add it to this thread when the conversion is complete.
I'm not educated in this field, I don't even have basic knowledge about music mixing procedures, everything I do here is logically derived and I read something somewhere.
I built it on the principle of signal subsampling. Example. You want to mix music and boost the frequency of 100 Hz. Good. So here's how I deduced it:
The basic signal has 44100 samples per second and the maximum captured frequency in such a signal is 22050 Hz. This means that for a frequency of 100 Hz we need 200 samples. So the program creates a signal by taking sample number 1 and sample number 201, looking at how much higher or lower the level of the samples is between them and calculating the missing 200 samples between them as a gradual increase or gradual difference of the signals. This creates a signal with a frequency of 100 Hz, which respects the original audio signal at its beginning and at its end.
The equalization itself is further implemented by choosing the mix ratio between this triangular signal and the original signal. These are added up and the result is then replayed. Basically, I had it in front of me in the past when I was creating WAVSAVE, but it didn't even occur to me at the time.
Before actually trying this program, I will first warn you about 4 things.
1) I tested the program only in a 32-bit IDE
2) I do not guarantee the correctness of this procedure, although it returns a fairly decent signal at deep frequencies
3) Turn down your speakers before trying, seriously loud production this time can destroy them.
4) Place correct music file name on row 2 in source code before compiling.
Program control is described in the program:
It's fully open, so you can even listen to the triangle signal itself and change the mixing level of the original signal. An equalizer should be made in a similar way, when the individual frequency curves will be mixed together with the original curve.
If someone comes up with a better solution, let them know.
Signal mixing with keys < and >
triangle signal volume (default ist 100%) can be set with keys + and -
frequency setting with keys q,w or Q,W
This source code is for QB64 2.02, I'm starting to convert it to Phoenix 3.5.0 today, 3/22/2023, I'll add it to this thread when the conversion is complete.
Code: (Select All)
$NoPrefix
s$ = "s.mp3" 'PLECE HERE CORRECT MUSIC FILE NAME!
s = SndOpen(s$)
Dim As MEM LS, RS 'arrays with default original MP3 values
Dim As Integer L1, R1, L2, R2, 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)
'zajistit, aby pole LS a RS bylo delitelne hodnotou KROK
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."
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) / OffConv(krok) 'calculation of the size of the increase or decrease of the signal to create a triangular signal [LEFT]
PropocetR = (R2 - R1) / OffConv(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
Locate 1, 60: Print "Frequency curve: [Hz] "; _SndRate / 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
Locate 1, 60: Print "Frequency curve: [Hz] "; _SndRate / krok / 2
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
Locate 1, 60: Print "Frequency curve: [Hz] "; _SndRate / krok / 2
Exit Do
End If
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, 40)-(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
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.
SndRaw SL, SR
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 = 0 'wait until all music samples are playing
Loop
Loop
Loop
Function OffConv& (InputVal As _Offset) 'convert offset value to normal long numeric value - tested in 32 bit IDE only
Dim co As Long, m As _MEM
m = _Mem(InputVal)
_MemGet m, m.OFFSET, co
_MemFree m
OffConv& = co
End Function