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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace.
- 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함.

### Fixed

- Keep unexpected key-detection dependency failures out of routine log payloads while retaining bounded operation and exception-class diagnostics.

## [0.1.3] - 2026-04-29

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class KeyDetector:
- Bounded by the size of the passed input array.
- Safe failure: degenerate input returns an empty result and no exception
is allowed to escape ``detect``.
- Unexpected dependency failures log only the operation and exception
class; dependency messages and tracebacks stay out of routine logs.
"""

def detect(self, audio: np.ndarray, sr: int) -> KeyResult:
Expand All @@ -83,8 +85,11 @@ def detect(self, audio: np.ndarray, sr: int) -> KeyResult:
# detection deterministic and avoids an unstable native pitch-track
# code path on pure synthetic tones.
chroma = librosa.feature.chroma_cqt(y=audio, sr=sr, tuning=0.0)
except Exception: # noqa: BLE001 - safe failure: never raise to caller.
logger.exception("chroma_cqt failed during key detection")
except Exception as error: # noqa: BLE001 - safe failure: never raise to caller.
logger.error(
"chroma_cqt failed during key detection: %s",
type(error).__name__,
)
return _empty_result()

if chroma.size == 0:
Expand Down
13 changes: 10 additions & 3 deletions services/analysis-engine/tests/test_key_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest.mock import patch

import numpy as np
import pytest

from bandscope_analysis.chords.key_detector import (
KeyDetector,
Expand Down Expand Up @@ -85,12 +86,18 @@ def test_detect_empty_audio() -> None:
assert result == {"key": "", "tonic": "", "mode": "", "confidence": 0.0}


def test_detect_chroma_cqt_exception() -> None:
"""A failure inside chroma_cqt yields the empty result and never raises."""
def test_detect_chroma_cqt_exception(caplog: pytest.LogCaptureFixture) -> None:
"""A dependency failure stays payload-safe in routine key-detection logs."""
audio = _tone(_NOTE_FREQS["C"], 1.0)
with patch("librosa.feature.chroma_cqt", side_effect=RuntimeError("boom")):
sensitive_detail = "/Users/Alice/private-song.wav token=super-secret"
with patch("librosa.feature.chroma_cqt", side_effect=RuntimeError(sensitive_detail)):
result = KeyDetector().detect(audio, SAMPLE_RATE)

assert result == _empty_result()
assert "chroma_cqt failed during key detection" in caplog.text
assert "/Users/Alice" not in caplog.text
assert "private-song.wav" not in caplog.text
assert "super-secret" not in caplog.text


def test_detect_empty_chroma() -> None:
Expand Down
Loading