📄 sound.c
字号:
* blocks normally from then on. */sample_block_type SND_flush(sound_type snd, long * cnt){ long mycnt; sample_block_type block = SND_get_first(snd, &mycnt); while (snd->current < 0) { block = SND_get_next(snd, &mycnt); } /* at this point, we've read to and including the block with * the first samples we want to return. If the block boundary * is in the right place, we can do a minimal fixup and return: */ if (snd->current == snd->list->block_len) { *cnt = snd->current; /* == snd->list->block_len */ /* snd->get_next = SND_get_next; -- done by SND_get_first */ return block; } else /* snd->current < snd->list->block_len */ { long i; sample_block_values_type from_ptr; /* we have to return a partial block */ /* NOTE: if we had been smart, we would have had SND_get_next * return a pointer to samples rather than a pointer to the * block, which has a reference count. Since the caller * expects a pointer to a reference count, we have to copy * snd->current samples to a new block */ snd_list_type snd_list = snd_list_create((snd_susp_type) snd->list->u.next); snd_list->u.next->refcnt++; falloc_sample_block(snd_list->block, "SND_flush"); /* now copy samples */ from_ptr = block->samples + snd->list->block_len - snd->current; for (i = 0; i < snd->current; i++) { snd_list->block->samples[i] = from_ptr[i]; } snd_list_unref(snd->list); snd->list = snd_list; *cnt = snd->current; return snd_list->block; }}/* SND_get_zeros -- the get_next function for prepended zeros *//* * when prepending zeros, we just return a pointer to the internal_zero_block * and decrement the prepend_cnt until it goes to zero. Then we revert to * the normal (original) get_next function. * */sample_block_type SND_get_zeros(sound_type snd, long * cnt){ int len = MIN(snd->prepend_cnt, max_sample_block_len); /* stdputstr("SND_get_zeros: "); */ if (len < 0) { char error[80]; sprintf(error, "SND_get_zeros snd %p len %d", snd, len); xlabort(error); } if (len == 0) { /* we've finished prepending zeros */ snd->get_next = snd->after_prepend; /* stdputstr("done, calling sound_get_next\n"); fflush(stdout); */ return sound_get_next(snd, cnt); } else { *cnt = len; snd->current += len; snd->prepend_cnt -= len;/* nyquist_printf("returning internal_zero_block@%p\n", internal_zero_block); fflush(stdout); */ return internal_zero_block; }}/***************************************************************************** SND_get_next* Inputs:* sound_type snd: The iterator whose next block is to be computed* int * cnt: Place to put count of samples returned* Result: snd_list_type* Pointer to the sample block computed ---------------------------+* Effect: |* force suspension to compute next block of samples |* |* Here's the protocol for using this and related functions: |* Every client (sample reader) has a private sound_type (an iterator), |* and the sound_type's 'list' field points to a header (of type |* snd_list_type). The header in turn points to a block of samples. |* |* +---------------------------------------+* |* |* | sample_block_type* (snd) V +---+--+--+--+--+--+--+-...-+--+* sound_type: snd_list_type +-->|ref| | | | |//|//| |//|* +---------+ +----------+ | +---+--+--+--+--+--+--+-...-+--+* | list +------->| block +--+ ^* +---------+ +----------+ :* | t0 | | block_len|....................:* +---------+ +----------+* | sr | | refcnt |* +---------+ +-+--------+* | current | | next +---->... Note: the union u* +---------+ |u|........| snd_list_type points to only one* | rate | | | susp +---->... of the indicated* +---------+ +-+--------+ susp_type types* | scalse | |log_stop |* +---------+ +----------+* | lsc |* +---------+* |get_next +-----> SND_get_next()* +---------+** The sound_type keeps track of where the next sample block will * come from. The field 'current' is the number of the first sample of* the next block to be returned, where sample numbers start* at zero. The normal fetch procedure is this one, although special* cases may generate special block generators, e.g., CONST does not need* to allocate and refill a block and can reuse the same block over and* over again, so it may have its own fetch procedure. This is the* general fetch procedure, which assumes that the generator function* actually produces a slightly different value for each sample.** The distinguishing characteristic of whether the 'u' field is to be* interpreted as 'next', a link to the next list element, or 'susp', a* reference to the suspension for generating a new sample block, is* whether the 'block' parameter is NULL or not. If it is NULL, then* u.susp tells how to generate the block; if it is not NULL, u.next is* a pointer to the next sound block in the list.** When the 'block' pointer is NULL, we create a block of samples, and* create a new sound list element which follows it which has a NULL* 'block' pointer; the 'u' field of the current list element is now* interpreted as 'u.next'.** The client calls SND_get_next to get a pointer to a block of samples.* The count of samples generated is returned via a ref parameter, and* SND_get_next will not be called again until this set is exhausted.** The next time SND_get_next is called, it knows that the sample block* has been exhausted. It releases its reference to the block (and if* that was the last reference, frees the block to the block allocation* pool), allocates a new block from the block pool, and proceeds to* fill it with samples.** Note that as an optimization, if the refcnt field goes to 0 it* could immediately re-use the block without freeing back to the block* pool and reallocating it.** Because of the way we handle sound sample blocks, the sound sample blocks* themselves are ref-counted, so freeing the snd_list_type may not free* the sample block it references. At the level of this procedure, that* is transparently handled by the snd_list_unref function.** Logical stop:** Logical stop is handled by several mechanisms. The /intrinsic/ logical* stop is an immutable property of the signal, and is determined by the* specification in the algorithm description file. When it is encountered,* the 'logically_stopped' flag of the snd_list_node is set.* The generators guarantee that the first time this is encountered, it* will always be constructed so that the first sample of the block it* references is the logical stop time.** In addition, the client may have set the /explicit logical stop time/ of* the iterator (e.g., in nyquist, the (set-logical-stop sound time) call copies* the sound, altering its logical stop). The logical stop time, when set* in this way, causes the logical_stop_cnt ('lsc' in the above diagram)* to be set to the count of the last sample to be generated before the* <logical stop time. This will guarantee that the sound will indicate that* it has reached its logical stop time when the indicated sample is * generated.****************************************************************************/void add_s1_s2_nn_fetch(); /* for debugging *//* SND_get_first -- the standard fn to get a block, after returning * the first block, plug in SND_get_next for successive blocks */sample_block_type SND_get_first(sound_type snd, long * cnt){ register snd_list_type snd_list = snd->list; /* * If there is not a block of samples, we need to generate one. */ if (snd_list->block == NULL) { /* * Call the 'fetch' method for this sound_type to generate * a new block of samples. */ snd_susp_type susp = snd_list->u.susp; snd_list->u.next = snd_list_create(susp); snd_list->block = internal_zero_block; /* nyquist_printf("SND_get_first: susp->fetch %p\n", susp->fetch); */ (*(susp->fetch))(susp, snd_list);#ifdef GC_DEBUG snd_list_debug(snd_list, "SND_get_first");#endif /* nyquist_printf("SND_get_first: snd_list %p, block %p, length %d\n", snd_list, snd_list->block, snd_list->block_len); */ } if ((snd->logical_stop_cnt == UNKNOWN) && snd_list->logically_stopped) { /* nyquist_printf("SND_get_first/next: snd %p logically stopped at %d\n", snd, snd->current); */ snd->logical_stop_cnt = snd->current; } /* see if clipping needs to be applied */ if (snd->current + snd_list->block_len > snd->stop) { /* need to clip: is clip on a block boundary? */ if (snd->current == snd->stop) { /* block boundary: replace with zero sound */ snd->list = zero_snd_list; snd_list_unref(snd_list); } else { /* not a block boundary: build new list */ snd->list = snd_list_create((snd_susp_type) zero_snd_list); snd->list->block_len = (short) (snd->stop - snd->current); snd->list->block = snd_list->block; snd->list->block->refcnt++; snd_list_unref(snd_list); } snd_list = snd->list; /* used below to return block ptr */ } *cnt = snd_list->block_len; /* this should never happen */ if (*cnt == 0) { stdputstr("SND_get_first returned 0 samples\n"); dbg_mem_print("snd_list info:", snd_list); dbg_mem_print("block info:", snd_list->block); sound_print_tree(snd); stdputstr("It is possible that you created a recursive sound\n"); stdputstr("using something like: (SETF X (SEQ (SOUND X) ...))\n"); stdputstr("Nyquist aborts from non-recoverable error\n"); abort(); } snd->current += snd_list->block_len; /* count how many we read */ snd->get_next = SND_get_next; return snd_list->block;}sample_block_type SND_get_next(sound_type snd, long * cnt){ register snd_list_type snd_list = snd->list; /* * SND_get_next is installed by SND_get_first, so we know * when we are called that we are done with the current block * of samples, so free it now. */ snd_list_type cur = snd_list; snd->list = snd_list = cur->u.next; snd_list_ref(snd_list); snd_list_unref(cur); /* release the reference to the current block */ /* now that we've deallocated, we can use SND_get_first to finish the job */ return SND_get_first(snd, cnt);}/***************************************************************************** make_zero_block* Inputs:* * Result: * * Effect: * ****************************************************************************/sample_block_type make_zero_block(void) { sample_block_type zb; int i; falloc_sample_block(zb, "make_zero_block"); /* leave room for lots more references before overflow, but set the count high so that even a large number of dereferences will not lead to a deallocation */ zb->refcnt = 0x6FFFFFFF; for (i = 0; i < max_sample_block_len; i++) { /* fill with zeros */ zb->samples[i] = 0.0F; } /* fill with zeros */ return zb; }/* min_cnt -- help compute the logical stop or terminate as minimum *//* * take the sound (which has just logically stopped or terminated at * current sample) and * convert the stop sample into the equivalent sample count as produced by * susp (which may have a different sample rate). If the count is less than * the current *cnt_ptr, overwrite cnt_ptr with a new minimum. By calling * this when each of S1, S2, ... Sn reach their logical stop or termiate * points, *cnt_ptr will end up with the minimum stop count, which is what * we want. NOTE: the logical stop time and terminate for signal addition * should be the MAX of logical stop times of arguments, so this routine * would not be used. */void min_cnt(long *cnt_ptr, sound_type sound, snd_susp_type susp, long cnt){ long c = (long) ((((sound->current - cnt) / sound->sr + sound->t0) - susp->t0) * susp->sr + 0.5); /* if *cnt_ptr is uninitialized, just plug in c, otherwise compute min */ if ((*cnt_ptr == UNKNOWN) || (*cnt_ptr > c)) {/* nyquist_printf("min_cnt %p: new count is %d\n", susp, c);*//* if (c == 0) sound_print_tree(printing_this_sound);*/ *cnt_ptr = c; }}/***************************************************************************** sound_init* Result: void* * Effect: * Module initialization* Allocates the 'zero block', the infinitely linked block of* 0-valued sounds. This is referenced by a list element which* refers to itself.****************************************************************************/void sound_init(void){ zero_block = make_zero_block(); internal_zero_block = make_zero_block(); falloc_snd_list(zero_snd_list, "sound_init"); zero_snd_list->block = zero_block; zero_snd_list->u.next = zero_snd_list; zero_snd_list->refcnt = 2; zero_snd_list->block_len = max_sample_block_len; zero_snd_list->logically_stopped = true;#ifdef GC_DEBUG { long s; stdputstr("sound_to_watch: "); scanf("%p", &s); watch_sound(s); }#endif sound_desc = create_desc("SOUND", sound_xlfree, sound_xlprint, sound_xlsave, sound_xlrestore, sound_xlmark);}/* sound_scale -- copy and change scale factor of a sound *//**/sound_type sound_scale(double factor, sound_type snd){ sound_type sndcopy = sound_copy(snd); sndcopy->scale *= (float) factor; return sndcopy;}/***************************************************************************** set_logical_stop_time* Inputs:* sound_type sound: The sound for which the logical stop time is* being set* time_type when: The logical stop time, expressed as an absolute* time.* Result: void* * Effect: * Converts the time 'when' into a count of samples.****************************************************************************/void set_logical_stop_time(sound_type sound, time_type when) { long n; /* 'when' is an absolute time. The number of samples to be generated is the number of samples between 't0' and 'when'. -----------+---+---+---+---+---+---+---+---+---+ | | t0 when */ n = (long) ((when - sound->t0) * sound->sr + 0.5); /* * n is kept as a local variable for debugging purposes. */ sound->logical_stop_cnt = n; }/* for debugging */sound_type printing_this_sound = NULL;void ((**watch_me)()) = NULL;void set_watch(where) void ((**where)());{ if (watch_me == NULL) { watch_me = where; nyquist_printf("set_watch: watch_me = %p\n", watch_me);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -