-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMusicLanguage.java
124 lines (109 loc) · 4.53 KB
/
MusicLanguage.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
124
package music;
import static music.Pitch.OCTAVE;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* MusicLanguage defines static methods for constructing and manipulating Music expressions.
*/
public class MusicLanguage {
// Prevent instantiation
protected MusicLanguage() {}
////////////////////////////////////////////////////
// Factory methods
////////////////////////////////////////////////////
/**
* Make Music from a string using a variant of abc notation
* (see http://www.walshaw.plus.com/abc/examples/).
*
* <p> The notation consists of whitespace-delimited symbols representing
* either notes or rests. The vertical bar | may be used as a delimiter
* for measures; notes() treats it as a space.
* Grammar:
* <pre>
* notes ::= symbol*
* symbol ::= . duration // for a rest
* | pitch duration // for a note
* pitch ::= accidental letter octave*
* accidental ::= empty string // for natural,
* | _ // for flat,
* | ^ // for sharp
* letter ::= [A-G]
* octave ::= ' // to raise one octave
* | , // to lower one octave
* duration ::= empty string // for 1-beat duration
* | /n // for 1/n-beat duration
* | n // for n-beat duration
* | n/m // for n/m-beat duration
* </pre>
* <p> Examples (assuming 4/4 common time, i.e. 4 beats per measure):
* C quarter note, middle C
* A'2 half note, high A
* _D/2 eighth note, middle D flat
*
* @param notes string of notes and rests in simplified abc notation given above
* @param instr instrument to play the notes with
* @return the music in notes played by instr
*/
public static Music notes(String notes, Instrument instr) {
Music music = rest(0);
for (String sym : notes.split("[\\s|]+")) {
if (!sym.isEmpty()) {
music = concat(music, parseSymbol(sym, instr));
}
}
return music;
}
/* Parse a symbol into a Note or a Rest. */
private static Music parseSymbol(String symbol, Instrument instr) {
Matcher m = Pattern.compile("([^/0-9]*)([0-9]+)?(/[0-9]+)?").matcher(symbol);
if (!m.matches()) throw new IllegalArgumentException("couldn't understand " + symbol);
String pitchSymbol = m.group(1);
double duration = 1.0;
if (m.group(2) != null) duration *= Integer.valueOf(m.group(2));
if (m.group(3) != null) duration /= Integer.valueOf(m.group(3).substring(1));
if (pitchSymbol.equals(".")) return rest(duration);
else return note(duration, parsePitch(pitchSymbol), instr);
}
/* Parse a symbol into a Pitch. */
private static Pitch parsePitch(String symbol) {
if (symbol.endsWith("'"))
return parsePitch(symbol.substring(0, symbol.length()-1)).transpose(OCTAVE);
else if (symbol.endsWith(","))
return parsePitch(symbol.substring(0, symbol.length()-1)).transpose(-OCTAVE);
else if (symbol.startsWith("^"))
return parsePitch(symbol.substring(1)).transpose(1);
else if (symbol.startsWith("_"))
return parsePitch(symbol.substring(1)).transpose(-1);
else if (symbol.length() != 1)
throw new IllegalArgumentException("can't understand " + symbol);
else
return new Pitch(symbol.charAt(0));
}
/**
* @param duration duration in beats, must be >= 0
* @param pitch pitch to play
* @param instrument instrument to use
* @return pitch played by instrument for duration beats
*/
public static Music note(double duration, Pitch pitch, Instrument instrument) {
return new Note(duration, pitch, instrument);
}
/**
* @param duration duration in beats, must be >= 0
* @return rest that lasts for duration beats
*/
public static Music rest(double duration) {
return new Rest(duration);
}
////////////////////////////////////////////////////
// Producers
////////////////////////////////////////////////////
/**
* @param m1 first piece of music
* @param m2 second piece of music
* @return m1 followed by m2
*/
public static Music concat(Music m1, Music m2) {
return new Concat(m1, m2);
}
}