dottes/abcrange.py

96 lines
2.4 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
#
# 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 above ('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() or start[0] == '+'):
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)