comparison abcrange.py @ 322:b4a0161e8870

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.
author Jim Hague <jim.hague@acm.org>
date Thu, 18 Jul 2013 15:27:57 +0100
parents
children 1b79867b4f35
comparison
equal deleted inserted replaced
321:b61c39beac5f 322:b4a0161e8870
1 #!/usr/bin/env python
2 #
3 # Find the range of a tune. Do minimal parsing of an ABC input file
4 # and print the lowest and highest notes therein. Accidentals are
5 # ignored.
6 #
7 # The output is given in purely numeric form, to avoid needing to
8 # re-parse it in an external script. A single line is printed with
9 # the highest note followed by a space and the lowest note. Middle C ('C') is
10 # 100. D an octave about ('d') is 108. D an octave above that ('d'') is
11 # 205. D below middle C ('d,') is 94. And so on.
12 #
13 # For example:
14 # $./abcrange.py choon.abc
15 # choon.abc: 112 97
16 #
17
18 import sys
19
20 def process(filename, inf):
21 highest = 0
22 lowest = 1000
23 for line in inf:
24 line = line.strip()
25 # If it is empty or starts "%", ignore it.
26 if len(line) == 0 or line[0] == "%":
27 continue
28
29 # Is it a header line? I.e. does it start LETTER COLON?
30 # If so, ignore.
31 start = line[:2]
32 if len(start) > 1 and start[1] == ":" and start[0].isalpha():
33 continue
34
35 # Tune line.
36 inchord = False
37 note = 0
38 notevals = {
39 "C": 100,
40 "D": 101,
41 "E": 102,
42 "F": 103,
43 "G": 104,
44 "A": 105,
45 "B": 106,
46 "c": 107,
47 "d": 108,
48 "e": 109,
49 "f": 110,
50 "g": 111,
51 "a": 112,
52 "b": 113,
53 }
54 for c in line:
55 if c == "," and note > 0:
56 note = note - 7
57 continue
58 elif c == "'" and note > 0:
59 note = note + 7
60 continue
61
62 if note > 0:
63 if note > highest:
64 highest = note
65 if note < lowest:
66 lowest = note
67 note = 0
68
69 if c == '"':
70 inchord = not inchord
71 continue
72 if inchord:
73 continue
74
75 if c in notevals:
76 note = notevals[c]
77
78 if note > 0:
79 if note > highest:
80 highest = note
81 if note < lowest:
82 lowest = note
83 note = 0
84
85 print "{0}: {1} {2}".format(filename, highest, lowest)
86
87 if len(sys.argv) > 1:
88 for arg in sys.argv[1:]:
89 try:
90 inf = open(arg, "r")
91 process(arg, inf)
92 finally:
93 inf.close()
94 else:
95 process("stdin", sys.stdin)