Skip to content

Commit 2ed88b5

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 0950ab1 commit 2ed88b5

File tree

5 files changed

+135
-123
lines changed

5 files changed

+135
-123
lines changed

audio_filters/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
__all__ = [
2222
"EqualLoudnessFilter",
23-
"IIRFilter",
23+
"IIRFilter",
2424
"make_highpass",
2525
"make_lowpass",
26-
]
26+
]

audio_filters/demo_equal_loudness_filter.py

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,130 +14,136 @@
1414
from audio_filters.equal_loudness_filter import EqualLoudnessFilter
1515

1616

17-
def generate_test_signal(frequency: float, duration: float, samplerate: int, amplitude: float = 0.5) -> List[float]:
17+
def generate_test_signal(
18+
frequency: float, duration: float, samplerate: int, amplitude: float = 0.5
19+
) -> List[float]:
1820
"""
1921
Generate a simple sine wave test signal.
20-
22+
2123
Args:
2224
frequency: Frequency of the sine wave in Hz
2325
duration: Duration of the signal in seconds
2426
samplerate: Sample rate in Hz
2527
amplitude: Amplitude of the sine wave (0-1)
26-
28+
2729
Returns:
2830
List of audio samples
2931
"""
3032
samples = []
3133
total_samples = int(duration * samplerate)
32-
34+
3335
for i in range(total_samples):
3436
t = i / samplerate # Time in seconds
3537
sample = amplitude * math.sin(2 * math.pi * frequency * t)
3638
samples.append(sample)
37-
39+
3840
return samples
3941

4042

4143
def demonstrate_equal_loudness_filter():
4244
"""Main demonstration function."""
4345
print("🎵 Equal Loudness Filter Demonstration")
4446
print("=" * 50)
45-
47+
4648
# Create filter instance
4749
samplerate = 44100
4850
filter_instance = EqualLoudnessFilter(samplerate)
49-
51+
5052
print(f"Filter initialized with sample rate: {samplerate} Hz")
5153
print(f"Filter order: {filter_instance.yulewalk_filter.order}")
5254
print()
53-
55+
5456
# Test 1: Process silence
5557
print("📌 Test 1: Processing silence")
5658
silence_result = filter_instance.process(0.0)
5759
print(f"Input: 0.0 → Output: {silence_result}")
5860
assert silence_result == 0.0, "Silence should remain silence"
5961
print("✅ Silence test passed!")
6062
print()
61-
63+
6264
# Test 2: Process various amplitude levels
6365
print("📌 Test 2: Processing different amplitude levels")
6466
test_amplitudes = [0.1, 0.25, 0.5, 0.75, 1.0, -0.1, -0.25, -0.5, -0.75, -1.0]
65-
67+
6668
for amplitude in test_amplitudes:
6769
result = filter_instance.process(amplitude)
6870
print(f"Input: {amplitude:6.2f} → Output: {result:10.6f}")
6971
print("✅ Amplitude test completed!")
7072
print()
71-
73+
7274
# Test 3: Process a sine wave
7375
print("📌 Test 3: Processing a 1kHz sine wave")
7476
test_freq = 1000 # 1kHz
75-
duration = 0.01 # 10ms
77+
duration = 0.01 # 10ms
7678
sine_wave = generate_test_signal(test_freq, duration, samplerate, 0.3)
77-
79+
7880
print(f"Generated {len(sine_wave)} samples of {test_freq}Hz sine wave")
79-
81+
8082
# Process the sine wave
8183
filtered_samples = []
8284
for sample in sine_wave[:10]: # Show first 10 samples
8385
filtered_sample = filter_instance.process(sample)
8486
filtered_samples.append(filtered_sample)
8587
print(f"Sample: {sample:8.5f} → Filtered: {filtered_sample:8.5f}")
86-
88+
8789
print("✅ Sine wave processing test completed!")
8890
print()
89-
91+
9092
# Test 4: Filter reset functionality
9193
print("📌 Test 4: Testing filter reset")
92-
94+
9395
# Process some samples to build internal state
9496
for _ in range(5):
9597
filter_instance.process(0.5)
96-
98+
9799
print("Processed 5 samples to build internal state")
98-
100+
99101
# Check state before reset
100102
history_before = filter_instance.yulewalk_filter.input_history.copy()
101103
print(f"Input history before reset: {history_before[:3]}...") # Show first 3 values
102-
104+
103105
# Reset the filter
104106
filter_instance.reset()
105-
107+
106108
# Check state after reset
107109
history_after = filter_instance.yulewalk_filter.input_history.copy()
108110
print(f"Input history after reset: {history_after[:3]}...")
109-
110-
assert all(val == 0.0 for val in history_after), "History should be cleared after reset"
111+
112+
assert all(val == 0.0 for val in history_after), (
113+
"History should be cleared after reset"
114+
)
111115
print("✅ Reset test passed!")
112116
print()
113-
117+
114118
# Test 5: Filter information
115119
print("📌 Test 5: Filter configuration information")
116120
filter_info = filter_instance.get_filter_info()
117-
121+
118122
for key, value in filter_info.items():
119123
if isinstance(value, list):
120124
print(f"{key}: [{len(value)} coefficients]")
121125
else:
122126
print(f"{key}: {value}")
123-
127+
124128
print("✅ Filter info test completed!")
125129
print()
126-
130+
127131
# Test 6: Different sample rates
128132
print("📌 Test 6: Testing different sample rates")
129133
test_samplerates = [22050, 44100, 48000, 96000]
130-
134+
131135
for sr in test_samplerates:
132136
test_filter = EqualLoudnessFilter(sr)
133137
result = test_filter.process(0.5)
134138
print(f"Sample rate: {sr:6d} Hz → Result: {result:10.6f}")
135-
139+
136140
print("✅ Sample rate test completed!")
137141
print()
138-
142+
139143
print("🎉 All demonstrations completed successfully!")
140-
print("\nThe Equal Loudness Filter is ready for use in audio processing applications!")
144+
print(
145+
"\nThe Equal Loudness Filter is ready for use in audio processing applications!"
146+
)
141147

142148

143149
if __name__ == "__main__":
@@ -149,4 +155,4 @@ def demonstrate_equal_loudness_filter():
149155
sys.exit(1)
150156
except Exception as e:
151157
print(f"❌ Demonstration failed: {e}")
152-
sys.exit(1)
158+
sys.exit(1)

audio_filters/equal_loudness_filter.py

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Equal-loudness filter implementation for audio processing.
33
4-
This module implements an equal-loudness filter which compensates for the human ear's
4+
This module implements an equal-loudness filter which compensates for the human ear's
55
non-linear response to sound using cascaded IIR filters.
66
"""
77

@@ -23,44 +23,44 @@ def _yulewalk_approximation(
2323
) -> tuple[np.ndarray, np.ndarray]:
2424
"""
2525
Simplified Yule-Walker approximation for filter design.
26-
26+
2727
This is a basic implementation that approximates the yulewalker functionality
2828
using numpy for creating filter coefficients from frequency response data.
29-
29+
3030
Args:
3131
order: Filter order
3232
frequencies: Normalized frequencies (0 to 1)
3333
gains: Desired gains at those frequencies
34-
34+
3535
Returns:
3636
Tuple of (a_coeffs, b_coeffs) for the IIR filter
3737
"""
3838
# Simple approach: create coefficients that approximate the desired response
3939
# This is a simplified version - in practice, yulewalker uses more sophisticated methods
40-
40+
4141
# Create a basic filter response approximation
4242
# Using a simple polynomial fit approach
4343
try:
4444
# Fit polynomial to log-magnitude response
4545
log_gains = np.log10(gains + 1e-10) # Avoid log(0)
4646
coeffs = np.polyfit(frequencies, log_gains, min(order, len(frequencies) - 1))
47-
47+
4848
# Convert polynomial coefficients to filter coefficients
4949
a_coeffs = np.zeros(order + 1)
5050
b_coeffs = np.zeros(order + 1)
51-
51+
5252
a_coeffs[0] = 1.0 # Normalized
53-
53+
5454
# Simple mapping from polynomial to filter coefficients
5555
for i in range(min(len(coeffs), order)):
5656
b_coeffs[i] = coeffs[-(i + 1)] * 0.1 # Scale factor for stability
57-
57+
5858
# Ensure some basic coefficients are set
5959
if b_coeffs[0] == 0:
6060
b_coeffs[0] = 0.1
61-
61+
6262
return a_coeffs, b_coeffs
63-
63+
6464
except (np.linalg.LinAlgError, ValueError):
6565
# Fallback to simple pass-through filter
6666
a_coeffs = np.zeros(order + 1)
@@ -74,27 +74,27 @@ class EqualLoudnessFilter:
7474
"""
7575
An equal-loudness filter which compensates for the human ear's non-linear response
7676
to sound.
77-
77+
7878
This filter corrects the frequency response by cascading a Yule-Walker approximation
7979
filter and a Butterworth high-pass filter.
80-
81-
The filter is designed for use with sample rates of 44.1kHz and above. If you're
80+
81+
The filter is designed for use with sample rates of 44.1kHz and above. If you're
8282
using a lower sample rate, use with caution.
83-
83+
8484
The equal-loudness contours are based on the Robinson-Dadson curves (1956), which
8585
describe how the human ear perceives different frequencies at various loudness levels.
86-
86+
8787
References:
8888
- Robinson, D. W., & Dadson, R. S. (1956). A re-determination of the equal-
8989
loudness relations for pure tones. British Journal of Applied Physics, 7(5), 166.
9090
- Original MATLAB implementation by David Robinson, 2001
91-
91+
9292
Examples:
9393
>>> filt = EqualLoudnessFilter(48000)
9494
>>> processed_sample = filt.process(0.5)
9595
>>> isinstance(processed_sample, float)
9696
True
97-
97+
9898
>>> # Process silence
9999
>>> filt = EqualLoudnessFilter()
100100
>>> filt.process(0.0)
@@ -104,17 +104,17 @@ class EqualLoudnessFilter:
104104
def __init__(self, samplerate: int = 44100) -> None:
105105
"""
106106
Initialize the equal-loudness filter.
107-
107+
108108
Args:
109109
samplerate: Sample rate in Hz (default: 44100)
110-
110+
111111
Raises:
112112
ValueError: If samplerate is not positive
113113
"""
114114
if samplerate <= 0:
115115
msg = "Sample rate must be positive"
116116
raise ValueError(msg)
117-
117+
118118
self.samplerate = samplerate
119119
self.yulewalk_filter = IIRFilter(10)
120120
self.butterworth_filter = make_highpass(150, samplerate)
@@ -138,17 +138,17 @@ def __init__(self, samplerate: int = 44100) -> None:
138138
def process(self, sample: Union[float, int]) -> float:
139139
"""
140140
Process a single sample through both filters.
141-
141+
142142
The sample is first processed through the Yule-Walker approximation filter
143143
to apply the equal-loudness curve correction, then through a high-pass
144144
Butterworth filter to remove low-frequency artifacts.
145-
145+
146146
Args:
147147
sample: Input audio sample (should be normalized to [-1, 1] range)
148-
148+
149149
Returns:
150150
Processed audio sample as float
151-
151+
152152
Examples:
153153
>>> filt = EqualLoudnessFilter()
154154
>>> filt.process(0.0)
@@ -160,17 +160,17 @@ def process(self, sample: Union[float, int]) -> float:
160160
"""
161161
# Convert to float for processing
162162
sample_float = float(sample)
163-
163+
164164
# Apply Yule-Walker approximation filter first
165165
tmp = self.yulewalk_filter.process(sample_float)
166-
166+
167167
# Then apply Butterworth high-pass filter
168168
return self.butterworth_filter.process(tmp)
169169

170170
def reset(self) -> None:
171171
"""
172172
Reset the filter's internal state (clear history).
173-
173+
174174
This is useful when starting to process a new audio stream
175175
to avoid artifacts from previous processing.
176176
"""
@@ -181,7 +181,7 @@ def reset(self) -> None:
181181
def get_filter_info(self) -> dict[str, Union[int, float, list[float]]]:
182182
"""
183183
Get information about the filter configuration.
184-
184+
185185
Returns:
186186
Dictionary containing filter parameters and coefficients
187187
"""
@@ -197,17 +197,17 @@ def get_filter_info(self) -> dict[str, Union[int, float, list[float]]]:
197197
if __name__ == "__main__":
198198
# Demonstration of the filter
199199
import doctest
200-
200+
201201
doctest.testmod()
202-
202+
203203
# Create a simple test
204204
filter_instance = EqualLoudnessFilter(44100)
205205
test_samples = [0.0, 0.1, 0.5, -0.3, 1.0, -1.0]
206-
206+
207207
print("Equal-Loudness Filter Demo:")
208208
print("Sample Rate: 44100 Hz")
209209
print("Test samples and their filtered outputs:")
210-
210+
211211
for sample in test_samples:
212212
filtered = filter_instance.process(sample)
213-
print(f"Input: {sample:6.1f} → Output: {filtered:8.6f}")
213+
print(f"Input: {sample:6.1f} → Output: {filtered:8.6f}")

audio_filters/tests/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Test suite for audio_filters module.
33
44
This package contains comprehensive tests for all audio filter implementations.
5-
"""
5+
"""

0 commit comments

Comments
 (0)