⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 multiseq.c

📁 Audacity是一款用於錄音和編輯聲音的、免費的開放源碼軟體。它可以執行於Mac OS X、Microsoft Windows、GNU/Linux和其它作業系統
💻 C
📖 第 1 页 / 共 2 页
字号:
/* multiseq.c -- return a multichannel signal until its logical stop, then   evaluate a closure to get another signal and convert to adds   of two multichannel signals *//* CHANGE LOG * -------------------------------------------------------------------- * 28Apr03  dm  changes for portability and fix compiler warnings */#include "stdio.h"#ifndef mips#include "stdlib.h"#endif#include "xlisp.h"#include "sound.h"#include "falloc.h"#include "multiseq.h"#include "add.h"#include "scale.h"#include "extern.h"#include "cext.h"/* #define MULTISEQ_GC_DEBUG */#ifdef MULTISEQ_GC_DEBUGextern snd_list_type snd_list_to_watch;#endif#define D if(0)/* Design:This operator implements sequences of multichannel signals.A single data structure manages an array of susps thatinitially are used to fetch blocks from the first multichannelsignal.  When the LAST logical stop is reached, a closureis evaluated to yield a new multichannel signal.  The componentsounds of this are stored into the susps which are thenconverted into add suspensions.  The other managing structuresare then freed.The main constraint here is that the conversion to add suspsmust take place at the same time across all channels, so beforethe conversion, a fetch from the susp can only be made if it isknown that the samples returned happen BEFORE the conversion willtake place.  Since the conversion takes place at the maximum ofthe logical stop times of all channels, we have to advance allchannels synchronously.  We keep track of a greatest lower bound,refered to as the horizon, on the maximum logical stop time.  Itis safe to fetch blocks up to the horizon, but not beyond.This synchronous fetching is done by a single routine and anauxilliarly structure that manages the whole multichannel arrayof susps.  The basic idea is that a fetch from a suspension gets forwarded to the managing structure, whichuses its array of susps to fetch from ALL suspensions up tothe requested time or until the logical stop, whichever comesfirst.  These "synchronous" fetches are not made by calling thefetch routines on the suspensions to avoid infinite recursion.At any time, there will be some set of channels whose logical stop time is unknown.  The "s1" fields (s1, s1_ptr, s1_bptr) ofthese suspensions are used to look ahead by geting a block froms1.  If no logical stop is indicated, then we can append the blockto the snd_list and update the horizon, allowing fetches from othersusps.  In other words, the s1_bptr of each susp provides a onebuffer lookahead by which we can obtain advance knowledge of themaximum logical stop time.The algorithm is as follows:1. When fetch is called on a suspension, compute when anyprefetched samples will end (if there are none, then fetcha block from s1 and compute the time at which the block ends).This becomes the target time for other fetches.2. Call multiseq_advance(), passing the target time and themanager structure (which has pointers to all other channels).(Note: every susp has a pointer to the manager).The function of multiseq_advance() is to push the horizon forthe logical stop forward.  This is done by iterating over the array of susps until the target is reached.Actually, the array contains pointers to the snd_list_node thatpoints to each susp and where the next block will be linked.The goal of this loop is to satisfy the original fetch, whichmeans we have to push low_water to greater than or equal to thetarget.  (Low water is the minimum time of the next sample tobe returned by any multiseq susp.) This goal will be met unlesswe reach the last logical stop time, in which case we evaluatethe closure for the next multichannel sound, convert everything to add's and let the additions take care of returningblocks.3. The Iteration Loop:low_water is the lowest sample count of the next sample horizon is the greatest lower bound on the maximum logical stop timeIterate over susps until low_water >= target(Note: whenever data is fetched for a sound whose logical stop timeis unknown, update the horizon.  If a logical stop time becomes known,then test if the final maximum logical stop time is known (by keepinga count of how many are still unknown), and if the count goes to zero,evaluate the continuation and convert to multiple adds.)(Another Note: we may reach the logical stop time and convert to multiple adds before the loop terminates, in which case we returnwithout finishing the loop.  Take care that the caller does the rightthing to produce a sample block in this case.)3a. If a block hasn't been prefetched, do it.3b. While the susp has prefetched a block that ends at or before horizon,put the block on the snd_list and prefetch another block.3c. If the susp hasn't a known logical stop time, set new_horizon tothe end time of the last sample prefetched in 3b.3d. If new_horizon == horizon, signal an error, no progress was made.3d. Set horizon to new_horizon and repeat the loop.NOTE ON A BUG FIX (1 Jul 95): old code assumed that when a logical stopwas detected it was at the beginning of the next block, but if logicalstop is explicit, then it may be way in the future.  We could convertto adds at this point, but that would force early evaluation of theclosure, which we'd like to delay (be lazy when possible). Therefore,we want to ignore knowledge of a logical stop time until the logicalstop time falls within the currently known block of samples. By "currentlyknown", I mean somewhere in the block referenced by ->s1_ptr and ->s1_cnt.*//* extern LVAL s_stdout; */void multiseq_convert(multiseq_type ms);void multiseq_free(add_susp_type susp);sample_block_type multiseq_get_next(sound_type snd, long * cnt);void multiseq_print_tree(add_susp_type susp, int n);#define susp_cnt_time(ssp, ms, cnt) (ssp->susp.t0 - ms->t0 + (cnt)/ssp->s1->sr)#define susp_time(ssp, ms) susp_cnt_time(ssp, ms, \                                         (ssp->susp.current + ssp->s1_cnt))#define susp_low_water(ssp, ms) susp_cnt_time(ssp, ms, ssp->susp.current)#define susp_log_stop_time(ssp, ms) susp_cnt_time(ssp, ms, ssp->susp.log_stop_cnt)/* multiseq_advance fetches from each channel to advance to target time *//* * If a channel terminates early, we must be careful: continuing to * fetch will return pointers to the zero_block, but this will * indicate termination to whoever is fetching from multiseq. We * must check the pointers and substitute internal_zero_block to * avoid premature termination. */void multiseq_advance(multiseq_type ms, time_type target){    int i;    time_type new_horizon;    time_type new_low_water;D    nyquist_printf("multiseq_advance: %p->low_water %g, target %g\n",             ms, ms->low_water, target);    while (ms->low_water < target - 0.000001) {        new_horizon = 0.0;D        nyquist_printf("multiseq_advance loop: target %g low_water %g horizon %g\n",                target, ms->low_water, ms->horizon);        /* new_low_water will be a minimum over every         * channel, so start with a big number */        new_low_water = target;        for (i = 0; i < ms->nchans; i++) {            snd_list_type snd_list = ms->chans[i];            add_susp_type susp = (add_susp_type) snd_list->u.susp;            time_type my_hor;            time_type my_low_water;D            nyquist_printf("chans[%d]: ", i);            /* fetch up to horizon */            /* see if susp has an unprocessed block (test on susp->s1_ptr             * is probably not necessary, in fact, it isn't initialized             * until the first block is fetched, but s1_cnt is             */            if (susp->s1_cnt && susp->s1_ptr &&                 susp->s1_ptr == susp->s1_bptr->samples) {                /* do nothing, unprocessed block already there as a                 * result of the initiating fetch                 */            } else if (susp->s1_cnt != 0) {                stdputstr("multiseq_advance: s1_cnt != 0\n");                EXIT(1);	/* this should never happen */            } else { /* otherwise fetch it */D                stdputstr("prefetching samples ");                susp_get_block_samples(s1, s1_bptr, s1_ptr, s1_cnt);                if (susp->s1_ptr == zero_block->samples) {                    susp->terminate_bits = 1;                    susp->s1_bptr = internal_zero_block;                    susp->s1_ptr = internal_zero_block->samples;                }                /* see if we've reached a logical stop                 * (I can't believe this code block is in 3 places -                 *  there must be a better way... RBD)                 */                if (!susp->logical_stop_bits) {                    if (susp->s1->logical_stop_cnt != UNKNOWN) {                        if (susp->susp.current + susp->s1_cnt >=                            susp->s1->logical_stop_cnt) {                            susp->logical_stop_bits = 1;                            susp->susp.log_stop_cnt =                                 susp->s1->logical_stop_cnt;                            ms->not_logically_stopped_cnt--;D			    nyquist_printf(  "snd_make_multiseq: Logical stop reached, not_logically_stopped_cnt %d\n",                                   ms->not_logically_stopped_cnt);                        }                    }                }            }D           nyquist_printf(" current %d cnt %d ",                 (int)susp->susp.current, (int)susp->s1_cnt);            /* while the susp has prefetched a block that ends at or             * before horizon, put the block on the snd_list and             * prefetch another block             */            while (susp_time(susp, ms) < ms->horizon + 0.000001) {                snd_list->block = susp->s1_bptr;                snd_list->block_len = (short) susp->s1_cnt;                susp->susp.current += susp->s1_cnt;                (susp->s1_bptr->refcnt)++;                susp->s1_cnt = 0;#ifdef MULTISEQ_GC_DEBUG                nyquist_printf(                "multiseq: output block %p%s on snd_list %p to chan %d\n",                       susp->s1_bptr,                       (susp->s1_bptr == internal_zero_block ?                        " (INTERNAL ZERO BLOCK)" : ""),                        snd_list, i);#endif                snd_list->u.next = snd_list_create(&(susp->susp));#ifdef MULTISEQ_GC_DEBUG                snd_list_debug(snd_list, "multiseq_advance");#endif                ms->chans[i] = snd_list = snd_list->u.next;                susp_get_block_samples(s1, s1_bptr, s1_ptr, s1_cnt);                if (susp->s1_ptr == zero_block->samples) {                    susp->terminate_bits = 1;                    susp->s1_bptr = internal_zero_block;                    susp->s1_ptr = internal_zero_block->samples;                }                if (susp->s1_ptr != susp->s1_bptr->samples) {                    stdputstr("bug in multiseq_advance\n");                    EXIT(1);                }                /* see if we've reached a logical stop                 * (I can't believe this code block is in 3 places -                 *  there must be a better way... RBD)                 */                if (!susp->logical_stop_bits) {                    if (susp->s1->logical_stop_cnt != UNKNOWN) {                        if (susp->susp.current + susp->s1_cnt >=                            susp->s1->logical_stop_cnt) {                            susp->logical_stop_bits = 1;                            susp->susp.log_stop_cnt =                                 susp->s1->logical_stop_cnt;                            ms->not_logically_stopped_cnt--;D			    nyquist_printf(  "snd_make_multiseq: Logical stop reached, not_logically_stopped_cnt %d\n",                                   ms->not_logically_stopped_cnt);                        }                    }                }D               nyquist_printf("\n\toutput block, current %d cnt %d ",                               (int)susp->susp.current, (int)susp->s1_cnt);            }            if (!susp->logical_stop_bits)                 my_hor = susp_time(susp, ms);            else my_hor = susp_log_stop_time(susp, ms);            if (new_horizon < my_hor) {D                nyquist_printf("new_horizon %g ", my_hor);                new_horizon = my_hor;            }            if (ms->not_logically_stopped_cnt == 0) {                ms->horizon = new_horizon; /* pass t0 to multiseq_convert */D		stdputstr("Calling multiseq_convert\n");                multiseq_convert(ms);                return;            }            my_low_water = susp_low_water(susp, ms);            if (my_low_water < new_low_water) {                new_low_water = my_low_water;            }D            stdputstr("\n");        }        ms->low_water = new_low_water;        if (new_horizon <= ms->horizon) {            stdputstr("no progress in multiseq_advance\n");            EXIT(1);        } else {            ms->horizon = new_horizon;        }    }}/* multiseq_convert -- eval closure and convert to adds *//**/void multiseq_convert(multiseq_type ms){    LVAL result, new;    sound_type snd;    time_type now = ms->t0 + ms->horizon;    int i;    long size;    xlsave1(result);    result = xleval(cons(ms->closure, consa(cvflonum(now))));    if (exttypep(result, a_sound)) {        snd = sound_copy(getsound(result));        result = newvector(ms->nchans);        setelement(result, 0, cvsound(snd));        for (i = 1; i < ms->nchans; i++) {            setelement(result, i, cvsound(sound_zero(now, ms->sr)));        }    } else if (vectorp(result)) {        if (getsize(result) > ms->nchans) {            xlerror("too few channels", result);        } else if (getsize(result) < ms->nchans) {            new = newvector(ms->nchans);            for (i = 1; i < getsize(result); i++) {                setelement(new, i, getelement(result, i));            }            for (i = getsize(result); i < ms->nchans; i++) {                setelement(new, i, cvsound(sound_zero(now, ms->sr)));            }            result = new;        }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -