@effetune/dsp 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/THIRD_PARTY_NOTICES.txt +11 -0
- package/dist/artifacts.js +185 -0
- package/dist/assets/NOTICE.txt +30 -0
- package/dist/assets/effetune-dsp.meta.json +502 -0
- package/dist/assets/effetune-dsp.simd.wasm +0 -0
- package/dist/assets/effetune-dsp.wasm +0 -0
- package/dist/assets.js +767 -0
- package/dist/catalog/effects-v1.json +5369 -0
- package/dist/catalog-entry.d.ts +9 -0
- package/dist/catalog-entry.js +5 -0
- package/dist/catalog.js +50 -0
- package/dist/effect.d.ts +23 -0
- package/dist/effect.js +167 -0
- package/dist/engine.js +291 -0
- package/dist/errors.js +31 -0
- package/dist/generated-effects.d.ts +1460 -0
- package/dist/generated-effects.js +1007 -0
- package/dist/index.d.ts +512 -0
- package/dist/index.js +175 -0
- package/dist/internal/dsp-engine-binding.js +859 -0
- package/dist/internal/dsp-params.generated.js +1379 -0
- package/dist/internal/dsp-wasm-loader.js +346 -0
- package/dist/internal/ir-asset-payload.js +98 -0
- package/dist/internal/ir-plugin-contract.js +265 -0
- package/dist/preset.js +518 -0
- package/dist/runtime.js +488 -0
- package/dist/schemas/bundle-v1.schema.json +223 -0
- package/dist/schemas/chain-v1.schema.json +6530 -0
- package/dist/semantics.js +309 -0
- package/dist/telemetry.js +322 -0
- package/dist/worklet-processor.js +175 -0
- package/dist/worklet.d.ts +32 -0
- package/dist/worklet.js +386 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026, Yoshiyuki Kobayashi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# @effetune/dsp
|
|
2
|
+
|
|
3
|
+
<!-- BEGIN DSP-LIBRARY-JAVASCRIPT-SUMMARY -->
|
|
4
|
+
EffeTune DSP provides the same MIT-licensed C++ audio kernels used by EffeTune
|
|
5
|
+
as a self-contained WebAssembly package for Node.js and evergreen browsers.
|
|
6
|
+
Version 0.1.0 exposes all 76 catalog types through the generic Chain and
|
|
7
|
+
`createEffect` APIs and 76 generated named convenience classes,
|
|
8
|
+
decoded analyzer telemetry, versioned semantic presets, deterministic seeds, and an AudioWorklet wrapper.
|
|
9
|
+
<!-- END DSP-LIBRARY-JAVASCRIPT-SUMMARY -->
|
|
10
|
+
|
|
11
|
+
Documentation: [effetune.frieve.com/dsp/](https://effetune.frieve.com/dsp/)
|
|
12
|
+
|
|
13
|
+
Source and issues:
|
|
14
|
+
[Frieve-A/effetune](https://github.com/Frieve-A/effetune)
|
|
15
|
+
|
|
16
|
+
<!-- BEGIN DSP-LIBRARY-JAVASCRIPT-START -->
|
|
17
|
+
```console
|
|
18
|
+
npm install @effetune/dsp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { createChain } from '@effetune/dsp';
|
|
23
|
+
|
|
24
|
+
const frames = 512;
|
|
25
|
+
const mono = Float32Array.from(
|
|
26
|
+
{ length: frames },
|
|
27
|
+
(_, frame) => 0.5 * Math.sin(2 * Math.PI * frame / 97)
|
|
28
|
+
);
|
|
29
|
+
const input = [mono.slice(), mono.slice()];
|
|
30
|
+
const chain = await createChain({
|
|
31
|
+
version: 1,
|
|
32
|
+
chain: [{
|
|
33
|
+
id: 'volume',
|
|
34
|
+
type: 'Volume',
|
|
35
|
+
parameters: { volume: -6 }
|
|
36
|
+
}]
|
|
37
|
+
});
|
|
38
|
+
const output = await chain.process(input, { sampleRate: 48000 });
|
|
39
|
+
console.log(output.length, output[0].length, output[0][0]);
|
|
40
|
+
chain.close();
|
|
41
|
+
```
|
|
42
|
+
<!-- END DSP-LIBRARY-JAVASCRIPT-START -->
|
|
43
|
+
|
|
44
|
+
The package is ESM-only. Save the example as `start.mjs` and run
|
|
45
|
+
`node start.mjs`, or set `"type": "module"` in the consumer's `package.json`.
|
|
46
|
+
CommonJS `require()` is not supported.
|
|
47
|
+
|
|
48
|
+
Public package entry points are:
|
|
49
|
+
|
|
50
|
+
- `@effetune/dsp` for the main ESM API
|
|
51
|
+
- `@effetune/dsp/worklet` for `EffeTuneNode`
|
|
52
|
+
- `@effetune/dsp/processor` for the side-effect AudioWorklet processor
|
|
53
|
+
- `@effetune/dsp/schemas/chain-v1.json` for the Chain v1 JSON Schema
|
|
54
|
+
- `@effetune/dsp/schemas/bundle-v1.json` for the Bundle v1 JSON Schema
|
|
55
|
+
- `@effetune/dsp/catalog` for ESM catalog exports
|
|
56
|
+
- `@effetune/dsp/catalog.json` for the machine-readable catalog JSON
|
|
57
|
+
|
|
58
|
+
Every offline `process()` call starts from fresh DSP state and returns newly
|
|
59
|
+
owned `Float32Array` channels. Input arrays are never mutated. Effects run in
|
|
60
|
+
array order; a disabled effect and an empty chain are identity operations.
|
|
61
|
+
All samples must be finite. Offline and streaming calls reject `NaN`,
|
|
62
|
+
`Infinity`, and `-Infinity` with `ValidationError` before native processing;
|
|
63
|
+
an invalid stream block does not change filter state.
|
|
64
|
+
|
|
65
|
+
Use a stream when DSP state must continue across blocks or when parameters
|
|
66
|
+
change at an exact frame:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
const stream = await chain.stream({
|
|
70
|
+
sampleRate: 48000,
|
|
71
|
+
channels: 2,
|
|
72
|
+
blockSize: 128,
|
|
73
|
+
seed: 42
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const output = await stream.process([left, right], {
|
|
77
|
+
events: [{
|
|
78
|
+
frame: 256,
|
|
79
|
+
effectId: 'voice',
|
|
80
|
+
parameters: { threshold: -24, ratio: 4 }
|
|
81
|
+
}]
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
stream.setParam('voice', 'threshold', -20);
|
|
85
|
+
stream.reset();
|
|
86
|
+
stream.close();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Event frames are relative to the start of that `process()` input. They must be
|
|
90
|
+
integers in input order from `0` through `frames - 1`; events sharing a frame
|
|
91
|
+
are applied in array order before that sample. Each parameter object is merged
|
|
92
|
+
with the effect's current semantic parameters and the complete packed block is
|
|
93
|
+
committed without recreating DSP state. `reset()` restores the parameters,
|
|
94
|
+
seed, and DSP state from stream creation.
|
|
95
|
+
|
|
96
|
+
`setParam()` and events cannot change parameters that require convolution
|
|
97
|
+
assets to be staged again. Open a new stream after changing
|
|
98
|
+
`IRReverb.channelMode`, `latency`, or `convolutionRate`;
|
|
99
|
+
`FIRCrossover.bandCount`, `latencyMode`, or `filterDelaySamples`; or
|
|
100
|
+
`latencyMode` / `filterDelaySamples` on `FiveBandFIRPEQ`, `GroupDelayEQ`, or
|
|
101
|
+
`RoomEQ`. The same restriction applies to `EffeTuneNode.setParam()`.
|
|
102
|
+
|
|
103
|
+
Canonical presets use semantic long parameter names:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"version": 1,
|
|
108
|
+
"chain": [
|
|
109
|
+
{
|
|
110
|
+
"id": "voice",
|
|
111
|
+
"type": "Compressor",
|
|
112
|
+
"enabled": true,
|
|
113
|
+
"channel": "stereo",
|
|
114
|
+
"parameters": { "threshold": -18, "ratio": 4 }
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Application `pipeline` and `plugins` presets are not canonical presets.
|
|
121
|
+
Convert a preset whose effects form one ordered serial path explicitly with
|
|
122
|
+
`importLegacyPreset()` before calling `createChain()`. Branched and multi-bus
|
|
123
|
+
routing is rejected because flattening it would change the acoustic result.
|
|
124
|
+
Move or copy the desired effects into one serial path in the app and export it
|
|
125
|
+
again, or reproduce the branching in the host around separate Chains.
|
|
126
|
+
Unsupported fields produce a validation error.
|
|
127
|
+
|
|
128
|
+
FIR Crossover, Five Band FIR PEQ, Group Delay EQ, IR Reverb, and Room EQ
|
|
129
|
+
require an `assets.impulseResponse` reference and an `assetResolver`. The four
|
|
130
|
+
FIR filter effects use prepared coefficient impulses at the processing sample
|
|
131
|
+
rate. Use the public `encodeEta1({ channels, sampleRate, topology, paths })`
|
|
132
|
+
helper to encode raw planar float32 arrays for a resolver. Bundle manifests
|
|
133
|
+
verify exact byte length and SHA-256 before accepting an ETA1 payload. The
|
|
134
|
+
complete payload and convolution footprint must fit the 32 MiB kernel cap.
|
|
135
|
+
|
|
136
|
+
`EFFECT_CATALOG` and `getEffectCatalog()` expose the machine-readable semantic
|
|
137
|
+
catalog for all 76 root classes and their `create<Type>()` factories. The
|
|
138
|
+
catalog contains channel choices, parameters, required assets, telemetry, and
|
|
139
|
+
latency declarations, but no private implementation mapping.
|
|
140
|
+
|
|
141
|
+
For stateful processing, `ChainStream.latencySamples` reports aggregate runtime
|
|
142
|
+
latency and matches Python `Stream.latency_samples` for the same chain and
|
|
143
|
+
sample rate. `chain.latencySamples({ sampleRate })` reports the same value
|
|
144
|
+
without opening a stream, which aligns offline `process()` output.
|
|
145
|
+
`EffeTuneNode` does not expose a latency getter.
|
|
146
|
+
|
|
147
|
+
For real-time processing:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
import { EffeTuneNode } from '@effetune/dsp/worklet';
|
|
151
|
+
|
|
152
|
+
const node = await EffeTuneNode.create(context, preset, {
|
|
153
|
+
channels: 2,
|
|
154
|
+
seed: 42,
|
|
155
|
+
assetResolver
|
|
156
|
+
});
|
|
157
|
+
source.connect(node).connect(context.destination);
|
|
158
|
+
await node.setParam('voice', 'threshold', -20);
|
|
159
|
+
await node.reset();
|
|
160
|
+
node.close();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`LevelMeter`, `Oscilloscope`, `SpectrumAnalyzer`, `Spectrogram`, and
|
|
164
|
+
`StereoMeter` provide opt-in decoded telemetry:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
const unsubscribe = node.subscribe(frame => {
|
|
168
|
+
if (frame.kind === 'level') console.log(frame.channels);
|
|
169
|
+
});
|
|
170
|
+
console.log(node.droppedTelemetryFrames);
|
|
171
|
+
unsubscribe();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Offline `Chain.process()` accepts `onTelemetry`; a stream accepts the same
|
|
175
|
+
option and also provides `subscribe()`, `unsubscribe()`, and
|
|
176
|
+
`droppedTelemetryFrames`. The first subscriber enables observations and the
|
|
177
|
+
last unsubscribe disables them. Arrays in delivered frames belong to the
|
|
178
|
+
caller. Raw DSP telemetry and AudioWorklet messages are not public APIs.
|
|
179
|
+
|
|
180
|
+
`EffeTuneNode.create()` waits until the package-owned worklet processor has
|
|
181
|
+
instantiated the selected baseline or SIMD artifact and committed every
|
|
182
|
+
required asset. Serve the package files from the same origin or provide
|
|
183
|
+
explicit `processorUrl`, `wasmUrl`, and `simdWasmUrl` options.
|
|
184
|
+
|
|
185
|
+
The package does not decode or encode audio files. Provide planar float32 audio
|
|
186
|
+
from Web Audio, WebCodecs, an audio-file library, or your own I/O layer.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
EffeTune DSP includes PFFFT v1.1.0.
|
|
2
|
+
|
|
3
|
+
PFFFT source: https://github.com/marton78/pffft
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2020 Dario Mambro
|
|
6
|
+
Copyright (c) 2019 Hayati Ayguen
|
|
7
|
+
Copyright (c) 2013 Julien Pommier
|
|
8
|
+
Copyright (c) 2004 the University Corporation for Atmospheric Research
|
|
9
|
+
|
|
10
|
+
PFFFT is distributed under a permissive BSD-style license. The complete
|
|
11
|
+
upstream notice is included in dist/assets/NOTICE.txt.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { detectSimdSupport } from './internal/dsp-wasm-loader.js';
|
|
2
|
+
import { instantiateDspBinding } from './internal/dsp-engine-binding.js';
|
|
3
|
+
import { getEffectCatalog, getEffectImplementation } from './catalog.js';
|
|
4
|
+
import { EffeTuneRuntimeError, ValidationError } from './errors.js';
|
|
5
|
+
|
|
6
|
+
const EXPECTED_ABI_VERSION = 1;
|
|
7
|
+
const cache = new Map();
|
|
8
|
+
|
|
9
|
+
function resourceUrl(value, fallback) {
|
|
10
|
+
if (value instanceof URL) return value;
|
|
11
|
+
if (value === undefined) return fallback;
|
|
12
|
+
try {
|
|
13
|
+
return new URL(String(value), import.meta.url);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
throw new ValidationError(`Invalid DSP resource URL: ${String(value)}`, { cause: error });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function readFileUrl(url, binary) {
|
|
20
|
+
try {
|
|
21
|
+
const { readFile } = await import('node:fs/promises');
|
|
22
|
+
const data = await readFile(url);
|
|
23
|
+
return binary
|
|
24
|
+
? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
|
|
25
|
+
: data.toString('utf8');
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw new EffeTuneRuntimeError(`Unable to read packaged DSP resource ${url.pathname}.`, { cause: error });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function fetchResource(url, fetchImpl, binary) {
|
|
32
|
+
if (url.protocol === 'file:') return readFileUrl(url, binary);
|
|
33
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
34
|
+
throw new ValidationError(`Unsupported DSP resource URL scheme: ${url.protocol}`);
|
|
35
|
+
}
|
|
36
|
+
if (url.protocol === 'http:') {
|
|
37
|
+
const moduleUrl = new URL(import.meta.url);
|
|
38
|
+
const local = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
39
|
+
if (!local && url.origin !== moduleUrl.origin) {
|
|
40
|
+
throw new ValidationError('Cross-origin DSP resources must use HTTPS.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (typeof fetchImpl !== 'function') {
|
|
44
|
+
throw new EffeTuneRuntimeError('fetch is required to load DSP resources in this environment.');
|
|
45
|
+
}
|
|
46
|
+
let response;
|
|
47
|
+
try {
|
|
48
|
+
response = await fetchImpl(url);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw new EffeTuneRuntimeError(`Unable to load DSP resource ${url}.`, { cause: error });
|
|
51
|
+
}
|
|
52
|
+
if (!response?.ok) {
|
|
53
|
+
throw new EffeTuneRuntimeError(`Unable to load DSP resource ${url} (HTTP ${response?.status ?? 'error'}).`);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return await (binary ? response.arrayBuffer() : response.text());
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new EffeTuneRuntimeError(`Unable to read DSP resource ${url}.`, { cause: error });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function selectVariant(requested, webAssembly) {
|
|
63
|
+
if (!['auto', 'baseline', 'simd'].includes(requested)) {
|
|
64
|
+
throw new ValidationError('variant must be auto, baseline, or simd.');
|
|
65
|
+
}
|
|
66
|
+
const supported = detectSimdSupport(webAssembly);
|
|
67
|
+
if (requested === 'simd' && !supported) {
|
|
68
|
+
throw new EffeTuneRuntimeError('This WebAssembly runtime does not support SIMD.');
|
|
69
|
+
}
|
|
70
|
+
return requested === 'auto' ? (supported ? 'simd' : 'baseline') : requested;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function validateMeta(meta) {
|
|
74
|
+
if (!meta || meta.abiVersion !== EXPECTED_ABI_VERSION || !Array.isArray(meta.kernels)) {
|
|
75
|
+
throw new EffeTuneRuntimeError('The packaged DSP metadata is incompatible.');
|
|
76
|
+
}
|
|
77
|
+
return new Map(meta.kernels.map(kernel => [kernel.name, kernel]));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function validateCapabilities(capabilities, kernels, variant) {
|
|
81
|
+
if (capabilities.abiVersion !== EXPECTED_ABI_VERSION) {
|
|
82
|
+
throw new EffeTuneRuntimeError('The packaged DSP module uses an incompatible ABI.');
|
|
83
|
+
}
|
|
84
|
+
if (capabilities.simd !== (variant === 'simd')) {
|
|
85
|
+
throw new EffeTuneRuntimeError(`The packaged ${variant} DSP artifact has inconsistent build flags.`);
|
|
86
|
+
}
|
|
87
|
+
const available = new Map(capabilities.kernels.map(kernel => [kernel.name, kernel]));
|
|
88
|
+
for (const effect of getEffectCatalog().effects) {
|
|
89
|
+
const implementation = getEffectImplementation(effect.type);
|
|
90
|
+
const meta = kernels.get(implementation.internalType);
|
|
91
|
+
const actual = available.get(implementation.internalType);
|
|
92
|
+
if (!meta || !actual ||
|
|
93
|
+
(meta.hash >>> 0) !== (implementation.layoutHash >>> 0) ||
|
|
94
|
+
(actual.hash >>> 0) !== (implementation.layoutHash >>> 0)) {
|
|
95
|
+
throw new EffeTuneRuntimeError(`${effect.type} is incompatible with the packaged DSP artifact.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function loadUncached({
|
|
101
|
+
variant,
|
|
102
|
+
webAssembly,
|
|
103
|
+
fetchImpl,
|
|
104
|
+
wasmUrl,
|
|
105
|
+
simdWasmUrl,
|
|
106
|
+
metaUrl
|
|
107
|
+
}) {
|
|
108
|
+
if (!webAssembly || typeof webAssembly.compile !== 'function') {
|
|
109
|
+
throw new EffeTuneRuntimeError('WebAssembly is unavailable.');
|
|
110
|
+
}
|
|
111
|
+
const selected = selectVariant(variant, webAssembly);
|
|
112
|
+
const baselineDefault = new URL('./assets/effetune-dsp.wasm', import.meta.url);
|
|
113
|
+
const simdDefault = new URL('./assets/effetune-dsp.simd.wasm', import.meta.url);
|
|
114
|
+
const metadataDefault = new URL('./assets/effetune-dsp.meta.json', import.meta.url);
|
|
115
|
+
const selectedUrl = selected === 'simd'
|
|
116
|
+
? resourceUrl(simdWasmUrl, simdDefault)
|
|
117
|
+
: resourceUrl(wasmUrl, baselineDefault);
|
|
118
|
+
const resolvedMetaUrl = resourceUrl(metaUrl, metadataDefault);
|
|
119
|
+
|
|
120
|
+
const [bytes, metaText] = await Promise.all([
|
|
121
|
+
fetchResource(selectedUrl, fetchImpl, true),
|
|
122
|
+
fetchResource(resolvedMetaUrl, fetchImpl, false)
|
|
123
|
+
]);
|
|
124
|
+
let meta;
|
|
125
|
+
try {
|
|
126
|
+
meta = JSON.parse(metaText);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
throw new EffeTuneRuntimeError('The packaged DSP metadata is not valid JSON.', { cause: error });
|
|
129
|
+
}
|
|
130
|
+
const kernels = validateMeta(meta);
|
|
131
|
+
let module;
|
|
132
|
+
try {
|
|
133
|
+
module = await webAssembly.compile(bytes);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
throw new EffeTuneRuntimeError(`Unable to compile the packaged ${selected} DSP artifact.`, { cause: error });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let binding;
|
|
139
|
+
try {
|
|
140
|
+
binding = await instantiateDspBinding(module, { webAssembly, warning: () => {} });
|
|
141
|
+
validateCapabilities(binding.getCapabilities(), kernels, selected);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (error instanceof EffeTuneRuntimeError) throw error;
|
|
144
|
+
throw new EffeTuneRuntimeError(`Unable to initialize the packaged ${selected} DSP artifact.`, { cause: error });
|
|
145
|
+
} finally {
|
|
146
|
+
binding?.close();
|
|
147
|
+
}
|
|
148
|
+
return { variant: selected, module, bytes, meta };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function loadDspArtifact(options = {}) {
|
|
152
|
+
const webAssembly = options.webAssembly ?? globalThis.WebAssembly;
|
|
153
|
+
const variant = selectVariant(options.variant ?? 'auto', webAssembly);
|
|
154
|
+
const wasmUrl = variant === 'simd' ? options.simdWasmUrl : options.wasmUrl;
|
|
155
|
+
const key = options.cache === false ||
|
|
156
|
+
options.fetch !== undefined ||
|
|
157
|
+
options.webAssembly !== undefined
|
|
158
|
+
? null
|
|
159
|
+
: [
|
|
160
|
+
variant,
|
|
161
|
+
String(wasmUrl ?? ''),
|
|
162
|
+
String(options.metaUrl ?? ''),
|
|
163
|
+
options.fetch === undefined ? 'default-fetch' : 'custom-fetch'
|
|
164
|
+
].join('|');
|
|
165
|
+
if (key && cache.has(key)) return cache.get(key);
|
|
166
|
+
const promise = loadUncached({
|
|
167
|
+
variant,
|
|
168
|
+
webAssembly,
|
|
169
|
+
fetchImpl: options.fetch ?? globalThis.fetch,
|
|
170
|
+
wasmUrl: options.wasmUrl,
|
|
171
|
+
simdWasmUrl: options.simdWasmUrl,
|
|
172
|
+
metaUrl: options.metaUrl
|
|
173
|
+
});
|
|
174
|
+
if (key) cache.set(key, promise);
|
|
175
|
+
try {
|
|
176
|
+
return await promise;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (key) cache.delete(key);
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function clearArtifactCache() {
|
|
184
|
+
cache.clear();
|
|
185
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
EffeTune DSP third-party notices
|
|
2
|
+
================================
|
|
3
|
+
|
|
4
|
+
PFFFT v1.1.0
|
|
5
|
+
Source: https://github.com/marton78/pffft
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2020 Dario Mambro (dario.mambro@gmail.com)
|
|
8
|
+
Copyright (c) 2019 Hayati Ayguen (h_ayguen@web.de)
|
|
9
|
+
Copyright (c) 2013 Julien Pommier (pommier@modartt.com)
|
|
10
|
+
Copyright (c) 2004 the University Corporation for Atmospheric Research (UCAR)
|
|
11
|
+
|
|
12
|
+
Redistribution and use of the Software in source and binary forms, with or without
|
|
13
|
+
modification, is permitted provided that the following conditions are met:
|
|
14
|
+
|
|
15
|
+
- Neither the names of NCAR's Computational and Information Systems Laboratory, the
|
|
16
|
+
University Corporation for Atmospheric Research, nor the names of its sponsors or
|
|
17
|
+
contributors may be used to endorse or promote products derived from this Software
|
|
18
|
+
without specific prior written permission.
|
|
19
|
+
- Redistributions of source code must retain the above copyright notices, this list of
|
|
20
|
+
conditions, and the disclaimer below.
|
|
21
|
+
- Redistributions in binary form must reproduce the above copyright notice, this list
|
|
22
|
+
of conditions, and the disclaimer below in the documentation and/or other materials
|
|
23
|
+
provided with the distribution.
|
|
24
|
+
|
|
25
|
+
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
26
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
27
|
+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE
|
|
28
|
+
LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
29
|
+
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|