/* * Simple minded guitar chords * * 4 Mar 97 Erich Rickheit KSC Created * 18 Apr 97 Erich Rickheit KSC More chords; recursive descent parser */ #include #include #include #include char notenames[12][4] = { "c--", "c#-", "d--", "d#-", "e--", "f--", "f#-", "g--", "g#-", "a--", "a#-", "b--", }; /* A B C D E F G */ int notebyname[] = { 9, 11, 0, 2, 4, 5, 7, }; int intervals[][24] = { /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 */ /* major */ { -1, 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24 }, /* minor */ { -1, 0, 1, 3, 4, 6, 8, 10, 12, 13, 14, 16, 19, 20, 22, 24 }, /* augmented */ { -1, 1, 3, 5, 6, 8, 10, 12, 13, 15, 17, 18, 20, 22, 24, 25 }, /* diminished */ { -1, -1, 0, 2, 4, 6, 7, 9, 11, 12, 14, 16, 18, 19, 21, 23 }, }; struct note { int interval, type, val; }; #define TPERFECT 0 #define TMAJOR 0 #define TMINOR 1 #define TAUGMENTED 2 #define TDIMINISHED 3 /* attributes of the instrument - can be command line arguments */ #define MAXSTR 64 int fingerct = 4; int fretreach = 3; int ignorestring = 2; int stringct = 6; int stringbase[MAXSTR] = { 4, 11, 7, 2, 9, 4 }; char *stringname[MAXSTR] = {"E", "B", "G", "D", "A", "E" }; int rootonlow = 0; char chordname[64]; char *ords(i) { if (i > 3 && i < 21) return "th"; i%=10; if (i==1) return "st"; if (i==2) return "nd"; if (i==3) return "rd"; return "th"; } struct note notes[128]; int notect = 0; int basenote = -1; char basename[4]; int rootnote = -1; int rootchar = 'C'; int fretted[64]; void draw_chord(); int do_string(int); int printstyle = 1; char *nn(n) struct note *n; { int c, v; static char buf[10]; char *p = buf; /* first, the note */ *p = rootchar + n->interval -1; while (*p > 'G') *p-=7; /* base value for that note */ c = notebyname[*p - 'A']; /* how far is the value from the base value for that note? */ v = rootnote + intervals[n->type][n->interval]; while (v<0) v += 12; while (v>11) v -= 12; /* put in the appropriate flats, sharps */ switch (v -c) { case -2: case 10: p[1] = 'b'; p[2] = 'b'; p += 2; break; case -1: case 11: p[1] = 'b'; p++; break; case 0: break; case 1: case -11: p[1] = '#'; p++; break; case 2: case -10: p[1] = '#'; p[2] = '#'; p += 2; break; default: fprintf(stderr, "Shit, _I_ dunno\n"); return "?"; } p++; *p =0; return buf; } char err[1024]; char *inp; int main(int argc, char *argv[]) { char buf[1024]; int c; /* parse command line here */ while ((c = getopt(argc, argv, "bf:i:p:r:t:")) != EOF) switch (c) { case 'b': rootonlow = 1; break; case 'f': if (!(c = atoi(optarg))) usage(); fingerct = c; break; case 'i': if (!(c = atoi(optarg))) c = 10000; ignorestring = c; break; case 'p': if (!(c = atoi(optarg))) usage(); printstyle = c; break; case 'r': if (!(c = atoi(optarg))) usage(); fretreach = c; break; case 't': /* a list of strings, lowest to highest */ stringct = 0; while (stringct < MAXSTR) { inp = optarg; if ((c = Pnote()) < 0) usage(); /* push the others along... */ memmove(stringbase+1, stringbase, stringct*sizeof(int)); memmove(stringname+1, stringname, stringct*sizeof(char *)); /* so we can fit this at the beginning */ stringbase[0] = c; stringname[0] = optarg; stringct++; /* get the next one */ if (optind >= argc) break; optarg = argv[optind++]; if (!optarg || !isalpha(*optarg)) break; } break; case '?': default: usage(); } /* fix the sense of ignorestring; we label strings backwards, here */ ignorestring = stringct - ignorestring; if (optind < argc) { buf[0] = 0; while (optind "); prompt = 1; } while (fgets(buf, 1023, stdin)) { dochord(buf); if (prompt) printf("\n-> "); } } return 0; } dochord(char *buf) { char *s, *d, *p; int i; /* save the original */ p = chordname; /* trim out ws & normalize case */ for (s=d=buf; *s; s++) { if (isspace(*s)) continue; *p++ = *s; if (isupper(*s)) *d = tolower(*s); else *d = *s; d++; } *d = 0; *p = 0; if (!*buf) return; /* just ignore blank lines */ if (!Pchord(buf)) fprintf(stderr, "Error parsing chord %s: %s\n", buf, err); /* printf("root = %d base = %d\n", rootnote, basenote); for (i=0; i 11) basenote -= 12; /* now figure values for each of those notes */ for (i=0; i 11) notes[i].val -= 12; /* and set the notename for that */ d = nn(notes+i); strncpy(notenames[notes[i].val], d, strlen(d)); /* do we have the base note? */ if (notes[i].val == basenote) basename[0] = 0; } /* found the base note yet? */ if (basename[0]) { notes[notect].val = basenote; strncpy(notenames[basenote], basename, strlen(basename)); notect++; } /* go find that chord on the instrument */ for (i=0; i= 'A' && *inp <='G') *inp += 'a'-'A'; if (*inp < 'a' || *inp >'g') { sprintf(err, "%c is not a valid note name", *inp); return -1; } note = notebyname[*inp - 'a']; inp++; /* look for an optional group of sharps or flats */ if (*inp == 'b' || *inp == '!') { while (*inp == 'b' || *inp == '!') { inp++; note--; if (note<0) note += 12; } } else if (*inp == '#') { while (*inp == '#') { inp++; note++; if (note>11) note -= 12; } } return note; } /* look for a minor key for the chord */ int Pminor() { int i; if (*inp != 'm') return 0; /* don't be fooled by a 'maj' */ if (inp[1] == 'a' && inp[2] == 'j') return 0; /* but accept 'min' */ inp++; if (inp[0] == 'i' && inp[1] == 'n') inp += 2; /* and turn a major third into a minor third */ for (i=0; i 15) { sprintf(err, "interval out of range: %d", n); return -1; } /* see if that interval is already in the chord */ for (i=0; i 15) { sprintf(err, "interval out of range: %d", n); return -1; } /* see if that interval is already in the chord */ for (i=0; i 15) { sprintf(err, "interval out of range: %d", n); return -1; } /* see if that interval is already in the chord */ for (i=0; i=7) /* me must have a (minor) 7th */ { for (i=0; i= notect) { notes[notect].interval = 7; notes[notect].type = TMINOR; notect++; } } /* make sure that interval is in the chord */ for (i=0; i 15) { sprintf(err, "interval out of range: %d", n); return -1; } /* see if that interval is already in the chord */ for (i=0; i= 'a' && inp[1]<='g') return 0; inp++; return Pmod(); case '(': inp++; if (!Pmods()) return -1; if (*inp != ')') { sprintf(err, "Unbalanced paren"); return -1; } inp++; break; default: return 0; } return 1; } /* see if we've specified a root note */ int Pbase() { int i; if (*inp != '/') return 0; inp++; /* only one is allowed */ if (basenote>=0) { sprintf(err, "base note is already set: %d", basenote); return -1; } /* get the note in question */ if ((basenote = Pnote()) < 0) return -1; return 1; } int Pnum() { int val = 0; if (*inp < '1' || *inp > '9') return 0; while (isdigit(*inp)) { val = 10*val + (*inp -'0'); inp++; } return val; } int do_string(int string) { int note, pos, i; int success = 0; if (string < stringct) { for (note=0; note 11) pos -= 12; /* check through a couple octaves */ for (; pos < 11; pos+=12) { /* fail if more than three other strings are fretted */ if (pos) { int count; for (i=count=0; i fingerct-1) { /* fails for an open chord--how about a barre chord? */ int failure = 0, mn=pos; /* find the lowest non-open string; that's the barre */ for (i=0; i mn) count++; if (pos>mn) count++; /* fail if that's too many fingers */ if (count > fingerct-1) continue; } } /* fail if reach of frets is too great */ if (pos) { int mx, mn; mx = mn = pos; for (i=0; imx) mx=fretted[i]; if (fretted[i] && fretted[i]= fretreach) continue; } /* seems to be successful */ fretted[string] = pos; success = do_string(string+1); fretted[string] = -1; } } if (success) return 1; if (string >= ignorestring) { for (i=string; i0 && fretted[i]>max) max=fretted[i]; if (fretted[i]>0 && fretted[i]1) printf(" %d", min); putchar('\n'); for (i=0; i 75) { for (i=0; i=10) td++; if (td) len++; /* and add the frets to the end of the string */ for (i=0; i=0; i--) { if (fretted[i] < 0) printf(" X"); else printf("%3d", fretted[i]); } printf("\n"); } void draw_chp_chord() { int i, min, max; /* find the range of frets */ min = 1000; max = 0; for (i=0; i0 && fretted[i]>max) max=fretted[i]; if (fretted[i]>0 && fretted[i]=0; i--) { if (fretted[i] <0) fputs(" x", stdout); else if (fretted[i]) printf(" %d", fretted[i]-min+1); else fputs(" 0", stdout); } printf(" %d\n", min); } usage() { fputs( "usage: ch [-b] [-f #] [-i #] [-p #] [-r #] [-t str str str.] [chord]\n" , stderr); exit(1); } void draw_chord() { switch (printstyle) { case 1: draw_reg_chord(); break; case 2: draw_tab_chord(0); break; case 3: draw_shp_chord(); break; case 4: draw_chp_chord(); break; default: fprintf(stderr, "Unknown print style; using default...\n"); printstyle = 1; draw_chord(); } } flush_print() { switch (printstyle) { case 2: draw_tab_chord(1); break; default: break; } }