Spaces:
Runtime error
Runtime error
| """Chargement, validation et prétraitement audio partagé par toutes les interfaces.""" | |
| from __future__ import annotations | |
| from pathlib import Path # Rend les chemins fiables sous Windows, Linux et macOS. | |
| # librosa décode WAV/MP3 et réalise le rééchantillonnage demandé par le modèle. | |
| import librosa | |
| # NumPy représente le signal audio sous forme de tableau de nombres réels. | |
| import numpy as np | |
| from src.errors import AudioValidationError | |
| # Wav2Vec2 français attend précisément 16 000 valeurs (échantillons) par seconde. | |
| TARGET_SAMPLE_RATE = 16_000 | |
| # La limite de l'énoncé protège l'API contre un traitement trop long ou trop coûteux. | |
| MAX_DURATION_SECONDS = 5 * 60 | |
| # Seuls les deux formats explicitement autorisés par l'énoncé sont acceptés. | |
| SUPPORTED_EXTENSIONS = {".wav", ".mp3"} | |
| def load_and_preprocess_audio(audio_path: str | Path) -> np.ndarray: | |
| """Retourne un signal mono normalisé à 16 kHz prêt pour Wav2Vec2. | |
| Wav2Vec2 a été entraîné sur des signaux mono à 16 kHz : imposer ce format | |
| évite les incohérences entre les fichiers WAV/MP3 envoyés par les clients. | |
| """ | |
| # Convertir la chaîne en Path permet ensuite de vérifier extension, existence et taille. | |
| path = Path(audio_path) | |
| if path.suffix.lower() not in SUPPORTED_EXTENSIONS: | |
| raise AudioValidationError("Format non supporté : utilisez un fichier .wav ou .mp3.") | |
| # Un fichier vide est rejeté avant de demander à librosa de le décoder. | |
| if not path.is_file() or path.stat().st_size == 0: | |
| raise AudioValidationError("Le fichier audio est introuvable ou vide.") | |
| try: | |
| # mono=True mélange les canaux ; sr=16000 effectue le rééchantillonnage. | |
| samples, sample_rate = librosa.load(path, sr=TARGET_SAMPLE_RATE, mono=True) | |
| except Exception as exc: | |
| raise AudioValidationError("Impossible de décoder ce fichier audio.") from exc | |
| # Après le chargement, la durée est le nombre d'échantillons divisé par la fréquence. | |
| duration = len(samples) / sample_rate | |
| if duration == 0: | |
| raise AudioValidationError("Le fichier audio ne contient aucun échantillon.") | |
| if duration > MAX_DURATION_SECONDS: | |
| raise AudioValidationError("La durée maximale autorisée est de 5 minutes.") | |
| # Le pic mesure l'amplitude la plus forte ; il sert à détecter le silence puis normaliser. | |
| peak = float(np.max(np.abs(samples))) | |
| # Un seuil non nul rejette les enregistrements silencieux/bruités sans écraser | |
| # leur amplitude lors de la normalisation. | |
| if peak < 1e-4: | |
| raise AudioValidationError("Audio silencieux : aucune voix exploitable n'a été détectée.") | |
| # La normalisation ramène le pic à 1.0 sans modifier le contenu relatif du signal. | |
| # float32 est le type attendu par PyTorch et réduit l'occupation mémoire par rapport à float64. | |
| return (samples / peak).astype(np.float32) | |