changeset 397:308ea1c2612e

Introduce a render loop service / singleton for work which needs to be animated with the play position. Use it for animating the cross-high. Much dupe with the live-play-head, which should be refactored.
author Lucas Thompson <dev@lucas.im>
date Fri, 02 Jun 2017 16:47:38 +0100
parents 3eab26a629e1
children 75cb17f28277
files src/app/app.module.ts src/app/services/render-loop/render-loop.service.ts src/app/visualisations/cross-hair-inspector.component.ts src/app/visualisations/waves-base.component.ts
diffstat 4 files changed, 72 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/app/app.module.ts	Thu Jun 01 23:04:39 2017 +0100
+++ b/src/app/app.module.ts	Fri Jun 02 16:47:38 2017 +0100
@@ -38,6 +38,7 @@
 import {GridComponent} from './visualisations/grid/grid.component';
 import {VerticalScaleComponent} from './visualisations/vertical-scale.component';
 import {CrossHairInspectorComponent} from './visualisations/cross-hair-inspector.component';
+import {RenderLoopService} from './services/render-loop/render-loop.service';
 
 export function createAudioContext(): AudioContext {
   return new (
@@ -151,7 +152,8 @@
     {provide: 'PiperRepoUri', useValue: 'assets/remote-extractors.json'},
     {provide: 'UrlResourceLifetimeManager', useFactory: createUrlResourceManager},
     {provide: 'ResourceReader', useFactory: createResourceReader},
-    {provide: 'DimensionObservable', useFactory: createWindowDimensionObservable}
+    {provide: 'DimensionObservable', useFactory: createWindowDimensionObservable},
+    RenderLoopService
   ],
   bootstrap: [AppComponent]
 })
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/services/render-loop/render-loop.service.ts	Fri Jun 02 16:47:38 2017 +0100
@@ -0,0 +1,55 @@
+/**
+ * Created by lucast on 02/06/2017.
+ */
+import {Injectable, NgZone} from '@angular/core';
+import {AudioPlayerService} from '../audio-player/audio-player.service';
+import {Subscription} from 'rxjs/Subscription';
+import {OnSeekHandler} from '../../playhead/PlayHeadHelpers';
+
+@Injectable()
+export class RenderLoopService {
+  private playingStateSubscription: Subscription;
+  private seekedSubscription: Subscription;
+  private tasks: OnSeekHandler[];
+
+  constructor(private player: AudioPlayerService,
+              private zone: NgZone) {
+    this.tasks = [];
+    this.seekedSubscription = this.player.seeked$.subscribe(() => {
+      if (!this.player.isPlaying()) {
+        this.zone.runOutsideAngular(() => {
+          this.runTasks();
+        });
+      }
+    });
+    this.playingStateSubscription = this.player.playingStateChange$.subscribe(
+      isPlaying => {
+        if (isPlaying) {
+          this.animate();
+        }
+      });
+  }
+
+  addPlayingTask(task: OnSeekHandler): void {
+    this.tasks.push(task);
+  }
+
+  private animate(): void {
+    this.zone.runOutsideAngular(() => {
+      const animateNextFrame = () => {
+        if (this.player.isPlaying()) {
+          this.runTasks();
+          requestAnimationFrame(animateNextFrame);
+        }
+      };
+      requestAnimationFrame(animateNextFrame);
+    });
+  }
+
+  private runTasks(): void {
+    const currentTime = this.player.getCurrentTime();
+    for (const task of this.tasks) {
+      task(currentTime);
+    }
+  }
+}
--- a/src/app/visualisations/cross-hair-inspector.component.ts	Thu Jun 01 23:04:39 2017 +0100
+++ b/src/app/visualisations/cross-hair-inspector.component.ts	Fri Jun 02 16:47:38 2017 +0100
@@ -13,6 +13,7 @@
   VerticalValueInspectorRenderer
 } from './waves-base.component';
 import {VerticalScaleComponent} from './vertical-scale.component';
+import {RenderLoopService} from '../services/render-loop/render-loop.service';
 
 @Component({
   selector: 'ugly-cross-hair-inspector',
@@ -26,9 +27,14 @@
   ) inspectorRenderers: QueryList<VerticalValueInspectorRenderer>;
   @Input() unit: string;
 
+  constructor(private renderLoop: RenderLoopService) {
+    super();
+  }
+
   ngAfterViewInit(): void {
     super.ngAfterViewInit();
     this.inspectorRenderers.forEach(renderer => {
+      this.renderLoop.addPlayingTask(renderer.updatePosition);
       renderer.renderInspector(this.cachedRanged, this.unit);
     });
   }
--- a/src/app/visualisations/waves-base.component.ts	Thu Jun 01 23:04:39 2017 +0100
+++ b/src/app/visualisations/waves-base.component.ts	Fri Jun 02 16:47:38 2017 +0100
@@ -23,6 +23,7 @@
   extends VerticalScaleRenderer {
   // TODO how do I know these layers are actually 'describable'?
   abstract renderInspector(range: [number, number], unit?: string): void;
+  abstract get updatePosition(): OnSeekHandler;
 }
 
 export abstract class WavesComponent<T extends ShapedFeatureData | AudioBuffer>
@@ -209,8 +210,14 @@
   @Input() set onSeek(handler: OnSeekHandler) {
     this.wrappedSeekHandler = (x: number) => {
       handler(x);
+      this.updatePosition(x);
+    };
+  }
+
+  get updatePosition() {
+    return (currentTime: number): void => {
       if (this.highlight) {
-        this.highlight.currentPosition = x;
+        this.highlight.currentPosition = currentTime;
         this.highlight.update();
       }
     };