¿Cómo puedo dividir un archivo midi programáticamente?

Estoy usando la biblioteca mido en python para trabajar con el archivo midi.

Dado cualquier archivo midi, me gustaría generar un archivo midi que consista solo en los primeros 10 segundos de la entrada. No veo ninguna función para hacer esto en la biblioteca, así que estoy tratando de hacer esto revisando cada una de las pistas.

Estoy encontrando algunas cosas confusas. Por ejemplo, a continuación se muestra lo que obtiene cuando examina una sola pista en su objeto midi.

<meta message track_name name='Slow Strings' time=0>
program_change channel=0 program=49 time=0
program_change channel=0 program=49 time=0
control_change channel=0 control=7 value=71 time=0
control_change channel=0 control=10 value=75 time=0
note_on channel=0 note=55 velocity=89 time=15360
note_on channel=0 note=59 velocity=95 time=0
note_on channel=0 note=62 velocity=92 time=0
note_off channel=0 note=55 velocity=64 time=1324
note_off channel=0 note=62 velocity=64 time=12

Entonces, ¿reviso esta lista de mensajes en la pista y cuento el atributo de tiempo hasta que la suma acumulada supera los 10000 (milisegundos)? No lo creo, porque mirando la pieza en Musescore, no sucede nada significativo a los 15,36 segundos de la canción.

Leí aquí que los tiempos no están en milisegundos, sino en tiempos delta, que deben convertirse a milisegundos a través de PPQ (pulsos por cuarto de nota). ¿Es esto correcto, y si es así, usar una suma de tiempo en ejecución es el enfoque correcto para crear un midi de salida de tiempo de 10 segundos?

¿Tiene que estar dentro de Python...? Simplemente puede ir a midi.mathewvp.com/midiTrim.htm cargar todo el archivo midi, escribir de 0 a 10 segundos y descargar la versión recortada nuevamente. Esta puede ser la forma más rápida, pero también puede hacerlo dentro de cualquier DAW si lo desea.

Respuestas (2)

Los tiempos no están en milisegundos, están en ticks midi que puedes convertir a segundos con la función tick2second(ticks, ticks_per_beat, tempo). Su código debería ser algo como esto:

from mido import MidiFile
from mido import tick2second

mid = MidiFile('input.mid')

for i, track in enumerate(mid.tracks):
    total_time = 0
    for msg in track:
        if msg.type == 'set_tempo':
            tempo = msg.tempo
        total_time  += tick2second(msg.time, mid.ticks_per_beat, tempo)
        print(msg, total_time) # or copy to output file
        if total_time > 10:
            break

Nota: esto supone que no hay cambios de tempo en los primeros 10 segundos de sus archivos de entrada. Si existen, su código deberá ser más complejo.

Si está de acuerdo con C# en lugar de Python, puede usar la biblioteca DryWetMIDI . El código para tomar los primeros 10 segundos de un archivo MIDI es muy fácil:

var midiFile = MidiFile.Read("My MIDI file.mid");
var newMidiFile = midiFile.TakePart(new MetricTimeSpan(0, 0, 10));
newMidiFile.Write("10sec version.mid");