Add abcrange.py to return the range of a tune, and use it in instrument transposition.
authorJim Hague <jim.hague@acm.org>
Thu, 18 Jul 2013 15:27:57 +0100
changeset 322 b4a0161e8870
parent 321 b61c39beac5f
child 323 1a240d1e2032
Add abcrange.py to return the range of a tune, and use it in instrument transposition. This lets us transpose on boundaries that aren't octave boundaries.
abcrange.py
makeCello.sh
makeHornInF.sh
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/abcrange.py	Thu Jul 18 15:27:57 2013 +0100
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+#
+# Find the range of a tune. Do minimal parsing of an ABC input file
+# and print the lowest and highest notes therein. Accidentals are
+# ignored.
+#
+# The output is given in purely numeric form, to avoid needing to
+# re-parse it in an external script. A single line is printed with
+# the highest note followed by a space and the lowest note. Middle C ('C') is
+# 100. D an octave about ('d') is 108. D an octave above that ('d'') is
+# 205. D below middle C ('d,') is 94. And so on.
+#
+# For example:
+# $./abcrange.py choon.abc
+# choon.abc: 112 97
+#
+
+import sys
+
+def process(filename, inf):
+    highest = 0
+    lowest = 1000
+    for line in inf:
+        line = line.strip()
+        # If it is empty or starts "%", ignore it.
+        if len(line) == 0 or line[0] == "%":
+            continue
+
+        # Is it a header line? I.e. does it start LETTER COLON?
+        # If so, ignore.
+        start = line[:2]
+        if len(start) > 1 and start[1] == ":" and start[0].isalpha():
+            continue
+
+        # Tune line.
+        inchord = False
+        note = 0
+        notevals = {
+            "C": 100,
+            "D": 101,
+            "E": 102,
+            "F": 103,
+            "G": 104,
+            "A": 105,
+            "B": 106,
+            "c": 107,
+            "d": 108,
+            "e": 109,
+            "f": 110,
+            "g": 111,
+            "a": 112,
+            "b": 113,
+        }
+        for c in line:
+            if c == "," and note > 0:
+                note = note - 7
+                continue
+            elif c == "'" and note > 0:
+                note = note + 7
+                continue
+
+            if note > 0:
+                if note > highest:
+                    highest = note
+                if note < lowest:
+                    lowest = note
+                note = 0
+
+            if c == '"':
+                inchord = not inchord
+                continue
+            if inchord:
+                continue
+
+            if c in notevals:
+                note = notevals[c]
+
+        if note > 0:
+            if note > highest:
+                highest = note
+            if note < lowest:
+                lowest = note
+            note = 0
+
+    print "{0}: {1} {2}".format(filename, highest, lowest)
+
+if len(sys.argv) > 1:
+    for arg in sys.argv[1:]:
+        try:
+            inf = open(arg, "r")
+            process(arg, inf)
+        finally:
+            inf.close()
+else:
+    process("stdin", sys.stdin)
--- a/makeCello.sh	Wed Jul 17 18:28:07 2013 +0100
+++ b/makeCello.sh	Thu Jul 18 15:27:57 2013 +0100
@@ -1,6 +1,7 @@
 #!/bin/bash
 #
-# Copy a booke to cello-friendly form. Bass clef, transposed down 2 octaves.
+# Copy a booke to cello-friendly form. Bass clef, transposed down
+# 1 or 2 octaves.
 #
 # It would be easier to do with transpose in dottes.fmt, but I can't get
 # that to work properly for a 2 octave downward transpose.
@@ -12,6 +13,12 @@
     exit 1
 fi
 
+# Transpose down (return 0) if bottom note was < C.
+transposedowntwo()
+{
+    return $(($3 >= 100))
+}
+
 dir=`pwd`
 
 booke=$dir/$1
@@ -27,13 +34,16 @@
 find $booke -name "*.abc" | sort |
     while read filename
     do
+        name=`basename $filename .abc`
+        range=`./abcrange.py $filename`
+
         # Move down either one octave or two, depending on the range
         # of the tune. If there are any notes below middle C, transpose
         # down one octave. The default is to transpose down two octaves.
         middle="d"
-        if grep -v "^[A-Z]:" $filename | sed -e 's/"[^"]*"//g' | grep -q "[A-Z],"; then
+        if transposedowntwo $range; then
             middle="D"
         fi
-        name=`basename $filename .abc`
+
         sed -e "/^ *K:/s/$/ clef=bass middle=$middle/" $filename > $outdir/$name.abc
     done
--- a/makeHornInF.sh	Wed Jul 17 18:28:07 2013 +0100
+++ b/makeHornInF.sh	Thu Jul 18 15:27:57 2013 +0100
@@ -7,6 +7,12 @@
     exit 1
 fi
 
+# Transpose down (return 0) if top note was > e (> a for horn).
+transposedown()
+{
+    return $(($2 <= 109))
+}
+
 dir=`pwd`
 
 booke=$dir/$1
@@ -23,17 +29,13 @@
     while read filename
     do
         name=`basename $filename .abc`
-        tmpfile=$outdir/$name.abc.tmp
-
-        # Strip out guitar chords first. This has the advantage of removing
-        # text with arbitary lower case characters too.
-        sed -e "s/\"[^\"]*\"//g" $filename > $tmpfile
+        range=`./abcrange.py $filename`
 
         # Transpose concert pitch up a fifth.
-        # If there are any notes at or above C above middle C, transpose
+        # If there are any notes above 'd' (Horn 'g'), transpose
         # down a seventh instead.
         transpose=5
-        if grep -v "^[A-Z]:" $tmpfile | sed -e 's/"[^"]*"//g' | grep -q "[a-g]"; then
+        if transposedown $range; then
             transpose=-7
         fi
 
@@ -42,7 +44,6 @@
         # output to be in treble clef; some lower tunes with the odd high
         # note will otherwise appear in bass clef, which is not what this
         # crap horn player wants.
-        abc2abc $tmpfile -e -t $transpose | \
+        abc2abc $filename -e -t $transpose | \
             sed -e "/^ *K:/s/$/ clef=treble/" > $outdir/$name.abc
-        rm $tmpfile
     done