melodix-api / app /utils /midi_parser.py
GitHub Action
deploy from github actions
440bac0
Raw
History Blame Contribute Delete
2.99 kB
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)}"
}