Skip to main content

breaks-machine

PyPI version CI codecov Python 3.13+ License: MIT

Time-stretch drum breaks to target BPMs while preserving transient quality.

Perfect for preparing drum breaks for hardware samplers, live performance, or production workflows where you need breaks at specific tempos.

Features

  • BPM Detection from Filenames: Automatically parses BPM from common filename patterns (e.g., amen_170.wav, 164_break.flac)
  • Multiple Target Modes: Stretch to single BPM, multiple targets, or ranges with custom steps
  • Batch Processing: Process entire directories of breaks in one command
  • High-Quality Stretching: Uses Rubberband's R3 engine (--fine --centre-focus) by default, the best option for percussive material
  • Format Conversion: Optional sample rate, bit depth, and channel conversion
  • Manual BPM Override: Specify exact source BPM with --bpm flag when needed

Requirements

System Dependencies

Rubberband is required for time-stretching:

macOS:

brew install rubberband

Ubuntu/Debian:

sudo apt-get install rubberband-cli libsndfile1 ffmpeg

Windows: Download Rubberband from https://breakfastquay.com/rubberband/ and add to PATH.

Python

Python 3.13+ required.

Installation

With uv (recommended):

uv tool install breaks-machine

With pip:

pip install breaks-machine

From source:

git clone https://github.com/thomasjohnflaherty/breaks-machine.git
cd breaks-machine
uv sync

Quick Start

Single File, Single Target

Stretch a break to 140 BPM:

breaks-machine stretch amen_170.wav --target 140

Output: output/amen_170/amen_140.wav

Multiple Targets

Create versions at different BPMs:

breaks-machine stretch amen_170.wav --targets 90,120,140,160

Output:

output/amen_170/
├── amen_90.wav
├── amen_120.wav
├── amen_140.wav
└── amen_160.wav

BPM Range

Generate versions across a range:

breaks-machine stretch break.wav --range 80-160 --step 10

Creates versions at 80, 90, 100, ..., 160 BPM.

Batch Processing

Process an entire directory:

breaks-machine stretch ./breaks/ --target 140 --output ./processed/

With Manual BPM Override

If auto-detection fails or you know the correct tempo:

breaks-machine stretch break.wav --bpm 175 --target 140

CLI Reference

breaks-machine stretch INPUT_PATH [OPTIONS]

Arguments

  • INPUT_PATH: Path to audio file (.wav, .flac) or directory containing audio files

Options

Target Specification (required, choose one):

  • -t, --target BPM: Single target BPM
  • --targets BPM,BPM,...: Comma-separated target BPMs
  • -r, --range START-END: BPM range with optional step

BPM Detection:

  • -b, --bpm BPM: Manual source BPM override
  • -w, --warn: Warn if detected BPM differs from filename

Output:

  • -o, --output DIR: Output directory (default: ./output)

Format Conversion:

  • --sample-rate HZ: Target sample rate (e.g., 44100, 48000)
  • --bit-depth {16,24}: Target bit depth
  • --mono: Convert to mono

Stretching:

  • --mode {stretch,repitch,hybrid,slice}: Processing mode (default: hybrid)
    • hybrid (default): repitch by up to --max-semitones, then time-stretch the remainder — splits the artifact load between pitch change and stretching. For moderate tempo changes (within 1.4x) it skips the repitch entirely and behaves identically to stretch
    • stretch: time-stretch with rubberband, pitch preserved
    • repitch: vinyl-style speed change via resampling — pitch follows tempo (slowing 170→90 drops ~11 semitones), no rubberband involved
    • slice: slice-and-shift (the ReCycle/jungle method) — cuts at detected onsets and moves slices on the timeline instead of stretching them. On slow-downs every transient is reproduced bit-exact; gaps open between hits and each slice's natural decay rings into them. Splice points are crossfaded, no rubberband involved
  • --max-semitones N: Maximum repitch amount in semitones for hybrid mode (default: 3.0)
  • --engine {auto,r2,r3}: Rubberband engine (default: auto). auto uses R3 when rubberband >= 3, r3 forces it (errors on older rubberband), r2 forces the legacy crispness-based engine
  • --crispness {0-6}: Rubberband crispness (default: 5, higher preserves transients). R2 engine only — ignored under R3
  • -s, --step N: Step size for range mode (default: 10)

All modes use the same output filename (amen_90.wav) regardless of --mode.

Examples

Basic usage:

# Stretch to single target
breaks-machine stretch amen_170.wav -t 140

# Multiple targets
breaks-machine stretch break.wav --targets 90,120,140

# Range with custom step
breaks-machine stretch break.wav --range 100-140 --step 5

With format conversion:

# Convert to 44.1kHz mono 16-bit
breaks-machine stretch break.wav -t 140 --sample-rate 44100 --bit-depth 16 --mono

Stretch modes:

# Hybrid (default): repitch up to 3 semitones, stretch the rest
breaks-machine stretch amen_170.wav -t 90

# Hybrid with a tighter pitch budget
breaks-machine stretch amen_170.wav -t 90 --max-semitones 2

# Pure time-stretch (pitch preserved)
breaks-machine stretch amen_170.wav -t 90 --mode stretch

# Vinyl-style repitch (pitch drops with tempo, no stretch artifacts)
breaks-machine stretch amen_170.wav -t 90 --mode repitch

# Slice-and-shift (transients untouched, gaps open between hits)
breaks-machine stretch amen_170.wav -t 90 --mode slice

Batch processing:

# Process directory
breaks-machine stretch ./breaks/ -t 140 -o ./output/

# With manual BPM for all files
breaks-machine stretch ./breaks/ -t 140 --bpm 170

How It Works

Architecture

Input Audio → BPM Detection → Time Stretching → Format Conversion → Output
                    ↓                 ↓                  ↓
              detector.py       stretcher.py      converter.py

BPM Detection Priority

  1. Manual Override (--bpm): If specified, uses this value (most reliable)
  2. Filename Parsing: Automatically detects BPM from these patterns:
    • With "bpm" suffix: amen-170bpm.wav, break_140_BPM.flac, drum-loop-120bpm.wav
    • Leading number: 164_HT_Drums.wav, 140_break.flac, 120-drums.wav
    • Trailing number: amen_170.wav, break-140.flac, Hydro Break 170.wav
    • Range: Must be 90-180 BPM to avoid false matches
  3. Auto-Detection (experimental): Falls back to librosa-based detection if no filename pattern found
    • Warning: Auto-detection is experimental and often produces incorrect results
    • Recommended: Use filename patterns or --bpm flag for reliable results
    • Multi-strategy detection with tempo priors and subdivision correction
    • May misidentify tempo by factors of 2x, 0.5x, or other subdivisions

Best Practice: Name your files with BPM in the filename (e.g., amen_170.wav) or use the --bpm flag to ensure accurate time-stretching.

Rubberband Engines

With rubberband 3.0 or newer (default --engine auto), the tool uses the R3 engine via --fine --centre-focus — Breakfastquay's max-quality offline recipe. R3 is markedly better than R2 for percussive material at large stretch ratios, and it has no crispness control: --crispness is ignored under R3.

With older rubberband (or --engine r2), the tool falls back to the legacy R2 engine with crispness=5, which:

  • Preserves transients (drum hits)
  • Minimizes phase artifacts
  • Is optimized for percussive material

On the R2 path you can adjust with --crispness {0-6} (higher = more transient preservation). Force a specific engine with --engine r2 or --engine r3 (r3 errors clearly if your rubberband is older than 3).

Supported Formats

  • Input: WAV, FLAC
  • Output: Same format as input (or specify conversion options)

Troubleshooting

BPM Detection Issues

If breaks-machine cannot detect the BPM or produces incorrect results:

Problem: "Could not determine BPM" error

Solutions:

  1. Add BPM to filename (recommended):

    # Rename your file to include BPM
    mv break.wav break_140.wav
    breaks-machine stretch break_140.wav -t 120
    
  2. Use manual BPM override:

    breaks-machine stretch break.wav --bpm 140 -t 120
    

Problem: Auto-detection finds wrong BPM (e.g., detects 85 BPM instead of 170 BPM)

Why: Librosa's tempo detection can be unreliable on drum breaks, often misidentifying tempo by factors of 2x or 0.5x. This appears to be related to the version of the Rubberband CLI tool available (pre-4.0.0).

Solution: Always use filename patterns or --bpm flag:

# Good filename patterns
amen_170.wav        # Trailing number
164_break.flac      # Leading number
drums-140bpm.wav    # With "bpm" suffix

# Or use manual override
breaks-machine stretch break.wav --bpm 170 -t 140

Problem: Batch processing with mixed BPMs

Solution: Ensure all files follow naming conventions or use individual processing with --bpm per file.

Development

Local Setup

  1. Clone the repository:

    git clone https://github.com/yourusername/breaks-machine.git
    cd breaks-machine
    
  2. Install system dependencies (see Requirements above)

  3. Install Python dependencies:

    uv sync --group dev
    
  4. Run tests:

    uv run pytest tests
    

Running Tests

# All tests
uv run pytest tests

# With coverage
uv run pytest tests --cov=src/breaks_machine

# Specific test file
uv run pytest tests/test_detector.py -v

Code Quality

# Format code
uvx ruff format

# Lint code
uvx ruff check --fix

# Type checking (if added)
uvx mypy src/

Manual Testing

The repository includes test breaks in the breaks/ directory:

# Test with real breaks
uv run breaks-machine stretch breaks/FR_Drum_Loop_160.wav -t 140

Project Structure

src/breaks_machine/
├── cli.py          # Click-based CLI entry point
├── detector.py     # BPM detection (filename + librosa)
├── stretcher.py    # Rubberband wrapper
├── converter.py    # Format conversion
└── processor.py    # Processing pipeline

tests/
├── test_cli.py
├── test_detector.py
├── test_stretcher.py
├── test_converter.py
└── test_processor.py

Technical Details

Dependencies

  • click: CLI framework
  • librosa: Audio analysis and BPM detection
  • soundfile: Audio I/O for WAV/FLAC

System Requirements

  • rubberband-cli: Time-stretching engine
  • libsndfile1: Audio file I/O library
  • ffmpeg: Audio codec support

See the Requirements section for platform-specific installation instructions.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests: uv run pytest tests
  5. Format code: uvx ruff format
  6. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Note: breaks-machine requires Rubberband CLI (GPL v2) as a system dependency. While breaks-machine itself is MIT licensed, you must comply with Rubberband's GPL v2 license when using it.

Acknowledgments

  • Rubberband: Industry-standard time-stretching library
  • librosa: Audio analysis toolkit
  • Built with modern Python tooling (uv, ruff, pytest)

Metadata

Release files for breaks-machine 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 breaks-machine 0.3.0
File Size Uploaded Provenance Actions
breaks_machine-0.3.0.tar.gz 82.9 kB Without provenance Details for breaks_machine-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for breaks-machine 0.3.0
File Interpreter ABI Platform Actions
breaks_machine-0.3.0-py3-none-any.whl Python 3 none any Details for breaks_machine-0.3.0-py3-none-any.whl

Total release size: 103.9 kB

Release files / breaks_machine-0.3.0.tar.gz

Download URL breaks_machine-0.3.0.tar.gz
Size 82.9 kB
Tags Source
SHA-256 checksum
How to use checksums
53518e70e1b97aa9403a4205829b554c3b297503b5283cedaeb8fb5145ca27c8
BLAKE2b-256 checksum
How to use checksums
6165df35b4047984cce66de6255c6225d5d18aff92f693d42c339e76ac201110
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

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

Download URL breaks_machine-0.3.0-py3-none-any.whl
Size 21.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a53b848ef1bca36055fbf5ba45ce709f81089d18020dd63cfd8a6c625c136d8
BLAKE2b-256 checksum
How to use checksums
3264179496c960c33e8aae38dffe1082c9b73dbad3ea8e416fe9d933b2d3b2fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Security for breaks-machine 0.3.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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