@sorisdk/matcher 0.6.8 → 0.6.9
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 +32 -0
- package/dist/generated/core.d.ts +10 -0
- package/dist/generated/core.js +1 -1
- package/dist/generated/core_bg.js +63 -5
- package/dist/generated/core_bg.wasm +0 -0
- package/dist/generated/core_bg.wasm.d.ts +3 -2
- package/dist/index.d.ts +9 -1
- package/dist/index.js +62 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,38 @@ const session = new MatcherSession({
|
|
|
55
55
|
});
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
## Continuous recognition and reset
|
|
59
|
+
|
|
60
|
+
Every query returns its matching result, including repeated material matches.
|
|
61
|
+
`match`, `bestMatch`, and their variant methods share one event observer:
|
|
62
|
+
continuous A → A emits one `match`, while A → B → A emits three. Material
|
|
63
|
+
identity includes the fingerprint type. There is no material cooldown.
|
|
64
|
+
|
|
65
|
+
Every successful observation resets the consecutive-miss counter and renews
|
|
66
|
+
the run, even when its public event is suppressed. Two consecutive no-match
|
|
67
|
+
results end the run and emit one `nomatch`; a single transient miss does not.
|
|
68
|
+
A gap **greater than 30 seconds since the last successful observation** also
|
|
69
|
+
ends continuity, including periods with no queries. This boundary is evaluated
|
|
70
|
+
on the next query: a success emits a new `match` immediately, or a no-match
|
|
71
|
+
emits `nomatch` without waiting for a second miss. The exported
|
|
72
|
+
`MATCH_CONTINUITY_GAP_MS` is shared with Web activity reporting.
|
|
73
|
+
|
|
74
|
+
Call `session.resetRecognitionState()` at an explicit input/capture boundary.
|
|
75
|
+
It synchronously clears event suppression and invalidates pending queries from
|
|
76
|
+
the previous run, retaining the loaded database. Invalidated queries return
|
|
77
|
+
`null` (best-match methods) or `[]` (multi-match methods) without match/no-match
|
|
78
|
+
events. Successful pack replacement and entry mutations also reset recognition
|
|
79
|
+
state; a failed pack load retains the previous state and entries.
|
|
80
|
+
|
|
81
|
+
The low-level generated WASM matcher always returns successful observations;
|
|
82
|
+
event deduplication belongs to `MatcherSession`. Its existing
|
|
83
|
+
`resetDistinctMatchState()` export remains available for compatibility.
|
|
84
|
+
|
|
85
|
+
For source regression tests, build the package first with
|
|
86
|
+
`corepack pnpm --filter @sorisdk/matcher build`, then run
|
|
87
|
+
`corepack pnpm --filter @sorisdk/matcher test`. Tests include production WASM
|
|
88
|
+
instantiated with the TypeScript session, using deterministic fingerprints.
|
|
89
|
+
|
|
58
90
|
## Pack loading
|
|
59
91
|
|
|
60
92
|
`loadPack(...)` accepts:
|
package/dist/generated/core.d.ts
CHANGED
|
@@ -35,11 +35,21 @@ export class WasmMatcherEngine {
|
|
|
35
35
|
matchQuery(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
|
|
36
36
|
matchQueryWithVariants(database: WasmMatcherDatabase, query: Uint8Array, pitch_query: any, config?: any | null, variant_config?: any | null): any;
|
|
37
37
|
constructor();
|
|
38
|
+
/**
|
|
39
|
+
* Compatibility no-op: continuous-run deduplication belongs to MatcherSession.
|
|
40
|
+
* The binding returns every successful observation and retains no run state.
|
|
41
|
+
*/
|
|
38
42
|
resetDistinctMatchState(): void;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export function detectAudioMarker(pcm16_le: Uint8Array, sample_rate: number, config?: any | null): any;
|
|
42
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Explicit v1 policy; existing marker exports remain on the legacy policy.
|
|
49
|
+
* Result includes raw detection, normalized diagnostics and shared eligibility.
|
|
50
|
+
*/
|
|
51
|
+
export function detectAudioMarkerLevelRobust(pcm16_le: Uint8Array, sample_rate: number, config?: any | null, limits?: any | null): any;
|
|
52
|
+
|
|
43
53
|
export function detectAudioMarkerWithLimits(pcm16_le: Uint8Array, sample_rate: number, config?: any | null, limits?: any | null): any;
|
|
44
54
|
|
|
45
55
|
export function detectPatternMark(pcm16_le: Uint8Array, sample_rate: number, config?: any | 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, detectAudioMarkerWithLimits, detectPatternMark, detectPatternMarkWithLimits, estimatePitchRatio, estimatePitchRatioAgainstReference, estimatePitchRatioAgainstSpectralFeatures, extractPitchReference, extractSpectralShiftReference, init_matcher_wasm
|
|
8
|
+
WasmMatcherDatabase, WasmMatcherEngine, detectAudioMarker, detectAudioMarkerLevelRobust, detectAudioMarkerWithLimits, detectPatternMark, detectPatternMarkWithLimits, estimatePitchRatio, estimatePitchRatioAgainstReference, estimatePitchRatioAgainstSpectralFeatures, extractPitchReference, extractSpectralShiftReference, init_matcher_wasm
|
|
9
9
|
} from "./core_bg.js";
|
|
@@ -359,6 +359,10 @@ export class WasmMatcherEngine {
|
|
|
359
359
|
WasmMatcherEngineFinalization.register(this, this.__wbg_ptr, this);
|
|
360
360
|
return this;
|
|
361
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Compatibility no-op: continuous-run deduplication belongs to MatcherSession.
|
|
364
|
+
* The binding returns every successful observation and retains no run state.
|
|
365
|
+
*/
|
|
362
366
|
resetDistinctMatchState() {
|
|
363
367
|
wasm.wasmmatcherengine_resetDistinctMatchState(this.__wbg_ptr);
|
|
364
368
|
}
|
|
@@ -379,6 +383,23 @@ export function detectAudioMarker(pcm16_le, sample_rate, config) {
|
|
|
379
383
|
return takeFromExternrefTable0(ret[0]);
|
|
380
384
|
}
|
|
381
385
|
|
|
386
|
+
/**
|
|
387
|
+
* Explicit v1 policy; existing marker exports remain on the legacy policy.
|
|
388
|
+
* Result includes raw detection, normalized diagnostics and shared eligibility.
|
|
389
|
+
* @param {Uint8Array} pcm16_le
|
|
390
|
+
* @param {number} sample_rate
|
|
391
|
+
* @param {any | null} [config]
|
|
392
|
+
* @param {any | null} [limits]
|
|
393
|
+
* @returns {any}
|
|
394
|
+
*/
|
|
395
|
+
export function detectAudioMarkerLevelRobust(pcm16_le, sample_rate, config, limits) {
|
|
396
|
+
const ret = wasm.detectAudioMarkerLevelRobust(pcm16_le, sample_rate, isLikeNone(config) ? 0 : addToExternrefTable0(config), isLikeNone(limits) ? 0 : addToExternrefTable0(limits));
|
|
397
|
+
if (ret[2]) {
|
|
398
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
399
|
+
}
|
|
400
|
+
return takeFromExternrefTable0(ret[0]);
|
|
401
|
+
}
|
|
402
|
+
|
|
382
403
|
/**
|
|
383
404
|
* @param {Uint8Array} pcm16_le
|
|
384
405
|
* @param {number} sample_rate
|
|
@@ -599,6 +620,10 @@ export function __wbg_call_14b169f759b26747() { return handleError(function (arg
|
|
|
599
620
|
const ret = arg0.call(arg1);
|
|
600
621
|
return ret;
|
|
601
622
|
}, arguments); }
|
|
623
|
+
export function __wbg_call_a24592a6f349a97e() { return handleError(function (arg0, arg1, arg2) {
|
|
624
|
+
const ret = arg0.call(arg1, arg2);
|
|
625
|
+
return ret;
|
|
626
|
+
}, arguments); }
|
|
602
627
|
export function __wbg_done_9158f7cc8751ba32(arg0) {
|
|
603
628
|
const ret = arg0.done;
|
|
604
629
|
return ret;
|
|
@@ -618,10 +643,22 @@ export function __wbg_error_a6fa202b58aa1cd3(arg0, arg1) {
|
|
|
618
643
|
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
|
619
644
|
}
|
|
620
645
|
}
|
|
646
|
+
export function __wbg_getOwnPropertyDescriptor_131bd582a45a6f5d(arg0, arg1) {
|
|
647
|
+
const ret = Object.getOwnPropertyDescriptor(arg0, arg1);
|
|
648
|
+
return ret;
|
|
649
|
+
}
|
|
650
|
+
export function __wbg_getPrototypeOf_a56da9261bbd1e5b(arg0) {
|
|
651
|
+
const ret = Object.getPrototypeOf(arg0);
|
|
652
|
+
return ret;
|
|
653
|
+
}
|
|
621
654
|
export function __wbg_get_1affdbdd5573b16a() { return handleError(function (arg0, arg1) {
|
|
622
655
|
const ret = Reflect.get(arg0, arg1);
|
|
623
656
|
return ret;
|
|
624
657
|
}, arguments); }
|
|
658
|
+
export function __wbg_get_6011fa3a58f61074() { return handleError(function (arg0, arg1) {
|
|
659
|
+
const ret = Reflect.get(arg0, arg1);
|
|
660
|
+
return ret;
|
|
661
|
+
}, arguments); }
|
|
625
662
|
export function __wbg_get_8360291721e2339f(arg0, arg1) {
|
|
626
663
|
const ret = arg0[arg1 >>> 0];
|
|
627
664
|
return ret;
|
|
@@ -716,10 +753,6 @@ export function __wbg_next_7646edaa39458ef7(arg0) {
|
|
|
716
753
|
const ret = arg0.next;
|
|
717
754
|
return ret;
|
|
718
755
|
}
|
|
719
|
-
export function __wbg_now_a9b7df1cbee90986() {
|
|
720
|
-
const ret = Date.now();
|
|
721
|
-
return ret;
|
|
722
|
-
}
|
|
723
756
|
export function __wbg_prototypesetcall_a6b02eb00b0f4ce2(arg0, arg1, arg2) {
|
|
724
757
|
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
|
|
725
758
|
}
|
|
@@ -740,6 +773,26 @@ export function __wbg_stack_3b0d974bbf31e44f(arg0, arg1) {
|
|
|
740
773
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
741
774
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
742
775
|
}
|
|
776
|
+
export function __wbg_static_accessor_GLOBAL_8cfadc87a297ca02() {
|
|
777
|
+
const ret = typeof global === 'undefined' ? null : global;
|
|
778
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
779
|
+
}
|
|
780
|
+
export function __wbg_static_accessor_GLOBAL_THIS_602256ae5c8f42cf() {
|
|
781
|
+
const ret = typeof globalThis === 'undefined' ? null : globalThis;
|
|
782
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
783
|
+
}
|
|
784
|
+
export function __wbg_static_accessor_SELF_e445c1c7484aecc3() {
|
|
785
|
+
const ret = typeof self === 'undefined' ? null : self;
|
|
786
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
787
|
+
}
|
|
788
|
+
export function __wbg_static_accessor_WINDOW_f20e8576ef1e0f17() {
|
|
789
|
+
const ret = typeof window === 'undefined' ? null : window;
|
|
790
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
791
|
+
}
|
|
792
|
+
export function __wbg_toStringTag_d6b1716c86d4892d() {
|
|
793
|
+
const ret = Symbol.toStringTag;
|
|
794
|
+
return ret;
|
|
795
|
+
}
|
|
743
796
|
export function __wbg_value_ee3a06f4579184fa(arg0) {
|
|
744
797
|
const ret = arg0.value;
|
|
745
798
|
return ret;
|
|
@@ -755,11 +808,16 @@ export function __wbindgen_cast_0000000000000002(arg0) {
|
|
|
755
808
|
return ret;
|
|
756
809
|
}
|
|
757
810
|
export function __wbindgen_cast_0000000000000003(arg0, arg1) {
|
|
811
|
+
// Cast intrinsic for `Ref(Slice(U8)) -> NamedExternref("Uint8Array")`.
|
|
812
|
+
const ret = getArrayU8FromWasm0(arg0, arg1);
|
|
813
|
+
return ret;
|
|
814
|
+
}
|
|
815
|
+
export function __wbindgen_cast_0000000000000004(arg0, arg1) {
|
|
758
816
|
// Cast intrinsic for `Ref(String) -> Externref`.
|
|
759
817
|
const ret = getStringFromWasm0(arg0, arg1);
|
|
760
818
|
return ret;
|
|
761
819
|
}
|
|
762
|
-
export function
|
|
820
|
+
export function __wbindgen_cast_0000000000000005(arg0) {
|
|
763
821
|
// Cast intrinsic for `U64 -> Externref`.
|
|
764
822
|
const ret = BigInt.asUintN(64, arg0);
|
|
765
823
|
return ret;
|
|
Binary file
|
|
@@ -4,6 +4,7 @@ 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
6
|
export const detectAudioMarker: (a: any, b: number, c: number) => [number, number, number];
|
|
7
|
+
export const detectAudioMarkerLevelRobust: (a: any, b: number, c: number, d: number) => [number, number, number];
|
|
7
8
|
export const detectAudioMarkerWithLimits: (a: any, b: number, c: number, d: number) => [number, number, number];
|
|
8
9
|
export const detectPatternMarkWithLimits: (a: any, b: number, c: number, d: number) => [number, number, number];
|
|
9
10
|
export const estimatePitchRatio: (a: number, b: number, c: number) => [number, number, number];
|
|
@@ -27,7 +28,6 @@ export const wasmmatcherengine_bestMatch: (a: number, b: number, c: number, d: n
|
|
|
27
28
|
export const wasmmatcherengine_bestMatchWithVariants: (a: number, b: number, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
|
|
28
29
|
export const wasmmatcherengine_detectAudioMarker: (a: number, b: any, c: number, d: number) => [number, number, number];
|
|
29
30
|
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
31
|
export const wasmmatcherengine_detectPatternMarkWithLimits: (a: number, b: any, c: number, d: number, e: number) => [number, number, number];
|
|
32
32
|
export const wasmmatcherengine_estimatePitchRatio: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
33
33
|
export const wasmmatcherengine_estimatePitchRatioAgainstReference: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
@@ -36,10 +36,11 @@ export const wasmmatcherengine_extractPitchReference: (a: number, b: number, c:
|
|
|
36
36
|
export const wasmmatcherengine_extractSpectralShiftReference: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
37
37
|
export const wasmmatcherengine_matchQuery: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
38
38
|
export const wasmmatcherengine_matchQueryWithVariants: (a: number, b: number, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
|
|
39
|
-
export const wasmmatcherengine_new: () => number;
|
|
40
39
|
export const wasmmatcherengine_resetDistinctMatchState: (a: number) => void;
|
|
40
|
+
export const wasmmatcherengine_new: () => number;
|
|
41
41
|
export const init_matcher_wasm: () => void;
|
|
42
42
|
export const detectPatternMark: (a: any, b: number, c: number) => [number, number, number];
|
|
43
|
+
export const wasmmatcherengine_detectPatternMark: (a: number, b: any, c: number, d: number) => [number, number, number];
|
|
43
44
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
44
45
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
45
46
|
export const __wbindgen_exn_store: (a: number) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -230,6 +230,7 @@ declare class MatcherSession {
|
|
|
230
230
|
private matcher;
|
|
231
231
|
private destroyed;
|
|
232
232
|
private packLoadTail;
|
|
233
|
+
private recognitionGeneration;
|
|
233
234
|
private readonly distinctMatchDeduper;
|
|
234
235
|
constructor(options?: MatcherSessionOptions);
|
|
235
236
|
on<T extends MatcherSessionEventName>(eventName: T, listener: MatcherSessionListener<T>): this;
|
|
@@ -255,6 +256,10 @@ declare class MatcherSession {
|
|
|
255
256
|
detectAudioMarkerWithLimits(pcm16Le: Uint8Array, sampleRate: number, config: AudioMarkerConfig | undefined, limits: AudioMarkerLimits): Promise<AudioMarkerDetection>;
|
|
256
257
|
matchWithVariants(query: Uint8Array, pitchQuery: VariantMatchQuery, config?: MatchConfig, variantConfig?: VariantMatchConfig): Promise<VariantMatch[]>;
|
|
257
258
|
bestMatchWithVariants(query: Uint8Array, pitchQuery: VariantMatchQuery, config?: MatchConfig, variantConfig?: VariantMatchConfig): Promise<VariantMatch | null>;
|
|
259
|
+
/** End the current recognition run without removing loaded entries.
|
|
260
|
+
* Pending queries from the previous run cannot update event suppression state.
|
|
261
|
+
*/
|
|
262
|
+
resetRecognitionState(): void;
|
|
258
263
|
destroy(): void;
|
|
259
264
|
private bootstrap;
|
|
260
265
|
private ensureLiveReady;
|
|
@@ -268,4 +273,7 @@ declare function __setMatcherWasmModuleForTests(moduleLike: unknown): void;
|
|
|
268
273
|
declare function __setMatcherEmbeddedPackForTests(bytes: Uint8Array): void;
|
|
269
274
|
declare function __resetMatcherWasmForTests(): void;
|
|
270
275
|
|
|
271
|
-
|
|
276
|
+
/** Maximum gap between successful observations in one continuous material run. */
|
|
277
|
+
declare const MATCH_CONTINUITY_GAP_MS = 30000;
|
|
278
|
+
|
|
279
|
+
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, MATCH_CONTINUITY_GAP_MS, 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
|
@@ -327,28 +327,31 @@ var AUDIO_MARKER_WORK_OVERFLOW_REASON = "detector_work_overflow";
|
|
|
327
327
|
|
|
328
328
|
// src/match-deduper.ts
|
|
329
329
|
var DEFAULT_NO_MATCH_CONFIRMATION_COUNT = 2;
|
|
330
|
+
var MATCH_CONTINUITY_GAP_MS = 3e4;
|
|
330
331
|
function getMatchIdentity(match) {
|
|
331
332
|
return `${match.name}:${match.afpType}`;
|
|
332
333
|
}
|
|
333
334
|
var DistinctMatchDeduper = class {
|
|
334
335
|
lastIdentity = null;
|
|
335
336
|
pendingNoMatchCount = 0;
|
|
336
|
-
|
|
337
|
+
lastObservedAt = null;
|
|
338
|
+
observe(best, now = Date.now()) {
|
|
339
|
+
const expired = this.lastObservedAt !== null && now - this.lastObservedAt > MATCH_CONTINUITY_GAP_MS;
|
|
337
340
|
if (!best) {
|
|
338
341
|
if (this.lastIdentity === null) {
|
|
339
342
|
return { kind: "repeat", best: null };
|
|
340
343
|
}
|
|
341
344
|
this.pendingNoMatchCount += 1;
|
|
342
|
-
if (this.pendingNoMatchCount < DEFAULT_NO_MATCH_CONFIRMATION_COUNT) {
|
|
345
|
+
if (!expired && this.pendingNoMatchCount < DEFAULT_NO_MATCH_CONFIRMATION_COUNT) {
|
|
343
346
|
return { kind: "repeat", best: null };
|
|
344
347
|
}
|
|
345
|
-
this.
|
|
346
|
-
this.pendingNoMatchCount = 0;
|
|
348
|
+
this.reset();
|
|
347
349
|
return { kind: "nomatch" };
|
|
348
350
|
}
|
|
349
351
|
const nextIdentity = getMatchIdentity(best);
|
|
350
352
|
this.pendingNoMatchCount = 0;
|
|
351
|
-
|
|
353
|
+
this.lastObservedAt = now;
|
|
354
|
+
if (!expired && this.lastIdentity === nextIdentity) {
|
|
352
355
|
return { kind: "repeat", best };
|
|
353
356
|
}
|
|
354
357
|
this.lastIdentity = nextIdentity;
|
|
@@ -357,6 +360,7 @@ var DistinctMatchDeduper = class {
|
|
|
357
360
|
reset() {
|
|
358
361
|
this.lastIdentity = null;
|
|
359
362
|
this.pendingNoMatchCount = 0;
|
|
363
|
+
this.lastObservedAt = null;
|
|
360
364
|
}
|
|
361
365
|
};
|
|
362
366
|
|
|
@@ -753,11 +757,6 @@ function normalizeResults(rawResults) {
|
|
|
753
757
|
function normalizeVariantMatches(rawResults) {
|
|
754
758
|
return rawResults.filter((item) => typeof item === "object" && item !== null).map((item) => normalizeVariantMatch(item));
|
|
755
759
|
}
|
|
756
|
-
function isInternallyDuplicatedBestMatch(result) {
|
|
757
|
-
return Boolean(
|
|
758
|
-
result && typeof result === "object" && result[INTERNAL_DUPLICATE_MATCH_FLAG] === true
|
|
759
|
-
);
|
|
760
|
-
}
|
|
761
760
|
function resolveFactory(moduleLike, names) {
|
|
762
761
|
const fn = getCallable(moduleLike, names);
|
|
763
762
|
return fn ? fn.bind(moduleLike) : null;
|
|
@@ -1050,6 +1049,7 @@ var MatcherSession = class {
|
|
|
1050
1049
|
matcher = null;
|
|
1051
1050
|
destroyed = false;
|
|
1052
1051
|
packLoadTail = Promise.resolve();
|
|
1052
|
+
recognitionGeneration = 0;
|
|
1053
1053
|
distinctMatchDeduper = new DistinctMatchDeduper();
|
|
1054
1054
|
constructor(options) {
|
|
1055
1055
|
this.fetcher = options?.fetch;
|
|
@@ -1102,8 +1102,7 @@ var MatcherSession = class {
|
|
|
1102
1102
|
}
|
|
1103
1103
|
this.database = stagedDatabase;
|
|
1104
1104
|
previousDatabase.destroy();
|
|
1105
|
-
this.
|
|
1106
|
-
this.matcher?.resetDistinctMatchState?.();
|
|
1105
|
+
this.resetRecognitionState();
|
|
1107
1106
|
this.events.emit("packload", {
|
|
1108
1107
|
sourceType: normalized.sourceType,
|
|
1109
1108
|
byteLength: normalized.bytes.byteLength
|
|
@@ -1116,8 +1115,7 @@ var MatcherSession = class {
|
|
|
1116
1115
|
try {
|
|
1117
1116
|
await this.ensureLiveReady();
|
|
1118
1117
|
this.databaseOrThrow().addEntry(name, afpType, toUint8ArrayCopy(fingerprint));
|
|
1119
|
-
this.
|
|
1120
|
-
this.matcher?.resetDistinctMatchState?.();
|
|
1118
|
+
this.resetRecognitionState();
|
|
1121
1119
|
} catch (error) {
|
|
1122
1120
|
handleError(this.events, "addEntry", error);
|
|
1123
1121
|
}
|
|
@@ -1131,8 +1129,7 @@ var MatcherSession = class {
|
|
|
1131
1129
|
toUint8ArrayCopy(fingerprint),
|
|
1132
1130
|
metadata
|
|
1133
1131
|
);
|
|
1134
|
-
this.
|
|
1135
|
-
this.matcher?.resetDistinctMatchState?.();
|
|
1132
|
+
this.resetRecognitionState();
|
|
1136
1133
|
} catch (error) {
|
|
1137
1134
|
handleError(this.events, "addOrReplace", error);
|
|
1138
1135
|
}
|
|
@@ -1141,8 +1138,7 @@ var MatcherSession = class {
|
|
|
1141
1138
|
try {
|
|
1142
1139
|
await this.ensureLiveReady();
|
|
1143
1140
|
this.databaseOrThrow().setEntryVariantMetadata(name, encodedMetadata);
|
|
1144
|
-
this.
|
|
1145
|
-
this.matcher?.resetDistinctMatchState?.();
|
|
1141
|
+
this.resetRecognitionState();
|
|
1146
1142
|
} catch (error) {
|
|
1147
1143
|
handleError(this.events, "setEntryVariantMetadata", error);
|
|
1148
1144
|
}
|
|
@@ -1151,8 +1147,7 @@ var MatcherSession = class {
|
|
|
1151
1147
|
try {
|
|
1152
1148
|
await this.ensureLiveReady();
|
|
1153
1149
|
this.databaseOrThrow().removeEntry(name);
|
|
1154
|
-
this.
|
|
1155
|
-
this.matcher?.resetDistinctMatchState?.();
|
|
1150
|
+
this.resetRecognitionState();
|
|
1156
1151
|
} catch (error) {
|
|
1157
1152
|
handleError(this.events, "removeEntry", error);
|
|
1158
1153
|
}
|
|
@@ -1161,8 +1156,7 @@ var MatcherSession = class {
|
|
|
1161
1156
|
try {
|
|
1162
1157
|
await this.ensureLiveReady();
|
|
1163
1158
|
this.databaseOrThrow().clear();
|
|
1164
|
-
this.
|
|
1165
|
-
this.matcher?.resetDistinctMatchState?.();
|
|
1159
|
+
this.resetRecognitionState();
|
|
1166
1160
|
} catch (error) {
|
|
1167
1161
|
handleError(this.events, "clear", error);
|
|
1168
1162
|
}
|
|
@@ -1176,27 +1170,38 @@ var MatcherSession = class {
|
|
|
1176
1170
|
}
|
|
1177
1171
|
}
|
|
1178
1172
|
async match(query, config) {
|
|
1173
|
+
const generation = this.recognitionGeneration;
|
|
1179
1174
|
try {
|
|
1180
1175
|
await this.ensureLiveReady();
|
|
1176
|
+
if (generation !== this.recognitionGeneration) {
|
|
1177
|
+
return [];
|
|
1178
|
+
}
|
|
1181
1179
|
const safeQuery = toUint8ArrayCopy(query);
|
|
1182
1180
|
this.events.emit("querystart", { queryByteLength: safeQuery.byteLength });
|
|
1181
|
+
if (generation !== this.recognitionGeneration || this.destroyed) {
|
|
1182
|
+
return [];
|
|
1183
|
+
}
|
|
1183
1184
|
const results = this.matcherOrThrow().findMatches(this.databaseOrThrow(), safeQuery, config);
|
|
1184
|
-
this.emitDistinctMatchEvent(results[0] ?? null, safeQuery.byteLength, results);
|
|
1185
|
+
this.emitDistinctMatchEvent(generation, results[0] ?? null, safeQuery.byteLength, results);
|
|
1185
1186
|
return results;
|
|
1186
1187
|
} catch (error) {
|
|
1187
1188
|
handleError(this.events, "match", error);
|
|
1188
1189
|
}
|
|
1189
1190
|
}
|
|
1190
1191
|
async bestMatch(query, config) {
|
|
1192
|
+
const generation = this.recognitionGeneration;
|
|
1191
1193
|
try {
|
|
1192
1194
|
await this.ensureLiveReady();
|
|
1195
|
+
if (generation !== this.recognitionGeneration) {
|
|
1196
|
+
return null;
|
|
1197
|
+
}
|
|
1193
1198
|
const safeQuery = toUint8ArrayCopy(query);
|
|
1194
1199
|
this.events.emit("querystart", { queryByteLength: safeQuery.byteLength });
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
return best;
|
|
1200
|
+
if (generation !== this.recognitionGeneration || this.destroyed) {
|
|
1201
|
+
return null;
|
|
1198
1202
|
}
|
|
1199
|
-
this.
|
|
1203
|
+
const best = this.matcherOrThrow().bestMatch(this.databaseOrThrow(), safeQuery, config);
|
|
1204
|
+
this.emitDistinctMatchEvent(generation, best, safeQuery.byteLength, best ? [best] : []);
|
|
1200
1205
|
return best;
|
|
1201
1206
|
} catch (error) {
|
|
1202
1207
|
handleError(this.events, "bestMatch", error);
|
|
@@ -1346,10 +1351,17 @@ var MatcherSession = class {
|
|
|
1346
1351
|
}
|
|
1347
1352
|
}
|
|
1348
1353
|
async matchWithVariants(query, pitchQuery, config, variantConfig) {
|
|
1354
|
+
const generation = this.recognitionGeneration;
|
|
1349
1355
|
try {
|
|
1350
1356
|
await this.ensureLiveReady();
|
|
1357
|
+
if (generation !== this.recognitionGeneration) {
|
|
1358
|
+
return [];
|
|
1359
|
+
}
|
|
1351
1360
|
const safeQuery = toUint8ArrayCopy(query);
|
|
1352
1361
|
this.events.emit("querystart", { queryByteLength: safeQuery.byteLength });
|
|
1362
|
+
if (generation !== this.recognitionGeneration || this.destroyed) {
|
|
1363
|
+
return [];
|
|
1364
|
+
}
|
|
1353
1365
|
const results = this.matcherOrThrow().findMatchesWithVariants(
|
|
1354
1366
|
this.databaseOrThrow(),
|
|
1355
1367
|
safeQuery,
|
|
@@ -1358,6 +1370,7 @@ var MatcherSession = class {
|
|
|
1358
1370
|
variantConfig
|
|
1359
1371
|
);
|
|
1360
1372
|
this.emitDistinctMatchEvent(
|
|
1373
|
+
generation,
|
|
1361
1374
|
results[0]?.matchResult ?? null,
|
|
1362
1375
|
safeQuery.byteLength,
|
|
1363
1376
|
results.map((result) => result.matchResult)
|
|
@@ -1368,10 +1381,17 @@ var MatcherSession = class {
|
|
|
1368
1381
|
}
|
|
1369
1382
|
}
|
|
1370
1383
|
async bestMatchWithVariants(query, pitchQuery, config, variantConfig) {
|
|
1384
|
+
const generation = this.recognitionGeneration;
|
|
1371
1385
|
try {
|
|
1372
1386
|
await this.ensureLiveReady();
|
|
1387
|
+
if (generation !== this.recognitionGeneration) {
|
|
1388
|
+
return null;
|
|
1389
|
+
}
|
|
1373
1390
|
const safeQuery = toUint8ArrayCopy(query);
|
|
1374
1391
|
this.events.emit("querystart", { queryByteLength: safeQuery.byteLength });
|
|
1392
|
+
if (generation !== this.recognitionGeneration || this.destroyed) {
|
|
1393
|
+
return null;
|
|
1394
|
+
}
|
|
1375
1395
|
const best = this.matcherOrThrow().bestMatchWithVariants(
|
|
1376
1396
|
this.databaseOrThrow(),
|
|
1377
1397
|
safeQuery,
|
|
@@ -1380,6 +1400,7 @@ var MatcherSession = class {
|
|
|
1380
1400
|
variantConfig
|
|
1381
1401
|
);
|
|
1382
1402
|
this.emitDistinctMatchEvent(
|
|
1403
|
+
generation,
|
|
1383
1404
|
best?.matchResult ?? null,
|
|
1384
1405
|
safeQuery.byteLength,
|
|
1385
1406
|
best ? [best.matchResult] : []
|
|
@@ -1389,10 +1410,19 @@ var MatcherSession = class {
|
|
|
1389
1410
|
handleError(this.events, "bestMatchWithVariants", error);
|
|
1390
1411
|
}
|
|
1391
1412
|
}
|
|
1413
|
+
/** End the current recognition run without removing loaded entries.
|
|
1414
|
+
* Pending queries from the previous run cannot update event suppression state.
|
|
1415
|
+
*/
|
|
1416
|
+
resetRecognitionState() {
|
|
1417
|
+
this.recognitionGeneration += 1;
|
|
1418
|
+
this.distinctMatchDeduper.reset();
|
|
1419
|
+
this.matcher?.resetDistinctMatchState?.();
|
|
1420
|
+
}
|
|
1392
1421
|
destroy() {
|
|
1393
1422
|
if (this.destroyed) {
|
|
1394
1423
|
return;
|
|
1395
1424
|
}
|
|
1425
|
+
this.resetRecognitionState();
|
|
1396
1426
|
this.destroyed = true;
|
|
1397
1427
|
this.matcher?.destroy();
|
|
1398
1428
|
this.database?.destroy();
|
|
@@ -1456,7 +1486,10 @@ var MatcherSession = class {
|
|
|
1456
1486
|
}
|
|
1457
1487
|
return this.database;
|
|
1458
1488
|
}
|
|
1459
|
-
emitDistinctMatchEvent(best, queryByteLength, results) {
|
|
1489
|
+
emitDistinctMatchEvent(generation, best, queryByteLength, results) {
|
|
1490
|
+
if (generation !== this.recognitionGeneration || this.destroyed) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1460
1493
|
const observation = this.distinctMatchDeduper.observe(best);
|
|
1461
1494
|
if (observation.kind === "match") {
|
|
1462
1495
|
this.events.emit("match", { results, best: observation.best });
|
|
@@ -1489,6 +1522,7 @@ export {
|
|
|
1489
1522
|
DEFAULT_MAX_AUDIO_MARKER_SAMPLES,
|
|
1490
1523
|
DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS,
|
|
1491
1524
|
DEFAULT_MAX_PACK_BYTES,
|
|
1525
|
+
MATCH_CONTINUITY_GAP_MS,
|
|
1492
1526
|
MatcherSession,
|
|
1493
1527
|
__resetMatcherWasmForTests,
|
|
1494
1528
|
__setMatcherEmbeddedPackForTests,
|