Skip to main content

AFX

Audio feature extraction for machine learning. AFX computes 47 features (time-domain, autocorrelation, spectral, spectrogram, cepstral, harmonic, linear prediction, and the eGeMAPS v02 parameter set) with NumPy and SciPy, selects them from a validated JSON config, and aggregates the frames into one vector per clip. The same config drives a scikit-learn transformer, a parallel batch CLI whose outputs record how they were made, and (for the 20 features with torch implementations) a differentiable PyTorch module.

Tests codecov PyPI Python License: MIT

Documentation: danilopena.com/AFX

import afx_audio as afx

config = afx.load_preset('speech@1')                     # or afx.load_config('my_config.json')
signal, sr = afx.load_audio('clip.wav', sr=config['sample_rate'])
features = afx.extract_all_features(signal, sr, config)  # {'mfcc_mean': (20,), 'mfcc_std': (20,), ...}
names, vector = afx.flatten_features(features)           # column names and one float vector

afx.egemaps(signal, sr)                                  # the 88 eGeMAPS v02 functionals, by name

Install

pip install afx-audio
pip install "afx-audio[all]"     # + scikit-learn, torch, pandas/pyarrow, matplotlib

The import name is afx_audio; the examples use import afx_audio as afx. The core install needs NumPy, SciPy and soundfile only. Extras: sklearn for AFXFeatureExtractor and the selectors, torch for the PyTorch path, parquet (pandas and pyarrow) for Parquet output, plot for afx_audio.utils.visualization, all for those four, notebooks, docs, and dev for everything plus the test and lint tools.

Why AFX

librosa computes frame-level features; openSMILE computes standard parameter sets under a research-only licence; TSFEL covers generic time series. AFX sits between them for audio ML work:

Need AFX
One vector per clip from a config JSON config, validated before any audio is read, with "did you mean" suggestions for typos
Standard voice features without licence restrictions eGeMAPS v02 under MIT, agreement with openSMILE published per functional (docs/egemaps.md)
Numbers you can check 14 features equal librosa to float32 precision or better, 20 torch features equal the NumPy path within rtol 1e-9 in float64, all in the test suite
Results you can reproduce and cite versioned presets (speech@1) frozen by hash, and every batch output records config hash, library versions and input file hashes
Knowing what a statistic is based on "coverage": true adds the fraction of valid frames next to every feature; feature_info(name)['undefined'] says when a frame is NaN
Pipelines scikit-learn transformer on file paths or arrays, differentiable torch module, batch CLI to npy/json/csv/parquet

The core has no compiled dependencies beyond NumPy, SciPy and soundfile.

Features

Every feature is a function extract_<name>(signal, sr, *, **params) returning {name: ndarray}, registered in afx.FEATURE_MAP. Per-frame shapes are (n_frames,) or (n_dims, n_frames), always float64. afx.feature_info(name) gives a feature's definition, units, parameters with defaults, when it is undefined, and its torch counterpart; the feature reference lists them all. afx.register_feature(name, func) adds your own extractor to configs and every front end.

Group Features
Time domain zcr, rms_energy, short_time_energy, energy_ratio, variance, kurtosis, crest_factor, entropy, sample_entropy, mobility, complexity
Autocorrelation autocorr_variance, autocorr_snr, autocorr_zero_crossings, autocorr_peak_width, autocorr_decay_rate, autocorr_coeffs_deviation, max_peak_autocorr_envelope
Spectral spectral_centroid, spectral_bandwidth, spectral_rolloff, spectral_flatness, spectral_entropy, spectral_flux, spectral_contrast, spectral_skewness, spectral_kurtosis, spectral_slope, spectral_crest, spectral_decrease
Spectrogram melspectrogram, cqt, chroma_stft, chroma_cqt, tonnetz, onset_strength
Cepstral mfcc, mfcc_delta, mfcc_delta_delta, gfcc
Harmonic pitch (YIN), hnr, thd
Linear prediction lpc, formants
Parameter set egemaps (88 functionals per clip), egemaps_lld (25 descriptors per 10 ms frame)

Framed features take frame_size, hop_length and center (default True, as in librosa; onset_strength is always centered and has no center), so features with the same framing share frame count and frame times; time-domain features analyze the whole clip when the sizes are omitted. Spectral features use an STFT with a periodic Hann window and n_fft = frame_size. With return_metadata=True every extractor also returns the time of each frame's center.

Config

{
  "sample_rate": 16000,
  "features": {
    "mfcc": {"n_mfcc": 20, "frame_size": 400, "hop_length": 160},
    "pitch": {"frame_size": 400, "hop_length": 160, "fmax": 600},
    "rms_energy": {"frame_size": 400, "hop_length": 160}
  },
  "aggregation": "summary",
  "coverage": true
}

aggregation is mean, std, summary (mean and std), advanced_summary (plus skewness and kurtosis), skew, kurtosis, sliding_window or stack. Frames where a feature is undefined (pitch on unvoiced frames, spectral entropy on silence) are NaN and skipped by the statistics, and coverage adds <feature>_valid_frac. afx.validate_config rejects unknown features, unknown parameters and non-positive sizes before any audio is read.

Bundled presets: speech, bioacoustics, music, machinery and egemaps (afx.list_presets(versions=True)). A preset id such as speech@1 names a frozen file that spells out every parameter; cite it with the package version and the config hash from the output's provenance record. ESC-50 baselines give reference accuracies for each preset. See Configuration for framing, aggregation, normalization and presets.

scikit-learn

from afx_audio.sklearn import AFXFeatureExtractor
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

pipe = make_pipeline(
    AFXFeatureExtractor(config='speech@1', n_jobs=-1),
    SimpleImputer(),
    StandardScaler(),
    SVC(),
)
pipe.fit(train_paths, y_train)       # file paths or 1-D signals
pipe[0].get_feature_names_out()      # ['mfcc_mean_0', ..., 'spectral_contrast_std_6']
pipe[0].provenance_                  # config hash, versions, preset id

Features that can be undefined for a whole clip (pitch of noise, formants of silence) come out as NaN, hence the imputer. examples/classify_folders.py runs a preset through such a pipeline with cross-validation on a folder per class.

PyTorch

from afx_audio.torch import AFXFeatureModule, functional as F_afx

mfcc = F_afx.mfcc(waveform, sr=16000, n_mfcc=13)   # (time,) or (batch, time), any device
frontend = AFXFeatureModule(features={'mfcc': {'n_mfcc': 13}, 'spectral_centroid': {}},
                            sample_rate=16000, aggregation='mean_std', output='vector')
frontend = AFXFeatureModule.from_config('bioacoustics@1', output='vector')  # preset, JSON path or dict

Twenty features (mel spectrogram, MFCC and deltas, GFCC, chroma_stft, the twelve spectral descriptors, zcr and RMS) have torch implementations with the NumPy conventions, batched and differentiable with respect to the waveform except through the counting in zcr and the bin selection in spectral_rolloff. afx.feature_info(name)['torch'] gives the tolerance each one is tested to. AFXFeatureModule.from_config reads the features, sample rate, aggregation and coverage of a config; a config with other features raises unless skip_unsupported=True, which drops them with an AFXWarning. import afx_audio does not import torch.

Batch extraction and CLI

afx dataset/ --preset speech@1 --output features.parquet --jobs 8 --stats
afx dataset/ --config my_config.json --features mfcc,zcr --aggregation summary --output features.csv
afx --provenance features.parquet
afx --list-features
afx --describe spectral_rolloff

or afx.extract_features_from_folder(folder, config, output, n_jobs=8). The output format follows the extension (npy, json, csv, parquet), and afx without --output writes features.csv; every file whose extension is a format soundfile reads (wav, flac, ogg, opus, mp3, aiff, ...; --ext wav,flac or file_exts= narrows it) is read, and rows are keyed by the path relative to the folder; in CSV and Parquet vector features expand to name_0, name_1, ... columns (eGeMAPS columns keep their openSMILE names); files that fail are listed in <output>.errors.json. Parallel output equals serial output.

Every output records its provenance (config and config hash, preset id, AFX, NumPy, SciPy and Python versions, sample rate and resampling, sha256 of each input file): in the Parquet schema metadata, otherwise in <output>.provenance.json. --stats (dataset_stats=True) also writes per-column mean, variance, min, max and NaN counts in one streaming pass, and afx_audio.sklearn.scaler_from_stats turns them into a fitted StandardScaler. See Reproducibility.

Workers never fork (forkserver on Linux, spawn on Windows and macOS), so a script that calls extract_features_from_folder with n_jobs > 1 needs an if __name__ == '__main__': guard, and custom features must be importable from a module.

Notebooks

notebooks/ has a numbered tutorial from 01_quickstart to 08_egemaps, each runnable on synthetic audio without a dataset, and benchmark_validation, which compares AFX with librosa, torchaudio, openSMILE, spafe and Praat.

Performance

On a 2-core 2.1 GHz Xeon VM, extract_all_features with the bundled config.json (39 features) takes 0.57 s for 10 s of audio at 22.05 kHz, of which sample_entropy is 0.27 s. Spectral features run at librosa's speed (8 to 12 ms each), and the torch module computes its 20 features for a batch of 16 clips at 0.14 s per clip. python benchmarks/bench_features.py --librosa --torch-batch 16 --markdown reproduces the benchmarks page on your machine.

Citing

If you use AFX in research, cite it with the metadata in CITATION.cff (GitHub's "Cite this repository" button), and give the preset id and config hash of the features you used. For eGeMAPS also cite Eyben et al. (2016).

Development

pip install -e ".[dev]"
pre-commit install
pytest                  # pytest -m "not slow" for a quick run
ruff check .
mypy afx_audio
mkdocs serve

python docs/gen_features.py regenerates the feature reference after an extractor changes; a test fails while it is stale. See CONTRIBUTING.md.

License

MIT, see LICENSE. No audio is committed to this repository; the notebooks synthesize clips or read datasets you download yourself.

Metadata

Release files for afx-audio 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for afx-audio 0.3.0
File Size Uploaded
afx_audio-0.3.0.tar.gz 221.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for afx-audio 0.3.0
File Interpreter ABI Platform
afx_audio-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 344.3 kB

Release files / afx_audio-0.3.0.tar.gz

Download URL afx_audio-0.3.0.tar.gz
Size 221.8 kB
Tags Source
SHA-256 checksum
How to use checksums
8e500ffc9f0696a6fe5cf3f4ec742860e58dd1fbadf0e196608be968ca203fac
BLAKE2b-256 checksum
How to use checksums
7a8a332cd3b0ac28b325c7e9cd5aec3c9d560acb7f5277bb880d0021b72d9209
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / afx_audio-0.3.0-py3-none-any.whl

Download URL afx_audio-0.3.0-py3-none-any.whl
Size 122.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
152572720ea7ff463e505301cb7bb56137b54ffff0d9c4387e788bb9ac7dfa4f
BLAKE2b-256 checksum
How to use checksums
77856a33b842fb737d30d2085be49a1fbec87a568d4886ddfa1bd06cdb36bdcf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.1

2 release files

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page