Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions src/intuitive_daw/audio/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@
self.mid_gain = mid_gain
self.high_freq = high_freq
self.high_gain = high_gain

# Cache
self._last_low_freq = None
self._last_sample_rate = None
self._sos = None
self._gain_linear = None
self._last_low_gain = None

def _process_impl(self, audio: np.ndarray) -> np.ndarray:
"""Apply EQ (simplified implementation)"""
Expand All @@ -109,14 +116,25 @@

# Apply low shelf
if abs(self.low_gain) > 0.1:
sos = signal.butter(
2,
self.low_freq,
btype='low',
fs=self.sample_rate,
output='sos'
)
gain = 10.0 ** (self.low_gain / 20.0)
if (self.low_freq != self._last_low_freq or
self.sample_rate != self._last_sample_rate):

Check notice on line 120 in src/intuitive_daw/audio/processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/intuitive_daw/audio/processor.py#L120

visually indented line with same indent as next logical line (E129)
self._sos = signal.butter(
2,
self.low_freq,
btype='low',
fs=self.sample_rate,
output='sos'
)
self._last_low_freq = self.low_freq
self._last_sample_rate = self.sample_rate

if self.low_gain != self._last_low_gain:
self._gain_linear = 10.0 ** (self.low_gain / 20.0)
self._last_low_gain = self.low_gain

sos = self._sos
gain = self._gain_linear

for ch in range(result.shape[1]):
filtered = signal.sosfilt(sos, result[:, ch])
result[:, ch] = result[:, ch] + (filtered - result[:, ch]) * (gain - 1)
Expand Down
27 changes: 27 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

Check notice on line 1 in tests/conftest.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/conftest.py#L1

Missing module docstring
import sys
from unittest.mock import MagicMock

class MockTensor:

Check notice on line 5 in tests/conftest.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/conftest.py#L5

Missing class docstring

Check notice on line 5 in tests/conftest.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/conftest.py#L5

Too few public methods (0/2)
pass

def pytest_configure(config):

Check notice on line 8 in tests/conftest.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/conftest.py#L8

Unused argument 'config'

Check warning on line 8 in tests/conftest.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/conftest.py#L8

Unused function 'pytest_configure' (unused-function)

Check warning on line 8 in tests/conftest.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/conftest.py#L8

Unused variable 'config' (unused-variable)
"""Mock heavy dependencies before tests run"""

# Create a mock torch that satisfies scipy's checks
mock_torch = MagicMock()
mock_torch.Tensor = MockTensor

Check warning on line 13 in tests/conftest.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/conftest.py#L13

Unused attribute 'Tensor' (unused-attribute)

heavy_modules = {
'sounddevice': MagicMock(),
'pyaudio': MagicMock(),
'torch': mock_torch,
'transformers': MagicMock(),
'pedalboard': MagicMock(),
'mido': MagicMock(),
'rtmidi': MagicMock(),
'librosa': MagicMock(),
}

for module_name, mock_obj in heavy_modules.items():
sys.modules[module_name] = mock_obj
70 changes: 70 additions & 0 deletions tests/test_audio_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

Check notice on line 1 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L1

Missing module docstring
import pytest

Check warning on line 2 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L2

'pytest' imported but unused (F401)

Check warning on line 2 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L2

Unused import pytest

Check warning on line 2 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L2

`pytest` imported but unused (F401)
import numpy as np
from src.intuitive_daw.audio.processor import EQEffect

class TestEQEffect:

Check notice on line 6 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L6

Missing class docstring
def test_eq_initialization(self):

Check notice on line 7 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L7

Missing function or method docstring
eq = EQEffect()

Check notice on line 8 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L8

Variable name "eq" doesn't conform to '[a-z_][a-z0-9_]{2,30}$' pattern
assert eq.name == "EQ"

Check warning on line 9 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L9

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check warning on line 9 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L9

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. (B101)
assert eq.is_enabled == True

Check notice on line 10 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L10

Avoid equality comparisons to `True`; use `eq.is_enabled:` for truth checks (E712)

Check warning on line 10 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L10

Comparison 'eq.is_enabled == True' should be 'eq.is_enabled is True' if checking for the singleton value True, or 'eq.is_enabled' if testing for truthiness

Check warning on line 10 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L10

Is "is_enabled" a function or an attribute?

Check warning on line 10 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L10

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check warning on line 10 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L10

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. (B101)

Check notice on line 10 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L10

comparison to True should be 'if cond is True:' or 'if cond:' (E712)
assert eq.low_gain == 0.0

Check notice on line 11 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L11

"eq.low_gain == 0.0" can be simplified to "not eq.low_gain", if it is strictly an int, as 0 is falsey

Check warning on line 11 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L11

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check warning on line 11 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L11

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. (B101)

def test_eq_processing_no_gain(self):
"""Test that processing with 0 gain returns audio unchanged (mostly)"""
sample_rate = 48000
duration = 0.1
audio = np.random.rand(int(sample_rate * duration), 2)

eq = EQEffect(sample_rate=sample_rate, low_gain=0.0)

Check notice on line 19 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L19

Variable name "eq" doesn't conform to '[a-z_][a-z0-9_]{2,30}$' pattern
processed = eq.process(audio)

# With 0 gain, it should be identical
np.testing.assert_array_equal(processed, audio)

def test_eq_processing_with_gain(self):
"""Test that processing with gain modifies the audio"""
sample_rate = 48000
duration = 0.1
audio = np.random.rand(int(sample_rate * duration), 2)

eq = EQEffect(sample_rate=sample_rate, low_gain=5.0)

Check notice on line 31 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L31

Variable name "eq" doesn't conform to '[a-z_][a-z0-9_]{2,30}$' pattern
processed = eq.process(audio)

# Should be different
assert not np.array_equal(processed, audio)

Check warning on line 35 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L35

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check warning on line 35 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L35

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. (B101)

# Output shape should remain same
assert processed.shape == audio.shape

Check warning on line 38 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L38

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check warning on line 38 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L38

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. (B101)

def test_eq_parameter_change(self):
"""Test that changing parameters updates processing"""
sample_rate = 48000
duration = 0.1
audio = np.random.rand(int(sample_rate * duration), 2)

eq = EQEffect(sample_rate=sample_rate, low_gain=5.0)
res1 = eq.process(audio)

# Change gain
eq.low_gain = 10.0
res2 = eq.process(audio)

# Should be different
assert not np.allclose(res1, res2)

Check warning on line 54 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L54

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.

Check warning on line 54 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L54

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. (B101)

def test_eq_frequency_change(self):
"""Test that changing frequency updates processing"""
sample_rate = 48000
duration = 0.1
audio = np.random.rand(int(sample_rate * duration), 2)

eq = EQEffect(sample_rate=sample_rate, low_gain=5.0, low_freq=100.0)

Check notice on line 62 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L62

Variable name "eq" doesn't conform to '[a-z_][a-z0-9_]{2,30}$' pattern
res1 = eq.process(audio)

# Change frequency
eq.low_freq = 200.0

Check warning on line 66 in tests/test_audio_processor.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_audio_processor.py#L66

Unused attribute 'low_freq' (unused-attribute)
res2 = eq.process(audio)

# Should be different
assert not np.allclose(res1, res2)