Well, there is python-midi, which is stable enough:

You can easily create a script that automatically "shifts" the timing of events by decreasing the tick of the first event.
By "easily" I imply some basic rules:
- all events which have to be shifted must be on the same midi track
- the first Note event in any of those tracks should not be at the very beginning of the file
- tempo changes, if any, should not be too big

Of course, it's possible to do the same even with these limitations, but it will be much more difficult to implement it in the right way.

Timing in midi files works with "ticks" or "pulses", and each file has its own resolution, which represents the tick resolution per beat, or the smallest possible quantization: if a midi file has a resolution equal to 128 it means that the smallest possible division is a 128th of a quarter note.

The python-midi module treats midi files as nested lists. The main object, representing the midi file read (or to be written), is a Pattern, which is a list of Tracks, which in turn is a list of MidiEvents.

A very basic example, assuming you know the exact structure of your midi file and you want to shift the second track:

import midi
pattern = midi.read_midifile('myfile.mid')
strings = pattern[1]
#let's see what's the next event with a positive relative tick
eventId = 0
event = strings[eventId]
while not event.tick:
    eventId += 1
    event = strings[eventId]
#ensure that the resulting tick is >= 0
firstEvent.tick = max(0, firstEvent.tick - 150)
midi.write_midifile('newfile.mid', pattern)

Sometimes midi files contain an "invisible" track which includes some metadata; if you know the track name (and there are no duplicate names), you can find it like this:

import midi
pattern = midi.read_midifile('myfile.mid')
#the TrackName event _should_ be the first event in the track...
for track in pattern:
    for eventId, event in enumerate(track):
        if isinstance(event, midi.TrackNameEvent) and event.text == 'Strings':
            break
eventId += 1
event = track[eventId]
while not event.tick:
    eventId += 1
    event = track[eventId]
event.tick = max(0, event.tick - 150)
midi.write_midifile('newfile.mid', pattern)

These are very simple examples, with no error checks at all.
Also, the 150 subtraction is arbitrary, and it should be computed according to the tempo and the tick resolution, which is accessible using pattern.resolution; supposing that your file has a resolution of 600 ticks per beat, the above examples will anticipate every event by a 16th, or 0.125 seconds if the tempo is set to 120bpm.
Once you've found out what's the preferred anticipation in seconds of your patch, you could compute the right timing in the script also: Tempo events are usually in the first track, so just cycle through it until you find a SetTempoEvent, and compute the "delta tick" like this:

deltaSecs = 0.125
deltaTick = int(deltaSecs * pattern.resolution * tempoEvent.bpm / 60.)
[...]
firstEvent.tick = max(0, event.tick - deltaTick)

I hope this helps!

MaurizioB


2018-08-29 22:30 GMT+02:00 Francesco Ariis <fa-ml@ariis.it>:
Hello LAU,
    lately I have been playing with ABC notation [1].
ABC outputs to postscript/PDF and MIDI (a good output), but there
some things I would like to change in automatic fashion. Example:
anticipate the attack on some channels (string).
Is there a command line Linux utility to mess with midi files,
similar to what ImageMagick is for images?
I know plenty of libraries in many programming languages, but before
diving into those I would like not to reinvent the wheel.

[1] http://trillian.mit.edu/~jc/music/abc/doc/ABCtut_Intro.html
_______________________________________________
Linux-audio-user mailing list
Linux-audio-user@lists.linuxaudio.org
https://lists.linuxaudio.org/listinfo/linux-audio-user



--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net