/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* Copyright (c) Philip Hazel, 1991 - 2019 */ /* Written by Philip Hazel, starting November 1991 */ /* This file last modified: August 2019 */ /* This file contains subroutines called by the code for creating a position table for a bar. */ #include "pmwhdr.h" #include "pagehdr.h" #include "poshdr.h" /************************************************* * Make space for non-note * *************************************************/ /* In fact, this is used for grace notes as well. The musical offset supplied is always negative. We have to check whether a position for the auxiliary item already exists, and if so, to adjust its place if necessary. The yield is the new value for this position. The "here" flag determines whether the item can be put to the left of the current position if there is space. Arguments: previous pointer to postable entry for the previous position, or NULL this pointer to current postable entry auxoffset offset for the non-note item (always negative) Rwidth width for the item used here TRUE if Returns: pointer to the postable entry after "this" */ static workposstr * pos_insertXpos(workposstr *previous, workposstr *this, int auxoffset, int Rwidth, int used, BOOL here) { workposstr *aux = NULL; Rwidth = mac_muldiv(Rwidth, main_stavemagn, 1000); /* See if any auxiliaries exist, and remember if this one */ if (this > page_postable) { workposstr *t = this - 1; if (t->auxid != 0) { while (t >= page_postable && t->auxid != 0) { aux = t; t--; } } } /* If there are no auxiliaries, the space available is the sum from the previous position (if any) to this position. If previously existing auxiliaries are to the right of this one, the same thing applies. We can do a straight insert with an appropriate offset in both cases. */ if (aux == NULL || auxoffset < aux->auxid) { workposstr *t; workposstr *insertpoint = (aux == NULL)? this : aux; for (t = page_posptr; t >= insertpoint; t--) /* Move up to make space */ memcpy(t+1, t, sizeof(workposstr)); page_posptr++; /* increase the end */ this++; /* for yielding */ aux = insertpoint++; insertpoint->space = 0; /* leave space on the new insert */ aux->moff = this->moff + auxoffset; aux->auxid = auxoffset; /* identifies the aux */ aux->posstaves = page_stave; /* flags which stave */ if (here) aux->xoff = insertpoint->xoff; else { int avail = 0; if (previous != NULL) { t = previous + 1; while (t <= aux) avail += (t++)->xoff; } avail -= Rwidth + used; if (avail < 0) insertpoint->xoff -= avail; aux->xoff = insertpoint->xoff - Rwidth; } insertpoint->xoff = Rwidth; } /* Either there are auxiliaries to the left of this one, or this one already exists. */ else { workposstr *insertpoint = aux; while (insertpoint != this) { if (insertpoint->auxid >= auxoffset) break; insertpoint++; } /* If this auxiliary already exists, test that there is enough space between it and the previous note. This works because we process the auxiliaries for one note from right to left. In the case of accidentals, we must adjust the space by the difference between this accidental's requirements and whatever is already there. */ if (insertpoint->auxid == auxoffset) { int avail = 0; workposstr *next = insertpoint + 1; workposstr *t = (previous == NULL)? page_postable : previous + 1; while (t <= insertpoint) avail += (t++)->xoff; /* For non-accidentals, check that there is enough space to the right, and increase if necessary. */ if (auxoffset != posx_acc) { if (Rwidth > next->xoff) next->xoff = Rwidth; } /* For accidentals, all we need to do is check that there is enough space on the left, since accidental printing doesn't actually use the calculated position - chords need several positions, for a start. */ else avail += next->xoff - Rwidth; /* If there is insufficient space, move *all* the auxiliaries to the right. */ avail -= used; if (avail < 0) aux->xoff -= avail; /* Flag which staves */ insertpoint->posstaves |= page_stave; } /* If this auxiliary does not exist, we must insert it. See if there is space on *all* staves between the first auxiliary and the previous note position. If there is, we can move those auxiliaries to the left of this one to the left. */ else { workposstr *t; workposstr *new = insertpoint; for (t = page_posptr; t >= insertpoint; t--) /* Move up to make a space */ memcpy(t+1, t, sizeof(workposstr)); page_posptr++; /* list is one longer now */ this++; /* for yielding */ insertpoint++; new->space = 0; new->moff = this->moff + auxoffset; new->auxid = auxoffset; /* identifies the aux */ new->posstaves = page_stave; /* flag which stave */ /* Distance from previous aux is its standard distance */ new->xoff = insertpoint->xoff; insertpoint->xoff = Rwidth; if (!here) { int avail = aux->xoff - Rwidth - used; if (avail > -Rwidth) aux->xoff -= (avail < 0)? Rwidth + avail : Rwidth; } } } return this; } /************************************************* * Insert non-note items * *************************************************/ /* This function is called to insert entries into the postable for non-note things such as accidentals and other markings. Arguments: moff music offset for the note xflags flags for various marks (comma, caesura, etc) accleft width required for accidental keyvector timevector gracevector previous pointer to the previous position item prevlength prevflags Returns: pointer to final inserted item */ workposstr * pos_insertextras(int moff, int xflags, int accleft, int *keyvector, int *timevector, int *gracevector, workposstr *previous, int prevlength, int prevflags) { workposstr *this; int rrepeatextra = 0; int used; int i; /* Find the postable entry for this note */ this = (previous == NULL)? page_postable : previous; while (this->moff < moff) this++; /* Now process the auxiliary items, in order from right to left. This makes it possible to do the best thing when more than one exists on a single note. They may come out of order when on different notes, and in this case the spacing may not be as perfect. But the likelihood is pretty rare. They are given conventional "musical offsets" that are just prior to that of the note they precede. First handle accidentals. Because these are much more common than anything else, it's worth a special test to avoid the rest of the procedure if there's nothing else to do. Note that "this" might already be pointing at an accidental entry -- so that extra space isn't inserted after it. Preserve this state of affairs. We compute "used" here specially, with a different default to the rest. */ if (accleft > 0) { used = (prevlength < 0)? 0 : pos_typewidth(7250, prevlength, prevflags); this = pos_insertXpos(previous, this, posx_acc, accleft, used, FALSE); if (xflags == 0) return this; } /* Insert positions for grace notes. This is messy. We give the widths as including the accidental widths so as to space them out correctly, but then we have to go back afterwards and correct the positions. We also have to take special action if there are already grace notes at this position, in case the accidentals are different (rare). "Used" also gets a special value for grace notes. */ if (gracevector[0] > 0) { int lastspacing = curmovt->gracespacing[0]; int first = 0; workposstr *pp = this; workposstr *p = this - 1; if (this >= page_posptr) lastspacing += ((xflags & xf_rrepeat) == 0)? 4000 : 8000; used = (prevlength < 0)? 0 : pos_typewidth(7250, prevlength, prevflags); /* As we process them from right to left, any new ones will be inserted first. When we get to pre-existing ones, save where to stop the moving scan, and instead just check existing widths. */ for (i = gracevector[0]; i >= 1; i--) { int id = posx_gracefirst + i - 1; if (pp == page_postable || p->auxid != id) { this = pos_insertXpos(previous, this, id, ((i == gracevector[0])? lastspacing : curmovt->gracespacing[1]) + gracevector[i], used, FALSE); } else { int spacing = (p->xoff == 0)? 0 : curmovt->gracespacing[1]; /* When we hit what was the last (rightmost) gracenote, but is no longer, adjust the appropriate spacing, just in case the gracespacing values are different. This is the first of the pre-existing grace notes. Remember which it was. */ if (first == 0) { int adjust = curmovt->gracespacing[1] - curmovt->gracespacing[0]; (p+1)->xoff += adjust; first = i; } if (p->xoff < spacing + gracevector[i]) p->xoff = spacing + gracevector[i]; p--; pp--; } } /* Go back from right to left and adjust positions, starting at the first additional grace note, and stopping when we get to any that were there before. */ p = this - 1; while (p->auxid == posx_acc) p--; pp = p + 1; for (i = gracevector[0]; i > first; i--) { p->xoff += gracevector[i]; pp->xoff -= gracevector[i]; pp = p--; } } /* Compute the space used by the previous note, if any, for the rest of the items. */ used = (prevlength < 0)? 0 : pos_typewidth(11000, prevlength, prevflags); /* Time Signature(s) */ for (i = timevector[0]; i >= 1; i--) this = pos_insertXpos(previous, this, posx_timefirst + i - 1, timevector[i], used, FALSE); /* Key Signature(s) */ for (i = keyvector[0]; i >= 1; i--) this = pos_insertXpos(previous, this, posx_keyfirst + i - 1, keyvector[i], used, FALSE); /* Left repeat. Check whether it is going to print on a bar line, and if not, insert a position for it, unless it is going to coincide with a right repeat. If it is on a barline, set a flag so that space can be inserted. (Can't insert here, 'cause it will then do it several times for multiple staves.) */ if ((xflags & xf_lrepeat) != 0) { if (previous == NULL && !page_startlinebar && pos_bp->posxRL == -posx_RLleft) pos_barstartrepeat = TRUE; else if (previous != NULL && (xflags & xf_rrepeat) != 0) rrepeatextra = 6500; else this = pos_insertXpos(previous, this, -pos_bp->posxRL, 12500, used, TRUE); } if ((xflags & xf_clef) != 0) this = pos_insertXpos(previous, this, posx_clef, (14 * (curmovt->fontsizes)->fontsize_clefs)/10, used, FALSE); if ((xflags & xf_rrepeat) != 0) { int x = 7500; if (this == page_posptr) page_lastendwide = TRUE; else x += 5100; this = pos_insertXpos(previous, this, posx_RR, x+rrepeatextra, used, TRUE); } if ((xflags & xf_dotbar) != 0) this = pos_insertXpos(previous, this, posx_dotbar, 6000, used, FALSE); if ((xflags & xf_tick) != 0) this = pos_insertXpos(previous, this, posx_tick, 6000, used, FALSE); if ((xflags & xf_comma) != 0) this = pos_insertXpos(previous, this, posx_comma, 6000, used, FALSE); if ((xflags & xf_caesura) != 0) this = pos_insertXpos(previous, this, posx_caesura, 13000, used, FALSE); return this; /* return for next previous */ } /************************************************* * Calculate typographic width * *************************************************/ /* This procedure calculates the magnified typographic width for a note of given length, with given flags. For a chord, the flags should be the 'or' of those for all the notes. Arguments: used the basic minimum width for the note (unmagnified) length the note's musical length (identifies the note) flags the note's flags Returns: the valued of "used" plus the calculated width */ int pos_typewidth(int used, int length, int flags) { /* Invisible notes use nothing, breves and semibreves need some extra, as do freestanding upquavers (to allow for the tail on the right). */ if ((flags & nf_hidden) != 0) return 0; if (length >= len_breve) used += 3300; else if (length >= len_semibreve) used += 800; else if ((flags & nf_fuq) != 0) used += 5000; /* Extra width for chord with inverted note and stem up, and even more if dotted. */ if ( (flags & (nf_stemup|nf_invert)) == (nf_stemup|nf_invert) && used < 12400) { used = 12400; if ((flags & (nf_dot | nf_plus)) != 0) used += 2000; } /* Extra width for dots or plus */ if ((flags & nf_plus) != 0) used += 8000; else if ((flags & nf_dot) != 0) { used += 3000; if ((flags & nf_dot2) != 0) used += 3500; } /* Allow for magnification */ return mac_muldiv(used, main_stavemagn, 1000); } /************************************************* * Calculate horizontal width * *************************************************/ /* This procedure calculates the magnified basic horizontal width required for a note of a given musical length. Argument: the note's musical length Returns: the magnified width */ int pos_notewidth(int length) { int thislength = len_breve; int type = breve; int offset; while (length < thislength) { type++; thislength /= 2; } /* Notes shorter than a hemi-demi-semiquaver can only be created by triplets or the like. These are unlikely ever to occur. Just ensure something non-crashy happens. */ if (type > hdsquaver) { offset = 0; type = hdsquaver + 1; } else offset = page_nextdata->notespacing[type]; if (length != thislength) { int extra = length - thislength; if (extra == thislength/2) { offset = mac_muldiv(offset, curmovt->dotspacefactor, 1000); } else if (extra == (3*thislength)/4) offset = mac_muldiv(offset, 3*curmovt->dotspacefactor-1000, 2000); /* We have a triplet or similar. Breve triplets are rarer than hen's teeth, so fudge the "next note" spacing to make things work. We then set the offset as a pro rata amount between the relevant two kinds of note. */ else { int nextup = (type == breve)? (3*offset)/2 : page_nextdata->notespacing[type-1]; offset += mac_muldiv(nextup-offset, extra, thislength); } } /* When we are re-evaluating bar widths because a system is being squashed, it works best if the notewidths themselves are squashed at this point. The layout of the underlay can then be better computed. */ if (page_layout_stretchn < page_layout_stretchd) offset = mac_muldiv(offset, page_layout_stretchn, page_layout_stretchd); /* Now apply the magnification factor and return */ return mac_muldiv(offset, main_stavemagn, 1000); } /* End of possubs.c */