<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@magenta/music@0.0.10">
</script>
</head>
<body>
<div id='message'>Loading model...</div>
<div id='chords'>
<table><tr>
<td><input id='chord1' type='text' value='C'></td>
<td><input id='chord2' type='text' value='G'></td>
<td><input id='chord3' type='text' value='Am'></td>
<td><input id='chord4' type='text' value='F'></td>
</tr></table>
</div>
<br>
<input id='play' type='button' value='Play' disabled>
</body>
</html>
body {text-align:center; color:white; background-color:black; font-size:10px; font-family:sans-serif; padding-top:50px}
div.chords {width=100%}
table {margin-left:auto; margin-right:auto; width:250px}
input {text-align:center; font-size:12px; width:50px}
#play {font-size:16px; width:75px; color:black}
#play:disabled {color:dimgray}
td {padding:5px}
// Number of steps to play each chord.
STEPS_PER_CHORD = 8;
STEPS_PER_PROG = 4 * STEPS_PER_CHORD;
// Number of times to repeat chord progression.
NUM_REPS = 4;
// Set up Improv RNN model and player.
const model = new mm.MusicRNN('https://storage.googleapis.com/magentadata/js/checkpoints/music_rnn/chord_pitches_improv');
const player = new mm.Player();
var playing = false;
// Current chords being played.
var currentChords = undefined;
// Sample over chord progression.
const playOnce = () => {
const chords = currentChords;
// Prime with root note of the first chord.
const root = mm.chords.ChordSymbols.root(chords[0]);
const seq = {
quantizationInfo: {stepsPerQuarter: 4},
notes: [],
totalQuantizedSteps: 1
};
document.getElementById('message').innerText = 'Improvising over: ' + chords;
model.continueSequence(seq, STEPS_PER_PROG + (NUM_REPS-1)*STEPS_PER_PROG - 1, 0.9, chords)
.then((contSeq) => {
// Add the continuation to the original.
contSeq.notes.forEach((note) => {
note.quantizedStartStep += 1;
note.quantizedEndStep += 1;
seq.notes.push(note);
});
const roots = chords.map(mm.chords.ChordSymbols.root);
for (var i=0; i<NUM_REPS; i++) {
// Add the bass progression.
seq.notes.push({
instrument: 1,
program: 32,
pitch: 36 + roots[0],
quantizedStartStep: i*STEPS_PER_PROG,
quantizedEndStep: i*STEPS_PER_PROG + STEPS_PER_CHORD
});
seq.notes.push({
instrument: 1,
program: 32,
pitch: 36 + roots[1],
quantizedStartStep: i*STEPS_PER_PROG + STEPS_PER_CHORD,
quantizedEndStep: i*STEPS_PER_PROG + 2*STEPS_PER_CHORD
});
seq.notes.push({
instrument: 1,
program: 32,
pitch: 36 + roots[2],
quantizedStartStep: i*STEPS_PER_PROG + 2*STEPS_PER_CHORD,
quantizedEndStep: i*STEPS_PER_PROG + 3*STEPS_PER_CHORD
});
seq.notes.push({
instrument: 1,
program: 32,
pitch: 36 + roots[3],
quantizedStartStep: i*STEPS_PER_PROG + 3*STEPS_PER_CHORD,
quantizedEndStep: i*STEPS_PER_PROG + 4*STEPS_PER_CHORD
});
}
// Set total sequence length.
seq.totalQuantizedSteps = STEPS_PER_PROG * NUM_REPS;
// Play it!
player.start(seq, 120).then(() => {
playing = false;
document.getElementById('message').innerText = 'Change chords and play again!';
checkChords();
});
})
}
// Check chords for validity and highlight invalid chords.
const checkChords = () => {
const chords = [
document.getElementById('chord1').value,
document.getElementById('chord2').value,
document.getElementById('chord3').value,
document.getElementById('chord4').value
];
const isGood = (chord) => {
if (!chord) {
return false;
}
try {
mm.chords.ChordSymbols.pitches(chord);
return true;
}
catch(e) {
return false;
}
}
var allGood = true;
if (isGood(chords[0])) {
document.getElementById('chord1').style.color = 'black';
} else {
document.getElementById('chord1').style.color = 'red';
allGood = false;
}
if (isGood(chords[1])) {
document.getElementById('chord2').style.color = 'black';
} else {
document.getElementById('chord2').style.color = 'red';
allGood = false;
}
if (isGood(chords[2])) {
document.getElementById('chord3').style.color = 'black';
} else {
document.getElementById('chord3').style.color = 'red';
allGood = false;
}
if (isGood(chords[3])) {
document.getElementById('chord4').style.color = 'black';
} else {
document.getElementById('chord4').style.color = 'red';
allGood = false;
}
var changed = false;
if (currentChords) {
if (chords[0] !== currentChords[0]) {changed = true;}
if (chords[1] !== currentChords[1]) {changed = true;}
if (chords[2] !== currentChords[2]) {changed = true;}
if (chords[3] !== currentChords[3]) {changed = true;}
}
else {changed = true;}
document.getElementById('play').disabled = !allGood || (!changed && playing);
}
// Initialize model then start playing.
model.initialize().then(() => {
document.getElementById('message').innerText = 'Done loading model.'
document.getElementById('play').disabled = false;
});
// Play when play button is clicked.
document.getElementById('play').onclick = () => {
playing = true;
document.getElementById('play').disabled = true;
currentChords = [
document.getElementById('chord1').value,
document.getElementById('chord2').value,
document.getElementById('chord3').value,
document.getElementById('chord4').value
];
mm.Player.tone.context.resume();
player.stop();
playOnce();
}
// Check chords for validity when changed.
document.getElementById('chord1').oninput = checkChords;
document.getElementById('chord2').oninput = checkChords;
document.getElementById('chord3').oninput = checkChords;
document.getElementById('chord4').oninput = checkChords;
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.