-
Notifications
You must be signed in to change notification settings - Fork 1
/
song.go
119 lines (110 loc) · 3.08 KB
/
song.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
117
118
119
package beepster
import (
"github.com/octotep/go-mxl"
"math"
)
type Song struct {
NumTracks uint8
Track []chan Note
Reps int
}
func CreateSong(numTracks uint8, numRepetitions int) *Song {
// Create the song and fill out all the information we know about it
mysong := new(Song)
mysong.NumTracks = numTracks
mysong.Reps = numRepetitions
mysong.Track = make([]chan Note, mysong.NumTracks)
for i := range mysong.Track {
mysong.Track[i] = make(chan Note, 14)
}
return mysong
}
func (song *Song) CreateFiller(trackId uint8, cleanup func(), tracks ...*[]Note) func() {
return func() {
defer cleanup()
for i := 0; i < song.Reps; i++ {
// Loop through all arrays given
for _, val := range tracks {
// Loop through all notes in one of the arrays
for _, note := range *val {
song.Track[trackId] <- note
}
}
}
close(song.Track[trackId])
}
}
func CreateSongFromXML(mxlDoc mxl.MXLDoc, numRepetitions int) *Song {
mysong := new(Song)
mysong.NumTracks = uint8(len(mxlDoc.Parts))
mysong.Reps = numRepetitions
mysong.Track = make([]chan Note, mysong.NumTracks)
for i := range mysong.Track {
mysong.Track[i] = make(chan Note, 14)
}
return mysong
}
func (song *Song) CreateFillerFromXml(trackId uint8, bpm uint, cleanup func(), track mxl.Part) func() {
return func() {
defer cleanup()
for i := 0; i < song.Reps; i++ {
// Loop through all arrays given
currentDiv := 1
for _, measure := range track.Measures {
// Check for new division
if measure.Atters.Divisions != 0 {
currentDiv = measure.Atters.Divisions
}
// Loop through all notes in one of the arrays
for _, note := range measure.Notes {
// Only process the note if it is not part of a chord
if note.Chord.Local == "" {
var freq float32
if note.Pitch.Step == "" {
// It's a rest
freq = 0.0
} else {
// Calc the correct pitch from the frequency
freq = PitchToFreq(note.Pitch)
}
// How long one quarter note is in seconds
lengthOfQuarter := 60.0 / float32(bpm)
// The length of the current note in seconds
totalTime := float32(note.Duration) / float32(currentDiv) * lengthOfQuarter
// Delay before the next note in ms
delay := 5
// Convert totalTime to ms and subtract delay at the end of the note
length := int(totalTime*1000) - delay
song.Track[trackId] <- Note{freq, uint32(length), uint32(delay)}
}
}
}
}
close(song.Track[trackId])
}
}
func PitchToFreq(pitch mxl.Pitch) float32 {
var letter int
if pitch.Step == "C" {
letter = 0
} else if pitch.Step == "D" {
letter = 2
} else if pitch.Step == "E" {
letter = 4
} else if pitch.Step == "F" {
letter = 5
} else if pitch.Step == "G" {
letter = 7
} else if pitch.Step == "A" {
letter = 9
} else if pitch.Step == "B" {
letter = 11
}
absoluteHalfSteps := 12*(pitch.Octave-1) + letter + int(pitch.Accidental)
a440 := 45
relativeHalfSteps := absoluteHalfSteps - a440
var freq float32
// 2^(n/12) * 440
freq = float32(math.Pow(2, float64(float64(relativeHalfSteps)/12.0))) * 440.0
return freq
}