📄 sound.c
字号:
/* sound.c -- nyquist sound data type *//* CHANGE LOG * -------------------------------------------------------------------- * 28Apr03 dm changes for portability and fix compiler warnings *//* define size_t: */#ifdef UNIX#include "sys/types.h"#endif #include <stdio.h>#include "xlisp.h"#include "sound.h"#include "falloc.h"#include "samples.h"#include "extern.h"#include "debug.h"/* #define GC_DEBUG */#ifdef GC_DEBUGsound_type sound_to_watch;#endif/* #define SNAPSHOTS */long table_memory;sample_block_type zero_block;sample_block_type internal_zero_block;snd_list_type zero_snd_list;xtype_desc sound_desc;LVAL a_sound;static void sound_xlfree();static void sound_xlprint();static void sound_xlsave();static unsigned char *sound_xlrestore();void sound_print_array(LVAL sa, long n);void sound_print_sound(sound_type s, long n);void sample_block_unref(sample_block_type sam);#ifdef SNAPSHOTSboolean sound_created_flag = false;#endif/* xlbadsr - report a "bad combination of sample rates" error */LVAL snd_badsr(void){ xlfail("bad combination of sample rates"); return NIL; /* never happens */}/* compute-phase - given a phase in radians, a wavetable specified as * the nominal pitch (in half steps), the table length, and the sample * rate, compute the sample number corresponding to the phase. This * routine makes it easy to initialize the table pointer at the beginning * of various oscillator implementations in Fugue. Note that the table * may represent several periods, in which case phase 360 is not the same * as 0. Also note that the phase increment is also computed and returned * through incr_ptr. */double compute_phase(phase, key, n, srate, new_srate, freq, incr_ptr) double phase; /* phase in degrees (depends on ANGLEBASE) */ double key; /* the semitone number of the table played at srate */ long n; /* number of samples */ double srate; /* the sample rate of the table */ double new_srate; /* sample rate of the result */ double freq; /* the desired frequency */ double *incr_ptr; /* the sample increment */{ double period = 1.0 / step_to_hz(key); /* convert phase to sample units */ phase = srate * period * (phase / (double) ANGLEBASE); /* phase is now in sample units; if phase is less than zero, then increase it by some number of sLength's to make it positive: */ if (phase < 0) phase += (((int) ((-phase) / n)) + 1) * n; /* if phase is longer than the sample length, wrap it by subtracting the integer part of the division by sLength: */ if (phase > n) phase -= ((int) (phase / n)) * n; /* Now figure the phase increment: to reproduce original pitch required incr = srate / new_srate. To get the new frequency, scale by freq / nominal_freq = freq * period: */ *incr_ptr = (srate / new_srate) * freq * period; return phase;}#ifndef GCBUGsnd_list_type gcbug_snd_list = 0;long blocks_to_watch_len = 0;sample_block_type blocks_to_watch[blocks_to_watch_max];void block_watch(long sample_block){ if (blocks_to_watch_len >= blocks_to_watch_max) { stdputstr("block_watch - no more space to save pointers\n"); return; } blocks_to_watch[blocks_to_watch_len++] = (sample_block_type) sample_block; nyquist_printf("block_watch - added %d = %x\n", (int)sample_block, (int)sample_block);}/* fetch_zeros -- the fetch function for appended zeros *//* * zeros are appended when the logical stop time exceeds the * (physical) terminate time. This fetch function is installed * by snd_list_terminate(). When appending zeros, we just return * a pointer to the internal_zero_block and increment current until * it reaches log_stop_cnt. Then we call snd_list_terminate() to * finish off the sound list. */void fetch_zeros(snd_susp_type susp, snd_list_type snd_list){ int len = MIN(susp->log_stop_cnt - susp->current, max_sample_block_len);/* nyquist_printf("fetch_zeros, lsc %d current %d len %d\n", susp->log_stop_cnt, susp->current, len); */ if (len < 0) { char error[80]; sprintf(error, "fetch_zeros susp %p (%s) len %d", susp, susp->name, len); xlabort(error); } if (len == 0) { /* we've reached the logical stop time */ /* nyquist_printf("fetch_zeros: reached the logical stop in %s cnt %d\n", susp->name, susp->log_stop_cnt); */ snd_list_terminate(snd_list); } else { snd_list->block_len = len; susp->current += len; }}/* sound_nth_block - fetch the address of the nth sample block of a sound *//* * NOTE: intended to be called from lisp. Lisp can then call block_watch * to keep an eye on the block. */long sound_nth_block(sound_type snd, long n){ long i; snd_list_type snd_list = snd->list; for (i = 0; i < n; i++) { if (i == 1) { gcbug_snd_list = snd_list; nyquist_printf("gcbug_snd_list = 0x%p\n", gcbug_snd_list); } if (!snd_list->block) return 0; snd_list = snd_list->u.next; } if (snd_list->block) return (long) snd_list->block; else return 0;}#endif/***************************************************************************** snd_list_create* Inputs:* snd_susp_type susp: A reference to the suspension* Result: snd_list_type* A newly-created sound list type* Effect: * Allocates and initializes a snd_list node:* block refcnt block_len susp logically_stopped* +--------+--------+-------+-------+---+* |////////| 1 | 0 | susp | F |* +--------+--------+-------+-------+---+****************************************************************************//* snd_list_create -- alloc and initialize a snd_list node *//**/snd_list_type snd_list_create(snd_susp_type susp){ snd_list_type snd_list; falloc_snd_list(snd_list, "snd_list_create"); snd_list->block = NULL; /* no block of samples */ snd_list->u.susp = susp; /* point to suspension */ snd_list->refcnt = 1; /* one ref */ snd_list->block_len = 0; /* no samples */ snd_list->logically_stopped = false;/* not stopped *//* nyquist_printf("snd_list_create => %p\n", snd_list);*/ return snd_list;}/***************************************************************************** sound_create* Inputs:* snd_susp_type susp: The suspension block to be used for this sound* time_type t0: The initial time for this sound* rate_type sr: The sampling rate for this sound* sample_type scale: The scaling factor for this sound* sample_block_type (*proc)(...): The get_next_sound method* Result: sound_type* * Effect: * Creates and initializes a sound type* Notes:* The MSDOS conditional is actually a test for ANSI headers; the* presence of float parameters means that an ANSI prototype and* a non-ANSI header are incompatible. Better solution would be* to ANSIfy source.****************************************************************************/sound_type last_sound = NULL;sound_type sound_create( snd_susp_type susp, time_type t0, rate_type sr, promoted_sample_type scale){ sound_type sound; falloc_sound(sound, "sound_create"); if (((long) sound) & 3) errputstr("sound not word aligned\n"); last_sound = sound; /* debug */ /* nyquist_printf("sound_create %p gets %g\n", sound, t0); */ sound->t0 = t0; sound->time = t0; sound->stop = MAX_STOP; sound->sr = sr; sound->current = 0; sound->scale = (float) scale; sound->list = snd_list_create(susp); sound->get_next = SND_get_first; sound->logical_stop_cnt = UNKNOWN; sound->table = NULL; sound->extra = NULL; /* nyquist_printf("sound_create susp %p snd_list %p\n", susp, sound->list); nyquist_printf("sound_create'd %p\n", sound); */#ifdef SNAPSHOTS sound_created_flag = true;#endif#ifdef GC_DEBUG if (sound == sound_to_watch) { nyquist_printf("Created watched sound\n"); watch_snd_list(sound->list); }#endif return sound;}/* sound_prepend_zeros -- modify sound_type so that it starts at t0 *//* * assumes t0 is earlier than snd->t0, so the sound should return zeros * until snd->t0 is reached, after which we revert to normal computation. * When we return, the new snd->t0 will be t0, meaning that the first * sample returned will be at time t0. * NOTE: t0 may not be an exact multiple of samples earlier than snd->t0, * but Nyquist allows any sound to be shifted by +/- 0.5 samples in * order to achieve alignment. Since sound_prepend_zeros can be called * many times on the same sound_type, there is a chance that rounding * errors could accumulate. My first solution was to return with * snd->t0 computed exactly and not reflecting any fractional sample * shift of the signal, but this caused problems for the caller: a * fractional sample shift at a low sample rate could correspond to * many client samples,fooling the client into thinking that some * initial samples should be discarded (or else requiring the client * to be pretty smart). The solution used here is to return to the * client with snd->t0 exactly equal to t0, but to save snd->true_t0 * equal to the time of the first sample with no sound shifting. This * time is used for any future sound_prepend_zeros operations so that * any accumulated rounding errors are due only to floating point * precision and not to accumulated fractional sample shifts of snd. */void sound_prepend_zeros(sound_type snd, time_type t0){ long n; /* first, see if we're already prepending some zeros */ if (snd->get_next != SND_get_zeros) {/* nyquist_printf("sound_prepend_zeros 1: snd->t0 %g t0 %g\n", snd->t0, t0); */ /* if not, then initialize some fields that support prepending */ snd->prepend_cnt = 0; snd->true_t0 = snd->t0; /* save old get_next and plug in special get_next function */ snd->after_prepend = snd->get_next; snd->get_next = SND_get_zeros; } n = (long) (((snd->true_t0 - t0) * snd->sr) + 0.5); /* how many samples to prepend */ /* add to prepend_cnt so first sample will correspond to new t0 */ snd->prepend_cnt += n; /* compute the true t0 which corresponds to the time of first sample */ snd->true_t0 -= (n / snd->sr); /* make caller happy by claiming the sound now starts at exactly t0; * this is always true within 0.5 samples as allowed by Fugue. */ snd->t0 = t0;/* nyquist_printf("sound_prepend_zeros: snd %p true_t0 %g sr %g n %d\n", snd, snd->true_t0, snd->sr, n);*/}/* sound_array_copy -- copy an array of sounds *//* * NOTE: be sure to protect the result from gc! */LVAL sound_array_copy(LVAL sa){ long i = getsize(sa); LVAL new_sa = newvector(i); xlprot1(new_sa); while (i > 0) { i--; setelement(new_sa, i, cvsound(sound_copy(getsound(getelement(sa, i))))); } xlpop(); return new_sa;}/* sound_copy - copy a sound structure, do reference counts *//**/sound_type sound_copy(sound_type snd){ sound_type sndcopy; falloc_sound(sndcopy, "sound_copy"); *sndcopy = *snd; /* copy the whole structure */ snd_list_ref(snd->list); /* copied a reference so fix the count *//* nyquist_printf("sound_copy'd %p to %p\n", snd, sndcopy); */ if (snd->table) snd->table->refcount++;#ifdef GC_DEBUG if (sndcopy == sound_to_watch) abort();#endif return sndcopy;}/* convert a sound to a wavetable, set length *//**/table_type sound_to_table(sound_type s){ long len = snd_length(s, max_table_len); long tx = 0; /* table index */ long blocklen; register double scale_factor = s->scale; sound_type original_s = s; table_type table; /* the new table */ long table_bytes; /* how big is the table */ if (s->table) { s->table->refcount++; return s->table; } if (len >= max_table_len) { char emsg[100]; sprintf(emsg, "maximum table size (%d) exceeded", max_table_len); xlcerror("use truncated sound for table", emsg, NIL); } else if (len == 0) { xlabort("table size must be greater than 0"); } len++; /* allocate extra sample at end of table */ s = sound_copy(s); /* nyquist_printf("sound_to_table: allocating table of size %d\n", len); */ table_bytes = table_size_in_bytes(len); table = (table_type) malloc(table_bytes); if (!table) xlfail("osc_init couldn't allocate memory for table"); table_memory += table_bytes; table->length = (double) (len - 1); while (len > 1) { sample_block_type sampblock = sound_get_next(s, &blocklen); long togo = MIN(blocklen, len); long i; sample_block_values_type sbufp = sampblock->samples;/* nyquist_printf("in sound_to_table, sampblock = %d\n", sampblock);*/ for (i = 0; i < togo; i++) { table->samples[tx++] = (float) (*sbufp++ * scale_factor); } len -= togo; } /* for interpolation, duplicate first sample at end of table */ table->samples[tx] = table->samples[0]; table->refcount = 2; /* one for the user, one from original_s */ sound_unref(s); s = NULL; original_s->table = table; return table;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -