Time-stretch drum breaks to target BPMs with automatic tempo detection
Project description
breaks-machine
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 with crispness=5 optimized for transient preservation
- Format Conversion: Optional sample rate, bit depth, and channel conversion
- Manual BPM Override: Specify exact source BPM with
--bpmflag 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:
--crispness {0-6}: Rubberband crispness (default: 5, higher preserves transients)-s, --step N: Step size for range mode (default: 10)
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
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
- Manual Override (
--bpm): If specified, uses this value (most reliable) - 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,drums_90.wav - Range: Must be 90-180 BPM to avoid false matches
- With "bpm" suffix:
- 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
--bpmflag 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 Crispness
The tool uses crispness=5 by default, which:
- Preserves transients (drum hits)
- Minimizes phase artifacts
- Optimized for percussive material
You can adjust with --crispness {0-6} (higher = more transient preservation).
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:
-
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
-
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
-
Clone the repository:
git clone https://github.com/yourusername/breaks-machine.git cd breaks-machine
-
Install system dependencies (see Requirements above)
-
Install Python dependencies:
uv sync --group dev
-
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
- pyrubberband: Python wrapper for Rubberband
- 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:
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
uv run pytest tests - Format code:
uvx ruff format - 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)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file breaks_machine-0.2.0.tar.gz.
File metadata
- Download URL: breaks_machine-0.2.0.tar.gz
- Upload date:
- Size: 71.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f154e9bd96eedec15b0f06ecc39542434ca815f53eeb78a0cb83a8499f7b15a2
|
|
| MD5 |
0fc02375f1661bc44eac52d6d4eef868
|
|
| BLAKE2b-256 |
5756bdf23dd525ec32e00544eb8c9471856236fe717a778aad4ef7db8cd3813f
|
File details
Details for the file breaks_machine-0.2.0-py3-none-any.whl.
File metadata
- Download URL: breaks_machine-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49aee6da67167916b836a822498974b02fea9352c4d0d66ff99653f40bb8a166
|
|
| MD5 |
98579018f6abf646c287654256cdd1f2
|
|
| BLAKE2b-256 |
2fe9787dc242becee02999952709d5d11937953ef491171f49f31a7ec1cccca9
|