Spaces:
Sleeping
Sleeping
add cc curve
Browse files- javascript/app.js +70 -15
javascript/app.js
CHANGED
@@ -229,9 +229,12 @@ class MidiVisualizer extends HTMLElement{
|
|
229 |
}
|
230 |
|
231 |
addTrack(id, tr, cl, name, color){
|
232 |
-
const track = {id, tr, cl, name, color,
|
|
|
233 |
instrument: cl===9?"Standard Drum":"Acoustic Grand",
|
234 |
-
svg: document.createElementNS('http://www.w3.org/2000/svg', 'g')
|
|
|
|
|
235 |
this.svg.appendChild(track.svg)
|
236 |
const trackItem = this.createTrackItem(track);
|
237 |
this.trackList.appendChild(trackItem);
|
@@ -269,12 +272,18 @@ class MidiVisualizer extends HTMLElement{
|
|
269 |
const content = document.createElement('div');
|
270 |
content.style.paddingLeft = '30px';
|
271 |
content.style.flexGrow = '1';
|
|
|
272 |
content.innerHTML = `<p>${track.name}<br>${track.instrument}</p>`;
|
273 |
trackItem.appendChild(content);
|
274 |
track.updateInstrument = function (instrument){
|
275 |
track.instrument = instrument;
|
276 |
content.innerHTML = `<p>${track.name}<br>${track.instrument}</p>`;
|
277 |
}
|
|
|
|
|
|
|
|
|
|
|
278 |
|
279 |
const toggleSwitch = document.createElement('input');
|
280 |
toggleSwitch.type = 'checkbox';
|
@@ -342,25 +351,31 @@ class MidiVisualizer extends HTMLElement{
|
|
342 |
duration = midiEvent[6]
|
343 |
}
|
344 |
let vis_track = this.getTrack(track, channel);
|
345 |
-
|
346 |
let x = (t/this.timePreBeat)*this.config.beatWidth
|
347 |
let y = (127 - pitch)*this.config.noteHeight
|
348 |
let w = (duration/this.timePreBeat)*this.config.beatWidth
|
349 |
let h = this.config.noteHeight
|
350 |
this.svgWidth = Math.ceil(Math.max(x + w, this.svgWidth))
|
351 |
-
let color = vis_track.color
|
352 |
let opacity = Math.min(1, velocity/127 + 0.1).toFixed(2)
|
353 |
-
let rect = this.drawNote(vis_track
|
354 |
-
|
355 |
-
midiEvent.push(rect)
|
356 |
this.setPlayTime(t);
|
357 |
this.pianoRoll.scrollTo(this.svgWidth - this.pianoRoll.offsetWidth, this.pianoRoll.scrollTop)
|
358 |
}else if(midiEvent[0] === "patch_change"){
|
359 |
-
let track = midiEvent[2]
|
360 |
-
let channel = midiEvent[3]
|
361 |
-
this.patches[channel].push([t, midiEvent[4]])
|
362 |
-
this.patches[channel].sort((a, b) => a[0] - b[0])
|
363 |
this.getTrack(track, channel);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
364 |
}
|
365 |
this.midiEvents.push(midiEvent);
|
366 |
this.svg.style.width = `${this.svgWidth}px`;
|
@@ -368,22 +383,54 @@ class MidiVisualizer extends HTMLElement{
|
|
368 |
|
369 |
}
|
370 |
|
371 |
-
drawNote(
|
372 |
-
if (!svg) {
|
373 |
return null;
|
374 |
}
|
375 |
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
376 |
rect.classList.add('note');
|
377 |
-
|
|
|
378 |
// Round values to the nearest integer to avoid partially filled pixels.
|
379 |
rect.setAttribute('x', `${Math.round(x)}`);
|
380 |
rect.setAttribute('y', `${Math.round(y)}`);
|
381 |
rect.setAttribute('width', `${Math.round(w)}`);
|
382 |
rect.setAttribute('height', `${Math.round(h)}`);
|
383 |
-
svg.appendChild(rect);
|
384 |
return rect
|
385 |
}
|
386 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
387 |
finishAppendMidiEvent(){
|
388 |
this.pause()
|
389 |
let midiEvents = this.midiEvents.sort((a, b)=>a[1]-b[1])
|
@@ -405,6 +452,14 @@ class MidiVisualizer extends HTMLElement{
|
|
405 |
}
|
406 |
lastT = t;
|
407 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
408 |
}
|
409 |
|
410 |
setPlayTime(t){
|
|
|
229 |
}
|
230 |
|
231 |
addTrack(id, tr, cl, name, color){
|
232 |
+
const track = {id, tr, cl, name, color, empty: true,
|
233 |
+
lastCC: new Map(),
|
234 |
instrument: cl===9?"Standard Drum":"Acoustic Grand",
|
235 |
+
svg: document.createElementNS('http://www.w3.org/2000/svg', 'g'),
|
236 |
+
ccPaths: new Map()
|
237 |
+
}
|
238 |
this.svg.appendChild(track.svg)
|
239 |
const trackItem = this.createTrackItem(track);
|
240 |
this.trackList.appendChild(trackItem);
|
|
|
272 |
const content = document.createElement('div');
|
273 |
content.style.paddingLeft = '30px';
|
274 |
content.style.flexGrow = '1';
|
275 |
+
content.style.color = "grey"
|
276 |
content.innerHTML = `<p>${track.name}<br>${track.instrument}</p>`;
|
277 |
trackItem.appendChild(content);
|
278 |
track.updateInstrument = function (instrument){
|
279 |
track.instrument = instrument;
|
280 |
content.innerHTML = `<p>${track.name}<br>${track.instrument}</p>`;
|
281 |
}
|
282 |
+
track.setEmpty = function (empty){
|
283 |
+
if (empty!==track.empty){
|
284 |
+
content.style.color = empty?"grey":"black";
|
285 |
+
}
|
286 |
+
}
|
287 |
|
288 |
const toggleSwitch = document.createElement('input');
|
289 |
toggleSwitch.type = 'checkbox';
|
|
|
351 |
duration = midiEvent[6]
|
352 |
}
|
353 |
let vis_track = this.getTrack(track, channel);
|
354 |
+
vis_track.setEmpty(false);
|
355 |
let x = (t/this.timePreBeat)*this.config.beatWidth
|
356 |
let y = (127 - pitch)*this.config.noteHeight
|
357 |
let w = (duration/this.timePreBeat)*this.config.beatWidth
|
358 |
let h = this.config.noteHeight
|
359 |
this.svgWidth = Math.ceil(Math.max(x + w, this.svgWidth))
|
|
|
360 |
let opacity = Math.min(1, velocity/127 + 0.1).toFixed(2)
|
361 |
+
let rect = this.drawNote(vis_track, x,y,w,h, opacity)
|
362 |
+
midiEvent.push(rect);
|
|
|
363 |
this.setPlayTime(t);
|
364 |
this.pianoRoll.scrollTo(this.svgWidth - this.pianoRoll.offsetWidth, this.pianoRoll.scrollTop)
|
365 |
}else if(midiEvent[0] === "patch_change"){
|
366 |
+
let track = midiEvent[2];
|
367 |
+
let channel = midiEvent[3];
|
368 |
+
this.patches[channel].push([t, midiEvent[4]]);
|
369 |
+
this.patches[channel].sort((a, b) => a[0] - b[0]);
|
370 |
this.getTrack(track, channel);
|
371 |
+
}else if(midiEvent[0] === "control_change"){
|
372 |
+
let track = midiEvent[2];
|
373 |
+
let channel = midiEvent[3];
|
374 |
+
let controller = midiEvent[4];
|
375 |
+
let value = midiEvent[5];
|
376 |
+
let vis_track = this.getTrack(track, channel);
|
377 |
+
this.drawCC(vis_track, t, controller, value);
|
378 |
+
this.setPlayTime(t);
|
379 |
}
|
380 |
this.midiEvents.push(midiEvent);
|
381 |
this.svg.style.width = `${this.svgWidth}px`;
|
|
|
383 |
|
384 |
}
|
385 |
|
386 |
+
drawNote(track, x, y, w, h, opacity) {
|
387 |
+
if (!track.svg) {
|
388 |
return null;
|
389 |
}
|
390 |
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
391 |
rect.classList.add('note');
|
392 |
+
const color = track.color;
|
393 |
+
rect.setAttribute('fill', `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity})`);
|
394 |
// Round values to the nearest integer to avoid partially filled pixels.
|
395 |
rect.setAttribute('x', `${Math.round(x)}`);
|
396 |
rect.setAttribute('y', `${Math.round(y)}`);
|
397 |
rect.setAttribute('width', `${Math.round(w)}`);
|
398 |
rect.setAttribute('height', `${Math.round(h)}`);
|
399 |
+
track.svg.appendChild(rect);
|
400 |
return rect
|
401 |
}
|
402 |
|
403 |
+
drawCC(track, t, controller, value){
|
404 |
+
if (!track.svg) {
|
405 |
+
return null;
|
406 |
+
}
|
407 |
+
let path = track.ccPaths.get(controller);
|
408 |
+
let x = (t/this.timePreBeat)*this.config.beatWidth
|
409 |
+
let y = (127 - value)*this.config.noteHeight
|
410 |
+
if (!path){
|
411 |
+
path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
412 |
+
path.setAttribute('visibility',"hidden");
|
413 |
+
path.setAttribute('fill', "transparent");
|
414 |
+
const color = track.color;
|
415 |
+
path.setAttribute('stroke', `rgba(${color.r}, ${color.g}, ${color.b}, 0.6)`);
|
416 |
+
path.setAttribute('stroke-width', "1");
|
417 |
+
path.setAttribute('d',
|
418 |
+
t===0?`M ${x} ${y}`:`M 0 ${127*this.config.noteHeight} H ${x} L ${x} ${y}`);
|
419 |
+
track.svg.appendChild(path);
|
420 |
+
track.ccPaths.set(controller, path);
|
421 |
+
track.lastCC.set(controller, value);
|
422 |
+
return path;
|
423 |
+
}
|
424 |
+
let lastVal = track.lastCC.get(controller);
|
425 |
+
if(lastVal !== value){
|
426 |
+
path.removeAttribute('visibility');
|
427 |
+
}
|
428 |
+
let d = path.getAttribute("d");
|
429 |
+
d += `H ${x} L ${x} ${y}`
|
430 |
+
path.setAttribute('d', d);
|
431 |
+
return path
|
432 |
+
}
|
433 |
+
|
434 |
finishAppendMidiEvent(){
|
435 |
this.pause()
|
436 |
let midiEvents = this.midiEvents.sort((a, b)=>a[1]-b[1])
|
|
|
452 |
}
|
453 |
lastT = t;
|
454 |
})
|
455 |
+
let x = (lastT/this.timePreBeat)*this.config.beatWidth;
|
456 |
+
this.trackMap.forEach((track, id)=>{
|
457 |
+
track.ccPaths.forEach((path, controller)=>{
|
458 |
+
let d = path.getAttribute("d");
|
459 |
+
d += `H ${x}`
|
460 |
+
path.setAttribute('d', d);
|
461 |
+
})
|
462 |
+
})
|
463 |
}
|
464 |
|
465 |
setPlayTime(t){
|