【p5.js 2021】Magenta.js の MusicVAE  を使った音作りを試す（p5.js Web Editor上で扱う）

この記事は、[2021年の p5.js のアドベントカレンダー](https://qiita.com/advent-calendar/2021/p5js) の 12日目の記事です。

11月に試して、以下の記事に書いていた「Magenta.js」に関連した話になります。
それを、今回も p5.js Web Editor上で実装して試します。

●Magenta.js の MusicRNN の basic_rnn/melody_rnn/chord_pitches_improv を試す（環境は p5.js Web Editor上） - Qiita
　https://qiita.com/youtoy/items/e3b3c26ea32dba5e8624
●機械学習・AI で音を作れる「Magenta」の JavaScript 版（Magenta.js）を p5.js Web Editor上で動かしてみる - Qiita
　https://qiita.com/youtoy/items/32eedd5c5c9280fe3f0f

## 今回の内容
11月に試した内容（上記2つの記事）では、いくつかある機械学習モデルの中で「MusicRNN」を使っていましたが、今回は、「MusicVAE」を使った音作りをやってみます。
![機械学習モデルの種類.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f29ac2c2-4dca-ecd5-b27e-9485c43b2fd2.jpeg)

具体的には、以下のページの「MusicVAE」の部分を進めていきます。

●Making music with magenta.js
　https://hello-magenta.glitch.me/#musicvae

### MusicVAE でできること
「Making music with magenta.js」のページ内の説明を見ると、以下の記載があります。
> The Music VAE implementation in @magenta/music in particular does two things: it can create new sequences (which are reconstructions or variations of the input data), or it can interpolate between two.

できることが2つあって、その 1つは「it can create new sequences (which are reconstructions or variations of the input data)」という部分、もう 1つが「it can interpolate between two」となるようです。
2つ目のほうが気になり、どうやら「利用者が特定の 2つのメロディを入力すると、その間の部分を作る」といった内容に見えます。

ちなみに、過去のお試しで使った「MusicRNN」は、「利用者が特定のメロディを入力すると、その続きを作ってくれる」というものでした。

## p5.js Web Editor上で試していく
今回の環境は、過去のお試しと同じで p5.js Web Editor　を使っていきます。
事前準備などは、過去に書いた記事に書いてあるとおりです。

今回は、ライブラリの読み込み等ができている前提で、sketch.js に書くプログラムをどうするかを書いていきます。

### 参照先のサイト
今回、元になるプログラムは「[Making music with magenta.js のページ]()」を見ていくのですが、必要に応じて以下も参照していきます。

- [magenta-js/music at master · magenta/magenta-js](https://github.com/magenta/magenta-js/tree/master/music)
- [MusicVAE | @magenta/music - v1.23.1](https://magenta.github.io/magenta-js/music/classes/_music_vae_model_.musicvae.html)

### 新しいシーケンスを作成
「[Making music with magenta.js のページの Creating new sequences の部分](https://hello-magenta.glitch.me/#creating-new-sequences)」にあるプログラムを見てみます。

![新しいシーケンスを作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f1d3cebe-8a35-b3f8-0039-303126012642.jpeg)

上記の [「MusicVAE | @magenta/music - v1.23.1」の sample の部分](https://magenta.github.io/magenta-js/music/classes/_music_vae_model_.musicvae.html#sample)を見てみると、参照したプログラムの sample の引数は「numSamples と temperature」となっており、以下の仕様となっているようです。
![パラメータ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/41c5395f-541e-0ee2-673b-11b66e6e51d3.jpeg)

元のプログラムで　1 が指定されていた numSamples を 1 から 3 の整数にして、複数のサンプルが得られた場合は、それをつなげたものを作って再生するようにしました。
得られた複数のサンプルをつなげる処理は、この後に試す「2つのシーケンス　の間を埋める」という場合の処理を見ていった時に出てきた `mm.sequences.concatenate()` というものを使いました。

また、temperature は、デフォルト値やそれ以外の値をいくつか設定するようにしてみました。
（この 2つ目の「temperature」の値について、細かな意味は、今回は把握しきれてないのですが...）

```javascript
let music_vae, vaePlayer;

function preload() {
  // Initialize the model.
  music_vae = new mm.MusicVAE(
    "https://storage.googleapis.com/magentadata/js/checkpoints/music_vae/mel_4bar_small_q2"
  );
  music_vae.initialize();

  // Create a player to play the sampled sequence.
  vaePlayer = new mm.Player();
}

function setup() {
  createCanvas(400, 400);
  background(220);
}

function draw() {}

function keyPressed() {
  switch (key) {
    case "a":
      playVAE(3, 0.5);
      break;
    case "s":
      playVAE(3, 3);
      break;
    case "d":
      playVAE(1, 1);
      break;
    case "f":
      playVAE(2, 1.5);
      break;
  }
}

function playVAE(numSamples, temperature) {
  if (vaePlayer.isPlaying()) {
    vaePlayer.stop();
    return;
  }
  music_vae.sample(numSamples, temperature).then((sample) => {
    console.log(sample, sample.length);
    const concatenated = mm.sequences.concatenate(sample);
    vaePlayer.start(concatenated);
  });
}
```

とりあえず、これでパラメータを変えた 4パターンを試すことができるようになりました。

### 2つのシーケンス　の間を埋める
次は、「[Making music with magenta.js のページの Interpolating between two sequences の部分](https://hello-magenta.glitch.me/#interpolating-between-two-sequences)」を進めていきます。

元のプログラムは以下となっています。
![2つのシーケンス　の間を埋める](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f60819d8-c16e-9e61-1057-9d71aada051b.jpeg)

基本的にはサンプル通りの処理ですあｇ、違いとして、自分が試してみるものは、「きらきら星」と「チューリップ」のメロディをつなげるようにしてみます。

```javascript
let music_vae, vaePlayer;

const TWINKLE_TWINKLE = {
  notes: [
    { pitch: 60, startTime: 0.0, endTime: 0.5 },
    { pitch: 60, startTime: 0.5, endTime: 1.0 },
    { pitch: 67, startTime: 1.0, endTime: 1.5 },
    { pitch: 67, startTime: 1.5, endTime: 2.0 },
    { pitch: 69, startTime: 2.0, endTime: 2.5 },
    { pitch: 69, startTime: 2.5, endTime: 3.0 },
    { pitch: 67, startTime: 3.0, endTime: 4.0 },
    { pitch: 65, startTime: 4.0, endTime: 4.5 },
    { pitch: 65, startTime: 4.5, endTime: 5.0 },
    { pitch: 64, startTime: 5.0, endTime: 5.5 },
    { pitch: 64, startTime: 5.5, endTime: 6.0 },
    { pitch: 62, startTime: 6.0, endTime: 6.5 },
    { pitch: 62, startTime: 6.5, endTime: 7.0 },
    { pitch: 60, startTime: 7.0, endTime: 8.0 },
  ],
  totalTime: 8,
};

const TULIP = {
  notes: [
    { pitch: 60, startTime: 0.0, endTime: 0.5 },
    { pitch: 62, startTime: 0.5, endTime: 1.0 },
    { pitch: 64, startTime: 1.0, endTime: 2.0 },
    { pitch: 60, startTime: 2.0, endTime: 2.5 },
    { pitch: 62, startTime: 2.5, endTime: 3.0 },
    { pitch: 64, startTime: 3.0, endTime: 4.0 },
    { pitch: 67, startTime: 4.0, endTime: 4.5 },
    { pitch: 64, startTime: 4.5, endTime: 5.0 },
    { pitch: 62, startTime: 5.0, endTime: 5.5 },
    { pitch: 60, startTime: 5.5, endTime: 6.0 },
    { pitch: 62, startTime: 6.0, endTime: 6.5 },
    { pitch: 64, startTime: 6.5, endTime: 7.0 },
    { pitch: 62, startTime: 7.0, endTime: 7.5 },
  ],
  totalTime: 7.5,
};

function preload() {
  // Initialize the model.
  music_vae = new mm.MusicVAE(
    "https://storage.googleapis.com/magentadata/js/checkpoints/music_vae/mel_4bar_small_q2"
  );
  music_vae.initialize();

  // Create a player to play the sampled sequence.
  vaePlayer = new mm.Player();
}

function setup() {
  createCanvas(400, 400);
  background(220);
}

function draw() {}

function keyPressed() {
  switch (key) {
    case "a":
      playInterpolation();
      break;
  }
}

function playInterpolation() {
  if (vaePlayer.isPlaying()) {
    vaePlayer.stop();
    return;
  }
  // Music VAE requires quantized melodies, so quantize them first.
  const star = mm.sequences.quantizeNoteSequence(TWINKLE_TWINKLE, 4);
  const tulip = mm.sequences.quantizeNoteSequence(TULIP, 4);
  music_vae.interpolate([star, tulip], 4).then((sample) => {
    const concatenated = mm.sequences.concatenate(sample);
    vaePlayer.start(concatenated);
  });
}
```

試してみると、2つのメロディの間に、生成されたメロディが流れるようになりました。
こちらは、指定する 2つのメロディは変更しない場合に、パラメータをどのように変えると色々なメロディの変化を起こせるか、仕様が十分に理解できておらず、引き続き、仕様の確認やお試しをやれればと思います。

## おわりに
今回、Magenta.js を p5.js　のデフォルト処理とが競合しないように動かすことと、p5.js のキー操作処理関数との組み合わせを試す、ということができました。

ただ、p5.js絡みの要素が少ないので、例えば「生成された音の結果によって、動的にキャンバスに何かが描画される」とか、そういうこともできればと思います。