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 pip install -e .

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.1.0.tar.gz (8.2 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.1.0-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: songtools-0.1.0.tar.gz
  • Upload date:
  • Size: 8.2 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.1.0.tar.gz
Algorithm Hash digest
SHA256 bd66a7efc2fef8a0497ea8ab128ad503b17970b3bf08f8f6025986b3f8941e87
MD5 398e02a7aad9edc9bf35390463a5b1a5
BLAKE2b-256 8f8ed6e9599bf258aadfe8f26135521596c6643e0eaf68bb74cce87195d7d5f2

See more details on using hashes here.

File details

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

File metadata

  • Download URL: songtools-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.2 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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3590264288bd7bd9c197c35306e5a2386d49a571324804f9a378f4505fb270a8
MD5 172f8d5407f667a052428c8eed2a4091
BLAKE2b-256 8826cdf8593fc10963a833581056671cb29a59a509c469c3792b561e478b5393

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