Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import logging
import warnings
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -35,11 +36,17 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures:
TemporalFeatures containing BPM and beat grids.
"""
path_str = str(audio_path)
if not Path(audio_path).exists():
raise FileNotFoundError(f"Audio file not found: {path_str}")

logger.info(f"Loading and decoding audio: {path_str}")

try:
# Load audio, converting to mono and standardizing sample rate
y, sr = librosa.load(path_str, sr=TARGET_SR, mono=True)
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=DeprecationWarning)
warnings.simplefilter("ignore", category=FutureWarning)
y, sr = librosa.load(path_str, sr=TARGET_SR, mono=True)
Comment on lines +46 to +49
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 목적: 런타임 코드의 광범위 ignore 필터 존재 여부 확인
rg -n --type=py 'simplefilter\("ignore", category=(DeprecationWarning|FutureWarning)\)|filterwarnings\("ignore"' services/analysis-engine/src

Repository: seonghobae/bandscope

Length of output: 357


🏁 Script executed:

# 1. 전체 코드베이스에서 모든 경고 관련 필터/무시 패턴 검색
rg -n --type=py 'warnings\.(simplefilter|filterwarnings|catch_warnings)' services/analysis-engine/src

# 2. librosa 로드 주변의 전체 함수 컨텍스트 확인
rg -B 15 -A 5 'librosa\.load' services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py

Repository: seonghobae/bandscope

Length of output: 1495


🏁 Script executed:

# 1. audioread이 프로젝트에서 사용되는지 확인
rg -n 'audioread' services/analysis-engine/src

# 2. 프로젝트 의존성 확인 (requirements, setup.py, pyproject.toml 등)
fd -t f '(requirements|setup|pyproject|poetry|pipfile)' services/analysis-engine/ | head -20

# 3. librosa 사용 패턴 확인
rg -n 'import librosa' services/analysis-engine/src

# 4. 코드에 경고 무시 이유에 대한 주석이 있는지 확인
rg -B 5 'catch_warnings' services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py

Repository: seonghobae/bandscope

Length of output: 569


🏁 Script executed:

# pyproject.toml 내용 확인
cat services/analysis-engine/pyproject.toml | head -100

Repository: seonghobae/bandscope

Length of output: 850


librosa.load() 호출 시 특정 모듈로 경고 필터링 범위 좁히기

DeprecationWarning/FutureWarning 전체를 무시하면 라이브러리 업그레이드 시 호환성 문제를 조기에 탐지하기 어렵습니다. audioread(librosa의 음성 로드 백엔드)에서만 이 경고들을 무시하도록 범위를 좁혀주세요.

제안 수정
             with warnings.catch_warnings():
-                warnings.simplefilter("ignore", category=DeprecationWarning)
-                warnings.simplefilter("ignore", category=FutureWarning)
+                warnings.filterwarnings(
+                    "ignore",
+                    category=DeprecationWarning,
+                    module=r"audioread\..*",
+                )
+                warnings.filterwarnings(
+                    "ignore",
+                    category=FutureWarning,
+                    module=r"audioread\..*",
+                )
                 y, sr = librosa.load(path_str, sr=TARGET_SR, mono=True)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py` around
lines 46 - 49, The current warnings.catch_warnings() block around the
librosa.load call ignores all DeprecationWarning/FutureWarning globally; narrow
this to only suppress warnings coming from audioread by replacing the
simplefilter calls with warnings.filterwarnings("ignore",
category=DeprecationWarning, module=r"^audioread") and
warnings.filterwarnings("ignore", category=FutureWarning, module=r"^audioread")
inside the same with warnings.catch_warnings(): context that surrounds the y, sr
= librosa.load(path_str, sr=TARGET_SR, mono=True) call so other
deprecation/future warnings still surface.


# Ensure it's a 1D float array for librosa
if not isinstance(y, np.ndarray):
Expand Down
18 changes: 9 additions & 9 deletions services/analysis-engine/tests/test_chord_recognizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_chord_recognizer_c_major_chord() -> None:
"""Test chord recognition with a clear C major chord."""
recognizer = ChordRecognizer()
sr = 22050
t = np.linspace(0, 1.0, sr)
t = np.linspace(0, 3.0, sr * 3, endpoint=False)
# C major: C4 (261.63Hz), E4 (329.63Hz), G4 (392.00Hz)
y = (
np.sin(2 * np.pi * 261.63 * t)
Expand All @@ -47,7 +47,7 @@ def test_chord_recognizer_c_major_chord() -> None:
def test_chord_recognizer_hpss_exception():
"""Test for test_chord_recognizer_hpss_exception."""
recognizer = ChordRecognizer()
y = np.random.randn(22050)
y = np.random.randn(22050 * 3)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

반복되는 샘플 수 계산은 상수/헬퍼로 통일하는 것이 좋습니다.

현재 패턴이 여러 테스트에 반복되어, 샘플링 레이트나 길이 변경 시 수정 지점이 많아집니다. 공통 상수/헬퍼로 추출하면 유지보수성이 좋아집니다.

예시 리팩터링 diff
 import numpy as np

 from bandscope_analysis.chords.chord_recognizer import ChordRecognizer

+SR = 22050
+DURATION_SEC = 3
+
+
+def _noise_signal(sr: int = SR, duration_sec: int = DURATION_SEC) -> np.ndarray:
+    return np.random.randn(sr * duration_sec)

@@
 def test_chord_recognizer_hpss_exception():
@@
-    y = np.random.randn(22050 * 3)
+    y = _noise_signal()

@@
 def test_chord_recognizer_chroma_cqt_exception():
@@
-    y = np.random.randn(22050 * 3)
+    y = _noise_signal()

@@
 def test_chord_recognizer_rms_exception():
@@
-    y = np.random.randn(22050 * 3)
+    y = _noise_signal()

@@
 def test_chord_recognizer_rms_padding():
@@
-    y = np.random.randn(22050 * 3)
+    y = _noise_signal()

@@
 def test_chord_recognizer_empty_chromagram():
@@
-    y = np.random.randn(22050 * 3)
+    y = _noise_signal()

@@
 def test_chord_recognizer_rms_longer():
@@
-    y = np.random.randn(22050 * 3)
+    y = _noise_signal()

Also applies to: 60-60, 70-70, 80-80, 94-94, 105-105

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/analysis-engine/tests/test_chord_recognizer.py` at line 50, Multiple
tests create signals using repeated expressions like "y = np.random.randn(22050
* 3)"; extract a shared constant or helper to compute sample counts (e.g.,
SAMPLE_RATE = 22050 and DURATION_SECONDS = 3 or a helper samples(seconds)) and
replace all occurrences in the tests (references: variable y initializations
across test_chord_recognizer tests and any other tests that build random
signals) to use SAMPLE_RATE * DURATION_SECONDS or samples(3). Update test
setup/fixtures to import or define these constants/helpers so future changes to
sample rate or duration only require one edit.


with patch("librosa.effects.hpss", side_effect=Exception("HPSS Error")):
chords = recognizer.recognize(y, sr=22050)
Expand All @@ -57,7 +57,7 @@ def test_chord_recognizer_hpss_exception():
def test_chord_recognizer_chroma_cqt_exception():
"""Test for test_chord_recognizer_chroma_cqt_exception."""
recognizer = ChordRecognizer()
y = np.random.randn(22050)
y = np.random.randn(22050 * 3)

with patch("librosa.feature.chroma_cqt", side_effect=Exception("CQT Error")):
chords = recognizer.recognize(y, sr=22050)
Expand All @@ -67,7 +67,7 @@ def test_chord_recognizer_chroma_cqt_exception():
def test_chord_recognizer_rms_exception():
"""Test for test_chord_recognizer_rms_exception."""
recognizer = ChordRecognizer()
y = np.random.randn(22050)
y = np.random.randn(22050 * 3)

with patch("librosa.feature.rms", side_effect=Exception("RMS Error")):
chords = recognizer.recognize(y, sr=22050)
Expand All @@ -77,7 +77,7 @@ def test_chord_recognizer_rms_exception():
def test_chord_recognizer_rms_padding():
"""Test for test_chord_recognizer_rms_padding."""
recognizer = ChordRecognizer()
y = np.random.randn(22050)
y = np.random.randn(22050 * 3)

# Mock RMS to return something shorter than chromagram
def mock_rms(*args, **kwargs):
Expand All @@ -91,7 +91,7 @@ def mock_rms(*args, **kwargs):
def test_chord_recognizer_empty_chromagram():
"""Test for test_chord_recognizer_empty_chromagram."""
recognizer = ChordRecognizer()
y = np.random.randn(22050)
y = np.random.randn(22050 * 3)

# Mock chroma_cqt to return empty array
with patch("librosa.feature.chroma_cqt", return_value=np.array([])):
Expand All @@ -102,7 +102,7 @@ def test_chord_recognizer_empty_chromagram():
def test_chord_recognizer_rms_longer():
"""Test for test_chord_recognizer_rms_longer."""
recognizer = ChordRecognizer()
y = np.random.randn(22050)
y = np.random.randn(22050 * 3)

# Mock RMS to return something longer than chromagram
def mock_rms(*args, **kwargs):
Expand All @@ -118,15 +118,15 @@ def test_chord_recognizer_changing_chords():
"""Test for test_chord_recognizer_changing_chords."""
recognizer = ChordRecognizer()
sr = 22050
t1 = np.linspace(0, 1.0, sr, endpoint=False)
t1 = np.linspace(0, 3.0, sr * 3, endpoint=False)
# C major
y1 = (
np.sin(2 * np.pi * 261.63 * t1)
+ np.sin(2 * np.pi * 329.63 * t1)
+ np.sin(2 * np.pi * 392.00 * t1)
) / 3.0

t2 = np.linspace(0, 1.0, sr, endpoint=False)
t2 = np.linspace(0, 3.0, sr * 3, endpoint=False)
# G major: G4 (392.00Hz), B4 (493.88Hz), D5 (587.33Hz)
y2 = (
np.sin(2 * np.pi * 392.00 * t2)
Expand Down
2 changes: 1 addition & 1 deletion services/analysis-engine/tests/test_temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test_temporal_analyzer_basic(dummy_audio_file: Path) -> None:
def test_temporal_analyzer_file_not_found() -> None:
"""Test that analyzer raises appropriate error for missing files."""
analyzer = TemporalAnalyzer()
with pytest.raises(ValueError, match="Temporal analysis failed"):
with pytest.raises(FileNotFoundError, match="Audio file not found"):
analyzer.analyze("nonexistent_file.wav")


Expand Down
Loading