@sorisdk/matcher 0.6.4 → 0.6.7
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 +33 -0
- package/dist/generated/core.d.ts +6 -0
- package/dist/generated/core.js +1 -1
- package/dist/generated/core_bg.js +62 -12
- package/dist/generated/core_bg.wasm +0 -0
- package/dist/generated/core_bg.wasm.d.ts +8 -4
- package/dist/index.d.ts +17 -1
- package/dist/index.js +176 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,6 +89,39 @@ per-entry limits remain independently enforced.
|
|
|
89
89
|
|
|
90
90
|
If you want campaign events and browser recognition flow on top of matching, use `@sorisdk/web-audio`.
|
|
91
91
|
|
|
92
|
+
## Audio-marker detection limits
|
|
93
|
+
|
|
94
|
+
`MatcherSession.detectAudioMarker(...)` preserves its existing signature and
|
|
95
|
+
preflights PCM before the browser copy into WASM. The defaults are 336,000
|
|
96
|
+
PCM16 bytes, 168,000 samples, 3,500 ms, and 1,000,000,000 conservative detector
|
|
97
|
+
work units. These are exported as `DEFAULT_MAX_AUDIO_MARKER_*` constants.
|
|
98
|
+
Input-budget failures return a normal absent detection whose `reason` is
|
|
99
|
+
`pcm_input_budget_exceeded`; work failures use
|
|
100
|
+
`detector_work_budget_exceeded`, and checked-estimate overflow uses
|
|
101
|
+
`detector_work_overflow`. Input is rejected, never silently truncated.
|
|
102
|
+
|
|
103
|
+
Use the additive API only for bounded, trusted offline input:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const detection = await session.detectAudioMarkerWithLimits(
|
|
107
|
+
trustedPcm16Le,
|
|
108
|
+
48_000,
|
|
109
|
+
undefined,
|
|
110
|
+
{
|
|
111
|
+
maxPcmBytes: 960_000,
|
|
112
|
+
maxSamples: 480_000,
|
|
113
|
+
maxDurationMillis: 10_000,
|
|
114
|
+
maxWorkUnits: 3_000_000_000
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The generated WASM module exposes the corresponding
|
|
120
|
+
`detectAudioMarkerWithLimits` function and method. It inspects the JavaScript
|
|
121
|
+
`Uint8Array` length before allocating a Rust-owned copy. Raising these budgets
|
|
122
|
+
permits greater memory and CPU use; it does not protect an untrusted host after
|
|
123
|
+
the caller opts into the larger envelope.
|
|
124
|
+
|
|
92
125
|
## Metadata lookup
|
|
93
126
|
|
|
94
127
|
Audiopack footer metadata is available through explicit lookup rather than being
|
package/dist/generated/core.d.ts
CHANGED
|
@@ -24,7 +24,9 @@ export class WasmMatcherEngine {
|
|
|
24
24
|
bestMatch(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
|
|
25
25
|
bestMatchWithVariants(database: WasmMatcherDatabase, query: Uint8Array, pitch_query: any, config?: any | null, variant_config?: any | null): any;
|
|
26
26
|
detectAudioMarker(pcm16_le: Uint8Array, sample_rate: number, config?: any | null): any;
|
|
27
|
+
detectAudioMarkerWithLimits(pcm16_le: Uint8Array, sample_rate: number, config?: any | null, limits?: any | null): any;
|
|
27
28
|
detectPatternMark(pcm16_le: Uint8Array, sample_rate: number, config?: any | null): any;
|
|
29
|
+
detectPatternMarkWithLimits(pcm16_le: Uint8Array, sample_rate: number, config?: any | null, limits?: any | null): any;
|
|
28
30
|
estimatePitchRatio(pcm16_le: Uint8Array, sample_rate: number): any;
|
|
29
31
|
estimatePitchRatioAgainstReference(pcm16_le: Uint8Array, sample_rate: number, reference_pitch_hz: number, reference_confidence?: number | null): any;
|
|
30
32
|
estimatePitchRatioAgainstSpectralFeatures(pcm16_le: Uint8Array, sample_rate: number, reference_features: string, reference_position_millis: number, reference_confidence?: number | null): any;
|
|
@@ -38,8 +40,12 @@ export class WasmMatcherEngine {
|
|
|
38
40
|
|
|
39
41
|
export function detectAudioMarker(pcm16_le: Uint8Array, sample_rate: number, config?: any | null): any;
|
|
40
42
|
|
|
43
|
+
export function detectAudioMarkerWithLimits(pcm16_le: Uint8Array, sample_rate: number, config?: any | null, limits?: any | null): any;
|
|
44
|
+
|
|
41
45
|
export function detectPatternMark(pcm16_le: Uint8Array, sample_rate: number, config?: any | null): any;
|
|
42
46
|
|
|
47
|
+
export function detectPatternMarkWithLimits(pcm16_le: Uint8Array, sample_rate: number, config?: any | null, limits?: any | null): any;
|
|
48
|
+
|
|
43
49
|
export function estimatePitchRatio(pcm16_le: Uint8Array, sample_rate: number): any;
|
|
44
50
|
|
|
45
51
|
export function estimatePitchRatioAgainstReference(pcm16_le: Uint8Array, sample_rate: number, reference_pitch_hz: number, reference_confidence?: number | null): any;
|
package/dist/generated/core.js
CHANGED
|
@@ -5,5 +5,5 @@ import { __wbg_set_wasm } from "./core_bg.js";
|
|
|
5
5
|
__wbg_set_wasm(wasm);
|
|
6
6
|
wasm.__wbindgen_start();
|
|
7
7
|
export {
|
|
8
|
-
WasmMatcherDatabase, WasmMatcherEngine, detectAudioMarker, detectPatternMark, estimatePitchRatio, estimatePitchRatioAgainstReference, estimatePitchRatioAgainstSpectralFeatures, extractPitchReference, extractSpectralShiftReference, init_matcher_wasm
|
|
8
|
+
WasmMatcherDatabase, WasmMatcherEngine, detectAudioMarker, detectAudioMarkerWithLimits, detectPatternMark, detectPatternMarkWithLimits, estimatePitchRatio, estimatePitchRatioAgainstReference, estimatePitchRatioAgainstSpectralFeatures, extractPitchReference, extractSpectralShiftReference, init_matcher_wasm
|
|
9
9
|
} from "./core_bg.js";
|
|
@@ -195,9 +195,21 @@ export class WasmMatcherEngine {
|
|
|
195
195
|
* @returns {any}
|
|
196
196
|
*/
|
|
197
197
|
detectAudioMarker(pcm16_le, sample_rate, config) {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
const ret = wasm.wasmmatcherengine_detectAudioMarker(this.__wbg_ptr, pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config));
|
|
199
|
+
if (ret[2]) {
|
|
200
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
201
|
+
}
|
|
202
|
+
return takeFromExternrefTable0(ret[0]);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* @param {Uint8Array} pcm16_le
|
|
206
|
+
* @param {number} sample_rate
|
|
207
|
+
* @param {any | null} [config]
|
|
208
|
+
* @param {any | null} [limits]
|
|
209
|
+
* @returns {any}
|
|
210
|
+
*/
|
|
211
|
+
detectAudioMarkerWithLimits(pcm16_le, sample_rate, config, limits) {
|
|
212
|
+
const ret = wasm.wasmmatcherengine_detectAudioMarkerWithLimits(this.__wbg_ptr, pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config), isLikeNone(limits) ? 0 : addToExternrefTable0(limits));
|
|
201
213
|
if (ret[2]) {
|
|
202
214
|
throw takeFromExternrefTable0(ret[1]);
|
|
203
215
|
}
|
|
@@ -210,9 +222,21 @@ export class WasmMatcherEngine {
|
|
|
210
222
|
* @returns {any}
|
|
211
223
|
*/
|
|
212
224
|
detectPatternMark(pcm16_le, sample_rate, config) {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
225
|
+
const ret = wasm.wasmmatcherengine_detectPatternMark(this.__wbg_ptr, pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config));
|
|
226
|
+
if (ret[2]) {
|
|
227
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
228
|
+
}
|
|
229
|
+
return takeFromExternrefTable0(ret[0]);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* @param {Uint8Array} pcm16_le
|
|
233
|
+
* @param {number} sample_rate
|
|
234
|
+
* @param {any | null} [config]
|
|
235
|
+
* @param {any | null} [limits]
|
|
236
|
+
* @returns {any}
|
|
237
|
+
*/
|
|
238
|
+
detectPatternMarkWithLimits(pcm16_le, sample_rate, config, limits) {
|
|
239
|
+
const ret = wasm.wasmmatcherengine_detectPatternMarkWithLimits(this.__wbg_ptr, pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config), isLikeNone(limits) ? 0 : addToExternrefTable0(limits));
|
|
216
240
|
if (ret[2]) {
|
|
217
241
|
throw takeFromExternrefTable0(ret[1]);
|
|
218
242
|
}
|
|
@@ -348,9 +372,22 @@ if (Symbol.dispose) WasmMatcherEngine.prototype[Symbol.dispose] = WasmMatcherEng
|
|
|
348
372
|
* @returns {any}
|
|
349
373
|
*/
|
|
350
374
|
export function detectAudioMarker(pcm16_le, sample_rate, config) {
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
375
|
+
const ret = wasm.detectAudioMarker(pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config));
|
|
376
|
+
if (ret[2]) {
|
|
377
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
378
|
+
}
|
|
379
|
+
return takeFromExternrefTable0(ret[0]);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* @param {Uint8Array} pcm16_le
|
|
384
|
+
* @param {number} sample_rate
|
|
385
|
+
* @param {any | null} [config]
|
|
386
|
+
* @param {any | null} [limits]
|
|
387
|
+
* @returns {any}
|
|
388
|
+
*/
|
|
389
|
+
export function detectAudioMarkerWithLimits(pcm16_le, sample_rate, config, limits) {
|
|
390
|
+
const ret = wasm.detectAudioMarkerWithLimits(pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config), isLikeNone(limits) ? 0 : addToExternrefTable0(limits));
|
|
354
391
|
if (ret[2]) {
|
|
355
392
|
throw takeFromExternrefTable0(ret[1]);
|
|
356
393
|
}
|
|
@@ -364,9 +401,22 @@ export function detectAudioMarker(pcm16_le, sample_rate, config) {
|
|
|
364
401
|
* @returns {any}
|
|
365
402
|
*/
|
|
366
403
|
export function detectPatternMark(pcm16_le, sample_rate, config) {
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
404
|
+
const ret = wasm.detectPatternMark(pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config));
|
|
405
|
+
if (ret[2]) {
|
|
406
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
407
|
+
}
|
|
408
|
+
return takeFromExternrefTable0(ret[0]);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @param {Uint8Array} pcm16_le
|
|
413
|
+
* @param {number} sample_rate
|
|
414
|
+
* @param {any | null} [config]
|
|
415
|
+
* @param {any | null} [limits]
|
|
416
|
+
* @returns {any}
|
|
417
|
+
*/
|
|
418
|
+
export function detectPatternMarkWithLimits(pcm16_le, sample_rate, config, limits) {
|
|
419
|
+
const ret = wasm.detectPatternMarkWithLimits(pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config), isLikeNone(limits) ? 0 : addToExternrefTable0(limits));
|
|
370
420
|
if (ret[2]) {
|
|
371
421
|
throw takeFromExternrefTable0(ret[1]);
|
|
372
422
|
}
|
|
Binary file
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
export const memory: WebAssembly.Memory;
|
|
4
4
|
export const __wbg_wasmmatcherdatabase_free: (a: number, b: number) => void;
|
|
5
5
|
export const __wbg_wasmmatcherengine_free: (a: number, b: number) => void;
|
|
6
|
-
export const detectAudioMarker: (a:
|
|
7
|
-
export const
|
|
6
|
+
export const detectAudioMarker: (a: any, b: number, c: number) => [number, number, number];
|
|
7
|
+
export const detectAudioMarkerWithLimits: (a: any, b: number, c: number, d: number) => [number, number, number];
|
|
8
|
+
export const detectPatternMarkWithLimits: (a: any, b: number, c: number, d: number) => [number, number, number];
|
|
8
9
|
export const estimatePitchRatio: (a: number, b: number, c: number) => [number, number, number];
|
|
9
10
|
export const estimatePitchRatioAgainstReference: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
|
|
10
11
|
export const estimatePitchRatioAgainstSpectralFeatures: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => [number, number, number];
|
|
@@ -24,8 +25,10 @@ export const wasmmatcherdatabase_removeEntry: (a: number, b: number, c: number)
|
|
|
24
25
|
export const wasmmatcherdatabase_setEntryVariantMetadata: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
25
26
|
export const wasmmatcherengine_bestMatch: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
26
27
|
export const wasmmatcherengine_bestMatchWithVariants: (a: number, b: number, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
|
|
27
|
-
export const wasmmatcherengine_detectAudioMarker: (a: number, b:
|
|
28
|
-
export const
|
|
28
|
+
export const wasmmatcherengine_detectAudioMarker: (a: number, b: any, c: number, d: number) => [number, number, number];
|
|
29
|
+
export const wasmmatcherengine_detectAudioMarkerWithLimits: (a: number, b: any, c: number, d: number, e: number) => [number, number, number];
|
|
30
|
+
export const wasmmatcherengine_detectPatternMark: (a: number, b: any, c: number, d: number) => [number, number, number];
|
|
31
|
+
export const wasmmatcherengine_detectPatternMarkWithLimits: (a: number, b: any, c: number, d: number, e: number) => [number, number, number];
|
|
29
32
|
export const wasmmatcherengine_estimatePitchRatio: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
30
33
|
export const wasmmatcherengine_estimatePitchRatioAgainstReference: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
31
34
|
export const wasmmatcherengine_estimatePitchRatioAgainstSpectralFeatures: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => [number, number, number];
|
|
@@ -36,6 +39,7 @@ export const wasmmatcherengine_matchQueryWithVariants: (a: number, b: number, c:
|
|
|
36
39
|
export const wasmmatcherengine_new: () => number;
|
|
37
40
|
export const wasmmatcherengine_resetDistinctMatchState: (a: number) => void;
|
|
38
41
|
export const init_matcher_wasm: () => void;
|
|
42
|
+
export const detectPatternMark: (a: any, b: number, c: number) => [number, number, number];
|
|
39
43
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
40
44
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
41
45
|
export const __wbindgen_exn_store: (a: number) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,13 @@ interface AudioMarkerCode {
|
|
|
13
13
|
code: string;
|
|
14
14
|
pattern: string;
|
|
15
15
|
}
|
|
16
|
+
declare const DEFAULT_MAX_AUDIO_MARKER_PCM_BYTES = 336000;
|
|
17
|
+
declare const DEFAULT_MAX_AUDIO_MARKER_SAMPLES = 168000;
|
|
18
|
+
declare const DEFAULT_MAX_AUDIO_MARKER_DURATION_MILLIS = 3500;
|
|
19
|
+
declare const DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS = 1000000000;
|
|
20
|
+
declare const AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON = "pcm_input_budget_exceeded";
|
|
21
|
+
declare const AUDIO_MARKER_WORK_BUDGET_EXCEEDED_REASON = "detector_work_budget_exceeded";
|
|
22
|
+
declare const AUDIO_MARKER_WORK_OVERFLOW_REASON = "detector_work_overflow";
|
|
16
23
|
interface AudioMarkerConfig {
|
|
17
24
|
bandLowHz?: number;
|
|
18
25
|
bandHighHz?: number;
|
|
@@ -29,6 +36,13 @@ interface AudioMarkerConfig {
|
|
|
29
36
|
maxSyncCandidates?: number;
|
|
30
37
|
codebook?: AudioMarkerCode[];
|
|
31
38
|
}
|
|
39
|
+
/** Trusted-input overrides for one-shot audio-marker detection. */
|
|
40
|
+
interface AudioMarkerLimits {
|
|
41
|
+
maxPcmBytes?: number;
|
|
42
|
+
maxSamples?: number;
|
|
43
|
+
maxDurationMillis?: number;
|
|
44
|
+
maxWorkUnits?: number;
|
|
45
|
+
}
|
|
32
46
|
interface AudioMarkerDetection {
|
|
33
47
|
pattern?: string | null;
|
|
34
48
|
code?: string | null;
|
|
@@ -237,6 +251,8 @@ declare class MatcherSession {
|
|
|
237
251
|
estimatePitchRatioAgainstReference(pcm16Le: Uint8Array, sampleRate: number, referencePitchHz: number, referenceConfidence?: number | null): Promise<PitchRatioEstimate>;
|
|
238
252
|
estimatePitchRatioAgainstReferenceFeatures(pcm16Le: Uint8Array, sampleRate: number, referenceFeatures: string, referencePositionMillis: number, referenceConfidence?: number | null): Promise<PitchRatioEstimate>;
|
|
239
253
|
detectAudioMarker(pcm16Le: Uint8Array, sampleRate: number, config?: AudioMarkerConfig): Promise<AudioMarkerDetection>;
|
|
254
|
+
/** Detect an audio marker with explicit limits for trusted offline input. */
|
|
255
|
+
detectAudioMarkerWithLimits(pcm16Le: Uint8Array, sampleRate: number, config: AudioMarkerConfig | undefined, limits: AudioMarkerLimits): Promise<AudioMarkerDetection>;
|
|
240
256
|
matchWithVariants(query: Uint8Array, pitchQuery: VariantMatchQuery, config?: MatchConfig, variantConfig?: VariantMatchConfig): Promise<VariantMatch[]>;
|
|
241
257
|
bestMatchWithVariants(query: Uint8Array, pitchQuery: VariantMatchQuery, config?: MatchConfig, variantConfig?: VariantMatchConfig): Promise<VariantMatch | null>;
|
|
242
258
|
destroy(): void;
|
|
@@ -252,4 +268,4 @@ declare function __setMatcherWasmModuleForTests(moduleLike: unknown): void;
|
|
|
252
268
|
declare function __setMatcherEmbeddedPackForTests(bytes: Uint8Array): void;
|
|
253
269
|
declare function __resetMatcherWasmForTests(): void;
|
|
254
270
|
|
|
255
|
-
export { AudioFingerprintType, type AudioMarkerCode, type AudioMarkerConfig, type AudioMarkerDetection, DEFAULT_MAX_PACK_BYTES, type EntryMetadata, type InitMatcherOptions, type InitMatcherResult, type MatchConfig, type MatchResult, type MatcherEphemeralAuthOptions, type MatcherEphemeralAuthResult, type MatcherEphemeralKeyExchangeMetadata, type MatcherEphemeralKeyExchangeOptions, MatcherSession, type MatcherSessionEventMap, type MatcherSessionEventName, type MatcherSessionListener, type MatcherSessionOptions, type PackSource, type PitchRatioEstimate, type PitchReferenceEstimate, type VariantDecision, type VariantDecisionStatus, type VariantMatch, type VariantMatchConfig, type VariantMatchQuery, __resetMatcherWasmForTests, __setMatcherEmbeddedPackForTests, __setMatcherWasmModuleForTests, initMatcher };
|
|
271
|
+
export { AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON, AUDIO_MARKER_WORK_BUDGET_EXCEEDED_REASON, AUDIO_MARKER_WORK_OVERFLOW_REASON, AudioFingerprintType, type AudioMarkerCode, type AudioMarkerConfig, type AudioMarkerDetection, type AudioMarkerLimits, DEFAULT_MAX_AUDIO_MARKER_DURATION_MILLIS, DEFAULT_MAX_AUDIO_MARKER_PCM_BYTES, DEFAULT_MAX_AUDIO_MARKER_SAMPLES, DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS, DEFAULT_MAX_PACK_BYTES, type EntryMetadata, type InitMatcherOptions, type InitMatcherResult, type MatchConfig, type MatchResult, type MatcherEphemeralAuthOptions, type MatcherEphemeralAuthResult, type MatcherEphemeralKeyExchangeMetadata, type MatcherEphemeralKeyExchangeOptions, MatcherSession, type MatcherSessionEventMap, type MatcherSessionEventName, type MatcherSessionListener, type MatcherSessionOptions, type PackSource, type PitchRatioEstimate, type PitchReferenceEstimate, type VariantDecision, type VariantDecisionStatus, type VariantMatch, type VariantMatchConfig, type VariantMatchQuery, __resetMatcherWasmForTests, __setMatcherEmbeddedPackForTests, __setMatcherWasmModuleForTests, initMatcher };
|
package/dist/index.js
CHANGED
|
@@ -198,6 +198,12 @@ function assertKnownByteLength(byteLength, maxPackBytes) {
|
|
|
198
198
|
throw packSizeError(maxPackBytes);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
function cancelWithoutWaiting(cancel) {
|
|
202
|
+
try {
|
|
203
|
+
void cancel().catch(() => void 0);
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
201
207
|
function classifyPackSource(source) {
|
|
202
208
|
if (source instanceof Uint8Array) {
|
|
203
209
|
return "uint8array";
|
|
@@ -231,7 +237,8 @@ function resolveFetch3(fetcher) {
|
|
|
231
237
|
}
|
|
232
238
|
return globalThis.fetch.bind(globalThis);
|
|
233
239
|
}
|
|
234
|
-
async function readResponseAsBytes(response, maxPackBytes) {
|
|
240
|
+
async function readResponseAsBytes(response, maxPackBytes = DEFAULT_MAX_PACK_BYTES) {
|
|
241
|
+
maxPackBytes = validateMaxPackBytes(maxPackBytes);
|
|
235
242
|
if (!response.ok) {
|
|
236
243
|
throw new Error(`Failed to load pack: HTTP ${response.status}`);
|
|
237
244
|
}
|
|
@@ -239,7 +246,13 @@ async function readResponseAsBytes(response, maxPackBytes) {
|
|
|
239
246
|
if (contentLength !== null && /^\d+$/.test(contentLength.trim())) {
|
|
240
247
|
const declaredLength = Number(contentLength);
|
|
241
248
|
if (Number.isSafeInteger(declaredLength)) {
|
|
242
|
-
|
|
249
|
+
if (declaredLength > maxPackBytes) {
|
|
250
|
+
const body = response.body;
|
|
251
|
+
if (body) {
|
|
252
|
+
cancelWithoutWaiting(() => body.cancel());
|
|
253
|
+
}
|
|
254
|
+
throw packSizeError(maxPackBytes);
|
|
255
|
+
}
|
|
243
256
|
}
|
|
244
257
|
}
|
|
245
258
|
if (!response.body) {
|
|
@@ -256,7 +269,7 @@ async function readResponseAsBytes(response, maxPackBytes) {
|
|
|
256
269
|
}
|
|
257
270
|
byteLength += value.byteLength;
|
|
258
271
|
if (byteLength > maxPackBytes) {
|
|
259
|
-
|
|
272
|
+
cancelWithoutWaiting(() => reader.cancel());
|
|
260
273
|
throw packSizeError(maxPackBytes);
|
|
261
274
|
}
|
|
262
275
|
chunks.push(value);
|
|
@@ -297,6 +310,21 @@ async function normalizePackSource(source, fetcher, maxPackBytes = DEFAULT_MAX_P
|
|
|
297
310
|
throw new Error("Unsupported pack source type");
|
|
298
311
|
}
|
|
299
312
|
|
|
313
|
+
// src/types.ts
|
|
314
|
+
var AudioFingerprintType = /* @__PURE__ */ ((AudioFingerprintType3) => {
|
|
315
|
+
AudioFingerprintType3[AudioFingerprintType3["Livestream"] = 1] = "Livestream";
|
|
316
|
+
AudioFingerprintType3[AudioFingerprintType3["ArchiveCf"] = 2] = "ArchiveCf";
|
|
317
|
+
AudioFingerprintType3[AudioFingerprintType3["ArchivePg"] = 4] = "ArchivePg";
|
|
318
|
+
return AudioFingerprintType3;
|
|
319
|
+
})(AudioFingerprintType || {});
|
|
320
|
+
var DEFAULT_MAX_AUDIO_MARKER_PCM_BYTES = 336e3;
|
|
321
|
+
var DEFAULT_MAX_AUDIO_MARKER_SAMPLES = 168e3;
|
|
322
|
+
var DEFAULT_MAX_AUDIO_MARKER_DURATION_MILLIS = 3500;
|
|
323
|
+
var DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS = 1e9;
|
|
324
|
+
var AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON = "pcm_input_budget_exceeded";
|
|
325
|
+
var AUDIO_MARKER_WORK_BUDGET_EXCEEDED_REASON = "detector_work_budget_exceeded";
|
|
326
|
+
var AUDIO_MARKER_WORK_OVERFLOW_REASON = "detector_work_overflow";
|
|
327
|
+
|
|
300
328
|
// src/match-deduper.ts
|
|
301
329
|
var DEFAULT_NO_MATCH_CONFIRMATION_COUNT = 2;
|
|
302
330
|
function getMatchIdentity(match) {
|
|
@@ -504,6 +532,23 @@ var WrappedMatcher = class {
|
|
|
504
532
|
detectAudioMarkerFn.call(this.inner, pcm16Le, sampleRate, config)
|
|
505
533
|
);
|
|
506
534
|
}
|
|
535
|
+
const detectAudioMarkerWithLimitsFn = getCallable(this.inner, [
|
|
536
|
+
"detectAudioMarkerWithLimits",
|
|
537
|
+
"detect_audio_marker_with_limits",
|
|
538
|
+
"detectPatternMarkWithLimits",
|
|
539
|
+
"detect_pattern_mark_with_limits"
|
|
540
|
+
]);
|
|
541
|
+
if (detectAudioMarkerWithLimitsFn) {
|
|
542
|
+
this.detectAudioMarkerWithLimits = (pcm16Le, sampleRate, config, limits) => normalizeAudioMarkerDetection(
|
|
543
|
+
detectAudioMarkerWithLimitsFn.call(
|
|
544
|
+
this.inner,
|
|
545
|
+
pcm16Le,
|
|
546
|
+
sampleRate,
|
|
547
|
+
config,
|
|
548
|
+
limits
|
|
549
|
+
)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
507
552
|
}
|
|
508
553
|
inner;
|
|
509
554
|
estimatePitchRatio;
|
|
@@ -511,6 +556,7 @@ var WrappedMatcher = class {
|
|
|
511
556
|
estimatePitchRatioAgainstReference;
|
|
512
557
|
estimatePitchRatioAgainstReferenceFeatures;
|
|
513
558
|
detectAudioMarker;
|
|
559
|
+
detectAudioMarkerWithLimits;
|
|
514
560
|
findMatches(database, query, config) {
|
|
515
561
|
const fn = getCallable(this.inner, ["findMatches", "find_matches", "matchQuery", "match_query", "match"]);
|
|
516
562
|
if (!fn) {
|
|
@@ -743,6 +789,12 @@ function createBindingsFromModule(moduleLike) {
|
|
|
743
789
|
"detectPatternMark",
|
|
744
790
|
"detect_pattern_mark"
|
|
745
791
|
]);
|
|
792
|
+
const detectAudioMarkerWithLimitsFn = resolveFactory(mod, [
|
|
793
|
+
"detectAudioMarkerWithLimits",
|
|
794
|
+
"detect_audio_marker_with_limits",
|
|
795
|
+
"detectPatternMarkWithLimits",
|
|
796
|
+
"detect_pattern_mark_with_limits"
|
|
797
|
+
]);
|
|
746
798
|
const DatabaseCtor = mod.WasmMatcherDatabase ?? mod.MatcherDatabase ?? mod.Database;
|
|
747
799
|
const MatcherCtor = mod.WasmMatcherEngine ?? mod.MatcherEngine ?? mod.Matcher ?? mod.AudioMatcher;
|
|
748
800
|
if (!createDatabaseFactory && !DatabaseCtor) {
|
|
@@ -874,6 +926,24 @@ function createBindingsFromModule(moduleLike) {
|
|
|
874
926
|
const destroy = getCallable(matcher, ["destroy", "free"]);
|
|
875
927
|
destroy?.call(matcher);
|
|
876
928
|
}
|
|
929
|
+
},
|
|
930
|
+
detectAudioMarkerWithLimits(pcm16Le, sampleRate, config, limits) {
|
|
931
|
+
if (detectAudioMarkerWithLimitsFn) {
|
|
932
|
+
return normalizeAudioMarkerDetection(
|
|
933
|
+
detectAudioMarkerWithLimitsFn(pcm16Le, sampleRate, config, limits)
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
const matcher = createMatcherFactory ? createMatcherFactory() : new MatcherCtor();
|
|
937
|
+
try {
|
|
938
|
+
const wrapped = new WrappedMatcher(matcher);
|
|
939
|
+
if (!wrapped.detectAudioMarkerWithLimits) {
|
|
940
|
+
throw new Error("WASM matcher is missing detectAudioMarkerWithLimits-compatible method");
|
|
941
|
+
}
|
|
942
|
+
return wrapped.detectAudioMarkerWithLimits(pcm16Le, sampleRate, config, limits);
|
|
943
|
+
} finally {
|
|
944
|
+
const destroy = getCallable(matcher, ["destroy", "free"]);
|
|
945
|
+
destroy?.call(matcher);
|
|
946
|
+
}
|
|
877
947
|
}
|
|
878
948
|
};
|
|
879
949
|
}
|
|
@@ -901,6 +971,61 @@ async function getBindings(options) {
|
|
|
901
971
|
function toUint8ArrayCopy(input) {
|
|
902
972
|
return new Uint8Array(input);
|
|
903
973
|
}
|
|
974
|
+
function resolveAudioMarkerLimits(limits) {
|
|
975
|
+
const resolved = {
|
|
976
|
+
maxPcmBytes: limits?.maxPcmBytes ?? DEFAULT_MAX_AUDIO_MARKER_PCM_BYTES,
|
|
977
|
+
maxSamples: limits?.maxSamples ?? DEFAULT_MAX_AUDIO_MARKER_SAMPLES,
|
|
978
|
+
maxDurationMillis: limits?.maxDurationMillis ?? DEFAULT_MAX_AUDIO_MARKER_DURATION_MILLIS,
|
|
979
|
+
maxWorkUnits: limits?.maxWorkUnits ?? DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS
|
|
980
|
+
};
|
|
981
|
+
for (const [name, value] of Object.entries(resolved)) {
|
|
982
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
983
|
+
throw new RangeError(`Invalid audio marker limits: ${name} must be a non-negative safe integer`);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return resolved;
|
|
987
|
+
}
|
|
988
|
+
function audioMarkerInputFailureReason(pcm16Le, sampleRate, limits) {
|
|
989
|
+
if (pcm16Le.byteLength > limits.maxPcmBytes) {
|
|
990
|
+
return AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON;
|
|
991
|
+
}
|
|
992
|
+
if (pcm16Le.byteLength % 2 !== 0) {
|
|
993
|
+
return "invalid_pcm16_byte_length";
|
|
994
|
+
}
|
|
995
|
+
const sampleCount = pcm16Le.byteLength / 2;
|
|
996
|
+
if (sampleCount > limits.maxSamples) {
|
|
997
|
+
return AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON;
|
|
998
|
+
}
|
|
999
|
+
if (Number.isSafeInteger(sampleRate) && sampleRate > 0 && sampleCount * 1e3 > limits.maxDurationMillis * sampleRate) {
|
|
1000
|
+
return AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON;
|
|
1001
|
+
}
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
function absentAudioMarkerDetection(config, reason) {
|
|
1005
|
+
return {
|
|
1006
|
+
pattern: null,
|
|
1007
|
+
code: null,
|
|
1008
|
+
confidence: null,
|
|
1009
|
+
score: 0,
|
|
1010
|
+
margin: 0,
|
|
1011
|
+
accuracy: 0,
|
|
1012
|
+
bandLowHz: config?.bandLowHz ?? 15e3,
|
|
1013
|
+
bandHighHz: config?.bandHighHz ?? 15800,
|
|
1014
|
+
syncScore: 0,
|
|
1015
|
+
chipCount: 0,
|
|
1016
|
+
frameStartSample: 0,
|
|
1017
|
+
frameCount: 0,
|
|
1018
|
+
voteCount: 0,
|
|
1019
|
+
reason,
|
|
1020
|
+
candidatePattern: null,
|
|
1021
|
+
candidateCode: null,
|
|
1022
|
+
candidateConfidence: null,
|
|
1023
|
+
runnerUpPattern: null,
|
|
1024
|
+
runnerUpCode: null,
|
|
1025
|
+
runnerUpScore: null,
|
|
1026
|
+
runnerUpAccuracy: null
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
904
1029
|
function handleError(emitter, phase, unknownError) {
|
|
905
1030
|
const error = unknownError instanceof Error ? unknownError : new Error(String(unknownError));
|
|
906
1031
|
emitter.emit("error", { phase, error });
|
|
@@ -1166,6 +1291,11 @@ var MatcherSession = class {
|
|
|
1166
1291
|
async detectAudioMarker(pcm16Le, sampleRate, config) {
|
|
1167
1292
|
try {
|
|
1168
1293
|
await this.ensureLiveReady();
|
|
1294
|
+
const limits = resolveAudioMarkerLimits();
|
|
1295
|
+
const failureReason = audioMarkerInputFailureReason(pcm16Le, sampleRate, limits);
|
|
1296
|
+
if (failureReason) {
|
|
1297
|
+
return absentAudioMarkerDetection(config, failureReason);
|
|
1298
|
+
}
|
|
1169
1299
|
const safePcm = toUint8ArrayCopy(pcm16Le);
|
|
1170
1300
|
const matcher = this.matcherOrThrow();
|
|
1171
1301
|
if (matcher.detectAudioMarker) {
|
|
@@ -1179,6 +1309,42 @@ var MatcherSession = class {
|
|
|
1179
1309
|
handleError(this.events, "detectAudioMarker", error);
|
|
1180
1310
|
}
|
|
1181
1311
|
}
|
|
1312
|
+
/** Detect an audio marker with explicit limits for trusted offline input. */
|
|
1313
|
+
async detectAudioMarkerWithLimits(pcm16Le, sampleRate, config, limits) {
|
|
1314
|
+
try {
|
|
1315
|
+
await this.ensureLiveReady();
|
|
1316
|
+
const resolvedLimits = resolveAudioMarkerLimits(limits);
|
|
1317
|
+
const failureReason = audioMarkerInputFailureReason(
|
|
1318
|
+
pcm16Le,
|
|
1319
|
+
sampleRate,
|
|
1320
|
+
resolvedLimits
|
|
1321
|
+
);
|
|
1322
|
+
if (failureReason) {
|
|
1323
|
+
return absentAudioMarkerDetection(config, failureReason);
|
|
1324
|
+
}
|
|
1325
|
+
const safePcm = toUint8ArrayCopy(pcm16Le);
|
|
1326
|
+
const matcher = this.matcherOrThrow();
|
|
1327
|
+
if (matcher.detectAudioMarkerWithLimits) {
|
|
1328
|
+
return matcher.detectAudioMarkerWithLimits(
|
|
1329
|
+
safePcm,
|
|
1330
|
+
sampleRate,
|
|
1331
|
+
config,
|
|
1332
|
+
resolvedLimits
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
if (!this.bindings) {
|
|
1336
|
+
throw new Error("Matcher bindings are not initialized");
|
|
1337
|
+
}
|
|
1338
|
+
return this.bindings.detectAudioMarkerWithLimits(
|
|
1339
|
+
safePcm,
|
|
1340
|
+
sampleRate,
|
|
1341
|
+
config,
|
|
1342
|
+
resolvedLimits
|
|
1343
|
+
);
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
handleError(this.events, "detectAudioMarkerWithLimits", error);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1182
1348
|
async matchWithVariants(query, pitchQuery, config, variantConfig) {
|
|
1183
1349
|
try {
|
|
1184
1350
|
await this.ensureLiveReady();
|
|
@@ -1313,16 +1479,15 @@ function __resetMatcherWasmForTests() {
|
|
|
1313
1479
|
__resetEmbeddedPackLoaderForTests();
|
|
1314
1480
|
initializedBindingsPromise = null;
|
|
1315
1481
|
}
|
|
1316
|
-
|
|
1317
|
-
// src/types.ts
|
|
1318
|
-
var AudioFingerprintType = /* @__PURE__ */ ((AudioFingerprintType2) => {
|
|
1319
|
-
AudioFingerprintType2[AudioFingerprintType2["Livestream"] = 1] = "Livestream";
|
|
1320
|
-
AudioFingerprintType2[AudioFingerprintType2["ArchiveCf"] = 2] = "ArchiveCf";
|
|
1321
|
-
AudioFingerprintType2[AudioFingerprintType2["ArchivePg"] = 4] = "ArchivePg";
|
|
1322
|
-
return AudioFingerprintType2;
|
|
1323
|
-
})(AudioFingerprintType || {});
|
|
1324
1482
|
export {
|
|
1483
|
+
AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON,
|
|
1484
|
+
AUDIO_MARKER_WORK_BUDGET_EXCEEDED_REASON,
|
|
1485
|
+
AUDIO_MARKER_WORK_OVERFLOW_REASON,
|
|
1325
1486
|
AudioFingerprintType,
|
|
1487
|
+
DEFAULT_MAX_AUDIO_MARKER_DURATION_MILLIS,
|
|
1488
|
+
DEFAULT_MAX_AUDIO_MARKER_PCM_BYTES,
|
|
1489
|
+
DEFAULT_MAX_AUDIO_MARKER_SAMPLES,
|
|
1490
|
+
DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS,
|
|
1326
1491
|
DEFAULT_MAX_PACK_BYTES,
|
|
1327
1492
|
MatcherSession,
|
|
1328
1493
|
__resetMatcherWasmForTests,
|