comparison src/app/app.component.ts @ 464:50f61d1945db

Hook up some buttons for navigating history (undo / redo). Some refactoring to allow for the audio player to get updated as a consequence of a state change (the audio related to the current top of the stack is used).
author Lucas Thompson <dev@lucas.im>
date Fri, 30 Jun 2017 14:01:22 +0100
parents ccce2c09502e
children 8820a133bcf5
comparison
equal deleted inserted replaced
463:c9c6b01e9b4f 464:50f61d1945db
17 isPendingAnalysisItem, 17 isPendingAnalysisItem,
18 isPendingRootAudioItem, 18 isPendingRootAudioItem,
19 isLoadedRootAudioItem, 19 isLoadedRootAudioItem,
20 Item, 20 Item,
21 RootAudioItem, 21 RootAudioItem,
22 LoadedRootAudioItem 22 getRootAudioItem
23 } from './analysis-item/AnalysisItem'; 23 } from './analysis-item/AnalysisItem';
24 import {OnSeekHandler} from './playhead/PlayHeadHelpers'; 24 import {OnSeekHandler} from './playhead/PlayHeadHelpers';
25 import {UrlResourceLifetimeManager} from './app.module'; 25 import {UrlResourceLifetimeManager} from './app.module';
26 import {createExtractionRequest} from './analysis-item/AnalysisItem'; 26 import {createExtractionRequest} from './analysis-item/AnalysisItem';
27 import {PersistentStack} from './Session'; 27 import {PersistentStack} from './Session';
36 private onAudioDataSubscription: Subscription; 36 private onAudioDataSubscription: Subscription;
37 private onProgressUpdated: Subscription; 37 private onProgressUpdated: Subscription;
38 private analyses: PersistentStack<Item>; // TODO some immutable state container describing entire session 38 private analyses: PersistentStack<Item>; // TODO some immutable state container describing entire session
39 private nRecordings: number; // TODO user control for naming a recording 39 private nRecordings: number; // TODO user control for naming a recording
40 private countingId: number; // TODO improve uniquely identifying items 40 private countingId: number; // TODO improve uniquely identifying items
41 private rootAudioItem: LoadedRootAudioItem;
42 private onSeek: OnSeekHandler; 41 private onSeek: OnSeekHandler;
43 42
44 constructor(private audioService: AudioPlayerService, 43 constructor(private audioService: AudioPlayerService,
45 private featureService: FeatureExtractionService, 44 private featureService: FeatureExtractionService,
46 private iconRegistry: MdIconRegistry, 45 private iconRegistry: MdIconRegistry,
51 this.analyses = new PersistentStack<AnalysisItem>(); 50 this.analyses = new PersistentStack<AnalysisItem>();
52 this.canExtract = false; 51 this.canExtract = false;
53 this.nRecordings = 0; 52 this.nRecordings = 0;
54 this.countingId = 0; 53 this.countingId = 0;
55 this.onSeek = (time) => this.audioService.seekTo(time); 54 this.onSeek = (time) => this.audioService.seekTo(time);
56 this.rootAudioItem = {} as any; // TODO eugh
57 55
58 iconRegistry.addSvgIcon( 56 iconRegistry.addSvgIcon(
59 'duck', 57 'duck',
60 sanitizer.bypassSecurityTrustResourceUrl('assets/duck.svg') 58 sanitizer.bypassSecurityTrustResourceUrl('assets/duck.svg')
61 ); 59 );
65 const wasError = (resource as AudioResourceError).message != null; 63 const wasError = (resource as AudioResourceError).message != null;
66 if (wasError) { 64 if (wasError) {
67 this.analyses.shift(); 65 this.analyses.shift();
68 this.canExtract = false; 66 this.canExtract = false;
69 } else { 67 } else {
70 this.rootAudioItem.audioData = (resource as AudioResource).samples; 68 const audioData = (resource as AudioResource).samples;
71 if (this.rootAudioItem.audioData) { 69 if (audioData) {
70 const rootAudio = getRootAudioItem(this.analyses.get(0));
72 this.canExtract = true; 71 this.canExtract = true;
73 const currentRootIndex = this.analyses.findIndex(val => { 72 const currentRootIndex = this.analyses.findIndex(val => {
74 return isLoadedRootAudioItem(val) && val.uri === this.rootAudioItem.uri; 73 return isPendingRootAudioItem(val) && val.uri === rootAudio.uri;
75 }); 74 });
76 if (currentRootIndex !== -1) { 75 if (currentRootIndex !== -1) {
77 this.analyses.set( 76 this.analyses.set(
78 currentRootIndex, 77 currentRootIndex,
79 Object.assign( 78 Object.assign(
80 {}, 79 {},
81 this.analyses.get(currentRootIndex), 80 this.analyses.get(currentRootIndex),
82 {audioData: this.rootAudioItem.audioData} 81 {audioData}
83 ) 82 )
84 ); 83 );
85 } 84 }
86 } 85 }
87 } 86 }
128 description: new Date().toLocaleString(), 127 description: new Date().toLocaleString(),
129 id: `${++this.countingId}`, 128 id: `${++this.countingId}`,
130 mimeType: file.type, 129 mimeType: file.type,
131 isExportable: createExportableItem 130 isExportable: createExportableItem
132 } as RootAudioItem; 131 } as RootAudioItem;
133 this.rootAudioItem = pending as LoadedRootAudioItem; // TODO this is silly
134 132
135 // TODO re-ordering of items for display 133 // TODO re-ordering of items for display
136 // , one alternative is a Angular Pipe / Filter for use in the Template 134 // , one alternative is a Angular Pipe / Filter for use in the Template
137 this.analyses.unshift(pending); 135 this.analyses.unshift(pending);
138 } 136 }
142 return; 140 return;
143 } 141 }
144 142
145 this.canExtract = false; 143 this.canExtract = false;
146 144
147 const placeholderCard: AnalysisItem = { 145 const rootAudio = getRootAudioItem(this.analyses.get(0));
148 parent: this.rootAudioItem, 146
149 hasSharedTimeline: true, 147 if (isLoadedRootAudioItem(rootAudio)) {
150 extractorKey: outputInfo.extractorKey, 148 const placeholderCard: AnalysisItem = {
151 outputId: outputInfo.outputId, 149 parent: rootAudio,
152 title: outputInfo.name, 150 hasSharedTimeline: true,
153 description: outputInfo.outputId, 151 extractorKey: outputInfo.extractorKey,
154 id: `${++this.countingId}`, 152 outputId: outputInfo.outputId,
155 progress: 0 153 title: outputInfo.name,
156 }; 154 description: outputInfo.outputId,
157 this.analyses.unshift(placeholderCard); 155 id: `${++this.countingId}`,
158 this.sendExtractionRequest(placeholderCard); 156 progress: 0
159 return placeholderCard.id; 157 };
158 this.analyses.unshift(placeholderCard);
159 this.sendExtractionRequest(placeholderCard);
160 return placeholderCard.id;
161 }
162 throw new Error('Cannot extract. No audio loaded');
160 } 163 }
161 164
162 removeItem(item: Item): void { 165 removeItem(item: Item): void {
163 const indicesToRemove: number[] = this.analyses.reduce( 166 const indicesToRemove: number[] = this.analyses.reduce(
164 (toRemove, current, index) => { 167 (toRemove, current, index) => {
168 toRemove.push(index); 171 toRemove.push(index);
169 } 172 }
170 return toRemove; 173 return toRemove;
171 }, []); 174 }, []);
172 this.analyses.remove(...indicesToRemove); 175 this.analyses.remove(...indicesToRemove);
173 if (isPendingRootAudioItem(item)) {
174 if (this.rootAudioItem.uri === item.uri) {
175 this.audioService.unload();
176 const topItem = this.analyses.get(0);
177 const nullRootAudio: LoadedRootAudioItem = {uri: ''} as any; // TODO eugh
178
179 if (topItem) {
180 if (isPendingAnalysisItem(topItem)) {
181 this.rootAudioItem = topItem.parent as LoadedRootAudioItem;
182 } else if (isPendingRootAudioItem(topItem)) {
183 this.rootAudioItem = topItem as LoadedRootAudioItem;
184 } else {
185 this.rootAudioItem = nullRootAudio;
186 }
187 } else {
188 this.rootAudioItem = nullRootAudio;
189 }
190 if (this.rootAudioItem) {
191 this.audioService.loadAudioFromUri(this.rootAudioItem.uri);
192 }
193 } else {
194 this.resourceManager.revokeUrlToResource(item.uri);
195 }
196 }
197 } 176 }
198 177
199 ngOnDestroy(): void { 178 ngOnDestroy(): void {
200 this.onAudioDataSubscription.unsubscribe(); 179 this.onAudioDataSubscription.unsubscribe();
201 this.onProgressUpdated.unsubscribe(); 180 this.onProgressUpdated.unsubscribe();