diff options
Diffstat (limited to 'nyqsrc/pvshell.c')
-rw-r--r-- | nyqsrc/pvshell.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/nyqsrc/pvshell.c b/nyqsrc/pvshell.c new file mode 100644 index 0000000..e281649 --- /dev/null +++ b/nyqsrc/pvshell.c @@ -0,0 +1,202 @@ +// pvshell.c -- This is a skeleton for a Nyquist primitive that +// returns a sound. The sound is obtained by calling a function +// with a request consisting of a location to put samples and +// a count of how many samples are needed. The function returns +// the actual number of samples computed and flags indicating +// if the signal has reached the logical stop or termination. +// In addition, there are interfaces for extracting samples +// from input sounds. +// This code is designed for a time-stretching phase vocoder, +// but could be used for other purposes. It is derived from +// compose.c, which might have been implmented with this +// skeleton had we started out with this abstraction. + +#include "stdio.h" +#ifndef mips +#include "stdlib.h" +#endif +#include "xlisp.h" +#include "sound.h" + +#include "falloc.h" +#include "cext.h" +#include "pvshell.h" +#include "assert.h" + +/* CHANGE LOG + * -------------------------------------------------------------------- + * 28Apr03 dm changes for portability and fix compiler warnings + */ + +void pvshell_free(); + + +typedef struct pvshell_susp_struct { + snd_susp_node susp; + long terminate_cnt; + boolean logically_stopped; + boolean started; + + pvshell_node pvshell; +} pvshell_susp_node, *pvshell_susp_type; + + +/* pvshell_test_f -- get next sample block and check flags + * + * Only call this from PVSHELL_TEST_F macro + */ +long pvshell_test_f(pvshell_type susp) +{ + long flags = 0; + susp_get_samples(f, f_ptr, f_cnt); /* warning: macro references susp */ + if (susp->f->logical_stop_cnt == susp->f->current - susp->f_cnt) { + flags |= PVSHELL_FLAG_LOGICAL_STOP; + } + if (susp->f_ptr == zero_block->samples) { + flags |= PVSHELL_FLAG_TERMINATE; + } + return flags; +} + + +/* pvshell_test_g -- get next sample block and check flags + * + * Only call this from PVSHELL_TEST_G macro + */ +long pvshell_test_g(pvshell_type susp) +{ + long flags = 0; + susp_get_samples(g, g_ptr, g_cnt); /* warning: macro references susp */ + if (susp->g->logical_stop_cnt == susp->g->current - susp->g_cnt) { + flags |= PVSHELL_FLAG_LOGICAL_STOP; + } + if (susp->g_ptr == zero_block->samples) { + flags |= PVSHELL_FLAG_TERMINATE; + } + return flags; +} + + +/* pvshell_fetch -- computes h(f, g, x, y) where f and g are + * sounds, x and y are doubles, and h implemented via a function + * pointer. This could certainly be generalized further, but + * maybe we should take this one step at a time. +/**/ +void pvshell_fetch(register pvshell_susp_type susp, snd_list_type snd_list) +{ + long n, flags; + sample_block_type out; + sample_block_values_type out_ptr; + + falloc_sample_block(out, "pvshell_fetch"); + out_ptr = out->samples; + snd_list->block = out; + + /* don't run past the f input sample block: */ + /* most fetch routines call susp_check_term_log_samples() here + * but we can't becasue susp_check_term_log_samples() assumes + * that output time progresses at the same rate as input time. + * Here, some time warping might be going on, so this doesn't work. + * It is up to the user to tell us when it is the logical stop + * time and the terminate time. + */ + /* don't run past terminate time */ + // if (susp->terminate_cnt != UNKNOWN && + // susp->terminate_cnt <= susp->susp.current + cnt + togo) { + // togo = susp->terminate_cnt - (susp->susp.current + cnt); + // if (togo == 0) break; + // } + /* don't run past logical stop time */ + // if (!susp->logically_stopped && susp->susp.log_stop_cnt != UNKNOWN) { + // int to_stop = susp->susp.log_stop_cnt - (susp->susp.current + cnt); + // if (to_stop < togo && ((togo = to_stop) == 0)) break; + // } + n = max_sample_block_len; // ideally, compute a whole block of samples + + flags = (susp->pvshell.h)(&(susp->pvshell), out_ptr, &n); + + /* test for termination */ + if (flags & PVSHELL_FLAG_TERMINATE) { + snd_list_terminate(snd_list); + } else { + snd_list->block_len = n; + susp->susp.current += n; + } + /* test for logical stop */ + if (flags & PVSHELL_FLAG_LOGICAL_STOP || susp->logically_stopped) { + snd_list->logically_stopped = true; + susp->logically_stopped = true; + } +} /* pvshell_fetch */ + + +void pvshell_mark(pvshell_susp_type susp) +{ + sound_xlmark(susp->pvshell.f); + sound_xlmark(susp->pvshell.g); +} + + +void pvshell_free(pvshell_susp_type susp) +{ + /* note that f or g can be NULL */ + sound_unref(susp->pvshell.f); + sound_unref(susp->pvshell.g); + ffree_generic(susp, sizeof(pvshell_susp_node), "pvshell_free"); +} + + +void pvshell_print_tree(pvshell_susp_type susp, int n) +{ + indent(n); + stdputstr("f:"); + sound_print_tree_1(susp->pvshell.f, n); + + indent(n); + stdputstr("g:"); + sound_print_tree_1(susp->pvshell.g, n); +} + + +sound_type snd_make_pvshell(char *name, rate_type sr, time_type t0, + h_fn_type h, sound_type f, sound_type g, + double *state, long n) +{ + register pvshell_susp_type susp; + int i; + + falloc_generic(susp, pvshell_susp_node, "snd_make_pvshell"); + susp->susp.fetch = pvshell_fetch; + susp->terminate_cnt = UNKNOWN; + + /* initialize susp state */ + susp->susp.free = pvshell_free; + susp->susp.sr = sr; + susp->susp.t0 = t0; + susp->susp.mark = pvshell_mark; + susp->susp.print_tree = pvshell_print_tree; + susp->susp.name = name; + susp->logically_stopped = false; + susp->susp.log_stop_cnt = UNKNOWN; + susp->susp.current = 0; + + /* copy the sound so that we have a private "reader" object */ + susp->pvshell.f = (f ? sound_copy(f) : f); + susp->pvshell.f_cnt = 0; + + susp->pvshell.g = (g ? sound_copy(g) : g); + susp->pvshell.g_cnt = 0; + + susp->pvshell.h = h; + + susp->pvshell.flags = 0; /* terminated and logically stopped flags -- these + are for the client of pvshell to use */ + + assert(n <= PVSHELL_STATE_MAX); + for (i = 0; i < n; i++) { + susp->pvshell.state[i] = state[i]; + } + + susp->started = false; + return sound_create((snd_susp_type)susp, t0, sr, 1.0); +} |