Skip to main content

Add your description here

Project description

songtools

A Python DSL for composing music from code. Notes carry expression, structure carries time. No magic strings, no duplicate logic or state.

Install

uv add songtools

Requires Python 3.14+ and sounddevice (PortAudio backend).

Core idea

Sound knows timbre, never time. Tune owns relative time. play / compile own absolute time (bpm). Combine them with operators and you get a tiny, composable music language.

Abstractions

Name What it is
Sound A voice (sample or synth). Knows timbre, never time.
Key Harmonic context: root + scale + quality.
Tune Anything arranged in time. Pure data — no methods that mutate.
Layer Parallel voices. Shorter sides loop to LCM.
compile() / play() The transport. Owns bpm and output.

Operators

Op Meaning
Tune(a, b, c) Slots of 1 beat; nesting subdivides
Layer(a, b, c) Parallel voices; shorter sides loop to LCM
a + b a, then b (sequence)
a * n repeat
x @ y realize: left plays right, in left's context

None is not used — use REST for a rest inside a Tune.

Vocabulary

  • Notes: KeyRoot.C, KeyRoot.D, ... (chromatic, 0–11)
  • Scales: Scale.MAJOR, Scale.MINOR, Scale.DORIAN
  • Qualities: Quality.TRIAD, Quality.SEVENTH, Quality.NINTH, Quality.SUS2, Quality.SUS4, Quality.POWER
  • Degrees: Degree.I, Degree.II, ... Degree.VII (also used as chord symbols)

Effects

Chain on any Sound with @:

Effect What it does
Gain(amount) Multiply amplitude
Decay(duration) Exponential fade (timedelta)
LowPass(hertz) One-pole lowpass filter
HighPass(hertz) One-pole highpass filter
Echo(seconds) Single-tap delay at −6 dB
Delay(seconds, fb, mix) Feedback delay with dry/wet blend
Gate(seconds) Hard cut to length
FadeIn(seconds) Linear ramp up from silence
FadeOut(seconds) Linear ramp down to silence
Reverse Reverse the buffer
Drive(amount) Tanh saturation
Clip(threshold) Hard-limit peaks
Normalize(peak) Scale so the loudest sample = peak
Tremolo(hertz, depth) Amplitude modulation
BitCrush(bits) Reduce bit depth (lo-fi)
Humanize(velocity) Subtle gain variation

Quick start

1. Generate some sounds

python scripts/sounds.py     # writes pluck.wav, kick.wav, snare.wav, hat.wav

2. Compose

from datetime import timedelta

from songtools.files import WavFile
from songtools.keys import Key
from songtools.layers import Layer
from songtools.sounds import REST, Sound
from songtools.tunes import Tune
from songtools.types import (
    Decay,
    Degree,
    Echo,
    Gain,
    KeyRoot,
    LowPass,
    Pitch,
    Quality,
    Scale,
)

# --- Load or synthesize sounds ---
pluck = WavFile(Path("pluck.wav")).parse(Pitch(60))
kick  = WavFile(Path("kick.wav")).parse(Pitch(60))
hat   = WavFile(Path("hat.wav")).parse(Pitch(60))

# --- Shape them with effects ---
warm_pluck = pluck @ LowPass(1800) @ Decay(timedelta(milliseconds=900)) @ Gain(0.5)
hard_hat   = hat @ Gain(0.3)

# --- Tune to a key ---
key = Key(KeyRoot.C, Scale.MAJOR, Quality.TRIAD)
melody = Tune(
    warm_pluck @ key @ Degree.I,
    warm_pluck @ key @ Degree.IV,
    warm_pluck @ key @ Degree.V,
    warm_pluck @ key @ Degree.I,
)

# --- Arrange ---
beat = Tune(hard_hat, REST, hard_hat, REST) * 4
bass = Tune(kick, REST, kick, REST) * 4

song = Layer(melody, beat, bass)

# --- Render ---
song.compile(beats_per_minute=120).play()          # play live
WavFile(Path("out.wav")).save(song.compile(beats_per_minute=120))  # or save

See scripts/sounds.py and scripts/test.py for full working examples.

Rules of thumb

  • Slot = 1 beat. Want it faster? Nest a Tune. Slower? Spread with REST. Global speed? bpm.
  • Variations are data. Write them: groove * 3 + fill.
  • Quality falls out of Key × degree. Never name a chord's quality — it emerges.
  • Transpose a layer: change one Key constant. Drums don't move.
  • @ chains left-to-right: sound @ key @ Degree.I, then add effects.

Project layout

src/songtools/
  types.py      # Buffer, Pitch, KeyRoot, Scale, Quality, Degree, effects
  sounds.py     # Sound, KeyedSound, REST
  keys.py       # Key (harmonic context)
  tunes.py      # Tune (sequential time)
  layers.py     # Layer (parallel time)
  transports.py # mixdown — the bpm-aware renderer
  files.py      # WavFile — load/save 16-bit mono WAV
scripts/
  sounds.py     # Synthesize pluck, kick, snare, hat
  test.py       # Full song example

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

songtools-0.3.2.tar.gz (8.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

songtools-0.3.2-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

Details for the file songtools-0.3.2.tar.gz.

File metadata

  • Download URL: songtools-0.3.2.tar.gz
  • Upload date:
  • Size: 8.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for songtools-0.3.2.tar.gz
Algorithm Hash digest
SHA256 19d61b4d0ba02a6a14761a4700dfb13c0034ad3a7e39f38a36144c3ac3fa257e
MD5 d9a00aedf41cadfe74d16ab560da1b54
BLAKE2b-256 0ecf33a74fd1a267d4fc530dcd14184a7c6f02444e7d56c9e8a1888ef9169091

See more details on using hashes here.

File details

Details for the file songtools-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: songtools-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 10.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for songtools-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 57a825c2d9ad8f81085d475713a7c6721ee9f3956412d88d4701abee16f1f400
MD5 859664bad5f65a21347d784869e0d74b
BLAKE2b-256 58f921e75add22152f94fc42aa45990d2e8a885a48d2d9e641dd247a51ce3c3c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page