comparison src/app/waveform/waveform.component.ts @ 54:5fb857f8553b

Add a number of ad-hoc workarounds for bugs / quirks with waves-ui - regarding removing layers and avoiding ghost listeners. Still work to be done here.
author Lucas Thompson <dev@lucas.im>
date Thu, 08 Dec 2016 15:09:03 +0000
parents ccfbce214751
children 214e41418460
comparison
equal deleted inserted replaced
53:ccfbce214751 54:5fb857f8553b
8 import {FeatureExtractionService} from "../services/feature-extraction/feature-extraction.service"; 8 import {FeatureExtractionService} from "../services/feature-extraction/feature-extraction.service";
9 import {Subscription} from "rxjs"; 9 import {Subscription} from "rxjs";
10 import {toSeconds} from "piper"; 10 import {toSeconds} from "piper";
11 11
12 type Timeline = any; // TODO what type actually is it.. start a .d.ts for waves-ui? 12 type Timeline = any; // TODO what type actually is it.. start a .d.ts for waves-ui?
13 type Layer = any;
14 type Track = any;
13 15
14 @Component({ 16 @Component({
15 selector: 'app-waveform', 17 selector: 'app-waveform',
16 templateUrl: './waveform.component.html', 18 templateUrl: './waveform.component.html',
17 styleUrls: ['./waveform.component.css'] 19 styleUrls: ['./waveform.component.css']
18 }) 20 })
19 export class WaveformComponent implements OnInit, AfterViewInit, OnDestroy { 21 export class WaveformComponent implements OnInit, AfterViewInit, OnDestroy {
20 22
21 @ViewChild('track') trackDiv: ElementRef; 23 @ViewChild('track') trackDiv: ElementRef;
22 24
23 private _audioBuffer: AudioBuffer = undefined; 25 private _audioBuffer: AudioBuffer;
24 private timeline: Timeline = undefined; 26 private timeline: Timeline;
25 private cursorLayer: any = undefined; 27 private cursorLayer: any;
28 private disposableLayers: Layer[];
26 29
27 @Input() 30 @Input()
28 set audioBuffer(buffer: AudioBuffer) { 31 set audioBuffer(buffer: AudioBuffer) {
29 this._audioBuffer = buffer || undefined; 32 this._audioBuffer = buffer || undefined;
30 if (this.audioBuffer) 33 if (this.audioBuffer)
41 private isPlaying: boolean; 44 private isPlaying: boolean;
42 45
43 constructor(private audioService: AudioPlayerService, 46 constructor(private audioService: AudioPlayerService,
44 private piperService: FeatureExtractionService, 47 private piperService: FeatureExtractionService,
45 public ngZone: NgZone) { 48 public ngZone: NgZone) {
49 this.disposableLayers = [];
50 this._audioBuffer = undefined;
51 this.timeline = undefined;
52 this.cursorLayer = undefined;
46 this.isPlaying = false; 53 this.isPlaying = false;
47 this.featureExtractionSubscription = piperService.featuresExtracted$.subscribe( 54 this.featureExtractionSubscription = piperService.featuresExtracted$.subscribe(
48 features => { 55 features => {
49 this.renderFeatures(features); 56 this.renderFeatures(features);
50 }); 57 });
74 const width: number = track.getBoundingClientRect().width; 81 const width: number = track.getBoundingClientRect().width;
75 const pixelsPerSecond = width / duration; 82 const pixelsPerSecond = width / duration;
76 const timeline = new wavesUI.core.Timeline(pixelsPerSecond, width); 83 const timeline = new wavesUI.core.Timeline(pixelsPerSecond, width);
77 timeline.timeContext.offset = 0.5 * timeline.timeContext.visibleDuration; 84 timeline.timeContext.offset = 0.5 * timeline.timeContext.visibleDuration;
78 timeline.createTrack(track, height, 'main'); 85 timeline.createTrack(track, height, 'main');
79 86 return timeline;
87 }
88
89 renderWaveform(buffer: AudioBuffer): void {
90 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height;
91 const mainTrack = this.timeline.getTrackById('main');
92 if (this.timeline) {
93 // resize
94 const width = this.trackDiv.nativeElement.getBoundingClientRect().width;
95 // loop through layers and remove them, waves-ui provides methods for this but it seems to not work properly
96 for (let i = 0; i < this.disposableLayers.length; ++i) {
97 let layer = this.disposableLayers.pop();
98 mainTrack.remove(layer);
99 layer.destroy();
100 }
101 this.timeline.visibleWidth = width;
102 this.timeline.pixelsPerSecond = width / buffer.duration;
103 mainTrack.height = height;
104 } else {
105 this.timeline = this.renderTimeline(buffer.duration)
106 }
80 // time axis 107 // time axis
81 const timeAxis = new wavesUI.helpers.TimeAxisLayer({ 108 const timeAxis = new wavesUI.helpers.TimeAxisLayer({
82 height: height, 109 height: height,
83 color: 'gray' 110 color: 'gray'
84 }); 111 });
85 112 this.addLayer(timeAxis, mainTrack, this.timeline.timeContext, true);
86 timeline.addLayer(timeAxis, 'main', 'default', true); 113
87 return timeline;
88 }
89
90 renderWaveform(buffer: AudioBuffer): void {
91 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height;
92 this.timeline = this.renderTimeline(buffer.duration);
93 const waveformLayer = new wavesUI.helpers.WaveformLayer(buffer, { 114 const waveformLayer = new wavesUI.helpers.WaveformLayer(buffer, {
94 top: 10, 115 top: 10,
95 height: height * 0.9, 116 height: height * 0.9,
96 color: 'darkblue' 117 color: 'darkblue'
97 }); 118 });
98 (this.timeline as any).addLayer(waveformLayer, 'main'); 119 this.addLayer(waveformLayer, mainTrack, this.timeline.timeContext);
99 120
100 this.cursorLayer = new wavesUI.helpers.CursorLayer({ 121 this.cursorLayer = new wavesUI.helpers.CursorLayer({
101 height: height 122 height: height
102 }); 123 });
103 this.timeline.addLayer(this.cursorLayer, 'main'); 124 this.addLayer(this.cursorLayer, mainTrack, this.timeline.timeContext);
104 this.timeline.state = new wavesUI.states.CenteredZoomState(this.timeline); 125 this.timeline.state = new wavesUI.states.CenteredZoomState(this.timeline);
126 mainTrack.render();
127 mainTrack.update();
105 this.animate(); 128 this.animate();
106 } 129 }
107 130
108 // TODO refactor - this doesn't belong here 131 // TODO refactor - this doesn't belong here
109 private renderFeatures(features: FeatureList): void { 132 private renderFeatures(features: FeatureList): void {
111 return { 134 return {
112 cx: toSeconds(feature.timestamp), 135 cx: toSeconds(feature.timestamp),
113 cy: feature.featureValues[0] 136 cy: feature.featureValues[0]
114 }; 137 };
115 }); 138 });
116 this.timeline.addLayer( 139 this.addLayer(
117 new wavesUI.helpers.BreakpointLayer(plotData, {color: 'green'}), 140 new wavesUI.helpers.BreakpointLayer(plotData, {color: 'green'}),
118 'main' 141 this.timeline.getTrackById('main'),
142 this.timeline.timeContext
119 ); 143 );
120 } 144 }
121 145
122 private animate(): void { 146 private animate(): void {
123 this.ngZone.runOutsideAngular(() => { 147 this.ngZone.runOutsideAngular(() => {
159 }; 183 };
160 updateSeekingCursor(); 184 updateSeekingCursor();
161 }); 185 });
162 } 186 }
163 187
188 private addLayer(layer: Layer, track: Track, timeContext: any, isAxis: boolean = false): void {
189 timeContext.zoom = 1.0;
190 if (!layer.timeContext) {
191 layer.setTimeContext(isAxis ?
192 timeContext : new wavesUI.core.LayerTimeContext(timeContext));
193 }
194 this.disposableLayers.push(layer);
195 track.add(layer);
196 layer.render();
197 layer.update();
198 }
199
164 ngOnDestroy(): void { 200 ngOnDestroy(): void {
165 this.featureExtractionSubscription.unsubscribe(); 201 this.featureExtractionSubscription.unsubscribe();
166 this.playingStateSubscription.unsubscribe(); 202 this.playingStateSubscription.unsubscribe();
167 this.seekedSubscription.unsubscribe(); 203 this.seekedSubscription.unsubscribe();
168 } 204 }