Mercurial > hg > ugly-duckling
changeset 53:ccfbce214751
Improve UI seeking / scrolling behaviour. Allowing for zooming without the play head fighting to be in view, and for seeking without having to consecutively page through.
author | Lucas Thompson <dev@lucas.im> |
---|---|
date | Wed, 07 Dec 2016 13:56:22 +0000 |
parents | ec38b85be3ac |
children | 5fb857f8553b |
files | src/app/waveform/waveform.component.ts |
diffstat | 1 files changed, 69 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/src/app/waveform/waveform.component.ts Wed Dec 07 13:54:46 2016 +0000 +++ b/src/app/waveform/waveform.component.ts Wed Dec 07 13:56:22 2016 +0000 @@ -7,6 +7,7 @@ import {FeatureList} from "piper/Feature"; import {FeatureExtractionService} from "../services/feature-extraction/feature-extraction.service"; import {Subscription} from "rxjs"; +import {toSeconds} from "piper"; type Timeline = any; // TODO what type actually is it.. start a .d.ts for waves-ui? @@ -21,6 +22,7 @@ private _audioBuffer: AudioBuffer = undefined; private timeline: Timeline = undefined; + private cursorLayer: any = undefined; @Input() set audioBuffer(buffer: AudioBuffer) { @@ -34,17 +36,32 @@ } private featureExtractionSubscription: Subscription; + private playingStateSubscription: Subscription; + private seekedSubscription: Subscription; + private isPlaying: boolean; constructor(private audioService: AudioPlayerService, private piperService: FeatureExtractionService, public ngZone: NgZone) { + this.isPlaying = false; this.featureExtractionSubscription = piperService.featuresExtracted$.subscribe( features => { this.renderFeatures(features); }); + this.playingStateSubscription = audioService.playingStateChange$.subscribe( + isPlaying => { + this.isPlaying = isPlaying; + if (this.isPlaying) + this.animate(); + }); + this.seekedSubscription = audioService.seeked$.subscribe(() => { + if (!this.isPlaying) + this.animate(); + }); } - ngOnInit() {} + ngOnInit() { + } ngAfterViewInit(): void { this.timeline = this.renderTimeline(); @@ -80,38 +97,73 @@ }); (this.timeline as any).addLayer(waveformLayer, 'main'); - const cursorLayer = new wavesUI.helpers.CursorLayer({ + this.cursorLayer = new wavesUI.helpers.CursorLayer({ height: height }); - this.timeline.addLayer(cursorLayer, 'main'); + this.timeline.addLayer(this.cursorLayer, 'main'); this.timeline.state = new wavesUI.states.CenteredZoomState(this.timeline); + this.animate(); + } + + // TODO refactor - this doesn't belong here + private renderFeatures(features: FeatureList): void { + const plotData = features.map(feature => { + return { + cx: toSeconds(feature.timestamp), + cy: feature.featureValues[0] + }; + }); + this.timeline.addLayer( + new wavesUI.helpers.BreakpointLayer(plotData, {color: 'green'}), + 'main' + ); + } + + private animate(): void { this.ngZone.runOutsideAngular(() => { // listen for time passing... - // TODO this gets the fans going on large files... worth fixing? or waiting to write a better component? - // or, can this be updated in a more efficient manner? const updateSeekingCursor = () => { - cursorLayer.currentPosition = this.audioService.getCurrentTime(); - cursorLayer.update(); - if (this.timeline.timeContext.offset + this.audioService.getCurrentTime() >= this.timeline.timeContext.visibleDuration) { - this.timeline.timeContext.offset -= this.timeline.timeContext.visibleDuration; + const currentTime = this.audioService.getCurrentTime(); + this.cursorLayer.currentPosition = currentTime; + this.cursorLayer.update(); + + const currentOffset = this.timeline.timeContext.offset; + const offsetTimestamp = currentOffset + + currentTime; + + const visibleDuration = this.timeline.timeContext.visibleDuration; + // TODO reduce duplication between directions and make more declarative + // this kinda logic should also be tested + const mustPageForward = offsetTimestamp > visibleDuration; + const mustPageBackward = currentTime < -currentOffset; + + if (mustPageForward) { + const hasSkippedMultiplePages = offsetTimestamp - visibleDuration > visibleDuration; + + this.timeline.timeContext.offset = hasSkippedMultiplePages + ? -currentTime + 0.5 * visibleDuration + : currentOffset - visibleDuration; this.timeline.tracks.update(); } - if (-this.audioService.getCurrentTime() > this.timeline.timeContext.offset) { - this.timeline.timeContext.offset += this.timeline.timeContext.visibleDuration; + + if (mustPageBackward) { + const hasSkippedMultiplePages = currentTime + visibleDuration < -currentOffset; + this.timeline.timeContext.offset = hasSkippedMultiplePages + ? -currentTime + 0.5 * visibleDuration + : currentOffset + visibleDuration; this.timeline.tracks.update(); } - requestAnimationFrame(updateSeekingCursor); + + if (this.isPlaying) + requestAnimationFrame(updateSeekingCursor); }; updateSeekingCursor(); }); } - // TODO refactor - this doesn't belong here - private renderFeatures(features: FeatureList): void { - console.log(features); - } - ngOnDestroy(): void { this.featureExtractionSubscription.unsubscribe(); + this.playingStateSubscription.unsubscribe(); + this.seekedSubscription.unsubscribe(); } }