Mercurial > hg > ugly-duckling
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: { |