crispasr 0.8.32
crispasr: ^0.8.32 copied to clipboard
Lightweight on-device speech recognition via ggml. Supports Whisper, Qwen3-ASR, FastConformer, and more.
Changelog #
0.8.32 #
- Four new backends: Raon-OpenTTS speech synthesis, Quds Persian recognition, and MT3 / Basic Pitch on the music surface, with Standard MIDI output (#250, #387). mel-band-roformer, already present on CPU, gains a fused GPU graph (#422).
- Source separation runs on the GPU by default where one is present — HTDemucs and the new fused mel-band-roformer graph (#413, #414, #422).
- Nemotron
-l autono longer conditions on English regardless of the detected language, and reads its 121-entry language table from the model, reaching 51 languages that were previously unnameable (#425). - Subtitle output returns the original script instead of transliterated text, and Canary reports wrong-language conditioning instead of failing silently (#419).
- Live/streaming WebM decodes past the first 100 ms timeslice (#417), and a malformed sample rate can no longer amplify a small file into a multi-gigabyte allocation.
- Kokoro honours
--tts-speed(#423); FunASR, piano transcription, the Bananamind vocoder and Sidon all get bit-identical speedups (#305, #416).
0.8.31 #
- Pocket-TTS now supports German, Spanish, Italian, Portuguese, and French
through managed GGUF variants, language-aware
-m autorouting, and the C session ABI (#411). - Chatterbox Nano gains a Finnish checkpoint (#382), while direct T3 KV-cache views remove redundant per-layer copies (#410).
- Streaming partials avoid repeated audio slicing and can opt into bounded tail decoding; CosyVoice3 enables packed Conv1d by default (#404, #406).
- GPU portability improves across Windows CUDA 13, old-CPU CUDA packages, Vulkan Chatterbox, Qwen3-TTS HIP fallbacks, and HTDemucs separation (#337, #398, #400, #402, #405).
- Silero language identification, server-side transcription progress, safer TTS padding, and Go speaker-turn access round out the release (#395, #408, #409).
0.8.30 #
- Audio input: files whose sample rate differs from the backend's are no
longer resampled by linear interpolation. A 10 kHz tone decoded to 16 kHz,
where it must vanish, previously survived at -10.3 dB and folded down into
the speech band; every 44.1/48 kHz recording carried that alias. Decoding now
happens at the file's own rate and resamples with the Kaiser-windowed
polyphase sinc (-89 dB on the same test).
CRISPASR_HQ_RESAMPLE=0restores the old path. - GPU binaries: the v0.8.29 GPU artifacts were built with
-march=nativeagainst a runner that has AVX-512, so the official Windows CUDA build died with SIGILL on any CPU without it (#374). Nine of eleven GPU jobs were affected. Replace any v0.8.29 GPU build. - A build/host CPU ISA mismatch now fails fast with a message naming the right download, instead of an illegal-instruction fault the Windows console swallows (#380).
- Windows: cached GGUFs larger than 2 GiB were reported missing and
re-downloaded on every run, because MSVC's
statuses a 32-bit size field and fails outright above 2 GiB (#393). - Punctuation restoration returned an empty string — not degraded output,
nothing — for every SentencePiece
fireredpuncmodel (fullstop-punc,punctuate-all). - VibeVoice-ASR answered in the wrong language: the input was never normalised
to -25 dBFS before the VAE encoders, and the 1.5B model was sent the 7B's
prompt format (#369). The model's own
[Silence]marker no longer leaks into transcripts and SRT files. - Canary long-form seam artifacts are gone — the real dynamic chunking from canary-1b-v2 replaces the parakeet-shaped machinery (#375), with opt-in seam dedup at chunk boundaries (#365).
- New backend: Confucius4-TTS (#377), with native voice cloning via
--voice. /v1/realtimeNemotron sessions are genuinely incremental — the stream now owns frontend, encoder and predictor state across appends instead of recomputing the whole buffer every 500 ms (#383).- New endpoint:
POST /v1/audio/separation(#381). - Bare voice names resolve against
--voice-dirin five TTS adapters, over HTTP as well as the CLI (#384). - Long-form progress is reported on the chunk-encoded, JA-sliced and unified dispatch routes, not only the common one (#385).
- Diarization forwards FoxNose speaker turns across the C ABI (#395), and the minimum speaker count is clamped to the distinct pyannote tracks (#368).
- f5-tts counts UTF-8 characters, not bytes, in its duration estimate (#372);
kyutai
stt-1b-en_frno longer claims to be English-only (#366); omnivoice matches upstream's target-token arithmetic exactly (#363). --align-onlyaccepts JSON input, for a JSON-to-JSON pipeline (#317).
0.8.29 #
- Dart binding:
inputSampleRateandoutputSampleRateare now bound (#321). They had been added to the five bindings that lackedspeechToSpeech, which skipped the three that already had it — so Dart kept s2s while never gaining the getter that says what rate to feed it. Both return 0 on a dylib that predates the symbol, matchingseparateSampleRate. - Chat: cancellation and prompt-token counting are bound, plus four defects fixed in the Dart chat surface (#361, #362). A session now waits for its in-flight calls before freeing, so tearing one down mid-generation is safe.
--diarize-method pyannotenever worked out of the box: the managed download for its segmentation model was tagged with the licence string"other", which the registry treats as restricted, so it refused to fetch and produced no speaker turns. It is MIT and ungated.- Diarization now returns speaker labels through
verbose_json, which previously dropped them entirely, and embeds segments across workers (1.6–2.0x on the embed stage). - Parakeet long-form dropped whole spans of 30–300 s audio (#350) and could emit segments out of time order (#356); both fixed, and long-form throughput is 2.1x (#353).
- Every GGUF load leaked its weight mapping —
MAP_PRIVATEwith write permission, so merely reading the weights privatized the pages. - CosyVoice3 cloning works through the session API when the reference clip has been prepared once through the CLI (#334).
- Windows CUDA packages now ship in split form, without the three NVIDIA runtime DLLs (#342) — 296 MB instead of 873 MB for the dev-lib package.
- There is a Dart binding CI job, so this package is compiled on every push.
0.8.28 #
- HIP/ROCm on Linux: first release since 0.8.25 with a
crispasr-linux-x86_64-hiptarball. The packaging step rewroteRUNPATHbefore askinglddwhat the binaries needed, so ROCm's OpenMP runtime (libomp.so, reachable only through thatRUNPATH) was silently dropped and the archive never built (#339). - The same defect in two more artifact kinds, neither reported (#341):
libcrispasr-linux-x86_64-hipshipped needing an unbundledlibomp.so, and the Python binding tarballs neededlibgomp.so.1andlibblas.so.3— soimport crispasrfailed in the loader on any host without OpenBLAS and gcc's OpenMP. Both are now bundled and gated. - GPU archives no longer copy the build machine's CUDA/ROCm install into the
tarball, and carry those toolkit directories in their own
RUNPATHinstead — so they resolve without the/etc/ld.so.conf.dpost-install step. - CLI tarballs now ship
LICENSEandTHIRD_PARTY_NOTICES.txt, which now also declare the bundled OpenMP runtimes (libgomp,libomp).
0.8.27 #
- Linux users on 0.8.26 should upgrade: that release published only one of its seven Linux binary tarballs (plain x86_64, arm64, CUDA, CUDA 13, Vulkan and HIP all failed to build). Two shell bugs in the release workflow, fixed (#339).
- Fixed a null-pointer crash in the audio loader on a malformed Ogg file: a
Vorbis comment header declares its entry count before the array is
allocated, so an attacker-sized count left a non-zero length with a null
pointer that the teardown path then indexed. Reachable from
crispasr_audio_loadon untrusted input. - qwen3-tts:
--temperaturenow reaches the talker (it had only ever reached the code predictor), plus greedy/replay/logit-dump levers for cross-backend diagnosis (#337).
0.8.26 #
- CosyVoice3 voice cloning was conditioned on the wrong speaker embedding
(cosine 0.737 against upstream's CAMPPlus ONNX): the export folds
out_nonlinear's BatchNorm into the preceding convolution and the fused bias was dropped. Now 0.999997. Baked bank voices were never affected (#334). - CosyVoice3
--ref-textis now optional — the reference is auto-transcribed and cached — and the decode has upstream's minimum-length floor, so it can no longer end at step 0 with no audio (#334). - New CosyVoice3 RL talker:
--backend cosyvoice3-tts-rl(#334). - Voxtral TTS could index its embedding table out of bounds on some inputs: a
Tekken vocabulary blob may serialize more pieces than the checkpoint
activates. Bounded, in both
voxtral-ttsandvoxtral4b(#338). - qwen3-tts could emit 300 s of audio for one sentence — the frame budget was the KV cache ceiling rather than anything derived from the input text (#337).
- madlad400 F16 and Q8_0 artifacts published; F16 verified at cosine 1.000000 on all 14 stages against the PyTorch reference (#333).
- Kokoro punctuation was being discarded, in German, French and Spanish too, and the contextual G2P rules had shipped switched off (#316).
-tl/--target-langwas silently discarded by cosyvoice3 and omnivoice (#329).- Rust and Dart diarize ABI mirrors were 24 bytes short; FoxNose exposed, plus
session_output_sample_rateand channel getters (#332).
0.8.25 #
- New ASR backend: GigaAM-v3 (Russian, CTC + RNN-T, punctuation-native
e2eheads). - New diarization method
foxnose— WeSpeaker embeddings + spectral clustering, no external binary required. - Diarization correctness: the pyannote powerset decode table had two entries
swapped (48.21% -> 33.37% DER), and
--diarize-max-speakerswas picking the speaker count instead of bounding it (15.74% -> 7.81% DER). - Diarization speed: pyannote segmentation now infers in parallel chunks (2888 s file, 8 cores: ~50 s -> 18.1 s); speaker embedding parallelised.
- Fixed sherpa diarization hanging indefinitely (#328) and the MSVC build failure in glint (#327).
- ggml synced to v0.17.0.
0.8.24 #
Session.setTtsPhonemes(String)— synthesize a phoneme string verbatim, skipping the grapheme-to-phoneme stage. Honoured bykokoroandpiper; other backends throw rather than silently synthesizing the text instead.SessionSegment.speaker— the native per-segment speaker label, in the"(Speaker N) "form, or""when the backend does not diarize natively. The ordinals are chunk-local:Speaker 1from onetranscribecall is not necessarily the same voice asSpeaker 1from the next.- Native improvements that ship with this release (no Dart API change):
- English pronunciation for Kokoro substantially fixed — numbers were silently dropped from synthesized text, and the phonemes were in the wrong alphabet for the model. Agreement with Kokoro's own G2P goes from 58% to 99.1%.
- MOSS-TTS 4B no longer generates past its stop token.
- CosyVoice3 cross-lingual synthesis — a clone reading a target language no longer carries the reference voice's accent.
- VibeVoice speaker turns and per-utterance timings are now parsed out of the model's answer instead of being returned as one raw JSON blob.
- Punctuation restoration no longer double-capitalizes or doubles a full stop.
0.8.23 #
- No Dart API change. Ships the native v0.8.23 improvements: the restored
Windows CPU binary, F5-TTS Chinese synthesis, VibeVoice voice-pack
correctness, broader NVIDIA GPU coverage, faster VAD, the opt-in
--strict-pipelinemode, and several TTS-on-Vulkan fixes.
0.8.22 #
- TTS provenance opt-out now requires a marking attestation. Synthesis is provenance-marked by default; raw (unmarked) synthesis and an explicit marking-responsibility affirmation are exposed for callers that take responsibility for labeling AI-generated audio. Marked-output flows are unchanged.
- Native improvements that ship with this release (no Dart API change):
- Tiron — a new multi-speaker meeting-ASR model (Whisper large-v3 + inline
speaker markers,
--diarizecross-window speaker linking) is reachable viaCrispasrSession.open()on the whisper path. - Source separation ~26× faster on Linux/Windows —
CrispasrSession.separate()with mel-band-roformer no longer runs the naive scalar path (BLAS + FFT iSTFT- threading), output bit-identical.
- NaN-robust ASR decode — canary-qwen and 7 other backends no longer emit garbage when a quantized weight goes non-finite.
- Tiron — a new multi-speaker meeting-ASR model (Whisper large-v3 + inline
speaker markers,
- No breaking Dart API changes beyond the provenance gate above.
0.8.21 #
--max-new-tokensis honored across all ASR backends. Ten backends (moss-diarize, canary, canary-qwen, glm-asr, funasr, mimo-asr, moss-transcribe, mini-omni2, higgs-stt, higgs) previously hardcoded their decode cap, so a long single-pass transcription truncated. Each now forwards the value while keeping its own default, so nothing regresses.- MOSS TTS synthesis params wired — moss-tts / moss-tts-local now honor max-new-tokens, duration (max-speech-tokens), top-p, top-k, and repetition-penalty instead of staying at hardcoded defaults.
- Chunk-local speaker scope — diarized segments now carry a
chunk_id;(Speaker N)labels are chunk-local, so a consumer useschunk_idto tell continuity from an ID swap. - No Dart API changes.
0.8.20 #
- iOS is now supported. The release ships an Apple
crispasr.xcframework(device + simulator slices for iOS, plus macOS/tvOS/visionOS) for the first time, so a Flutter iOS app can link the native library. Earlier releases shipped no iOS artifact at all. - Prebuilt native library bundles load as delivered. The macOS and Linux
libcrispasrbundles had a broken embedded run-path and could not bedlopened after extraction; they now resolve their bundledggmllibraries relative to their own location. Android and the desktop bundles are laid out as a flatlib/directory. - canary-qwen long audio fixed. Clips longer than ~30 s no longer skip chunking, return near-empty transcripts, or grow memory without bound — the backend had advertised an internal chunker it did not implement (#290).
- No Dart API changes; existing code upgrades untouched.
0.8.19 #
- Beat and downbeat tracking:
CrispasrSession.beats()returns a beat grid from mono 22050 Hz float32 PCM, backed by the Beat This! backend. Adds theBeatrecord typedef (timeS,isDownbeat), abeatsSampleRatecapability probe and abeatsTempoBpmmedian-interval tempo estimate. Previously beat tracking was reachable only from the CLI and the C ABI. - Every downbeat is also reported as a beat: the postprocessor snaps each
downbeat onto its nearest detected beat, so downbeats are a strict subset —
filter on
isDownbeatfor the bar grid, and never merge two lists. - Beat This! is MIT for code and weights and uses no DBN (postprocessing is peak-picking only), so unlike most beat trackers this arm carries none of madmom's patent-encumbered, non-commercially-licensed machinery.
- The native GGUF architecture auto-detect recognises
beat-this, so plainCrispasrSession.open()works for beat models;backend: 'beat-this'remains valid and is required against an older native library.
0.8.17 #
- Source separation:
CrispasrSession.separate()splits interleaved stereo PCM into named stems (drums/bass/other/vocalsfor htdemucs), with theStemrecord typedef and aseparateSampleRatecapability probe. Previously separation was reachable only from the CLI, the C ABI and Python — never from Dart. - Note the input contract:
separate()takes interleaved stereo (L,R,L,R,…) at 44100 Hz, and the native side counts samples per channel. To feed a stem topitch(), downmix to mono and resample to 16 kHz first — the dartdoc has the recipe. - Piano transcription:
CrispasrSession.pianoNotes()returns structured note events —PianoNote = ({int midi, double onMs, double offMs, int velocity})— plus apianoSampleRateprobe. Previously the only route out of the piano backend wastranscribe(), whose segment text reads like"C4 v=80"; parsing that back was lossy. - Native fixes that ship with this release: htdemucs now segments long audio (7.8 s windows, 25% overlap, weighted overlap-add), so separating a full song no longer needs tens of GB — peak memory is flat instead of growing with length. And the htdemucs loader no longer silently zero-fills quantized tensors it cannot read, which had made the q4_k model produce garbage; unreadable weights are now fatal.
0.8.16 #
- Pitch (F0) estimation:
CrispasrSession.pitch()returns a monophonic pitch track from mono 16 kHz float32 PCM, backed by the CREPE backend. Adds thePitchFramerecord typedef (timeMs,f0Hz,voicedProb) and thepitchSampleRatecapability probe. - The native GGUF architecture auto-detect now recognises
crepe(andhtdemucs), so plainCrispasrSession.open()works for those models; previously only the CLI auto-detected them and every binding had to name the backend. Passingbackend: 'crepe'explicitly remains valid and is required against an older native library.
0.8.11 #
- Session introspection accessors: CTC vocab, Whisper
no_speech_prob, anddetected_language, mirrored across the language bindings. - Docs/quality: add an example, enable
lints/core, and apply the resulting fixes (flow-control braces + FFI typedef casing).
0.8.10 #
- Initial pub.dev release for the current CrispASR 0.8 line.
- Exposes Dart FFI bindings for the unified CrispASR session API, Whisper-shaped legacy API, audio decode helpers, language detection, diarization, chat/text helpers, grammar support and alt-token capture.
- Keeps the package pure Dart FFI: native
libcrispasrmust be installed or bundled separately.
0.5.13 #
- Whisper alt-token capture (
--alt Nparity) — per-token top-N alternative-candidate capture for whisper greedy decode. Closes the last open whisper-cli equivalent gap. NewWord.altslist (with a newAltTokenvalue class, defaultconst []so existing call-sites stay source-compatible) on both the low-levelSegmentshape (fromtranscribePcm) and the unified sessionSessionSegmentshape (fromCrispasrSession.transcribe). NewTranscribeOptions.altN(default 0 = off) plumbs through the low-level path; stickyCrispasrSession.setAltN(int)(matches thesetFallbackThresholds/setWhisperDecodeExtraspattern) threads through the session path. Pre-0.5.13 dylibs raiseUnsupportedErrorfor the session setter and reportWord.alts == []from both readers, so apps stay loadable and just hide the affordance. - Capture happens inside
whisper_sample_tokenso the alts are softmax probabilities at the same decode step as the chosen token. Beam search is excluded — siblings are beam-conditional rather than greedy alternatives. Chosen token is not included in the alt list; entries are sorted descending by probability. - Whisper session-transcribe path now also populates
SessionSegment.words(it previously returned only segment-level text) — closes a long-standing gap with the parakeet / canary backends as a side-benefit of needing per-token data for the word-alt mapping. Whisper sub-word BPE means a multi-token word's alts cover the first content token only (e.g. "kubectl" surfaces alts for "kub"); full word-level enumeration is deferred. - C-ABI symbols:
crispasr_params_set_alt_n,crispasr_session_set_alt_n,crispasr_token_n_alts/_alt_id/_alt_p/_alt_text,crispasr_session_result_word_n_alts/_alt_text/_alt_p. New whisper getterswhisper_full_get_token_n_alts/_alt_id/_alt_p(plus_from_statevariants). All nine symbols pinned inbindings_smoke_test.dart. - Live test
test/alt_tokens_live_test.dart(taggedlive) exercises the full stack againstggml-tiny.en.bin+samples/jfk.wav. Asserts ≥1 returned word has alts, every alt's p ∈ [0, 1] and the list is descending, chosen token excluded from its own alts, andsetAltN(0)on a re-decode actually clears them. Skips silently whenCRISPASR_LIB/CRISPASR_MODELaren't set. Representative dev-box result: 22/22 words on JFK get runner-ups (e.g. "Americans → America(4.85%), americ(3.84%), American(3.35%)").
0.5.12 #
- Audio enhancement (RNNoise pre-step) — new top-level
enhanceAudioRnnoise(Float32List pcm)runs xiph/rnnoise v0.1 on a 16 kHz mono float32 buffer (upsample to 48 kHz → RNNoise frame loop → downsample back) and returns a fresh same-lengthFloat32List. Backed by the new C-ABIcrispasr_enhance_audio_rnnoise; pre-0.5.12 dylibs raiseUnsupportedErrorso callers graceful-degrade. State is per-call so worker isolates can run enhancement concurrently without coordination.
0.5.11 #
- Whisper text-suppression + prompt-carry extras — three
more whisper-only
wparamsknobs the CLI surfaces (--suppress-nst,--suppress-regex,--carry-initial-prompt) now have Dart bindings. New sticky setterCrispasrSession.setWhisperDecodeExtras(suppressNonSpeechTokens:, suppressRegex:, carryInitialPrompt:). Empty regex clears any prior pattern (passesnullptrto wparams = whisper's "no suppression" sentinel). Pre-0.5.11 dylibs raiseUnsupportedErrorso callers can graceful-degrade. - Underlying C-ABI:
crispasr_session_set_whisper_decode_extras; three new sticky session fields default to whisper's upstream values so an unmodified session matches stock whisper.cpp.
0.5.10 #
- Whisper decoder-fallback thresholds —
wparamsknobs that decide when the decoder falls back to a higher temperature pass (hard audio, low logprob) or treats a segment as silence are now exposed via the session API:CrispasrSession.setFallbackThresholds(entropyThold:, logprobThold:, noSpeechThold:, temperatureInc:). Mirrors the CLI's--entropy-thold/--logprob-thold/--no-speech-thold/--temperature-inc/--no-fallbackflags.temperatureIncis clamped to[0, 1]; setting0.0disables the temperature-fallback loop entirely (= the CLI's--no-fallback). Defaults matchwhisper_full_default_paramsso an unmodified session behaves bit-identical to stock whisper.cpp. - Underlying C-ABI:
crispasr_session_set_fallback_thresholds. Pre-0.5.10 dylibs raiseUnsupportedError. Non-whisper backends silently ignore — none have an analog for these fields today.
0.4.9 #
- Initial pub.dev release.
- Dart FFI bindings for the CrispASR C ABI (
src/crispasr_c_api.cpp). - Supports all 17 backends: Whisper, Qwen3-ASR, FastConformer, Canary, Parakeet, Cohere, Granite-Speech, Voxtral (Mistral 1.0/4B), wav2vec2, GLM-ASR, Kyutai-STT, Moonshine, FireRed, OmniASR, VibeVoice-ASR, plus FireRedPunc post-processor.
- Unified
SessionAPI across all backends; legacyCrispASRWhisper-shaped API preserved. - Word-level alignment, speaker diarization, and language ID helpers.
- Auto-download of registered models via the model registry.