Watermarker trait gains embed_with_message(audio, message) with a
default impl forwarding to embed (no-op for watermarkers without a
payload). AudioSealWatermarker overrides to use the requested message
instead of self.message; ResampledWatermarker forwards through the
resample dance.
TtsRequest gains optional watermark_message: Option<String> (decimal or
0xHEX). Useful for clawsample to tag each generation with a unique ID
(e.g. job_id mod 0x10000) for audit trails. When omitted, falls back
to the server-startup --audioseal-message default.
Verified end-to-end: override "0xBEEF" -> detect 0xBEEF (mean_presence
0.9995, 16/16 bits). Default fallback also decodes correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
A single \`generate\` invocation now produces a watermarked WAV when
AudioSeal weights are passed via CLI. End-to-end verified on real CSM
speech: mean_presence=1.0000, 16/16 message bits decoded.
- Generator gains \`watermarker: Option<Box<dyn Watermarker>>\` slot;
\`generate_to_wav\` runs \`wm.embed(&pcm)\` after post-process, before
WAV write. Field is Send+Sync so the existing Arc<Mutex<Generator>>
tts_server pattern still works.
- watermark.rs ships ResampledWatermarker<W> adapter for handling rate
mismatches (CSM 24 kHz ↔ AudioSeal 16 kHz). Output length is normalized
to input length so it's a transparent drop-in.
- examples/generate.rs gains --watermark-generator/--watermark-detector/
--watermark-message flags. Loads AudioSeal, wraps in resampler, installs.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>