import pretty_midi import os import logging logger = logging.getLogger(__name__) def parse_midi_file(filepath: str) -> dict: """ Parsea un archivo MIDI (.mid o .midi) y extrae todas sus pistas, notas, tempo y clasificación GM. """ if not os.path.exists(filepath): raise FileNotFoundError(f"No se encontró el archivo MIDI en: {filepath}") try: pm = pretty_midi.PrettyMIDI(filepath) tempo = pm.estimate_tempo() duration = pm.get_end_time() tracks = [] for idx, inst in enumerate(pm.instruments): notes = [] for note in inst.notes: notes.append({ "note": pretty_midi.note_number_to_name(note.pitch), "midi": note.pitch, "start": round(note.start, 3), "duration": round(note.end - note.start, 3), "velocity": note.velocity }) # Si no tiene notas, la ignoramos para mantener el payload limpio if not notes: continue if inst.is_drum: inst_type = "drums" name = "Batería / Percusión" else: prog = inst.program if 0 <= prog <= 7: inst_type = "piano" name = f"Piano (GM {prog})" elif 24 <= prog <= 31: inst_type = "guitar" name = f"Guitarra (GM {prog})" elif 32 <= prog <= 39: inst_type = "bass" name = f"Bajo (GM {prog})" elif 16 <= prog <= 23: inst_type = "organ" name = f"Órgano (GM {prog})" elif 40 <= prog <= 55: inst_type = "strings" name = f"Cuerdas/Orquesta (GM {prog})" elif 56 <= prog <= 71: inst_type = "brass" name = f"Metales/Vientos (GM {prog})" else: inst_type = "synth" name = f"Sintetizador (GM {prog})" tracks.append({ "id": idx, "nombre": name, "program": inst.program, "is_drum": inst.is_drum, "tipo_instrumento": inst_type, "total_notas": len(notes), "notas": notes }) return { "success": True, "tempo_bpm": round(tempo, 1) if tempo > 0 else 120.0, "duracion_segundos": round(duration, 2), "total_pistas": len(tracks), "pistas": tracks } except Exception as e: logger.error(f"Error parseando archivo MIDI {filepath}: {e}") return { "success": False, "error": f"El archivo no es un MIDI válido o está dañado: {str(e)}" }