Skip to content
Open
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
61 changes: 58 additions & 3 deletions services/analysis-engine/tests/test_chord_recognizer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tests for the chord recognizer module."""

import typing
from unittest.mock import patch

import numpy as np
Expand Down Expand Up @@ -86,7 +87,7 @@ def test_chord_recognizer_rms_padding() -> None:
y = np.random.randn(SAMPLE_RATE * DURATION_SECONDS)

# Mock RMS to return something shorter than chromagram
def mock_rms(*args, **kwargs):
def mock_rms(*args: typing.Any, **kwargs: typing.Any) -> np.ndarray:
return np.array([[0.1, 0.1]])

with patch("librosa.feature.rms", side_effect=mock_rms):
Expand All @@ -111,7 +112,7 @@ def test_chord_recognizer_rms_longer() -> None:
y = np.random.randn(SAMPLE_RATE * DURATION_SECONDS)

# Mock RMS to return something longer than chromagram
def mock_rms(*args, **kwargs):
def mock_rms(*args: typing.Any, **kwargs: typing.Any) -> np.ndarray:
# Return a very long array
return np.array([np.ones(1000)])

Expand Down Expand Up @@ -300,7 +301,7 @@ def test_chord_recognizer_compute_confidence_downgrade_path() -> None:

original_compute = recognizer._compute_confidence

def mock_confidence(similarity, best_state):
def mock_confidence(similarity: np.ndarray, best_state: int) -> str:
try:
return next(confidence_values)
except StopIteration:
Expand All @@ -318,3 +319,57 @@ def mock_confidence(similarity, best_state):
# Since first frame was high but subsequent were low,
# the segment confidence should be low (conservative)
assert non_n[0]["confidence"] == "low"


def test_recognize_orchestration() -> None:
"""Test that recognize properly orchestrates the helper methods."""
from unittest.mock import patch

recognizer = ChordRecognizer()
y = np.array([1.0, 2.0, 3.0])
sr = 22050

expected_chords = [{"start_time": 0.0, "end_time": 1.0, "chord": "C", "confidence": "high"}]

with (
patch.object(recognizer, "_separate_harmonic", return_value=y) as mock_separate,
patch.object(
recognizer, "_extract_chromagram", return_value=np.ones((12, 10))
) as mock_extract,
patch.object(recognizer, "_calculate_rms", return_value=np.ones(10)) as mock_rms,
patch.object(
recognizer,
"_match_templates",
return_value=(np.ones((24, 10)), np.zeros(10, dtype=int)),
) as mock_match,
patch.object(
recognizer, "_create_chord_segments", return_value=expected_chords
) as mock_create,
):
result = recognizer.recognize(y, sr=sr)

mock_separate.assert_called_once_with(y)
mock_extract.assert_called_once_with(y, sr)
mock_rms.assert_called_once()
mock_match.assert_called_once()
mock_create.assert_called_once()

assert result == expected_chords


def test_create_chord_segments_empty() -> None:
"""Test _create_chord_segments when no chord segments are created."""
from unittest.mock import patch

recognizer = ChordRecognizer()

chromagram = np.zeros((12, 0))
similarity = np.zeros((24, 0))
rms = np.zeros(0)

with (
patch.object(recognizer, "_build_observation_probs", return_value=np.zeros((25, 0))),
patch.object(recognizer, "_viterbi_decode", return_value=np.array([], dtype=np.intp)),
):
result = recognizer._create_chord_segments(chromagram, similarity, rms, sr=22050)
assert result == []