fcg@philabs.philips.com (Frank C. Guida) (01/10/90)
Fellow guitarists/bassists: I wrote a program that prints a guitar fretboard diagram, and labels notes or patterns according command-line args. I found it helpful in learning and scales and patterns. The user can specify scale notes directly, or supply a root and formula for a scale. Positions on the fretboard diagram may be labled with a user-defined character if desired (this is good for learning patterns as opposed to actual note names). Here's a sample output: synapse /betty/b3/fcg 17> fretbd -i G# 2 2 1 2 2 2 Scale = G# A# C C# D# F G + + + + + + + + + + -||-F-|---|-G-|-G#|---|-A#|---|-C-|-C#|---|-D#|---|-F-|---|-G-|-G#|---|-A#|---| -||-C-|-C#|---|-D#|---|-F-|---|-G-|-G#|---|-A#|---|-C-|-C#|---|-D#|---|-F-|---| G||-G#|---|-A#|---|-C-|-C#|---|-D#|---|-F-|---|-G-|-G#|---|-A#|---|-C-|-C#|---| -||-D#|---|-F-|---|-G-|-G#|---|-A#|---|-C-|-C#|---|-D#|---|-F-|---|-G-|-G#|---| -||-A#|---|-C-|-C#|---|-D#|---|-F-|---|-G-|-G#|---|-A#|---|-C-|-C#|---|-D#|---| -||-F-|---|-G-|-G#|---|-A#|---|-C-|-C#|---|-D#|---|-F-|---|-G-|-G#|---|-A#|---| This is my first posting of code to the net; any comments or suggestions are welcome. Please use e-mail replies where appropriate. - Yours truly /* ********************************************************************** */ /* * FILE: fretbd.2.c * AUTHOR: Frank C. Guida Philips Laboratories * (fcg@philabs.philips.com) Briarcliff Manor, NY 10510 * * DATE: Fri Dec 22 10:49:29 1989 * * Please acknowledge the author in any reproduction or modification. * * SYNOPSIS: fretbd [-h] [-s] [-m char] * [-i root interval_list] | [-n note_list] * * DESCRIPTION: fretbd prints a guitar fretboard (20 frets) * and labels notes according to the command-line * args. Notes may be entered in upper or lower * case; use b or # to indicate accidentals. * * OPTION SUMMARY: (no args): print blank fretboard. * -h: print this message. * -s: use sharps instead of flats as the * dflt_note_type (use with -i option). * -m: use char to mark note positions as * opposed to letter names. * -i: construct scale using specified root * and intervals (in half-steps). * -n: construct scale using specified notes. * * EXAMPLE: to print the F#maj scale - * host> fretbd -i F# 2 2 1 2 2 2 * */ /* ********************************************************************** */ #include <stdio.h> #include <ctype.h> #include <string.h> #define HELP_MESSAGE "\ Use: fretbd [-h] [-s] [-m char] [-i root interval_list] | [-n note_list]\n\n\ Option Summary: (no args): print blank fretboard.\n\ -h: print this message.\n\ -s: use sharps instead of flats as the\n\ dflt_note_type (use with -i option).\n\ -m: use char to mark note positions as\n\ opposed to letter names.\n\ -i: construct scale using specified root\n\ and intervals (in half-steps).\n\ -n: construct scale using specified notes.\n" #define OP_HELP "-h" #define OP_SHARPS "-s" #define OP_POS_MARK "-m" #define OP_INTERVALS "-i" #define OP_NOTES "-n" #define NUM_FRETS 20 /* numbered 0-19 */ #define NUM_STRINGS 6 /* numbered 1-6 */ #define MAX_NOTES 12 #define FLATS 0 #define SHARPS 1 #define NECK_INDEX "\ + + + + + + +\ + + +\n" /* Basic macros */ #define streq(s1, s2) (strcmp(s1, s2) == 0) #define not_option(arg) !(streq(arg, OP_HELP) ||\ streq(arg, OP_SHARPS) ||\ streq(arg, OP_POS_MARK) ||\ streq(arg, OP_INTERVALS) ||\ streq(arg, OP_NOTES)) /* function declarations */ char *get_note(); short note_index(); void error(); void print_fretbd(); void print_note(); /* global variables */ static char *chrom[2][12] = { /* 1 2 3 4 5 6 7 */ {"C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B"}, /* 0 1 2 3 4 5 6 7 8 9 10 11 */ {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"} }; char pos_mark_char = '\0'; unsigned short note_type; unsigned short num_notes = 0; /* ********************************************************************** */ main( argc, argv ) int argc; char *argv[]; { char *scale[MAX_NOTES]; short note_idx; unsigned short a, i, interval; unsigned short dflt_note_type = FLATS; for( i=0; i < MAX_NOTES; i++ ) /* initialize scale array */ scale[i] = "-"; /* Parse and verify command-line args. Args are checked for * consistency with any associated option flag (i.e. if the -i option * is given, the following arg should be a valid note name, and * subsequent args should be valid intervals or a new option flag). */ for( a=1; a < argc; a++ ) { if( streq(argv[a], OP_HELP) ) error( "", "" ); /* not really an error */ else if( streq(argv[a], OP_SHARPS) ) dflt_note_type = SHARPS; else if( streq(argv[a], OP_POS_MARK) ) { if( (a+1 < argc) && not_option(argv[a+1]) ) sscanf( argv[++a], "%c", &pos_mark_char ); else error( "", "Specify one character for position mark." ); } else if( streq(argv[a], OP_INTERVALS) ) { i = 0; while( (a+1 < argc) && not_option(argv[a+1]) ) { if( i == 0 ) /* first arg to -i option should be name of root */ { /* convert case if needed */ if( islower(*(argv[++a])) ) *(argv[a]) = toupper( *(argv[a]) ); if( (note_idx = note_index(argv[a])) >= 0 ) /* valid note? */ { scale[i++] = chrom[note_type][note_idx]; /* if root is an accidental, the scale should be */ /* formed from the same note type, i.e. bs or #s */ switch( note_idx ) { case 1: case 3: case 6: case 8: case 10: /* accidentals */ dflt_note_type = note_type; } } else error( argv[a], "is not a valid note!" ); } else /* subsequent args to -i option should be intervals */ { if( sscanf( argv[++a], "%hu", &interval ) == 1 ) { /* determine new note_idx from last note_idx */ note_idx = note_index(scale[i-1]) + (short)interval; while( note_idx > 11 ) /* correct any overflow */ note_idx =- 12; scale[i++] = chrom[dflt_note_type][note_idx]; } else error( argv[a], "is not a valid interval!" ); } } num_notes = i; } else if( streq(argv[a], OP_NOTES) ) { i = 0; while( (a+1 < argc) && not_option(argv[a+1]) ) { /* convert case if needed */ if( islower(*(argv[++a])) ) *(argv[a]) = toupper( *(argv[a]) ); if( note_index(argv[a]) >= 0 ) /* valid note? */ scale[i++] = argv[a]; else error( argv[a], "is not a valid note!" ); } num_notes = i; } } /* end of argc loop */ /* print scale */ printf( "Scale =" ); for( i=0; i < num_notes; i++ ) printf( " %s", scale[i] ); printf( " \n" ); print_fretbd( scale ); exit(0); } /* ********************************************************************** */ void error( item, message ) char *item, *message; /* Print error message, help message, and exit. */ { fprintf( stderr," %s %s\n", item, message ); fprintf( stderr, HELP_MESSAGE ); exit(-1); } /* ********************************************************************** */ short note_index( note ) char *note; /* Returns the chromatic scale index of note and sets note_type * if note is an accidental; returns -1 if note is not a member * of the chromatic scale. */ { short i; for( i=0; i < 12; i++ ) { note_type = FLATS; if( streq(note, chrom[note_type][i]) ) return(i); note_type = SHARPS; if( streq(note, chrom[note_type][i]) ) return(i); } return(-1); } /* ********************************************************************** */ void print_fretbd( scale ) char *scale[]; /* Print fretboard, labeling notes in scale[]. */ { char *note; register unsigned short string, fret_idx, fret; printf( NECK_INDEX ); for( string=1; string <= NUM_STRINGS; string++ ) { switch( string ) { case 2: fret_idx = 11; break; /* B */ case 3: fret_idx = 7; break; /* G */ case 4: fret_idx = 2; break; /* D */ case 5: fret_idx = 9; break; /* A */ default: fret_idx = 4; break; /* E */ } for( fret=0; fret < NUM_FRETS; fret++ ) { note = get_note( fret_idx, scale ); print_note( note, fret ); fret_idx++; while( fret_idx > 11 ) fret_idx -= 12; } printf( "\n" ); } } /* ********************************************************************** */ char *get_note( fret_idx, scale ) unsigned short fret_idx; char *scale[]; /* Returns note if chrom[][fret_idx] is a member of scale[]; * returns "-" if chrom[][fret_idx] is not a member of scale[]. */ { register unsigned short i; for( i=0; i < num_notes; i++ ) { if( streq( scale[i], chrom[FLATS][fret_idx] ) || streq( scale[i], chrom[SHARPS][fret_idx] ) ) return( scale[i] ); } return( "-" ); } /* ********************************************************************** */ void print_note( note, fret ) char *note; unsigned short fret; /* Prints note or specified pos_mark_char, then proper fill chars. */ { if( streq( note, "-" ) || (pos_mark_char == '\0') ) { printf( "%s", note ); /* if note is a single-char string, print "-" */ if( (fret > 0) && (*(note + 1) == '\0') ) printf( "-" ); } else { printf( "%c", pos_mark_char ); if( fret > 0 ) printf( "-" ); } switch( fret ) /* print fret indicators */ { case 0: printf( "||-" ); return; case 19: printf( "|" ); return; default: printf( "|-" ); return; } } /* ******************************* END ********************************** */ ______________________________________________________________________ Frank C. Guida Philips Laboratories fcg@philabs.philips.com Autonomous Systems Dept. (914)-945-6246 Briarcliff Manor, NY 10510
fcg@synapse (Frank C. Guida) (01/13/90)
Sorry folks, I discovered a typo shortly after posting my fretbd program to the net. I'd also like to thank the other readers who pointed this bug out. The problem is the loop near line 170. Its purpose being to bring the note index into the correct range (0-11) when building a scale from an interval pattern. while( note_idx > 11 ) /* correct any overflow */ note_idx =- 12; The "=-" should be changed to "-=". Newer compilers will expand this to "note_idx = - 12", resulting in a negative array subscript which will cause various errors. Stephen R. Tate <cs.duke.edu!srt@uunet> suggested a more elegant solution to the problem: replace the entire loop with the single line note_idx %= 12; /* correct any overflow */ A similar situation can be found in the function print_fretbd(), at around line 176 (except that here I used the correct form of "-="). Thanks again for your input. - Yours truly ______________________________________________________________________ Frank C. Guida Philips Laboratories fcg@philabs.philips.com Autonomous Systems Dept. (914)-945-6246 Briarcliff Manor, NY 10510 ______________________________________________________________________ Frank C. Guida Philips Laboratories fcg@philabs.philips.com Autonomous Systems Dept. (914)-945-6246 Briarcliff Manor, NY 10510