class Synth { // SYNTH VOICE CONSTRUCTOR // Variables? constructor(audio, type, env = { a: 0.2, r: 0.5, }) { this.audio = audio this.t = this.audio.currentTime this.env = { a: env.a, r: env.r, } // PRIMARY GAIN this.gain = this.audio.createGain() this.gain.gain.value = 0 // PRIMARY VOICE this.primVoice = this.audio.createOscillator() this.primVoice.type = typeof type === 'string' ? type : 'sine' this.primVoice.connect(this.gain) this.primVoice.start() // MOD GAIN this.modGain = this.audio.createGain() this.modGain.gain.value = 0 this.modGain.connect(this.gain) // MOD VOICE this.modVoice = this.audio.createOscillator() this.modVoice.type = 'triangle' this.modVoice.connect(this.modGain) this.modVoice.start() } // OSC -> ADSR -> GAIN noteOn(freq) { // note frequency this.freq = freq ? freq : (Math.random() * 350) + 50 this.primVoice.frequency.setValueAtTime(this.freq, this.t) this.modVoice.frequency.setValueAtTime(this.freq - 20, this.t) // clear preScheduled events this.gain.gain.cancelScheduledValues(this.audio.currentTime) // attack this.gain.gain.setTargetAtTime(0.9, this.t + this.env.a, 0.2) } noteOff() { // release this.gain.gain.setTargetAtTime(0.0, this.t + this.env.r, 1) } mod() { this.modGain.gain.cancelScheduledValues(this.audio.currentTime) // attack this.modGain.gain.setTargetAtTime(0.9, this.audio.currentTime, 0.5) // release this.modGain.gain.setTargetAtTime(0.0, this.audio.currentTime + 0.4, 0.5) } }