mirror of
https://github.com/sshlien/abcmidi.git
synced 2025-12-06 06:55:06 +00:00
228 lines
10 KiB
Plaintext
228 lines
10 KiB
Plaintext
Notes on the midi2abc code
|
|
--------------------------
|
|
written by Seymour Shlien
|
|
|
|
|
|
midi2abc.txt - last updated December 5 1999
|
|
|
|
|
|
Introduction
|
|
|
|
Midi2abc is a program for converting a midi files into an abc file.
|
|
|
|
A midi file merely consists of a list of commands to turn on
|
|
and off a set of voices at specific times. The time units are
|
|
expressed in pulses where the number of pulses per second can
|
|
be deduced from the information in the midi file. Pitch is
|
|
specified in semitone units ranging from 0 to 127 where middle C
|
|
is assigned a value of 60.
|
|
|
|
There are two types of midi in general use. In type 0, all the
|
|
voices are interleaved in time in one track. Each voice goes
|
|
to a specific channel which is mapped to a particular musical
|
|
instrument or program. There is a limit of 16 voices since only
|
|
4 bits are assigned to indicate the channel number. In type 2,
|
|
the voices are placed in separate tracks. This format allows
|
|
handling more than 16 voices.
|
|
|
|
In order to create an abc file, midi2abc must determine the
|
|
length of a musical unit (for example an eighth note) in terms
|
|
of midi pulses, determine the key signature, and, finally map the
|
|
midi commands into musical notes.
|
|
|
|
Though the midi file specifies the number of pulses per quarter note
|
|
(PPQN) in the Mthd header chunk, this is not necessarily an
|
|
accurate indication. It is primarily used together with the tempo
|
|
indication to determine the number of pulses per second. Unless
|
|
the midi file was created by a computer program, the actual length
|
|
of a quarter note in midi pulses can depart from the nominal indication.
|
|
Midi2abc tries to determine the length using its own heuristic
|
|
algorithm.
|
|
|
|
The sources of the program are contained in two files: midi2abc.c and
|
|
midifile.c. This document does not try to describe completely how
|
|
this program works, but is more of a general road map to the software.
|
|
|
|
|
|
Algorithmic Description
|
|
|
|
The conversion is done in multiple passes. In the first pass, the
|
|
midi file is read and an internal representation of the file is
|
|
created and stored in global dynamic memory. In each subsequent pass,
|
|
other information is added to the internal representation and finally
|
|
it is written to the output abc file.
|
|
|
|
After parsing the input run parameters and performing various
|
|
initializations, the main program calls the procedure mfread
|
|
which is found in the midifile.c module. Mfread parses the different
|
|
midi components of the file and calls the appropriate functions
|
|
midi2abc to process these components. These functions begin with
|
|
the txt_ prefix, some of which are listed here.
|
|
|
|
txt_header midi header chunk
|
|
txt_trackstart start of midi track
|
|
txt_trackend end of midi track
|
|
txt_noteon note on channel command
|
|
txt_noteoff note off channel command
|
|
txt_program midi program definition or change
|
|
txt_metatext midi textual information
|
|
txt_keysig midi key signature indication
|
|
txt_tempo midi tempo indication
|
|
|
|
Many other functions such as txt_pressure and txt_parameter
|
|
corresponding to other midi commands, do nothing here.
|
|
|
|
These functions build up an internal representation of the midi file
|
|
in a global structure referenced through the track[64] structure.
|
|
Up to 64 midi tracks can be stored in the internal representation.
|
|
Each track structure (atrack structure) contains single and double
|
|
linked lists of notes or text strings which are described below.
|
|
In addition there are other linked lists (referenced by playinghead
|
|
and chordhead), used for keeping track of notes currently playing
|
|
and musical chords.
|
|
|
|
Each note is represented by an anote structure which stores various
|
|
parameters of the note. The anote structure is allocated dynamically
|
|
as the midi file is read. The first four entries of the structure
|
|
are filled in while the structures are being created by mfread.
|
|
These are entries are listed below.
|
|
|
|
anote.pitch pitch in midi units
|
|
anote.chan midi channel number
|
|
anote.vel midi velocity (corresponding to loudness)
|
|
anote.time time in midi pulses.
|
|
|
|
The other entries in the anote structure are determined in later passes.
|
|
The anote structures are linked together into a a listx structure which
|
|
is included in the atrack structure. The head and tail of the list are
|
|
contained in this structure to facilitate the construction of this list.
|
|
In addition there is tlistx structure for storing all the textual
|
|
information (for example words in a Karaoke midi file) which may be found
|
|
in the track.
|
|
|
|
There is a doubly linked list structure of anotes (dlistx,
|
|
*playinghead) which is used as a work space for matching the note off
|
|
command with the corresponding note on command. This is needed in order
|
|
to determine the length of time the note was turned on (called anote.tplay).
|
|
|
|
The internal representation is mainly constructed by the important
|
|
functions txt_noteon and txt_noteoff called by mfread. These functions
|
|
in turn call the functions addnote and notestop. The midi file contains
|
|
separate commands for turning a note on or off so that in order to
|
|
determine the length of time that a note has been on, it is necessary to
|
|
match a note-off command with the corresponding note-on command.
|
|
Every time a note is turned on, it is also added to the playhead (tail)
|
|
structure. The procedure notestop finds the corresponding note-on
|
|
command in the playhead structure, removes it, and records the
|
|
duration of the note which was turned on.
|
|
|
|
At the end of the first pass, the number of tracks is counted and each
|
|
track is processed by the function postprocess which computes the entry
|
|
anote.dtnext for each anote structure. This entry contains the time
|
|
interval between the current and the next note in the linked list of
|
|
notes.
|
|
|
|
The abc time unit length is either 1/8 or 1/16 depending on the time
|
|
signature. A time signature of 4/4 is assumed unless it is specified
|
|
by the run time parameters (-m or -xm). (If -xm is specified, then the
|
|
program uses the time signature given by the midi meta command if it
|
|
exists in the file.)
|
|
|
|
The next step involves the quantization of the midi time units
|
|
expressed in pulse counts into abc time units. It is first necessary
|
|
to estimate the length of an abc time unit in terms of midi time
|
|
units. This either is estimated using a heuristic algorithm,
|
|
guesslength, or is determined from the run time parameters (-Q or -b).
|
|
|
|
The function guesslength makes an initial guess by dividing the
|
|
total number of pulse counts in the track by the total number
|
|
of notes. It then tries 100 different lengths in the neighbourhood
|
|
of this initial guess and chooses the one which leads to the smallest
|
|
quantization error. The quantization error is determined by the function
|
|
quantizer which keeps track of the total deviation of the time
|
|
a note starts (in pulse counts) from the expected time the note
|
|
starts assuming the standard musical intervals. This deviation
|
|
can either drift positively or negatively with time. The total
|
|
error is determined by summing the absolute values of these
|
|
deviations for each bar.
|
|
|
|
Once the unit length has been determined, all the notes in all
|
|
tracks are quantized by the function quantizer. This function
|
|
assigns values to the anote entries, anote.xnum, anote.playnum
|
|
and anote.denom.
|
|
|
|
anote.xnum interval between the current note and following note
|
|
also called the gap.
|
|
anote.playnum note duration.
|
|
anote.denom always has the value of 2.
|
|
|
|
A musical part does not necessarily begin at the start of a bar line,
|
|
but may have some leading notes. This is called anacrusis.
|
|
There are two methods to estimate the anacrusis. The function findana
|
|
searches for the first upbeat by examining the velocities (loudness) of
|
|
the notes. The function guessana, chooses the anacrusis which minimizes
|
|
the number of tied notes across a bar line which is determined by the
|
|
function testtrack.
|
|
|
|
At this point the key signature of the tune must be determined.
|
|
The procedure findkey computes the frequency distribution of all the
|
|
notes in the piece and stores it in the local array n[]. The key
|
|
signature is determined by transposing the distribution by each
|
|
of the 12 keys and counting the number of sharp or flat notes. The
|
|
key signature is determined from the key which leads to the minimum
|
|
number of black keys on the piano. The mode of the scale (major,
|
|
minor, Dorian, etc.) is determined by looking at the final note of
|
|
the piece.
|
|
|
|
Once the key signature is determined, the assumed flats or sharps
|
|
are determined by the procedure setupkey. The program is now ready
|
|
for its final pass where the musical parts (or voices) are printed
|
|
in the output abc file.
|
|
|
|
The procedure printtrack processes the internal representation
|
|
of each midi track producing a separate voice for each track.
|
|
In order to handle chords which may be present in an individual
|
|
track, printtrack maintains a structure referenced by chordhead
|
|
by calling the support functions addchord(), advancechord(),
|
|
removechord() and printchord(). These functions handle single
|
|
notes as well as chords. Another function, dospecial(), handles
|
|
the triplets and broken rhythms (eg. dotted notes followed by
|
|
half sized note or vice versa) whenever they are detected. The
|
|
printchord() function is supported by the functions printfraction()
|
|
and printpitch().
|
|
|
|
After the track is printed, the memory allocated to the structures
|
|
for the internal representation of the tracks is freed.
|
|
|
|
The option -splitvoice was introduced to handle polyphonic chords.
|
|
Without this option polyphonic chords appear in the abc file
|
|
like this.
|
|
|
|
[DF-A-][FA-]Az|
|
|
|
|
this will be represented by three voices
|
|
|
|
V: split1A
|
|
D2 z6|
|
|
V: split1B
|
|
F4 z4|
|
|
V: split1C
|
|
A6 z2|
|
|
|
|
This option is implemented by the function printtrack_split_voice().
|
|
The function label_split_voices() is called to assign all the notes
|
|
in the track to their split voice. This assignment of each note is
|
|
stored in note->splitnum. This is a complex function since it needs
|
|
to try to match the onset and end of each note in the chord. If
|
|
it is unable to do this for a particular note, then the note is
|
|
assigned to a different split. At the end, nsplits were created.
|
|
The notes in each split are printed in a separate voice by the
|
|
function printtrack_split(). The function printtrack_split(splitnumber)
|
|
was derived from printtrack(); however, it only processes the
|
|
notes belonging to a particular splitnumber.
|
|
|
|
It was necessary to determine and store the offset of the first
|
|
note in each split using the function set_first_gaps() prior
|
|
to calling printtrack_split().
|
|
|