Mercurial > hg > ugly-duckling
comparison src/app/waveform/waveform.component.ts @ 301:fff3a4fba992
Change guard at top of renderFeatures for change in response shape + a bunch of formatting changes.
author | Lucas Thompson <dev@lucas.im> |
---|---|
date | Wed, 10 May 2017 17:45:06 +0100 |
parents | cda9307d9eb7 |
children | dc415a620b15 |
comparison
equal
deleted
inserted
replaced
297:73beb0e970c5 | 301:fff3a4fba992 |
---|---|
20 import {Subscription} from 'rxjs/Subscription'; | 20 import {Subscription} from 'rxjs/Subscription'; |
21 import { | 21 import { |
22 FeatureCollection, | 22 FeatureCollection, |
23 SimpleResponse, | 23 SimpleResponse, |
24 VectorFeatures, | 24 VectorFeatures, |
25 MatrixFeatures, | 25 MatrixFeatures |
26 TrackFeature, | |
27 TrackFeatures | |
28 } from 'piper/HigherLevelUtilities'; | 26 } from 'piper/HigherLevelUtilities'; |
29 import {toSeconds} from 'piper'; | 27 import {toSeconds} from 'piper'; |
30 import {FeatureList, Feature} from 'piper/Feature'; | 28 import {FeatureList, Feature} from 'piper/Feature'; |
31 import * as Hammer from 'hammerjs'; | 29 import * as Hammer from 'hammerjs'; |
32 import {WavesSpectrogramLayer} from '../spectrogram/Spectrogram'; | 30 import {WavesSpectrogramLayer} from '../spectrogram/Spectrogram'; |
388 element.addEventListener('touchend', () => { | 386 element.addEventListener('touchend', () => { |
389 if (isZooming) { | 387 if (isZooming) { |
390 isZooming = false; | 388 isZooming = false; |
391 zoomGestureJustEnded = true; | 389 zoomGestureJustEnded = true; |
392 } | 390 } |
393 }); | 391 }); |
394 element.addEventListener('touchmove', zoom); | 392 element.addEventListener('touchmove', zoom); |
395 } | 393 } |
396 // this.timeline.createTrack(track, height/2, `wave-${this.trackIdPrefix}`); | 394 // this.timeline.createTrack(track, height/2, `wave-${this.trackIdPrefix}`); |
397 // this.timeline.createTrack(track, height/2, `grid-${this.trackIdPrefix}`); | 395 // this.timeline.createTrack(track, height/2, `grid-${this.trackIdPrefix}`); |
398 } | 396 } |
424 } | 422 } |
425 } | 423 } |
426 } | 424 } |
427 if (sample.length === 0) { | 425 if (sample.length === 0) { |
428 console.log('WARNING: No samples gathered, even though we hoped for ' + | 426 console.log('WARNING: No samples gathered, even though we hoped for ' + |
429 (m_per * w) + ' of them'); | 427 (m_per * w) + ' of them'); |
430 return 0.0; | 428 return 0.0; |
431 } | 429 } |
432 sample.sort((a, b) => { return a - b; }); | 430 sample.sort((a, b) => { return a - b; }); |
433 const ix = Math.floor((sample.length * percentile) / 100); | 431 const ix = Math.floor((sample.length * percentile) / 100); |
434 console.log('Estimating ' + percentile + '-%ile of ' + | 432 console.log('Estimating ' + percentile + '-%ile of ' + |
435 n + '-sample dataset (' + w + ' x ' + h + ') as value ' + ix + | 433 n + '-sample dataset (' + w + ' x ' + h + ') as value ' + ix + |
436 ' of sorted ' + sample.length + '-sample subset'); | 434 ' of sorted ' + sample.length + '-sample subset'); |
437 const estimate = sample[ix]; | 435 const estimate = sample[ix]; |
438 console.log('Estimate is: ' + estimate + ' (where min sampled value = ' + | 436 console.log('Estimate is: ' + estimate + ' (where min sampled value = ' + |
439 sample[0] + ' and max = ' + sample[sample.length - 1] + ')'); | 437 sample[0] + ' and max = ' + sample[sample.length - 1] + ')'); |
440 return estimate; | 438 return estimate; |
441 } | 439 } |
442 | 440 |
443 interpolatingMapper(hexColours) { | 441 interpolatingMapper(hexColours) { |
444 const colours = hexColours.map(n => { | 442 const colours = hexColours.map(n => { |
445 const i = parseInt(n, 16); | 443 const i = parseInt(n, 16); |
446 return [ ((i >> 16) & 255) / 255.0, | 444 return [ ((i >> 16) & 255) / 255.0, |
447 ((i >> 8) & 255) / 255.0, | 445 ((i >> 8) & 255) / 255.0, |
448 ((i) & 255) / 255.0 ]; | 446 ((i) & 255) / 255.0 ]; |
449 }); | 447 }); |
450 const last = colours.length - 1; | 448 const last = colours.length - 1; |
451 return (value => { | 449 return (value => { |
452 const m = value * last; | 450 const m = value * last; |
453 if (m >= last) { | 451 if (m >= last) { |
460 const prop0 = base + 1.0 - m; | 458 const prop0 = base + 1.0 - m; |
461 const prop1 = m - base; | 459 const prop1 = m - base; |
462 const c0 = colours[base]; | 460 const c0 = colours[base]; |
463 const c1 = colours[base + 1]; | 461 const c1 = colours[base + 1]; |
464 return [ c0[0] * prop0 + c1[0] * prop1, | 462 return [ c0[0] * prop0 + c1[0] * prop1, |
465 c0[1] * prop0 + c1[1] * prop1, | 463 c0[1] * prop0 + c1[1] * prop1, |
466 c0[2] * prop0 + c1[2] * prop1 ]; | 464 c0[2] * prop0 + c1[2] * prop1 ]; |
467 }); | 465 }); |
468 } | 466 } |
469 | 467 |
470 iceMapper() { | 468 iceMapper() { |
471 const hexColours = [ | 469 const hexColours = [ |
483 const p = v * (1 - s); | 481 const p = v * (1 - s); |
484 const q = v * (1 - f * s); | 482 const q = v * (1 - f * s); |
485 const t = v * (1 - (1 - f) * s); | 483 const t = v * (1 - (1 - f) * s); |
486 let r = 0, g = 0, b = 0; | 484 let r = 0, g = 0, b = 0; |
487 switch (i % 6) { | 485 switch (i % 6) { |
488 case 0: r = v; g = t; b = p; break; | 486 case 0: r = v; g = t; b = p; break; |
489 case 1: r = q; g = v; b = p; break; | 487 case 1: r = q; g = v; b = p; break; |
490 case 2: r = p; g = v; b = t; break; | 488 case 2: r = p; g = v; b = t; break; |
491 case 3: r = p; g = q; b = v; break; | 489 case 3: r = p; g = q; b = v; break; |
492 case 4: r = t; g = p; b = v; break; | 490 case 4: r = t; g = p; b = v; break; |
493 case 5: r = v; g = p; b = q; break; | 491 case 5: r = v; g = p; b = q; break; |
494 } | 492 } |
495 return [ r, g, b ]; | 493 return [ r, g, b ]; |
496 } | 494 } |
497 | 495 |
498 greenMapper() { | 496 greenMapper() { |
620 if (!extracted.hasOwnProperty('features') | 618 if (!extracted.hasOwnProperty('features') |
621 || !extracted.hasOwnProperty('outputDescriptor')) { | 619 || !extracted.hasOwnProperty('outputDescriptor')) { |
622 return; | 620 return; |
623 } | 621 } |
624 if (!extracted.features.hasOwnProperty('shape') | 622 if (!extracted.features.hasOwnProperty('shape') |
625 || !extracted.features.hasOwnProperty('data')) { | 623 || !extracted.features.hasOwnProperty('collected')) { |
626 return; | 624 return; |
627 } | 625 } |
628 const features: FeatureCollection = (extracted.features as FeatureCollection); | 626 const features: FeatureCollection = (extracted.features as FeatureCollection); |
629 const outputDescriptor = extracted.outputDescriptor; | 627 const outputDescriptor = extracted.outputDescriptor; |
630 // const height = this.trackDiv.nativeElement.getBoundingClientRect().height / 2; | 628 // const height = this.trackDiv.nativeElement.getBoundingClientRect().height / 2; |
631 const height = this.trackDiv.nativeElement.getBoundingClientRect().height; | 629 const height = this.trackDiv.nativeElement.getBoundingClientRect().height; |
632 const waveTrack = this.timeline.getTrackById(`wave-${this.trackIdPrefix}`); | 630 const waveTrack = this.timeline.getTrackById(`wave-${this.trackIdPrefix}`); |
633 | 631 |
634 // TODO refactor all of this | 632 // TODO refactor all of this |
635 switch (features.shape) { | 633 switch (features.shape) { |
636 case 'vector': { | 634 case 'vector': { |
637 const collected = features.collected as VectorFeatures; | 635 const collected = features.collected as VectorFeatures; |
638 const stepDuration = collected.stepDuration; | 636 const stepDuration = collected.stepDuration; |
639 const featureData = collected.data; | 637 const featureData = collected.data; |
640 if (featureData.length === 0) { | 638 if (featureData.length === 0) { |
641 return; | 639 return; |
642 } | 640 } |
650 let max = featureData.reduce((m, f) => Math.max(m, f), -Infinity); | 648 let max = featureData.reduce((m, f) => Math.max(m, f), -Infinity); |
651 if (min === Infinity) { | 649 if (min === Infinity) { |
652 min = 0; | 650 min = 0; |
653 max = 1; | 651 max = 1; |
654 } | 652 } |
655 console.log("adding line layer: min = " + min + ", max = " + max); | 653 console.log('adding line layer: min = ' + min + ', max = ' + max); |
656 if (min !== min || max !== max) { | 654 if (min !== min || max !== max) { |
657 console.log("WARNING: min or max is NaN"); | 655 console.log('WARNING: min or max is NaN'); |
658 min = 0; | 656 min = 0; |
659 max = 1; | 657 max = 1; |
660 } | 658 } |
661 const lineLayer = new wavesUI.helpers.LineLayer(plotData, { | 659 const lineLayer = new wavesUI.helpers.LineLayer(plotData, { |
662 color: colour, | 660 color: colour, |
663 height: height, | 661 height: height, |
664 yDomain: [ min, max ] | 662 yDomain: [ min, max ] |
665 }); | 663 }); |
692 this.timeline.timeContext | 690 this.timeline.timeContext |
693 ); | 691 ); |
694 break; | 692 break; |
695 } | 693 } |
696 case 'list': { | 694 case 'list': { |
697 const featureData = features.collected as FeatureList; | 695 const featureData = features.collected as FeatureList; |
698 if (featureData.length === 0) { | 696 if (featureData.length === 0) { |
699 return; | 697 return; |
700 } | 698 } |
701 // TODO look at output descriptor instead of directly inspecting features | 699 // TODO look at output descriptor instead of directly inspecting features |
702 const hasDuration = outputDescriptor.configured.hasDuration; | 700 const hasDuration = outputDescriptor.configured.hasDuration; |
704 && outputDescriptor.configured.binCount === 0 | 702 && outputDescriptor.configured.binCount === 0 |
705 && featureData[0].featureValues == null; | 703 && featureData[0].featureValues == null; |
706 const isRegion = hasDuration | 704 const isRegion = hasDuration |
707 && featureData[0].timestamp != null; | 705 && featureData[0].timestamp != null; |
708 console.log('Have list features: length ' + featureData.length + | 706 console.log('Have list features: length ' + featureData.length + |
709 ', isMarker ' + isMarker + ', isRegion ' + isRegion + | 707 ', isMarker ' + isMarker + ', isRegion ' + isRegion + |
710 ', hasDuration ' + hasDuration); | 708 ', hasDuration ' + hasDuration); |
711 // TODO refactor, this is incomprehensible | 709 // TODO refactor, this is incomprehensible |
712 if (isMarker) { | 710 if (isMarker) { |
713 const plotData = featureData.map(feature => ({ | 711 const plotData = featureData.map(feature => ({ |
714 time: toSeconds(feature.timestamp), | 712 time: toSeconds(feature.timestamp), |
715 label: feature.label | 713 label: feature.label |
794 ); | 792 ); |
795 } | 793 } |
796 break; | 794 break; |
797 } | 795 } |
798 case 'matrix': { | 796 case 'matrix': { |
799 const collected = features.collected as MatrixFeatures; | 797 const collected = features.collected as MatrixFeatures; |
800 const stepDuration = collected.stepDuration; | 798 const stepDuration = collected.stepDuration; |
801 // !!! + start time | 799 // !!! + start time |
802 const matrixData = collected.data; | 800 const matrixData = collected.data; |
803 | 801 |
804 if (matrixData.length === 0) { | 802 if (matrixData.length === 0) { |
808 console.log('matrix data length = ' + matrixData.length); | 806 console.log('matrix data length = ' + matrixData.length); |
809 console.log('height of first column = ' + matrixData[0].length); | 807 console.log('height of first column = ' + matrixData[0].length); |
810 const targetValue = this.estimatePercentile(matrixData, 95); | 808 const targetValue = this.estimatePercentile(matrixData, 95); |
811 const gain = (targetValue > 0.0 ? (1.0 / targetValue) : 1.0); | 809 const gain = (targetValue > 0.0 ? (1.0 / targetValue) : 1.0); |
812 console.log('setting gain to ' + gain); | 810 console.log('setting gain to ' + gain); |
813 const matrixEntity = | 811 const matrixEntity = new wavesUI.utils.PrefilledMatrixEntity( |
814 new wavesUI.utils.PrefilledMatrixEntity(matrixData, | 812 matrixData, |
815 0, // startTime | 813 0, // startTime |
816 stepDuration); | 814 stepDuration |
815 ); | |
817 const matrixLayer = new wavesUI.helpers.MatrixLayer(matrixEntity, { | 816 const matrixLayer = new wavesUI.helpers.MatrixLayer(matrixEntity, { |
818 gain, | 817 gain, |
819 top: 0, | 818 top: 0, |
820 height: height, | 819 height: height, |
821 normalise: 'none', | 820 normalise: 'none', |
865 // this kinda logic should also be tested | 864 // this kinda logic should also be tested |
866 const mustPageForward = offsetTimestamp > visibleDuration; | 865 const mustPageForward = offsetTimestamp > visibleDuration; |
867 const mustPageBackward = currentTime < -currentOffset; | 866 const mustPageBackward = currentTime < -currentOffset; |
868 | 867 |
869 if (mustPageForward) { | 868 if (mustPageForward) { |
870 const hasSkippedMultiplePages = offsetTimestamp - visibleDuration > visibleDuration; | 869 const hasSkippedMultiplePages = offsetTimestamp - |
871 | 870 visibleDuration > visibleDuration; |
872 this.timeline.timeContext.offset = hasSkippedMultiplePages ? | 871 |
873 -currentTime + 0.5 * visibleDuration : | 872 this.timeline.timeContext.offset = hasSkippedMultiplePages ? |
874 currentOffset - visibleDuration; | 873 -currentTime + 0.5 * visibleDuration : |
874 currentOffset - visibleDuration; | |
875 this.timeline.tracks.update(); | 875 this.timeline.tracks.update(); |
876 } | 876 } |
877 | 877 |
878 if (mustPageBackward) { | 878 if (mustPageBackward) { |
879 const hasSkippedMultiplePages = currentTime + visibleDuration < -currentOffset; | 879 const hasSkippedMultiplePages = currentTime + |
880 this.timeline.timeContext.offset = hasSkippedMultiplePages ? | 880 visibleDuration < -currentOffset; |
881 -currentTime + 0.5 * visibleDuration : | 881 this.timeline.timeContext.offset = hasSkippedMultiplePages ? |
882 currentOffset + visibleDuration; | 882 -currentTime + 0.5 * visibleDuration : |
883 currentOffset + visibleDuration; | |
883 this.timeline.tracks.update(); | 884 this.timeline.tracks.update(); |
884 } | 885 } |
885 | 886 |
886 if (this.isPlaying) { | 887 if (this.isPlaying) { |
887 requestAnimationFrame(updateSeekingCursor); | 888 requestAnimationFrame(updateSeekingCursor); |