@@ -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 }
0 commit comments