audio-data-quality-tool / tests /test_checks.py
ManuBo's picture
v0.2.0: quality score and integration
b89a72f
Raw
History Blame Contribute Delete
5.51 kB
from __future__ import annotations
import numpy as np
import tempfile
from scipy.io import wavfile
from audio_qa.checks.noise import estimate_snr, check_noise
from audio_qa.checks.clipping import check_clipping
from audio_qa.checks.silence import check_silence
from audio_qa.checks.duration import check_duration
from audio_qa.checks.sample_rate import check_sample_rate
from audio_qa.checks.loudness import check_loudness
from audio_qa.checks.upsampling import check_upsampling
from audio_qa.checks.transcript_ratio import check_transcript_ratio
from audio_qa.checks.channel import check_channel
from audio_qa.pipeline import check_file
def test_clean_has_high_snr(clean_speech):
audio, sr = clean_speech
snr = estimate_snr(audio, sr)
assert snr > 8
def test_noisy_has_low_snr(noisy_signal):
audio, sr = noisy_signal
snr = estimate_snr(audio, sr)
assert snr < 20
def test_check_noise_clean(clean_speech):
audio, sr = clean_speech
result = check_noise(audio, sr, snr_threshold=5)
assert result["passed"]
def test_no_clipping_clean(clean_speech):
audio, sr = clean_speech
result = check_clipping(audio, sr)
assert result["passed"]
def test_detects_clipping(clipped_signal):
audio, sr = clipped_signal
result = check_clipping(audio, sr)
assert not result["passed"] or result["clip_regions"] > 0
def test_clean_no_silence_issues(clean_speech):
audio, sr = clean_speech
result = check_silence(audio, sr)
assert result["passed"]
def test_silent_signal_flagged(silent_signal):
audio, sr = silent_signal
result = check_silence(audio, sr)
assert result["leading_silence_s"] > 1.0 or result["trailing_silence_s"] > 1.0
def test_duration_in_range():
result = check_duration(22050 * 5, 22050, min_seconds=1.0, max_seconds=30.0)
assert result["passed"]
def test_duration_too_short():
result = check_duration(22050, 22050 * 10, min_seconds=1.0)
assert not result["passed"]
def test_sample_rate_standard():
result = check_sample_rate(22050)
assert result["passed"]
def test_sample_rate_nonstandard():
result = check_sample_rate(11025)
assert not result["passed"]
def test_upsampling_clean(clean_speech):
audio, sr = clean_speech
result = check_upsampling(audio, sr)
assert result["passed"]
def test_transcript_ratio_normal():
result = check_transcript_ratio(5.0, "This is a normal sentence for testing purposes.")
assert result["passed"]
def test_transcript_ratio_too_fast():
result = check_transcript_ratio(0.5, "This is way too much text for half a second of audio really.")
assert not result["passed"]
def test_channel_mono():
sr = 22050
audio = np.random.randn(sr * 2).astype(np.float32) * 0.3
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
wavfile.write(f.name, sr, (audio * 32767).astype(np.int16))
result = check_channel(f.name)
assert result["passed"]
def test_check_file_runs():
sr = 22050
n = int(sr * 3.0)
f0_drift = np.cumsum(np.random.randn(n) * 0.05)
f0_curve = 150 + np.clip(f0_drift - np.mean(f0_drift), -40, 40)
phase = np.cumsum(2 * np.pi * f0_curve / sr)
audio = 0.3 * np.sin(phase) + 0.02 * np.random.randn(n)
audio = audio / np.max(np.abs(audio)) * 0.5
audio_16 = (audio * 32767).astype(np.int16)
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
wavfile.write(f.name, sr, audio_16)
result = check_file(f.name)
assert result["num_checks"] >= 10
assert "checks" in result
assert "quality_score" in result
assert "grade" in result
assert 0 <= result["quality_score"] <= 10
assert result["grade"] in ("A", "B", "C", "D", "F")
def test_nonexistent_file():
result = check_file("/nonexistent.wav")
assert "error" in result
def test_quality_score_perfect():
from audio_qa.checks.quality_score import compute_quality_score
checks = [
{"check": "background_noise", "passed": True, "snr_db": 40},
{"check": "clipping", "passed": True, "clip_ratio": 0},
{"check": "silence", "passed": True, "leading_silence_s": 0.1, "trailing_silence_s": 0.1, "max_internal_silence_s": 0},
{"check": "loudness", "passed": True, "deviation_db": 1},
{"check": "tts_metallic", "passed": True, "metallic_frame_ratio": 0.02},
{"check": "upsampling", "passed": True},
{"check": "channel", "passed": True, "issues": []},
{"check": "duration", "passed": True},
]
result = compute_quality_score(checks)
assert result["quality_score"] >= 8.0
assert result["grade"] in ("A", "B")
def test_quality_score_bad():
from audio_qa.checks.quality_score import compute_quality_score
checks = [
{"check": "background_noise", "passed": False, "snr_db": 3},
{"check": "clipping", "passed": False, "clip_ratio": 0.05},
{"check": "silence", "passed": False, "leading_silence_s": 3, "trailing_silence_s": 3, "max_internal_silence_s": 5},
{"check": "loudness", "passed": False, "deviation_db": 15},
{"check": "tts_metallic", "passed": False, "metallic_frame_ratio": 0.8},
{"check": "upsampling", "passed": False},
{"check": "channel", "passed": False, "issues": ["stereo", "silent left", "phase inverted"]},
{"check": "duration", "passed": False},
]
result = compute_quality_score(checks)
assert result["quality_score"] <= 3.0
assert result["grade"] in ("D", "F")