以下の記事に書いた 「Magenta」の JavaScript 版である Magenta.js のお試しの話の続きです。
●機械学習・AI で音を作れる「Magenta」の JavaScript 版(Magenta.js)を p5.js Web Editor上で動かしてみる - Qiita
https://qiita.com/youtoy/items/32eedd5c5c9280fe3f0f
今回は、上記の記事でも試した「MusicRNN」の話です。
ちなみに MusicRNN は、「利用者が特定のメロディを入力すると、その続きを作ってくれる」という機械学習モデルになっているようです。
上記の記事の中では、公式の手順を試す中で MusicRNN の「basic_rnn」を使う部分がありました。
それ以外に、「melody_rnn」や「chord_pitches_improv」といったものもあるようだったので、今回はそれらを試すプログラムを作っていきます。
なお、上記の画像の中で「drum_kit_rnn」というのがありますが、これは別の機会に試していければと思います。
【追記】 音が鳴る様子の動画を追加しました
どんな音が作られるのか、この記事で書いていたプログラムを実行しているところを、キャプチャしたものをツイートしました。
前に以下の記事を書いていた件、どんな音がなるのか分からない状態だったので、音が作られる様子を動画にしました。
— you (@youtoy) December 12, 2021
●Magenta.js の MusicRNN の basic_rnn/melody_rnn/chord_pitches_improv を試す(環境は p5.js Web Editor上) - Qiita
https://t.co/MbvbL7WrOG#p5js pic.twitter.com/sQmFMUWAyd
音を作る
p5.js Web Editor の下準備
前回の記事の手順と同じように、まずは p5.js Web Editor の index.html でライブラリを読み込みます(前回と同じ、以下を読み込み)。
<script src="https://cdn.jsdelivr.net/npm/@magenta/music@^1.0.0"></script>
次に、sketch.js を作っていきます。
JavaScript のプログラム
今回、入力として与えるデータを 2つ増やしてみました(「チューリップ」と「かえるの歌」を用意してみました)。
また preload()
の部分で「basic_rnn/melody_rnn/chord_pitches_improv」の 3つをそれぞれ読み込んでいます。
本来は、もう少しきれいに書けそうな内容になってますが、とりあえず版ということで...
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,
};
const FROG = {
notes: [
{ pitch: 60, startTime: 0.0, endTime: 0.5 },
{ pitch: 62, startTime: 0.5, endTime: 1.0 },
{ pitch: 64, startTime: 1.0, endTime: 1.5 },
{ pitch: 65, startTime: 1.5, endTime: 2.0 },
{ pitch: 64, startTime: 2.0, endTime: 2.5 },
{ pitch: 62, startTime: 2.5, endTime: 3.0 },
{ pitch: 60, startTime: 3.0, endTime: 4.0 },
],
totalTime: 4,
};
let player;
function preload() {
music_rnn_melody_rnn = new mm.MusicRNN(
"https://storage.googleapis.com/magentadata/js/checkpoints/music_rnn/melody_rnn"
);
music_rnn_melody_rnn.initialize();
music_rnn_chord_pitches_improv = new mm.MusicRNN(
"https://storage.googleapis.com/magentadata/js/checkpoints/music_rnn/chord_pitches_improv"
);
music_rnn_chord_pitches_improv.initialize();
music_rnn_basic_rnn = new mm.MusicRNN(
"https://storage.googleapis.com/magentadata/js/checkpoints/music_rnn/basic_rnn"
);
music_rnn_basic_rnn.initialize();
}
function setup() {
createCanvas(400, 400);
background(220);
player = new mm.Player();
}
function draw() {}
function keyPressed() {
if (!player.isPlaying()) {
let rnn_steps, rnn_temperature;
switch (key) {
case "q":
player.start(TWINKLE_TWINKLE);
break;
case "w":
makeMusicBasic(TWINKLE_TWINKLE, 4, 16 * 2, 1.5);
break;
case "e":
makeMusicBasic(TWINKLE_TWINKLE, 4, 16 * 8, 1.5);
break;
case "r":
makeMusicMelody(TWINKLE_TWINKLE, 4, 16 * 2, 1.5);
break;
case "t":
makeMusicChord(TWINKLE_TWINKLE, 4, 16 * 2, 1.5, ["G", "Em", "C", "D"]);
break;
case "a":
player.start(TULIP);
break;
case "s":
makeMusicBasic(TULIP, 4, 16 * 2, 1.5);
break;
case "z":
player.start(FROG);
break;
case "x":
makeMusicBasic(FROG, 4, 16 * 2, 1.5);
break;
}
} else if (player.isPlaying()) {
switch (key) {
case " ":
console.log("Stop");
player.stop();
break;
}
}
}
function makeMusicBasic(SONG, steps_per_quarter, rnn_steps, rnn_temperature) {
const qns = mm.sequences.quantizeNoteSequence(SONG, steps_per_quarter);
music_rnn_basic_rnn
.continueSequence(qns, rnn_steps, rnn_temperature)
.then((sample) => player.start(sample));
}
function makeMusicMelody(SONG, steps_per_quarter, rnn_steps, rnn_temperature) {
const qns = mm.sequences.quantizeNoteSequence(SONG, steps_per_quarter);
music_rnn_melody_rnn
.continueSequence(qns, rnn_steps, rnn_temperature)
.then((sample) => player.start(sample));
}
function makeMusicChord(
SONG,
steps_per_quarter,
rnn_steps,
rnn_temperature,
chord_progression
) {
const qns = mm.sequences.quantizeNoteSequence(SONG, steps_per_quarter);
music_rnn_chord_pitches_improv
.continueSequence(qns, rnn_steps, rnn_temperature, chord_progression)
.then((sample) => {
console.log(sample);
player.start(sample);
});
}
上記を実行して、あとは特定のキーを押下していけば、以下の内容を試せます。
- 入力をそのまま実行
- きらきら星、チューリップ、かえるの歌 の3つ
- 音を生成1: basic_rnn
- 3つの入力のそれぞれを、32ステップで指定(他のパラメータは前回の記事と同様)
- きらきら星について、128ステップで指定(他のパラメータは前回の記事と同様)
- 音を生成2・3: melody_rnn/chord_pitches_improv
- きらきら星について、32ステップで指定(他のパラメータは前回の記事と同様)
上記を試してみると、それぞれで異なる音が鳴るのが確認できました。
補足: chord_pitches_improv を使った場合
chord_pitches_improv を使った場合だけ、 continueSequence()
でのパラメータ指定が他と異なります。
公式情報の continueSequence の部分で、以下の画像中に出てきている「chordProgression」の指定をしています。
これは、basic_rnn や melody_rnn を使った場合と同じ処理内容を、chord_pitches_improv を使った場合にも用いてみたところ、エラーメッセージで chordProgression に関するメッセージが出たためです。
今回のプログラム中では、公式情報の例に記載されている「["G", "Em", "C", "D"]」を指定してみました。
余談
上記の補足の話の中に出てくる「tonalパッケージ」というのも気になりました。
●tonaljs/tonal: A functional music theory library for Javascript
https://github.com/tonaljs/tonal
ドキュメントをざっくり見てみたところ、音系の処理を扱うのに便利そうでした。