-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathindex.js
78 lines (70 loc) · 2.32 KB
/
index.js
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
'use strict'
var Voltage = require('voltage-source-node')
var isNum = function (n) { return typeof n === 'number' }
var NUMS = ['duration', 't1', 't2', 't3', 't4', 'l1', 'l2', 'l3']
var DEFAULTS = {
duration: Infinity, l1: 1, l2: 0.2, l3: 0.8,
t1: 0.01, t2: 0.1, t3: 0, t4: 0.2
}
function rampFn (l) {
return l ? 'linearRampToValueAtTime' : 'exponentialRampToValueAtTime'
}
function ramp (l, node, level, time) { node.gain[rampFn(l)](level, time) }
/**
* Create an envelope generator.
* @param {AudioContext} ac - the audio context
* @param {Object} options - (Optional) the envelope options
* @return {AudioNode} the envelope generator node
*/
function Contour (ac, options) {
var env = ac.createGain()
var opts = Contour.params(options, env)
var isL = opts.ramp === 'linear'
var tail = ac.createGain()
tail.connect(env)
var head = ac.createGain()
head.connect(tail)
var cv = Voltage(ac)
cv.connect(head)
env.start = function (time) {
time = Math.max(time || 0, ac.currentTime)
if (env.onstart) env.onstart(time)
cv.start(time)
head.gain.setValueAtTime(0, time)
head.gain.setValueAtTime(0.01, time + 0.000001)
ramp(isL, head, opts.l1, time + opts.t1)
ramp(isL, head, opts.l2, time + opts.t1 + opts.t2)
ramp(isL, head, opts.l3, time + opts.t1 + opts.t2 + opts.t3)
if (isFinite(opts.duration)) env.stop(time + opts.duration)
}
env.stop = function (time) {
time = Math.max(time || 0, ac.currentTime)
tail.gain.cancelScheduledValues(time)
tail.gain.setValueAtTime(env.gain.value, time)
var endsAt = time + opts.t4
ramp(isL, tail, 0.0001, endsAt)
if (env.onended) {
var s = Voltage(ac, 0)
s.connect(ac.destination)
s.onended = env.onended
s.start(ac.currentTime)
s.stop(endsAt)
}
return endsAt
}
return env
}
Contour.params = function (options, dest) {
dest = dest || {}
options = options || {}
NUMS.forEach(function (name) {
dest[name] = isNum(options[name]) ? options[name] : DEFAULTS[name]
})
if (isNum(options.attack)) dest.t1 = options.attack
if (isNum(options.decay)) dest.t2 = options.decay
if (isNum(options.sustain)) dest.l3 = options.sustain
if (isNum(options.release)) dest.t4 = options.release
dest.ramp = options.ramp === 'exponential' ? options.ramp : 'linear'
return dest
}
module.exports = Contour