Spaces:
Sleeping
Sleeping
| import numpy as np | |
| import scipy.signal as sps | |
| def detect_audio_issues(spectral, time_stats): | |
| """Detect audio processing artifacts using forensic rules.""" | |
| issues = [] | |
| energy = spectral["energy_distribution"] | |
| freqs = spectral["freqs"] | |
| hf_env = spectral.get("hf_env", None) | |
| lf_env = spectral.get("lf_env", None) | |
| flatness = spectral.get("spectral_flatness", None) | |
| notches = spectral.get("spectral_notches", []) | |
| # ============================================================ | |
| # 1️⃣ HF LOSS LOGIC | |
| # ============================================================ | |
| hf_8_12 = energy["8k_12khz"] | |
| highest_freq = spectral["highest_freq_minus60db"] | |
| if hf_8_12 < 0.01 and highest_freq < 9000: | |
| issues.append(( | |
| "HF_LOSS", "HIGH", | |
| f"Severe HF cutoff: {hf_8_12:.3f}% in 8–12k and rolloff at {highest_freq:.1f} Hz." | |
| )) | |
| elif hf_8_12 < 0.02: | |
| issues.append(( | |
| "HF_LOSS", "LOW", | |
| f"Low HF energy ({hf_8_12:.3f}%). Normal for speech." | |
| )) | |
| # ============================================================ | |
| # 2️⃣ LPF DETECTOR | |
| # ============================================================ | |
| if hf_env is not None: | |
| hf_region = (freqs >= 5000) & (freqs <= 12000) | |
| hf_vals = hf_env[hf_region] | |
| hf_freq = freqs[hf_region] | |
| if len(hf_vals) > 10: | |
| coef = np.polyfit(hf_freq, hf_vals, 1) | |
| slope_per_hz = coef[0] | |
| slope_db_oct = slope_per_hz * np.log2(2) * 12000 | |
| if highest_freq < 10000: | |
| issues.append(( | |
| "LPF_DETECTED", "HIGH", | |
| f"Low-pass filter near {highest_freq:.0f} Hz." | |
| )) | |
| elif slope_db_oct < -6: | |
| issues.append(( | |
| "HF_EQ_SHELF", "LOW", | |
| f"HF rolloff detected (~{slope_db_oct:.1f} dB/oct)." | |
| )) | |
| # ============================================================ | |
| # 3️⃣ HPF DETECTOR | |
| # ============================================================ | |
| if lf_env is not None: | |
| low_region = (freqs >= 20) & (freqs <= 300) | |
| min_len = min(len(low_region), len(lf_env)) | |
| low_region = low_region[:min_len] | |
| lf_env_trim = lf_env[:min_len] | |
| freqs_trim = freqs[:min_len] | |
| lf_vals = lf_env_trim[low_region] | |
| lf_freq = freqs_trim[low_region] | |
| if len(lf_vals) > 10: | |
| coef_l = np.polyfit(lf_freq, lf_vals, 1) | |
| slope_l = coef_l[0] | |
| slope_db_oct_l = slope_l * np.log2(2) * 300 | |
| if energy["below_100hz"] < 0.5: | |
| if slope_db_oct_l > 6: | |
| issues.append(( | |
| "HPF_DETECTED", "HIGH", | |
| f"High-pass filter detected (~{slope_db_oct_l:.1f} dB/oct)." | |
| )) | |
| else: | |
| issues.append(( | |
| "HPF_SUSPECTED", "LOW", | |
| "Possible mild HPF (LF rolloff)." | |
| )) | |
| # ============================================================ | |
| # 4️⃣ NOISE REDUCTION DETECTOR | |
| # ============================================================ | |
| if flatness is not None: | |
| hf_flat = flatness | |
| if hf_flat > 0.40 and len(notches) >= 3: | |
| issues.append(( | |
| "NOISE_REDUCTION_ARTIFACTS", "HIGH", | |
| f"NR artifacts: HF flattening ({hf_flat:.2f}) + {len(notches)} notches." | |
| )) | |
| elif hf_flat > 0.35: | |
| issues.append(( | |
| "NR_SOFT", "LOW", | |
| f"Mild noise reduction detected (HF flattening={hf_flat:.2f})." | |
| )) | |
| # ============================================================ | |
| # 5️⃣ SPECTRAL NOTCHES | |
| # ============================================================ | |
| if len(notches) > 0: | |
| issues.append(( | |
| "SPECTRAL_NOTCHES", "MEDIUM", | |
| f"{len(notches)} spectral notches detected." | |
| )) | |
| # ============================================================ | |
| # 6️⃣ BRICK-WALL DETECTOR | |
| # ============================================================ | |
| if spectral["brick_wall_detected"]: | |
| issues.append(( | |
| "BRICK_WALL", "HIGH", | |
| f"Brick-wall behavior at {spectral['brick_wall_freq']:.0f} Hz." | |
| )) | |
| # ============================================================ | |
| # 7️⃣ COMPRESSION / DYNAMICS | |
| # ============================================================ | |
| crest = time_stats["crest_factor_db"] | |
| if crest < 3: | |
| issues.append(( | |
| "OVER_COMPRESSION", "HIGH", | |
| f"Very low crest factor ({crest:.1f} dB)." | |
| )) | |
| elif crest < 6: | |
| issues.append(( | |
| "COMPRESSION", "MEDIUM", | |
| f"Moderate compression ({crest:.1f} dB)." | |
| )) | |
| # ============================================================ | |
| # 8️⃣ CLIPPING | |
| # ============================================================ | |
| if time_stats["peak"] >= 0.999: | |
| issues.append(( | |
| "CLIPPING", "CRITICAL", | |
| f"Peak amplitude {time_stats['peak']:.6f}. Possible clipping." | |
| )) | |
| # ============================================================ | |
| # 9️⃣ DE-ESSER DETECTION | |
| # ============================================================ | |
| if hf_env is not None: | |
| band_3_6k = (freqs >= 3000) & (freqs <= 6000) | |
| band_6_10k = (freqs >= 6000) & (freqs <= 10000) | |
| presence_energy = np.mean(hf_env[band_3_6k]) | |
| sibilance_energy = np.mean(hf_env[band_6_10k]) | |
| if sibilance_energy < (presence_energy * 0.20): | |
| issues.append(( | |
| "DE_ESSER_DETECTED", "MEDIUM", | |
| "Sibilance band (6–10 kHz) strongly reduced vs presence band (3–6 kHz)." | |
| )) | |
| # ============================================================ | |
| # 🔟 MULTIBAND COMPRESSION | |
| # ============================================================ | |
| if hf_env is not None: | |
| def band_crest(env, band): | |
| vals = env[band] | |
| if len(vals) == 0: | |
| return None | |
| return np.max(vals) - np.mean(vals) | |
| lf_band = (freqs >= 80) & (freqs <= 300) | |
| mf_band = (freqs >= 300) & (freqs <= 3000) | |
| hf_band = (freqs >= 3000) & (freqs <= 8000) | |
| cf_lf = band_crest(hf_env, lf_band) | |
| cf_mf = band_crest(hf_env, mf_band) | |
| cf_hf = band_crest(hf_env, hf_band) | |
| if cf_lf is not None and cf_mf is not None and cf_hf is not None: | |
| if cf_hf < (cf_lf * 0.4): | |
| issues.append(( | |
| "MULTIBAND_COMPRESSION", "MEDIUM", | |
| "HF crest factor significantly lower than LF." | |
| )) | |
| if cf_mf < (cf_lf * 0.5): | |
| issues.append(( | |
| "MULTIBAND_COMPRESSION", "LOW", | |
| "Mid-band crest factor compressed vs LF." | |
| )) | |
| # ============================================================ | |
| # 1️⃣1️⃣ EQ CURVE CLASSIFIER | |
| # ============================================================ | |
| if hf_env is not None: | |
| smooth = sps.medfilt(hf_env, kernel_size=9) | |
| coef_eq = np.polyfit(freqs, smooth, 1) | |
| tilt = coef_eq[0] | |
| curvature = np.polyfit(freqs, smooth, 2)[0] | |
| if tilt > 0.00002: | |
| issues.append(( | |
| "EQ_HF_BOOST", "LOW", | |
| "HF shelf boost detected (positive tilt)." | |
| )) | |
| elif tilt < -0.00002: | |
| issues.append(( | |
| "EQ_HF_CUT", "LOW", | |
| "HF shelf cut detected (negative tilt)." | |
| )) | |
| if curvature > 1e-12: | |
| issues.append(( | |
| "EQ_PEAKING", "LOW", | |
| "Spectral curvature suggests midrange peaking EQ." | |
| )) | |
| if abs(tilt) > 0.00001 and abs(curvature) < 1e-12: | |
| issues.append(( | |
| "EQ_TILT", "LOW", | |
| "Tilt EQ detected (linear spectral tilt)." | |
| )) | |
| return issues | |