mscales is a Python package to generate, visualize, and sonify musical scales. It provides tools for pitch-class set theory analysis, scale generation, and musical visualization.
uv pip install -e ".[dev]" # Install with uv (recommended)
# or
pip install -e ".[dev]" # Install with pippytest # Run all tests
pytest mscales/tests/test_examples.py # Run single file
pytest mscales/tests/test_examples.py::test_generate_scales # Run single test
pytest -v # Verbose outputruff check mscales/ # Lint code
ruff format mscales/ # Format code
pre-commit run --all-files # Run all hookspip install build # Install build tools
python -m build # Build package- Python 3.11+
- Ruff for linting and formatting (line length: 88)
- Classes:
PascalCase(e.g.,PitchClass,Scales,PitchClassSet) - Functions/variables:
snake_case(e.g.,all_scales(),n_scales) - Constants:
SCREAMING_SNAKE_CASE - Private: Leading underscore (e.g.,
_private_method())
Use type hints in function signatures:
def transpose(self, n: int) -> "PitchClassSet":
return PitchClassSet((self.pcs + n) % self.c)Order: stdlib → third-party → local (separated by blank lines):
import numpy as np
from itertools import combinations
from collections import Counter
import matplotlib.pyplot as plt
import pretty_midi as pm
from .utils import find_ngrams
from .scales import ScalesUse NumPy-style docstrings:
def function_name(param: int, param2: str = "default") -> return_type:
"""
Short description.
Parameters
----------
param : int
Description.
param2 : str, optional
Description, by default "default".
Returns
-------
return_type
Description.
"""
return result- TypeError for type mismatches:
raise TypeError(f"Can't add type {type(other)} to pitch class {self.p}.")
- ValueError for invalid values
- assertions for internal validation
- Use f-strings:
f"PitchClass({self.p})" - Use trailing commas for multi-line structures
- Use
from collections.abc import Iterablefor abstract base classes
Include __init__, __repr__, __str__ methods. Implement dunder methods (__add__, __sub__, __eq__, etc.):
class PitchClass:
def __init__(self, p, c: int = 12):
self.c = c
self.p = p % self.c
def __repr__(self):
return f"PitchClass({self.p})"
def __str__(self):
return str(self.p)
def __add__(self, other):
# implementation
passmscales/
├── __init__.py # Package exports
├── scales.py # Scales class
├── basic.py # Core classes
├── plots.py # Visualization
├── sound.py # Audio/sonification
├── utils.py # Utilities
└── tests/
├── test_examples.py
└── conftest.py
Uses .pre-commit-config.yaml with Ruff. Run pre-commit install to set up git hooks.