@devaloop/devalang 0.0.1-beta.2 → 0.0.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/Cargo.toml +84 -81
  2. package/README.md +3 -2
  3. package/docs/CHANGELOG.md +41 -0
  4. package/docs/ROADMAP.md +3 -3
  5. package/examples/chain.deva +19 -0
  6. package/examples/plugin.deva +10 -10
  7. package/examples/routing.deva +23 -0
  8. package/out-tsc/bin/project-version.json +6 -0
  9. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -8
  10. package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
  11. package/out-tsc/scripts/version/copy-to-binary.js +79 -0
  12. package/package.json +23 -10
  13. package/project-version.json +3 -3
  14. package/rust/bindings/Cargo.toml +9 -0
  15. package/rust/bindings/src/lib.rs +86 -0
  16. package/rust/cli/addon/commands.rs +35 -0
  17. package/rust/cli/addon/download.rs +234 -0
  18. package/rust/cli/addon/install.rs +33 -0
  19. package/rust/cli/addon/list.rs +224 -0
  20. package/rust/cli/addon/metadata.rs +124 -0
  21. package/rust/cli/addon/mod.rs +8 -0
  22. package/rust/cli/addon/remove.rs +271 -0
  23. package/rust/cli/addon/update.rs +305 -0
  24. package/rust/cli/{install/addon.rs → addon/utils.rs} +109 -118
  25. package/rust/cli/build/commands.rs +153 -153
  26. package/rust/cli/build/process.rs +165 -165
  27. package/rust/cli/check/mod.rs +208 -208
  28. package/rust/cli/discover/commands.rs +275 -253
  29. package/rust/cli/discover/config.rs +109 -111
  30. package/rust/cli/discover/fs.rs +19 -19
  31. package/rust/cli/discover/install.rs +214 -103
  32. package/rust/cli/discover/metadata.rs +48 -48
  33. package/rust/cli/discover/mod.rs +5 -5
  34. package/rust/cli/me/commands.rs +52 -0
  35. package/rust/cli/me/mod.rs +1 -0
  36. package/rust/cli/mod.rs +12 -12
  37. package/rust/cli/parser.rs +30 -69
  38. package/rust/cli/play/commands.rs +375 -375
  39. package/rust/cli/play/process.rs +159 -159
  40. package/rust/core/audio/engine/driver.rs +19 -2
  41. package/rust/core/audio/engine/export.rs +169 -169
  42. package/rust/core/audio/engine/mod.rs +56 -56
  43. package/rust/core/audio/engine/notes/dsp.rs +88 -85
  44. package/rust/core/audio/engine/notes/mod.rs +53 -44
  45. package/rust/core/audio/engine/notes/params.rs +294 -294
  46. package/rust/core/audio/engine/sample/insert.rs +148 -47
  47. package/rust/core/audio/engine/sample/mod.rs +40 -40
  48. package/rust/core/audio/engine/sample/padding.rs +170 -170
  49. package/rust/core/audio/evaluator/condition.rs +61 -61
  50. package/rust/core/audio/evaluator/numeric.rs +152 -152
  51. package/rust/core/audio/evaluator/rhs.rs +16 -16
  52. package/rust/core/audio/evaluator/string_expr.rs +94 -94
  53. package/rust/core/audio/interpreter/driver.rs +574 -574
  54. package/rust/core/audio/interpreter/mod.rs +2 -2
  55. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +9 -5
  56. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -384
  57. package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
  58. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +1 -0
  59. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +66 -11
  60. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -3
  61. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -192
  62. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -24
  63. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -116
  64. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -97
  65. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -100
  66. package/rust/core/audio/interpreter/statements/automate.rs +16 -16
  67. package/rust/core/audio/interpreter/statements/call.rs +31 -1
  68. package/rust/core/audio/interpreter/statements/condition.rs +72 -72
  69. package/rust/core/audio/interpreter/statements/function.rs +24 -24
  70. package/rust/core/audio/interpreter/statements/let_.rs +36 -36
  71. package/rust/core/audio/interpreter/statements/load.rs +17 -17
  72. package/rust/core/audio/interpreter/statements/loop_.rs +115 -115
  73. package/rust/core/audio/interpreter/statements/spawn.rs +51 -2
  74. package/rust/core/audio/interpreter/statements/trigger.rs +242 -239
  75. package/rust/core/audio/loader/trigger.rs +98 -98
  76. package/rust/core/audio/player.rs +70 -70
  77. package/rust/core/audio/special/mod.rs +9 -9
  78. package/rust/core/builder/mod.rs +129 -129
  79. package/rust/core/debugger/lexer.rs +27 -27
  80. package/rust/core/debugger/logs.rs +52 -52
  81. package/rust/core/debugger/preprocessor.rs +27 -27
  82. package/rust/core/debugger/store.rs +38 -38
  83. package/rust/core/lexer/driver.rs +59 -59
  84. package/rust/core/lexer/handler/arrow.rs +82 -82
  85. package/rust/core/lexer/handler/at.rs +21 -21
  86. package/rust/core/lexer/handler/brace.rs +41 -41
  87. package/rust/core/lexer/handler/colon.rs +21 -21
  88. package/rust/core/lexer/handler/comment.rs +30 -30
  89. package/rust/core/lexer/handler/dot.rs +21 -21
  90. package/rust/core/lexer/handler/driver.rs +337 -337
  91. package/rust/core/lexer/handler/identifier.rs +47 -47
  92. package/rust/core/lexer/handler/indent.rs +66 -66
  93. package/rust/core/lexer/handler/mod.rs +15 -15
  94. package/rust/core/lexer/handler/newline.rs +23 -23
  95. package/rust/core/lexer/handler/number.rs +31 -31
  96. package/rust/core/lexer/handler/operator.rs +46 -46
  97. package/rust/core/lexer/handler/parenthesis.rs +41 -41
  98. package/rust/core/lexer/handler/slash.rs +21 -21
  99. package/rust/core/lexer/handler/string.rs +63 -63
  100. package/rust/core/lexer/mod.rs +3 -3
  101. package/rust/core/mod.rs +9 -9
  102. package/rust/core/parser/driver/block.rs +111 -111
  103. package/rust/core/parser/driver/cursor.rs +82 -82
  104. package/rust/core/parser/driver/driver_impl.rs +21 -1
  105. package/rust/core/parser/driver/mod.rs +6 -6
  106. package/rust/core/parser/driver/parse_array.rs +120 -120
  107. package/rust/core/parser/driver/parse_map.rs +247 -223
  108. package/rust/core/parser/driver/parser.rs +160 -160
  109. package/rust/core/parser/handler/arrow_call.rs +65 -14
  110. package/rust/core/parser/handler/identifier/synth.rs +171 -135
  111. package/rust/core/parser/handler/mod.rs +9 -9
  112. package/rust/core/parser/handler/pattern.rs +24 -1
  113. package/rust/core/plugin/loader.rs +137 -137
  114. package/rust/core/plugin/mod.rs +2 -2
  115. package/rust/core/plugin/runner/non_wasm.rs +481 -297
  116. package/rust/core/plugin/runner/wasm32.rs +1 -0
  117. package/rust/core/preprocessor/loader/inject.rs +313 -278
  118. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -110
  119. package/rust/core/preprocessor/loader/mod.rs +235 -235
  120. package/rust/core/preprocessor/module.rs +55 -55
  121. package/rust/core/preprocessor/processor/handlers.rs +107 -107
  122. package/rust/core/preprocessor/resolver/bank.rs +49 -49
  123. package/rust/core/preprocessor/resolver/call.rs +124 -124
  124. package/rust/core/preprocessor/resolver/condition.rs +95 -95
  125. package/rust/core/preprocessor/resolver/driver.rs +324 -324
  126. package/rust/core/preprocessor/resolver/function.rs +69 -69
  127. package/rust/core/preprocessor/resolver/group.rs +122 -122
  128. package/rust/core/preprocessor/resolver/let_.rs +32 -32
  129. package/rust/core/preprocessor/resolver/loop_.rs +318 -318
  130. package/rust/core/preprocessor/resolver/mod.rs +16 -16
  131. package/rust/core/preprocessor/resolver/pattern.rs +95 -83
  132. package/rust/core/preprocessor/resolver/spawn.rs +99 -99
  133. package/rust/core/preprocessor/resolver/synth.rs +54 -54
  134. package/rust/core/preprocessor/resolver/tempo.rs +48 -48
  135. package/rust/core/preprocessor/resolver/trigger.rs +116 -116
  136. package/rust/core/preprocessor/resolver/value.rs +176 -176
  137. package/rust/core/store/global.rs +57 -57
  138. package/rust/lib.rs +323 -323
  139. package/rust/macros/Cargo.toml +14 -0
  140. package/rust/macros/src/lib.rs +52 -0
  141. package/rust/main.rs +311 -142
  142. package/rust/types/Cargo.toml +1 -1
  143. package/rust/types/src/addons.rs +3 -1
  144. package/rust/types/src/config.rs +1 -3
  145. package/rust/utils/Cargo.toml +5 -2
  146. package/rust/utils/src/file.rs +397 -14
  147. package/rust/utils/src/path.rs +31 -2
  148. package/rust/utils/src/version.rs +38 -7
  149. package/rust/web/auth.rs +5 -0
  150. package/rust/web/forge.rs +5 -0
  151. package/rust/web/mod.rs +5 -3
  152. package/typescript/scripts/version/copy-to-binary.ts +82 -0
  153. package/rust/cli/bank/api.rs +0 -122
  154. package/rust/cli/bank/commands.rs +0 -306
  155. package/rust/cli/bank/mod.rs +0 -29
  156. package/rust/cli/install/bank.rs +0 -72
  157. package/rust/cli/install/commands.rs +0 -35
  158. package/rust/cli/install/mod.rs +0 -4
  159. package/rust/cli/install/plugin.rs +0 -80
package/Cargo.toml CHANGED
@@ -1,81 +1,84 @@
1
- [package]
2
- name = "devalang"
3
- version = "0.0.1-beta.2"
4
- authors = ["Devaloop <contact@devaloop.com>"]
5
- description = "Write music like code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text."
6
- license = "MIT"
7
- repository = "https://github.com/devaloop-labs/devalang"
8
- keywords = ["music", "dsl", "audio", "cli"]
9
- categories = ["command-line-utilities", "development-tools", "parser-implementations"]
10
- readme = "README.md"
11
- homepage = "https://devalang.com"
12
- documentation = "https://docs.devalang.com/"
13
- edition = "2024"
14
-
15
- [[bin]]
16
- name = "devalang"
17
- path = "rust/main.rs"
18
-
19
- [lib]
20
- name = "devalang_core"
21
- path = "rust/lib.rs"
22
- crate-type = ["cdylib", "rlib"]
23
-
24
- [profile.release]
25
- opt-level = "s"
26
-
27
- [features]
28
- default = ["cli"]
29
- cli = ["crossterm", "indicatif", "inquire", "zip", "reqwest", "flate2", "tokio"]
30
-
31
- [dependencies]
32
- devalang_types = { path = "rust/types", version = "0.0.3" }
33
- clap = { version = "4.5", features = ["derive"] }
34
- serde = { version = "1.0", features = ["derive"] }
35
- serde_json = "1.0"
36
- rodio = "0.17"
37
- hound = "3.4.0"
38
- toml = "0.8"
39
- notify = "6.1"
40
- fs_extra = "1.3"
41
- include_dir = "0.7"
42
- wasm-bindgen = "0.2"
43
- serde-wasm-bindgen = "0.4"
44
- nom_locate = "4.0.0"
45
- chrono = "0.4"
46
- crossterm = { version = "0.27", optional = true }
47
- indicatif = { version = "0.17", optional = true }
48
- inquire = { version = "0.7.5", optional = true }
49
- js-sys = "0.3"
50
- getrandom = { version = "0.3", features = ["wasm_js"] }
51
- reqwest = { version = "0.12.22", optional = true, features = ["json"] }
52
- flate2 = { version = "1.0", optional = true }
53
- tokio = { version = "1", features = ["full"], optional = true }
54
- zip = { version = "4.3.0", optional = true }
55
- rayon = "1.10.0"
56
- webbrowser = "0.8"
57
- tiny_http = "0.9.0"
58
- dirs = "5"
59
- urlencoding = "2.1"
60
- uuid = { version = "1.18.0", features = ["v4"] }
61
- console_error_panic_hook = "0.1.7"
62
- midly = "0.5.3"
63
-
64
- [dev-dependencies]
65
- assert_cmd = "2"
66
- predicates = "2"
67
- tempfile = "3"
68
-
69
- # For wasm32, enable wasm-friendly randomness backends and avoid
70
- # pulling in native-only CLI dependencies.
71
- [target.'cfg(target_arch = "wasm32")'.dependencies]
72
- getrandom = { version = "0.3", features = ["wasm_js"] }
73
- uuid = { version = "1.18.0", features = ["v4", "js", "rng-getrandom"] }
74
- # Keep a lightweight linkage to the utils crate for wasm builds without
75
- # enabling native CLI features.
76
- devalang_utils = { path = "rust/utils", default-features = false, version = "0.0.2" }
77
-
78
- # devalang_utils with CLI and wasmtime are only needed for native targets.
79
- [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
80
- devalang_utils = { path = "rust/utils", features = ["cli"], version = "0.0.2" }
81
- wasmtime = "19"
1
+ [package]
2
+ name = "devalang"
3
+ version = "0.0.1-beta.3"
4
+ authors = ["Devaloop <contact@devaloop.com>"]
5
+ description = "Write music like code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text."
6
+ license = "MIT"
7
+ repository = "https://github.com/devaloop-labs/devalang"
8
+ license-file = "LICENSE"
9
+ keywords = ["music", "dsl", "audio", "cli", "synthesis"]
10
+ categories = ["command-line-utilities", "development-tools", "parser-implementations"]
11
+ readme = "README.md"
12
+ homepage = "https://devalang.com"
13
+ documentation = "https://docs.devalang.com/"
14
+ edition = "2024"
15
+
16
+ [[bin]]
17
+ name = "devalang"
18
+ path = "rust/main.rs"
19
+
20
+ [lib]
21
+ name = "devalang_core"
22
+ path = "rust/lib.rs"
23
+ crate-type = ["cdylib", "rlib"]
24
+
25
+ [profile.release]
26
+ opt-level = "s"
27
+
28
+ [features]
29
+ default = ["cli"]
30
+ cli = ["crossterm", "indicatif", "inquire", "zip", "reqwest", "flate2", "tokio"]
31
+
32
+ [dependencies]
33
+ devalang_types = { path = "rust/types", version = "0.0.4" }
34
+ devalang_bindings = { path = "rust/bindings", version = "0.0.1" }
35
+ devalang_macros = { path = "rust/macros", version = "0.0.1" }
36
+ clap = { version = "4.5", features = ["derive"] }
37
+ serde = { version = "1.0", features = ["derive"] }
38
+ serde_json = "1.0"
39
+ rodio = "0.17"
40
+ hound = "3.4.0"
41
+ toml = "0.8"
42
+ notify = "6.1"
43
+ fs_extra = "1.3"
44
+ include_dir = "0.7"
45
+ wasm-bindgen = "0.2"
46
+ serde-wasm-bindgen = "0.4"
47
+ nom_locate = "4.0.0"
48
+ chrono = "0.4"
49
+ crossterm = { version = "0.27", optional = true }
50
+ indicatif = { version = "0.17", optional = true }
51
+ inquire = { version = "0.7.5", optional = true }
52
+ js-sys = "0.3"
53
+ getrandom = { version = "0.3", features = ["wasm_js"] }
54
+ reqwest = { version = "0.12.22", optional = true, features = ["json"] }
55
+ flate2 = { version = "1.0", optional = true }
56
+ tokio = { version = "1", features = ["full"], optional = true }
57
+ zip = { version = "4.3.0", optional = true }
58
+ rayon = "1.10.0"
59
+ webbrowser = "0.8"
60
+ tiny_http = "0.9.0"
61
+ dirs = "5"
62
+ urlencoding = "2.1"
63
+ uuid = { version = "1.18.0", features = ["v4"] }
64
+ console_error_panic_hook = "0.1.7"
65
+ midly = "0.5.3"
66
+
67
+ [dev-dependencies]
68
+ assert_cmd = "2"
69
+ predicates = "2"
70
+ tempfile = "3"
71
+
72
+ # For wasm32, enable wasm-friendly randomness backends and avoid
73
+ # pulling in native-only CLI dependencies.
74
+ [target.'cfg(target_arch = "wasm32")'.dependencies]
75
+ getrandom = { version = "0.3", features = ["wasm_js"] }
76
+ uuid = { version = "1.18.0", features = ["v4", "js", "rng-getrandom"] }
77
+ # Keep a lightweight linkage to the utils crate for wasm builds without
78
+ # enabling native CLI features.
79
+ devalang_utils = { path = "rust/utils", default-features = false, version = "0.0.3" }
80
+
81
+ # devalang_utils with CLI and wasmtime are only needed for native targets.
82
+ [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
83
+ devalang_utils = { path = "rust/utils", features = ["cli"], version = "0.0.3" }
84
+ wasmtime = "19"
package/README.md CHANGED
@@ -32,9 +32,8 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
32
32
  >
33
33
  > Includes synthesis, playback, and rendering features, but is still in early development, and breaking changes may occur.
34
34
  >
35
- > **NEW**: [Devaforge is now available for creating addons](https://github.com/devaloop-labs/devaforge).
35
+ > **NEW**: [Devalang WORKSHOP is now available](https://workshop.devalang.com).
36
36
  >
37
- > **NEW**: Now available for Windows, Linux, and macOS.
38
37
 
39
38
  ## 📚 Quick Access
40
39
 
@@ -192,6 +191,8 @@ devalang play --repeat
192
191
 
193
192
  ## 📰 What's new
194
193
 
194
+ - **Effects chaining**: Added support for chaining multiple effects on synths and samples.
195
+ - **New addons system**: Revamped the addons system for better management and integration.
195
196
  - **MIDI export**: Added the ability to export MIDI files from Devalang scripts.
196
197
  - **Synthesizer improvements**: Enhanced the built-in synthesizer with new types and modulation options.
197
198
  - **Devaforge**: Introduced a new system for creating and managing addons, including a CLI for addon generation.
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,47 @@
4
4
 
5
5
  # Changelog
6
6
 
7
+ ## Version 0.0.1-beta.3 (2025-09-20)
8
+
9
+ ### ✨ Language Features
10
+
11
+ - Implemented `synth` effects chaining to apply effects to individual synth notes.
12
+ - Example: `mySynth -> note(C4) -> slide({ duration: 2000 }) -> reverb({ room_size: 0.8 })`
13
+
14
+ ### 🛠️ CLI & Addons
15
+
16
+ - Introduced a unified addon manager and removed the legacy `install` / `bank` commands:
17
+
18
+ - Use `devalang addon install <publisher>.<name>` to install banks or plugins.
19
+ - Use `devalang addon remove <publisher>.<name>` to remove banks or plugins.
20
+ - Use `devalang addon list` to list installed addons.
21
+
22
+ - Improved the `discover` workflow:
23
+
24
+ - Recursively scans `.deva` for compiled archives (`.tar.gz` / `.tgz`) and proposes them for installation.
25
+ - Pre-classifies addon types by inspecting archive contents so installations can target the correct folder (bank/plugin/preset/template).
26
+ - Robust selection labels and mapping ensure the chosen addon is installed correctly.
27
+
28
+ - Installation and extraction improvements:
29
+
30
+ - Archives are extracted into the appropriate destination based on detected type.
31
+ - Temporary extraction folder is cleaned up only when empty after installation.
32
+ - Added `--no-clear-tmp` flag to `discover` and addon install flows to preserve the temporary folder when desired.
33
+
34
+ - Bugfixes: fixed selection-to-install mapping and improved success/error logging during install flows.
35
+
36
+ ### 🧪 Telemetry & Versioning
37
+
38
+ - Telemetry now uses a runtime-first strategy to resolve the CLI version
39
+
40
+ ### 📦 Packaging & Tools
41
+
42
+ - Added `typescript/scripts/version/copy-to-binary.ts` to copy `project-version.json` next to a packaged binary during release/packaging steps.
43
+
44
+ ### ✅ Bugfixes & Misc
45
+
46
+ - Various fixes around temporary folder cleanup, archive detection, config updates and CLI ergonomics.
47
+
7
48
  ## Version 0.0.1-beta.2 (2025-09-07)
8
49
 
9
50
  ### 🛠️ MIDI & format
package/docs/ROADMAP.md CHANGED
@@ -23,16 +23,16 @@ Devalang is a work in progress. Here’s what we’re planning next:
23
23
  - ✅ **Sample loading**: Add `@load` assignment to load samples (.mp3, .wav) for use as values.
24
24
  - ✅ **WASM support**: Compile Devalang to WebAssembly for use in web applications and other environments.
25
25
  - ✅ **VSCode extension**: Create a VSCode extension for syntax highlighting and code completion.
26
+ - ✅ **Addon generator**: Implement addon generation for creating reusable plugins, banks and presets.
27
+ - ✅ **Effect chains**: Implement effect chains for applying multiple effects to a single note or group.
26
28
 
27
29
  ## In Progress
28
30
 
29
- - ⏳ **Addon generator**: Implement addon generation for creating reusable plugins, banks and presets.
30
31
  - ⏳ **Testing**: Expand test coverage for all features.
32
+ - ⏳ **Mixing & Routing**: Implement mixing and routing capabilities for advanced audio control.
31
33
 
32
34
  ## Planned
33
35
 
34
36
  - ⏳ **MIDI mapping**: Implement MIDI mapping for external control.
35
37
  - ⏳ **GUI**: Develop a graphical user interface for easier interaction.
36
- - ⏳ **Mixing & Routing**: Implement mixing and routing capabilities for advanced audio control.
37
- - ⏳ **Effect chains**: Implement effect chains for applying multiple effects to a single note or group.
38
38
  - ⏳ **Smart modules**: Let Devalang detect and use groups, samples, and variables without needing to import them manually.
@@ -0,0 +1,19 @@
1
+ let myChord = synth sine
2
+
3
+ myChord -> note(C4)
4
+ -> arp({ pattern: "up", rate: 1/8 })
5
+ -> slide({ duration: 2000 })
6
+ -> echo({ delay: 250, feedback: 0.5 })
7
+ -> reverb({ room_size: 0.8 })
8
+
9
+ myChord -> note(E4)
10
+ -> arp({ pattern: "up", rate: 1/8 })
11
+ -> slide({ duration: 2000 })
12
+ -> echo({ delay: 250, feedback: 0.5 })
13
+ -> reverb({ room_size: 0.8 })
14
+
15
+ myChord -> note(G4)
16
+ -> arp({ pattern: "up", rate: 1/8 })
17
+ -> slide({ duration: 2000 })
18
+ -> echo({ delay: 250, feedback: 0.5 })
19
+ -> reverb({ room_size: 0.8 })
@@ -1,16 +1,16 @@
1
1
  # This is an example of using a plugin in Devalang.
2
- # Make sure you have the plugin installed in your Devaloop application. (devalang install plugin <author>.<plugin_name>)
2
+ # Make sure you have the plugin installed in your Devaloop application. (devalang addon install <author>.<addon_name>)
3
3
 
4
4
  @use devaloop.acid as acidPlugin
5
5
 
6
- let acidSynth = synth acidPlugin.synth {
7
- waveform: "saw",
8
- accent: 0.1,
9
- decay: 250
10
- }
6
+ group acidSynthGroup:
7
+ let acidSynth = synth acidPlugin.synth {
8
+ waveform: "saw",
9
+ resonance: 1.0,
10
+ }
11
11
 
12
- group mySynthGroup:
13
- acidSynth -> note(C4, { duration: 600 })
14
- acidSynth -> note(E4, { duration: 600 })
12
+ acidSynth -> note(C4, { duration: 1000 })
13
+ acidSynth -> note(E3, { duration: 1000 })
14
+ acidSynth -> note(B2, { duration: 1000 })
15
15
 
16
- call mySynthGroup
16
+ call acidSynthGroup
@@ -0,0 +1,23 @@
1
+ # NOTE Future version of Deva may include built-in routing capabilities.
2
+
3
+ # @load "./samples/kick-808.wav" as kickCustom
4
+
5
+ # group myLead:
6
+ # let mySynth = synth sine
7
+
8
+ # mySynth -> note(C4, { duration: 400 })
9
+
10
+ # group myKick:
11
+ # .kickCustom 1/4
12
+
13
+ # routing:
14
+ # node $master
15
+ # node myLead
16
+ # node myKick
17
+ # node hallReverb = effect reverb { decay: 3.0, mix: 0.5 }
18
+
19
+ # connect myLead -> hallReverb -> $master
20
+
21
+ # connect myLead -> $master with { delay: { time: 300, feedback: 0.3 } }
22
+
23
+ # connect myKick -> myLead with { sidechain: 0.6 }
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": "0.0.1-beta.3",
3
+ "channel": "beta",
4
+ "lastCommit": "d9c8d5927288897aebf7faceb2b3495b5b8b87eb",
5
+ "build": 2
6
+ }
@@ -1,29 +1,29 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
- export const parse: (a: number, b: number, c: number, d: number) => [number, number, number];
4
+ export const collect_playhead_events: () => [number, number, number];
5
5
  export const debug_render: (a: number, b: number) => [number, number, number];
6
- export const render_audio: (a: number, b: number) => [number, number, number];
6
+ export const parse: (a: number, b: number, c: number, d: number) => [number, number, number];
7
7
  export const register_playhead_callback: (a: any) => void;
8
- export const collect_playhead_events: () => [number, number, number];
8
+ export const render_audio: (a: number, b: number) => [number, number, number];
9
9
  export const unregister_playhead_callback: () => void;
10
- export const rust_lzma_wasm_shim_malloc: (a: number) => number;
11
10
  export const rust_lzma_wasm_shim_calloc: (a: number, b: number) => number;
12
11
  export const rust_lzma_wasm_shim_free: (a: number) => void;
12
+ export const rust_lzma_wasm_shim_malloc: (a: number) => number;
13
+ export const rust_lzma_wasm_shim_memchr: (a: number, b: number, c: number) => number;
13
14
  export const rust_lzma_wasm_shim_memcmp: (a: number, b: number, c: number) => number;
14
15
  export const rust_lzma_wasm_shim_memcpy: (a: number, b: number, c: number) => number;
15
16
  export const rust_lzma_wasm_shim_memmove: (a: number, b: number, c: number) => number;
16
17
  export const rust_lzma_wasm_shim_memset: (a: number, b: number, c: number) => number;
17
18
  export const rust_lzma_wasm_shim_strlen: (a: number) => number;
18
- export const rust_lzma_wasm_shim_memchr: (a: number, b: number, c: number) => number;
19
- export const rust_zstd_wasm_shim_qsort: (a: number, b: number, c: number, d: number) => void;
20
- export const rust_zstd_wasm_shim_malloc: (a: number) => number;
21
- export const rust_zstd_wasm_shim_memcmp: (a: number, b: number, c: number) => number;
22
19
  export const rust_zstd_wasm_shim_calloc: (a: number, b: number) => number;
23
20
  export const rust_zstd_wasm_shim_free: (a: number) => void;
21
+ export const rust_zstd_wasm_shim_malloc: (a: number) => number;
22
+ export const rust_zstd_wasm_shim_memcmp: (a: number, b: number, c: number) => number;
24
23
  export const rust_zstd_wasm_shim_memcpy: (a: number, b: number, c: number) => number;
25
24
  export const rust_zstd_wasm_shim_memmove: (a: number, b: number, c: number) => number;
26
25
  export const rust_zstd_wasm_shim_memset: (a: number, b: number, c: number) => number;
26
+ export const rust_zstd_wasm_shim_qsort: (a: number, b: number, c: number, d: number) => void;
27
27
  export const __wbindgen_malloc: (a: number, b: number) => number;
28
28
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
29
29
  export const __wbindgen_exn_store: (a: number) => void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const argv = process.argv.slice(2);
9
+ let sourceArg;
10
+ let binaryArg;
11
+ let outDirArg;
12
+ for (let i = 0; i < argv.length; i++) {
13
+ const a = argv[i];
14
+ if (a === "--source") {
15
+ sourceArg = argv[++i];
16
+ }
17
+ else if (a === "--binary") {
18
+ binaryArg = argv[++i];
19
+ }
20
+ else if (a === "--out-dir") {
21
+ outDirArg = argv[++i];
22
+ }
23
+ else if (a === "--help" || a === "-h") {
24
+ process.exit(0);
25
+ }
26
+ else {
27
+ console.error(`Unknown arg: ${a}`);
28
+ process.exit(1);
29
+ }
30
+ }
31
+ // Default source: attempt to locate project-version.json at repo root
32
+ const defaultSource = path_1.default.resolve(__dirname, "..", "..", "..", "project-version.json");
33
+ const sourcePath = sourceArg ? path_1.default.resolve(sourceArg) : defaultSource;
34
+ if (!fs_1.default.existsSync(sourcePath)) {
35
+ console.error(`Source project-version.json not found at '${sourcePath}'. Please provide --source or ensure file exists.`);
36
+ process.exit(2);
37
+ }
38
+ let destDir = null;
39
+ if (binaryArg) {
40
+ const binPath = path_1.default.resolve(binaryArg);
41
+ // If it's an existing file, use its directory, otherwise assume user passed target path and use its parent
42
+ if (fs_1.default.existsSync(binPath) && fs_1.default.statSync(binPath).isFile()) {
43
+ destDir = path_1.default.dirname(binPath);
44
+ }
45
+ else {
46
+ // If binPath looks like a file path (has extension) use parent, else treat as dir
47
+ const ext = path_1.default.extname(binPath);
48
+ if (ext) {
49
+ destDir = path_1.default.dirname(binPath);
50
+ }
51
+ else {
52
+ destDir = binPath;
53
+ }
54
+ }
55
+ }
56
+ else if (outDirArg) {
57
+ destDir = path_1.default.resolve(outDirArg);
58
+ }
59
+ else {
60
+ // Default: try to copy next to the running node current working dir (useful when packaging)
61
+ destDir = process.cwd();
62
+ }
63
+ if (!destDir) {
64
+ console.error("Could not resolve destination directory");
65
+ process.exit(3);
66
+ }
67
+ try {
68
+ if (!fs_1.default.existsSync(destDir)) {
69
+ fs_1.default.mkdirSync(destDir, { recursive: true });
70
+ }
71
+ const destPath = path_1.default.join(destDir, "project-version.json");
72
+ fs_1.default.copyFileSync(sourcePath, destPath);
73
+ console.log(`project-version.json copied to '${destPath}'`);
74
+ process.exit(0);
75
+ }
76
+ catch (err) {
77
+ console.error("Failed to copy project-version.json:", err);
78
+ process.exit(4);
79
+ }
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@devaloop/devalang",
3
3
  "private": false,
4
- "version": "0.0.1-beta.2",
4
+ "version": "0.0.1-beta.3",
5
5
  "description": "Write music like code. Devalang is a domain-specific language (DSL) for sound designers and music hackers. Compose, automate, and control sound — in plain text.",
6
+ "longDescription": "Devalang is a compact, performant DSL and toolchain for composing, automating and rendering sound. It provides a CLI, WASM bindings, a TypeScript API, and plugins for editors. Ideal for live-coding, sample-based production, algorithmic composition, and fast prototyping.",
6
7
  "main": "out-tsc/index.js",
7
8
  "bin": {
8
9
  "devalang": "./out-tsc/bin/index.js"
@@ -18,6 +19,7 @@
18
19
  "rust:prepack": "cargo build --release",
19
20
  "scripts:postbuild": "tsc && node out-tsc/scripts/postbuild.js",
20
21
  "scripts:version:bump": "tsc && node out-tsc/scripts/version/index.js",
22
+ "scripts:copy-version": "tsc && node out-tsc/scripts/version/copy-to-binary.js --out-dir ./out-tsc/bin",
21
23
  "scripts:build": "tsc",
22
24
  "types:build": "tsc --emitDeclarationOnly",
23
25
  "types:wasm": "tsc && node out-tsc/scripts/copy-wasm-dts.js",
@@ -25,25 +27,36 @@
25
27
  "test:rust": "cargo test",
26
28
  "test:ts": "mocha -r ts-node/register tests/typescript/**/*.spec.ts --exit",
27
29
  "test": "npm run test:ts",
28
- "postinstall": "node out-tsc/scripts/postinstall.js"
30
+ "postinstall": "node out-tsc/scripts/postinstall.js",
31
+ "beforepublish": "npm run types:all && npm run scripts:copy-version"
29
32
  },
30
33
  "homepage": "https://devalang.com",
31
34
  "keywords": [
32
35
  "devalang",
33
36
  "music",
34
37
  "sound",
35
- "domain-specific language",
36
38
  "dsl",
37
- "programming language",
38
- "sound design",
39
- "music hacking",
39
+ "domain-specific-language",
40
40
  "audio",
41
+ "music-programming",
41
42
  "synthesis",
42
- "scripting",
43
- "sound synthesis",
44
- "music programming"
43
+ "midi",
44
+ "wasm",
45
+ "vscode",
46
+ "live-coding",
47
+ "sound-design",
48
+ "samples",
49
+ "cli"
45
50
  ],
46
- "author": "Devaloop",
51
+ "author": {
52
+ "name": "Devaloop",
53
+ "email": "contact@devaloop.com",
54
+ "url": "https://devaloop.com"
55
+ },
56
+ "bugs": {
57
+ "url": "https://github.com/devaloop-labs/devalang/issues",
58
+ "email": "contact@devaloop.com"
59
+ },
47
60
  "license": "MIT",
48
61
  "repository": {
49
62
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.0.1-beta.2",
2
+ "version": "0.0.1-beta.3",
3
3
  "channel": "beta",
4
- "lastCommit": "024b7abee45bdc5e034a4efd4a3f60dbd958eec0",
5
- "build": 1
4
+ "lastCommit": "d9c8d5927288897aebf7faceb2b3495b5b8b87eb",
5
+ "build": 2
6
6
  }
@@ -0,0 +1,9 @@
1
+ [package]
2
+ name = "devalang_bindings"
3
+ version = "0.0.1"
4
+ description = "Bindings for Devalang (to use them in external contexts)"
5
+ license = "MIT"
6
+ authors = ["Devaloop <contact@devaloop.com>"]
7
+ edition = "2024"
8
+
9
+ [dependencies]
@@ -0,0 +1,86 @@
1
+ //! Minimal, focused bindings helpers for writing custom render functions in
2
+ //! plugin crates.
3
+ //!
4
+ //! The core runtime already handles registration and FFI bridging. This crate
5
+ //! only exports the types and function-signature aliases that plugin authors
6
+ //! should implement in safe Rust. The host will convert raw buffers/pointers
7
+ //! into these safe types before calling the plugin function.
8
+ //!
9
+ //! Guiding principle: plugin authors write purely safe Rust functions with
10
+ //! clear parameters (slices and small structs). No registration helpers or
11
+ //! global registries are provided here.
12
+
13
+ /// Lightweight representation of a musical note.
14
+ #[derive(Debug, Clone, Copy)]
15
+ pub struct Note {
16
+ /// MIDI pitch 0..127
17
+ pub pitch: u8,
18
+ /// Velocity 0..127
19
+ pub velocity: u8,
20
+ /// Note duration in milliseconds (approximate)
21
+ pub duration_ms: u32,
22
+ }
23
+
24
+ impl Default for Note {
25
+ fn default() -> Self {
26
+ Self { pitch: 60, velocity: 100, duration_ms: 500 }
27
+ }
28
+ }
29
+
30
+ /// Parameters describing the output buffer and audio context.
31
+ #[derive(Debug, Clone, Copy)]
32
+ pub struct BufferParams {
33
+ /// Sample rate in Hz
34
+ pub sample_rate: u32,
35
+ /// Number of channels (1 = mono, 2 = stereo, ...)
36
+ pub channels: u32,
37
+ /// Number of frames (samples per channel) available in the buffer
38
+ pub frames: u32,
39
+ }
40
+
41
+ /// Helper to compute expected buffer length (frames * channels).
42
+ impl BufferParams {
43
+ /// Returns the expected length of the interleaved buffer (frames * channels)
44
+ /// as a host-friendly `usize`. The host is responsible for converting
45
+ /// its platform-sized integers into these explicit-width fields.
46
+ pub fn buffer_len(&self) -> usize {
47
+ (self.frames as usize).saturating_mul(self.channels as usize)
48
+ }
49
+
50
+ /// Quick validation helper that plugins can call to ensure the provided
51
+ /// output slice matches the declared `params` length.
52
+ pub fn validate_buffer(out: &mut [f32], params: BufferParams) -> Result<(), &'static str> {
53
+ let expected = params.buffer_len();
54
+ if out.len() < expected { Err("output buffer too small") } else { Ok(()) }
55
+ }
56
+ }
57
+
58
+ impl Default for BufferParams {
59
+ fn default() -> Self {
60
+ Self { sample_rate: 44100, channels: 1, frames: 0 }
61
+ }
62
+ }
63
+
64
+ /// Common render function signature plugin authors should implement.
65
+ ///
66
+ /// - `out` is a mutable slice of f32 samples (interleaved if channels > 1).
67
+ /// - `params` provides sample rate / channels / frames info.
68
+ /// - `note` is the Note descriptor.
69
+ /// - `freq`/`amp` give additional voice parameters the host may pass.
70
+ pub type RenderFn = fn(out: &mut [f32], params: BufferParams, note: Note, freq: f32, amp: f32);
71
+
72
+ /// A more complete signature used by synth-style plugins that also need
73
+ /// access to extra controls (e.g. voice index or time offset).
74
+ pub type RenderFnExt = fn(
75
+ out: &mut [f32],
76
+ params: BufferParams,
77
+ note: Note,
78
+ freq: f32,
79
+ amp: f32,
80
+ voice_index: u32,
81
+ time_ms: u64,
82
+ );
83
+
84
+ /// Optional signature for control parameter setters. The host can call these
85
+ /// safely by converting raw values into primitives.
86
+ pub type SetParamFn = fn(param_name: &str, value: f32);