mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 23:00:20 +00:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7883c978f7 | |||
| 36d558573c | |||
| 1e2f09e2ab | |||
| 9404afc1f3 | |||
| 72aa097552 | |||
| 5ebf0bdd97 | |||
| 1d897c6609 | |||
| 4113aad5d0 | |||
| 6ecef688e1 | |||
| fcc5db8e3d | |||
| 9de113b6e7 | |||
| 0b98f7bd77 | |||
| e0a1ce9d18 | |||
| de7575fe0a | |||
| 665a6f5de5 | |||
| 63362df697 | |||
| 755b33a63b | |||
| 40901d603d | |||
| 9b3cbd9065 | |||
| 0911947971 | |||
| c2f748d5f3 | |||
| 7a6942c8e4 | |||
| db7fabf985 | |||
| a07b7e7cea | |||
| 7245cd0e51 | |||
| 9e85a48d0e | |||
| 95b7bd830c | |||
| 150c57ed3d | |||
| d35d2b12f3 | |||
| 2084473788 | |||
| 970c730012 | |||
| 5f94e1939b | |||
| b649b2e659 | |||
| ed6ba2ab9f | |||
| fd317f9cfd | |||
| c57e29fe28 | |||
| 938024bfa2 | |||
| acc92f9a60 | |||
| 0d340dad30 | |||
| 1762500108 | |||
| ac2801d07d | |||
| c49ec27b1b | |||
| 5f4070c4a7 | |||
| 8735393aaa | |||
| 12f15d5138 | |||
| 20fc5e40b8 | |||
| 91d16595b7 | |||
| 54659d39b1 | |||
| 7cb2c166f9 | |||
| ba2038d7ff | |||
| 198fded20e | |||
| 51159e309a | |||
| 54df949089 | |||
| 30c70da468 | |||
| c633bd6f61 | |||
| bc652c37d0 | |||
| 417d9a6908 | |||
| f7d8f08446 | |||
| b8d1fe5e81 | |||
| 2612444146 | |||
| 48a954d063 | |||
| 8a07be23e6 | |||
| d771117d5c | |||
| 1ae9404f07 | |||
| 2e5b18de2e | |||
| 248594fb21 | |||
| 3ce890c54c | |||
| 499c49b6eb | |||
| f85504b456 | |||
| d0624f8b78 | |||
| fb37a7c27b | |||
| fb36e75a42 | |||
| 81b54d2394 | |||
| fa6a3090cb | |||
| 58286ddb69 | |||
| 6d137be9f5 | |||
| 383802a1e1 | |||
| 7f2aeb2395 | |||
| 16b4c7d1fa | |||
| b9ee5c9cde | |||
| 7375d58209 | |||
| 6316a6c910 | |||
| 237cfe171c | |||
| 1751f97617 | |||
| 943a12b3bb | |||
| 04d2de3e70 | |||
| ead42751ef | |||
| 8dee0d00d8 | |||
| de112e0d9f | |||
| f469ad90f8 | |||
| bab7f39304 | |||
| 62557ba534 | |||
| 8b50a9c325 | |||
| 7e9caac70b |
@@ -7,3 +7,4 @@ t2.py
|
||||
__pycache__
|
||||
pytheory.egg-info
|
||||
docs/_build
|
||||
.claude/worktrees/
|
||||
|
||||
+149
@@ -2,6 +2,155 @@
|
||||
|
||||
All notable changes to PyTheory are documented here.
|
||||
|
||||
## 0.42.1
|
||||
|
||||
- **Fretboard tuning support** — `to_tab()` now accepts `Fretboard` objects as
|
||||
the `tuning` parameter. Works with `Fretboard.guitar()`, `Fretboard.bass()`,
|
||||
`Fretboard.ukulele()`, `Fretboard.mandolin()`, `Fretboard.banjo()`, and any
|
||||
custom Fretboard with capo.
|
||||
|
||||
## 0.42.0
|
||||
|
||||
- **LilyPond export** — `Score.to_lilypond()` generates complete LilyPond source
|
||||
files with multi-staff scores, key/time signatures, tempo markings, and
|
||||
automatic bass clef detection. Output can be compiled to publication-quality
|
||||
PDFs with LilyPond.
|
||||
- **MusicXML export** — `Score.to_musicxml()` generates MusicXML 4.0 documents
|
||||
that can be opened in MuseScore, Sibelius, Finale, and any notation software.
|
||||
Includes proper ties, chords, clef detection, and tempo/time signature metadata.
|
||||
- **Guitar/bass tablature** — `Part.to_tab()` and `Score.to_tab()` generate ASCII
|
||||
tablature. Supports guitar (6-string), bass (4-string), drop D, and custom
|
||||
tunings. Automatically maps notes to the best string/fret positions.
|
||||
|
||||
## 0.41.4
|
||||
|
||||
- **Fix** — `to_abc()` now ties long notes across barlines instead of emitting
|
||||
oversized durations that abcjs can't render (e.g. 16-beat notes become four
|
||||
tied whole notes).
|
||||
|
||||
## 0.41.3
|
||||
|
||||
- **Fix** — `to_abc()` now skips parts with only drum tones or rests (no pitched
|
||||
notes), fixing "pitch is undefined" errors in abcjs. Chords are correctly
|
||||
recognized as pitched content.
|
||||
|
||||
## 0.41.2
|
||||
|
||||
- **Auto bass clef** — `to_abc()` detects low-register parts (808, bass, timpani)
|
||||
and assigns `clef=bass` automatically based on average note octave.
|
||||
|
||||
## 0.41.1
|
||||
|
||||
- **Fix** — `to_abc()` no longer crashes on parts containing drum tones.
|
||||
|
||||
## 0.41.0
|
||||
|
||||
- **ABC notation export** — `Score.to_abc()` converts scores to ABC notation
|
||||
strings. Supports multi-voice scores (via `V:` directives), chords, rests,
|
||||
accidentals, and all standard durations. Pass `html=True` to get a
|
||||
self-contained HTML page that renders sheet music in the browser via abcjs.
|
||||
|
||||
## 0.40.9
|
||||
|
||||
- **Mellotron synth** — tape-replay keyboard with wow/flutter, tape saturation,
|
||||
bandwidth limiting, hiss, and 8-second tape fadeout. Three tape banks via the
|
||||
`tape` parameter: `"strings"` (default), `"flute"`, and `"choir"`.
|
||||
- **Analog oscillator synths** — four new waveform generators for fat, alive,
|
||||
analog-style sounds:
|
||||
- `Synth.HARD_SYNC` — slave oscillator hard-synced to a master (Prophet-5
|
||||
leads). `slave_ratio` parameter controls harmonic content.
|
||||
- `Synth.RING_MOD` — two oscillators multiplied for metallic, bell-like
|
||||
inharmonic tones. `mod_ratio` parameter.
|
||||
- `Synth.WAVEFOLD` — west coast wavefolding (Buchla-style). `folds` parameter
|
||||
sweeps from warm to gnarly.
|
||||
- `Synth.DRIFT` — analog VCO with pitch drift, jitter, and noise floor.
|
||||
`shape` parameter (`"saw"`, `"square"`, `"triangle"`, `"pulse"`) and
|
||||
`drift_amount` for instability level.
|
||||
- **Synth kwargs passthrough** — `play()`, `save()`, and `_render()` now accept
|
||||
`**synth_kw` for forwarding parameters to synth wave functions (e.g.
|
||||
`play(tone, synth=Synth.MELLOTRON, tape="choir")`).
|
||||
- **14 new instrument presets** — `mellotron`, `mellotron_strings`,
|
||||
`mellotron_flute`, `mellotron_choir`, `sync_lead`, `sync_lead_bright`,
|
||||
`ring_mod_bell`, `ring_mod_metallic`, `wavefold_warm`, `wavefold_gnarly`,
|
||||
`drift_saw`, `drift_square`, `analog_pad`, `analog_bass`.
|
||||
- **808 bass envelope fix** — changed from `pluck` (zero sustain, wrong for 808)
|
||||
to `piano` (sharp attack with long decay tail).
|
||||
|
||||
## 0.40.8
|
||||
|
||||
- **Fix hold() inflating duration** — `Note.beats` was returning the full
|
||||
duration for held notes (`_hold=True`), causing `Part.total_beats` and
|
||||
`Score.duration_ms` to overcount. A part with `hold(Sa, WHOLE * 4)` followed
|
||||
by `add(Pa, QUARTER)` would report 17 beats instead of 1. Now held notes
|
||||
return 0 beats, matching the renderer which already skipped advancing the
|
||||
timeline for held notes.
|
||||
|
||||
## 0.40.7
|
||||
|
||||
- **Expose missing Synth enum entries** — rhodes, wurlitzer, vibraphone,
|
||||
pipe organ, and choir wave functions were already implemented but not
|
||||
accessible via the Synth enum. Now available as `Synth.RHODES`,
|
||||
`Synth.WURLITZER`, `Synth.VIBRAPHONE`, `Synth.PIPE_ORGAN`, `Synth.CHOIR`.
|
||||
|
||||
## 0.40.6
|
||||
|
||||
- **Saxophone presets cleaned up** — removed lowpass filters and vel_to_filter
|
||||
from all sax instrument presets (saxophone, alto_sax, tenor_sax, bari_sax).
|
||||
The saxophone wave function already shapes its own spectrum; the extra
|
||||
filters were dulling the tone.
|
||||
|
||||
## 0.40.5
|
||||
|
||||
- **Saxophone synth overhaul** — reed nonlinearity (asymmetric soft clipping),
|
||||
conical bore formant resonances, breath noise with attack envelope, separate
|
||||
reed buzz, key click transient, and sub-harmonic warmth. Vibrato dialed back
|
||||
to subtle, delayed onset.
|
||||
|
||||
## 0.40.4
|
||||
|
||||
- **Distortion overhaul** — multi-stage clipping (preamp → power amp →
|
||||
asymmetric rectifier) replaces single-stage tanh. Crunch, distorted,
|
||||
orange crunch, and metal guitar presets now sound properly driven.
|
||||
|
||||
## 0.40.3
|
||||
|
||||
- **Crotales synth** — tuned bronze discs with long ring and bright harmonics
|
||||
- **Tingsha synth** — paired Tibetan cymbals with beating from two detuned discs
|
||||
- **Rain stick** — cascading pebbles (steep and slow/shallow variants)
|
||||
- **Ocean drum** — steel beads rolling inside a frame drum, surf wash
|
||||
- **Cabasa** — metal bead chain on cylinder, bright metallic scrape
|
||||
- **Wind chimes** — multiple suspended metal tubes ringing at random offsets
|
||||
- **Finger cymbal** — single zill tap, bright metallic ping
|
||||
- `crotales`, `tingsha`, `singing_bowl`, `singing_bowl_ring` instrument presets
|
||||
- Audio demos in docs for all new sounds
|
||||
|
||||
## 0.40.2
|
||||
|
||||
- **Master compressor dialed back** — threshold raised from 0.5 to 0.7,
|
||||
makeup gain capped at 3x. Sparse arrangements no longer get
|
||||
over-amplified to clipping.
|
||||
|
||||
## 0.40.1
|
||||
|
||||
- **Singing bowl synth** — two variants: strike (mallet hit with chirp
|
||||
and long decay) and ring (rim-rubbed sustained tone with slow build).
|
||||
Inharmonic partials beat against near-degenerate mode pairs for
|
||||
authentic Himalayan bowl shimmer.
|
||||
- `singing_bowl` and `singing_bowl_ring` instrument presets
|
||||
- Audio demos in docs for both variants
|
||||
|
||||
## 0.40.0
|
||||
|
||||
- **Rhodes electric piano synth** — tine + tonebar + electromagnetic
|
||||
pickup model. `electric_piano` preset now uses dedicated `rhodes_synth`
|
||||
instead of FM
|
||||
- **73 audio demos in docs** — every synth, every drum pattern, every
|
||||
code example with `play_score()` now has an embedded audio player
|
||||
- Idiomatic demos: harp arpeggiates, guitars strum, cello bows, sitar
|
||||
drones, strings use ensemble
|
||||
- Trailing silence trimming on all audio exports
|
||||
- Raw waveform demos (no envelope) for classic waveforms
|
||||
|
||||
## 0.39.3
|
||||
|
||||
- **33 audio samples in documentation** — every `play_score()` example
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
"""Demo the 5 new synths: Mellotron, Hard Sync, Ring Mod, Wavefold, Drift.
|
||||
|
||||
Each synth gets a short musical phrase — not just a scale run — with
|
||||
reverb and rhythmic variety to show off its character.
|
||||
"""
|
||||
from pytheory import Score, Duration, play_score
|
||||
|
||||
EIGHTH = Duration.EIGHTH
|
||||
QUARTER = Duration.QUARTER
|
||||
HALF = Duration.HALF
|
||||
DOTTED_Q = Duration.DOTTED_QUARTER
|
||||
WHOLE = Duration.WHOLE
|
||||
|
||||
|
||||
# ── Mellotron Strings ────────────────────────────────────────────────────────
|
||||
# Strawberry Fields vibes — slow, haunted, with rests that breathe.
|
||||
|
||||
print("=== MELLOTRON STRINGS ===")
|
||||
s = Score("4/4", bpm=72)
|
||||
p = s.part("tape", instrument="mellotron_strings",
|
||||
reverb=0.45, reverb_type="cathedral", reverb_decay=2.0)
|
||||
p.add("G4", HALF).add("B4", QUARTER).add("D5", QUARTER)
|
||||
p.add("C5", DOTTED_Q).add("B4", EIGHTH).add("A4", HALF)
|
||||
p.rest(QUARTER)
|
||||
p.add("G4", DOTTED_Q).add("F#4", EIGHTH).add("G4", WHOLE)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Mellotron Flute ──────────────────────────────────────────────────────────
|
||||
# Lonely, breathy, with space between phrases.
|
||||
|
||||
print("\n=== MELLOTRON FLUTE ===")
|
||||
s = Score("3/4", bpm=84)
|
||||
p = s.part("flute", instrument="mellotron_flute",
|
||||
reverb=0.5, reverb_type="taj_mahal", reverb_decay=2.5)
|
||||
p.add("E5", HALF).add("D5", QUARTER)
|
||||
p.add("C5", DOTTED_Q).add("B4", EIGHTH).rest(QUARTER)
|
||||
p.add("A4", HALF).add("G4", QUARTER)
|
||||
p.add("A4", HALF).rest(QUARTER)
|
||||
p.add("E5", QUARTER).add("D5", QUARTER).add("C5", QUARTER)
|
||||
p.add("B4", HALF + QUARTER)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Mellotron Choir ──────────────────────────────────────────────────────────
|
||||
# Ghostly pad — slow chords, big reverb.
|
||||
|
||||
print("\n=== MELLOTRON CHOIR ===")
|
||||
s = Score("4/4", bpm=60)
|
||||
p = s.part("choir", instrument="mellotron_choir",
|
||||
reverb=0.6, reverb_type="cathedral", reverb_decay=3.0)
|
||||
p.add("C4", WHOLE)
|
||||
p.add("E4", HALF).add("G4", HALF)
|
||||
p.add("A4", DOTTED_Q).add("G4", EIGHTH).add("F4", HALF)
|
||||
p.add("E4", WHOLE)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Hard Sync Lead ───────────────────────────────────────────────────────────
|
||||
# Aggressive, punchy — fast 16ths and syncopation.
|
||||
|
||||
print("\n=== HARD SYNC LEAD ===")
|
||||
s = Score("4/4", bpm=128)
|
||||
p = s.part("sync", instrument="sync_lead",
|
||||
reverb=0.25, reverb_type="plate")
|
||||
p.add("E4", EIGHTH).add("E4", EIGHTH).rest(EIGHTH).add("G4", EIGHTH)
|
||||
p.add("A4", QUARTER).add("G4", EIGHTH).add("E4", EIGHTH)
|
||||
p.add("D4", EIGHTH).rest(EIGHTH).add("E4", EIGHTH).add("G4", EIGHTH)
|
||||
p.add("A4", HALF)
|
||||
p.rest(QUARTER).add("B4", EIGHTH).add("A4", EIGHTH)
|
||||
p.add("G4", QUARTER).add("E4", QUARTER).add("D4", HALF)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Hard Sync Bright ─────────────────────────────────────────────────────────
|
||||
# Higher slave ratio — more harmonics, screaming lead.
|
||||
|
||||
print("\n=== HARD SYNC BRIGHT ===")
|
||||
s = Score("4/4", bpm=138)
|
||||
p = s.part("sync2", instrument="sync_lead_bright",
|
||||
reverb=0.2, reverb_type="plate")
|
||||
p.add("A4", EIGHTH).add("C5", EIGHTH).add("D5", QUARTER)
|
||||
p.rest(EIGHTH).add("E5", EIGHTH).add("D5", EIGHTH).add("C5", EIGHTH)
|
||||
p.add("A4", QUARTER).rest(QUARTER).add("G4", EIGHTH).add("A4", EIGHTH)
|
||||
p.add("C5", HALF)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Ring Mod Bell ────────────────────────────────────────────────────────────
|
||||
# Shimmery, metallic — sparse hits with long reverb tail.
|
||||
|
||||
print("\n=== RING MOD BELL ===")
|
||||
s = Score("4/4", bpm=66)
|
||||
p = s.part("bell", instrument="ring_mod_bell",
|
||||
reverb=0.6, reverb_type="cave", reverb_decay=3.0)
|
||||
p.add("C5", HALF).rest(QUARTER).add("G4", QUARTER)
|
||||
p.rest(HALF).add("E5", HALF)
|
||||
p.add("D5", QUARTER).rest(QUARTER).add("C5", HALF)
|
||||
p.rest(WHOLE)
|
||||
p.add("G4", QUARTER).add("A4", QUARTER).add("C5", HALF)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Ring Mod Metallic ────────────────────────────────────────────────────────
|
||||
# Alien, inharmonic — atonal stabs.
|
||||
|
||||
print("\n=== RING MOD METALLIC ===")
|
||||
s = Score("4/4", bpm=100)
|
||||
p = s.part("metal", instrument="ring_mod_metallic",
|
||||
reverb=0.4, reverb_type="parking_garage", reverb_decay=2.0)
|
||||
p.add("F4", EIGHTH).rest(EIGHTH).add("Ab4", EIGHTH).add("F4", EIGHTH)
|
||||
p.rest(QUARTER).add("Db5", QUARTER).rest(QUARTER)
|
||||
p.add("C5", EIGHTH).add("Ab4", EIGHTH).rest(QUARTER).add("F4", HALF)
|
||||
p.rest(HALF).add("Db5", QUARTER).add("C5", QUARTER)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Wavefold Warm ────────────────────────────────────────────────────────────
|
||||
# Gentle folds — round and musical, like a filtered saw with overtones.
|
||||
|
||||
print("\n=== WAVEFOLD WARM ===")
|
||||
s = Score("4/4", bpm=108)
|
||||
p = s.part("fold", instrument="wavefold_warm",
|
||||
reverb=0.3, reverb_type="plate")
|
||||
p.add("A3", QUARTER).add("C4", QUARTER).add("E4", QUARTER).add("A4", QUARTER)
|
||||
p.add("G4", DOTTED_Q).add("E4", EIGHTH).add("C4", HALF)
|
||||
p.add("D4", QUARTER).add("F4", QUARTER).add("A4", HALF)
|
||||
p.add("G4", WHOLE)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Wavefold Gnarly ──────────────────────────────────────────────────────────
|
||||
# Cranked folds — buzzy, aggressive, with syncopation.
|
||||
|
||||
print("\n=== WAVEFOLD GNARLY ===")
|
||||
s = Score("4/4", bpm=130)
|
||||
p = s.part("gnarly", instrument="wavefold_gnarly",
|
||||
reverb=0.2, reverb_type="spring")
|
||||
p.add("E3", EIGHTH).add("E3", EIGHTH).rest(EIGHTH).add("G3", EIGHTH)
|
||||
p.add("A3", EIGHTH).rest(EIGHTH).add("B3", EIGHTH).add("A3", EIGHTH)
|
||||
p.add("E3", QUARTER).add("G3", EIGHTH).add("A3", EIGHTH).add("B3", QUARTER)
|
||||
p.rest(QUARTER)
|
||||
p.add("E4", EIGHTH).add("D4", EIGHTH).add("B3", QUARTER).add("A3", HALF)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Drift Saw ────────────────────────────────────────────────────────────────
|
||||
# Warm, alive analog saw — the Minimoog pad.
|
||||
|
||||
print("\n=== DRIFT SAW (vintage VCO) ===")
|
||||
s = Score("4/4", bpm=88)
|
||||
p = s.part("drift", instrument="drift_saw",
|
||||
reverb=0.35, reverb_type="taj_mahal", reverb_decay=2.0)
|
||||
p.add("D4", HALF).add("F4", HALF)
|
||||
p.add("A4", DOTTED_Q).add("G4", EIGHTH).add("F4", QUARTER).rest(QUARTER)
|
||||
p.add("D4", QUARTER).add("E4", QUARTER).add("F4", HALF)
|
||||
p.add("D4", WHOLE)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Drift Square ─────────────────────────────────────────────────────────────
|
||||
# Hollow, wobbly — 8-bit with analog soul.
|
||||
|
||||
print("\n=== DRIFT SQUARE ===")
|
||||
s = Score("4/4", bpm=110)
|
||||
p = s.part("dsq", instrument="drift_square",
|
||||
reverb=0.25, reverb_type="plate")
|
||||
p.add("C4", EIGHTH).add("E4", EIGHTH).add("G4", QUARTER).add("E4", QUARTER)
|
||||
p.rest(QUARTER)
|
||||
p.add("A4", EIGHTH).add("G4", EIGHTH).add("E4", QUARTER).add("C4", HALF)
|
||||
p.add("D4", QUARTER).add("F4", EIGHTH).add("G4", EIGHTH).add("A4", HALF)
|
||||
p.add("G4", WHOLE)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Analog Pad ───────────────────────────────────────────────────────────────
|
||||
# Slow, drifting chords — Juno-style lushness.
|
||||
|
||||
print("\n=== ANALOG PAD ===")
|
||||
s = Score("4/4", bpm=70)
|
||||
p = s.part("pad", instrument="analog_pad",
|
||||
reverb=0.5, reverb_type="taj_mahal", reverb_decay=3.0)
|
||||
p.add("A3", WHOLE)
|
||||
p.add("C4", HALF).add("E4", HALF)
|
||||
p.add("F4", WHOLE)
|
||||
p.add("E4", HALF).add("D4", HALF)
|
||||
p.add("C4", WHOLE)
|
||||
play_score(s)
|
||||
|
||||
|
||||
# ── Analog Bass ──────────────────────────────────────────────────────────────
|
||||
# Tight, punchy — Moog bass with filter sweep.
|
||||
|
||||
print("\n=== ANALOG BASS ===")
|
||||
s = Score("4/4", bpm=120)
|
||||
p = s.part("bass", instrument="analog_bass",
|
||||
reverb=0.1, reverb_type="plate")
|
||||
p.add("E2", EIGHTH).add("E2", EIGHTH).rest(EIGHTH).add("G2", EIGHTH)
|
||||
p.add("A2", QUARTER).rest(QUARTER)
|
||||
p.add("E2", EIGHTH).rest(EIGHTH).add("B2", EIGHTH).add("A2", EIGHTH)
|
||||
p.add("G2", QUARTER).add("E2", QUARTER).rest(HALF)
|
||||
p.add("E2", EIGHTH).add("E2", EIGHTH).add("G2", EIGHTH).add("A2", EIGHTH)
|
||||
p.add("B2", QUARTER).add("A2", QUARTER).add("E2", HALF)
|
||||
play_score(s)
|
||||
|
||||
|
||||
print("\nDone!")
|
||||
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+705
-49
@@ -23,7 +23,20 @@ os.makedirs(AUDIO_DIR, exist_ok=True)
|
||||
|
||||
|
||||
def save_wav(buf, path):
|
||||
"""Save a float32 buffer as 16-bit stereo WAV."""
|
||||
"""Save a float32 buffer as 16-bit stereo WAV, trimming trailing silence."""
|
||||
# Trim trailing silence (below -60dB threshold)
|
||||
threshold = 0.001
|
||||
if buf.ndim == 2:
|
||||
amplitude = numpy.abs(buf).max(axis=1)
|
||||
else:
|
||||
amplitude = numpy.abs(buf)
|
||||
# Find last sample above threshold
|
||||
above = numpy.where(amplitude > threshold)[0]
|
||||
if len(above) > 0:
|
||||
# Keep 0.2s of tail after last audible sample for natural decay
|
||||
tail = min(int(SAMPLE_RATE * 0.2), len(buf) - above[-1])
|
||||
buf = buf[:above[-1] + tail]
|
||||
|
||||
# Handle both mono (n,) and stereo (n, 2) buffers
|
||||
if buf.ndim == 1:
|
||||
channels = 1
|
||||
@@ -172,43 +185,74 @@ def gen_tabla():
|
||||
score = Score("4/4", bpm=80)
|
||||
score.drums("teental", repeats=2)
|
||||
score.drums("chakradar", repeats=1)
|
||||
score.set_drum_effects(reverb=0.2)
|
||||
score.set_drum_effects(reverb=0.3, reverb_type="hall")
|
||||
render("tabla", score)
|
||||
|
||||
|
||||
# ── Marching snare ───────────────────────────────────────────────────────
|
||||
|
||||
def gen_metal_blast():
|
||||
score = Score("4/4", bpm=200)
|
||||
score.drums("metal blast", repeats=8, fill="metal cascade", fill_every=4)
|
||||
score = Score("4/4", bpm=190)
|
||||
# Showcase all metal patterns: groove → gallop → triplet fill → blast
|
||||
score.drums("metal groove", repeats=2)
|
||||
score.drums("metal gallop", repeats=4, fill="metal triplet", fill_every=4)
|
||||
score.drums("metal blast", repeats=2, fill="metal cascade", fill_every=2)
|
||||
score.drums("double kick", repeats=2, fill="metal blast", fill_every=2)
|
||||
render("metal_blast", score)
|
||||
|
||||
|
||||
def gen_cajon():
|
||||
score = Score("4/4", bpm=100)
|
||||
score.drums("cajon", repeats=8, fill="cajon flam", fill_every=4)
|
||||
score.set_drum_effects(reverb=0.25, reverb_type="room")
|
||||
render("cajon", score)
|
||||
|
||||
|
||||
def gen_tabla_teental():
|
||||
score = Score("4/4", bpm=160)
|
||||
score.drums("teental", repeats=4)
|
||||
score.set_drum_effects(reverb=0.2)
|
||||
score.drums("teental", repeats=3)
|
||||
score.drums("teental", repeats=1, fill="bayan", fill_every=1)
|
||||
score.set_drum_effects(reverb=0.3, reverb_type="hall")
|
||||
render("tabla_teental", score)
|
||||
|
||||
|
||||
def gen_tabla_keherwa():
|
||||
score = Score("4/4", bpm=180)
|
||||
score.drums("keherwa", repeats=4, fill="chakkardar", fill_every=4)
|
||||
score.set_drum_effects(reverb=0.2)
|
||||
# Manual part so we can add ge_bend hits
|
||||
tabla = score.part("tabla", synth="sine", volume=0.5, reverb=0.3, reverb_type="hall")
|
||||
DHA = DrumSound.TABLA_DHA
|
||||
NA = DrumSound.TABLA_NA
|
||||
TIN = DrumSound.TABLA_TIN
|
||||
TIT = DrumSound.TABLA_TIT
|
||||
GE = DrumSound.TABLA_GE
|
||||
GB = DrumSound.TABLA_GE_BEND
|
||||
# Keherwa with ge_bend accents
|
||||
for _ in range(3):
|
||||
tabla.hit(DHA, Duration.EIGHTH, velocity=90, articulation="accent")
|
||||
tabla.hit(GE, Duration.EIGHTH, velocity=65)
|
||||
tabla.hit(NA, Duration.EIGHTH, velocity=72)
|
||||
tabla.hit(TIT, Duration.EIGHTH, velocity=45)
|
||||
tabla.hit(NA, Duration.EIGHTH, velocity=68)
|
||||
tabla.hit(TIT, Duration.EIGHTH, velocity=42)
|
||||
tabla.hit(DHA, Duration.EIGHTH, velocity=85, articulation="accent")
|
||||
tabla.hit(NA, Duration.EIGHTH, velocity=70)
|
||||
# Last bar with bayan bends
|
||||
tabla.hit(DHA, Duration.EIGHTH, velocity=95, articulation="marcato")
|
||||
tabla.hit(GB, Duration.EIGHTH, velocity=80)
|
||||
tabla.hit(NA, Duration.EIGHTH, velocity=72)
|
||||
tabla.hit(GB, Duration.EIGHTH, velocity=82)
|
||||
tabla.hit(DHA, Duration.EIGHTH, velocity=100, articulation="accent")
|
||||
tabla.hit(GB, Duration.EIGHTH, velocity=85)
|
||||
tabla.hit(DHA, Duration.QUARTER, velocity=110, articulation="fermata")
|
||||
render("tabla_keherwa", score)
|
||||
|
||||
|
||||
def gen_tabla_chakradar():
|
||||
score = Score("4/4", bpm=200)
|
||||
score.drums("teental", repeats=2)
|
||||
score.drums("teental", repeats=1)
|
||||
score.drums("teental", repeats=1, fill="bayan", fill_every=1)
|
||||
score.drums("chakradar", repeats=1)
|
||||
score.set_drum_effects(reverb=0.2)
|
||||
score.set_drum_effects(reverb=0.3, reverb_type="hall")
|
||||
render("tabla_chakradar", score)
|
||||
|
||||
|
||||
@@ -232,31 +276,120 @@ def gen_mridangam():
|
||||
|
||||
def gen_march_snare():
|
||||
score = Score("4/4", bpm=120)
|
||||
p = score.part("snare", synth="sine", volume=0.8, reverb=0.15)
|
||||
|
||||
S = DrumSound.MARCH_SNARE
|
||||
R = DrumSound.MARCH_RIMSHOT
|
||||
C = DrumSound.MARCH_CLICK
|
||||
Q1 = DrumSound.QUAD_1
|
||||
Q2 = DrumSound.QUAD_2
|
||||
Q3 = DrumSound.QUAD_3
|
||||
Q4 = DrumSound.QUAD_4
|
||||
QS = DrumSound.QUAD_SPOCK
|
||||
B1 = DrumSound.BASS_1
|
||||
B2 = DrumSound.BASS_2
|
||||
B3 = DrumSound.BASS_3
|
||||
B4 = DrumSound.BASS_4
|
||||
B5 = DrumSound.BASS_5
|
||||
|
||||
for _ in range(4):
|
||||
p.hit(C, Duration.QUARTER, velocity=95)
|
||||
# Snare line — 8 players (high volume to compensate for ensemble division)
|
||||
sn = score.part("snares", synth="sine", volume=1.5, reverb=0.2, ensemble=8)
|
||||
# Quads — 4 players
|
||||
q = score.part("quads", synth="sine", volume=0.5, reverb=0.2, ensemble=4)
|
||||
# Basses — 5 players
|
||||
b = score.part("basses", synth="sine", volume=0.55, reverb=0.2, ensemble=5)
|
||||
|
||||
# Click count-off
|
||||
for _ in range(4):
|
||||
p.hit(R, Duration.SIXTEENTH, velocity=118)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=32)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
p.hit(R, Duration.SIXTEENTH, velocity=115)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=32)
|
||||
p.hit(R, Duration.SIXTEENTH, velocity=118)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=35)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
p.hit(R, Duration.SIXTEENTH, velocity=120)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
p.hit(S, Duration.SIXTEENTH, velocity=32)
|
||||
sn.hit(C, Duration.QUARTER, velocity=95)
|
||||
q.rest(Duration.QUARTER)
|
||||
b.rest(Duration.QUARTER)
|
||||
|
||||
# Bar 1-2: snare groove, quads accent, bass on beats
|
||||
for _ in range(2):
|
||||
sn.hit(R, Duration.SIXTEENTH, velocity=118)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
sn.hit(R, Duration.SIXTEENTH, velocity=115)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
||||
sn.hit(R, Duration.SIXTEENTH, velocity=118)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=35)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
sn.hit(R, Duration.SIXTEENTH, velocity=120)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
||||
|
||||
q.hit(Q1, Duration.SIXTEENTH, velocity=95)
|
||||
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q3, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q3, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q1, Duration.SIXTEENTH, velocity=95)
|
||||
q.hit(QS, Duration.SIXTEENTH, velocity=100)
|
||||
q.hit(Q1, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q3, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q1, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(QS, Duration.SIXTEENTH, velocity=100)
|
||||
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q1, Duration.SIXTEENTH, velocity=90)
|
||||
|
||||
b.hit(B3, Duration.QUARTER, velocity=100)
|
||||
b.hit(B1, Duration.EIGHTH, velocity=90)
|
||||
b.hit(B5, Duration.EIGHTH, velocity=95)
|
||||
b.hit(B3, Duration.QUARTER, velocity=100)
|
||||
b.hit(B5, Duration.EIGHTH, velocity=90)
|
||||
b.hit(B1, Duration.EIGHTH, velocity=95)
|
||||
|
||||
# Bar 3: flams + diddles
|
||||
sn.flam(S, Duration.QUARTER, velocity=120)
|
||||
sn.diddle(S, Duration.EIGHTH, velocity=45)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
||||
sn.flam(S, Duration.QUARTER, velocity=118)
|
||||
sn.diddle(S, Duration.EIGHTH, velocity=42)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
||||
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
||||
|
||||
q.hit(Q1, Duration.QUARTER, velocity=95)
|
||||
q.hit(Q3, Duration.EIGHTH, velocity=55)
|
||||
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
||||
q.hit(QS, Duration.QUARTER, velocity=100)
|
||||
q.hit(Q4, Duration.EIGHTH, velocity=55)
|
||||
q.hit(Q1, Duration.EIGHTH, velocity=90)
|
||||
|
||||
b.hit(B5, Duration.QUARTER, velocity=100)
|
||||
b.hit(B3, Duration.QUARTER, velocity=95)
|
||||
b.hit(B1, Duration.QUARTER, velocity=100)
|
||||
b.hit(B3, Duration.QUARTER, velocity=95)
|
||||
|
||||
# Bar 4: buzz roll finale into big hit
|
||||
for i in range(28):
|
||||
sn.hit(S, 0.0625, velocity=min(25 + i * 3, 100))
|
||||
sn.hit(R, Duration.EIGHTH, velocity=125)
|
||||
sn.hit(R, Duration.EIGHTH, velocity=127)
|
||||
|
||||
for i in range(8):
|
||||
q.hit([Q1,Q2,Q3,Q4,Q4,Q3,Q2,Q1][i], Duration.SIXTEENTH, velocity=60+i*4)
|
||||
q.hit(QS, Duration.HALF, velocity=110)
|
||||
|
||||
b.hit(B1, Duration.SIXTEENTH, velocity=90)
|
||||
b.hit(B2, Duration.SIXTEENTH, velocity=90)
|
||||
b.hit(B3, Duration.SIXTEENTH, velocity=90)
|
||||
b.hit(B4, Duration.SIXTEENTH, velocity=90)
|
||||
b.hit(B5, Duration.SIXTEENTH, velocity=95)
|
||||
b.hit(B4, Duration.SIXTEENTH, velocity=90)
|
||||
b.hit(B3, Duration.SIXTEENTH, velocity=90)
|
||||
b.hit(B2, Duration.SIXTEENTH, velocity=90)
|
||||
b.hit(B3, Duration.HALF, velocity=100)
|
||||
|
||||
render("march_snare", score)
|
||||
|
||||
|
||||
@@ -391,6 +524,470 @@ def gen_jazz_ballad():
|
||||
|
||||
# ── Quickstart example ───────────────────────────────────────────────────
|
||||
|
||||
# ── Synth waveform demos ──────────────────────────────────────────────────
|
||||
|
||||
def _synth_demo(name, synth, envelope="none", **kwargs):
|
||||
"""Short C major melody on a given synth."""
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth=synth, envelope=envelope, volume=0.5,
|
||||
reverb=0.2, **kwargs)
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render(f"synth_{name}", score)
|
||||
|
||||
|
||||
def gen_synth_sine():
|
||||
_synth_demo("sine", "sine")
|
||||
|
||||
def gen_synth_saw():
|
||||
_synth_demo("saw", "saw")
|
||||
|
||||
def gen_synth_triangle():
|
||||
_synth_demo("triangle", "triangle")
|
||||
|
||||
def gen_synth_square():
|
||||
_synth_demo("square", "square")
|
||||
|
||||
def gen_synth_piano():
|
||||
score = Score("4/4", bpm=85)
|
||||
p = score.part("demo", instrument="piano", volume=0.5, reverb=0.3)
|
||||
# Hold chords with melody on top
|
||||
p.hold("C3", Duration.WHOLE * 2, velocity=60)
|
||||
p.hold("E3", Duration.WHOLE * 2, velocity=55)
|
||||
p.hold("G3", Duration.WHOLE * 2, velocity=55)
|
||||
for n in ["E4", "G4", "C5", "G4", "E4", "D4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=80)
|
||||
render("synth_piano", score)
|
||||
|
||||
def gen_synth_acoustic_guitar():
|
||||
from pytheory import Fretboard
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", instrument="acoustic_guitar", volume=0.5,
|
||||
reverb=0.25, fretboard=Fretboard.guitar())
|
||||
for ch in ["G", "D", "Em", "C"]:
|
||||
p.strum(ch, Duration.WHOLE, velocity=75)
|
||||
render("synth_acoustic_guitar", score)
|
||||
|
||||
def gen_synth_pulse():
|
||||
_synth_demo("pulse", "pulse")
|
||||
|
||||
def gen_synth_noise():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth="noise", envelope="pad", volume=0.3,
|
||||
lowpass=2000, reverb=0.3)
|
||||
p.add("C4", Duration.WHOLE * 2, velocity=80)
|
||||
render("synth_noise", score)
|
||||
|
||||
def gen_synth_pwm_slow():
|
||||
_synth_demo("pwm_slow", "pwm_slow", envelope="pad")
|
||||
|
||||
def gen_synth_pwm_fast():
|
||||
_synth_demo("pwm_fast", "pwm_fast")
|
||||
|
||||
def gen_synth_fm():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth="fm", envelope="bell", volume=0.5,
|
||||
fm_ratio=3.0, fm_index=5.0, reverb=0.3)
|
||||
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
||||
p.add(n, Duration.QUARTER, velocity=80)
|
||||
render("synth_fm", score)
|
||||
|
||||
def gen_synth_rhodes():
|
||||
score = Score("4/4", bpm=80)
|
||||
p = score.part("demo", instrument="electric_piano", volume=0.5, reverb=0.3)
|
||||
# Jazz chords with hold
|
||||
p.hold("C3", Duration.WHOLE * 2, velocity=60)
|
||||
p.hold("E3", Duration.WHOLE * 2, velocity=55)
|
||||
p.hold("Bb3", Duration.WHOLE * 2, velocity=55)
|
||||
for n in ["G4", "Bb4", "C5", "Bb4", "G4", "F4", "E4", "G4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=75)
|
||||
render("synth_rhodes", score)
|
||||
|
||||
def gen_synth_supersaw():
|
||||
_synth_demo("supersaw", "supersaw", envelope="pad")
|
||||
|
||||
def gen_synth_bass_guitar():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth="bass_guitar_synth", envelope="none",
|
||||
volume=0.5, reverb=0.2)
|
||||
for n in ["C2", "E2", "G2", "C3", "G2", "E2", "C2", "E2"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_bass_guitar", score)
|
||||
|
||||
def gen_synth_flute():
|
||||
_synth_demo("flute", "flute_synth", envelope="none")
|
||||
|
||||
def gen_synth_trumpet():
|
||||
_synth_demo("trumpet", "trumpet_synth", envelope="none")
|
||||
|
||||
def gen_synth_clarinet():
|
||||
_synth_demo("clarinet", "clarinet_synth", envelope="none")
|
||||
|
||||
def gen_synth_oboe():
|
||||
_synth_demo("oboe", "oboe_synth", envelope="none")
|
||||
|
||||
def gen_synth_cello():
|
||||
score = Score("4/4", bpm=70)
|
||||
p = score.part("demo", instrument="cello", volume=0.5, reverb=0.3, ensemble=3)
|
||||
for n in ["C3", "E3", "G3", "C4"]:
|
||||
p.add(n, Duration.WHOLE, velocity=80)
|
||||
render("synth_cello", score)
|
||||
|
||||
def gen_synth_harpsichord():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth="harpsichord_synth", envelope="none",
|
||||
volume=0.5, reverb=0.25)
|
||||
# Baroque ornamental runs
|
||||
p.hold("C3", Duration.WHOLE, velocity=70)
|
||||
for n in ["C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"]:
|
||||
p.add(n, Duration.EIGHTH, velocity=80)
|
||||
p.hold("G3", Duration.WHOLE, velocity=70)
|
||||
for n in ["C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4"]:
|
||||
p.add(n, Duration.EIGHTH, velocity=78)
|
||||
render("synth_harpsichord", score)
|
||||
|
||||
def gen_synth_electric_guitar():
|
||||
from pytheory import Fretboard
|
||||
score = Score("4/4", bpm=110)
|
||||
p = score.part("demo", instrument="electric_guitar", volume=0.5,
|
||||
reverb=0.15, fretboard=Fretboard.guitar())
|
||||
for ch in ["Am", "F", "C", "G"]:
|
||||
p.strum(ch, Duration.WHOLE, velocity=80)
|
||||
render("synth_electric_guitar", score)
|
||||
|
||||
def gen_synth_kalimba():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", instrument="kalimba", volume=0.5, reverb=0.3)
|
||||
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_kalimba", score)
|
||||
|
||||
def gen_synth_wurlitzer():
|
||||
score = Score("4/4", bpm=90)
|
||||
p = score.part("demo", instrument="wurlitzer", volume=0.5, reverb=0.25)
|
||||
p.hold("C3", Duration.WHOLE * 2, velocity=60)
|
||||
p.hold("Eb3", Duration.WHOLE * 2, velocity=55)
|
||||
p.hold("G3", Duration.WHOLE * 2, velocity=55)
|
||||
for n in ["G4", "Bb4", "C5", "Bb4", "G4", "F4", "Eb4", "G4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=78)
|
||||
render("synth_wurlitzer", score)
|
||||
|
||||
def gen_synth_vibraphone():
|
||||
score = Score("4/4", bpm=90)
|
||||
p = score.part("demo", instrument="vibraphone", volume=0.5)
|
||||
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
||||
p.add(n, Duration.QUARTER, velocity=75)
|
||||
render("synth_vibraphone", score)
|
||||
|
||||
def gen_synth_pipe_organ():
|
||||
score = Score("4/4", bpm=70)
|
||||
p = score.part("demo", instrument="pipe_organ", volume=0.5)
|
||||
p.hold("C3", Duration.WHOLE * 4, velocity=70)
|
||||
p.hold("G3", Duration.WHOLE * 4, velocity=65)
|
||||
for n in ["C4", "D4", "E4", "F4", "G4", "F4", "E4", "D4",
|
||||
"C4", "E4", "G4", "C5", "G4", "E4", "C4", "C4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=75)
|
||||
render("synth_pipe_organ", score)
|
||||
|
||||
def gen_synth_choir():
|
||||
score = Score("4/4", bpm=70)
|
||||
p = score.part("demo", instrument="choir", volume=0.5)
|
||||
for n, v in [("C4", "ah"), ("E4", "oh"), ("G4", "ah"), ("C5", "ee"),
|
||||
("G4", "oh"), ("E4", "ah"), ("C4", "oo"), ("C4", "ah")]:
|
||||
p.add(n, Duration.HALF, velocity=70, lyric=v)
|
||||
render("synth_choir", score)
|
||||
|
||||
def gen_synth_organ():
|
||||
_synth_demo("organ", "organ_synth", envelope="organ")
|
||||
|
||||
def gen_synth_marimba():
|
||||
_synth_demo("marimba", "marimba_synth", envelope="mallet")
|
||||
|
||||
def gen_synth_sitar():
|
||||
score = Score("4/4", bpm=80)
|
||||
p = score.part("demo", instrument="sitar", volume=0.4, reverb=0.35)
|
||||
# Drone under melody
|
||||
p.hold("C3", Duration.WHOLE * 4, velocity=55)
|
||||
for n, d in [("C4", 1.0), ("D4", 0.5), ("E4", 0.5), ("G4", 1.0),
|
||||
("A4", 0.5), ("G4", 0.5), ("E4", 1.0), ("D4", 0.5),
|
||||
("C4", 0.5), ("D4", 1.0), ("C4", 2.0)]:
|
||||
p.add(n, d, velocity=75)
|
||||
render("synth_sitar", score)
|
||||
|
||||
|
||||
def gen_synth_harp():
|
||||
score = Score("4/4", bpm=80)
|
||||
p = score.part("demo", synth="harp_synth", envelope="none",
|
||||
volume=0.5, reverb=0.3)
|
||||
# Arpeggiated chords with hold — harp style
|
||||
p.hold("C3", Duration.WHOLE * 2, velocity=70)
|
||||
for n in ["E4", "G4", "C5", "E5", "G5", "C5", "G4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=75)
|
||||
render("synth_harp", score)
|
||||
|
||||
def gen_synth_upright_bass():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth="upright_bass_synth", envelope="none",
|
||||
volume=0.5, reverb=0.2)
|
||||
for n in ["C2", "E2", "G2", "C3", "G2", "E2", "C2", "E2"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_upright_bass", score)
|
||||
|
||||
def gen_synth_timpani():
|
||||
score = Score("4/4", bpm=140)
|
||||
timp = score.part("timp", synth="timpani_synth", volume=0.5, reverb=0.25)
|
||||
timp.roll("C3", Duration.WHOLE, velocity_start=20, velocity_end=110, speed=0.125)
|
||||
timp.add("C3", Duration.HALF, velocity=127)
|
||||
timp.rest(Duration.HALF)
|
||||
timp.roll("G2", Duration.WHOLE, velocity_start=20, velocity_end=110, speed=0.125)
|
||||
timp.add("G2", Duration.HALF, velocity=127)
|
||||
render("synth_timpani", score)
|
||||
|
||||
def gen_synth_strings():
|
||||
score = Score("4/4", bpm=70)
|
||||
p = score.part("demo", synth="strings_synth", envelope="bowed",
|
||||
volume=0.5, reverb=0.35, ensemble=8)
|
||||
for n in ["C4", "E4", "G4", "C5"]:
|
||||
p.add(n, Duration.WHOLE, velocity=75)
|
||||
render("synth_strings", score)
|
||||
|
||||
def gen_synth_saxophone():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", instrument="tenor_sax", volume=0.5, reverb=0.2)
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_saxophone", score)
|
||||
|
||||
def gen_synth_pedal_steel():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", instrument="pedal_steel", volume=0.5, reverb=0.3)
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_pedal_steel", score)
|
||||
|
||||
def gen_synth_theremin():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", instrument="theremin", volume=0.5, reverb=0.3)
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_theremin", score)
|
||||
|
||||
def gen_synth_steel_drum():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", instrument="steel_drum", volume=0.5, reverb=0.2)
|
||||
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_steel_drum", score)
|
||||
|
||||
def gen_synth_accordion():
|
||||
score = Score("4/4", bpm=110)
|
||||
p = score.part("demo", instrument="accordion", volume=0.5, reverb=0.2)
|
||||
# Waltz feel with held chords
|
||||
p.hold("C3", Duration.WHOLE, velocity=65)
|
||||
p.hold("E3", Duration.WHOLE, velocity=60)
|
||||
p.hold("G3", Duration.WHOLE, velocity=60)
|
||||
for n in ["E4", "G4", "C5", "G4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=78)
|
||||
p.hold("F3", Duration.WHOLE, velocity=65)
|
||||
p.hold("A3", Duration.WHOLE, velocity=60)
|
||||
p.hold("C4", Duration.WHOLE, velocity=60)
|
||||
for n in ["A4", "C5", "F5", "C5"]:
|
||||
p.add(n, Duration.QUARTER, velocity=78)
|
||||
render("synth_accordion", score)
|
||||
|
||||
def gen_synth_didgeridoo():
|
||||
score = Score("4/4", bpm=80)
|
||||
p = score.part("demo", instrument="didgeridoo", volume=0.5, reverb=0.3)
|
||||
p.add("C2", Duration.WHOLE * 2, velocity=85)
|
||||
render("synth_didgeridoo", score)
|
||||
|
||||
def gen_synth_bagpipe():
|
||||
score = Score("4/4", bpm=90)
|
||||
p = score.part("demo", instrument="bagpipe", volume=0.4, reverb=0.2)
|
||||
# Drone on low A + E (like real Highland pipes)
|
||||
p.hold("A3", Duration.WHOLE * 4, velocity=70)
|
||||
p.hold("E3", Duration.WHOLE * 4, velocity=65)
|
||||
# Chanter melody on top
|
||||
for n, d in [("A4", 1.0), ("B4", 0.5), ("C5", 0.5), ("D5", 1.0),
|
||||
("E5", 1.0), ("D5", 0.5), ("C5", 0.5), ("B4", 1.0),
|
||||
("A4", 1.0), ("G4", 1.0), ("A4", 2.0)]:
|
||||
p.add(n, d, velocity=80)
|
||||
render("synth_bagpipe", score)
|
||||
|
||||
def gen_synth_banjo():
|
||||
from pytheory import Fretboard
|
||||
score = Score("4/4", bpm=130)
|
||||
p = score.part("demo", instrument="banjo", volume=0.5, reverb=0.15,
|
||||
fretboard=Fretboard.guitar())
|
||||
# Strum into a picking lick
|
||||
p.strum("G", Duration.WHOLE, velocity=80)
|
||||
p.strum("C", Duration.WHOLE, velocity=78)
|
||||
# Bluegrass lick — 16th note picking
|
||||
for n in ["G4", "B4", "D5", "G5", "D5", "B4", "A4", "G4",
|
||||
"D4", "G4", "B4", "D5", "B4", "G4", "D4", "G4"]:
|
||||
p.add(n, Duration.SIXTEENTH, velocity=82)
|
||||
render("synth_banjo", score)
|
||||
|
||||
def gen_synth_mandolin():
|
||||
score = Score("4/4", bpm=110)
|
||||
p = score.part("demo", instrument="mandolin", volume=0.5, reverb=0.2)
|
||||
# Tremolo rolls on held notes — the mandolin signature
|
||||
p.roll("G4", Duration.WHOLE, velocity_start=65, velocity_end=85, speed=Duration.SIXTEENTH)
|
||||
p.roll("A4", Duration.WHOLE, velocity_start=65, velocity_end=85, speed=Duration.SIXTEENTH)
|
||||
# Quick melody
|
||||
for n in ["B4", "C5", "D5", "C5", "B4", "A4", "G4", "A4"]:
|
||||
p.add(n, Duration.EIGHTH, velocity=80)
|
||||
# End on a roll
|
||||
p.roll("G4", Duration.WHOLE, velocity_start=70, velocity_end=90, speed=Duration.SIXTEENTH)
|
||||
render("synth_mandolin", score)
|
||||
|
||||
def gen_synth_ukulele():
|
||||
from pytheory import Fretboard
|
||||
score = Score("4/4", bpm=110)
|
||||
p = score.part("demo", instrument="ukulele", volume=0.5, reverb=0.25,
|
||||
fretboard=Fretboard.ukulele())
|
||||
for ch in ["C", "Am", "F", "G"]:
|
||||
p.strum(ch, Duration.WHOLE, velocity=72)
|
||||
render("synth_ukulele", score)
|
||||
|
||||
def gen_synth_hard_sync():
|
||||
score = Score("4/4", bpm=120)
|
||||
p = score.part("demo", instrument="sync_lead_bright", volume=0.5)
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=90)
|
||||
render("synth_hard_sync", score)
|
||||
|
||||
|
||||
def gen_synth_ring_mod():
|
||||
score = Score("4/4", bpm=90)
|
||||
p = score.part("demo", instrument="ring_mod_bell", volume=0.5)
|
||||
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
||||
p.add(n, Duration.QUARTER, velocity=80)
|
||||
render("synth_ring_mod", score)
|
||||
|
||||
|
||||
def gen_synth_wavefold():
|
||||
score = Score("4/4", bpm=110)
|
||||
p = score.part("demo", instrument="wavefold_warm", volume=0.5)
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_wavefold", score)
|
||||
|
||||
|
||||
def gen_synth_drift():
|
||||
score = Score("4/4", bpm=90)
|
||||
p = score.part("demo", instrument="drift_saw", volume=0.5, reverb=0.35,
|
||||
reverb_type="taj_mahal")
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.HALF, velocity=75)
|
||||
render("synth_drift", score)
|
||||
|
||||
|
||||
def gen_synth_karplus():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth="pluck_synth", envelope="none",
|
||||
volume=0.5, reverb=0.2)
|
||||
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
||||
p.add(n, Duration.QUARTER, velocity=85)
|
||||
render("synth_karplus", score)
|
||||
|
||||
|
||||
def gen_synth_mellotron():
|
||||
score = Score("4/4", bpm=80)
|
||||
p = score.part("demo", instrument="mellotron_flute", volume=0.5)
|
||||
for n in ["C4", "E4", "G4", "C5"]:
|
||||
p.add(n, Duration.WHOLE, velocity=75)
|
||||
render("synth_mellotron", score)
|
||||
|
||||
|
||||
def gen_synth_granular():
|
||||
score = Score("4/4", bpm=80)
|
||||
p = score.part("demo", instrument="granular_pad", volume=0.5, reverb=0.4)
|
||||
for n in ["C4", "E4", "G4", "C5"]:
|
||||
p.add(n, Duration.WHOLE, velocity=75)
|
||||
render("synth_granular", score)
|
||||
|
||||
|
||||
def gen_synth_crotales():
|
||||
score = Score("4/4", bpm=60)
|
||||
p = score.part("demo", synth="crotales_synth", envelope="none",
|
||||
volume=0.5, reverb=0.3)
|
||||
for n in ["C6", "E6", "G6", "C7", "G6", "E6", "C6"]:
|
||||
p.add(n, Duration.HALF, velocity=80)
|
||||
render("synth_crotales", score)
|
||||
|
||||
|
||||
def gen_synth_tingsha():
|
||||
score = Score("4/4", bpm=40)
|
||||
p = score.part("demo", synth="tingsha_synth", envelope="none",
|
||||
volume=0.5, reverb=0.4)
|
||||
for n in ["E5", "A5", "E6", "A5"]:
|
||||
p.add(n, Duration.WHOLE, velocity=75)
|
||||
render("synth_tingsha", score)
|
||||
|
||||
|
||||
def gen_rainstick():
|
||||
score = Score("4/4", bpm=60)
|
||||
p = score.part("demo", synth="sine", volume=1.0)
|
||||
p.hit(DrumSound.RAINSTICK, Duration.WHOLE * 3, velocity=90)
|
||||
render("rainstick", score)
|
||||
|
||||
|
||||
def gen_rainstick_slow():
|
||||
score = Score("4/4", bpm=60)
|
||||
p = score.part("demo", synth="sine", volume=1.0)
|
||||
p.hit(DrumSound.RAINSTICK_SLOW, Duration.WHOLE * 4, velocity=85)
|
||||
render("rainstick_slow", score)
|
||||
|
||||
|
||||
def gen_ocean_drum():
|
||||
score = Score("4/4", bpm=60)
|
||||
p = score.part("demo", synth="sine", volume=1.0)
|
||||
p.hit(DrumSound.OCEAN_DRUM, Duration.WHOLE * 3, velocity=85)
|
||||
render("ocean_drum", score)
|
||||
|
||||
|
||||
def gen_cabasa():
|
||||
score = Score("4/4", bpm=100)
|
||||
p = score.part("demo", synth="sine", volume=1.0)
|
||||
for _ in range(16):
|
||||
p.hit(DrumSound.CABASA, Duration.EIGHTH, velocity=100)
|
||||
render("cabasa", score)
|
||||
|
||||
|
||||
def gen_wind_chimes():
|
||||
score = Score("4/4", bpm=60)
|
||||
p = score.part("demo", synth="sine", volume=1.0)
|
||||
p.hit(DrumSound.WIND_CHIMES, Duration.WHOLE * 3, velocity=85)
|
||||
render("wind_chimes", score)
|
||||
|
||||
|
||||
def gen_finger_cymbal():
|
||||
score = Score("4/4", bpm=80)
|
||||
p = score.part("demo", synth="sine", volume=1.0)
|
||||
for _ in range(8):
|
||||
p.hit(DrumSound.FINGER_CYMBAL, Duration.QUARTER, velocity=85)
|
||||
render("finger_cymbal", score)
|
||||
|
||||
|
||||
def gen_synth_singing_bowl_strike():
|
||||
score = Score("4/4", bpm=40)
|
||||
p = score.part("demo", synth="singing_bowl_strike_synth", envelope="none",
|
||||
volume=0.5, reverb=0.4)
|
||||
for n in ["A3", "D4", "F4", "A4"]:
|
||||
p.add(n, Duration.WHOLE, velocity=80)
|
||||
render("synth_singing_bowl_strike", score)
|
||||
|
||||
|
||||
def gen_synth_singing_bowl_ring():
|
||||
score = Score("4/4", bpm=30)
|
||||
p = score.part("demo", synth="singing_bowl_ring_synth", envelope="none",
|
||||
volume=0.5, reverb=0.4)
|
||||
for n in ["A3", "D4", "A4"]:
|
||||
p.add(n, Duration.WHOLE * 2, velocity=75)
|
||||
render("synth_singing_bowl_ring", score)
|
||||
|
||||
|
||||
def gen_arpeggio():
|
||||
score = Score("4/4", bpm=132)
|
||||
score.drums("house", repeats=8)
|
||||
@@ -419,25 +1016,24 @@ def gen_legato_glide():
|
||||
|
||||
|
||||
def gen_quickstart():
|
||||
score = Score("4/4", bpm=140)
|
||||
score.drums("bossa nova", repeats=4)
|
||||
chords = score.part("chords", synth="sine", envelope="pad",
|
||||
reverb=0.4, volume=0.3)
|
||||
lead = score.part("lead", synth="saw", envelope="pluck",
|
||||
lowpass=2000, lowpass_q=3.0, distortion=0.8,
|
||||
legato=True, glide=0.03, volume=0.4)
|
||||
bass = score.part("bass", synth="sine", lowpass=500)
|
||||
key = Key("A", "minor")
|
||||
for chord in key.progression("i", "iv", "V", "i"):
|
||||
chords.add(chord, Duration.WHOLE)
|
||||
chords.add(chord, Duration.WHOLE)
|
||||
lead.arpeggio("Am", bars=2, pattern="updown", octaves=2)
|
||||
lead.arpeggio("Dm", bars=2, pattern="updown", octaves=2)
|
||||
lead.set(lowpass=5000, reverb=0.3)
|
||||
lead.arpeggio("E7", bars=2, pattern="up", octaves=2)
|
||||
lead.arpeggio("Am", bars=2, pattern="updown", octaves=2)
|
||||
for n in ["A2", "E2", "A2", "C3"] * 4:
|
||||
bass.add(n, Duration.QUARTER)
|
||||
score = Score("4/4", bpm=120)
|
||||
score.drums("rock", repeats=8, fill="rock", fill_every=4)
|
||||
piano = score.part("piano", instrument="piano", reverb=0.3)
|
||||
lead = score.part("lead", synth="saw", envelope="pluck", volume=0.4,
|
||||
delay=0.2, reverb=0.2, lowpass=4000)
|
||||
bass = score.part("bass", synth="triangle", lowpass=900)
|
||||
for chord in Key("G", "major").progression("I", "V", "vi", "IV") * 2:
|
||||
piano.add(chord, Duration.WHOLE)
|
||||
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
||||
lead.add("G5", 1).add("E5", 1)
|
||||
lead.add("D5", 0.5).add("B4", 0.5).add("A4", 1)
|
||||
lead.add("G4", 2).rest(2)
|
||||
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
||||
lead.add("G5", 1).add("A5", 1)
|
||||
lead.add("G5", 0.5).add("E5", 0.5).add("D5", 1)
|
||||
lead.add("B4", 2).rest(2)
|
||||
for n in ["G2", "G2", "D2", "D2", "E2", "E2", "C2", "C2"] * 2:
|
||||
bass.add(n, Duration.HALF)
|
||||
render("quickstart", score)
|
||||
|
||||
|
||||
@@ -459,7 +1055,7 @@ def gen_complete_rock():
|
||||
lead = score.part("lead", synth="saw", envelope="pluck", volume=0.4,
|
||||
delay=0.2, delay_time=0.33, reverb=0.2, lowpass=3000)
|
||||
bass = score.part("bass", synth="triangle", envelope="pluck", volume=0.45,
|
||||
lowpass=600)
|
||||
lowpass=1200)
|
||||
for chord in Key("G", "major").progression("I", "V", "vi", "IV") * 2:
|
||||
piano.add(chord, Duration.WHOLE)
|
||||
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
||||
@@ -550,6 +1146,66 @@ GENERATORS = [
|
||||
gen_ensemble,
|
||||
gen_strum,
|
||||
gen_swell,
|
||||
gen_synth_sine,
|
||||
gen_synth_saw,
|
||||
gen_synth_triangle,
|
||||
gen_synth_square,
|
||||
gen_synth_pulse,
|
||||
gen_synth_noise,
|
||||
gen_synth_pwm_slow,
|
||||
gen_synth_pwm_fast,
|
||||
gen_synth_fm,
|
||||
gen_synth_rhodes,
|
||||
gen_synth_supersaw,
|
||||
gen_synth_piano,
|
||||
gen_synth_bass_guitar,
|
||||
gen_synth_flute,
|
||||
gen_synth_trumpet,
|
||||
gen_synth_clarinet,
|
||||
gen_synth_oboe,
|
||||
gen_synth_cello,
|
||||
gen_synth_harpsichord,
|
||||
gen_synth_acoustic_guitar,
|
||||
gen_synth_electric_guitar,
|
||||
gen_synth_sitar,
|
||||
gen_synth_kalimba,
|
||||
gen_synth_wurlitzer,
|
||||
gen_synth_vibraphone,
|
||||
gen_synth_pipe_organ,
|
||||
gen_synth_choir,
|
||||
gen_synth_organ,
|
||||
gen_synth_marimba,
|
||||
gen_synth_harp,
|
||||
gen_synth_upright_bass,
|
||||
gen_synth_timpani,
|
||||
gen_synth_strings,
|
||||
gen_synth_saxophone,
|
||||
gen_synth_pedal_steel,
|
||||
gen_synth_theremin,
|
||||
gen_synth_steel_drum,
|
||||
gen_synth_accordion,
|
||||
gen_synth_didgeridoo,
|
||||
gen_synth_bagpipe,
|
||||
gen_synth_banjo,
|
||||
gen_synth_mandolin,
|
||||
gen_synth_ukulele,
|
||||
gen_synth_hard_sync,
|
||||
gen_synth_ring_mod,
|
||||
gen_synth_wavefold,
|
||||
gen_synth_drift,
|
||||
gen_synth_karplus,
|
||||
gen_synth_mellotron,
|
||||
gen_synth_granular,
|
||||
gen_synth_crotales,
|
||||
gen_synth_tingsha,
|
||||
gen_synth_singing_bowl_strike,
|
||||
gen_synth_singing_bowl_ring,
|
||||
gen_rainstick,
|
||||
gen_rainstick_slow,
|
||||
gen_ocean_drum,
|
||||
gen_cabasa,
|
||||
gen_wind_chimes,
|
||||
gen_finger_cymbal,
|
||||
gen_arpeggio,
|
||||
gen_legato_glide,
|
||||
gen_acid_house,
|
||||
|
||||
+173
-7
@@ -3,19 +3,21 @@ Playback and Export
|
||||
|
||||
This is the output layer. You've built your theory, composed your
|
||||
arrangement, shaped your sounds -- now you need to hear it. PyTheory
|
||||
gives you three ways to get your music out: speakers, WAV files, and
|
||||
MIDI files.
|
||||
gives you four ways to get your music out: speakers, WAV files, MIDI
|
||||
files, and sheet music.
|
||||
|
||||
Use **speakers** for immediate feedback while you're sketching and
|
||||
experimenting. Use **WAV export** when you want to share actual audio
|
||||
-- post it, send it, drop it into a video. Use **MIDI export** when you
|
||||
want to bring your sketch into a real DAW and finish it with
|
||||
professional instruments, mixing, and mastering. Each output serves a
|
||||
different stage of the creative process.
|
||||
professional instruments, mixing, and mastering. Use **ABC notation
|
||||
export** when you want sheet music -- rendered in the browser or shared
|
||||
as plain text. Each output serves a different stage of the creative
|
||||
process.
|
||||
|
||||
PyTheory can play audio through your speakers, save to WAV, or export
|
||||
to MIDI. Everything is synthesized from waveforms -- no samples or
|
||||
external audio files needed.
|
||||
PyTheory can play audio through your speakers, save to WAV, export to
|
||||
MIDI, or generate sheet music as ABC notation. Everything is synthesized
|
||||
from waveforms -- no samples or external audio files needed.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -44,6 +46,22 @@ Optional parameters for synth, envelope, and temperament:
|
||||
play(Tone.from_string("C4"), synth=Synth.SAW, envelope=Envelope.PLUCK, t=1_000)
|
||||
play(Tone.from_string("C4"), temperament="pythagorean", t=1_000)
|
||||
|
||||
Synth-specific parameters are passed through as keyword arguments:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Mellotron with flute tape
|
||||
play(Tone.from_string("C4"), synth=Synth.MELLOTRON, tape="choir", t=2_000)
|
||||
|
||||
# Hard sync with custom slave ratio
|
||||
play(Tone.from_string("C4"), synth=Synth.HARD_SYNC, slave_ratio=2.5)
|
||||
|
||||
# Wavefolding with 4 folds
|
||||
play(Tone.from_string("C4"), synth=Synth.WAVEFOLD, folds=4.0)
|
||||
|
||||
# Drift oscillator with square shape
|
||||
play(Tone.from_string("C4"), synth=Synth.DRIFT, shape="square")
|
||||
|
||||
play_score() -- Full Arrangements
|
||||
---------------------------------
|
||||
|
||||
@@ -155,6 +173,154 @@ Score-based export (with time signature, tempo, and parts):
|
||||
score.add(chord, Duration.WHOLE)
|
||||
score.save_midi("progression.mid")
|
||||
|
||||
to_abc() -- ABC Notation / Sheet Music
|
||||
---------------------------------------
|
||||
|
||||
ABC notation is a human-readable text format for music that tools can
|
||||
turn into staff notation and MIDI. It's widely used for folk tunes,
|
||||
lead sheets, and quick sketches. PyTheory can export any Score as ABC
|
||||
notation -- and optionally wrap it in an HTML page that renders
|
||||
sheet music right in the browser using `abcjs <https://www.abcjs.net/>`_.
|
||||
|
||||
Basic export:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import Score, Duration, Key
|
||||
|
||||
score = Score("4/4", bpm=120)
|
||||
lead = score.part("lead")
|
||||
for chord in Key("C", "major").progression("I", "V", "vi", "IV"):
|
||||
lead.add(chord, Duration.WHOLE)
|
||||
|
||||
print(score.to_abc(title="Pop Chords", key="C"))
|
||||
|
||||
Output:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
X:1
|
||||
T:Pop Chords
|
||||
M:4/4
|
||||
Q:1/4=120
|
||||
L:1/8
|
||||
K:C
|
||||
[CEG]8 | [GBd]8 | [Ace]8 | [FAc]8 |
|
||||
|
||||
Open sheet music in the browser with ``html=True``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
html = score.to_abc(title="Pop Chords", key="C", html=True)
|
||||
|
||||
with open("chords.html", "w") as f:
|
||||
f.write(html)
|
||||
|
||||
import webbrowser
|
||||
webbrowser.open("chords.html")
|
||||
|
||||
This generates a self-contained HTML page with an embedded
|
||||
``<script>`` tag that loads abcjs from a CDN and renders the notation
|
||||
as SVG -- no build steps, no dependencies, just open the file.
|
||||
|
||||
Multi-part scores automatically get ``V:`` (voice) directives so each
|
||||
instrument appears on its own staff. Bass parts (average note below C4)
|
||||
get bass clef automatically. Drum-only parts are skipped. Notes longer
|
||||
than one measure are split into tied notes across barlines.
|
||||
|
||||
Parameters:
|
||||
|
||||
- **title** -- Tune title for the ``T:`` header (default ``"Untitled"``).
|
||||
- **key** -- ABC key signature string (default ``"C"``). Use ``"Am"`` for
|
||||
A minor, ``"Bb"`` for B-flat major, ``"F#m"`` for F-sharp minor, etc.
|
||||
- **html** -- If ``True``, return a full HTML document instead of raw ABC
|
||||
(default ``False``).
|
||||
|
||||
to_lilypond() -- LilyPond Export
|
||||
---------------------------------
|
||||
|
||||
`LilyPond <https://lilypond.org/>`_ is the gold standard for
|
||||
publication-quality music engraving. ``to_lilypond()`` generates
|
||||
complete LilyPond source files that you can compile to PDF:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
score = Score("4/4", bpm=120)
|
||||
lead = score.part("lead")
|
||||
for note in ["C4", "D4", "E4", "F4"]:
|
||||
lead.add(note, Duration.QUARTER)
|
||||
|
||||
ly = score.to_lilypond(title="My Score", key="C", mode="major")
|
||||
|
||||
with open("score.ly", "w") as f:
|
||||
f.write(ly)
|
||||
|
||||
Then compile with ``lilypond score.ly`` to get a PDF. Multi-part scores
|
||||
get separate staves in a ``StaffGroup``, bass clef is auto-detected,
|
||||
and long notes are split with ties across barlines.
|
||||
|
||||
Parameters:
|
||||
|
||||
- **title** -- Title for the ``\header`` block (default ``"Untitled"``).
|
||||
- **key** -- Key signature root (default ``"C"``). Use note names like
|
||||
``"Bb"``, ``"F#"``, ``"Eb"``.
|
||||
- **mode** -- LilyPond mode string (default ``"major"``). Use ``"minor"``
|
||||
for minor keys.
|
||||
|
||||
to_musicxml() -- MusicXML Export
|
||||
---------------------------------
|
||||
|
||||
MusicXML is the interchange format for notation software. Export your
|
||||
score and open it in MuseScore, Sibelius, Finale, Dorico, or any
|
||||
other notation app:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
xml = score.to_musicxml(title="My Score")
|
||||
|
||||
with open("score.musicxml", "w") as f:
|
||||
f.write(xml)
|
||||
|
||||
The output is a complete MusicXML 4.0 partwise document with proper
|
||||
time signatures, tempo markings, clef detection, tied notes across
|
||||
barlines, and chord notation. No external dependencies needed.
|
||||
|
||||
to_tab() -- Guitar/Bass Tablature
|
||||
-----------------------------------
|
||||
|
||||
Generate ASCII tablature from any Part or Score:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
lead = score.part("lead")
|
||||
lead.add("E4", Duration.QUARTER)
|
||||
lead.add("B3", Duration.QUARTER)
|
||||
lead.add("G3", Duration.QUARTER)
|
||||
lead.add("D3", Duration.QUARTER)
|
||||
|
||||
print(lead.to_tab())
|
||||
|
||||
Output::
|
||||
|
||||
e|---0---------|
|
||||
B|------0------|
|
||||
G|---------0---|
|
||||
D|------------0|
|
||||
A|-------------|
|
||||
E|-------------|
|
||||
|
||||
Works on Score too -- it picks the first melodic part automatically:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
print(score.to_tab()) # auto-pick part
|
||||
print(score.to_tab(part_name="bass")) # specific part
|
||||
print(score.to_tab(tuning="bass")) # 4-string bass tab
|
||||
print(score.to_tab(tuning="drop_d")) # drop D guitar
|
||||
|
||||
Supports ``"guitar"`` (6-string standard), ``"bass"`` (4-string),
|
||||
``"drop_d"``, or a custom list of MIDI note numbers for any tuning.
|
||||
|
||||
play_pattern() -- Drum Patterns
|
||||
-------------------------------
|
||||
|
||||
|
||||
+15
-33
@@ -143,45 +143,27 @@ chords, melody, bass, each with their own synth and effects:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import Score, Pattern, Key, Duration, Chord
|
||||
from pytheory import Score, Key, Duration
|
||||
from pytheory.play import play_score
|
||||
|
||||
score = Score("4/4", bpm=140)
|
||||
score.drums("bossa nova", repeats=4)
|
||||
score = Score("4/4", bpm=120)
|
||||
score.drums("rock", repeats=8, fill="rock", fill_every=4)
|
||||
|
||||
chords = score.part(
|
||||
"chords",
|
||||
synth="fm",
|
||||
envelope="pad",
|
||||
reverb=0.4,
|
||||
)
|
||||
lead = score.part(
|
||||
"lead",
|
||||
synth="saw",
|
||||
envelope="pluck",
|
||||
delay=0.3,
|
||||
lowpass=3000,
|
||||
humanize=0.2,
|
||||
)
|
||||
bass = score.part(
|
||||
"bass",
|
||||
synth="sine",
|
||||
lowpass=500,
|
||||
)
|
||||
piano = score.part("piano", instrument="piano", reverb=0.3)
|
||||
lead = score.part("lead", synth="saw", envelope="pluck",
|
||||
delay=0.2, reverb=0.2, lowpass=4000)
|
||||
bass = score.part("bass", synth="triangle", lowpass=900)
|
||||
|
||||
key = Key("A", "minor")
|
||||
for chord in key.progression("i", "iv", "V", "i"):
|
||||
chords.add(chord, Duration.WHOLE)
|
||||
chords.add(chord, Duration.WHOLE)
|
||||
for chord in Key("G", "major").progression("I", "V", "vi", "IV") * 2:
|
||||
piano.add(chord, Duration.WHOLE)
|
||||
|
||||
lead.arpeggio("Am", bars=2, pattern="updown", octaves=2)
|
||||
lead.arpeggio("Dm", bars=2, pattern="updown", octaves=2)
|
||||
lead.set(lowpass=5000, reverb=0.3)
|
||||
lead.arpeggio("E7", bars=2, pattern="up", octaves=2)
|
||||
lead.arpeggio("Am", bars=2, pattern="updown", octaves=2)
|
||||
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
||||
lead.add("G5", 1).add("E5", 1)
|
||||
lead.add("D5", 0.5).add("B4", 0.5).add("A4", 1)
|
||||
lead.add("G4", 2).rest(2)
|
||||
|
||||
for n in ["A2", "E2", "A2", "C3"] * 4:
|
||||
bass.add(n, Duration.QUARTER)
|
||||
for n in ["G2", "G2", "D2", "D2", "E2", "E2", "C2", "C2"] * 2:
|
||||
bass.add(n, Duration.HALF)
|
||||
|
||||
play_score(score)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user