dev@10
|
1 import {
|
dev@51
|
2 Component, OnInit, ViewChild, ElementRef, Input, AfterViewInit, NgZone,
|
dev@51
|
3 OnDestroy
|
dev@10
|
4 } from '@angular/core';
|
dev@39
|
5 import {AudioPlayerService} from "../services/audio-player/audio-player.service";
|
dev@36
|
6 import wavesUI from 'waves-ui';
|
dev@51
|
7 import {FeatureList} from "piper/Feature";
|
dev@51
|
8 import {FeatureExtractionService} from "../services/feature-extraction/feature-extraction.service";
|
dev@51
|
9 import {Subscription} from "rxjs";
|
dev@8
|
10
|
dev@20
|
11 type Timeline = any; // TODO what type actually is it.. start a .d.ts for waves-ui?
|
dev@6
|
12
|
dev@6
|
13 @Component({
|
dev@6
|
14 selector: 'app-waveform',
|
dev@6
|
15 templateUrl: './waveform.component.html',
|
dev@6
|
16 styleUrls: ['./waveform.component.css']
|
dev@6
|
17 })
|
dev@51
|
18 export class WaveformComponent implements OnInit, AfterViewInit, OnDestroy {
|
dev@20
|
19
|
dev@8
|
20 @ViewChild('track') trackDiv: ElementRef;
|
dev@6
|
21
|
dev@16
|
22 private _audioBuffer: AudioBuffer = undefined;
|
dev@51
|
23 private timeline: Timeline = undefined;
|
dev@16
|
24
|
dev@16
|
25 @Input()
|
dev@16
|
26 set audioBuffer(buffer: AudioBuffer) {
|
dev@16
|
27 this._audioBuffer = buffer || undefined;
|
dev@20
|
28 if (this.audioBuffer)
|
dev@20
|
29 this.renderWaveform(this.audioBuffer);
|
dev@16
|
30 }
|
dev@16
|
31
|
dev@16
|
32 get audioBuffer(): AudioBuffer {
|
dev@16
|
33 return this._audioBuffer;
|
dev@16
|
34 }
|
dev@16
|
35
|
dev@51
|
36 private featureExtractionSubscription: Subscription;
|
dev@51
|
37
|
dev@31
|
38 constructor(private audioService: AudioPlayerService,
|
dev@51
|
39 private piperService: FeatureExtractionService,
|
dev@51
|
40 public ngZone: NgZone) {
|
dev@51
|
41 this.featureExtractionSubscription = piperService.featuresExtracted$.subscribe(
|
dev@51
|
42 features => {
|
dev@51
|
43 this.renderFeatures(features);
|
dev@51
|
44 });
|
dev@51
|
45 }
|
dev@51
|
46
|
dev@10
|
47 ngOnInit() {}
|
dev@10
|
48
|
dev@10
|
49 ngAfterViewInit(): void {
|
dev@51
|
50 this.timeline = this.renderTimeline();
|
dev@20
|
51 }
|
dev@20
|
52
|
dev@20
|
53 renderTimeline(duration: number = 1.0): Timeline {
|
dev@18
|
54 const track: HTMLElement = this.trackDiv.nativeElement;
|
dev@20
|
55 track.innerHTML = "";
|
dev@18
|
56 const height: number = track.getBoundingClientRect().height;
|
dev@18
|
57 const width: number = track.getBoundingClientRect().width;
|
dev@18
|
58 const pixelsPerSecond = width / duration;
|
dev@18
|
59 const timeline = new wavesUI.core.Timeline(pixelsPerSecond, width);
|
dev@33
|
60 timeline.timeContext.offset = 0.5 * timeline.timeContext.visibleDuration;
|
dev@18
|
61 timeline.createTrack(track, height, 'main');
|
dev@18
|
62
|
dev@18
|
63 // time axis
|
dev@18
|
64 const timeAxis = new wavesUI.helpers.TimeAxisLayer({
|
dev@18
|
65 height: height,
|
dev@18
|
66 color: 'gray'
|
dev@18
|
67 });
|
dev@18
|
68
|
dev@18
|
69 timeline.addLayer(timeAxis, 'main', 'default', true);
|
dev@20
|
70 return timeline;
|
dev@16
|
71 }
|
dev@16
|
72
|
dev@20
|
73 renderWaveform(buffer: AudioBuffer): void {
|
dev@20
|
74 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height;
|
dev@51
|
75 this.timeline = this.renderTimeline(buffer.duration);
|
dev@20
|
76 const waveformLayer = new wavesUI.helpers.WaveformLayer(buffer, {
|
dev@10
|
77 top: 10,
|
dev@20
|
78 height: height * 0.9,
|
dev@16
|
79 color: 'darkblue'
|
dev@16
|
80 });
|
dev@51
|
81 (this.timeline as any).addLayer(waveformLayer, 'main');
|
dev@31
|
82
|
dev@31
|
83 const cursorLayer = new wavesUI.helpers.CursorLayer({
|
dev@31
|
84 height: height
|
dev@31
|
85 });
|
dev@51
|
86 this.timeline.addLayer(cursorLayer, 'main');
|
dev@51
|
87 this.timeline.state = new wavesUI.states.CenteredZoomState(this.timeline);
|
dev@31
|
88 this.ngZone.runOutsideAngular(() => {
|
dev@31
|
89 // listen for time passing...
|
dev@31
|
90 // TODO this gets the fans going on large files... worth fixing? or waiting to write a better component?
|
dev@31
|
91 // or, can this be updated in a more efficient manner?
|
dev@31
|
92 const updateSeekingCursor = () => {
|
dev@31
|
93 cursorLayer.currentPosition = this.audioService.getCurrentTime();
|
dev@31
|
94 cursorLayer.update();
|
dev@51
|
95 if (this.timeline.timeContext.offset + this.audioService.getCurrentTime() >= this.timeline.timeContext.visibleDuration) {
|
dev@51
|
96 this.timeline.timeContext.offset -= this.timeline.timeContext.visibleDuration;
|
dev@51
|
97 this.timeline.tracks.update();
|
dev@34
|
98 }
|
dev@51
|
99 if (-this.audioService.getCurrentTime() > this.timeline.timeContext.offset) {
|
dev@51
|
100 this.timeline.timeContext.offset += this.timeline.timeContext.visibleDuration;
|
dev@51
|
101 this.timeline.tracks.update();
|
dev@34
|
102 }
|
dev@31
|
103 requestAnimationFrame(updateSeekingCursor);
|
dev@31
|
104 };
|
dev@31
|
105 updateSeekingCursor();
|
dev@31
|
106 });
|
dev@6
|
107 }
|
dev@16
|
108
|
dev@51
|
109 // TODO refactor - this doesn't belong here
|
dev@51
|
110 private renderFeatures(features: FeatureList): void {
|
dev@51
|
111 console.log(features);
|
dev@51
|
112 }
|
dev@51
|
113
|
dev@51
|
114 ngOnDestroy(): void {
|
dev@51
|
115 this.featureExtractionSubscription.unsubscribe();
|
dev@51
|
116 }
|
dev@6
|
117 }
|