@libraz/libsonare 1.3.1 → 1.3.2
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/README.md +232 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +114 -4
- package/dist/index.js.map +1 -1
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +51 -1
- package/dist/worklet.js +92 -4
- package/dist/worklet.js.map +1 -1
- package/package.json +1 -1
- package/src/effects_mastering.ts +16 -0
- package/src/errors.ts +44 -0
- package/src/index.ts +2 -0
- package/src/mixer.ts +11 -0
- package/src/module_state.ts +115 -4
- package/src/sonare.js.d.ts +6 -0
package/package.json
CHANGED
package/src/effects_mastering.ts
CHANGED
|
@@ -376,6 +376,22 @@ export function masteringInsertNames(): string[] {
|
|
|
376
376
|
).masteringInsertNames();
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Returns the camelCase parameter names a given insert / FX processor reads, for
|
|
381
|
+
* tooling/validation. Any key NOT in this list is silently ignored by the
|
|
382
|
+
* processor (and would be reported via {@link Mixer.sceneWarnings} when a scene
|
|
383
|
+
* carrying it is loaded). Band/sub-band processors enumerate their indexed
|
|
384
|
+
* `band{i}.<field>` keys. Returns an empty array for an unknown name (or one
|
|
385
|
+
* whose insert needs an unavailable build feature, e.g. FX).
|
|
386
|
+
*
|
|
387
|
+
* @param name - Insert processor name (see {@link masteringInsertNames}).
|
|
388
|
+
*/
|
|
389
|
+
export function masteringInsertParamNames(name: string): string[] {
|
|
390
|
+
return (
|
|
391
|
+
requireModule() as unknown as { masteringInsertParamNames: (name: string) => string[] }
|
|
392
|
+
).masteringInsertParamNames(name);
|
|
393
|
+
}
|
|
394
|
+
|
|
379
395
|
export function masteringPairProcessorNames(): PairProcessor[] {
|
|
380
396
|
return requireModule().masteringPairProcessorNames() as PairProcessor[];
|
|
381
397
|
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Numeric error codes carried by a {@link SonareError}. Mirrors the C ABI
|
|
3
|
+
* `SonareError` enum (and the Node / Python surfaces), so the same failure
|
|
4
|
+
* reports the same numeric code on every binding.
|
|
5
|
+
*/
|
|
6
|
+
export enum ErrorCode {
|
|
7
|
+
Ok = 0,
|
|
8
|
+
FileNotFound = 1,
|
|
9
|
+
InvalidFormat = 2,
|
|
10
|
+
DecodeFailed = 3,
|
|
11
|
+
InvalidParameter = 4,
|
|
12
|
+
OutOfMemory = 5,
|
|
13
|
+
NotSupported = 6,
|
|
14
|
+
InvalidState = 7,
|
|
15
|
+
Unknown = 99,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Error thrown by libsonare on a native (C++) failure. Carries a numeric
|
|
20
|
+
* {@link ErrorCode} `code` plus its canonical `codeName`, so callers can branch
|
|
21
|
+
* on the cause instead of matching message text.
|
|
22
|
+
*/
|
|
23
|
+
export class SonareError extends Error {
|
|
24
|
+
/** Numeric error code, equal to an {@link ErrorCode} value. */
|
|
25
|
+
readonly code: number;
|
|
26
|
+
/** Canonical name of `code`, e.g. `'InvalidParameter'`. */
|
|
27
|
+
readonly codeName: string;
|
|
28
|
+
|
|
29
|
+
constructor(code: number, codeName: string, message: string) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'SonareError';
|
|
32
|
+
this.code = code;
|
|
33
|
+
this.codeName = codeName;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Type guard: whether a caught value is a libsonare {@link SonareError}. */
|
|
38
|
+
export function isSonareError(value: unknown): value is SonareError {
|
|
39
|
+
return (
|
|
40
|
+
value instanceof Error &&
|
|
41
|
+
(value as { name?: unknown }).name === 'SonareError' &&
|
|
42
|
+
typeof (value as { code?: unknown }).code === 'number'
|
|
43
|
+
);
|
|
44
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -67,6 +67,7 @@ export {
|
|
|
67
67
|
masteringDynamicsGate,
|
|
68
68
|
masteringDynamicsTransientShaper,
|
|
69
69
|
masteringInsertNames,
|
|
70
|
+
masteringInsertParamNames,
|
|
70
71
|
masteringPairAnalysisNames,
|
|
71
72
|
masteringPairAnalyze,
|
|
72
73
|
masteringPairProcess,
|
|
@@ -98,6 +99,7 @@ export {
|
|
|
98
99
|
voiceChange,
|
|
99
100
|
voiceChangeRealtime,
|
|
100
101
|
} from './effects_mastering';
|
|
102
|
+
export { ErrorCode, isSonareError, SonareError } from './errors';
|
|
101
103
|
export type { MelodyOptions } from './feature_music';
|
|
102
104
|
export {
|
|
103
105
|
amplitudeToDb,
|
package/src/mixer.ts
CHANGED
|
@@ -76,6 +76,17 @@ export class Mixer {
|
|
|
76
76
|
this.mixer.compile();
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Non-fatal warnings captured when this mixer was built from scene JSON: one
|
|
81
|
+
* entry per channel-strip insert that was handed param keys it does not read
|
|
82
|
+
* (a likely typo, or a key meant for a different processor). The scene still
|
|
83
|
+
* loaded; these keys simply took no effect. Empty when every key was consumed.
|
|
84
|
+
* Use {@link masteringInsertParamNames} to discover the keys an insert accepts.
|
|
85
|
+
*/
|
|
86
|
+
sceneWarnings(): string[] {
|
|
87
|
+
return this.mixer.sceneWarnings();
|
|
88
|
+
}
|
|
89
|
+
|
|
79
90
|
/**
|
|
80
91
|
* Mix one block of per-strip stereo audio into the stereo master.
|
|
81
92
|
*
|
package/src/module_state.ts
CHANGED
|
@@ -1,14 +1,125 @@
|
|
|
1
|
+
import { ErrorCode, SonareError } from './errors';
|
|
1
2
|
import type { SonareModule } from './sonare.js';
|
|
2
3
|
|
|
3
|
-
let
|
|
4
|
+
let wrappedModule: SonareModule | null = null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shape of the structured info the native `sonareExceptionInfo(ptr)` returns.
|
|
8
|
+
*/
|
|
9
|
+
interface NativeExceptionInfo {
|
|
10
|
+
code: number;
|
|
11
|
+
codeName: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Recover the native exception-object pointer from a value thrown across the
|
|
17
|
+
* WASM boundary. emscripten surfaces a C++ throw in two shapes depending on the
|
|
18
|
+
* toolchain/exception mode:
|
|
19
|
+
* - a raw pointer number (older / classic surfacing), or
|
|
20
|
+
* - a `CppException` object exposing the pointer as `excPtr` (emscripten with
|
|
21
|
+
* `-fexceptions`).
|
|
22
|
+
* Returns null when the thrown value is neither (a genuine JS error), so the
|
|
23
|
+
* caller rethrows it unchanged.
|
|
24
|
+
*/
|
|
25
|
+
function nativeExceptionPtr(error: unknown): number | null {
|
|
26
|
+
if (typeof error === 'number') {
|
|
27
|
+
return error;
|
|
28
|
+
}
|
|
29
|
+
if (error !== null && typeof error === 'object') {
|
|
30
|
+
const ptr = (error as { excPtr?: unknown }).excPtr;
|
|
31
|
+
if (typeof ptr === 'number') {
|
|
32
|
+
return ptr;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Turn a thrown native exception pointer into a {@link SonareError}. The bound
|
|
40
|
+
* `sonareExceptionInfo` decodes the pointer back into { code, codeName,
|
|
41
|
+
* message }.
|
|
42
|
+
*/
|
|
43
|
+
function makeSonareError(raw: SonareModule, thrown: number): SonareError {
|
|
44
|
+
let code: number = ErrorCode.Unknown;
|
|
45
|
+
let codeName = 'Unknown';
|
|
46
|
+
let message = `libsonare native exception (${thrown})`;
|
|
47
|
+
try {
|
|
48
|
+
const info = (
|
|
49
|
+
raw as unknown as { sonareExceptionInfo?: (ptr: number) => NativeExceptionInfo }
|
|
50
|
+
).sonareExceptionInfo?.(thrown);
|
|
51
|
+
if (info) {
|
|
52
|
+
code = info.code ?? code;
|
|
53
|
+
codeName = info.codeName ?? codeName;
|
|
54
|
+
message = info.message || message;
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// Fall back to the generic message if decoding fails.
|
|
58
|
+
}
|
|
59
|
+
return new SonareError(code, codeName, message);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Wrap the embind module so a native C++ exception (which surfaces as a raw
|
|
64
|
+
* pointer number or a `CppException` carrying one) is rethrown as a
|
|
65
|
+
* {@link SonareError}. Only function-valued
|
|
66
|
+
* members are wrapped, and the wrapper is cached per member so repeated access
|
|
67
|
+
* stays cheap; non-function members (typed-array heap views, etc.) pass through
|
|
68
|
+
* unchanged. The dedicated realtime `sonare-rt` module is separate and is not
|
|
69
|
+
* affected by this wrapper.
|
|
70
|
+
*/
|
|
71
|
+
function wrapModuleErrors(raw: SonareModule): SonareModule {
|
|
72
|
+
const cache = new Map<PropertyKey, unknown>();
|
|
73
|
+
const convert = (error: unknown): never => {
|
|
74
|
+
const ptr = nativeExceptionPtr(error);
|
|
75
|
+
if (ptr !== null) {
|
|
76
|
+
throw makeSonareError(raw, ptr);
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
};
|
|
80
|
+
return new Proxy(raw, {
|
|
81
|
+
get(target, prop, receiver) {
|
|
82
|
+
const value = Reflect.get(target, prop, receiver);
|
|
83
|
+
if (typeof value !== 'function') {
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
const cached = cache.get(prop);
|
|
87
|
+
if (cached) {
|
|
88
|
+
return cached;
|
|
89
|
+
}
|
|
90
|
+
// Wrap as a Proxy (not a plain function) so embind class constructors
|
|
91
|
+
// invoked via `new module.Foo(...)` keep their `[[Construct]]` behaviour
|
|
92
|
+
// and prototype while still converting thrown native pointers.
|
|
93
|
+
const fn = value as (...a: unknown[]) => unknown;
|
|
94
|
+
const wrapped = new Proxy(fn, {
|
|
95
|
+
apply(t, thisArg, args) {
|
|
96
|
+
try {
|
|
97
|
+
return Reflect.apply(t, thisArg, args as unknown[]);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return convert(error);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
construct(t, args, newTarget) {
|
|
103
|
+
try {
|
|
104
|
+
return Reflect.construct(t, args as unknown[], newTarget) as object;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return convert(error) as object;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
cache.set(prop, wrapped);
|
|
111
|
+
return wrapped;
|
|
112
|
+
},
|
|
113
|
+
}) as SonareModule;
|
|
114
|
+
}
|
|
4
115
|
|
|
5
116
|
export function setSonareModule(module: SonareModule): void {
|
|
6
|
-
|
|
117
|
+
wrappedModule = wrapModuleErrors(module);
|
|
7
118
|
}
|
|
8
119
|
|
|
9
120
|
export function getSonareModule(): SonareModule {
|
|
10
|
-
if (!
|
|
121
|
+
if (!wrappedModule) {
|
|
11
122
|
throw new Error('Module not initialized. Call init() first.');
|
|
12
123
|
}
|
|
13
|
-
return
|
|
124
|
+
return wrappedModule;
|
|
14
125
|
}
|
package/src/sonare.js.d.ts
CHANGED
|
@@ -1845,6 +1845,11 @@ export interface SonareModule {
|
|
|
1845
1845
|
|
|
1846
1846
|
// Mixing - scene-based Mixer
|
|
1847
1847
|
createMixerFromSceneJson: (json: string, sampleRate: number, blockSize: number) => WasmMixer;
|
|
1848
|
+
|
|
1849
|
+
// Decodes a thrown native exception-object pointer (emscripten classic EH
|
|
1850
|
+
// surfaces a C++ throw as the raw pointer number) into a structured error.
|
|
1851
|
+
// Consumed by the module-error wrapper in module_state.ts.
|
|
1852
|
+
sonareExceptionInfo: (ptr: number) => { code: number; codeName: string; message: string };
|
|
1848
1853
|
}
|
|
1849
1854
|
|
|
1850
1855
|
export interface WasmStreamingMasteringChain {
|
|
@@ -1947,6 +1952,7 @@ export interface WasmMixer {
|
|
|
1947
1952
|
outputRightView: () => Float32Array;
|
|
1948
1953
|
processPreparedStereo: (numSamples: number) => void;
|
|
1949
1954
|
stripCount: () => number;
|
|
1955
|
+
sceneWarnings: () => string[];
|
|
1950
1956
|
scheduleInsertAutomation: (
|
|
1951
1957
|
stripIndex: number,
|
|
1952
1958
|
insertIndex: number,
|