first commit
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# `fingerprint-matcher` — Time-offset histogram match scoring
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`FingerprintMatcher` scores a query fingerprint against a corpus of candidate
|
||||
fingerprints using the classic Shazam time-offset histogramming technique. For
|
||||
every hash that appears in both query and candidate, the matcher accumulates
|
||||
`(candidateOffset − queryOffset)` into a 10 ms-wide histogram bucket; the
|
||||
heaviest bucket count is the match score and its midpoint is the alignment
|
||||
offset. Results sort by descending score, ready for ranked display.
|
||||
|
||||
## Features
|
||||
|
||||
- `match(query, corpus)` returns ranked `MatchResult[]`.
|
||||
- `matchOne(query, candidate)` returns the best bin only — useful for unit
|
||||
tests and direct pairwise comparison.
|
||||
- Configurable `binWidthSec` (default 0.01 s).
|
||||
- Hash-index pre-step keeps the cost at `O(|query| + |candidate|)` per pair.
|
||||
- Wraps iteration-time corpus failures in
|
||||
`REACT_AUDIO_FINGERPRINT.MATCH_FAILED`.
|
||||
- Empty inputs return zero-scored results without throwing.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { FingerprintMatcher } from "@suaveplan/react-audio-fingerprint/fingerprint/fingerprint-matcher";
|
||||
|
||||
const ranked = new FingerprintMatcher().match(queryFp, corpus);
|
||||
console.log(ranked[0]?.id, ranked[0]?.score);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Drive a tighter alignment window for short clips:
|
||||
|
||||
```ts
|
||||
import { FingerprintMatcher } from "@suaveplan/react-audio-fingerprint/fingerprint/fingerprint-matcher";
|
||||
|
||||
const m = new FingerprintMatcher();
|
||||
const one = m.matchOne(queryFp, candidateFp, { binWidthSec: 0.005 });
|
||||
console.log(one.score, one.offsetSec, one.matchedHashCount);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class FingerprintMatcher`.
|
||||
- `match(query, corpus, opts?): MatchResult[]`.
|
||||
- `matchOne(query, candidate, opts?): MatchOneResult`.
|
||||
- `MatchResult = { id, score, offsetSec, matchedHashCount }`.
|
||||
- `CorpusLike = { values(): Iterable<{ id; fingerprint }> }`.
|
||||
- `MatcherOptions = { binWidthSec?: number }` (default `0.01`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
A candidate's hash table is built lazily on each call — caching is left to
|
||||
callers because corpora are typically streamed. Hash indices use `Map<number,
|
||||
number[]>` since duplicate hashes inside one fingerprint are common (the same
|
||||
constellation pair can recur at different times). The histogram is also a
|
||||
`Map<number, number>` because the bucket range is unbounded in principle.
|
||||
Bucket midpoints (rather than left edges) keep the reported offset symmetric
|
||||
around the true alignment. The matcher is pure — given the same inputs it
|
||||
produces the same outputs — so callers can safely memoise results keyed by
|
||||
`(queryFp, corpusVersion)`.
|
||||
Reference in New Issue
Block a user