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
17static 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
25static int O = 2, bg = 0, vol = 75;
26static int period, duration, pitch = 440;
27static 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//
34void cmd_beep() {
35 dev_beep();
36}
37
38//
39// SOUND frq, dur [, vol] [BG]
40//
41void 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
69void cmd_nosound() {
70 dev_clear_sound_queue();
71}
72
73void 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; } }
90void 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