Skip to content

chinarjoshi/ChINAR

Repository files navigation

ChucK is hard to language model; TOML is trivial. This is a high-level TOML DSL for writing simple musical “pieces” and a complete end-to-end pipeline that compiles them to ChucK. The core idea is that the DSL is intentionally higher abstraction than ChucK source: instead of writing UGens and timing and whatnot directly, you describe musical intent in a structured form that can be reasoned about and transformed semantically. The DSL is backed by a Piece protobuf that models the essential musical units (notes+patterns), voice structure, and timbre configuration (oscillator, envelope, filter, LFO, gain, reverb), plus a global “complexity” knob.

On the front-end, a TOML parser reads the DSL into a dsl::Piece message with good validation and diagnostics. It performs explicit mapping from TOML tables/arrays into the proto and handles string-to-enum conversions validates ranges like MIDI pitch. To keep iteration time low, it also adds small standalone tools in src/core/dsl: dsl_dump for inspecting the parsed proto and dsl_codegen for generating ChucK from a TOML file without rebuilding the full Chuck binary.

The lowering step is intentionally template-style code generation rather than AST construction. From a Piece, dsl_codegen emits a plain ChucK program that sets up tempo, declares one function per voice, builds an appropriate UGen chain (osc → filter → ADSR → reverb → pan → dac), and schedules playback by iterating pattern notes with ADSR keyOn/keyOff and beat-scaled durations. Pattern-level knobs like density and repeat are reflected in the generated code, and the codegen avoids illegal operations (e.g., .freq assignment for Noise). This keeps the compiler integration simple because generated code goes through the existing ChucK compilation path.

The main value-add beyond “TOML that turns into ChucK” is the middle-end optimizer: a semantic transformation engine that operates on the proto, not on text. A sort of “creative intent” slider. Given a well-formed Piece, it returns a different Piece that preserves its identity while smoothly shifting it toward “simpler” or “richer” variants. It’s implemented as a pipeline of small, orthogonal rewrite passes, each sensitive to the same global complexity curve: Pattern Density, Pitch Complexity, Rhythmic Complexity, and Timbre Complexity. When simplifying, the passes reduce note density without destroying contour and collapses large leaps, merges rhythms, and simplify timbre (like less detune/LFO/reverb, smoother envelopes). When expanding, the same passes add variation like neighbor/approach tones, passing tones and octave displacement, subdivisions and off-beats, and more lively timbre. The optimizer is deterministic right now to make iteration and testing stable, and it outputs valid DSL TOML again to enable repeated optimize → codegen → compile cycles.

These steps are tested E2E in the flake env:

# enter dev env (protobuf, g++, alsa-lib, libsndfile, pulseaudio, jack2)
nix develop
make

# 1) Parse TOML -> proto (prints proto)
./dsl_dump test/beethoven.toml

# 2) TOML -> ChucK (writes simple.ck in the current dir by default)
./dsl_codegen test/beethoven.toml
./chuck --syntax beethoven.ck

# or explicitly choose output
./dsl_codegen test/beethoven.toml -o /tmp/beethoven.ck
./chuck --syntax /tmp/beethoven.ck

# 3) Optimize TOML -> TOML, then codegen -> ChucK, then syntax-check
./dsl_opt test/beethoven.toml --complexity 0.2 -o /tmp/opt.toml
./dsl_codegen /tmp/opt.toml -o /tmp/opt.ck
./chuck --syntax /tmp/opt.ck

About

ChucK Integrated NL->Audio Rectifier

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages