Metadata-Version: 2.4
Name: fastpeaks
Version: 0.1.0
Summary: Fast waveform peak generation for WaveSurfer.js using FFmpeg and NumPy.
Author: stayml
License-Expression: MIT
Project-URL: Homepage, https://github.com/stayml/fastpeaks
Project-URL: Repository, https://github.com/stayml/fastpeaks
Project-URL: Issues, https://github.com/stayml/fastpeaks/issues
Keywords: audio,waveform,peaks,wavesurfer,wavesurfer.js,mp3,ffmpeg
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
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: Programming Language :: Python :: 3.14
Classifier: Operating System :: OS Independent
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: twine>=5.1; extra == "dev"
Dynamic: license-file

# FastPeaks

FastPeaks is a small Python library and command-line tool that generates normalized waveform peaks from audio files.

It is designed for supplying precomputed waveform data to browser waveform libraries such as [WaveSurfer.js](https://wavesurfer.xyz/).

FastPeaks uses FFmpeg to decode audio directly to low-sample-rate mono PCM and NumPy to calculate waveform peaks efficiently. A 77-minute MP3 can be processed in approximately four seconds on typical desktop hardware, while cached results can be loaded in milliseconds.

## Features

- Generate normalized RMS waveform peaks
- Process MP3 and other FFmpeg-supported audio formats
- Decode directly to low-sample-rate mono audio
- Choose the number of returned peaks
- Optional JSON caching
- Automatic cache invalidation when the source file changes
- Force cache regeneration when required
- Python API
- Command-line interface
- JSON output suitable for WaveSurfer.js

## Requirements

FastPeaks requires:

- Python 3.10 or newer
- FFmpeg
- NumPy

NumPy is installed automatically with FastPeaks. FFmpeg must be installed separately and available through your system's `PATH`.

Check whether FFmpeg is installed:

```bash
ffmpeg -version
```

### Installing FFmpeg on macOS

Using Homebrew:

```bash
brew install ffmpeg
```

### Installing FFmpeg on Ubuntu or Debian

```bash
sudo apt update
sudo apt install ffmpeg
```

### Installing FFmpeg on Windows

FFmpeg can be installed using Windows Package Manager:

```powershell
winget install Gyan.FFmpeg
```

Restart your terminal after installation and confirm that this works:

```powershell
ffmpeg -version
```

## Installation

Once FastPeaks is available from PyPI:

```bash
pip install fastpeaks
```

For local development, clone the repository and install it in editable mode:

```bash
git clone https://github.com/stayml/fastpeaks.git
cd fastpeaks

python -m venv .venv
source .venv/bin/activate

python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```

On Windows, activate the virtual environment with:

```powershell
.venv\Scripts\activate
```

## Python usage

Generate 1,000 waveform peaks:

```python
import fastpeaks

peaks = fastpeaks.generate_peaks("audio.mp3")
```

Generate 2,000 peaks:

```python
import fastpeaks

peaks = fastpeaks.generate_peaks(
    "audio.mp3",
    num_peaks=2000,
)
```

Enable caching:

```python
import fastpeaks

peaks = fastpeaks.generate_peaks(
    "audio.mp3",
    num_peaks=2000,
    cache=True,
)
```

Force cache regeneration:

```python
import fastpeaks

peaks = fastpeaks.generate_peaks(
    "audio.mp3",
    num_peaks=2000,
    cache=True,
    refresh_cache=True,
)
```

Use a custom cache directory:

```python
import fastpeaks

peaks = fastpeaks.generate_peaks(
    "audio.mp3",
    cache=True,
    cache_dir="./peak_cache",
)
```

Display performance timings:

```python
import fastpeaks

peaks = fastpeaks.generate_peaks(
    "audio.mp3",
    num_peaks=2000,
    cache=True,
    show_timings=True,
)
```

Timing information is written to stderr.

## Function parameters

```python
fastpeaks.generate_peaks(
    audio_path,
    num_peaks=1000,
    decode_sample_rate=2000,
    decimals=4,
    cache=False,
    cache_dir=None,
    refresh_cache=False,
    show_timings=False,
)
```

| Parameter | Description |
|---|---|
| `audio_path` | Path to the input audio file. |
| `num_peaks` | Number of peaks to return. Defaults to `1000`. |
| `decode_sample_rate` | FFmpeg output sample rate used for analysis. Defaults to `2000`. |
| `decimals` | Decimal places in returned values. Defaults to `4`. |
| `cache` | Load and save JSON cache data when `True`. |
| `cache_dir` | Optional cache directory. Defaults to `.fastpeaks_cache`. |
| `refresh_cache` | Ignore existing cache data and regenerate it. |
| `show_timings` | Print performance information to stderr. |

`refresh_cache=True` automatically enables caching.

The returned value is a normal Python list:

```python
[0.4835, 0.6149, 0.2875, 0.7414, 1.0]
```

Values are normalized between `0.0` and `1.0`.

## Command-line usage

Generate peaks and print JSON to the terminal:

```bash
fastpeaks "audio.mp3"
```

Generate 2,000 peaks:

```bash
fastpeaks "audio.mp3" --num-peaks 2000
```

Enable caching:

```bash
fastpeaks "audio.mp3" --cache
```

Force cache regeneration:

```bash
fastpeaks "audio.mp3" --refresh-cache
```

Write the peaks to a file:

```bash
fastpeaks "audio.mp3" --output peaks.json
```

Generate formatted JSON:

```bash
fastpeaks "audio.mp3" --pretty --output peaks.json
```

Use a custom cache directory:

```bash
fastpeaks "audio.mp3" \
    --cache-dir "./peak_cache" \
    --output peaks.json
```

Display timings:

```bash
fastpeaks "audio.mp3" \
    --cache \
    --timings \
    --output peaks.json
```

See all available options:

```bash
fastpeaks --help
```

## Using peaks with WaveSurfer.js

Generate the peak file:

```bash
fastpeaks "audio.mp3" \
    --num-peaks 2000 \
    --output peaks.json
```

Load both the peaks and audio in JavaScript:

```javascript
async function createWaveform() {
    const response = await fetch("/peaks.json");

    if (!response.ok) {
        throw new Error(
            `Could not load waveform peaks: ${response.status}`
        );
    }

    const peaks = await response.json();

    const wavesurfer = WaveSurfer.create({
        container: "#waveform",
        url: "/audio.mp3",
        peaks: [peaks],
        duration: 300,
    });

    return wavesurfer;
}

createWaveform().catch((error) => {
    console.error(error);
});
```

Replace `duration: 300` with the duration of your audio in seconds.

## Caching

Caching is disabled by default.

When enabled, FastPeaks stores JSON cache files under:

```text
.fastpeaks_cache/
```

A custom directory can be supplied using `cache_dir` or `--cache-dir`.

Cache entries account for:

- Resolved audio path
- Audio file size
- Audio modification time
- Number of requested peaks
- Decode sample rate
- Decimal precision
- FastPeaks algorithm version

If the source file changes, its existing cache entry is treated as stale and regenerated.

Cache files are written atomically to reduce the risk of incomplete files.

## How it works

FastPeaks asks FFmpeg to:

1. Decode the source audio
2. Select the first audio stream
3. Downmix it to mono
4. Resample it to a low analysis sample rate
5. Return signed 16-bit PCM to Python

NumPy then:

1. Converts the PCM data to normalized floating-point samples
2. Divides the samples into equally sized sections
3. Calculates the RMS amplitude of every section
4. Normalizes the largest result to `1.0`
5. Returns the requested number of peaks

The low decode sample rate dramatically reduces memory usage and Python processing time. FFmpeg decoding remains the largest part of first-time processing.

## Development

Install the development dependencies:

```bash
python -m pip install -e ".[dev]"
```

Run the tests:

```bash
pytest
```

Build the package:

```bash
python -m build
```

Check the generated distributions:

```bash
python -m twine check dist/*
```

## Licence

FastPeaks is released under the MIT License.
