[TypeScript][Chart.js] Play my own voice 3
Tags: typescript, chartjs
Published: 2021-09-12 22:25:59

## Intro
This time, I will try "WaveShaperNode" to use distortion.

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

### Environments
* Node.js ver.16.8.0
* TypeScript ver.4.3.5
* Webpack ver.5.42.0
* ts-loader ver.9.2.3
* webpack-cli ver.4.7.2
* chart.js ver.3.5.1

## Use WaveShaperNode
The distortion is made by clipping the wave.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ucg4sg876crxadla2f9w.png)

* [Recording Basics: Distortion and Saturation](https://hub.yamaha.com/proaudio/recording/distortion-and-saturation/)

To intentionally cause clipping, I can use "WaveShaperNode".

#### index.html
```html
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Audio sample</title>
        <meta charset="utf-8">
    </head>
    <body>
        <div>
            <canvas id="curve_view" style="width: 30vw; height: 30vh;"></canvas>
        </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 distortion = audioContext.createWaveShaper();
    distortion.curve = makeDistortionCurve(50);
    distortion.oversample = '4x';

    audioSourceNode
        .connect(distortion)
        .connect(audioContext.destination);
}
function makeDistortionCurve(amount: number): Float32Array {
    const sample = 44100;
    const curve = new Float32Array(sample);
    const deg = Math.PI / 180;

    const viewCanvas = document.getElementById('curve_view') as HTMLCanvasElement;
    for (let i = 0; i < sample; ++i ) {
        const x = i * 2 / sample - 1;
        curve[i] = ( 3 + amount ) * x * 20 * deg / ( Math.PI + amount * Math.abs(x));
    }
    return curve;
}
```

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

## Curve
WaveShaperNode has two properties.
They are "curve" and "oversample".

"oversample" is for anti-aliasing for drawing the curve.

So I can think the most important property for distortion is "curve".

Like the code sample above, the curve is expressed by Float32Array.
Because I want to display the curve visually, I use Chart.js. 

### Draw the curve
I add Chart.js and some code like last time.

* [【TypeScript】Try Chart.js](https://dev.to/masanori_msl/typescript-try-chart-js-23kj)

#### main.page.ts
```ts
import { Chart, ChartConfiguration, ChartTypeRegistry } from "chart.js";

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 distortion = audioContext.createWaveShaper();

    distortion.curve = makeDistortionCurve(50);
    distortion.oversample = '4x';

    audioSourceNode
        .connect(distortion)
        .connect(audioContext.destination);
}
function makeDistortionCurve(amount: number): Float32Array {
    const sample = 44100;
    const curve = new Float32Array(sample);
    const deg = Math.PI / 180;

    for (let i = 0; i < sample; ++i ) {
        const x = i * 2 / sample - 1;
        curve[i] = ( 3 + amount ) * x * 20 * deg / ( Math.PI + amount * Math.abs(x));
    } 
    drawSample(curve);
    return curve;
}
function drawSample(values: Float32Array) {
    const viewCanvas = document.getElementById('curve_view') as HTMLCanvasElement;
    
    const labels: string[] = [];
    for(const v of values) {
        labels.push(v.toString());
    }
      const data = {
        labels: labels,
        datasets: [{
          label: 'curve',
          backgroundColor: 'rgb(255, 99, 132)',
          borderColor: 'rgb(255, 99, 132)',
          data: [...values],
        }]
      };
      const config: ChartConfiguration<keyof ChartTypeRegistry> = {
        type: 'line',
        data: data,
        options: {}
      };
    new Chart(canvas, config);
}
```

But I got an error.

```
ncaught (in promise) Error: "line" is not a registered controller.
```

After all, I added "registerables".

#### main.page.ts
```ts
import { Chart, ChartConfiguration, ChartTypeRegistry, registerables } from "chart.js";

let audioContext: AudioContext;

export async function init(): Promise<void> {
    Chart.register(...registerables);
...
}
```

* [Chart.js core.js:6162 ERROR Error: "line" is not a registered controller - Stack Overflow](https://stackoverflow.com/questions/67060070/chart-js-core-js6162-error-error-line-is-not-a-registered-controller)
* [Chart.js - GitHub](https://github.com/chartjs/Chart.js)

## Curve shape
The result of drawing the curve like below.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlhoeiyaqghkyyk39kca.png)

I change the curve values.

### amount
If I change the "amount" value of the argument of "makeDistortionCurve" more larger, the curve will be close to a right angle and the distortion will be harder.

#### amount = 400
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hp2kh6iy2q1sy1p7nh9i.png)

If I set "0", I can hardly feel the distortion.

#### amount = 0
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzx988by28ngulpqnmfc.png)

### Range
Their range is from -1 to 1.
If I change the range more wider, the sound will be more larger.

#### main.page.ts
```ts
...
function makeDistortionCurve(amount: number): Float32Array {
    const sample = 44100;
    const curve = new Float32Array(sample);
    const deg = Math.PI / 180;

    const viewCanvas = document.getElementById('curve_view') as HTMLCanvasElement;
    for (let i = 0; i < sample; ++i ) {
        const x = i * 2 / sample - 1;
        curve[i] = (Math.PI + amount) * x / (Math.PI + amount * Math.abs(x));
    } 
    drawSample(viewCanvas, curve);
    return curve;
}
...
```

* [WaveShaper node in WebAudio - how to emulate distortion? - Stack Overflow](https://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1nbb63yq8fjt0u62l9w.png)