This is a big job, and not for the faint of heart - I wish you luck!
Skink is written in Java, and uses a parser written in Javacc. (so a c++ set of datastructures would have done me little good...) I actually started off with a C++ version using Flex++ and Bison, but found Java and never looked back... Some coments below, for what it's worth.


My strategy for

Paul Rosen wrote:

I've finally gotten a few free moments of time, and started thinking about
the "universal parser" that we talked about a few months back. I started to
write down the requirements for the output, which I've included below. I've
included everything I could think of for formatting and playing music.

The idea here is to create a universal data structure suitable for all ABC
applications. Then someone writing a better parser and someone writing a
better formatter could work independently.

I found that parsing could be made fast enough that I can effectively re-parse the entire file and redisplay
it on every keystroke. Very long files are not great, but moderate size files (~100 tunes) respond reasonably well


If you are interested in this, please comment on the following and tell me
if I've left anything out or made incorrect assumptions. When I get a little
more time, I'll create a C structure that can handle this and post that.

-----

ABC parser output data structure.

Goal

3. This is the data structure between the ABC parser and the application.
There are two main applications: a sheet music formatter, and music player.
I think other applications (like a transposer) would not have any unique
demands on the structure.
4. The goal is to have a flexible enough structure that additional
requirements would be handled without requiring fundamental changes to the
structure. With luck, some additional requirements may be handled without
any changes to the structure at all.
5. The purpose of this structure is to completely separate the parser, the
formatter, and the player. Each one of these three functions can be
developed independently, and different developers can have their pieces snap
in.


This is more-or-less what I did with Skink, though the intermediate representation is a set of objects,
not a file. I have defined an interface for my renderer (I have two, for different versions of Java)
and for my player, (although I only have one at the moment.)


General requirements

7. This structure is for a single tune. To do multiple tunes, you can simply
have an array of these.

Not quite - there is additional content that is not inside any tune, and some of it might want to be displayed.
In particular, creating tables-of-contents, indexes, and pagenumbers needs some back-and-forth between
the tune and the display.


8. Everything possible should be interpreted as much as possible. In other
words, a chord of "Gm" would be stored as a G minor chord, not the text
"Gm". This allows the player to not need to do any parsing.

I'm not sure I agree with this - the chord notation has been much abused.

9. however, the original wording should be available if needed. In other
words, "GM" and "G" are both a G major chord. Some formatting programs may
wish to use one or the other exclusively, or may choose to parrot whatever
was specified.

Yes...

10. Extra formatting information may be in the structure. The most obvious
is the new staff indicator (the exclamation point) and the
don't-connect-eighths indicator (the space). Room should be made for many
others that may be put in the language later. For instance, what if the user
wanted a measure to be spaced out more than the formatter naturally would?
Or wanted to force the notes to be pointed up? Or wanted more room between
two particular staffs? Or a new page? Or wanted to decide whether the last
staff was justified or aligned to the left? In any case, no formatting is
required, and in that case, the formatter makes all its own decisions.

OK

11. The structure should have a simple, fixed format so that it can be saved
to disk easily, or passed between programs written in different languages.
The saved version would be something of a "compiled" version, that is, our
equivalent of an OBJ file. This means that there can't be any pointers in
the structure.

I think this restriction essentially ends up being true only for an intermediate form. For example, if you have
a | abcd | dead | cabb :| age


(a purposely "wrong", but often found construct) you will need to figure out how to point back to the
first barline for the repeat. In other words, the player will have to construct an intermediate form that
does have pointers.


12. A C++ class will encapsulate the structure, so that users of C++ can
easily gain access to the data in a natural way.


Java? Perl? Ruby? python? ...

General structure

14. There are two parts:
15. The header contains one-time only information. that includes the title,
etc. It also contains global information, like whether to force all notes to
be pointed up.

Voices will need to have this info.

16. The body is an array of items. These items define a particular vertical
space on the notation, and a particular time for the player. Note that the
amount of space and amount of time is variable for each item. A simple
example follows. Three possible items are a whole note, a half note, and a
bar. each of the three takes a different amount of time to play (the bar
takes no time to play). The bar takes a different amount of space to display
than the notes.

You will need to define an 'extent' of notes that have certain characteristics. Some items will cause changes
in the player and/or the display context. For example


K:D
|: abcd || [K:Bb] def
abc :|

The first staff has two sharps, there is a change in key signature which changes the player context and the
second staff must have two flats. Then when your player repeats, it needs to be back to two sharps.


17. Items need to refer to neighboring (and farther off) items. For
instance, an item may indicate that a slur has begun, and another item would
indicate that it has ended.

Elements in the item

19. (All of the following are for a single stave. An element may also have
multiple staves.)

I'm not sure if you are advocating collapsing all voices into a single 'element'. That would make some things
easier, but it would make it perhaps more difficult to print separate voices on a separate sheet (since I have
yet to implement multi-voice, I guess I shouldn't be too picky..!)


20. Multiple notes, each with a separate rhythm, and a voice number.
21. Chord symbol
22. Singing words (possibly multiple verses)
23. slurs and ties (connected to a particular note if multiple notes are
present)
24. The greater and less than signs that show the differences in volume.
25. New key signature, new tempo, new time signature.
26. The following accents (multiple ones could be in each item, and
sometimes multiple ones can be on each note in an item): slide up and down,
muffled (note head is an X), diamond head note, loudness markings, fingering
numbers, trill indicator, ritard, fermat, arpeggio, legato, staccato &
pizzicato, bowing symbols, pedal on, pedal off,  accent, attack,
27. This item is part of a set of grace notes (the rhythm for them should be
stolen from the previous note.)
28. First ending, second ending, etc.
29. Bar line type: repeat, double, thick on left, etc.
30. Text in the staff instead of notes. That is: "8 bar intro",  "piano
solo", etc.
31. Non-chord note above the staff. for instance, to indicate a different
instrument should start playing, or to label an interesting spot.

Elements in the header

33. All of the items in the header section of the ABC spec.
34. Additional verses. (probably printed at the bottom)
35. Global indicators, like number of staves and their clef.
36. Instrument name to the left of the staff.
37. Amount of swing (8th notes played dotted by how much)


Phil Taylor has done a great job in his 'stress programs' - specifying for a particular
rhythm how to adjust note length and volume.


One thing you might want to think about early is how to handle errors - Skink puts in its object
stream an 'error object' that refers to the place where the parser picked up the error, and then
recovers at the start of the next tune.


I'd strongly suggest using one of the parser builders out there - I was playing with PCCTS before
I jumped to Java, and I believer there are other compiler-compilers (including, of course, the
old stalwarts of lex (or flex++) and yacc (or bison).


Good luck!

wil


Paul Rosen --- Life is a musical, every once in a while the plot stops and you start singing and dancing --- http://home.earthlink.net/~catharsis.music/ http://home.earthlink.net/~theplums/


To subscribe/unsubscribe, point your browser to: http://www.tullochgorm.com/lists.html



To subscribe/unsubscribe, point your browser to: http://www.tullochgorm.com/lists.html

Reply via email to