Skip to content

Commit 7287753

Browse files
authored
Merge branch 'TheAlgorithms:master' into Euler-P78
2 parents f540cc6 + 31061aa commit 7287753

33 files changed

+1336
-72
lines changed

DIRECTORY.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
* [Newton Raphson](https://github.com/TheAlgorithms/Python/blob/master/arithmetic_analysis/newton_raphson.py)
1111
* [Secant Method](https://github.com/TheAlgorithms/Python/blob/master/arithmetic_analysis/secant_method.py)
1212

13+
## Audio Filters
14+
* [Butterworth Filter](https://github.com/TheAlgorithms/Python/blob/master/audio_filters/butterworth_filter.py)
15+
* [Iir Filter](https://github.com/TheAlgorithms/Python/blob/master/audio_filters/iir_filter.py)
16+
* [Show Response](https://github.com/TheAlgorithms/Python/blob/master/audio_filters/show_response.py)
17+
1318
## Backtracking
1419
* [All Combinations](https://github.com/TheAlgorithms/Python/blob/master/backtracking/all_combinations.py)
1520
* [All Permutations](https://github.com/TheAlgorithms/Python/blob/master/backtracking/all_permutations.py)
@@ -48,6 +53,7 @@
4853
## Cellular Automata
4954
* [Conways Game Of Life](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/conways_game_of_life.py)
5055
* [Game Of Life](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/game_of_life.py)
56+
* [Nagel Schrekenberg](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/nagel_schrekenberg.py)
5157
* [One Dimensional](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/one_dimensional.py)
5258

5359
## Ciphers
@@ -60,6 +66,7 @@
6066
* [Base64 Encoding](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base64_encoding.py)
6167
* [Base85](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base85.py)
6268
* [Beaufort Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/beaufort_cipher.py)
69+
* [Bifid](https://github.com/TheAlgorithms/Python/blob/master/ciphers/bifid.py)
6370
* [Brute Force Caesar Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/brute_force_caesar_cipher.py)
6471
* [Caesar Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/caesar_cipher.py)
6572
* [Cryptomath Module](https://github.com/TheAlgorithms/Python/blob/master/ciphers/cryptomath_module.py)
@@ -106,6 +113,7 @@
106113

107114
## Conversions
108115
* [Binary To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_decimal.py)
116+
* [Binary To Hexadecimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_hexadecimal.py)
109117
* [Binary To Octal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_octal.py)
110118
* [Decimal To Any](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_any.py)
111119
* [Decimal To Binary](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_binary.py)
@@ -121,6 +129,7 @@
121129
* [Rgb Hsv Conversion](https://github.com/TheAlgorithms/Python/blob/master/conversions/rgb_hsv_conversion.py)
122130
* [Roman Numerals](https://github.com/TheAlgorithms/Python/blob/master/conversions/roman_numerals.py)
123131
* [Temperature Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/temperature_conversions.py)
132+
* [Volume Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/volume_conversions.py)
124133
* [Weight Conversion](https://github.com/TheAlgorithms/Python/blob/master/conversions/weight_conversion.py)
125134

126135
## Data Structures
@@ -282,6 +291,9 @@
282291
* Tests
283292
* [Test Send File](https://github.com/TheAlgorithms/Python/blob/master/file_transfer/tests/test_send_file.py)
284293

294+
## Financial
295+
* [Interest](https://github.com/TheAlgorithms/Python/blob/master/financial/interest.py)
296+
285297
## Fractals
286298
* [Julia Sets](https://github.com/TheAlgorithms/Python/blob/master/fractals/julia_sets.py)
287299
* [Koch Snowflake](https://github.com/TheAlgorithms/Python/blob/master/fractals/koch_snowflake.py)
@@ -351,6 +363,7 @@
351363
* [Multi Heuristic Astar](https://github.com/TheAlgorithms/Python/blob/master/graphs/multi_heuristic_astar.py)
352364
* [Page Rank](https://github.com/TheAlgorithms/Python/blob/master/graphs/page_rank.py)
353365
* [Prim](https://github.com/TheAlgorithms/Python/blob/master/graphs/prim.py)
366+
* [Random Graph Generator](https://github.com/TheAlgorithms/Python/blob/master/graphs/random_graph_generator.py)
354367
* [Scc Kosaraju](https://github.com/TheAlgorithms/Python/blob/master/graphs/scc_kosaraju.py)
355368
* [Strongly Connected Components](https://github.com/TheAlgorithms/Python/blob/master/graphs/strongly_connected_components.py)
356369
* [Tarjans Scc](https://github.com/TheAlgorithms/Python/blob/master/graphs/tarjans_scc.py)
@@ -938,6 +951,7 @@
938951
* [Capitalize](https://github.com/TheAlgorithms/Python/blob/master/strings/capitalize.py)
939952
* [Check Anagrams](https://github.com/TheAlgorithms/Python/blob/master/strings/check_anagrams.py)
940953
* [Check Pangram](https://github.com/TheAlgorithms/Python/blob/master/strings/check_pangram.py)
954+
* [Credit Card Validator](https://github.com/TheAlgorithms/Python/blob/master/strings/credit_card_validator.py)
941955
* [Detecting English Programmatically](https://github.com/TheAlgorithms/Python/blob/master/strings/detecting_english_programmatically.py)
942956
* [Frequency Finder](https://github.com/TheAlgorithms/Python/blob/master/strings/frequency_finder.py)
943957
* [Indian Phone Validator](https://github.com/TheAlgorithms/Python/blob/master/strings/indian_phone_validator.py)
@@ -955,6 +969,7 @@
955969
* [Rabin Karp](https://github.com/TheAlgorithms/Python/blob/master/strings/rabin_karp.py)
956970
* [Remove Duplicate](https://github.com/TheAlgorithms/Python/blob/master/strings/remove_duplicate.py)
957971
* [Reverse Letters](https://github.com/TheAlgorithms/Python/blob/master/strings/reverse_letters.py)
972+
* [Reverse Long Words](https://github.com/TheAlgorithms/Python/blob/master/strings/reverse_long_words.py)
958973
* [Reverse Words](https://github.com/TheAlgorithms/Python/blob/master/strings/reverse_words.py)
959974
* [Split](https://github.com/TheAlgorithms/Python/blob/master/strings/split.py)
960975
* [Upper](https://github.com/TheAlgorithms/Python/blob/master/strings/upper.py)
@@ -979,6 +994,8 @@
979994
* [Fetch Jobs](https://github.com/TheAlgorithms/Python/blob/master/web_programming/fetch_jobs.py)
980995
* [Get Imdb Top 250 Movies Csv](https://github.com/TheAlgorithms/Python/blob/master/web_programming/get_imdb_top_250_movies_csv.py)
981996
* [Get Imdbtop](https://github.com/TheAlgorithms/Python/blob/master/web_programming/get_imdbtop.py)
997+
* [Get Top Hn Posts](https://github.com/TheAlgorithms/Python/blob/master/web_programming/get_top_hn_posts.py)
998+
* [Get User Tweets](https://github.com/TheAlgorithms/Python/blob/master/web_programming/get_user_tweets.py)
982999
* [Giphy](https://github.com/TheAlgorithms/Python/blob/master/web_programming/giphy.py)
9831000
* [Instagram Crawler](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_crawler.py)
9841001
* [Instagram Pic](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_pic.py)

audio_filters/__init__.py

Whitespace-only changes.

audio_filters/butterworth_filter.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
from math import cos, sin, sqrt, tau
2+
3+
from audio_filters.iir_filter import IIRFilter
4+
5+
"""
6+
Create 2nd-order IIR filters with Butterworth design.
7+
8+
Code based on https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
9+
Alternatively you can use scipy.signal.butter, which should yield the same results.
10+
"""
11+
12+
13+
def make_lowpass(
14+
frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)
15+
) -> IIRFilter:
16+
"""
17+
Creates a low-pass filter
18+
19+
>>> filter = make_lowpass(1000, 48000)
20+
>>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE
21+
[1.0922959556412573, -1.9828897227476208, 0.9077040443587427, 0.004277569313094809,
22+
0.008555138626189618, 0.004277569313094809]
23+
"""
24+
w0 = tau * frequency / samplerate
25+
_sin = sin(w0)
26+
_cos = cos(w0)
27+
alpha = _sin / (2 * q_factor)
28+
29+
b0 = (1 - _cos) / 2
30+
b1 = 1 - _cos
31+
32+
a0 = 1 + alpha
33+
a1 = -2 * _cos
34+
a2 = 1 - alpha
35+
36+
filt = IIRFilter(2)
37+
filt.set_coefficients([a0, a1, a2], [b0, b1, b0])
38+
return filt
39+
40+
41+
def make_highpass(
42+
frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)
43+
) -> IIRFilter:
44+
"""
45+
Creates a high-pass filter
46+
47+
>>> filter = make_highpass(1000, 48000)
48+
>>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE
49+
[1.0922959556412573, -1.9828897227476208, 0.9077040443587427, 0.9957224306869052,
50+
-1.9914448613738105, 0.9957224306869052]
51+
"""
52+
w0 = tau * frequency / samplerate
53+
_sin = sin(w0)
54+
_cos = cos(w0)
55+
alpha = _sin / (2 * q_factor)
56+
57+
b0 = (1 + _cos) / 2
58+
b1 = -1 - _cos
59+
60+
a0 = 1 + alpha
61+
a1 = -2 * _cos
62+
a2 = 1 - alpha
63+
64+
filt = IIRFilter(2)
65+
filt.set_coefficients([a0, a1, a2], [b0, b1, b0])
66+
return filt
67+
68+
69+
def make_bandpass(
70+
frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)
71+
) -> IIRFilter:
72+
"""
73+
Creates a band-pass filter
74+
75+
>>> filter = make_bandpass(1000, 48000)
76+
>>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE
77+
[1.0922959556412573, -1.9828897227476208, 0.9077040443587427, 0.06526309611002579,
78+
0, -0.06526309611002579]
79+
"""
80+
w0 = tau * frequency / samplerate
81+
_sin = sin(w0)
82+
_cos = cos(w0)
83+
alpha = _sin / (2 * q_factor)
84+
85+
b0 = _sin / 2
86+
b1 = 0
87+
b2 = -b0
88+
89+
a0 = 1 + alpha
90+
a1 = -2 * _cos
91+
a2 = 1 - alpha
92+
93+
filt = IIRFilter(2)
94+
filt.set_coefficients([a0, a1, a2], [b0, b1, b2])
95+
return filt
96+
97+
98+
def make_allpass(
99+
frequency: int, samplerate: int, q_factor: float = 1 / sqrt(2)
100+
) -> IIRFilter:
101+
"""
102+
Creates an all-pass filter
103+
104+
>>> filter = make_allpass(1000, 48000)
105+
>>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE
106+
[1.0922959556412573, -1.9828897227476208, 0.9077040443587427, 0.9077040443587427,
107+
-1.9828897227476208, 1.0922959556412573]
108+
"""
109+
w0 = tau * frequency / samplerate
110+
_sin = sin(w0)
111+
_cos = cos(w0)
112+
alpha = _sin / (2 * q_factor)
113+
114+
b0 = 1 - alpha
115+
b1 = -2 * _cos
116+
b2 = 1 + alpha
117+
118+
filt = IIRFilter(2)
119+
filt.set_coefficients([b2, b1, b0], [b0, b1, b2])
120+
return filt
121+
122+
123+
def make_peak(
124+
frequency: int, samplerate: int, gain_db: float, q_factor: float = 1 / sqrt(2)
125+
) -> IIRFilter:
126+
"""
127+
Creates a peak filter
128+
129+
>>> filter = make_peak(1000, 48000, 6)
130+
>>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE
131+
[1.0653405327119334, -1.9828897227476208, 0.9346594672880666, 1.1303715025601122,
132+
-1.9828897227476208, 0.8696284974398878]
133+
"""
134+
w0 = tau * frequency / samplerate
135+
_sin = sin(w0)
136+
_cos = cos(w0)
137+
alpha = _sin / (2 * q_factor)
138+
big_a = 10 ** (gain_db / 40)
139+
140+
b0 = 1 + alpha * big_a
141+
b1 = -2 * _cos
142+
b2 = 1 - alpha * big_a
143+
a0 = 1 + alpha / big_a
144+
a1 = -2 * _cos
145+
a2 = 1 - alpha / big_a
146+
147+
filt = IIRFilter(2)
148+
filt.set_coefficients([a0, a1, a2], [b0, b1, b2])
149+
return filt
150+
151+
152+
def make_lowshelf(
153+
frequency: int, samplerate: int, gain_db: float, q_factor: float = 1 / sqrt(2)
154+
) -> IIRFilter:
155+
"""
156+
Creates a low-shelf filter
157+
158+
>>> filter = make_lowshelf(1000, 48000, 6)
159+
>>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE
160+
[3.0409336710888786, -5.608870992220748, 2.602157875636628, 3.139954022810743,
161+
-5.591841778072785, 2.5201667380627257]
162+
"""
163+
w0 = tau * frequency / samplerate
164+
_sin = sin(w0)
165+
_cos = cos(w0)
166+
alpha = _sin / (2 * q_factor)
167+
big_a = 10 ** (gain_db / 40)
168+
pmc = (big_a + 1) - (big_a - 1) * _cos
169+
ppmc = (big_a + 1) + (big_a - 1) * _cos
170+
mpc = (big_a - 1) - (big_a + 1) * _cos
171+
pmpc = (big_a - 1) + (big_a + 1) * _cos
172+
aa2 = 2 * sqrt(big_a) * alpha
173+
174+
b0 = big_a * (pmc + aa2)
175+
b1 = 2 * big_a * mpc
176+
b2 = big_a * (pmc - aa2)
177+
a0 = ppmc + aa2
178+
a1 = -2 * pmpc
179+
a2 = ppmc - aa2
180+
181+
filt = IIRFilter(2)
182+
filt.set_coefficients([a0, a1, a2], [b0, b1, b2])
183+
return filt
184+
185+
186+
def make_highshelf(
187+
frequency: int, samplerate: int, gain_db: float, q_factor: float = 1 / sqrt(2)
188+
) -> IIRFilter:
189+
"""
190+
Creates a high-shelf filter
191+
192+
>>> filter = make_highshelf(1000, 48000, 6)
193+
>>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE
194+
[2.2229172136088806, -3.9587208137297303, 1.7841414181566304, 4.295432981120543,
195+
-7.922740859457287, 3.6756456963725253]
196+
"""
197+
w0 = tau * frequency / samplerate
198+
_sin = sin(w0)
199+
_cos = cos(w0)
200+
alpha = _sin / (2 * q_factor)
201+
big_a = 10 ** (gain_db / 40)
202+
pmc = (big_a + 1) - (big_a - 1) * _cos
203+
ppmc = (big_a + 1) + (big_a - 1) * _cos
204+
mpc = (big_a - 1) - (big_a + 1) * _cos
205+
pmpc = (big_a - 1) + (big_a + 1) * _cos
206+
aa2 = 2 * sqrt(big_a) * alpha
207+
208+
b0 = big_a * (ppmc + aa2)
209+
b1 = -2 * big_a * pmpc
210+
b2 = big_a * (ppmc - aa2)
211+
a0 = pmc + aa2
212+
a1 = 2 * mpc
213+
a2 = pmc - aa2
214+
215+
filt = IIRFilter(2)
216+
filt.set_coefficients([a0, a1, a2], [b0, b1, b2])
217+
return filt

audio_filters/iir_filter.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from __future__ import annotations
2+
3+
4+
class IIRFilter:
5+
r"""
6+
N-Order IIR filter
7+
Assumes working with float samples normalized on [-1, 1]
8+
9+
---
10+
11+
Implementation details:
12+
Based on the 2nd-order function from
13+
https://en.wikipedia.org/wiki/Digital_biquad_filter,
14+
this generalized N-order function was made.
15+
16+
Using the following transfer function
17+
H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}}{a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}}
18+
we can rewrite this to
19+
y[n]={\frac{1}{a_{0}}}\left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)-\left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right)
20+
"""
21+
22+
def __init__(self, order: int) -> None:
23+
self.order = order
24+
25+
# a_{0} ... a_{k}
26+
self.a_coeffs = [1.0] + [0.0] * order
27+
# b_{0} ... b_{k}
28+
self.b_coeffs = [1.0] + [0.0] * order
29+
30+
# x[n-1] ... x[n-k]
31+
self.input_history = [0.0] * self.order
32+
# y[n-1] ... y[n-k]
33+
self.output_history = [0.0] * self.order
34+
35+
def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None:
36+
"""
37+
Set the coefficients for the IIR filter. These should both be of size order + 1.
38+
a_0 may be left out, and it will use 1.0 as default value.
39+
40+
This method works well with scipy's filter design functions
41+
>>> # Make a 2nd-order 1000Hz butterworth lowpass filter
42+
>>> import scipy.signal
43+
>>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000,
44+
... btype='lowpass',
45+
... fs=48000)
46+
>>> filt = IIRFilter(2)
47+
>>> filt.set_coefficients(a_coeffs, b_coeffs)
48+
"""
49+
if len(a_coeffs) < self.order:
50+
a_coeffs = [1.0] + a_coeffs
51+
52+
if len(a_coeffs) != self.order + 1:
53+
raise ValueError(
54+
f"Expected a_coeffs to have {self.order + 1} elements for {self.order}"
55+
f"-order filter, got {len(a_coeffs)}"
56+
)
57+
58+
if len(b_coeffs) != self.order + 1:
59+
raise ValueError(
60+
f"Expected b_coeffs to have {self.order + 1} elements for {self.order}"
61+
f"-order filter, got {len(a_coeffs)}"
62+
)
63+
64+
self.a_coeffs = a_coeffs
65+
self.b_coeffs = b_coeffs
66+
67+
def process(self, sample: float) -> float:
68+
"""
69+
Calculate y[n]
70+
71+
>>> filt = IIRFilter(2)
72+
>>> filt.process(0)
73+
0.0
74+
"""
75+
result = 0.0
76+
77+
# Start at index 1 and do index 0 at the end.
78+
for i in range(1, self.order + 1):
79+
result += (
80+
self.b_coeffs[i] * self.input_history[i - 1]
81+
- self.a_coeffs[i] * self.output_history[i - 1]
82+
)
83+
84+
result = (result + self.b_coeffs[0] * sample) / self.a_coeffs[0]
85+
86+
self.input_history[1:] = self.input_history[:-1]
87+
self.output_history[1:] = self.output_history[:-1]
88+
89+
self.input_history[0] = sample
90+
self.output_history[0] = result
91+
92+
return result

0 commit comments

Comments
 (0)