comparison src/app/app.component.ts @ 350:524f5cd75737

Split AnalysisItem out into individual types for root audio items and features. This is messy as is, these need revising and should perhaps be actual concrete types with methods.
author Lucas Thompson <dev@lucas.im>
date Fri, 26 May 2017 12:59:41 +0100
parents 82d476b976e0
children 02e7be2daf31
comparison
equal deleted inserted replaced
349:bf038a51f7e3 350:524f5cd75737
6 import {FeatureExtractionService} from './services/feature-extraction/feature-extraction.service'; 6 import {FeatureExtractionService} from './services/feature-extraction/feature-extraction.service';
7 import {ExtractorOutputInfo} from './feature-extraction-menu/feature-extraction-menu.component'; 7 import {ExtractorOutputInfo} from './feature-extraction-menu/feature-extraction-menu.component';
8 import {DomSanitizer} from '@angular/platform-browser'; 8 import {DomSanitizer} from '@angular/platform-browser';
9 import {MdIconRegistry} from '@angular/material'; 9 import {MdIconRegistry} from '@angular/material';
10 import {Subscription} from 'rxjs/Subscription'; 10 import {Subscription} from 'rxjs/Subscription';
11 import {AnalysisItem} from './analysis-item/analysis-item.component'; 11 import {
12 AnalysisItem, isRootAudioItem,
13 Item, PendingAnalysisItem, PendingRootAudioItem, RootAudioItem
14 } from './analysis-item/analysis-item.component';
12 import {OnSeekHandler} from './playhead/PlayHeadHelpers'; 15 import {OnSeekHandler} from './playhead/PlayHeadHelpers';
13 16
14 class PersistentStack<T> { 17 class PersistentStack<T> {
15 private stack: T[]; 18 private stack: T[];
16 private history: T[][]; 19 private history: T[][];
69 export class AppComponent implements OnDestroy { 72 export class AppComponent implements OnDestroy {
70 audioBuffer: AudioBuffer; // TODO consider revising 73 audioBuffer: AudioBuffer; // TODO consider revising
71 canExtract: boolean; 74 canExtract: boolean;
72 private onAudioDataSubscription: Subscription; 75 private onAudioDataSubscription: Subscription;
73 private onProgressUpdated: Subscription; 76 private onProgressUpdated: Subscription;
74 private analyses: PersistentStack<AnalysisItem>; // TODO some immutable state container describing entire session 77 private analyses: PersistentStack<Item>; // TODO some immutable state container describing entire session
75 private nRecordings: number; // TODO user control for naming a recording 78 private nRecordings: number; // TODO user control for naming a recording
76 private countingId: number; // TODO improve uniquely identifying items 79 private countingId: number; // TODO improve uniquely identifying items
77 private rootAudioUri: string; 80 private rootAudioItem: RootAudioItem;
78 private onSeek: OnSeekHandler; 81 private onSeek: OnSeekHandler;
79 82
80 constructor(private audioService: AudioPlayerService, 83 constructor(private audioService: AudioPlayerService,
81 private featureService: FeatureExtractionService, 84 private featureService: FeatureExtractionService,
82 private iconRegistry: MdIconRegistry, 85 private iconRegistry: MdIconRegistry,
98 if (wasError) { 101 if (wasError) {
99 this.analyses.shift(); 102 this.analyses.shift();
100 this.canExtract = false; 103 this.canExtract = false;
101 } else { 104 } else {
102 this.audioBuffer = (resource as AudioResource).samples; 105 this.audioBuffer = (resource as AudioResource).samples;
106 this.rootAudioItem.audioData = this.audioBuffer;
103 if (this.audioBuffer) { 107 if (this.audioBuffer) {
104 this.canExtract = true; 108 this.canExtract = true;
105 const currentRootIndex = this.analyses.findIndex(val => { 109 const currentRootIndex = this.analyses.findIndex(val => {
106 return val.rootAudioUri === this.rootAudioUri && val.isRoot; 110 return isRootAudioItem(val) && val.uri === this.rootAudioItem.uri;
107 }); 111 });
108 if (currentRootIndex !== -1) { 112 if (currentRootIndex !== -1) {
109 this.analyses.set( 113 this.analyses.set(
110 currentRootIndex, 114 currentRootIndex,
111 Object.assign( 115 Object.assign(
139 } 143 }
140 144
141 onFileOpened(file: File | Blob) { 145 onFileOpened(file: File | Blob) {
142 this.canExtract = false; 146 this.canExtract = false;
143 const url = this.audioService.loadAudio(file); 147 const url = this.audioService.loadAudio(file);
144 this.rootAudioUri = url; // TODO this isn't going to work to id previously loaded files
145
146 // TODO is it safe to assume it is a recording? 148 // TODO is it safe to assume it is a recording?
147 const title = (file instanceof File) ? 149 const title = (file instanceof File) ?
148 (file as File).name : `Recording ${this.nRecordings++}`; 150 (file as File).name : `Recording ${this.nRecordings++}`;
149 151
150 if (this.analyses.filter(item => item.title === title).length > 0) { 152 if (this.analyses.filter(item => item.title === title).length > 0) {
153 // in a suitable way in the actual event of a duplicate file 155 // in a suitable way in the actual event of a duplicate file
154 console.warn('There is already a notebook based on this audio file.'); 156 console.warn('There is already a notebook based on this audio file.');
155 return; 157 return;
156 } 158 }
157 159
158 // TODO re-ordering of items for display 160 const pending = {
159 // , one alternative is a Angular Pipe / Filter for use in the Template 161 uri: url,
160 this.analyses.unshift({
161 rootAudioUri: url,
162 hasSharedTimeline: true, 162 hasSharedTimeline: true,
163 extractorKey: 'not:real',
164 isRoot: true,
165 title: title, 163 title: title,
166 description: new Date().toLocaleString(), 164 description: new Date().toLocaleString(),
167 id: `${++this.countingId}` 165 id: `${++this.countingId}`
168 }); 166 } as PendingRootAudioItem;
167 this.rootAudioItem = pending as RootAudioItem; // TODO this is silly
168
169 // TODO re-ordering of items for display
170 // , one alternative is a Angular Pipe / Filter for use in the Template
171 this.analyses.unshift(pending);
169 } 172 }
170 173
171 extractFeatures(outputInfo: ExtractorOutputInfo): void { 174 extractFeatures(outputInfo: ExtractorOutputInfo): void {
172 if (!this.canExtract || !outputInfo) { 175 if (!this.canExtract || !outputInfo) {
173 return; 176 return;
174 } 177 }
175 178
176 this.canExtract = false; 179 this.canExtract = false;
177 180
178 this.analyses.unshift({ 181 const placeholderCard: PendingAnalysisItem = {
179 rootAudioUri: this.rootAudioUri, 182 parent: this.rootAudioItem,
180 hasSharedTimeline: true, 183 hasSharedTimeline: true,
181 extractorKey: outputInfo.combinedKey, 184 extractorKey: outputInfo.combinedKey,
182 isRoot: false,
183 title: outputInfo.name, 185 title: outputInfo.name,
184 description: outputInfo.outputId, 186 description: outputInfo.outputId,
185 id: `${++this.countingId}`, 187 id: `${++this.countingId}`,
186 progress: 0 188 progress: 0
187 }); 189 };
190 this.analyses.unshift(placeholderCard);
188 191
189 this.featureService.extract(`${this.countingId}`, { 192 this.featureService.extract(`${this.countingId}`, {
190 audioData: [...Array(this.audioBuffer.numberOfChannels).keys()] 193 audioData: [...Array(this.audioBuffer.numberOfChannels).keys()]
191 .map(i => this.audioBuffer.getChannelData(i)), 194 .map(i => this.audioBuffer.getChannelData(i)),
192 audioFormat: { 195 audioFormat: {