1 | // This file is part of SmallBASIC |
2 | // |
3 | // SmallBASIC RTL - SOUND |
4 | // |
5 | // This program is distributed under the terms of the GPL v2.0 or later |
6 | // Download the GNU Public License (GPL) from www.gnu.org |
7 | // |
8 | // Copyright(C) 2000 Nicholas Christopoulos |
9 | |
10 | #include "common/sys.h" |
11 | #include "common/str.h" |
12 | #include "common/kw.h" |
13 | #include "common/var.h" |
14 | #include "common/blib.h" |
15 | #include "common/pproc.h" |
16 | |
17 | static int tones[] = { |
18 | //c c# d d# e f f# g g# a a# b |
19 | 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 116, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, |
20 | 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, |
21 | 831, 880, 932, 988, 1046, 1109, 1175, 1245, 1318, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, |
22 | 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978, 5274, 5587, 5919, |
23 | 6271, 6645, 7040, 7459, 7902, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; |
24 | |
25 | static int O = 2, bg = 0, vol = 75; |
26 | static int period, duration, pitch = 440; |
27 | static double L = 4.0, T = 240.0, M = 1.0, TM = 1.0; |
28 | |
29 | #define FILE_PREFIX_LEN 7 |
30 | |
31 | // |
32 | // BEEP |
33 | // |
34 | void cmd_beep() { |
35 | dev_beep(); |
36 | } |
37 | |
38 | // |
39 | // SOUND frq, dur [, vol] [BG] |
40 | // |
41 | void cmd_sound() { |
42 | int frq, ms, vol = 100; |
43 | int bg = 0; |
44 | |
45 | frq = par_getint(); |
46 | if (!prog_error) { |
47 | par_getcomma(); |
48 | if (!prog_error) { |
49 | ms = par_getint(); |
50 | if (!prog_error) { |
51 | if (code_peek() == kwTYPE_SEP) { |
52 | par_getcomma(); |
53 | if (!prog_error) { |
54 | vol = par_getint(); |
55 | } |
56 | } |
57 | if (code_peek() == kwBACKG) { |
58 | code_skipnext(); |
59 | bg = 1; |
60 | } |
61 | if (!prog_error) { |
62 | dev_sound(frq, ms, vol, bg); |
63 | } |
64 | } |
65 | } |
66 | } |
67 | } |
68 | |
69 | void cmd_nosound() { |
70 | dev_clear_sound_queue(); |
71 | } |
72 | |
73 | void cmd_play_reset() { |
74 | O = 2; |
75 | bg = 0; |
76 | vol = 75; |
77 | period = 0; |
78 | duration = 0; |
79 | pitch = 440; |
80 | L = 4.0; |
81 | T = 240.0; |
82 | M = 1.0; |
83 | TM = 1.0; |
84 | } |
85 | |
86 | // |
87 | // PLAY str-cmds |
88 | // |
89 | #define CPLERR(c,a) { if ( (c) ) { rt_raise((a)); free(str); return; } } |
90 | void cmd_play() { |
91 | char *p; |
92 | var_t var; |
93 | int n = 0; |
94 | int calc_time_f = 1; |
95 | char *str, *s; |
96 | double TmpL; |
97 | |
98 | par_getstr(&var); |
99 | if (prog_error) { |
100 | return; |
101 | } |
102 | if (strncmp("file://" , var.v.p.ptr, FILE_PREFIX_LEN) == 0) { |
103 | const char *path = var.v.p.ptr + FILE_PREFIX_LEN; |
104 | if (dev_fexists(path)) { |
105 | dev_audio(path); |
106 | } else { |
107 | err_file_not_found(); |
108 | } |
109 | v_free(&var); |
110 | return; |
111 | } |
112 | |
113 | str = (char *) malloc(var.v.p.length + 1); |
114 | |
115 | // copy without spaces |
116 | p = var.v.p.ptr; |
117 | s = str; |
118 | while (*p) { |
119 | if (*p > 32) { |
120 | *s++ = to_upper(*p); |
121 | } |
122 | p++; |
123 | } |
124 | *s = '\0'; |
125 | v_free(&var); |
126 | |
127 | // run |
128 | p = str; |
129 | while (*p) { |
130 | if (dev_events(0) < 0) { |
131 | break; |
132 | } |
133 | if (calc_time_f) { |
134 | period = (4.0 / L) * (60000 / T) * TM; |
135 | duration = M * period; |
136 | calc_time_f = 0; |
137 | } |
138 | |
139 | switch (*p) { |
140 | // Volume |
141 | case 'V': |
142 | n = 0; |
143 | while (is_digit(*(p + 1))) { |
144 | p++; |
145 | n = (n * 10) + (*p - '0'); |
146 | } |
147 | |
148 | CPLERR((n < 0 || n > 100), "PLAY: V0-100" ); |
149 | vol = n; |
150 | break; |
151 | |
152 | // clear queue |
153 | case 'Q': |
154 | dev_clear_sound_queue(); |
155 | break; |
156 | |
157 | // Octaves |
158 | case '<': |
159 | if (O > 0) |
160 | O--; |
161 | break; |
162 | case '>': |
163 | if (O < 6) |
164 | O++; |
165 | break; |
166 | case 'O': |
167 | O = -1; |
168 | if (is_digit(*(p + 1))) { |
169 | p++; |
170 | O = *p - '0'; |
171 | } |
172 | |
173 | CPLERR((O < 0 || O > 6), "PLAY: O0-6" ); |
174 | break; |
175 | |
176 | // Time |
177 | case 'L': |
178 | n = 0; |
179 | while (is_digit(*(p + 1))) { |
180 | p++; |
181 | n = (n * 10) + (*p - '0'); |
182 | } |
183 | |
184 | CPLERR((n < 1 || n > 64), "PLAY: L1-64" ); |
185 | |
186 | L = n; |
187 | calc_time_f = 1; |
188 | break; |
189 | |
190 | case 'T': |
191 | n = 0; |
192 | while (is_digit(*(p + 1))) { |
193 | p++; |
194 | n = (n * 10) + (*p - '0'); |
195 | } |
196 | |
197 | CPLERR((n < 45 || n > 255), "PLAY: T32-255" ); |
198 | |
199 | T = n; |
200 | calc_time_f = 1; |
201 | break; |
202 | |
203 | case 'M': |
204 | p++; |
205 | switch (*p) { |
206 | case 'S': |
207 | M = 0.5; |
208 | break; |
209 | case 'N': |
210 | M = 3.0 / 4.0; |
211 | break; |
212 | case 'L': |
213 | M = 1.0; |
214 | break; |
215 | case 'F': |
216 | bg = 0; |
217 | break; |
218 | case 'B': |
219 | bg = 1; |
220 | break; |
221 | default: |
222 | rt_raise("PLAY: M%c UNSUPPORTED" , *p); |
223 | v_free(&var); |
224 | } |
225 | |
226 | calc_time_f = 1; |
227 | break; |
228 | |
229 | // Pause |
230 | case 'P': |
231 | n = 0; |
232 | while (is_digit(*(p + 1))) { |
233 | p++; |
234 | n = (n * 10) + (*p - '0'); |
235 | } |
236 | |
237 | if (*(p + 1) == '.') { |
238 | p++; |
239 | TM = 1.5; |
240 | } else { |
241 | TM = 1.0; |
242 | } |
243 | CPLERR((n < 1 || n > 64), "PLAY: P1-64" ); |
244 | period = (4.0 / n) * (60000 / T) * TM; |
245 | dev_sound(0, period, vol, bg); |
246 | calc_time_f = 1; |
247 | break; |
248 | |
249 | // Play N |
250 | case 'N': |
251 | n = 0; |
252 | while (is_digit(*(p + 1))) { |
253 | p++; |
254 | n = (n * 10) + (*p - '0'); |
255 | } |
256 | |
257 | CPLERR((n < 0 || n > 84), "PLAY: N0-84" ); |
258 | |
259 | if (n) { |
260 | // oct = n / 12; |
261 | // pitch = tones[n - oct * 12] * (1 << oct); |
262 | pitch = tones[n]; |
263 | } |
264 | |
265 | if (n == 0) |
266 | dev_sound(0, period, vol, bg); |
267 | else { |
268 | dev_sound(pitch, duration, vol, bg); |
269 | if (duration < period) |
270 | dev_sound(0, period - duration, vol, bg); |
271 | } |
272 | |
273 | calc_time_f = 1; |
274 | break; |
275 | |
276 | // Play note |
277 | case 'A': |
278 | case 'B': |
279 | case 'C': |
280 | case 'D': |
281 | case 'E': |
282 | case 'F': |
283 | case 'G': |
284 | switch (*p) { |
285 | case 'A': |
286 | n = 13; |
287 | break; |
288 | case 'B': |
289 | n = 15; |
290 | break; |
291 | case 'C': |
292 | n = 4; |
293 | break; |
294 | case 'D': |
295 | n = 6; |
296 | break; |
297 | case 'E': |
298 | n = 8; |
299 | break; |
300 | case 'F': |
301 | n = 9; |
302 | break; |
303 | case 'G': |
304 | n = 11; |
305 | break; |
306 | } |
307 | if (*(p + 1) == '-' || *(p + 1) == '+' || *(p + 1) == '#') { |
308 | p++; |
309 | if (*p == '-') |
310 | n--; |
311 | else |
312 | n++; |
313 | } |
314 | if (is_digit(*(p + 1))) { |
315 | TmpL = 0; |
316 | while (is_digit(*(p + 1))) { |
317 | p++; |
318 | TmpL = (TmpL * 10) + (*p - '0'); |
319 | } |
320 | |
321 | calc_time_f = 1; |
322 | } else { |
323 | TmpL = L; |
324 | } |
325 | if (*(p + 1) == '.') { |
326 | p++; |
327 | TM = 1.5; |
328 | } else { |
329 | TM = 1.0; |
330 | } |
331 | period = (4.0 / TmpL) * (60000 / T) * TM; |
332 | duration = M * period; |
333 | |
334 | // pitch = tones[n] * (1 << O); |
335 | pitch = tones[(n - 4) + O * 12]; |
336 | dev_sound(pitch, duration, vol, bg); |
337 | if (duration < period) { |
338 | dev_sound(0, period - duration, vol, bg); |
339 | } |
340 | break; |
341 | default: |
342 | rt_raise("PLAY: '%c' UNSUPPORTED" , *p); |
343 | free(str); |
344 | return; |
345 | } |
346 | |
347 | // next |
348 | if (*p) { |
349 | p++; |
350 | } |
351 | } |
352 | |
353 | free(str); |
354 | } |
355 | #undef CPLERR |
356 | |