-
Notifications
You must be signed in to change notification settings - Fork 0
/
note.go
116 lines (99 loc) · 1.74 KB
/
note.go
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
package musicgo
import (
"fmt"
"io"
"math"
"strings"
)
type Cents int
type NoteIndex int
type Note float64
const nilNote Note = 0
func normalize(n Note) Note {
return n - Note(math.Floor(float64(n/12))*12)
}
func (n Note) Interval(i Interval) Note {
return normalize(n + Note(i))
}
func (n Note) Index() NoteIndex {
m := NoteIndex(math.Floor(float64(n))) % 12
if m < 0 {
return NoteIndex(12 + m)
}
return m
}
func (n Note) Cents() Cents {
n64 := float64(n)
return Cents(math.Floor(0.5 + (n64-math.Floor(n64))*100))
}
func (n Note) Octave(o Octave) Pitch {
norm := normalize(n)
return Pitch(o)*12 + Pitch(norm)
}
func (n Note) String() string {
name := noteNames[Note(n.Index())]
c := n.Cents()
if c != 0 {
return fmt.Sprintf("%v (%v cents)", name, c)
}
return name
}
func ParseNote(s string) (Note, error) {
return parseNote(strings.NewReader(s))
}
func parseNote(r *strings.Reader) (Note, error) {
// read the letter
c, _, err := r.ReadRune()
if err != nil {
return nilNote, err
}
if c >= 'a' && c <= 'z' {
c += 'A' - 'a'
}
n, ok := noteOffsets[c]
if !ok {
return nilNote, fmt.Errorf("Invalid note letter: %c", c)
}
// read sharps or flats
for {
c, _, err = r.ReadRune()
if err != nil && err != io.EOF {
return nilNote, err
}
if err == io.EOF {
break
}
switch c {
case '#':
n = n + 1
case 'b':
n = n - 1
default:
return nilNote, fmt.Errorf("Invalid rune: %v", c)
}
}
return normalize(n), nil
}
var noteOffsets map[rune]Note = map[rune]Note{
'C': 0,
'D': 2,
'E': 4,
'F': 5,
'G': 7,
'A': 9,
'B': 11,
}
var noteNames map[Note]string = map[Note]string{
0: "C",
1: "C#",
2: "D",
3: "D#",
4: "E",
5: "F",
6: "F#",
7: "G",
8: "G#",
9: "A",
10: "A#",
11: "B",
}