1 |
SndLib |
2 |
|
3 |
Bill Schottstaedt (bil@ccrma.stanford.edu) |
4 |
|
5 |
Contents |
6 |
|
7 |
Introduction |
8 |
Headers |
9 |
Data |
10 |
Hardware |
11 |
Music V |
12 |
Examples |
13 |
SndInfo |
14 |
SndPlay |
15 |
SndRecord |
16 |
AudInfo |
17 |
SndSine |
18 |
clmosc |
19 |
Other Examples |
20 |
How to Make Sndlib and the examples |
21 |
Current Status |
22 |
Lower Levels |
23 |
Sndlib and Guile |
24 |
|
25 |
Introduction |
26 |
|
27 |
The sound library is a collection of sound file and audio hardware handlers |
28 |
written in C and running currently on SGI (either audio library), NeXT, Sun, |
29 |
Be, OSS or ALSA (Linux and others), Mac, HPUX, MkLinux/LinuxPPC, and Windoze |
30 |
systems. It provides relatively straightforward access to many sound file |
31 |
headers and data types, and most of the features of the audio hardware. |
32 |
|
33 |
The following files make up sndlib: |
34 |
|
35 |
* io.c (read and write sound file data) |
36 |
* headers.c (read and write sound file headers) |
37 |
* audio.c (read and write sound hardware ports) |
38 |
* sound.c (provide slightly higher level access to the preceding files) |
39 |
* sndlib.h (header for the preceding files) |
40 |
* sndlib2scm.c and sndlib-strings.h (tie preceding into Guile) |
41 |
* clm.c and clm.h (Music V implementation) |
42 |
* clm2scm.c, vct.c and vct.h (tie clm.c into Guile) |
43 |
* old-sndlib.h (old names) |
44 |
|
45 |
In version 6.0, I changed most of the exported names to use the prefix "mus" |
46 |
or "SNDLIB" (to be more in line with the Gnu standard); see old-sndlib.h for |
47 |
backwards compatibility. |
48 |
|
49 |
To build sndlib (sndlib.so if possible, and sndlib.a): |
50 |
|
51 |
./configure |
52 |
make |
53 |
|
54 |
To install it, 'make install' -- I've tested this process in Linux, SGI, |
55 |
Sun, and NeXT. It could conceivably work elsewhere. For more details see How |
56 |
to Make Sndlib below. |
57 |
|
58 |
Headers |
59 |
|
60 |
Sound files have built-in descriptors known as headers. The following |
61 |
functions return the information in the header. In each case the argument to |
62 |
the function is the full file name of the sound file. |
63 |
|
64 |
int sound_samples (char *arg) /* samples of sound according to header (can be incorrect) */ |
65 |
int sound_frames (char *arg) /* samples per channel */ |
66 |
float sound_duration (char *arg) |
67 |
int sound_datum_size (char *arg) /* bytes per sample */ |
68 |
int sound_data_location (char *arg) /* location of first sample (bytes) */ |
69 |
int sound_chans (char *arg) /* number of channels (samples are interleaved) */ |
70 |
int sound_srate (char *arg) /* sampling rate */ |
71 |
int sound_header_type (char *arg) /* header type (aiff etc) */ |
72 |
int sound_data_format (char *arg) /* data format (alaw etc) */ |
73 |
int sound_original_format (char *arg) /* unmodified data format specifier */ |
74 |
char *sound_comment (char *arg) /* comment if any */ |
75 |
int sound_comment_start (char *arg) /* comment start (bytes) if any */ |
76 |
int sound_comment_end (char *arg) /* comment end (bytes) */ |
77 |
int sound_length (char *arg) /* true file length (for error checks) */ |
78 |
int sound_fact_samples (char *arg) /* compression scheme data */ |
79 |
int sound_distributed (char *arg) /* is header scattered around in sound file */ |
80 |
int sound_write_date (char *arg) /* bare (uninterpreted) file write date */ |
81 |
int sound_type_specifier (char *arg) /* original header type identifier */ |
82 |
int sound_align (char *arg) /* more compression data */ |
83 |
int sound_bits_per_sample(char *arg) /* bits per sample */ |
84 |
int sound_bytes_per_sample(int format) /* bytes per sample */ |
85 |
int sound_max_amp(char *arg, int *vals)/* return list of max-amp sample pairs */ |
86 |
int sound_aiff_p(char *arg) /* is it an old-style AIFF file (not AIFC) */ |
87 |
void initialize_sndlib(void) /* initialize everything */ |
88 |
int sound_aiff_p(char *arg) /* if sound's header actually an old-style AIFF (not AIFC) header */ |
89 |
|
90 |
The following can be used to provide user-understandable descriptions of the |
91 |
header type and the data format: |
92 |
|
93 |
char *sound_type_name(int type) /* "AIFF" etc */ |
94 |
char *sound_format_name(int format) /* "16-bit big endian linear" etc */ |
95 |
|
96 |
In all cases if an error occurs, -1 is returned; for information about the |
97 |
error use: |
98 |
|
99 |
int audio_error(void) /* returns error code indicated by preceding audio call */ |
100 |
char *audio_error_name(int err) /* gives string decription of error code */ |
101 |
|
102 |
Header data is cached internally, so the actual header is read only if it |
103 |
hasn't already been read, or the write date has changed. Loop points are |
104 |
also available, if there's interest. To go below the "sound" level, see |
105 |
headers.c -- once a header has been read, all the components that have been |
106 |
found can be read via functions such as mus_header_srate. |
107 |
|
108 |
Data |
109 |
|
110 |
The following functions provide access to sound file data: |
111 |
|
112 |
int open_sound_input (char *arg) |
113 |
int open_sound_output (char *arg, int srate, int chans, int data_format, int header_type, char *comment) |
114 |
int reopen_sound_output (char *arg, int type, int format, int data_loc) |
115 |
int close_sound_input (int fd) |
116 |
int close_sound_output (int fd, int bytes_of_data) |
117 |
int read_sound (int fd, int beg, int end, int chans, int **bufs) |
118 |
int write_sound (int fd, int beg, int end, int chans, int **bufs) |
119 |
int seek_sound (int fd, long offset, int origin) |
120 |
int seek_sound_frame (int fd, int frame) |
121 |
int mus_float_sound(char *charbuf, int samps, int charbuf_format, float *buffer) |
122 |
|
123 |
open_sound_input opens arg for reading. Most standard uncompressed formats |
124 |
are readable. This function returns the associated file number, or -1 upon |
125 |
failure. |
126 |
|
127 |
close_sound_input closes an open sound file. Its argument is the integer |
128 |
returned by open_sound_input. |
129 |
|
130 |
open_sound_output opens arg, setting its sampling rate to be srate, number |
131 |
of channels to chans, data format to data_format (see sndlib.h for these |
132 |
types: SNDLIB_16_LINEAR, for example, means 16-bit 2's complement big endian |
133 |
fractions), header type to header_type (AIFF for example; the available |
134 |
writable header types are AIFF_sound_file, RIFF_sound_file ('wave'), |
135 |
NeXT_sound_file, and IRCAM_sound_file), and comment (if any) to comment. The |
136 |
header is not considered complete without an indication of the data size, |
137 |
but since this is rarely known in advance, it is supplied when the sound |
138 |
file is closed. This function returns the associated file number. |
139 |
|
140 |
close_sound_output first updates the file's header to reflect the final data |
141 |
size bytes_of_data, then closes the file. The argument fd is the integer |
142 |
returned by open_sound_output. |
143 |
|
144 |
read_sound reads data from the file indicated by fd, placing data in the |
145 |
array obufs as 32-bit integers in the host's byte order. chans determines |
146 |
how many arrays of ints are in obufs, which is filled by read_sound from its |
147 |
index beg to end with zero padding if necessary. See the sndplay example |
148 |
below if this is not obvious. |
149 |
|
150 |
write_sound writes data to the file indicated by fd, starting for each of |
151 |
chans channels in obufs at beg and ending at end. |
152 |
|
153 |
seek_sound moves the read or write position for the file indicated by fd to |
154 |
offset given the origin indication (both treated as in lseek). The new |
155 |
actual position attained is returned. In both cases (the returned value and |
156 |
offset), the output datum size is considered to be 2, no matter what it |
157 |
really is. That is, use byte positions as if you were always reading and |
158 |
writing 16-bit data, and seek_sound will compensate if its actually 32-bit |
159 |
floats or whatever. Since this is impossible to understand, there's also |
160 |
seek_sound_frame which moves to the indicated frame. |
161 |
|
162 |
mus_float_sound takes a buffer full of sound data in some format |
163 |
(charbuf_format and returns the data as a buffer full of (unscaled) floats. |
164 |
|
165 |
Hardware |
166 |
|
167 |
The following functions provide access to audio harware. If an error occurs, |
168 |
they return -1, and the audio_error functions can be used to find out what |
169 |
went wrong. |
170 |
|
171 |
int initialize_audio(void) |
172 |
void save_audio_state(void) |
173 |
void restore_audio_state(void) |
174 |
void describe_audio_state(void) |
175 |
char *report_audio_state(void) |
176 |
int open_audio_output(int dev, int srate, int chans, int format, int size) |
177 |
int open_audio_input(int dev, int srate, int chans, int format, int size) |
178 |
int write_audio(int line, char *buf, int bytes) |
179 |
int close_audio(int line) |
180 |
int read_audio(int line, char *buf, int bytes) |
181 |
int read_audio_state(int dev, int field, int chan, float *val) |
182 |
int write_audio_state(int dev, int field, int chan, float *val) |
183 |
int audio_systems(void) |
184 |
char *audio_system_name(int system) |
185 |
void setup_dsps(int cards, int *dsps, int *mixers) /* OSS only */ |
186 |
|
187 |
initialize_audio takes care of any necessary intialization. |
188 |
|
189 |
save_audio_state saves the current audio hardware state. |
190 |
|
191 |
restore_audio_state restores the audio hardware to the last saved state. |
192 |
|
193 |
describe_audio_state prints to stdout a description of the current state of |
194 |
the audio hardware. report_audio_state returns the same description as a |
195 |
string. |
196 |
|
197 |
audio_systems returns the number of separate and complete audio systems |
198 |
(soundcards essentially) that are available. audio_system_name returns some |
199 |
user-recognizable name for the given card. |
200 |
|
201 |
open_audio_input opens an audio port to read sound data (i.e. a microphone, |
202 |
line in, etc). The input device is dev (see sndlib.h for details; when in |
203 |
doubt, use SNDLIB_DEFAULT_DEVICE). The input sampling rate is srate or as |
204 |
close as we can get to it. The number of input channels (if available) is |
205 |
chans. The input data format is format (when in doubt, use the macro |
206 |
SNDLIB_COMPATIBLE_FORMAT). And the input buffer size (if settable at all) is |
207 |
size (bytes). This function returns an integer to distinguish its port from |
208 |
others that might be in use. In this and other related functions, the device |
209 |
has an optional second portion that refers to the soundcard or system for |
210 |
that device. SNDLIB_AUDIO_SYSTEM(n) refers to the nth such card, so |
211 |
(SNDLIB_DAC_DEVICE | SNDLIB_AUDIO_SYSTEM(1)) is the 2nd card's dac (the |
212 |
default is system 0, the first card). |
213 |
|
214 |
open_audio_output opens an audio port to write date (i.e. speakers, line |
215 |
out, etc). The output device is dev (see sndlib.h). Its sampling rate is |
216 |
srate, number of channels chans, data format format, and buffer size size. |
217 |
This function returns the associated line number of the output port. |
218 |
|
219 |
close_audio closes the port (input or output) associated with line. |
220 |
|
221 |
read_audio reads sound data from line. The incoming bytes bytes of data are |
222 |
placed in buf. If no error was returned from open_audio_input, the data is |
223 |
in the format requested by that function with channels interleaved. |
224 |
|
225 |
write_audio writes bytes bytes of data in buf to the output port associated |
226 |
with line. This data is assumed to be in the format requested by |
227 |
open_audio_output with channels interleaved. |
228 |
|
229 |
read_audio_state and write_audio_state are complicated. They get and set the |
230 |
audio hardware state. The audio hardware is treated as a set of "systems" |
231 |
(sound cards) each of which has a set of "devices" (dacs, adcs, etc), with |
232 |
various "fields" that can be read or set (gain, channels active, etc). For |
233 |
example, a microphone is called the SNDLIB_MICROPHONE_DEVICE, and its |
234 |
hardware gain setting (if any) is called the SNDLIB_AMP_FIELD. All gains are |
235 |
considered to be linear between 0.0 and 1.0, so to set the microphone's |
236 |
first channel amplitude to .5 (that is, the gain of the signal before it |
237 |
reaches the analog-to-digital converter), |
238 |
|
239 |
float vals[1]; |
240 |
vals[0]=0.5; |
241 |
write_audio_state(SNDLIB_MICROPHONE_DEVICE,SNDLIB_AMP_FIELD,0,vals); |
242 |
|
243 |
Similarly |
244 |
|
245 |
read_audio_state(SNDLIB_MICROPHONE_DEVICE,SNDLIB_AMP_FIELD,0,vals); |
246 |
amp=vals[0]; |
247 |
|
248 |
returns the current gain in the float array vals. read_audio_state can also |
249 |
return a description of the currently available audio hardware. |
250 |
|
251 |
If a requested operation is not implemented, -1 is returned, and |
252 |
SNDLIB_AUDIO_ERROR is set to SNDLIB_CANT_READ or SNDLIB_CANT_WRITE. If an |
253 |
error occurs during the requested operation, -1 is returned, and |
254 |
SNDLIB_AUDIO_ERROR is set to SNDLIB_READ_ERROR or SNDLIB_WRITE_ERROR. If |
255 |
some operation cannot be performed on the current hardware, -1 is returned |
256 |
and SNDLIB_AUDIO_ERROR tries to indicate what portion of the requested |
257 |
operation is impossible (SNDLIB_SRATE_NOT_AVAILABLE, |
258 |
SNDLIB_FORMAT_NOT_AVAILABLE, and so on). |
259 |
|
260 |
Systems |
261 |
|
262 |
Each separate sound card is called a system, accessible via the device |
263 |
argument through the macro SNDLIB_AUDIO_SYSTEM(n). The count starts at 0 |
264 |
which is the default. The function audio_systems returns how many such cards |
265 |
are available. (Currently it returns more than one only on Linux systems |
266 |
with multiple sound cards). |
267 |
|
268 |
Devices |
269 |
|
270 |
Each audio system has a set of available devices. To find out what is |
271 |
available on a given system |
272 |
|
273 |
#define LIST_MAX_SIZE 32; |
274 |
float device_list[LIST_MAX_SIZE]; |
275 |
read_audio_state(SNDLIB_AUDIO_SYSTEM(0),SNDLIB_DEVICE_FIELD,LIST_MAX_SIZE,device_list); |
276 |
|
277 |
The list of available devices is returned in the device_list array, with the |
278 |
number of the devices as device_list[0]. The set of device identifiers is in |
279 |
sndlib.h (SNDLIB_LINE_IN_DEVICE for example). Two special devices are |
280 |
SNDLIB_MIXER_DEVICE and SNDLIB_DAC_FILTER_DEVICE. The latter refers to the |
281 |
low-pass filter often associated with a DAC. The former refers to a set of |
282 |
analog gain and tone controls often associated with a sound card. The |
283 |
individual gains are accessed through the various fields (described below). |
284 |
|
285 |
Fields |
286 |
|
287 |
The field argument in read-audio-state and write-audio-state selects one |
288 |
aspect of the given card's devices' controls. The simplest operations |
289 |
involve SNDLIB_AMP_FIELD and SNDLIB_SRATE_FIELD. The latter gets or sets the |
290 |
sampling rate of the device, and the former gets or sets the amplitude |
291 |
(between 0.0 and 1.0) of the specified channel of the device. The value to |
292 |
be set or returned is in the 0th element of the vals array. An example of |
293 |
reading the current microphone gain is given above. The meaning of the field |
294 |
argument can depend on which device it is applied to, so there is some |
295 |
complexity here. The channel argument usually selects which channel we are |
296 |
interested in, but in some cases it instead tells read-audio-state how big a |
297 |
returned list can get. A brief description of the fields: |
298 |
|
299 |
SNDLIB_AMP_FIELD gain or volume control (0.0 to 1.0) |
300 |
SNDLIB_SRATE_FIELD sampling rate |
301 |
SNDLIB_CHANNEL_FIELD active channels |
302 |
|
303 |
SNDLIB_BASS_FIELD, SNDLIB_TREBLE_FIELD mixer's tone control |
304 |
SNDLIB_LINE_FIELD mixer's line-in gain control |
305 |
SNDLIB_MIC_FIELD mixer's microphone gain control |
306 |
similarly for SNDLIB_IMIX_FIELD, SNDLIB_IGAIN_FIELD, |
307 |
SNDLIB_RECLEV_FIELD, SNDLIB_PCM_FIELD, SNDLIB_PCM2_FIELD, |
308 |
SNDLIB_OGAIN_FIELD, SNDLIB_LINE1_FIELD, |
309 |
SNDLIB_LINE2_FIELD, SNDLIB_LINE3_FIELD, SNDLIB_SYNTH_FIELD |
310 |
|
311 |
SNDLIB_FORMAT_FIELD return list of usable sound formats (e.g. SNDLIB_16_LINEAR) |
312 |
SNDLIB_DEVICE_FIELD return list of available devices (e.g. SNDLIB_MICROPHONE_DEVICE) |
313 |
|
314 |
MusicV |
315 |
|
316 |
clm.c and friends implement all the generators found in CLM, a common lisp |
317 |
music V implementation, and clm2scm.c ties these into Guile (Scheme). The |
318 |
primary clm documentation (which describes both the Scheme and Common Lisp |
319 |
implementations) is clm.html found in clm-2.tar.gz alongside sndlib at |
320 |
ccrma-ftp. The simplest way to try these out is to load them into Snd; see |
321 |
extsnd.html and examp.scm in snd-3.tar.gz for more details. The C |
322 |
implementation is essentially the same as the two Lisp versions, but (as |
323 |
might be expected), works at a lower level, expecting the caller to handle |
324 |
garbage collection and so forth. The following briefly describes the C calls |
325 |
(see clm.h). |
326 |
|
327 |
clm.c implements a bunch of generators and sound IO handlers. Each generator |
328 |
has three associated functions, make-gen, gen, and gen_p; the first creates |
329 |
the generator (if needed), the second gets the next sample from the |
330 |
generator, and the last examines some pointer to determine if it is that |
331 |
kind of generator. In addition, there are a variety of "generic" functions |
332 |
that generators respond to: mus_free, for example, frees a generator, and |
333 |
mus_frequency returns its current frequency, if relevant. All generators are |
334 |
pointers to mus_any structs. Finally, CLM has two special data types: frame |
335 |
and mixer. A frame is an array that represents a multi-channel sample (that |
336 |
is, in a stereo file, at time 0.0, there are two samples, one for each |
337 |
channel). A mixer is a array of arrays that represents a set of input and |
338 |
output scalers, as if it were the current state of a mixing console's volume |
339 |
controls. A frame (a multi-channel input) can be "mixed" into a new frame (a |
340 |
multi-channel output) by passing it through a "mixer" (a matrix, the |
341 |
operation being a matrix multiply). |
342 |
|
343 |
* oscil -- generate a sine wave. |
344 |
o mus_any *mus_make_oscil (float freq, float phase) |
345 |
o float mus_oscil (mus_any *o, float fm, float pm) |
346 |
o int mus_oscil_p (mus_any *ptr) |
347 |
|
348 |
mus_any *osc; |
349 |
init_mus_module(); |
350 |
osc = mus_make_oscil(440.0,0.0); |
351 |
if (oscil_p(osc)) fprintf(stderr,"%.3f, %.3f ",.1 * mus_oscil(osc,0.0,0.0),mus_frequency(osc)); |
352 |
mus_free(osc); |
353 |
|
354 |
The other generators are: |
355 |
|
356 |
* sum_of_cosines -- generate a pulse train made up of cosines |
357 |
* delay -- a delay line with optional interpolation |
358 |
* tap -- read delay line |
359 |
* comb -- comb filter |
360 |
* notch -- notch filter |
361 |
* all_pass -- all pass filter |
362 |
* table_lookup -- interpolating table lookup |
363 |
* sawtooth_wave, triangle_wave, pulse_train, square_wave |
364 |
* rand -- white noise (a step function) |
365 |
* rand-interp -- interpolating noise |
366 |
* asymmetric_fm -- a variety of FM |
367 |
* one_zero, two_zero, one_pole, two_pole -- basic filters |
368 |
* formant -- create a formant region (two poles, two zeros) |
369 |
* sine_summation -- another way to create sine waves |
370 |
* filter, fir_filter, iir_filter -- direct form filters of any order |
371 |
* wave_train -- sequence of possibly overlapping waves |
372 |
* buffer -- a way to handle block processing in the generator world |
373 |
* env -- envelopes |
374 |
* waveshape -- waveshaping |
375 |
* readin, file2sample, file2frame, in_any -- file sample input |
376 |
* locsig, sample2file, frame2file, out_any -- file sample output |
377 |
* src -- sampling rate conversion |
378 |
* granulate -- granular synthesis |
379 |
* convolve -- convolution |
380 |
|
381 |
Some useful functions provided by clm.c are: |
382 |
|
383 |
* float mus_radians2hz(float rads) -- convert radians/sample to |
384 |
cycles/sec. |
385 |
* float mus_hz2radians(float hz) -- and the reverse. |
386 |
* float mus_degrees2radians(float deg) -- convert degrees to radians. |
387 |
* float mus_radians2degrees(float rads) -- and the reverse. |
388 |
* float mus_srate(void) -- current sampling rate |
389 |
* float mus_set_srate(float rate) -- set current sampling rate |
390 |
* float mus_ring_modulate(float sig1, float sig2) -- multiply sig1 by |
391 |
sig2 |
392 |
* float mus_amplitude_modulate(float s1, float s2, float s3) -- AM |
393 |
* float mus_contrast_enhancement(float sig, float index) |
394 |
* float mus_dot_product(float *data1, float *data2, int size) |
395 |
* void mus_clear_array(float *arr, int size) |
396 |
* float mus_array_interp(float *wave, float phase, int size) |
397 |
* float mus_polynomial(float *coeffs, float x, int ncoeffs); |
398 |
* void mus_multiply_arrays(float *data, float *window, int len); |
399 |
* void mus_rectangular2polar(float *rl, float *im, int size); |
400 |
* void mus_spectrum(float *rdat, float *idat, float *window, int n, int |
401 |
type) |
402 |
* void mus_fft(float *rl, float *im, int n, int isign, int ipow) |
403 |
* float *mus_make_fft_window(int size, int type, float beta) |
404 |
* void mus_convolution(float* rl1, float* rl2, int n, int ipow) |
405 |
* float *mus_partials2wave(float *partial_data, int partials, float |
406 |
*table, int table_size, int normalize) |
407 |
* float *mus_phasepartials2wave(float *partial_data, int partials, float |
408 |
*table, int table_size, int normalize) |
409 |
|
410 |
and various others -- see clm.h. |
411 |
|
412 |
The more useful generic functions are: |
413 |
|
414 |
* int mus_free(mus_any *ptr) |
415 |
* char *mus_describe(mus_any *gen) |
416 |
* float mus_phase(mus_any *gen) |
417 |
* float mus_set_phase(mus_any *gen, float val) |
418 |
* float mus_set_frequency(mus_any *gen, float val) |
419 |
* float mus_frequency(mus_any *gen) |
420 |
* int mus_length(mus_any *gen) |
421 |
* int mus_set_length(mus_any *gen, int len) |
422 |
* float *mus_data(mus_any *gen) |
423 |
* float *mus_set_data(mus_any *gen, float *data) |
424 |
* char *mus_name(mus_any *ptr) |
425 |
* int mus_type(mus_any *ptr) |
426 |
* float mus_scaler(mus_any *gen) |
427 |
* float mus_set_scaler(mus_any *gen, float val) |
428 |
|
429 |
Before using any of these functions, call init_mus_module. Errors are |
430 |
reported through mus_error which can be redirected or muffled. See clm2scm.c |
431 |
for an example. |
432 |
|
433 |
------------------------------------------------------------------------ |
434 |
|
435 |
Examples |
436 |
|
437 |
In the following examples I've omitted the usual garrulous C-header gab and |
438 |
other inessential stuff. The full program code is available as noted below. |
439 |
|
440 |
SndInfo |
441 |
|
442 |
This program prints out a description of a sound file (sndinfo.c). |
443 |
|
444 |
int main(int argc, char *argv[]) |
445 |
{ |
446 |
int fd,chans,srate,samples; |
447 |
float length; |
448 |
time_t date; |
449 |
char *comment; |
450 |
char timestr[64]; |
451 |
initialize_sndlib(); |
452 |
fd = mus_open_read(argv[1]); /* see if it exists */ |
453 |
if (fd != -1) |
454 |
{ |
455 |
close(fd); |
456 |
date = sound_write_date(argv[1]); |
457 |
srate = sound_srate(argv[1]); |
458 |
chans = sound_chans(argv[1]); |
459 |
samples = sound_samples(argv[1]); |
460 |
comment = sound_comment(argv[1]); |
461 |
length = (float)samples / (float)(chans * srate); |
462 |
strftime(timestr,64,"%a %d-%b-%y %H:%M %Z",localtime(&date)); |
463 |
fprintf(stdout,"%s:\n srate: %d\n chans: %d\n length: %f\n", |
464 |
argv[1],srate,chans,length); |
465 |
fprintf(stdout," type: %s\n format: %s\n written: %s\n comment: %s\n", |
466 |
sound_type_name(sound_header_type(argv[1])), |
467 |
sound_format_name(sound_data_format(argv[1])), |
468 |
timestr,comment); |
469 |
} |
470 |
else |
471 |
fprintf(stderr,"%s: %s\n",argv[1],strerror(errno)); |
472 |
return(0); |
473 |
} |
474 |
|
475 |
SndPlay |
476 |
|
477 |
This code plays a sound file (sndplay.c): |
478 |
|
479 |
int main(int argc, char *argv[]) |
480 |
{ |
481 |
int fd,afd,i,j,n,k,chans,srate,frames,outbytes; |
482 |
int **bufs; |
483 |
short *obuf; |
484 |
initialize_sndlib(); |
485 |
fd = open_sound_input(argv[1]); |
486 |
if (fd != -1) |
487 |
{ |
488 |
chans = sound_chans(argv[1]); |
489 |
srate = sound_srate(argv[1]); |
490 |
frames = sound_frames(argv[1]); |
491 |
outbytes = BUFFER_SIZE * chans * 2; |
492 |
bufs = (int **)calloc(chans,sizeof(int *)); |
493 |
for (i=0;i<chans;i++) bufs[i] = (int *)calloc(BUFFER_SIZE,sizeof(int)); |
494 |
obuf = (short *)calloc(BUFFER_SIZE * chans,sizeof(short)); |
495 |
afd = open_audio_output(SNDLIB_DEFAULT_DEVICE,srate,chans,SNDLIB_COMPATIBLE_FORMAT,outbytes); |
496 |
if (afd != -1) |
497 |
{ |
498 |
for (i=0;i<frames;i+=BUFFER_SIZE) |
499 |
{ |
500 |
read_sound(fd,0,BUFFER_SIZE-1,chans,bufs); |
501 |
for (k=0,j=0;k<BUFFER_SIZE;k++,j+=chans) |
502 |
for (n=0;n<chans;n++) obuf[j+n] = bufs[n][k]; |
503 |
write_audio(afd,(char *)obuf,outbytes); |
504 |
} |
505 |
close_audio(afd); |
506 |
} |
507 |
close_sound_input(fd); |
508 |
for (i=0;i<chans;i++) free(bufs[i]); |
509 |
free(bufs); |
510 |
free(obuf); |
511 |
} |
512 |
else |
513 |
fprintf(stderr,"%s: %s ",argv[1],audio_error_name(audio_error())); |
514 |
return(0); |
515 |
} |
516 |
|
517 |
SndRecord |
518 |
|
519 |
This code records a couple seconds of sound from a microphone. Input formats |
520 |
and sampling rates are dependent on available hardware, so in a "real" |
521 |
program, you'd use read_audio_state to find out what was available, then |
522 |
float-sound to turn that data into a stream of floats. You'd also provide, |
523 |
no doubt, some whizzy user interface to turn the thing off. (sndrecord.c) |
524 |
|
525 |
int main(int argc, char *argv[]) |
526 |
{ |
527 |
int fd,afd,i,err; |
528 |
short *ibuf; |
529 |
#if MACOS |
530 |
argc = ccommand(&argv); |
531 |
#endif |
532 |
afd = -1; |
533 |
initialize_sndlib(); |
534 |
fd = open_sound_output(argv[1],22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by sndrecord"); |
535 |
if (fd != -1) |
536 |
{ |
537 |
ibuf = (short *)calloc(BUFFER_SIZE,sizeof(short)); |
538 |
afd = open_audio_input(SNDLIB_MICROPHONE_DEVICE,22050,1,SNDLIB_16_LINEAR,BUFFER_SIZE); |
539 |
if (afd != -1) |
540 |
{ |
541 |
for (i=0;i<10;i++) /* grab 10 buffers of input */ |
542 |
{ |
543 |
err = read_audio(afd,(char *)ibuf,BUFFER_SIZE*2); |
544 |
if (err != SNDLIB_NO_ERROR) {fprintf(stderr,audio_error_name(audio_error())); break;} |
545 |
write(fd,ibuf,BUFFER_SIZE*2); |
546 |
} |
547 |
close_audio(afd); |
548 |
} |
549 |
else |
550 |
fprintf(stderr,audio_error_name(audio_error())); |
551 |
close_sound_output(fd,BUFFER_SIZE*10*2); |
552 |
free(ibuf); |
553 |
} |
554 |
else |
555 |
fprintf(stderr,"%s: %s ",argv[1],strerror(errno)); |
556 |
return(0); |
557 |
} |
558 |
|
559 |
AudInfo |
560 |
|
561 |
This program describes the current audio harware state (audinfo.c): |
562 |
|
563 |
int main(int argc, char *argv[]) |
564 |
{ |
565 |
initialize_sndlib(); |
566 |
describe_audio_state(); |
567 |
return(0); |
568 |
} |
569 |
|
570 |
SndSine |
571 |
|
572 |
This program writes a one channel NeXT/Sun sound file containing a sine wave |
573 |
at 440 Hz. |
574 |
|
575 |
int main(int argc, char *argv[]) |
576 |
{ |
577 |
int fd,i,k,frames; |
578 |
float phase,incr; |
579 |
int *obuf[1]; |
580 |
initialize_sndlib(); |
581 |
fd = open_sound_output(argv[1],22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by sndsine"); |
582 |
if (fd != -1) |
583 |
{ |
584 |
frames = 22050; |
585 |
phase = 0.0; |
586 |
incr = 2*PI*440.0/22050.0; |
587 |
obuf[0] = (int *)calloc(BUFFER_SIZE,sizeof(int)); |
588 |
k=0; |
589 |
for (i=0;i<frames;i++) |
590 |
{ |
591 |
obuf[0][k] = (int)(3276.8 * sin(phase)); /* amp = .1 */ |
592 |
phase += incr; |
593 |
k++; |
594 |
if (k == BUFFER_SIZE) |
595 |
{ |
596 |
write_sound(fd,0,BUFFER_SIZE-1,1,obuf); |
597 |
k=0; |
598 |
} |
599 |
} |
600 |
if (k>0) write_sound(fd,0,k-1,1,obuf); |
601 |
close_sound_output(fd,22050*mus_format2bytes(SNDLIB_16_LINEAR)); |
602 |
free(obuf[0]); |
603 |
} |
604 |
return(0); |
605 |
} |
606 |
|
607 |
clmosc |
608 |
|
609 |
This is program uses the clm.c oscillator and output functions to write the |
610 |
same sine wave as we wrote in SndSine. (Compile clm.c with -DHAVE_SNDLIB=1). |
611 |
|
612 |
int main(int argc, char *argv[]) |
613 |
{ |
614 |
int i; |
615 |
mus_any *osc,*op; |
616 |
initialize_sndlib(); |
617 |
init_mus_module(); |
618 |
osc = mus_make_oscil(440.0,0.0); |
619 |
op = mus_make_file_output("test.snd",22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by clmosc"); |
620 |
if (op) for (i=0;i<22050;i++) mus_sample2file(op,i,0,.1 * mus_oscil(osc,0.0,0.0)); |
621 |
mus_free(osc); |
622 |
if (op) mus_free(op); |
623 |
return(0); |
624 |
} |
625 |
|
626 |
Here is the fm-violin and a sample with-sound call: |
627 |
|
628 |
static int feq(float x, int i) {return(fabs(x-i)<.00001);} |
629 |
|
630 |
void fm_violin(float start, float dur, float frequency, float amplitude, float fm_index, mus_any *op) |
631 |
{ |
632 |
float pervibfrq = 5.0, |
633 |
ranvibfrq = 16.0, |
634 |
pervibamp = .0025, |
635 |
ranvibamp = .005, |
636 |
noise_amount = 0.0, |
637 |
noise_frq = 1000.0, |
638 |
gliss_amp = 0.0, |
639 |
fm1_rat = 1.0, |
640 |
fm2_rat = 3.0, |
641 |
fm3_rat = 4.0, |
642 |
reverb_amount = 0.0, |
643 |
degree = 0.0, |
644 |
distance = 1.0; |
645 |
float fm_env[] = {0.0, 1.0, 25.0, 0.4, 75.0, 0.6, 100.0, 0.0}; |
646 |
float amp_env[] = {0.0, 0.0, 25.0, 1.0, 75.0, 1.0, 100.0, 0.0}; |
647 |
float frq_env[] = {0.0, -1.0, 15.0, 1.0, 25.0, 0.0, 100.0, 0.0}; |
648 |
int beg = 0,end,easy_case = 0,npartials,i; |
649 |
float *coeffs,*partials; |
650 |
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; |
651 |
mus_any *carrier,*fmosc1,*fmosc2,*fmosc3,*ampf,*indf1,*indf2,*indf3,*fmnoi = NULL,*pervib,*ranvib,*frqf = NULL,*loc; |
652 |
beg = start * mus_srate(); |
653 |
end = beg + dur * mus_srate(); |
654 |
frq_scl = mus_hz2radians(frequency); |
655 |
maxdev = frq_scl * fm_index; |
656 |
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; |
657 |
logfrq = log(frequency); |
658 |
sqrtfrq = sqrt(frequency); |
659 |
index1 = maxdev * 5.0 / logfrq; if (index1 > M_PI) index1 = M_PI; |
660 |
index2 = maxdev * 3.0 * (8.5 - logfrq) / (3.0 + frequency * .001); if (index2 > M_PI) index2 = M_PI; |
661 |
index3 = maxdev * 4.0 / sqrtfrq; if (index3 > M_PI) index3 = M_PI; |
662 |
if (easy_case) |
663 |
{ |
664 |
npartials = floor(fm1_rat); |
665 |
if ((floor(fm2_rat)) > npartials) npartials = floor(fm2_rat); |
666 |
if ((floor(fm3_rat)) > npartials) npartials = floor(fm3_rat); |
667 |
npartials++; |
668 |
partials = (float *)CALLOC(npartials,sizeof(float)); |
669 |
partials[(int)(fm1_rat)] = index1; |
670 |
partials[(int)(fm2_rat)] = index2; |
671 |
partials[(int)(fm3_rat)] = index3; |
672 |
coeffs = mus_partials2polynomial(npartials,partials,1); |
673 |
norm = 1.0; |
674 |
} |
675 |
else norm = index1; |
676 |
carrier = mus_make_oscil(frequency,0.0); |
677 |
if (easy_case == 0) |
678 |
{ |
679 |
fmosc1 = mus_make_oscil(frequency * fm1_rat,0.0); |
680 |
fmosc2 = mus_make_oscil(frequency * fm2_rat,0.0); |
681 |
fmosc3 = mus_make_oscil(frequency * fm3_rat,0.0); |
682 |
} |
683 |
else fmosc1 = mus_make_oscil(frequency,0.0); |
684 |
ampf = mus_make_env(amp_env,4,amplitude,0.0,1.0,dur,0,0,NULL); |
685 |
indf1 = mus_make_env(fm_env,4,norm,0.0,1.0,dur,0,0,NULL); |
686 |
if (gliss_amp != 0.0) frqf = mus_make_env(frq_env,4,gliss_amp * frq_scl,0.0,1.0,dur,0,0,NULL); |
687 |
if (easy_case == 0) |
688 |
{ |
689 |
indf2 = mus_make_env(fm_env,4,index2,0.0,1.0,dur,0,0,NULL); |
690 |
indf3 = mus_make_env(fm_env,4,index3,0.0,1.0,dur,0,0,NULL); |
691 |
} |
692 |
pervib = mus_make_triangle_wave(pervibfrq,frq_scl * pervibamp,0.0); |
693 |
ranvib = mus_make_rand_interp(ranvibfrq,frq_scl * ranvibamp); |
694 |
if (noise_amount != 0.0) fmnoi = mus_make_rand(noise_frq,noise_amount * M_PI); |
695 |
loc = mus_make_locsig(degree,distance,reverb_amount,1,(mus_output *)op,NULL); |
696 |
for (i=beg;i<end;i++) |
697 |
{ |
698 |
if (noise_amount != 0.0) fuzz = mus_rand(fmnoi,0.0); |
699 |
if (frqf) vib = mus_env(frqf); else vib = 0.0; |
700 |
vib += mus_triangle_wave(pervib,0.0) + mus_rand_interp(ranvib,0.0); |
701 |
if (easy_case) |
702 |
modulation = mus_env(indf1) * mus_polynomial(coeffs,mus_oscil(fmosc1,vib,0.0),npartials); |
703 |
else |
704 |
modulation = mus_env(indf1) * mus_oscil(fmosc1,(fuzz + fm1_rat * vib),0.0) + |
705 |
mus_env(indf2) * mus_oscil(fmosc2,(fuzz + fm2_rat * vib),0.0) + |
706 |
mus_env(indf3) * mus_oscil(fmosc3,(fuzz + fm3_rat * vib),0.0); |
707 |
mus_locsig(loc,i,mus_env(ampf) * mus_oscil(carrier,vib + indfuzz * modulation,0.0)); |
708 |
} |
709 |
mus_free(pervib); |
710 |
mus_free(ranvib); |
711 |
mus_free(carrier); |
712 |
mus_free(fmosc1); |
713 |
mus_free(ampf); |
714 |
mus_free(indf1); |
715 |
if (fmnoi) mus_free(fmnoi); |
716 |
if (frqf) mus_free(frqf); |
717 |
if (easy_case == 0) |
718 |
{ |
719 |
mus_free(indf2); |
720 |
mus_free(indf3); |
721 |
mus_free(fmosc2); |
722 |
mus_free(fmosc3); |
723 |
} |
724 |
else |
725 |
FREE(partials); |
726 |
mus_free(loc); |
727 |
} |
728 |
|
729 |
int main(int argc, char *argv[]) |
730 |
{ |
731 |
mus_any *osc = NULL,*op = NULL; |
732 |
initialize_sndlib(); |
733 |
init_mus_module(); |
734 |
op = mus_make_file_output("test.snd",22050,1,SNDLIB_16_LINEAR,NeXT_sound_file,"created by clmosc"); |
735 |
if (op) |
736 |
{ |
737 |
fm_violin(0.0,20.0,440.0,.3,1.0,op); |
738 |
mus_free(op); |
739 |
} |
740 |
return(0); |
741 |
} |
742 |
|
743 |
The CLM version is v.ins, the Scheme version can be found in examp.scm. This |
744 |
code can be run: |
745 |
|
746 |
cc v.c -o vc -O3 -lm io.o headers.o audio.o sound.o clm.o -DLINUX |
747 |
|
748 |
where clm.o was compiled with -DHAVE_SNDLIB. |
749 |
|
750 |
------------------------------------------------------------------------ |
751 |
|
752 |
Other Examples |
753 |
|
754 |
The primary impetus for the sound library was the development of Snd and |
755 |
CLM, both of which are freely available. |
756 |
|
757 |
------------------------------------------------------------------------ |
758 |
|
759 |
How to Make Sndlib and the examples |
760 |
|
761 |
The Sndlib files can be used as separate modules or made into a library. The |
762 |
following sequence, for example, builds the sndplay program from scratch on |
763 |
an SGI: |
764 |
|
765 |
cc -c io.c -O -DSGI |
766 |
cc -c headers.c -O -DSGI |
767 |
cc -c audio.c -O -DSGI |
768 |
cc -c sound.c -O -DSGI |
769 |
cc sndplay.c -o sndplay -O -DSGI audio.o io.o headers.o sound.o -laudio -lm |
770 |
|
771 |
To make a library out of the sndlib files, first compile them as above, |
772 |
then: |
773 |
|
774 |
ld -r audio.o io.o headers.o sound.o -o sndlib.a |
775 |
cc sndplay.c -o sndplay -O -DSGI sndlib.a -laudio -lm |
776 |
|
777 |
The full sequence in Linux: |
778 |
|
779 |
cc -c io.c -O -DLINUX |
780 |
cc -c audio.c -O -DLINUX |
781 |
cc -c headers.c -O -DLINUX |
782 |
cc -c sound.c -O -DLINUX |
783 |
cc sndplay.c -o sndplay -O -DLINUX audio.o io.o headers.o sound.o -lm |
784 |
|
785 |
ld -r audio.o io.o headers.o sound.o -o sndlib.a |
786 |
cc sndplay.c -o sndplay -O -DLINUX sndlib.a -lm |
787 |
|
788 |
And on a NeXT: |
789 |
|
790 |
cc -c io.c -O -DNEXT |
791 |
cc -c audio.c -O -DNEXT |
792 |
cc -c headers.c -O -DNEXT |
793 |
cc -c sound.c -O -DNEXT |
794 |
cc sndplay.c -o sndplay -O -DNEXT audio.o io.o headers.o sound.o |
795 |
|
796 |
ld -r audio.o io.o headers.o sound.o -o sndlib.a |
797 |
cc sndplay.c -o sndplay -O -DNEXT sndlib.a |
798 |
|
799 |
Some similar sequence should work on a Sun (-DSOLARIS) or in HP-UX (-DHPUX). |
800 |
On a Mac, you need to make a project in CodeWarrior or whatever that |
801 |
includes all the basic sndlib .c and .h files (io.c, audio.c headers.c, |
802 |
sound.c, sndlib.h) as source files. Add the main program you're interested |
803 |
in (say sndplay.c), and "Make" the project. When the project is "Run", a |
804 |
dialog pops up asking for the arguments to the program (in this case the |
805 |
name of the file to be played, as a quoted string). In Windoze, you can use |
806 |
the C IDE (a project builder as in the Mac case), or run the compiler from a |
807 |
DOS shell. In the latter case, (in Watcom C) cl io.c -c -DWINDOZE to create |
808 |
the object files (io.obj and so on), then |
809 |
|
810 |
cl sndplay sndplay.obj -DWINDOZE audio.obj io.obj headers.obj sound.obj |
811 |
|
812 |
or in MS C |
813 |
|
814 |
cl -c io.c -DWINDOZE |
815 |
(and so on) |
816 |
cl sndplay.c -DWINDOZE sndplay.obj audio.obj io.obj headers.obj sound.obj winmm.lib |
817 |
|
818 |
or in gcc (available via the cygwin project) |
819 |
|
820 |
gcc -c io.c -DWINDOZE -O2 |
821 |
|
822 |
You can run the program from the DOS shell (sndplay oboe.snd or |
823 |
./sndplay.exe oboe.snd). On a Be, you can either build a project or use a |
824 |
makefile. The C compiler's name is mwcc. The tricky part here is that you |
825 |
have to find and include explicitly the Be audio library, libmedia.so -- |
826 |
look first in beos/system/lib. Or |
827 |
|
828 |
make sndplay |
829 |
|
830 |
To make sndlib into a shared library, |
831 |
|
832 |
ld -shared io.o headers.o audio.o sound.o -o sndlib.so |
833 |
|
834 |
(in Linux), or (to include the CLM module), |
835 |
|
836 |
ld -shared io.o headers.o audio.o sound.o clm.o -o sndlib.so |
837 |
|
838 |
------------------------------------------------------------------------ |
839 |
|
840 |
Current Status |
841 |
|
842 |
System SndSine SndInfo Audinfo SndPlay SndRecord CLM |
843 |
NeXT 68k ok ok ok ok ok ok |
844 |
NeXT Intel ok ok ok interruptionsruns (*) untried |
845 |
SGI old and new |
846 |
AL ok ok ok ok ok ok |
847 |
OSS (Linux et |
848 |
al) ok ok ok ok ok ok |
849 |
Be ok ok ok ok ok untried |
850 |
Mac ok ok ok ok ok ok |
851 |
|
852 |
Windoze ok ok ok ok not ok |
853 |
written |
854 |
Sun ok ok ok ok runs (*) ok |
855 |
HPUX untested untested untested untested untested untried |
856 |
|
857 |
MkLinux/LinuxPPCok ok ok ok untested ok |
858 |
(**) |
859 |
ALSA untested untested untested untested untested untested |
860 |
|
861 |
(*) I can't find a microphone. |
862 |
(**) Last I looked, recording was still not supported in this OS. |
863 |
|
864 |
headers supported read/write |
865 |
NeXT/Sun/DEC/AFsp |
866 |
AIFF/AIFC |
867 |
RIFF (Microsoft wave) |
868 |
IRCAM (old style) |
869 |
NIST-sphere |
870 |
no header |
871 |
headers supported read-only |
872 |
8SVX (IFF), IRCAM Vax float, EBICSF, INRS, ESPS, |
873 |
SPPACK, ADC (OGI), AVR, VOC, |
874 |
Sound Tools, Turtle Beach SMP, SoundFont 2.0, |
875 |
Sound Designer I and II, PSION, MAUD, Kurzweil 2000, |
876 |
Tandy DeskMate, Gravis Ultrasound, ASF, |
877 |
Comdisco SPW, Goldwave sample, omf, quicktime |
878 |
Sonic Foundry, SBStudio II, Delusion digital, |
879 |
Digiplayer ST3, Farandole Composer WaveSample, |
880 |
Ultratracker WaveSample, Sample Dump exchange, |
881 |
Yamaha SY85, SY99, and TX16, Covox v8, SPL, AVI, |
882 |
|
883 |
Incomplete: OMF, AVI, ASF, QuickTime, SoundFont 2.0. |
884 |
Not handled: Esignal, ILS, HTK, DVSM, SoundEdit. |
885 |
Handled by Snd: Mus10, IEEE text, HCOM, various compression schemes. |
886 |
|
887 |
Lower Levels |
888 |
|
889 |
If you'd like to go below the "sound" interface described above, the |
890 |
following functions are exported from sndlib. You need to remember to call |
891 |
sndlib_initialize (or the underlying initializers) before using these |
892 |
functions (this is normally done for you by the various "sound_" functions). |
893 |
|
894 |
int mus_read_header (char *name) |
895 |
int mus_write_header (char *name, int type, int in_srate, int in_chans, int loc, int size, int format, char *comment, int len) |
896 |
int mus_update_header (char *name, int type, int size, int srate, int format, int chans, int loc) |
897 |
int mus_header_writable(int type, int format) |
898 |
|
899 |
These read and write a sound file's header. The loc parameter is normally 0 |
900 |
(the data location depends on many things -- you'd normally write the |
901 |
header, then use mus_header_data_location to get the resultant data |
902 |
location). len is the length (bytes) of comment. mus_update_header is |
903 |
normally used only to set the file size after the sound has been written. |
904 |
mus_header_writable returns 1 if the given combination of header type and |
905 |
data format can be handled by sndlib. If you already have the file |
906 |
descriptor (as returned by open), the corresponding lower level calls are: |
907 |
|
908 |
int mus_read_header_with_fd (int fd) |
909 |
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) |
910 |
int mus_update_header_with_fd (int fd, int type, int siz) |
911 |
|
912 |
Once mus_read_header has been called, the data in it can be accessed |
913 |
through: |
914 |
|
915 |
int mus_header_samples (void) samples |
916 |
int mus_header_frames (void) frames (samples / chans) |
917 |
int mus_header_data_location (void) location of data (bytes) |
918 |
int mus_header_chans (void) channels |
919 |
int mus_header_srate (void) srate |
920 |
int mus_header_type (void) header type (i.e. aiff, wave, etc) (see sndlib.h) |
921 |
int mus_header_format (void) data format (see sndlib.h) |
922 |
int mus_header_distributed (void) true if header info is scattered around in the file |
923 |
int mus_header_comment_start (void) comment start location (if any) (bytes) |
924 |
int mus_header_comment_end (void) comment end location |
925 |
int mus_header_aux_comment_start (int n) if multiple comments, nth start location |
926 |
int mus_header_aux_comment_end (int n) if multiple comments, nth end location |
927 |
int mus_header_type_specifier (void) original (header-specific) type ID |
928 |
int mus_header_bits_per_sample (void) sample width in bits |
929 |
int mus_true_file_length (void) true (lseek) file length |
930 |
int mus_header_format2bytes (void) sample width in bytes |
931 |
int mus_header_aiff_p(void) is header actually old-style AIFF, not AIFC |
932 |
char *mus_header_type2string (int type) sound_type_name |
933 |
char *mus_header_data_format2string (int format) sound_format_name |
934 |
|
935 |
Various less useful header fields are accessible: see headers.c or sndlib.h |
936 |
for details. The next functions handle various IO calls: |
937 |
|
938 |
int mus_open_read (char *arg) open file read-only |
939 |
int mus_probe_file (char *arg) return 1 if file exists |
940 |
int mus_open_write (char *arg) open file read-write, creating it if necessary, else truncating |
941 |
int mus_create (char *arg) create file |
942 |
int mus_reopen_write (char *arg) open file read-write without changing anything |
943 |
int mus_close (int fd) close file |
944 |
long mus_seek (int tfd, long offset, int origin) |
945 |
int mus_seek_frame (int tfd, int frame) go to a specific frame in file |
946 |
int mus_read (int fd, int beg, int end, int chans, int **bufs) |
947 |
int mus_read_chans (int fd, int beg, int end, int chans, int **bufs, int *cm) |
948 |
int mus_read_any (int tfd, int beg, int chans, int nints, int **bufs, int *cm) |
949 |
int mus_write_zeros (int tfd, int num) |
950 |
int mus_write (int tfd, int beg, int end, int chans, int **bufs) |
951 |
int mus_float_sound (char *charbuf, int samps, int charbuf_format, float *buffer) |
952 |
int mus_unshort_sound (short *in_buf, int samps, int new_format, char *out_buf) |
953 |
int sound_max_amp (char *ifile, int *vals) |
954 |
|
955 |
If you're trying to deal with various data types yourself, the following |
956 |
functions may be useful; they perform various byte-order-aware type |
957 |
conversions: |
958 |
|
959 |
void mus_set_big_endian_int (unsigned char *j, int x) |
960 |
int mus_big_endian_int (unsigned char *inp) |
961 |
void mus_set_little_endian_int (unsigned char *j, int x) |
962 |
int mus_little_endian_int (unsigned char *inp) |
963 |
int mus_uninterpreted_int (unsigned char *inp) |
964 |
void mus_set_big_endian_float (unsigned char *j, float x) |
965 |
float mus_big_endian_float (unsigned char *inp) |
966 |
void mus_set_little_endian_float (unsigned char *j, float x) |
967 |
float mus_little_endian_float (unsigned char *inp) |
968 |
void mus_set_big_endian_short (unsigned char *j, short x) |
969 |
short mus_big_endian_short (unsigned char *inp) |
970 |
void mus_set_little_endian_short (unsigned char *j, short x) |
971 |
short mus_little_endian_short (unsigned char *inp) |
972 |
void mus_set_big_endian_unsigned_short (unsigned char *j, unsigned short x) |
973 |
unsigned short mus_big_endian_unsigned_short (unsigned char *inp) |
974 |
void mus_set_little_endian_unsigned_short (unsigned char *j, unsigned short x) |
975 |
unsigned short mus_little_endian_unsigned_short (unsigned char *inp) |
976 |
double mus_little_endian_double (unsigned char *inp) |
977 |
double mus_big_endian_double (unsigned char *inp) |
978 |
void mus_set_big_endian_double (unsigned char *j, double x) |
979 |
void mus_set_little_endian_double (unsigned char *j, double x) |
980 |
unsigned int mus_big_endian_unsigned_int (unsigned char *inp) |
981 |
unsigned int mus_little_endian_unsigned_int (unsigned char *inp) |
982 |
|
983 |
Finally, a couple functions are provided to read and write sound files to |
984 |
and from arrays: |
985 |
|
986 |
int mus_file2array (char *filename, int chan, int start, int samples, int *array) |
987 |
int mus_array2file (char *filename, int *ddata, int len, int srate, int channels) |
988 |
|
989 |
Sndlib and Guile |
990 |
|
991 |
Much of sndlib is accessible at run time in any program that has Guile; the |
992 |
modules sndlib2scm and clm2scm tie most of the library into Scheme making it |
993 |
possible to call the library functions from Guile. The documentation is |
994 |
scattered around, unfortunately: the clm side is in clm.html and extsnd.html |
995 |
with many examples in Snd's examp.scm. Most of these are obvious |
996 |
translations of the constants and functions described above into Scheme. |
997 |
|
998 |
snd-16-linear snd-16-linear-little-endian snd-24-linear snd-24-linear-little-endian |
999 |
snd-32-float snd-32-float-little-endian snd-32-linear snd-32-linear-little-endian |
1000 |
snd-64-double snd-64-double-little-endian snd-8-alaw snd-8-linear |
1001 |
snd-8-mulaw snd-8-unsigned snd-16-unsigned snd-16-unsigned-little-endian |
1002 |
|
1003 |
next-sound-file nist-sound-file aiff-sound-file ircam-sound-file raw-sound-file riff-sound-file |
1004 |
|
1005 |
sndlib-default-device sndlib-read-write-device sndlib-line-out-device |
1006 |
sndlib-line-in-device sndlib-microphone-device sndlib-speakers-device |
1007 |
sndlib-dac-out-device sndlib-adat-in-device sndlib-aes-in-device |
1008 |
sndlib-digital-in-device sndlib-digital-out-device sndlib-adat-out-device |
1009 |
sndlib-aes-out-device sndlib-dac-filter-device sndlib-mixer-device |
1010 |
sndlib-line1-device sndlib-line2-device sndlib-line3-device |
1011 |
sndlib-aux-input-device sndlib-cd-in-device sndlib-aux-output-device |
1012 |
sndlib-spdif-in-device sndlib-spdif-out-device |
1013 |
|
1014 |
sndlib-amp-field sndlib-srate-field sndlib-channel-field |
1015 |
sndlib-format-field sndlib-device-field sndlib-imix-field |
1016 |
sndlib-igain-field sndlib-reclev-field sndlib-pcm-field |
1017 |
sndlib-pcm2-field sndlib-ogain-field sndlib-line-field |
1018 |
sndlib-mic-field sndlib-line1-field sndlib-line2-field |
1019 |
sndlib-line3-field sndlib-synth-field sndlib-bass-field |
1020 |
sndlib-treble-field sndlib-cd-field |
1021 |
|
1022 |
sound-samples (filename) samples of sound according to header (can be incorrect) |
1023 |
sound-frames (filename) frames of sound according to header (can be incorrect) |
1024 |
sound-duration (filename) duration of sound in seconds |
1025 |
sound-datum-size (filename) bytes per sample |
1026 |
sound-data-location (filename) location of first sample (bytes) |
1027 |
sound-chans (filename) number of channels (samples are interleaved) |
1028 |
sound-srate (filename) sampling rate |
1029 |
sound-header-type (filename) header type (e.g. aiff-sound-file) |
1030 |
sound-data-format(filename) data format (e.g. 16-linear) |
1031 |
sound-length (filename) true file length (bytes) |
1032 |
sound-type-specifier (filename) original header type identifier |
1033 |
sound-max-amp(filename) returns a vector of max amps and locations thereof |
1034 |
|
1035 |
sound-type-name (type) e.g. "AIFF" |
1036 |
sound-format-name (format) e.g. "16-bit big endian linear" |
1037 |
sound-comment (filename) header comment, if any |
1038 |
sound-bytes-per-sample (format) bytes per sample |
1039 |
|
1040 |
audio-error () returns error code indicated by preceding audio call |
1041 |
audio-error-name(err) string decription of error code |
1042 |
describe-audio () describe audio hardware state |
1043 |
report-audio-state() return audio hardware state as a string |
1044 |
set-oss-buffers (num size) in Linux (OSS) sets the number and size of the OSS "fragments" |
1045 |
audio-outputs(speaker, headphones, line-out) On the Sun, cause output to go to the chosen devices |
1046 |
|
1047 |
open-sound-input (filename) open filename (a sound file) returning an integer ("fd" below) |
1048 |
open-sound-output (filename srate chans data-format header-type comment) |
1049 |
create a new sound file with the indicated attributes, return "fd" |
1050 |
reopen-sound-output (filename chans data-format header-type data-location) |
1051 |
reopen (without disturbing) filename, ready to be written |
1052 |
close-sound-input (fd) close sound file |
1053 |
close-sound-output (fd bytes) close sound file and update its length indication, if any |
1054 |
read-sound (fd beg end chans sdata) read data from sound file fd from frame beg to end |
1055 |
sdata is a sound-data object that should be able to accomodate the read |
1056 |
write-sound (fd beg end chans sdata) write data to sound file fd |
1057 |
seek-sound (fd offset origin) complicated -- see seek_sound above |
1058 |
seek-sound-frame (fd frame) move to frame in sound file fd |
1059 |
|
1060 |
open-audio-output (device srate chans format bufsize) |
1061 |
open audio port device ready for output with the indicated attributes |
1062 |
open-audio-input (device srate chans format bufsize) |
1063 |
open audio port device ready for input with the indicated attributes |
1064 |
write-audio (line sdata frames) write frames of data from sound-data object sdata to port line |
1065 |
read-audio (line sdata frames) read frames of data into sound-data object sdata from port line |
1066 |
close-audio (line) close audio port line |
1067 |
read-audio-state (device field channel vals) |
1068 |
read current state of device's field -- see read_audio_state above. |
1069 |
write-audio-state (device field channel vals) |
1070 |
write new state for device's field -- see write_audio_state above. |
1071 |
audio-systems () returns how many separate "systems" (soundcards) it can find |
1072 |
save-audio-state () write current audio state to .mixer or whatever |
1073 |
restore-audio-state () read previously stored audio state |
1074 |
|
1075 |
make-sound-data (chans, frames) return a sound-data object with chans arrays, each of length frames |
1076 |
sound-data-ref (obj chan frame) return (as a float) the sample in channel chan at location frame |
1077 |
sound-data-set! (obj chan frame val) set obj's sample at frame in chan to (the float) val |
1078 |
sound-data? (obj) #t if obj is of type sound-data |
1079 |
sound-data-length (obj) length of each channel of data in obj |
1080 |
sound-data-chans (obj) number of channels of data in obj |
1081 |
sound-data->vct (sdobj chan vobj) place sound-data channel data in vct |
1082 |
vct->sound-data (vobj sdobj chan) place vct data in sound-data |
1083 |
|
1084 |
;;; this function prints header information |
1085 |
(define info |
1086 |
(lambda (file) |
1087 |
(string-append |
1088 |
file |
1089 |
": chans: " (number->string (sound-chans file)) |
1090 |
", srate: " (number->string (sound-srate file)) |
1091 |
", " (sound-type-name (sound-header-type file)) |
1092 |
", " (sound-format-name (sound-data-format file)) |
1093 |
", len: " (number->string |
1094 |
(/ (sound-samples file) |
1095 |
(* (sound-chans file) (sound-srate file))))))) |
1096 |
|
1097 |
;;; this function reads the first 32 samples of a file, returning the 30th in channel 0 |
1098 |
(define read-sample-30 |
1099 |
(lambda (file) |
1100 |
(let* ((fd (open-sound-input file)) |
1101 |
(chans (sound-chans file)) |
1102 |
(data (make-sound-data chans 32))) |
1103 |
(read-sound fd 0 31 chans data) |
1104 |
;; we could use sound-data->vct here to return all the samples |
1105 |
(let ((val (sound-data-ref data 0 29))) |
1106 |
(close-sound-input fd) |
1107 |
val)))) |
1108 |
|
1109 |
;;; here we get the microphone volume, then set it to .5 |
1110 |
(define vals (make-vector 32)) |
1111 |
(read-audio-state sndlib-microphone-device sndlib-amp-field 0 vals) |
1112 |
(vector-ref vals 0) |
1113 |
(vector-set! vals 0 .5) |
1114 |
(write-audio-state sndlib-microphone-device sndlib-amp-field 0 vals) |
1115 |
|
1116 |
;;; this function plays a sound (we're assuming that we can play 16-bit linear little-endian data) |
1117 |
(define play-sound |
1118 |
(lambda (file) |
1119 |
(let* ((sound-fd (open-sound-input file)) |
1120 |
(chans (sound-chans file)) |
1121 |
(frames (sound-frames file)) |
1122 |
(bufsize 256) |
1123 |
(data (make-sound-data chans bufsize)) |
1124 |
(bytes (* bufsize chans 2))) |
1125 |
(read-sound sound-fd 0 (1- bufsize) chans data) |
1126 |
(let ((audio-fd (open-audio-output sndlib-default-device (sound-srate file) chans snd-16-linear-little-endian bytes))) |
1127 |
(do ((i 0 (+ i bufsize))) |
1128 |
((>= i frames)) |
1129 |
(write-audio audio-fd data bufsize) |
1130 |
(read-sound sound-fd 0 (1- bufsize) chans data)) |
1131 |
(close-sound-input sound-fd) |
1132 |
(close-audio audio-fd))))) |