#! /bin/bash
# Allen Barker
# Sept. 17, 2004
# Chord and Scale Pattern Generator, Version 0.1.
# This is "human rights ware," copyright (C) Allen Barker, 2004.
# It can be freely copied and modified as long as the human
# rights message remains. If you distribute modified copies,
# please document somewhere in the code, etc., that you have
# modified it. (Yes, the code is ugly, but at least it works!)
numColumns="77" # rounded down to the nearest factor of 4 plus 1
numRows="50" # rounded down to the nearest factor of 4 plus 1
nonMarkedChar=" "
function isMarked() {
# Usage: isMarked
# Outputs "true" if note is in markedNotes, "false" otherwise.
for i in $markedNotes
do
if [ "$1" == "$i" ] ; then
echo "true"
return
fi
done
echo "false"
return
}
function addNotesMod12() {
# Usage: addNotesMod12
note1=$1
note2=$2
if [ "$note1" == "a" ]; then (( note1=10 )); fi
if [ "$note1" == "b" ]; then (( note1=11 )); fi
if [ "$note2" == "a" ]; then (( note2=10 )); fi
if [ "$note2" == "b" ]; then (( note2=11 )); fi
(( sum = note1 + note2 ))
if (( sum > 11 )); then (( sum -= 12 )); fi
if (( sum == 10 )); then sum="a"; fi
if (( sum == 11 )); then sum="b"; fi
echo $sum
}
function transposeList() {
# Usage: transposeList
# Adds the delta factor to all the notes on the list and returns
# the transposed list via echo.
delta=$1
newList=""
shift
for i in $*
do
newList="$newList `addNotesMod12 $delta $i`"
done
echo $newList
}
function printRow() {
# Usage: printRow
markedCols=""
markedDiagCols1=""
markedDiagCols2=""
markedDiagCols3=""
(( colCount = 1 ))
while true
do
for note in $1 $2 $3
do
if [ "`isMarked $note`" == "true" ]; then
# see if the previous note was marked...
prevNote=`addNotesMod12 $note 8`
if [ "`isMarked $prevNote`" == "true" ]; then
if (( colCount > 1 )); then
echo -n "---"
fi
else
if (( colCount > 1 )); then
echo -n "$nonMarkedChar$nonMarkedChar$nonMarkedChar"
fi
fi
# mark the columns for later...
belowNote=`addNotesMod12 $note 9`
if [ "`isMarked $belowNote`" == "true" ]; then
markedCols="$markedCols $colCount"
fi
belowLeft=`addNotesMod12 $note 5`
if [ "`isMarked $belowLeft`" == "true" ]; then
(( tmpCol = colCount-1 ))
markedDiagCols1="$markedDiagCols1 $tmpCol"
(( tmpCol = colCount-2 ))
markedDiagCols2="$markedDiagCols2 $tmpCol"
(( tmpCol = colCount-3 ))
markedDiagCols3="$markedDiagCols3 $tmpCol"
fi
else # not a marked note
if (( colCount > 1 )); then
echo -n "$nonMarkedChar$nonMarkedChar$nonMarkedChar"
fi
fi
(( colCount=colCount+4 ))
echo -n $note
if (( colCount > numColumns )) ; then
echo -n " " # this space is needed to make the sub to standard notes work
echo
return
fi
done
done
}
function printBetweenRows() {
# Usage: printBetweenRows
# Uses the global column-marking lists.
for betweenRow in 1 2 3
do
(( colCount = 1 ))
(( mod4Count = 0 ))
while true
do
if (( colCount > numColumns )) ; then
echo
break
fi
printedChar="false"
# loop to test if current column is marked
for colTest in $markedCols
do
if (( colCount == colTest )) ; then
echo -n "|"
printedChar="true"
fi
done
if [ "$betweenRow" == "1" ]; then
markedDiagCols="$markedDiagCols1"
elif [ "$betweenRow" == "2" ]; then
markedDiagCols="$markedDiagCols2"
else # betweenRow ==3
markedDiagCols="$markedDiagCols3"
fi
for colTest in $markedDiagCols
do
if (( colCount == colTest )) ; then
echo -n "/"
printedChar="true"
fi
done
# print the usual symbol if no marked char printed
if [ "$printedChar" == "false" ]; then
if (( mod4Count == 0 )); then
echo -n "$nonMarkedChar"
elif (( mod4Count == 1 )); then
if [ "$betweenRow" == "3" ]; then
echo -n "$nonMarkedChar"
else
echo -n " "
fi
elif (( mod4Count == 2 )); then
if [ "$betweenRow" == "2" ]; then
echo -n "$nonMarkedChar"
else
echo -n " "
fi
elif (( mod4Count == 3 )); then
if [ "$betweenRow" == "1" ]; then
echo -n "$nonMarkedChar"
else
echo -n " "
fi
fi
fi
(( colCount=colCount+1 ))
(( mod4Count=mod4Count+1 ))
if (( mod4Count == 4 )); then (( mod4Count = 0 )); fi
done
done
}
#============================= Start Main Menu ============================
echo
echo
echo "-----------------------------------------------------------------------"
echo "Welcome to the chord and scale pattern generator, by Allen Barker, 2004."
echo "All notes are represented in base 12 for entry and default display,"
echo "but can be converted to standard note representations. The (arbitrary)"
echo "convention is: 0=B 1=C 2=C# 3=D 4=D# 5=E 6=F 7=F# 8=G 9=G# a=A b=A#."
echo "Any yes/no menu choices default to \"n\" if nothing is entered."
echo "-----------------------------------------------------------------------"
echo "|\/\/\/\/\/\/\/\/\/\/\ Demand Human Rights! /\/\/\/\/\/\/\/\/\/\/\/\/\|"
echo "-----------------------------------------------------------------------"
echo
echo
echo "All preset patterns assume a root or key of 0, which can be"
echo "transposed. Any user-entered note collections will also be transposed."
echo "(I.e., the transposition factor is added to all the notes mod 12;"
echo "transposing *down* by x is the same as transposing up by 12-x.)"
echo
echo "What root note/key to transpose to (where 0 = no transposition)?"
echo -n "Please enter a base 12 digit: [0123456789ab] "
read rootNote
if [ "$rootNote" == "" ]; then rootNote="0"; fi
echo
echo "What collection of notes would you like highlighted links between?"
echo " 1) major scale"
echo " 2) natural minor scale"
echo " 3) some mode of the major scale"
echo " 4) pentatonic minor scale"
echo " 5) pentatonic major scale"
echo " 6) harmonic minor scale"
echo " 7) melodic minor scale"
echo " 8) all notes, chromatic scale"
echo " 9) other, user-specified"
echo -n "Enter a choice: [1-6] "
read noteSet
if [ "$noteSet" == "" ]; then noteSet="9"; fi
if [ "$noteSet" = 1 ]; then
markedNotes="2 5 9 0 4 7 b"
elif [ "$noteSet" = 2 ]; then
markedNotes="5 8 0 3 7 a 2"
elif [ "$noteSet" = 3 ]; then
echo
echo " Ionian = major scale; Aeolian = natural minor scale"
echo
echo " Scale name Root, relative to a major scale in key 0"
echo " 1) Ionian 0"
echo " 2) Dorian 2"
echo " 3) Phrygian 4"
echo " 4) Lydian 5"
echo " 5) Mixolydian 7"
echo " 6) Aeolian 9"
echo " 7) Locrian b"
echo
echo -n " Enter the number 1-7 corresponding to the mode: "
read modeName
if [ "$modeName" == "" ]; then modeName="1"; fi
if [ "$modeName" = 1 ]; then modeOffset="0"
elif [ "$modeName" = 2 ]; then modeOffset="a"
elif [ "$modeName" = 3 ]; then modeOffset="8"
elif [ "$modeName" = 4 ]; then modeOffset="7"
elif [ "$modeName" = 5 ]; then modeOffset="5"
elif [ "$modeName" = 6 ]; then modeOffset="3"
elif [ "$modeName" = 7 ]; then modeOffset="1"
fi
markedNotes="2 5 9 0 4 7 b"
markedNotes="`transposeList $modeOffset $markedNotes`"
elif [ "$noteSet" = 4 ]; then
markedNotes="0 3 5 7 a"
elif [ "$noteSet" = 5 ]; then
markedNotes="0 2 4 7 9"
elif [ "$noteSet" = 6 ]; then
markedNotes="2 5 8 0 b 3 7"
elif [ "$noteSet" = 7 ]; then
markedNotes="0 2 3 5 7 9 b"
elif [ "$noteSet" = 8 ]; then
markedNotes="0 1 2 3 4 5 6 7 8 9 a b"
elif [ "$noteSet" = 9 ]; then
echo
echo " Please enter a list of notes from 0 to b, separated by spaces."
echo " (Be sure to use a and b for base 12 versions of 10 and 11.)"
echo -n " Notes as base 12 digits: "
read markedNotes
if [ "$markedNotes" == "" ]; then markedNotes="0"; fi
fi
echo
echo -n "Do you want a chord diagram printed after the table [yn]? "
read printChordDiagram
echo
outputFile="1"
echo -n "Do you want the output sent to a file? [yn] "
read yesno
if [ "$yesno" == "y" ]; then
echo -n "Please enter the name of the file to write (or overwrite): "
read outputFile
if [ "$outputFile" == "" ]; then outputFile="1"; fi
echo -n "Would you like the output to be in (very basic) HTML format? [yn] "
read useHtml
echo -n "Fire up a Mozilla process on the generated output file? [yn] "
read mozillaProcess
fi
echo
echo -n "Do you want the output translated to standard note names? [yn] "
read useStandardNames
if [ "$useStandardNames" == "y" ]; then
echo " By arbitrary convention, 0=B, 1=C, 2=C#, etc."
echo -n " Use sharp symbol (#) or flat symbol (b)? [#b] "
read sharpOrFlat
fi
echo
echo -n "Select a number of columns and rows other than the default? [yn] "
read yesno
if [ "$yesno" == "y" ]; then
echo "All numbers rounded down to the nearest multiple of four plus one."
echo -n "Enter the number of rows: "
read numRows
if [ "$numRows" == "" ]; then numRows="50"; fi
echo -n "Enter the number of columns: "
read numColumns
if [ "$numColumns" == "" ]; then numColumns="78"; fi
fi
echo
title="Note Pattern Diagram" # default title
echo -n "Do you want a title printed at the top of the page? [yn] "
read printTitle
if [ "$printTitle" == "y" ]; then
echo -n "Enter the title here: "
read title
fi
echo
echo
# Round down the row and column numbers.
(( numColumns = (numColumns - 1) / 4 * 4 + 1 ))
(( numRows = (numRows - 1) / 4 * 4 + 1 ))
# transpose after all other options chosen
markedNotes=`transposeList $rootNote $markedNotes`
# these defs are for translating to standard note names with sed
(( rs = 1 )) # start row for diagram
if [ "$useHtml" == "y" ]; then (( rs = rs + 6 )); fi
if [ "$printTitle" == "y" ]; then (( rs = rs + 2 )); fi
(( re = rs + numRows )) # end row for diagram, 3 to cover overspill
if [ "$useStandardNames" == "y" ]; then
if [ "$sharpOrFlat" == "#" ]; then
x0="0-"; x1="1-"; x2="2-"; x3="3-"; x4="4-"; x5="5-"
x6="6-"; x7="7-"; x8="8-"; x9="9-"; xa="a-"; xb="b-"
y0="B-"; y1="C-"; y2="C#"; y3="D-"; y4="D#"; y5="E-"
y6="F-"; y7="F#"; y8="G-"; y9="G#"; ya="A-"; yb="A#"
z0="0 "; z1="1 "; z2="2 "; z3="3 "; z4="4 "; z5="5 "
z6="6 "; z7="7 "; z8="8 "; z9="9 "; za="a "; zb="b "
w0="B "; w1="C "; w2="C#"; w3="D "; w4="D#"; w5="E "
w6="F "; w7="F#"; w8="G "; w9="G#"; wa="A "; wb="A#"
else
x0="0-"; x1="1-"; x2="2-"; x3="3-"; x4="4-"; x5="5-"
x6="6-"; x7="7-"; x8="8-"; x9="9-"; xa="a-"; xb="b-"
y0="B-"; y1="C-"; y2="Db"; y3="D-"; y4="Eb"; y5="E-"
y6="F-"; y7="Gb"; y8="G-"; y9="Ab"; ya="A-"; yb="Bb"
z0="0 "; z1="1 "; z2="2 "; z3="3 "; z4="4 "; z5="5 "
z6="6 "; z7="7 "; z8="8 "; z9="9 "; za="a "; zb="b "
w0="B "; w1="C "; w2="Db"; w3="D "; w4="Eb"; w5="E "
w6="F "; w7="Gb"; w8="G "; w9="Ab"; wa="A "; wb="Bb"
fi
else # null translation
x0="0-"; x1="1-"; x2="2-"; x3="3-"; x4="4-"; x5="5-"
x6="6-"; x7="7-"; x8="8-"; x9="9-"; xa="a-"; xb="b-"
y0="0-"; y1="1-"; y2="2-"; y3="3-"; y4="4-"; y5="5-"
y6="6-"; y7="7-"; y8="8-"; y9="9-"; ya="a-"; yb="b-"
z0="0 "; z1="1 "; z2="2 "; z3="3 "; z4="4 "; z5="5 "
z6="6 "; z7="7 "; z8="8 "; z9="9 "; za="a "; zb="b "
w0="0 "; w1="1 "; w2="2 "; w3="3 "; w4="4 "; w5="5 "
w6="6 "; w7="7 "; w8="8 "; w9="9 "; wa="a "; wb="b "
fi
{ # <-- start a block, to redirect output to a file if selected
(( row = 0 ))
# if you change the preamble text, be sure to modify
# rs and re above (outside this block scope) to fix number of lines
if [ "$useHtml" == "y" ]; then
echo ""
echo ""
echo "$title"
echo ""
echo ""
echo ""
fi
if [ "$printTitle" == "y" ]; then
echo $title
echo
(( row = row + 2 )) # these appear on the page, so count as rows
fi
while true
do
printRow 0 4 8
(( row = row + 1 ))
if (( row >= numRows )); then break; fi
printBetweenRows
printRow 9 1 5
(( row = row + 4 ))
if (( row >= numRows )); then break; fi
printBetweenRows
printRow 6 a 2
(( row = row + 4 ))
if (( row >= numRows )); then break; fi
printBetweenRows
printRow 3 7 b
(( row = row + 4 ))
if (( row >= numRows )); then break; fi
printBetweenRows
(( row = row + 3 ))
done
echo
if [ "$printChordDiagram" == "y" ]; then
echo
echo "Chords with root 0 (the pattern is the same for all chords):"
echo
echo " 9th=7th+add9"
echo "3rd 3--7 5th 7 5th 7th a 2 add9 9 7th 0--4--7"
echo " | / /| | / (-3rd=sus2) |"
echo " |/ / | |/ | augmented"
echo " 0 0--4 3rd 7--b maj7 6"
echo " root root /| |"
echo " / | |"
echo " minor major 0--4 3"
echo " /| |"
echo " / | |"
echo " sus4 5 9 0"
echo " (-3rd) 6th"
echo " diminished"
echo
echo "Note the cycle of 5ths along the / diagonals; counting along the \ diagonals."
echo "Note guitar/bass fretboard pattern (minus \"guitar bump\") along alternate \ diags."
echo "Standard note convention: 0=B 1=C 2=C# 3=D 4=D# 5=E 6=F 7=F# 8=G 9=G# a=A b=A#"
echo
fi
if [ "$useHtml" == "y" ]; then
echo "
"
echo ""
echo ""
fi
} | \
sed -e "$rs,$re s/$xb/$yb/g" -e "$rs,$re s/$x1/$y1/g" -e "$rs,$re s/$x2/$y2/g" \
-e "$rs,$re s/$x3/$y3/g" -e "$rs,$re s/$x4/$y4/g" -e "$rs,$re s/$x5/$y5/g" \
-e "$rs,$re s/$x6/$y6/g" -e "$rs,$re s/$x7/$y7/g" -e "$rs,$re s/$x8/$y8/g" \
-e "$rs,$re s/$x9/$y9/g" -e "$rs,$re s/$xa/$ya/g" -e "$rs,$re s/$x0/$y0/g" \
-e "$rs,$re s/$zb/$wb/g" -e "$rs,$re s/$z1/$w1/g" -e "$rs,$re s/$z2/$w2/g" \
-e "$rs,$re s/$z3/$w3/g" -e "$rs,$re s/$z4/$w4/g" -e "$rs,$re s/$z5/$w5/g" \
-e "$rs,$re s/$z6/$w6/g" -e "$rs,$re s/$z7/$w7/g" -e "$rs,$re s/$z8/$w8/g" \
-e "$rs,$re s/$z9/$w9/g" -e "$rs,$re s/$za/$wa/g" -e "$rs,$re s/$z0/$w0/g" \
1>&$outputFile
if [ "$mozillaProcess" == "y" ]; then
if [ "$outputFile" != "1" ]; then
exec mozilla $outputFile
fi
fi
|