#!/usr/bin/python """ Chord Calculator Version 0.3 Copyright (c) 28 Dec 2008, Gek S. Low Free for personal use. All other rights reserved. USE AT YOUR OWN RISK! This software is provided AS IS and does not make any claim that it actually works, or that it will not cause your computer to self-destruct or eat up your homework. Note that some calculated chords may be playable only by aliens with 10 tentacles. Please use your common sense. The author will not be responsible for any injuries from attempts at impossible fingerings. The author reserves the right to change the behavior of this software without prior notice. """ import getopt import sys import re # Constants APP_NAME = "Chord Calculator" APP_VER = "0.3" APP_DESC = "A command-line python script to calculate possible chord fingerings for various fretted instruments (e.g. guitar, ukulele, mandolin). Various tunings are supported.\n\nWARNING: Some calculated chords may be playable only by aliens with 10 tentacles. Please use your common sense. The author will not be responsible for any injuries from attempts at impossible fingerings." APP_COPYRIGHT = "Copyright (c) 28 Dec 2008, Gek S. Low.\nFree for personal use. All other rights reserved." APP_AUTHOR = "Gek S. Low" APP_USAGE = """ Usage: chordcalc [options] Chord1 [Chord2] ... where [options] can be: -t, --tuning= Instrument tunings (default: GUITAR) -p, --position= Position (default: 0) -s, --span= Number of frets to span (default depends on tuning) -f, --filter= Optional filters, useful for large chords on limited number of strings (default: FULL_CHORD). Multiple filters can be entered, separated by commas. -h, --help This help text --help-tunings List of valid tunings --help-filters List of valid filters --help-chords List of valid chord types Chords are case-sensitive (e.g. Dbm7, not DBM7 or dbm7) """ # Note numbers - add multiples of 12 for higher octaves NOTE_C = 0 NOTE_Cs = NOTE_Db = 1 NOTE_D = 2 NOTE_Ds = NOTE_Eb = 3 NOTE_E = 4 NOTE_F = 5 NOTE_Fs = NOTE_Gb = 6 NOTE_G = 7 NOTE_Gs = NOTE_Ab = 8 NOTE_A = 9 NOTE_As = NOTE_Bb = 10 NOTE_B = 11 NOTE_NAMES = ['C','C#/Db','D','D#/Eb','E','F','F#/Gb','G','G#/Ab','A','A#/Bb','B'] NOTES = { 'C': NOTE_C, 'C#': NOTE_Cs, 'Db': NOTE_Db, 'D': NOTE_D, 'D#': NOTE_Ds, 'Eb': NOTE_Eb, 'E': NOTE_E, 'F': NOTE_F, 'F#': NOTE_Fs, 'Gb': NOTE_Gb, 'G': NOTE_G, 'G#': NOTE_Gs, 'Ab': NOTE_Ab, 'A': NOTE_A, 'A#': NOTE_As, 'Bb': NOTE_Bb, 'B': NOTE_B, } SCALENOTES = ['R','b2','2','b3','3','4','b5','5','#5','6','b7','7','R','b9','9','#9','3','11','#11','5','b13','13'] # Chordtype definitions # These are guitar-centric CHORDTYPE = { '': [0,4,7], # default is major 'maj': [0,4,7], 'min': [0,3,7], '#5': [0,4,8], '(b5)': [0,4,6], '+': [0,4,8], '+7': [0,4,8,10], '+7b9#11': [0,4,8,10,13,18], '+9': [0,4,8,10,14], '+9M7': [0,4,8,11,14], '+M7': [0,4,8,11], '11': [0,7,10,14,17], # no 3rd '11b9': [0,4,7,10,13,17], '13': [0,4,7,10,21], # no 9th and 11th '13#11': [0,4,7,10,18,21], # no 9th '13#9': [0,4,7,10,15,21], # no 11th '13b5': [0,4,6,10,21], # no 9th and 11th '13b9': [0,4,7,10,13,21], # no 11th '13sus': [0,5,7,10,14,21], '13susb9': [0,5,7,10,13,21], '5': [0,7], '6': [0,4,7,9], '6(add9)': [0,4,7,9,14], '69': [0,4,7,9,14], '6/9': [0,4,7,9,14], '7': [0,4,7,10], '7#11': [0,4,7,10,18], # no 9th '7#5': [0,4,8,10], '7#5#9': [0,4,8,10,15], '7#5b9': [0,4,8,10,13], '7#9': [0,4,7,10,15], '7#9#11': [0,4,7,10,15,18], '7#9b13': [0,4,7,10,15,20], '7(omit3)': [0,7,10], '7+': [0,4,8,10], '7+5': [0,4,8,10], '7+9': [0,4,7,10,15], '7-5': [0,4,6,10], '7-9': [0,4,7,10,13], '7alt': [0,4,6,10,13], '7b5': [0,4,6,10], '7b5#9': [0,4,6,10,15], '7b5b9': [0,4,6,10,13], '7b9': [0,4,7,10,13], '7b9#11': [0,4,7,10,13,18], '7sus': [0,5,7,10], '7sus2': [0,2,7,10], '7sus4': [0,5,7,10], '7susb9': [0,5,7,10,13], '9': [0,4,7,10,14], '9#11': [0,4,7,10,14,18], '9#5': [0,4,8,10,14], '9+5': [0,4,8,10,14], '9-5': [0,4,6,10,14], '9b5': [0,4,6,10,14], '9sus': [0,5,7,10,14], '9sus4': [0,5,7,10,14], 'M13': [0,4,7,11,21], # no 9th and 11th 'M13#11': [0,4,7,11,18,21], # no 9th 'M6': [0,4,7,9], 'M7': [0,4,7,11], 'M7#11': [0,4,7,11,18], # no 9th 'M7#5': [0,4,8,11], 'M7(add13)': [0,4,7,10,13,21], # no 11th 'M7+5': [0,4,8,11], 'M7-5': [0,4,6,11], 'M7b5': [0,4,6,11], 'M9': [0,4,7,11,14], 'M9#11': [0,4,7,11,14,18], 'add9': [0,4,7,14], 'aug': [0,4,8], 'aug7': [0,4,8,10], 'aug7#9': [0,4,8,10,15], 'aug7b9': [0,4,8,10,13], 'aug9': [0,4,8,10,14], 'aug9M7': [0,4,8,11,14], 'dim': [0,3,6], # note: not dim7 'dim7': [0,3,6,9], 'dim7(addM7)': [0,3,6,11], 'm': [0,3,7], 'm#5': [0,3,8], 'm#7': [0,3,7,11], 'm(add9)': [0,3,7,14], 'm(b5)': [0,3,6], 'm(maj7)': [0,3,7,11], 'm(sus9)': [0,3,7,14], 'm+5': [0,3,8], 'm+7': [0,3,7,11], 'm+7#9': [0,3,7,10,15], 'm+7b9': [0,3,7,10,13], 'm+7b9#11': [0,3,7,10,13,18], 'm11': [0,3,7,10,14,17], 'm11b5': [0,3,6,10,17], 'm13': [0,3,7,10,21], # no 9th and 11th 'm6': [0,3,7,9], 'm6(add9)': [0,3,7,9,14], 'm69': [0,3,7,9,14], 'm6/9': [0,3,7,9,14], 'm7': [0,3,7,10], 'm7#9': [0,3,7,10,15], 'm7(#9)': [0,3,7,10,15], 'm7(add11)': [0,3,7,10,17], 'm7(add13)': [0,3,7,10,21], 'm7(b9)': [0,3,7,10,13], 'm7(omit5)': [0,3,10], 'm7-5': [0,3,6,10], # half dim 'm7b5': [0,3,6,10], # half dim 'm7b5b9': [0,3,6,10,13], 'm7b9': [0,3,7,10,13], 'm7b9#11': [0,3,7,10,13,18], 'm7omit5': [0,3,10], 'm9': [0,3,7,10,14], 'm9#11': [0,3,7,10,14,18], 'mM7': [0,3,7,11], 'mM7(add9)': [0,3,7,11,14], 'maj13': [0,4,7,11,21], # no 9th and 11th 'maj7': [0,4,7,11], 'maj9': [0,4,7,11,14], 'mb5': [0,3,6], 'min#7': [0,3,7,11], 'min(maj7)': [0,3,7,11], 'omit3(add9)': [0,7,14], 'omit3add9': [0,7,14], 'sus': [0,5,7], 'sus2': [0,2,7], 'sus4': [0,5,7], 'sus9': [0,5,7,10,14], } # Tunings and their corresponding default spans # Instruments I don't play has default span = 3 # I only care about the relative pitches of the open strings against the lowest bass note # But actually the pitches are currently not used in any way SPAN_DEFAULT_UNKNOWN = 3 SPAN_DEFAULT_GUITAR = 3 SPAN_DEFAULT_BASS = 3 SPAN_DEFAULT_UKULELE = 5 SPAN_DEFAULT_MANDOLIN = 7 TUNINGS = { # Guitar 'GUITAR': [[NOTE_E, NOTE_A, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_DROPD': [[NOTE_D, NOTE_A, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_DROPDG': [[NOTE_D, NOTE_G, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_G': [[NOTE_D, NOTE_G, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_DADGAD': [[NOTE_D, NOTE_A, NOTE_D+12, NOTE_G+12, NOTE_A+12, NOTE_D+24], SPAN_DEFAULT_GUITAR], 'GUITAR_LUTE': [[NOTE_E, NOTE_A, NOTE_D+12, NOTE_Fs+12, NOTE_B+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_OPEN_A': [[NOTE_E, NOTE_A, NOTE_E+12, NOTE_A+12, NOTE_Cs+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_OPEN_C': [[NOTE_C, NOTE_G, NOTE_C+12, NOTE_E+12, NOTE_G+12, NOTE_C+24], SPAN_DEFAULT_GUITAR], 'GUITAR_OPEN_D': [[NOTE_D, NOTE_A, NOTE_D+12, NOTE_Fs+12, NOTE_A+12, NOTE_D+24], SPAN_DEFAULT_GUITAR], 'GUITAR_OPEN_Db': [[NOTE_Db, NOTE_Ab, NOTE_Db+12, NOTE_F+12, NOTE_Ab+12, NOTE_Db+24], SPAN_DEFAULT_GUITAR], 'GUITAR_OPEN_E': [[NOTE_E, NOTE_B, NOTE_E+12, NOTE_Gs+12, NOTE_A+12, NOTE_C+24], SPAN_DEFAULT_GUITAR], 'GUITAR_OPEN_F': [[NOTE_C, NOTE_F, NOTE_C+12, NOTE_F+12, NOTE_A+12, NOTE_C+24], SPAN_DEFAULT_GUITAR], 'GUITAR_OPEN_G': [[NOTE_D, NOTE_G, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_D+24], SPAN_DEFAULT_GUITAR], 'GUITAR_BIG_D': [[NOTE_D, NOTE_A, NOTE_D+12, NOTE_Fs+12, NOTE_A+12, NOTE_A+24], SPAN_DEFAULT_GUITAR], 'GUITAR_Bm7': [[NOTE_D, NOTE_A, NOTE_D+12, NOTE_Fs+12, NOTE_B+12, NOTE_D+24], SPAN_DEFAULT_GUITAR], 'GUITAR_Am': [[NOTE_C, NOTE_G, NOTE_C+12, NOTE_G+12, NOTE_A+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_DOBRO': [[NOTE_G, NOTE_B, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_D+24], SPAN_DEFAULT_GUITAR], 'GUITAR_EM7': [[NOTE_E, NOTE_B, NOTE_E+12, NOTE_Gs+12, NOTE_B+12, NOTE_Ds+24], SPAN_DEFAULT_GUITAR], 'GUITAR_HAWAIIAN1': [[NOTE_C, NOTE_G, NOTE_E+12, NOTE_G+12, NOTE_A+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'GUITAR_HAWAIIAN2': [[NOTE_C, NOTE_G, NOTE_C+12, NOTE_G+12, NOTE_A+12, NOTE_D+24], SPAN_DEFAULT_GUITAR], 'GUITAR_SLACK_C': [[NOTE_C, NOTE_G, NOTE_C+12, NOTE_E+12, NOTE_A+12, NOTE_C+24], SPAN_DEFAULT_GUITAR], 'GUITAR_WAHINE1': [[NOTE_C, NOTE_G, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_D+24], SPAN_DEFAULT_GUITAR], 'GUITAR_WAHINE2': [[NOTE_D, NOTE_A, NOTE_D+12, NOTE_Fs+12, NOTE_A+12, NOTE_Cs+24], SPAN_DEFAULT_GUITAR], # Bass 'BASS': [[NOTE_E, NOTE_A, NOTE_D+12, NOTE_G+12], SPAN_DEFAULT_BASS], # Ukulele 'UKULELE': [[NOTE_G, NOTE_C, NOTE_E, NOTE_A], SPAN_DEFAULT_UKULELE], 'UKULELE_C': [[NOTE_G, NOTE_C, NOTE_E, NOTE_A], SPAN_DEFAULT_UKULELE], 'UKULELE_D': [[NOTE_A, NOTE_D, NOTE_Fs, NOTE_B], SPAN_DEFAULT_UKULELE], 'UKULELE_BARITONE': [[NOTE_D, NOTE_G, NOTE_B, NOTE_E+12], SPAN_DEFAULT_GUITAR], # Mandolin 'MANDOLIN': [[NOTE_G, NOTE_D+12, NOTE_A+12, NOTE_E+24], SPAN_DEFAULT_MANDOLIN], 'MANDOCELLO': [[NOTE_C, NOTE_G, NOTE_D+12, NOTE_A+12], SPAN_DEFAULT_UNKNOWN], 'MANDOLA': [[NOTE_C, NOTE_G, NOTE_D+12, NOTE_A+12], SPAN_DEFAULT_UNKNOWN], # Bouzouki 'BOUZOUKI': [[NOTE_C, NOTE_F, NOTE_A, NOTE_D+12], SPAN_DEFAULT_UNKNOWN], 'BOUZOUKI_GDAD': [[NOTE_G, NOTE_D+12, NOTE_A+12, NOTE_D+24], SPAN_DEFAULT_UNKNOWN], # Banjo 'BANJO': [[NOTE_C, NOTE_G, NOTE_B, NOTE_D+12], SPAN_DEFAULT_UNKNOWN], 'BANJO_CGBD': [[NOTE_C, NOTE_G, NOTE_B, NOTE_D+12], SPAN_DEFAULT_UNKNOWN], 'BANJO_DGBD': [[NOTE_D, NOTE_G, NOTE_B, NOTE_D+12], SPAN_DEFAULT_UNKNOWN], 'BANJO_CGDA': [[NOTE_C, NOTE_G, NOTE_D+12, NOTE_A+12], SPAN_DEFAULT_UNKNOWN], 'BANJO_CGCD': [[NOTE_C, NOTE_G, NOTE_C+12, NOTE_D+12], SPAN_DEFAULT_UNKNOWN], # Balalaika 'BALALAIKA': [[NOTE_E, NOTE_E, NOTE_A], SPAN_DEFAULT_UNKNOWN], # Charango 'CHARANGO': [[NOTE_G, NOTE_C+12, NOTE_E+12, NOTE_A, NOTE_E+12], SPAN_DEFAULT_UNKNOWN], # Vihuela (da mano) 'VIHUELA': [[NOTE_G, NOTE_C+12, NOTE_F+12, NOTE_A+12, NOTE_D+24, NOTE_G+24], SPAN_DEFAULT_GUITAR], 'VIHUELA_C': [[NOTE_C, NOTE_F, NOTE_Bb+12, NOTE_D+12, NOTE_G+12, NOTE_C+24], SPAN_DEFAULT_GUITAR], # Others 'BAROQUE_GUITAR': [[NOTE_A, NOTE_D+12, NOTE_G+12, NOTE_B+12, NOTE_E+24], SPAN_DEFAULT_GUITAR], 'VIHUELA_MEXICANA': [[NOTE_A+12, NOTE_D+24, NOTE_G+24, NOTE_B+12, NOTE_E+24], SPAN_DEFAULT_UNKNOWN], } # OPTIONS OPT_FULL_CHORD = 0 OPT_NOROOT_OK = 1 OPT_NO3RD_OK = 2 OPT_NO5TH_OK = 4 OPT_AUTO_REDUCE = 8 OPT_NO_OPEN = 16 FILTERS = { 'FULL_CHORD': [OPT_FULL_CHORD, "All notes must be present (default)"], 'NOROOT_OK': [OPT_NOROOT_OK, "Can omit root"], 'NO3RD_OK': [OPT_NO3RD_OK, "Can omit 3rd"], 'NO5TH_OK': [OPT_NO5TH_OK, "Can omit 5th"], 'AUTO_REDUCE': [OPT_AUTO_REDUCE, "If no complete chord fingerings can be found, progressively omit 5th, root, and 3rd (that that order), until fingerings are found"], 'NO_OPEN': [OPT_NO_OPEN, "No open string fingerings"], } # Finds the chord fingerings for a given tuning (number of strings implied) # Pos is the "barre" position, span is how many frets to cover # Returns a list of fingerings def findFingerings(key, chordtype, tuning, pos, span, option=OPT_FULL_CHORD): # Get valid frets on the strings validfrets = findValidFrets(key, chordtype, tuning, pos, span) # Find all candidates candidates = findCandidates(validfrets) #print candidates # Filter out the invalid candidates candidates = filterCandidates(key, chordtype, tuning, candidates, option) # Filter out "impossible" fingerings? # To be implemented... # Perhaps also some sorting options? return candidates # For a given list of starting frets and span, find the ones that are in the chord for that tuning # Returns a list of valid frets for each string # Open strings are included if valid def findValidFrets(key, chordtype, tuning, pos, span): strings = [] for string in tuning: frets = [] searchrange = range(pos, pos+span+1) if pos != 0: # include open strings is not at pos 0 searchrange = [0] + searchrange for fret in searchrange: for chordrelnote in chordtype: note = (string + fret) % 12 chordnote = (key + chordrelnote) % 12 if note == chordnote: frets.append(fret) strings.append(frets) return strings # Finds all candidate fingerings, given all valid frets # Includes strings that should not be played # Note that this is just a permutation function and is independent of keys, tunings or chords def findCandidates(validfrets): # Set up the counter which will track the permutations max_counter = [] counter = [] candidatefrets = [] for string in validfrets: # Include the possibility of not playing the string # Current approach prioritises open and fretted strings over unplayed strings candidatefrets.append(string + [-1]) max_counter.append(len(string)) counter.append(0) l = len(counter)-1 #print candidatefrets #print max_counter # Number of possible permutations numperm = 1 for c in max_counter: numperm *= c+1 #print numperm candidates = [] # Permute for perm in range(numperm): # get the candidate candidate = [] for string, fret in enumerate(counter): #print string, fret candidate.append(candidatefrets[string][fret]) #print perm, counter, candidate # increment counter, starting from highest index string for i, v in enumerate(counter): if counter[l-i] < max_counter[l-i]: counter[l-i] += 1 break else: counter[l-i] = 0 candidates += [candidate] return candidates # Tests whether a fingering is valid # Should allow various possibilities - full chord, no 5th, no 3rd, no root, etc def isValidChord(key, chordtype, tuning, candidate, option=OPT_FULL_CHORD): result = True # If option is no open strings if option & OPT_NO_OPEN == OPT_NO_OPEN: for note in candidate: if note == 0: return False # which chord notes are present? present = {} for chordrelnote in chordtype: # assume chord notes are not present present[chordrelnote] = False chordnote = (key + chordrelnote) %12 for i, v in enumerate(candidate): # ignore unplayed strings if candidate[i] != -1: note = (tuning[i] + candidate[i]) % 12 if chordnote == note: present[chordrelnote] = True break #print present, candidate # do we accept this fingering? depends on the option for note in present.keys(): if present[note] == False: if option == OPT_FULL_CHORD: result = False break if option & OPT_NO3RD_OK == OPT_NO3RD_OK: if note == 4 or note == 3: continue if option & OPT_NO5TH_OK == OPT_NO5TH_OK: if note == 7: continue if option & OPT_NOROOT_OK == OPT_NOROOT_OK: if note == 0: continue result = result & present[note] return result # Tests if a given note is in the chord # Not used here def isInChord(key, chordtype, note): for chordrelnote in chordtype: chordnote = (key + chordrelnote) % 12 if note == chordnote: #print note, chordnote return True return False # Filter out the invalid chords from the list of candidates # Criteria for invalid chords may vary # Returns the list of valid chords def filterCandidates(key, chordtype, tuning, candidates, option=OPT_FULL_CHORD): newlist = [] for candidate in candidates: if isValidChord(key, chordtype, tuning, candidate, option): newlist += [candidate] # If using auto reduce option if option & OPT_AUTO_REDUCE == OPT_AUTO_REDUCE: # Try omitting 5th if newlist == []: newlist = filterCandidates(key, chordtype, tuning, candidates, OPT_NO5TH_OK) # Try omitting root if newlist == []: newlist = filterCandidates(key, chordtype, tuning, candidates, OPT_NO5TH_OK|OPT_NOROOT_OK) # Try omitting 3rd if newlist == []: newlist = filterCandidates(key, chordtype, tuning, candidates, OPT_NO5TH_OK|OPT_NOROOT_OK|OPT_NO3RD_OK) return newlist # Given a fingering, gets the scale note relative to the key def getScaleNotes(key, chordtype, tuning, fingering): scalenotes = [] for i, v in enumerate(fingering): if v == -1: scalenotes.append('x') else: fingerednote = (tuning[i] + fingering[i]) % 12 for chordrelnote in chordtype: chordnote = (key + chordrelnote) % 12 if fingerednote == chordnote: scalenotes.append(SCALENOTES[chordrelnote]) return scalenotes # Display routines def printChords(key, chordtype, tuning, chords): for fingering in chords: printChord(key, chordtype, tuning, fingering) #printChordGrid(fingering) #print isValidChord(key, chordtype, tuning, fingering) #print print "Done:",len(chords), "fingerings found!" def printChord(key, chordtype, tuning, chord): #print chord outstr = "" for fret in chord: if fret == -1: outstr += 'x ' else: outstr += str(fret)+' ' print outstr+"[", for scalenote in getScaleNotes(key, chordtype, tuning, chord): print scalenote, print "]" return def printChordGrid(chord): # Find the maximum fret number, this is when to stop printing maxfret = max(chord) # Find the minimum fret number that's not open minfret = maxfret for i in chord: if minfret > i and i != 0 and i != -1: minfret = i if minfret < 3: pos = 1 else: pos = minfret # open and unplayed strings outstr = "" for i in chord: if i == 0: outstr += 'o' elif i == -1: outstr += 'x' else: outstr += ' ' print outstr # print the nut outstr = "" for i in chord: outstr += '-' print outstr printrange = maxfret-pos+1 if printrange < 3: printrange = 3 for p in range(printrange): outstr = "" for i in chord: if p == i-pos: outstr += str(i) else: outstr += '|' if p == 0 and pos != 1: outstr += ' ' + str(pos) print outstr return # Application functions def printHelp(): print "Description:", APP_DESC print APP_USAGE def printVersion(): print APP_VER def printTuning(tuning_name, tuning): print tuning_name,"[", for note in tuning: print NOTE_NAMES[note % 12], print "]" def printTunings(): print "Valid tunings are:" for tuning_name in sorted(TUNINGS.keys()): printTuning(tuning_name, TUNINGS[tuning_name][0]) def printChordType(chordtype_name): print "[", for note in CHORDTYPE[chordtype_name]: print SCALENOTES[note], print "]" def printChordTypes(): print "Valid chord types are:" for type in sorted(CHORDTYPE.keys()): print type, printChordType(type) print def printFilter(filter): if filter == 0: print 'FULL_CHORD' return for x in FILTERS.keys(): if filter & FILTERS[x][0] == FILTERS[x][0] and FILTERS[x][0] != 0: print x, print def printFilters(): print "Valid filters are:" for x in sorted(FILTERS.keys()): print x, ":", FILTERS[x][1] print def parseChordName(chordstr): p = re.compile('([A-G][#b]{0,1})(.*)', re.IGNORECASE) m = p.match(chordstr) if m != None: return m.group(1,2) # key and chordtype else: return ['',''] def printHeader(quiet): if not quiet: print APP_NAME, APP_VER print APP_COPYRIGHT print # Start of main program def main(): try: opts, args = getopt.getopt(sys.argv[1:], "ht:p:f:s:vq", ["help", "tuning=", "position=","span=","filter=","help-tunings","help-filters","help-chords","version","quiet"]) except getopt.GetoptError, err: # print help information and exit: print str(err) print printHeader(False) printHelp() sys.exit(2) tuning_name = "GUITAR" position = 0 span = 0 option = OPT_FULL_CHORD quiet = False #print opts # Look for quiet flag first for o, a in opts: if o in ("-q", "--quiet"): quiet = True if o in ("-v", "--version"): printVersion() sys.exit() printHeader(quiet) # Gather the parameters for o, a in opts: if o in ("-q", "--quiet"): continue elif o in ("-v", "--version"): printVersion() sys.exit() elif o in ("-h", "--help"): printHelp() sys.exit() elif o in ("-t", "--tuning"): if a.upper() in TUNINGS.keys(): tuning_name = a.upper() else: print "[error]: Unknown tuning",a.upper() print "Use --help-tunings for list of valid tunings" #printTunings() sys.exit(2) elif o in ("-p", "--position"): try: position = int(a) except: position = 0 if not quiet: print "[warning]: Invalid --position, using default position =", position elif o in ("-s", "--span"): try: span = int(a) except: span = SPAN_DEFAULT_UNKNOWN if not quiet: print "[warning]: Invalid --span, using default span =", span elif o in ("-f", "--filter"): for x in a.split(','): if x.upper() in FILTERS.keys(): option = option | FILTERS[x.upper()][0] else: print "[error]: Invalid --filter",x print "Use --help-filters for list of valid filters" sys.exit(2) elif o == "--help-tunings": printTunings() sys.exit() elif o == "--help-filters": printFilters() sys.exit() elif o == "--help-chords": printChordTypes() sys.exit() else: print "Option",o,"not implemented yet" sys.exit(2) # Check the span if span == 0: try: span = int(TUNINGS[tuning_name][1]) except: span = SPAN_DEFAULT_UNKNOWN if not quiet: print "[warning]: Invalid span, using default span =", span # Set the tuning tuning = TUNINGS[tuning_name][0] if not quiet: print "Tuning:", printTuning(tuning_name, tuning) print "Position:", position print "Span:", span print "Filters:", printFilter(option) print if args == []: print "No chord supplied" if not quiet: printHelp() sys.exit() for arg in args: key_name, chordtype_name = parseChordName(arg) if key_name not in NOTES.keys(): if not quiet: print "[error]: Invalid chord:", arg continue if chordtype_name not in CHORDTYPE.keys(): if not quiet: print "[error]: Unrecognised chord type:",chordtype_name print "Use --help-chords for list of valid chord types" #printChordTypes() continue print "Chord:", key_name, chordtype_name, printChordType(chordtype_name) key = NOTES[key_name] chordtype = CHORDTYPE[chordtype_name] fingerings = findFingerings(key, chordtype, tuning, position, span, option) printChords(key, chordtype, tuning, fingerings) print if __name__ == "__main__": main()