annotate src/app/app.component.ts @ 206:1db0bb28688b

Introduce id field for analysis items, currently optional but arguably shouldn't be. Currently an incrementing counter uniquely identifies an item - not ideal.
author Lucas Thompson <dev@lucas.im>
date Fri, 24 Mar 2017 16:14:57 +0000
parents f9088265a1fc
children 4865567d9e43
rev   line source
dev@193 1 import {Component, OnDestroy} from '@angular/core';
dev@193 2 import {
dev@193 3 AudioPlayerService,
dev@193 4 AudioResourceError, AudioResource
dev@193 5 } from "./services/audio-player/audio-player.service";
dev@47 6 import {FeatureExtractionService} from "./services/feature-extraction/feature-extraction.service";
dev@47 7 import {ExtractorOutputInfo} from "./feature-extraction-menu/feature-extraction-menu.component";
dev@89 8 import {DomSanitizer} from '@angular/platform-browser';
dev@89 9 import {MdIconRegistry} from '@angular/material';
dev@193 10 import {Subscription} from "rxjs";
dev@203 11 import {AnalysisItem} from "./analysis-item/analysis-item.component";
angular-cli@0 12
angular-cli@0 13 @Component({
angular-cli@0 14 selector: 'app-root',
angular-cli@0 15 templateUrl: './app.component.html',
angular-cli@0 16 styleUrls: ['./app.component.css']
angular-cli@0 17 })
dev@193 18 export class AppComponent implements OnDestroy {
dev@31 19 audioBuffer: AudioBuffer; // TODO consider revising
dev@49 20 canExtract: boolean;
dev@193 21 private onAudioDataSubscription: Subscription;
dev@203 22 private analyses: AnalysisItem[]; // TODO some immutable state container describing entire session
dev@203 23 private nRecordings: number; // TODO user control for naming a recording
dev@206 24 private countingId: number; // TODO improve uniquely identifying items
dev@203 25 private rootAudioUri: string;
dev@1 26
dev@47 27 constructor(private audioService: AudioPlayerService,
dev@89 28 private piperService: FeatureExtractionService,
dev@89 29 private iconRegistry: MdIconRegistry,
dev@89 30 private sanitizer: DomSanitizer) {
dev@203 31 this.analyses = [];
dev@49 32 this.canExtract = false;
dev@203 33 this.nRecordings = 0;
dev@206 34 this.countingId = 1;
dev@206 35
dev@89 36 iconRegistry.addSvgIcon(
dev@89 37 'duck',
dev@89 38 sanitizer.bypassSecurityTrustResourceUrl('assets/duck.svg')
dev@89 39 );
dev@193 40
dev@193 41 this.onAudioDataSubscription = this.audioService.audioLoaded$.subscribe(
dev@193 42 resource => {
dev@193 43 const wasError = (resource as AudioResourceError).message != null;
dev@193 44 if (wasError) {
dev@203 45 this.analyses.shift();
dev@193 46 this.canExtract = false;
dev@193 47 } else {
dev@193 48 this.audioBuffer = (resource as AudioResource).samples;
dev@193 49 if (this.audioBuffer) {
dev@193 50 this.canExtract = true;
dev@193 51 }
dev@193 52 }
dev@193 53 }
dev@193 54 );
dev@48 55 }
dev@16 56
dev@134 57 onFileOpened(file: File | Blob) {
dev@49 58 this.canExtract = false;
dev@203 59 const url = this.audioService.loadAudio(file);
dev@203 60 this.rootAudioUri = url; // TODO this isn't going to work to id previously loaded files
dev@203 61
dev@203 62 // TODO is it safe to assume it is a recording?
dev@203 63 const title = (file instanceof File) ?
dev@203 64 (file as File).name : `Recording ${this.nRecordings++}`;
dev@203 65
dev@203 66 if (this.analyses.filter(item => item.title === title).length > 0) {
dev@203 67 // TODO this reveals how brittle the current name / uri based id is
dev@203 68 // need something more robust, and also need to notify the user
dev@203 69 // in a suitable way in the actual event of a duplicate file
dev@203 70 console.warn('There is already a notebook based on this audio file.');
dev@203 71 return;
dev@203 72 }
dev@203 73
dev@203 74 // TODO re-ordering of items for display
dev@203 75 // , one alternative is a Angular Pipe / Filter for use in the Template
dev@203 76 this.analyses.unshift({
dev@203 77 rootAudioUri: url,
dev@203 78 hasSharedTimeline: true,
dev@203 79 extractorKey: 'not:real',
dev@203 80 isRoot: true,
dev@203 81 title: title,
dev@206 82 description: new Date().toLocaleString(),
dev@206 83 id: `${this.countingId++}`
dev@203 84 });
dev@16 85 }
dev@47 86
dev@48 87 extractFeatures(outputInfo: ExtractorOutputInfo): void {
dev@50 88 if (!this.canExtract || !outputInfo) return;
dev@49 89 this.canExtract = false;
dev@203 90
dev@203 91 this.analyses.unshift({
dev@203 92 rootAudioUri: this.rootAudioUri,
dev@203 93 hasSharedTimeline: true,
dev@203 94 extractorKey: outputInfo.combinedKey,
dev@203 95 isRoot: false,
dev@203 96 title: outputInfo.name,
dev@206 97 description: outputInfo.outputId,
dev@206 98 id: `${this.countingId++}`
dev@203 99 });
dev@203 100
dev@63 101 this.piperService.collect({
dev@47 102 audioData: [...Array(this.audioBuffer.numberOfChannels).keys()]
dev@47 103 .map(i => this.audioBuffer.getChannelData(i)),
dev@47 104 audioFormat: {
dev@47 105 sampleRate: this.audioBuffer.sampleRate,
dev@47 106 channelCount: this.audioBuffer.numberOfChannels
dev@47 107 },
dev@47 108 key: outputInfo.extractorKey,
dev@47 109 outputId: outputInfo.outputId
dev@50 110 }).then(() => {
dev@49 111 this.canExtract = true;
dev@115 112 }).catch(err => {
dev@115 113 this.canExtract = true;
dev@115 114 console.error(err)
dev@115 115 });
dev@47 116 }
dev@193 117
dev@193 118 ngOnDestroy(): void {
dev@193 119 this.onAudioDataSubscription.unsubscribe();
dev@193 120 }
angular-cli@0 121 }