DesenrolaAi / python-basic-pitch /audio_regression_support.py
Azure DevOps Pipeline
deploy: Merged PR 29: melhoria no projeto, refatoracao e desempenho para 100 pessoas logadas
07e5a95
Raw
History Blame Contribute Delete
8.37 kB
from __future__ import annotations
from dataclasses import dataclass
import hashlib
import os
from pathlib import Path
import subprocess
import tempfile
from typing import Optional
import numpy as np
@dataclass(frozen=True)
class HarmonicRegressionFixture:
instrumento: str
signature: str
progression: str
tonic: str
mode: str
auxiliary: str = ""
@dataclass(frozen=True)
class MelodicRegressionFixture:
instrumento: str
signature: str
notes: tuple[str, ...]
tonic: str
mode: str
@dataclass(frozen=True)
class MelodicPatternPrior:
instrumento: str
notes: tuple[str, ...]
tonic: str
mode: str
@dataclass(frozen=True)
class HarmonicPatternPrior:
instrumento: str
progression: tuple[str, ...]
tonic: str
mode: str
weight: float = 1.0
REPO_ROOT = Path(__file__).resolve().parents[1]
FFMPEG_BINARY_CANDIDATES = [
Path(os.getenv("FFMPEG_PATH", "")).expanduser() if os.getenv("FFMPEG_PATH") else None,
REPO_ROOT / "backend" / "node_modules" / "ffmpeg-static" / ("ffmpeg.exe" if os.name == "nt" else "ffmpeg"),
]
HARMONIC_FIXTURES: tuple[HarmonicRegressionFixture, ...] = (
HarmonicRegressionFixture(
instrumento="violao",
signature="dc30512e9b12fc55b7f778fe1dfa0f5b8d39a960cb67b80e701f63957b7984c3",
progression="Am C G D",
tonic="A",
mode="menor",
),
HarmonicRegressionFixture(
instrumento="violao",
signature="a5e7a6c92c1025557e9b91adfbcbb7f37c2452fd0495389dd5e14d5bb8e5de51",
progression="G Em C D",
tonic="G",
mode="maior",
),
HarmonicRegressionFixture(
instrumento="violao",
signature="abbf896890dc1b79dda8cad3c6fa71ebf45c6dad243059adbb095707ae17f874",
progression="F G Am Em",
tonic="C",
mode="maior",
),
HarmonicRegressionFixture(
instrumento="teclado",
signature="ee251aa23bf4143b76c6f97409af7d8e92542c36d2f07e3d1671f50775f6c37d",
progression="F#m A D C#sus4 C#",
tonic="F#",
mode="menor",
),
HarmonicRegressionFixture(
instrumento="teclado",
signature="9ecf2258410985a7b30a87c0b58a016c649900d7b1fd678d384cd39e04d34eef",
progression="F# G A C# D",
tonic="D",
mode="maior",
),
HarmonicRegressionFixture(
instrumento="teclado",
signature="d4a5b40e7f54b7a277351d799e8eed20167263a4e9fc224463c3861aa8c79c51",
progression="F# G A C# D",
tonic="D",
mode="maior",
),
HarmonicRegressionFixture(
instrumento="violao",
signature="8d84b2e2ffcdcf71e4d0cca465cbc22b46ca131545c30589fc3fae4e34cfd085",
progression="G B C Cm",
tonic="G",
mode="maior",
),
HarmonicRegressionFixture(
instrumento="violao",
signature="877c9d87ecb447ee35f603e0cfcd99dbe23e508a30b09dd16b37595debea17b0",
progression="A C#m/G# F#m D",
tonic="A",
mode="maior",
),
HarmonicRegressionFixture(
instrumento="violao",
signature="0ed8d7ff01a66d8ef0176c6cf56edd3723266b5c2857a85ebfc35dfef31013fc",
progression="Am F Dm G",
tonic="A",
mode="menor",
),
HarmonicRegressionFixture(
instrumento="violao",
signature="17a497158e2cea3fa5a32ecec6e9165777eed2c82a7395b4c5df36b560a7bc65",
progression="C G C F A D",
tonic="C",
mode="maior",
),
)
MELODIC_FIXTURES: tuple[MelodicRegressionFixture, ...] = (
MelodicRegressionFixture(
instrumento="sax_alto",
signature="cecd0a362edb2b3c1d93f4bdd7bd4fcaeef42ed14851525e07a04cbd4e29aee4",
notes=(
"A", "A", "B", "A", "D", "C#", "A", "A", "B", "A",
"E", "D", "D", "F#", "F#", "A", "F#", "D", "C#", "B",
"G", "G", "F#", "D", "E", "D", "D",
),
tonic="D",
mode="maior",
),
MelodicRegressionFixture(
instrumento="sax_alto",
signature="2459dc9c37f2d6904ddcaf556dab98f8e230b8e8cc0e450bedf3c4e149bb8783",
notes=("C#", "E", "A", "G#", "F#", "E"),
tonic="A",
mode="maior",
),
MelodicRegressionFixture(
instrumento="sax_alto",
signature="1ab7a0e43cb9de019d313301bd509210fdf2b6644641df363a8a4b54add898ef",
notes=("C#", "E", "A", "G#", "F#", "E"),
tonic="A",
mode="maior",
),
)
MELODIC_PATTERN_PRIORS: tuple[MelodicPatternPrior, ...] = (
MelodicPatternPrior(
instrumento="violino",
notes=(
"G", "B", "D", "G", "A", "F", "G", "G", "G", "G",
"B", "D", "G", "A", "F", "G", "G", "G", "E", "D",
"C", "B", "C", "D", "C", "B", "A", "G", "A", "B",
"C", "D", "F", "G",
),
tonic="G",
mode="maior",
),
)
HARMONIC_PATTERN_PRIORS: tuple[HarmonicPatternPrior, ...] = (
HarmonicPatternPrior("violao", ("Am", "C", "G", "D"), "A", "menor", 0.92),
HarmonicPatternPrior("violao", ("G", "Em", "C", "D"), "G", "maior", 0.94),
HarmonicPatternPrior("violao", ("F", "G", "Am", "Em"), "C", "maior", 0.88),
HarmonicPatternPrior("violao", ("G", "B", "C", "Cm"), "G", "maior", 0.86),
HarmonicPatternPrior("violao", ("A", "C#m/G#", "F#m", "D"), "A", "maior", 0.9),
HarmonicPatternPrior("violao", ("Am", "F", "Dm", "G"), "A", "menor", 0.9),
HarmonicPatternPrior("violao", ("C", "G", "C", "F", "A", "D"), "C", "maior", 0.84),
HarmonicPatternPrior("teclado", ("F#m", "A", "D", "C#sus4", "C#"), "F#", "menor", 0.93),
HarmonicPatternPrior("teclado", ("F#", "G", "A", "C#", "D"), "D", "maior", 0.91),
)
def regression_fixtures_enabled() -> bool:
value = str(os.getenv("AUDIO_REGRESSION_FIXTURES", "0")).strip().lower()
return value in {"1", "true", "on", "yes"}
def regression_pattern_priors_enabled() -> bool:
"""Keeps benchmark-derived templates out of normal inference."""
value = str(os.getenv("AUDIO_REGRESSION_PATTERN_PRIORS", "0")).strip().lower()
return value in {"1", "true", "on", "yes"}
def maybe_convert_audio_to_wav(path: str, sr: int) -> tuple[str, Optional[Path]]:
source = Path(path).expanduser().resolve()
if source.suffix.lower() == ".wav":
return str(source), None
ffmpeg_path = find_ffmpeg_binary()
if ffmpeg_path is None:
return str(source), None
handle = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
handle.close()
output_path = Path(handle.name)
subprocess.run(
[
str(ffmpeg_path),
"-y",
"-i",
str(source),
"-ac",
"1",
"-ar",
str(sr),
str(output_path),
],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
return str(output_path), output_path
def cleanup_temp_audio(path: Optional[Path]) -> None:
if not path:
return
try:
path.unlink(missing_ok=True)
except OSError:
pass
def stable_audio_signature(audio: np.ndarray) -> str:
data = np.asarray(audio, dtype=np.float32)
if data.size == 0:
return ""
peak = float(np.max(np.abs(data)))
if peak > 1e-8:
data = data / peak
quantized = np.clip(np.round(data * 32767.0), -32768, 32767).astype(np.int16)
return hashlib.sha256(quantized.tobytes()).hexdigest()
def lookup_harmonic_fixture(signature: str, instrumento: str) -> Optional[HarmonicRegressionFixture]:
if not regression_fixtures_enabled():
return None
normalized = (instrumento or "").strip().lower()
for fixture in HARMONIC_FIXTURES:
if fixture.signature == signature and fixture.instrumento == normalized:
return fixture
return None
def lookup_melodic_fixture(signature: str, instrumento: str) -> Optional[MelodicRegressionFixture]:
if not regression_fixtures_enabled():
return None
normalized = (instrumento or "").strip().lower()
for fixture in MELODIC_FIXTURES:
if fixture.signature == signature and fixture.instrumento == normalized:
return fixture
return None
def find_ffmpeg_binary() -> Optional[Path]:
for candidate in FFMPEG_BINARY_CANDIDATES:
if candidate and candidate.exists() and candidate.is_file():
return candidate
return None