Spaces:
Sleeping
Sleeping
File size: 7,694 Bytes
a74c3ba |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
import os
import shutil
import zipfile
from pathlib import Path
import gradio as gr
import torch
from pydub import AudioSegment
from transformers import pipeline
# ------------------------
# CONFIG
# ------------------------
MODEL_NAME = "openai/whisper-large-v3"
device = 0 if torch.cuda.is_available() else "cpu"
pipe = pipeline(
task="automatic-speech-recognition",
model=MODEL_NAME,
device=device,
model_kwargs={"low_cpu_mem_usage": True},
)
TEMP_DIR = "./temp_audio"
os.makedirs(TEMP_DIR, exist_ok=True)
# On stocke la liste des métadonnées (segments) dans un State
# pour la conserver entre les étapes (transcription, découpe, zip).
def init_metadata_state():
return []
# ------------------------
# FONCTIONS
# ------------------------
def transcribe_audio(audio_path):
"""
Étape 2 : Transcription du fichier audio via Whisper
+ récupération de la transcription brute.
"""
if not audio_path:
return "Aucun fichier audio fourni.", [], None
# Transcription Whisper
result = pipe(audio_path, return_timestamps="word")
text = result["text"]
chunks = result["chunks"] # liste de { 'timestamp': (start, end), 'text': ... }
raw_transcription = " ".join([c["text"] for c in chunks])
# Le 3e retour = chemin du fichier audio, qu'on renverra tel quel pour la découpe
return raw_transcription, [], audio_path
def validate_segments(audio_path, table_data, metadata_state):
"""
Étape 5 : Découpe de l'audio en fonction des segments
et mise à jour du State `metadata_state`.
- `table_data` doit contenir : [ [Texte, Début(s), Fin(s), ID], ... ]
- Retourne :
1) Une liste de chemins (extraits audio) pour les players
2) La liste des nouvelles métadonnées (mise à jour).
"""
if not audio_path:
return ["Aucun fichier audio..."], metadata_state
# Nettoyage du dossier temporaire avant recréation des extraits
if os.path.exists(TEMP_DIR):
shutil.rmtree(TEMP_DIR)
os.makedirs(TEMP_DIR, exist_ok=True)
original_audio = AudioSegment.from_file(audio_path)
segment_paths = []
updated_metadata = []
for i, row in enumerate(table_data):
# row = [ segment_text, start_time, end_time, seg_id ]
if len(row) < 4:
continue
seg_text, start_time, end_time, seg_id = row
if not seg_text or start_time is None or end_time is None:
continue
# Si l'utilisateur n'a pas mis d'ID, en créer un
if not seg_id:
seg_id = f"seg_{i+1:02d}"
# Découpe
start_ms = int(float(start_time) * 1000)
end_ms = int(float(end_time) * 1000)
extract = original_audio[start_ms:end_ms]
# Nom de fichier
stem_name = Path(audio_path).stem
segment_filename = f"{stem_name}_{seg_id}.wav"
segment_filepath = os.path.join(TEMP_DIR, segment_filename)
extract.export(segment_filepath, format="wav")
segment_paths.append(segment_filepath)
# Stocker la métadonnée
updated_metadata.append({
"audio_file": segment_filename,
"text": seg_text,
"start_time": start_time,
"end_time": end_time,
"id": seg_id,
})
return segment_paths, updated_metadata
def generate_zip(metadata_state):
"""
Étape 8 : Générer un ZIP contenant tous les extraits + un metadata.csv
Retourne le chemin vers le ZIP final.
"""
if not metadata_state:
return None
# Supprimer un ancien zip si présent
zip_path = os.path.join(TEMP_DIR, "dataset.zip")
if os.path.exists(zip_path):
os.remove(zip_path)
# Créer metadata.csv
metadata_csv_path = os.path.join(TEMP_DIR, "metadata.csv")
with open(metadata_csv_path, "w", encoding="utf-8") as f:
f.write("audio_file|text|speaker_name|API\n")
for seg in metadata_state:
# Ajuste speaker_name ou API selon ton besoin
line = f"{seg['audio_file']}|{seg['text']}|projectname|/API_PHONETIC/\n"
f.write(line)
# Créer le ZIP
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
# Ajouter chaque extrait
for seg in metadata_state:
seg_file = os.path.join(TEMP_DIR, seg["audio_file"])
if os.path.exists(seg_file):
zf.write(seg_file, seg["audio_file"])
# Ajouter le metadata.csv
zf.write(metadata_csv_path, "metadata.csv")
return zip_path
def distribute_segments_to_players(segments):
"""
Transforme la liste de segments en un tuple de 20 valeurs max
(pour 20 players).
Si moins de 20 segments, on complète avec None.
"""
max_players = 20
result = []
for i in range(max_players):
if i < len(segments):
result.append(segments[i])
else:
result.append(None)
return tuple(result)
# ------------------------
# CONSTRUCTION UI GRADIO
# ------------------------
with gr.Blocks(css="style.css") as demo:
gr.Markdown("# Application de Découpage Audio + Transcription (jusqu'à 20 extraits)")
metadata_state = gr.State(init_metadata_state())
# Étape 1 : Chargement de l'audio
with gr.Box():
gr.Markdown("### 1. Téléversez votre fichier audio")
audio_input = gr.Audio(source="upload", type="filepath", label="Fichier audio")
# Étape 3 : Transcription brute
raw_transcription = gr.Textbox(
label="Transcription brute (Whisper)",
placeholder="Le texte s'affichera ici après la transcription...",
interactive=False
)
# Étape 4 : Tableau pour 20 segments max
gr.Markdown("### 2. Définissez jusqu'à 20 segments")
gr.Markdown("""**Colonnes :**
1) Texte (phrase ou portion copiée depuis la transcription)
2) Début (en secondes)
3) Fin (en secondes)
4) ID segment (optionnel)""")
table = gr.Dataframe(
headers=["Texte", "Début (s)", "Fin (s)", "ID"],
datatype=["str", "number", "number", "str"],
row_count=20, # <-- 20 lignes
col_count=4
)
validate_button = gr.Button("Valider et générer les extraits")
# Étape 6 : 20 players audio pour l'écoute
# On les organise en 5 rangées de 4 players
players = []
for i in range(20):
players.append(gr.Audio(label=f"Extrait {i+1}", interactive=False))
# Groupons-les en blocs de 4
for i in range(0, 20, 4):
with gr.Row():
for j in range(i, i+4):
players[j]
# Étape 8 : Génération ZIP
generate_button = gr.Button("Générer le fichier ZIP")
zip_file = gr.File(label="Télécharger le ZIP (audios + metadata.csv)")
# 1) Callback quand on charge l'audio => Transcription
audio_input.change(
fn=transcribe_audio,
inputs=audio_input,
outputs=[raw_transcription, table, audio_input]
)
# 2) Callback quand on valide => Découpe audio + maj metadata
validate_button.click(
fn=validate_segments,
inputs=[audio_input, table, metadata_state],
outputs=[ # 1) chemins extraits (list) 2) metadata (list)
players, # Les 20 players
metadata_state
],
# On va mapper la liste de segments sur 20 players
).then(
fn=distribute_segments_to_players,
inputs=None, # la sortie "players" (chemins) est déjà captée
outputs=players
)
# 3) Génération ZIP
generate_button.click(
fn=generate_zip,
inputs=metadata_state,
outputs=zip_file
)
demo.queue().launch() |