chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / midiutil.c
1
2 /* Copyright (c) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2005 by Arkkra Enterprises */
3 /* All rights reserved */
4
5 /* utility functions for creating MIDI output from Mup input. These functions
6  * are split out into this file to keep midi.c from being so huge */
7
8 #ifdef __WATCOMC__
9 #include <io.h>
10 #endif
11 #include "defines.h"
12 #include "structs.h"
13 #include "globals.h"
14
15
16 static struct GRPSYL *create_prev_grp P((struct MAINLL *mll_p, int staffno,
17                 int v));
18 static struct GRPSYL *create_meas_space P((struct MAINLL *mll_p));
19 static void fix_spacechord P((struct MAINLL *chmll_p, struct CHORD *ch_p));
20 static void splitspace P((struct GRPSYL *gs_p, RATIONAL duration));
21 static void splicechord P((struct GRPSYL *gs_p, struct CHORD *ch_p));
22 static void guitar_grpsyl_transpose P((struct GRPSYL *gs_p));
23 static RATIONAL find_acc_end_time P((RATIONAL begin_time, struct GRPSYL *gs_p,
24                 int n));
25 static void propogate_accidental P((struct NOTE *note_p, RATIONAL begin_time,
26                 RATIONAL end_time, struct GRPSYL *gs_p));
27 static void mv_skipped_midi P((struct STUFF *stuff_p, int staffno,
28                 struct MAINLL *topstaff_mll_p));
29 \f
30
31 /* seek back to where header size is in file, and fill in the correct size,
32  * now that we know what it is. */
33
34 void
35 fix_track_size(mfile, track_start, track_size)
36
37 int mfile;              /* file descriptor of MIDI file */
38 long track_start;       /* offset in file where size needs to be put */
39 long track_size;        /* track length in bytes */
40
41 {
42         unsigned char buff[4];
43
44
45         debug(512, "fix_track_size");
46
47         /* go to where track size is stored in file */
48         (void) lseek(mfile, track_start + 4, SEEK_SET);
49
50         /* convert to 4-byte number with correct byte ordering regardless
51          * of machine byte ordering */
52         buff[0] = (track_size >> 24) & 0xff;
53         buff[1] = (track_size >> 16) & 0xff;
54         buff[2] = (track_size >> 8) & 0xff;
55         buff[3] = track_size & 0xff;
56         (void) write(mfile, buff, 4);
57
58         /* go back to end of file in case there are more track to write */
59         (void) lseek(mfile, 0L, SEEK_END);
60 }
61 \f
62
63 /* given an octave mark string, return number of octaves to tranpose (could
64  * be negative if transposing down) */
65
66 int
67 parse_octave(string, place, fname, lineno)
68
69 char *string;           /* typically "8va" */
70 int place;              /* PL_ABOVE or PL_BELOW */
71 char *fname;            /* file name for errors */
72 int lineno;
73                 
74
75 {
76         int font, size;
77         int octave_value = 0;
78         int code;               /* ASCII of character in string */
79
80         
81         font = string[0];
82         size = string[1];
83         string += 2;
84         code = next_str_char(&string, &font, &size);
85         if (isdigit(code)) {
86                 octave_value = code - '0';
87                 code = next_str_char(&string, &font, &size);
88                 /* might be a second digit. If user is crazy enough to use
89                  * an octave number greater than 2 digits, ignore the rest */
90                 if (isdigit(code)) {
91                         octave_value = (octave_value * 10) + (code - '0');
92                 }
93         }
94
95         /* must be either a non-zero multiple of 8, or things like 15, 22,
96          * etc if some musical mathematician adds 7 instead of 8 */
97         if (octave_value < 8 || ((octave_value - 8) % 7 != 0 &&
98                                  (octave_value - 8) % 8 != 0)) {
99                 l_ufatal(fname, lineno, "invalid octave mark string");
100         }
101         if (octave_value % 8 == 0) {
102                 octave_value /= 8;
103         } else {
104                 octave_value = 1 + (octave_value - 8) % 7;
105         }
106
107         return(place == PL_BELOW ? -octave_value : octave_value);
108 }
109 \f
110
111 /* determine "clocks per metronome tick." It not entirely clear to me
112  * how that is supposed to work, but... if the time signature denominator is
113  * 4, we'll use 24. If 8, use 12, etc. Only go down as far as 3, since that's
114  * not divisible by 2. Really, in fast tempo triple time, the denominator
115  * isn't the beat, but this will do for now. */
116
117 int
118 clocks(num)
119
120 int num;
121
122 {
123         switch(num) {
124         case 1:
125                 return(96);
126         case 2:
127                 return(48);
128         case 4:
129                 return(24);
130         case 8:
131                 return(12);
132         case 16:
133                 return(6);
134         default:
135                 return(3);
136         }
137 }
138 \f
139
140 /* given a string, if it contains a word followed by an =, followed by a word,
141  * any of which may be separated by white space, return, via pointers,
142  * a pointer to the beginning of the word, the length of the word, and a pointer
143  * to the first non-white-space character after the =, and return YES.
144  * Otherwise, return NO. A "word" is a sequence of non-white-space chars. */
145
146 int
147 getkeyword(string, key_p, leng_p, arg_p_p)
148
149 char *string;           /* check this string */
150 char **key_p;           /* return pointer to keyword via this */
151 int *leng_p;            /* return length of keyword via this */
152 char **arg_p_p;         /* return pointer to argument after = via this */
153
154 {
155         char *tok;
156
157
158         /* skip leading white space */
159         for (*key_p = string; **key_p == ' ' || **key_p == '\t'; (*key_p)++) {
160                 ;
161         }
162
163         /* go till hit white space or equals sign */
164         for (tok = *key_p; *tok != '\0'; tok++) {
165                 if (*tok == ' ' || *tok == '\t' || *tok == '=') {
166                         break;
167                 }
168         }
169
170         /* fill in length of key */
171         *leng_p = tok - *key_p;
172
173         if (*leng_p == 0) {
174                 return(NO);
175         }
176
177         /* find first non-white beyond the = */
178         for (   ; *tok != '\0'; tok++) {
179                 if (*tok == '=') {
180                         for (tok++; *tok != '\0'; tok++) {
181                                 if (*tok != ' ' && *tok != '\t') {
182                                         *arg_p_p = tok;
183                                         return(YES);
184                                 }
185                         }
186                 }
187         }
188         return(NO);
189 }
190 \f
191
192 /* given a user-specified key, see if it matches the given command name. Return
193  * YES if it does, NO if it doesn't. User only has to specify the first 3 or
194  * more characters of the command, because that's enough to make it unique,
195  * and saves them from typing longer names. */
196
197 int
198 matches(key, leng, cmd)
199
200 char *key;              /* user specified key to be checked */
201 int leng;               /* length of key */
202 char *cmd;              /* check if key matches this command */
203
204 {
205         if (leng < 3) {
206                 return(NO);
207         }
208         return(strncmp(key, cmd, leng) == 0 ? YES : NO);
209 }
210 \f
211
212 /* given an ASCII hex digit, return its value 0-15 */
213
214 int
215 hexdig(ch)
216
217 int ch;
218
219 {
220         if (ch >= '0' && ch <= '9') {
221                 return(ch - '0');
222         }
223         else if (ch >= 'a' && ch <= 'f') {
224                 return(ch - 'a' + 10);
225         }
226         else if (ch >= 'A' && ch <= 'F') {
227                 return(ch - 'A' + 10);
228         }
229         pfatal("bad hex digit");
230         /*NOTREACHED*/
231         return(0);
232 }
233 \f
234
235 /* given a string, output it to midi file, prefixed by its length. */
236 /* return number of bytes written */
237
238 UINT32B
239 midi_wrstring(mfile, str, internalform)
240
241 int mfile;      /* MIDI file */
242 char *str;      /* string to write to file */
243 int internalform;       /* YES if str is in Mup format, NO if just ASCII,
244                          * C-style null-terminated string to be copied */
245
246 {
247         char *buff;             /* for all-ASCII version of str */
248         UINT32B bytes;          /* number of bytes in length value */
249         int length;             /* of string */
250
251
252         /* get plain ascii version of string. Write out length of
253          * string, then plain string itself */
254         if (internalform == YES) {
255                 buff = ascii_str(str, NO, YES, TM_NONE);
256                 length = strlen(buff);
257                 bytes = wr_varlength(mfile, (UINT32B) length);
258                 bytes += write(mfile, buff, (unsigned) length);
259         }
260         else {
261                 length = strlen(str);
262                 bytes = wr_varlength(mfile, (UINT32B) length);
263                 bytes += write(mfile, str, (unsigned) length);
264         }
265
266         /* return number of bytes written */
267         return(bytes);
268 }
269 \f
270
271 /* given a number, write to MIDI file in MIDI variable length format.
272  * Return number of bytes written. */
273
274 UINT32B
275 wr_varlength(mfile, num)
276
277 UINT32B num;
278
279 {
280         unsigned char buff[4];
281         int i;
282         int shift;
283         
284
285         /* Because only 7 bits of each MIDI byte can be used,
286          * there is only support for numbers up to 28 bits long. */
287         if ((num & 0xf0000000) != 0) {
288                 ufatal("midi value too large");
289         }
290
291         /* convert value to the MIDI variable-length number, which
292          * uses the lower 7 bits of each byte as parts of the number, and
293          * the high order bit as a flag to say which is the last byte of
294          * the (potentially) multi-byte number */
295         for (i = 0, shift = 21; shift >= 7; shift -= 7) {
296                 if ( (num >> shift) || (i > 0)) {
297                         buff[i++] = 0x80 | ((num >> shift) & 0x7f);
298                 }
299         }
300         buff[i] = num & 0x7f;
301         (void) write(mfile, buff, (unsigned) (i + 1));
302         return (UINT32B) (i+1);
303 }
304 \f
305
306 /* do key signature. Return number of bytes written */
307
308 UINT32B
309 midi_keysig(mfile, sharps, is_minor)
310
311 int mfile;
312 int sharps;
313 int is_minor;   /* YES if minor */
314
315 {
316         UINT32B bytes;
317         unsigned char buff[8];
318
319
320         bytes = write_delta(mfile);
321         buff[0] = 0xff;
322         buff[1] = 0x59;
323         buff[2] = 0x02;
324         buff[3] = (char) sharps;
325         buff[4] = (is_minor == YES ? 1 : 0);
326         (void) write(mfile, buff, 5);
327
328         return(bytes + 5);
329 }
330 \f
331
332 /* write out the timesig in Score SSV. Return number of bytes written */
333
334 UINT32B
335 midi_timesig(mfile)
336
337 int mfile;
338 {
339         UINT32B bytes;
340         unsigned char buff[8];
341
342
343         /* With additive time signatures, it is possible to get an effective
344          * time signature that won't fit in 7 bits. In that case, we don't
345          * do any time signature, since we can't represent it. */
346         if (Score.timenum > 127) {
347                 return(0);
348         }
349
350         bytes = write_delta(mfile);
351         buff[0] = 0xff;
352         buff[1] = 0x58;
353         buff[2] = 0x04;
354         buff[3] = (char) Score.timenum;
355         buff[4] = (unsigned char) drmo(Score.timeden);
356         buff[5] = clocks(Score.timeden);
357         buff[6] = 0x8;
358         bytes += write(mfile, buff, 7);
359         return(bytes);
360 }
361 \f
362
363 /* find group before given group. If none before it in current measure,
364  * back up in main list to find corresponding group list, and use final group
365  * in that list. If no group exists, create one. */
366
367 struct GRPSYL *
368 grp_before(gs_p, mll_p, staffno, v)
369
370 struct GRPSYL *gs_p;    /* find group before this one */
371 struct MAINLL *mll_p;   /* the list containing gs_p is attached to main list here */
372 int staffno;
373 int v;                  /* voice */
374
375 {
376         int found_bar = NO;
377
378
379         if (gs_p->prev != (struct GRPSYL *) 0) {
380                 /* oh good. There's another group before this one in the
381                  * current measure, so just return it */
382                 return(gs_p->prev);
383         }
384
385         /* have to go back to previous measure, if any. Start searching
386          * backwards in main list. */
387         for (mll_p = mll_p->prev; mll_p != (struct MAINLL *) 0;
388                                                 mll_p = mll_p->prev) {
389                 switch (mll_p->str) {
390                 case S_STAFF:
391                         if (found_bar == NO) {
392                                 /* still in current measure */
393                                 break;
394                         }
395
396                         if (mll_p->u.staff_p->staffno == staffno) {
397                                 /* we found the previous measure */
398                                 if (mll_p->u.staff_p->groups_p[v]
399                                                 != (struct GRPSYL *) 0) {
400                                         /* find and return last group */
401                                         for (gs_p=mll_p->u.staff_p->groups_p[v];
402                                                         gs_p->next !=
403                                                         (struct GRPSYL *) 0;
404                                                         gs_p = gs_p->next) {
405                                                 ;
406                                         }
407                                         return(gs_p);
408                                 }
409                                 else {
410                                         /* this voice wasn't present before.
411                                          * Will have to create a measure */
412                                         return(create_meas_space(mll_p));
413                                 }
414                         }
415                         else if (mll_p->u.staff_p->staffno < staffno) {
416                                 /* corresponding staff does not exist in this
417                                  * measure. The only time this should happen is
418                                  * if user changed the number of staffs.
419                                  * So create staff */
420                                 return(create_prev_grp(mll_p, staffno, v));
421                         }
422                         break;
423
424                 case S_BAR:
425                         found_bar = YES;
426                         break;
427
428                 default:
429                         /* ignore other things */
430                         break;
431                 }
432         }
433
434         /* Fell off the top of the list. This used to be possible,
435          * and we called create_prev_grp() to create a measure.
436          * But the measure really needs to be created much earlier--
437          * before makechords() is run--in order for squeezing to work right.
438          * So that's what we do now. So we should never get here. */
439         pfatal("fell off top of list in grp_before()");
440         /* NOTREACHED */
441         return(create_prev_grp(mll_p, staffno, v));
442 }
443 \f
444
445 /* create a new STAFF struct and insert in main list, with grpcont of
446  * space and fulltime of the measure. Return pointer to the GRPSYL of
447  * appropriate voice of the STAFF that was created. */
448
449 static struct GRPSYL *
450 create_prev_grp(mll_p, staffno, v)
451
452 struct MAINLL *mll_p;   /* insert here */
453 int staffno;
454 int v;
455
456 {
457         struct MAINLL *new_p;   /* new STAFF */
458         int i;
459
460
461         new_p = newMAINLLstruct(S_STAFF, -1);
462         new_p->u.staff_p->staffno = (short) staffno;
463         insertMAINLL(new_p, mll_p);
464         for (i = 0; i < MAXVOICES; i++) {
465                 new_p->u.staff_p->groups_p[i]
466                                         = create_meas_space(mll_p);
467         }
468
469         /* if added to beginning of list, have to add bar as well */
470         if (mll_p == (struct MAINLL *) 0) {
471                 struct MAINLL *mbar_p;
472
473                 mbar_p = newMAINLLstruct(S_BAR, -1);
474                 insertMAINLL(mbar_p, new_p);
475         }
476
477         return(new_p->u.staff_p->groups_p[v]);
478 }
479 \f
480
481 /* create a measure space as long as that of the reference measure (or of 4/4
482  * if no reference) and return it */
483
484 static struct GRPSYL *
485 create_meas_space(mll_p)
486
487 struct MAINLL *mll_p;   /* use this for reference to get measure length */
488
489 {
490         struct GRPSYL *gs_p;    /* new grpsyl */
491         struct GRPSYL *egs_p;   /* existing grpsyl */
492
493
494         gs_p = newGRPSYL(GS_GROUP);
495         gs_p->grpcont = GC_SPACE;
496
497         /* figure out how much full time to give the group. If mll_p is not
498          * null, we are adding a staff to an existing measure, so use
499          * length of its first voice. Count up the length of existing measure */
500         gs_p->fulltime = Zero;
501         if (mll_p != (struct MAINLL *) 0 && mll_p->str == S_STAFF) {
502                 for (egs_p = mll_p->u.staff_p->groups_p[0];
503                                         egs_p != (struct GRPSYL *) 0;
504                                         egs_p = egs_p->next) {
505
506                         gs_p->fulltime = radd(gs_p->fulltime, egs_p->fulltime);
507                 }
508         }
509         else {
510                 /* at beginning of list, use default  of 1/1 (the reduced
511                  * form of 4/4) */
512                 gs_p->fulltime.n = gs_p->fulltime.d = 1;
513         }
514
515         return(gs_p);
516
517 \f
518
519 /* add a rest of the specified fulltime duration after the specified group.
520  * Since this is just for midi purposes, don't worry about filling in all
521  * the fields. */
522
523 void
524 add_rest(gs_p, fulltime)
525
526 struct GRPSYL *gs_p;    /* add rest after this group */
527 RATIONAL fulltime;      /* make it this long */
528
529 {
530         struct GRPSYL *newgs_p;
531
532
533         if (gs_p == (struct GRPSYL *) 0) {
534                 pfatal("null group passed to add_rest");
535         }
536
537         newgs_p = newGRPSYL(GS_GROUP);
538         newgs_p->grpcont = GC_REST;
539         rred (&fulltime);
540         newgs_p->fulltime = fulltime;
541         newgs_p->next = gs_p->next;
542         newgs_p->prev = gs_p;
543         gs_p->next = newgs_p;
544         if (newgs_p->next != (struct GRPSYL *) 0) {
545                 newgs_p->next->prev = newgs_p;
546         }
547 }
548 \f
549
550 /* when all voices have space, that should be squeezed to zero time.
551  * Go through main list. For each CHHEAD found,
552  * go down the list of chords. For each chord, see if
553  * if it is an all-space chord. If so, call fix_spacechord() to
554  * handle it.
555  */
556
557 void
558 midi_squeeze()
559
560 {
561         struct MAINLL *mll_p;   /* walk through main list */
562         struct CHORD *ch_p;     /* walk through list of chords */
563
564
565         debug(256, "midi_squeeze");
566
567         initstructs();
568         for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0;
569                                                 mll_p = mll_p->next) {
570
571                 /* skip everything except CHHEADs and SSV updates */
572                 if (mll_p->str != S_CHHEAD) {
573                         if (mll_p->str == S_SSV) {
574                                 asgnssv(mll_p->u.ssv_p);
575                         }
576                         continue;
577                 }
578
579                 /* do each chord */
580                 for (ch_p = mll_p->u.chhead_p->ch_p;
581                                                 ch_p != (struct CHORD *) 0;
582                                                 ch_p = ch_p->ch_p) {
583
584                         if (ch_p->width == 0.0) {
585                                 /* found one to squeeze, do it */
586                                 fix_spacechord(mll_p, ch_p);
587                         }
588                 }
589         }
590 }
591 \f
592
593 /* given an all-space chord to crunch, split up any groups that
594  * spill into this chord or extend beyond it. Then set the grpvalue of each
595  * group in the chord to GV_ZERO.
596  */
597
598 static void
599 fix_spacechord(chmll_p, ch_p)
600
601 struct MAINLL *chmll_p;         /* chord is hanging off this CHHEAD */
602 struct CHORD *ch_p;             /* zero-width chord */
603
604 {
605         struct MAINLL *mll_p;   /* walk through STAFFs in main list */
606         struct GRPSYL *gs_p;    /* walk through groups in chord */
607         struct GRPSYL *group_p; /* head of list of grpsyls in measure */
608         RATIONAL minspacetime;  /* time of shortest space in chord */
609         RATIONAL chordstart;    /* where in measure chord begins */
610         RATIONAL chordend;      /* where the space ends */
611         RATIONAL new_chordend;  /* tentative new value for chordend */
612         RATIONAL grpstart;      /* where in measure grpsyl begins */
613         int v;                  /* voice index */
614
615
616         /* first find the smallest duration in the chord. Can't use
617         * ch_p->duration here because grace notes have been adjusted
618         * by now to take some time */
619         minspacetime = ch_p->gs_p->fulltime;
620         for (gs_p = ch_p->gs_p->gs_p; gs_p != (struct GRPSYL *) 0;
621                                                 gs_p = gs_p->gs_p) {
622
623                 /* skip lyrics */
624                 if (gs_p->grpsyl == GS_SYLLABLE) {
625                         continue;
626                 }
627
628                 /* skip things on tab staff--we use the associated tabnotes
629                  * staff instead */
630                 if (is_tab_staff(gs_p->staffno) == YES) {
631                         continue;
632                 }
633
634                 /* double check */
635                 if (gs_p->grpcont != GC_SPACE) {
636                         pfatal("non-space in zero-width chord");
637                 }
638
639                 /* find minimum time value of space */
640                 if (LT(gs_p->fulltime, minspacetime)) {
641                         minspacetime = gs_p->fulltime;
642                 }
643         }
644
645         /* find the start of the chord. Can't use ch_p->startime because
646          * we may have squeezed out time earlier in the measure and need
647          * to compensate for that. So have to count up the time before
648          * the group at the top of the chord. */
649         for (chordstart = Zero, gs_p = ch_p->gs_p->prev;
650                                 gs_p != (struct GRPSYL *) 0;
651                                 gs_p = gs_p->prev) {
652                 chordstart = radd(chordstart, gs_p->fulltime);
653         }
654
655         /* if spaces are overlapped in strange ways between different
656          * voices, the minspacetime we found above may actually be too long.
657          * If the time between this chord and the next is less than the
658          * minspacetime found so far, make minspacetime the time till the
659          * next chord */
660         if (ch_p->ch_p != (struct CHORD *) 0) {
661                 if (LT(rsub(ch_p->ch_p->starttime, chordstart), minspacetime)) {
662                         minspacetime = rsub(ch_p->ch_p->starttime, chordstart);
663                 }
664         }
665
666         /* That still isn't completely adequate to find where the space actually
667          * ends, because if there were lots of grace notes moved back into a
668          * space, and a very short space in another voice, they could overlap.
669          * So go through the list of voices, seeing where the real end is.
670          * This code is unfortunately very similar to the code below it,
671          * yet different enough to make it hard to make it
672          * into a common function. */
673         chordend = radd(chordstart, minspacetime);
674         for (mll_p = chmll_p->next; mll_p != (struct MAINLL *) 0;
675                                                 mll_p = mll_p->next) {
676
677                 /* CHHEAD is followed immediately by STAFFS, so when we
678                  * hit something other than STAFF, we are done */
679                 if (mll_p->str != S_STAFF) {
680                         break;
681                 }
682
683                 /* do each voice */
684                 for (v = 0; v < MAXVOICES; v++ ) {
685                         /* Check if we have a special case where the actual
686                          * space is shorter than we thought. We unfortunately
687                          * cannot use hasspace() here, because it ignores grace
688                          * notes, and by now, grace notes have some time.
689                          * Find the chord that begins the space in question. */
690                         grpstart = Zero;
691
692                         if ((gs_p = mll_p->u.staff_p->groups_p[v])
693                                                 == (struct GRPSYL *) 0) {
694                                 /* no voice here, so that's all space.
695                                  * That's good: we're done with this voice */
696                                 continue;
697                         }
698
699                         /* grace notes at the beginning of the measure
700                          * have effectively been moved timewise
701                          * into previous measure, so discount them. */
702                         for (   ; gs_p->grpvalue == GV_ZERO
703                                         && gs_p->grpcont == GC_NOTES;
704                                         gs_p = gs_p->next) {
705                                 ;
706                         }
707
708                         for (  ; gs_p != (struct GRPSYL *) 0;
709                                                         gs_p = gs_p->next) {
710
711                                 /* see if this group start corresponds
712                                  * with start of chord, or spills into
713                                  * the chord or is the last chord. If
714                                  * so, that's the one we want. */
715                                 if (EQ(grpstart, chordstart) ||
716                                                 GT(radd(grpstart,
717                                                 gs_p->fulltime),
718                                                 chordstart) ||
719                                                 gs_p->next ==
720                                                 (struct GRPSYL *) 0) {
721                                         /* found appropriate group */
722                                         break;
723                                 }
724                                 else {
725                                         /* accummulate time so far */
726                                         grpstart = radd(grpstart, gs_p->fulltime);
727                                 }
728                         }
729
730                         if (gs_p == (struct GRPSYL *) 0) {
731                                 pfatal("failed to find space group");
732                         }
733
734                         if (gs_p->grpcont != GC_SPACE) {
735                                 /* things overlapped so much after the
736                                  * grace note adjustments and such
737                                  * that this isn't really a
738                                  * crunch-able chord after all. */
739                                 return;
740                         }
741
742                         /* need to adjust amount of space we
743                          * can really crunch. Find where this chord
744                          * ends, and add in the time of any immediately
745                          * following space groups. */
746                         new_chordend = radd(grpstart, gs_p->fulltime);
747                         for (gs_p = gs_p->next; gs_p != (struct GRPSYL *) 0;
748                                                 gs_p = gs_p->next) {
749
750                                 if (gs_p->grpcont == GC_SPACE) {
751                                         new_chordend = radd(new_chordend,
752                                                         gs_p->fulltime);
753                                 }
754                                 else {
755                                         break;
756                                 }
757                         }
758
759                         /* if the newly calculated end is sooner than
760                          * what we had before, then adjust accordingly. */
761                         if (LT(new_chordend, chordend)) {
762                                 chordend = new_chordend;
763                         }
764                 }
765         }
766
767         /* recalculate the minspace time after any adjustment */
768         minspacetime = rsub(chordend, chordstart);
769         
770
771         /* go down each voice of each staff.
772          * For each, find the space group associated with the chord.
773          * If it has the same starttime as the chord and has minspacetime
774          * duration, it's easy: we just mark it GV_ZERO. Otherwise, if
775          * it starts earlier, we have to split off a group in the front
776          * first, and if it lasts longer than the end of the chord, we
777          * have to split off a group at the end first.
778          */
779         for (mll_p = chmll_p->next; mll_p != (struct MAINLL *) 0;
780                                                 mll_p = mll_p->next) {
781                 /* CHHEAD is followed immediately by STAFFS, so when we
782                  * hit something other than STAFF, we are done */
783                 if (mll_p->str != S_STAFF) {
784                         break;
785                 }
786
787                 /* do each voice */
788                 for (v = 0; v < MAXVOICES; v++ ) {
789
790                         /* get shorter name for list of grpsyls */
791                         group_p = mll_p->u.staff_p->groups_p[v];
792
793                         if (group_p == (struct GRPSYL *) 0) {
794                                 continue;
795                         }
796
797                         /* go through groups, add up time till we find the
798                          * group we're looking for */
799                         grpstart = Zero;
800
801                         /* grace notes at the beginning of the measure
802                          * have effectively been moved timewise
803                          * into previous measure, so discount them. */
804                         for (gs_p = group_p; gs_p->grpvalue == GV_ZERO
805                                         && gs_p->grpcont == GC_NOTES;
806                                         gs_p = gs_p->next) {
807                                 ;
808                         }
809
810                         for (  ; gs_p != (struct GRPSYL *) 0;
811                                                 gs_p = gs_p->next) {
812
813                                 /* see if this group start corresponds with
814                                  * start of chord */
815                                 if (EQ(grpstart, chordstart)) {
816                                         /* found appropriate group */
817                                         break;
818                                 }
819
820                                 else if (GT(radd(grpstart,gs_p->fulltime),
821                                                                  chordstart)) {
822                                         /* This group spills into space
823                                          * to be crunched.
824                                          * Split off beginning of group. */
825                                         splitspace(gs_p,
826                                                 rsub(chordstart, grpstart));
827
828                                         /* point to added group */
829                                         gs_p = gs_p->next;
830
831                                         /* splice added group into chord */
832                                         splicechord(gs_p, ch_p);
833
834                                         /* found appropriate group */
835                                         break;
836                                 }
837                                 else {
838                                         /* haven't gotten to the group yet.
839                                          * Add on the time taken by this
840                                          * group in preparation for next
841                                          * trip around the loop */
842                                         grpstart = radd(grpstart, gs_p->fulltime);
843                                         /* if last group in measure, this has
844                                          * to be the appropriate one */
845                                         if (gs_p->next == (struct GRPSYL *) 0) {
846                                                 break;
847                                         } 
848                                 }
849                         }
850
851                         if (gs_p == (struct GRPSYL *) 0) {
852                                 pfatal("failed to find space group");
853                         }
854
855                         /* if group extended beyond end of
856                          * chord, split the group and splice added group
857                          * into chord */
858                         if (GT(gs_p->fulltime, minspacetime)) {
859                                 splitspace(gs_p, minspacetime);
860                                 splicechord(gs_p->next, ch_p->ch_p);
861                         }
862
863                         /* mark as taking no time */
864                         gs_p->grpvalue = GV_ZERO;
865                 }
866         }
867 }
868 \f
869
870 /* split a space grpsyl into two. The original group becomes the first
871  * group, having the specified duration. A new group is added after it,
872  * having the remainder of the time taken by the original group. */
873
874 static void
875 splitspace(gs_p, duration)
876
877 struct GRPSYL *gs_p;    /* split this group */
878 RATIONAL duration;      /* make the first group of split this long */
879
880 {
881         struct GRPSYL *newgs_p; /* added group */
882
883
884         /* bug insurance */
885         if (gs_p == (struct GRPSYL *) 0 ||
886                         (gs_p->grpcont != GC_SPACE &&
887                         svpath(gs_p->staffno, VISIBLE)->visible == YES) ) {
888                 pfatal("bad group passed to splitspace");
889         }
890
891         /* split into 2 groups, one taking duration, and the
892          * other taking the remainder */
893         newgs_p = newGRPSYL(GS_GROUP);
894         copy_attributes(newgs_p, gs_p);
895         newgs_p->grpcont = GC_SPACE;
896         newgs_p->fulltime = rsub(gs_p->fulltime, duration);
897         gs_p->fulltime = duration;
898
899         /* link new one into list */
900         newgs_p->next = gs_p->next;
901         newgs_p->prev = gs_p;
902         gs_p->next = newgs_p;   
903         if (newgs_p->next != (struct GRPSYL *) 0) {
904                 newgs_p->next->prev = newgs_p;
905         }
906 }
907 \f
908
909 /* splice a grpsyl into a chord */
910
911 static void
912 splicechord(gs_p, ch_p)
913
914 struct GRPSYL *gs_p;    /* splice in this group */
915 struct CHORD *ch_p;     /* splice into this chord */
916
917 {
918         struct GRPSYL *nxtgs_p;         /* next group in chord list */
919         struct GRPSYL **ins_p_p;        /* where to insert new grpsyl */
920
921
922         if (ch_p == (struct CHORD *) 0) {
923                 /* this could happen if user gave measure of space, and
924                  * then following measure started with a grace note. The
925                  * grace note got moved into the space, but there was no
926                  * chhead created for it. It should be safe to just
927                  * not link it into a chhead, so return */
928                 return;
929         }
930
931         if (gs_p == (struct GRPSYL *) 0) {
932                 pfatal("null pointer in splicechord");
933         }
934
935         /* Figure out where to insert. */
936         for (ins_p_p = &(ch_p->gs_p); (*ins_p_p) != (struct GRPSYL *) 0;
937                                                 ins_p_p = &((*ins_p_p)->gs_p)) {
938
939                 /* gets inserted here if next group is for a higher staffno or
940                  * for a higher voice number of the same staffno */
941                 nxtgs_p = *ins_p_p;
942                 if (nxtgs_p == (struct GRPSYL *) 0) {
943                         /* goes at end of list */
944                         break;
945                 }
946                 if (nxtgs_p->staffno > gs_p->staffno ||
947                                    (nxtgs_p->staffno == gs_p->staffno &&
948                                    nxtgs_p->vno > gs_p->vno)) {
949                         break;
950                 }
951         }
952
953         if (ins_p_p == (struct GRPSYL **) 0) {
954                 pfatal("couldn't find where to insert new grpsyl into chord");
955         }
956
957         /* insert it */
958         gs_p->gs_p = *ins_p_p;
959         *ins_p_p = gs_p;
960 }
961 \f
962
963 /* if user specifies the default guitar tab staff, then we want to transpose
964  * everything down an octave, because a standard guitar sounds an octave
965  * lower than it is written. */
966
967 void
968 guitar_transpose()
969
970 {
971         struct MAINLL *mll_p;
972
973
974         /* go through the main list looking for things to transpose */
975         initstructs();
976         for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) {
977                 if (mll_p->str == S_STAFF) {
978                         /* if there is a staff below this and that staff is a
979                          * guitar tab staff, then we need to transpose.
980                          * Index into Staff by this staff's number, because
981                          * staff numbers start at 1, but Staff index starts at 0
982                          */
983                         if (mll_p->u.staff_p->staffno < Score.staffs &&
984                                         Staff[mll_p->u.staff_p->staffno]
985                                         .strinfo == Guitar) {
986                                 guitar_grpsyl_transpose(mll_p->u.staff_p->groups_p[0]);
987                         }
988                 }
989                 else if (mll_p->str == S_SSV) {
990                         asgnssv(mll_p->u.ssv_p);
991                 }
992         }
993 }
994 \f
995
996 /* given a group, transpose everything down by an octave */
997
998 static void
999 guitar_grpsyl_transpose(gs_p)
1000
1001 struct GRPSYL *gs_p;
1002
1003 {
1004         register int n;
1005
1006         for(   ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
1007                 for (n = 0; n < gs_p->nnotes; n++) {
1008                         if (gs_p->notelist[n].octave > 0) {
1009                                 (gs_p->notelist[n].octave)--;
1010                         }
1011                         else {
1012                                 pfatal("guitar transposition results in note below octave 0");
1013                         }
1014                 }
1015         }
1016 }
1017 \f
1018
1019 /* If there is an accidental on a note on one voice, it should really apply
1020  * the other voice on that same staff too. So propogate these accidentals
1021  * to the other voice if necessary.
1022  */
1023
1024 void
1025 other_voice_accidentals(staff_p)
1026
1027 struct STAFF *staff_p;
1028
1029 {
1030         struct GRPSYL *gs_p;
1031         int v;                  /* voice */
1032         int n;                  /* notelist index */
1033         RATIONAL begin_time, end_time;
1034
1035         /* if there is only one voice on the staff, or if either
1036          * voice is a measure space or rest or multirest, nothing to do. Since
1037          * we're doing MIDI, we know a "measure" GRPSYL
1038          * will be rest or space, not mrpt, so can just check that. */
1039          if ( (staff_p->groups_p[0]->is_meas == YES) ||
1040                         (staff_p->groups_p[1] == (struct GRPSYL *) 0) ||
1041                         (staff_p->groups_p[1]->is_meas == YES) ) {
1042                 return;
1043         }
1044
1045         /* do each voice */
1046         for (v = 0; v < MAXVOICES; v++) {
1047
1048                 /* do each note of each chord */
1049                 begin_time = Zero;
1050                 for (gs_p = staff_p->groups_p[v]; gs_p != (struct GRPSYL *) 0;
1051                                                         gs_p = gs_p->next) {
1052
1053                         for (n = 0; n < gs_p->nnotes; n++) {
1054
1055                                 /* if this note doesn't have an accidental
1056                                  * nothing more to do on it */
1057                                 if (gs_p->notelist[n].accidental == '\0') {
1058                                         continue;
1059                                 }
1060
1061                                 /* This note does have an accidental.
1062                                  * Go forward and see how long it lasts.
1063                                  * It will last until there is another
1064                                  * accidental on the same note letter/octave
1065                                  * or until end of measure.
1066                                  */
1067                                 end_time = find_acc_end_time(begin_time, gs_p, n);
1068
1069                                 /* now check the other voice.
1070                                  * If the same letter/octave note appears
1071                                  * on that voice on or after the begin time but
1072                                  * before the end time, and that note
1073                                  * does not already have an accidental,
1074                                  * give it the same accidental as
1075                                  * was found on the other voice.
1076                                  */
1077                                 propogate_accidental( &(gs_p->notelist[n]),
1078                                         begin_time, end_time,
1079                                         staff_p->groups_p[v == 0 ? 1 : 0]);
1080                         }
1081
1082                         /* accumulate time so far in measure */
1083                         begin_time = radd(begin_time, gs_p->fulltime);
1084                 }
1085         }
1086 }
1087
1088 /* Given an GRPSYL and a note n in its notelist, and a begin_time, return
1089  * the end_time which is either the end of the measure or the next instance
1090  * of the given note which has an accidental on it. */
1091
1092 static RATIONAL
1093 find_acc_end_time(begin_time, gs_p, n)
1094
1095 RATIONAL begin_time;
1096 struct GRPSYL *gs_p;
1097 int n;
1098
1099 {
1100         RATIONAL end_time;
1101         struct GRPSYL *end_gs_p;
1102         int n2;
1103
1104
1105         end_time = begin_time;
1106         end_gs_p = gs_p;
1107         for ( ; ; ) {
1108                 /* Add up time that accidental lasts */
1109                 end_time = radd(end_time, end_gs_p->fulltime);
1110
1111                 if ((end_gs_p = end_gs_p->next) == (struct GRPSYL *) 0) {
1112                         /* Hit end of measure */
1113                         return(end_time);
1114                 }
1115
1116                 /* See if this group has the same note as had the accidental,
1117                  * and if so, whether it has a new accidental. */
1118                 for (n2 = 0; n2 < end_gs_p->nnotes; n2++) {
1119                         if ((gs_p->notelist[n].letter
1120                                         == end_gs_p->notelist[n2].letter) &&    
1121                                         (gs_p->notelist[n].octave
1122                                         == end_gs_p->notelist[n2].octave)) {
1123
1124                                 /* does have the same note. check accidental */
1125                                 if (end_gs_p->notelist[n2].accidental != '\0') {
1126                                         /* a new accidental, so this cancels
1127                                          * the one we had */
1128                                         return(end_time);
1129                                 }
1130                                 break;
1131                         }
1132                 }
1133         }
1134 }
1135 \f
1136
1137 /* Check the groups in list pointed to by gs_p.
1138  * If the letter/octave of the given note_p appears on or after the begin time
1139  * but before the end time, and that note does not already have an accidental,
1140  * give it the same accidental as was found on note_p. */
1141
1142 static void
1143 propogate_accidental(note_p, begin_time, end_time, gs_p)
1144
1145 struct NOTE *note_p;    /* look for note matching this one */
1146 RATIONAL begin_time;    /* look between the begin_time and end_time */
1147 RATIONAL end_time;
1148 struct GRPSYL *gs_p;    /* look in this list and adjust if needed */
1149
1150 {
1151         RATIONAL accumulated_time;
1152         int n;
1153
1154
1155         for (accumulated_time = Zero;
1156                                 gs_p != (struct GRPSYL *) 0;
1157                                 accumulated_time = radd(accumulated_time,
1158                                 gs_p->fulltime), gs_p = gs_p->next) {
1159
1160                 if (LT(accumulated_time, begin_time)) {
1161                         /* haven't gotten to begin yet */
1162                         continue;
1163                 }
1164
1165                 if (GE(accumulated_time, end_time)) {
1166                         /* reached end time */
1167                         return;
1168                 }
1169
1170                 /* See if this group contains the note of interest */
1171                 for (n = 0; n < gs_p->nnotes; n++) {
1172                         if ( (gs_p->notelist[n].letter == note_p->letter) &&
1173                                         (gs_p->notelist[n].octave == note_p->octave) ) {
1174                                 /* if note already has an accidental,
1175                                  * the one from the other voice doesn't
1176                                  * count. Otherwise propogate the
1177                                  * accidental from the other voice */
1178                                 if (gs_p->notelist[n].accidental == '\0') {
1179                                         gs_p->notelist[n].accidental
1180                                                         = note_p->accidental;
1181                                 }
1182
1183                                 /* Found the note and fixed it if needed,
1184                                  * so our job here is done */
1185                                 return;
1186                         }
1187                 }
1188         }
1189 }
1190 \f
1191
1192 /* If doing MIDI with the -x option, we move all the midi STUFFs in the
1193  * skipped leading measures to the "extra" empty measure that exists for MIDI.
1194  * This ensure that any MIDI parameters, instruments, etc are correct
1195  * for the place in the song where we are actually starting.
1196  */
1197
1198 void
1199 mv_midi_items(mll_p, topstaff_mll_p)
1200
1201 struct MAINLL *mll_p;   /* points to a STAFF that may have MIDI items to move */
1202 struct MAINLL *topstaff_mll_p;  /* points to STAFF where "all" MIDI items will go */
1203
1204 {
1205         struct STUFF *stuff_p;  /* stuff currently being processed */
1206         struct STUFF *next;     /* we may move the current STUFF to another
1207                                  * list, so need to save its "next" */
1208         char *key;              /* midi directive keyword */
1209         int leng;               /* length of key */
1210         char *arg;              /* arg after the = */
1211         int staffno;
1212
1213
1214         staffno = mll_p->u.staff_p->staffno;
1215         for (stuff_p = mll_p->u.staff_p->stuff_p; stuff_p != 0; stuff_p = next) {
1216                 /* keep track of next, in case we delete the current */
1217                 next = stuff_p->next;
1218
1219                 if (next == stuff_p) {
1220                         pfatal("loop detected in MIDI list");
1221                 }
1222
1223                 if (stuff_p->stuff_type == ST_MIDI) {
1224                         if (getkeyword(stuff_p->string+2, &key, &leng, &arg)
1225                                                                 == NO) {
1226                                 /* invalid, so ignore it. */
1227                                 continue;
1228                         }
1229
1230                         /* Some MIDI things, like cue point should just be
1231                          * ignored if we are skipping over the music,
1232                          * so just check for and move things we care about.
1233                          */
1234                         if (matches(key, leng, "program") == YES ||
1235                                         matches(key, leng, "tempo") == YES ||
1236                                         matches(key, leng, "onvelocity") == YES ||
1237                                         matches(key, leng, "channel") == YES ||
1238                                         matches(key, leng, "parameter") == YES ||
1239                                         matches(key, leng, "offvelocity") == YES ||
1240                                         matches(key, leng, "name") == YES ||
1241                                         matches(key, leng, "instrument") == YES ||
1242                                         matches(key, leng, "hex") == YES ||
1243                                         matches(key, leng, "port") == YES ||
1244                                         matches(key, leng, "chanpressure") == YES) {
1245                                 /* Found something to move; move it */
1246                                 mv_skipped_midi(stuff_p, staffno, topstaff_mll_p);
1247                         }
1248                 }
1249         }
1250 }
1251 \f
1252
1253 /* With -x option and MIDI, this moves a MIDI STUFF from where it was in
1254  * a skipped measure to the special "empty" measure at the beginning of the
1255  * piece.  In general, for any given MIDI thing, only the last one matters,
1256  * so we only need to keep the last one.
1257  */
1258
1259 static void
1260 mv_skipped_midi(stuff_p, staffno, topstaff_mll_p)
1261
1262 struct STUFF *stuff_p;          /* Midi STUFF to be moved */
1263 int staffno;                    /* Which staff it is for */
1264 struct MAINLL *topstaff_mll_p;  /* The STAFF where midi "all" STUFF go.
1265                                  * Other STAFFs, if any, will surround it. */
1266
1267 {
1268         int leng;                       /* length of MIDI keyword */
1269         char *oldtext;                  /* text of STUFF that already
1270                                          * exists in the list */
1271         char *newtext;                  /* text of STUFF to be added */
1272         struct MAINLL *mll_p;           /* for finding STAFF to link to */
1273         struct STAFF *staff_p = 0;      /* the STAFF to link to */
1274         struct STUFF **stuff_p_p;       /* for finding where to add
1275                                          * the new stuff_p into list */
1276         static char *alphabet = "abcdefghijklmnopqrstuvwxyz";
1277
1278         /* Find the correct STAFF to attach to. We all passed a pointer
1279          * to the STAFF for midi "all" items. For non-"all" items,
1280          * we search in the main list for the correct STAFF. 
1281          */
1282         if (stuff_p->all == YES || staffno == topstaff_mll_p->u.staff_p->staffno) {
1283                 staff_p = topstaff_mll_p->u.staff_p;
1284         }
1285         else if (staffno < topstaff_mll_p->u.staff_p->staffno) {
1286                 /* Search backward for correct staff from the "all" staff.
1287                  * This case probably won't happen often--only if some
1288                  * staffs at the top are currently invisible (so they are
1289                  * not the current "top visible staff").
1290                  */
1291                 for (mll_p = topstaff_mll_p; mll_p->str == S_STAFF;
1292                                                 mll_p = mll_p->prev) {
1293                         if (mll_p->u.staff_p->staffno == staffno) {
1294                                 staff_p = mll_p->u.staff_p;
1295                                 break;
1296                         }
1297                 }
1298         }
1299         else {
1300                 /* Search forwards for correct staff from the "all" staff. */
1301                 for (mll_p = topstaff_mll_p; mll_p->str == S_STAFF;
1302                                                 mll_p = mll_p->next) {
1303                         if (mll_p->u.staff_p->staffno == staffno) {
1304                                 staff_p = mll_p->u.staff_p;
1305                                 break;
1306                         }
1307                 }
1308         }
1309         if (staff_p == 0) {
1310                 /* User must have reduced the number of staffs during the
1311                  * skipped part, so this one is irrelevant. */
1312                 return;
1313         }
1314
1315         /* Add to end of list. When going through the list,
1316          * see if there is already an item of that type (for param
1317          * have to check the specific param matches too), and if so delete
1318          * the earlier one, because this new one overrides it.
1319          */
1320         newtext = stuff_p->string + 2;
1321         for (stuff_p_p = &(staff_p->stuff_p); *stuff_p_p != 0;
1322                                         stuff_p_p = &((*stuff_p_p)->next) ) {
1323
1324                 /* If for different place,
1325                  * this one doesn't count for matching */
1326                 if ( (*stuff_p_p)->place != stuff_p->place) {
1327                         continue;
1328                 }
1329
1330                 oldtext = (*stuff_p_p)->string + 2;
1331
1332                 /* Since hex is arbitrary data, we always keep it */
1333                 if (matches(oldtext, strlen(oldtext), "hex")) {
1334                         continue;
1335                 }
1336
1337                 /* Names can be abbreviated,
1338                  * so match up to whichever is shorter */
1339                 leng = MIN(strspn(oldtext, alphabet), strspn(newtext, alphabet));
1340                 if (matches(newtext, leng, oldtext)== YES) {
1341                         /* If it's something other than param, we can get
1342                          * rid of the existing one */
1343                         if (strncmp(newtext, "par", 3) != 0) {
1344                                 *stuff_p_p = (*stuff_p_p)->next;
1345                         }
1346                         else {
1347                                 /* Compare parameter numbers and if they match,
1348                                  * delete the existing one.
1349                                  */
1350                                 char *new_eq_p, *old_eq_p;      /* loc of '=' */
1351                                 int oldparm, oldval;
1352                                 int newparm, newval;
1353
1354                                 new_eq_p = strchr(newtext, '=');
1355                                 old_eq_p = strchr(oldtext, '=');
1356                                 if (new_eq_p != 0 && old_eq_p != 0) {
1357                                         if (get_param(new_eq_p + 1,
1358                                                         stuff_p->inputfile,
1359                                                         stuff_p->inputlineno,
1360                                                         &newparm, &newval)
1361                                                         == YES &&
1362                                                         get_param(old_eq_p + 1,
1363                                                         (*stuff_p_p)->inputfile,
1364                                                         (*stuff_p_p)->inputlineno,
1365                                                         &oldparm, &oldval)
1366                                                         == YES &&
1367                                                         oldparm == newparm) {
1368                                                 *stuff_p_p = (*stuff_p_p)->next;
1369                                         }
1370                                 }
1371                         }
1372
1373                         if (*stuff_p_p == 0) {
1374                                 /* If deleted last item in the existing list,
1375                                  * jump out, so we won't try to deference
1376                                  * this null pointer. */
1377                                 break;
1378                         }
1379                 }
1380         }
1381
1382         /* Just do everything at beat zero. The beat at which user specified
1383          * might not even exist in the current time signature, and basically
1384          * we just want to do everything ASAP anyway. We know there is never
1385          * a 'til' on midi, so no need to deal with that. We could end up
1386          * with a lot to do at count zero, potentially enough to overwhelm
1387          * the limited MIDI bandwidth and delay the first actual note.
1388          * But that potential is always there, and by discarding things
1389          * that were overwritten later, which we did above, unless there
1390          * are an awfully lot of parameters on every possible channel,
1391          * it's probably only going to take less than 0.1 second,
1392          * so it doesn't seem worth the bother to try to do something
1393          * fancy to calculate how much time we need for this stuff
1394          * and delay the actual music by that much. */
1395         stuff_p->start.count = 0.0;
1396         stuff_p->start.steps = 0.0;
1397
1398         /* link onto end of list */
1399         *stuff_p_p = stuff_p;
1400         stuff_p->next = 0;
1401 }
1402 \f
1403
1404 /* Given the argument to a MIDI "parameter" command, extract and return
1405  * (via pointers) the two numbers (the parameter number and its value).
1406  * If successful, returns YES, else prints a warning and returns NO.
1407  * If NO is returned, the pointed to return values are not fill in.
1408  */
1409
1410 int
1411 get_param(arg, inputfile, inputlineno, parmnum_p, parmval_p)
1412
1413 char *arg;              /* the argument part after "parameter=" */
1414 char *inputfile;        /* for error messages */
1415 int inputlineno;        /* for error messages */
1416 int *parmnum_p;         /* parameter number is returned here */
1417 int *parmval_p;         /* parameter value is returned here */
1418
1419 {
1420         int parmnum;    /* parameter number */
1421         int parmval;    /* parameter value */
1422         char *parm_p;   /* pointer to current place in string */
1423
1424
1425         /* extract first number */
1426         parmnum = strtol(arg, &parm_p, 10);
1427         if (parm_p == arg) {
1428                 l_warning(inputfile, inputlineno,
1429                         "parameter requires two values");
1430                 return(NO);
1431         }
1432         /* skip white space, if any */
1433         while (*parm_p == ' ' || *parm_p == '\t') {
1434                 parm_p++;
1435         }
1436         /* next we better have a comma */
1437         if (*parm_p != ','){
1438                 l_warning(inputfile, inputlineno,
1439                         "parameter is missing required comma");
1440                 return(NO);
1441         }
1442         /* extract the second number */
1443         arg = parm_p + 1;
1444         parmval = strtol(arg, &parm_p, 10);
1445         if (parm_p == arg) {
1446                 l_warning(inputfile, inputlineno,
1447                         "parameter is missing second value");
1448                 return(NO);
1449         }
1450         /* verify both numbers are within range */
1451         if ((l_rangecheck(parmnum, 0, 127, "parameter number",
1452                         inputfile, inputlineno) == YES) &&
1453                         (l_rangecheck(parmval, 0, 127, "parameter value",
1454                         inputfile, inputlineno) == YES)) {
1455                 *parmnum_p = parmnum;
1456                 *parmval_p = parmval;
1457                 return(YES);
1458         }
1459         return(NO);
1460 }