dot-audio
Version:
A simple web audio library for making synthesizers
190 lines (161 loc) • 5.26 kB
JavaScript
import DotAudioNode from 'nodes/core/DotAudioNode'
import ConstantSource from 'nodes/sources/ConstantSource'
const defaultProps = {
attack: 0,
decay: 0,
sustain: 1,
release: 0,
modifier: 1,
}
/**
* A general-purpose ADSR envelope that can be connected to AudioParams to modulate values over time.
* Built using a ConstantSource node.
*
* @example
* // | A | D | S | R |
* // / \
* // / \
* // / \ _____
* // / \
* // / \
*
* const env = new Dot.Envelope(AC, { decay: 0.4, sustain: 0 })
* const gain = new Dot.Gain(AC)
*
* env.connect(gain.getParam('gain'))
*
* @extends DotAudioNode
* @param {AudioContext} AC - Audio context
* @param {Object} opts - Initialization options
* @param {Number} opts.attack - Amount of time for the envelope to reach the modifier value from 0 (default: 0)
* @param {Number} opts.decay - Amount of time for the envelope to reach the sustain value after the attack time (default: 0)
* @param {Number} opts.sustain - The sustain value of the envelope after teh attack and decay times (default: 1)
* @param {Number} opts.release - Amount of time for the envelope to reach 0 after release (default: 0)
* @param {Number} opts.modifier - The modulation value of the envelope (default: 1)
* @returns {Envelope} Envelope Node
*/
class Envelope extends DotAudioNode {
constructor(AC, opts = {}) {
super(AC)
this.name = 'Envelope'
this.source = new ConstantSource(this.AC, { start: true })
this.timeoutIds = []
this.attack = 0
this.decay = 0
this.sustain = 1
this.release = 0
this.modifier = 1
this.params = {}
this.inputs = null
this.outputs = [this.source]
// Initialize
const initProps = { ...defaultProps, ...opts }
this.setAttack(initProps.attack)
this.setDecay(initProps.decay)
this.setSustain(initProps.sustain)
this.setRelease(initProps.release)
this.setModifier(initProps.modifier)
this.source.setOffset(0)
return this
}
// --- Public Methods ---
// - Getters -
/**
* Get the attack time of the envelope.
* @returns {Number} Attack time
*/
getAttack = () => this.attack
/**
* Get the decay time of the envelope.
* @returns {Number} Decay time
*/
getDecay = () => this.decay
/**
* Get the sustain value of the envelope.
* @returns {Number} Sustain value
*/
getSustain = () => this.sustain
/**
* Get the release time of the envelope.
* @returns {Number} Release time
*/
getRelease = () => this.release
/**
* Get the modifier value of the envelope.
* @returns {Number} Modifier value
*/
getModifier = () => this.modifier
// - Setters -
/**
* Get the attack time of the envelope.
* @param {Number} val - attack time value
*/
setAttack = (val) => this.attack = val
/**
* Get the decay time of the envelope.
* @param {Number} val - decay time value
*/
setDecay = (val) => this.decay = val
/**
* Get the sustain value of the envelope.
* @param {Number} val - sustain value
*/
setSustain = (val) => this.sustain = val
/**
* Get the release time of the envelope.
* @param {Number} val - release time value
*/
setRelease = (val) => this.release = val
/**
* Get the modifier value of the envelope.
* @param {Number} val - modifier value
*/
setModifier = (val) => this.modifier = val
// - Util Methods -
connect = (destination) => {
if (!destination instanceof AudioParam) {
console.error('Envelopes must be connected to an AudioParam')
return
}
this._connect(destination)
}
// - Trigger Methods -
/**
* Triggers the attack of the envelope.
* Will automatically trigger the decay after the attack time.
*/
triggerAttack = () => {
this._clearTimeouts()
const sustainVal = this.sustain * this.modifier
if (this.attack) {
this.source.setOffset(0) // Reset to 0
this.source.setOffset(this.modifier, this.attack) // Attack
const timeoutId = setTimeout(() => {
this.source.setOffset(sustainVal, this.decay) // Decay
}, (this.attack * 1000))
this.timeoutIds.push(timeoutId)
} else if (this.decay) {
this.source.setOffset(this.modifier) // Reset to max
this.source.setOffset(sustainVal, this.decay) // Decay
} else if (this.sustain) {
this.source.setOffset(sustainVal)
}
}
/**
* Triggers the release of the envelope.
*/
triggerRelease = () => {
this._clearTimeouts()
this.source.setOffset(0, this.release) // Release
}
/**
* Triggers an instant stop of the envelope.
*/
triggerStop = () => {
this._clearTimeouts()
this.source.setOffset(0)
}
// --- Private Methods ---
_clearTimeouts = () => this.timeoutIds.forEach((id) => clearTimeout(id))
}
export default Envelope