annotate src/app/Session.ts @ 509:041468f553e1 tip master

Merge pull request #57 from LucasThompson/fix/session-stack-max-call-stack Fix accidental recursion in PersistentStack
author Lucas Thompson <LucasThompson@users.noreply.github.com>
date Mon, 27 Nov 2017 11:04:30 +0000
parents 7ac80ad913c1
children
rev   line source
dev@460 1 /**
dev@460 2 * Created by lucast on 08/06/2017.
dev@460 3 */
dev@460 4 import {
dev@460 5 Item,
dev@460 6 RootAudioItem
dev@460 7 } from './analysis-item/AnalysisItem';
dev@460 8
dev@460 9 export const exampleSession: SerialisedNotebook = {
dev@460 10 root: {
dev@460 11 id: '1',
dev@460 12 hasSharedTimeline: true,
dev@460 13 title: 'Drum Loop',
dev@460 14 description: 'Remotely hosted audio file',
dev@460 15 uri: 'https://piper-audio.github.io/waves-ui-piper/examples/assets/drum-loop.wav'
dev@460 16 },
dev@460 17 analyses: [
dev@460 18 {
dev@460 19 id: '2',
dev@460 20 hasSharedTimeline: true,
dev@460 21 extractorKey: 'vamp-example-plugins:amplitudefollower',
dev@460 22 outputId: 'amplitude',
dev@460 23 title: 'Amplitude',
dev@460 24 description: 'amplitude'
dev@460 25 },
dev@460 26 {
dev@460 27 id: '3',
dev@460 28 hasSharedTimeline: true,
dev@460 29 extractorKey: 'vamp-example-plugins:powerspectrum',
dev@460 30 outputId: 'powerspectrum',
dev@460 31 title: 'Simple Power Spectrum',
dev@460 32 description: 'powerspectrum'
dev@460 33 },
dev@460 34
dev@460 35 ]
dev@460 36 };
dev@460 37
dev@460 38 export interface SerialisedAnalysisItem extends Item {
dev@460 39 extractorKey: string;
dev@460 40 outputId: string;
dev@460 41 }
dev@460 42
dev@460 43 export interface SerialisedNotebook {
dev@460 44 root: RootAudioItem;
dev@460 45 analyses: SerialisedAnalysisItem[];
dev@460 46 }
dev@460 47
dev@460 48 export type ResourceRetriever = (url: string) => Promise<Blob>;
dev@460 49
dev@460 50 export const downloadResource: ResourceRetriever = async (url) => {
dev@460 51 const response = await fetch(url);
dev@460 52 const mimeType = response.headers.get('content-type');
dev@460 53 // Safari's fetch.blob implementation doesn't populate the type property
dev@460 54 // causing the audio player to fail due to an unsupported type.
dev@460 55 // Manually create a blob from an array buffer and the content type in
dev@460 56 // the response object
dev@460 57 const arrayBufferToBlob = async () => {
dev@460 58 const arrayBuffer = await response.arrayBuffer();
dev@460 59 return new Blob([arrayBuffer], {type: mimeType});
dev@460 60 };
dev@460 61 return mimeType ? arrayBufferToBlob() : response.blob();
dev@460 62 };
dev@460 63
dev@460 64 export class PersistentStack<T> {
dev@460 65 private stack: T[];
dev@460 66 private history: T[][];
dev@462 67 private historyOffset: number;
dev@460 68
dev@460 69 constructor() {
dev@460 70 this.stack = [];
dev@462 71 this.history = [[]];
dev@462 72 this.historyOffset = 0;
dev@460 73 }
dev@460 74
dev@465 75 shiftMutating(): T {
dev@460 76 const item = this.stack[0];
dev@460 77 this.stack = this.stack.slice(1);
dev@465 78 return item;
dev@465 79 }
dev@465 80
dev@465 81 shift(): T {
dev@465 82 const item = this.shiftMutating();
dev@467 83 this.updateHistory();
dev@460 84 return item;
dev@460 85 }
dev@460 86
dev@465 87 unshiftMutating(item: T): number {
dev@465 88 this.stack = [item, ...this.stack];
dev@465 89 return this.stack.length;
dev@465 90 }
dev@465 91
dev@460 92 unshift(item: T): number {
dev@508 93 const newLength = this.unshiftMutating(item);
dev@467 94 this.updateHistory();
dev@465 95 return newLength;
dev@460 96 }
dev@460 97
dev@460 98 findIndex(predicate: (value: T,
dev@460 99 index: number,
dev@460 100 array: T[]) => boolean): number {
dev@460 101 return this.stack.findIndex(predicate);
dev@460 102 }
dev@460 103
dev@485 104 findIndexAndUse(predicate: (value: T,
dev@485 105 index: number,
dev@485 106 array: T[]) => boolean,
dev@485 107 use: (index: number) => void): boolean {
dev@485 108 const index = this.stack.findIndex(predicate);
dev@485 109 const didFind = index !== -1;
dev@485 110 if (didFind) {
dev@485 111 use(index);
dev@485 112 }
dev@485 113 return didFind;
dev@485 114 }
dev@485 115
dev@460 116 filter(predicate: (value: T, index: number, array: T[]) => boolean): T[] {
dev@460 117 return this.stack.filter(predicate);
dev@460 118 }
dev@460 119
dev@460 120 get(index: number): T {
dev@460 121 return this.stack[index];
dev@460 122 }
dev@460 123
dev@460 124 set(index: number, value: T) {
dev@465 125 this.setMutating(index, value);
dev@467 126 this.updateHistory();
dev@465 127 }
dev@465 128
dev@465 129 setMutating(index: number, value: T) {
dev@460 130 this.stack = [
dev@460 131 ...this.stack.slice(0, index),
dev@460 132 value,
dev@460 133 ...this.stack.slice(index + 1)
dev@460 134 ];
dev@460 135 }
dev@460 136
dev@460 137 map<U>(transform: (value: T, index: number, array: T[]) => U): U[] {
dev@460 138 return this.stack.map(transform);
dev@460 139 }
dev@460 140
dev@460 141 reduce<U>(reducer: (previousValue: U,
dev@460 142 currentValue: T,
dev@460 143 currentIndex: number,
dev@460 144 array: T[]) => U,
dev@460 145 initialValue: U): U {
dev@460 146 return this.stack.reduce(reducer, initialValue);
dev@460 147 }
dev@460 148
dev@460 149 remove(...indices: number[]) {
dev@460 150 this.stack = this.stack.reduce((acc, item, i) => {
dev@460 151 if (!indices.includes(i)) {
dev@460 152 acc.push(item);
dev@460 153 }
dev@460 154 return acc;
dev@460 155 }, [] as T[]);
dev@467 156 this.updateHistory();
dev@462 157 }
dev@462 158
dev@462 159 stepBack(): void {
dev@462 160 const latest = this.history.length - 1;
dev@462 161 if (++this.historyOffset <= latest) {
dev@462 162 this.stack = this.history[latest - this.historyOffset];
dev@462 163 } else {
dev@462 164 this.historyOffset = latest;
dev@462 165 }
dev@462 166 }
dev@462 167
dev@462 168 stepForward(): void {
dev@462 169 const latest = this.history.length - 1;
dev@462 170 if (--this.historyOffset >= 0) {
dev@462 171 this.stack = this.history[latest - this.historyOffset];
dev@462 172 } else {
dev@462 173 this.historyOffset = 0;
dev@462 174 }
dev@460 175 }
dev@460 176
dev@468 177 toIterable(): Iterable<T> {
dev@468 178 return this.stack;
dev@468 179 }
dev@468 180
dev@468 181 private updateHistory(): void {
dev@467 182 if (this.historyOffset !== 0) {
dev@467 183 this.history = this.history.slice(
dev@467 184 0,
dev@467 185 this.history.length - this.historyOffset
dev@467 186 );
dev@467 187 this.historyOffset = 0;
dev@467 188 }
dev@467 189 this.history.push([...this.stack]);
dev@467 190 }
dev@460 191 }