Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import os | |
| import json | |
| import tempfile | |
| import shutil | |
| import re | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| from grid_fixer_v2 import fix_grid_advanced | |
| def sanitize_filename(name): | |
| """Remove espaços e caracteres especiais do nome do ficheiro.""" | |
| name = re.sub(r'[\s]+', '_', name) | |
| name = re.sub(r'[^\w\-\.]', '', name) | |
| return name | |
| def process_audio(input_file, target_bpm, pitch_semitones, phase_lock, verbose): | |
| if input_file is None: | |
| return "Por favor, faz upload de um ficheiro de áudio.", "", "" | |
| try: | |
| base_name = sanitize_filename(Path(input_file).stem) | |
| # Guardar na pasta de trabalho atual (onde está o app_v2.py) | |
| # O Gradio permite servir ficheiros desta pasta por defeito | |
| output_dir = Path.cwd() / "output" | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| output_wav = str(output_dir / f"{base_name}_fixed.wav") | |
| bpm_value = target_bpm if target_bpm > 0 else None | |
| metadata = fix_grid_advanced( | |
| input_file, | |
| output_wav, | |
| target_bpm=bpm_value, | |
| pitch_semitones=float(pitch_semitones), | |
| phase_lock=phase_lock, | |
| verbose=verbose | |
| ) | |
| if metadata is None: | |
| return "❌ Erro no processamento. Verifica o terminal para detalhes.", "", "" | |
| # Ler metadados | |
| meta_path = str(Path(output_wav).with_suffix(".json")) | |
| if os.path.exists(meta_path): | |
| with open(meta_path, 'r', encoding='utf-8') as f: | |
| meta = json.load(f) | |
| else: | |
| meta = metadata | |
| pitch_info = f"{meta['pitch_shift_semitons']:+.1f} semitons" if meta['pitch_shift_semitons'] != 0 else "Sem alteração" | |
| status = (f"✅ Processamento concluído!\n\n" | |
| f"📊 BPM Original: {meta['original_bpm']:.2f}\n" | |
| f"🎯 BPM Alvo: {meta['target_bpm']:.2f}\n" | |
| f"🎵 Pitch: {pitch_info}\n" | |
| f"🔒 Phase-Lock: {'Ativo' if meta['phase_lock'] else 'Desativado'}\n" | |
| f"⚡ Modo: {'Metronómico (Linear)' if meta.get('is_metronomic') else 'Variável (Warp)'}\n" | |
| f"📈 Variação Rítmica: {meta.get('ibi_cv', 0)*100:.2f}%\n" | |
| f"🎹 Key Estimada: {meta.get('estimated_key', 'N/A')}\n" | |
| f"⏱️ Primeiro Beat: {meta['first_beat_time_original']:.3f}s\n" | |
| f"🌀 Fase Offset: {meta.get('phase_offset_degrees', 0):.1f}°\n" | |
| f"🔢 Beats Corrigidos: {meta['beats_count']}") | |
| file_info = f"📁 {output_wav}" | |
| return status, file_info, output_wav | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| return f"❌ Erro: {str(e)}", "", "" | |
| def open_folder(): | |
| """Abre a pasta de saída no explorador de ficheiros.""" | |
| output_dir = Path.cwd() / "output" | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| if os.name == 'nt': # Windows | |
| os.startfile(str(output_dir)) | |
| elif os.name == 'posix': # Linux/Mac | |
| subprocess.call(['open' if sys.platform == 'darwin' else 'xdg-open', str(output_dir)]) | |
| return f"📂 Pasta aberta: {output_dir}" | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 🎛️ Forca-Grid v2") | |
| gr.Markdown("Beatgrid + Pitch + Phase") | |
| with gr.Row(): | |
| with gr.Column(): | |
| input_audio = gr.Audio(label="Upload Áudio", type="filepath") | |
| target_bpm = gr.Number(label="BPM Alvo (0 = auto)", value=0) | |
| pitch_shift = gr.Slider(label="Pitch (semitons)", minimum=-12, maximum=12, step=0.5, value=0) | |
| phase_lock = gr.Checkbox(label="Phase-Lock", value=True) | |
| verbose_mode = gr.Checkbox(label="Verbose", value=True) | |
| with gr.Row(): | |
| btn_process = gr.Button("🚀 Processar", variant="primary") | |
| btn_open = gr.Button("📂 Abrir Pasta de Saída") | |
| with gr.Column(): | |
| status_text = gr.Textbox(label="📋 Status / Metadados", interactive=False, lines=12) | |
| file_path = gr.Textbox(label="📁 Path do Ficheiro", interactive=False) | |
| # Preview de áudio - agora deve funcionar porque o ficheiro está na pasta permitida | |
| output_preview = gr.Audio(label="🔊 Preview", type="filepath") | |
| # Processamento | |
| btn_process.click( | |
| fn=process_audio, | |
| inputs=[input_audio, target_bpm, pitch_shift, phase_lock, verbose_mode], | |
| outputs=[status_text, file_path, output_preview] | |
| ) | |
| # Abrir pasta | |
| btn_open.click( | |
| fn=open_folder, | |
| inputs=[], | |
| outputs=[file_path] | |
| ) | |
| if __name__ == "__main__": | |
| # allowed_paths garante que o Gradio pode servir ficheiros da pasta output | |
| output_dir = str(Path.cwd() / "output") | |
| demo.launch( | |
| share=False, | |
| server_name="0.0.0.0", | |
| allowed_paths=[output_dir] | |
| ) | |