annotate src/app/analysis-item/analysis-item.component.ts @ 454:f93582c38b70

Don't dispose of old audio when loading new audio files. Also move resource manager type out of audio-player.
author Lucas Thompson <dev@lucas.im>
date Thu, 29 Jun 2017 20:08:11 +0100
parents 8113b6f5a75e
children 7bb0bac6f8dc
rev   line source
dev@170 1 /**
dev@170 2 * Created by lucast on 21/03/2017.
dev@170 3 */
dev@231 4 import {
dev@231 5 ChangeDetectionStrategy,
dev@231 6 Component,
dev@231 7 Input,
dev@408 8 OnDestroy,
dev@231 9 OnInit
dev@236 10 } from '@angular/core';
dev@348 11 import {naivePagingMapper} from '../visualisations/WavesJunk';
dev@408 12 import {OnSeekHandler} from '../playhead/PlayHeadHelpers';
dev@361 13 import {
dev@381 14 defaultColourGenerator,
dev@361 15 HigherLevelFeatureShape,
dev@361 16 KnownShapedFeature
dev@361 17 } from '../visualisations/FeatureUtilities';
dev@408 18 import {
dev@408 19 RenderLoopService,
dev@408 20 TaskRemover
dev@408 21 } from '../services/render-loop/render-loop.service';
dev@170 22
dev@350 23 export interface Item {
dev@350 24 id: string;
dev@200 25 hasSharedTimeline: boolean;
dev@200 26 title?: string;
dev@200 27 description?: string;
dev@224 28 progress?: number;
dev@350 29 }
dev@350 30
dev@350 31 export interface PendingRootAudioItem extends Item {
dev@350 32 uri: string;
dev@453 33 mimeType?: string;
dev@350 34 }
dev@378 35 export interface RootAudioItem extends PendingRootAudioItem {
dev@350 36 audioData: AudioBuffer;
dev@350 37 }
dev@350 38
dev@350 39 export interface PendingAnalysisItem extends Item {
dev@350 40 parent: RootAudioItem;
dev@350 41 extractorKey: string;
dev@350 42 }
dev@350 43
dev@396 44 export type AnalysisItem = PendingAnalysisItem & KnownShapedFeature & {
dev@396 45 unit?: string
dev@396 46 };
dev@361 47
dev@361 48 export function isItem(item: Item): item is Item {
dev@361 49 return item.id != null && item.hasSharedTimeline != null;
dev@350 50 }
dev@350 51
dev@350 52 export function isPendingRootAudioItem(item: Item): item is PendingRootAudioItem {
dev@361 53 return isItem(item) && typeof (item as RootAudioItem).uri === 'string';
dev@350 54 }
dev@350 55
dev@350 56 export function isRootAudioItem(item: Item): item is RootAudioItem {
dev@410 57 return item && isPendingRootAudioItem(item) &&
dev@351 58 (item as RootAudioItem).audioData instanceof AudioBuffer;
dev@350 59 }
dev@350 60
dev@350 61 export function isPendingAnalysisItem(item: Item): item is AnalysisItem {
dev@350 62 const downcast = (item as AnalysisItem);
dev@350 63 return isRootAudioItem(downcast.parent)
dev@350 64 && typeof downcast.extractorKey === 'string';
dev@350 65 }
dev@350 66
dev@350 67 export function isAnalysisItem(item: Item): item is AnalysisItem {
dev@350 68 const downcast = (item as AnalysisItem);
dev@361 69 return isPendingAnalysisItem(item) &&
dev@361 70 downcast.shape != null &&
dev@361 71 downcast.collected != null;
dev@350 72 }
dev@350 73
dev@350 74 // these should probably be actual concrete types with their own getUri methods
dev@350 75 export function getRootUri(item: Item): string {
dev@350 76 if (isPendingRootAudioItem(item)) {
dev@350 77 return item.uri;
dev@350 78 }
dev@350 79 if (isPendingAnalysisItem(item)) {
dev@350 80 return item.parent.uri;
dev@350 81 }
dev@350 82 throw new Error('Invalid item: No URI property set.');
dev@170 83 }
dev@170 84
dev@170 85 @Component({
dev@170 86 selector: 'ugly-analysis-item',
dev@170 87 templateUrl: './analysis-item.component.html',
dev@231 88 styleUrls: ['./analysis-item.component.css'],
dev@231 89 changeDetection: ChangeDetectionStrategy.OnPush
dev@170 90 })
dev@408 91 export class AnalysisItemComponent implements OnInit, OnDestroy {
dev@224 92
dev@408 93 // TODO should be TimelineTimeContext?
dev@408 94 @Input() set timeline(timeline: Timeline) {
dev@408 95 this.mTimeline = timeline;
dev@408 96 this.resetRemoveAnimation();
dev@408 97 }
dev@408 98
dev@408 99 get timeline(): Timeline {
dev@408 100 return this.mTimeline;
dev@408 101 }
dev@408 102
dev@408 103 @Input() set isActive(isActive: boolean) {
dev@408 104 this.removeAnimation();
dev@408 105 this.mIsActive = isActive;
dev@408 106 if (isActive) {
dev@408 107 this.resetRemoveAnimation();
dev@408 108 }
dev@408 109 }
dev@408 110
dev@408 111 get isActive() {
dev@408 112 return this.mIsActive;
dev@408 113 }
dev@408 114
dev@350 115 @Input() item: Item;
dev@285 116 @Input() contentWidth: number;
dev@348 117 @Input() onSeek: OnSeekHandler;
dev@408 118 // TODO move / re-think - naivePagingMapper feels like a big ol' bodge
dev@408 119 private removeAnimation: TaskRemover;
dev@224 120 private hasProgressOnInit = false;
dev@408 121 private mIsActive: boolean;
dev@408 122 private mTimeline: Timeline;
dev@224 123
dev@408 124 constructor(private renderLoop: RenderLoopService) {}
dev@348 125
dev@224 126 ngOnInit(): void {
dev@408 127 this.resetRemoveAnimation();
dev@231 128 this.hasProgressOnInit = this.item.progress != null;
dev@224 129 }
dev@224 130
dev@224 131 isLoading(): boolean {
dev@231 132 return this.hasProgressOnInit && this.item.progress < 100;
dev@224 133 }
dev@348 134
dev@348 135 isAudioItem(): boolean {
dev@408 136 return this.item && isRootAudioItem(this.item);
dev@348 137 }
dev@361 138
dev@410 139 isPending(): boolean {
dev@410 140 return this.item &&
dev@410 141 !isRootAudioItem(this.item) && !isAnalysisItem(this.item) &&
dev@410 142 (isPendingAnalysisItem(this.item) || isPendingRootAudioItem(this.item));
dev@410 143 }
dev@410 144
dev@361 145 getFeatureShape(): HigherLevelFeatureShape | null {
dev@361 146 return !isPendingRootAudioItem(this.item) &&
dev@361 147 isAnalysisItem(this.item) ? this.item.shape : null;
dev@361 148 }
dev@381 149
dev@412 150 getDuration(): number | null {
dev@412 151 if (isRootAudioItem(this.item)) {
dev@412 152 return this.item.audioData.duration;
dev@412 153 }
dev@412 154 if (isAnalysisItem(this.item)) {
dev@412 155 return this.item.parent.audioData.duration;
dev@412 156 }
dev@412 157 }
dev@412 158
dev@381 159 getNextColour(): string {
dev@381 160 return defaultColourGenerator.next().value;
dev@381 161 }
dev@408 162
dev@408 163 ngOnDestroy(): void {
dev@408 164 this.removeAnimation();
dev@408 165 }
dev@408 166
dev@408 167 private resetRemoveAnimation(): void {
dev@408 168 if (this.removeAnimation) {
dev@408 169 this.removeAnimation();
dev@408 170 }
dev@408 171 const createPagingTask = () => {
dev@408 172 const pagingMapper = naivePagingMapper(this.timeline);
dev@408 173 return this.renderLoop.addPlayingTask(currentTime => {
dev@408 174 pagingMapper(currentTime);
dev@408 175 });
dev@408 176 };
dev@408 177 // only add a pager to audio items, it can drive the feature items
dev@408 178 const remover = this.timeline && this.isAudioItem() ?
dev@408 179 createPagingTask() : () => {};
dev@408 180 this.removeAnimation = () => {
dev@408 181 remover();
dev@408 182 this.removeAnimation = () => {};
dev@408 183 };
dev@408 184 }
dev@170 185 }