dev@193: import {Component, OnDestroy} from '@angular/core'; dev@193: import { dev@193: AudioPlayerService, dev@193: AudioResourceError, AudioResource dev@193: } from "./services/audio-player/audio-player.service"; dev@47: import {FeatureExtractionService} from "./services/feature-extraction/feature-extraction.service"; dev@47: import {ExtractorOutputInfo} from "./feature-extraction-menu/feature-extraction-menu.component"; dev@89: import {DomSanitizer} from '@angular/platform-browser'; dev@89: import {MdIconRegistry} from '@angular/material'; dev@193: import {Subscription} from "rxjs"; dev@203: import {AnalysisItem} from "./analysis-item/analysis-item.component"; angular-cli@0: angular-cli@0: @Component({ angular-cli@0: selector: 'app-root', angular-cli@0: templateUrl: './app.component.html', angular-cli@0: styleUrls: ['./app.component.css'] angular-cli@0: }) dev@193: export class AppComponent implements OnDestroy { dev@31: audioBuffer: AudioBuffer; // TODO consider revising dev@49: canExtract: boolean; dev@193: private onAudioDataSubscription: Subscription; dev@226: private onProgressUpdated: Subscription; dev@203: private analyses: AnalysisItem[]; // TODO some immutable state container describing entire session dev@203: private nRecordings: number; // TODO user control for naming a recording dev@206: private countingId: number; // TODO improve uniquely identifying items dev@203: private rootAudioUri: string; dev@1: dev@47: constructor(private audioService: AudioPlayerService, dev@89: private piperService: FeatureExtractionService, dev@89: private iconRegistry: MdIconRegistry, dev@89: private sanitizer: DomSanitizer) { dev@203: this.analyses = []; dev@49: this.canExtract = false; dev@203: this.nRecordings = 0; dev@226: this.countingId = 0; dev@206: dev@89: iconRegistry.addSvgIcon( dev@89: 'duck', dev@89: sanitizer.bypassSecurityTrustResourceUrl('assets/duck.svg') dev@89: ); dev@193: dev@193: this.onAudioDataSubscription = this.audioService.audioLoaded$.subscribe( dev@193: resource => { dev@193: const wasError = (resource as AudioResourceError).message != null; dev@193: if (wasError) { dev@203: this.analyses.shift(); dev@193: this.canExtract = false; dev@193: } else { dev@193: this.audioBuffer = (resource as AudioResource).samples; dev@193: if (this.audioBuffer) { dev@193: this.canExtract = true; dev@193: } dev@193: } dev@193: } dev@193: ); dev@226: this.onProgressUpdated = this.piperService.progressUpdated$.subscribe( dev@226: progress => { dev@226: const index = this.analyses.findIndex(val => val.id === progress.id); dev@226: if (index === -1) return; dev@226: this.analyses[index].progress = progress.value; dev@226: } dev@226: ); dev@48: } dev@16: dev@134: onFileOpened(file: File | Blob) { dev@49: this.canExtract = false; dev@203: const url = this.audioService.loadAudio(file); dev@203: this.rootAudioUri = url; // TODO this isn't going to work to id previously loaded files dev@203: dev@203: // TODO is it safe to assume it is a recording? dev@203: const title = (file instanceof File) ? dev@203: (file as File).name : `Recording ${this.nRecordings++}`; dev@203: dev@203: if (this.analyses.filter(item => item.title === title).length > 0) { dev@203: // TODO this reveals how brittle the current name / uri based id is dev@203: // need something more robust, and also need to notify the user dev@203: // in a suitable way in the actual event of a duplicate file dev@203: console.warn('There is already a notebook based on this audio file.'); dev@203: return; dev@203: } dev@203: dev@203: // TODO re-ordering of items for display dev@203: // , one alternative is a Angular Pipe / Filter for use in the Template dev@203: this.analyses.unshift({ dev@203: rootAudioUri: url, dev@203: hasSharedTimeline: true, dev@203: extractorKey: 'not:real', dev@203: isRoot: true, dev@203: title: title, dev@206: description: new Date().toLocaleString(), dev@226: id: `${++this.countingId}` dev@203: }); dev@16: } dev@47: dev@48: extractFeatures(outputInfo: ExtractorOutputInfo): void { dev@50: if (!this.canExtract || !outputInfo) return; dev@49: this.canExtract = false; dev@203: dev@203: this.analyses.unshift({ dev@203: rootAudioUri: this.rootAudioUri, dev@203: hasSharedTimeline: true, dev@203: extractorKey: outputInfo.combinedKey, dev@203: isRoot: false, dev@203: title: outputInfo.name, dev@206: description: outputInfo.outputId, dev@226: id: `${++this.countingId}` dev@203: }); dev@203: dev@226: this.piperService.extract(`${this.countingId}`, { dev@47: audioData: [...Array(this.audioBuffer.numberOfChannels).keys()] dev@47: .map(i => this.audioBuffer.getChannelData(i)), dev@47: audioFormat: { dev@47: sampleRate: this.audioBuffer.sampleRate, dev@226: channelCount: this.audioBuffer.numberOfChannels, dev@226: length: this.audioBuffer.length dev@47: }, dev@47: key: outputInfo.extractorKey, dev@47: outputId: outputInfo.outputId dev@50: }).then(() => { dev@49: this.canExtract = true; dev@115: }).catch(err => { dev@115: this.canExtract = true; dev@226: this.analyses.shift(); dev@226: console.error(`Error whilst extracting: ${err}`); dev@115: }); dev@47: } dev@193: dev@193: ngOnDestroy(): void { dev@193: this.onAudioDataSubscription.unsubscribe(); dev@226: this.onProgressUpdated.unsubscribe(); dev@193: } angular-cli@0: }