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 }
|