機械学習・AI で音を作れる「Magenta」の JavaScript 版（Magenta.js）を p5.js Web Editor上で動かしてみる

公式ページで「Make music and art using machine learning」と書かれているように、機械学習で音楽・アートを作成できる Magenta の話です。
今どきの言い方だと、「AIで作曲」といった表現がされそうな所でしょうか。

その Magenta の JavaScript版（Magenta.js）を p5.js Web Editor上でまずは軽く動かしてみる、という話です。

## Magenta の JavaScript版について
### 公式ページ
Magenta は、Googleさんが提供している仕組みで、公式ページ等は以下があります。

●Magenta
　https://magenta.tensorflow.org/
![Magenta.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d29811f1-9fdd-de80-e8b6-36878899464a.jpeg)

●Magenta – Google Research
　https://research.google/teams/brain/magenta/
![Magenta_–_Google_Research.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f20028d7-886b-5cc2-7a9f-619e13a181a1.jpeg)

### Python版と JavaScript版
上記の 1つ目のページを少し下に移動していくと、以下のような部分があります。
![Magentaの各言語用の仕組み.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/849d1d31-bc32-fb26-884a-bf54a9aaf92c.jpeg)

利用可能な言語として、Python と JavaScript があり、JavaScript版のほうは「Magenta.js」というライブラリになるようです。

上記の部分の Magenta.js のほうで、「Try it live」を選んで進めていきます。

### JavaScript版を試す
そうすると、以下のページに遷移します。

●Making music with magenta.js
　https://hello-magenta.glitch.me/
![Making_music_with_magenta_js.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4c120409-16a1-2a4e-52a8-199498e360cc.jpeg)

この中の、以下の内容を p5.js Web Editor上で使えるようにしていきます。

- Step 1: Making sounds with your browser
   - Playing a NoteSequence
- Step 2: Using Machine Learning to make music
   - MusicRNN

#### p5.js Web Editor での下準備
まずは、p5.js Web Editor の index.html でライブラリを読み込みます。
「Step 0: First things first!」という部分を見ると、以下を読み込むようにと書いてあるので、その通り進めます。

```html
<script src="https://cdn.jsdelivr.net/npm/@magenta/music@^1.0.0"></script>
```

次に、sketch.js を作っていきます。

#### プログラムを書いていく
上で「今回試す部分」と書いていたところを、1つのプログラムでまとめて動かせるようにします。
具体的には、以下のとおりです。

```javascript
// ●Making music with magenta.js  https: hello-magenta.glitch.me/

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,
};

let player, player2;

function preload() {
  player1 = new mm.Player();
  player2 = new mm.SoundFontPlayer(
    "https://storage.googleapis.com/magentadata/js/soundfonts/sgm_plus"
  );

  music_rnn = new mm.MusicRNN(
    "https://storage.googleapis.com/magentadata/js/checkpoints/music_rnn/basic_rnn"
  );
  music_rnn.initialize();
}

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

function draw() {}

function keyPressed() {
  if (!player1.isPlaying() && !player2.isPlaying()) {
    switch (key) {
      case "a":
        player1.start(TWINKLE_TWINKLE);
        break;
      case "s":
        player1.setTempo(200);
        player1.start(TWINKLE_TWINKLE);
        break;
      case "d":
        player2.start(TWINKLE_TWINKLE);
        break;
      case "z":
        const rnn_steps = 20;
        const rnn_temperature = 1.5;
        const qns = mm.sequences.quantizeNoteSequence(TWINKLE_TWINKLE, 4);
        music_rnn
          .continueSequence(qns, rnn_steps, rnn_temperature)
          .then((sample) => player1.start(sample));
        break;
    }
  }
}
```

`player2` と `music_rnn` は、外部からファイルを読み込む処理になるようなので、「preload()」の中に入れることにしました。
`player` も「preload()」の中に入っているのは、それらと一緒の場所に書いた方が分かりやすいというだけで、「setup()」の中で読み込んでも問題はないです。

それと `player` 、 `player2` は同時には使えないようだったので（音を出す仕組みのところで何やら競合するような感じらしい？）、どちらか 1つで音を鳴らしている途中は、別の音を鳴らす処理を実行させないようにしました。

あとは、プログラムを実行して特定のキーを押下するだけです。
試してみると、きらきら星が流れたり、それをベースに生成された音が流れたりするのが確認できました。

#### 補足1： 音を作る部分の補足
きらきら星のメロディをベースに音を作る部分は、「MusicRNN」を使っています。

公式手順の中で、以下のように説明されているものです。
![MusicRNN.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/8f2de294-c93f-a140-0a31-1590bdac80e5.jpeg)

ディープラーニングの話なんかで、時系列データ解析等の話で良く出てくる LSTM（Long short-term memory）をベースにした機械学習モデルになるようです。
また、できることとしては、「利用者が特定のメロディを入力すると、その続きを作ってくれる」というものになるようです。

#### 補足2： quantizeNoteSequence について
MusicRNN の処理の関連で出てくる「quantizeNoteSequence」が気になったので、公式情報を見てみました。

以下は、[「"core/sequences"」の中の「quantizeNoteSequence」について書かれた部分](https://magenta.github.io/magenta-js/music/modules/_core_sequences_.html#quantizenotesequence)のキャプチャです。
![quantizeNoteSequence.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4683747f-5e47-336a-2755-518a8c81313c.jpeg)

今回の記事の中で試した処理の前後の内容を出力すると、以下のようになりました（一部のみ抜粋）。

＞ 入力
![元.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/40862f27-bd9b-2bd2-993f-c81c5f0db830.jpeg)

＞ 出力
![出力後.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/78386116-b5cc-b870-9868-f12988475508.jpeg)

こちらについて MusicRNN の出力にどういう影響があるのかは、まだ調査中です。

## 【追記】 basic_rnn 以外も試してみました
●Magenta.js の MusicRNN の basic_rnn/melody_rnn/chord_pitches_improv を試す（環境は p5.js Web Editor上） - Qiita
　https://qiita.com/youtoy/items/e3b3c26ea32dba5e8624

## 【追記2】 記事で引用いただきました
「昔のゲームのBGMっぽいものを自動生成」という、自分の興味のど真ん中を打ち抜かれるようなタイトルで読み進めていたら、以下の記事が詳しい」と引用いただいていました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Magenta.jsを使ったゲームBGMの自動生成について書いた / <a href="https://t.co/rTlImYEdp3">https://t.co/rTlImYEdp3</a></p>&mdash; ABA (@abagames) <a href="https://twitter.com/abagames/status/1485556853782507526?ref_src=twsrc%5Etfw">January 24, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

あと、記事で出てきてたこちらを初めて知りました。

●Super Mario Melodies «
　http://www.losdoggies.com/archives/1302