mirror of
https://github.com/sshlien/abcmidi.git
synced 2025-12-06 06:55:06 +00:00
427 lines
13 KiB
Plaintext
427 lines
13 KiB
Plaintext
Notes on the code
|
|
-----------------
|
|
|
|
These notes are for anyone who wants to re-compile, re-write or re-use
|
|
bits of the code. Additional information is available by downloading
|
|
the file abcextra.zip. This includes :
|
|
|
|
* man pages for abc2midi and midi2abc.
|
|
* A detailed description of the inner workings of abc2midi, written by
|
|
Seymour Shlien.
|
|
|
|
Compilation
|
|
-----------
|
|
|
|
The file midifile.c and the header file midifile.h are slightly changed from
|
|
the midifilelib distribution. To see the program dependencies, examine the
|
|
Makefile.
|
|
|
|
The code is written in K&R style C and should be fairly easy to compile
|
|
on any system with a C compiler and a make utility. Makefiles are
|
|
provided for gcc/unix, DJGPP/DOS and PCC/DOS. There are also some notes
|
|
on using the GUI front-end to Pacific C/DOS. You may get warning
|
|
messages if your compiler prefers ANSI C style function prototypes.
|
|
|
|
Choose the most suitable makefile; unix.mak, djgpp.mak or pcc.mak and
|
|
rename it as 'makefile'. If you are not using any of the above compilers,
|
|
you may have to edit the makefile so that it uses compilation flags
|
|
suitable for your compiler.
|
|
|
|
To compile the code, type
|
|
|
|
make all
|
|
|
|
If the complete compilation does not work, you can try compiling the
|
|
individual programs separately :
|
|
|
|
make midi2abc (or make midi2abc.exe)
|
|
make abc2midi (or make abc2midi.exe)
|
|
make abc2abc (or make abc2abc.exe)
|
|
make mftext (or make mftext.exe)
|
|
make yaps (or make yaps.exe)
|
|
|
|
Note that the make utility on some systems (e.g. GNU make) may require the
|
|
Makefile to be a unix text file and not a DOS text file. The difference
|
|
is that unix uses newline to mark the end of each line while DOS uses
|
|
carriage return and newline.
|
|
|
|
Note, if you have difficulty compiling the package because you do not have
|
|
snprintf see the note in doc/CHANGES dated January 08 2005 (and also
|
|
December 17 2004).
|
|
|
|
|
|
---------------------------------------------------------------------
|
|
Calling abc2midi or midi2abc from a GUI.
|
|
----------------------------------------
|
|
|
|
The programs should now have an exit value of 0 for normal termination
|
|
and 1 for an error state. However, abc2midi will still exit with 0 if
|
|
it finds non-fatal errors in the code, so the user should always have
|
|
the option of looking at the program's text output (I don't want to
|
|
get blamed when useful diagnostic output turns into the ubiquitous
|
|
'OK' click-button).
|
|
|
|
----------------------------------------------------------------------
|
|
Man pages
|
|
---------
|
|
Files: abc2midi.1 and midi2abc.1
|
|
|
|
Christoph Dalitz has written some man pages for abc2midi and
|
|
midi2abc. These should be installed in the sub-directory /man1
|
|
of the directory given by the MANPATH environment variable.
|
|
(The man command is usually only found on Unix variants).
|
|
|
|
The source distribution has been re-organized to contain only the
|
|
source and a few text files. If you want these man pages, you need
|
|
to download the file abcextra.zip.
|
|
|
|
---------------------------------------------------------------------
|
|
Code Layout and Indentation style
|
|
---------------------------------
|
|
If you want to add your own code and make it fit in with the existing
|
|
coding style, you can use GNU indent. The following is a DOS batch file
|
|
to invoke indent with the appropriate options.
|
|
|
|
indent -bad -bap -br -ce -cli0 -npcs -di1 -nbc -i2 -ts0 -psl -lp -ipo %1
|
|
rem
|
|
rem options for GNU indent to achieve my personal style
|
|
rem
|
|
rem -bad blank line after declaration block
|
|
rem -bap blank line after procedure body
|
|
rem -br brace on same line after if, struct and enum
|
|
rem -ce cuddle else
|
|
rem -cli0 case at same identation as switch
|
|
rem -npcs no space between procedure name and following open bracket
|
|
rem -di1 one space between variable type and variable name
|
|
rem -nbc comma-separated variables on the same line
|
|
rem -i2 indent 2 spaces
|
|
rem -ts0 no tabs
|
|
rem -npsl function type on same line as function name
|
|
rem -lp continuations matched to left parenthesis
|
|
rem -ip0 no indention of variables in K&R function headers
|
|
rem -ncs no space after cast
|
|
|
|
---------------------------------------------------------------------
|
|
|
|
Extensions to the abc Format
|
|
----------------------------
|
|
|
|
1. The parser recognizes
|
|
%%package <string>
|
|
as some package-specific command and calls event_specific with the
|
|
package name and the string that follows it.
|
|
|
|
2. The abc standard defines notation for 4 octaves :
|
|
|
|
C, - B,
|
|
C - B
|
|
c - b
|
|
c' - b'
|
|
|
|
The parser recognizes each additional comma as meaning "going down
|
|
an extra octave", giving
|
|
|
|
C,, - B,,
|
|
C,,, - B,,,
|
|
and so on.
|
|
|
|
Likewise, each additional prime symbols s interpreted as "go up an extra
|
|
octave" :
|
|
|
|
c'' - b''
|
|
c''' - b'''
|
|
and so on.
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
abc2midi
|
|
--------
|
|
abc2midi consists of the following C source files:
|
|
|
|
parseabc.c - parses the input text and calls a routine for
|
|
every parsed element encountered.
|
|
parser2.c - performs some additional parsing that may not be
|
|
required.
|
|
store.c - builds an internal representation of the abc tune.
|
|
genmidi.c - uses the internal representation to generate
|
|
the MIDI file, using calls to MIDI-specific routines
|
|
in midifile.c
|
|
queues.c - library of routines for handling 'queues', a data
|
|
structure used internally by genmidi.c
|
|
midifile.c - Thompson and Czeisperger's public domain library of
|
|
MIDI file manipulation routines.
|
|
|
|
In the first phase of parsing, the abc file is read and when, say, a note
|
|
is encountered, the routine event_note() is called. The code for event_note
|
|
is in the file store.c. Encountering an X in the abc generally causes a
|
|
routine called event_X() to be called. abc2midi builds up an internal
|
|
representation of the tune. At the end of the abc tune, a little bit of
|
|
processing is done on the internal representation before the routine
|
|
writetrack() is called to actually write out the MIDI file. If there are
|
|
repeats in the music, something that appears only once in the abc may be
|
|
invoked twice by writetrack().
|
|
|
|
The internal representation uses the arrays feature[], pitch[], num[],
|
|
and denom[]. feature[] holds a description of an object while the other
|
|
arrays hold data relating to the object. The list of possible features
|
|
can be found in the file abc.h . The main features are NOTE, a note of
|
|
specified duration, REST, a pause of specified duration and TNOTE, a
|
|
note of specified duration with no interval between when it starts and
|
|
when the next item starts. This provides a simple way of representing
|
|
chords. Ties, broken rhythm signs and grace note brackets are all
|
|
deal with before writetrack() is called.
|
|
|
|
To add your own special features, you could define a new feature type.
|
|
However, an easier option is to use the %%MIDI format. If the parser
|
|
encounters "%%MIDI command", where command is not recognized by the
|
|
routine event_specific(), the string following %%MIDI is stored away
|
|
and passed to the routine dodeferred() by writetrack() when the MIDI
|
|
file is being written.
|
|
----------------------------------------------------------------------
|
|
abc2abc
|
|
-------
|
|
abc2abc shares the parser with abc2midi, but instead of storing the
|
|
abc code in an internal format, it is written almost straight out
|
|
again. The components of abc2abc are:
|
|
|
|
parseabc.c - parser
|
|
toabc.c - generates output abc.
|
|
midifile.c - public domain MIDI library.
|
|
|
|
----------------------------------------------------------------------
|
|
YAPS
|
|
----
|
|
|
|
YAPS is written mainly using ANSI C style function headers and
|
|
compiled and tested using DJGPP (DOS/Windows port of gcc).
|
|
|
|
The code is composed of the following files:
|
|
|
|
parseabc.c - reads through the abc text file. When it recognizes an abc
|
|
"unit" it calls a routine such as event_note() or event_key().
|
|
|
|
yapstree.c - creates a data structure to represent a tune.
|
|
Generally, I have tried to use as small a number of passes through the
|
|
data structure as possible. This means that things like applying a
|
|
broken rhythm symbol > is done as the notes are read in, rather than
|
|
using a second pass. This results in a lot of variables being needed in
|
|
'struct voice' to keep track of what is going on as we read in the
|
|
current symbols. Different sets of these variables are used on different
|
|
passes through the data.
|
|
|
|
drawtune.c - responsible for creating the PostScript file. When a whole
|
|
tune has been read in, this calculates the size of each individual
|
|
element, works out spacing within each line, decides how to place the beam
|
|
for a group of beamed notes, then finally generates the PostScript for
|
|
the tune.
|
|
|
|
position.c - called by drawtune.c. This set of routines is responsible
|
|
for spacing each line correctly. Where the input abc is multi-voice,
|
|
elements played at the same time but in different voices are aligned.
|
|
|
|
pslib.c - a single routine to print out the entire library of PostScript
|
|
functions used by yaps.
|
|
|
|
debug.c - routines to print to screen the contents of a tune data
|
|
structure.
|
|
|
|
abc.h - header file defining abc element types.
|
|
|
|
structs.h - header file defining the data structures used by the program.
|
|
The voice structure holds a lot of cuurent context information, used by
|
|
the program as it does a pass through the voice.
|
|
|
|
sizes.h - header file containing macros to define sizes in points for all
|
|
the graphic elements.
|
|
|
|
Dynamic Memory Management
|
|
-------------------------
|
|
abc2midi uses a system of re-sizable arrays in order to handle arbitrary
|
|
size input. This scheme was a natural way to adapt the original fixed size
|
|
arrays, and is not as flexible as it might be. Yaps instead uses linked
|
|
lists. There is a tune data structure containing a linked list of voices
|
|
and the notes and other elements are stored in a linked list in each tune.
|
|
Some music elements contain their own linked lists; for example a note
|
|
may have a linked list of lyric syllables. Adding a new data item to an
|
|
element (e.g. a note) involves the following :
|
|
|
|
1. find 'struct note' in structs.h and add the element.
|
|
2. Initialize it in newnote().
|
|
3. If it is a pointer to a new data structure, make sure the new data
|
|
structure is de-allocated in freevoice().
|
|
|
|
----------------------------------------------------------------------
|
|
The abc parser
|
|
--------------
|
|
The parser (parseabc.c) has been written in such a way that it forms
|
|
an independent unit which must be linked with routines to handle
|
|
parsed units. This is very similar to the way that the
|
|
midifilelib utilities work.
|
|
|
|
The abc is parsed line by line. Each line may be
|
|
|
|
* A comment
|
|
* A package-specific command
|
|
* A field (which may have a trailing comment)
|
|
* A blank line
|
|
* A TeX command
|
|
|
|
Having detected one of these, the parser calls an appropriate
|
|
routine. If it is none of these, then within the tune body it is
|
|
|
|
* A line of music (which may have a trailing comment).
|
|
|
|
Which is parsed and individual elements recognized. Outside the tune
|
|
body it is
|
|
|
|
* A line of arbitrary text.
|
|
|
|
and an appropriate routine is called.
|
|
|
|
Routines called by the parser
|
|
-----------------------------
|
|
These may be a bit out of date - look in the file parseabc.c for the actual
|
|
interfaces.
|
|
|
|
event_init(argc, argv, filename)
|
|
int argc;
|
|
char* argv[];
|
|
char** filename;
|
|
- first routine called by the parser. Expects filename to be the name of the
|
|
abc file to parse on return.
|
|
|
|
event_text(s)
|
|
char *s;
|
|
- called whenever a line of text is encountered.
|
|
|
|
event_tex(s)
|
|
char *s;
|
|
- called whenever a TeX command is encountered.
|
|
|
|
event_linebreak()
|
|
- called whenever a newline character is encountered.
|
|
|
|
event_blankline()
|
|
- called whenever a blank line is encountered.
|
|
|
|
event_eof()
|
|
- called when the end of file is reached.
|
|
|
|
event_error(s)
|
|
char *s;
|
|
- called whenever an error condition is detected. Errors are
|
|
generally not fatal within the parser.
|
|
|
|
event_warning(s)
|
|
char *s;
|
|
- called whenever a condition which is likely to be an error is
|
|
detected.
|
|
|
|
event_comment(s)
|
|
char *s;
|
|
- called whenever a comment is encountered.
|
|
|
|
The following are calls are invoked by fields in the abc :
|
|
|
|
event_specific(package, s)
|
|
char *package, *s;
|
|
- recognizes %%package s
|
|
|
|
event_length(n)
|
|
int n;
|
|
- recognizes L:
|
|
|
|
event_refno(n)
|
|
int n;
|
|
- recognizes X:
|
|
|
|
event_tempo(n, a, b, rel)
|
|
int n, a, b;
|
|
int relative;
|
|
- recognizes Q:
|
|
|
|
event_timesig(n, m)
|
|
int n, m;
|
|
- recognizes M:
|
|
|
|
event_key(sharps, s, minor)
|
|
int sharps;
|
|
char *s;
|
|
int minor;
|
|
- recognizes K:
|
|
|
|
event_part(s)
|
|
char* s;
|
|
- recognizes P:
|
|
|
|
When any other field is encountered in the abc :
|
|
|
|
event_field(k, f)
|
|
char k;
|
|
char *f;
|
|
|
|
If a line of music is encountered, the elements of that line each
|
|
trigger a call to one of the following events, provided that parsing
|
|
of music lines has been enabled :
|
|
|
|
event_graceon()
|
|
|
|
event_graceoff()
|
|
|
|
event_rep1()
|
|
|
|
event_rep2()
|
|
|
|
event_slur(t)
|
|
int t;
|
|
|
|
event_tie()
|
|
|
|
event_rest(n,m)
|
|
int n, m;
|
|
|
|
event_bar(type)
|
|
int type;
|
|
|
|
event_space()
|
|
|
|
event_lineend(ch, n)
|
|
char ch;
|
|
int n;
|
|
|
|
event_broken(type, mult)
|
|
int type, n;
|
|
|
|
event_tuple(p, q, r)
|
|
int p, q, r;
|
|
- general tuple: q and r are zero if not present
|
|
|
|
event_chord()
|
|
- called whenever + is encountered.
|
|
|
|
event_chordon()
|
|
- called whenever [ is encountered.
|
|
|
|
event_chordoff()
|
|
- called whenever ] is encountered.
|
|
|
|
event_gchord(s)
|
|
char* s;
|
|
|
|
event_reserved(p)
|
|
char p;
|
|
|
|
event_note(roll, staccato, updown, accidental, mult, note, octave, n, m)
|
|
int roll, staccato, mult;
|
|
char updown, accidental, note;
|
|
int octave, n, m;
|
|
|
|
In addition, there are 2 other routines :
|
|
|
|
int getline() - returns the line currently being parsed
|
|
|
|
parseron() - enable parsing of music lines.
|
|
|
|
parseroff() - disable parsing of music lines.
|
|
|