1/**
2 * Compression measurements off the main thread, so the scrolling view never
3 * stutters while zstd -19 or the LPC fit runs. One message in (the model),
4 * one message out (the nine codec results).
5 */
6import { LatentSource } from '../model/latent'
7import {
8 initCodecs,
9 compressAll,
10 entropyBounds,
11 type BoundResult,
12 ZLIB,
13 ZSTD,
14 ANS,
15 DELTA_ZLIB,
16 DELTA_ZSTD,
17 DELTA_ANS,
18 lpcCodecs,
19 type CodecResult,
20} from '../compress/codecs'
22export interface CompressRequest {
23 id: number
24 kernel: Float64Array
25 sigma: number
26 blockSize: number
27 lpcOrder: number
28 seed: number
29}
31export interface CompressResponse {
32 id: number
33 results: CodecResult[]
34 /** Order-0 entropy limit for each prefilter group. */
35 bounds: BoundResult[]
36 /** Empirical std of the quantized block, for display sanity. */
37 empiricalStd: number
38 error?: string
39}
41const PLAIN_CODECS = [ZLIB, ZSTD, ANS, DELTA_ZLIB, DELTA_ZSTD, DELTA_ANS]
43const post = self.postMessage as (message: CompressResponse) => void
45self.onmessage = async (e: MessageEvent<CompressRequest>) => {
46 const { id, kernel, sigma, blockSize, lpcOrder, seed } = e.data
47 try {
48 await initCodecs()
49 const samples = new LatentSource(seed).window(0, blockSize, kernel, sigma)
50 let sum = 0
51 let sumSq = 0
52 for (let i = 0; i < samples.length; i++) {
53 sum += samples[i]
54 sumSq += samples[i] * samples[i]
55 }
56 const mean = sum / samples.length
57 const empiricalStd = Math.sqrt(Math.max(0, sumSq / samples.length - mean * mean))
58 post({
59 id,
60 results: compressAll(samples, [...PLAIN_CODECS, ...lpcCodecs(lpcOrder)]),
61 bounds: entropyBounds(samples, lpcOrder),
62 empiricalStd,
63 })
64 } catch (err) {
65 post({ id, results: [], bounds: [], empiricalStd: 0, error: String(err) })
66 }
67}