File: sndlib.html

package info (click to toggle)
snd 3.4-4
  • links: PTS
  • area: main
  • in suites: potato
  • size: 5,148 kB
  • ctags: 12,594
  • sloc: ansic: 86,516; lisp: 3,480; sh: 1,507; makefile: 119
file content (1221 lines) | stat: -rw-r--r-- 52,801 bytes parent folder | download
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
<html>
<head><title>The Sound Library</title></head>
<body bgcolor="#ffffff">
<center>
<h1>SndLib</h1>
</center>
<br><br>
<center>Bill Schottstaedt (bil@ccrma.stanford.edu)</center>
<br><br>

<h2>Contents</h2>
<dl>
<dt><a href="#introduction">Introduction</a>
<dt><a href="#headers">Headers</a>
<dt><a href="#data">Data</a>
<dt><a href="#hardware">Hardware</a>
<dt><a href="#music5">Music V</a>
<dt><a href="#examples">Examples</a>
<dd><a href="#sndinfo">SndInfo</a>
<dd><a href="#sndplay">SndPlay</a>
<dd><a href="#sndrecord">SndRecord</a>
<dd><a href="#audinfo">AudInfo</a>
<dd><a href="#sndsine">SndSine</a>
<dd><a href="#clmosc">clmosc</a>
<dd><a href="#otherexamples">Other Examples</a>
<dt><a href="#building">How to Make Sndlib and the examples</a>
<dt><a href="#currentstatus">Current Status</a>
<dt><a href="#lowerlevels">Lower Levels</a>
<dt><a href="#sndlibguile">Sndlib and Guile</a>
</dl>

<h2><a name="introduction">Introduction</a></h2>

<p>The sound library is a collection of sound file and audio hardware
handlers written in C and running currently on SGI (either
audio library), NeXT, Sun, Be, OSS or ALSA (Linux and others), Mac, HPUX, MkLinux/LinuxPPC, and Windoze systems.  
It provides relatively straightforward access to many sound file headers and data
types, and most of the features of the audio hardware. </p>

<p>The following files make up sndlib:</p>
<ul>
<li>io.c (read and write sound file data)
<li>headers.c (read and write sound file headers)
<li>audio.c (read and write sound hardware ports)
<li>sound.c (provide slightly higher level access to the preceding files)
<li>sndlib.h (header for the preceding files)
<li>sndlib2scm.c and sndlib-strings.h (tie preceding into Guile)
<li>clm.c and clm.h (Music V implementation)
<li>clm2scm.c, vct.c and vct.h (tie clm.c into Guile)
<li>old-sndlib.h (old names)
</ul>
<p>In version 6.0, I changed most of the exported names to
use the prefix "mus" or "SNDLIB" (to be more in line with
the Gnu standard); see old-sndlib.h for backwards compatibility.
</p>
<p>To build sndlib (sndlib.so if possible, and sndlib.a):</p>
<pre>
  ./configure
  make
</pre>
<p>To install it, 'make install' -- I've tested this process in Linux,
SGI, Sun, and NeXT.  It could conceivably work elsewhere.  For more
details see <a href="#building">How to Make Sndlib</a> below.</p>

<h2><a name="headers">Headers</a></h2>

<p>Sound files have built-in descriptors known as headers.
The following functions return the information in the header.
In each case the argument to the function is the full file
name of the sound file.
</p>
<pre>
  int sound_samples (char *arg)          /* samples of sound according to header (can be incorrect) */
  int sound_frames (char *arg)           /* samples per channel */
  float sound_duration (char *arg)
  int sound_datum_size (char *arg)       /* bytes per sample */
  int sound_data_location (char *arg)    /* location of first sample (bytes) */
  int sound_chans (char *arg)            /* number of channels (samples are interleaved) */
  int sound_srate (char *arg)            /* sampling rate */
  int sound_header_type (char *arg)      /* header type (aiff etc) */
  int sound_data_format (char *arg)      /* data format (alaw etc) */
  int sound_original_format (char *arg)  /* unmodified data format specifier */
  char *sound_comment (char *arg)        /* comment if any */
  int sound_comment_start (char *arg)    /* comment start (bytes) if any */
  int sound_comment_end (char *arg)      /* comment end (bytes) */
  int sound_length (char *arg)           /* true file length (for error checks) */
  int sound_fact_samples (char *arg)     /* compression scheme data */
  int sound_distributed (char *arg)      /* is header scattered around in sound file */
  int sound_write_date (char *arg)       /* bare (uninterpreted) file write date */
  int sound_type_specifier (char *arg)   /* original header type identifier */
  int sound_align (char *arg)            /* more compression data */
  int sound_bits_per_sample(char *arg)   /* bits per sample */
  int sound_bytes_per_sample(int format) /* bytes per sample */
  int sound_max_amp(char *arg, int *vals)/* return list of max-amp sample pairs */
  int sound_aiff_p(char *arg)            /* is it an old-style AIFF file (not AIFC) */
  void initialize_sndlib(void)           /* initialize everything */
  int sound_aiff_p(char *arg)            /* if sound's header actually an old-style AIFF (not AIFC) header */
</pre>
<p>The following can be used to provide user-understandable descriptions
of the header type and the data format:</p>
<pre>
  char *sound_type_name(int type)          /* "AIFF" etc */
  char *sound_format_name(int format)      /* "16-bit big endian linear" etc */
</pre>
<p>In all cases if an error occurs, -1 is returned; for information about
the error use:</p>
<pre>
  int audio_error(void)                    /* returns error code indicated by preceding audio call */
  char *audio_error_name(int err)          /* gives string decription of error code */
</pre>

<p>Header data is cached internally, so the actual header is read
only if it hasn't already been read, or the write date has changed.
Loop points are also available, if there's interest.  To go below the
"sound" level, see headers.c -- once a header has been read, all the
components that have been found can be read via functions such as
<b>mus_header_srate</b>.
</p>

<h2><a name="data">Data</a></h2>

<p>The following functions provide access to
sound file data:</p>
<pre>
  int open_sound_input (char *arg) 
  int open_sound_output (char *arg, int srate, int chans, int data_format, int header_type, char *comment)
  int reopen_sound_output (char *arg, int type, int format, int data_loc)
  int close_sound_input (int fd) 
  int close_sound_output (int fd, int bytes_of_data) 
  int read_sound (int fd, int beg, int end, int chans, int **bufs) 
  int write_sound (int fd, int beg, int end, int chans, int **bufs) 
  int seek_sound (int fd, long offset, int origin) 
  int seek_sound_frame (int fd, int frame)
  int mus_float_sound(char *charbuf, int samps, int charbuf_format, float *buffer)
</pre>
<p><i>open_sound_input</i> opens <i>arg</i> for reading.  Most standard
uncompressed formats are readable.  This function returns the associated
file number, or -1 upon failure. </p>

<p><i>close_sound_input</i> closes an open sound file.  Its argument is
the integer returned by open_sound_input.</p>

<p><i>open_sound_output</i> opens <i>arg</i>, setting its sampling rate
to be <i>srate</i>, number of channels to <i>chans</i>, data format
to <i>data_format</i> (see sndlib.h for these types: SNDLIB_16_LINEAR,
for example, means 16-bit 2's complement big endian fractions),
header type to <i>header_type</i> (AIFF for example; the available
writable header types are AIFF_sound_file, RIFF_sound_file ('wave'),
NeXT_sound_file, and IRCAM_sound_file), and comment (if any) to
<i>comment</i>.  The header is not considered complete without
an indication of the data size, but since this is rarely known
in advance, it is supplied when the sound file is closed.  This
function returns the associated file number.</p>

<p><i>close_sound_output</i> first updates the file's header to 
reflect the final data size <i>bytes_of_data</i>, then closes
the file.  The argument <i>fd</i> is the integer returned by
open_sound_output.</p>

<p><i>read_sound</i> reads data from the file indicated by <i>fd</i>,
placing data in the array <i>obufs</i> as 32-bit integers in the
host's byte order.  <i>chans</i> determines how many arrays of
ints are in obufs, which is filled by read_sound from its
index <i>beg</i> to <i>end</i> with zero padding if necessary.
See the sndplay example below if this is not obvious.</p>

<p><i>write_sound</i> writes data to the file indicated by <i>fd</i>,
starting for each of <i>chans</i> channels in <i>obufs</i> at
<i>beg</i> and ending at <i>end</i>.</p>

<p><i>seek_sound</i> moves the read or write position for the
file indicated by <i>fd</i> to <i>offset</i> given the
<i>origin</i> indication (both treated as in lseek).
The new actual position attained is returned.  In both
cases (the returned value and offset), the output datum
size is considered to be 2, no matter what it really is.
That is, use byte positions as if you were always reading
and writing 16-bit data, and seek_sound will compensate
if its actually 32-bit floats or whatever.  Since this
is impossible to understand, there's also seek_sound_frame
which moves to the indicated frame.
</p>

<p><a name="floatsound"><i>mus_float_sound</i></a> takes a buffer full of sound data in
some format (<i>charbuf_format</i> and returns the data
as a buffer full of (unscaled) floats.</p>


<h2><a name="hardware">Hardware</a></h2>

<p>The following functions provide access to audio harware.  If an
error occurs, they return -1, and the audio_error functions
can be used to find out what went wrong.</p>
<pre>
  int initialize_audio(void)
  void save_audio_state(void)
  void restore_audio_state(void)
  void describe_audio_state(void)
  char *report_audio_state(void)
  int open_audio_output(int dev, int srate, int chans, int format, int size)
  int open_audio_input(int dev, int srate, int chans, int format, int size)
  int write_audio(int line, char *buf, int bytes)
  int close_audio(int line)
  int read_audio(int line, char *buf, int bytes)
  int read_audio_state(int dev, int field, int chan, float *val)
  int write_audio_state(int dev, int field, int chan, float *val)
  int audio_systems(void)
  char *audio_system_name(int system)
  void setup_dsps(int cards, int *dsps, int *mixers) /* OSS only */
</pre>

<p><i>initialize_audio</i> takes care of any necessary intialization.</p>

<p><i>save_audio_state</i> saves the current audio hardware state.</p>

<p><i>restore_audio_state</i> restores the audio hardware to the last saved state.</p>

<p><i>describe_audio_state</i> prints to stdout a description of the
current state of the audio hardware.  <i>report_audio_state</i>
returns the same description as a string.</p>

<p><i>audio_systems</i> returns the number of separate and complete
audio systems (soundcards essentially) that are available.
<i>audio_system_name</i>  returns some user-recognizable name for the given card.
</p>

<p><i>open_audio_input</i> opens an audio port to read sound data (i.e. a microphone, line in, etc).
The input device is <i>dev</i> (see sndlib.h for details; when in doubt, use SNDLIB_DEFAULT_DEVICE).
The input sampling rate is <i>srate</i> or as close as we
can get to it.  The number of input channels (if available) is <i>chans</i>.
The input data format is <i>format</i> (when in doubt, use the macro SNDLIB_COMPATIBLE_FORMAT).
And the input buffer size (if settable at all) is <i>size</i> (bytes).  This
function returns an integer to distinguish its port from others that might be
in use.  In this and other related functions, the device has an optional second
portion that refers to the soundcard or system for that device.  SNDLIB_AUDIO_SYSTEM(n)
refers to the nth such card, so (SNDLIB_DAC_DEVICE | SNDLIB_AUDIO_SYSTEM(1)) is the 2nd
card's dac (the default is system 0, the first card).
</p>

<p><i>open_audio_output</i> opens an audio port to write date (i.e. speakers, line out, etc).
The output device is <i>dev</i> (see sndlib.h).  Its sampling rate is <i>srate</i>, number
of channels <i>chans</i>, data format <i>format</i>, and buffer size <i>size</i>.  This
function returns the associated line number of the output port.</p>

<p><i>close_audio</i> closes the port (input or output) associated with <i>line</i>.</p>

<p><i>read_audio</i> reads sound data from <i>line</i>.  The incoming <i>bytes</i> bytes of data are placed
in <i>buf</i>.  If no error was returned from open_audio_input, the data is in the format requested
by that function with channels interleaved.</p>

<p><i>write_audio</i> writes <i>bytes</i> bytes of data in <i>buf</i> to the output
port associated with <i>line</i>.  This data is assumed to be in the format
requested by open_audio_output with channels interleaved.</p>

<p><a name="readaudiostate"><i>read_audio_state</i></a> and <i>write_audio_state</i> are complicated.  They
get and set the audio hardware state.  The audio hardware is treated as a
set of "systems" (sound cards) each of which has a set of "devices" (dacs, adcs, etc),
with various "fields" that can be read or set (gain, channels active, etc).
For example, a microphone is called
the SNDLIB_MICROPHONE_DEVICE, and its hardware gain setting (if any) is called
the SNDLIB_AMP_FIELD.  All gains are considered to be linear between 0.0 and
1.0, so to set the microphone's first channel amplitude to .5 (that is,
the gain of the signal before it reaches the analog-to-digital converter),</p>
<pre>
  float vals[1];
  vals[0]=0.5;
  write_audio_state(SNDLIB_MICROPHONE_DEVICE,SNDLIB_AMP_FIELD,0,vals);
</pre>
<p>Similarly</p>
<pre>
  read_audio_state(SNDLIB_MICROPHONE_DEVICE,SNDLIB_AMP_FIELD,0,vals);
  amp=vals[0];
</pre>
<p>returns the current gain in the float array vals.  read_audio_state
can also return a description of the currently available audio
hardware.</p>

<p>If a requested operation is not implemented, -1 is returned, and
SNDLIB_AUDIO_ERROR is set to SNDLIB_CANT_READ or SNDLIB_CANT_WRITE.  If an error occurs
during the requested operation, -1 is returned, and SNDLIB_AUDIO_ERROR
is set to SNDLIB_READ_ERROR or SNDLIB_WRITE_ERROR.  If some operation cannot
be performed on the current hardware, -1 is returned and SNDLIB_AUDIO_ERROR
tries to indicate what portion of the requested operation is
impossible (SNDLIB_SRATE_NOT_AVAILABLE, SNDLIB_FORMAT_NOT_AVAILABLE, and so on).</p>

<h4>Systems</h4>
<p>Each separate sound card is called a system, accessible via the <i>device</i>
argument through the macro SNDLIB_AUDIO_SYSTEM(n). The count starts at 0 which is the default.  The
function <b>audio_systems</b> returns how many such cards are available.  (Currently
it returns more than one only on Linux systems with multiple sound cards).
</p>

<h4>Devices</h4>
<p>Each audio system has a set of available devices.  To find out what is 
available on a given system</p>
<pre>
  #define LIST_MAX_SIZE 32;
  float device_list[LIST_MAX_SIZE];
  read_audio_state(SNDLIB_AUDIO_SYSTEM(0),SNDLIB_DEVICE_FIELD,LIST_MAX_SIZE,device_list);
</pre>
<p>The list of available devices is returned in the device_list array,
with the number of the devices as device_list[0].  The set of device
identifiers is in sndlib.h (SNDLIB_LINE_IN_DEVICE for example). Two special
devices are SNDLIB_MIXER_DEVICE and SNDLIB_DAC_FILTER_DEVICE.  The latter refers
to the low-pass filter often associated with a DAC.  The former
refers to a set of analog gain and tone controls often associated
with a sound card.  The individual gains are accessed through the
various fields (described below).
</p>

<h4>Fields</h4>
<p>The field argument in read-audio-state and write-audio-state selects
one aspect of the given card's devices' controls.  
The simplest operations involve SNDLIB_AMP_FIELD and SNDLIB_SRATE_FIELD.  The latter
gets or sets the sampling rate of the device, and the former gets or sets
the amplitude (between 0.0 and 1.0) of the specified channel of the device.
The value to be set or returned is in the 0th element of the <i>vals</i>
array.  An example of reading the current microphone gain is given above.
The meaning of the field argument can depend on which device it is
applied to, so there is some complexity here.  The <i>channel</i>
argument usually selects which channel we are interested in, but in
some cases it instead tells read-audio-state how big a returned list
can get.  A brief description of the fields:</p>
<pre>
SNDLIB_AMP_FIELD       gain or volume control (0.0 to 1.0)
SNDLIB_SRATE_FIELD     sampling rate
SNDLIB_CHANNEL_FIELD   active channels

SNDLIB_BASS_FIELD, SNDLIB_TREBLE_FIELD    mixer's tone control
SNDLIB_LINE_FIELD      mixer's line-in gain control
SNDLIB_MIC_FIELD       mixer's microphone gain control
similarly for SNDLIB_IMIX_FIELD, SNDLIB_IGAIN_FIELD, 
              SNDLIB_RECLEV_FIELD, SNDLIB_PCM_FIELD, SNDLIB_PCM2_FIELD,
              SNDLIB_OGAIN_FIELD, SNDLIB_LINE1_FIELD, 
              SNDLIB_LINE2_FIELD, SNDLIB_LINE3_FIELD, SNDLIB_SYNTH_FIELD 

SNDLIB_FORMAT_FIELD    return list of usable sound formats (e.g. SNDLIB_16_LINEAR)
SNDLIB_DEVICE_FIELD    return list of available devices (e.g. SNDLIB_MICROPHONE_DEVICE)
</pre>


<h2><a name="music5">MusicV</a></h2>

<p>clm.c and friends implement all the generators found in CLM, a common lisp
music V implementation, and clm2scm.c ties these into Guile (Scheme).  The
primary clm documentation (which describes both the Scheme and Common Lisp implementations)
is clm.html found in clm-2.tar.gz alongside sndlib at ccrma-ftp.
The simplest way to try these out is to load them into Snd; see extsnd.html
and examp.scm in snd-3.tar.gz for more details.
The C implementation is essentially the same as the two Lisp versions, but (as might be expected), works
at a lower level, expecting the caller to handle garbage collection and so forth.
The following briefly describes the C calls (see clm.h).
</p>

<p>clm.c implements a bunch of generators and sound IO handlers.  Each generator
has three associated functions, make-gen, gen, and gen_p; the first
creates the generator (if needed), the second gets the next sample from
the generator, and the last examines some pointer to determine if it is
that kind of generator.  In addition, there are a variety of "generic"
functions that generators respond to: mus_free, for example, frees a
generator, and mus_frequency returns its current frequency, if relevant.
All generators are pointers to mus_any structs.  Finally, CLM has two
special data types: frame and mixer.  A frame is an array that represents
a multi-channel sample (that is, in a stereo file, at time 0.0, there
are two samples, one for each channel).  A mixer is a array of arrays
that represents a set of input and output scalers, as if it were the
current state of a mixing console's volume controls.  A frame (a multi-channel
input) can be "mixed" into a new frame (a multi-channel output) by passing
it through a "mixer" (a matrix, the operation being a matrix multiply).
</p>

<ul>
<li>oscil -- generate a sine wave.
<ul>
<li>mus_any *mus_make_oscil (float freq, float phase)
<li>float mus_oscil (mus_any *o, float fm, float pm)
<li>int mus_oscil_p (mus_any *ptr)
</ul>
<pre><font size = "2">
  mus_any *osc;
  init_mus_module();
  osc = mus_make_oscil(440.0,0.0);
  if (oscil_p(osc)) fprintf(stderr,"%.3f, %.3f ",.1 * mus_oscil(osc,0.0,0.0),mus_frequency(osc));
  mus_free(osc);
</font></pre>
</ul>
<p>The other generators are:</p>
<ul>
<li>sum_of_cosines -- generate a pulse train made up of cosines
<li>delay -- a delay line with optional interpolation
<li>tap -- read delay line
<li>comb -- comb filter
<li>notch -- notch filter
<li>all_pass -- all pass filter
<li>table_lookup -- interpolating table lookup
<li>sawtooth_wave, triangle_wave, pulse_train, square_wave
<li>rand -- white noise (a step function)
<li>rand-interp -- interpolating noise
<li>asymmetric_fm -- a variety of FM
<li>one_zero, two_zero, one_pole, two_pole -- basic filters
<li>formant -- create a formant region (two poles, two zeros)
<li>sine_summation -- another way to create sine waves
<li>filter, fir_filter, iir_filter -- direct form filters of any order
<li>wave_train -- sequence of possibly overlapping waves
<li>buffer -- a way to handle block processing in the generator world
<li>env -- envelopes
<li>waveshape -- waveshaping
<li>readin, file2sample, file2frame, in_any -- file sample input
<li>locsig, sample2file, frame2file, out_any -- file sample output
<li>src -- sampling rate conversion
<li>granulate -- granular synthesis
<li>convolve -- convolution
</ul>

<p>Some useful functions provided by clm.c are: </p>
<ul>
<li>float mus_radians2hz(float rads) -- convert radians/sample to cycles/sec.
<li>float mus_hz2radians(float hz) -- and the reverse.
<li>float mus_degrees2radians(float deg) -- convert degrees to radians.
<li>float mus_radians2degrees(float rads) -- and the reverse.
<li>float mus_srate(void) -- current sampling rate
<li>float mus_set_srate(float rate) -- set current sampling rate
<li>float mus_ring_modulate(float sig1, float sig2) -- multiply sig1 by sig2
<li>float mus_amplitude_modulate(float s1, float s2, float s3) -- AM
<li>float mus_contrast_enhancement(float sig, float index)
<li>float mus_dot_product(float *data1, float *data2, int size)
<li>void mus_clear_array(float *arr, int size)
<li>float mus_array_interp(float *wave, float phase, int size)
<li>float mus_polynomial(float *coeffs, float x, int ncoeffs);
<li>void mus_multiply_arrays(float *data, float *window, int len);
<li>void mus_rectangular2polar(float *rl, float *im, int size);
<li>void mus_spectrum(float *rdat, float *idat, float *window, int n, int type)
<li>void mus_fft(float *rl, float *im, int n, int isign, int ipow)
<li>float *mus_make_fft_window(int size, int type, float beta)
<li>void mus_convolution(float* rl1, float* rl2, int n, int ipow)
<li>float *mus_partials2wave(float *partial_data, int partials, float *table, int table_size, int normalize)
<li>float *mus_phasepartials2wave(float *partial_data, int partials, float *table, int table_size, int normalize)
</ul>
<p>and various others -- see clm.h.</p>

<p>The more useful generic functions are:</p>
<ul>
<li>int mus_free(mus_any *ptr)
<li>char *mus_describe(mus_any *gen)
<li>float mus_phase(mus_any *gen)
<li>float mus_set_phase(mus_any *gen, float val)
<li>float mus_set_frequency(mus_any *gen, float val)
<li>float mus_frequency(mus_any *gen)
<li>int mus_length(mus_any *gen)
<li>int mus_set_length(mus_any *gen, int len)
<li>float *mus_data(mus_any *gen)
<li>float *mus_set_data(mus_any *gen, float *data)
<li>char *mus_name(mus_any *ptr)
<li>int mus_type(mus_any *ptr)
<li>float mus_scaler(mus_any *gen)
<li>float mus_set_scaler(mus_any *gen, float val)
</ul>

<p>Before using any of these functions, call init_mus_module.  Errors are reported
through mus_error which can be redirected or muffled.  See clm2scm.c for an example.
</p>
<hr>

<h2><a name="examples">Examples</a></h2>

<p>In the following examples I've omitted the usual garrulous C-header
gab and other inessential stuff.  The full
program code is available as noted below.</p>

<h3><a name="sndinfo">SndInfo</a></h3>

<p>This program prints out a description of a sound file (sndinfo.c).</p>
<pre>
<font size="2">
int main(int argc, char *argv[])
{
  int fd,chans,srate,samples;
  float length;
  time_t date;
  char *comment;
  char timestr[64];
  initialize_sndlib();	
  fd = mus_open_read(argv[1]); /* see if it exists */
  if (fd != -1)
    {
      close(fd);
      date = sound_write_date(argv[1]);
      srate = sound_srate(argv[1]);
      chans = sound_chans(argv[1]);
      samples = sound_samples(argv[1]);
      comment = sound_comment(argv[1]); 
      length = (float)samples / (float)(chans * srate);
      strftime(timestr,64,"%a %d-%b-%y %H:%M %Z",localtime(&amp;date));
      fprintf(stdout,"%s:\n  srate: %d\n  chans: %d\n  length: %f\n",
	      argv[1],srate,chans,length);
      fprintf(stdout,"  type: %s\n  format: %s\n  written: %s\n  comment: %s\n",
	      sound_type_name(sound_header_type(argv[1])),
	      sound_format_name(sound_data_format(argv[1])),
	      timestr,comment);
    }
  else
    fprintf(stderr,"%s: %s\n",argv[1],strerror(errno));
  return(0);
}
</font>
</pre>

<h3><a name="sndplay">SndPlay</a></h3>

<p>This code plays a sound file (sndplay.c):</p>

<pre>
<font size="2">

int main(int argc, char *argv[])
{
  int fd,afd,i,j,n,k,chans,srate,frames,outbytes;
  int **bufs;
  short *obuf;
  initialize_sndlib();	
  fd = open_sound_input(argv[1]);
  if (fd != -1)
    {
      chans = sound_chans(argv[1]);
      srate = sound_srate(argv[1]);
      frames = sound_frames(argv[1]);
      outbytes = BUFFER_SIZE * chans * 2;
      bufs = (int **)calloc(chans,sizeof(int *));
      for (i=0;i&lt;chans;i++) bufs[i] = (int *)calloc(BUFFER_SIZE,sizeof(int));
      obuf = (short *)calloc(BUFFER_SIZE * chans,sizeof(short));
      afd = open_audio_output(SNDLIB_DEFAULT_DEVICE,srate,chans,SNDLIB_COMPATIBLE_FORMAT,outbytes);
      if (afd != -1)
	{
	  for (i=0;i&lt;frames;i+=BUFFER_SIZE)
	    {
	      read_sound(fd,0,BUFFER_SIZE-1,chans,bufs);
	      for (k=0,j=0;k&lt;BUFFER_SIZE;k++,j+=chans)
		for (n=0;n&lt;chans;n++) obuf[j+n] = bufs[n][k];
	      write_audio(afd,(char *)obuf,outbytes);
	    }
	  close_audio(afd);
	}
      close_sound_input(fd);
      for (i=0;i&lt;chans;i++) free(bufs[i]);
      free(bufs);
      free(obuf);
    }
  else
    fprintf(stderr,"%s: %s ",argv[1],audio_error_name(audio_error()));
  return(0);
}

</font>
</pre>
<h3><a name="sndrecord">SndRecord</a></h3>

<p>This code records a couple seconds of sound from a microphone.
Input formats and sampling rates are dependent on available
hardware, so in a "real" program, you'd use <a href="#readaudiostate">read_audio_state</a>
to find out what was available, then <a href="#floatsound">float-sound</a> to
turn that data into a stream of floats.
You'd also provide, no doubt,
some whizzy user interface to turn the thing off. (sndrecord.c)</p>
<pre>
<font size="2">
int main(int argc, char *argv[])
{
  int fd,afd,i,err;
  short *ibuf;
#if MACOS
  argc = ccommand(&amp;argv);
#endif
  afd = -1;
  initialize_sndlib();	
  fd = open_sound_output(argv[1],22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by sndrecord");
  if (fd != -1)
    {
      ibuf = (short *)calloc(BUFFER_SIZE,sizeof(short));
      afd = open_audio_input(SNDLIB_MICROPHONE_DEVICE,22050,1,SNDLIB_16_LINEAR,BUFFER_SIZE);
      if (afd != -1)
	{
	  for (i=0;i&lt;10;i++) /* grab 10 buffers of input */
	    {
	      err = read_audio(afd,(char *)ibuf,BUFFER_SIZE*2);
	      if (err != SNDLIB_NO_ERROR) {fprintf(stderr,audio_error_name(audio_error())); break;}
	      write(fd,ibuf,BUFFER_SIZE*2);
	    }
	  close_audio(afd);
	}
      else 
	fprintf(stderr,audio_error_name(audio_error()));
      close_sound_output(fd,BUFFER_SIZE*10*2);
      free(ibuf);
    }
  else
    fprintf(stderr,"%s: %s ",argv[1],strerror(errno));
  return(0);
}
</font>
</pre>
<h3><a name="audinfo">AudInfo</a></h3>

<p>This program describes the current audio harware state (audinfo.c):</p>
<pre>
<font size="2">

int main(int argc, char *argv[])
{
  initialize_sndlib();	
  describe_audio_state();
  return(0);
}
</font>
</pre>

<h3><a name="sndsine">SndSine</a></h3>

<p>This program writes a one channel NeXT/Sun sound file
containing a sine wave at 440 Hz.</p>

<pre>
<font size="2">
int main(int argc, char *argv[])
{
  int fd,i,k,frames;
  float phase,incr;
  int *obuf[1];
  initialize_sndlib();	
  fd = open_sound_output(argv[1],22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by sndsine");
  if (fd != -1)
    {
      frames = 22050;
      phase = 0.0;
      incr = 2*PI*440.0/22050.0;
      obuf[0] = (int *)calloc(BUFFER_SIZE,sizeof(int));
      k=0;
      for (i=0;i&lt;frames;i++)
	{
	  obuf[0][k] = (int)(3276.8 * sin(phase)); /* amp = .1 */
	  phase += incr;
	  k++;
	  if (k == BUFFER_SIZE)
	    {
	      write_sound(fd,0,BUFFER_SIZE-1,1,obuf);
	      k=0;
	    }
	}
      if (k&gt;0) write_sound(fd,0,k-1,1,obuf);
      close_sound_output(fd,22050*mus_format2bytes(SNDLIB_16_LINEAR));
      free(obuf[0]);
    }
  return(0);
}
</font>
</pre>

<h3><a name="clmosc">clmosc</a></h3>

<p>This is program uses the clm.c oscillator and output functions to write the same sine wave 
as we wrote in SndSine. (Compile clm.c with -DHAVE_SNDLIB=1).<p>
<pre>
<font size="2">
int main(int argc, char *argv[])
{
  int i;
  mus_any *osc,*op;
  initialize_sndlib();	
  init_mus_module();
  osc = mus_make_oscil(440.0,0.0);
  op = mus_make_file_output("test.snd",22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by clmosc");
  if (op) for (i=0;i&lt;22050;i++) mus_sample2file(op,i,0,.1 * mus_oscil(osc,0.0,0.0));
  mus_free(osc);
  if (op) mus_free(op);
  return(0);
}
</font>
</pre>
<p>Here is the fm-violin and a sample with-sound call:</p>
<pre><font size = "2">

static int feq(float x, int i) {return(fabs(x-i)&lt;.00001);}

void fm_violin(float start, float dur, float frequency, float amplitude, float fm_index, mus_any *op)
{
 float pervibfrq = 5.0,
   ranvibfrq = 16.0,
   pervibamp = .0025,
   ranvibamp = .005,
   noise_amount = 0.0,
   noise_frq = 1000.0,
   gliss_amp = 0.0,
   fm1_rat = 1.0,
   fm2_rat = 3.0,
   fm3_rat = 4.0,
   reverb_amount = 0.0,
   degree = 0.0, 
   distance = 1.0;
  float fm_env[] = {0.0, 1.0, 25.0, 0.4, 75.0, 0.6, 100.0, 0.0};
  float amp_env[] = {0.0, 0.0,  25.0, 1.0, 75.0, 1.0, 100.0, 0.0};
  float frq_env[] = {0.0, -1.0, 15.0, 1.0, 25.0, 0.0, 100.0, 0.0};
  int beg = 0,end,easy_case = 0,npartials,i;
  float *coeffs,*partials;
  float frq_scl,maxdev,logfrq,sqrtfrq,index1,index2,index3,norm,vib = 0.0,modulation = 0.0,fuzz = 0.0,indfuzz = 1.0,ampfuzz = 1.0;
  mus_any *carrier,*fmosc1,*fmosc2,*fmosc3,*ampf,*indf1,*indf2,*indf3,*fmnoi = NULL,*pervib,*ranvib,*frqf = NULL,*loc;
  beg = start * mus_srate();
  end = beg + dur * mus_srate();
  frq_scl = mus_hz2radians(frequency);
  maxdev = frq_scl * fm_index;
  if ((noise_amount == 0.0) && (feq(fm1_rat,floor(fm1_rat))) && (feq(fm2_rat,floor(fm2_rat))) && (feq(fm3_rat,floor(fm3_rat)))) easy_case = 1;
  logfrq = log(frequency);
  sqrtfrq = sqrt(frequency);
  index1 = maxdev * 5.0 / logfrq; if (index1 &gt; M_PI) index1 = M_PI;
  index2 = maxdev * 3.0 * (8.5 - logfrq) / (3.0 + frequency * .001); if (index2 &gt; M_PI) index2 = M_PI;
  index3 = maxdev * 4.0 / sqrtfrq; if (index3 &gt; M_PI) index3 = M_PI;
  if (easy_case)
    {
      npartials = floor(fm1_rat);
      if ((floor(fm2_rat)) &gt; npartials) npartials = floor(fm2_rat);
      if ((floor(fm3_rat)) &gt; npartials) npartials = floor(fm3_rat);
      npartials++;
      partials = (float *)CALLOC(npartials,sizeof(float));
      partials[(int)(fm1_rat)] = index1;
      partials[(int)(fm2_rat)] = index2;
      partials[(int)(fm3_rat)] = index3;
      coeffs = mus_partials2polynomial(npartials,partials,1);
      norm = 1.0;
    }
  else norm = index1;
  carrier = mus_make_oscil(frequency,0.0);
  if (easy_case == 0)
    {
      fmosc1 = mus_make_oscil(frequency * fm1_rat,0.0);
      fmosc2 = mus_make_oscil(frequency * fm2_rat,0.0);
      fmosc3 = mus_make_oscil(frequency * fm3_rat,0.0);
    }
  else fmosc1 = mus_make_oscil(frequency,0.0);
  ampf = mus_make_env(amp_env,4,amplitude,0.0,1.0,dur,0,0,NULL);
  indf1 = mus_make_env(fm_env,4,norm,0.0,1.0,dur,0,0,NULL);
  if (gliss_amp != 0.0) frqf = mus_make_env(frq_env,4,gliss_amp * frq_scl,0.0,1.0,dur,0,0,NULL);
  if (easy_case == 0)
    {
      indf2 = mus_make_env(fm_env,4,index2,0.0,1.0,dur,0,0,NULL);
      indf3 = mus_make_env(fm_env,4,index3,0.0,1.0,dur,0,0,NULL);
    }
  pervib = mus_make_triangle_wave(pervibfrq,frq_scl * pervibamp,0.0);
  ranvib = mus_make_rand_interp(ranvibfrq,frq_scl * ranvibamp);
  if (noise_amount != 0.0) fmnoi = mus_make_rand(noise_frq,noise_amount * M_PI);
  loc = mus_make_locsig(degree,distance,reverb_amount,1,(mus_output *)op,NULL);
  for (i=beg;i&lt;end;i++)
    {
      if (noise_amount != 0.0) fuzz = mus_rand(fmnoi,0.0);
      if (frqf) vib = mus_env(frqf); else vib = 0.0;
      vib += mus_triangle_wave(pervib,0.0) + mus_rand_interp(ranvib,0.0);
      if (easy_case)
	modulation = mus_env(indf1) * mus_polynomial(coeffs,mus_oscil(fmosc1,vib,0.0),npartials);
      else
	modulation = mus_env(indf1) * mus_oscil(fmosc1,(fuzz + fm1_rat * vib),0.0) + 
	             mus_env(indf2) * mus_oscil(fmosc2,(fuzz + fm2_rat * vib),0.0) + 
	             mus_env(indf3) * mus_oscil(fmosc3,(fuzz + fm3_rat * vib),0.0);
      mus_locsig(loc,i,mus_env(ampf) * mus_oscil(carrier,vib + indfuzz * modulation,0.0));
    }
  mus_free(pervib);
  mus_free(ranvib);
  mus_free(carrier);
  mus_free(fmosc1);
  mus_free(ampf);
  mus_free(indf1);
  if (fmnoi) mus_free(fmnoi);
  if (frqf) mus_free(frqf);
  if (easy_case == 0)
    {
      mus_free(indf2);
      mus_free(indf3);
      mus_free(fmosc2);
      mus_free(fmosc3);
    }
  else
    FREE(partials);
  mus_free(loc);
}

int main(int argc, char *argv[])
{
  mus_any *osc = NULL,*op = NULL;
  initialize_sndlib();	
  init_mus_module();
  op = mus_make_file_output("test.snd",22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by clmosc");
  if (op)
    {
      fm_violin(0.0,20.0,440.0,.3,1.0,op);
      mus_free(op);
    }
  return(0);
}
</font></pre>
<p>The CLM version is v.ins, the Scheme version can be found in examp.scm.
This code can be run:</p>
<pre>
cc v.c -o vc -O3 -lm io.o headers.o audio.o sound.o clm.o -DLINUX
</pre>
<p>where clm.o was compiled with -DHAVE_SNDLIB.</p>


<hr>
<h3><a name="otherexamples">Other Examples</a></h3>

<p>The primary impetus for the sound library was the development
of Snd and CLM, both of which are freely available.</p>
<hr>


<h3><a name="building">How to Make Sndlib and the examples</a></h3>

<p>The Sndlib files can be used as separate modules or made into a
library.  The following sequence, for example, builds the sndplay
program from scratch on an SGI:</p>
<pre>
cc -c io.c -O -DSGI
cc -c headers.c -O -DSGI
cc -c audio.c -O -DSGI
cc -c sound.c -O -DSGI
cc sndplay.c -o sndplay -O -DSGI audio.o io.o headers.o sound.o -laudio -lm
</pre>
<p>To make a library out of 
the sndlib files, first compile them as above, then:</p>
<pre>
ld -r audio.o io.o headers.o sound.o -o sndlib.a
cc sndplay.c -o sndplay -O -DSGI sndlib.a -laudio -lm
</pre>
<p>The full sequence in Linux:</p>
<pre>
cc -c io.c -O -DLINUX
cc -c audio.c -O -DLINUX
cc -c headers.c -O -DLINUX
cc -c sound.c -O -DLINUX
cc sndplay.c -o sndplay -O -DLINUX audio.o io.o headers.o sound.o -lm

ld -r audio.o io.o headers.o sound.o -o sndlib.a
cc sndplay.c -o sndplay -O -DLINUX sndlib.a -lm
</pre>
<p>And on a NeXT:</p>
<pre>
cc -c io.c -O -DNEXT
cc -c audio.c -O -DNEXT
cc -c headers.c -O -DNEXT
cc -c sound.c -O -DNEXT
cc sndplay.c -o sndplay -O -DNEXT audio.o io.o headers.o sound.o 

ld -r audio.o io.o headers.o sound.o -o sndlib.a
cc sndplay.c -o sndplay -O -DNEXT sndlib.a
</pre>
<p>Some similar sequence should work on a Sun (-DSOLARIS) or in HP-UX (-DHPUX).
On a Mac, you need to make a project in CodeWarrior or whatever that
includes all the basic sndlib .c and .h files (io.c, audio.c
headers.c, sound.c, sndlib.h) as source
files.  Add the main program you're interested in
(say sndplay.c), and "Make" the project.  When the
project is "Run", a dialog pops up asking for
the arguments to the program (in this case the
name of the file to be played, as a quoted string).
In Windoze, you can use the C IDE (a project builder as in the Mac case),
or run the compiler from a DOS shell.  In the latter case,
(in Watcom C) cl io.c -c -DWINDOZE to create the object files (io.obj and so on), then</p>
<pre>
cl sndplay sndplay.obj -DWINDOZE audio.obj io.obj headers.obj sound.obj 
</pre>
<p>or in MS C</p>
<pre>
cl -c io.c -DWINDOZE
(and so on)
cl sndplay.c -DWINDOZE sndplay.obj audio.obj io.obj headers.obj sound.obj winmm.lib
</pre>
<p>or in gcc (available via the cygwin project)</p>
<pre>
gcc -c io.c -DWINDOZE -O2
</pre>
<p>
You can run the program from the DOS shell (sndplay oboe.snd or ./sndplay.exe oboe.snd).
On a Be, you can either build a project or use a makefile.
The C compiler's name is mwcc.
The tricky part
here is that you have to find and include explicitly
the Be audio library, libmedia.so -- look first in beos/system/lib.  
Or</p>
<pre>
make sndplay
</pre>
<p>To make sndlib into a shared library,</p>
<pre>
ld -shared io.o headers.o audio.o sound.o -o sndlib.so
</pre>
<p>(in Linux), or (to include the CLM module),</p>
<pre>
ld -shared io.o headers.o audio.o sound.o clm.o -o sndlib.so
</pre>
<hr>

<h2><a name="currentstatus">Current Status</a></h2>

<table border>
  <tr><th>System<th>SndSine<th>SndInfo<th>Audinfo<th>SndPlay<th>SndRecord<th>CLM
  <tr><td>NeXT 68k<td>ok<td>ok<td>ok<td>ok<td>ok<td>ok
  <tr><td>NeXT Intel<td>ok<td>ok<td>ok<td>interruptions<td>runs (*)<td>untried
  <tr><td>SGI old and new AL<td>ok<td>ok<td>ok<td>ok<td>ok<td>ok
  <tr><td>OSS (Linux et al)<td>ok<td>ok<td>ok<td>ok<td>ok<td>ok
  <tr><td>Be<td>ok<td>ok<td>ok<td>ok<td>ok<td>untried
  <tr><td>Mac<td>ok<td>ok<td>ok<td>ok<td>ok<td>ok
  <tr><td>Windoze<td>ok<td>ok<td>ok<td>ok<td>not written<td>ok
  <tr><td>Sun<td>ok<td>ok<td>ok<td>ok<td>runs (*)<td>ok
  <tr><td>HPUX<td>untested<td>untested<td>untested<td>untested<td>untested<td>untried
  <tr><td>MkLinux/LinuxPPC<td>ok<td>ok<td>ok<td>ok<td>untested (**)<td>ok
  <tr><td>ALSA<td>untested<td>untested<td>untested<td>untested<td>untested<td>untested
</table>
<br>
<pre>
<font size="2">
(*) I can't find a microphone.
(**) Last I looked, recording was still not supported in this OS.
</font>
</pre>
<br>

<dl>
<dt>headers supported read/write
<dd>NeXT/Sun/DEC/AFsp
<dd>AIFF/AIFC
<dd>RIFF (Microsoft wave)
<dd>IRCAM (old style)
<dd>NIST-sphere
<dd>no header
<dt>headers supported read-only
<dd>8SVX (IFF), IRCAM Vax float, EBICSF, INRS, ESPS, 
<dd>SPPACK, ADC (OGI), AVR, VOC,
<dd>Sound Tools, Turtle Beach SMP, SoundFont 2.0, 
<dd>Sound Designer I and II, PSION, MAUD, Kurzweil 2000,
<dd>Tandy DeskMate, Gravis Ultrasound, ASF,
<dd>Comdisco SPW, Goldwave sample, omf, quicktime
<dd>Sonic Foundry, SBStudio II, Delusion digital, 
<dd>Digiplayer ST3, Farandole Composer WaveSample,
<dd>Ultratracker WaveSample, Sample Dump exchange, 
<dd>Yamaha SY85, SY99, and TX16, Covox v8, SPL, AVI, 
</dl>

<pre>
<font size="2">
Incomplete: OMF, AVI, ASF, QuickTime, SoundFont 2.0.
Not handled: Esignal, ILS, HTK, DVSM, SoundEdit.
Handled by Snd: Mus10, IEEE text, HCOM, various compression schemes.
</font>
</pre>

<h2><a name="lowerlevels">Lower Levels</a></h2>

<p>If you'd like to go below the "sound" interface described above,
the following functions are exported from sndlib.  You need to remember
to call sndlib_initialize (or the underlying initializers) before
using these functions (this is normally done for you by the various
"sound_" functions).</p>
<pre>
<font size="2">
  int mus_read_header (char *name)
  int mus_write_header (char *name, int type, int in_srate, int in_chans, int loc, int size, int format, char *comment, int len)
  int mus_update_header (char *name, int type, int size, int srate, int format, int chans, int loc)
  int mus_header_writable(int type, int format)
</font>
</pre>

These read and write a sound file's header.  The <i>loc</i> parameter is normally 0 (the data location depends
on many things -- you'd normally write the header, then use mus_header_data_location to get the resultant
data location). <i>len</i> is the length (bytes) of <i>comment</i>.  <i>mus_update_header</i> is normally
used only to set the file size after the sound has been written.  <i>mus_header_writable</i> returns
1 if the given combination of header type and data format can be handled by sndlib.  If you already have
the file descriptor (as returned by open), the corresponding lower level calls are:</p>
<pre>
<font size="2">
  int mus_read_header_with_fd (int fd)
  int mus_write_header_with_fd (int fd, int type, int in_srate, int in_chans, int loc, int size, int format, char *comment, int len)
  int mus_update_header_with_fd (int fd, int type, int siz)
</font>
</pre>

<p>Once mus_read_header has been called, the data in it can be accessed through:</p>
<pre>
<font size="2">
  int mus_header_samples (void)              samples 
  int mus_header_frames (void)               frames (samples / chans)
  int mus_header_data_location (void)        location of data (bytes)
  int mus_header_chans (void)                channels
  int mus_header_srate (void)                srate
  int mus_header_type (void)                 header type (i.e. aiff, wave, etc)  (see sndlib.h)
  int mus_header_format (void)               data format (see sndlib.h)
  int mus_header_distributed (void)          true if header info is scattered around in the file
  int mus_header_comment_start (void)        comment start location (if any) (bytes)
  int mus_header_comment_end (void)          comment end location
  int mus_header_aux_comment_start (int n)   if multiple comments, nth start location
  int mus_header_aux_comment_end (int n)     if multiple comments, nth end location
  int mus_header_type_specifier (void)       original (header-specific) type ID
  int mus_header_bits_per_sample (void)      sample width in bits
  int mus_true_file_length (void)            true (lseek) file length
  int mus_header_format2bytes (void)         sample width in bytes
  int mus_header_aiff_p(void)                is header actually old-style AIFF, not AIFC
  char *mus_header_type2string (int type)    sound_type_name
  char *mus_header_data_format2string (int format) sound_format_name
</font>
</pre>

<p>Various less useful header fields are accessible:
see headers.c or sndlib.h for details.  The next functions handle various IO calls:</p>
<pre>
<font size="2">
  int mus_open_read (char *arg)              open file read-only
  int mus_probe_file (char *arg)             return 1 if file exists
  int mus_open_write (char *arg)             open file read-write, creating it if necessary, else truncating
  int mus_create (char *arg)                 create file
  int mus_reopen_write (char *arg)           open file read-write without changing anything
  int mus_close (int fd)                     close file
  long mus_seek (int tfd, long offset, int origin) 
  int mus_seek_frame (int tfd, int frame)    go to a specific frame in file
  int mus_read (int fd, int beg, int end, int chans, int **bufs)
  int mus_read_chans (int fd, int beg, int end, int chans, int **bufs, int *cm)
  int mus_read_any (int tfd, int beg, int chans, int nints, int **bufs, int *cm)
  int mus_write_zeros (int tfd, int num)
  int mus_write (int tfd, int beg, int end, int chans, int **bufs)
  int mus_float_sound (char *charbuf, int samps, int charbuf_format, float *buffer)
  int mus_unshort_sound (short *in_buf, int samps, int new_format, char *out_buf)
  int sound_max_amp (char *ifile, int *vals)
</font>
</pre>

<p>If you're trying to deal with various data types yourself,
the following functions may be useful; they perform various
byte-order-aware type conversions:</p>
<pre>
<font size="2">
  void mus_set_big_endian_int (unsigned char *j, int x)
  int mus_big_endian_int (unsigned char *inp)
  void mus_set_little_endian_int (unsigned char *j, int x)
  int mus_little_endian_int (unsigned char *inp)
  int mus_uninterpreted_int (unsigned char *inp)
  void mus_set_big_endian_float (unsigned char *j, float x)
  float mus_big_endian_float (unsigned char *inp)
  void mus_set_little_endian_float (unsigned char *j, float x)
  float mus_little_endian_float (unsigned char *inp)
  void mus_set_big_endian_short (unsigned char *j, short x)
  short mus_big_endian_short (unsigned char *inp)
  void mus_set_little_endian_short (unsigned char *j, short x)
  short mus_little_endian_short (unsigned char *inp)
  void mus_set_big_endian_unsigned_short (unsigned char *j, unsigned short x)
  unsigned short mus_big_endian_unsigned_short (unsigned char *inp)
  void mus_set_little_endian_unsigned_short (unsigned char *j, unsigned short x)
  unsigned short mus_little_endian_unsigned_short (unsigned char *inp)
  double mus_little_endian_double (unsigned char *inp)
  double mus_big_endian_double (unsigned char *inp)
  void mus_set_big_endian_double (unsigned char *j, double x)
  void mus_set_little_endian_double (unsigned char *j, double x)
  unsigned int mus_big_endian_unsigned_int (unsigned char *inp)
  unsigned int mus_little_endian_unsigned_int (unsigned char *inp)
</font>
</pre>

<p>Finally, a couple functions are provided to read and write sound files
to and from arrays:</p>
<pre>
<font size="2">
  int mus_file2array (char *filename, int chan, int start, int samples, int *array)
  int mus_array2file (char *filename, int *ddata, int len, int srate, int channels)
</font>
</pre>

<h2><a name="sndlibguile">Sndlib and Guile</a></h2>

<p>Much of sndlib is accessible at run time in any program that has Guile;
the modules sndlib2scm and clm2scm tie most of the library into Scheme
making it possible to call the library functions from Guile.  The documentation
is scattered around, unfortunately: the clm side is in clm.html and extsnd.html with many
examples in Snd's examp.scm.  Most of these are obvious translations of the
constants and functions described above into Scheme. 
</p>
<pre>
<font size="2">
  snd-16-linear   snd-16-linear-little-endian  snd-24-linear   snd-24-linear-little-endian
  snd-32-float    snd-32-float-little-endian   snd-32-linear   snd-32-linear-little-endian
  snd-64-double   snd-64-double-little-endian  snd-8-alaw      snd-8-linear
  snd-8-mulaw     snd-8-unsigned               snd-16-unsigned snd-16-unsigned-little-endian

  next-sound-file nist-sound-file aiff-sound-file ircam-sound-file raw-sound-file riff-sound-file

  sndlib-default-device    sndlib-read-write-device   sndlib-line-out-device
  sndlib-line-in-device    sndlib-microphone-device   sndlib-speakers-device
  sndlib-dac-out-device    sndlib-adat-in-device      sndlib-aes-in-device
  sndlib-digital-in-device sndlib-digital-out-device  sndlib-adat-out-device
  sndlib-aes-out-device    sndlib-dac-filter-device   sndlib-mixer-device
  sndlib-line1-device      sndlib-line2-device        sndlib-line3-device
  sndlib-aux-input-device  sndlib-cd-in-device        sndlib-aux-output-device
  sndlib-spdif-in-device   sndlib-spdif-out-device    

  sndlib-amp-field     sndlib-srate-field   sndlib-channel-field
  sndlib-format-field  sndlib-device-field  sndlib-imix-field
  sndlib-igain-field   sndlib-reclev-field  sndlib-pcm-field
  sndlib-pcm2-field    sndlib-ogain-field   sndlib-line-field
  sndlib-mic-field     sndlib-line1-field   sndlib-line2-field
  sndlib-line3-field   sndlib-synth-field   sndlib-bass-field
  sndlib-treble-field  sndlib-cd-field      

  sound-samples (filename)             samples of sound according to header (can be incorrect)
  sound-frames (filename)              frames of sound according to header (can be incorrect)
  sound-duration (filename)            duration of sound in seconds
  sound-datum-size (filename)          bytes per sample
  sound-data-location (filename)       location of first sample (bytes)
  sound-chans (filename)               number of channels (samples are interleaved)
  sound-srate (filename)               sampling rate
  sound-header-type (filename)         header type (e.g. <i>aiff-sound-file</i>)
  sound-data-format(filename)          data format (e.g. <i>16-linear</i>)
  sound-length (filename)              true file length (bytes)
  sound-type-specifier (filename)      original header type identifier
  sound-max-amp(filename)              returns a vector of max amps and locations thereof

  sound-type-name (type)               e.g. "AIFF"
  sound-format-name (format)           e.g. "16-bit big endian linear"
  sound-comment (filename)             header comment, if any
  sound-bytes-per-sample (format)      bytes per sample

  audio-error ()                       returns error code indicated by preceding audio call
  audio-error-name(err)                string decription of error code
  describe-audio ()                    describe audio hardware state
  report-audio-state()                 return audio hardware state as a string
  set-oss-buffers (num size)           in Linux (OSS) sets the number and size of the OSS "fragments"
  audio-outputs(speaker, headphones, line-out) On the Sun, cause output to go to the chosen devices

  open-sound-input (filename)          open filename (a sound file) returning an integer ("fd" below)
  open-sound-output (filename srate chans data-format header-type comment)
                                       create a new sound file with the indicated attributes, return "fd"
  reopen-sound-output (filename chans data-format header-type data-location)
                                       reopen (without disturbing) filename, ready to be written
  close-sound-input (fd)               close sound file
  close-sound-output (fd bytes)        close sound file and update its length indication, if any
  read-sound (fd beg end chans sdata)  read data from sound file <i>fd</i> from frame beg to end
                                       <i>sdata</i> is a sound-data object that should be able to accomodate the read
  write-sound (fd beg end chans sdata) write data to sound file <i>fd</i>
  seek-sound (fd offset origin)        complicated -- see seek_sound above
  seek-sound-frame (fd frame)          move to <i>frame</i> in sound file <i>fd</i>

  open-audio-output (device srate chans format bufsize)
                                       open audio port <i>device</i> ready for output with the indicated attributes
  open-audio-input (device srate chans format bufsize)
                                       open audio port <i>device</i> ready for input with the indicated attributes
  write-audio (line sdata frames)      write <i>frames</i> of data from sound-data object <i>sdata</i> to port <i>line</i>
  read-audio (line sdata frames)       read <i>frames</i> of data into sound-data object <i>sdata</i> from port <i>line</i>
  close-audio (line)                   close audio port <i>line</i>
  read-audio-state (device field channel vals)
                                       read current state of <i>device</i>'s <i>field</i> -- see read_audio_state above.
  write-audio-state (device field channel vals)
                                       write new state for <i>device</i>'s <i>field</i> -- see write_audio_state above.
  audio-systems ()                     returns how many separate "systems" (soundcards) it can find.  To specify
                                       a particular system in the "device" parameters, add (ash system 16) to the device.
  save-audio-state ()                  write current audio state to .mixer or whatever
  restore-audio-state ()               read previously stored audio state

  make-sound-data (chans, frames)      return a sound-data object with <i>chans</i> arrays, each of length <i>frames</i>
  sound-data-ref (obj chan frame)      return (as a float) the sample in channel <i>chan</i> at location <i>frame</i>
  sound-data-set! (obj chan frame val) set <i>obj</i>'s sample at <i>frame</i> in <i>chan</i> to (the float) <i>val</i>
  sound-data? (obj)                    #t if <i>obj</i> is of type sound-data
  sound-data-length (obj)              length of each channel of data in <i>obj</i>
  sound-data-chans (obj)               number of channels of data in <i>obj</i>
  sound-data->vct (sdobj chan vobj)    place sound-data channel data in vct 
  vct->sound-data (vobj sdobj chan)    place vct data in sound-data

;;; this function prints header information
(define info
  (lambda (file)
    (string-append
     file
     ": chans: " (number-&gt;string (sound-chans file))
     ", srate: " (number-&gt;string (sound-srate file))
     ", " (sound-type-name (sound-header-type file))
     ", " (sound-format-name (sound-data-format file))
     ", len: " (number-&gt;string
                (/ (sound-samples file)
                   (* (sound-chans file) (sound-srate file)))))))

;;; this function reads the first 32 samples of a file, returning the 30th in channel 0
(define read-sample-30 
  (lambda (file)
    (let* ((fd (open-sound-input file))
	   (chans (sound-chans file))
	   (data (make-sound-data chans 32)))
      (read-sound fd 0 31 chans data)
      ;; we could use sound-data-&gt;vct here to return all the samples
      (let ((val (sound-data-ref data 0 29)))
	(close-sound-input fd)
	val))))

;;; here we get the microphone volume, then set it to .5
  (define vals (make-vector 32))
  (read-audio-state sndlib-microphone-device sndlib-amp-field 0 vals)
  (vector-ref vals 0)
  (vector-set! vals 0 .5)
  (write-audio-state sndlib-microphone-device sndlib-amp-field 0 vals)

;;; this function plays a sound (we're assuming that we can play 16-bit linear little-endian data)
(define play-sound
  (lambda (file)
    (let* ((sound-fd (open-sound-input file))
	   (chans (sound-chans file))
	   (frames (sound-frames file))
	   (bufsize 256)
	   (data (make-sound-data chans bufsize))
	   (bytes (* bufsize chans 2)))
      (read-sound sound-fd 0 (1- bufsize) chans data)
      (let ((audio-fd (open-audio-output sndlib-default-device (sound-srate file) chans snd-16-linear-little-endian bytes)))
	(do ((i 0 (+ i bufsize)))
	    ((>= i frames))
	  (write-audio audio-fd data bufsize)
	  (read-sound sound-fd 0 (1- bufsize) chans data))
	(close-sound-input sound-fd)
	(close-audio audio-fd)))))

</font>
</pre>

</body></html>