Spaces:
Running
Running
class RobotEffect { | |
constructor (audioContext, startTime, endTime) { | |
this.audioContext = audioContext; | |
this.input = this.audioContext.createGain(); | |
this.output = this.audioContext.createGain(); | |
this.passthrough = this.audioContext.createGain(); | |
this.effectInput = this.audioContext.createGain(); | |
this.passthrough.gain.value = 1; | |
this.effectInput.gain.value = 0; | |
this.passthrough.gain.setValueAtTime(0, startTime); | |
this.passthrough.gain.setValueAtTime(1, endTime); | |
this.effectInput.gain.setValueAtTime(1, startTime); | |
this.effectInput.gain.setValueAtTime(0, endTime); | |
// Ring modulator inspired by BBC Dalek voice | |
// http://recherche.ircam.fr/pub/dafx11/Papers/66_e.pdf | |
// https://github.com/bbc/webaudio.prototyping.bbc.co.uk | |
// > There are four parallel signal paths, two which process the | |
// > combination Vc + Vin / 2 and two which process Vc - Vin/2. | |
// > Each branch consists of a non-linearity [diode]... | |
const createDiodeNode = () => { | |
const node = this.audioContext.createWaveShaper(); | |
// Piecewise function given by (2) in Parker paper | |
const transform = (v, vb = 0.2, vl = 0.4, h = 0.65) => { | |
if (v <= vb) return 0; | |
if (v <= vl) return h * (Math.pow(v - vb, 2) / ((2 * vl) - (2 * vb))); | |
return (h * v) - (h * vl) + (h * (Math.pow(v - vb, 2) / ((2 * vl) - (2 * vb)))); | |
}; | |
// Create the waveshaper curve with the voltage transform above | |
const bufferLength = 1024; | |
const curve = new Float32Array(bufferLength); | |
for (let i = 0; i < bufferLength; i++) { | |
const voltage = (2 * (i / bufferLength)) - 1; | |
curve[i] = transform(voltage); | |
} | |
node.curve = curve; | |
return node; | |
}; | |
const oscillator = this.audioContext.createOscillator(); | |
oscillator.frequency.value = 50; | |
oscillator.start(0); | |
const vInGain = this.audioContext.createGain(); | |
vInGain.gain.value = 0.5; | |
const vInInverter1 = this.audioContext.createGain(); | |
vInInverter1.gain.value = -1; | |
const vInInverter2 = this.audioContext.createGain(); | |
vInInverter2.gain.value = -1; | |
const vInDiode1 = createDiodeNode(this.audioContext); | |
const vInDiode2 = createDiodeNode(this.audioContext); | |
const vInInverter3 = this.audioContext.createGain(); | |
vInInverter3.gain.value = -1; | |
const vcInverter1 = this.audioContext.createGain(); | |
vcInverter1.gain.value = -1; | |
const vcDiode3 = createDiodeNode(this.audioContext); | |
const vcDiode4 = createDiodeNode(this.audioContext); | |
const compressor = this.audioContext.createDynamicsCompressor(); | |
compressor.threshold.value = -5; | |
compressor.knee.value = 15; | |
compressor.ratio.value = 12; | |
compressor.attack.value = 0; | |
compressor.release.value = 0.25; | |
const biquadFilter = this.audioContext.createBiquadFilter(); | |
biquadFilter.type = 'highpass'; | |
biquadFilter.frequency.value = 1000; | |
biquadFilter.gain.value = 1.25; | |
this.input.connect(this.effectInput); | |
this.input.connect(this.passthrough); | |
this.passthrough.connect(this.output); | |
this.effectInput.connect(vcInverter1); | |
this.effectInput.connect(vcDiode4); | |
vcInverter1.connect(vcDiode3); | |
oscillator.connect(vInGain); | |
vInGain.connect(vInInverter1); | |
vInGain.connect(vcInverter1); | |
vInGain.connect(vcDiode4); | |
vInInverter1.connect(vInInverter2); | |
vInInverter1.connect(vInDiode2); | |
vInInverter2.connect(vInDiode1); | |
vInDiode1.connect(vInInverter3); | |
vInDiode2.connect(vInInverter3); | |
vInInverter3.connect(compressor); | |
vcDiode3.connect(compressor); | |
vcDiode4.connect(compressor); | |
this.effectInput.connect(biquadFilter); | |
biquadFilter.connect(compressor); | |
compressor.connect(this.output); | |
} | |
} | |
export default RobotEffect; | |