Thank you for your interest in contributing to BPM & Key Detector! This document provides guidelines and information for contributors.
- Code of Conduct
- Getting Started
- Development Setup
- Making Changes
- Testing
- Submitting Changes
- Code Style
- Documentation
- Reporting Issues
This project adheres to a code of conduct that we expect all contributors to follow. Please be respectful and constructive in all interactions.
- Python 3.12 or higher
- Git
- Basic understanding of audio processing and music theory (helpful but not required)
We welcome contributions in the following areas:
-
Core Analysis Features
- Improving BPM detection accuracy
- Enhancing key detection algorithms
- Adding new chord progression analysis features
- Optimizing performance
-
New Analysis Modules
- Additional rhythm pattern detection
- More sophisticated instrument classification
- Advanced harmonic analysis
- Genre classification
-
Performance Optimization
- Reducing processing time
- Memory usage optimization
- Parallel processing implementation
-
Documentation
- API documentation improvements
- Usage examples
- Tutorial content
- Translation to other languages
-
Testing
- Adding test cases
- Improving test coverage
- Performance benchmarks
# Fork the repository on GitHub, then clone your fork
git clone git@github.com:libraz/bpm-detector.git
cd bpm-detector# Install dependencies with rye
rye sync
# Activate the virtual environment
rye shell# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install in development mode
pip install -e .
# Install development dependencies
pip install pytest pytest-cov black isort flake8 mypy# Run basic tests
python -m pytest tests/
# Test CLI functionality
python -m bpm_detector.cli examples/test_audio.wav --detect-keygit checkout -b feature/your-feature-name
# or
git checkout -b fix/issue-descriptionUse short, descriptive branch names in English (e.g., feature/tempo-estimation).
- Maintain backward compatibility
- Add comprehensive tests for new features
- Document any new parameters or options
- Consider performance implications
- Follow the existing module structure (see
src/bpm_detector/) - Implement the standard
analyze()method - Add appropriate error handling
- Include docstrings with parameter descriptions
"""New analysis module."""
import numpy as np
from typing import Dict, Any
class NewAnalyzer:
"""New analysis functionality."""
def __init__(self, hop_length: int = 128):
"""Initialize analyzer.
Args:
hop_length: Hop length for analysis
"""
self.hop_length = hop_length
def analyze(self, y: np.ndarray, sr: int) -> Dict[str, Any]:
"""Perform analysis.
Args:
y: Audio signal
sr: Sample rate
Returns:
Analysis results dictionary
"""
# Implementation here
return {}# Run all tests
python -m pytest
# Run with coverage
python -m pytest --cov=src/bpm_detector
# Run specific test file
python -m pytest tests/test_chord_analyzer.py
# Run with verbose output
python -m pytest -v- Add tests for all new functionality
- Use descriptive test names
- Include edge cases and error conditions
- Test with various audio formats and lengths
import unittest
import numpy as np
from src.bpm_detector.new_analyzer import NewAnalyzer
class TestNewAnalyzer(unittest.TestCase):
"""Test cases for NewAnalyzer."""
def setUp(self):
"""Set up test fixtures."""
self.analyzer = NewAnalyzer()
self.sr = 22050
self.test_signal = np.random.randn(self.sr * 5) # 5 seconds
def test_basic_functionality(self):
"""Test basic analysis functionality."""
result = self.analyzer.analyze(self.test_signal, self.sr)
# Check result structure
self.assertIsInstance(result, dict)
self.assertIn('expected_key', result)
def test_empty_input(self):
"""Test behavior with empty input."""
empty_signal = np.array([])
result = self.analyzer.analyze(empty_signal, self.sr)
# Should handle gracefully
self.assertIsInstance(result, dict)# Run performance benchmarks
python examples/performance_comparison.py
# Profile specific functions
python -m cProfile -s cumulative your_script.py- All tests pass
- Code follows style guidelines
- Documentation is updated
- Performance impact is considered
- Commit messages are clear
Use clear, descriptive commit messages. Prefix each message with a short type such as feat:, fix:, docs:, chore:, refactor:, style:, test: or perf:.
# Good examples
git commit -m "feat: add chord progression complexity scoring"
git commit -m "fix: resolve memory leak in structure analyzer"
git commit -m "perf: optimize similarity matrix computation"
# Include issue numbers when applicable
git commit -m "fix: handle short files in BPM detection (fixes #123)"- Push your branch to your fork
- Create a pull request against the main repository
- Fill out the pull request template
- Wait for review and address feedback
- Ensure CI checks pass
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Performance improvement
- [ ] Documentation update
- [ ] Other (please describe)
## Testing
- [ ] Added tests for new functionality
- [ ] All existing tests pass
- [ ] Tested with various audio files
## Performance Impact
- [ ] No performance impact
- [ ] Performance improvement
- [ ] Performance regression (justified)
## Documentation
- [ ] Updated relevant documentation
- [ ] Added docstrings for new functions
- [ ] Updated README if neededWe follow PEP 8 with some modifications:
# Format code with black
black src/ tests/
# Sort imports with isort
isort src/ tests/
# Check style with flake8
flake8 src/ tests/
# Type checking with mypy
mypy src/- Line length: 88 characters (black default)
- Use type hints for function parameters and return values
- Write descriptive docstrings for all public functions
- Use meaningful variable names
- Add comments for complex algorithms
- Write all code, comments, and docstrings in English
def analyze_chord_progression(
chroma: np.ndarray,
sr: int,
hop_length: int = 128
) -> Dict[str, Any]:
"""Analyze chord progression from chroma features.
Args:
chroma: Chroma feature matrix (12 x n_frames)
sr: Sample rate in Hz
hop_length: Hop length for frame timing
Returns:
Dictionary containing:
- main_progression: List of chord names
- complexity: Complexity score (0-1)
- confidence: Detection confidence (0-1)
Raises:
ValueError: If chroma matrix has wrong dimensions
"""
if chroma.shape[0] != 12:
raise ValueError(f"Expected 12 chroma bins, got {chroma.shape[0]}")
# Implementation here
return {
'main_progression': [],
'complexity': 0.0,
'confidence': 0.0
}- Use Google-style docstrings
- Include parameter types and descriptions
- Document return values and exceptions
- Provide usage examples
When adding new features:
- Update feature list in README.md
- Add usage examples
- Update performance benchmarks if applicable
- Update README_ja.md (Japanese version)
- Explain complex algorithms
- Document non-obvious design decisions
- Include references to papers or algorithms used
Include the following information:
- Python version
- Operating system
- Audio file format and characteristics
- Complete error message and stack trace
- Minimal code example to reproduce
- Describe the use case
- Explain the expected behavior
- Consider implementation complexity
- Discuss potential performance impact
- Include benchmark results
- Specify audio file characteristics
- Compare with expected performance
- Suggest potential optimizations
We use semantic versioning (MAJOR.MINOR.PATCH):
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes
- All tests pass
- Documentation updated
- Performance benchmarks updated
- Version number bumped
- Changelog updated
- Docker image builds successfully
- Questions: Open a GitHub issue with the "question" label
- Discussions: Use GitHub Discussions for general topics
- Real-time chat: Join our Discord server (link in README)
Contributors will be recognized in:
- CONTRIBUTORS.md file
- Release notes
- Documentation credits
Thank you for contributing to BPM & Key Detector! 🎵