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

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 (220) hide show
  1. package/.devalang +9 -10
  2. package/Cargo.toml +5 -4
  3. package/README.md +7 -5
  4. package/docs/CHANGELOG.md +42 -0
  5. package/docs/ROADMAP.md +5 -1
  6. package/docs/TODO.md +3 -14
  7. package/examples/bus.deva +10 -0
  8. package/examples/effect.deva +2 -0
  9. package/examples/filter.deva +11 -0
  10. package/examples/lfo.deva +9 -0
  11. package/examples/synth.deva +11 -1
  12. package/examples/synth_types.deva +17 -0
  13. package/out-tsc/core/functions/index.d.ts +5 -0
  14. package/out-tsc/core/functions/index.js +11 -0
  15. package/out-tsc/pkg/devalang_core.d.ts +2 -0
  16. package/out-tsc/pkg/devalang_core.js +17 -2
  17. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -7
  18. package/package.json +1 -1
  19. package/project-version.json +3 -3
  20. package/rust/cli/bank/api.rs +122 -122
  21. package/rust/cli/bank/commands.rs +33 -2
  22. package/rust/cli/bank/mod.rs +29 -29
  23. package/rust/cli/build/commands.rs +53 -3
  24. package/rust/cli/build/mod.rs +2 -2
  25. package/rust/cli/build/process.rs +26 -7
  26. package/rust/cli/check/mod.rs +2 -2
  27. package/rust/cli/discover/commands.rs +253 -253
  28. package/rust/cli/discover/config.rs +111 -111
  29. package/rust/cli/discover/fs.rs +19 -19
  30. package/rust/cli/discover/install.rs +103 -103
  31. package/rust/cli/discover/metadata.rs +48 -48
  32. package/rust/cli/discover/mod.rs +5 -5
  33. package/rust/cli/install/addon.rs +118 -118
  34. package/rust/cli/install/bank.rs +22 -3
  35. package/rust/cli/install/commands.rs +35 -35
  36. package/rust/cli/install/mod.rs +4 -4
  37. package/rust/cli/install/plugin.rs +80 -61
  38. package/rust/cli/login/commands.rs +124 -124
  39. package/rust/cli/mod.rs +12 -12
  40. package/rust/cli/parser.rs +46 -1
  41. package/rust/cli/play/commands.rs +71 -20
  42. package/rust/cli/play/mod.rs +5 -5
  43. package/rust/cli/play/process.rs +14 -5
  44. package/rust/cli/play/realtime.rs +91 -91
  45. package/rust/cli/telemetry/commands.rs +22 -22
  46. package/rust/cli/telemetry/event_creator.rs +80 -80
  47. package/rust/cli/telemetry/mod.rs +3 -3
  48. package/rust/cli/telemetry/send.rs +51 -51
  49. package/rust/cli/template/commands.rs +69 -69
  50. package/rust/config/driver.rs +112 -103
  51. package/rust/config/mod.rs +3 -3
  52. package/rust/config/ops.rs +26 -26
  53. package/rust/config/settings.rs +101 -101
  54. package/rust/core/audio/engine/driver.rs +220 -0
  55. package/rust/core/audio/engine/export.rs +169 -0
  56. package/rust/core/audio/engine/helpers.rs +178 -170
  57. package/rust/core/audio/engine/mod.rs +51 -2
  58. package/rust/core/audio/engine/notes/dsp.rs +85 -0
  59. package/rust/core/audio/engine/notes/mod.rs +44 -0
  60. package/rust/core/audio/engine/notes/params.rs +294 -0
  61. package/rust/core/audio/engine/sample/insert.rs +199 -0
  62. package/rust/core/audio/engine/sample/mod.rs +40 -0
  63. package/rust/core/audio/engine/sample/padding.rs +170 -0
  64. package/rust/core/audio/evaluator/condition.rs +61 -0
  65. package/rust/core/audio/evaluator/mod.rs +9 -0
  66. package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +1 -159
  67. package/rust/core/audio/evaluator/rhs.rs +16 -0
  68. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  69. package/rust/core/audio/interpreter/driver.rs +55 -23
  70. package/rust/core/audio/interpreter/mod.rs +1 -13
  71. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +175 -0
  72. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +384 -0
  73. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +2 -0
  74. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +316 -0
  75. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  76. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  77. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  78. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  79. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  80. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  81. package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +16 -18
  82. package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +5 -4
  83. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +2 -1
  84. package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +2 -4
  85. package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +2 -4
  86. package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +2 -4
  87. package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +2 -1
  88. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  89. package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
  90. package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +3 -2
  91. package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
  92. package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +1 -1
  93. package/rust/core/audio/loader/trigger.rs +2 -1
  94. package/rust/core/audio/mod.rs +6 -7
  95. package/rust/core/audio/player.rs +70 -70
  96. package/rust/core/audio/special/easing.rs +189 -189
  97. package/rust/core/audio/special/env.rs +45 -45
  98. package/rust/core/audio/special/math.rs +134 -134
  99. package/rust/core/audio/special/mod.rs +9 -9
  100. package/rust/core/audio/special/modulator.rs +143 -143
  101. package/rust/core/builder/mod.rs +45 -2
  102. package/rust/core/debugger/lexer.rs +27 -27
  103. package/rust/core/debugger/{module.rs → logs.rs} +3 -6
  104. package/rust/core/debugger/mod.rs +30 -30
  105. package/rust/core/debugger/preprocessor.rs +27 -27
  106. package/rust/core/debugger/store.rs +2 -4
  107. package/rust/core/error/mod.rs +269 -269
  108. package/rust/core/lexer/driver.rs +59 -61
  109. package/rust/core/lexer/handler/arrow.rs +82 -82
  110. package/rust/core/lexer/handler/at.rs +21 -21
  111. package/rust/core/lexer/handler/brace.rs +41 -41
  112. package/rust/core/lexer/handler/colon.rs +21 -21
  113. package/rust/core/lexer/handler/comment.rs +30 -30
  114. package/rust/core/lexer/handler/dot.rs +21 -21
  115. package/rust/core/lexer/handler/driver.rs +337 -337
  116. package/rust/core/lexer/handler/identifier.rs +47 -47
  117. package/rust/core/lexer/handler/indent.rs +66 -66
  118. package/rust/core/lexer/handler/mod.rs +15 -15
  119. package/rust/core/lexer/handler/newline.rs +23 -23
  120. package/rust/core/lexer/handler/number.rs +31 -31
  121. package/rust/core/lexer/handler/operator.rs +46 -46
  122. package/rust/core/lexer/handler/parenthesis.rs +41 -41
  123. package/rust/core/lexer/handler/slash.rs +21 -21
  124. package/rust/core/lexer/handler/string.rs +63 -63
  125. package/rust/core/lexer/mod.rs +3 -3
  126. package/rust/core/mod.rs +0 -1
  127. package/rust/core/parser/driver/block.rs +111 -0
  128. package/rust/core/parser/driver/cursor.rs +82 -0
  129. package/rust/core/parser/driver/driver_impl.rs +139 -0
  130. package/rust/core/parser/driver/mod.rs +6 -0
  131. package/rust/core/parser/driver/parse_array.rs +120 -0
  132. package/rust/core/parser/driver/parse_map.rs +223 -0
  133. package/rust/core/parser/driver/parser.rs +160 -0
  134. package/rust/core/parser/handler/arrow_call.rs +28 -4
  135. package/rust/core/parser/handler/at.rs +279 -279
  136. package/rust/core/parser/handler/bank.rs +104 -104
  137. package/rust/core/parser/handler/condition.rs +83 -83
  138. package/rust/core/parser/handler/dot.rs +148 -148
  139. package/rust/core/parser/handler/identifier/automate.rs +254 -254
  140. package/rust/core/parser/handler/identifier/call.rs +91 -91
  141. package/rust/core/parser/handler/identifier/emit.rs +70 -70
  142. package/rust/core/parser/handler/identifier/function.rs +113 -113
  143. package/rust/core/parser/handler/identifier/group.rs +89 -89
  144. package/rust/core/parser/handler/identifier/let_.rs +173 -173
  145. package/rust/core/parser/handler/identifier/mod.rs +55 -55
  146. package/rust/core/parser/handler/identifier/on.rs +107 -107
  147. package/rust/core/parser/handler/identifier/print.rs +49 -49
  148. package/rust/core/parser/handler/identifier/sleep.rs +96 -43
  149. package/rust/core/parser/handler/identifier/spawn.rs +91 -91
  150. package/rust/core/parser/handler/identifier/synth.rs +135 -135
  151. package/rust/core/parser/handler/loop_.rs +194 -194
  152. package/rust/core/parser/handler/mod.rs +9 -9
  153. package/rust/core/parser/handler/pattern.rs +1 -1
  154. package/rust/core/parser/handler/tempo.rs +105 -57
  155. package/rust/core/parser/statement.rs +10 -11
  156. package/rust/core/plugin/loader.rs +1 -1
  157. package/rust/core/plugin/mod.rs +2 -2
  158. package/rust/core/plugin/runner/mod.rs +11 -0
  159. package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +297 -347
  160. package/rust/core/plugin/runner/wasm32.rs +43 -0
  161. package/rust/core/preprocessor/loader/inject.rs +278 -0
  162. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  163. package/rust/core/preprocessor/loader/mod.rs +235 -0
  164. package/rust/core/preprocessor/module.rs +2 -7
  165. package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +6 -13
  166. package/rust/core/preprocessor/processor/mod.rs +1 -0
  167. package/rust/core/preprocessor/resolver/bank.rs +49 -49
  168. package/rust/core/preprocessor/resolver/call.rs +124 -124
  169. package/rust/core/preprocessor/resolver/condition.rs +95 -95
  170. package/rust/core/preprocessor/resolver/driver.rs +324 -324
  171. package/rust/core/preprocessor/resolver/function.rs +2 -2
  172. package/rust/core/preprocessor/resolver/group.rs +46 -18
  173. package/rust/core/preprocessor/resolver/let_.rs +32 -32
  174. package/rust/core/preprocessor/resolver/loop_.rs +318 -318
  175. package/rust/core/preprocessor/resolver/mod.rs +16 -16
  176. package/rust/core/preprocessor/resolver/pattern.rs +83 -83
  177. package/rust/core/preprocessor/resolver/spawn.rs +99 -99
  178. package/rust/core/preprocessor/resolver/synth.rs +54 -54
  179. package/rust/core/preprocessor/resolver/tempo.rs +48 -48
  180. package/rust/core/preprocessor/resolver/trigger.rs +116 -116
  181. package/rust/core/preprocessor/resolver/value.rs +176 -176
  182. package/rust/core/store/global.rs +2 -6
  183. package/rust/core/store/mod.rs +1 -5
  184. package/rust/lib.rs +18 -3
  185. package/rust/main.rs +27 -3
  186. package/rust/types/Cargo.toml +1 -1
  187. package/rust/types/src/addons.rs +55 -55
  188. package/rust/types/src/config.rs +84 -74
  189. package/rust/types/src/lib.rs +15 -12
  190. package/rust/types/src/plugin.rs +20 -0
  191. package/rust/types/src/store.rs +139 -0
  192. package/rust/types/src/telemetry.rs +85 -85
  193. package/rust/utils/Cargo.toml +2 -2
  194. package/rust/utils/src/file.rs +94 -94
  195. package/rust/utils/src/first_usage.rs +97 -97
  196. package/rust/utils/src/lib.rs +9 -9
  197. package/rust/utils/src/logger.rs +200 -200
  198. package/rust/utils/src/path.rs +129 -88
  199. package/rust/utils/src/signature.rs +41 -41
  200. package/rust/utils/src/spinner.rs +20 -20
  201. package/rust/utils/src/version.rs +27 -27
  202. package/rust/utils/src/watcher.rs +46 -46
  203. package/rust/web/api.rs +5 -5
  204. package/rust/web/cdn.rs +34 -34
  205. package/rust/web/mod.rs +3 -3
  206. package/tests/integration.rs +21 -21
  207. package/typescript/core/functions/index.ts +11 -0
  208. package/typescript/pkg/devalang_core.ts +20 -4
  209. package/rust/core/audio/engine/sample.rs +0 -366
  210. package/rust/core/audio/engine/synth.rs +0 -325
  211. package/rust/core/audio/interpreter/arrow_call.rs +0 -311
  212. package/rust/core/audio/renderer.rs +0 -54
  213. package/rust/core/parser/driver.rs +0 -584
  214. package/rust/core/preprocessor/loader.rs +0 -637
  215. package/rust/core/store/export.rs +0 -28
  216. package/rust/core/store/function.rs +0 -40
  217. package/rust/core/store/import.rs +0 -28
  218. package/rust/core/store/variable.rs +0 -51
  219. package/rust/core/utils/mod.rs +0 -1
  220. package/rust/core/utils/path.rs +0 -37
@@ -1,124 +1,124 @@
1
- use crate::config::settings::set_user_config_value;
2
- use crate::web::sso::get_sso_url;
3
- use std::str::FromStr;
4
- use tiny_http::Header;
5
- use tiny_http::{Response, Server};
6
- use webbrowser;
7
-
8
- /// Handle the login command
9
- /// This function initiates the login process by opening the browser and waiting for the callback.
10
- #[cfg(feature = "cli")]
11
- pub async fn handle_login_command() -> Result<(), String> {
12
- use devalang_utils::logger::LogLevel;
13
- use devalang_utils::logger::Logger;
14
- use devalang_utils::spinner::start_spinner;
15
-
16
- let logger = Logger::new();
17
-
18
- let mut listener_port = 7878;
19
-
20
- let test_port_already_in_use = format!("127.0.0.1:{}", listener_port);
21
- while std::net::TcpListener::bind(&test_port_already_in_use).is_err() {
22
- listener_port += 1;
23
- }
24
-
25
- let redirect_uri = format!("http://127.0.0.1:{}/callback", listener_port);
26
- let login_url = format!(
27
- "{}/?response_type=code&referer=cli&redirect_uri={}",
28
- get_sso_url(),
29
- redirect_uri
30
- );
31
-
32
- if webbrowser::open(&login_url).is_ok() {
33
- logger.log_message(LogLevel::Info, "Opening browser for login...");
34
- logger.log_message(
35
- LogLevel::Info,
36
- &format!(
37
- "If the browser does not open, please visit the following URL: {}",
38
- login_url
39
- ),
40
- );
41
- } else {
42
- logger.log_message(
43
- LogLevel::Info,
44
- "Please open the following URL in your browser to login:",
45
- );
46
- logger.log_message(LogLevel::Info, &login_url);
47
- }
48
-
49
- let server = Server::http(format!("127.0.0.1:{}", listener_port)).unwrap();
50
-
51
- let spinner = start_spinner("Waiting for authentication...");
52
-
53
- for request in server.incoming_requests() {
54
- let query = request.url().to_string();
55
- if request.url().starts_with("/callback") {
56
- if query.contains("session=") || query.contains("error=") {
57
- let token = query.split("session=").nth(1).unwrap_or("").to_string();
58
-
59
- if !token.is_empty() {
60
- let response_html = r#"
61
- <html>
62
- <body>
63
- <h1>Authentication Successful</h1>
64
- <h2>You can now close this window.</h2>
65
- </body>
66
- </html>
67
- "#;
68
-
69
- let response = Response::from_string(response_html)
70
- .with_status_code(200)
71
- .with_header(Header::from_str("Content-Type: text/html").unwrap());
72
-
73
- request.respond(response).unwrap();
74
-
75
- save_token(&token);
76
-
77
- spinner.finish_and_clear();
78
-
79
- logger.log_message(
80
- LogLevel::Success,
81
- "Authentication successful. Token saved to ~/.devalang/config.json",
82
- );
83
-
84
- break;
85
- } else {
86
- spinner.finish_and_clear();
87
- logger.log_message(LogLevel::Error, "Invalid session token.");
88
- request
89
- .respond(Response::from_string("Invalid session token."))
90
- .unwrap();
91
-
92
- break;
93
- }
94
- } else {
95
- println!("Invalid callback: {}", request.url());
96
-
97
- spinner.finish_and_clear();
98
- logger.log_message(LogLevel::Error, "Invalid callback.");
99
- request
100
- .respond(Response::from_string("Invalid callback."))
101
- .unwrap();
102
-
103
- break;
104
- }
105
- } else if request.url().starts_with("/favicon.ico") {
106
- // Ignore favicon requests
107
- } else {
108
- spinner.finish_and_clear();
109
- logger.log_message(LogLevel::Error, "Invalid request.");
110
- request
111
- .respond(Response::from_string("Invalid request."))
112
- .unwrap();
113
-
114
- break;
115
- }
116
- }
117
-
118
- Ok(())
119
- }
120
-
121
- /// Save the session token to a file in the user's home directory
122
- fn save_token(token: &str) {
123
- set_user_config_value("session", serde_json::Value::String(token.to_string()));
124
- }
1
+ use crate::config::settings::set_user_config_value;
2
+ use crate::web::sso::get_sso_url;
3
+ use std::str::FromStr;
4
+ use tiny_http::Header;
5
+ use tiny_http::{Response, Server};
6
+ use webbrowser;
7
+
8
+ /// Handle the login command
9
+ /// This function initiates the login process by opening the browser and waiting for the callback.
10
+ #[cfg(feature = "cli")]
11
+ pub async fn handle_login_command() -> Result<(), String> {
12
+ use devalang_utils::logger::LogLevel;
13
+ use devalang_utils::logger::Logger;
14
+ use devalang_utils::spinner::start_spinner;
15
+
16
+ let logger = Logger::new();
17
+
18
+ let mut listener_port = 7878;
19
+
20
+ let test_port_already_in_use = format!("127.0.0.1:{}", listener_port);
21
+ while std::net::TcpListener::bind(&test_port_already_in_use).is_err() {
22
+ listener_port += 1;
23
+ }
24
+
25
+ let redirect_uri = format!("http://127.0.0.1:{}/callback", listener_port);
26
+ let login_url = format!(
27
+ "{}/?response_type=code&referer=cli&redirect_uri={}",
28
+ get_sso_url(),
29
+ redirect_uri
30
+ );
31
+
32
+ if webbrowser::open(&login_url).is_ok() {
33
+ logger.log_message(LogLevel::Info, "Opening browser for login...");
34
+ logger.log_message(
35
+ LogLevel::Info,
36
+ &format!(
37
+ "If the browser does not open, please visit the following URL: {}",
38
+ login_url
39
+ ),
40
+ );
41
+ } else {
42
+ logger.log_message(
43
+ LogLevel::Info,
44
+ "Please open the following URL in your browser to login:",
45
+ );
46
+ logger.log_message(LogLevel::Info, &login_url);
47
+ }
48
+
49
+ let server = Server::http(format!("127.0.0.1:{}", listener_port)).unwrap();
50
+
51
+ let spinner = start_spinner("Waiting for authentication...");
52
+
53
+ for request in server.incoming_requests() {
54
+ let query = request.url().to_string();
55
+ if request.url().starts_with("/callback") {
56
+ if query.contains("session=") || query.contains("error=") {
57
+ let token = query.split("session=").nth(1).unwrap_or("").to_string();
58
+
59
+ if !token.is_empty() {
60
+ let response_html = r#"
61
+ <html>
62
+ <body>
63
+ <h1>Authentication Successful</h1>
64
+ <h2>You can now close this window.</h2>
65
+ </body>
66
+ </html>
67
+ "#;
68
+
69
+ let response = Response::from_string(response_html)
70
+ .with_status_code(200)
71
+ .with_header(Header::from_str("Content-Type: text/html").unwrap());
72
+
73
+ request.respond(response).unwrap();
74
+
75
+ save_token(&token);
76
+
77
+ spinner.finish_and_clear();
78
+
79
+ logger.log_message(
80
+ LogLevel::Success,
81
+ "Authentication successful. Token saved to ~/.devalang/config.json",
82
+ );
83
+
84
+ break;
85
+ } else {
86
+ spinner.finish_and_clear();
87
+ logger.log_message(LogLevel::Error, "Invalid session token.");
88
+ request
89
+ .respond(Response::from_string("Invalid session token."))
90
+ .unwrap();
91
+
92
+ break;
93
+ }
94
+ } else {
95
+ println!("Invalid callback: {}", request.url());
96
+
97
+ spinner.finish_and_clear();
98
+ logger.log_message(LogLevel::Error, "Invalid callback.");
99
+ request
100
+ .respond(Response::from_string("Invalid callback."))
101
+ .unwrap();
102
+
103
+ break;
104
+ }
105
+ } else if request.url().starts_with("/favicon.ico") {
106
+ // Ignore favicon requests
107
+ } else {
108
+ spinner.finish_and_clear();
109
+ logger.log_message(LogLevel::Error, "Invalid request.");
110
+ request
111
+ .respond(Response::from_string("Invalid request."))
112
+ .unwrap();
113
+
114
+ break;
115
+ }
116
+ }
117
+
118
+ Ok(())
119
+ }
120
+
121
+ /// Save the session token to a file in the user's home directory
122
+ fn save_token(token: &str) {
123
+ set_user_config_value("session", serde_json::Value::String(token.to_string()));
124
+ }
package/rust/cli/mod.rs CHANGED
@@ -1,12 +1,12 @@
1
- pub mod bank;
2
- pub mod build;
3
- pub mod check;
4
- pub mod discover;
5
- pub mod init;
6
- pub mod install;
7
- pub mod login;
8
- pub mod parser;
9
- pub mod play;
10
- pub mod telemetry;
11
- pub mod template;
12
- pub mod update;
1
+ pub mod bank;
2
+ pub mod build;
3
+ pub mod check;
4
+ pub mod discover;
5
+ pub mod init;
6
+ pub mod install;
7
+ pub mod login;
8
+ pub mod parser;
9
+ pub mod play;
10
+ pub mod telemetry;
11
+ pub mod template;
12
+ pub mod update;
@@ -1,4 +1,17 @@
1
- use clap::{Parser, Subcommand};
1
+ use clap::{Parser, Subcommand, ValueEnum};
2
+
3
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
4
+ pub enum OutputFormat {
5
+ Wav,
6
+ Mid,
7
+ }
8
+
9
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
10
+ pub enum AudioFormat {
11
+ Wav16,
12
+ Wav24,
13
+ Wav32,
14
+ }
2
15
 
3
16
  #[derive(Parser)]
4
17
  #[command(name = "devalang")]
@@ -124,6 +137,26 @@ pub enum Commands {
124
137
  ///
125
138
  output: Option<String>,
126
139
 
140
+ #[arg(long, value_enum, value_delimiter = ',', default_value = "wav,mid")]
141
+ /// Which output formats to generate. Comma-separated list is accepted.
142
+ ///
143
+ /// ### Default value
144
+ /// - `wav,mid`
145
+ ///
146
+ output_format: Vec<OutputFormat>,
147
+
148
+ #[arg(long, default_value = "wav16")]
149
+ /// Audio format to use for file outputs (affects WAV export).
150
+ ///
151
+ /// ### Default value
152
+ /// - `wav16`
153
+ ///
154
+ audio_format: AudioFormat,
155
+
156
+ #[arg(long, default_value_t = 44100u32)]
157
+ /// Sample rate to use for audio export and playback (e.g. 44100, 48000)
158
+ sample_rate: u32,
159
+
127
160
  #[arg(long, default_value_t = false)]
128
161
  /// Whether to watch for changes and rebuild.
129
162
  ///
@@ -213,6 +246,18 @@ pub enum Commands {
213
246
  /// The directory where the output files will be generated.
214
247
  output: Option<String>,
215
248
 
249
+ #[arg(long, default_value_t = 44100u32)]
250
+ /// Sample rate to use for playback (e.g. 44100, 48000)
251
+ sample_rate: u32,
252
+
253
+ #[arg(long, default_value = "wav16")]
254
+ /// Audio format to use for file outputs (affects WAV export).
255
+ ///
256
+ /// ### Default value
257
+ /// - `wav16`
258
+ ///
259
+ audio_format: AudioFormat,
260
+
216
261
  #[arg(long, default_value_t = false)]
217
262
  /// Whether to watch for changes and re-play.
218
263
  ///
@@ -1,5 +1,6 @@
1
1
  use crate::config::driver::ProjectConfig;
2
2
  use devalang_utils::logger::{LogLevel, Logger};
3
+ use devalang_utils::path::{find_entry_file, normalize_path};
3
4
  use std::{sync::mpsc::channel, thread};
4
5
 
5
6
  pub use crate::cli::play::io::wav_duration_seconds;
@@ -17,12 +18,34 @@ pub fn handle_play_command(
17
18
  config: Option<ProjectConfig>,
18
19
  entry: Option<String>,
19
20
  output: Option<String>,
21
+ audio_format: crate::cli::parser::AudioFormat,
22
+ sample_rate: u32,
20
23
  watch: bool,
21
24
  repeat: bool,
22
25
  debug: bool,
23
26
  ) -> Result<(), String> {
24
27
  let logger = Logger::new();
25
28
 
29
+ // Determine final audio_format and sample_rate, preferring CLI values but falling back to config defaults
30
+ let mut final_audio_format = audio_format;
31
+ let mut final_sample_rate = sample_rate;
32
+ if let Some(cfg) = config.as_ref() {
33
+ if let Some(af) = cfg.defaults.audio_format.as_ref() {
34
+ let af_low = af.to_lowercase();
35
+ final_audio_format = match af_low.as_str() {
36
+ "wav24" => crate::cli::parser::AudioFormat::Wav24,
37
+ "wav32" => crate::cli::parser::AudioFormat::Wav32,
38
+ _ => crate::cli::parser::AudioFormat::Wav16,
39
+ };
40
+ }
41
+ if let Some(sr) = cfg.defaults.sample_rate {
42
+ // Only override if CLI provided a 0 or unrealistic value (we assume CLI provides a valid value)
43
+ if final_sample_rate == 0 {
44
+ final_sample_rate = sr;
45
+ }
46
+ }
47
+ }
48
+
26
49
  let entry_path = entry
27
50
  .or_else(|| config.as_ref().and_then(|c| c.defaults.entry.clone()))
28
51
  .unwrap_or_default();
@@ -45,7 +68,7 @@ pub fn handle_play_command(
45
68
  return Err("missing entry or output".to_string());
46
69
  }
47
70
 
48
- let entry_file = match crate::core::utils::path::find_entry_file(&entry_path) {
71
+ let entry_file = match find_entry_file(&entry_path) {
49
72
  Some(p) => p,
50
73
  None => {
51
74
  logger.log_message(LogLevel::Error, "index.deva not found");
@@ -53,10 +76,7 @@ pub fn handle_play_command(
53
76
  }
54
77
  };
55
78
 
56
- let audio_file = format!(
57
- "{}/audio/index.wav",
58
- crate::core::utils::path::normalize_path(&output_path)
59
- );
79
+ let audio_file = format!("{}/audio/index.wav", normalize_path(&output_path));
60
80
  let mut audio_player = AudioPlayer::new();
61
81
 
62
82
  if watch && fetched_repeat {
@@ -79,8 +99,14 @@ pub fn handle_play_command(
79
99
  });
80
100
 
81
101
  // Main thread: build + play in a loop
82
- let (bpm, entry_stmts, variables, functions, global_store) =
83
- process_play(&config, &entry_file, &output_path, debug)?;
102
+ let (bpm, entry_stmts, variables, functions, global_store) = process_play(
103
+ &config,
104
+ &entry_file,
105
+ &output_path,
106
+ final_audio_format,
107
+ final_sample_rate,
108
+ debug,
109
+ )?;
84
110
  audio_player.play_file_once(&audio_file);
85
111
  // Estimate duration: base on statement count plus extra for loop iterations (1 beat per iter)
86
112
  let loop_iters: usize = entry_stmts
@@ -130,14 +156,20 @@ pub fn handle_play_command(
130
156
  // Stop previous real-time runner before restarting playback
131
157
  stop_realtime_runner(&mut rt_runner);
132
158
 
133
- let (bpm, entry_stmts, variables, functions, global_store) =
134
- match process_play(&config, &entry_file, &output_path, debug) {
135
- Ok(v) => v,
136
- Err(e) => {
137
- logger.log_message(LogLevel::Error, &format!("Rebuild failed: {}", e));
138
- continue;
139
- }
140
- };
159
+ let (bpm, entry_stmts, variables, functions, global_store) = match process_play(
160
+ &config,
161
+ &entry_file,
162
+ &output_path,
163
+ final_audio_format,
164
+ final_sample_rate,
165
+ debug,
166
+ ) {
167
+ Ok(v) => v,
168
+ Err(e) => {
169
+ logger.log_message(LogLevel::Error, &format!("Rebuild failed: {}", e));
170
+ continue;
171
+ }
172
+ };
141
173
 
142
174
  logger.log_message(LogLevel::Info, "🎵 Playback started (once mode)...");
143
175
 
@@ -180,8 +212,14 @@ pub fn handle_play_command(
180
212
  }
181
213
  } else if fetched_repeat {
182
214
  // Initial build to start from a clean slate
183
- let (bpm, entry_stmts, variables, functions, global_store) =
184
- process_play(&config, &entry_file, &output_path, debug)?;
215
+ let (bpm, entry_stmts, variables, functions, global_store) = process_play(
216
+ &config,
217
+ &entry_file,
218
+ &output_path,
219
+ final_audio_format,
220
+ final_sample_rate,
221
+ debug,
222
+ )?;
185
223
 
186
224
  logger.log_message(LogLevel::Info, "🎵 Playback started (repeat mode)...");
187
225
 
@@ -241,7 +279,14 @@ pub fn handle_play_command(
241
279
 
242
280
  // Rebuild in a separate thread
243
281
  std::thread::spawn(move || {
244
- if let Err(e) = process_play(&config_clone, &entry_file, &output_path, debug) {
282
+ if let Err(e) = process_play(
283
+ &config_clone,
284
+ &entry_file,
285
+ &output_path,
286
+ final_audio_format,
287
+ final_sample_rate,
288
+ debug,
289
+ ) {
245
290
  eprintln!("Rebuild failed in background: {}", e);
246
291
  }
247
292
  });
@@ -294,8 +339,14 @@ pub fn handle_play_command(
294
339
  }
295
340
  } else {
296
341
  // Single execution
297
- let (bpm, entry_stmts, variables, functions, global_store) =
298
- process_play(&config, &entry_file, &output_path, debug)?;
342
+ let (bpm, entry_stmts, variables, functions, global_store) = process_play(
343
+ &config,
344
+ &entry_file,
345
+ &output_path,
346
+ final_audio_format,
347
+ final_sample_rate,
348
+ debug,
349
+ )?;
299
350
 
300
351
  logger.log_message(LogLevel::Info, "🎵 Playback started (once mode)...");
301
352
 
@@ -1,5 +1,5 @@
1
- pub mod commands;
2
- pub mod io;
3
- pub mod process;
4
- pub mod realtime;
5
- pub mod utils;
1
+ pub mod commands;
2
+ pub mod io;
3
+ pub mod process;
4
+ pub mod realtime;
5
+ pub mod utils;
@@ -4,15 +4,15 @@ use crate::{
4
4
  builder::Builder,
5
5
  debugger::{
6
6
  lexer::write_lexer_log_file,
7
- module::{write_module_function_log_file, write_module_variable_log_file},
7
+ logs::{write_module_function_log_file, write_module_variable_log_file},
8
8
  preprocessor::write_preprocessor_log_file,
9
9
  store::{write_function_log_file, write_variables_log_file},
10
10
  },
11
11
  preprocessor::loader::ModuleLoader,
12
12
  store::global::GlobalStore,
13
- utils::path::normalize_path,
14
13
  },
15
14
  };
15
+ use devalang_utils::path::normalize_path;
16
16
  use devalang_utils::{
17
17
  logger::{LogLevel, Logger},
18
18
  spinner::start_spinner,
@@ -22,13 +22,15 @@ pub fn process_play(
22
22
  _config: &Option<ProjectConfig>,
23
23
  entry_file: &str,
24
24
  output: &str,
25
+ audio_format: crate::cli::parser::AudioFormat,
26
+ sample_rate: u32,
25
27
  debug: bool,
26
28
  ) -> Result<
27
29
  (
28
30
  f32,
29
31
  Vec<crate::core::parser::statement::Statement>,
30
- crate::core::store::variable::VariableTable,
31
- crate::core::store::function::FunctionTable,
32
+ devalang_types::VariableTable,
33
+ devalang_types::FunctionTable,
32
34
  crate::core::store::global::GlobalStore,
33
35
  ),
34
36
  String,
@@ -127,7 +129,14 @@ pub fn process_play(
127
129
  // SECTION Building AST and Audio
128
130
  let builder = Builder::new();
129
131
  builder.build_ast(&modules_statements, output, false);
130
- builder.build_audio(&modules_statements, output, &mut global_store);
132
+ let audio_format_str = format!("{:?}", audio_format);
133
+ builder.build_audio(
134
+ &modules_statements,
135
+ output,
136
+ &mut global_store,
137
+ Some(audio_format_str),
138
+ Some(sample_rate),
139
+ );
131
140
 
132
141
  // SECTION Logging
133
142
  let logger = Logger::new();