MIDISTATS(1)

๐Ÿ“ฆ
version 2026_10_09 ยท 4 versions
, in arch rolling, fedora 43, fedora 44, nixos 26.05, nixpkgs unstable, ubuntu stonking

๐ŸŽ€ NAME ๐ŸŽ€

midistats โ€” bestie, this is the program that summarizes the statistical properties of a midi file. It reads your MIDI and just, like, tells you everything about it. ๐Ÿ’–


โœจ SYNOPSIS โœจ

okay so real talk: this manual is long, and that's because the program pulls out so many different parameters from the midi file depending on which options you pick. So this manual is gonna try to describe all these parameters and how they get computed. ๐Ÿซถ

And these parameters are formatted in a way so other applications can read them โ€” think midiexplorer.tcl, runstats.tcl, and a whole pile of Python scripts. iykyk.

Also, a lot of these options were designed specifically for analyzing the percussion track of the midi file. Those ones live in their own separate section down below (Other options). Keep scrolling bestie. ๐ŸŒธ


๐Ÿ’… DESCRIPTION ๐Ÿ’…

So if you run midistats with no options other than the name of the midi input file, it will produce a table of values described right here. Every line of output starts with the name of the variable (or variable array) and then the associated values. Periodt. โœจ

  • ntrks โ€” the number of tracks in the midi file.
  • ppqn โ€” the number of midi pulses per quarter note.
  • keysig โ€” the key signature, and then a major/minor flag, the number of sharps (positive) or flats (negative) in the key, and the beat number where the key signature was found. ๐ŸŽผ
  • trk โ€” followed by the track number that all the following information applies to.
  • program โ€” followed by the channel number and the General Midi Program number.

๐Ÿฆ‹ trkinfo, the main character ๐Ÿฆ‹

trkinfo is an array of 19 numbers which indicates the statistical properties of the track of interest. Here's the whole lineup, no skips:

  • 0 โ€” the channel number,
  • 1 โ€” the first program assigned to this channel,
  • 2 โ€” the number of notes for this channel, counting any chords as one note,
  • 3 โ€” the number of chord notes excluding the bass note,
  • 4 โ€” the sum of the MIDI pitches for all the notes,
  • 5 โ€” the sum of the note durations in MIDI pulse units,
  • 6 โ€” the number of pitchbend messages,
  • 7 โ€” the number of control parameter messages,
  • 8 โ€” the number of pressure messages.
  • 9 โ€” the number of distinct rhythm patterns for each channel
  • 10 โ€” the number of pulses the channel was inactive
  • 11 โ€” the minimum pitch value
  • 12 โ€” the maximum pitch value
  • 13 โ€” the minimum note length in pulses
  • 14 โ€” the maximum note length in pulses
  • 15 โ€” the number of gaps in the channel
  • 16 โ€” the entropy of the pitch class histogram for that channel
  • 17 โ€” the number of notes whose pitch were the same as the previous note
  • 18 โ€” the number of notes whose pitch changed by less than 4 semitones
  • 19 โ€” the number of notes whose pitch changed by 4 or more semitones
  • 20 โ€” the average note velocity
  • 21 โ€” the standard deviation of the note velocity
(In event of a chords the maximum pitches are compared.) ๐ŸŽน

๐ŸŒˆ the whole-file era ๐ŸŒˆ

After it's done processing all the individual tracks, the following information applies to the entire midi file. this is the big picture section bestie:

  • npulses โ€” the length of the longest midi track in midi pulse units
  • tempocmds โ€” specifies the number of times the tempo is changed in this file.
  • pitchbends โ€” specifies the total number of pitchbends in this file.
  • pitchbendin c n โ€” specifies the number of pitchbends n in channel c
  • progs โ€” a list of all the midi programs addressed
  • progsact โ€” the amount of activity for each of the above midi programs. The activity is the sum of the note durations in midi pulse units.
  • progcolor โ€” a 17 dimensional vector where each component maps into a specific group of MIDI programs. Some of these groups are, keyboard instruments, brass instruments, wind instruments, and etc. More information can be found in the midiexplorer documentation.
  • drums โ€” a list of all the percussion instruments (channel 9) that were used. ๐Ÿฅ
  • drumhits โ€” indicates the number of notes for each of the above percussion instruments.
  • pitches โ€” a histogram for the 11 pitch classes (C, C#, D ...B) that occur in the midi file.
  • key โ€” indicates the key of the music, the number of sharps (positive) or flats (negative) in the key signature, and a measure of the confidence in this key signature. The key was estimated from the above pitch histogram by convolving with Craig Sapp's model. The peak of rmaj or rmin (below) indicates the key. A correlation less than 0.4 indicates that the pitch histogram does not follow the histogram of a major or minor scale. (It may be the result of a mixture of two key signatures.)
  • rmaj โ€” the cross correlation coefficients with Craig Sapp's major key model for each of the 11 keys (C, C#, D, ...,B).
  • rmaj โ€” the cross correlation coefficients with Craig Sapp's minor key model for each of the 11 keys (C, C#, D, ...,B).
  • pitchact โ€” is a similar histogram but is weighted by the length of the notes.
  • chanvol โ€” indicates the value of the control volume commands in the midi file for each of the 16 channels. The maximum value is 127. It scales the loudness of the notes (velocity) by its value.
  • chnact โ€” returns the amount of note activity in each channel.
  • trkact โ€” returns the number of notes in each track.
  • totalrhythmpatterns โ€” is the total number of bar rhythm patterns for all channels except the percussion channel.
  • collisions โ€” Midistats counts the bar rhythm patterns using a hashing function. Presently collisions are ignored so occasionally two distinct rhythm patterns are counted as one. ๐Ÿ’€ (yeah, it's a known thing, not a vibe, it's real)

๐Ÿ’œ the channel arrays ๐Ÿ’œ

Midistats prints a number of arrays which may be useful in determining where the music in the track is a melody line or chordal rhythmic support. These arrays indicate the properties for each of the 16 channels. (The percussion channel 9 contains zeros.) In the case same channel occurs in several tracks, these numbers are the totals for all track containing that channel. Here is a description of these properties.

  • programs โ€” channel to midi program mapping
  • cnotes โ€” the total number of notes in each channel
  • nnotes โ€” the number of notes in each channel not including those playing in the same time interval.
  • nzeros โ€” the number of notes whose previous note was the same pitch
  • nsteps โ€” the number of notes whose pitch difference with the previous note was less than 4 semitones.
  • njumps โ€” the number of notes whose pitch difference with the previous note was 4 or more semitones.
  • rpats โ€” the number of rhythmpatterns for each channels. This is a duplication of data printed previously.
  • pavg โ€” the average pitch of all the notes for each channel.
  • spread โ€” the percentage of the track that each channel is active.
If some of the channels appear in more than one track, then some of the above values may be incorrect. ๐Ÿ˜ญ fr fr, watch out for that one.

๐Ÿซถ the extra vibes codes ๐Ÿซถ

In addition the midistats may return other codes that describe other characteristics. They include:

  • unquantized โ€” the note onsets are not quantized
  • triplets โ€” 3 notes played in the time of 2 notes are present
  • qnotes โ€” the rhythm is basically simple
  • clean_quantization โ€” the note onsets are quantized into 1/4, 1/8, 1/16 time units.
  • dithered_quantization โ€” small variations in the quantized note onsets.
  • Lyrics โ€” lyrics are present in the meta data
  • programcmd โ€” there may be multiple program changes in a midi channel
  • ntimesig โ€” the number of time signature commands

๐Ÿฅ Other options ๐Ÿฅ

okay so it is recommended that you only select one of the options described here, because the program was not designed to handle a multiple number of options. Like, one at a time bestie, don't be greedy. ๐Ÿ’…

๐Ÿ the -CSV era ๐Ÿ

If you run midistats with the -CSV option, it will return the results in a form of comma separated values that can be loaded into a Python panda dataframe. Each line refers to one of the 16 midi channels. The following Python 3 code illustrates how you would load the midistats output into a dataframe.

import pandas as pd
import io
import subprocess
cmd = ("midistats", "-CSV" , inputmidifilepath)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
csv =  io.StringIO(process.stdout.read().decode())
df = pd.read_csv(csv)

where inputmidifilepath is the path to the midi file that you are using. (eg. 'clean_midi/Zero/Chi sei.mid') โ€” yes that's the actual example, iconic. โœจ


๐ŸŒธ percussion track analysis, the origin story ๐ŸŒธ

The MIDI file devotes channel 9 to the percussion instruments and over 60 percussion instruments are defined in the MIDI standard. Though there is a lot of diversity in the percussion track, for most MIDI files only the first 10 or so percussion instruments are important in defining the character of the track. The program Midiexplorer has various tools for exposing the percussion channel which are described in the documentation. The goal here is to find the essential characteristics of the percussion track which distinguishes the MIDI files. This is attempted in the program midistats. Here is a short description. ๐Ÿซถ

A number of experimental tools for analyzing the percussion channel (track) were introduced into midistats and are accessible through the runtime arguments. When these tools are used in a script which runs through a collection of midi files, you can build a database of percussion descriptors. ๐Ÿ‘‘


  • -corestats โ€” spits out one line, five numbers, all tab-separated. Like so:
1    8    384    4057    375

And what are those five little icons bestie? In order: the number of tracks, the number of channels, the number of divisions per quarter note beat (that's ppqn if you're nasty), the number of note onsets in the midi file, and the maximum number of quarter note beats in the midi file. Five numbers, whole personality. ๐Ÿ’…

  • -pulseanalysis โ€” counts the number of note onsets as a function of its onset time relative to a beat, groups them into 12 intervals, and hands back the result as a discrete probability density function. Generally the distribution is gonna have a couple of peaks โ€” those are the quarter notes or the eighth notes showing up. But if the distribution is flat? That's the tell: the times of the note occurrences have not been quantized into beats and fractions. Here's a sample output, look at her:
0.349,0.000,0.000,0.160,0.000,0.000,0.298,0.000,0.000,0.191,0.000,0.000
  • -panal โ€” counts the number of note onsets for each percussion instrument. First number = the code (pitch) of the instrument, second number = the number of occurrences. eg.
35 337  37 16   38 432  39 208  40 231  42 1088 46 384  49 42   54 1104
57 5    70 1040 85 16
  • -ppatfor n โ€” okay so here n is the code number of the percussion instrument. Each beat gets represented by a 4 bit number, where the position of the on-bit tells you when in the beat the drum onset lands. The bits are ordered left to right (higher order bits down to lower order bits) โ€” which is literally the order of bits you'd expect in a time series, so it's giving sensible, fr fr. So: 0 means there was no note onset in that beat, 1 means a note onset at the end of the beat, 4 means a note onset in the middle of the beat, and etc. The function returns a string of numbers ranging from 0 to 7 showing the presence of note onsets for the percussion instrument you picked, for the sequence of beats in the midi file. Truncated sample output, slay:
11 71 c1 1 11 51 f1 c1 1 11 51 e1 1 11 d1 c1 31 1 1 1 21 b1 d1 d1 51
51 51 51 51 51 41 71 c1 11 1 11 41 11 1 11 41 11 1 11 41 11 31 51 41 11
  • -ppat โ€” here midistats attempts to find two percussion instruments in the midi file which come closest to acting as the bass drum and the snare drum. If it's unsuccessful it returns a message of its failue (yes, its little typo, we love her, no notes ๐Ÿ’–). Otherwise it encodes the position of these drum onsets in an 8 bit byte for each quarter note beat in the midi file. The lower (right) 4 bits encode the bass drum and the higher (left) 4 bits encode the snare drum, in the same manner as described above for -ppatfor. The integers are printed in hexadecimal.
02 88 20 02 a0 08 80 02 82 08 80 02 82 08 80 02 82 08 80 02 82 08
80 02 82 08 80 02 82 08 80 02 82 08 20 a0 0a 08 80 02 82 08 80 02
  • -ppathist โ€” computes and displays the histogram of the values that would appear when running the -ppat. eg.
bass 35 337
snare 38 432
1 (0.1) 64  32 (2.0) 8  33 (2.1) 136  144 (9.0) 8  145 (9.1) 136

The bass percussion code, the number of onsets, and the snare percussion code and the number of onsets are given in the first two lines. In the next line you get the number of occurrences of each value in the -ppat listing. The number in parentheses splits the two 4-bit values with a period. Thus 33 = (2*16 + 1). Math ate, honestly. ๐Ÿงฎ

  • -pitchclass โ€” returns the pitch class distribution for the entire midi file. That's it, that's the whole thing, and she's iconic for it. โœจ
  • -keystability โ€” segments the midi file into 8 beat units (probably 2 bars), and for each segment it uses Craig Sapp's algorithm to determine the key signature based on the distribution of the pitch classes. It returns the number of sharps (positive number) or the number of flats (negative number) for that key signature. For example, the key of G major or E minor would be represented by the value 1.

The list of sharp/flats is returned in the line

localkeysf  0 0 0 0 0 1 0 0 0 0 0 0 3 0 0 0 0 0 -1 -5 -5 -5 -5

For example, in the above sequence (ABBA/Money, Money, Money.7.mid) the key signature starts in A minor but switches to Bb minor in the last 8 bars. Character development!

โš ๏ธ Beware of enharmonic keys bestie. For example, B Major (5#) and Cb Major (7b) contain the same notes. Similarly F# Major (6#) and Gb Major (6b), and C# Major (7#) and Db Major (5b), and all the minor key equivalents. Same vibe, different spelling. ๐Ÿ’€
  • -nseqfor n โ€” note sequence for channel n. This option produces a string of bytes indicating the presence of a note in a time unit corresponding to an eighth note. So each quarter note beat is represented by two bytes. The pitch class is represented by the line number on the staff, where 0 is C. So the notes on a scale are represented by 7 numbers, and sharps and flats are ignored. That line number then gets converted to a bit position in the byte, so the pitch classes are represented by the numbers 1,2,4,8, and etc. A chord consisting of two note onsets would set two of the corresponding bits. If we were to represent the full chromatic scale consisting of 12 pitches, then we would require two-byte integers or twice as much memory. Though the pitch resolution is not sufficient to distinguish major or minor chords, it should be sufficient to be identify some repeating patterns. Lowkey lossy, highkey useful. ๐ŸŽ€
  • -nseq โ€” same as above except it is applied to all channels except the percussion channel. Group chat mode. ๐Ÿซถ
  • -nseqtokens โ€” returns the number of distinct sequence elements for each channel. The channel number and number of distinct elements separated by a comma is returned in a tab separated list for all active channels except the percussion channel. Here is an example.
2,3  3,4  4,11 5,6  6,3  7,3  8,6  9,3  11,2 12,1
  • -ver (version number)

๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

Seymour Shlien <fy733@ncf.ca> โ€” the actual main character, the reason we're all here, no cap. ๐Ÿซก๐Ÿ’–


And that's the whole page bestie, we did it!! ๐ŸŽ‰ This was MIDISTATS(1), dated 22 February 2026 โ€” so if you're reading this, the girlies are still shipping it. Go count some onsets, you absolute legend. periodt. ๐Ÿ’…โœจ๐Ÿฆ‹