dev@460: /** dev@460: * Created by lucast on 08/06/2017. dev@460: */ dev@460: import { dev@460: Item, dev@460: RootAudioItem dev@460: } from './analysis-item/AnalysisItem'; dev@460: dev@460: export const exampleSession: SerialisedNotebook = { dev@460: root: { dev@460: id: '1', dev@460: hasSharedTimeline: true, dev@460: title: 'Drum Loop', dev@460: description: 'Remotely hosted audio file', dev@460: uri: 'https://piper-audio.github.io/waves-ui-piper/examples/assets/drum-loop.wav' dev@460: }, dev@460: analyses: [ dev@460: { dev@460: id: '2', dev@460: hasSharedTimeline: true, dev@460: extractorKey: 'vamp-example-plugins:amplitudefollower', dev@460: outputId: 'amplitude', dev@460: title: 'Amplitude', dev@460: description: 'amplitude' dev@460: }, dev@460: { dev@460: id: '3', dev@460: hasSharedTimeline: true, dev@460: extractorKey: 'vamp-example-plugins:powerspectrum', dev@460: outputId: 'powerspectrum', dev@460: title: 'Simple Power Spectrum', dev@460: description: 'powerspectrum' dev@460: }, dev@460: dev@460: ] dev@460: }; dev@460: dev@460: export interface SerialisedAnalysisItem extends Item { dev@460: extractorKey: string; dev@460: outputId: string; dev@460: } dev@460: dev@460: export interface SerialisedNotebook { dev@460: root: RootAudioItem; dev@460: analyses: SerialisedAnalysisItem[]; dev@460: } dev@460: dev@460: export type ResourceRetriever = (url: string) => Promise; dev@460: dev@460: export const downloadResource: ResourceRetriever = async (url) => { dev@460: const response = await fetch(url); dev@460: const mimeType = response.headers.get('content-type'); dev@460: // Safari's fetch.blob implementation doesn't populate the type property dev@460: // causing the audio player to fail due to an unsupported type. dev@460: // Manually create a blob from an array buffer and the content type in dev@460: // the response object dev@460: const arrayBufferToBlob = async () => { dev@460: const arrayBuffer = await response.arrayBuffer(); dev@460: return new Blob([arrayBuffer], {type: mimeType}); dev@460: }; dev@460: return mimeType ? arrayBufferToBlob() : response.blob(); dev@460: }; dev@460: dev@460: export class PersistentStack { dev@460: private stack: T[]; dev@460: private history: T[][]; dev@462: private historyOffset: number; dev@460: dev@460: constructor() { dev@460: this.stack = []; dev@462: this.history = [[]]; dev@462: this.historyOffset = 0; dev@460: } dev@460: dev@465: shiftMutating(): T { dev@460: const item = this.stack[0]; dev@460: this.stack = this.stack.slice(1); dev@465: return item; dev@465: } dev@465: dev@465: shift(): T { dev@465: const item = this.shiftMutating(); dev@467: this.updateHistory(); dev@460: return item; dev@460: } dev@460: dev@465: unshiftMutating(item: T): number { dev@465: this.stack = [item, ...this.stack]; dev@465: return this.stack.length; dev@465: } dev@465: dev@460: unshift(item: T): number { dev@508: const newLength = this.unshiftMutating(item); dev@467: this.updateHistory(); dev@465: return newLength; dev@460: } dev@460: dev@460: findIndex(predicate: (value: T, dev@460: index: number, dev@460: array: T[]) => boolean): number { dev@460: return this.stack.findIndex(predicate); dev@460: } dev@460: dev@485: findIndexAndUse(predicate: (value: T, dev@485: index: number, dev@485: array: T[]) => boolean, dev@485: use: (index: number) => void): boolean { dev@485: const index = this.stack.findIndex(predicate); dev@485: const didFind = index !== -1; dev@485: if (didFind) { dev@485: use(index); dev@485: } dev@485: return didFind; dev@485: } dev@485: dev@460: filter(predicate: (value: T, index: number, array: T[]) => boolean): T[] { dev@460: return this.stack.filter(predicate); dev@460: } dev@460: dev@460: get(index: number): T { dev@460: return this.stack[index]; dev@460: } dev@460: dev@460: set(index: number, value: T) { dev@465: this.setMutating(index, value); dev@467: this.updateHistory(); dev@465: } dev@465: dev@465: setMutating(index: number, value: T) { dev@460: this.stack = [ dev@460: ...this.stack.slice(0, index), dev@460: value, dev@460: ...this.stack.slice(index + 1) dev@460: ]; dev@460: } dev@460: dev@460: map(transform: (value: T, index: number, array: T[]) => U): U[] { dev@460: return this.stack.map(transform); dev@460: } dev@460: dev@460: reduce(reducer: (previousValue: U, dev@460: currentValue: T, dev@460: currentIndex: number, dev@460: array: T[]) => U, dev@460: initialValue: U): U { dev@460: return this.stack.reduce(reducer, initialValue); dev@460: } dev@460: dev@460: remove(...indices: number[]) { dev@460: this.stack = this.stack.reduce((acc, item, i) => { dev@460: if (!indices.includes(i)) { dev@460: acc.push(item); dev@460: } dev@460: return acc; dev@460: }, [] as T[]); dev@467: this.updateHistory(); dev@462: } dev@462: dev@462: stepBack(): void { dev@462: const latest = this.history.length - 1; dev@462: if (++this.historyOffset <= latest) { dev@462: this.stack = this.history[latest - this.historyOffset]; dev@462: } else { dev@462: this.historyOffset = latest; dev@462: } dev@462: } dev@462: dev@462: stepForward(): void { dev@462: const latest = this.history.length - 1; dev@462: if (--this.historyOffset >= 0) { dev@462: this.stack = this.history[latest - this.historyOffset]; dev@462: } else { dev@462: this.historyOffset = 0; dev@462: } dev@460: } dev@460: dev@468: toIterable(): Iterable { dev@468: return this.stack; dev@468: } dev@468: dev@468: private updateHistory(): void { dev@467: if (this.historyOffset !== 0) { dev@467: this.history = this.history.slice( dev@467: 0, dev@467: this.history.length - this.historyOffset dev@467: ); dev@467: this.historyOffset = 0; dev@467: } dev@467: this.history.push([...this.stack]); dev@467: } dev@460: }