Recursive AI

Speaker identity and scores

All onespeech docs

SpeakerEmbedder is the synchronous CPU extraction role. Its model metadata identifies the checkpoint and licence; Dimension fixes output length and SampleRate fixes mono input Hz. Embed writes into caller-owned storage and accepts cancellation. Input must be nonempty, mono, and already resampled. Implementations return finite, nonzero vectors and reject unsupported input rather than silently changing channels. Callers dispose model resources after consumers stop. CampPlusEmbedder loads a locally converted CAM++ graph; see below.

Arithmetic

EmbeddingMath.Cosine accumulates in double precision and clamps rounding error to [-1,1]. Both vectors must be finite, nonempty, equal-dimensional and nonzero. Normalize supports in-place normalization. Average first unit-normalizes each vector, computes an equal-weight mean, and unit-normalizes that mean. Opposite vectors whose mean is zero are an error, not an invented speaker identity. The functions neither retain nor persist their inputs.

AdaptiveScoreNormalizer is optional symmetric adaptive cohort normalization (AS-norm). It copies a cohort of equal-dimensional vectors. For each side of a trial, take its highest topCount cosine scores against the cohort (default 100, capped at cohort size, minimum two). Compute their mean and population standard deviation. The returned score is 0.5 * ((cosine - mean1) / std1 + (cosine - mean2) / std2). Standard deviations are floored at 1e-6 by default. Scores are unbounded, not probabilities. Use a disjoint, consented development cohort; evaluation identities must not enter the cohort. The library does not select, fetch or persist one.

LogisticScoreCalibration fits an affine sigmoid to labeled development scores, not speech-model weights. Labels indicate same-speaker trials. The objective is mean binary cross-entropy plus half the specified L2 penalty times the sum of squared coefficients. Both coefficients are penalized; the default penalty is 1e-4. Scores are standardized with population standard deviation floored at 1e-6. Zero-initialized, fixed-step gradient descent runs 100 iterations by default, with step 1 / (0.5 + penalty); there is no randomness. Both classes must occur. Output probabilities reflect the development set's empirical class prior, not a deployment population prior. Changing trial balance changes their meaning. Choose a deployment threshold on held-out data, never on the reported evaluation trials.

In-memory registry

InMemorySpeakerRegistry implements the existing SpeakerRegistry contract. It borrows a SpeakerEmbedder, serializes its own extraction calls, and stores only unit-normalized averages keyed by case-sensitive IDs. The caller must coordinate any embedder use outside this registry. Enrollment replaces an ID atomically after all utterances succeed; the overload accepting multiple utterances uses the arithmetic above. A failed or cancelled extraction leaves the previous enrollment intact. Very short or silent audio remains the embedder's responsibility.

The constructor requires an explicit, inclusive acceptance threshold; no universal threshold is claimed. Identification returns the highest score, breaking exact ties by ordinal ID. Below the threshold it returns null ID with the best score. An empty registry returns null ID and negative infinity. Verification scores a pair without modifying enrollment. Raw scoring is cosine unless an optional AS-norm cohort is supplied. Calibration is separate and is never silently applied. Remove clears an enrollment and returns whether it existed. Dispose clears all enrollments, but does not dispose the borrowed embedder. Clearing managed arrays is best effort: it is not a memory-erasure guarantee against runtime copies, crash dumps or a hostile host.

There is no embedding serialization, file storage or network enrollment. Hosts can share one registry instance in their process; the separate Speech helpers manage a process-wide registry per supported CAM++ model, with calibrated identification thresholds described below.

Privacy: speaker embeddings and enrollment audio are biometric data. Obtain appropriate consent and determine applicable local-law requirements, including Illinois BIPA and GDPR Article 9 where relevant. Avoid logging embeddings, restrict access to process memory, and remove enrollments when no longer needed. The library never persists embeddings or audio. Arithmetic tests use algebraic vectors. Speaker integration tests use public LibriSpeech speech only; embeddings are never committed.

Checkpoint inspection

The development converter can fetch and SHA-256 verify four publisher checkpoints:

Terminal
dotnet run --project tools/OneSpeech.Convert -- speaker-fetch /caller/selected/model-cache
dotnet run --project tools/OneSpeech.Convert -- speaker-inspect /caller/selected/checkpoint

The fetch command verifies existing files too; a mismatch fails rather than overwriting an existing checkpoint. Downloads are bounded to 128 MiB, verified before rename, and temporary files are removed on failure. Do not run concurrent fetches against the same directory. Inspection prints only tensor names and dimensions, not a runtime graph or inferred architecture. These commands do not register a model or select a default embedder.

Inspection supports a deliberately restricted tensor-only ZIP/pickle data language. It never executes Python or invokes globals. Only inert ordered-map, storage and tensor-view constructors are accepted; all other global names, reductions and object state are refused. Float32, float64, float16 and int64 storage can be converted to float32 arrays, including bounded noncontiguous views. Optional ordered-map metadata is parsed under the same bounds and discarded. No archive entry is extracted to disk. All index text is strict UTF-8.

Limits: 256 MiB archive, expanded entries, and output arrays (each budget independently); 8 MiB central directory and pickle index; 20,000 ZIP entries; rank at most 8; dimensions at most 10,000,000; no negative strides or views outside storage; two million parser operations and aggregate collection slots; 200,000 stack and memo entries; 65,536-byte strings. Multi-disk and ZIP64-only directory records are not supported. Invalid data throws InvalidDataException; ordinary stream I/O failures remain I/O errors. Inspection is a developer tool, not the shipped model-loading API. No embedding model implementation or parity claim is provided by checkpoint inspection.

CAM++ inference

OneSpeech.Embedders.CampPlusEmbedder implements SpeakerEmbedder over a locally converted OneModel graph. Construct it with an explicit graph path, truthful checkpoint ModelInfo, and embedding dimension (512 for the English VoxCeleb checkpoint; 192 for the Chinese common checkpoint). The default is one CPU thread. It never downloads, trains, saves embeddings, or selects a default model. Input is mono 16 kHz float PCM, 45 ms through 60 s; destination storage belongs to the caller. Inference calls and disposal are serialized per instance. Cancellation is checked during feature extraction and before graph inference; OneModel's synchronous graph call itself is not interruptible.

SpeakerFbank computes 80 natural-log mel energies on complete 400-sample windows at 160-sample hops. It uses unscaled float PCM, removes each window's DC component, applies 0.97 preemphasis (with the first sample repeated), a symmetric Povey window, and a zero-padded 512-point power FFT. Mel triangles span 20 Hz through 8 kHz on the mel scale. Energies are floored at float machine epsilon before taking natural logs. Each feature dimension has its utterance mean removed. FFT twiddles are cached at construction-independent fixed sizes. Mel accumulation visits only each triangle's nonzero support, in the original bin order; there is no SIMD reduction or change of rounding order. The optimized path is bit-identical to the dense arithmetic on five fixed LibriSpeech probes and the synthetic test inputs. All power bins, including zero-weight bins, are checked for overflow. Output is planar [mel, frame], not interleaved frames. These settings were observed by a pass-through capture at the documented public fbank API invoked by ModelScope 1.40.2. The wrapper returned all values unchanged; no implementation source was inspected. The extractor is deterministic, requires finite samples, and accepts 25 ms through 60 s. A partial final window is not padded.

The converter implements Wang et al. (2023), Sections 2.1–2.4: frequency-downsampling residual FCM, densely connected TDNN blocks of 12/24/16 layers with growth 32 and dilations 1/2/2, and context-aware masks combining global and consecutive 100-frame segment means. An incomplete last segment is divided by its actual frame count. All nonlinear normalization stages use BN then ReLU. Statistics pooling concatenates the mean and unbiased standard deviation before the final projection and non-affine batch normalization. Architecture details left unstated by the paper were settled by black-box numerical probes with identical input features. All neural operators run in OneModel; there is no native inference dependency.

Terminal
dotnet run --project tools/OneSpeech.Convert -- speaker-convert-camp checkpoint.bin camp.onemodel

The checkpoint must already have been verified against its publisher pin. Conversion does not execute pickle objects or load Python. The graph and weights stay outside the repository. No corpus-quality or default-selection claim follows from successful conversion alone.

Verification slice

tools/scripts/generate-speaker-trials.sh TEST_CLEAN TEST_OTHER trials.tsv 1729 constructs ID-only trials from separately fetched LibriSpeech test-clean/test-other data. Sort IDs ordinally, group by speaker, and retain the first 20 utterances per speaker. A seeded .NET 10 PRNG selects speakers uniformly and utterances uniformly within speakers; reject identical utterances and duplicate unordered pairs. Same-speaker and different-speaker classes contain 10,000 unique pairs each. Trials are finally sorted by their two IDs. No audio or embeddings are included in the trial file. This is an open-data verification slice, not VoxCeleb1-O and not an estimate of a deployment prior.

speaker-score trials.tsv OUR_EMBEDDING_DIR REFERENCE_EMBEDDING_DIR result.json applies the same cosine scorer to both embeddings, grouping equal scores at ROC thresholds. EER linearly interpolates at the false-accept/miss crossing. Minimum DCF uses target prior 0.01, miss and false-alarm costs 1, and normalization by the always-reject cost (0.01); it includes the always-reject operating point. The paired bootstrap uses 1,000 resamples, seed 1729, independently sampling trials with replacement within each same/different class while keeping our/reference scores paired. Percentile endpoints use linear quantiles at 0.025 and 0.975. Confidence intervals describe the score difference conditional on the fixed utterance/speaker set, not uncertainty over independent unseen speakers; trials sharing speakers are not independent samples of a speaker population. No normalization or score calibration is applied to these evaluation rows.

The English VoxCeleb checkpoint at revision 032b8131a7ad812f87061955ca974c99060c5a03 was checked against ModelScope 1.40.2 (torch/torchaudio 2.11.0 CPU, float32) on the 50 IDs in eval/manifests/librispeech-speaker-parity50.txt. Feature maximum absolute error was 0.000597 and mean absolute error 0.00000624; the documented float32 feature tolerance is max 0.001 on these probes. Embedding minimum/mean cosine were 0.999999999957/0.999999999985, above the 0.999 target. This is numeric parity on a fixed probe set, not a speaker-verification quality claim. The converted English graph is 29,414,032 bytes, SHA-256 f333a00d5d66b9bf3d7e7abf35f36ff4a01e6e8a0df235b89a125e9fbe3cc15c.

On the committed 20,000-trial LibriSpeech verification slice, both this graph and the pinned ModelScope reference scored 0.75% EER and 0.0379 normalized minDCF. The paired bootstrap 95% intervals for our-minus-reference differences were [0,0] percentage points and [0,0], respectively: the relevant ROC operating points were identical. These are conditional trial-resampling intervals, not confidence intervals over a new population of speakers. The same feature extractor was also checked on deterministic silence, tone, impulse, DC-offset, clipped, seeded-noise, exact-window and two-window probes; maximum absolute error across them was 0.000851 (within the 0.001 tolerance).

These CPU/float32 checks used Linux x64, an AMD Ryzen 9 7950X3D (32 logical CPUs, AVX2 and AVX-512 available), eight inference threads, .NET SDK 10.0.403/runtime 10.0.14, OneModel 1.0.0 and OneAudio 1.7.2. No GPU was used. Oracle dependencies and wheel hashes are pinned in the speaker oracle's uv.lock; model and identifier pins are given above. Conversion is deterministic: its recorded graph SHA-256 was reproduced from the same checkpoint independently.

ECAPA-TDNN

EcapaTdnnEmbedder accepts the converted VoxCeleb checkpoint and truthful ModelInfo, returning 192 raw embedding values at mono 16 kHz. Calls are serialized; inputs must be 40 ms through 60 s. The minimum ensures reflected temporal convolution pads are smaller than the frame dimension. The graph follows Desplanques et al. (2020), Sections 3.1–3.3 and the pinned checkpoint config: 1024-channel TDNN, three scale-8 SE-Res2Blocks (dilations 2/3/4), multi-layer aggregation, channel-dependent attentive statistics pooling and the final 192-dimensional projection. Convolution uses reflected padding; TDNN activation is Conv → ReLU → BN. The first Res2 split bypasses its convolution; subsequent splits add the preceding processed split. Global and weighted pooling standard deviations use population statistics with a 1e-12 variance floor. These details were confirmed against numeric black-box outputs, not implementation source.

EcapaFbank is a distinct public feature profile, not the CAM++ filterbank: centered zero-padded 400-sample periodic Hamming windows, 160-sample hops, 400-point power FFT, no preemphasis/DC removal or amplitude scaling. There are 80 symmetric Hz-domain triangles with centers evenly spaced in HTK mel from 0 through 8 kHz; each triangle's half-width is its center's distance from the preceding center. Energies are floored at 1e-10, converted by 10 log10, clipped at the utterance maximum minus 80 dB, and mean-normalized by feature dimension. ComputeRaw exposes the decibel energies before mean subtraction. Numeric public feature probes settle this profile; the model paper's MFCC input description does not replace its released checkpoint's Fbank configuration.

The 50 committed LibriSpeech probe IDs, using SpeechBrain 1.1.2 with torch/torchaudio 2.11.0 CPU, produce max/mean normalized-feature absolute errors 0.001542/0.00002079 dB, and minimum/mean embedding cosine 0.999999999983/0.999999999990. Feature tolerance is max 0.002 dB (about 0.000461 natural-log energy units); embedding target is cosine >=0.999. The mixed-radix 400-point transform uses managed 16-point FFTs and 25-way combination, not a native frontend. Feature-only probes also cover silence, tone, DC-offset, impulse, seeded noise and sub-window audio. The feature extractor accepts 1 sample through 60 s; the model's stricter 40 ms limit still applies.

Terminal
dotnet run --project tools/OneSpeech.Convert -- speaker-convert-ecapa embedding_model.ckpt ecapa.onemodel

The converted graph is 83,294,800 bytes, SHA-256 f107ead463783cab70914fd0470cc6b42e2ec74b7a717bb42c1360879076c669. As with CAM++, its catalog release URI is reserved/unpublished; the model store converts verified publisher files locally. ECAPA is available by name; CAM++ English is the speaker default.

On the same 20,000-trial LibriSpeech slice, ECAPA and its pinned reference both score 1.10% EER and 0.0548 normalized minDCF, using the same raw cosine scorer, prior/costs and conditional paired-trial bootstrap described above. Our-minus-reference intervals are [0,0] for both metrics. The ECAPA oracle uses the publisher's exact /resolve/<commit> URL as its source and pretrained path, because the released from_hparams interface does not accept a revision constructor keyword. Auxiliary classifier/global-normalization files loaded by the oracle are recorded in notices but are not part of the converted graph; embedding normalization is explicitly disabled on the reference call.

CAM++ Chinese common checkpoint

The same converter/frontend and CampPlusEmbedder (dimension 192) support iic/speech_campplus_sv_zh-cn_16k-common revision 013c18884dffb82846cf472dd06dd4a08d97e2cd. On the same 50 IDs, feature max/mean errors are 0.000597/0.00000624 and embedding minimum/mean cosines are 0.999999999966/0.999999999984. This meets the numeric parity target. The converted graph is 28,098,192 bytes, SHA-256 4b2f0fef5ec8cb855b53c6b5abae5dd4cd72c215137c52d32f5c477e4a792f49, registered under the same unpublished-artifact convention. No default selection is implied.

Information only: this checkpoint is trained on Chinese speech, while these trials contain English read speech. Both our graph and the reference score 2.87% EER and 0.1876 normalized minDCF on the shared 20,000-trial English slice, with paired difference intervals [0,0]. The same prior, costs and conditional trial bootstrap apply. This is an implementation comparison and not a language-appropriate accuracy bar or a new-speaker population confidence interval.

Convenience identification operating points

The file helpers use raw cosine without AS-norm or a fitted affine map. Threshold fitting uses eval/manifests/librispeech-speaker-trials.tsv (SHA-256 8a7e63167cd625bd0633a29bd5f517ceb37e9a32fccec062328a52fd44aa43ae, seed 1729). Sort its 73 distinct speaker IDs ordinally; the first floor(73/3) = 24 identities are development, the remaining 49 are holdout. Retain a trial only when both identities belong to the same partition; discard cross-partition trials. This is a partition of the existing LibriSpeech test-clean/test-other trial manifest, not the official LibriSpeech dev split and not a new independent benchmark. Neither holdout scores nor query examples select the threshold.

Development IDs: 1089, 1188, 121, 1221, 1284, 1320, 1580, 1688, 1995, 1998, 2033, 2094, 2300, 237, 2414, 260, 2609, 2830, 2961, 3005, 3080, 3331, 3528, 3538. There are 3,296 positive and 1,036 negative development trials. Choose the observed score minimizing normalized DCF = miss rate + 99 × false-accept rate (target prior .01, unit miss/false-accept costs). Acceptance is inclusive; cost ties prefer the larger threshold. Also report interpolated EER and the observed threshold nearest equal false-reject/false-accept rates (stricter on ties). OneSpeech.Convert speaker-calibrate <manifest> <embedding-directory> reproduces the selection from the existing parity-checked embeddings without changing any model weight.

ModelDev EER %EER operating pointDev minDCFIdentification thresholdHoldout miss %Holdout false accept %Holdout DCF
CAM++ English0.60680.415541840190809860.016380.49299862573735653.19210.022270.05397
CAM++ Chinese2.70020.58906922532488510.162920.722907543803802515.51310.066820.22128

Holdout contains 11,194 retained trials. These are conditional trial rates, not population confidence intervals or an authentication guarantee. Chinese CAM++ is measured on English read speech here; this does not establish a Chinese-speaking population operating point. Open-set identification with many enrolled names and cluster-average embeddings has different error rates from pair verification. Deployments must validate their own population, recording conditions, consent and risk policy using the low-level registry's explicit threshold. The convenience operating point is a conservative read-speech default, not a liveness check or anti-spoofing system.