forked from CryHavoc/dottes
96 lines
2.4 KiB
Python
Executable File
96 lines
2.4 KiB
Python
Executable File
#!/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)
|