This C code is a framework for changing MIDI files. Most of my MIDI format information is from here (once there) or here (once there). The framework proper is in files r.c and w.c together with h.h. To use this framework compile a main program that includes h.h and these routines.
Type BP has a little information that pervades a particular MIDI file. In a BP value is:
Type note represents mostly notes but also timed events in specific MIDI tracks. They are a defined by a typedef in h.h. Note sets are abstracted in this framework by values of type note * which heads a linked list of notes. The note is a C structure with a field called next but the plan here is that only the code in r.c and w.c use that field. Other fields in the note structure are:
o.opaque.subtype | content | size (o.opaque.cc) |
1 | Text | varies |
2 | Copyright | varies |
3 | Track Name | varies |
7 | Cue Point | varies |
33 | MIDI Port | 1 |
81 | Tempo | 3 |
84 | SMPTE Offset | 5 |
88 | Time Signature | 4 |
89 | Key Signature | 2 |
Routine note* Read(char* fn, BP*); (defined in r.c) causes a MIDI file fn to be read and its contents returned as a note set. If fn="" then standard input is used. The BP structure is filled from MIDI file header information. Routine void Write(char* fn, note *, BP *); (from w.c) causes the note set to be written into a new MIDI file with header information from the BP value and notes and events from the note set. The file name is fn unless fn is "" in which case it is “out.midi”. The note set is unordered but the Write operations sorts them by the value in trk and start fields. This file illustrates a vacuous call to the framework. Even this may cause some changes by reordering simultaneous events.
Routine void scan(seeNote exam, note* music, int consume); reads a note set music and calls your routine exam for each note in the note set. The caller of scan provides the routine exam of type seeNote. scan consumes the notes of its input and their space is recovered if the boolean consume is true. Otherwise the input node set remains available for other operations. Routine void aug(note n, note** mus); adds note n to the note set at mus. Routine void Free(note ** m) recovers the space occupied by the note set at m.
This main routine copies a MIDI file while looking, incuriously, at each note, passing each back to aug,
while
this variation looks at each note and discards those from track 3 by not depositing them with aug.
This version does something possibly useful: it moves each program change event to the beginning of its track.
I think that other places have no stable meaning between MIDI players.
This program’s output is a printed list of non-note events on the time-line and a count of notes per track.
Map reports which tracks use which channels.
ChanMov oc to nc nt changes a channel number oc in a specific track to to another channel nc in the same or a different track nt.
Segregates so that each track has its own private channel.
This minimizes the ‘density’ or ticks per quarter note.
Compile thus:
gcc lcm.c r.c w.c gran.c -O3
This version inserts a program (voice) changes in various tracks.
I think you need to use program segr first to avoid channel conflicts.
Use map too to see what you are doing.
This prints a loudness histogram.
This makes a new MIDI file with loudness of each note adjusted by the value of the signed integer command line argument.
This moves tempo events from track 2 to track 1 and omits other tempos events.
This version plays the music backwards.
This program transposes a file up or down k half steps.
Here is a program to permute tracks, delete or even duplicate them.
The command line specifies track numbers to be taken from the input file (which is standard in).
The output file (out.midi) will contain just those tracks in the specified order.
“rmtrk 1 3 4 < xx.mid” extracts tracks 1, 3 and 4 from file xx.mid and makes them tracks 1, 2 and 3 of file out.midi.
Note that it is conventional for track 1 to be the “conductor track” so normally track 1 of the output should be track 1 of the input.
Slide <fn> k
increases the start time of each note in the file by k quarter notes.
Dead air is at the beginning.
If k is negative then the first k notes are removed from the beginning of the file.
Its output is prn.mid.
Here is a version that concatenates MIDI files.
Compile thus:
gcc r.c w.c lcm.c mcat.c -o ~/bin/mcat
Then the command mcat *.mid all.mid concatenates the .mid files in your directory into a new file all.mid.
The result plays the components sequentially.
It is good enough to have produced the files named “all.mid” in the English Suites.
Here is a program that chimes half past the hour and has some routines to steal, most likely with your own mods.
This code is a very finicky compare program that ignores note duration and loudness, meta events, and matches notes that start on the same tick. The resolution is 2−n notes, where n is in the command line. Use thus
mdif n <fn1> <fn2>.