diff options
Diffstat (limited to 'nyqsrc/phasevocoder.c')
-rw-r--r-- | nyqsrc/phasevocoder.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/nyqsrc/phasevocoder.c b/nyqsrc/phasevocoder.c new file mode 100644 index 0000000..d446095 --- /dev/null +++ b/nyqsrc/phasevocoder.c @@ -0,0 +1,102 @@ +/* phasevocoder.c -- this is a stub showing how you might hook a + phase vocoder into Nyquist using pvshell + */ + +#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 "phasevocoder.h" + +/* use the state[] info for sample interpolation */ +#define X_VALUE state[0] /* a parameter value */ +#define F_COUNT state[1] /* counts samples of f */ +#define G_COUNT state[2] /* counts samples of g */ +#define G_PREV state[3] /* previous value from g */ +#define G_NEXT state[4] /* next (current?) value from g */ +/* invariant: G_NEXT is the G_COUNT'th sample of g */ + +/* pv_fetch -- this is an example, but it doesn't really do + * phase vocoding. Instead, it will just multiply f, g, and x + * + * To make things a bit more interesting, we will assume g has + * an arbitrary sample rate with respect to f, and will interpolate. + * + */ +long pv_fetch(pvshell_type susp, + sample_block_values_type out, long *n) +{ + int i; + for (i = 0; i < *n; i++) { + long new_flags; + sample_type f; + double g; + /* NOTE: in DSP terms, this is poor code because of the + * division operations -- it could be made faster + */ + /* To get a value from g, first compute the time */ + double f_time = susp->F_COUNT / susp->f->sr; + /* Now compute g count that is past the time */ + double g_count = f_time * susp->g->sr; + while (susp->G_COUNT < g_count) { + PVSHELL_TEST_G(susp); /* prepare to get a sample */ + /* ignore flags from g -- we could, if we wanted, + * terminate when either f or g terminated, etc. + */ + susp->G_PREV = susp->G_NEXT; + susp->G_NEXT = PVSHELL_FETCH_G(susp); + susp->G_COUNT++; + } + /* now interpolate to get the value of g at f_time */ + g = susp->G_PREV + (susp->G_NEXT - susp->G_PREV) * + (g_count - (susp->G_COUNT - 1)); + new_flags = PVSHELL_TEST_F(susp); + susp->flags |= new_flags; + if (new_flags) break; + f = PVSHELL_FETCH_F(susp); + susp->F_COUNT++; /* count how many samples we have taken */ + + /* now we have f, g, x */ + *out++ = f * g * susp->X_VALUE; + } + /* i is the number of samples we acutally computed */ + *n = i; + /* if we computed samples, we want to return them before + * returning flags that say we're done or stopped + */ + return (i ? 0 : susp->flags); +} + + +sound_type snd_phasevocoder(sound_type f, sound_type g, double x) +{ + /* we're using 5 doubles of state. The first is a parameter, + * and the rest are initialized to zero except for state[2], + * aka G_COUNT. This is the number of samples we have read + * from G. Since we're interpolating we need a one-sample + * lookahead, and initializing the count to -1 causes an + * extra fetch and hence 1-sample lookahead. This state is copied + * into the pvshell structure, so we don't need to allocate + * a vector on the heap. + */ + double state[5] = {0, 0, -1, 0, 0}; + state[0] = x; + /* If f and g do not start at the same time, we should really + * should do something about it, but we'll just throw an error. + * Be careful to allow small differences (within one sample). + */ + if (fabs(f->t0 - g->t0) * f->sr > 0.5) { + xlfail("phasevocoder inputs must start at the same time"); + } + /* output the same sample rate and start time as f */ + return snd_make_pvshell("snd_phasevocoder", f->sr, f->t0, + &pv_fetch, f, g, + state, sizeof(state) / sizeof(state[0])); +} |