cmt/oworpheus: a tiny music programming language - Forgejo: If it works, we don't run it
This website requires JavaScript.
cmt/oworpheus
Watch
Star
Fork
You've already forked oworpheus
Code
Issues
Pull requests
Projects
Releases
Packages
Wiki
Activity
Actions
a tiny music programming language
3 commits
1 branch
0 tags
93 KiB
Rust
100%
main
Find a file
HTTPS
Download ZIP<br>Download TAR.GZ<br>Download BUNDLE
Open with VS Code
Open with VSCodium
Open with Intellij IDEA
Exact
Exact
Union
RegExp
cmt
77c6186ea4
Update SPEC.md
2026-06-10 20:58:50 -07:00
examples
Initial commit
2026-06-10 20:55:23 -07:00
src
Initial commit
2026-06-10 20:55:23 -07:00
.gitignore
Initial commit
2026-06-10 20:55:23 -07:00
Cargo.lock
Initial commit
2026-06-10 20:55:23 -07:00
Cargo.toml
Initial commit
2026-06-10 20:55:23 -07:00
LICENSE
Initial commit
2026-06-10 20:55:23 -07:00
README.md
Initial commit
2026-06-10 20:55:23 -07:00
SPEC.md
Update SPEC.md
2026-06-10 20:58:50 -07:00
README.md
oworpheus
A tiny music programming language. Write .owo source files in a C-like syntax,<br>compile them straight to .wav.
cargo build --release<br>target/release/oworpheus examples/demo.owo<br># wrote examples/demo.wav (4.66s @ 44100 Hz)<br>examples/ also has two simple chiptune covers to play with: mario.owo<br>(Super Mario Bros. overworld, square lead + triangle bass) and zelda.owo<br>(Zelda overworld theme, detuned saw "brass").
Language
The normative language definition is SPEC.md; this<br>section is a working overview.
An .owo file has four kinds of top-level items: tempo, instrument,<br>track, and play. Comments are // and /* ... */.
tempo 110;
instrument lead {<br>let vib = sine(5) * 4; // 5 Hz LFO, +/- 4 Hz vibrato<br>let body = saw(freq + vib) * 0.5<br>+ sine(freq * 2) * 0.2; // octave-up sine layer<br>out body * adsr(0.02, 0.15, 0.6, 0.3) * amp;
track melody uses lead {<br>note E4 1/2; // pitch, duration in beats<br>note G4 1/2 0.9; // optional velocity 0..1 (default 0.8)<br>chord [C4, E4, G4] 2; // several pitches at once<br>rest 1;
play melody;<br>tempo
tempo ; sets the global tempo. Defaults to 120 if omitted.
instrument
An instrument describes how one note sounds, as a signal expression evaluated<br>per sample. It is a sequence of let bindings followed by exactly one out.
Inside an instrument these names are predefined for the note being played:
name<br>meaning
freq<br>pitch in Hz
amp<br>velocity from the score, 0..1
time<br>seconds since the note started
dur<br>the note's held duration in seconds
Signal generators:
sine(hz), saw(hz), square(hz), triangle(hz) — oscillators. The<br>argument is itself a signal, so oscillators can modulate each other:<br>sine(freq + sine(5) * 4) is vibrato, saw(freq * 1.01) is a detuned layer.
noise() — white noise.
adsr(attack, decay, sustain, release) — envelope, times in seconds,<br>sustain as a 0..1 level. Release plays out after the note's duration ends,<br>so notes ring past their slot in the score. Arguments must be constants.
Expressions support + - * /, unary minus, parentheses, and number literals.<br>let bindings are evaluated once per sample and shared, so a bound noise()<br>or oscillator is a single generator no matter how often it is referenced.
track
track uses { ... } is a sequence of timed events:
note [velocity];
chord [, , ...] [velocity];
rest ;
Pitches are written C4, F#3, Bb2 (A4 = 440 Hz). Durations are in beats<br>and may be fractions (1/2, 3/4) or decimals (0.25).
play
play [, ...]; mixes the named tracks together starting at<br>time zero. With no play statement, all tracks play. The final mix is<br>peak-normalized only if it would clip.
Output
Mono 16-bit PCM WAV at 44100 Hz. oworpheus song.owo writes song.wav;<br>use -o to pick another path.