/
pattern.go
149 lines (130 loc) · 3.78 KB
/
pattern.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package audio
import (
"fmt"
"reflect"
"sort"
)
type Pattern struct {
Name string
Notes []*Note
Attributes map[string][]*ControlPoint
}
type Note struct {
Time float64
Attributes map[string][]*ControlPoint
}
type PatternPlayer struct {
pattern *Pattern
inst Instrument
play reflect.Value
i int
t, dt float64
}
func NewPatternPlayer(pattern *Pattern, inst Instrument) *PatternPlayer {
return &PatternPlayer{pattern: pattern, inst: inst, play: InstrumentPlayMethod(inst)}
}
func (p *PatternPlayer) InitAudio(params Params) {
Init(p.inst, params)
p.dt = 1 / params.SampleRate
p.SetTime(p.t)
}
func (p *PatternPlayer) GetTime() float64 { return p.t }
func (p *PatternPlayer) SetTime(t float64) {
noteType := p.play.Type().In(0)
for _, n := range p.pattern.Notes {
for name := range n.Attributes {
if _, ok := noteType.FieldByName(name); !ok {
panic(fmt.Sprintf("Pattern %s: Instrument %T has no attribute %s.", p.pattern.Name, p.inst, name))
}
}
for i := 0; i < noteType.NumField(); i++ {
name := noteType.Field(i).Name
if _, ok := n.Attributes[name]; !ok {
panic(fmt.Sprintf("Pattern %s: Note has no attribute %s for instrument %T.", p.pattern.Name, name, p.inst))
}
}
}
sort.Sort(notesByTime(p.pattern.Notes))
for p.i = 0; p.i < len(p.pattern.Notes) && p.pattern.Notes[p.i].Time < t; p.i++ {
}
for _, c := range InstrumentControls(p.inst) {
c.SetPoints(p.pattern.Attributes[c.Name])
c.SetTime(t)
}
p.t = t
}
type notesByTime []*Note
func (n notesByTime) Len() int { return len(n) }
func (n notesByTime) Less(i, j int) bool { return n[i].Time < n[j].Time }
func (n notesByTime) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (p *PatternPlayer) Play() {
for ; p.i < len(p.pattern.Notes); p.i++ {
n := p.pattern.Notes[p.i]
if n.Time > p.t {
break
}
note := reflect.New(p.play.Type().In(0)).Elem()
for name, val := range n.Attributes {
note.FieldByName(name).Set(reflect.ValueOf(val))
}
p.play.Call([]reflect.Value{note})
}
p.t += p.dt
}
func (p *PatternPlayer) Sing() float64 {
p.Play()
return p.inst.Sing()
}
func (p *PatternPlayer) Done() bool {
return p.i == len(p.pattern.Notes) && p.inst.Done()
}
// For an Instrument to play notes, it must have a method Play(noteType) where noteType is a struct with exported fields of type []*ControlPoint.
// For an Instrument to be controlled, it must export fields of type audio.Control.
type Instrument interface {
Voice
Stop()
}
func InstrumentPlayMethod(inst Instrument) reflect.Value {
// TODO: Play method is optional.
m := reflect.ValueOf(inst).MethodByName("Play")
if !m.IsValid() {
panic(fmt.Sprintf("Type %T must have a method named Play.", inst))
}
if m.Type().NumIn() != 1 {
panic(fmt.Sprintf("Method (%T).Play must have a single parameter.", inst))
}
n := m.Type().In(0)
if n.Kind() != reflect.Struct {
panic(fmt.Sprintf("The parameter to method (%T).Play must be a struct.", inst))
}
t := reflect.TypeOf([]*ControlPoint(nil))
for i := 0; i < n.NumField(); i++ {
f := n.Field(i)
if f.Type != t || f.PkgPath != "" {
panic(fmt.Sprintf("The parameter to method (%T).Play must only have exported fields of type %s.", inst, t))
}
}
return m
}
func InstrumentControls(inst Instrument) []NamedControl {
c := []NamedControl{}
v := reflect.Indirect(reflect.ValueOf(inst))
t := v.Type()
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Type == reflect.TypeOf(Control{}) && f.PkgPath == "" {
c = append(c, NamedControl{f.Name, v.Field(i).Addr().Interface().(*Control)})
}
}
return c
}
type NamedControl struct {
Name string
*Control
}
// TODO: remove me? no longer relevant as Play method is optional
func IsInstrument(i Instrument) (ok bool) {
defer func() { ok = recover() == nil }()
InstrumentPlayMethod(i)
return
}