playback.worker.js.map (21149B)
1 {"version":3,"file":"bundles/78cd486dc4f7ffb56898/playback.worker.js","mappings":"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","sources":["webpack://element-web/./src/utils/arrays.ts","webpack://element-web/javascript/node_modules/.pnpm/source-map-loader@5.0.0_webpack@5.107.2/node_modules/source-map-loader/dist/packages/shared-components/dist/numbers.js","webpack://element-web/webpack/runtime/compat","webpack://element-web/./src/audio/consts.ts","webpack://element-web/./src/workers/playback.worker.ts"],"sourcesContent":["/*\nCopyright 2024 New Vector Ltd.\nCopyright 2020, 2021 The Matrix.org Foundation C.I.C.\n\nSPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport { percentageOf, percentageWithin } from \"@element-hq/web-shared-components/numbers\";\n\n/**\n * Quickly resample an array to have less/more data points. If an input which is larger\n * than the desired size is provided, it will be downsampled. Similarly, if the input\n * is smaller than the desired size then it will be upsampled.\n * @param {number[]} input The input array to resample.\n * @param {number} points The number of samples to end up with.\n * @returns {number[]} The resampled array.\n */\nexport function arrayFastResample(input: number[], points: number): number[] {\n if (input.length === points) return input; // short-circuit a complicated call\n\n // Heavily inspired by matrix-media-repo (used with permission)\n // https://github.com/turt2live/matrix-media-repo/blob/abe72c87d2e29/util/util_audio/fastsample.go#L10\n const samples: number[] = [];\n if (input.length > points) {\n // Danger: this loop can cause out of memory conditions if the input is too small.\n const everyNth = Math.round(input.length / points);\n for (let i = 0; i < input.length; i += everyNth) {\n samples.push(input[i]);\n }\n } else {\n // Smaller inputs mean we have to spread the values over the desired length. We\n // end up overshooting the target length in doing this, but we're not looking to\n // be super accurate so we'll let the sanity trims do their job.\n const spreadFactor = Math.ceil(points / input.length);\n for (const val of input) {\n samples.push(...arraySeed(val, spreadFactor));\n }\n }\n\n // Trim to size & return\n return arrayTrimFill(samples, points, arraySeed(input[input.length - 1], points));\n}\n\n/**\n * Attempts a smooth resample of the given array. This is functionally similar to arrayFastResample\n * though can take longer due to the smoothing of data.\n * @param {number[]} input The input array to resample.\n * @param {number} points The number of samples to end up with.\n * @returns {number[]} The resampled array.\n */\nexport function arraySmoothingResample(input: number[], points: number): number[] {\n if (input.length === points) return input; // short-circuit a complicated call\n\n let samples: number[] = [];\n if (input.length > points) {\n // We're downsampling. To preserve the curve we'll actually reduce our sample\n // selection and average some points between them.\n\n // All we're doing here is repeatedly averaging the waveform down to near our\n // target value. We don't average down to exactly our target as the loop might\n // never end, and we can over-average the data. Instead, we'll get as far as\n // we can and do a followup fast resample (the neighbouring points will be close\n // to the actual waveform, so we can get away with this safely).\n while (samples.length > points * 2 || samples.length === 0) {\n samples = [];\n for (let i = 1; i < input.length - 1; i += 2) {\n const prevPoint = input[i - 1];\n const nextPoint = input[i + 1];\n const currPoint = input[i];\n const average = (prevPoint + nextPoint + currPoint) / 3;\n samples.push(average);\n }\n input = samples;\n }\n\n return arrayFastResample(samples, points);\n } else {\n // In practice there's not much purpose in burning CPU for short arrays only to\n // end up with a result that can't possibly look much different than the fast\n // resample, so just skip ahead to the fast resample.\n return arrayFastResample(input, points);\n }\n}\n\n/**\n * Rescales the input array to have values that are inclusively within the provided\n * minimum and maximum.\n * @param {number[]} input The array to rescale.\n * @param {number} newMin The minimum value to scale to.\n * @param {number} newMax The maximum value to scale to.\n * @returns {number[]} The rescaled array.\n */\nexport function arrayRescale(input: number[], newMin: number, newMax: number): number[] {\n const min: number = Math.min(...input);\n const max: number = Math.max(...input);\n return input.map((v) => percentageWithin(percentageOf(v, min, max), newMin, newMax));\n}\n\n/**\n * Creates an array of the given length, seeded with the given value.\n * @param {T} val The value to seed the array with.\n * @param {number} length The length of the array to create.\n * @returns {T[]} The array.\n */\nexport function arraySeed<T>(val: T, length: number): T[] {\n // Size the array up front for performance, and use `fill` to let the browser\n // optimize the operation better than we can with a `for` loop, if it wants.\n return new Array<T>(length).fill(val);\n}\n\n/**\n * Trims or fills the array to ensure it meets the desired length. The seed array\n * given is pulled from to fill any missing slots - it is recommended that this be\n * at least `len` long. The resulting array will be exactly `len` long, either\n * trimmed from the source or filled with the some/all of the seed array.\n * @param {T[]} a The array to trim/fill.\n * @param {number} len The length to trim or fill to, as needed.\n * @param {T[]} seed Values to pull from if the array needs filling.\n * @returns {T[]} The resulting array of `len` length.\n */\nexport function arrayTrimFill<T>(a: T[], len: number, seed: T[]): T[] {\n // Dev note: we do length checks because the spread operator can result in some\n // performance penalties in more critical code paths. As a utility, it should be\n // as fast as possible to not cause a problem for the call stack, no matter how\n // critical that stack is.\n if (a.length === len) return a;\n if (a.length > len) return a.slice(0, len);\n return a.concat(seed.slice(0, len - a.length));\n}\n\n/**\n * Clones an array as fast as possible, retaining references of the array's values.\n * @param a The array to clone. Must be defined.\n * @returns A copy of the array.\n */\nexport function arrayFastClone<T>(a: T[]): T[] {\n return a.slice(0, a.length);\n}\n\n/**\n * Determines if the two arrays are different either in length, contents,\n * or order of those contents.\n * @param a The first array. Must be defined.\n * @param b The second array. Must be defined.\n * @returns True if they are different, false otherwise.\n */\nexport function arrayHasOrderChange(a: any[], b: any[]): boolean {\n if (a.length === b.length) {\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return true;\n }\n return false;\n } else {\n return true; // like arrayHasDiff, a difference in length is a natural change\n }\n}\n\n/**\n * Determines if two arrays are different through a shallow comparison.\n * @param a The first array. Must be defined.\n * @param b The second array. Must be defined.\n * @returns True if they are different, false otherwise.\n */\nexport function arrayHasDiff(a: any[], b: any[]): boolean {\n if (a.length === b.length) {\n // When the lengths are equal, check to see if either array is missing\n // an element from the other.\n if (b.some((i) => !a.includes(i))) return true;\n if (a.some((i) => !b.includes(i))) return true;\n\n // if all the keys are common, say so\n return false;\n } else {\n return true; // different lengths means they are naturally diverged\n }\n}\n\nexport type Diff<T> = { added: T[]; removed: T[] };\n\n/**\n * Performs a diff on two arrays. The result is what is different with the\n * first array (`added` in the returned object means objects in B that aren't\n * in A). Shallow comparisons are used to perform the diff.\n * @param a The first array. Must be defined.\n * @param b The second array. Must be defined.\n * @returns The diff between the arrays.\n */\nexport function arrayDiff<T>(a: T[], b: T[]): Diff<T> {\n return {\n added: b.filter((i) => !a.includes(i)),\n removed: a.filter((i) => !b.includes(i)),\n };\n}\n\n/**\n * Returns the intersection of two arrays.\n * @param a The first array. Must be defined.\n * @param b The second array. Must be defined.\n * @returns The intersection of the arrays.\n */\nexport function arrayIntersection<T>(a: T[], b: T[]): T[] {\n return a.filter((i) => b.includes(i));\n}\n\n/**\n * Unions arrays, deduping contents using a Set.\n * @param a The arrays to merge.\n * @returns The union of all given arrays.\n */\nexport function arrayUnion<T>(...a: T[][]): T[] {\n return Array.from(\n a.reduce((c, v) => {\n v.forEach((i) => c.add(i));\n return c;\n }, new Set<T>()),\n );\n}\n\n/**\n * Moves a single element from fromIndex to toIndex.\n * @param {array} list the list from which to construct the new list.\n * @param {number} fromIndex the index of the element to move.\n * @param {number} toIndex the index of where to put the element.\n * @returns {array} A new array with the requested value moved.\n */\nexport function moveElement<T>(list: T[], fromIndex: number, toIndex: number): T[] {\n const result = Array.from(list);\n const [removed] = result.splice(fromIndex, 1);\n result.splice(toIndex, 0, removed);\n\n return result;\n}\n\n/**\n * Helper functions to perform LINQ-like queries on arrays.\n */\nexport class ArrayUtil<T> {\n /**\n * Create a new array helper.\n * @param a The array to help. Can be modified in-place.\n */\n public constructor(private a: T[]) {}\n\n /**\n * The value of this array, after all appropriate alterations.\n */\n public get value(): T[] {\n return this.a;\n }\n\n /**\n * Groups an array by keys.\n * @param fn The key-finding function.\n * @returns This.\n */\n public groupBy<K>(fn: (a: T) => K): GroupedArray<K, T> {\n const obj = this.a.reduce((rv: Map<K, T[]>, val: T) => {\n const k = fn(val);\n if (!rv.has(k)) rv.set(k, []);\n rv.get(k)!.push(val);\n return rv;\n }, new Map<K, T[]>());\n return new GroupedArray(obj);\n }\n}\n\n/**\n * Helper functions to perform LINQ-like queries on groups (maps).\n */\nexport class GroupedArray<K, T> {\n /**\n * Creates a new group helper.\n * @param val The group to help. Can be modified in-place.\n */\n public constructor(private val: Map<K, T[]>) {}\n\n /**\n * The value of this group, after all applicable alterations.\n */\n public get value(): Map<K, T[]> {\n return this.val;\n }\n\n /**\n * Orders the grouping into an array using the provided key order.\n * @param keyOrder The key order.\n * @returns An array helper of the result.\n */\n public orderBy(keyOrder: K[]): ArrayUtil<T> {\n const a: T[] = [];\n for (const k of keyOrder) {\n if (!this.val.has(k)) continue;\n a.push(...this.val.get(k)!);\n }\n return new ArrayUtil(a);\n }\n}\n\nexport const concat = (...arrays: Uint8Array<ArrayBuffer>[]): Uint8Array<ArrayBuffer> => {\n return arrays.reduce((concatenatedSoFar: Uint8Array<ArrayBuffer>, toBeConcatenated: Uint8Array<ArrayBuffer>) => {\n const concatenated = new Uint8Array(concatenatedSoFar.length + toBeConcatenated.length);\n concatenated.set(concatenatedSoFar, 0);\n concatenated.set(toBeConcatenated, concatenatedSoFar.length);\n return concatenated;\n }, new Uint8Array(0));\n};\n\n/**\n * Async version of Array.every.\n */\nexport async function asyncEvery<T>(values: Iterable<T>, predicate: (value: T) => Promise<boolean>): Promise<boolean> {\n for (const value of values) {\n if (!(await predicate(value))) return false;\n }\n return true;\n}\n\n/**\n * Async version of Array.some.\n */\nexport async function asyncSome<T>(values: Iterable<T>, predicate: (value: T) => Promise<boolean>): Promise<boolean> {\n for (const value of values) {\n if (await predicate(value)) return true;\n }\n return false;\n}\n\n/**\n * Async version of Array.some that runs all promises in parallel.\n * @param values\n * @param predicate\n */\nexport async function asyncSomeParallel<T>(\n values: Array<T>,\n predicate: (value: T) => Promise<boolean>,\n): Promise<boolean> {\n try {\n return await Promise.any<boolean>(\n values.map((value) =>\n predicate(value).then((result) => (result ? Promise.resolve(true) : Promise.reject(false))),\n ),\n );\n } catch (e) {\n // If the array is empty or all the promises are false, Promise.any will reject an AggregateError\n if (e instanceof AggregateError) return false;\n throw e;\n }\n}\n\n/**\n * Async version of Array.filter.\n * If one of the promises rejects, the whole operation will reject.\n * @param values\n * @param predicate\n */\nexport async function asyncFilter<T>(values: Array<T>, predicate: (value: T) => Promise<boolean>): Promise<Array<T>> {\n const results = await Promise.all(values.map(predicate));\n return values.filter((_, i) => results[i]);\n}\n\nexport function filterBoolean<T>(values: Array<T | null | undefined>): T[] {\n return values.filter(Boolean) as T[];\n}\n","//#region src/core/utils/numbers.ts\nfunction e(e, t) {\n\treturn Number.isFinite(e) ? Number(e) : t;\n}\nfunction t(e, t, n) {\n\treturn Math.min(Math.max(e, t), n);\n}\nfunction n(...e) {\n\treturn [...e].reduce((e, t) => t + e, 0);\n}\nfunction r(e, t, n) {\n\treturn e * (n - t) + t;\n}\nfunction i(e, t, n) {\n\tlet r = (e - t) / (n - t);\n\treturn Number.isNaN(r) ? 0 : r;\n}\n//#endregion\nexport { t as clamp, e as defaultNumber, i as percentageOf, r as percentageWithin, n as sum };\n","(() => {\n if (typeof __webpack_require__ !== 'undefined') {\n var oldGetScript = __webpack_require__.u;\n var oldLoadScript = __webpack_require__.e;\n var queryMap = {};\n var countMap = {};\n var getRetryDelay = function () {\n return 500;\n };\n __webpack_require__.u = function (chunkId) {\n var result = oldGetScript(chunkId);\n return (\n result +\n (queryMap.hasOwnProperty(chunkId) ? '?' + queryMap[chunkId] : '')\n );\n };\n __webpack_require__.e = function (chunkId) {\n var result = oldLoadScript(chunkId);\n return result.catch(function (error) {\n var retries = countMap.hasOwnProperty(chunkId) ? countMap[chunkId] : 3;\n if (retries < 1) {\n var realSrc = oldGetScript(chunkId);\n error.message =\n 'Loading chunk ' +\n chunkId +\n ' failed after 3 retries.\\n(' +\n realSrc +\n ')';\n error.request = realSrc;\n throw error;\n }\n return new Promise(function (resolve) {\n var retryAttempt = 3 - retries + 1;\n setTimeout(function () {\n var retryAttemptString = '&retry-attempt=' + retryAttempt;\n var cacheBust = (() => Date.now())();\n +retryAttemptString;\n queryMap[chunkId] = cacheBust;\n countMap[chunkId] = retries - 1;\n resolve(__webpack_require__.e(chunkId));\n }, getRetryDelay(retryAttempt));\n });\n });\n };\n }\n})();\n","/*\nCopyright 2024 New Vector Ltd.\nCopyright 2021 The Matrix.org Foundation C.I.C.\n\nSPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport { arraySeed } from \"../utils/arrays\";\n\nexport const WORKLET_NAME = \"mx-voice-worklet\";\n\nexport enum PayloadEvent {\n Timekeep = \"timekeep\",\n AmplitudeMark = \"amplitude_mark\",\n}\n\nexport interface IPayload {\n ev: PayloadEvent;\n}\n\nexport interface ITimingPayload extends IPayload {\n ev: PayloadEvent.Timekeep;\n timeSeconds: number;\n}\n\nexport interface IAmplitudePayload extends IPayload {\n ev: PayloadEvent.AmplitudeMark;\n forIndex: number;\n amplitude: number;\n}\n\nexport const PLAYBACK_WAVEFORM_SAMPLES = 39;\nexport const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);\n","/*\nCopyright 2024 New Vector Ltd.\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nSPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport { type WorkerPayload } from \"./worker\";\nimport { arrayRescale, arraySmoothingResample } from \"../utils/arrays\";\nimport { PLAYBACK_WAVEFORM_SAMPLES } from \"../audio/consts\";\n\nconst ctx: Worker = self as any;\n\nexport interface Request {\n data: number[];\n}\n\nexport interface Response {\n waveform: number[];\n}\n\nctx.addEventListener(\"message\", async (event: MessageEvent<Request & WorkerPayload>): Promise<void> => {\n const { seq, data } = event.data;\n\n // First, convert negative amplitudes to positive so we don't detect zero as \"noisy\".\n const noiseWaveform = data.map((v) => Math.abs(v));\n\n // Then, we'll resample the waveform using a smoothing approach so we can keep the same rough shape.\n // We also rescale the waveform to be 0-1 so we end up with a clamped waveform to rely upon.\n const waveform = arrayRescale(arraySmoothingResample(noiseWaveform, PLAYBACK_WAVEFORM_SAMPLES), 0, 1);\n\n ctx.postMessage({ seq, waveform });\n});\n"],"names":["arrayFastResample","input","points","length","samples","everyNth","Math","round","i","push","spreadFactor","ceil","val","arraySeed","a","len","seed","slice","concat","arrayRescale","newMin","newMax","min","max","map","v","e","t","n","percentageWithin","r","Number","isNaN","percentageOf","Array","fill","__webpack_require__","oldGetScript","u","oldLoadScript","queryMap","countMap","chunkId","hasOwnProperty","catch","error","retries","realSrc","message","request","Promise","resolve","setTimeout","cacheBust","Date","now","ctx","self","addEventListener","async","seq","data","event","waveform","average","arraySmoothingResample","abs","postMessage"],"sourceRoot":""}