【p5.js】p5.soundで音を利用した表現を描画する

この記事は Qiita p5.js アドベントカレンダー23日目の記事です。

## これはなに
いろんなものを描画できるp5.jsですがたくさん[ライブラリ](https://p5js.org/libraries/)が用意されておりまして、
その中の**p5.sound**を利用して遊んでみようというものです。

## p5.sound
p5.jsのコアライブラリとして提供されている拡張機能です。
こちらを利用することで音に関する動作、情報の取得が容易にできるようになるみたい。

ということで[リファレンス](https://p5js.org/reference/#/libraries/p5.sound)をみてみるとめちゃくちゃ関数があります！
もうなんでもできそうですね。ドキドキしてきました。

## 試してみる

### ライブラリを読み込む
リファレンスにある通り、index.htmlのheadタグ内でライブラリを読み込みます。

```html
<head>
  <!-- 追加 -->
  <script src="p5.sound.js"></script> 
</head>
```

### 音を読み込んで鳴らす
リファレンスを参考に音を鳴らしてみます。
今回使う音源は私が以前作っていた音源を使用します。
よかったら聴いてください！↓
https://soundcloud.com/tenten1105/20201005a

```javascript
// setup前に音源を読み込む
function preload() {
  sound = loadSound('./20201005.mp3')
}

function setup() {
  canvas = createCanvas(600, 600);
  // canvasをクリックすると音が鳴る
  canvas.mousePressed(onClickPlay)
}

function draw() {
  background(0);
}

// 音源を鳴らす
function onClickPlay () {
  sound = sound.play()
}
```

`preload`でロードされた音源はsoundのインスタンスとして利用できるようです。

canvas内をクリックすると`sound.play`により音源が鳴り始めました！
もちろん`stop`メソッドも持っているので、これを利用すればミニプレーヤーができてしまいます。

### 音量を取得する
流れている音量を数値として取得してみましょう。
ついでに`onClickPlay`に「再生してたら音とめる」な処理も追加しました。

```javascript
function preload() {
  sound = loadSound('./20201005.mp3')
}

function setup() {
  canvas = createCanvas(600, 600);
  canvas.mousePressed(onClickPlay)
  // Amplitudeのインスタンスを生成
  amplitude = new p5.Amplitude();
}

function draw() {
  background(0);
  textSize(32);
  fill('white')
  // getLevel()で音量を取得
  const level = amplitude.getLevel()
  text(level, 100, 100)
}

function onClickPlay () {
  if(sound.isPlaying()) {  
    sound.stop()  
    return
  }
  sound.play()
}
```

![スクリーンショット 2021-12-22 1.19.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358229/7af18507-99cf-24b0-b6ae-dd2fdbb42035.png)


しっかりと音量を取得することができました！

### 音量のピークを取得する
今度は音量の最大値を取得し、最大かそうじゃないかで文字列を出しわけてみます。

```javascript
function draw() {
  background(0);

  const level = amplitude.getLevel()
  // getPeak()で読み込んだ音源の最大値を取得
  const peak = sound.getPeaks(1)
  text(level, 100, 100)
  
  textSize(32);
  fill('white')
  // テキストを出し分ける
  if(level > peak - 0.2) {
    text('音量がpeak - 0.2を超えた', 50, 50)  
  } else {
    text('音量がpeak - 0.2未満', 50, 150)
  }
}
```

![画面収録-2021-12-22-1.40.48.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358229/d0de4a79-5dae-409a-1d5a-bd15a18ecfaf.gif)

音量によって文字の出し分けをすることができました👏
このようにp5.soundを利用すれば簡単に音による制御を加えることができます。

## 簡単なビジュアライザを作ってみる
ellipseで円の半径と色が変わるビジュアライザを作成してみました。
下記リンクに遷移し、canvasをクリックすると音源の再生と描画が始まります。
https://editor.p5js.org/shibuya01055/sketches/2qa0oHKEg

```javascript
function preload() {
  sound = loadSound('./20201005.mp3')
}

function setup() {
  canvas = createCanvas(600, 600);
  canvas.mousePressed(onClickPlay)
  amplitude = new p5.Amplitude();
  colorMode(HSB)
  rowVolumeColor = color(100, 255, 255)
  highVolumeColor = color(0, 100, 100)
}

function draw() {
  background(0, 80);

  const level = amplitude.getLevel()
  const peak = sound.getPeaks(1)
  text(level, 100, 100)
  
  textSize(32)
  noStroke()
  fill(isSpecifiedMax(level, peak))
  ellipse(width / 2, height / 2, level * width)
  
  push()
  fill(100, 100, level*100)
  ellipse(width / 2, height / 2, level * width / 3)
  pop()
}

function onClickPlay () {
  if(sound.isPlaying()) {  
    sound.stop()  
    return
  }
  sound.play()
}
  
function isSpecifiedMax (level, peak) {
  if(level > peak - 0.2) {
    return highVolumeColor
  } else {
    return rowVolumeColor
  }
}
```
![画面収録-2021-12-22-2.24.03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358229/73c3784c-4c0e-d51a-e356-fe2800386bd7.gif)


## 参考
[p5.js reference - p5.sound library](https://p5js.org/reference/#/libraries/p5.sound)