[TypeScript] Play my own voice 2
Tags: typescript, webaudioapi
Published: 2021-09-05 14:26:40

## Intro
This time, I try "BiquadFilterNode" to control tone.

* [[TypeScript] Play my own voice](https://dev.to/masanori_msl/typescript-play-my-own-voice-58i4)

## Sample code
#### index.html
```html
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>xlsx sample</title>
        <meta charset="utf-8">
    </head>
    <body>
        <div>
            <div>EQ</div>
            <select id="eq_input">
                <option>allpass</option>
                <option>bandpass</option>
                <option>highpass</option>
                <option>highshelf</option>
                <option>lowpass</option>
                <option>lowshelf</option>
                <option>notch</option>
                <option>peaking</option>
            </select>
            <div>Frequency</div>
            <select id="eq_frequency">
                <option>10</option>
                <option>100</option>
                <option>350</option>
                <option>1000</option>
                <option>5000</option>
                <option>10000</option>
                <option>22050</option>
            </select>
            <div>Gain</div>
            <select id="eq_gain">
                <option>-40</option>
                <option>-20</option>
                <option>-10</option>
                <option>0</option>
                <option>10</option>
                <option>20</option>
                <option>40</option>
            </select>
            <div>Detune</div>
            <select id="eq_detune">
                <option>0</option>
                <option>10</option>
                <option>100</option>
                <option>350</option>
                <option>1000</option>
                <option>5000</option>
                <option>10000</option>
                <option>22050</option>
            </select>
            <div>Q</div>
            <select id="eq_q">
                <option>0</option>
                <option>0.0001</option>
                <option>0.001</option>
                <option>0.01</option>
                <option>0.1</option>
                <option>1</option>
                <option>10</option>
                <option>100</option>
                <option>1000</option>
            </select>
        </div>
        <script src="./js/main.page.js"></script>
        <script>Page.init();</script>
    </body>
</html>
```
#### main.page.ts
```ts
let audioContext: AudioContext;

export async function init(): Promise<void> {
    const medias = await navigator.mediaDevices.getUserMedia({
        video: false,
        audio: true,
    });
    
    audioContext = new AudioContext();
    const audioSourceNode = audioContext.createMediaStreamSource(medias);
    const biquadFilter = audioContext.createBiquadFilter();

    audioSourceNode
        .connect(biquadFilter)
        .connect(audioContext.destination);

    const eqSelect = document.getElementById('eq_input') as HTMLSelectElement;
    eqSelect.onchange = () => {
        const eqValue = eqSelect.options[eqSelect.selectedIndex]?.text;
        if(eqValue == null) {
            return;
        }
        switch(eqValue) {
            case 'allpass':
                biquadFilter.type = 'allpass';
                break;
            case 'bandpass':
                biquadFilter.type = 'bandpass';
                break;
            case 'highpass':
                biquadFilter.type = 'highpass';
                break;
            case 'highshelf':
                biquadFilter.type = 'highshelf';
                break;
            case 'lowpass':
                biquadFilter.type = 'lowpass';
                break;
            case 'lowshelf':
                biquadFilter.type = 'lowshelf';
                break;
            case 'notch':
                biquadFilter.type = 'notch';
                break;
            case 'peaking':
                biquadFilter.type = 'peaking';
                break;
        }
    };
    const eqFrequencySelect = document.getElementById('eq_frequency') as HTMLSelectElement;
    eqFrequencySelect.onchange = () => {
        const eqFrequencyText = eqFrequencySelect.options[eqFrequencySelect.selectedIndex]?.text;
        if(eqFrequencyText == null) {
            return;
        }
        const eqFrequency = parseInt(eqFrequencyText);
        if(isNaN(eqFrequency)) {
            return;
        }
        biquadFilter.frequency.setValueAtTime(eqFrequency, audioContext.currentTime);
    };
    const eqGainSelect = document.getElementById('eq_gain') as HTMLSelectElement;
    eqGainSelect.onchange = () => {
        const eqGainText = eqGainSelect.options[eqGainSelect.selectedIndex]?.text;
        if(eqGainText == null) {
            return;
        }
        const eqGain = parseInt(eqGainText);
        if(isNaN(eqGain)) {
            return;
        }
        biquadFilter.gain.setValueAtTime(eqGain, audioContext.currentTime);
    };
    const eqDetuneSelect = document.getElementById('eq_detune') as HTMLSelectElement;
    eqDetuneSelect.onchange = () => {
        const eqDetuneText = eqDetuneSelect.options[eqDetuneSelect.selectedIndex]?.text;
        if(eqDetuneText == null) {
            return;
        }
        const eqDetune = parseInt(eqDetuneText);
        if(isNaN(eqDetune)) {
            return;
        }
        biquadFilter.detune.setValueAtTime(eqDetune, audioContext.currentTime);
    };
    const eqQSelect = document.getElementById('eq_q') as HTMLSelectElement;
    eqQSelect.onchange = () => {
        const eqQText = eqQSelect.options[eqQSelect.selectedIndex]?.text;
        if(eqQText == null) {
            return;
        }
        const eqQ = parseInt(eqQText);
        if(isNaN(eqQ)) {
            return;
        }
        biquadFilter.Q.setValueAtTime(eqQ, audioContext.currentTime);
    };
}
```
## type
"BiquadFilterNode" has preset filters. I can use them by "BiquadFilterNode.type".

* allpass
* bandpass
* highpass
* highshelf
* lowpass
* lowshelf
* notch
* peaking

* [BiquadFilterNode - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode)
* [Web Audio API](https://webaudio.github.io/web-audio-api/#BiquadFilterNode)

## Modify filters
I can modify the selected filter by "gain", "frequency", "detune", "Q".

### gain
I can control gain of filter by "BiquadFilterNode.gain".
Only "highshelf", "lowshelf", "peaking" of "BiquadFilterNode.type" can use.
The lowest value is -40(dB) and the highest one is 40(dB).

* [BiquadFilterNode.gain - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode/gain)

### frequency, detune
"frequency", "detune" are used to compute frequency of the filter.

```
computedFrequency(t) = frequency(t) * pow(2, detune(t) / 1200)
```

* [Web Audio API #BiquadFilterNode](https://webaudio.github.io/web-audio-api/#BiquadFilterNode)
* [BiquadFilterNode.frequency - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode/frequency)
* [BiquadFilterNode.detune - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode/detune)

Because some types emphasize specific frequency by default, I can't hear the sound when I set specific values into them.

|type|the value what I can't hear the sound|
|-|-|
|bandpass|10|
|highpass|22050|
|lowpass|10|

I can't feel the differences when I change the values with "highshelf", "lowshelf", "peaking".

### Q
This express Quality factor.
I haven't understood what is Quality factor.

So maybe I will wrtite more about it later.

The max value and min value are different per types.
For example, when I use "bandpass" and set Q value as 100, I can't hear the sound.
When I use "highpass" and set Q value as 100, I hear some noize like howling.

* [Web Audio API #dom-biquadfilternode-detune](https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-detune)
* [BiquadFilterNode.Q - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode/Q)