FeelRight is a small, deterministic melody engine (binary name melody). Given a chord progression, a key and
an optional tension curve, it searches for the best melody with a
hand-crafted evaluation function (35 rules, each in its own file under
src/rules/, weights in rules.toml) and bar-by-bar beam search. No
neural nets, no LLM, no audio.
The core mechanic: rules are simple and the engine knows when to break them. Breaking a breakable rule adds tension; the engine spends that tension where the curve asks for it.
cargo build --release
cargo test
melody generate \
--chords "Am Dm E7 Am | F Dm E7 Am" \
--key A:minor --meter 4/4 --tempo 84 --bars 8 \
--tension 0.2,0.3,0.4,0.6,0.3,0.5,0.9,0.2 \
--seed 42 --variants 3 \
--out out.mid --explain
| Flag | Meaning |
|---|---|
--chords |
One chord per bar when the token count equals --bars (then | is a phrase mark). Otherwise | separates bars with one or two chords each. Shorter progressions loop. |
--key |
A:minor, C:major, Am, F# |
--tension |
One value 0..1 per bar. Default: an arch peaking at 70 %. |
--style |
classical (even/dotted rhythms, Alberti bass) or pop (syncopated rhythms, block chords) |
--form |
auto, sentence, period, aaba |
--engine |
beam (default) or random (M1 baseline) |
--rules |
Path to a rules.toml; defaults to ./rules.toml, else the built-in copy |
--variants N |
Writes out-1.mid .. out-N.mid with seeds seed..seed+N |
--explain |
Per-bar target vs observed tension, top contributions, broken rules |
Output MIDI has a conductor track, one melody track per section (velocity follows the tension curve and the phrase shape), accompaniment, bass and, for orchestral styles, a second accompaniment layer.
melody suite --file examples/orkesteri.toml --out orkesteri.mid --explore 6 --explain
A suite file lists [[section]] tables with chords, key, meter,
tempo, bars, tension, seed, instrument, octave, style
(classical, pop, orchestral, brass, concerto, waltz, rapids),
form, and optionally melody (a fixed tune in C4:4 D4:2 r:2 notation,
with transpose), ends_open = true when the section leads into the
next one, bridge = N to append N bars on the dominant of the next
section's key, theme_from = "A" to borrow section A's opening motif
(transformed to this section's key), and rules_override for per-section
weight or parameter changes, e.g.
rules_override = { max_leap = { soft_max = 12 }, density = { weight = 3.0 } }.
--explore N tries N seeds per section and keeps the best by evaluator
score; --target-score X keeps adding batches of N seeds (up to 12)
until every section reaches X; --refine N then rewrites one bar per
round and keeps only improvements. See examples/*.toml.
melody prompt "mahtipontinen orkesterikappale d-molli ABC viulu" --seeds 6 --out piece.mid --save-suite
A deterministic keyword mapper (Finnish and English, no language model)
turns a short description into a suite: mood words set mode, tempo and
energy; style words pick the accompaniment; instrument names, a key such
as d-molli or Bb major, a 120 bpm tempo and section letters (ABC,
ABA, ABCA) are honoured. The evaluator then searches seeds per
section, renders the best, and --save-suite writes the chosen suite as
TOML for hand editing.
The engine itself contains no neural network and no language model. It is a deterministic instrument: the same suite file and seeds always produce the same notes, and every note is checked and explained by the rules.
The intended workflow puts the AI outside the engine, as the composer who writes its instructions:
- You describe the music in plain language to an assistant such as Claude Code, e.g. "a Dvořák-style brass piece, chorale, dance, tutti".
- The assistant writes a suite file (
examples/*.toml): chord progressions, keys, tempos, forms, instruments, per-bar tension curves,theme_fromlinks between sections, bridges, andrules_overridetables that bend the rules toward the style (allow wide leaps for an aria, forbid syncopation for Bach, reward sequences, and so on). - The engine renders it:
melody suite --file piece.toml --explore 8 --out piece.mid. Seed exploration and refinement pick and polish the melody by the evaluator's score. - The assistant reads
--explain(target vs observed tension per bar, broken rules, weak spots) and revises the suite. You listen and steer: "the middle section should breathe more", "make the ending grander". Repeat.
Every piece in examples/ was made exactly this way in one session with
Claude Code. The assistant never wrote a note; it wrote instructions, the
engine wrote the notes.
melody prompt "..." is a small built-in stand-in for step 2 (a keyword
table, Finnish and English) for use without an assistant. melody rate
and melody tune close the loop on taste: your A/B choices and a corpus
of known melodies re-weight rules.toml.
A minimal script that automates step 2 with the Claude API:
import anthropic, subprocess
client = anthropic.Anthropic()
context = open("README.md").read() + open("examples/orkesteri2.toml").read()
msg = client.messages.create(
model="claude-sonnet-5", max_tokens=2000,
system="You write FeelRight suite files. Reply with TOML only.
" + context,
messages=[{"role": "user", "content": "a sad cello piece, ABA, E minor"}],
)
open("piece.toml", "w").write(msg.content[0].text)
subprocess.run(["melody", "suite", "--file", "piece.toml", "--out", "piece.mid", "--explore", "8"])src/theory.rs,src/parser.rs,src/model.rs— pitch classes, chords, keys, grid, notessrc/generate.rs— rhythm libraries and the random baselinesrc/motif.rs— motif extraction and transformationssrc/form.rs— form templates and bar rolessrc/search.rs— beam searchsrc/rules/— one file per rule,Ruletrait inmod.rssrc/tension.rs— default curve, observed tension, match termsrc/suite.rs— suite file format, rendering, seed explorationsrc/prompt.rs— keyword translator from text to suitesrc/midi.rs— MIDI writerrules.toml— all weights and parametersexamples/out/— generated examples with their explanations
PolyForm Noncommercial 1.0.0, see LICENSE. Free for personal, hobby, research, educational and other noncommercial use; commercial use needs a separate licence from the author.
Copyright (c) 2026 Joose Hotari.