annotate src/app/waveform/waveform.component.ts @ 51:8619f25ff52a

Communicate new features being extracted via subscriptions to the stream in FeatureExtractionService.
author Lucas Thompson <dev@lucas.im>
date Tue, 06 Dec 2016 14:19:03 +0000
parents e6eb133fa47c
children ccfbce214751
rev   line source
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 }