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.0.tar.gz (8.7 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.0-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: songtools-0.3.0.tar.gz
  • Upload date:
  • Size: 8.7 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.0.tar.gz
Algorithm Hash digest
SHA256 c3af0d8e020af3b128bf27d00382587187b42a560e818d70053db3b8d93de651
MD5 bb160a06cbcde7496fd10dfae2a484a4
BLAKE2b-256 cd018e3661720231d76f4174dc058fe73bea3415e565a4d7acf4f2e025b10734

See more details on using hashes here.

File details

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

File metadata

  • Download URL: songtools-0.3.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4e12b37cbf77a4ab17a687fd589ee01f6ca3101b89e2a3f642c74ea1a47175ad
MD5 b763cbce1a4447765d14b17f4cfe065f
BLAKE2b-256 1cb5f7ea4c624b8f559203525f0f087db430901e31ed774161e4ab91b757417c

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