devalang 0.2.0

Write music with code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text.
Documentation
<div align="center">
    <img src="https://devalang.com/images/devalang-logo-min.png" alt="Devalang Logo" width="100" />
</div>

# Changelog


All notable changes to Devalang will be documented in this file.

## Version 0.2.0 - 2026-06-11


### 🚀 What's New


- Implemented the new audio graph core used by routing and rendering.
  - Added explicit audio nodes, routes, route kinds, master node normalization, route validation, and graph optimization.
  - `$master` and `master` are now normalized internally to the same output node.
- Added `eq` / `equalizer` as a built-in effect available for synths and triggers.
  - Supports low, mid, and high gain processing.
- Added shared runtime duration conversion for parsed durations.
  - Supports milliseconds, beat fractions, normalized temporal identifiers, numeric durations, and `auto`.
- Added enhanced pattern symbol handling.
  - `x`: normal hit.
  - `X`: accented hit.
  - `o`: soft hit.
  - `.`: visual placeholder.
  - `-`: silence.
- Added pattern shorthand parsing.
  - Syntax: `<pattern_name>: <target> = "<pattern>"`
  - Syntax with options: `<pattern_name>: <target> -> swing(0.08) -> velocity(0.7) = "<pattern>"`
- Added live-coding friendly synth chaining aliases.
  - `wave(...)` / `osc(...)` normalize to waveform configuration.
  - `preset(...)` normalizes synth preset/type selection.
  - `env({...})` normalizes ADSR envelope configuration.
  - `voices(...)` configures polyphony.

### 🛠️ Improvements


- Refactored the audio interpreter driver to split collection, extraction, pattern execution, rendering, and graph rendering more clearly.
- Improved routing rendering so events can resolve their graph node from explicit routing declarations, pattern sources, and fallback routing.
- Improved effect-chain handling for structured parameter maps and trigger-specific processing.
- Improved MIDI export and audio event collection with stronger validation of timings and values.
- Improved parser robustness:
  - `//` comment lines are now accepted in addition to `#`.
  - UTF-8 BOM prefixes are stripped when parsing files.
  - Unknown statements with close keyword matches now carry structured suggestions.
  - `else` bodies are attached more reliably to their preceding `if`.
- Updated examples to demonstrate the v0.2.0 synth chain, routing master normalization, and enhanced pattern symbols.
- Updated package versions to `0.2.0` across Cargo, npm, and `project-version.json`.

### 🐛 Bug Fixes


- Fixed the npm/TypeScript package build by keeping the TypeScript entrypoints required by `package.json`, `tsconfig.json`, the npm binary wrapper, and published type declarations.
- Fixed `npm run ts:build`, which otherwise failed because the TypeScript source entrypoints were missing.
- Fixed release-readiness checks for the documented npm flow by verifying TypeScript declarations and the combined npm build.
- Fixed formatting drift by running `cargo fmt`.

## Version 0.1.9 - 2026-05-27


### 🐛 Bug Fixes


- Fixed the postinstall script for npm users to correctly download the binary from GitHub releases and place it in the expected location for the CLI to work. The previous implementation had issues with the download URL and file handling, which have now been resolved.

## Version 0.1.8 - 2025-11-19


### 🚀 What's New


- Implemented `stop`, `mute` and `silence` (aliases) to immediately stop audio playback of specified entities or all if no entity is specified. This statement supports scoping when inside blocks.
  - Syntax: `<stop | mute | silence> <entity> after <duration>` or `<stop | mute | silence> all after <duration>`.
- Implemented `fade` statement to automate parameter fading during a specified duration.
  - Syntax: `fade <in | out> <entity>.<property> during <duration>`
- Implemented `section` block to organize code into named sections with specified duration.
  - Syntax: `section <name> during <opt_duration>:`
- Implemented `timeline` statement to define linear playback order of sections. This simplifies complex event scheduling by allowing you to list `sections` to be played in defined order (auto sequential calling) but **blocks the flow until section completion**.
  - Syntax: `timeline [<section_name> during <opt_duration>, ...]`
- Implemented `schedule` block for time-based execution control. Unlike `timeline` which defines the order of sections, `schedule` allows executing any statement at specific times. Unlike `timeline`, `schedule` **does not block the flow**.
- Syntax example:
  ```deva
  schedule:
      at 0 bars:
          ...
      at 2 bars and during 4 bars:
          ...
      ...
  ```
- Implemented `&&` (aliased `and`), `||` (aliased `or`), and `!` (aliased `not`) logical operators for conditions in conditions statements.
- Implemented `while` loop statement block for repeated execution based on a condition.
  - Syntax: `while <condition>:`
- Added `swing(<float>)` parameter to apply swing effect to synths and patterns.
- Added `humanize({ amount: <float> })` parameter to apply humanization effect to synths and patterns.
- Added `fade({ from: <float>, to: <float>, duration: <duration>})` parameter to apply fade effect to synths and patterns.
- Added `step` duration unit to specify step duration in musical time (e.g. `sleep 1/16 step` for a sixteenth note).

### 🛠️ Improvements


- Improved `group` statement to accept optional duration parameter. (similar to `section` but locally, and cannot be called from `timeline` (use `section` instead)).
  - Syntax: `group <name> during <duration>:`
- Improved `on` events to support values inside `beat(<int>)` and `bar(<int>)` for more precise timing.
- Improved `loop` statement to support other durations units for max timeout (e.g. `loop pass(4 bars):`).
- Improved `routing` system to auto-route audio nodes and patterns to `$master` if not explicitly defined in routing.

### 🐛 Bug Fixes


- Fixed `play` command not respecting `--input` flag (wrong module called).
- Fixed `routing` ducking not applying reduction correctly.

## Version 0.1.7 - 2025-11-05


### 🚀 What's New


- Implemented `routing` capabilities to route audio between different nodes and effects in the audio graph.
  - Added `node` definitions to create audio nodes linked variables (synths, groups, triggers...).
  - Added `fx` definitions to apply global effects to nodes.
  - Added `route` definitions to connect nodes and effects together.
  - Added `duck` definitions to create sidechain ducking between nodes.
  - Added `sidechain` definitions to create sidechain compression between nodes.
- Implemented `bpm` and `tempo` block syntax to set tempo within specific blocks.
- Added more duration units and specified their usage:
  - `ms`: milliseconds (e.g. `sleep 500ms` for 500 milliseconds)
  - `s`: seconds (e.g. `sleep 2s` for 2 seconds)
  - `beat`: musical beat based on current BPM (e.g. `sleep 1 beat` for 1 beat)
  - `bar`: musical bar based on current BPM (e.g. `sleep 1 bar` for 1 bar)
  - `measure`: musical measure based on current BPM (e.g. `sleep 1 measure` for 1 measure)
- Added `--no-rule` flag to the `build`, `check`, and `play` commands to disable rule checking.
- Added rule checker system with severity levels:
  - `error`: for critical issues that require immediate attention.
  - `warning`: for potential issues that may not be critical but should be addressed.
  - `info`: for informational messages that may be helpful for understanding code behavior.
  - `off`: to disable specific rules if not needed.

### 🛠️ Improvements


- Improved `on` events to support duration types and frequencies (e.g. `on every beat:`, `on once per bar:`).
- Improved error logging with structured error details including file location, error type, and suggestions.
- Improved loop range handling for using without explicit array (e.g. `for i in 1..5:` to loop 5 times).
- Aliased `call` by `sequence` and `spawn` by `layer` for better readability.
- Added `foreach` alias for `for` loops for better readability.
- Added `rest` and `wait` aliases for `sleep` statement for better readability in musical context.
- Added `tempo` as an alias for `bpm` statement for better readability.
- Removed `@` in `@import`, `@export`, `@load` and `@use` statements for cleaner syntax.
- Removed deprecated `var` keyword; use `let` instead.

## Version 0.1.6 - 2025-10-31


### 🚀 What's New


- Re-implemented auto binary download for NPM users in the postinstall script.
- Implemented for `loop` statement the support of `pass(<duration_ms>)` mode to run loop in background for a specified optional duration in ms (e.g. `loop pass(5000):`).
- Implemented for `for` and `loop` statements the support of `break` keyword inside their body to exit the loop early.
- Implemented `function` definition to create reusable code blocks with parameters.
  - Syntax: `function <function_name>(<arg1>, <arg2>, ...):`
  - Call syntax: `call <function_name>(<arg1_value>, <arg2_value>, ...)`
  - Return syntax: `return <value>`
- Implemented new common effects :
  - `dist({ amount: <float>, color: <float>, mix: <float> })`: applies distortion effect.
  - `bitcrush({ depth: <int>, sample_rate: <int>, mix: <float> })`: applies bitcrushing effect.
  - `lowpass(...)` or `lpf({ cutoff: <float>, resonance: <float> })` : applies low-pass filter effect.
  - `highpass(...)` or `hpf({ cutoff: <float>, resonance: <float> })` : applies high-pass filter effect.
  - `bandpass(...)` or `bpf({ cutoff: <float>, resonance: <float> })` : applies band-pass filter effect.
  - `tremolo({ rate: <float>, depth: <float>, sync: <bool> })`: applies tremolo effect.
  - `vibrato({ rate: <float>, depth: <float>, sync: <bool> })`: applies vibrato effect.
  - `monoizer({ enabled: <bool>, mix: <float> })`: set to mono (one channel) the audio signal.
  - `stereo({ width: <float> })`: applies stereo widening effect.
  - `freeze({ enabled: <bool>, fade: <float>, hold: <float> })`: applies a freeze effect to the audio.
  - ... full list in documentation.
- Implemented trigger-specific effects :
  - `reverse(<bool>)`: reverses the audio sample when set to true.
  - `speed(<float>)`: changes the playback speed of the audio sample.
  - `slice({ segments: <int>, mode: <"sequential" | "random">, crossfade: <float> })`: slices the audio sample into segments and plays them back in the specified mode.
  - `stretch({ factor: <float>, pitch: <int>, formant: <bool> })`: time-stretches the audio sample by the specified factor.

    > Note: the current `stretch` processor in this release implements a simple resampling-based time-stretch. The exposed `pitch` and `formant` parameters are accepted by the API but advanced pitch/formant-preserving algorithms (e.g. PSOLA or phase-vocoder) are not implemented in this version, they are reserved for future improvements.

  - `roll({ duration_ms: <int>, sync: <bool>, repeats: <int>, fade: <float> })`: applies a roll effect to the audio sample.

- Implemented synth-specific effects :
  - `adsr({ attack: <float>, decay: <float>, sustain: <float>, release: <float> })`: applies ADSR enveloppe for the synth
  - `type(<"bass" | "pad" | "pluck" | "lead">)`: defines the type of synth

### 🛠️ Improvements


- Moved unit tests to dedicated files next to source files for better organization.
- Added more unit tests for new features and edge cases.
- Improved `loop` statement to support infinite loops when no number is provided (e.g. `loop:`).
- Improved `on` events to support `bar(<int>)`, `beat(<int>)` for more precise timing.
- Refactored example scripts to use new features and best practices.
- Synths and triggers now supports chained params. See [effect examples]../examples/scripts/effect.deva for more details.
- Triggers can now be stored in variables. (e.g. `let myTrigger = .myBank.kick -> velocity(100) -> duration(500)`).
- Properties of objects can now be **accessed** and **modified** using dot notation. (e.g. `mySynth.volume`, `myTrigger.reverse`).
- Array items can now be accessed using bracket with index. (e.g. `myArray[0]` to access the first item).
- Arrays are now stored as a map under form `{ index: 0, value: <statement> }` to allow easier access and modification of items.
- Properties and array items can be **combined** using dot and bracket notation. (e.g. `myObject.myArray[2].volume`).
- Added arithmetic operators support `i + 1`, `i++`, `i - 1`, `i--`, `i * 2`, `i / 2`, `i % 2` inside loops and functions.
- Improved `print` statement to support printing variables, expressions, and complex objects.

### 🐛 Bug Fixes


- Fixed issues with `if`, `else if`, and `else` conditions that were not evaluated correctly.
- Fixed issues with `for` loops that were not iterating as expected.
- Fixed issues with `print` statements not displaying correct outputs and not handling variables properly.

## Version 0.1.5 - 2025-10-24


### 🚀 What's New


#### Automation [(See automation examples for more details)]../examples/automation


Get more expressive with parameter automation using curves !

- Implemented `automate <instrument_name> mode <global|note>:` statement to automate parameters using curves.
  - **Global mode**: automates all notes played by the instrument.
  - **Notes mode**: automates each of the notes defined inside the block.
- Implemented built-in curves:
  - `$curve.linear` : default linear curve.
  - `$curve.easeIn` : quadratic ease-in curve.
  - `$curve.easeOut` : quadratic ease-out curve.
  - `$curve.easeInOut` : quadratic ease-in-out curve.
- Implemented advanced curves:
  - `$curve.swing(<intensity>)` : sinusoidal swing curve with optional intensity (0.0 to 1.0).
  - `$curve.bounce(<height>)` : bouncing curve with optional height (0.0 to 1.0).
  - `$curve.elastic(<intensity>)` : elastic curve with optional intensity (0.0 to 2.0+).
  - `$curve.bezier(<x1>, <y1>, <x2>, <y2>)` : cubic Bezier curve with 4 control points.
  - `$curve.step(<steps>)` : stepped curve with specified number of steps.
  - `$curve.random` : random curve generating random values between 0.0 and 1.0.
  - `$curve.perlin` : Perlin noise curve for organic variations.
- Implemented LFO support in synths for modulation effects.
  - Added `lfo` parameter in synth definitions with `rate`, `depth`, `shape`, and `target` options.

### 🐛 Bug Fixes


- Fixed MIDI rendering issue that was rendering empty MIDI files.

## Version 0.1.4 - 2025-10-14


### 🚀 What's New


#### MIDI devices support I/O (beta) [(See mapping examples for more details)]../examples/routing/mapping.deva


Generate notes and patterns using your favorite MIDI controller !

- Implemented MIDI devices `mapping` statement inside scripts for real-time control.
  - Added `mapping` global object to access MIDI device information and states.
  - Added `on` event handler to react to MIDI events in scripts. (e.g. `on mapping.out.<device_name>.noteOn:`).
  - Possibility to bind `pattern` and `synth` from MIDI events. (e.g. `bind <pattern_name> -> mapping.out.<device_name> with { port: 1, channel: 10 }`).
- Implemented MIDI devices `list` command in the CLI to list available MIDI devices.
- Implemented MIDI devices `preview` command in the CLI to preview MIDI data from a device.
  - Added `--port <port_number>` to preview MIDI data from a specific port.
- Implemented MIDI devices `write` command in the CLI to write MIDI data inside scripts.
  - Added `--out <file_path>` to specify the file to write in.
  - Added `--mode <mode>` to specify the write mode ('synth' or 'pattern').
  - Added `--port <port_number>` to specify the input port.
  - Added `--step <step>` to specify the step duration as fraction. (e.g. `1/16` for sixteenth note).
  - Added `--bpm <bpm>` to specify the BPM for the MIDI file (default: 120).
  - Added `--live` flag to write MIDI data in real-time (default: false).
  - Added `--rewrite` flag to overwrite existing file (default: false).
  - Added `--velocity-zero-as-rest` flag to write notes with velocity 0 as `rest/sleep` (default: true).
  - **Synth mode**: writes MIDI note events as `synth` calls.
    - Added `--synth-name <synth_name>` to specify the synth name.
    - Added `--waveform <waveform>` to specify the waveform for the generated synth (default: "saw").
    - Added `--group <group_name>` to specify the group name for the generated synth.
  - **Pattern mode**: writes MIDI note events as `pattern` definitions.
    - Added `--pattern-name <pattern_name>` to specify the pattern name.
    - Added `--trigger <trigger_name>` to specify the trigger name for the generated pattern.

### 🛠️ Improvements


- Added support for notes like "Bb4" [(see issue #3)]https://github.com/devaloop-labs/devalang/issues/3.
- Removed temporary debug `println!` statements and replaced useful logs with structured `Logger` calls.
- Fixed unused imports and variables warnings in MIDI native module.

### 🐛 Bug Fixes


- Fixed issue with inline `pattern` not working as expected.
- Fixed `synth` and `pattern` not working when called together with `spawn`.
- Patched CLI dependencies to include mp3lame and exclude it from the WASM build.

## Version 0.1.3 - 2025-10-08


### 🛠️ Improvements


- Improved addon management in the CLI.
  - Use `devalang addon discover --local --install` to discover and install local **gzipped** addons (banks, plugins, etc.).
- Modularized audio interpreter to improve maintainability and extensibility.
- Re-implemented plugin usage in scripts with `@use <author>.<name> as <optional_alias>` syntax.
  - Usage with synth: `let mySynth = synth myPlugin.synthVar = { waveform: "saw", ... }`
- Re-implemented `@load` statement usable with both audio files (**.wav, .mp3, .ogg, .flac**) and MIDI (**.mid**) files.
- Re-implemented `@import` and `@export` statements for modularizing scripts.
- Implemented audio export to **MP3** format.

### 🐛 Bug Fixes


- Fixed triggers not playing when classic call syntax was used
- Fixed unwrap without checking error when parsing wav files
- Fixed unwrap without checking error when parsing print statements

## Version 0.1.2 - 2025-10-05


### 🛠️ Improvements


- Added installers for Windows, macOS and Linux in CI
- Implemented bank samples lazy loading
- Added `pattern` support (inline, direct and with options)
- Added on-the-fly assignation for `synth`

### 🐛 Bug Fixes


- Fixed some not working examples

## Version 0.1.1 - 2025-10-04


Complete rewrite of the codebase with improved architecture, performance, and developer experience.

### 🚀 What's New


- **Audio engine overhaul** with better timing and scheduling
- **Audio rendering** improvements for lower latency and higher fidelity
- **Complete codebase refactoring** for better maintainability and performance
- **Improved architecture** with cleaner separation of concerns
- **Parallel spawn execution** using Rayon for better performance
- **Enhanced type system** with better error handling
- **Modern CLI** with intuitive commands

### 🐛 Bug Fixes


- Fixed issues with audio playback synchronization
- Resolved memory leaks in the WASM module
- Improved error handling for invalid input

### 🛠️ Breaking Changes


- No more config inside note/chord functions; use chained parameters instead
- Config file format now supports JSON and TOML
- Some internal APIs have changed; refer to the updated documentation for details
- CLI commands have been updated; refer to the new documentation for details
- Removed deprecated features and modules for a cleaner codebase
- Removed deprecated devalang\_\* crates; use the main crate with feature flags instead

---

<div align="center">
    <strong>Made with ❤️ by <a href="https://labscend.studio">Labscend Studios</a></strong>
    <br />
    <sub>Star ⭐ the repo if you like it !</sub>
</div>