forked from peterbourgon/goop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
generators.go
285 lines (245 loc) · 6.86 KB
/
generators.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
package main
import (
"fmt"
"math"
)
const (
SRATE = 44100 // audio sample rate
BUFSZ = 2048 // audio buffer size
AUDIO_CHAN_BUFFER = 0 // unbuffered
EVENT_CHAN_BUFFER = 10
)
// generatorChannels are designed to be embedded into Generators
// to satisfy the Node and AudioSender interfaces.
type generatorChannels struct {
eventIn chan Event
audioOut chan []float32
}
func (gc *generatorChannels) Events() chan<- Event {
return gc.eventIn
}
func (gc *generatorChannels) AudioOut() <-chan []float32 {
return gc.audioOut
}
func (gc *generatorChannels) Reset() {
close(gc.audioOut)
gc.audioOut = make(chan []float32, AUDIO_CHAN_BUFFER)
}
func makeGeneratorChannels() generatorChannels {
ei := make(chan Event, EVENT_CHAN_BUFFER)
ao := make(chan []float32, AUDIO_CHAN_BUFFER)
return generatorChannels{ei, ao}
}
//
//
//
const (
KeyDown = "keydown"
KeyUp = "keyup"
Gain = "gain"
)
func KeyDownEvent(n Note) Event { return Event{KeyDown, n.Hz(), n} }
func KeyUpEvent(n Note) Event { return Event{KeyUp, n.Hz(), n} }
func GainEvent(g float32) Event { return Event{Gain, g, nil} }
// simpleParameters are sufficient to control simple,
// single-mode Generators.
type simpleParameters struct {
hz float32
phase float32 // 0..1
gain float32 // 0..1
}
// processEvent satisfies the eventProcessor interface.
// It applies Events which should have an effect on simpleParameters.
func (sp *simpleParameters) processEvent(ev Event) {
switch ev.Type {
case KeyDown:
sp.hz = ev.Value
case KeyUp:
sp.hz = 0.0
case Gain:
sp.gain = ev.Value
}
}
func makeSimpleParameters() simpleParameters {
return simpleParameters{0.0, 0.0, 1.0}
}
//
//
//
// valueProviders implement a method that yields a float32 audio value.
// Those values are stacked together into a buffer by nextBuffer, and
// typically yielded over an AudioSender port.
//
// Probably every Generator should satisfy the valueProvider interface.
type valueProvider interface {
nextValue() float32
}
// nextBuffer aggregates BUFSZ values from the valueProvider
// into a single buffer, which it then returns.
func nextBuffer(vp valueProvider) []float32 {
buf := make([]float32, BUFSZ)
for i := 0; i < BUFSZ; i++ {
buf[i] = vp.nextValue()
}
return buf
}
//
//
//
// A GeneratorFunction should define output for input [0 .. 1].
// We scale that to the range [0 .. 0.25]. Call that scaled output
// 'F'. We generate a waveform based on phase [0 .. 1] as follows:
//
// phase < 0.25: output = F
// phase < 0.50: output = F mirrored horizontally
// phase < 0.75: output = F mirrored vertically
// phase < 1.00: output = F mirrored horizontally + vertically
//
// (Thanks to Alexander Surma for the idea on this one.)
type GeneratorFunction func(float32) float32
func nextGeneratorFunctionValue(f GeneratorFunction, hz float32, phase *float32) float32 {
var val, p float32 = 0.0, 0.0
switch {
case *phase <= 0.25:
p = (*phase - 0.00) * 4
val = f(p) // no mirror
case *phase <= 0.50:
p = (*phase - 0.25) * 4
val = f(1 - p) // horizontal mirror
case *phase <= 0.75:
p = (*phase - 0.50) * 4
val = -f(p) // vertical mirror
case *phase <= 1.00:
p = (*phase - 0.75) * 4
val = -f(1 - p) // horizontal + vertical mirror
default:
panic("unreachable")
}
*phase += hz / SRATE
if *phase > 1.0 {
*phase -= 1.0
}
return val
}
func saw(x float32) float32 {
return x
}
func sine(x float32) float32 {
// want only 1/4 sine over range [0..1], so need x/4
return float32(math.Sin(2 * math.Pi * float64(x/4)))
}
func square(x float32) float32 {
if x < 0.5 {
return 1.0
}
return 0.0
}
//
//
//
// A simpleGenerator is any generator which can provide audio data
// using only simpleParameters. Handily, this describes a large class of
// generators.
//
// A simpleGenerator also claims singleAncestry; that is,
// it has up to 1 parent and 1 child Node in the Field.
type simpleGenerator struct {
generatorChannels
simpleParameters
nodeName
singleChild
noParents
}
func (g *simpleGenerator) String() string {
return fmt.Sprintf(
"<Hz=%.2f gain=%.2f Parents=%d Children=%d>",
g.hz,
g.gain,
len(g.Parents()),
len(g.Children()),
)
}
// generatorLoop is the common function that should drive all Generators
// which contain generatorChannels and are driven by simpleParameters.
//
// It processes certain common Event types, and passes the remaining Events
// off to the given simpleParameters.
//
// It uses the passed valueProvider (typically the concrete Generator itself)
// to generate audio buffers which are pushed over the outgoing audio channel.
func (sg *simpleGenerator) loop(vp valueProvider) {
var buf []float32 = nil
for {
if buf == nil {
buf = nextBuffer(vp)
}
select {
case sg.generatorChannels.audioOut <- buf:
buf = nil
case ev := <-sg.generatorChannels.eventIn:
switch ev.Type {
case Connection, Disconnection:
D("simpleGenerator got ignored %s Event", ev.Type)
break // no parents: ignore
case Connect:
n, ok := ev.Arg.(Node)
if !ok {
panic("simpleGenerator Connect to non-Node")
}
// This is tentatively commented-out, because the scheduler
// may cause it to be executed *after* the downstream node has
// already called our AudioOut() and started consuming.
//
//sg.generatorChannels.Reset()
//
// UPDATE
// Unfortunately, without a signal to the original downstream
// Node, it will continue to attempt to receive audio from an
// abandoned channel. Since we can't guarantee order of
// evaluation of Connect signals, we need the Connect action
// to propegate a Disconnection signal to the original Node
// in single-ancestry situations.
sg.ChildNode = n
D("simpleGenerator got Connect %s OK", n.Name())
case Disconnect:
sg.ChildNode = nilNode
sg.generatorChannels.Reset()
D("simpleGenerator got Disconnect OK")
case Kill:
sg.ChildNode = nilNode
sg.generatorChannels.Reset()
return
default:
sg.simpleParameters.processEvent(ev)
}
}
}
}
//
//
//
// thanks to #go-nuts skelterjohn for this construction idiom
type SineGenerator struct{ simpleGenerator }
func (g *SineGenerator) Kind() string { return "Sine Generator" }
func (g *SineGenerator) String() string {
return fmt.Sprintf("[%s %s]", NodeLabel(g), g.simpleGenerator.String())
}
func NewSineGenerator(name string) *SineGenerator {
g := SineGenerator{
simpleGenerator{
generatorChannels: makeGeneratorChannels(),
simpleParameters: makeSimpleParameters(),
nodeName: nodeName(name),
},
}
go g.simpleGenerator.loop(&g)
return &g
}
func NewSineGeneratorNode(name string) Node {
return Node(NewSineGenerator(name))
}
// nextValue for a SineGenerator will output a pure sine waveform at the
// frequency described by the simpleParameter's hz parameter.
func (g *SineGenerator) nextValue() float32 {
return nextGeneratorFunctionValue(sine, g.hz, &g.phase) * g.gain
}