Skip to content

Commit cb38d24

Browse files
committed
Allow staff visualizers to be split into multiple lines
1 parent e63d19c commit cb38d24

File tree

4 files changed

+51
-17
lines changed

4 files changed

+51
-17
lines changed

package-lock.json

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"@babel/preset-env": "^7.11.0",
4848
"@rollup/plugin-babel": "^5.2.0",
4949
"@rollup/plugin-typescript": "^5.0.2",
50+
"@types/node": "^17.0.29",
5051
"focus-visible": "^5.1.0",
5152
"rollup": "^2.26.4",
5253
"rollup-plugin-dev": "^1.1.2",

src/visualizer.ts

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type Visualizer = mm.PianoRollSVGVisualizer | mm.WaterfallSVGVisualizer | mm.Sta
1919
*
2020
* @prop src - MIDI file URL
2121
* @prop type - Visualizer type
22+
* @prop lines - Number of lines in the visualizer (Only for `staff` type)
2223
* @prop noteSequence - Magenta note sequence object representing the currently displayed content
2324
* @prop config - Magenta visualizer config object
2425
*/
@@ -27,7 +28,8 @@ export class VisualizerElement extends HTMLElement {
2728
private initTimeout: number;
2829

2930
protected wrapper: HTMLDivElement;
30-
protected visualizer: Visualizer;
31+
protected visualizers: Visualizer[];
32+
protected lastChunkIndex: number = 0;
3133

3234
protected ns: INoteSequence = null;
3335
protected _config: mm.VisualizerConfig = {};
@@ -81,15 +83,24 @@ export class VisualizerElement extends HTMLElement {
8183
this.wrapper.classList.add('piano-roll-visualizer');
8284
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
8385
this.wrapper.appendChild(svg);
84-
this.visualizer = new mm.PianoRollSVGVisualizer(this.ns, svg, this._config);
86+
this.visualizers = [new mm.PianoRollSVGVisualizer(this.ns, svg, this._config)];
8587
} else if (this.type === 'waterfall') {
8688
this.wrapper.classList.add('waterfall-visualizer');
87-
this.visualizer = new mm.WaterfallSVGVisualizer(this.ns, this.wrapper, this._config);
89+
this.visualizers = [new mm.WaterfallSVGVisualizer(this.ns, this.wrapper, this._config)];
8890
} else if (this.type === 'staff') {
8991
this.wrapper.classList.add('staff-visualizer');
90-
const div = document.createElement('div');
91-
this.wrapper.appendChild(div);
92-
this.visualizer = new mm.StaffSVGVisualizer(this.ns, div, this._config);
92+
this.visualizers = [];
93+
const chunkSize = Math.ceil(this.ns.notes.length / this.lines);
94+
for (let i = 0; i < this.ns.notes.length; i += chunkSize) {
95+
const chunk = structuredClone(this.ns.notes.slice(i, i + chunkSize));
96+
let startTime = chunk[0].startTime;
97+
chunk.forEach(n => {n.startTime -= startTime;n.endTime -= startTime;});
98+
const div = document.createElement('div');
99+
this.wrapper.appendChild(div);
100+
const new_ns = structuredClone(this.ns);
101+
new_ns.notes = chunk;
102+
this.visualizers.push(new mm.StaffSVGVisualizer(new_ns, div, this._config));
103+
}
93104
}
94105
}
95106

@@ -98,14 +109,26 @@ export class VisualizerElement extends HTMLElement {
98109
}
99110

100111
redraw(activeNote?: NoteSequence.INote) {
101-
if (this.visualizer) {
102-
this.visualizer.redraw(activeNote, activeNote != null);
112+
if (this.visualizers) {
113+
if (this.type == "staff") {
114+
let chunkIndex = Math.floor(this.ns.notes.indexOf(activeNote) / Math.ceil(this.ns.notes.length / this.lines));
115+
if (chunkIndex != this.lastChunkIndex) {
116+
this.visualizers[this.lastChunkIndex].redraw(activeNote, false); // clearActiveNotes() doesn't work
117+
this.lastChunkIndex = chunkIndex;
118+
}
119+
const note = structuredClone(activeNote);
120+
note.startTime -= this.ns.notes[chunkIndex * Math.ceil(this.ns.notes.length / this.lines)].startTime;
121+
this.visualizers[chunkIndex].redraw(note, activeNote != null);
122+
}
123+
else {
124+
this.visualizers.forEach(visualizer => visualizer.redraw(activeNote, activeNote != null));
125+
}
103126
}
104127
}
105128

106129
clearActiveNotes() {
107-
if (this.visualizer) {
108-
this.visualizer.clearActiveNotes();
130+
if (this.visualizers) {
131+
this.visualizers.forEach(visualizer => visualizer.clearActiveNotes());
109132
}
110133
}
111134

@@ -148,6 +171,15 @@ export class VisualizerElement extends HTMLElement {
148171
this.setOrRemoveAttribute('type', value);
149172
}
150173

174+
get lines() {
175+
let lines = Number(this.getAttribute('lines'))
176+
return lines == 0 ? 1 : lines;
177+
}
178+
179+
set lines(value: number) {
180+
this.setOrRemoveAttribute('lines', value.toString() == '0' ? null : value.toString());
181+
}
182+
151183
get config() {
152184
return this._config;
153185
}

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,10 +1092,10 @@
10921092
"@types/node" "*"
10931093
form-data "^3.0.0"
10941094

1095-
"@types/node@*", "@types/node@>=13.7.0":
1096-
version "14.6.0"
1097-
resolved "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz"
1098-
integrity sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==
1095+
"@types/node@*", "@types/node@^17.0.29", "@types/node@>=13.7.0":
1096+
version "17.0.29"
1097+
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz"
1098+
integrity sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==
10991099

11001100
"@types/offscreencanvas@~2019.3.0":
11011101
version "2019.3.0"

0 commit comments

Comments
 (0)