Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | |
2 | /* Copyright (c) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */ | |
3 | /* All rights reserved */ | |
4 | ||
5 | /* This file contains functions for dealing with symbol tables. | |
6 | * There is a symbol table that maps the names of location variables | |
7 | * to the addresses of their array of coordinate info, | |
8 | * a symbol table to map headshape names to the list of shapes, | |
9 | * a table to map time signatures to beamstyle and/or timeunit values, | |
10 | * and a symbol table to map grid names to definitions of the grids. | |
11 | * Symbol names are hashed for fast lookup. | |
12 | */ | |
13 | ||
14 | #include "defines.h" | |
15 | #include "structs.h" | |
16 | #include "globals.h" | |
17 | ||
18 | ||
19 | /* shapes for quarter and shorter, half, whole, double whole */ | |
20 | #define MAX_SHAPE_DURS (4) | |
21 | /* UP and DOWN */ | |
22 | #define MAX_STEM_DIRS (2) | |
23 | ||
24 | /* First index of headchar and headfont is based on stem direction; | |
25 | * also index of ystem_off. */ | |
26 | #define STEMINDEX(stemdir) ( (stemdir) == UP ? 0 : 1 ) | |
27 | /* Second index of headchar and headfont is based on basictime */ | |
28 | #define HCDUR(basictime) ( (basictime) > 2 ? 0 : 3 - (basictime) ) | |
29 | /* First index of Nhead_map */ | |
30 | #define FONTINDEX(font) (font - FONT_MUSIC) | |
31 | /* Second index of Nhead_map */ | |
32 | #define CHARINDEX(ch) (ch - FIRST_CHAR) | |
33 | ||
34 | struct HDSHAPEINFO { | |
35 | /* We give each headshape instance a unique index number, | |
36 | * as a compact way to refer to it. */ | |
37 | short index; | |
38 | ||
39 | /* This says what notehead characters to use for this shape. | |
40 | * The first dimension is for stem direction, the second for | |
41 | * duration (quarter and shorter, half, whole, double whole). | |
42 | */ | |
43 | char headchar[MAX_STEM_DIRS][MAX_SHAPE_DURS]; | |
44 | /* This array parallels the notehead array, saying which music | |
45 | * font the character is in. */ | |
46 | char headfont[MAX_STEM_DIRS][MAX_SHAPE_DURS]; | |
47 | }; | |
48 | ||
49 | /* How many "headshape" entries we allow. This must be small enough to | |
50 | * fit in the number of bits allowed for shape indexes in GRPSYL and NOTE. | |
51 | * Entry 0 isn't used since we need an "unknown" value. | |
52 | */ | |
53 | #define MAX_SHAPE_ENTRIES (32) | |
54 | ||
55 | /* These are the predefined headshape entries. Users can define more. | |
56 | * We define them here using the same syntax as user would, so we can | |
57 | * use the same code to add to our internal table. */ | |
58 | struct SHAPENAMES { | |
59 | char *clan_name; /* name for the set of shapes */ | |
60 | char *member_names; /* The names of the 4 shapes in the set */ | |
61 | } Predef_shape_names[] = { | |
62 | { "norm", "4n 2n 1n dblwhole" }, | |
63 | { "x", "xnote diamond diamond dwhdiamond" }, | |
64 | { "allx", "xnote xnote xnote xnote" }, | |
65 | { "diam", "filldiamond diamond diamond dwhdiamond" }, | |
66 | { "blank", "blankhead blankhead blankhead blankhead" }, | |
67 | { "righttri", "u?fillrighttriangle u?righttriangle u?righttriangle u?dwhrighttriangle" }, | |
68 | { "isostri", "fillisostriangle isostriangle isostriangle dwhisostriangle" }, | |
69 | { "rect", "fillrectangle rectangle rectangle dwhrectangle" }, | |
70 | { "pie", "fillpiewedge piewedge piewedge dwhpiewedge" }, | |
71 | { "semicirc", "fillsemicircle semicircle semicircle dwhsemicircle" }, | |
72 | { "allslash", "fillslashhead fillslashhead fillslashhead fillslashhead" }, | |
73 | { "slash", "fillslashhead slashhead slashhead dwhslashhead" }, | |
74 | { 0, 0 } | |
75 | }; | |
76 | ||
77 | /* Information about characters that are allowed to be noteheads */ | |
78 | struct HEADINFO { | |
79 | char ch; /* code number 32-127 */ | |
80 | char font; /* FONT_MUSIC* */ | |
81 | float ystem_off[MAX_STEM_DIRS]; /* stepsizes from y to end stem */ | |
82 | }; | |
83 | ||
84 | /* Predefined note head music characters and their attributes. */ | |
85 | struct HEADDATA { | |
86 | char *name; | |
87 | struct HEADINFO info; | |
88 | } Predef_headinfo[] = { | |
89 | { "dblwhole", { C_DBLWHOLE, FONT_MUSIC, { 0.0, 0.0 } } }, | |
90 | { "1n", { C_1N, FONT_MUSIC, { 0.0, 0.0 } } }, | |
91 | { "2n", { C_2N, FONT_MUSIC, { 0.25, -0.25 } } }, | |
92 | { "4n", { C_4N, FONT_MUSIC, { 0.25, -0.25 } } }, | |
93 | { "xnote", { C_XNOTE, FONT_MUSIC, { 1.0, -1.0 } } }, | |
94 | { "dwhdiamond", { C_DWHDIAMOND, FONT_MUSIC, { 0.0, 0.0 } } }, | |
95 | { "diamond", { C_DIAMOND, FONT_MUSIC, { 0.0, 0.0 } } }, | |
96 | { "filldiamond", { C_FILLDIAMOND, FONT_MUSIC, { 0.0, 0.0 } } }, | |
97 | { "dwhrighttriangle", { C_DWHRIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.0 } } }, | |
98 | { "righttriangle", { C_RIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.9 } } }, | |
99 | { "fillrighttriangle",{ C_FILLRIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.9 } } }, | |
100 | { "udwhrighttriangle",{ C_UDWHRIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.9 } } }, | |
101 | { "urighttriangle", { C_URIGHTTRIANGLE, FONT_MUSIC2, { -0.9, 0.0 } } }, | |
102 | { "ufillrighttriangle",{ C_UFILLRIGHTTRIANGLE,FONT_MUSIC2, { -0.9, 0.0 } } }, | |
103 | { "dwhrectangle", { C_DWHRECTANGLE, FONT_MUSIC2, { -0.9, 0.0 } } }, | |
104 | { "rectangle", { C_RECTANGLE, FONT_MUSIC2, { 0.0, 0.0 } } }, | |
105 | { "fillrectangle", { C_FILLRECTANGLE, FONT_MUSIC2, { 0.0, 0.0 } } }, | |
106 | { "dwhisostriangle", { C_DWHISOSTRIANGLE, FONT_MUSIC2, { -0.8, -0.8 } } }, | |
107 | { "isostriangle", { C_ISOSTRIANGLE, FONT_MUSIC2, { -0.8, -0.8 } } }, | |
108 | { "fillisostriangle", { C_FILLISOSTRIANGLE, FONT_MUSIC2, { -0.8, -0.8 } } }, | |
109 | { "dwhpiewedge", { C_DWHPIEWEDGE, FONT_MUSIC2, { 0.1, 0.2 } } }, | |
110 | { "piewedge", { C_PIEWEDGE, FONT_MUSIC2, { 0.1, 0.2 } } }, | |
111 | { "fillpiewedge", { C_FILLPIEWEDGE, FONT_MUSIC2, { 0.1, 0.2 } } }, | |
112 | { "dwhsemicircle", { C_DWHSEMICIRCLE, FONT_MUSIC2, { 0.8, 0.8 } } }, | |
113 | { "semicircle", { C_SEMICIRCLE, FONT_MUSIC2, { 0.8, 0.8 } } }, | |
114 | { "fillsemicircle", { C_FILLSEMICIRCLE, FONT_MUSIC2, { 0.8, 0.8 } } }, | |
115 | { "blankhead", { C_BLANKHEAD, FONT_MUSIC2, { 0.0, 0.0 } } }, | |
116 | { "slashhead", { C_SLASHHEAD, FONT_MUSIC2, { 1.8, -1.8 } } }, | |
117 | { "fillslashhead", { C_FILLSLASHHEAD, FONT_MUSIC2, { 1.8, -1.8 } } }, | |
118 | { "dwhslashhead", { C_DWHSLASHHEAD, FONT_MUSIC2, { 1.8, -1.8 } } }, | |
119 | { 0, { 0, 0, { 0.0, 0.0 } } } | |
120 | }; | |
121 | ||
122 | /* | |
123 | * This struct provides a mapping from a time signature to all the | |
124 | * beamstyle and timeunit values to be associated with that time signature. | |
125 | * | |
126 | * The [0][0] entry is used for the C_SCORE value. | |
127 | * The entries [0][1] through [0][MAXVOICES+1] are unused. | |
128 | * The [s][0] entries are used for C_STAFF values where 1 <= s <= MAXSTAFFS | |
129 | * The [s][v] entries are used for C_VOICE values where 1 <= s <= MAXSTAFFS | |
130 | * and 1 <= v <= MAXVOICES | |
131 | * There will be one of these allocated for each time signature used in the | |
132 | * input, if and only if the user also specified at least one beamstyle | |
133 | * or timeunit while that time signature was in effect. | |
134 | */ | |
135 | struct SSVTABLES { | |
136 | struct SSV *beamstyle_table[MAXSTAFFS+1][MAXVOICES+1]; | |
137 | struct SSV *timeunit_table[MAXSTAFFS+1][MAXVOICES+1]; | |
138 | }; | |
139 | ||
140 | ||
141 | /* information about a symbol: its name and current value. | |
142 | * This is used for location tags, chord grids, headshapes, and time signatures. | |
143 | * Note that in the case of location tags, | |
144 | * if the same name is used in a later measure, just the coordlist_p will | |
145 | * change, and when the symbol table is queried for the value of a symbol, | |
146 | * it will get the current value for that symbol */ | |
147 | struct Sym { | |
148 | char *symname; | |
149 | union { | |
150 | float *coordlist_p; /* when used for location tags, this is | |
151 | * where its AX, RX, etc values are */ | |
152 | struct GRID *grid_p; /* when used for grids */ | |
153 | struct HDSHAPEINFO *shapeinfo_p;/* when used for headshapes */ | |
154 | struct HEADINFO *noteinfo_p; /* when used for note head info */ | |
155 | /* Info about beamstyles and/or timeunits associated with | |
156 | * the time signature given by the symname. */ | |
157 | struct SSVTABLES *ssvtables_p; | |
158 | } val; | |
159 | struct Sym *next; /* for collision chain off hash table */ | |
160 | }; | |
161 | ||
162 | /* symbol hash table size-- if this changes, hash() has to change accordingly */ | |
163 | #define SYMTBLSIZE (128) | |
164 | ||
165 | /* this is the symbol table for location tags */ | |
166 | static struct Sym *Tag_table[SYMTBLSIZE]; | |
167 | ||
168 | /* this is the symbol table for guitar grids. It is malloc-ed at runtime | |
169 | * only if needed */ | |
170 | static struct Sym **Grid_table; | |
171 | ||
172 | /* This is the symbol table for headshapes */ | |
173 | static struct Sym *Shape_table[SYMTBLSIZE]; | |
174 | ||
175 | /* This maps headshape indexes to the corresponding info. | |
176 | * Element 0 is unused, since index 0 means "unknown" shape. | |
177 | */ | |
178 | static struct Sym *Shape_map[MAX_SHAPE_ENTRIES]; | |
179 | /* How many Shape_map entries are actually used */ | |
180 | static short Shape_entries = 0; | |
181 | ||
182 | /* This is the symbol table for noteheads, to get stem offsets */ | |
183 | static struct Sym *Nhead_table[SYMTBLSIZE]; | |
184 | ||
185 | /* This maps notehead character codes to the stem offset info */ | |
186 | static struct HEADINFO *Nhead_map[NUM_MFONTS][CHARS_IN_FONT]; | |
187 | ||
188 | /* This is used to remember what beamstyle and/or timeunit values were | |
189 | * associated with time signatures. This is really only needed during parse, | |
190 | * and then only if user specifies beamstyle or timeunit somewhere. | |
191 | */ | |
192 | static struct Sym *Time_map[SYMTBLSIZE]; | |
193 | ||
194 | /* Internal name for tag used to store the virtual _win coords for blocks */ | |
195 | char Blockwin[] = "~blockwin"; | |
196 | /* This points to where in the symbol table | |
197 | * to save the pointer to the coord array for blocks. | |
198 | * Having this pointer is a speed optimization, | |
199 | * to save us from having to look it up every time. */ | |
200 | float **Blockcoord_p_p; | |
201 | ||
202 | ||
203 | /* static functions */ | |
204 | static struct GRID *parse_grid P((char *griddef)); | |
205 | static struct Sym *add2tbl P((char *symname, struct Sym **table)); | |
206 | static struct Sym *findSym P((char *symname, struct Sym **table)); | |
207 | static int hash P((char *string)); | |
208 | static int coordhash P((float *key)); | |
209 | static void rep_ref P((float **old_ref_p_p, float **new_ref_p_p)); | |
210 | static void delete_coord P((float *coord_p)); | |
211 | static int is_valid_notehead P((int ch, int font)); | |
212 | static void add_head P((char *name, struct HEADINFO *info_p)); | |
213 | ||
214 | ||
215 | /* size of Coordinfo hash table. Should be prime */ | |
216 | #define COORDTBLSIZE (271) | |
217 | ||
218 | static struct COORD_INFO *Coord_table[COORDTBLSIZE]; | |
219 | \f | |
220 | ||
221 | /* Add predefined values to the symbol tables */ | |
222 | ||
223 | void | |
224 | init_symtbl() | |
225 | ||
226 | { | |
227 | struct Sym *sym_p; | |
228 | int i; | |
229 | ||
230 | addsym("_page", _Page, CT_BUILTIN); | |
231 | addsym("_cur", _Cur, CT_BUILTIN); | |
232 | ||
233 | /* Blocks each have their own virtual _win, | |
234 | * so we put a placeholder in the symbol table | |
235 | * and save a pointer to its tag's coord pointer. | |
236 | * Then we can update the coords via set_win_coords(). | |
237 | */ | |
238 | addsym(Blockwin, 0, CT_BUILTIN); | |
239 | sym_p = findSym(Blockwin, Tag_table); | |
240 | if (sym_p == 0) { | |
241 | pfatal("couldn't find %s coord right after inserting it!", | |
242 | Blockwin); | |
243 | } | |
244 | Blockcoord_p_p = &(sym_p->val.coordlist_p); | |
245 | ||
246 | /* Put the predefined notehead shapes into table. */ | |
247 | for (i = 0; Predef_headinfo[i].name != 0; i++) { | |
248 | add_head(Predef_headinfo[i].name, &(Predef_headinfo[i].info)); | |
249 | } | |
250 | ||
251 | /* Put the predefined head shapes in the shapes table */ | |
252 | for (i = 0; Predef_shape_names[i].clan_name != 0; i++) { | |
253 | add_shape(Predef_shape_names[i].clan_name, | |
254 | Predef_shape_names[i].member_names); | |
255 | } | |
256 | } | |
257 | \f | |
258 | ||
259 | /* add a symbol to the table if not already there and fill in its coordlist_p | |
260 | * in symbol table. */ | |
261 | ||
262 | void | |
263 | addsym(symname, coordlist_p, coordtype) | |
264 | ||
265 | char *symname; /* what to add to table */ | |
266 | float *coordlist_p; /* set of 13 coordinates associated with symbol */ | |
267 | int coordtype; /* CT_BAR, CT_GRPSYL, etc */ | |
268 | ||
269 | { | |
270 | struct Sym *sym_p; /* pointer to info about symbol in hash tbl */ | |
271 | ||
272 | ||
273 | debug(4, "addsym(symname=%s coordlist_p=0x%lx, coordtype=%d)", | |
274 | symname, coordlist_p, coordtype); | |
275 | ||
276 | /* find in symbol table or add if not yet there */ | |
277 | sym_p = add2tbl(symname, Tag_table); | |
278 | ||
279 | /* fill in coordlist pointer */ | |
280 | sym_p->val.coordlist_p = coordlist_p; | |
281 | ||
282 | /* put entry in coord table */ | |
283 | add_coord(coordlist_p, coordtype); | |
284 | } | |
285 | \f | |
286 | ||
287 | /* add a symbol to the specified hash table */ | |
288 | ||
289 | static struct Sym * | |
290 | add2tbl(symname, table) | |
291 | ||
292 | char *symname; /* what to add */ | |
293 | struct Sym **table; /* which table to add to */ | |
294 | ||
295 | { | |
296 | struct Sym *sym_p; | |
297 | int h; /* hash number of symbol */ | |
298 | ||
299 | if ((sym_p = findSym(symname, table)) == (struct Sym *) 0) { | |
300 | ||
301 | /* not in list before. Add it */ | |
302 | MALLOC(Sym, sym_p, 1); | |
303 | MALLOCA(char, sym_p->symname, strlen(symname) + 1); | |
304 | (void) strcpy(sym_p->symname, symname); | |
305 | h = hash(symname); | |
306 | ||
307 | /* link onto front of list off hash table */ | |
308 | sym_p->next = table[h]; | |
309 | table[h] = sym_p; | |
310 | } | |
311 | return(sym_p); | |
312 | } | |
313 | \f | |
314 | ||
315 | /* given a symbol name, return pointer to its symbol table entry, or NULL | |
316 | * if none */ | |
317 | ||
318 | static struct Sym * | |
319 | findSym(symname, table) | |
320 | ||
321 | char *symname; /* which symbol to look for */ | |
322 | struct Sym **table; /* which table to look in */ | |
323 | ||
324 | { | |
325 | struct Sym *sym_p; /* symbol info currently being checked | |
326 | * for match with symname */ | |
327 | int h; /* hash number */ | |
328 | ||
329 | ||
330 | h = hash(symname); | |
331 | /* go down the linked list (of hash collisions) off the table | |
332 | * searching for match */ | |
333 | for (sym_p = table[h]; sym_p != (struct Sym *) 0; | |
334 | sym_p = sym_p->next) { | |
335 | if (strcmp(sym_p->symname, symname) == 0) { | |
336 | return(sym_p); | |
337 | } | |
338 | } | |
339 | return((struct Sym *) 0); | |
340 | } | |
341 | \f | |
342 | ||
343 | /* For top/bot/top2/bot2/block we temporarily set the ~blockwin tag to point | |
344 | * to the appropriate blockhead's coord array. Before doing a set_win for | |
345 | * any of those, this function should be called to set things up, and | |
346 | * it should be called again afterwards with 0 to mark we are no longer | |
347 | * inside a block. | |
348 | */ | |
349 | ||
350 | void | |
351 | set_win_coord(coord_p) | |
352 | ||
353 | float *coord_p; | |
354 | ||
355 | { | |
356 | *Blockcoord_p_p = coord_p; | |
357 | } | |
358 | \f | |
359 | ||
360 | /* Given a tag name, return its value (a pointer to the coordinate array | |
361 | * containing the AX, AY, etc of the variable). | |
362 | * If the tag is not found, an error is printed and 0 is returned. | |
363 | * If the ref_p_p is non-null, | |
364 | * save that value as something that references the tag. That way if the | |
365 | * tag gives moved somewhere else, we can update all the references. | |
366 | * If referencing the value of a coord that can never move (a builtin coord | |
367 | * like _win), ref_p_p may be null. | |
368 | */ | |
369 | ||
370 | float * | |
371 | symval(symname, ref_p_p) | |
372 | ||
373 | char *symname; /* which symbol to look up */ | |
374 | float **ref_p_p; /* address of reference to the tag */ | |
375 | ||
376 | { | |
377 | struct Sym *sym_p; /* symbol info currently being checked */ | |
378 | ||
379 | ||
380 | /* _win is a special case: its actual symbol depends on context */ | |
381 | if (strcmp(symname, "_win") == 0) { | |
382 | /* If we're inside a block, use its coord, | |
383 | * else use the global _Win */ | |
384 | if (*Blockcoord_p_p != 0) { | |
385 | return(*Blockcoord_p_p); | |
386 | } | |
387 | else { | |
388 | return(_Win); | |
389 | } | |
390 | } | |
391 | ||
392 | /* find the symbol table entry */ | |
393 | if ((sym_p = findSym(symname, Tag_table)) != (struct Sym *) 0) { | |
394 | if (sym_p->val.coordlist_p != (float *) 0) { | |
395 | /* save reference information */ | |
396 | if (ref_p_p != 0) { | |
397 | struct COORD_INFO *coordinfo_p; | |
398 | struct COORD_REF *ref_p; | |
399 | CALLOC(COORD_REF, ref_p, 1); | |
400 | ref_p->ref_p_p = ref_p_p; | |
401 | /* link onto reference list */ | |
402 | coordinfo_p = find_coord(sym_p->val.coordlist_p); | |
403 | ref_p->next = coordinfo_p->ref_list_p; | |
404 | coordinfo_p->ref_list_p = ref_p; | |
405 | } | |
406 | return(sym_p->val.coordlist_p); | |
407 | } | |
408 | } | |
409 | ||
410 | /* whoops! not in table */ | |
411 | l_yyerror(Curr_filename, yylineno, | |
412 | "reference to uninitialized location tag '%s'", | |
413 | symname); | |
414 | return((float *) 0); | |
415 | } | |
416 | \f | |
417 | ||
418 | /* add item to grid table if not already there. */ | |
419 | ||
420 | void | |
421 | add_grid(name, griddef) | |
422 | ||
423 | char *name; /* chord name */ | |
424 | char *griddef; /* user's definition of the grid */ | |
425 | ||
426 | { | |
427 | char *internal_name; /* internal format name */ | |
428 | char *grid_name; /* permanent copy of internal_name */ | |
429 | char *symname; /* ASCII-ized name */ | |
430 | char *key; /* permanent copy of symname, key into hash tbl */ | |
431 | struct Sym *sym_p; | |
432 | struct GRID *grid_p; | |
433 | ||
434 | ||
435 | /* if table doesn't exist yet, create it */ | |
436 | if (Grid_table == 0) { | |
437 | int g; | |
438 | ||
439 | MALLOCA(struct Sym *, Grid_table, SYMTBLSIZE); | |
440 | for (g = 0; g < SYMTBLSIZE; g++) { | |
441 | Grid_table[g] = 0; | |
442 | } | |
443 | } | |
444 | ||
445 | /* Do all the transforms to get into internal form with | |
446 | * accidentals as music characters. For historical reasons, | |
447 | * this has to be done in several steps, in just the right order. */ | |
448 | internal_name = modify_chstr(name, TM_CHORD); | |
449 | internal_name = fix_string(internal_name, Score.font, Score.size, | |
450 | Curr_filename, yylineno); | |
451 | /* convert accidentals, then convert to all ASCII */ | |
452 | grid_name = tranchstr(internal_name, -1); | |
453 | symname = ascii_str(grid_name, YES, NO, TM_CHORD); | |
454 | ||
455 | if (strlen(symname) == 0) { | |
456 | yyerror("empty grid name not allowed"); | |
457 | return; | |
458 | } | |
459 | ||
460 | if ((sym_p = findSym(symname, Grid_table)) != 0) { | |
461 | l_warning(Curr_filename, yylineno, | |
462 | "duplicate definition of grid for '%s', discarding previous", | |
463 | symname); | |
464 | /* discard old definition, use new one */ | |
465 | if (sym_p->val.grid_p != 0) { | |
466 | if (sym_p->val.grid_p->name != 0) { | |
467 | FREE(sym_p->val.grid_p->name); | |
468 | } | |
469 | FREE(sym_p->val.grid_p); | |
470 | sym_p->val.grid_p = 0; | |
471 | } | |
472 | key = sym_p->symname; | |
473 | } | |
474 | else { | |
475 | /* make permanent copy of key */ | |
476 | MALLOCA(char, key, strlen(symname) + 1); | |
477 | (void) strcpy(key, symname); | |
478 | } | |
479 | ||
480 | if ((grid_p = parse_grid(griddef)) != 0) { | |
481 | /* it's good, so put in hash table */ | |
482 | sym_p = add2tbl(key, Grid_table); | |
483 | ||
484 | /* fill in the name and grid pointer */ | |
485 | grid_p->name = grid_name; | |
486 | sym_p->val.grid_p = grid_p;; | |
487 | { | |
488 | /* buffer--2 digits and a space for each string, plus null */ | |
489 | char fretlist[3 * MAXTABLINES + 1]; | |
490 | int f; | |
491 | for (f = 0; f < grid_p->numstr; f++) { | |
492 | (void) sprintf(fretlist + 3 * f, "%3d", | |
493 | grid_p->positions[f]); | |
494 | } | |
495 | fretlist[sizeof(fretlist) - 1] = '\0'; | |
496 | debug(4, "added grid '%s' key '%s' with %d strings: %s curve %d to %d", | |
497 | name, key, | |
498 | grid_p->numstr, fretlist, | |
499 | grid_p->curvel, grid_p->curver); | |
500 | } | |
501 | } | |
502 | } | |
503 | \f | |
504 | ||
505 | /* take the user's grid definition, like "2 (3 1) o x -" | |
506 | * and populate a GRID struct with the info. If not parse-able, | |
507 | * return 0 */ | |
508 | ||
509 | static struct GRID * | |
510 | parse_grid(griddef) | |
511 | ||
512 | char *griddef; /* user's definition of the grid */ | |
513 | ||
514 | { | |
515 | struct GRID *grid_p; /* the malloc-ed grid to populate & return */ | |
516 | int error = NO; | |
517 | int value; | |
518 | ||
519 | MALLOC(GRID, grid_p, 1); | |
520 | grid_p->numstr = 0; | |
521 | grid_p->curvel = grid_p->curver = 0; | |
522 | grid_p->used = NO; | |
523 | /* the +2 is to skip the font/size bytes */ | |
524 | for (griddef += 2; *griddef != '\0' && error == NO; griddef++) { | |
525 | /* init to something illegal */ | |
526 | value = -1000; | |
527 | ||
528 | while (*griddef == ' ' || *griddef == '\t') { | |
529 | griddef++; | |
530 | } | |
531 | if (*griddef == '\0') { | |
532 | break; | |
533 | } | |
534 | ||
535 | if ( isdigit(*griddef) ) { | |
536 | value = *griddef - '0'; | |
537 | if (isdigit(*(griddef+1))) { | |
538 | value *= 10; | |
539 | griddef++; | |
540 | value += *griddef - '0'; | |
541 | } | |
542 | if (value == 0) { | |
543 | yyerror("fret of zero not allowed; use 'o' for open or '-' for nothing"); | |
544 | error = YES; | |
545 | } | |
546 | } | |
547 | else if (*griddef == '(') { | |
548 | if (grid_p->curvel != 0) { | |
549 | yyerror("only one '(' allowed in grid definition"); | |
550 | error = YES; | |
551 | } | |
552 | else { | |
553 | grid_p->curvel = grid_p->numstr + 1; | |
554 | } | |
555 | } | |
556 | else if (*griddef == ')') { | |
557 | if (grid_p->curver != 0) { | |
558 | yyerror("only one ')' allowed in grid definition"); | |
559 | error = YES; | |
560 | } | |
561 | else if (grid_p->curvel == 0) { | |
562 | yyerror("missing '(' in grid definition"); | |
563 | error = YES; | |
564 | } | |
565 | else if (grid_p->curvel == grid_p->numstr) { | |
566 | yyerror("curve in grid definition must encompass more than one string"); | |
567 | error = YES; | |
568 | } | |
569 | else { | |
570 | grid_p->curver = grid_p->numstr; | |
571 | } | |
572 | } | |
573 | else if (*griddef == 'o') { | |
574 | value = 0; | |
575 | } | |
576 | else if (*griddef == 'x') { | |
577 | value = -1; | |
578 | } | |
579 | else if (*griddef == '-') { | |
580 | value = -2; | |
581 | } | |
582 | else { | |
583 | yyerror("invalid grid specification"); | |
584 | FREE(grid_p); | |
585 | return (0); | |
586 | } | |
587 | ||
588 | if (value >= -2) { | |
589 | /* We found a fret value (not parentheses). | |
590 | * Next better be white space sort of thing */ | |
591 | char c; | |
592 | c = *(griddef + 1); | |
593 | if (c != ' ' && c != '\t' && c != '(' && c != ')' | |
594 | && c != '\0') { | |
595 | yyerror("missing white space in grid specification"); | |
596 | error = YES; | |
597 | } | |
598 | else if (grid_p->numstr < MAXTABLINES) { | |
599 | /* all is well; save info for current string */ | |
600 | grid_p->positions[grid_p->numstr] = value; | |
601 | (grid_p->numstr)++; | |
602 | } | |
603 | else { | |
604 | yyerror("too many strings in grid specification"); | |
605 | error = YES; | |
606 | } | |
607 | } | |
608 | } | |
609 | ||
610 | if (error == NO && grid_p->curvel != 0 && grid_p->curver == 0) { | |
611 | yyerror("missing ')' in grid specification"); | |
612 | error = YES; | |
613 | } | |
614 | ||
615 | if (grid_p->numstr < 1) { | |
616 | yyerror("grid must include at least one string"); | |
617 | error = YES; | |
618 | } | |
619 | ||
620 | /* every curve must have at least one real fret in it */ | |
621 | if (grid_p->curvel != 0) { | |
622 | int s; | |
623 | /* the -1 is because curves start at 1, but positions at 0 */ | |
624 | for (s = grid_p->curvel - 1; s <= grid_p->curver - 1; s++) { | |
625 | if (grid_p->positions[s] > 0) { | |
626 | break; | |
627 | } | |
628 | } | |
629 | if (s == grid_p->curver) { | |
630 | yyerror("grid curve must include at least one fret number, not just x, o, and -"); | |
631 | error = YES; | |
632 | } | |
633 | } | |
634 | ||
635 | if (error == YES) { | |
636 | /* clean up */ | |
637 | FREE(grid_p); | |
638 | grid_p = 0; | |
639 | } | |
640 | return(grid_p); | |
641 | } | |
642 | \f | |
643 | ||
644 | /* Locate a named GRID in the grid hash table. Returns 0 if not found, | |
645 | * else pointer to desired GRID. */ | |
646 | ||
647 | struct GRID * | |
648 | findgrid(name) | |
649 | ||
650 | char *name; /* find GRID with this chord name */ | |
651 | ||
652 | { | |
653 | struct Sym *sym_p; | |
654 | char *ascii_name; | |
655 | int length; | |
656 | int first_char; | |
657 | ||
658 | if (Grid_table == 0) { | |
659 | /* no grids defined */ | |
660 | return((struct GRID *) 0); | |
661 | } | |
662 | ||
663 | ascii_name = ascii_str(name, YES, NO, TM_CHORD); | |
664 | /* Chord names in STUFF have a space padding at the end of them | |
665 | * unless they are in a box or circle, so strip that off for matching | |
666 | * the name. We store the grid name without the space, because for | |
667 | * grid printing, we want the name centered without end padding. */ | |
668 | length = strlen(ascii_name); | |
669 | first_char = ((int)*(name+2)) & 0xff; | |
670 | if (first_char != STR_BOX && first_char != STR_CIR | |
671 | && ascii_name[length-1] == ' ') { | |
672 | ascii_name[length-1] = '\0'; | |
673 | } | |
674 | ||
675 | /* look it up */ | |
676 | sym_p = findSym(ascii_name, Grid_table); | |
677 | ||
678 | return (sym_p ? sym_p->val.grid_p : 0); | |
679 | } | |
680 | \f | |
681 | ||
682 | /* Function to iterate through all the GRIDs. First time, call it with | |
683 | * argument of zero, and it returns the first GRID it finds. To walk | |
684 | * through the list, call it repeatedly, each time passing the GRID | |
685 | * you got the last time. When you get back a zero, the list is done. | |
686 | * Restrictions: you must either call with zero or the last one you got. | |
687 | * You can't remember some previous value and use that to try to jump | |
688 | * to elsewhere on the list, or have multiple walks going on at once. | |
689 | * Caller should assume values are returned in arbitrary order. | |
690 | */ | |
691 | ||
692 | struct GRID * | |
693 | nextgrid(grid_p) | |
694 | ||
695 | struct GRID *grid_p; | |
696 | ||
697 | { | |
698 | static int tbl_index = -1; | |
699 | static struct Sym *last_sym_p = 0; /* remember where we were the | |
700 | * last time we were called */ | |
701 | ||
702 | if (grid_p == 0) { | |
703 | /* starting at beginning */ | |
704 | tbl_index = -1; | |
705 | last_sym_p = 0; | |
706 | } | |
707 | else if (last_sym_p == 0 || grid_p != last_sym_p->val.grid_p) { | |
708 | pfatal("nextgrid called incorrectly"); | |
709 | } | |
710 | ||
711 | /* if there is another on the current collision chain, use that */ | |
712 | if (last_sym_p != 0) { | |
713 | last_sym_p = last_sym_p->next; | |
714 | } | |
715 | ||
716 | /* if none, either if just starting, or if ran off the end | |
717 | * of a collision chain, find next chain. */ | |
718 | if (last_sym_p == 0) { | |
719 | for (tbl_index++; tbl_index < SYMTBLSIZE; tbl_index++) { | |
720 | if (Grid_table[tbl_index] != 0) { | |
721 | /* found a populated chain */ | |
722 | last_sym_p = Grid_table[tbl_index]; | |
723 | break; | |
724 | } | |
725 | } | |
726 | } | |
727 | ||
728 | return(last_sym_p == 0 ? 0 : last_sym_p->val.grid_p); | |
729 | } | |
730 | \f | |
731 | ||
732 | /* return a hash number from a string. XOR the bytes together */ | |
733 | ||
734 | static int | |
735 | hash(string) | |
736 | ||
737 | char *string; /* hash this string */ | |
738 | ||
739 | { | |
740 | int h; /* hash number */ | |
741 | ||
742 | for (h = 0; *string != '\0'; string++) { | |
743 | h ^= *string; | |
744 | } | |
745 | /* return hash number between 0 and 127 */ | |
746 | return(h & 0x7f); | |
747 | } | |
748 | \f | |
749 | ||
750 | /* add entry to COORD_INFO table */ | |
751 | ||
752 | void | |
753 | add_coord(coordlist_p, coordtype) | |
754 | ||
755 | float *coordlist_p; /* address of set of 13 coordinates to add to tbl */ | |
756 | int coordtype; /* CT_GRPSYL, etc */ | |
757 | ||
758 | { | |
759 | struct COORD_INFO *new_p; /* space for saving coord info */ | |
760 | int h; /* hash number */ | |
761 | ||
762 | ||
763 | /* if not already in table, add it */ | |
764 | if (find_coord(coordlist_p) == (struct COORD_INFO *) 0) { | |
765 | ||
766 | /* get space, fill in coord type, and link into hash table */ | |
767 | CALLOC(COORD_INFO, new_p, 1); | |
768 | ||
769 | new_p->coordlist_p = coordlist_p; | |
770 | new_p->flags = (short) coordtype; | |
771 | ||
772 | h = coordhash(coordlist_p); | |
773 | new_p->next = Coord_table[h]; | |
774 | Coord_table[h] = new_p; | |
775 | } | |
776 | } | |
777 | \f | |
778 | ||
779 | /* Given an address of an array of floats (a coordinate array), return a hash | |
780 | * number, which is modulo of the Coord_table table size */ | |
781 | ||
782 | static int | |
783 | coordhash(key) | |
784 | ||
785 | float *key; /* hash this number */ | |
786 | ||
787 | { | |
788 | return ((int) ( (unsigned long) key % COORDTBLSIZE)); | |
789 | } | |
790 | \f | |
791 | ||
792 | /* Given a coordinate (pointer to array of floats), return the location of | |
793 | * the info about it in the Coord_table, or 0 if not in table */ | |
794 | ||
795 | struct COORD_INFO * | |
796 | find_coord(key) | |
797 | ||
798 | float *key; /* look up this key in hash table */ | |
799 | ||
800 | { | |
801 | int h; /* hash number */ | |
802 | struct COORD_INFO *c_p; | |
803 | ||
804 | ||
805 | /* search hash table for matching entry */ | |
806 | h = coordhash(key); | |
807 | for (c_p = Coord_table[h]; c_p != (struct COORD_INFO *) 0; | |
808 | c_p = c_p->next) { | |
809 | if (key == c_p->coordlist_p) { | |
810 | return(c_p); | |
811 | } | |
812 | } | |
813 | return( (struct COORD_INFO *) 0); | |
814 | } | |
815 | \f | |
816 | ||
817 | /* Given an existing INPCOORD, and a new INPCOORD that is to replace it, | |
818 | * adjust any tag references (hor_p or vert_p) to point to the new one. */ | |
819 | ||
820 | void | |
821 | rep_inpcoord(old_inpcoord_p, new_inpcoord_p) | |
822 | ||
823 | struct INPCOORD *old_inpcoord_p; | |
824 | struct INPCOORD *new_inpcoord_p; | |
825 | ||
826 | { | |
827 | rep_ref( &(old_inpcoord_p->hor_p), &(new_inpcoord_p->hor_p) ); | |
828 | rep_ref( &(old_inpcoord_p->vert_p), &(new_inpcoord_p->vert_p) ); | |
829 | ||
830 | } | |
831 | ||
832 | /* Given an existing reference to a tag in an INPCOORD, replace that | |
833 | * reference with the new reference. This is for when transferring a | |
834 | * temporary INPCOORD to a permanent one. | |
835 | */ | |
836 | ||
837 | static void | |
838 | rep_ref(old_ref_p_p, new_ref_p_p) | |
839 | ||
840 | float **old_ref_p_p; | |
841 | float **new_ref_p_p; | |
842 | ||
843 | { | |
844 | struct COORD_INFO *coordinfo_p; | |
845 | struct COORD_REF *ref_p; | |
846 | ||
847 | if (*old_ref_p_p == 0) { | |
848 | /* This can happen if user references uninitialized tag. | |
849 | * We already give error message elsewhere for that. */ | |
850 | return; | |
851 | } | |
852 | if (*new_ref_p_p == 0) { | |
853 | pfatal("attempt to replace tag reference with null."); | |
854 | return; | |
855 | } | |
856 | ||
857 | /* Find this information about the coordinate */ | |
858 | if ((coordinfo_p = find_coord(*old_ref_p_p)) == 0) { | |
859 | /* Must have been earlier user error. */ | |
860 | return; | |
861 | } | |
862 | ||
863 | /* Find any references matching the old and update them. | |
864 | * Really should be only one, but seems safer to check all. | |
865 | */ | |
866 | for (ref_p = coordinfo_p->ref_list_p; ref_p != 0; ref_p = ref_p->next) { | |
867 | if (ref_p->ref_p_p == old_ref_p_p) { | |
868 | ref_p->ref_p_p = new_ref_p_p; | |
869 | } | |
870 | } | |
871 | } | |
872 | \f | |
873 | ||
874 | /* Given an existing coord array address and an address it is being moved to, | |
875 | * update all references to the old to point to the new, and update our | |
876 | * hash table to insert the old and delete the old. | |
877 | */ | |
878 | ||
879 | void | |
880 | upd_ref(oldcoord_p, newcoord_p) | |
881 | ||
882 | float * oldcoord_p; | |
883 | float * newcoord_p; | |
884 | ||
885 | { | |
886 | struct COORD_INFO *coordinfo_p; /* existing info about oldcoord_p */ | |
887 | struct COORD_INFO *newcoordinfo_p; /* for info about newcoord_p */ | |
888 | struct COORD_REF *ref_p; /* for walking through ref list */ | |
889 | ||
890 | if ((coordinfo_p = find_coord(oldcoord_p)) == 0) { | |
891 | /* apparently no tags associated with this coord array */ | |
892 | return; | |
893 | } | |
894 | /* update all the references to the tag with the new value */ | |
895 | for (ref_p = coordinfo_p->ref_list_p; ref_p != 0; ref_p = ref_p->next) { | |
896 | *(ref_p->ref_p_p) = newcoord_p; | |
897 | } | |
898 | ||
899 | /* Now need to create new entry for the new coord, and delete old */ | |
900 | add_coord(newcoord_p, coordinfo_p->flags); | |
901 | /* Append reference list to new coord info */ | |
902 | if ((newcoordinfo_p = find_coord(newcoord_p)) != 0) { | |
903 | /* Should always get here; the 'if' is to just paranoia | |
904 | * to be sure to avoid null pointer deference. */ | |
905 | /* If coord already existed before, because one coord is | |
906 | * being combined with another, we want to concatenate the | |
907 | * reference list, so find end to append to. */ | |
908 | struct COORD_REF **append_p_p; | |
909 | for (append_p_p = &(newcoordinfo_p->ref_list_p); | |
910 | *append_p_p != 0; | |
911 | append_p_p = &((*append_p_p)->next)) { | |
912 | ; | |
913 | } | |
914 | ||
915 | *append_p_p = coordinfo_p->ref_list_p; | |
916 | coordinfo_p->ref_list_p = 0; | |
917 | } | |
918 | delete_coord(oldcoord_p); | |
919 | } | |
920 | \f | |
921 | ||
922 | /* Delete the given coordinate information */ | |
923 | ||
924 | static void | |
925 | delete_coord(coord_p) | |
926 | ||
927 | float * coord_p; | |
928 | ||
929 | { | |
930 | int h; /* hash number */ | |
931 | struct COORD_INFO **c_p_p; | |
932 | ||
933 | /* search hash table for matching entry and delete it */ | |
934 | h = coordhash(coord_p); | |
935 | for (c_p_p = &(Coord_table[h]); *c_p_p != 0; *(c_p_p) = (*c_p_p)->next) { | |
936 | if ((*c_p_p)->coordlist_p == coord_p) { | |
937 | struct COORD_INFO * to_delete_p; | |
938 | to_delete_p = *c_p_p; | |
939 | *c_p_p = (*c_p_p)->next; | |
940 | FREE(to_delete_p); | |
941 | return; | |
942 | } | |
943 | } | |
944 | } | |
945 | \f | |
946 | ||
947 | /* Given a shape name and string containing the 4 note heads to use for it, | |
948 | * as would exist in a line of "headshapes" context, parse the | |
949 | * string with the noteheads and save all the information | |
950 | * in the shapes hash table. | |
951 | */ | |
952 | ||
953 | void | |
954 | add_shape(name, shapes) | |
955 | ||
956 | char *name; /* name of the list of shapes */ | |
957 | char *shapes; /* list of 4 shape names */ | |
958 | ||
959 | { | |
960 | struct Sym *sym_p; /* where added into Shape_table */ | |
961 | struct HDSHAPEINFO *shapeinfo_p;/* internal format for shape data */ | |
962 | int i; /* index through shapes */ | |
963 | int d; /* index through durations */ | |
964 | int nameleng; /* length of one name in shapes list */ | |
965 | char namebuff[40]; /* one of the names in shapes list. | |
966 | * Needs to be big enough to hold longer | |
967 | * name of any valid note head character. */ | |
968 | char ch; /* music character corresponding to head name */ | |
969 | int font; /* which font FONT_MUSIC* */ | |
970 | int size; /* not really needed here, but function we | |
971 | * call expect it */ | |
972 | short flips; /* YES if stem down uses upside down version */ | |
973 | ||
974 | debug(4, "add_shape name='%s' shapes='%s'", name, shapes); | |
975 | ||
976 | /* Add to symbol table */ | |
977 | sym_p = add2tbl(name, Shape_table); | |
978 | MALLOC(HDSHAPEINFO, shapeinfo_p, 1); | |
979 | sym_p->val.shapeinfo_p = shapeinfo_p; | |
980 | ||
981 | /* Allocate an index and fill in the index-to-info mapping array */ | |
982 | shapeinfo_p->index = ++Shape_entries; | |
983 | if (Shape_entries >= MAX_SHAPE_ENTRIES) { | |
984 | yyerror("Too many headshapes"); | |
985 | return; | |
986 | } | |
987 | else { | |
988 | Shape_map[Shape_entries] = sym_p; | |
989 | } | |
990 | ||
991 | /* Parse the list of 4 shapes and save their info */ | |
992 | size = DFLT_SIZE; | |
993 | for (d = i = 0; shapes[i] != '\0'; ) { | |
994 | /* Skip white space */ | |
995 | if (isspace(shapes[i])) { | |
996 | i++; | |
997 | continue; | |
998 | } | |
999 | ||
1000 | /* Make sure user didn't give too many shapes */ | |
1001 | if (d >= MAX_SHAPE_DURS) { | |
1002 | l_yyerror(Curr_filename, yylineno, | |
1003 | "Too many shapes for headshape '%s' (%d expected, %d found)\n", | |
1004 | name, MAX_SHAPE_DURS, d); | |
1005 | return; | |
1006 | } | |
1007 | ||
1008 | /* Check if stem down gets a flipped character */ | |
1009 | if (shapes[i] == 'u' && shapes[i+1] == '?') { | |
1010 | flips = YES; | |
1011 | i += 2; | |
1012 | } | |
1013 | else { | |
1014 | flips = NO; | |
1015 | } | |
1016 | ||
1017 | /* get copy of current head name, and look up its character */ | |
1018 | nameleng = strcspn(shapes + i, " \t\r\n"); | |
1019 | /* leave room for null and 'u' for upsidedown */ | |
1020 | if (nameleng > sizeof(namebuff) - 2) { | |
1021 | ufatal("head shape name too long"); | |
1022 | } | |
1023 | if (nameleng == 0 && flips == YES) { | |
1024 | l_yyerror(Curr_filename, yylineno, | |
1025 | "'u?' must be followed immediately by a note head character name"); | |
1026 | return; | |
1027 | } | |
1028 | strncpy(namebuff, shapes + i, nameleng); | |
1029 | namebuff[nameleng] = '\0'; | |
1030 | ch = mc_name2num(namebuff, Curr_filename, yylineno, &size, &font); | |
1031 | if (is_valid_notehead(ch, font) == NO) { | |
1032 | l_yyerror(Curr_filename, yylineno, | |
1033 | "'%s' is not a valid note head name", namebuff); | |
1034 | return; | |
1035 | } | |
1036 | ||
1037 | shapeinfo_p->headchar[STEMINDEX(UP)][d] = ch; | |
1038 | shapeinfo_p->headfont[STEMINDEX(UP)][d] = font; | |
1039 | ||
1040 | /* If flips, get upside down version. Else use same again */ | |
1041 | if (flips == YES) { | |
1042 | namebuff[0] = 'u'; | |
1043 | strncpy(namebuff + 1, shapes + i, nameleng); | |
1044 | namebuff[nameleng+1] = '\0'; | |
1045 | ch = mc_name2num(namebuff, Curr_filename, yylineno, &size, &font); | |
1046 | if (is_valid_notehead(ch, font) == NO) { | |
1047 | l_yyerror(Curr_filename, yylineno, | |
1048 | "'%s' is not a valid note head name", namebuff); | |
1049 | return; | |
1050 | } | |
1051 | } | |
1052 | shapeinfo_p->headchar[STEMINDEX(DOWN)][d] = ch; | |
1053 | shapeinfo_p->headfont[STEMINDEX(DOWN)][d] = font; | |
1054 | ||
1055 | /* Prepare for next in the list, if any */ | |
1056 | d++; | |
1057 | i += nameleng; | |
1058 | } | |
1059 | if (d < MAX_SHAPE_DURS) { | |
1060 | l_yyerror(Curr_filename, yylineno, | |
1061 | "Too few shapes for headshape '%s' (%d expected, %d found)\n", | |
1062 | name, MAX_SHAPE_DURS, d); | |
1063 | } | |
1064 | } | |
1065 | \f | |
1066 | ||
1067 | /* Given a head shape index, stemdir, and basictime, return the notehead | |
1068 | * character to use and (via font_p pointer) | |
1069 | * which music font that notehead character is in. | |
1070 | */ | |
1071 | ||
1072 | int | |
1073 | nheadchar(headshape, basictime, stemdir, font_p) | |
1074 | ||
1075 | int headshape; /* head shape index */ | |
1076 | int basictime; /* 8 for eighth, 2 for half, etc */ | |
1077 | int stemdir; /* UP or DOWN */ | |
1078 | int *font_p; /* FONT_MUSIC* is returned here */ | |
1079 | ||
1080 | { | |
1081 | struct Sym *info_p; /* shape to character map */ | |
1082 | int dir; /* first index into headchar */ | |
1083 | int dur; /* second index into headchar */ | |
1084 | ||
1085 | if (headshape == HS_UNKNOWN || headshape > Shape_entries) { | |
1086 | pfatal("illegal headshape index to nheadchar (%d)", headshape); | |
1087 | } | |
1088 | ||
1089 | info_p = Shape_map[headshape]; | |
1090 | ||
1091 | dir = STEMINDEX(stemdir); | |
1092 | dur = HCDUR(basictime); | |
1093 | *font_p = info_p->val.shapeinfo_p->headfont[dir][dur]; | |
1094 | return(info_p->val.shapeinfo_p->headchar[dir][dur]); | |
1095 | } | |
1096 | \f | |
1097 | ||
1098 | /* Given a head shape name, return the internal index number we use | |
1099 | * to refer to that shape. | |
1100 | */ | |
1101 | ||
1102 | int | |
1103 | get_shape_num(shapename) | |
1104 | ||
1105 | char *shapename; | |
1106 | ||
1107 | { | |
1108 | struct Sym *sym_p; | |
1109 | ||
1110 | if ((sym_p = findSym(shapename, Shape_table)) != 0) { | |
1111 | return(sym_p->val.shapeinfo_p->index); | |
1112 | } | |
1113 | return(HS_UNKNOWN); | |
1114 | } | |
1115 | \f | |
1116 | ||
1117 | /* Return YES if given character is a valid note head character, else NO */ | |
1118 | ||
1119 | static int | |
1120 | is_valid_notehead(ch, font) | |
1121 | ||
1122 | int ch; /* character code */ | |
1123 | int font; /* FONT_MUSIC* */ | |
1124 | ||
1125 | { | |
1126 | if ( IS_MUSIC_FONT(font) == NO || ch < 0 || ch >= CHARS_IN_FONT) { | |
1127 | /* If caller passes us the return from mc_name2num(), | |
1128 | * that could return BAD_CHAR, so we don't pfatal here, | |
1129 | * but do return right away, so we don't do an illegal | |
1130 | * array index below. | |
1131 | */ | |
1132 | return(NO); | |
1133 | } | |
1134 | return (Nhead_map[FONTINDEX(font)][CHARINDEX(ch)] == 0 ? NO : YES); | |
1135 | } | |
1136 | \f | |
1137 | ||
1138 | /* Save note head information in table */ | |
1139 | /** If we allow users to supply their own some day, | |
1140 | * would need a function that allocates and fills in a HEADINFO, | |
1141 | * and then calls this one. */ | |
1142 | ||
1143 | static void | |
1144 | add_head(name, info_p) | |
1145 | ||
1146 | char *name; /* music character name */ | |
1147 | struct HEADINFO *info_p; /* char/font/stem offset */ | |
1148 | ||
1149 | { | |
1150 | struct Sym *sym_p; | |
1151 | ||
1152 | ||
1153 | sym_p = add2tbl(name, Nhead_table); | |
1154 | sym_p->val.noteinfo_p = info_p; | |
1155 | ||
1156 | /* Fill in reverse lookup map */ | |
1157 | Nhead_map[FONTINDEX(info_p->font)][CHARINDEX(info_p->ch)] = info_p; | |
1158 | } | |
1159 | \f | |
1160 | ||
1161 | /* Given a note head character and stem direction, | |
1162 | * return the y offset for the stem to end, in stepsizes. | |
1163 | */ | |
1164 | ||
1165 | double | |
1166 | stem_yoff(headch, font, stemdir) | |
1167 | ||
1168 | int headch; /* music character code */ | |
1169 | int font; /* FONT_MUSIC* */ | |
1170 | int stemdir; /* UP or DOWN */ | |
1171 | ||
1172 | { | |
1173 | struct HEADINFO *info_p; | |
1174 | ||
1175 | if ( IS_MUSIC_FONT(font) == NO || headch < 0 || headch >= CHARS_IN_FONT) { | |
1176 | pfatal("invalid argument: stem_yoffset(%d, %d, stemdir)", | |
1177 | headch, font, stemdir); | |
1178 | } | |
1179 | info_p = Nhead_map[FONTINDEX(font)][CHARINDEX(headch)]; | |
1180 | if (info_p == 0) { | |
1181 | pfatal("No notehead map for ch=%d, font=%d", headch, font); | |
1182 | } | |
1183 | if (stemdir == UNKNOWN) { | |
1184 | pfatal("stem_yoff called with unknown stemdir"); | |
1185 | } | |
1186 | return(info_p->ystem_off[STEMINDEX(stemdir)]); | |
1187 | } | |
1188 | \f | |
1189 | ||
1190 | /* This should be called when an SSV has been collected by the user. | |
1191 | * If the user set beamstyle and/or timeunit in the SSV, | |
1192 | * create a mapping between the current time signature and that beamstyle | |
1193 | * and/or timeunit, so that if the user later sets the same time signature, | |
1194 | * they don't have to set the other things too. | |
1195 | * If they set time signature, see if we have a mapping | |
1196 | * for that time signature. If so, set the beamstyles and timeunits | |
1197 | * from that mapping. | |
1198 | */ | |
1199 | ||
1200 | void | |
1201 | remember_tsig_params(mll_p) | |
1202 | ||
1203 | struct MAINLL *mll_p; /* contains SSV */ | |
1204 | ||
1205 | { | |
1206 | struct SSV *ssv_p; /* the SSV to process */ | |
1207 | char *timesig; /* current time signature representation */ | |
1208 | struct Sym *entry; /* entry in time sig info mapping table */ | |
1209 | ||
1210 | ||
1211 | if (mll_p->str != S_SSV) { | |
1212 | pfatal("remember_tsig_params got bad str value %d", mll_p->str); | |
1213 | } | |
1214 | ssv_p = mll_p->u.ssv_p; | |
1215 | ||
1216 | if (ssv_p->used[TIME] == NO && ssv_p->used[BEAMSTLIST] == NO && | |
1217 | ssv_p->used[TIMEUNIT] == NO) { | |
1218 | /* nothing of interest in this SSV */ | |
1219 | return; | |
1220 | } | |
1221 | ||
1222 | /* If user set time signature in this SSV, that's the time sig | |
1223 | * of interest, otherwise use the current time signature */ | |
1224 | timesig = (ssv_p->used[TIME] == YES ? ssv_p->timerep : Score.timerep); | |
1225 | if ((entry = findSym(timesig, Time_map)) == 0) { | |
1226 | entry = add2tbl(timesig, Time_map); | |
1227 | /* We'll only allocate the actual table if user gives a | |
1228 | * beamstyle or timeunit somewhere. If they never do, | |
1229 | * this will avoid wasting memory. | |
1230 | */ | |
1231 | entry->val.ssvtables_p = 0; | |
1232 | } | |
1233 | ||
1234 | /* If beamstyle or timeunit are set in this SSV, associate them | |
1235 | * with the current time signature. */ | |
1236 | if (ssv_p->used[BEAMSTLIST] == YES) { | |
1237 | if (entry->val.ssvtables_p == 0) { | |
1238 | CALLOC(SSVTABLES, entry->val.ssvtables_p, 1); | |
1239 | } | |
1240 | /* Note that when staffno and voiceno are zero, | |
1241 | * that's actually a score entry, and when voiceno is zero, | |
1242 | * but staffno is non-zero, that is actually a staff entry. */ | |
1243 | entry->val.ssvtables_p->beamstyle_table[ssv_p->staffno][ssv_p->voiceno] = ssv_p; | |
1244 | } | |
1245 | if (ssv_p->used[TIMEUNIT] == YES) { | |
1246 | if (entry->val.ssvtables_p == 0) { | |
1247 | CALLOC(SSVTABLES, entry->val.ssvtables_p, 1); | |
1248 | } | |
1249 | entry->val.ssvtables_p->timeunit_table[ssv_p->staffno][ssv_p->voiceno] = ssv_p; | |
1250 | } | |
1251 | ||
1252 | /* If time signature is set in this SSV, see if we have any | |
1253 | * beamstyles or timeunits associated with that time signature. | |
1254 | * If so, restore their values. */ | |
1255 | if (ssv_p->used[TIME] == YES && entry->val.ssvtables_p != 0) { | |
1256 | /* Make new SSVs and copy the relevant fields for any | |
1257 | * remembered beamstyles and/or timesunits associated | |
1258 | * with this time signature. */ | |
1259 | struct SSV *beamstyle_ssv_p; /* SSV having beamstyle info */ | |
1260 | struct SSV *timeunit_ssv_p; /* SSV having timeunit info */ | |
1261 | struct MAINLL *mll_ssv_p; /* new SSV to add to list */ | |
1262 | struct SSV *nssv_p; /* new SSV to add to list */ | |
1263 | int s; /* staff */ | |
1264 | int v; /* voice */ | |
1265 | ||
1266 | /* Check all SSV contexts. The [0][0] entry is for Scoreo * The rest of row [0] is unused. | |
1267 | * The [0] column is for staffs, for s > 0. | |
1268 | */ | |
1269 | for (s = 0; s <= MAXSTAFFS; s++) { | |
1270 | for (v = 0; v <= MAXVOICES; v++) { | |
1271 | ||
1272 | /* Check if we need to create an SSV for | |
1273 | * this staff/voice */ | |
1274 | beamstyle_ssv_p = entry->val.ssvtables_p->beamstyle_table[s][v]; | |
1275 | timeunit_ssv_p = entry->val.ssvtables_p->timeunit_table[s][v]; | |
1276 | if (beamstyle_ssv_p == 0 && | |
1277 | timeunit_ssv_p == 0) { | |
1278 | /* nothing to do for this one */ | |
1279 | continue; | |
1280 | } | |
1281 | ||
1282 | /* If both saved SSVs are either zero or | |
1283 | * the same SSV as where the time was just | |
1284 | * set, no need to make another SSV */ | |
1285 | if ( (beamstyle_ssv_p == 0 || | |
1286 | beamstyle_ssv_p == ssv_p) && | |
1287 | (timeunit_ssv_p == 0 || | |
1288 | timeunit_ssv_p == ssv_p) ) { | |
1289 | continue; | |
1290 | } | |
1291 | ||
1292 | /* need to create an SSV */ | |
1293 | mll_ssv_p = newMAINLLstruct(S_SSV, -1); | |
1294 | insertMAINLL(mll_ssv_p, mll_p); | |
1295 | mll_p = mll_ssv_p; | |
1296 | ||
1297 | /* populate the new SSV */ | |
1298 | nssv_p = mll_ssv_p->u.ssv_p; | |
1299 | if (beamstyle_ssv_p != 0) { | |
1300 | nssv_p->nbeam = beamstyle_ssv_p->nbeam; | |
1301 | nssv_p->beamstlist = beamstyle_ssv_p->beamstlist; | |
1302 | nssv_p->beamrests = beamstyle_ssv_p->beamrests; | |
1303 | nssv_p->beamspaces = beamstyle_ssv_p->beamspaces; | |
1304 | nssv_p->nsubbeam = beamstyle_ssv_p->nsubbeam; | |
1305 | nssv_p->subbeamstlist = beamstyle_ssv_p->subbeamstlist; | |
1306 | nssv_p->used[BEAMSTLIST] = YES; | |
1307 | } | |
1308 | if (timeunit_ssv_p != 0) { | |
1309 | nssv_p->timeunit = timeunit_ssv_p->timeunit; | |
1310 | nssv_p->used[TIMEUNIT] = YES; | |
1311 | } | |
1312 | ||
1313 | /* fill in the SSV header */ | |
1314 | if (v != 0) { | |
1315 | nssv_p->context = C_VOICE; | |
1316 | } | |
1317 | else if (s != 0) { | |
1318 | /* The [s][0] entry is for staff s | |
1319 | * when s > 0 */ | |
1320 | nssv_p->context = C_STAFF; | |
1321 | } | |
1322 | else { | |
1323 | /* The [0][0] entry is actually score */ | |
1324 | nssv_p->context = C_SCORE; | |
1325 | } | |
1326 | nssv_p->staffno = s; | |
1327 | nssv_p->voiceno = v; | |
1328 | asgnssv(nssv_p); | |
1329 | } | |
1330 | } | |
1331 | } | |
1332 | } |