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:

Such a value is produced by the read routine and consumed by the write routine.

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:

int start
the start time of the note in units where the quarter note has duration bp.den
unsigned short dur
is the note’s duration in the same units. The highest several values of dur are reserved to denote timed events that are not notes. Alternative fields of the union o are used in this case. Values of dur greater than maxdur, and their uses are:
OpaqueStuff
which by its MIDI file coding lacks channel information. The field o.opaque.subtype is the ‘sub-type’. For ‘text’, ‘Copyright, ‘Track Name’ and ‘Cue Point’ the field o.opaque.odp points to zero terminated ASCII text found in the MIDI file. o.opaque.cc is the text size, without termination.
o.opaque.subtypecontentsize
(o.opaque.cc)
1Textvaries
2Copyright varies
3Track Name varies
7Cue Point varies
33MIDI Port1
81Tempo3
84SMPTE Offset5
88Time Signature4
89Key Signature2
ProgramChange
o.prog: is the new program, (voice, timbre, etc.)
PitchWheel
o.pitch: pitch information
char chan
is the channel of the note. It is the number in the file plus one.
note.o.freq
Which note—a code for the frequency, when n.dur <= maxdur. Only values from 0 to 127 are valid. 69 denotes A 440 and frequency for j = 2(j-69)/12440 cycles/second.
note.o.loud
is an unsigned char: 7 bits of loudness, often called ‘velocity’.

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.

Sample main routines

Each of these may be compiled by the shell command:
gcc xx.c r.c w.c
where xx.c is the name of the particular file as given below.

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>
.
Here is some custom code for a piece from the Well Tempered Clavier. The MIDI file is made ab initio—there are no input files.