-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPitch.java
123 lines (105 loc) · 3.15 KB
/
Pitch.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package music;
/**
* Pitch is an immutable type representing the frequency of a musical note.
* Standard music notation represents pitches by letters: A, B, C, ..., G.
* Pitches can be sharp or flat, or whole octaves up or down from these
* primitive generators.
*
* <p> For example:
* <br> new Pitch('C') makes middle C
* <br> new Pitch('C').transpose(1) makes C-sharp
* <br> new Pitch('E').transpose(-1) makes E-flat
* <br> new Pitch('C').transpose(OCTAVE) makes high C
* <br> new Pitch('C').transpose(-OCTAVE) makes low C
*/
public class Pitch {
private final int value;
/*
* Rep invariant: true.
*
* Abstraction function AF(value):
* AF(0),...,AF(11) map to middle C, C-sharp, D, ..., A, A-sharp, B.
* AF(i+12n) maps to n octaves above middle AF(i)
* AF(i-12n) maps to n octaves below middle AF(i)
*/
private static final int[] SCALE = {
9, // A
11, // B
0, // C
2, // D
4, // E
5, // F
7, // G
};
private static final String[] VALUE_TO_STRING = {
"C", "^C", "D", "^D", "E", "F", "^F", "G", "^G", "A", "^A", "B"
};
/**
* Middle C.
*/
public static final Pitch MIDDLE_C = new Pitch('C');
/**
* Number of pitches in an octave.
*/
public static final int OCTAVE = 12;
private Pitch(int value) {
this.value = value;
}
/**
* Make a Pitch named c in the middle octave of the piano keyboard.
* For example, new Pitch('C') constructs middle C.
* @param c letter in {'A',...,'G'}
*/
public Pitch(char c) {
try {
value = SCALE[c-'A'];
} catch (ArrayIndexOutOfBoundsException aioobe) {
throw new IllegalArgumentException(c + " must be in the range A-G", aioobe);
}
}
/**
* @return pitch made by transposing this pitch by semitonesUp semitones;
* for example, middle C transposed by 12 semitones is high C, and
* E transposed by -1 semitones is E flat
*/
public Pitch transpose(int semitonesUp) {
return new Pitch(value + semitonesUp);
}
/**
* @return number of semitones between this and that; i.e., n such that
* that.transpose(n).equals(this)
*/
public int difference(Pitch that) {
return this.value - that.value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (obj.getClass() != this.getClass()) return false;
Pitch that = (Pitch) obj;
return this.value == that.value;
}
@Override
public int hashCode() {
return value;
}
/**
* @return this pitch in abc music notation
* @see http://www.walshaw.plus.com/abc/examples/
*/
@Override
public String toString() {
String suffix = "";
int v = value;
while (v < 0) {
suffix += ",";
v += 12;
}
while (v >= 12) {
suffix += "'";
v -= 12;
}
return VALUE_TO_STRING[v] + suffix;
}
}