Fretboard string lists and Fingering positions/string-names now read
low-to-high (lowest-pitched string first), matching how chord diagrams
and tablature are conventionally written. Pass high_to_low=True to any
fretboard constructor to restore the pre-0.43 high-to-low behavior.
Design: each board keeps a private canonical (high-to-low) tone store
so the fingering scorer and GUITAR_OVERRIDES table stay untouched; a
single _orient() helper re-orients at the user-facing boundary (and,
being self-inverse, also canonicalizes custom tuning/position input).
Fingering carries its own orientation flag and presents oriented
positions/names via properties. The fingering cache key now includes
orientation so the two orderings don't collide.
to_tab() and Part.strum() now sort by pitch internally, so their output
is identical regardless of board orientation.
- All 25 instrument presets gain a high_to_low param, routed through a
canonical build path.
- Tests updated for the new default; added orientation-specific tests.
- Docs/examples flipped to low-to-high; chord_charts.py example now uses
the built-in Fingering.tab() instead of a hand-rolled renderer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- chords: open_voicing() alongside other voicings, normal_form() in
pitch class sets section
- tones: is_natural, is_sharp, is_flat accidental properties
- scales: Key.seventh() for individual degrees, expanded
Scale.recommend() explanation of how ranking works
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New documentation section covering the Nashville number system,
blues scale theory, and tablature export — topics that were
previously scattered across cookbook and fretboard docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
to_tab(tuning=Fretboard.guitar()) now works, along with bass,
ukulele, mandolin, banjo, and any custom Fretboard with capo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three new export methods on Score:
- to_lilypond() — complete LilyPond source files for PDF engraving
- to_musicxml() — MusicXML 4.0 for MuseScore/Sibelius/Finale
- to_tab() — ASCII guitar/bass tablature (also on Part)
All three handle multi-part scores, bass clef detection, tied notes
across barlines, chords, and drum tone filtering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Notes longer than one measure are split into tied pieces so abcjs
can render them correctly (e.g. 16-beat choir drone becomes four
tied whole notes).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parts with only drum tones or rests are excluded from ABC output.
Chords correctly recognized as pitched content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New method converts scores to ABC notation with support for multi-voice,
chords, rests, accidentals, and all durations. Pass html=True for a
self-contained HTML page with abcjs sheet music rendering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supports multi-voice scores, chords, rests, accidentals, and all durations.
Pass html=True to get a self-contained page using abcjs for sheet music.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Five new synth waveforms: tape-replay Mellotron (strings/flute/choir
tapes with wow, flutter, saturation, 8s fadeout), hard sync oscillator,
ring modulation, wavefolding, and analog drift VCO with pitch
instability. 14 new instrument presets for Score.part(). Synth kwargs
now pass through play()/save()/_render(). 808 bass envelope fixed
from pluck to piano.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Note.beats now returns 0.0 for held notes (_hold=True), matching the
renderer which already skipped advancing the beat position. Previously
every hold() call added its full duration to the part's total, causing
duration reports to be 2-3x too long on tracks with drone notes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single tanh was too mild. Now chains preamp gain → power amp clip →
asymmetric rectifier sag for proper overdrive/fuzz character.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Threshold 0.5 → 0.7 so more dynamics survive. Makeup gain capped
at 3x so sparse arrangements (solo singing bowl, etc.) don't get
over-amplified to clipping.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two variants modeling Himalayan singing bowl acoustics:
- Strike: mallet hit with chirp from inharmonic partials, long ring
- Ring: rim-rubbed sustained tone with slow build and beating modes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reverted all frequency/harmonic changes — original thump, metal, sub,
click all restored. Only change from original: envelope uses exp(-0.8*t)
instead of _exp_decay(n_samples, 6) so the sweep sustains long enough
to be audible. Dispatch override for sound_value 108 kept to bypass
stale closure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dispatch dict lambda was holding a stale function reference that
survived module reloads. Added explicit override for sound_value 108
to always call the current _synth_tabla_ge_bend directly.
Synth improvements:
- Sweep: 50→450Hz with slow exp(-1.5t) so ear tracks the bend
- Sustain: exp(-0.8t) envelope gives ~1.2s of audible signal
- Removed static sub/metal that masked the sweep
- Added 2nd harmonic for richness
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced _exp_decay (which uses sample-count-based decay and was too fast)
with a direct time-based exponential: exp(-0.8*t) giving ~1.2s of signal.
The sweep from 50→450Hz is now actually hearable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Sweep range: 50→450Hz (was 60→240Hz)
- Slower sweep rate: exp(-1.5t) so ear can track it (was -4t)
- Longer sustain: decay rate 2.5 (was 6) — bend lives long enough to hear
- Removed static sub and metal that masked the sweep
- Added 2nd harmonic for richness
- Louder body (1.2 gain)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pytheory-live is now a proper command after pip install pytheory[live].
TUI moved to pytheory/live_tui.py, registered as console script.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tracks held keys. OS keyboard repeat is ignored (same key refreshes
timer). Note releases 150ms after last repeat stops — approximates
key-up detection. All notes released on Esc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Piano-style layout across the full QWERTY keyboard:
- Bottom rows (ZXCVBNM + ASDFGHJKL): lower octave, white+black keys
- Top rows (QWERTYUIOP + 1234567890): upper octave, white+black keys
- Every letter mapped, ~3.5 octaves total
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MIDI port is now optional. If no device found or port fails,
the audio stream still starts so keyboard mode works. Cleanup
handles missing MIDI gracefully.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Keyboard mode is now a proper modal: kbd enters, Esc exits
- All keys go to MIDI while in keyboard mode, Up/Down change octave
- Header shows KBD and REC indicators
- VU meters use ASCII-safe characters
- play_recording.py: render MIDI through full engine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Tab completes commands, instruments, patterns, fx params
- Left/Right arrows move cursor, insert mid-line
- Home/Ctrl-A, End/Ctrl-E for jump to start/end
- fx <ch> <param> <val> for live effect tweaking
- fx alone lists all params, fx <ch> shows current values
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_Channel now stores and applies: chorus, detune, distortion,
saturation, tremolo, analog, delay, phaser, sub_osc, noise_mix.
TUI fx command: fx <ch> <param> <val> to tweak any effect live.
fx alone lists all available params. fx <ch> shows current values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>