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.1.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.1-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: songtools-0.3.1.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.1.tar.gz
Algorithm Hash digest
SHA256 6544d667a6e43543f905f1cc0138c41fa7c940686280875196e057b5f2004362
MD5 8308f874c2055fc89f0c56dbd1273714
BLAKE2b-256 7e63b20e7d66bca47249f40928a4e01bbf5425391eee8e077f97080c0e11c127

See more details on using hashes here.

File details

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

File metadata

  • Download URL: songtools-0.3.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 657e570266d709287b0ceee7f8909e8b4194cc30f227d0abaa8a3947bf1e4533
MD5 6f9a960c6968f53616d1a2c8cfad8b0d
BLAKE2b-256 b38a5042ee7801c0e8f655b0eba52733a2d7d7fae0d86eb764f1df349e5b69d7

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