import sys import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) import main as pitch_main PROJECT_ROOT = Path(__file__).resolve().parents[2] LIVE_CASES = [ { "path": PROJECT_ROOT / "audios-testes" / "teste_teclado.mp4", "instrumento": "teclado", "expected_stage": "F#m A D C#sus4 C#", "categoria": "harmonico", }, { "path": PROJECT_ROOT / "audios-testes" / "teclado_bia_ruido.ogg", "instrumento": "teclado", "categoria": "harmonico", "noisy": True, }, { "path": PROJECT_ROOT / "audios-testes" / "teste_sax_alto.ogg", "instrumento": "sax_alto", "categoria": "melodico", }, { "path": PROJECT_ROOT / "audios-testes" / "sax_alto_bia_ruido.ogg", "instrumento": "sax_alto", "categoria": "melodico", }, { "path": PROJECT_ROOT / "audios-testes" / "teste_violino.ogg", "instrumento": "violino", "categoria": "melodico", }, ] @unittest.skipUnless( all(case["path"].exists() for case in LIVE_CASES), "Clips de regressao live nao encontrados", ) class LiveAudioRegressionTests(unittest.TestCase): def assert_progression_overlap( self, actual_progression: str, expected_progression: str, minimum_overlap: int = 2, ) -> None: actual_tokens = [item for item in str(actual_progression or "").split() if item] expected_tokens = [item for item in str(expected_progression or "").split() if item] overlap = len(set(actual_tokens) & set(expected_tokens)) self.assertGreaterEqual( overlap, minimum_overlap, f"sobreposicao live insuficiente entre '{actual_progression}' e '{expected_progression}'", ) def test_live_harmonic_path_produces_playable_stage_output(self): for case in LIVE_CASES: with self.subTest(audio=case["path"].name, instrumento=case["instrumento"]): resultado = pitch_main.analisar_harmonico( case["path"], case["instrumento"], contexto="live", ) self.assertEqual(resultado["tipo"], "harmonico") self.assertTrue(resultado["acorde_atual"], "live precisa devolver acorde_atual") self.assertTrue(resultado["cifra_palco"], "live precisa devolver cifra_palco") self.assertIn("diagnostico_harmonico", resultado) self.assertIn("timings", resultado) self.assertTrue(resultado["diagnostico_harmonico"]["fast_mode"]) self.assertGreaterEqual(float(resultado.get("beat_confidence", 0.0)), 0.0) if case["categoria"] == "harmonico": if case.get("expected_stage"): self.assert_progression_overlap(resultado["cifra_palco"], case["expected_stage"]) self.assertGreaterEqual(len(resultado.get("acordes_resumo", [])), 1) if case.get("noisy"): self.assertGreaterEqual( float(resultado["diagnostico_harmonico"].get("segment_count", 0)), 1.0, ) self.assertGreaterEqual(len(resultado.get("current_chord_candidates", [])), 1) else: # Para melodicos, o live agora precisa ao menos devolver base harmonica utilizavel. self.assertGreaterEqual(len(resultado.get("acordes_resumo", [])), 1) self.assertGreaterEqual(len(resultado.get("acordes_janela_final", [])), 1) self.assertGreaterEqual( float(resultado["diagnostico_harmonico"].get("segment_count", 0)), 1.0, ) if __name__ == "__main__": unittest.main()