Metadata-Version: 2.4
Name: multislope
Version: 0.1.1
Summary: Multi-slope sound energy decay estimation: DecayFitNet, Bayesian decay analysis, and friends
Project-URL: Homepage, https://github.com/artificial-audio/multislope
Project-URL: Repository, https://github.com/artificial-audio/multislope
Project-URL: Issues, https://github.com/artificial-audio/multislope/issues
Project-URL: Changelog, https://github.com/artificial-audio/multislope/blob/main/CHANGELOG.md
Author: Georg Götz, Sebastian J. Schlecht
Maintainer-email: "Sebastian J. Schlecht" <sebastian.schlecht@fau.de>
License-Expression: MIT
License-File: LICENSE
Keywords: acoustics,decay analysis,impulse response,multi-slope,reverberation,reverberation time,room acoustics
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Requires-Dist: numpy>=1.22
Requires-Dist: onnxruntime>=1.15
Requires-Dist: scipy>=1.9
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: h5py>=3.7; extra == 'dev'
Requires-Dist: matplotlib>=3.5; extra == 'dev'
Requires-Dist: onnx>=1.14; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: soundfile>=0.12; extra == 'dev'
Requires-Dist: tensorboard>=2.10; extra == 'dev'
Requires-Dist: torch>=2.0; extra == 'dev'
Requires-Dist: torchaudio>=2.0; extra == 'dev'
Requires-Dist: tqdm>=4.64; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: plot
Requires-Dist: matplotlib>=3.5; extra == 'plot'
Provides-Extra: torch
Requires-Dist: torch>=2.0; extra == 'torch'
Requires-Dist: torchaudio>=2.0; extra == 'torch'
Provides-Extra: train
Requires-Dist: h5py>=3.7; extra == 'train'
Requires-Dist: onnx>=1.14; extra == 'train'
Requires-Dist: tensorboard>=2.10; extra == 'train'
Requires-Dist: torch>=2.0; extra == 'train'
Requires-Dist: torchaudio>=2.0; extra == 'train'
Requires-Dist: tqdm>=4.64; extra == 'train'
Description-Content-Type: text/markdown

# multislope

Multi-slope sound energy decay estimation for room impulse responses.

Rooms rarely decay with a single reverberation time. Coupled volumes, non-diffuse
fields, and strongly absorbing surfaces produce energy decay curves that need two
or three exponentials to be described well. `multislope` estimates those decay
times, their amplitudes, and the noise floor, per octave band, from a measured RIR.

```bash
pip install multislope
```

The core install is NumPy, SciPy, and ONNX Runtime — no PyTorch required.

## Quickstart

```python
import multislope
import soundfile as sf

rir, fs = sf.read("my_rir.wav")

net = multislope.DecayFitNet(sample_rate=fs)
fit = net.estimate(rir)

print(fit)
# DecayFit(method='DecayFitNet', sample_rate=48000)
#      125 Hz  T = [0.690] s  A = [0.963]  N = 1.9e-08
#      250 Hz  T = [0.530, 2.303] s  A = [0.787, 0.0634]  N = 1.15e-09
#   ...

fit.t          # (n_bands, n_slopes) decay times in seconds; 0 marks an inactive slope
fit.a          # (n_bands, n_slopes) amplitudes, linear scale
fit.n          # (n_bands, 1) noise floor, linear scale
fit.n_slopes   # (n_bands,) number of active slopes per band
```

Reconstruct the fitted EDC to compare it against the measured one:

```python
import numpy as np

pre = multislope.PreprocessRIR(sample_rate=fs)
measured_edc, _ = pre.schroeder(rir)              # (n_channels, n_bands, n_samples)
time_axis = np.arange(measured_edc.shape[-1]) / fs

fitted_edc = fit.edc(time_axis)                   # (n_bands, n_samples)
```

## Methods

| | `DecayFitNet` | `BayesianDecayAnalysis` |
|---|---|---|
| Approach | trained neural network (bundled, ~10 MB of ONNX weights) | slice sampling over a discrete parameter grid |
| Speed | milliseconds per RIR | seconds to minutes per RIR |
| Slopes | 1–3, either fixed or estimated by the network | 1–3, either fixed or selected by BIC |
| Needs training data | already trained | no |
| Determinism | deterministic | stochastic; pass `seed=` to reproduce |

Both take the same arguments and return the same `DecayFit`:

```python
net = multislope.DecayFitNet(n_slopes=0, sample_rate=fs)          # 0 = estimate the count
bda = multislope.BayesianDecayAnalysis(n_slopes=0, sample_rate=fs, n_iterations=100, seed=0)

fit_net = net.estimate(rir)
fit_bayes = bda.estimate(rir)
```

## Options

**Fixing the number of slopes.** `n_slopes=2` fits exactly two slopes and skips
model-order selection. `n_slopes=0` lets the estimator decide, and inactive
slopes come back with `T = A = 0`.

**Frequency bands.** The default bands are 125 Hz to 4 kHz. Pass your own centre
frequencies; a `0` adds a lowpass band below the lowest octave, and
`sample_rate / 2` adds a highpass band above the highest one.

```python
net = multislope.DecayFitNet(sample_rate=fs, filter_frequencies=[0, 125, 250, 500, 1000, 2000, 4000, fs / 2])
```

**Direct sound.** By default the whole RIR is analysed. Pass
`analyse_full_rir=False` to detect the direct-sound onset and discard everything
before it.

**Analysing an EDC directly.** If you already have an energy decay curve, pass
`input_is_edc=True` to skip filtering and backwards integration.

## Example data

The example RIRs from the DecayFitNet repository are downloaded on first use and
cached locally, so they are not part of the wheel:

```python
from multislope import data

rir, fs = data.example_rir("doubleslope")
print(data.available_examples())
```

## Optional extras

```bash
pip install multislope[plot]    # matplotlib helpers in multislope.plotting
pip install multislope[train]   # PyTorch training code in multislope.training
```

`multislope.training` re-exports the original DecayFitNet training pipeline
(dataset, model definition, and EDC loss) for anyone who wants to retrain or
fine-tune the network. It is not needed for inference.

## Attribution

**The original implementation was developed by [Georg Götz](https://github.com/georg-goetz).**
Both estimators in this package, and the bundled network weights, come from his
[DecayFitNet toolbox](https://github.com/georg-goetz/DecayFitNet), written with
Sebastian J. Schlecht and Ville Pulkki and released under the MIT license. The
network architecture, the training procedure that produced the weights, and the
Bayesian analysis are all his work.

What this package adds is packaging, not method: the Python toolbox is ported
from PyTorch to NumPy/SciPy, published to PyPI with the weights bundled, and
given a common API that further estimators can plug into. The numerics are
unchanged, and regression tests against the reference implementation are what
verify that.

If you use this software, please cite:

```bibtex
@article{goetz2022decayfitnet,
  title   = {Neural network for multi-exponential sound energy decay analysis},
  author  = {G{\"o}tz, Georg and Schlecht, Sebastian J. and Pulkki, Ville},
  journal = {The Journal of the Acoustical Society of America},
  volume  = {152},
  number  = {2},
  pages   = {942--953},
  year    = {2022},
  doi     = {10.1121/10.0013416}
}
```

The Bayesian analysis follows Xiang et al., "Bayesian characterization of
multiple-slope sound energy decays in coupled-volume systems", JASA 129(2),
741–752, 2011, and Jasa & Xiang, "Efficient estimation of decay parameters in
acoustically coupled-spaces using slice sampling", JASA 126(3), 1269–1279, 2009.

## Development

```bash
git clone https://github.com/artificial-audio/multislope
cd multislope
pip install -e ".[dev]"

python -m multislope.data download   # the regression tests analyse the example RIRs
pytest                               # full suite, including torch parity and slow Bayesian tests
pytest -m "not slow"                 # fast subset
ruff check .
```

The regression tests compare every estimate against
`tests/data/golden_upstream.npz`, captured by running the reference PyTorch
implementation. `scripts/make_golden_fixtures.py` regenerates it; see its
docstring for how.

## License

MIT. See [LICENSE](LICENSE).
