File size: 4,204 Bytes
6bcb42f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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;