@devaloop/devalang 0.0.1-alpha.7 → 0.0.1-alpha.9

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 (86) hide show
  1. package/Cargo.toml +1 -1
  2. package/README.md +31 -16
  3. package/docs/CHANGELOG.md +49 -2
  4. package/docs/ROADMAP.md +2 -2
  5. package/docs/SYNTAX.md +41 -7
  6. package/docs/TODO.md +3 -3
  7. package/examples/condition.deva +20 -0
  8. package/examples/group.deva +3 -3
  9. package/examples/index.deva +9 -8
  10. package/examples/loop.deva +10 -8
  11. package/examples/synth.deva +14 -0
  12. package/examples/variables.deva +2 -2
  13. package/out-tsc/bin/devalang.exe +0 -0
  14. package/out-tsc/scripts/version/fetch.js +1 -5
  15. package/package.json +1 -1
  16. package/project-version.json +3 -3
  17. package/rust/cli/build.rs +6 -1
  18. package/rust/core/audio/engine.rs +89 -12
  19. package/rust/core/audio/evaluator.rs +31 -0
  20. package/rust/core/audio/interpreter/arrow_call.rs +129 -0
  21. package/rust/core/audio/interpreter/call.rs +64 -0
  22. package/rust/core/audio/interpreter/condition.rs +69 -0
  23. package/rust/core/audio/interpreter/driver.rs +216 -0
  24. package/rust/core/audio/interpreter/let_.rs +19 -0
  25. package/rust/core/audio/interpreter/load.rs +18 -0
  26. package/rust/core/audio/interpreter/loop_.rs +67 -0
  27. package/rust/core/audio/interpreter/mod.rs +12 -0
  28. package/rust/core/audio/interpreter/sleep.rs +36 -0
  29. package/rust/core/audio/interpreter/spawn.rs +66 -0
  30. package/rust/core/audio/interpreter/tempo.rs +16 -0
  31. package/rust/core/audio/interpreter/trigger.rs +69 -0
  32. package/rust/core/audio/loader/mod.rs +1 -0
  33. package/rust/core/audio/{loader.rs → loader/trigger.rs} +3 -1
  34. package/rust/core/audio/mod.rs +2 -1
  35. package/rust/core/audio/renderer.rs +54 -0
  36. package/rust/core/builder/mod.rs +1 -1
  37. package/rust/core/debugger/lexer.rs +1 -1
  38. package/rust/core/debugger/mod.rs +1 -0
  39. package/rust/core/debugger/store.rs +25 -0
  40. package/rust/core/error/mod.rs +1 -1
  41. package/rust/core/lexer/handler/arrow.rs +31 -0
  42. package/rust/core/lexer/handler/driver.rs +226 -0
  43. package/rust/core/lexer/handler/identifier.rs +3 -0
  44. package/rust/core/lexer/handler/mod.rs +4 -227
  45. package/rust/core/lexer/handler/operator.rs +44 -0
  46. package/rust/core/lexer/mod.rs +25 -4
  47. package/rust/core/lexer/token.rs +40 -9
  48. package/rust/core/parser/driver.rs +331 -0
  49. package/rust/core/parser/handler/arrow_call.rs +126 -0
  50. package/rust/core/parser/handler/at.rs +3 -7
  51. package/rust/core/parser/handler/bank.rs +5 -2
  52. package/rust/core/parser/handler/condition.rs +74 -0
  53. package/rust/core/parser/handler/dot.rs +1 -1
  54. package/rust/core/parser/handler/identifier/call.rs +41 -0
  55. package/rust/core/parser/handler/identifier/group.rs +75 -0
  56. package/rust/core/parser/handler/identifier/let_.rs +133 -0
  57. package/rust/core/parser/handler/identifier/mod.rs +51 -0
  58. package/rust/core/parser/handler/identifier/sleep.rs +33 -0
  59. package/rust/core/parser/handler/identifier/spawn.rs +41 -0
  60. package/rust/core/parser/handler/identifier/synth.rs +65 -0
  61. package/rust/core/parser/handler/loop_.rs +25 -19
  62. package/rust/core/parser/handler/mod.rs +3 -1
  63. package/rust/core/parser/handler/tempo.rs +1 -1
  64. package/rust/core/parser/mod.rs +3 -237
  65. package/rust/core/parser/statement.rs +36 -35
  66. package/rust/core/preprocessor/loader.rs +64 -49
  67. package/rust/core/preprocessor/module.rs +3 -6
  68. package/rust/core/preprocessor/processor.rs +13 -4
  69. package/rust/core/preprocessor/resolver/call.rs +123 -0
  70. package/rust/core/preprocessor/resolver/condition.rs +92 -0
  71. package/rust/core/preprocessor/resolver/driver.rs +227 -0
  72. package/rust/core/preprocessor/resolver/group.rs +35 -87
  73. package/rust/core/preprocessor/resolver/let_.rs +31 -0
  74. package/rust/core/preprocessor/resolver/loop_.rs +62 -116
  75. package/rust/core/preprocessor/resolver/mod.rs +9 -153
  76. package/rust/core/preprocessor/resolver/spawn.rs +58 -0
  77. package/rust/core/preprocessor/resolver/synth.rs +50 -0
  78. package/rust/core/preprocessor/resolver/trigger.rs +51 -50
  79. package/rust/core/preprocessor/resolver/value.rs +78 -0
  80. package/rust/core/utils/path.rs +17 -32
  81. package/rust/core/utils/validation.rs +30 -28
  82. package/typescript/scripts/version/fetch.ts +1 -6
  83. package/rust/core/audio/interpreter.rs +0 -317
  84. package/rust/core/audio/render.rs +0 -53
  85. package/rust/core/lexer/handler/equal.rs +0 -32
  86. package/rust/core/parser/handler/identifier.rs +0 -260
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "devalang"
3
- version = "0.0.1-alpha.7"
3
+ version = "0.0.1-alpha.9"
4
4
  authors = ["Devaloop <contact@devaloop.com>"]
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
6
  license = "MIT"
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  ## 🎼 Devalang, by **Devaloop Labs**
20
20
 
21
- 🎶 Compose music with code — simple, structured, sonic.
21
+ 🎶 Compose music with code — structured, expressive, and fast.
22
22
 
23
23
  Devalang is a tiny domain-specific language (DSL) for music makers, sound designers, and audio hackers.
24
24
  Compose loops, control samples, render and play audio — all in clean, readable text.
@@ -27,11 +27,13 @@ Compose loops, control samples, render and play audio — all in clean, readable
27
27
 
28
28
  From studio sketches to live sets, Devalang gives you rhythmic control — with the elegance of code.
29
29
 
30
- > 🚧 **v0.0.1-alpha.5 Notice** 🚧
30
+ > 🚧 **v0.0.1-alpha.9 Notice** 🚧
31
31
  >
32
32
  > NEW: Devalang VSCode extension is now available !
33
33
  > [Get it here](https://marketplace.visualstudio.com/items?itemName=devaloop.devalang-vscode).
34
34
  >
35
+ > Includes synthesis, playback, and rendering features, but is still in early development.
36
+ >
35
37
  > Currently, Devalang CLI is only available for **Windows**.
36
38
  > Linux and macOS binaries will be added in future releases via cross-platform builds.
37
39
 
@@ -45,6 +47,15 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
45
47
  - [🎨 Prettier Plugin](https://www.npmjs.com/package/@devaloop/prettier-plugin-devalang)
46
48
  - [🌐 Project Website](https://devalang.com)
47
49
 
50
+ ## ❓ Why Devalang?
51
+
52
+ - 🎹 Prototype audio ideas without opening a DAW
53
+ - 💻 Integrate sound into code-based workflows
54
+ - 🎛️ Control audio parameters through readable syntax
55
+ - 🧪 Build musical logic with variables and conditions
56
+
57
+ > Producer, coder, or both — Devalang gives you musical structure, instantly.
58
+
48
59
  ## 🚀 Features
49
60
 
50
61
  - 🎵 **Audio Engine**: Integrated audio playback and rendering
@@ -53,6 +64,8 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
53
64
  - 🔢 **Basic data types**: strings, numbers, booleans, maps, arrays
54
65
  - 👁️ **Watch mode** for `build`, `check` and `play` commands
55
66
  - 📂 **Project templates** for quick setup
67
+ - 🎛️ **Custom samples**: easily load and trigger your own audio files
68
+ - 🔄 **Looping and grouping**: create complex patterns with ease
56
69
 
57
70
  ## 📆 Installation
58
71
 
@@ -63,13 +76,13 @@ From studio sketches to live sets, Devalang gives you rhythmic control — with
63
76
  Install the package globally (NPM)
64
77
 
65
78
  ```bash
66
- npm install -g @devaloop/devalang
79
+ npm install -g @devaloop/devalang@latest
67
80
  ```
68
81
 
69
82
  Usage without install (NPX)
70
83
 
71
84
  ```bash
72
- npx @devaloop/devalang <command>
85
+ npx @devaloop/devalang@latest
73
86
  ```
74
87
 
75
88
  ### For contributors
@@ -78,10 +91,11 @@ npx @devaloop/devalang <command>
78
91
  > - ⚠️ Requires [Rust 1.70+](https://www.rust-lang.org/learn/get-started#installing-rust)
79
92
 
80
93
  ```bash
81
- > git clone https://github.com/devaloop-labs/devalang.git
82
- > cd devalang
83
- > npm install
84
- > cargo install --path .
94
+ git clone https://github.com/devaloop-labs/devalang.git
95
+
96
+ cd devalang
97
+
98
+ npm install
85
99
  ```
86
100
 
87
101
  Development usage (you can customize arguments in package.json)
@@ -89,6 +103,7 @@ Development usage (you can customize arguments in package.json)
89
103
  ```bash
90
104
  # For syntax checking test
91
105
  npm run rust:dev:check
106
+
92
107
  # For building test
93
108
  npm run rust:dev:build
94
109
  ```
@@ -166,25 +181,24 @@ bpm globalBpm
166
181
  bank globalBank
167
182
  # Will declare a custom instrument bank using the globalBank variable
168
183
 
169
- # Loops
170
-
171
184
  loop 5:
172
185
  .customKick kickDuration {reverb=50, drive=25}
173
186
  # Will play 5 times a custom sample for 500ms with reverb and overdrive effects
174
187
 
175
- # Groups
176
-
177
188
  group myGroup:
178
189
  .customKick kickDuration {reverb=50, drive=25}
179
190
  # Will play the same sample in a group, allowing for more complex patterns
180
191
 
181
- # Will be executed line by line (sequentially)
182
192
  call myGroup
193
+ # Will be executed line by line (sequentially)
183
194
 
184
- # Will be executed in parallel (concurrently)
185
195
  # spawn myGroup
196
+ # Will be executed in parallel (concurrently)
197
+ # ⚠️ Note: `spawn` runs the entire group in parallel, but the group’s internal logic remains sequential unless it uses `spawn` internally.
186
198
  ```
187
199
 
200
+ > 🧠 Note: `call` and `spawn` only work with `group` blocks. They do not apply to individual samples or other statements.
201
+
188
202
  ```deva
189
203
  # variables.deva
190
204
 
@@ -197,14 +211,15 @@ let kickDuration = 500
197
211
 
198
212
  ## 🧯 Known issues
199
213
 
200
- - No support yet for `if`, `else`, `else if`, `pattern`, `function`, ... statements
214
+ - No smart modules yet, all groups, variables, and samples must be explicitly imported where used
215
+ - No support yet for `pattern`, `function`, ... statements
201
216
  - No support yet for cross-platform builds (Linux, macOS)
202
217
 
203
218
  ## 🧪 Roadmap Highlights
204
219
 
205
220
  For more info, see [docs/ROADMAP.md](./docs/ROADMAP.md)
206
221
 
207
- - ⏳ Other statements (e.g `if`, `function`, ...)
222
+ - ⏳ Other statements (e.g `function`, `pattern`, ...)
208
223
  - ⏳ Cross-platform support (Linux, macOS)
209
224
  - ⏳ More built-in instruments (e.g. snare, hi-hat, etc.)
210
225
 
package/docs/CHANGELOG.md CHANGED
@@ -4,11 +4,58 @@
4
4
 
5
5
  # Changelog
6
6
 
7
- ## Version 0.0.1-alpha.6 (2025-07-11)
7
+ ## Version 0.0.1-alpha.9 (2025-07-14)
8
+
9
+ ### ✨ Syntax
10
+
11
+ - Added support for `synth` directives to define synthesizer sounds directly in code
12
+ → Example:
13
+
14
+ ```deva
15
+ let mySynth = synth sine
16
+ ```
17
+
18
+ ### 🧠 Core Engine
19
+
20
+ - ✅ **Major refactor** of the resolution layer:
21
+
22
+ - `condition`, `loop`, `group`, `trigger`, `call`, `spawn`, and `driver` were fully rewritten
23
+ - Ensures **correct variable scoping**, **cross-module references**, and **imported symbol resolution**
24
+
25
+ - ✅ Audio interpreter updated:
26
+ - `call` and `spawn` now execute correctly in parallel, preserving **temporal alignment** across groups
27
+
28
+ ### 🧩 Language Features
29
+
30
+ - Improved `@import` behavior:
31
+ - Modules can now be imported using **relative paths only**
32
+ - No need for absolute or root-based imports
33
+
34
+ ## Version 0.0.1-alpha.8 (2025-07-12)
35
+
36
+ ### Syntax
37
+
38
+ - Implemented `if` directive to conditionally execute blocks of code.
39
+ - Implemented `else` directive to provide an alternative block of code when the `if` condition is not met.
40
+ - Implemented `else if` directive to provide additional conditions for the `if` directive.
41
+
42
+ ### Core Components
43
+
44
+ - Implemented evaluator for audio statements, to execute conditional statements.
45
+ - Fixed `group` resolution and export issues.
46
+ - Implemented `Global Store` debugger to inspect variables by module for build command.
47
+ - Organized `TokenKind` and `StatementKind` enums for better clarity and maintainability.
48
+ - Refactored audio interpreter to handle the new syntax and directives.
49
+ - Refactored lexer to handle new directives and improve tokenization.
50
+ - Refactored parser to handle new directives and improve parsing logic.
51
+ - Added support for `call` and `spawn` execution of imported groups.
52
+ - Enforced scoped resolution of groups in `spawn` and `call` (must be imported in current module).
53
+
54
+ ## Version 0.0.1-alpha.7 (2025-07-11)
8
55
 
9
56
  ## Examples
10
57
 
11
- - Added example for `group` directive in `examples/group.devalang`.
58
+ - Added examples in `examples` folder (group, loop, variables, index).
12
59
 
13
60
  ## Structure
14
61
 
package/docs/ROADMAP.md CHANGED
@@ -24,8 +24,8 @@ Devalang is a work in progress. Here’s what we’re planning next:
24
24
 
25
25
  ## Upcoming
26
26
 
27
+ - ⏳ **Smart modules**: Let Devalang detect and use groups, samples, and variables without needing to import them manually.
27
28
  - ⏳ **VSCode extension**: Create a VSCode extension for syntax highlighting and code completion.
28
- - ⏳ **Other statements**: Implement `if`, `else`, and other control structures.
29
- - ⏳ **Pattern and group statements**: Add support for `@pattern` and `@group` to organize code.
29
+ - ⏳ **Other statements**: Implement `pattern`, `function`, and other control structures.
30
30
  - ⏳ **Functions**: Add support for defining and calling functions.
31
31
  - ⏳ **Testing**: Expand test coverage for all features.
package/docs/SYNTAX.md CHANGED
@@ -58,7 +58,7 @@ let boolean = false
58
58
  Maps are key-value pairs defined using curly braces. Keys are strings, and values can be of any type (string, number, boolean, map, or array).
59
59
 
60
60
  ```deva
61
- let map = {myKey: 99}
61
+ let map = { myKey: 99 }
62
62
  ```
63
63
 
64
64
  ### Array
@@ -93,13 +93,17 @@ bank 808
93
93
 
94
94
  Modules can be imported and exported to share variables between different files.
95
95
 
96
+ > ⚠️ The import/export system is still experimental and may change in the future.
97
+ >
98
+ > You must explicitly declare imports and exports in each file — Devalang does not automatically detect or resolve them.
99
+
96
100
  Exporting variables from a module :
97
101
 
98
102
  ```deva
99
103
  # exported.deva
100
104
 
101
105
  let exportedIterator = 10
102
- let exportedParams = {drive: 50, decay: 30}
106
+ let exportedParams = { drive: 50, decay: 30 }
103
107
 
104
108
  @export { exportedIterator, exportedParams }
105
109
  ```
@@ -115,7 +119,7 @@ loop exportedIterator:
115
119
  .kick auto exportedParams
116
120
  ```
117
121
 
118
- ### Triggers
122
+ ### Loading Samples
119
123
 
120
124
  You can load your own samples and use them in your music.
121
125
 
@@ -129,7 +133,7 @@ Trigger usage : `.<name> <duration> <params>`
129
133
  .mySample auto {reverb: 50, drive: 25}
130
134
  ```
131
135
 
132
- ### Let variables
136
+ ### Variables
133
137
 
134
138
  Variables are defined using the `let` keyword, followed by the variable name and its value. The value can be of any type (string, number, boolean, map, or array).
135
139
 
@@ -137,7 +141,7 @@ Variables are defined using the `let` keyword, followed by the variable name and
137
141
  let number = 0
138
142
  let boolean = true
139
143
  let string = "string"
140
- let map = {myKey: 200}
144
+ let map = { myKey: 200 }
141
145
  let array = [0, 1, 2]
142
146
  ```
143
147
 
@@ -154,14 +158,40 @@ loop 10:
154
158
 
155
159
  Groups are defined using the `group` keyword, followed by the group name. The body of the group is indented.
156
160
 
161
+ Groups allow you to organize your code into reusable blocks. They can be called or spawned later in the code.
162
+
157
163
  ```deva
158
164
  group myGroup:
159
165
  # ...
160
166
  ```
161
167
 
168
+ ### Conditions
169
+
170
+ Conditions are defined using the `if` keyword, followed by a condition. The body of the condition is indented.
171
+
172
+ ```deva
173
+ if myCondition:
174
+ # ...
175
+ ```
176
+
177
+ You can also use `else` and `else if` for alternative branches:
178
+
179
+ ```deva
180
+ if myCondition:
181
+ # ...
182
+ else if anotherCondition:
183
+ # ...
184
+ else:
185
+ # ...
186
+ ```
187
+
162
188
  ### Calling Groups (Sequential Execution)
163
189
 
164
- Groups can be called using the `call` keyword, which executes the group in sequence.
190
+ Groups can be called using the `call` keyword, which executes only the group in sequence.
191
+
192
+ > ⚠️ `call` only works on `group` declarations. It does not apply to other statements.
193
+
194
+ This executes the entire group in the current execution thread, following a sequential order.
165
195
 
166
196
  ```deva
167
197
  call myGroup
@@ -169,7 +199,11 @@ call myGroup
169
199
 
170
200
  ### Spawning Groups (Parallel Execution)
171
201
 
172
- Groups can be spawned using the `spawn` keyword, which executes the group in parallel.
202
+ Groups can be spawned using the `spawn` keyword, which executes only the group in parallel.
203
+
204
+ > ⚠️ spawn also only works on group declarations. It does not make the group’s content parallel unless it explicitly uses spawn inside.
205
+
206
+ This runs the entire group in a separate execution thread, allowing it to play alongside other actions.
173
207
 
174
208
  ```deva
175
209
  spawn myGroup
package/docs/TODO.md CHANGED
@@ -54,9 +54,9 @@ This is a list of tasks and features to be implemented in Devalang. Note that th
54
54
  - [x] bank
55
55
  - [x] loop
56
56
  - [x] let
57
- - [ ] if
58
- - [ ] else
59
- - [ ] else if
57
+ - [x] if
58
+ - [x] else
59
+ - [x] else if
60
60
 
61
61
  ## Triggers
62
62
 
@@ -0,0 +1,20 @@
1
+ # This file demonstrates the use of conditional blocks in Devalang.
2
+
3
+ @import { duration, default_bank, params, loopCount, tempo } from "./variables.deva"
4
+ @import { myGroup } from "./group.deva"
5
+
6
+ @load "./samples/kick-808.wav" as kickCustom
7
+ @load "./samples/hat-808.wav" as hatCustom
8
+
9
+ group conditionBlock:
10
+ if tempo > 120:
11
+ # Will be executed if the condition is true
12
+ .kickCustom auto
13
+ else if tempo > 155:
14
+ # Will be executed if the condition is false
15
+ .hatCustom auto
16
+ else:
17
+ .kickCustom auto
18
+ .hatCustom auto
19
+
20
+ @export { conditionBlock }
@@ -1,9 +1,9 @@
1
1
  # This file demonstrates the use of grouping in Devalang.
2
2
 
3
- @import { duration, default_bank, params, loopCount, tempo } from "./examples/variables.deva"
3
+ @import { duration, default_bank, params, loopCount, tempo } from "./variables.deva"
4
4
 
5
- @load "./examples/samples/kick-808.wav" as kickCustom
6
- @load "./examples/samples/hat-808.wav" as hatCustom
5
+ @load "./samples/kick-808.wav" as kickCustom
6
+ @load "./samples/hat-808.wav" as hatCustom
7
7
 
8
8
  group myGroup:
9
9
  .kickCustom duration params
@@ -1,17 +1,18 @@
1
1
  # This file demonstrates the use of main features in Devalang.
2
2
 
3
- @import { duration, default_bank, params, loopCount, tempo } from "./examples/variables.deva"
4
- @import { myGroup } from "./examples/group.deva"
3
+ @import { duration, default_bank, params, loopCount, tempo } from "./variables.deva"
4
+ @import { myLead } from "./synth.deva"
5
+ @import { myLoop } from "./loop.deva"
5
6
 
6
- @load "./examples/samples/kick-808.wav" as kickCustom
7
- @load "./examples/samples/hat-808.wav" as hatCustom
7
+ @load "./samples/kick-808.wav" as kickCustom
8
+ @load "./samples/hat-808.wav" as hatCustom
8
9
 
9
10
  bpm tempo
10
11
 
11
12
  bank default_bank
12
13
 
13
- # Will be executed line by line (sequentially)
14
- call myGroup
14
+ group myTrack:
15
+ spawn myLoop
16
+ spawn myLead
15
17
 
16
- # Will be executed in parallel (concurrently)
17
- # spawn myGroup
18
+ call myTrack
@@ -1,15 +1,17 @@
1
1
  # This file demonstrates the use of a loop in Devalang.
2
2
 
3
- @import { duration, default_bank, params, loopCount, tempo } from "./examples/variables.deva"
3
+ @import { duration, default_bank, params, loopCount, tempo } from "./variables.deva"
4
4
 
5
- @load "./examples/samples/kick-808.wav" as kickCustom
6
- @load "./examples/samples/hat-808.wav" as hatCustom
5
+ @load "./samples/kick-808.wav" as kickCustom
6
+ @load "./samples/hat-808.wav" as hatCustom
7
7
 
8
- loop loopCount:
9
- .kickCustom duration params
8
+ group myLoop:
9
+ loop loopCount:
10
+ .kickCustom duration params
10
11
 
11
- # Uncomment the next line (.hat) while executing "play" command
12
- # with `--repeat` option to see magic happen !
12
+ # Uncomment the next line (.hat) while executing "play" command
13
+ # with `--repeat` option to see magic happen !
13
14
 
14
- .hatCustom duration params
15
+ .hatCustom duration params
15
16
 
17
+ @export { myLoop }
@@ -0,0 +1,14 @@
1
+ # This file defines a simple synth lead using Devalang's syntax.
2
+
3
+ group myLead:
4
+ let mySynth = synth sine
5
+
6
+ mySynth -> note(C4, { duration: 400 })
7
+ mySynth -> note(G4, { duration: 400 })
8
+ mySynth -> note(E4, { duration: 600 })
9
+ mySynth -> note(A4, { duration: 400 })
10
+ mySynth -> note(F4, { duration: 800 })
11
+ mySynth -> note(D4, { duration: 400 })
12
+ mySynth -> note(B3, { duration: 600 })
13
+
14
+ @export { myLead }
@@ -2,8 +2,8 @@
2
2
 
3
3
  let duration = auto
4
4
  let default_bank = 808
5
- let params = {decay:10, delay:30}
5
+ let params = { decay: 10, delay: 30 }
6
6
  let loopCount = 5
7
- let tempo = 155
7
+ let tempo = 115
8
8
 
9
9
  @export { duration, default_bank, params, loopCount, tempo }
Binary file
@@ -16,19 +16,15 @@ exports.fetchVersion = void 0;
16
16
  const fs_1 = __importDefault(require("fs"));
17
17
  const child_process_1 = require("child_process");
18
18
  const fetchVersion = (projectVersionPath) => __awaiter(void 0, void 0, void 0, function* () {
19
- // Lire le fichier
20
19
  const data = JSON.parse(fs_1.default.readFileSync(projectVersionPath, "utf-8"));
21
- // Incrémenter le numéro de build
22
20
  data.build = (data.build || 0) + 1;
23
- // Récupérer le dernier hash git
24
21
  try {
25
22
  const commit = (0, child_process_1.execSync)("git rev-parse HEAD").toString().trim();
26
23
  data.lastCommit = commit;
27
24
  }
28
25
  catch (err) {
29
- console.warn("⚠️ Impossible de récupérer le hash git.");
26
+ console.warn("⚠️ Unable to fetch git commit hash. Ensure you are in a git repository.");
30
27
  }
31
- // Écrire la mise à jour
32
28
  fs_1.default.writeFileSync(projectVersionPath, JSON.stringify(data, null, 2));
33
29
  });
34
30
  exports.fetchVersion = fetchVersion;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@devaloop/devalang",
3
3
  "private": false,
4
- "version": "0.0.1-alpha.7",
4
+ "version": "0.0.1-alpha.9",
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
6
  "main": "out-tsc/index.js",
7
7
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.0.1-alpha.7",
2
+ "version": "0.0.1-alpha.9",
3
3
  "channel": "alpha",
4
- "lastCommit": "81db490b7d9cc92f0512b49cf3dbaeb74cf57de1",
5
- "build": 6
4
+ "lastCommit": "61ab816a54275f2617ffd4327eb388ed822c36eb",
5
+ "build": 8
6
6
  }
package/rust/cli/build.rs CHANGED
@@ -2,7 +2,11 @@ use crate::{
2
2
  config::Config,
3
3
  core::{
4
4
  builder::Builder,
5
- debugger::{ lexer::write_lexer_log_file, preprocessor::write_preprocessor_log_file },
5
+ debugger::{
6
+ lexer::write_lexer_log_file,
7
+ preprocessor::write_preprocessor_log_file,
8
+ store::write_store_log_file,
9
+ },
6
10
  preprocessor::loader::ModuleLoader,
7
11
  store::global::GlobalStore,
8
12
  utils::path::{ find_entry_file, normalize_path },
@@ -113,6 +117,7 @@ fn begin_build(entry: String, output: String) {
113
117
  "resolved_statements.log",
114
118
  modules_statements.clone()
115
119
  );
120
+ write_store_log_file(&normalized_output_dir, "global_store.log", global_store.modules.clone());
116
121
 
117
122
  // SECTION Building AST and Audio
118
123
  let builder = Builder::new();
@@ -2,7 +2,10 @@ use std::{ collections::HashMap, fs::File, io::BufReader };
2
2
  use hound::{ SampleFormat, WavSpec, WavWriter };
3
3
  use rodio::{ Decoder, Source };
4
4
 
5
- use crate::core::{ store::variable::VariableTable, utils::path::normalize_path };
5
+ use crate::core::{
6
+ store::variable::VariableTable,
7
+ utils::path::{ normalize_path, resolve_relative_path },
8
+ };
6
9
 
7
10
  const SAMPLE_RATE: u32 = 44100;
8
11
  const CHANNELS: u16 = 2;
@@ -12,14 +15,16 @@ pub struct AudioEngine {
12
15
  pub volume: f32,
13
16
  pub variables: VariableTable,
14
17
  pub buffer: Vec<i16>,
18
+ pub module_name: String,
15
19
  }
16
20
 
17
21
  impl AudioEngine {
18
- pub fn new() -> Self {
22
+ pub fn new(module_name: String) -> Self {
19
23
  AudioEngine {
20
24
  volume: 1.0,
21
25
  buffer: vec![],
22
26
  variables: VariableTable::new(),
27
+ module_name,
23
28
  }
24
29
  }
25
30
 
@@ -32,14 +37,28 @@ impl AudioEngine {
32
37
  }
33
38
  }
34
39
 
35
- pub fn set_duration(&mut self, duration_secs: f32) {
36
- let mut total_samples = (duration_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
40
+ pub fn merge_with(&mut self, other: AudioEngine) {
41
+ if other.buffer.iter().all(|&s| s == 0) {
42
+ eprintln!("⚠️ Skipping merge: other buffer is silent");
43
+ return;
44
+ }
37
45
 
38
- if total_samples % (CHANNELS as usize) != 0 {
39
- total_samples += 1;
46
+ if self.buffer.iter().all(|&s| s == 0) {
47
+ self.buffer = other.buffer;
48
+ self.variables.variables.extend(other.variables.variables);
49
+ return;
40
50
  }
41
51
 
42
- self.buffer.resize(total_samples, 0);
52
+ self.mix(&other);
53
+ self.variables.variables.extend(other.variables.variables);
54
+ }
55
+
56
+ pub fn set_duration(&mut self, duration_secs: f32) {
57
+ let total_samples = (duration_secs * (SAMPLE_RATE as f32) * (CHANNELS as f32)) as usize;
58
+
59
+ if self.buffer.len() < total_samples {
60
+ self.buffer.resize(total_samples, 0);
61
+ }
43
62
  }
44
63
 
45
64
  pub fn set_variables(&mut self, variables: VariableTable) {
@@ -72,18 +91,76 @@ impl AudioEngine {
72
91
  Ok(())
73
92
  }
74
93
 
75
- pub fn insert(
94
+ pub fn insert_note(
95
+ &mut self,
96
+ waveform: String,
97
+ freq: f32,
98
+ amp: f32,
99
+ start_time_ms: f32,
100
+ duration_ms: f32
101
+ ) {
102
+ let sample_rate = SAMPLE_RATE as f32;
103
+ let channels = CHANNELS as usize;
104
+
105
+ let total_samples = ((duration_ms / 1000.0) * sample_rate) as usize;
106
+ let start_sample = ((start_time_ms / 1000.0) * sample_rate) as usize;
107
+ let amplitude = (i16::MAX as f32) * amp.clamp(0.0, 1.0);
108
+
109
+ let mut samples = Vec::with_capacity(total_samples);
110
+ let fade_len = (sample_rate * 0.01) as usize; // 10 ms fade
111
+
112
+ for i in 0..total_samples {
113
+ let t = ((start_sample + i) as f32) / sample_rate;
114
+ let phase = 2.0 * std::f32::consts::PI * freq * t;
115
+
116
+ let mut value = match waveform.as_str() {
117
+ "sine" => phase.sin(),
118
+ "square" => if phase.sin() >= 0.0 { 1.0 } else { -1.0 }
119
+ "saw" => 2.0 * (freq * t - (freq * t + 0.5).floor()),
120
+ "triangle" => (2.0 * (2.0 * (freq * t).fract() - 1.0)).abs() * 2.0 - 1.0,
121
+ _ => 0.0,
122
+ };
123
+
124
+ // Fade in/out
125
+ if i < fade_len {
126
+ value *= (i as f32) / (fade_len as f32);
127
+ } else if i >= total_samples - fade_len {
128
+ value *= ((total_samples - i) as f32) / (fade_len as f32);
129
+ }
130
+
131
+ samples.push((value * amplitude) as i16);
132
+ }
133
+
134
+ // Convert to stereo
135
+ let stereo_samples: Vec<i16> = samples
136
+ .iter()
137
+ .flat_map(|s| vec![*s, *s])
138
+ .collect();
139
+
140
+ let offset = start_sample * channels;
141
+ let required_len = offset + stereo_samples.len();
142
+
143
+ if self.buffer.len() < required_len {
144
+ self.buffer.resize(required_len, 0);
145
+ }
146
+
147
+ for (i, sample) in stereo_samples.iter().enumerate() {
148
+ if *sample != 0 {
149
+ }
150
+ self.buffer[offset + i] = self.buffer[offset + i].saturating_add(*sample);
151
+ }
152
+ }
153
+
154
+ pub fn insert_sample(
76
155
  &mut self,
77
156
  filepath: &str,
78
157
  time_secs: f32,
79
158
  dur_sec: f32,
80
159
  effects: Option<HashMap<String, f32>>
81
160
  ) {
82
- let normalized_filepath = normalize_path(filepath);
161
+ let resolved = resolve_relative_path(&self.module_name.clone(), filepath);
83
162
 
84
- let file = BufReader::new(
85
- File::open(normalized_filepath).expect("Failed to open audio file")
86
- );
163
+ let file = BufReader::new(File::open(resolved).expect("Failed to open audio file"));
87
164
  let decoder = Decoder::new(file).expect("Failed to decode audio file");
88
165
 
89
166
  // Mono or stereo reading possible here, we will duplicate in L/R