1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
|
/* sound.c -- nyquist sound data type */
/* CHANGE LOG
* --------------------------------------------------------------------
* 28Apr03 dm changes for portability and fix compiler warnings
*/
/* define size_t: */
#ifdef UNIX
#include "sys/types.h"
#endif
#include <stdio.h>
#include "xlisp.h"
#include "sound.h"
#include "falloc.h"
#include "samples.h"
#include "extern.h"
#include "debug.h"
#include "assert.h"
#ifdef OSC
#include "nyq-osc-server.h"
#endif
#include "cext.h"
#include "userio.h"
/* #define GC_DEBUG */
#ifdef GC_DEBUG
extern sound_type sound_to_watch;
#endif
snd_list_type list_watch; //DBY
/* #define SNAPSHOTS */
long table_memory;
sample_block_type zero_block;
sample_block_type internal_zero_block;
snd_list_type zero_snd_list;
xtype_desc sound_desc;
LVAL a_sound;
LVAL s_audio_markers;
static void sound_xlfree();
static void sound_xlprint();
static void sound_xlsave();
static unsigned char *sound_xlrestore();
void sound_print_array(LVAL sa, long n);
void sound_print_sound(sound_type s, long n);
void sample_block_unref(sample_block_type sam);
#ifdef SNAPSHOTS
boolean sound_created_flag = false;
#endif
#ifdef OSC
int nosc_enabled = false;
#endif
double sound_latency = 0.3; /* default value */
/* these are used so get times for *AUDIO-MARKERS* */
double sound_srate = 44100.0;
long sound_frames = 0;
double snd_set_latency(double latency)
{
double r = sound_latency;
sound_latency = latency;
return r;
}
/* xlbadsr - report a "bad combination of sample rates" error */
LVAL snd_badsr(void)
{
xlfail("bad combination of sample rates");
return NIL; /* never happens */
}
/* compute-phase - given a phase in radians, a wavetable specified as
* the nominal pitch (in half steps), the table length, and the sample
* rate, compute the sample number corresponding to the phase. This
* routine makes it easy to initialize the table pointer at the beginning
* of various oscillator implementations in Nyquist. Note that the table
* may represent several periods, in which case phase 360 is not the same
* as 0. Also note that the phase increment is also computed and returned
* through incr_ptr.
*/
double compute_phase(phase, key, n, srate, new_srate, freq, incr_ptr)
double phase; /* phase in degrees (depends on ANGLEBASE) */
double key; /* the semitone number of the table played at srate */
long n; /* number of samples */
double srate; /* the sample rate of the table */
double new_srate; /* sample rate of the result */
double freq; /* the desired frequency */
double *incr_ptr; /* the sample increment */
{
double period = 1.0 / step_to_hz(key);
/* convert phase to sample units */
phase = srate * period * (phase / (double) ANGLEBASE);
/* phase is now in sample units; if phase is less than zero, then increase
it by some number of sLength's to make it positive:
*/
if (phase < 0)
phase += (((int) ((-phase) / n)) + 1) * n;
/* if phase is longer than the sample length, wrap it by subtracting the
integer part of the division by sLength:
*/
if (phase > n)
phase -= ((int) (phase / n)) * n;
/* Now figure the phase increment: to reproduce original pitch
required incr = srate / new_srate. To get the new frequency,
scale by freq / nominal_freq = freq * period:
*/
*incr_ptr = (srate / new_srate) * freq * period;
return phase;
}
#ifndef GCBUG
snd_list_type gcbug_snd_list = 0;
long blocks_to_watch_len = 0;
sample_block_type blocks_to_watch[blocks_to_watch_max];
void block_watch(long sample_block)
{
if (blocks_to_watch_len >= blocks_to_watch_max) {
stdputstr("block_watch - no more space to save pointers\n");
return;
}
blocks_to_watch[blocks_to_watch_len++] = (sample_block_type) sample_block;
nyquist_printf("block_watch - added %d = %x\n",
(int)sample_block, (int)sample_block);
}
/* fetch_zeros -- the fetch function for appended zeros */
/*
* zeros are appended when the logical stop time exceeds the
* (physical) terminate time. This fetch function is installed
* by snd_list_terminate(). When appending zeros, we just return
* a pointer to the internal_zero_block and increment current until
* it reaches log_stop_cnt. Then we call snd_list_terminate() to
* finish off the sound list.
*/
void fetch_zeros(snd_susp_type susp, snd_list_type snd_list)
{
int len = MIN(susp->log_stop_cnt - susp->current,
max_sample_block_len);
/* nyquist_printf("fetch_zeros, lsc %d current %d len %d\n",
susp->log_stop_cnt, susp->current, len); */
if (len < 0) {
char error[80];
sprintf(error, "fetch_zeros susp %p (%s) len %d", susp, susp->name, len);
xlabort(error);
}
if (len == 0) { /* we've reached the logical stop time */
/* nyquist_printf("fetch_zeros: reached the logical stop in %s cnt %d\n",
susp->name, susp->log_stop_cnt); */
snd_list_terminate(snd_list);
} else {
snd_list->block_len = len;
susp->current += len;
}
}
/* sound_nth_block - fetch the address of the nth sample block of a sound */
/*
* NOTE: intended to be called from lisp. Lisp can then call block_watch
* to keep an eye on the block.
*/
long sound_nth_block(sound_type snd, long n)
{
long i;
snd_list_type snd_list = snd->list;
for (i = 0; i < n; i++) {
if (i == 1) {
gcbug_snd_list = snd_list;
nyquist_printf("gcbug_snd_list = 0x%p\n", gcbug_snd_list);
}
if (!snd_list->block) return 0;
snd_list = snd_list->u.next;
}
if (snd_list->block) return (long) snd_list->block;
else return 0;
}
#endif
/****************************************************************************
* snd_list_create
* Inputs:
* snd_susp_type susp: A reference to the suspension
* Result: snd_list_type
* A newly-created sound list type
* Effect:
* Allocates and initializes a snd_list node:
* block refcnt block_len susp logically_stopped
* +--------+--------+-------+-------+---+
* |////////| 1 | 0 | susp | F |
* +--------+--------+-------+-------+---+
****************************************************************************/
/* snd_list_create -- alloc and initialize a snd_list node */
/**/
snd_list_type snd_list_create(snd_susp_type susp)
{
snd_list_type snd_list;
falloc_snd_list(snd_list, "snd_list_create");
snd_list->block = NULL; /* no block of samples */
snd_list->u.susp = susp; /* point to suspension */
snd_list->refcnt = 1; /* one ref */
snd_list->block_len = 0; /* no samples */
snd_list->logically_stopped = false;/* not stopped */
/* nyquist_printf("snd_list_create => %p\n", snd_list);*/
return snd_list;
}
/****************************************************************************
* sound_create
* Inputs:
* snd_susp_type susp: The suspension block to be used for this sound
* time_type t0: The initial time for this sound
* rate_type sr: The sampling rate for this sound
* sample_type scale: The scaling factor for this sound
* sample_block_type (*proc)(...): The get_next_sound method
* Result: sound_type
*
* Effect:
* Creates and initializes a sound type
* Notes:
* The MSDOS conditional is actually a test for ANSI headers; the
* presence of float parameters means that an ANSI prototype and
* a non-ANSI header are incompatible. Better solution would be
* to ANSIfy source.
****************************************************************************/
sound_type last_sound = NULL;
sound_type sound_create(
snd_susp_type susp,
time_type t0,
rate_type sr,
promoted_sample_type scale)
{
sound_type sound;
falloc_sound(sound, "sound_create");
if (((long) sound) & 3) errputstr("sound not word aligned\n");
last_sound = sound; /* debug */
if (t0 < 0) xlerror("attempt to create a sound with negative starting time", s_unbound);
/* nyquist_printf("sound_create %p gets %g\n", sound, t0); */
sound->t0 = sound->true_t0 = sound->time = t0;
sound->stop = MAX_STOP;
sound->sr = sr;
sound->current = 0;
sound->scale = (float) scale;
sound->list = snd_list_create(susp);
sound->get_next = SND_get_first;
sound->logical_stop_cnt = UNKNOWN;
sound->table = NULL;
sound->extra = NULL;
/* nyquist_printf("sound_create susp %p snd_list %p\n", susp, sound->list);
nyquist_printf("sound_create'd %p\n", sound); */
#ifdef SNAPSHOTS
sound_created_flag = true;
#endif
#ifdef GC_DEBUG
if (sound == sound_to_watch) {
nyquist_printf("Created watched sound\n");
watch_snd_list(sound->list);
}
#endif
return sound;
}
/* sound_prepend_zeros -- modify sound_type so that it starts at t0 */
/*
* assumes t0 is earlier than snd->t0, so the sound should return zeros
* until snd->t0 is reached, after which we revert to normal computation.
* When we return, the new snd->t0 will be t0, meaning that the first
* sample returned will be at time t0.
* NOTE: t0 may not be an exact multiple of samples earlier than snd->t0,
* but Nyquist allows any sound to be shifted by +/- 0.5 samples in
* order to achieve alignment. Since sound_prepend_zeros can be called
* many times on the same sound_type, there is a chance that rounding
* errors could accumulate. My first solution was to return with
* snd->t0 computed exactly and not reflecting any fractional sample
* shift of the signal, but this caused problems for the caller: a
* fractional sample shift at a low sample rate could correspond to
* many client samples,fooling the client into thinking that some
* initial samples should be discarded (or else requiring the client
* to be pretty smart). The solution used here is to return to the
* client with snd->t0 exactly equal to t0, but to save snd->true_t0
* equal to the time of the first sample with no sound shifting. This
* time is used for any future sound_prepend_zeros operations so that
* any accumulated rounding errors are due only to floating point
* precision and not to accumulated fractional sample shifts of snd.
*/
void sound_prepend_zeros(sound_type snd, time_type t0)
{
long n;
/* first, see if we're already prepending some zeros */
if (snd->get_next != SND_get_zeros) {
/* nyquist_printf("sound_prepend_zeros 1: snd->t0 %g t0 %g\n", snd->t0, t0); */
/* if not, then initialize some fields that support prepending */
snd->prepend_cnt = 0;
snd->true_t0 = snd->t0;
/* save old get_next and plug in special get_next function */
snd->after_prepend = snd->get_next;
snd->get_next = SND_get_zeros;
}
n = (long) (((snd->true_t0 - t0) * snd->sr) + 0.5); /* how many samples to prepend */
/* add to prepend_cnt so first sample will correspond to new t0 */
snd->prepend_cnt += n;
/* compute the true t0 which corresponds to the time of first sample */
snd->true_t0 -= (n / snd->sr);
/* make caller happy by claiming the sound now starts at exactly t0;
* this is always true within 0.5 samples as allowed by Nyquist. */
snd->t0 = t0;
/* nyquist_printf("sound_prepend_zeros: snd %p true_t0 %g sr %g n %d\n",
snd, snd->true_t0, snd->sr, n);*/
}
/* sound_array_copy -- copy an array of sounds */
/*
* NOTE: be sure to protect the result from gc!
*/
LVAL sound_array_copy(LVAL sa)
{
long i = getsize(sa);
LVAL new_sa = newvector(i);
xlprot1(new_sa);
while (i > 0) {
i--;
setelement(new_sa, i,
cvsound(sound_copy(getsound(getelement(sa, i)))));
}
xlpop();
return new_sa;
}
/* sound_copy - copy a sound structure, do reference counts */
/**/
sound_type sound_copy(sound_type snd)
{
sound_type sndcopy;
falloc_sound(sndcopy, "sound_copy");
*sndcopy = *snd; /* copy the whole structure */
sndcopy->extra = NULL; /* except for the (private) extra data */
snd_list_ref(snd->list); /* copied a reference so fix the count */
/* nyquist_printf("sound_copy'd %p to %p\n", snd, sndcopy); */
if (snd->table) snd->table->refcount++;
#ifdef GC_DEBUG
if (sndcopy == sound_to_watch)
printf("sndcopy->table %x\n", sndcopy->table);
#endif
return sndcopy;
}
/* convert a sound to a wavetable, set length */
/**/
table_type sound_to_table(sound_type s)
{
long len = snd_length(s, max_table_len);
long tx = 0; /* table index */
long blocklen;
register double scale_factor = s->scale;
sound_type original_s = s;
table_type table; /* the new table */
long table_bytes; /* how big is the table */
if (s->table) {
s->table->refcount++;
return s->table;
}
if (len >= max_table_len) {
char emsg[100];
sprintf(emsg, "maximum table size (%d) exceeded", max_table_len);
xlcerror("use truncated sound for table", emsg, NIL);
} else if (len == 0) {
xlabort("table size must be greater than 0");
}
len++; /* allocate extra sample at end of table */
s = sound_copy(s);
/* nyquist_printf("sound_to_table: allocating table of size %d\n", len); */
table_bytes = table_size_in_bytes(len);
table = (table_type) malloc(table_bytes);
if (!table) xlfail("osc_init couldn't allocate memory for table");
table_memory += table_bytes;
table->length = (double) (len - 1);
while (len > 1) {
sample_block_type sampblock = sound_get_next(s, &blocklen);
long togo = MIN(blocklen, len);
long i;
sample_block_values_type sbufp = sampblock->samples;
/* nyquist_printf("in sound_to_table, sampblock = %d\n", sampblock);*/
for (i = 0; i < togo; i++) {
table->samples[tx++] = (float) (*sbufp++ * scale_factor);
}
len -= togo;
}
/* for interpolation, duplicate first sample at end of table */
table->samples[tx] = table->samples[0];
table->refcount = 2; /* one for the user, one from original_s */
sound_unref(s);
s = NULL;
original_s->table = table;
return table;
}
void table_free(table_type table)
{
long len = (long) (table->length) + 1;
long bytes = table_size_in_bytes(len);
free(table);
table_memory -= bytes;
}
void table_unref(table_type table)
{
if (!table) return;
table->refcount--;
if (table->refcount <= 0) {
/* nyquist_printf("table refcount went to zero\n"); */
table_free(table);
}
}
void sound_unref(sound_type snd)
/* note that sounds do not have ref counts, so sound_unref
* always frees the sound object
*/
{
if (!snd) return;
snd_list_unref(snd->list);
table_unref(snd->table);
/* nyquist_printf("\t\t\t\t\tfreeing sound@%p\n", snd);*/
if (snd->extra) free(snd->extra);
ffree_sound(snd, "sound_unref");
}
void snd_list_ref(snd_list_type list)
{
list->refcnt++;
}
void snd_list_terminate(snd_list)
snd_list_type snd_list;
{
snd_susp_type susp = snd_list->u.next->u.susp;
long lsc = susp->log_stop_cnt;
long current = susp->current;
/* unreference the empty sample block that was allocated: */
sample_block_unref(snd_list->block);
/* use zero_block instead */
snd_list->block = zero_block;
/* either fetch more zeros or terminate now */
if (lsc != UNKNOWN && lsc > current) {
/* nyquist_printf("snd_list_terminate: lsc %d current %d\n",
lsc, current); */
susp->fetch = fetch_zeros;
fetch_zeros(susp, snd_list);
} else {
snd_list->block_len = max_sample_block_len;
snd_list->logically_stopped = true;
snd_list_unref(snd_list->u.next);
snd_list->u.next = zero_snd_list; /* be zero forever */
}
}
void snd_list_unref(snd_list_type list)
{
void (*freefunc)();
if (list == NULL || list == zero_snd_list) {
if (list == NULL)
nyquist_printf("why did snd_list_unref get %p?\n", list);
return;
}
list->refcnt--;
/* nyquist_printf("snd_list_unref "); print_snd_list_type(list); stdputstr("\n"); */
if (list->refcnt == 0) {
if (list->block && list->block != zero_block) {
/* there is a next snd_list */
/* stdputstr("["); */
sample_block_unref(list->block);
/* stdputstr("]"); */
snd_list_unref(list->u.next);
}
else if (list->block == NULL) { /* the next thing is the susp */
/* free suspension structure */
/* nyquist_printf("freeing susp@%p\n", list->u.susp); */
freefunc = list->u.susp->free;
(*freefunc)(list->u.susp);
}
/* nyquist_printf("freeing snd_list@%p\n", list); */
//DBY
if (list == list_watch) printf("freeing watched snd_list %p\n", list);
//DBY
ffree_snd_list(list, "snd_list_unref");
}
}
void sample_block_ref(sample_block_type sam)
{
sam->refcnt++;
}
void sample_block_test(sample_block_type sam, char *s)
{
/* see if this block is being watched */
int i;
for (i = 0; i < blocks_to_watch_len; i++) {
if ((sam > (blocks_to_watch[i] - 1)) &&
(sam < (blocks_to_watch[i] + 1))) {
nyquist_printf(
"WOOPS! %s(0x%p) refers to a block 0x%p on the watch list!\n",
s, sam, blocks_to_watch[i]);
}
}
}
void sample_block_unref(sample_block_type sam)
{
sam->refcnt--;
if (sam->refcnt == 0) {
#ifndef GCBUG
sample_block_test(sam, "sample_block_unref");
#endif
/* nyquist_printf("freeing sample block %p\n", sam); */
ffree_sample_block(sam, "sample_block_unref");
}
}
/****************************************************************************
* interp_style
* Inputs:
* sound_type s: The sound we are using
* rate_type sr: The sampling rate
* Result: int
* A small integer which is one of the symbolic values:
* The values are ordered, smallest to largest, as
* INTERP_n - none
* INTERP_s - scale
* INTERP_i - interpolated
* INTERP_r - ramp
*
* Notes:
* The sampling rate s->sr and scale factor s->scale are compared
* with other values exactly (no fuzz).
****************************************************************************/
int interp_style(sound_type s, rate_type sr)
{
if (s->sr == sr)
{ /* same sample rate */
return ((s->scale == 1.0) ? INTERP_n : INTERP_s);
} /* same sample rate */
else
if (s->sr * 10.0 > sr)
{ /* 10x sample rate */
return INTERP_i;
} /* 10x sample rate */
else
return INTERP_r;
}
/****************************************************************************
* snd_sort_2
* Inputs:
* sound_type * s1_ptr:
* sound_type * s2_ptr:
* rate_type sr:
* Result: void
*
* Effect:
* If the interp_style of s1 dominates the interp_style of s2,
* the sound_types input are interchanged.
****************************************************************************/
/* snd_sort_2 -- sort 2 arguments by interpolation method */
void snd_sort_2(sound_type *s1_ptr, sound_type *s2_ptr, rate_type sr)
{
if (interp_style(*s1_ptr, sr) > interp_style(*s2_ptr, sr)) {
sound_type s = *s1_ptr;
*s1_ptr = *s2_ptr;
*s2_ptr = s;
}
}
/* snd_sref -- access a sound at a given time point */
/**/
double snd_sref(sound_type s, time_type t)
{
double exact_cnt; /* how many fractional samples to scan */
int cnt; /* how many samples to flush */
sample_block_type sampblock = NULL;
long blocklen;
sample_type x1, x2; /* interpolate between these samples */
/* changed true_t0 to just t0 based on comment that true_t0 is only
* for use by snd_prepend_zeros -RBD
*/
exact_cnt = (t - s->t0) * s->sr;
if (exact_cnt < 0.0) return 0.0;
s = sound_copy(s); /* don't modify s, create new reader */
cnt = (long) exact_cnt; /* rounds down */
exact_cnt -= cnt; /* remember fractional remainder */
/* now flush cnt samples */
while (cnt >= 0) {
sampblock = sound_get_next(s, &blocklen);
cnt -= blocklen;
if (sampblock == zero_block) {
sound_unref(s);
return 0.0;
}
}
/* -blocklen <= cnt <= -1 */
/* get next 2 samples and interpolate */
x1 = sampblock->samples[blocklen + cnt];
if (cnt == -1) {
sampblock = sound_get_next(s, &blocklen);
cnt -= blocklen;
}
x2 = sampblock->samples[blocklen + cnt + 1];
sound_unref(s); /* free the reader */
return (x1 + exact_cnt * (x2 - x1)) * s->scale;
}
/* snd_sref_inverse -- find time point corresponding to some value */
/**/
double snd_sref_inverse(sound_type s, double val)
{
double exact_cnt; /* how many fractional samples to scan */
int i;
sample_block_type sampblock;
long blocklen;
sample_type x1, x2; /* interpolate between these samples */
if (val < 0) {
xlcerror("return 0", "negative value", cvflonum(val));
return 0.0;
}
s = sound_copy(s); /* don't modify s, create new reader */
x1 = 0.0F;
/* now flush cnt samples */
while (true) {
sampblock = sound_get_next(s, &blocklen);
x2 = sampblock->samples[blocklen - 1];
if (x2 >= val) break;
x1 = x2;
if (sampblock == zero_block) {
xlcerror("return 0", "too large, no inverse", cvflonum(val));
sound_unref(s);
return 0.0;
}
}
/* x1 = last sample of previous block,
sampblock contains a value larger than val
blocklen is the length of sampblock */
/* search for first element exceeding val - could
* use binary search, but maximum block size places
* an upper bound on how bad this can get and we
* search for the right block linearly anyway.
*/
for (i = 0; i < blocklen && sampblock->samples[i] <= val; i++) ;
/* now i is index of element exceeding val */
if (i > 1) x1 = sampblock->samples[i - 1];
x2 = sampblock->samples[i];
/* now interpolate to get fractional part */
if (x2 == x1) exact_cnt = 0;
else exact_cnt = (val - x1) / (x2 - x1);
/* and add the sample count of x1 */
exact_cnt += (s->current - blocklen) + (i - 1);
/* negative counts are possible because the first x1 is at
* sample -1, so force the location to be at least 0
*/
if (exact_cnt < 0) exact_cnt = 0;
/* compute time = t0 + count / samplerate; */
exact_cnt = s->t0 + exact_cnt / s->sr;
sound_unref(s); /* free the reader */
return exact_cnt;
}
time_type snd_stop_time(sound_type s)
{
if (s->stop == MAX_STOP) return MAX_STOP_TIME;
else return s->t0 + (s->stop + 0.5) / s->sr;
}
/* snd_xform -- return a sound with transformations applied */
/*
* The "logical" sound starts at snd->time and runs until some
* as yet unknown termination time. (There is also a possibly
* as yet unknown logical stop time that is irrelevant here.)
* The sound is clipped (zero) until snd->t0 and after snd->stop,
* the latter being a sample count, not a time_type.
* So, the "physical" sound starts at snd->t0 and runs for up to
* snd->stop samples (or less if the sound terminates beforehand).
*
* The snd_xform procedure operates at the "logical" level, shifting
* the sound from its snd->time to time. The sound is stretched as
* a result of setting the sample rate to sr. It is then (further)
* clipped between start_time and stop_time. If initial samples
* are clipped, the sound is shifted again so that it still starts
* at time. The sound is then scaled by scale.
*
* To support clipping of initial samples, the "physical" start time
* t0 is set to when the first unclipped sample will be returned, but
* the number of samples to clip is saved as a negative count. The
* fetch routine SND_flush is installed to flush the clipped samples
* at the time of the first fetch. SND_get_first is then installed
* for future fetches.
*
* An empty (zero) sound will be returned if all samples are clipped.
*
*/
sound_type snd_xform(sound_type snd,
rate_type sr,
time_type time,
time_type start_time,
time_type stop_time,
promoted_sample_type scale)
{
long start_cnt, stop_cnt; /* clipping samples (sample 0 at new t0) */
/* start_cnt should reflect max of where the sound starts (t0)
* and the new start_time.
*/
if (start_time == MIN_START_TIME) {
start_cnt = 0;
} else {
double new_start_cnt = ((start_time - time) * sr) + 0.5;
start_cnt = ((new_start_cnt > 0) ? (long) new_start_cnt : 0);
}
/* if (start_cnt < -(snd->current)) start_cnt = -(snd->current); */
/* stop_cnt should reflect min of the new stop_time and the previous
* snd->stop.
*/
if (stop_time == MAX_STOP_TIME) {
stop_cnt = MAX_STOP;
} else {
double new_stop_cnt = ((stop_time - time) * sr) + 0.5;
if (new_stop_cnt < MAX_STOP) {
stop_cnt = (long) new_stop_cnt;
} else {
errputstr("Warning: stop count overflow in snd_xform\n");
stop_cnt = MAX_STOP;
}
}
if (stop_cnt > snd->stop) {
stop_cnt = snd->stop;
}
if (stop_cnt < 0 || start_cnt >= stop_cnt) {
snd = sound_create(NULL, time, sr, 1.0);
/* sound_create goes ahead and allocates a snd_list node, so
* we need to free it.
* Calling snd_list_unref here seems like the right thing, but
* it assumes too much structure is in place. ffree_snd_list
* is simpler and more direct:
*/
ffree_snd_list(snd->list, "snd_xform");
snd->list = zero_snd_list;
nyquist_printf("snd_xform: (stop_time < t0 or start >= stop) "
"-> zero sound = %p\n", snd);
} else {
snd = sound_copy(snd);
snd->t0 = time;
if (start_cnt) {
snd->current -= start_cnt; /* indicate flush with negative num. */
/* the following code assumes that SND_get_first is the
routine to be called to get the first samples from this
sound. We're going to replace it with SND_flush. First,
make sure that the assumption is correct:
*/
if ((snd->get_next != SND_get_first) &&
(snd->get_next != SND_flush)) {
errputstr("snd_xform: SND_get_first expected\n");
EXIT(1);
}
/* this will flush -current samples and revert to SND_get_first */
snd->get_next = SND_flush;
stop_cnt -= start_cnt;
}
snd->stop = stop_cnt;
snd->sr = sr;
snd->scale *= (float) scale;
}
return snd;
}
/* SND_flush -- the get_next function for flushing clipped samples */
/*
* this only gets called once: it flushes -current samples (a
* non-real-time operation) and installs SND_get_next to return
* blocks normally from then on.
*/
sample_block_type SND_flush(sound_type snd, long * cnt)
{
long mycnt;
sample_block_type block = SND_get_first(snd, &mycnt);
/* changed from < to <= because we want to read at least the first sample */
while (snd->current <= 0) {
block = SND_get_next(snd, &mycnt);
}
/* at this point, we've read to and including the block with
* the first samples we want to return. If the block boundary
* is in the right place, we can do a minimal fixup and return:
*/
if (snd->current == snd->list->block_len) {
*cnt = snd->current; /* == snd->list->block_len */
/* snd->get_next = SND_get_next; -- done by SND_get_first */
return block;
} else /* snd->current < snd->list->block_len */ {
long i;
sample_block_values_type from_ptr;
/* we have to return a partial block */
/* NOTE: if we had been smart, we would have had SND_get_next
* return a pointer to samples rather than a pointer to the
* block, which has a reference count. Since the caller
* expects a pointer to a reference count, we have to copy
* snd->current samples to a new block
*/
snd_list_type snd_list = snd_list_create((snd_susp_type) snd->list->u.next);
snd_list->u.next->refcnt++;
falloc_sample_block(snd_list->block, "SND_flush");
/* now copy samples */
from_ptr = block->samples + snd->list->block_len - snd->current;
for (i = 0; i < snd->current; i++) {
snd_list->block->samples[i] = from_ptr[i];
}
snd_list_unref(snd->list);
snd->list = snd_list;
*cnt = snd->current;
return snd_list->block;
}
}
/* SND_get_zeros -- the get_next function for prepended zeros */
/*
* when prepending zeros, we just return a pointer to the internal_zero_block
* and decrement the prepend_cnt until it goes to zero. Then we revert to
* the normal (original) get_next function.
*
*/
sample_block_type SND_get_zeros(sound_type snd, long * cnt)
{
int len = MIN(snd->prepend_cnt, max_sample_block_len);
/* stdputstr("SND_get_zeros: "); */
if (len < 0) {
char error[80];
sprintf(error, "SND_get_zeros snd %p len %d", snd, len);
xlabort(error);
}
if (len == 0) { /* we've finished prepending zeros */
snd->get_next = snd->after_prepend;
/* stdputstr("done, calling sound_get_next\n"); fflush(stdout); */
return sound_get_next(snd, cnt);
} else {
*cnt = len;
snd->current += len;
snd->prepend_cnt -= len;
/* nyquist_printf("returning internal_zero_block@%p\n", internal_zero_block);
fflush(stdout); */
return internal_zero_block;
}
}
/****************************************************************************
* SND_get_next
* Inputs:
* sound_type snd: The iterator whose next block is to be computed
* int * cnt: Place to put count of samples returned
* Result: snd_list_type
* Pointer to the sample block computed ---------------------------+
* Effect: |
* force suspension to compute next block of samples |
* |
* Here's the protocol for using this and related functions: |
* Every client (sample reader) has a private sound_type (an iterator), |
* and the sound_type's 'list' field points to a header (of type |
* snd_list_type). The header in turn points to a block of samples. |
* |
* +---------------------------------------+
* |
* |
* | sample_block_type
* (snd) V +---+--+--+--+--+--+--+-...-+--+
* sound_type: snd_list_type +-->|ref| | | | |//|//| |//|
* +---------+ +----------+ | +---+--+--+--+--+--+--+-...-+--+
* | list +------->| block +--+ ^
* +---------+ +----------+ :
* | t0 | | block_len|....................:
* +---------+ +----------+
* | sr | | refcnt |
* +---------+ +-+--------+
* | current | | next +---->... Note: the union u
* +---------+ |u|........| snd_list_type points to only one
* | rate | | | susp +---->... of the indicated
* +---------+ +-+--------+ susp_type types
* | scalse | |log_stop |
* +---------+ +----------+
* | lsc |
* +---------+
* |get_next +-----> SND_get_next()
* +---------+
*
* The sound_type keeps track of where the next sample block will
* come from. The field 'current' is the number of the first sample of
* the next block to be returned, where sample numbers start
* at zero. The normal fetch procedure is this one, although special
* cases may generate special block generators, e.g., CONST does not need
* to allocate and refill a block and can reuse the same block over and
* over again, so it may have its own fetch procedure. This is the
* general fetch procedure, which assumes that the generator function
* actually produces a slightly different value for each sample.
*
* The distinguishing characteristic of whether the 'u' field is to be
* interpreted as 'next', a link to the next list element, or 'susp', a
* reference to the suspension for generating a new sample block, is
* whether the 'block' parameter is NULL or not. If it is NULL, then
* u.susp tells how to generate the block; if it is not NULL, u.next is
* a pointer to the next sound block in the list.
*
* When the 'block' pointer is NULL, we create a block of samples, and
* create a new sound list element which follows it which has a NULL
* 'block' pointer; the 'u' field of the current list element is now
* interpreted as 'u.next'.
*
* The client calls SND_get_next to get a pointer to a block of samples.
* The count of samples generated is returned via a ref parameter, and
* SND_get_next will not be called again until this set is exhausted.
*
* The next time SND_get_next is called, it knows that the sample block
* has been exhausted. It releases its reference to the block (and if
* that was the last reference, frees the block to the block allocation
* pool), allocates a new block from the block pool, and proceeds to
* fill it with samples.
*
* Note that as an optimization, if the refcnt field goes to 0 it
* could immediately re-use the block without freeing back to the block
* pool and reallocating it.
*
* Because of the way we handle sound sample blocks, the sound sample blocks
* themselves are ref-counted, so freeing the snd_list_type may not free
* the sample block it references. At the level of this procedure, that
* is transparently handled by the snd_list_unref function.
*
* Logical stop:
*
* Logical stop is handled by several mechanisms. The /intrinsic/ logical
* stop is an immutable property of the signal, and is determined by the
* specification in the algorithm description file. When it is encountered,
* the 'logically_stopped' flag of the snd_list_node is set.
* The generators guarantee that the first time this is encountered, it
* will always be constructed so that the first sample of the block it
* references is the logical stop time.
*
* In addition, the client may have set the /explicit logical stop time/ of
* the iterator (e.g., in nyquist, the (set-logical-stop sound time) call copies
* the sound, altering its logical stop). The logical stop time, when set
* in this way, causes the logical_stop_cnt ('lsc' in the above diagram)
* to be set to the count of the last sample to be generated before the
* <logical stop time. This will guarantee that the sound will indicate that
* it has reached its logical stop time when the indicated sample is
* generated.
****************************************************************************/
void add_s1_s2_nn_fetch(); /* for debugging */
/* SND_get_first -- the standard fn to get a block, after returning
* the first block, plug in SND_get_next for successive blocks
*/
sample_block_type SND_get_first(sound_type snd, long * cnt)
{
register snd_list_type snd_list = snd->list;
/*
* If there is not a block of samples, we need to generate one.
*/
if (snd_list->block == NULL) {
/*
* Call the 'fetch' method for this sound_type to generate
* a new block of samples.
*/
snd_susp_type susp = snd_list->u.susp;
snd_list->u.next = snd_list_create(susp);
snd_list->block = internal_zero_block;
/* nyquist_printf("SND_get_first: susp->fetch %p\n",
susp->fetch); */
assert(susp->log_stop_cnt == UNKNOWN || susp->log_stop_cnt >= 0);
(*(susp->fetch))(susp, snd_list);
#ifdef GC_DEBUG
snd_list_debug(snd_list, "SND_get_first");
#endif
/* nyquist_printf("SND_get_first: snd_list %p, block %p, length %d\n",
snd_list, snd_list->block, snd_list->block_len); */
}
if ((snd->logical_stop_cnt == UNKNOWN) && snd_list->logically_stopped) {
/* nyquist_printf("SND_get_first/next: snd %p logically stopped at %d\n",
snd, snd->current); */
snd->logical_stop_cnt = snd->current;
}
/* see if clipping needs to be applied */
if (snd->current + snd_list->block_len > snd->stop) {
/* need to clip: is clip on a block boundary? */
if (snd->current == snd->stop) {
/* block boundary: replace with zero sound */
snd->list = zero_snd_list;
snd_list_unref(snd_list);
} else {
/* not a block boundary: build new list */
snd->list = snd_list_create((snd_susp_type) zero_snd_list);
snd->list->block_len = (short) (snd->stop - snd->current);
snd->list->block = snd_list->block;
snd->list->block->refcnt++;
snd_list_unref(snd_list);
}
snd_list = snd->list; /* used below to return block ptr */
}
*cnt = snd_list->block_len;
/* this should never happen */
if (*cnt == 0) {
stdputstr("SND_get_first returned 0 samples\n");
#if DEBUG_MEM
dbg_mem_print("snd_list info:", snd_list);
dbg_mem_print("block info:", snd_list->block);
#endif
sound_print_tree(snd);
stdputstr("It is possible that you created a recursive sound\n");
stdputstr("using something like: (SETF X (SEQ (SOUND X) ...))\n");
stdputstr("Nyquist aborts from non-recoverable error\n");
abort();
}
snd->current += snd_list->block_len; /* count how many we read */
snd->get_next = SND_get_next;
return snd_list->block;
}
sample_block_type SND_get_next(sound_type snd, long * cnt)
{
register snd_list_type snd_list = snd->list;
/*
* SND_get_next is installed by SND_get_first, so we know
* when we are called that we are done with the current block
* of samples, so free it now.
*/
snd_list_type cur = snd_list;
snd->list = snd_list = cur->u.next;
snd_list_ref(snd_list);
snd_list_unref(cur); /* release the reference to the current block */
/* now that we've deallocated, we can use SND_get_first to finish the job */
return SND_get_first(snd, cnt);
}
/****************************************************************************
* make_zero_block
* Inputs:
*
* Result:
*
* Effect:
*
****************************************************************************/
sample_block_type make_zero_block(void)
{
sample_block_type zb;
int i;
falloc_sample_block(zb, "make_zero_block");
/* leave room for lots more references before overflow,
but set the count high so that even a large number of
dereferences will not lead to a deallocation */
zb->refcnt = 0x6FFFFFFF;
for (i = 0; i < max_sample_block_len; i++)
{ /* fill with zeros */
zb->samples[i] = 0.0F;
} /* fill with zeros */
return zb;
}
/* min_cnt -- help compute the logical stop or terminate as minimum */
/*
* take the sound (which has just logically stopped or terminated at
* current sample) and
* convert the stop sample into the equivalent sample count as produced by
* susp (which may have a different sample rate). If the count is less than
* the current *cnt_ptr, overwrite cnt_ptr with a new minimum. By calling
* this when each of S1, S2, ... Sn reach their logical stop or termiate
* points, *cnt_ptr will end up with the minimum stop count, which is what
* we want. NOTE: the logical stop time and terminate for signal addition
* should be the MAX of logical stop times of arguments, so this routine
* would not be used.
*/
void min_cnt(long *cnt_ptr, sound_type sound, snd_susp_type susp, long cnt)
{
long c = (long) ((((sound->current - cnt) / sound->sr + sound->t0) - susp->t0) *
susp->sr + 0.5);
/* if *cnt_ptr is uninitialized, just plug in c, otherwise compute min */
if ((*cnt_ptr == UNKNOWN) || (*cnt_ptr > c)) {
/* nyquist_printf("min_cnt %p: new count is %d\n", susp, c);*/
/* if (c == 0) sound_print_tree(printing_this_sound);*/
*cnt_ptr = c;
}
}
/****************************************************************************
* sound_init
* Result: void
*
* Effect:
* Module initialization
* Allocates the 'zero block', the infinitely linked block of
* 0-valued sounds. This is referenced by a list element which
* refers to itself.
****************************************************************************/
void sound_init(void)
{
zero_block = make_zero_block();
internal_zero_block = make_zero_block();
falloc_snd_list(zero_snd_list, "sound_init");
zero_snd_list->block = zero_block;
zero_snd_list->u.next = zero_snd_list;
zero_snd_list->refcnt = 2;
zero_snd_list->block_len = max_sample_block_len;
zero_snd_list->logically_stopped = true;
#ifdef GC_DEBUG
{ long s;
stdputstr("sound_to_watch: ");
scanf("%p", &s);
watch_sound((sound_type) s);
}
#endif
sound_desc = create_desc("SOUND", sound_xlfree, sound_xlprint,
sound_xlsave, sound_xlrestore, sound_xlmark);
}
/* sound_scale -- copy and change scale factor of a sound */
/**/
sound_type sound_scale(double factor, sound_type snd)
{
sound_type sndcopy = sound_copy(snd);
sndcopy->scale *= (float) factor;
return sndcopy;
}
/****************************************************************************
* set_logical_stop_time
* Inputs:
* sound_type sound: The sound for which the logical stop time is
* being set
* time_type when: The logical stop time, expressed as an absolute
* time.
* Result: void
*
* Effect:
* Converts the time 'when' into a count of samples.
****************************************************************************/
void set_logical_stop_time(sound_type sound, time_type when)
{
/*
'when' is an absolute time. The number of samples to
be generated is the number of samples between 't0' and
'when'.
-----------+---+---+---+---+---+---+---+---+---+
| |
t0 when
*/
long n = (long) ((when - sound->t0) * sound->sr + 0.5);
if (n < 0) {
xlcerror("retain the current logical stop",
"logical stop sample count is negative", NIL);
} else {
sound->logical_stop_cnt = n;
}
}
/* for debugging */
sound_type printing_this_sound = NULL;
void ((**watch_me)()) = NULL;
void set_watch(where)
void ((**where)());
{
if (watch_me == NULL) {
watch_me = where;
nyquist_printf("set_watch: watch_me = %p\n", watch_me);
}
}
/*
* additional routines
*/
void sound_print(snd_expr, n)
LVAL snd_expr;
long n;
{
LVAL result;
xlsave1(result);
result = xleval(snd_expr);
if (vectorp(result)) {
/* make sure all elements are of type a_sound */
long i = getsize(result);
while (i > 0) {
i--;
if (!exttypep(getelement(result, i), a_sound)) {
xlerror("sound_print: array has non-sound element",
result);
}
}
sound_print_array(result, n);
} else if (exttypep(result, a_sound)) {
sound_print_sound(getsound(result), n);
} else {
xlerror("sound_print: expression did not return a sound",
result);
}
xlpop();
}
void sound_print_sound(sound_type s, long n)
{
int ntotal = 0;
long blocklen;
sample_block_type sampblock;
/* for debugging */
printing_this_sound = s;
nyquist_printf("sound_print: start at time %g\n", s->t0);
while (ntotal < n) {
if (s->logical_stop_cnt != UNKNOWN)
nyquist_printf("LST=%d ", (int)s->logical_stop_cnt);
sound_print_tree(s);
sampblock = sound_get_next(s, &blocklen);
if (sampblock == zero_block || blocklen == 0) {
break;
}
print_sample_block_type("sound_print", sampblock,
MIN(blocklen, n - ntotal));
ntotal += blocklen;
}
nyquist_printf("total samples: %d\n", ntotal);
}
void sound_print_array(LVAL sa, long n)
{
long blocklen;
long i, len;
long upper = 0;
sample_block_type sampblock;
time_type t0, tmax;
len = getsize(sa);
if (len == 0) {
stdputstr("sound_print: 0 channels!\n");
return;
}
/* take care of prepending zeros if necessary */
t0 = tmax = (getsound(getelement(sa, 0)))->t0;
for (i = 1; i < len; i++) {
sound_type s = getsound(getelement(sa, i));
t0 = MIN(s->t0, t0);
tmax = MAX(s->t0, tmax);
}
/* if necessary, prepend zeros */
if (t0 != tmax) {
stdputstr("prepending zeros to channels: ");
for (i = 0; i < len; i++) {
sound_type s = getsound(getelement(sa, i));
if (t0 < s->t0) {
nyquist_printf(" %d ", (int)i);
sound_prepend_zeros(s, t0);
}
}
stdputstr("\n");
}
nyquist_printf("sound_print: start at time %g\n", t0);
while (upper < n) {
int i;
boolean done = true;
for (i = 0; i < len; i++) {
sound_type s = getsound(getelement(sa, i));
long current = -1; /* always get first block */
while (current < upper) {
sampblock = sound_get_next(s, &blocklen);
if (sampblock != zero_block && blocklen != 0) {
done = false;
}
current = s->current - blocklen;
nyquist_printf("chan %d current %d:\n", i, (int)current);
print_sample_block_type("sound_print", sampblock,
MIN(blocklen, n - current));
current = s->current;
upper = MAX(upper, current);
}
}
if (done) break;
}
nyquist_printf("total: %d samples x %d channels\n",
(int)upper, (int)len);
}
/* sound_play -- compute sound, do not retain samples */
/*
* NOTE: we want the capability of computing a sound without
* retaining samples. This requires that no references to
* the sound exist, but if the sound is passed as an argument,
* the argument stack will have a reference. So, we pass in
* an expression that evaluates to the sound we want. The
* expression is eval'd, the result copied (in case the
* expression was a sound or a global variable and we really
* want to preserve the sound), and then a GC is run to
* get rid of the original if there really are no other
* references. Finally, the copy is used to play the
* sounds.
*/
void sound_play(snd_expr)
LVAL snd_expr;
{
int ntotal;
long blocklen;
sample_block_type sampblock;
LVAL result;
sound_type s;
xlsave1(result);
result = xleval(snd_expr);
if (!exttypep(result, a_sound)) {
xlerror("sound_play: expression did not return a sound",
result);
}
ntotal = 0;
s = getsound(result);
/* if snd_expr was simply a symbol, then s now points to
a shared sound_node. If we read samples from it, then
the sound bound to the symbol will be destroyed, so
copy it first. If snd_expr was a real expression that
computed a new value, then the next garbage collection
will reclaim the sound_node. We need to explicitly
free the copy since the garbage collector cannot find
it.
*/
s = sound_copy(s);
while (1) {
#ifdef OSC
if (nosc_enabled) nosc_poll();
#endif
sampblock = sound_get_next(s, &blocklen);
if (sampblock == zero_block || blocklen == 0) {
break;
}
/* print_sample_block_type("sound_play", sampblock, blocklen); */
ntotal += blocklen;
}
nyquist_printf("total samples: %d\n", ntotal);
sound_unref(s);
xlpop();
}
/* sound_print_tree -- print a tree version of sound structure */
/**/
void sound_print_tree(snd)
sound_type snd;
{
/* nyquist_printf("sample_block_free %p\n", sample_block_free);*/
nyquist_printf("SOUND PRINT TREE of %p\n", snd);
sound_print_tree_1(snd, 0);
}
void indent(int n)
{
while (n-- > 0) stdputstr(" ");
}
void sound_print_tree_1(snd, n)
sound_type snd;
int n;
{
int i;
snd_list_type snd_list;
if (n > 100) {
stdputstr("... (skipping remainder of sound)\n");
return;
}
if (!snd) {
stdputstr("\n");
return;
}
nyquist_printf("sound_type@%p(%s@%p)t0 "
"%g stop %d sr %g lsc %d scale %g pc %d",
snd,
(snd->get_next == SND_get_next ? "SND_get_next" :
(snd->get_next == SND_get_first ? "SND_get_first" : "?")),
snd->get_next, snd->t0, (int)snd->stop, snd->sr,
(int)snd->logical_stop_cnt, snd->scale,
(int)snd->prepend_cnt);
snd_list = snd->list;
nyquist_printf("->snd_list@%p", snd_list);
if (snd_list == zero_snd_list) {
stdputstr(" = zero_snd_list\n");
return;
}
for (i = 0; ; i++) {
if (snd_list == zero_snd_list) {
if (i > 1) nyquist_printf(" (skipping %d) ", i-1);
stdputstr("->zero_snd_list\n");
return;
}
if (!snd_list->block) {
if (i > 0) nyquist_printf(" (skipping %d) ", i);
stdputstr("->\n");
indent(n + 2);
nyquist_printf("susp@%p(%s)toss_cnt %d "
"current %d lsc %d sr %g t0 %g %p\n",
snd_list->u.susp, snd_list->u.susp->name,
(int)snd_list->u.susp->toss_cnt,
(int)snd_list->u.susp->current,
(int)snd_list->u.susp->log_stop_cnt,
snd_list->u.susp->sr,
snd_list->u.susp->t0, snd_list);
/* stdputstr("HI THERE AGAIN\n");*/
susp_print_tree(snd_list->u.susp, n + 4);
return;
}
snd_list = snd_list->u.next;
}
}
/* mark_audio_time -- record the current playback time
*
* The global variable *audio-markers* is treated as a list.
* When the user types ^Q, this function pushes the current
* playback time onto the list
*/
void mark_audio_time()
{
double playback_time = sound_frames / sound_srate - sound_latency;
LVAL time_node = cvflonum(playback_time);
setvalue(s_audio_markers, cons(time_node, getvalue(s_audio_markers)));
gprintf(TRANS, " %g ", playback_time);
fflush(stdout);
}
/* compute constants p1 and p2:
pitchconvert(0) * 2 = pitchconvert(12) - octaves
exp(p2) * 2 = exp(12 * p1 + p2)
2 = exp(12 * p1)
log(2) = 12 * p1
p1 = log(2.0)/12;
pitchconvert(69) gives 440Hz
exp(69 * p1 + p2) = 440
69 * p1 + p2 = log(440)
p2 = log(440.0) - (69 * p1);
*/
#define p1 0.0577622650466621
#define p2 2.1011784386926213
double hz_to_step(double hz)
{
return (log(hz) - p2) / p1;
}
double step_to_hz(steps)
double steps;
{
return exp(steps * p1 + p2);
}
/*
* from old stuff...
*/
static void sound_xlfree(s)
sound_type s;
{
/* nyquist_printf("sound_xlfree(%p)\n", s);*/
sound_unref(s);
}
static void sound_xlprint(LVAL fptr, sound_type s)
{
/* the type cast from s to LVAL is OK because
* putatm does not dereference the 3rd parameter */
putatm(fptr, "Sound", (LVAL) s);
}
static void sound_xlsave(fp, s)
FILE *fp;
sound_type s;
{
stdputstr("sound_save called\n");
}
static unsigned char *sound_xlrestore(FILE *fp)
{
stdputstr("sound_restore called\n");
return NULL;
}
/* sound_xlmark -- mark LVAL nodes reachable from this sound */
/**/
void sound_xlmark(s)
sound_type s;
{
snd_list_type snd_list;
long counter = 0;
#ifdef TRACESNDGC
nyquist_printf("sound_xlmark(%p)\n", s);
#endif
if (!s) return; /* pointers to sounds are sometimes NULL */
snd_list = s->list;
while (snd_list->block != NULL) {
if (snd_list == zero_snd_list) {
#ifdef TRACESNDGC
stdputstr(" terminates at zero_snd_list\n");
#endif
return;
} else if (counter > 1000000) {
stdputstr("You created a recursive sound! This is a Nyquist bug.\n");
stdputstr("The only known way to do this is by a SETF on a\n");
stdputstr("local variable or parameter that is being passed to SEQ\n");
stdputstr("or SEQREP. The garbage collector assumes that sounds are\n");
stdputstr("not recursive or circular, and follows sounds to their\n");
stdputstr("end. After following a million nodes, I'm pretty sure\n");
stdputstr("that there is a cycle here, but since this is a bug,\n");
stdputstr("I cannot promise to recover. Prepare to crash. If you\n");
stdputstr("cannot locate the cause of this, contact the author -RBD.\n");
}
snd_list = snd_list->u.next;
counter++;
}
if (snd_list->u.susp->mark) {
#ifdef TRACESNDGC
nyquist_printf(" found susp (%s) at %p with mark method\n",
snd_list->u.susp->name, snd_list->u.susp);
#endif
(*(snd_list->u.susp->mark))(snd_list->u.susp);
} else {
#ifdef TRACESNDGC
nyquist_printf(" no mark method on susp %p (%s)\n",
snd_list->u.susp, snd_list->u.susp->name);
#endif
}
}
void sound_symbols()
{
a_sound = xlenter("SOUND");
s_audio_markers = xlenter("*AUDIO-MARKERS*");
setvalue(s_audio_markers, NIL);
}
/* The SOUND Type: */
boolean soundp(s)
LVAL s;
{
return (exttypep(s, a_sound));
}
/* sound_zero - create and return a zero that terminates now */
/**/
sound_type sound_zero(time_type t0,rate_type sr)
{
sound_type sound;
falloc_sound(sound, "sound_zero");
sound->get_next = SND_get_first;
sound->list = zero_snd_list;
sound->logical_stop_cnt = sound->current = 0;
sound->true_t0 = sound->t0 = sound->time = t0;
sound->stop = MAX_STOP;
sound->sr = sr;
sound->scale = 1.0F;
sound->table = NULL;
sound->extra = NULL;
return sound;
}
LVAL cvsound(s)
sound_type s;
{
/* nyquist_printf("cvsound(%p)\n", s);*/
return (cvextern(sound_desc, (unsigned char *) s));
}
|