Eikonal Blog

2010.07.13

Chord and Scale Pattern Generator

Filed under: music — Tags: , — sandokan65 @ 13:42

Source: I have lost the reference to the original source.

#! /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

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: