# bpm-finder-tools
`bpm-finder-tools` is a lightweight Rust utility package for audio-file BPM analysis, tap tempo analysis, BPM conversion, and practical tempo normalization. It is maintained by [BPM Finder](https://bpm-finder.net/), the browser-based tool for fast and privacy-first BPM workflows.
## Why this crate exists
Many music and workflow tools only need a few reliable tempo primitives:
- Detect BPM from supported audio files
- Estimate BPM from tap intervals
- Convert BPM into milliseconds per beat or bar
- Normalize half-time and double-time readings into a practical range
This crate keeps those utilities small, dependency-light, and easy to embed in Rust applications or command-line workflows.
Supported file decoding currently includes WAV, MP3, FLAC, OGG/Vorbis, and common MP4/M4A AAC or ALAC audio files.
## Installation
Add the crate to your project:
```toml
[dependencies]
bpm-finder-tools = "0.1.0"
```
Or install the CLI from source:
```bash
cargo install bpm-finder-tools
```
## Library usage
### Tap tempo
```rust
use bpm_finder_tools::tap;
let analysis = tap::analyze_intervals(&[500.0, 480.0, 495.0, 505.0])?;
assert_eq!(analysis.average_interval_ms, 495.0);
assert_eq!(analysis.bpm, 121.212);
assert_eq!(analysis.rounded_bpm, 121);
# Ok::<(), bpm_finder_tools::TapTempoError>(())
```
### Analyze an audio file
```rust
use bpm_finder_tools::file;
let analysis = file::analyze_path("fixtures/loop.wav", 70.0, 180.0)?;
println!("Detected BPM: {}", analysis.bpm);
println!("Normalized BPM: {}", analysis.normalized_bpm);
# Ok::<(), bpm_finder_tools::TapTempoError>(())
```
### BPM and milliseconds conversion
```rust
use bpm_finder_tools::convert;
let beat_ms = convert::bpm_to_ms_per_beat(128.0)?;
let bar_ms = convert::bpm_to_ms_per_bar(128.0, 4)?;
let bpm = convert::ms_per_bar_to_bpm(1875.0, 4)?;
assert_eq!(beat_ms, 468.75);
assert_eq!(bar_ms, 1875.0);
assert_eq!(bpm, 128.0);
# Ok::<(), bpm_finder_tools::TapTempoError>(())
```
### Normalize into a practical range
```rust
use bpm_finder_tools::range;
assert_eq!(range::normalize(72.0, 90.0, 180.0)?, 144.0);
assert_eq!(range::double_time(87.5)?, 175.0);
assert_eq!(range::half_time(174.0)?, 87.0);
assert!(range::is_within(128.0, 90.0, 180.0)?);
# Ok::<(), bpm_finder_tools::TapTempoError>(())
```
## CLI usage
```bash
bpm-finder-tools file ./loop.wav
bpm-finder-tools tap 500 480 495 505
bpm-finder-tools ms 128
bpm-finder-tools bpm 468.75
bpm-finder-tools normalize 72 --min 90 --max 180
```
Example output:
```text
$ bpm-finder-tools file ./loop.wav
File: ./loop.wav
Detected BPM: 120
Rounded BPM: 120
Normalized BPM: 120
Confidence: 0.742
Duration: 8
Sample rate: 44100 Hz
```
```text
$ bpm-finder-tools tap 500 480 495 505
Tap intervals: 500, 480, 495, 505
Average interval: 495 ms
Exact BPM: 121.212
Rounded BPM: 121
```
## Public API
### `TapTempoAnalysis`
- `average_interval_ms: f64`
- `bpm: f64`
- `rounded_bpm: u32`
### `AudioFileAnalysis`
- `bpm: f64`
- `rounded_bpm: u32`
- `normalized_bpm: f64`
- `confidence: f64`
- `duration_seconds: f64`
- `analyzed_seconds: f64`
- `sample_rate: u32`
### `file`
- `analyze_path(path, min_bpm, max_bpm) -> Result<AudioFileAnalysis, TapTempoError>`
- `analyze_samples(samples, sample_rate, min_bpm, max_bpm) -> Result<AudioFileAnalysis, TapTempoError>`
### `tap`
- `analyze_intervals(intervals_ms: &[f64]) -> Result<TapTempoAnalysis, TapTempoError>`
- `bpm_from_intervals(intervals_ms: &[f64]) -> Result<f64, TapTempoError>`
### `convert`
- `bpm_to_ms_per_beat(bpm: f64) -> Result<f64, TapTempoError>`
- `bpm_to_ms_per_bar(bpm: f64, beats_per_bar: u32) -> Result<f64, TapTempoError>`
- `ms_per_beat_to_bpm(milliseconds: f64) -> Result<f64, TapTempoError>`
- `ms_per_bar_to_bpm(milliseconds: f64, beats_per_bar: u32) -> Result<f64, TapTempoError>`
### `range`
- `normalize(bpm: f64, min: f64, max: f64) -> Result<f64, TapTempoError>`
- `is_within(bpm: f64, min: f64, max: f64) -> Result<bool, TapTempoError>`
- `half_time(bpm: f64) -> Result<f64, TapTempoError>`
- `double_time(bpm: f64) -> Result<f64, TapTempoError>`
## Development
```bash
cargo test
cargo package --list
cargo publish --dry-run
```
## Release notes
Current version: `0.1.0`
- First public release
- Library API plus CLI
- Basic audio-file BPM analysis for supported formats
## License
MIT