rts-sounds.c
1 //! @file rts-sounds.c
2 //! @author J. Marcel van der Veer
3
4 //! @section Copyright
5 //!
6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
7 //! Copyright 2001-2024 J. Marcel van der Veer [algol68g@xs4all.nl].
8
9 //! @section License
10 //!
11 //! This program is free software; you can redistribute it and/or modify it
12 //! under the terms of the GNU General Public License as published by the
13 //! Free Software Foundation; either version 3 of the License, or
14 //! (at your option) any later version.
15 //!
16 //! This program is distributed in the hope that it will be useful, but
17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 //! more details. You should have received a copy of the GNU General Public
20 //! License along with this program. If not, see [http://www.gnu.org/licenses/].
21
22 //! @section Synopsis
23 //!
24 //! SOUND routines.
25
26 #include "a68g.h"
27 #include "a68g-genie.h"
28 #include "a68g-prelude.h"
29
30 // Implementation of SOUND values.
31
32 #define MAX_BYTES 4
33
34 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
35 #define A68_LITTLE_ENDIAN A68_TRUE
36 #define A68_BIG_ENDIAN A68_FALSE
37 #elif (__BYTE_ORDER == __BIG_ENDIAN)
38 #define A68_LITTLE_ENDIAN A68_FALSE
39 #define A68_BIG_ENDIAN A68_TRUE
40 #else
41 #error "undefined endianness"
42 #endif
43
44 // From public Microsoft RIFF documentation.
45
46 #define WAVE_FORMAT_UNKNOWN (0x0000)
47 #define WAVE_FORMAT_PCM (0x0001)
48 #define WAVE_FORMAT_ADPCM (0x0002)
49 #define WAVE_FORMAT_IEEE_FLOAT (0x0003)
50 #define WAVE_FORMAT_IBM_FORMAT_CVSD (0x0005)
51 #define WAVE_FORMAT_ALAW (0x0006)
52 #define WAVE_FORMAT_MULAW (0x0007)
53 #define WAVE_FORMAT_OKI_ADPCM (0x0010)
54 #define WAVE_FORMAT_DVI_ADPCM (0x0011)
55 #define WAVE_FORMAT_MEDIASPACE_ADPCM (0x0012)
56 #define WAVE_FORMAT_SIERRA_ADPCM (0x0013)
57 #define WAVE_FORMAT_G723_ADPCM (0X0014)
58 #define WAVE_FORMAT_DIGISTD (0x0015)
59 #define WAVE_FORMAT_DIGIFIX (0x0016)
60 #define WAVE_FORMAT_YAMAHA_ADPCM (0x0020)
61 #define WAVE_FORMAT_SONARC (0x0021)
62 #define WAVE_FORMAT_DSPGROUP_TRUESPEECH (0x0022)
63 #define WAVE_FORMAT_ECHOSCI1 (0x0023)
64 #define WAVE_FORMAT_AUDIOFILE_AF36 (0x0024)
65 #define WAVE_FORMAT_APTX (0x0025)
66 #define WAVE_FORMAT_AUDIOFILE_AF10 (0x0026)
67 #define WAVE_FORMAT_DOLBY_AC2 (0x0030)
68 #define WAVE_FORMAT_GSM610 (0x0031)
69 #define WAVE_FORMAT_ANTEX_ADPCME (0x0033)
70 #define WAVE_FORMAT_CONTROL_RES_VQLPC (0x0034)
71 #define WAVE_FORMAT_DIGIREAL (0x0035)
72 #define WAVE_FORMAT_DIGIADPCM (0x0036)
73 #define WAVE_FORMAT_CONTROL_RES_CR10 (0x0037)
74 #define WAVE_FORMAT_NMS_VBXADPCM (0x0038)
75 #define WAVE_FORMAT_ROCKWELL_ADPCM (0x003b)
76 #define WAVE_FORMAT_ROCKWELL_DIGITALK (0x003c)
77 #define WAVE_FORMAT_G721_ADPCM (0x0040)
78 #define WAVE_FORMAT_G728_CELP (0x0041)
79 #define WAVE_FORMAT_MPEG (0x0050)
80 #define WAVE_FORMAT_MPEGLAYER3 (0x0055)
81 #define WAVE_FORMAT_G726_ADPCM (0x0064)
82 #define WAVE_FORMAT_G722_ADPCM (0x0065)
83 #define WAVE_FORMAT_IBM_FORMAT_MULAW (0x0101)
84 #define WAVE_FORMAT_IBM_FORMAT_ALAW (0x0102)
85 #define WAVE_FORMAT_IBM_FORMAT_ADPCM (0x0103)
86 #define WAVE_FORMAT_CREATIVE_ADPCM (0x0200)
87 #define WAVE_FORMAT_FM_TOWNS_SND (0x0300)
88 #define WAVE_FORMAT_OLIGSM (0x1000)
89 #define WAVE_FORMAT_OLIADPCM (0x1001)
90 #define WAVE_FORMAT_OLICELP (0x1002)
91 #define WAVE_FORMAT_OLISBC (0x1003)
92 #define WAVE_FORMAT_OLIOPR (0x1004)
93 #define WAVE_FORMAT_EXTENSIBLE (0xfffe)
94
95 static unt pow256[] = { 1, 256, 65536, 16777216 };
96
97 //! @brief Test bits per sample.
98
99 void test_bits_per_sample (NODE_T * p, unt bps)
100 {
101 if (bps <= 0 || bps > 24) {
102 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "unsupported number of bits per sample");
103 exit_genie (p, A68_RUNTIME_ERROR);
104 }
105 }
106
107 //! @brief Code string into big-endian unt.
108
109 unt code_string (NODE_T * p, char *s, int n)
110 {
111 if (n > MAX_BYTES) {
112 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "too long word length");
113 exit_genie (p, A68_RUNTIME_ERROR);
114 }
115 unt v = 0; int m = n - 1;
116 for (int k = 0; k < n; k++, m--) {
117 v += ((unt) s[k]) * pow256[m];
118 } return v;
119 }
120
121 //! @brief Code unt into string.
122
123 char *code_unt (NODE_T * p, unt n)
124 {
125 static char text[MAX_BYTES + 1];
126 (void) p;
127 for (int k = 0; k < MAX_BYTES; k++) {
128 char ch = (char) (n % 0x100);
129 if (ch == NULL_CHAR) {
130 ch = BLANK_CHAR;
131 } else if (ch < BLANK_CHAR) {
132 ch = '?';
133 }
134 text[MAX_BYTES - k - 1] = ch;
135 n >>= 8;
136 }
137 text[MAX_BYTES] = NULL_CHAR;
138 return text;
139 }
140
141 //! @brief WAVE format category
142
143 char *format_category (unt n)
144 {
145 switch (n) {
146 case WAVE_FORMAT_UNKNOWN: {
147 return "WAVE_FORMAT_UNKNOWN";
148 }
149 case WAVE_FORMAT_PCM: {
150 return "WAVE_FORMAT_PCM ";
151 }
152 case WAVE_FORMAT_ADPCM: {
153 return "WAVE_FORMAT_ADPCM";
154 }
155 case WAVE_FORMAT_IEEE_FLOAT: {
156 return "WAVE_FORMAT_IEEE_FLOAT";
157 }
158 case WAVE_FORMAT_IBM_FORMAT_CVSD: {
159 return "WAVE_FORMAT_IBM_FORMAT_CVSD";
160 }
161 case WAVE_FORMAT_ALAW: {
162 return "WAVE_FORMAT_ALAW";
163 }
164 case WAVE_FORMAT_MULAW: {
165 return "WAVE_FORMAT_MULAW";
166 }
167 case WAVE_FORMAT_OKI_ADPCM: {
168 return "WAVE_FORMAT_OKI_ADPCM";
169 }
170 case WAVE_FORMAT_DVI_ADPCM: {
171 return "WAVE_FORMAT_DVI_ADPCM";
172 }
173 case WAVE_FORMAT_MEDIASPACE_ADPCM: {
174 return "WAVE_FORMAT_MEDIASPACE_ADPCM";
175 }
176 case WAVE_FORMAT_SIERRA_ADPCM: {
177 return "WAVE_FORMAT_SIERRA_ADPCM";
178 }
179 case WAVE_FORMAT_G723_ADPCM: {
180 return "WAVE_FORMAT_G723_ADPCM";
181 }
182 case WAVE_FORMAT_DIGISTD: {
183 return "WAVE_FORMAT_DIGISTD";
184 }
185 case WAVE_FORMAT_DIGIFIX: {
186 return "WAVE_FORMAT_DIGIFIX";
187 }
188 case WAVE_FORMAT_YAMAHA_ADPCM: {
189 return "WAVE_FORMAT_YAMAHA_ADPCM";
190 }
191 case WAVE_FORMAT_SONARC: {
192 return "WAVE_FORMAT_SONARC";
193 }
194 case WAVE_FORMAT_DSPGROUP_TRUESPEECH: {
195 return "WAVE_FORMAT_DSPGROUP_TRUESPEECH";
196 }
197 case WAVE_FORMAT_ECHOSCI1: {
198 return "WAVE_FORMAT_ECHOSCI1";
199 }
200 case WAVE_FORMAT_AUDIOFILE_AF36: {
201 return "WAVE_FORMAT_AUDIOFILE_AF36";
202 }
203 case WAVE_FORMAT_APTX: {
204 return "WAVE_FORMAT_APTX";
205 }
206 case WAVE_FORMAT_AUDIOFILE_AF10: {
207 return "WAVE_FORMAT_AUDIOFILE_AF10";
208 }
209 case WAVE_FORMAT_DOLBY_AC2: {
210 return "WAVE_FORMAT_DOLBY_AC2";
211 }
212 case WAVE_FORMAT_GSM610: {
213 return "WAVE_FORMAT_GSM610 ";
214 }
215 case WAVE_FORMAT_ANTEX_ADPCME: {
216 return "WAVE_FORMAT_ANTEX_ADPCME";
217 }
218 case WAVE_FORMAT_CONTROL_RES_VQLPC: {
219 return "WAVE_FORMAT_CONTROL_RES_VQLPC";
220 }
221 case WAVE_FORMAT_DIGIREAL: {
222 return "WAVE_FORMAT_DIGIREAL";
223 }
224 case WAVE_FORMAT_DIGIADPCM: {
225 return "WAVE_FORMAT_DIGIADPCM";
226 }
227 case WAVE_FORMAT_CONTROL_RES_CR10: {
228 return "WAVE_FORMAT_CONTROL_RES_CR10";
229 }
230 case WAVE_FORMAT_NMS_VBXADPCM: {
231 return "WAVE_FORMAT_NMS_VBXADPCM";
232 }
233 case WAVE_FORMAT_ROCKWELL_ADPCM: {
234 return "WAVE_FORMAT_ROCKWELL_ADPCM";
235 }
236 case WAVE_FORMAT_ROCKWELL_DIGITALK: {
237 return "WAVE_FORMAT_ROCKWELL_DIGITALK";
238 }
239 case WAVE_FORMAT_G721_ADPCM: {
240 return "WAVE_FORMAT_G721_ADPCM";
241 }
242 case WAVE_FORMAT_G728_CELP: {
243 return "WAVE_FORMAT_G728_CELP";
244 }
245 case WAVE_FORMAT_MPEG: {
246 return "WAVE_FORMAT_MPEG";
247 }
248 case WAVE_FORMAT_MPEGLAYER3: {
249 return "WAVE_FORMAT_MPEGLAYER3";
250 }
251 case WAVE_FORMAT_G726_ADPCM: {
252 return "WAVE_FORMAT_G726_ADPCM";
253 }
254 case WAVE_FORMAT_G722_ADPCM: {
255 return "WAVE_FORMAT_G722_ADPCM";
256 }
257 case WAVE_FORMAT_IBM_FORMAT_MULAW: {
258 return "WAVE_FORMAT_IBM_FORMAT_MULAW";
259 }
260 case WAVE_FORMAT_IBM_FORMAT_ALAW: {
261 return "WAVE_FORMAT_IBM_FORMAT_ALAW";
262 }
263 case WAVE_FORMAT_IBM_FORMAT_ADPCM: {
264 return "WAVE_FORMAT_IBM_FORMAT_ADPCM";
265 }
266 case WAVE_FORMAT_CREATIVE_ADPCM: {
267 return "WAVE_FORMAT_CREATIVE_ADPCM";
268 }
269 case WAVE_FORMAT_FM_TOWNS_SND: {
270 return "WAVE_FORMAT_FM_TOWNS_SND";
271 }
272 case WAVE_FORMAT_OLIGSM: {
273 return "WAVE_FORMAT_OLIGSM";
274 }
275 case WAVE_FORMAT_OLIADPCM: {
276 return "WAVE_FORMAT_OLIADPCM";
277 }
278 case WAVE_FORMAT_OLICELP: {
279 return "WAVE_FORMAT_OLICELP";
280 }
281 case WAVE_FORMAT_OLISBC: {
282 return "WAVE_FORMAT_OLISBC";
283 }
284 case WAVE_FORMAT_OLIOPR: {
285 return "WAVE_FORMAT_OLIOPR";
286 }
287 case WAVE_FORMAT_EXTENSIBLE: {
288 return "WAVE_FORMAT_EXTENSIBLE";
289 }
290 default: {
291 return "other";
292 }
293 }
294 }
295
296 //! @brief Read RIFF item.
297
298 unt read_riff_item (NODE_T * p, FILE_T fd, int n, BOOL_T little)
299 {
300 unt v, z;
301 int k, m, r;
302 if (n > MAX_BYTES) {
303 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "too long word length");
304 exit_genie (p, A68_RUNTIME_ERROR);
305 }
306 if (little) {
307 for (k = 0, m = 0, v = 0; k < n; k++, m++) {
308 z = 0;
309 errno = 0;
310 r = (int) io_read (fd, &z, (size_t) 1);
311 if (r != 1 || errno != 0) {
312 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "error while reading file");
313 exit_genie (p, A68_RUNTIME_ERROR);
314 }
315 v += z * pow256[m];
316 }
317 } else {
318 for (k = 0, m = n - 1, v = 0; k < n; k++, m--) {
319 z = 0;
320 errno = 0;
321 r = (int) io_read (fd, &z, (size_t) 1);
322 if (r != 1 || errno != 0) {
323 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "error while reading file");
324 exit_genie (p, A68_RUNTIME_ERROR);
325 }
326 v += z * pow256[m];
327 }
328 }
329 return v;
330 }
331
332 //! @brief Read sound from file.
333
334 void read_sound (NODE_T * p, A68_REF ref_file, A68_SOUND * w)
335 {
336 A68_FILE *f = FILE_DEREF (&ref_file);
337 int r;
338 unt fmt_cat;
339 unt blockalign, byterate, chunksize, subchunk2size, z;
340 BOOL_T data_read = A68_FALSE;
341 if (read_riff_item (p, FD (f), 4, A68_BIG_ENDIAN) != code_string (p, "RIFF", 4)) {
342 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "file format is not RIFF");
343 exit_genie (p, A68_RUNTIME_ERROR);
344 }
345 chunksize = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
346 if ((z = read_riff_item (p, FD (f), 4, A68_BIG_ENDIAN)) != code_string (p, "WAVE", 4)) {
347 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL_STRING, M_SOUND, "file format is not \"WAVE\" but", code_unt (p, z));
348 exit_genie (p, A68_RUNTIME_ERROR);
349 }
350 // Now read chunks.
351 while (data_read == A68_FALSE) {
352 z = read_riff_item (p, FD (f), 4, A68_BIG_ENDIAN);
353 if (z == code_string (p, "fmt ", 4)) {
354 // Read fmt chunk.
355 z = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
356 int skip = (int) z - 0x10; // Bytes to skip in extended wave format
357 fmt_cat = read_riff_item (p, FD (f), 2, A68_LITTLE_ENDIAN);
358 if (fmt_cat != WAVE_FORMAT_PCM) {
359 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL_STRING, M_SOUND, "category is not WAVE_FORMAT_PCM but", format_category (fmt_cat));
360 exit_genie (p, A68_RUNTIME_ERROR);
361 }
362 NUM_CHANNELS (w) = read_riff_item (p, FD (f), 2, A68_LITTLE_ENDIAN);
363 SAMPLE_RATE (w) = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
364 byterate = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
365 blockalign = read_riff_item (p, FD (f), 2, A68_LITTLE_ENDIAN);
366 BITS_PER_SAMPLE (w) = read_riff_item (p, FD (f), 2, A68_LITTLE_ENDIAN);
367 test_bits_per_sample (p, BITS_PER_SAMPLE (w));
368 for (int k = 0; k < skip; k++) {
369 z = read_riff_item (p, FD (f), 1, A68_LITTLE_ENDIAN);
370 }
371 } else if (z == code_string (p, "LIST", 4)) {
372 // Skip a LIST chunk.
373 z = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
374 int skip = (int) z;
375 for (int k = 0; k < skip; k++) {
376 z = read_riff_item (p, FD (f), 1, A68_LITTLE_ENDIAN);
377 }
378 } else if (z == code_string (p, "cue ", 4)) {
379 // Skip a cue chunk.
380 z = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
381 int skip = (int) z;
382 for (int k = 0; k < skip; k++) {
383 z = read_riff_item (p, FD (f), 1, A68_LITTLE_ENDIAN);
384 }
385 } else if (z == code_string (p, "fact", 4)) {
386 // Skip a fact chunk.
387 z = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
388 int skip = (int) z;
389 for (int k = 0; k < skip; k++) {
390 z = read_riff_item (p, FD (f), 1, A68_LITTLE_ENDIAN);
391 }
392 } else if (z == code_string (p, "data", 4)) {
393 // Read data chunk.
394 subchunk2size = read_riff_item (p, FD (f), 4, A68_LITTLE_ENDIAN);
395 NUM_SAMPLES (w) = subchunk2size / NUM_CHANNELS (w) / (unt) A68_SOUND_BYTES (w);
396 DATA (w) = heap_generator (p, M_SOUND_DATA, (int) subchunk2size);
397 r = (int) io_read (FD (f), ADDRESS (&(DATA (w))), subchunk2size);
398 if (r != (int) subchunk2size) {
399 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "cannot read all of the data");
400 exit_genie (p, A68_RUNTIME_ERROR);
401 }
402 data_read = A68_TRUE;
403 } else {
404 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL_STRING, M_SOUND, "chunk is", code_unt (p, z));
405 exit_genie (p, A68_RUNTIME_ERROR);
406 }
407 }
408 (void) z;
409 (void) blockalign;
410 (void) byterate;
411 (void) chunksize;
412 (void) subchunk2size;
413 STATUS (w) = INIT_MASK;
414 }
415
416 //! @brief Write RIFF item.
417
418 void write_riff_item (NODE_T * p, FILE_T fd, unt z, int n, BOOL_T little)
419 {
420 unt char y[MAX_BYTES];
421 if (n > MAX_BYTES) {
422 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "too long word length");
423 exit_genie (p, A68_RUNTIME_ERROR);
424 }
425 for (int k = 0; k < n; k++) {
426 y[k] = (unt char) (z & 0xff);
427 z >>= 8;
428 }
429 if (little) {
430 for (int k = 0; k < n; k++) {
431 ASSERT (io_write (fd, &(y[k]), 1) != -1);
432 }
433 } else {
434 for (int k = n - 1; k >= 0; k--) {
435 int r = (int) io_write (fd, &(y[k]), 1);
436 if (r != 1) {
437 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "error while writing file");
438 exit_genie (p, A68_RUNTIME_ERROR);
439 }
440 }
441 }
442 }
443
444 //! @brief Write sound to file.
445
446 void write_sound (NODE_T * p, A68_REF ref_file, A68_SOUND * w)
447 {
448 A68_FILE *f = FILE_DEREF (&ref_file);
449 int r;
450 unt blockalign = NUM_CHANNELS (w) * (unt) (A68_SOUND_BYTES (w));
451 unt byterate = SAMPLE_RATE (w) * blockalign;
452 unt subchunk2size = NUM_SAMPLES (w) * blockalign;
453 unt chunksize = 4 + (8 + 16) + (8 + subchunk2size);
454 write_riff_item (p, FD (f), code_string (p, "RIFF", 4), 4, A68_BIG_ENDIAN);
455 write_riff_item (p, FD (f), chunksize, 4, A68_LITTLE_ENDIAN);
456 write_riff_item (p, FD (f), code_string (p, "WAVE", 4), 4, A68_BIG_ENDIAN);
457 write_riff_item (p, FD (f), code_string (p, "fmt ", 4), 4, A68_BIG_ENDIAN);
458 write_riff_item (p, FD (f), 16, 4, A68_LITTLE_ENDIAN);
459 write_riff_item (p, FD (f), 1, 2, A68_LITTLE_ENDIAN);
460 write_riff_item (p, FD (f), NUM_CHANNELS (w), 2, A68_LITTLE_ENDIAN);
461 write_riff_item (p, FD (f), SAMPLE_RATE (w), 4, A68_LITTLE_ENDIAN);
462 write_riff_item (p, FD (f), byterate, 4, A68_LITTLE_ENDIAN);
463 write_riff_item (p, FD (f), blockalign, 2, A68_LITTLE_ENDIAN);
464 write_riff_item (p, FD (f), BITS_PER_SAMPLE (w), 2, A68_LITTLE_ENDIAN);
465 write_riff_item (p, FD (f), code_string (p, "data", 4), 4, A68_BIG_ENDIAN);
466 write_riff_item (p, FD (f), subchunk2size, 4, A68_LITTLE_ENDIAN);
467 if (IS_NIL (DATA (w))) {
468 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "sound has no data");
469 exit_genie (p, A68_RUNTIME_ERROR);
470 }
471 r = (int) io_write (FD (f), ADDRESS (&(DATA (w))), subchunk2size);
472 if (r != (int) subchunk2size) {
473 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "error while writing file");
474 exit_genie (p, A68_RUNTIME_ERROR);
475 }
476 }
477
478 //! @brief PROC new sound = (INT bits, INT sample rate, INT channels, INT samples) SOUND
479
480 void genie_new_sound (NODE_T * p)
481 {
482 A68_INT num_channels, sample_rate, bits_per_sample, num_samples;
483 A68_SOUND w;
484 POP_OBJECT (p, &num_samples, A68_INT);
485 POP_OBJECT (p, &num_channels, A68_INT);
486 POP_OBJECT (p, &sample_rate, A68_INT);
487 POP_OBJECT (p, &bits_per_sample, A68_INT);
488 NUM_SAMPLES (&w) = (unt) (VALUE (&num_samples));
489 NUM_CHANNELS (&w) = (unt) (VALUE (&num_channels));
490 SAMPLE_RATE (&w) = (unt) (VALUE (&sample_rate));
491 BITS_PER_SAMPLE (&w) = (unt) (VALUE (&bits_per_sample));
492 test_bits_per_sample (p, BITS_PER_SAMPLE (&w));
493 DATA_SIZE (&w) = (unt) A68_SOUND_DATA_SIZE (&w);
494 DATA (&w) = heap_generator (p, M_SOUND_DATA, (int) DATA_SIZE (&w) * sizeof (unt));
495 STATUS (&w) = INIT_MASK;
496 PUSH_OBJECT (p, w, A68_SOUND);
497 }
498
499 //! @brief PROC get sound = (SOUND w, INT channel, sample) INT
500
501 void genie_get_sound (NODE_T * p)
502 {
503 A68_INT channel, sample;
504 A68_SOUND w;
505 int addr, k, n, z, m;
506 BYTE_T *d;
507 POP_OBJECT (p, &sample, A68_INT);
508 POP_OBJECT (p, &channel, A68_INT);
509 POP_OBJECT (p, &w, A68_SOUND);
510 if (!(VALUE (&channel) >= 1 && VALUE (&channel) <= (int) NUM_CHANNELS (&w))) {
511 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "channel index out of range");
512 exit_genie (p, A68_RUNTIME_ERROR);
513 }
514 if (!(VALUE (&sample) >= 1 && VALUE (&sample) <= (int) NUM_SAMPLES (&w))) {
515 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "sample index out of range");
516 exit_genie (p, A68_RUNTIME_ERROR);
517 }
518 if (IS_NIL (DATA (&w))) {
519 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "sound has no data");
520 exit_genie (p, A68_RUNTIME_ERROR);
521 }
522 n = A68_SOUND_BYTES (&w);
523 addr = ((VALUE (&sample) - 1) * (int) (NUM_CHANNELS (&w)) + (VALUE (&channel) - 1)) * n;
524 ABEND (addr < 0 || addr >= (int) DATA_SIZE (&w), ERROR_INTERNAL_CONSISTENCY, __func__);
525 d = &(ADDRESS (&(DATA (&w)))[addr]);
526 // Convert from little-endian, irrespective from the platform we work on.
527 for (k = 0, z = 0, m = 0; k < n; k++) {
528 z += ((int) (d[k]) * (int) (pow256[k]));
529 m = k;
530 }
531 PUSH_VALUE (p, (d[m] & 0x80 ? (n == 4 ? z : z - (int) pow256[m + 1]) : z), A68_INT);
532 }
533
534 //! @brief PROC set sound = (SOUND w, INT channel, sample, value) VOID
535
536 void genie_set_sound (NODE_T * p)
537 {
538 A68_INT channel, sample, value;
539 int addr, k, n, z;
540 BYTE_T *d;
541 A68_SOUND w;
542 POP_OBJECT (p, &value, A68_INT);
543 POP_OBJECT (p, &sample, A68_INT);
544 POP_OBJECT (p, &channel, A68_INT);
545 POP_OBJECT (p, &w, A68_SOUND);
546 if (!(VALUE (&channel) >= 1 && VALUE (&channel) <= (int) NUM_CHANNELS (&w))) {
547 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "channel index out of range");
548 exit_genie (p, A68_RUNTIME_ERROR);
549 }
550 if (!(VALUE (&sample) >= 1 && VALUE (&sample) <= (int) NUM_SAMPLES (&w))) {
551 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "sample index out of range");
552 exit_genie (p, A68_RUNTIME_ERROR);
553 }
554 if (IS_NIL (DATA (&w))) {
555 diagnostic (A68_RUNTIME_ERROR, p, ERROR_SOUND_INTERNAL, M_SOUND, "sound has no data");
556 exit_genie (p, A68_RUNTIME_ERROR);
557 }
558 n = A68_SOUND_BYTES (&w);
559 addr = ((VALUE (&sample) - 1) * (int) (NUM_CHANNELS (&w)) + (VALUE (&channel) - 1)) * n;
560 ABEND (addr < 0 || addr >= (int) DATA_SIZE (&w), ERROR_INTERNAL_CONSISTENCY, __func__);
561 d = &(ADDRESS (&(DATA (&w)))[addr]);
562 // Convert to little-endian.
563 for (k = 0, z = VALUE (&value); k < n; k++) {
564 d[k] = (BYTE_T) (z & 0xff);
565 z >>= 8;
566 }
567 }
568
569 //! @brief OP SOUND = (SOUND) INT
570
571 void genie_sound_samples (NODE_T * p)
572 {
573 A68_SOUND w;
574 POP_OBJECT (p, &w, A68_SOUND);
575 PUSH_VALUE (p, (int) (NUM_SAMPLES (&w)), A68_INT);
576 }
577
578 //! @brief OP RATE = (SOUND) INT
579
580 void genie_sound_rate (NODE_T * p)
581 {
582 A68_SOUND w;
583 POP_OBJECT (p, &w, A68_SOUND);
584 PUSH_VALUE (p, (int) (SAMPLE_RATE (&w)), A68_INT);
585 }
586
587 //! @brief OP CHANNELS = (SOUND) INT
588
589 void genie_sound_channels (NODE_T * p)
590 {
591 A68_SOUND w;
592 POP_OBJECT (p, &w, A68_SOUND);
593 PUSH_VALUE (p, (int) (NUM_CHANNELS (&w)), A68_INT);
594 }
595
596 //! @brief OP RESOLUTION = (SOUND) INT
597
598 void genie_sound_resolution (NODE_T * p)
599 {
600 A68_SOUND w;
601 POP_OBJECT (p, &w, A68_SOUND);
602 PUSH_VALUE (p, (int) (BITS_PER_SAMPLE (&w)), A68_INT);
603 }
© 2002-2024 J.M. van der Veer (jmvdveer@xs4all.nl)
|