annotate src/app/Session.ts @ 462:2624bb55dbf6

Fix ordering of adding to history (after an action). Add functions for stepping through the history.
author Lucas Thompson <dev@lucas.im>
date Fri, 30 Jun 2017 13:59:21 +0100
parents ccce2c09502e
children caef9a302bec
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@460 75 shift(): T {
dev@460 76 const item = this.stack[0];
dev@460 77 this.stack = this.stack.slice(1);
dev@462 78 this.history.push([...this.stack]);
dev@460 79 return item;
dev@460 80 }
dev@460 81
dev@460 82 unshift(item: T): number {
dev@462 83 this.stack = [item, ...this.stack];
dev@460 84 this.history.push([...this.stack]);
dev@460 85 return this.stack.length;
dev@460 86 }
dev@460 87
dev@460 88 findIndex(predicate: (value: T,
dev@460 89 index: number,
dev@460 90 array: T[]) => boolean): number {
dev@460 91 return this.stack.findIndex(predicate);
dev@460 92 }
dev@460 93
dev@460 94 filter(predicate: (value: T, index: number, array: T[]) => boolean): T[] {
dev@460 95 return this.stack.filter(predicate);
dev@460 96 }
dev@460 97
dev@460 98 get(index: number): T {
dev@460 99 return this.stack[index];
dev@460 100 }
dev@460 101
dev@460 102 set(index: number, value: T) {
dev@460 103 this.stack = [
dev@460 104 ...this.stack.slice(0, index),
dev@460 105 value,
dev@460 106 ...this.stack.slice(index + 1)
dev@460 107 ];
dev@462 108 this.history.push([...this.stack]);
dev@460 109 }
dev@460 110
dev@460 111 map<U>(transform: (value: T, index: number, array: T[]) => U): U[] {
dev@460 112 return this.stack.map(transform);
dev@460 113 }
dev@460 114
dev@460 115 reduce<U>(reducer: (previousValue: U,
dev@460 116 currentValue: T,
dev@460 117 currentIndex: number,
dev@460 118 array: T[]) => U,
dev@460 119 initialValue: U): U {
dev@460 120 return this.stack.reduce(reducer, initialValue);
dev@460 121 }
dev@460 122
dev@460 123 remove(...indices: number[]) {
dev@460 124 this.stack = this.stack.reduce((acc, item, i) => {
dev@460 125 if (!indices.includes(i)) {
dev@460 126 acc.push(item);
dev@460 127 }
dev@460 128 return acc;
dev@460 129 }, [] as T[]);
dev@462 130 this.history.push([...this.stack]);
dev@462 131 }
dev@462 132
dev@462 133 stepBack(): void {
dev@462 134 const latest = this.history.length - 1;
dev@462 135 if (++this.historyOffset <= latest) {
dev@462 136 this.stack = this.history[latest - this.historyOffset];
dev@462 137 } else {
dev@462 138 this.historyOffset = latest;
dev@462 139 }
dev@462 140 }
dev@462 141
dev@462 142 stepForward(): void {
dev@462 143 const latest = this.history.length - 1;
dev@462 144 if (--this.historyOffset >= 0) {
dev@462 145 this.stack = this.history[latest - this.historyOffset];
dev@462 146 } else {
dev@462 147 this.historyOffset = 0;
dev@462 148 }
dev@460 149 }
dev@460 150
dev@460 151 toIterable(): Iterable<T> {
dev@460 152 return this.stack;
dev@460 153 }
dev@460 154 }