[Woof!]
Hi all,
Here’s the second revision of the FORM CMUS, as downloaded from
BIX. Again, email me any comments that you wish to make to Talin.
(That doesn’t mean that there shouldn’t be any discussion here on the
Net; it’s just that BIX doesn’t have a Usenet connection, so they’ll
never see it if it isn’t relayed.)
Enjoy,
–dds
========== Begin Quoted Text:
==========================
amiga.dev/iff #3456, from talin, 30489 chars, Thu Jun 4 01:07:11 1992
————————–
TITLE: FORM CMUS, version II
/* ============================================================================
*
CMUS – Common Musical Score
An IFF File format for interchanging
musical data using Common Music Notation
by Talin
Note this spec is preliminary. It WILL be changed.
Feel free to re-post this document in it’s entirety
* ============================================================================
*/
#define CMUS_H
/* ============================================================================
*
Note on Structures:
~~~~~~~~~~~~~~~~~~~
Because ANSI doesn’t define the ordering or size of bitfields, I have
had to remove all of the bitfield definitions from this spec. Unfortunately,
this has made the spec a lot less readable.
* ============================================================================
*
Note on Timing:
~~~~~~~~~~~~~~~
Common Music Notation is a symbolic, rather than a literal representatio
n.
It is supposed to be interpreted by the player. A note which is listed as
"A quarter note", will seldom be played at the exact time or duration
as written. These deviations from mathematically perfect time are important;
they are part of what musicians call "feel" or "liveliness".
Accordingly, FORM CMUS has two different kinds of timing information.
_Formal Time_ is represented in symbolic form: Each symbol has a field
which indicates it’s duration (dotted quarter-note, etc) in symbolic units.
The formal start time of an event can be obtained by summing the durations
of all the previous times in the measure.
In addition, there is also _Casual Time_. Each event has a "start time"
which is the number of basic clock units from the start of the measure to
the start of that event. Some event types also have "duration" fields of
a similar nature.
In general, although there will probably be a strong correlation between
formal time and casual time, there is no guarantee of this. Certainly
this FORM does not enforce any relationship between the two. This means that
you cannot, in general, derive one from the other. You can at most make
an educated guess, and even that is a non-trivial problem from an algorithmi
c
point of view.
* ============================================================================
*
Note on Layout Measurements:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In general, I have tried to make all measurements as "device-independent
"
as possible.
Measurements of page dimensions and other page-related information such
as margins and indentations are represented as in fractions of a meter.
A "Page Unit" is defined as a tenth of a millimeter.
Converting from page units to inches and "points" can be done with the
following formulas:
inches = page-units / 254;
points = page-units * 72 / 254;
(Side note: The above formula assumes 2.450 centimeters to the inch,
and 72 points to the inch. Both of these are approximations. Note that
in the computer industry, most software uses a definition of
72 points = 1 inch, which is not entirely accurate, however I suggest
that we continue using the same approximation, for compatibility.)
Vertical distances of musical items are all measured in "Levels".
A level is one half the distance between the lines of a staff. A note on
the centerline of the staff is at level zero. Placing the note just above
that line (between the 2nd and 3rd staff line) makes it level 1, while
placing it below the centerline makes it level -1. Note that up is positive
in this coordinate system.
Horizontal distances are usually measured as a fraction of the enclosing
structure. Note positions are recorded as a fraction of the measure width,
while measure widths are recorded as a fraction of the document width.
* ============================================================================
*
Rules for clipboard use:
~~~~~~~~~~~~~~~~~~~~~~~~
A CMUS chunk may be copied to the clipboard. In such cases, only a
subset of the data may be written. Specifically, measures and signatures
which occur before the first selection point, or after the last selection
point should not be included. Note that the measure containing the first
selection point should be written, however, even if it is not in the
selection range itself. (As to whether measure-lines are selectable is
up to the application). In addition, an initial time signature item for
that measure should be written as well; key signature and clef items
are optional in this case. The application receiving the clip has the
option of whether to use the signature items in the clip, or to ignore
them and use the existing signatures in the score. The application can
also decide to "insert" the clip into the score (causing existing other
events to be shifted later in time), or "merging" the events with the
existing items. The application can also choose to respect measure
lines in the clip (each new measure line causes the notes to be pasted
into the next measure) or to "flow" the notes across measure boundaries.
Note that the notes in a clip may be non-contiguous. For example,
If the user were to select every second note and copy those to the
clipboard, there would be "gaps" in the clipped track. Unfortunately,
a reader program would not be able to detect those gaps (since formal
time does not have an explicit start time) and thus the formal time
and the casual time would get out of sync. To avoid this problem,
"filler" events can be inserted into the score to fill up the empty space.
Note that the duration of a filler event is formal, unlike all the
other events.
Note that a filler event at the end of the measure is not neccessary.
In fact, there is no requirement in CMUS that a measure be "filled".
In addition, certain music programs allow more notes in a measure than
would legally fit (only while editing, the extra notes are never played).
CMUS readers should handle this case robustly.
This allows a reader to make intelligent use of the clip. The clip
can be pasted relative to an insertion point, and the relationship of
notes to measures can be (optionally) preserved, even if the selection
was non-contiguous.
Issues to be resolved:
What units should the overall page size be stored in? It seems that we
should have a standard "page setup" chunk, which would contain all the thing
s
that would normally be in a "page setup" requester. Also, look at other
page-layout forms to see what they use. Should we use metric, points, etc?
*/
/* unit of measurement */
typedef UWORD PageUnit; /* I think 6.5 meters is plenty
*/
/* ============================================================================
*
Score Header Chunk (SCHD)
* ============================================================================
*/
typedef struct {
WORD scBarsPerLine; /* preferred bars per line
*/
/* these are in the same units as used in the page setup chunk */
PageUnit scTitleSpace, /* top margin of score on page 1
*/
scFirstLineIndent, /* left margin indent on line 1
*/
scLineIndent, /* left indent on other lines
*/
scLevelSize; /* distance between staff lines
*/
WORD scOverallVolume; /* overall volume of score
*/
} ScoreHeaderChunk;
/* ============================================================================
*
Page Setup Chunk (PAGE)
* ============================================================================
*/
/* NOTE: I Could use a lot of feedback on this one. Specifically, this
chunk needs to be reworked to be more machine-independent. (Remember,
IFF is supposed to be INTERCHANGE file format. I happen to know
at least one non-amiga programmer who may be interested in this
file format).
*/
typedef struct {
UWORD pageFlags; /* see below
*/
/* This field would have standard values for US Letter, A4, etc… */
WORD pageType; /* Standard page size
*/
PageUnit pageWidth, /* Actual Page Dimensions
*/
pageHeight,
pageLeftMargin,
pageRightMargin,
pageTopMargin;
} PageSetupChunk;
enum page_setup_flags {
PSETUPF_LANDSCAPE = (1<<0) /* print in landscape mode
*/
/* ISSUE: What other flags will be needed? */
};
/* ============================================================================
*
Staff Table Chunk (STAF)
This section describes the data structures which are used in the CMUS ‘STFF’
Chunk. There is one STFF chunk per score, which contains an array of
StaffEntry structres (1 per staff in the document).
* ============================================================================
*/
typedef struct {
WORD staffFlags; /* various flags
*/
/* This defines the vertical size of a measure. Both of the distances
are measured from the center line of the staff (in fact all staff-
relative distances are represented this way).
*/
UBYTE staffSpaceAbove, /* space above staff, in levels
*/
staffSpaceBelow; /* space below staff, in levels
*/
} StaffEntry;
/* This flag indicates that a formfeed should be done before printing
this staff (used when a score has more staffs than will fit on a page.
*/
#define STAFF_PAGEBREAK (1<<0)
/* This indicates that the measure lines for this staff should not be
connected to the measure lines for the staff below
*/
#define STAFF_BAR_BROKEN (1<<1)
/* This flag indicates that a set of "curly braces" should connect this
staff with the staff below.
*/
#define STAFF_BRACED (1<<2) /* Staff is "braced" with next
*/
/* These flags indicate the start and end of a square bracket which can
span over several staffs.
*/
#define STAFF_BRACKET_START (1<<3)
#define STAFF_BRACKET_END (1<<4)
/* ============================================================================
*
Track Chunk (TRCK)
This section describes the data structures which are used in the CMUS ‘TRCK’
Chunk. There is one TRCK chunk per melody line in the score.
* ============================================================================
*/
/* Track Header structure:
Each track begins with a track header structure, followed by any number
of score items. (Use the chunk length to determine when to stop reading
score items).
*/
typedef struct {
UWORD trkStaff, /* staff number to place this on
*/
trkTrack, /* track number within staff
*/
trkFlags; /* flags for staff header
*/
/* Sometimes notes on the staff are written transposed from how they
should actually be played. This is the number that should be added
to the pitch before it is actually played back.
*/
WORD trkTransposition; /* playback transposition
*/
} CM_TrackHeader;
/* ============================================================================
*
Track Item
* ============================================================================
*/
/* Item Header:
Score items are variable in length. The first nibble of the item is the
length of the item in WORDS. This will allow new item types to be added
in the future. All score items are an integer number of WORDS long.
Each score item has a standard header structure, followed by a variable
about of item-specific data. The itemType field is used to determine what
that data is.
‘itemLength’ is the length of the item in WORDS. This allow items to be
from 2 to 30 bytes long. The value ’0′ is a special case which indicated
an item which is 32 bytes long.
‘itemXPos’ contains the X position of the item in fractions of the measure’s
width. Note that the area containing the signatures, and the area just
before the ending measure line are not considered part of this range.
Think of it this way: The value 0 is the first possible note position.
The value 255 if the last possible note position. Items placed at these
positions should not run into the graphics at either the beginning or
the end of the measure.
‘itemStart’ contains the real starting time of each event. IMPORTANT NOTE:
This does NOT have to exactly match the event’s "formal" time. For
example, an event at the beginning of a measure does not have to start
at exactly time zero, but can be offset somewhat. This allows the subtle
nuances of a live performance to be preserved, if the notation software
allows for that capability.
The ‘itemStart’ field (and the noteDuration field defined later) use a
clock standard of 960 clock ticks per whole note. Thus, a quarter note
is one/quarter that, or 240. This number is divisible by 3, 5, and several
powers of two, making it convenient for representing triplets and
quintuplets as well as small note values.
*/
/* What the structure would look like with bitfields:
unsigned int itemLength : 4, – length of this item in WORDS
itemType : 4, – type of item
itemXPos : 8; – horizontal position of item
— in fractions of measure width
itemStart : 16; — start time, in ticks
*/
typedef struct {
UBYTE itemType, /* bitfields for item header
*/
itemXPos; /* horizontal position of item
*/
UWORD itemStart; /* start time, in ticks
*/
} CM_ItemHeader;
#define CM_ItemLength(f) ((f).itemType >> 4)
#define CM_ItemType(f) ((f).itemType & 0xf)
#define CM_SetItemHeader(field, length, type) \
((field).itemType = (length << 4) | type)
#define WHOLE_NOTE_DURATION 960 /* duration of a whole note
*/
/* type codes for chunk item types */
enum notation_item_types {
MEASURE_ITEM = 0, /* measure line
*/
SIGNATURE_ITEM, /* time sig., key sig., or clef
*/
NOTE_ITEM, /* first note in a chord
*/
CHORD_ITEM, /* additional notes in a chord
*/
FILLER_ITEM, /* fills up empty gaps
*/
DYNAMIC_ITEM, /* dynamic volume item (fff)
*/
INSTRUMENT_ITEM, /* instrument change item
*/
TEMPO_ITEM, /* tempo change item
*/
BEGIN_GROUP_ITEM, /* begin slur, crescendo, etc.
*/
END_GROUP_ITEM /* end slur, crecendo, etc.
*/
};
/* ============================================================================
*
Measure Line Item
* ============================================================================
*/
/* This item represents the beginning of a new measure. As such, there should
be one of these at the beginning of the track, but not at the end.
*/
typedef struct {
CM_ItemHeader measureItem; /* item header
*/
/* Width of measure body as a fraction of the score width
(full width of score == 0x7fff). If negative, it means that
the measure width was not set by the user, but calculated
on the fly, and can be re-adjusted feely if needed to line
things up. Note that this width includes signatures, but does
not include any indentation from the left margin of the document.
*/
UWORD measureWidth;
WORD measureFlags; /* various flags, see below
*/
} CM_Measure;
#define MEASURE_FULL_WIDTH 0x7fff
enum measureFlags {
/* Draw a double bar at the end of this measure. The reason for
associating the double-bar flag with the next measure line is becaus
e
double bar can occur at the end of the score but not at the
beginning.
*/
MEASUREF_DOUBLE_BAR = (1<<0),
/* This is a "line break", in other words it indicates that this
measure should start a new line.
*/
MEASUREF_NEW_LINE = (1<<1),
/* This measure is the first measure of a repeat section.
*/
MEASUREF_BEGIN_RPT = (1<<2),
/* This measure is the last measure of a repeat section. Note that the
"End repeat" symbol is drawn at the END of the measure, i.e. at the
next measure line.
*/
MEASUREF_END_RPT = (1<<3),
/* This measure is part of a "1st ending" block.
*/
MEASUREF_1ST_ENDING = (1<<4),
/* This measure is part of a "2nd ending" block.
*/
MEASUREF_2ND_ENDING = (1<<5),
};
/* ============================================================================
*
Signature Items
* ============================================================================
*/
/* Signature items are usually placed just after the measure line.
Some notators have the ability to change clef in the middle of a measure,
but not all notators need support this. If it is not supported, and a clef
is encountered in the middle of a measure, it is assumed to apply to the
entire measure and therefore is associated with the previous measure line.
*/
enum SignatureTypes {
SIGTYPE_TIMESIG=1,
SIGTYPE_CLEF,
SIGTYPE_MAJORKEY,
SIGTYPE_MINORKEY
};
/* Stores a time signature. ‘Beats’ is the number above the line, and
‘Notes’ is the number below the line. For example, ’3/4′ time is
stored as beats = 3, notes = 4.
*/
typedef struct {
CM_ItemHeader sigItem; /* item header
*/
UBYTE sigSubType, /* (= SIGTYPE_TIMESIG)
*/
sigBeats, /* beats per bar
*/
sigNotes, /* size of each beat
*/
sigPad; /* pad out to WORD size
*/
} CM_TimeSignature;
/* stores a Clef */
typedef struct {
CM_ItemHeader sigItem; /* item header
*/
UBYTE sigSubType; /* (= SIGTYPE_CLEF)
*/
UBYTE sigClef; /* new clef
*/
} CM_Clef;
/* definitions of clef types */
enum ClefTypes {
TREBLE_CLEF = 0,
BASS_CLEF = 1,
ALTO_CLEF = 2,
TENOR_CLEF = 3
};
/* stores a Key Signature. (used for both major and minor)
‘sigKeySig’ is a signed BYTE, where ’0′ is the key of C. Positive number
s
represent the number of sharps, so 1=G, 2=D, etc, around the curcle of
fifths. Negative numbers represent the number of flats, F=-1, B-Flat = –
2,
etc.
For minor keys, 0 is the key of A-minor, which like C has no sharps or
flats.
Other types of key signatures may be supported in the future, but
will probably be done as a different type of signature item.
*/
typedef struct {
CM_ItemHeader sigItem; /* item header
*/
UBYTE sigSubType; /* (== SIGTYPE_KEYSIG)
*/
BYTE sigKeySig; /* new key signature
*/
} CM_KeySignature;
/* major key definitions */
#define KEY_OF_C_MAJOR 0
#define KEY_OF_G_MAJOR 1
#define KEY_OF_D_MAJOR 2
#define KEY_OF_A_MAJOR 3
#define KEY_OF_E_MAJOR 4
#define KEY_OF_B_MAJOR 5
#define KEY_OF_F_SHARP_MAJOR 6
#define KEY_OF_C_SHARP_MAJOR 7
#define KEY_OF_F_MAJOR -1
#define KEY_OF_B_FLAT_MAJOR -2
#define KEY_OF_E_FLAT_MAJOR -3
#define KEY_OF_A_FLAT_MAJOR -4
#define KEY_OF_D_FLAT_MAJOR -5
#define KEY_OF_G_FLAT_MAJOR -6
#define KEY_OF_C_FLAT_MAJOR -7
/* minor key definitions */
#define KEY_OF_A_MINOR 0
#define KEY_OF_E_MINOR 1
#define KEY_OF_B_MINOR 2
#define KEY_OF_F_SHARP_MINOR 3
#define KEY_OF_C_SHARP_MINOR 4
#define KEY_OF_G_SHARP_MINOR 5
#define KEY_OF_D_SHARP_MINOR 6
#define KEY_OF_A_SHARP_MINOR 7
#define KEY_OF_D_MINOR -1
#define KEY_OF_G_MINOR -2
#define KEY_OF_C_MINOR -3
#define KEY_OF_F_MINOR -4
#define KEY_OF_A_FLAT_MINOR -5
#define KEY_OF_E_FLAT_MINOR -6
#define KEY_OF_C_FLAT_MINOR -7
/* ============================================================================
*
Note or Chord item
* ============================================================================
*/
/* ISSUE: Need to redefine tuplets based on svigna’s suggestion */
/* What the structure looks like with bitfields:
CM_ItemHeader noteItem; — item header
UWORD noteDuration; — real duration, in ticks
UWORD noteFlags;
unsigned int noteTuplet : 2, — triplet, quintuplet, etc.
noteDots : 2, — dotted, double-dotted
noteDivision : 4, — quarter note, etc.
UBYTE noteStyle; – Note Style type
UBYTE notePitch; – MIDI note number
signed int noteLevel : 6; — dist from staff centerline
unsigned int noteAccidental : 2; — accidental code
BYTE noteBeamHeight; — y position of beam
UBYTE notePad;
*/
typedef struct {
CM_ItemHeader noteItem; /* item header
*/
UWORD noteDuration; /* real duration, in ticks
*/
UWORD noteFlags;
UBYTE noteDivision; /* formal note length
*/
UBYTE noteStyle; /* Note Style type
*/
UBYTE notePitch; /* MIDI note number
*/
BYTE noteLevel; /* vertical position and accid.
*/
BYTE noteBeamHeight; /* y position of beam
*/
UBYTE notePad;
} CM_Note;
/* macros to access the various bitfields */
#define CM_NoteTuplet(f) ((f).noteDivision >> 6)
#define CM_NoteDots(f) (((f).noteDivision >> 4) & 0×03)
#define CM_NoteDivision(f) ((f).noteDivision & 0x0f)
#define CM_NoteLevel(f) ((f).noteLevel >> 2)
#define CM_NoteAccidental(f) ((f).noteLevel & 3)
#define CM_SetNoteDivision(note, division, tuplet, dots) \
((note).noteDivision = (tuplet << 6) | (dots << 4) | divisio
n)
#define CM_SetNoteLevel(note, level, accidental) \
((note).noteLevel = (level << 2) | accidental)
/* CHORDS: The first note of a chord is always of type "note".
Additional notes, or "intervals" are stored using the "chord" type.
The itemXPos, noteTuple, noteDots, noteDivision, noteStyle and
noteBeamHeight fields are ignored for chord items and are derived from
the base note, however score writers should set them to the same as
the base note for consistency.
RESTS: A rest is a note item with a notePitch of 0. Rests may not be
chorded.
*/
/* Values for fields in the note structure:
Note that the definition for a septuplet is different than the one used
in SMUS (which is wrong: SMUS says 6/7, should be 4/7).
*/
enum note_tuples {
TUPLE_NONE=0, /* Note is normal duration
*/
TUPLE_TRIPLET, /* Note is 2/3 duration
*/
TUPLE_QUINTUPLET, /* Note is 4/5 duration
*/
TUPLE_SEPTUPLET /* note is 4/7 duration
*/
};
enum note_dots {
NO_DOT = 0, /* Note is normal duration
*/
DOTTED_NOTE = 1, /* Note is 50% longer
*/
DOUBLE_DOTTED = 2 /* note is 75% longer
*/
};
enum note_divisions {
DOUBLE_WHOLE_NOTE = 0,
WHOLE_NOTE,
HALF_NOTE,
QUARTER_NOTE,
EIGHTH_NOTE,
SIXTEENTH_NOTE,
NOTE_32,
NOTE_64,
NOTE_128,
NOTE_256
};
enum note_accidentals {
NOTE_ACC_NONE,
NOTE_ACC_FLAT,
NOTE_ACC_NATURAL,
NOTE_ACC_SHARP
};
enum noteFlags {
NOTEF_STEMDOWN = (1<<0), /* Note’s stem is down
*/
NOTEF_BEAMED = (1<<1), /* Note is beamed with next note
*/
NOTEF_TIED = (1<<2), /* Note is tied with prev note
*/
NOTEF_TIEDOWN = (1<<3), /* tie direction is DOWN
*/
};
/* ============================================================================
*
Filler item
* ============================================================================
*/
/* This item is used for supporting sparse clips. The fillerDuration field
contains the total of the formal durations of the missing items
between the previous event and the next one.
*/
typedef struct {
CM_ItemHeader fillerItem; /* item header
*/
UWORD fillerDuration; /* formal size of items left out
*/
} CM_Filler;
/* ============================================================================
*
Dynamic Item
* ============================================================================
*/
/* This item specifies a MIDI volume. Note that the relationship between
Volume and dynamic markings (such as fff, pp, etc) is defined elsewhere.
*/
typedef struct {
CM_ItemHeader dynItem; /* item header
*/
BYTE dynLevelPos; /* vertical position in leveks
*/
UBYTE dynVolume; /* midi pressure number (0..127)
*/
} CM_Dynamic;
/* ============================================================================
*
Instrument item
* ============================================================================
*/
/* Rather than embedding the instrument names in the actual score, a
seperate "instrument table" chunk will be defined.
*/
typedef struct {
CM_ItemHeader instItem; /* instrument item
*/
UBYTE instNumber; /* instrument number from table
*/
UBYTE instPad;
} CM_Instrument;
/* ============================================================================
*
Tempo Item
* ============================================================================
*/
/* For compatibility with Standard MIDI files, tempo is represented as
microseconds per quarter note, rather than the more commonly used
quarter notes per minute. To convert from one to the other, the
following formula works both ways:
T = 60,000,000 / t;
For accuracy, you may want to round:
T = (60,000,000 + t/2) / t;
Of course, the user interface of the program should not use units
like this.
*/
typedef struct {
CM_ItemHeader tempoItem; /* item header
*/
ULONG tempoValue; /* new tempo value
*/
} CM_Tempo;
/* ============================================================================
*
Group Item
* ============================================================================
*/
/* A "Group" Item is defined as a Slur, Crescendo, or Octave Raiser.
In general, groups can apply to any contiguous range of notes
on a track, and groups of the same type can note overlap.
Note that in some cases, such as for example a crecendo, although
the modification is technically "attached" to a particular
track, it affects all the tracks on that staff.
*/
typedef struct {
CM_ItemHeader groupItem; /* item header
*/
UBYTE groupType; /* subtype of group
*/
/* To even out the structure, we’ll add an extra byte which means
different things based on the group type. Right now it is
only defined in the case of a crescendo / decrescendo in which
case it means the final volume.
For all others, it should be set to zero.
*/
UBYTE groupVal;
} CM_Group;
/* Types of group items supported */
enum group_types {
GROUPTYPE_SLUR_UP=0,
GROUPTYPE_SLUR_DOWN,
GROUPTYPE_CRESCENDO,
GROUPTYPE_DECRESCENDO,
GROUPTYPE_OCTAVE_UP,
GROUPTYPE_OCTAVE_DOWN,
};
No more unread messages in this topic
Hit <RETURN> for next active conf/topic.
Return for next, m for menu, or message number:
========== End Quoted Text
—
"Weave a circle round him thrice, | Dale D. Snell BIX: ddsnell
And close your eyes in holy dread, | Usenet: da…@tekcci.CCI.TEK.COM
For he on donkey-dung hath fed, | CServe: 74756….@compuserve.COM
And drunk the milk of many mice." | DISCLAIMER: These are *my* opinions.
— found on a wall in Coal Ridge, Nebraska — with apologies to Sam.