mirror of
https://github.com/sshlien/abcmidi.git
synced 2025-12-06 06:55:06 +00:00
abcMIDI-2020.07.06.zip
This commit is contained in:
420
doc/programming/abc2midi.txt
Normal file
420
doc/programming/abc2midi.txt
Normal file
@@ -0,0 +1,420 @@
|
||||
Some Notes on the abc2midi Code
|
||||
-------------------------------
|
||||
written by Seymour Shlien
|
||||
|
||||
|
||||
Abc2midi.txt - last updated 29 November 1999.
|
||||
|
||||
|
||||
This file provides an algorithmic description of the program
|
||||
abc2midi which converts an abc file into a midi file.
|
||||
|
||||
The sources of abc2midi now comprising of 6700 lines of C code are
|
||||
contained in the following five files.
|
||||
|
||||
parseabc.c is the front end which scans the abc file and invokes
|
||||
the appropriate event handler for each element it encounters
|
||||
(eg. bar lines, notes, chords etc.) It happens to be the
|
||||
front end for other programs such as abc2ps, yaps and
|
||||
abc2abc.
|
||||
store.c contains all the event handlers which are invoked from
|
||||
parseabc. It converts the abc file into an internal
|
||||
representation described later in this file.
|
||||
genmidi.c converts this internal representation in an output midi
|
||||
file.
|
||||
queues.c are some utilities needed by genmidi.
|
||||
midifile.c is a library of public domain routines to read and write
|
||||
midifiles.
|
||||
|
||||
In order to handle the multiple voices, parts, repeats and accompaniments
|
||||
which occur in abc files, the program performs multiple passes. The
|
||||
midi file stores the different voices, accompaniment and lyrics in
|
||||
individual tracks, so it is necessary to separates these parts prior
|
||||
to writing it to the midi file. If there are repeats in the music,
|
||||
something which appears only once in the abc file may be invoked twice
|
||||
when creating the output midi file.
|
||||
|
||||
Parseabc.c
|
||||
----------
|
||||
|
||||
The parser has been written so that it should be possible to write
|
||||
your own utility to handle the interpreted abc and link it with
|
||||
the parser code. This is very similar to the way that the midifilelib
|
||||
utilities work. The parser is designed so that it can write out
|
||||
almost exactly what it reads in. This means that in some cases
|
||||
the level of parsing is fairly primitive and it may be necessary to make
|
||||
use of routines in store.c which perform further processing on an item.
|
||||
|
||||
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. An internal representation of the tune
|
||||
is built up and when the end of an abc tune is reached, a little bit of
|
||||
processing is done on the internal representation before the routine
|
||||
mywritetrack() is called to actually write out the MIDI file.
|
||||
|
||||
The main program to abc2midi is contained in this file. Since this
|
||||
main program is used to start other programs such as yaps, it is
|
||||
fairly simple. It calls event_init defined in store.c which obtains
|
||||
all the user parameters (eg. input filename) and then executes the
|
||||
top level procedure, parsefile.
|
||||
|
||||
The procedure parsefile (filename), opens the specified file (or stdin)
|
||||
and processes it one character at a time. The characters fill a line
|
||||
buffer which is passed to the procedure parseline whenever a linebreak
|
||||
character (eg. linefeed) is encountered. By doing it in this fashion,
|
||||
abc2midi is able to eliminate the extra carriage return which occurs
|
||||
in DOS files.
|
||||
|
||||
The procedure parseline first checks for blank lines and comments. A
|
||||
blank line signals the end of the tune and the event_blankline is called
|
||||
to initiate the next stage in processing. If it is neither a comment or
|
||||
blank line, the procedure must decide whether the line is a a field line
|
||||
(eg."X:2") or part of a music body (eg. "|:C>DGz|..."). This is decided
|
||||
using the following rule. If the first letter begins with one of the field
|
||||
letter commands and is immediately followed by a ":", then it is treated
|
||||
as a field line and parsefield is called. Otherwise if a K: field line
|
||||
was already encountered (variable inbody set to 1), then it is treated
|
||||
as a line of music. If neither case is satisfied, then the line is
|
||||
intepreted as plain text.
|
||||
|
||||
The procedure parsefield identifies the particular field line type
|
||||
and invokes the appropriate support function or event handler. If the
|
||||
field command occurs after the first K: line, then only certain field
|
||||
lines are legal. (For example it is illegal to place a T: (title) line
|
||||
after the K: (key signature) line.)
|
||||
|
||||
The procedure parsemusic is the most complex procedure in the file and
|
||||
recognizes the musical notes, chords, guitar chord names, bar lines,
|
||||
repeats, slurs, ties, graces etc. Appropriate event handlers and
|
||||
support functions are called to complete the tasks of parsing the
|
||||
information. The parsenote function, for example, must check for
|
||||
decorations, accidentals, octave shifts as well as identifying the
|
||||
note and its determining its duration.
|
||||
|
||||
Unlike the other software modules, parseabc.c contains few global
|
||||
variables. The global variables linenum, inhead, inbody, parsing,
|
||||
slur, parserinchord indicate which lexical component of the abc
|
||||
tune is being processed.
|
||||
|
||||
|
||||
|
||||
|
||||
store.c
|
||||
-------
|
||||
|
||||
This is the most complex module consisting of almost 3000 lines
|
||||
of source code. This module contains all of the event handlers which
|
||||
may be invoked in the file parseabc.c. The main purpose of these
|
||||
handlers are to build up an internal representation of the abc file
|
||||
in the global memory arrays. This is a complete representation
|
||||
which allows the regeneration of the abc file (eg. abc2abc) or
|
||||
the creation of a midi file. The internal representation is used
|
||||
by the genmidi.c software to create the midi file.
|
||||
|
||||
|
||||
Global variables
|
||||
|
||||
The internal representation is stored in global variables defined
|
||||
at the beginning of the file. A description of the most important
|
||||
variables is given here.
|
||||
|
||||
The music is stored in the four arrays, feature, pitch, num, denom.
|
||||
These arrays contains a list of the lexical components in the order
|
||||
that they have been encountered or created.
|
||||
|
||||
The array "feature" identifies the type of component which can be one
|
||||
of the following enum features.
|
||||
|
||||
SINGLE_BAR, DOUBLE_BAR, BAR_REP, REP_BAR, REP1, REP2, BAR1,
|
||||
REP_BAR2, DOUBLE_REP, THICK_THIN, THIN_THICK, PART, TEMPO,
|
||||
TIME, KEY, REST, TUPLE, NOTE, NONOTE, OLDTIE, TEXT, SLUR_ON,
|
||||
SLUR_OFF, TIE, TITLE, CHANNEL, TRANSPOSE, RTRANSPOSE, GRACEON,
|
||||
GRACEOFF, SETGRACE, SETC, GCHORD, GCHORDON, GCHORDOFF, VOICE,
|
||||
CHORDON, CHORDOFF, SLUR_TIE, TNOTE, LT, GT, DYNAMIC, LINENUM,
|
||||
MUSICLINE, MUSICSTOP, WORDLINE, WORDSTOP
|
||||
|
||||
The array pitch[] mainly stores the pitch of a musical note,
|
||||
represented in the same manner as in a midi file. Thus middle
|
||||
C has a value of 60. The array has other uses, for example
|
||||
in a LINENUM feature it stores the line number relative to
|
||||
the X: reference command where the lexical feature has been
|
||||
detected. The LINENUM feature is useful when reporting warnings
|
||||
or errors in the input file. Duration of the notes are represented
|
||||
as a fraction, where the standard unit is defined by the L:
|
||||
command. If feature[n] is a NOTE, then num[n] and denom[n]
|
||||
contain the numerator and denominator of this fraction.
|
||||
|
||||
Here is an example of the contents of these arrays for the
|
||||
following small file in the same order that they are stored in
|
||||
these arrays.
|
||||
|
||||
X: 1
|
||||
T: sample
|
||||
M: 2/4
|
||||
L: 1/8
|
||||
K: G
|
||||
|: C>D[EF]G |C4:|
|
||||
|
||||
|
||||
Feature Pitch Num Denom
|
||||
LINENUM 2 0 0
|
||||
TITLE 0 0 0
|
||||
LINENUM 3 0 0
|
||||
LINENUM 4 0 0
|
||||
LINENUM 5 0 0
|
||||
DOUBLE_BAR 0 0 0
|
||||
LINENUM 6 0 0
|
||||
MUSICLINE 0 0 0
|
||||
BAR_REP 0 0 0
|
||||
NOTE 60 2 3
|
||||
NOTE 62 1 3
|
||||
CHORDON 0 0 0
|
||||
NOTE 64 1 2
|
||||
NOTE 66 1 2
|
||||
CHORDOFF 0 1 2
|
||||
NOTE 67 1 2
|
||||
SINGLE_BAR 0 0 0
|
||||
NOTE 60 2 1
|
||||
REP_BAR 0 0 0
|
||||
MUSIC_STOP 0 0 0
|
||||
LINENUM 7 0 0
|
||||
|
||||
|
||||
In order to support multivoice abc files in the form
|
||||
V:1
|
||||
| ...| ...|....
|
||||
V:2
|
||||
| ...| ...|....
|
||||
%
|
||||
V:1
|
||||
| ...| ...|....
|
||||
V:2
|
||||
| ...| ...|....
|
||||
etc.
|
||||
|
||||
abc2midi maintains a voicecontext structure for each voice.
|
||||
This allows each voice to define its own key signature, default
|
||||
note length using internal field commands. There is a head
|
||||
voicecontext which is used when no voice field commands are
|
||||
defined in the abc file. The v[] array maintains a set of
|
||||
all voices active in the abc file and voicecount keeps track
|
||||
of the number of voices.
|
||||
|
||||
Similarly, abcmidi maintains a list of all the part stored
|
||||
in the feature[], pitch[], num[] and denom[] arrays.
|
||||
|
||||
All text items (eg. title and some other fields) are stored
|
||||
in char atext[][] arrays, so that they can be included in
|
||||
the midi output file. The textual information is repeated
|
||||
in each track of the output midi file.
|
||||
|
||||
Following the conventions in the midi file, the program uses
|
||||
the quarter note as the standard unit of time instead of the
|
||||
whole note. In contrast, the standard length in the abc file
|
||||
is based on the whole note. For example in L:1/8, the fraction
|
||||
refers to a whole note. In order to convert time units to
|
||||
quarter notes, the numerator of the time specifications
|
||||
for note lengths is multiplied by 4 when it is added to
|
||||
the feature list.
|
||||
|
||||
Table of contents of procedures in store.c
|
||||
|
||||
getarg(option, argc, argv) - gets run time arguments.
|
||||
newvoice(n) - creates a new voice context.
|
||||
getvoicecontext() - finds or makes a voice context.
|
||||
clearvoicecontexts() - frees up the memory of the voice context
|
||||
|
||||
event_init() - called by main program
|
||||
event_text(s) - called when a line of text is encountered.
|
||||
event_reserved(p) - handles reserved character codes H-Z.
|
||||
event_tex(s) - called whenever a TeX command is encountered.
|
||||
event_linebreak() - called whenever a newline character is encountered.
|
||||
event_startmusicline() - called for each new music line.
|
||||
event_endmusicline() - called at the end of each music line.
|
||||
event_eof() - end of abc file encountered.
|
||||
event_fatal_error(s) - reports fatal error.
|
||||
event_error(s) - reports an error.
|
||||
event_warning(s) - reports a potential problem.
|
||||
event_comment(s) - called whenever a comment is encountered.
|
||||
event_specific(package, s) - recognizes %%package s.
|
||||
event_startinline() - beginning of an in-music field command.
|
||||
event_closeinline() - finishes an in-music field command.
|
||||
event_field(k,f) - for R:, T: and any other unprocessed field commands.
|
||||
event_words(p) - processes a w: field command.
|
||||
|
||||
char_out(list,out,ch) - for building up a parts list in a P: command.
|
||||
read_spec() - converts P:A(AB)3 to P:AABABAB etc.
|
||||
event_part(s) - handles a P: field.
|
||||
|
||||
char_break() - reports error for improper voice change.
|
||||
event_voice(n,s) - processes a V: field.
|
||||
event_length(n) - recognizes L: field
|
||||
event_blankline - starts finishfile() procedure.
|
||||
event_refno(n) - recognizes X:
|
||||
event_tempo(n, a, b, rel) - recognizes Q:
|
||||
event_timesig(n, m) - recognizes M:
|
||||
event_key(sharps, s, minor, modmap, modmul) - recognizes K:
|
||||
event_graceon() - start of grace notes, "{" encountered.
|
||||
event_graceoff() - end of grace notes, "}" encountered.
|
||||
event_rep1() - handles first repeat indicated by [1.
|
||||
event_rep2() - handles second repeat indicated by [2.
|
||||
event_slur(t) -called when s encountered in abc.
|
||||
event_sluron(t) called when "(" is encountered in abc.
|
||||
event_sluroff(t) called when ")" is encountered in abc.
|
||||
slurtotie() - converts slur into tied notes.
|
||||
event_tie() - handles tie indication "-".
|
||||
event_rest(n,m) - processes rest indication Z or z.
|
||||
event_bar(type) - processes various types of bar lines.
|
||||
event_space() - space character encountered. Ignored here.
|
||||
event_linend(ch,n) - handles line continuation at end of line (eg. \).
|
||||
event_broken(type, mult) - handles >, <, >> etc. in abc.
|
||||
event_tuple(n,q,r) - handles triplets and general tuplets.
|
||||
event_chord() - called whenever + is encountered.
|
||||
marknotestart() - remembers last few notes in voice context.
|
||||
marknoteend() - this is used to process broken rhythms (eg. >).
|
||||
marknote() - called for single note (as opposed to chord).
|
||||
event_chordon() - called whenever [ is encountered.
|
||||
event_chordoff() - called whenever ] is encountered.
|
||||
splitstring(s,sep,handler) - splits string with separator sep.
|
||||
event_instuction(s) - handles !...! event.
|
||||
getchordnumber(s) - searches known chords for chord s.
|
||||
addchordname(s, len, notes) - adds chord name to known chord list.
|
||||
event_gchord(s) - handles guitar chords.
|
||||
event_handle_gchord(s) - handler for guitar chords.
|
||||
event_handle_instruction(s) - handles dynamic indications (eg. !ppp!).
|
||||
event_finger(p) - handles 1,2,...5 in guitar chord field (does nothing).
|
||||
hornp(num,denom) - modifies rhythm to hornpipe.
|
||||
event_note(roll, staccato, updown, accidental, mult, note, octave, n, m)
|
||||
doroll(note,octave,n,m,pitch) - applies roll to note.
|
||||
dotrill(note,octave,n,m,pitch) - applies trill to note.
|
||||
pitchof(note,accidental,mult,octave) -finds MIDI pitch value
|
||||
setmap(sf,map,mult) - converts key signature to accidental map.
|
||||
altermap(v,modmap,modmul) - modifies accidental map.
|
||||
copymap(v) - sets up working accidental map at beginning of bar.
|
||||
|
||||
addfeature(f,p,n,d) - places feature in internal tables.
|
||||
autoextend(maxnotes) - increase memory limits for feature arrays.
|
||||
textextend(maxstrings, stringarray) - resize array pointers to strings.
|
||||
myputc(c) - workaround for problems with PCC compiler.
|
||||
tiefix() - connect up tied notes.
|
||||
dotie(j,xinchord) - called in preprocessing stage to handle ties.
|
||||
addfrac(xnum,xdenom,a,b) - add a/b to the number of units in bar.
|
||||
applybroken(place, type, n) - adjust length of broken notes.
|
||||
brokenadjust() -adjust length of broken notes.
|
||||
applygrace() - assign lengths to grace notes.
|
||||
dograce() - assign lengths to grace notes.
|
||||
lenmul(n, a, b) - multiply num(n),denom(n) by a/b.
|
||||
zerobar() - start a new count of beats in the bar.
|
||||
delendrep() - remove bogus repeat.
|
||||
placeendrep(j) - patch up missing repeat.
|
||||
placestartrep(j) - patch up missing repeat.
|
||||
fixreps() - find and correct missing repeats in music.
|
||||
startfile() - initialization performed after an event_refno.
|
||||
tempounits(t_num, t_denom) - interprets Q: field.
|
||||
setbeat() - sets default gchord command for time signature.
|
||||
headerprocess() - called after the first K: field in tune.
|
||||
finishfile() - starts next stage of processing when end of tune
|
||||
is encountered.
|
||||
|
||||
All the functions in this file respond to event calls from parseabc.
|
||||
Once the internal representation of the abc file is completed, the
|
||||
procedure finishfile is called to perform some clean up and create
|
||||
the midi file. An internal representation of the midi file is
|
||||
first created and then it is written onto the designated output file.
|
||||
As finishfile provides the link to the next module, genmidi.c, here
|
||||
is a brief description of what it does.
|
||||
|
||||
proc finishfile performs several passes through the internal
|
||||
representation to clean up the graces (dograce), the tied notes
|
||||
(tiefix) and any unbalanced repeats. It then calls writetrack(i)
|
||||
for each midi track to create the internal midi representation
|
||||
and finally the midi representation is recorded in the output
|
||||
file.
|
||||
|
||||
|
||||
|
||||
genmidi.c
|
||||
---------
|
||||
The procedure finishfile described above, creates each midi track
|
||||
by calling the function writetrack which is defined here. To create
|
||||
a track from the internal representation, the program must find all
|
||||
the parts and put them in order with all the repeats. In addition, if
|
||||
it contains karaoke text, this too must be placed in a separate track.
|
||||
Any chordal accompaniment is generated from the guitar chord indications
|
||||
and placed in another track. For multivoice and multipart music, a voice
|
||||
may be missing in a particular part. If the voice is missing, the
|
||||
procedure fillvoice ensures that all voices remain properly aligned when
|
||||
the voice reappears in another part.
|
||||
|
||||
Here is a simple road map to the important procedures included in this
|
||||
file.
|
||||
|
||||
dodeferred is here used to handle any dynamic indications (eg. !ppp!)
|
||||
which may be contained in the file. The %%MIDI messages are stored
|
||||
in a atext string which is pointed to by the contents of the pitch[]
|
||||
array.
|
||||
|
||||
checkbar is called each time a bar line is encountered and reports
|
||||
a warning if the wrong number of beats occur.
|
||||
|
||||
Transitions between parts are handled by the procedure partbreak.
|
||||
|
||||
There are a number of procedures for handling karoake text --
|
||||
karaokestarttrack(), findwline(startline), getword(place,w),
|
||||
write_syllable(place) and checksyllables().
|
||||
|
||||
For the first track, the meter, tempo and key signature are recorded
|
||||
using the functions set_meter(n,m), write_meter(n,m), write_keysig(sf,mi).
|
||||
|
||||
Chordal accompaniment is produced by the procedure dogchords(i).
|
||||
|
||||
|
||||
|
||||
queues.c
|
||||
--------
|
||||
|
||||
For each midi note, it is necessary to send a note-on and a note-off
|
||||
instruction. When polyphonic music is played on the same track, keeping
|
||||
track of the time to issue a note-off instruction may get complicated.
|
||||
The procedures in this file are used to maintain a linked list for the
|
||||
notes to be turned off. The notes are put into the list in the order
|
||||
that they are encountered but the order in which to issue note-off
|
||||
commands is maintained by the links. As many as 50 notes playing
|
||||
simultaneously can be handled by the list.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Addendum
|
||||
--------
|
||||
|
||||
The following section contains clarifications on various components
|
||||
of abc2midi.
|
||||
|
||||
29 June 2003
|
||||
|
||||
Treatment of octave shifts.
|
||||
|
||||
The key signature field command K: has provision for shifting
|
||||
a note using either transpose= or octave= subcommands. Both
|
||||
of these functions operate quite differently and deserve some
|
||||
description especially for multivoiced tunes.
|
||||
|
||||
The octave shift, is performed in event_note in store.c, using
|
||||
the cached value v.octaveshift which is stored in the global
|
||||
voicecontext structure, v. There is a structure for each voice
|
||||
in the abc file. Whenever a new V: command is encountered,
|
||||
event_voice (store.c) is invoked, which swaps the appropriate
|
||||
voices structure into the global v array using the function
|
||||
getvoicecontext(n). If getvoicecontext cannot find a cached
|
||||
structure for that voice, then a new voice structure is created
|
||||
and added to the linked list of voice structures. The v.octaveshift
|
||||
variable is updated by event_octave which is called by event_key
|
||||
(store.c) which is called by parsekey in parseabc.c
|
||||
(Comment: it is not too clear how an octave switch is
|
||||
handled in the middle of a repeat section. i.e. does the old
|
||||
value get restored when repeating.)
|
||||
|
||||
(Description of transpose shift is in CHANGES July 1 2003.)
|
||||
426
doc/programming/coding.txt
Normal file
426
doc/programming/coding.txt
Normal file
@@ -0,0 +1,426 @@
|
||||
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.
|
||||
|
||||
6
doc/programming/cvs/Entries
Normal file
6
doc/programming/cvs/Entries
Normal file
@@ -0,0 +1,6 @@
|
||||
/abc2midi.txt/1.1.1.1/Thu Sep 28 18:17:04 2006//
|
||||
/coding.txt/1.1.1.1/Thu Sep 28 18:17:04 2006//
|
||||
/midi2abc.txt/1.1.1.1/Thu Sep 28 18:17:04 2006//
|
||||
/split.abc/1.1.1.1/Thu Sep 28 18:17:04 2006//
|
||||
/yaps.txt/1.1.1.1/Thu Sep 28 18:17:04 2006//
|
||||
D
|
||||
1
doc/programming/cvs/Repository
Normal file
1
doc/programming/cvs/Repository
Normal file
@@ -0,0 +1 @@
|
||||
abcmidi/doc/programming
|
||||
1
doc/programming/cvs/Root
Normal file
1
doc/programming/cvs/Root
Normal file
@@ -0,0 +1 @@
|
||||
/home/seymour/CVSREPOS
|
||||
227
doc/programming/midi2abc.txt
Normal file
227
doc/programming/midi2abc.txt
Normal file
@@ -0,0 +1,227 @@
|
||||
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().
|
||||
|
||||
131
doc/programming/split.abc
Normal file
131
doc/programming/split.abc
Normal file
@@ -0,0 +1,131 @@
|
||||
X:1
|
||||
T: test split file 1
|
||||
M: 2/4
|
||||
L: 1/4
|
||||
K: G
|
||||
G & E & C|D|GA & EF|
|
||||
|
||||
|
||||
X:2
|
||||
T: test split file 2
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
%%MIDI program 20
|
||||
D|G & E|C|F|G & E|
|
||||
|
||||
X:3
|
||||
T: test split file 3
|
||||
M: 2/4
|
||||
L: 1/8
|
||||
Q: 50
|
||||
K: D
|
||||
CD |EF AB & CD FG| G2 | BD D2 & GB B2|
|
||||
|
||||
X:4
|
||||
T: test split file 4
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
V:1
|
||||
|:G |[1 D & B:|
|
||||
V:2
|
||||
|:g |[1 d & b:|
|
||||
V:1
|
||||
[2 F & A|
|
||||
V:2
|
||||
[2 f & a|
|
||||
|
||||
X:5
|
||||
T: test split file 5
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
G & E |D|
|
||||
|
||||
X:6
|
||||
T: test split file 6
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
D|G & E|
|
||||
|
||||
X:7
|
||||
T: test split file 7
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
D|G & E & C|F|
|
||||
|
||||
X:8
|
||||
T: test split file 8
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
G & E & C|D|
|
||||
|
||||
X:9
|
||||
T: test split file 9
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
G & E & C|D|F & A|
|
||||
|
||||
X:10
|
||||
T: test split file 10
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
V:1
|
||||
|:G |[1 D & B
|
||||
V:2
|
||||
|:g |[1 d & b
|
||||
V:1
|
||||
:|[2 F & A|
|
||||
V:2
|
||||
:|[2 f & a|
|
||||
|
||||
X:11
|
||||
T: test split file 11
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
|:G & E :|D|F & A|
|
||||
|
||||
X:12
|
||||
T: test split file 12
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
|:G & E |D:|F & A|
|
||||
|
||||
X:13
|
||||
T: test split file 13
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
|:G & E |[1 D:|[2 F & A|
|
||||
|
||||
X:14
|
||||
T: test split file 14
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
|:G |[1 D & B:|[2 F & A|
|
||||
|
||||
X:15
|
||||
T: splits with octave=1
|
||||
M: 4/4
|
||||
L: 1/4
|
||||
K: G octave=1
|
||||
G A B C & E F G A|
|
||||
|
||||
|
||||
X:16
|
||||
T: test split file 13
|
||||
M: 1/4
|
||||
L: 1/4
|
||||
K: G
|
||||
|:G & E |1 D:|2 F & A|
|
||||
|
||||
|
||||
134
doc/programming/yaps.txt
Normal file
134
doc/programming/yaps.txt
Normal file
@@ -0,0 +1,134 @@
|
||||
Some Notes on the yaps Code
|
||||
-------------------------------------------------------------
|
||||
written by Seymour Shlien November 21 2004
|
||||
|
||||
This file gives an algorithmic description of the
|
||||
yaps program which converts an abc file to a PostScript file.
|
||||
|
||||
The source consists of almost 10000 lines of C in the following
|
||||
files.
|
||||
|
||||
parseabc.c is the front end which scans the abc file and invokes
|
||||
the appropriate event handler for each element it encounters
|
||||
(eg. bar lines, notes, chords etc.) It happens to be the
|
||||
front end for other programs such as abc2midi, and
|
||||
abc2abc. More details are given in abc2midi.txt.
|
||||
|
||||
yapstree.c contains all the event handlers called by the parser.
|
||||
It produces a complex data structure called tune which
|
||||
references many other structures. When the parser completes
|
||||
processing the tune, event_blankline or event_eof calls
|
||||
printtune (in drawtree.c) which processes the tune structure
|
||||
producing a PostScript file. Unlike abc2midi, most of the
|
||||
layout including the detailed beaming is done in the first
|
||||
pass during the parsing stage. The information in this
|
||||
structure is then processed by drawtune.c in two more
|
||||
passes. An overview of these structures is provided here.
|
||||
More details on the tune structure is provided in this
|
||||
file.
|
||||
|
||||
drawtune.c contains the function printtune which turns the tune
|
||||
structure into an actual PostScript file. This is done
|
||||
in two passes. Before writing the PostScript file it
|
||||
is necessary to determine the boundingbox of the output
|
||||
image since this information is recorded in the header
|
||||
of the output file. This is determined in the first pass
|
||||
where functions monospace() or spacevoices() (in position.c)
|
||||
are called. During this pass the pixel positions
|
||||
of each object are recorded in the associated structures.
|
||||
In the second pass, the actual PostScript file is written.
|
||||
The function calls printlib() (in pslib.c) outputs
|
||||
all the PostScript boiler plate macro definitions required
|
||||
by yaps. The information in the tune structure is used
|
||||
to output the music notation in a PostScript file.
|
||||
|
||||
|
||||
position.c contains functions for determining the amount of space
|
||||
that is required for the different music objects drawn
|
||||
in the PostScript file.
|
||||
|
||||
parser2.c contains additional parsing functions missing in parseabc.
|
||||
They handle more recent features added to the abcmidi package.
|
||||
|
||||
pslib.c Definition of new PostScript commands (eg. notes, clefs,
|
||||
stems, tails, ...) which are copied to the PostScript
|
||||
file.
|
||||
|
||||
debug.c Functions which support the -d option in yaps. It prints
|
||||
some of the contents of the internal tune structure.
|
||||
|
||||
|
||||
Data Structures
|
||||
---------------
|
||||
|
||||
The data structures are defined in the include file structs.h.
|
||||
|
||||
The top level structure, tune is instantiated by init_tune which
|
||||
is called by event_init or event_refno. It stores all the
|
||||
information in the abc field commands (X: ,M:, L:, C:, O: and etc.).
|
||||
It contains a pointer to the voice structure and a list of voice
|
||||
pointers where most of the body information is recorded.
|
||||
|
||||
The voice structure is instantiated by the function newvoice
|
||||
which is called by setvoice(n) whenever the voice context
|
||||
switches in the abc file. Setvoice performs the voice switching
|
||||
(which occurs in voice interleaved files) and either creates
|
||||
a new voice or finds the matching voice structure already existing.
|
||||
The voice structure contains the feature structure which
|
||||
is somewhat similar in function to the one defined in store.c
|
||||
(for abc2midi).
|
||||
|
||||
The feature structure encodes all the detailed information in
|
||||
the body, using the same typedef enum featuretype defined
|
||||
in abc.h. Unlike store.c there is no num and denom arrays. Instead
|
||||
the feature struct contains a void* pointer called item which
|
||||
can point to anything and where additional information can be
|
||||
stored. If the feature type is a NOTE then a "note" struct
|
||||
is created which records a lot of detailed information related
|
||||
to the placement and visual representation of the note. They
|
||||
include:
|
||||
tail_type which specifies whether the note appears in
|
||||
isolation or is part of a beamed group
|
||||
base_exp whole, half, quarter, ... notes
|
||||
dots how many dots follow
|
||||
stemlength, stemup, fliphead, pitch, octave, accidentals, accents,...
|
||||
(see struct.h)
|
||||
Most of the information except actual positioning is determined
|
||||
by functions such as count_dots and beamitem in yapstree.c.
|
||||
You can view this information by running yaps.exe with the -d
|
||||
run time parameter.
|
||||
|
||||
The handling of the note positioning is quite complex as
|
||||
outlined below. It is therefore not easy to implement the
|
||||
splitvoice feature into yaps. Here is a description.
|
||||
|
||||
The positioning of notes is done by the function
|
||||
spacemultiline() which calls advance() unless one is printing
|
||||
the voices separately. The positioning is done on all the voices
|
||||
at the same time to ensure that they are lined up properly.
|
||||
Spacemultiline accesses all the active voices using the functions
|
||||
firstitem() and nextitem() which are used for handling any lists
|
||||
of objects. Each voice maintains its own pointer to the current
|
||||
feature being scanned (v->place). Each voice also maintains its
|
||||
own pointer to the relative time of the note being scanned
|
||||
in the variable v->time. (v->time contains both a numerator
|
||||
and denominator.) The advance() function updates v->place,
|
||||
v->time and interprets the voice features determining the
|
||||
amount of space that is needed to plot the next object (itemspace)
|
||||
and the number of plotable objects (items). The position to
|
||||
plot the next object (x) is maintained by spacemultiline and
|
||||
passed to advance. Advance() updates v->place->x with the position
|
||||
to plot the object based on its width. Spacemultiline()
|
||||
maintains a mastertime variable for maintaining synchrony
|
||||
between all voices.
|
||||
|
||||
At least two passes are made through the voice features.
|
||||
In the first pass provisional positions are computed for the
|
||||
notes. The amount of space left over in the music lines is
|
||||
computed and is used to determine the internote gaps.
|
||||
In the second pass, the notes are repositioned to use up
|
||||
the entire space in the staff line.
|
||||
|
||||
Spacemultiline is called for each staff line whose end is
|
||||
signaled by a linefeed feature in the voice.
|
||||
|
||||
Reference in New Issue
Block a user