mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-22 07:10:06 +02:00
103 lines
3.7 KiB
C
103 lines
3.7 KiB
C
/* 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]));
|
|
}
|