foley.provenance.disclosure#
Disclosure, watermarking & safety for AI-generated audio (#9b).
The sibling of foley.provenance.credits: where credits renders TASL
attribution, this module makes a generated clip **traceable, disclosed, and
safe** for a published product (report 07 §6–7):
AudioSeal watermark — an imperceptible, detectable mark embedded on every generated clip so downstream systems (and foley itself) can verify it was machine-made. Populates
LicenseRecord.watermark. Meta AudioSeal is MIT (code + weights) but a 16 kHz mono speech model used off-label on 44.1 kHz stereo SFX, so the per-clip mark is a soft-binding provenance signal (the achieved detection probability is recorded); C2PA is the hard-binding carrier.Content Credential (C2PA) — #9b writes a portable, self-asserted JSON “content credential” sidecar (a C2PA-shaped assertion dict: the “AI use” action, the training-mining opt-out, and the TASL/license) into a provenance store, and points
LicenseRecord.c2pa_manifest_refat it. A real signed + embedded C2PA manifest (viac2pa-python) over the final mix is a weave/export concern deferred to #8;build_content_credential()is the SSOT dict that step promotes verbatim.EU AI Act Art. 50 —
art50_checklist()is a pure reader over theLicenseRecordreporting which transparency obligations a clip has met vs. still pending (deadline 2 Aug 2026).Safety gates —
scan_prompt()matches a generation prompt against a registry of trademarked audio logos (THX, NBC chimes, Netflix Ta-dum, …) and recognizable-voice patterns. The generate façade decides on the result (fail-closed refuse by default, or warn-and-flag); this module only detects.
Layering: this module depends only on foley.base / foley.stores
(never on foley.sources). The safety-refusal exceptions live in
foley.sources.generate (their GenerationError taxonomy home) and are
raised there; disclosure returns a PromptScan and the caller
decides. The module top level is stdlib-only — audioseal / torch /
torchaudio are imported inside the watermark functions (the
foley[provenance] extra), so importing this module (and import foley) stays
dependency-light.
Module Attributes
The FIXED 16-bit foley provenance id embedded by AudioSeal. |
|
IPTC digitalSourceType vocabulary (C2PA |
|
Seed registry of branded audio logos foley must not knowingly generate for commercial use (report 07 §7.2). |
Functions
|
Return the per-clip EU AI Act Art. |
|
Build the portable, self-asserted C2PA-shaped content-credential dict. |
|
Detect a foley watermark in |
|
Resolve the effective |
|
Scan a generation |
|
Write |
Classes
|
The default |
|
The result of scanning a generation prompt for safety flags (pure). |
|
One trademarked audio logo + the casefolded prompt substrings that flag it. |
|
The output of a |
|
Embeds a detectable provenance watermark into audio bytes (the DI seam). |
Exceptions
Raised when a watermark is explicitly requested but |
- class foley.provenance.disclosure.AudioSealWatermarker(*, message=61470)[source]#
Bases:
objectThe default
Watermarker— Meta AudioSeal, run on CPU, deterministic.audioseal/torch/torchaudioare imported lazily inside the methods (thefoley[provenance]extra), so importing this class stays dependency-light. Watermarking runs on CPU with a fixed message andeval()/no_gradfor cross-machine reproducibility (the dedup invariant). Because AudioSeal is a 16 kHz mono model, 44.1 kHz stereo SFX is watermarked per channel via a 16 kHz round-trip (lossy) — the mark is a soft-binding signal and the achieved detection probability is recorded in the meta.- embed(audio_bytes, *, message=None)[source]#
Embed the AudioSeal watermark; return the marked bytes + meta.
- Parameters:
audio_bytes (
bytes) – The clip’s container bytes (WAV/FLAC/…).message (
Optional[int]) – The 16-bit id to embed (defaultDEFAULT_WATERMARK_MESSAGE).
- Return type:
- Returns:
A
WatermarkResultwhoseaudio_bytesis lossless WAV (float32) — a deterministic PCM round-trip so its content-hash id is reproducible — and whosemetais theLicenseRecord.watermarkdict.
- foley.provenance.disclosure.DEFAULT_WATERMARK_MESSAGE: int = 61470#
The FIXED 16-bit foley provenance id embedded by AudioSeal. It MUST be constant (no per-call nonce / timestamp): a deterministic watermark keeps the stored content-hash id reproducible so a byte-identical regeneration still dedups (
skipped_dup) — the #6 generation-flywheel promise. SeeAudioSealWatermarker.embed().
- foley.provenance.disclosure.IPTC_TRAINED_ALGORITHMIC_MEDIA = 'http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia'#
IPTC digitalSourceType vocabulary (C2PA
c2pa.actionsassertion).
- class foley.provenance.disclosure.PromptScan(trademark_hits=(), voice_hits=())[source]#
Bases:
objectThe result of scanning a generation prompt for safety flags (pure).
- foley.provenance.disclosure.TRADEMARK_REGISTRY: tuple[TrademarkEntry, ...] = (TrademarkEntry(canonical='THX Deep Note', aliases=frozenset({'deep note', 'thx'})), TrademarkEntry(canonical='NBC chimes', aliases=frozenset({'nbc chimes', 'nbc three-note', 'nbc chime'})), TrademarkEntry(canonical='Netflix Ta-dum', aliases=frozenset({'ta-dum', 'netflix intro', 'tudum', 'netflix chime', 'ta dum', 'netflix sound'})), TrademarkEntry(canonical='MGM lion roar', aliases=frozenset({'metro-goldwyn-mayer lion', 'mgm lion', 'mgm roar'})), TrademarkEntry(canonical='20th Century Fox fanfare', aliases=frozenset({'fox fanfare', '20th century fox fanfare', 'century fox intro'})), TrademarkEntry(canonical='Intel five-note bong', aliases=frozenset({'intel chime', 'intel bong', 'intel jingle', 'intel inside'})), TrademarkEntry(canonical="Homer Simpson D'oh", aliases=frozenset({'homer doh', "d'oh", 'homer simpson doh'})))#
Seed registry of branded audio logos foley must not knowingly generate for commercial use (report 07 §7.2). Each entry maps a canonical mark to a set of casefolded trigger substrings. Extend freely — this is a best-effort aid, not a legal guarantee.
- class foley.provenance.disclosure.TrademarkEntry(canonical, aliases)[source]#
Bases:
objectOne trademarked audio logo + the casefolded prompt substrings that flag it.
- class foley.provenance.disclosure.WatermarkResult(audio_bytes, meta, detection_prob=None)[source]#
Bases:
objectThe output of a
Watermarker: the marked bytes + provenance meta.
Bases:
RuntimeErrorRaised when a watermark is explicitly requested but
foley[provenance]is absent.
- class foley.provenance.disclosure.Watermarker(*args, **kwargs)[source]#
Bases:
ProtocolEmbeds a detectable provenance watermark into audio bytes (the DI seam).
The default is
AudioSealWatermarker; tests inject a deterministic fake — exactly theadapter=/pipeline=seam the source adapters use.embedMUST be deterministic for a fixedmessage(same input bytes → same output bytes) so the stored content-hash id stays reproducible and the generation-flywheel dedup keeps working.
- foley.provenance.disclosure.art50_checklist(record)[source]#
Return the per-clip EU AI Act Art. 50 transparency checklist (pure reader).
Reports, per obligation, whether it is
requiredfor this clip and whether it has beenmet— so a caller can see what a publish still needs (e.g. the machine-readable mark is pending whenfoley[provenance]was absent at generation). A pureLicenseRecordreader (flags never re-derived), mirroringfoley.provenance.credits. The render-scoped rollup over a whole mix is a weave/#8 concern.- Parameters:
record (
Union[SoundRecord,Candidate,LicenseRecord]) – ASoundRecord,Candidate, orLicenseRecord.- Returns:
{name: {required, met, detail}}, “pending”: […], “publish_ready”: bool, “provenance”: {…}}``.
- Return type:
- foley.provenance.disclosure.build_content_credential(record, *, asset_id, asset_hash=None)[source]#
Build the portable, self-asserted C2PA-shaped content-credential dict.
A pure reader over the
LicenseRecordSSOT (flags never re-derived), mirroringfoley.provenance.credits.credit_entry()’s discipline. Themanifestsub-object is byte-for-byte the dict a future signer (c2pa.Builder, #8/weave) promotes into a real signed + embedded manifest — so the credential shape can never fork between the generate-time sidecar and the export-time manifest.- Parameters:
record (
Union[SoundRecord,Candidate,LicenseRecord]) – ASoundRecord,Candidate, orLicenseRecord.asset_id (
str) – The clip’s content-hash id (the sidecar store key / ref).asset_hash (
Optional[dict]) – Optional{"alg": "sha256", "value": <hex>}of the asset bytes.
- Return type:
- Returns:
A JSON-serializable content-credential dict (
signed/embeddedFalse — it is self-asserted until #8 signs it).
- foley.provenance.disclosure.detect_watermark(audio_bytes)[source]#
Detect a foley watermark in
audio_bytes(lazy AudioSeal).Downmixes to mono + resamples to 16 kHz, then runs the AudioSeal detector.
- foley.provenance.disclosure.resolve_watermarker(watermark, watermarker)[source]#
Resolve the effective
Watermarkerfor a generate call.Progressive disclosure: generation works with or without
foley[provenance].- Parameters:
watermark (
Optional[bool]) –Truerequire a watermark (raise if unavailable),Falsenever watermark,None(auto) watermark iffaudiosealis installed.watermarker (
Optional[Watermarker]) – An injected watermarker (the DI seam) — wins over auto-detect.
- Return type:
- Returns:
A
Watermarker, orNonewhen watermarking is off/unavailable.- Raises:
WatermarkUnavailable – If
watermark=Truebutaudiosealis not installed (and nowatermarkerwas injected).
- foley.provenance.disclosure.scan_prompt(prompt)[source]#
Scan a generation
promptfor trademarked-audio-logo + voice-clone risk.Pure and stdlib-only (casefold substring + regex). Returns a
PromptScan; it never raises and makes no decision — the generate façade decides (fail-closed refuse by default, or warn-and-flag).- Parameters:
prompt (
str) – The natural-language generation prompt.- Return type:
- Returns:
A
PromptScannaming the matched marks / voice patterns.
- foley.provenance.disclosure.write_content_credential(store, asset_id, credential)[source]#
Write
credentialintostorekeyed byasset_id; returnasset_id.storeis anyMutableMapping[str, dict](default:foley.stores.make_provenance_store(), a local JSON-file store; swap for a clouddolstore to move sidecars off-box).- Return type: