File: sound.c

package info (click to toggle)
audacity 2.0.6-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 80,076 kB
  • sloc: cpp: 192,859; ansic: 158,072; sh: 34,021; python: 24,248; lisp: 7,495; makefile: 3,667; xml: 573; perl: 31; sed: 16
file content (1709 lines) | stat: -rw-r--r-- 55,947 bytes parent folder | download | duplicates (4)
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));
}