foley.audio
Audio I/O and DSP primitives for foley.
The foundation every other foley layer decodes/encodes/transforms audio through. It commits to a small, explicit set of representations (report 09):
Working (in RAM, between DSP ops): a
float32NumPy array at48 kHz— shape(frames,)mono or(frames, channels). This is the lingua franca ofsoundfile/librosa/soxrand exactly what CLAP expects.Archive (bytes at rest): FLAC, source sample-rate and bit-depth preserved — lossless, ~40-60% smaller than WAV, self-describing.
Delivery / preview (derived): Opus by default (not implemented in the foundation — an FFmpeg/foley[ffmpeg] concern surfaced later).
Design rules honoured here:
Zero-dep import. This module imports only the stdlib at top level; the heavy libraries (
numpy,soundfile,soxr,librosa,pyloudnorm) are lazy-imported inside the functions that need them, soimport foley.audioalways succeeds on a bare install and only the function you call pays for (and requires) its dependency.No magic numbers. Every default (sample rate, dtype, archive subtype, resample quality, trim/fade/loudness targets) is a named module-level constant used as a keyword-only argument default.
Never bundle FFmpeg / pydub / torchaudio. MP3/AAC/Opus transcode is an optional external-tool concern, not part of this royalty-free core.
All ops assume the working representation (float array, time on axis 0). load
is the one function that produces it from arbitrary sources (path, raw bytes, or
a file-like object).
- foley.audio.AudioSource
a filesystem path, raw encoded bytes, or an already-open binary file-like object (e.g.
io.BytesIO).- Type:
A source
loadcan decode
alias of
str|PathLike|bytes|BinaryIO
- foley.audio.encode(samples: ndarray, sample_rate: int, *, fmt: str = 'flac', subtype: str = 'PCM_24') bytes[source]
Encode
samplesfully in memory and return the container bytes.This is the producer for the content-addressed byte store: default output is the FLAC archive form.
- Parameters:
samples – The working array to encode.
sample_rate – Sample rate in Hz.
fmt – Container/codec name (case-insensitive).
subtype – Sample subtype (e.g.
PCM_24).
- Returns:
The encoded audio as
bytes.
Lazy dependency:
soundfile.
- foley.audio.ensure_channels(samples: ndarray, *, channels: int) ndarray[source]
Coerce
samplesto exactlychannelschannels.Mappings: mono -> N by duplication; N -> mono by mean; N -> M (N != M, both > 1) by collapsing to mono then tiling up to M.
- Parameters:
samples – Mono or multichannel working array.
channels – Target channel count (must be >= 1).
- Returns:
A
(frames,)array whenchannels == 1, else a(frames, channels)array.- Raises:
ValueError – If
channels < 1.
Lazy dependency:
numpy(only for the up-mix / tile path).
- foley.audio.fade(samples: ndarray, sample_rate: int, *, fade_in_s: float = 0.01, fade_out_s: float = 0.01, kind: str = 'linear') ndarray[source]
Apply in/out gain ramps to
samples(a short declick by default).Ramp lengths are clamped to at most
len(samples) // 2so the in- and out-ramps never overlap on tiny inputs.- Parameters:
samples – Working array (mono or multichannel).
sample_rate – Sample rate in Hz (converts the fade durations to samples).
fade_in_s – Fade-in duration in seconds.
fade_out_s – Fade-out duration in seconds.
kind –
'linear'or'equal_power'ramp shape.
- Returns:
A new array with the fade envelope applied (input is not mutated).
Lazy dependency:
numpy.
- foley.audio.load(src: AudioSource, *, target_sr: int | None = None, mono: bool = False, dtype: str = 'float32') tuple['ndarray', int][source]
Decode audio into a float working array.
srcmay be a filesystem path, raw encodedbytes(wrapped in aBytesIOso nothing touches disk), or any binary file-like object.- Parameters:
src – Path, raw bytes, or file-like object to decode.
target_sr – If given, resample the decoded audio to this rate (via
resample()); otherwise the native rate is returned.mono – If
True, down-mix multichannel audio to mono.dtype – NumPy dtype string for the returned array (default
float32).
- Returns:
A
(samples, sample_rate)tuple.sampleshas shape(frames,)(mono) or(frames, channels);sample_ratereflects any resample.
Lazy dependencies:
soundfile(andsoxrwhentarget_srdiffers).
- foley.audio.loudness_normalize(samples: ndarray, sample_rate: int, *, target_lufs: float = -16.0, peak_ceiling_dbfs: float = -1.0, min_block_s: float = 0.4) tuple['ndarray', float][source]
Loudness-normalize to
target_lufs, then keep it peak-safe.Integrated loudness is measured (ITU-R BS.1770-4 / EBU R128), the signal is scaled to
target_lufs, and finally attenuated so its sample peak sits at or belowpeak_ceiling_dbfs. Two inputs are returned unchanged (flag them, don’t amplify): near-silent input (measured loudness at or belowLUFS_GATE_FLOOR), and a clip shorter than one BS.1770 gating block (min_block_s) — whichpyloudnormcannot measure and would otherwise raiseValueErroron (routine for one-shots: clicks, blips, gunshots).- Parameters:
samples – Working array (mono or multichannel, time on axis 0 — the layout pyloudnorm expects).
sample_rate – Sample rate in Hz.
target_lufs – Desired integrated loudness (default = foley’s podcast target).
peak_ceiling_dbfs – Sample-peak ceiling (dBFS) applied after loudness normalization. Note this is a sample-peak limit, not an inter-sample true-peak (dBTP) limit — see
foley.qc.true_peak_dbtp()for the oversampled measurement.min_block_s – Minimum clip length (seconds) that can be loudness-measured; shorter clips are returned unchanged with
measured = -inf.
- Returns:
(normalized, measured_input_lufs). When the input is near-silent or too short to measure,normalizedis the unchanged input andmeasured_input_lufsis at or belowLUFS_GATE_FLOOR(-inffor the too-short case).
Lazy dependencies:
pyloudnorm(+numpy).
- foley.audio.resample(samples: ndarray, sample_rate: int, *, target_sr: int = 48000, quality: str = 'HQ') ndarray[source]
Resample
samplestotarget_sr(a no-op when already there).- Parameters:
samples – Working array (mono
(frames,)or(frames, channels)).sample_rate – The array’s current rate in Hz.
target_sr – Desired output rate in Hz (default = the working rate).
quality – soxr quality preset (
QQ/LQ/MQ/HQ/VHQ).
- Returns:
The resampled array (the input unchanged when
sample_rate == target_sr). dtype is preserved by soxr.
Lazy dependency:
soxr.
- foley.audio.save(samples: ndarray, sample_rate: int, dst: str | os.PathLike | BinaryIO, *, fmt: str = 'flac', subtype: str = 'PCM_24') None[source]
Write
samplestodstasfmt/subtype(default = FLAC archive).- Parameters:
samples – The working array to write (shape
(frames,)or(frames, channels)).sample_rate – Sample rate in Hz.
dst – Destination path or writable binary file-like object.
fmt – Container/codec name (case-insensitive; passed to libsndfile).
subtype – Sample subtype (e.g.
PCM_24,PCM_16,FLOAT).
Lazy dependency:
soundfile.
- foley.audio.to_mono(samples: ndarray) ndarray[source]
Down-mix to mono by averaging channels; 1-D input passes through.
- Parameters:
samples – Mono
(frames,)or multichannel(frames, channels)array.- Returns:
A 1-D mono array (dtype preserved).
- foley.audio.to_working(samples: ndarray, sample_rate: int, *, mono: bool = True, target_sr: int = 48000, dtype: str = 'float32') ndarray[source]
Produce the canonical CLAP/QC working array from an arbitrary clip.
Down-mixes (when
mono), resamples totarget_sr, and casts todtype— thefloat32@ 48 kHz mono array every embedder/tagger/QC check consumes.- Parameters:
samples – Decoded working array (mono or multichannel).
sample_rate – The array’s current rate in Hz.
mono – If
True, down-mix to mono.target_sr – Working sample rate in Hz.
dtype – Output NumPy dtype string.
- Returns:
The canonical working array.
Lazy dependency:
soxr(only whensample_rate != target_sr).
- foley.audio.trim_silence(samples: ndarray, sample_rate: int, *, top_db: float = 30.0) tuple['ndarray', tuple[int, int]][source]
Strip leading/trailing silence, returning the clip and its kept span.
Silence detection runs on a transient mono down-mix (so librosa’s time-last convention never clashes with foley’s time-first
(frames, channels)layout); the returned sample indices then slice the original array along axis 0, preserving its channel layout.- Parameters:
samples – Working array (mono or multichannel).
sample_rate – Sample rate in Hz (kept in the signature for API symmetry; trimming is index-based).
top_db – A frame is silent when it sits at least this many dB below the reference (peak) level.
- Returns:
(trimmed, (start_sample, end_sample)). On all-silent (or otherwise degenerate) input the original array is returned unchanged with a full-length span(0, len(samples)).
Lazy dependency:
librosa.