1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
// 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(snd_susp_type a_susp, snd_list_type snd_list)
{
pvshell_susp_type susp = (pvshell_susp_type) a_susp;
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(snd_susp_type a_susp)
{
pvshell_susp_type susp = (pvshell_susp_type) a_susp;
sound_xlmark(susp->pvshell.f);
sound_xlmark(susp->pvshell.g);
}
void pvshell_free(snd_susp_type a_susp)
{
pvshell_susp_type susp = (pvshell_susp_type) a_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(snd_susp_type a_susp, int n)
{
pvshell_susp_type susp = (pvshell_susp_type) a_susp;
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);
}
|