-
Notifications
You must be signed in to change notification settings - Fork 10
/
note_visualizer.js
101 lines (89 loc) · 3.62 KB
/
note_visualizer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* @license
* Copyright 2018 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let templ = document.createElement('template');
templ.innerHTML = `
<button id="btnPlay">Play</button>
<button id="btnDownload">Download MIDI</button>
<br>
<div class="visualizer-container" id="container">
<canvas id="canvas"></canvas>
</div>
`
// Use it in updateEverything like so:
// const visualizer = document.getElementById('visualizer');
// visualizer.stepsPerQuarter = KIND === TYPES.PERFORMANCE ? 80 : 4;
// visualizer.pixelsPerTimeStep = KIND === TYPES.PERFORMANCE ? 1 : 10;
// visualizer.displaySequence(parser.getNoteSequence());
class NoteVisualizer extends HTMLElement {
constructor() {
super();
this.appendChild(document.importNode(templ.content, true));
this.stepsPerQuarter = 80;
this.pixelsPerTimeStep = 1;
this.container = this.querySelector('#container')
// Some event listeners.
this.querySelector('#btnPlay').addEventListener('click', (e) => this.playOrPause(e));
this.querySelector('#btnDownload').addEventListener('click', (e) => this.download(e));
document.getElementById('stepsInput').addEventListener('change', (e) => this.changeStepsPerQuarter(e));
document.getElementById('pixelsInput').addEventListener('change', (e) => this.changePixelsPerTimeStep(e));
// Visualizer will be set up when the music is loaded.
this.visualizer = null;
// Initialize the player.
this.player = new mm.SoundFontPlayer('https://storage.googleapis.com/magentadata/js/soundfonts/salamander');
this.player.callbackObject = {
run: (note) => {
const currentNotePosition = this.visualizer.redraw(note);
// See if we need to scroll the container.
const containerWidth = this.container.getBoundingClientRect().width;
if (currentNotePosition > (container.scrollLeft + containerWidth)) {
container.scrollLeft = currentNotePosition - 20;
}
},
stop: () => {}
}
}
// TODO: need getter/setter for stepsPerQuarter and pixelsPerTimeStep
displaySequence(sequence) {
this.visualizer = new mm.Visualizer(
sequence, this.querySelector('#canvas'),
{pixelsPerTimeStep: this.pixelsPerTimeStep, noteSpacing:0.1});
}
changeStepsPerQuarter(event) {
this.visualizer.noteSequence.quantizationInfo.stepsPerQuarter = parseInt(event.target.value);
}
changePixelsPerTimeStep(event) {
const seq = this.visualizer.noteSequence;
this.visualizer = new mm.Visualizer(
seq, this.querySelector('#canvas'),
{pixelsPerTimeStep: parseInt(event.target.value), noteSpacing:0.1});
}
playOrPause(event) {
if (this.player.isPlaying()) {
this.player.stop();
event.target.textContent = 'Play';
} else {
this.player.start(this.visualizer.noteSequence)
.then(() => (event.target.textContent = 'Play'));
event.target.textContent = 'Stop';
}
}
download(event) {
event.stopImmediatePropagation();
saveAs(new File([mm.sequenceProtoToMidi(this.visualizer.noteSequence)], 'transcription.mid'));
}
}
window.customElements.define('note-visualizer', NoteVisualizer);