Observed since 20 Oct, 17:59 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Generate backing track audio from chord charts
Generate backing track audio from chord charts in Python — like iReal Pro, but scriptable.
from accompy import generate_accompaniment
# Generate a bossa nova backing track
audio = generate_accompaniment("| Dm7 | G7 | C^7 | A7b9 |", style="bossa", tempo=140)
print(f"Generated: {audio}") # -> /tmp/xxx.wav
Score, iReal URLs, or (chord, beats) tuplesbackend="auto"|"mma"|"builtin" (with backwards-compatible use_mma)pyRealParser).mid directly (skip audio rendering)pip install accompy
# Or install from source:
git clone https://github.com/thorwhalen/accompy
pip install -e accompy
accompy requires FluidSynth and a SoundFont for audio rendering.
The easiest way to get started:
# Install accompy
pip install -e .
# Run automated setup (interactive)
python -c "from accompy import verify_and_setup; verify_and_setup()"
# This will:
# - Check all dependencies
# - Offer to install missing ones (with your permission)
# - Download and configure SoundFont files
# - Verify everything works
If you prefer to install manually:
# macOS
brew install fluid-synth
# Ubuntu/Debian
sudo apt-get install fluidsynth fluid-soundfont-gm
# Install Python dependencies
pip install midiutil mingus
# Verify setup
python -m accompy --check-deps
FluidSynth renders MIDI using SoundFont sample banks (.sf2 files).
The SoundFont determines the quality of every instrument you hear.
FluidSynth's bundled SoundFont (VintageDreamsWaves-v2.sf2, ~300 KB) is a
bare-minimum placeholder made of basic waveforms — it will sound terrible.
For good results, install a full General MIDI SoundFont with real instrument samples:
mkdir -p ~/.fluidsynth
# MuseScore General (~200 MB, MIT license, good all-around quality)
curl -L -o ~/.fluidsynth/default_sound_font.sf2 \
"https://ftp.osuosl.org/pub/musescore/soundfont/MuseScore_General/MuseScore_General.sf2"
Other recommended SoundFonts:
Browse more at https://musical-artifacts.com/artifacts?formats=sf2.
After downloading, place or symlink the file at
~/.fluidsynth/default_sound_font.sf2 (accompy looks there by default).
# Quick check
python -c "from accompy import check_dependencies; print(check_dependencies())"
# Detailed diagnostic report
python -c "from accompy import print_diagnostic_report; print_diagnostic_report()"
from accompy import generate_accompaniment
# Generate with default settings
audio = generate_accompaniment("| C | Am | F | G |")
# Specify style and tempo
audio = generate_accompaniment(
"| Dm7 | G7 | Cmaj7 | Am7 |",
style="swing",
tempo=160,
repeats=4,
output_path="my_track.wav",
)
# Generate MIDI only (skip audio rendering)
midi = generate_accompaniment(
"| Dm7 | G7 | Cmaj7 | Am7 |",
output_path="my_track.mid",
)
For more control, create a Score object:
from accompy import Score, generate_accompaniment
# Parse chord string
score = Score.from_string(
"| Dm7 | G7 | Cmaj7 | % |", # % = repeat previous chord
title="ii-V-I in C",
key="C",
time_signature=(4, 4)
)
# Generate audio
audio = generate_accompaniment(score, style="swing", tempo=120)
### Flexible Chord Inputs
`generate_accompaniment(...)` accepts several common chord progression formats.
```python
from accompy import ensure_score, generate_accompaniment
# 1) iReal-style chord strings
generate_accompaniment("| C | Am | F | G |")
# 2) iReal Pro URL strings (irealbook://... or irealb://...)
generate_accompaniment("irealbook://Autumn%20Leaves=...")
# 3) List of (chord, beats) tuples (like `accompany`)
chords = [("F#m7b5", 4), ("B7", 4), ("Em", 8)]
score = ensure_score(chords, key="E")
generate_accompaniment(score)
Choose which generator to use:
from accompy import generate_accompaniment
# Auto: use MMA if available, else builtin
generate_accompaniment("| Dm7 | G7 | Cmaj7 | Am7 |", backend="auto")
# Force builtin generator
generate_accompaniment("| Dm7 | G7 | Cmaj7 | Am7 |", backend="builtin")
# Force MMA (errors if MMA isn't installed)
generate_accompaniment("| Dm7 | G7 | Cmaj7 | Am7 |", backend="mma")
### Chord Notation
accompy supports flexible chord notation:
```python
# Jazz notation (iReal Pro style)
"| C^7 | D-7 | G7 | C^7 |" # ^7=maj7, -7=min7
# Standard notation
"| Cmaj7 | Dm7 | G7 | Cmaj7 |"
# Simple notation
"| C | Dm | G | C |"
# Multiple chords per bar
"| Dm7 G7 | Cmaj7 |" # Two chords, split evenly
# Repeat symbols
"| C | G | % | F |" # % repeats G
Chord symbol reference:
| Notation | Meaning |
|---|---|
C, Cmaj | C major |
C-, Cm, Cmin | C minor |
C7 | C dominant 7 |
C^7, Cmaj7 | C major 7 |
C-7, Cm7, Cmin7 | C minor 7 |
Co, Cdim | C diminished |
Co7, Cdim7 | C diminished 7 |
Ch7, Cm7b5 | C half-diminished |
C+, Caug | C augmented |
Csus, Csus4 | C suspended 4th |
Available styles with characteristic patterns:
| Style | Description | Typical Tempo |
|---|---|---|
swing | Jazz swing with walking bass | 120-200 BPM |
bossa | Bossa nova with syncopated feel | 100-150 BPM |
rock | Straight 8ths rock beat | 100-140 BPM |
funk | Syncopated funk groove | 90-120 BPM |
ballad | Slow, sustained ballad | 60-90 BPM |
latin | Latin/salsa with clave | 100-130 BPM |
waltz | 3/4 waltz pattern | 90-150 BPM |
blues | Blues shuffle | 80-120 BPM |
Parse and play iReal Pro chord charts:
from accompy import Score, generate_accompaniment
# From iReal Pro URL (copy from app or forum)
url = "irealb://6dim=Composer%20Unknown==Medium%20Swing=C=7=1r34LbKcu7QyX%2DA6XyQ%7C%23G%7CQyXG%2F6C%7CQyXFo%7CQyXE%2F6C%7CQyXoDoXyQ%7CC44T%7BXQyXQQ%7DXyQQyXQyXQyXQyXQyQXyXQyXQyXQyXQyXXyQXyyXoB%7CQyXQyXyQXyyXQyXQyXQyXQyXyQXQyXQyXQyXQyXQQXyQXQyXQyyXQyXQXyQXXQyXQyXQyXQyXQXyQyXQyXQyXQyXQyyQXyQyXQyXQXyQXyQXyQXyQXyQ%20Z%20=Jazz%2DMedium%20Up%20Swing%202=118=21]6dim"
score = Score.from_ireal_url(url)
# Generate backing track
audio = generate_accompaniment(score, style="swing", tempo=140)
Note: pyRealParser is optional. If installed, Score.from_ireal_url(...) will use it.
If not installed, accompy uses a best-effort built-in parser.
Full control with AccompanimentConfig:
from accompy import AccompanimentConfig, generate_accompaniment
config = AccompanimentConfig(
style="swing",
tempo=160,
repeats=4,
instruments={
"drums": True,
"bass": True,
"piano": True,
"guitar": False,
},
volumes={
"drums": 0.7,
"bass": 1.0,
"piano": 0.6,
},
sample_rate=44100,
output_format="mp3", # wav, mp3, flac, midi
)
audio = generate_accompaniment(
"| Dm7 | G7 | Cmaj7 | A7 |", config=config, output_path="backing_track.mp3"
)
For simple chord renderings (no drums or bass — just restriking chords with a
rhythmic feel), use rhythm_to_midi or rhythm_to_audio:
from accompy import rhythm_to_midi, rhythm_to_audio
# Tresillo feel (1.5 + 1.5 + 1 beats)
midi = rhythm_to_midi("| Dm7 | G7 | Cmaj7 |", skeleton="tresillo", tempo=120)
midi.write("tresillo.mid")
# Whole notes (one strike per measure) — the default
midi = rhythm_to_midi("| C | Am | F | G |")
# Charleston, bossa, waltz, and 25+ other built-in skeletons
midi = rhythm_to_midi("| C | Am | F | G |", skeleton="charleston")
# Or pass a raw duration tuple
midi = rhythm_to_midi("| C | Am |", skeleton=(1.5, 0.5, 1.5, 0.5))
# Render all the way to audio
audio = rhythm_to_audio("| C | Am |", skeleton="half_notes", tempo=100)
A rhythmic skeleton is just a tuple of durations that sum to the measure length —
e.g. (1.5, 1.5, 1) says "hit, hold for a dotted quarter; hit again, hold for
another dotted quarter; hit once more, hold for a quarter." No pitches, no
voicings, no velocities. The skeleton defines when you strike within a measure;
the chord progression defines what you play.
from accompy import resolve_skeleton, list_skeletons, register_skeleton
# Resolve by key, name, or style
resolve_skeleton("tresillo") # (1.5, 1.5, 1)
resolve_skeleton("Dotted half + quarter") # (3, 1)
resolve_skeleton("reggae") # picks first skeleton for that style
# List available skeletons
list_skeletons() # all keys
list_skeletons(beats_per_measure=3) # waltz-family only
list_skeletons(style="jazz") # jazz-associated skeletons
# Register your own
register_skeleton("my_groove", (1, 0.5, 0.5, 2), name="My Groove", styles=["custom"])
New in v0.2.0: accompy now supports extensive customization through a protocol-based architecture.
Provide your own chord-to-notes resolver:
from accompy import set_chord_resolver, generate_accompaniment
def jazz_voicing_resolver(symbol: str) -> list[int]:
"""Custom jazz voicings with rootless chords, tensions, etc."""
# Your custom chord resolution logic
# Return list of MIDI note numbers
return [55, 59, 62, 65, 69] # Example: Dm9 voicing
# Set as default resolver
set_chord_resolver(jazz_voicing_resolver)
# Now all accompaniments use your custom voicings
audio = generate_accompaniment("| Dm7 | G7 | Cmaj7 |")
Register your own accompaniment patterns:
from accompy import (
register_style,
DrumPattern,
BassPattern,
DrumHit,
NoteEvent,
KICK,
SNARE,
CLOSED_HIHAT,
)
# Define custom drum pattern
my_drum = DrumPattern(
name="my_funk",
beats_per_bar=4,
hits=[
DrumHit(0, KICK, 110),
DrumHit(0.75, KICK, 85),
DrumHit(1, SNARE, 105),
DrumHit(2, KICK, 110),
DrumHit(3, SNARE, 105),
# ... more hits
],
)
# Define custom bass pattern
my_bass = BassPattern(
name="my_funk",
notes=[
NoteEvent(0, 0, 0.4, 110), # Root, short
NoteEvent(0.75, 0, 0.2, 80), # Root, ghostNote
NoteEvent(1.5, 7, 0.3, 90), # 5th
# ... more notes
],
)
# Register the custom style
register_style("my_funk", drums=[my_drum], bass=[my_bass], comp=[])
# Use it
audio = generate_accompaniment("| C7 | F7 | C7 | G7 |", style="my_funk")
The pattern registry is a MutableMapping, making it Pythonic and extensible:
from accompy import get_pattern_registry
registry = get_pattern_registry()
# Check available styles
print(registry.available_styles()) # ['swing', 'bossa', 'rock', ...]
# Access patterns like a dict
swing_patterns = registry["swing"]
print(swing_patterns["drums"]) # List of DrumPattern objects
# Add/modify styles at runtime
registry["custom_groove"] = {
"drums": [custom_drum_pattern],
"bass": [custom_bass_pattern],
"comp": [custom_comp_pattern],
}
For advanced use cases (future integration with hum/pyo for real-time synthesis):
from accompy import RealtimeAccompaniment, AccompanimentConfig
# Create real-time player
config = AccompanimentConfig(tempo=120, style="swing")
player = RealtimeAccompaniment(config)
# Set chord progression
player.set_chords("| Dm7 | G7 | Cmaj7 | A7 |")
# Get event iterator (for future real-time playback)
for event in player.events():
# event.time, event.note, event.velocity, event.duration
# Future: send to real-time synth like hum/pyo
pass
All major components implement protocols for maximum flexibility:
from accompy.protocols import ChordResolver, PatternSource, SynthesizerBackend
# Implement custom components that satisfy these protocols
# See accompy/protocols.py for full definitions
# Example: Custom synthesis backend
class MySynthBackend(SynthesizerBackend):
def render_to_file(self, midi_path, output_path, *, sample_rate=44100):
# Your custom synthesis logic (e.g., using hum, pyo, etc.)
pass
@classmethod
def is_available(cls):
return True # Check if dependencies are available
# Use with dependency injection
from accompy import AccompanimentConfig, generate_accompaniment
config = AccompanimentConfig(synthesis_backend=MySynthBackend())
audio = generate_accompaniment("| C | G | Am | F |", config=config)
# Generate backing track
python -m accompy "| C | Am | F | G |" -s bossa -t 120 -o track.wav
# Check dependencies
python -m accompy --check-deps
# Options
python -m accompy --help
Score object with normalized chord symbolsFor the best quality backing tracks, install MMA:
# MMA provides 50+ professionally designed grooves
git clone https://github.com/infojunkie/mma
cd mma && python install # Follow MMA installation instructions
MMA offers extensive groove libraries with realistic fills, variations, and transitions.
Required:
midiutil — MIDI file generationRecommended:
mingus — Music theory (better chord parsing)pyRealParser — iReal Pro URL parsingmidi2audio — Optional Python wrapper for FluidSynth (can improve portability)pydub — Audio format conversion (if no ffmpeg)pip install midiutil mingus pyRealParser midi2audio pydub
generate_accompaniment(chords, *, style, tempo, repeats, output_path, output_format, config, use_mma, backend)Generate accompaniment audio from chord progression.
Parameters:
chords (str | Score | Iterable[tuple[str, int|float]] | Iterable[str] | list[list[str]]): Chord progressionstyle (str): "swing", "bossa", "rock", "ballad", "funk", "latin", "waltz", "blues"tempo (int): BPM (default: 120)repeats (int): Number of times through the form (default: 2)output_path (str | Path): Where to save audio (default: temp file)output_format (str | None): "wav", "mp3", "flac", "midi" (optional; inferred from output_path)config (AccompanimentConfig): Full config (overrides style/tempo/repeats)use_mma (bool): Backwards-compatible MMA toggle (default: True)backend (str | None): "auto", "mma", "builtin" (optional)Returns: Path to generated audio file
Score.from_string(chord_string, *, title, key, time_signature)Parse chord string into Score object.
Score.from_ireal_url(url)Parse iReal Pro URL into Score object.
check_dependencies()Returns dict of available dependencies.
print_setup_instructions()Print installation instructions for missing dependencies.
Problem: The generated audio plays but sounds robotic, hollow, or like cheap ringtones from 2003.
Cause: You're using a tiny placeholder SoundFont. FluidSynth ships with
VintageDreamsWaves-v2.sf2 (~300 KB), which is made of basic sine waves — not
real instrument samples. This is the #1 reason accompy output sounds bad.
How to check:
ls -lh ~/.fluidsynth/default_sound_font.sf2
# If it's under 1 MB, that's your problem.
Fix — install a real SoundFont:
# Download MuseScore General (~200 MB, real sampled instruments)
curl -L -o ~/.fluidsynth/default_sound_font.sf2 \
"https://ftp.osuosl.org/pub/musescore/soundfont/MuseScore_General/MuseScore_General.sf2"
A proper SoundFont should be 30–200+ MB. See the SoundFont section above for more options, or browse https://musical-artifacts.com/artifacts?formats=sf2.
Tip: If you're using an AI coding agent (Claude Code, Cursor, etc.), just tell it: "the accompy output sounds bad, find and install a good SoundFont" — it can diagnose and fix this in seconds.
Problem: RuntimeError: No SoundFont found. Download FluidR3_GM.sf2 and place in ~/.fluidsynth/default_sound_font.sf2
Solutions:
Automated fix:
python -c "from accompy import setup_soundfont; setup_soundfont()"
Check if FluidSynth includes a SoundFont:
# macOS with Homebrew
find /opt/homebrew/Cellar/fluid-synth -name "*.sf2"
If found, run the automated setup to link it.
Manual download:
mkdir -p ~/.fluidsynth
# Download MuseScore General (high quality, 35MB)
curl -L -o ~/.fluidsynth/default_sound_font.sf2 \
"https://ftp.osuosl.org/pub/musescore/soundfont/MuseScore_General/MuseScore_General.sf2"
Verify the file:
ls -lh ~/.fluidsynth/default_sound_font.sf2
# Should be several MB (not just a few bytes)
Problem: RuntimeError: FluidSynth not found. Install with: brew install fluidsynth
Solutions:
macOS:
brew install fluid-synth
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install fluidsynth
Windows: Download from FluidSynth releases and add to PATH
Verify installation:
which fluidsynth
fluidsynth --version
Problem: Downloaded SoundFont is tiny (255 bytes) and appears to be XML
Cause: Download URL redirected to an error page
Solution:
# Remove corrupted file
rm ~/.fluidsynth/default_sound_font.sf2
# Use automated setup
python -c "from accompy import setup_soundfont; setup_soundfont(force=True)"
# Or manually download from a reliable source
curl -L -o ~/.fluidsynth/default_sound_font.sf2 \
"https://ftp.osuosl.org/pub/musescore/soundfont/MuseScore_General/MuseScore_General.sf2"
Problem: Warning on import: accompy setup incomplete - missing: fluidsynth, soundfont
Solutions:
Run setup:
python -c "from accompy import verify_and_setup; verify_and_setup()"
Temporarily disable warning:
export ACCOMPY_SKIP_SETUP_CHECK=1
python your_script.py
Fix manually and verify:
# Install dependencies
brew install fluid-synth # or apt-get install fluidsynth
pip install midiutil
# Run diagnostic
python -c "from accompy import print_diagnostic_report; print_diagnostic_report()"
Problem: Missing Python dependency
Solution:
pip install midiutil mingus
Get a comprehensive report of your setup:
from accompy import print_diagnostic_report
print_diagnostic_report()
This shows:
from accompy import diagnose_issues
for issue, description, solution in diagnose_issues():
print(f"Issue: {issue}")
print(f"Description: {description}")
print(f"Solution: {solution}\n")
from accompy import check_dependencies
deps = check_dependencies()
print(f"FluidSynth: {deps['fluidsynth']}")
print(f"SoundFont: {deps['soundfont']}")
print(f"midiutil: {deps['midiutil']}")
print(f"mingus: {deps['mingus']}")
print(f"MMA: {deps['mma']}")
If you're still having issues:
Run diagnostic report and save output:
python -c "from accompy import print_diagnostic_report; print_diagnostic_report()" > diagnostic.txt
Check existing issues: GitHub Issues
Create a new issue with:
Contributions welcome! Areas of interest:
Two skills ship with this repository. Their real files live in skills/, with a
relative symlink per skill in .claude/skills/ so Claude Code finds them too —
gh skill discovers non-hidden skills/ directories and skips hidden ones, so
one location plus a symlink serves both.
| Skill | What it covers |
|---|---|
accompy-patterns | The conventions this codebase follows |
user-data-folder | Where an app's user data lives (XDG paths, config2py.AppData, seeding from package data) |
gh skill install thorwhalen/accompy user-data-folder
MIT
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.3.13 | 20 Oct 2026 | 19 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 17:59 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:135a15c7263345c232c0be576f99e0ae25d43d4f4e4fdfd4b3c045e68ad0efdc