/* * Parsing functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2007 Jean-François Moine * Adapted from abc2ps, Copyright (C) 1996,1997 Michael Methfessel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "abcparse.h" #include "abc2ps.h" struct STAFF_S staff_tb[MAXSTAFF]; /* staff table */ int nstaff; /* (0..MAXSTAFF-1) */ struct VOICE_S voice_tb[MAXVOICE]; /* voice table */ static struct VOICE_S *curvoice; /* current voice while parsing */ struct VOICE_S *first_voice; /* first voice */ struct FORMAT dfmt; /* current global format */ unsigned short *micro_tb; /* ptr to the microtone table of the tune */ static int lyric_nb; /* current number of lyric lines */ static struct SYMBOL *lyric_start; /* 1st note of the line for w: */ static struct SYMBOL *lyric_cont; /* current symbol when w: continuation */ static struct SYMBOL *grace_head, *grace_tail; static struct SYMBOL *voice_over; /* voice overlay */ static int over_bar; /* voice overlay in a measure */ static int staves_found; static int abc2win; static int bar_number; /* (for %%setbarnb) */ float multicol_start; /* (for multicol) */ static float multicol_max; static float lmarg, rmarg; static void get_clef(struct SYMBOL *s); static void get_key(struct SYMBOL *s); static void get_meter(struct SYMBOL *s); static void get_voice(struct SYMBOL *s); static void get_note(struct SYMBOL *s); static struct abcsym *process_pscomment(struct abcsym *as); static void set_tuplet(struct SYMBOL *s); static void sym_link(struct SYMBOL *s, int type); /* -- duplicate the symbols of the voices appearing in many staves -- */ void voice_dup(void) { struct VOICE_S *p_voice, *p_voice1; struct SYMBOL *s, *s2, *g, *g2; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { int voice; if (p_voice->clone < 0) continue; voice = p_voice - voice_tb; p_voice1 = &voice_tb[(unsigned) p_voice->clone]; p_voice->name = p_voice1->name; for (s = p_voice1->sym; s != 0; s = s->next) { s2 = (struct SYMBOL *) getarena(sizeof *s2); memcpy(s2, s, sizeof *s2); if (p_voice->sym != 0) { p_voice->last_symbol->next = s2; s2->prev = p_voice->last_symbol; } else p_voice->sym = s2; p_voice->last_symbol = s2; s2->voice = voice; s2->staff = p_voice->staff; s2->ly = 0; if ((g = s2->grace) != 0) { g2 = (struct SYMBOL *) getarena(sizeof *g2); memcpy(g2, g, sizeof *g2); s2->grace = g2; s2 = g2; s2->voice = voice; s2->staff = p_voice->staff; for (g = g->next; g != 0; g = g->next) { g2 = (struct SYMBOL *) getarena(sizeof *g2); memcpy(g2, g, sizeof *g2); s2->next = g2; g2->prev = s2; s2 = g2; s2->voice = voice; s2->staff = p_voice->staff; } } } } } /* -- change the accidentals and "\\n" in the guitar chords -- */ static void gchord_adjust(struct SYMBOL *s) { char *p; int freegchord, l; s->gcf = cfmt.gcf; s->anf = cfmt.anf; freegchord = cfmt.freegchord; p = s->as.text; if (*p != '\0' && strchr("^_<>@", *p) != 0) freegchord = 1; /* annotation */ /*fixme: treat 'dim' as 'o', 'halfdim' as '/o', and 'maj' as a triangle*/ while (*p != '\0') { switch (*p) { case '#': if (!freegchord) *p = '\201'; break; case 'b': if (!freegchord) *p = '\202'; break; case '=': if (!freegchord) *p = '\203'; break; case '\\': p++; switch (*p) { case '\0': return; case 'n': p[-1] = ';'; goto move; case '#': p[-1] = '\201'; goto move; case 'b': p[-1] = '\202'; goto move; case '=': p[-1] = '\203'; move: l = strlen(p); memmove(p, p + 1, l); p--; break; } break; } if (*p == ';' && p[-1] != '\\') if (p[1] != '\0' && strchr("^_<>@", p[1]) != 0) *p = '\n'; if (*p == '\n') { if (p[1] != '\0' && strchr("^_<>@", p[1]) != 0) freegchord = 1; else freegchord = cfmt.freegchord; } p++; } } /* -- parse a lyric (vocal) definition -- */ static char *get_lyric(char *p) { struct SYMBOL *s; char word[128], *q; int ln; struct FONTSPEC *f; f = &cfmt.font_tb[cfmt.vof]; str_font(cfmt.vof); /* (for tex_str) */ if ((s = lyric_cont) == 0) { if (lyric_nb >= MAXLY) return "Too many lyric lines"; ln = lyric_nb++; s = lyric_start; } else { lyric_cont = 0; ln = lyric_nb - 1; } /* scan the lyric line */ while (*p != '\0') { while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; switch (*p) { case '|': while (s != 0 && (s->type != BAR || s->as.u.bar.type == B_OBRA || s->as.u.bar.type == B_CBRA)) s = s->next; if (s == 0) return "Not enough bar lines for lyric line"; s = s->next; p++; continue; case '-': word[0] = '\x02'; word[1] = '\0'; p++; break; case '_': word[0] = '\x03'; word[1] = '\0'; p++; break; case '*': word[0] = *p++; word[1] = '\0'; break; case '\\': if (p[1] == '\0') { lyric_cont = s; return 0; } /* fall thru */ default: q = word; for (;;) { unsigned char c; c = *p; switch (c) { case '\0': case ' ': case '\t': case '_': case '*': case '|': break; case '~': c = ' '; goto addch; case '-': c = '\x02'; goto addch; case '\\': if (p[1] == '\0') break; switch (p[1]) { case '~': case '_': case '*': case '|': case '-': case ' ': c = *++p; break; } /* fall thru */ default: addch: if (q < &word[sizeof word - 1]) *q++ = c; p++; if (c == '\x02') break; continue; } break; } *q = '\0'; break; } /* store word in next note */ while (s != 0 && s->type != NOTE) s = s->next; if (s == 0) return "Too many words in lyric line"; if (word[0] != '*') { struct lyl *lyl; float w; if (s->ly == 0) { s->ly = (struct lyrics *) getarena(sizeof (struct lyrics)); memset(s->ly, 0, sizeof (struct lyrics)); } w = tex_str(word); /* handle the font change at start of text */ q = tex_buf; if (*q == '$' && isdigit((unsigned char) q[1]) && (unsigned) (q[1] - '0') < FONT_UMAX) { int ft; ft = q[1] - '0'; if (ft == 0) ft = cfmt.vof; f = &cfmt.font_tb[ft]; str_font(ft); q += 2; } lyl = (struct lyl *) getarena(sizeof *s->ly->lyl[0] + strlen(q)); s->ly->lyl[ln] = lyl; lyl->f = f; lyl->w = w; strcpy(lyl->t, q); } s = s->next; } while (s != 0 && s->type != NOTE) s = s->next; if (s != 0) return "Not enough words for lyric line"; return 0; } /* -- get a voice overlay -- */ static void get_over(struct SYMBOL *s) { struct VOICE_S *p_voice, *p_voice2; int ctime; /* treat the end of overlay */ p_voice = curvoice; if (over_bar) { struct SYMBOL *s2; s2 = sym_add(p_voice, BAR); s2->as.type = ABC_T_BAR; s2->as.linenum = s->as.linenum; s2->as.colnum = s->as.colnum; } if (s->as.type == ABC_T_BAR || s->as.u.v_over.type == V_OVER_E) { over_bar = 0; if (voice_over == 0) { error(1, s, "Erroneous end of voice overlap"); return; } voice_over = 0; if (s->as.type != ABC_T_BAR) p_voice = &voice_tb[s->as.u.v_over.voice]; else for (p_voice = p_voice->prev; ; p_voice = p_voice->prev) if (p_voice->name[0] != '&') break; curvoice = p_voice; return; } /* treat the overlay start */ if (s->as.u.v_over.type == V_OVER_S) { voice_over = s; return; } /* create the extra voice if not done yet */ p_voice2 = &voice_tb[s->as.u.v_over.voice]; if (p_voice2->name == 0) { p_voice2->name = "&"; p_voice2->second = 1; p_voice2->cstaff = p_voice2->staff = p_voice->staff; p_voice2->scale = p_voice->scale; if ((p_voice2->next = p_voice->next) != 0) p_voice2->next->prev = p_voice2; p_voice->next = p_voice2; p_voice2->prev = p_voice; } if (voice_over == 0) { voice_over = s; over_bar = 1; ctime = p_voice2->time; for (s = p_voice->last_symbol; /*s != 0*/; s = s->prev) if (s->type == BAR || s->time <= ctime) break; } else { struct SYMBOL *tmp; tmp = s; s = (struct SYMBOL *) voice_over->as.next; /*fixme: what if this symbol is not in the voice?*/ if (s->voice != curvoice - voice_tb) { error(1, s, "Voice overlay not closed"); voice_over = 0; return; } voice_over = tmp; } ctime = s->time; p_voice2->time = ctime; curvoice = p_voice2; } /* -- get staves definition (%%staves) -- */ static void get_staves(struct SYMBOL *s) { int i, staff, flags0, dup_voice, v; struct staff_s *p_staff; struct VOICE_S *p_voice, *p_voice2; /* clear, then link the voices */ for (i = 0, p_voice = voice_tb; i < MAXVOICE; i++, p_voice++) { p_voice->clone = -1; p_voice->next = 0; p_voice->prev = 0; p_voice->second = 0; p_voice->floating = 0; if (p_voice->name != 0 && p_voice->name[0] == '&') p_voice->name = 0; } p_voice2 = 0; dup_voice = MAXVOICE; for (i = 0, p_staff = s->as.u.staves; i < MAXVOICE && p_staff->voice>= 0; i++, p_staff++) { p_voice = &voice_tb[p_staff->voice]; /* if voice already inserted, duplicate it */ if (p_voice == p_voice2 || p_voice->next || p_voice->prev) { struct VOICE_S *p_voice3; dup_voice--; p_voice3 = &voice_tb[dup_voice]; memcpy(p_voice3, p_voice, sizeof *p_voice3); p_voice3->clone = p_staff->voice; p_voice3->next = 0; p_voice3->second = 0; p_voice3->floating = 0; p_voice = p_voice3; p_staff->voice = dup_voice; } /* link the voices */ if ((p_voice->prev = p_voice2) == 0) first_voice = p_voice; else p_voice2->next = p_voice; p_voice2 = p_voice; } /* define the staves */ memset(staff_tb, 0, sizeof staff_tb); staff = -1; for (i = 0, p_staff = s->as.u.staves; i < MAXVOICE && p_staff->voice >= 0; i++, p_staff++) { flags0 = p_staff->flags[0]; #if MAXSTAFF < MAXVOICE if (staff >= MAXSTAFF - 1) { error(1, s, "Too many staves - aborting"); exit(2); } #endif staff++; p_voice = &voice_tb[p_staff->voice]; p_voice->staff = p_voice->cstaff = staff; if ((flags0 & (OPEN_PARENTH | CLOSE_PARENTH)) == (OPEN_PARENTH | CLOSE_PARENTH)) flags0 &= ~(OPEN_PARENTH | CLOSE_PARENTH); if (flags0 == 0 && p_staff->flags[1] == 0) continue; staff_tb[staff].flags[0] = flags0; staff_tb[staff].flags[1] = p_staff->flags[1]; if ((flags0 & OPEN_BRACE) || (p_staff->flags[1] & OPEN_BRACE)) { for (v = i + 1; v < MAXVOICE; v++) if ((s->as.u.staves[v].flags[0] & CLOSE_BRACE) || (s->as.u.staves[v].flags[1] & CLOSE_BRACE)) break; switch (v - i) { case 2: /* {a b c} */ if (flags0 & OPEN_PARENTH || (p_staff[1].flags[0] & OPEN_PARENTH)) break; i++; p_staff++; p_voice = &voice_tb[p_staff->voice]; p_voice->second = 1; p_voice->floating = 1; p_voice->staff = p_voice->cstaff = staff; break; case 3: /* {a b c d} */ if (flags0 & OPEN_PARENTH || (p_staff[1].flags[0] & OPEN_PARENTH) || (p_staff[2].flags[0] & OPEN_PARENTH)) break; /*fixme:to remove*/ /* -> {(a b) (c d)} */ flags0 |= OPEN_PARENTH; p_staff[1].flags[0] |= CLOSE_PARENTH; p_staff[2].flags[0] |= OPEN_PARENTH; p_staff[3].flags[0] |= CLOSE_PARENTH; break; } } if (flags0 & OPEN_PARENTH) { while (i < MAXVOICE) { i++; p_staff++; p_voice = &voice_tb[p_staff->voice]; p_voice->second = 1; p_voice->staff = p_voice->cstaff = staff; if (p_staff->flags[0] & CLOSE_PARENTH) break; } staff_tb[staff].flags[0] |= p_staff->flags[0]; staff_tb[staff].flags[1] |= p_staff->flags[1]; } } if (staff < 0) staff = 0; nstaff = staff; if (cfmt.alignbars) cfmt.alignbars = nstaff + 1; } /* -- re-initialize all potential voices -- */ static void voice_init(void) { struct VOICE_S *p_voice; int i; for (i = 0, p_voice = voice_tb; i < MAXVOICE; i++, p_voice++) { p_voice->sym = p_voice->last_symbol = 0; p_voice->clone = -1; p_voice->bar_start = 0; p_voice->time = 0; p_voice->slur_st = 0; p_voice->hy_st = 0; p_voice->tie = 0; p_voice->rtie = 0; } } /* -- output the music and lyrics after tune -- */ static void out_musly(int eob) { output_music(); if (info['W' - 'A'] != 0) { put_words(info['W' - 'A']); info['W' - 'A'] = 0; } if (eob) buffer_eob(); } /* -- identify info line, store in proper place -- */ static char *state_txt[4] = { "global", "header", "tune", "embedded" }; static void get_info(struct SYMBOL *s, int info_type, struct abctune *t) { INFO *inf; char *p; /* change global or local */ inf = s->as.state == ABC_S_GLOBAL ? &default_info : &info; lvlarena(s->as.state != ABC_S_GLOBAL); switch (info_type) { case 'd': break; case 'I': process_pscomment(&s->as); /* same as pseudo-comment */ break; case 'K': get_key(s); if (s->as.state != ABC_S_HEAD) break; tunenum++; PUT2("%% --- %s (%s) ---\n" "%% --- font ", &info['X' - 'A']->as.text[2], &info['T' - 'A']->as.text[2]); outft = -1; set_font(TITLEFONT); /* for index */ outft = -1; PUT0("\n"); if (!epsf) bskip(cfmt.topspace); write_heading(t); reset_gen(); if (!cfmt.printtempo) info['Q' - 'A'] = 0; nbar = nbar_rep = cfmt.measurefirst; /* measure numbering */ curvoice = first_voice; /* switch to the 1st voice */ break; case 'L': break; case 'M': get_meter(s); break; case 'P': if (!cfmt.printparts) break; switch (s->as.state) { case ABC_S_GLOBAL: case ABC_S_HEAD: (*inf)['P' - 'A'] = s; break; case ABC_S_TUNE: { struct VOICE_S *p_voice; p_voice = curvoice; curvoice = first_voice; sym_link(s, PART); if (p_voice->time != curvoice->time) { error(1, s, "Misplaced P:"); if (p_voice->time > curvoice->time) s->time = curvoice->time = p_voice->time; else p_voice->time = curvoice->time; } curvoice = p_voice; break; } default: sym_link(s, PART); break; } break; case 'Q': if (curvoice != first_voice) /* tempo only for first voice */ break; switch (s->as.state) { case ABC_S_GLOBAL: case ABC_S_HEAD: (*inf)['Q' - 'A'] = s; break; default: if (cfmt.printtempo) sym_link(s, TEMPO); break; } break; case 's': break; case 'T': switch (s->as.state) { case ABC_S_GLOBAL: /* T: without X: */ goto newtune; case ABC_S_HEAD: goto addinfo; default: out_musly(1); write_title(s); bskip(cfmt.musicspace + 0.2 CM); voice_init(); reset_gen(); /* (display the time signature) */ curvoice = first_voice; break; } break; case 'U': { unsigned char *deco; deco = s->as.state == ABC_S_GLOBAL ? deco_glob : deco_tune; deco[s->as.u.user.symbol] = deco_intern(s->as.u.user.value); break; } case 'u': break; case 'V': get_voice(s); break; case 'w': if (cfmt.musiconly || s->as.state != ABC_S_TUNE) break; if (lyric_start == 0) break; p = &s->as.text[2]; while (isspace((unsigned char) *p)) p++; if ((p = get_lyric(p)) != 0) error(1, s, "%s", p); curvoice->have_ly = 1; break; case 'W': if (s->as.state == ABC_S_GLOBAL) break; goto addinfo; case 'X': newtune: if (!epsf) write_buffer(); /* flush stuff left from %% lines */ dfmt = cfmt; /* save the format at start of tune */ memcpy(&info, &default_info, sizeof info); info['X' - 'A'] = s; if (info_type == 'T') info['T' - 'A'] = s; memcpy(&deco_tune, &deco_glob, sizeof deco_tune); voice_init(); /* initialize all the voices */ break; default: addinfo: if (info_type >= 'A' && info_type <= 'Z') { struct SYMBOL *prev; prev = (*inf)[info_type - 'A']; if (prev == 0 || (prev->as.state == ABC_S_GLOBAL && s->as.state != ABC_S_GLOBAL)) { (*inf)[info_type - 'A'] = s; break; } while (prev->next != 0) prev = prev->next; prev->next = s; s->prev = prev; break; } error(1, s, "%s info '%c:' not treated", state_txt[(int) s->as.state], info_type); break; } } /* -- set head type, dots, flags for note -- */ void identify_note(struct SYMBOL *s, int dur, int *p_head, int *p_dots, int *p_flags) { int head, dots, flags; if (dur % 12 != 0) error(1, s, "Invalid note duration"); dur /= 12; /* see BASE_LEN for values */ if (dur == 0) error(1, s, "Note too short"); for (flags = 5; dur != 0; dur >>= 1, flags--) { if (dur & 1) break; } dur >>= 1; switch (dur) { case 0: dots = 0; break; case 1: dots = 1; break; case 3: dots = 2; break; case 7: dots = 3; break; default: error(1, s, "Note too much dotted"); dots = 3; break; } flags -= dots; if (flags >= 0) head = H_FULL; else switch (flags) { default: error(1, s, "Note too long"); flags = -4; /* fall thru */ case -4: head = H_SQUARE; break; case -3: head = cfmt.squarebreve ? H_SQUARE : H_OVAL; break; case -2: head = H_OVAL; break; case -1: head = H_EMPTY; break; } *p_head = head; *p_flags = flags; *p_dots = dots; } /* -- measure bar -- */ static void get_bar(struct SYMBOL *s) { int bar_type, t; struct SYMBOL *s2; bar_type = s->as.u.bar.type; /* remove the repeat indication if not wanted */ if (curvoice->norepbra && s->as.u.bar.repeat_bar) return; /* remove the invisible repeat bars when no shift needed */ if (bar_type == B_OBRA && (curvoice == first_voice || curvoice->second || (staff_tb[curvoice->staff - 1].flags[0] & STOP_BAR))) { s2 = curvoice->last_symbol; if (s2 != 0 && s2->type == BAR && s2->as.text == 0) { s2->as.text = s->as.text; s2->as.u.bar.repeat_bar = s->as.u.bar.repeat_bar; return; } } /* merge back-to-back repeat bars */ if (bar_type == B_LREP && s->as.text == 0) { s2 = curvoice->last_symbol; t = curvoice->time; while (s2 != 0 && s2->time == t) { if (s2->type == BAR && s2->as.u.bar.type == B_RREP) { s2->as.u.bar.type = B_DREP; return; } s2 = s2->prev; } } sym_link(s, BAR); if ((bar_type & 0xf0) != 0) { do { bar_type >>= 4; } while ((bar_type & 0xf0) != 0); if (bar_type == B_COL) s->sflags |= S_RRBAR; } if (bar_number != 0 && curvoice == first_voice) { s->u = bar_number; bar_number = 0; } /* the bar must be before a key signature */ /*fixme: and also before a time signature*/ if ((s2 = s->prev) != 0 && s2->type == KEYSIG && (s2->prev == 0 || s2->prev->type != BAR)) { curvoice->last_symbol = s2; s2->next = 0; s2->prev->next = s; s->prev = s2->prev; s->next = s2; s2->prev = s; } if (s->as.u.bar.dc.n > 0) deco_cnv(&s->as.u.bar.dc, s); /* convert the decorations */ if (s->as.text != 0 && !s->as.u.bar.repeat_bar) gchord_adjust(s); /* adjust the guitar chords */ } /* -- do a tune -- */ void do_tune(struct abctune *t, int header_only) { struct abcsym *as; struct SYMBOL *s, *s2; int i; /* initialize */ memset(voice_tb, 0, sizeof voice_tb); voice_tb[0].name = "1"; /* implicit voice */ voice_over = 0; nstaff = 0; memset(staff_tb, 0, sizeof staff_tb); staves_found = 0; for (i = MAXVOICE; --i >= 0; ) { voice_tb[i].clef.line = 2; /* treble clef on 2nd line */ voice_tb[i].clef.stafflines = 5; voice_tb[i].clef.staffscale = 1; voice_tb[i].meter.nmeter = 1; voice_tb[i].meter.wmeasure = BASE_LEN; voice_tb[i].meter.meter[0].top[0] = '4'; voice_tb[i].meter.meter[0].bot[0] = '4'; voice_tb[i].wmeasure = BASE_LEN; voice_tb[i].scale = 1; } voice_init(); /* initialize all the voices */ for (i = 0; i < nwhistle; i++) voice_tb[whistle_tb[i].voice].whistle = 1; micro_tb = t->micro_tb; /* microtone values */ curvoice = first_voice = voice_tb; if (cfmt.oneperpage) { use_buffer = 0; close_page(); } else use_buffer = !cfmt.splittune; /* set the note duration - this is needed for tuplets */ if (!header_only) { for (as = t->first_sym; as != 0; as = as->next) { if (as->type != ABC_T_NOTE && as->type != ABC_T_REST) continue; s = (struct SYMBOL *) as; s->dur = s->as.u.note.lens[0]; } } /* scan the tune */ grace_head = 0; for (as = t->first_sym; as != 0; as = as->next) { if (header_only && as->state != ABC_S_GLOBAL) break; s = (struct SYMBOL *) as; switch (as->type) { case ABC_T_INFO: { int info_type; switch (as->text[0]) { case 'X': case 'T': case 'W': if (header_only) continue; } info_type = as->text[0]; for (;;) { get_info(s, info_type, t); if (as->next == 0 || as->next->type != ABC_T_INFO2) break; as = as->next; s = (struct SYMBOL *) as; } break; } case ABC_T_PSCOM: as = process_pscomment(as); break; case ABC_T_NOTE: case ABC_T_REST: get_note(s); break; case ABC_T_BAR: if (over_bar) get_over(s); get_bar(s); break; case ABC_T_CLEF: get_clef(s); break; case ABC_T_EOLN: if (cfmt.continueall || cfmt.barsperstaff || as->u.eoln.type == 1) /* if '\' */ continue; if (as->u.eoln.type != 0) { /* if '!' */ if (!abc2win) { /* if 1st time */ struct VOICE_S *p_voice; abc2win = 1; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { for (s = p_voice->sym; s != 0; s = s->next) { if (s->sflags & S_EOLN) s->sflags &= ~S_EOLN; } } } } else if (abc2win) continue; if (curvoice->last_symbol != 0) curvoice->last_symbol->sflags |= S_EOLN; if (!cfmt.alignbars) continue; i = (curvoice - voice_tb) + 1; if (i < cfmt.alignbars) { curvoice = &voice_tb[i]; continue; } out_musly(1); curvoice = &voice_tb[0]; continue; case ABC_T_MREST: { int dur; dur = curvoice->wmeasure * as->u.bar.len; if (curvoice->second) { curvoice->time += dur; break; } sym_link(s, MREST); s->dur = dur; if (s->as.text != 0) /* adjust the */ gchord_adjust(s); /* guitar chords */ if (s->as.u.bar.dc.n > 0) deco_cnv(&s->as.u.bar.dc, s); break; } case ABC_T_MREP: { int n; if (as->next == 0 || as->next->type != ABC_T_BAR) { error(1, s, "Measure repeat not followed by a bar"); break; } n = as->u.bar.len; if (curvoice->second) { curvoice->time += curvoice->wmeasure * n; break; } s2 = sym_add(curvoice, REST); s2->as.type = ABC_T_REST; s2->as.linenum = as->linenum; s2->as.colnum = as->colnum; s2->as.flags |= ABC_F_INVIS; s2->dur = curvoice->wmeasure; curvoice->time += curvoice->wmeasure; if (n == 1) { as->next->u.bar.len = n; /* in the next bar */ break; } while (--n > 0) { s2 = sym_add(curvoice, BAR); s2->as.linenum = as->linenum; s2->as.colnum = as->colnum; s2->as.u.bar.type = B_SINGLE; if (n == as->u.bar.len - 1) s2->as.u.bar.len = as->u.bar.len; s2 = sym_add(curvoice, REST); s2->as.type = ABC_T_REST; s2->as.linenum = as->linenum; s2->as.colnum = as->colnum; s2->as.flags |= ABC_F_INVIS; s2->dur = curvoice->wmeasure; curvoice->time += curvoice->wmeasure; } break; } case ABC_T_V_OVER: get_over(s); continue; case ABC_T_TUPLET: set_tuplet(s); break; default: continue; } if (s->type == 0) continue; if (s->dur != 0 && grace_head == 0) curvoice->time += s->dur; else if (s->as.flags & ABC_F_GR_END) grace_head = 0; } out_musly(0); if (!header_only) { if (cfmt.writehistory) put_history(); } buffer_eob(); if (epsf) { if (nbuf > 0) write_eps(); } else write_buffer(); if (info['X' - 'A'] != 0) { cfmt = dfmt; /* restore the format at start of tune */ info['X' - 'A'] = 0; } } /* -- get a clef definition (in K: or V:) -- */ static void get_clef(struct SYMBOL *s) { struct VOICE_S *p_voice; struct SYMBOL *s2; int i, stafflines; float staffscale; p_voice = curvoice; if (s->as.prev->type == ABC_T_INFO) { switch (s->as.prev->text[0]) { case 'K': if (s->as.prev->state == ABC_S_HEAD) { if (s->as.u.clef.type >= 0) { for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { stafflines = p_voice->clef.stafflines; staffscale = p_voice->clef.staffscale; memcpy(&p_voice->clef, &s->as.u.clef, sizeof p_voice->clef); p_voice->clef.stafflines = stafflines; p_voice->clef.staffscale = staffscale; p_voice->forced_clef = 1; } } if ((stafflines = s->as.u.clef.stafflines) >= 0) { for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) p_voice->clef.stafflines = stafflines; } if ((staffscale = s->as.u.clef.staffscale) != 0) { for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) p_voice->clef.staffscale = staffscale; } return; } break; case 'V': /* clef relative to a voice definition */ p_voice = &voice_tb[(int) s->as.prev->u.voice.voice]; break; } } if (p_voice->sym == 0) { i = p_voice->staff; if ((stafflines = s->as.u.clef.stafflines) < 0) stafflines = p_voice->clef.stafflines; if ((staffscale = s->as.u.clef.staffscale) == 0) staffscale = p_voice->clef.staffscale; if (s->as.u.clef.type >= 0) { memcpy(&p_voice->clef, &s->as.u.clef, /* initial clef */ sizeof p_voice->clef); } p_voice->clef.stafflines = stafflines; p_voice->clef.staffscale = staffscale; } else { sym_link(s, CLEF); s->u = 1; /* small clef */ /* the clef change must be before a key signature */ s2 = s->prev; if (s2->type == KEYSIG) { s2->next = 0; p_voice->last_symbol = s2; if ((s->prev = s2->prev) != 0) s->prev->next = s; s->next = s2; s2->prev = s; } /* the clef change must be before a bar */ s2 = s->prev; if (s2 != 0 && s2->type == BAR) { if ((s2->next = s->next) != 0) s->next->prev = s2; else p_voice->last_symbol = s2; if ((s->prev = s2->prev) != 0) s->prev->next = s; s->next = s2; s2->prev = s; } } if (s->as.u.clef.type >= 0) p_voice->forced_clef = 1; /* don't change */ } /* -- get a key signature definition (K:) -- */ static void get_key(struct SYMBOL *s) { struct VOICE_S *p_voice; struct SYMBOL *s2; int i; if (s->as.u.key.empty == 1) return; /* clef only */ switch (s->as.state) { case ABC_S_HEAD: for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { memcpy(&p_voice->key, &s->as.u.key, sizeof p_voice->key); p_voice->sfp = s->as.u.key.sf; if (p_voice->key.bagpipe && p_voice->stem == 0) p_voice->stem = -1; } break; case ABC_S_TUNE: case ABC_S_EMBED: if (curvoice->sym == 0) { /* if first symbol of the first voice, apply to all voices */ if (curvoice == first_voice && s->as.state == ABC_S_TUNE) { /* (not embedded) */ for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { memcpy(&p_voice->key, &s->as.u.key, sizeof p_voice->key); p_voice->sfp = s->as.u.key.sf; if (p_voice->key.bagpipe && p_voice->stem == 0) p_voice->stem = -1; } } else { memcpy(&curvoice->key, &s->as.u.key, sizeof curvoice->key); curvoice->sfp = s->as.u.key.sf; if (curvoice->key.bagpipe && curvoice->stem == 0) curvoice->stem = -1; } break; } if (curvoice->sfp == s->as.u.key.sf /* if same key */ && s->as.next->type != ABC_T_CLEF) /* but not explicit clef */ break; /* ignore */ sym_link(s, KEYSIG); s->u = curvoice->sfp; /* old key signature */ curvoice->sfp = s->as.u.key.sf; /* the key signature must be just after a bar */ if ((s2 = s->prev) != 0 && s2->prev != 0) { switch (s2->type) { case TIMESIG: case TEMPO: case PART: /* case FMTCHG: */ curvoice->last_symbol = s2; s2->next = 0; s2->prev->next = s; s->prev = s2->prev; s->next = s2; s2->prev = s; break; } } break; } } /* -- set meter from M: -- */ static void get_meter(struct SYMBOL *s) { struct VOICE_S *p_voice; int i; switch (s->as.state) { case ABC_S_GLOBAL: /*fixme: keep the values and apply to all tunes?? */ break; case ABC_S_HEAD: { for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { memcpy(&p_voice->meter, &s->as.u.meter, sizeof p_voice->meter); p_voice->wmeasure = s->as.u.meter.wmeasure; } break; } case ABC_S_TUNE: case ABC_S_EMBED: curvoice->wmeasure = s->as.u.meter.wmeasure; if (curvoice->sym == 0) { /* if first symbol of the first voice, apply to all voices */ if (curvoice == first_voice && s->as.state == ABC_S_TUNE) { /* (not embedded) */ for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { memcpy(&p_voice->meter, &s->as.u.meter, sizeof p_voice->meter); p_voice->wmeasure = s->as.u.meter.wmeasure; } } else memcpy(&curvoice->meter, &s->as.u.meter, sizeof curvoice->meter); reset_gen(); /* (display the time signature) */ break; } if (s->as.u.meter.nmeter == 0) break; /* M:none */ sym_link(s, TIMESIG); break; } } /* -- treat a 'V:' -- */ static void get_voice(struct SYMBOL *s) { int voice; struct VOICE_S *p_voice; voice = s->as.u.voice.voice; p_voice = &voice_tb[voice]; if (p_voice->prev == 0 && p_voice != first_voice) { /* new voice */ struct VOICE_S *p_voice2; if (cfmt.alignbars) { error(1, s, "V: does not work with %%%%alignbars"); } if (!staves_found) { if (!s->as.u.voice.merge) { #if MAXSTAFF < MAXVOICE if (nstaff >= MAXSTAFF - 1) { error(1, s, "Too many staves"); return; } #endif nstaff++; } else p_voice->second = 1; p_voice->staff = p_voice->cstaff = nstaff; for (p_voice2 = first_voice; p_voice2->next != 0; p_voice2 = p_voice2->next) ; p_voice2->next = p_voice; p_voice->prev = p_voice2; } else p_voice->staff = p_voice->cstaff = nstaff + 1; } /* if in tune, switch to this voice */ switch (s->as.state) { case ABC_S_TUNE: case ABC_S_EMBED: curvoice = p_voice; break; } /* if something has changed, update */ if (s->as.u.voice.name != 0) p_voice->name = s->as.u.voice.name; if (s->as.u.voice.fname != 0) { p_voice->nm = s->as.u.voice.fname; p_voice->new_name = 1; } if (s->as.u.voice.nname != 0) p_voice->snm = s->as.u.voice.nname; if (s->as.u.voice.stem != 0) p_voice->stem = s->as.u.voice.stem != 2 ? s->as.u.voice.stem : 0; if (s->as.u.voice.gstem != 0) p_voice->gstem = s->as.u.voice.gstem != 2 ? s->as.u.voice.gstem : 0; if (s->as.u.voice.dyn != 0) p_voice->dyn = s->as.u.voice.dyn != 2 ? s->as.u.voice.dyn : 0; if (s->as.u.voice.lyrics != 0) p_voice->ly_pos = s->as.u.voice.lyrics != 2 ? s->as.u.voice.lyrics : 0; if (s->as.u.voice.scale != 0) p_voice->scale = s->as.u.voice.scale; } /* -- note or rest -- */ static void get_note(struct SYMBOL *s) { int i, m, type; s->nhd = m = s->as.u.note.nhd; type = s->as.type == ABC_T_NOTE ? NOTE : REST; if (!(s->as.flags & ABC_F_GRACE)) { /* normal note/rest */ if (grace_head != 0) grace_head = 0; sym_link(s, type); s->stem = curvoice->stem; } else { /* in a grace note sequence */ int div; if (grace_head == 0) { struct SYMBOL *s2; s2 = sym_add(curvoice, GRACE); s2->as.linenum = s->as.linenum; s2->as.colnum = s->as.colnum; grace_head = s2; grace_head->grace = grace_tail = s; s2->stem = curvoice->gstem; } else { grace_tail->next = s; s->prev = grace_tail; grace_tail = s; } s->voice = curvoice - voice_tb; s->staff = curvoice->cstaff; /* adjust the grace note duration */ if (!curvoice->key.bagpipe) { div = 4; if (s->prev == 0) { if (s->as.next == 0 || s->as.next->type != ABC_T_NOTE || !(s->as.next->flags & ABC_F_GRACE)) div = 2; } } else div = 8; for (i = 0; i <= m; i++) s->as.u.note.lens[i] /= div; s->dur = s->as.u.note.lens[0]; s->type = type; } s->nohdix = -1; /* convert the decorations */ if (s->as.u.note.dc.n > 0) deco_cnv(&s->as.u.note.dc, s); /* change the figure of whole measure rests */ if (s->type == REST) { if (s->dur == curvoice->wmeasure) { if (s->dur < BASE_LEN * 2) s->as.u.note.lens[0] = BASE_LEN; else if (s->dur < BASE_LEN * 4) s->as.u.note.lens[0] = BASE_LEN * 2; else s->as.u.note.lens[0] = BASE_LEN * 4; } } /* sort by pitch the notes of the chord (lowest first) */ else for (;;) { int nx = 0; for (i = 1; i <= m; i++) { if (s->as.u.note.pits[i] < s->as.u.note.pits[i-1]) { int k; #define xch(f) \ k = s->as.u.note.f[i]; \ s->as.u.note.f[i] = s->as.u.note.f[i-1]; \ s->as.u.note.f[i-1] = k xch(pits); xch(lens); xch(accs); xch(sl1); xch(sl2); xch(ti1); xch(decs); #undef xch nx++; } } if (nx == 0) break; } memcpy(s->pits, s->as.u.note.pits, sizeof s->pits); /* get the max head type, number of dots and number of flags */ { int head, dots, nflags, l; if ((l = s->as.u.note.lens[0]) != 0) { identify_note(s, l, &head, &dots, &nflags); s->head = head; s->dots = dots; if (!(s->sflags & S_TREM)) { s->nflags = nflags; for (i = 1; i <= m; i++) { if (s->as.u.note.lens[i] == l) continue; identify_note(s, s->as.u.note.lens[i], &head, &dots, &nflags); if (head > s->head) s->head = head; if (dots > s->dots) s->dots = dots; if (nflags > s->nflags) s->nflags = nflags; } if (s->sflags & S_XSTEM) s->nflags = 0; /* word start+end */ } else { /* 2nd tremolo note */ s->prev->head = head; if (head >= H_OVAL) { s->as.flags |= ABC_F_STEMLESS; s->prev->as.flags |= ABC_F_STEMLESS; } } } } if (s->nflags <= -2) s->as.flags |= ABC_F_STEMLESS; for (i = 0; i <= m; i++) { if (s->as.u.note.sl1[i] != 0) s->sflags |= S_SL1; if (s->as.u.note.sl2[i] != 0) s->sflags |= S_SL2; if (s->as.u.note.ti1[i] != 0) s->sflags |= S_TI1; } if (s->as.flags & ABC_F_LYRIC_START) { lyric_start = s; lyric_cont = 0; lyric_nb = 0; } /* adjust the guitar chords */ if (s->as.text != 0) gchord_adjust(s); } /* -- treat a postscript definition -- */ static void ps_def(struct SYMBOL *s, char *p) { if (s->as.state == ABC_S_TUNE || s->as.state == ABC_S_EMBED) { sym_link(s, FMTCHG); s->u = PSSEQ; s->as.text = p; return; } if (file_initialized) { PUT1("%s\n", p); return; } interpret_fmt_line("postscript", p, 0); } /* -- process a pseudo-comment (%% or I:) -- */ static struct abcsym *process_pscomment(struct abcsym *as) { char w[32], *p; float h1; struct SYMBOL *s = (struct SYMBOL *) as; p = as->text + 2; /* skip '%%' */ while (isspace((unsigned char) *p)) p++; if (strncasecmp(p, "fmt ", 4) == 0) { p += 4; /* skip 'fmt' */ while (isspace((unsigned char) *p)) p++; } p = get_str(w, p, sizeof w); switch (w[0]) { case 'b': if (strcmp(w, "beginps") == 0) { for (;;) { if (as->next == 0) return as; as = as->next; p = as->text; if (*p == '%' && p[1] == '%') { p += 2; while (isspace((unsigned char) *p)) p++; if (strncasecmp(p, "fmt ", 4) == 0) { p += 4; while (isspace((unsigned char) *p)) p++; } if (strncmp(p, "endps", 5) == 0) return as; } if (*p == '%') continue; /* skip comment lines */ s = (struct SYMBOL *) as; ps_def(s, p); } /* not reached */ } if (strcmp(w, "begintext") == 0) { int job; if (epsf && as->state != ABC_S_HEAD) return as; if ((job = cfmt.textoption) == T_SKIP) return as; if (*p != '\0') { job = get_textopt(p); if (job < 0) { error(1, s, "Bad argument for begintext: %s", p); job = T_LEFT; } } out_musly(1); for (;;) { if (as->next == 0) return as; as = as->next; p = as->text; if (*p == '%' && p[1] == '%') { p += 2; while (isspace((unsigned char) *p)) p++; if (strncasecmp(p, "fmt ", 4) == 0) { p += 4; while (isspace((unsigned char) *p)) p++; } if (strncmp(p, "endtext", 7) == 0) { if (job != T_SKIP) write_text_block(job, as->state); return as; } } if (job != T_SKIP) add_to_text_block(p, job); } /* not reached */ } break; case 'E': if (strcmp(w, "EPS") == 0) { float x1, y1, x2, y2; FILE *fp; char fn[BSIZE], line[BSIZE]; out_musly(1); if (cfmt.textoption == T_SKIP) return as; get_str(line, p, BSIZE); if ((fp = open_file(line, "eps", fn)) == 0) { error(1, s, "No such file: %s", line); return as; } /* get the bounding box */ while (fgets(line, sizeof line, fp)) { if (strncmp(line, "%%BoundingBox:", 14) == 0) { if (sscanf(&line[14], "%f %f %f %f", &x1, &y1, &x2, &y2) == 4) break; } } fclose(fp); if (strncmp(line, "%%BoundingBox:", 14) != 0) { error(1, s, "No bounding box in '%s'", fn); return as; } if (cfmt.textoption == T_CENTER || cfmt.textoption == T_RIGHT) { float lw; lw = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale; if (cfmt.textoption == T_CENTER) x1 += (lw - (x2 - x1)) * 0.5; else x1 += lw - (x2 - x1); } PUT0("\001"); /* include file (must be the first after eob) */ bskip(y2 - y1); PUT3("%.2f %.2f%%%s\n", x1, y1, fn); buffer_eob(); return as; } break; case 'm': if (strcmp(w, "maxsysstaffsep") == 0) { if (as->state != ABC_S_TUNE && as->state != ABC_S_EMBED) break; curvoice->maxsep = scan_u(p); return as; } if (strcmp(w, "measrep") == 0) goto irepeat; if (strcmp(w, "multicol") == 0) { float bposy; out_musly(1); if (strncmp(p, "start", 5) == 0) { if (!in_page) PUT0("%%\n"); /* initialize the output */ bposy = get_bposy(); multicol_max = multicol_start = bposy; lmarg = cfmt.leftmargin; rmarg = cfmt.rightmargin; } else if (strncmp(p, "new", 3) == 0) { if (multicol_start == 0) error(1, s, "%%%%multicol new without start"); else { bposy = get_bposy(); if (bposy < multicol_start) abskip(bposy - multicol_start); if (bposy < multicol_max) multicol_max = bposy; cfmt.leftmargin = lmarg; cfmt.rightmargin = rmarg; } } else if (strncmp(p, "end", 3) == 0) { if (multicol_start == 0) error(1, s, "%%%%multicol end without start"); else { bposy = get_bposy(); if (bposy > multicol_max) abskip(bposy - multicol_max); cfmt.leftmargin = lmarg; cfmt.rightmargin = rmarg; multicol_start = 0; PUT0("%%\n"); /* force write_buffer */ buffer_eob(); } } else { error(1, s, "Unknown keyword '%s' in %%%%multicol", p); } return as; } break; case 'n': if (strcmp(w, "newpage") == 0) { if (epsf) return as; out_musly(0); write_buffer(); use_buffer = 0; if (isdigit((unsigned char) *p)) pagenum = atoi(p); close_page(); return as; } break; case 'p': if (strcmp(w, "postscript") == 0) { ps_def(s, p); return as; } break; case 'r': if (strcmp(w, "repbra") == 0) { if (as->state != ABC_S_TUNE && as->state != ABC_S_EMBED) return as; curvoice->norepbra = !atoi(p); return as; } if (strcmp(w, "repeat") == 0) { int n, k; irepeat: if (curvoice->last_symbol == 0) { error(1, s, "%%%%repeat cannot start a tune"); return as; } if (*p == '\0') { n = 1; k = 1; } else { n = atoi(p); if (n < 1 || (curvoice->last_symbol->type == BAR && n > 2)) { error(1, s, "Incorrect 1st value in %%%%repeat"); return as; } while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; if (*p == '\0') k = 1; else { k = atoi(p); if (k < 1 || (curvoice->last_symbol->type == BAR && n == 2 && k > 1)) { error(1, s, "Incorrect 2nd value in %%%%repeat"); return as; } } } sym_link(s, FMTCHG); s->u = REPEAT; if (curvoice->last_symbol->prev->type == BAR) s->doty = n; else s->doty = -n; s->nohdix = k; as->text = 0; return as; } case 's': if (strcmp(w, "setbarnb") == 0) { if (as->state == ABC_S_TUNE || as->state == ABC_S_EMBED) { bar_number = atoi(p); return as; } strcpy(w, "measurefirst"); break; } if (strcmp(w, "sep") == 0) { float h2, len, lwidth; out_musly(0); lwidth = (cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin; h1 = h2 = len = 0; if (*p != '\0') { h1 = scan_u(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } if (*p != '\0') { h2 = scan_u(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } if (*p != '\0') len = scan_u(p); if (h1 < 1) h1 = 0.5 CM; if (h2 < 1) h2 = h1; if (len < 1) len = 3.0 CM; bskip(h1); PUT2("%.1f %.1f sep0\n", len / cfmt.scale, (lwidth - len) * 0.5 / cfmt.scale); bskip(h2); buffer_eob(); return as; } if (strcmp(w, "staff") == 0) { int staff; if (as->state != ABC_S_TUNE && as->state != ABC_S_EMBED) return as; if (*p == '+') staff = curvoice->cstaff + atoi(p + 1); else if (*p == '-') staff = curvoice->cstaff - atoi(p + 1); else staff = atoi(p) - 1; if ((unsigned) staff > nstaff) { error(1, s, "Bad staff in %%%%staff"); return as; } curvoice->floating = 0; curvoice->cstaff = staff; return as; } if (strcmp(w, "staffbreak") == 0) { if (as->state != ABC_S_TUNE && as->state != ABC_S_EMBED) return as; sym_link(s, FMTCHG); s->u = STBRK; if (isdigit(*p)) { s->xmx = scan_u(p); if (p[strlen(p) - 1] == 'f') s->doty = 1; } else { s->xmx = 0.5 CM; if (*p == 'f') s->doty = 1; } return as; } if (strcmp(w, "staves") == 0 || strcmp(w, "score") == 0) { if (as->state == ABC_S_GLOBAL) return as; if (as->state == ABC_S_TUNE || as->state == ABC_S_EMBED) { output_music(); buffer_eob(); voice_init(); } get_staves(s); curvoice = first_voice; staves_found = 1; return as; } if (strcmp(w, "sysstaffsep") == 0) { if (as->state != ABC_S_TUNE && as->state != ABC_S_EMBED) break; curvoice->sep = scan_u(p); return as; } break; case 'c': case 't': if (strcmp(w, "text") == 0 || strcmp(w, "center") == 0) { int job; if (epsf && as->state == ABC_S_GLOBAL) return as; if ((job = cfmt.textoption) == T_SKIP) return as; if (w[0] == 'c') job = T_CENTER; out_musly(1); add_to_text_block(p, job); write_text_block(job, as->state); return as; } if (strcmp(w, "tablature") == 0) { float h; if (as->state != ABC_S_TUNE && as->state != ABC_S_EMBED) return as; /* %%tablature [] */ h = scan_u(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; if (isdigit(*p)) { curvoice->tabha = h; curvoice->tabhu = scan_u(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } else curvoice->tabhu = h; if (*p != '\0') { curvoice->tabhead = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; } if (*p != '\0') { *p = '\0'; p++; while (isspace((unsigned char) *p)) p++; curvoice->tabnote = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; } if (*p != '\0') { *p = '\0'; p++; while (isspace((unsigned char) *p)) p++; curvoice->tabbar = p; } else { error(1, s, "Wrong values in %%%%tablature"); curvoice->tabhead = 0; } } break; case 'v': if (strcmp(w, "vskip") == 0) { out_musly(0); h1 = scan_u(p); bskip(h1); buffer_eob(); return as; } break; } if (as->state == ABC_S_TUNE || as->state == ABC_S_EMBED) { if (strcmp(w, "leftmargin") == 0 || strcmp(w, "rightmargin") == 0 || strcmp(w, "scale") == 0) { out_musly(1); } } interpret_fmt_line(w, p, 0); if (cfmt.alignbars && strcmp(w, "alignbars") == 0) { struct VOICE_S *p_voice; int i; output_music(); if ((unsigned) cfmt.alignbars > MAXSTAFF) { error(1, s, "Too big value in %%%%alignbars"); cfmt.alignbars = MAXSTAFF; } if (staves_found) cfmt.alignbars = nstaff + 1; first_voice = curvoice = p_voice = &voice_tb[0]; for (i = 0; i < cfmt.alignbars; i++) { staff_tb[i].flags[0] |= STOP_BAR; p_voice->staff = p_voice->cstaff = i; if (i > 0) p_voice->prev = p_voice - 1; else p_voice->prev = 0; if (i >= cfmt.alignbars - 1) { p_voice->next = 0; break; } p_voice->next = p_voice + 1; p_voice++; } nstaff = i; } return as; } /* -- set the duration of notes/rests in a tuplet -- */ static void set_tuplet(struct SYMBOL *t) { struct abcsym *as, *first; struct SYMBOL *s; int l, r, lplet, grace; r = t->as.u.tuplet.r_plet; grace = t->as.flags & ABC_F_GRACE; l = 0; first = 0; for (as = t->as.next; as != 0; as = as->next) { /*fixme: KO if voice change..*/ if (as->type != ABC_T_NOTE && as->type != ABC_T_REST) continue; if (as->u.note.lens[0] == 0) /* space ('y') */ continue; if (grace) { if (!(as->flags & ABC_F_GRACE)) continue; } else { if (as->flags & ABC_F_GRACE) continue; } if (first == 0) first = as; s = (struct SYMBOL *) as; l += s->dur; if (--r <= 0) break; } if (as == 0) { error(1, t, "End of tune found inside a tuplet"); return; } lplet = (l * t->as.u.tuplet.q_plet) / t->as.u.tuplet.p_plet; r = t->as.u.tuplet.r_plet; for (as = first; as != 0; as = as->next) { int olddur; /*fixme: KO if voice change..*/ if (as->type != ABC_T_NOTE && as->type != ABC_T_REST) continue; if (as->u.note.lens[0] == 0) continue; if (grace) { if (!(as->flags & ABC_F_GRACE)) continue; } else { if (as->flags & ABC_F_GRACE) continue; } s = (struct SYMBOL *) as; if (as != first) s->sflags |= S_IN_TUPLET; olddur = s->dur; s->dur = (olddur * lplet) / l; if (--r <= 0) { break; } l -= olddur; lplet -= s->dur; } /*fixme: KO if in a grace sequence*/ if (grace) error(1, t, "Tuplets in grace note sequence not yet treated"); else { sym_link(t, TUPLET); t->u = cfmt.tuplets; } } /* -- add a new symbol at end of list -- */ struct SYMBOL *sym_add(struct VOICE_S *p_voice, int type) { struct SYMBOL *s; struct VOICE_S *p_voice2; s = (struct SYMBOL *) getarena(sizeof *s); memset(s, 0, sizeof *s); p_voice2 = curvoice; curvoice = p_voice; sym_link(s, type); curvoice = p_voice2; return s; } /* -- link a ABC symbol into a voice -- */ static void sym_link(struct SYMBOL *s, int type) { struct VOICE_S *p_voice = curvoice; /* memset((&s->as) + 1, 0, sizeof (struct SYMBOL) - sizeof (struct abcsym)); */ if (p_voice->sym != 0) { p_voice->last_symbol->next = s; s->prev = p_voice->last_symbol; } else p_voice->sym = s; p_voice->last_symbol = s; s->type = type; s->voice = p_voice - voice_tb; s->staff = p_voice->cstaff; s->time = p_voice->time; }