annotate src/app/Session.ts @ 465:caef9a302bec

Add methods for mutating the current state without updating the history (naming might not be that declarative).
author Lucas Thompson <dev@lucas.im>
date Fri, 30 Jun 2017 14:49:40 +0100
parents 2624bb55dbf6
children b963af7e6eaf
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@462 83 this.history.push([...this.stack]);
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@465 93 const newLength = this.unshift(item);
dev@460 94 this.history.push([...this.stack]);
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@460 104 filter(predicate: (value: T, index: number, array: T[]) => boolean): T[] {
dev@460 105 return this.stack.filter(predicate);
dev@460 106 }
dev@460 107
dev@460 108 get(index: number): T {
dev@460 109 return this.stack[index];
dev@460 110 }
dev@460 111
dev@460 112 set(index: number, value: T) {
dev@465 113 this.setMutating(index, value);
dev@465 114 this.history.push([...this.stack]);
dev@465 115 }
dev@465 116
dev@465 117 setMutating(index: number, value: T) {
dev@460 118 this.stack = [
dev@460 119 ...this.stack.slice(0, index),
dev@460 120 value,
dev@460 121 ...this.stack.slice(index + 1)
dev@460 122 ];
dev@460 123 }
dev@460 124
dev@460 125 map<U>(transform: (value: T, index: number, array: T[]) => U): U[] {
dev@460 126 return this.stack.map(transform);
dev@460 127 }
dev@460 128
dev@460 129 reduce<U>(reducer: (previousValue: U,
dev@460 130 currentValue: T,
dev@460 131 currentIndex: number,
dev@460 132 array: T[]) => U,
dev@460 133 initialValue: U): U {
dev@460 134 return this.stack.reduce(reducer, initialValue);
dev@460 135 }
dev@460 136
dev@460 137 remove(...indices: number[]) {
dev@460 138 this.stack = this.stack.reduce((acc, item, i) => {
dev@460 139 if (!indices.includes(i)) {
dev@460 140 acc.push(item);
dev@460 141 }
dev@460 142 return acc;
dev@460 143 }, [] as T[]);
dev@462 144 this.history.push([...this.stack]);
dev@462 145 }
dev@462 146
dev@462 147 stepBack(): void {
dev@462 148 const latest = this.history.length - 1;
dev@462 149 if (++this.historyOffset <= latest) {
dev@462 150 this.stack = this.history[latest - this.historyOffset];
dev@462 151 } else {
dev@462 152 this.historyOffset = latest;
dev@462 153 }
dev@462 154 }
dev@462 155
dev@462 156 stepForward(): void {
dev@462 157 const latest = this.history.length - 1;
dev@462 158 if (--this.historyOffset >= 0) {
dev@462 159 this.stack = this.history[latest - this.historyOffset];
dev@462 160 } else {
dev@462 161 this.historyOffset = 0;
dev@462 162 }
dev@460 163 }
dev@460 164
dev@460 165 toIterable(): Iterable<T> {
dev@460 166 return this.stack;
dev@460 167 }
dev@460 168 }