1 /*
2     pvinterp.c:
3 
4     Copyright (C) 1996 Richard Karpen
5 
6     This file is part of Csound.
7 
8     The Csound Library is free software; you can redistribute it
9     and/or modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2.1 of the License, or (at your option) any later version.
12 
13     Csound is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU Lesser General Public License for more details.
17 
18     You should have received a copy of the GNU Lesser General Public
19     License along with Csound; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21     02110-1301 USA
22 */
23 
24 /*****************************************/
25 /*********** PVINTERP, PVCROSS ***********/
26 /******** By Richard Karpen 1996 *********/
27 /*****************************************/
28 
29 #include "pvoc.h"
30 #include <math.h>
31 
32 #define WLN   1         /* time window is WLN*2*ksmps long */
33 #define OPWLEN (2*WLN*CS_KSMPS)    /* manifest used for final time wdw */
34 
35 /************************************************************/
36 /*************PVBUFREAD**************************************/
37 /************************************************************/
38 
pvbufreadset_(CSOUND * csound,PVBUFREAD * p,int32_t stringname)39 int32_t pvbufreadset_(CSOUND *csound, PVBUFREAD *p, int32_t stringname)
40 {
41     char     pvfilnam[MAXNAME];
42     PVOCEX_MEMFILE  pp;
43     int32_t      frInc, chans; /* THESE SHOULD BE SAVED IN PVOC STRUCT */
44 
45     {
46       PVOC_GLOBALS  *p_ = PVOC_GetGlobals(csound);
47       p_->pvbufreadaddr = p;
48     }
49 
50     if (p->auxch.auxp == NULL) {              /* if no buffers yet, alloc now */
51       /* Assumes PVDATASIZE, PVFFTSIZE, PVWINLEN constant */
52       MYFLT *fltp;
53       csound->AuxAlloc(csound,
54                        (PVDATASIZE + PVFFTSIZE * 3 + PVWINLEN) * sizeof(MYFLT),
55                        &p->auxch);
56       fltp = (MYFLT *) p->auxch.auxp;
57       p->lastPhase = fltp;   fltp += PVDATASIZE;    /* and insert addresses */
58       p->fftBuf = fltp;   /* fltp += PVFFTSIZE; */  /* Not needed */
59     }
60 
61     if (stringname==0){
62       if (csound->ISSTRCOD(*p->ifilno))
63         strNcpy(pvfilnam,get_arg_string(csound, *p->ifilno), MAXNAME-1);
64       else csound->strarg2name(csound, pvfilnam, p->ifilno, "pvoc.",0);
65     }
66     else strNcpy(pvfilnam, ((STRINGDAT *)p->ifilno)->data, MAXNAME-1);
67 
68     if (UNLIKELY(csound->PVOCEX_LoadFile(csound, pvfilnam, &pp) != 0))
69       return csound->InitError(csound, Str("PVBUFREAD cannot load %s"),
70                                        pvfilnam);
71 
72     p->frSiz = pp.fftsize;
73     frInc    = pp.overlap;
74     chans    = pp.chans;
75     p->asr   = pp.srate;
76     if (UNLIKELY(p->asr != CS_ESR)) {                /* & chk the data */
77       csound->Warning(csound, Str("%s's srate = %8.0f, orch's srate = %8.0f"),
78                               pvfilnam, p->asr, CS_ESR);
79     }
80     if (UNLIKELY(p->frSiz > PVFRAMSIZE)) {
81       return csound->InitError(csound,
82                                Str("PVOC frame %ld bigger than %ld in %s"),
83                                (long) p->frSiz, (long) PVFRAMSIZE, pvfilnam);
84     }
85     if (UNLIKELY(p->frSiz < 128)) {
86       return csound->InitError(csound,
87                                Str("PVOC frame %ld seems too small in %s"),
88                                (long) p->frSiz, pvfilnam);
89     }
90     if (UNLIKELY(chans != 1)) {
91       return csound->InitError(csound, Str("%d chans (not 1) in PVOC file %s"),
92                                        (int32_t) chans, pvfilnam);
93     }
94     p->frPtr = (float*) pp.data;
95     p->maxFr = pp.nframes - 1;
96     p->frPktim = (MYFLT) CS_KSMPS / (MYFLT) frInc;
97     p->frPrtim = CS_ESR / (MYFLT) frInc;
98     p->prFlg = 1;       /* true */
99     /* amplitude scale for PVOC */
100  /* p->scale = (MYFLT) pp.fftsize * ((MYFLT) pp.fftsize / (MYFLT) pp.winsize);
101   */
102     p->scale = (MYFLT) pp.fftsize * FL(0.5);
103     p->scale *= csound->GetInverseRealFFTScale(csound, pp.fftsize);
104 
105     if (UNLIKELY((OPWLEN / 2 + 1) > PVWINLEN )) {
106       return csound->InitError(csound, Str("ksmps of %d needs wdw of %d, "
107                                            "max is %d for pv %s"),
108                                        CS_KSMPS, (int32_t) (OPWLEN / 2 + 1),
109                                        (int32_t) PVWINLEN, pvfilnam);
110     }
111 
112     return OK;
113 }
114 
pvbufreadset(CSOUND * csound,PVBUFREAD * p)115 int32_t pvbufreadset(CSOUND *csound, PVBUFREAD *p){
116   return pvbufreadset_(csound,p,0);
117 }
118 
pvbufreadset_S(CSOUND * csound,PVBUFREAD * p)119 int32_t pvbufreadset_S(CSOUND *csound, PVBUFREAD *p){
120   return pvbufreadset_(csound,p,1);
121 }
122 
pvbufread(CSOUND * csound,PVBUFREAD * p)123 int32_t pvbufread(CSOUND *csound, PVBUFREAD *p)
124 {
125     MYFLT  frIndx;
126     MYFLT  *buf = p->fftBuf;
127     int32_t    size = pvfrsiz(p);
128 
129     if (UNLIKELY(p->auxch.auxp == NULL)) goto err1;        /* RWD fix */
130     if (UNLIKELY((frIndx = *p->ktimpnt * p->frPrtim) < 0)) goto err2;
131     if (frIndx > (MYFLT) p->maxFr) {    /* not past last one */
132       frIndx = (MYFLT) p->maxFr;
133       if (UNLIKELY(p->prFlg)) {
134         p->prFlg = 0;   /* false */
135         csound->Warning(csound, Str("PVOC ktimpnt truncated to last frame"));
136       }
137     }
138     FetchIn(p->frPtr, buf, size, frIndx);
139     p->buf = buf;
140 
141     return OK;
142  err1:
143     return csound->PerfError(csound, &(p->h),
144                              Str("pvbufread: not initialised"));
145  err2:
146     return csound->PerfError(csound, &(p->h), Str("PVOC timpnt < 0"));
147 }
148 
149 /************************************************************/
150 /*************PVINTERP**************************************/
151 /************************************************************/
pvinterpset_(CSOUND * csound,PVINTERP * p,int32_t stringname)152 int32_t pvinterpset_(CSOUND *csound, PVINTERP *p, int32_t stringname)
153 {
154     uint32_t i;
155     char     pvfilnam[MAXNAME];
156     PVOCEX_MEMFILE  pp;
157     int32_t      frInc, chans; /* THESE SHOULD BE SAVED IN PVOC STRUCT */
158 
159     p->pp = PVOC_GetGlobals(csound);
160     p->pvbufread = p->pp->pvbufreadaddr;
161     if (UNLIKELY(p->pvbufread == NULL))
162       return csound->InitError(csound,
163                                Str("pvinterp: associated pvbufread not found"));
164 
165     if (p->auxch.auxp == NULL) {              /* if no buffers yet, alloc now */
166       MYFLT *fltp;
167       csound->AuxAlloc(csound,
168                        (PVDATASIZE + PVFFTSIZE * 3 + PVWINLEN) * sizeof(MYFLT),
169                        &p->auxch);
170       fltp = (MYFLT *) p->auxch.auxp;
171       p->lastPhase = fltp;   fltp += PVDATASIZE;    /* and insert addresses */
172       p->fftBuf = fltp;      fltp += PVFFTSIZE;
173       p->dsBuf = fltp;       fltp += PVFFTSIZE;
174       p->outBuf = fltp;      fltp += PVFFTSIZE;
175       p->window = fltp;
176     }
177 
178     if (stringname==0){
179       if (csound->ISSTRCOD(*p->ifilno))
180         strNcpy(pvfilnam,get_arg_string(csound, *p->ifilno), MAXNAME-1);
181       else csound->strarg2name(csound, pvfilnam, p->ifilno, "pvoc.",0);
182     }
183     else strNcpy(pvfilnam, ((STRINGDAT *)p->ifilno)->data, MAXNAME-1);;
184     if (UNLIKELY(csound->PVOCEX_LoadFile(csound, pvfilnam, &pp) != 0))
185       return csound->InitError(csound, Str("PVINTERP cannot load %s"),
186                                        pvfilnam);
187 
188     p->frSiz = pp.fftsize;
189     frInc    = pp.overlap;
190     chans    = pp.chans;
191     p->asr   = pp.srate;
192     if (UNLIKELY(p->asr != CS_ESR)) {                /* & chk the data */
193       csound->Warning(csound, Str("%s's srate = %8.0f, orch's srate = %8.0f"),
194                               pvfilnam, p->asr, CS_ESR);
195     }
196     if (UNLIKELY(p->frSiz != p->pvbufread->frSiz)) {
197       return csound->InitError(csound,
198                                Str("pvinterp: %s: frame size %d does not "
199                                    "match pvbufread frame size %d\n"), pvfilnam,
200                                (int32_t) p->frSiz, (int32_t) p->pvbufread->frSiz);
201     }
202     if (UNLIKELY(chans != 1)) {
203       return csound->InitError(csound, Str("%d chans (not 1) in PVOC file %s"),
204                                        (int32_t) chans, pvfilnam);
205     }
206     /* Check that pv->frSiz is a power of two too ? */
207     p->frPtr = (float*) pp.data;
208     p->baseFr = 0;  /* point to first data frame */
209     p->maxFr = pp.nframes - 1;
210     /* highest possible frame index */
211     p->frPktim = (MYFLT) CS_KSMPS / (MYFLT) frInc;
212     /* factor by which to mult expand phase diffs (ratio of samp spacings) */
213     p->frPrtim = CS_ESR / (MYFLT) frInc;
214     /* factor by which to mulitply 'real' time index to get frame index */
215     /* amplitude scale for PVOC */
216  /* p->scale = (MYFLT) pp.fftsize * ((MYFLT) pp.fftsize / (MYFLT) pp.winsize);
217   */
218     p->scale = (MYFLT) pp.fftsize * FL(0.5);
219     p->scale *= csound->GetInverseRealFFTScale(csound, pp.fftsize);
220     /* 2*incr/OPWLEN scales down for win ovlp, windo'd 1ce (but 2ce?) */
221     /* 1/frSiz is the required scale down before (i)FFT */
222     p->prFlg = 1;    /* true */
223     p->opBpos = 0;
224     p->lastPex = FL(1.0);    /* needs to know last pitchexp to update phase */
225     /* Set up time window */
226     memset(p->lastPhase,'\0', pvdasiz(p)*sizeof(MYFLT));
227     /* for (i = 0; i < pvdasiz(p); ++i) {      /\* or maybe pvdasiz(p) *\/ */
228     /*   p->lastPhase[i] = FL(0.0); */
229     /* } */
230     if (UNLIKELY((OPWLEN / 2 + 1) > PVWINLEN)) {
231       return csound->InitError(csound, Str("ksmps of %d needs wdw of %d, "
232                                            "max is %d for pv %s"),
233                                        CS_KSMPS, (OPWLEN / 2 + 1),
234                                        PVWINLEN, pvfilnam);
235     }
236     for (i = 0; i < OPWLEN / 2 + 1; ++i)    /* time window is OPWLEN long */
237       p->window[i] = (MYFLT) (0.5 - 0.5 * cos(TWOPI*(double)i/(double)OPWLEN));
238     /* NB: HANNING */
239     memset(p->outBuf, 0, pvfrsiz(p)*sizeof(MYFLT));
240     /* for (i = 0; i< pvfrsiz(p); ++i) */
241     /*   p->outBuf[i] = FL(0.0); */
242     MakeSinc(p->pp);                    /* sinctab is same for all instances */
243 
244     return OK;
245 }
246 
pvinterpset(CSOUND * csound,PVINTERP * p)247 int32_t pvinterpset(CSOUND *csound, PVINTERP *p){
248   return pvinterpset_(csound,p,0);
249 }
250 
pvinterpset_S(CSOUND * csound,PVINTERP * p)251 int32_t pvinterpset_S(CSOUND *csound, PVINTERP *p){
252   return pvinterpset_(csound,p,1);
253 }
254 
255 
pvinterp(CSOUND * csound,PVINTERP * p)256 int32_t pvinterp(CSOUND *csound, PVINTERP *p)
257 {
258     MYFLT   *ar = p->rslt;
259     MYFLT   frIndx;
260     MYFLT   *buf = p->fftBuf;
261     MYFLT   *buf2 = p->dsBuf;
262     int32_t     asize = pvdasiz(p); /* fix */
263     int32_t     size = pvfrsiz(p);
264     int32_t     buf2Size, outlen;
265     int32_t     circBufSize = PVFFTSIZE;
266     MYFLT   pex, scaleFac = p->scale;
267     PVBUFREAD *q = p->pvbufread;
268     int32    i, j;
269 
270     if (UNLIKELY(p->auxch.auxp == NULL)) goto err1;    /* RWD Fix */
271     pex = *p->kfmod;
272     outlen = (int32_t) (((MYFLT) size) / pex);
273     /* use outlen to check window/krate/transpose combinations */
274     if (UNLIKELY(outlen>PVFFTSIZE))  /* Maximum transposition down is one octave */
275                            /* ..so we won't run into buf2Size problems */
276       goto err2;
277     if (UNLIKELY(outlen<(int32_t)(2*CS_KSMPS)))
278       goto err3;   /* minimum post-squeeze windowlength */
279     buf2Size = OPWLEN;     /* always window to same length after DS */
280     if (UNLIKELY((frIndx = *p->ktimpnt * p->frPrtim) < 0)) goto err4;
281     if (frIndx > (MYFLT)p->maxFr) { /* not past last one */
282       frIndx = (MYFLT)p->maxFr;
283       if (UNLIKELY(p->prFlg)) {
284         p->prFlg = 0;   /* false */
285         csound->Warning(csound, Str("PVOC ktimpnt truncated to last frame"));
286       }
287     }
288     FetchIn(p->frPtr, buf, size, frIndx);
289 
290 /* Here's where the interpolation happens ***********************/
291     if (pex > FL(1.0))
292       scaleFac /= pex;
293     for (i = 0, j = 1; i <= size; i += 2, j += 2) {
294       buf[i] = buf[i] * *p->kampscale2;
295       q->buf[i] = q->buf[i] * *p->kampscale1;
296       buf[j] = buf[j] * *p->kfreqscale2;
297       q->buf[j] = q->buf[j] * *p->kfreqscale1;
298       buf[i] = (buf[i] + ((q->buf[i] - buf[i]) * *p->kampinterp)) * scaleFac;
299       buf[j] = (buf[j] + ((q->buf[j] - buf[j]) * *p->kfreqinterp));
300     }
301 /*******************************************************************/
302     FrqToPhase(buf, asize, pex * (MYFLT) CS_KSMPS, p->asr,
303                (MYFLT) (0.5 * ((pex / p->lastPex) - 1)));
304     /* accumulate phase and wrap to range -PI to PI */
305     RewrapPhase(buf, asize, p->lastPhase);
306 
307     Polar2Real_PVOC(csound, buf, (int32_t) size);
308 
309     if (pex != FL(1.0))
310       UDSample(p->pp, buf,
311                (FL(0.5) * ((MYFLT) size - pex * (MYFLT) buf2Size)), buf2,
312                size, buf2Size, pex);
313     else
314       memcpy(buf2, buf + (int32_t) ((size - buf2Size) >> 1),
315              sizeof(MYFLT) * buf2Size);
316     ApplyHalfWin(buf2, p->window, buf2Size);
317 
318     addToCircBuf(buf2, p->outBuf, p->opBpos, CS_KSMPS, circBufSize);
319     writeClrFromCircBuf(p->outBuf, ar, p->opBpos, CS_KSMPS, circBufSize);
320     p->opBpos += CS_KSMPS;
321     if (UNLIKELY(p->opBpos > circBufSize))
322       p->opBpos -= circBufSize;
323     addToCircBuf(buf2 + CS_KSMPS, p->outBuf, p->opBpos,
324                  buf2Size - CS_KSMPS, circBufSize);
325     p->lastPex = pex;        /* needs to know last pitchexp to update phase */
326 
327     return OK;
328  err1:
329     return csound->PerfError(csound, &(p->h),
330                              Str("pvinterp: not initialised"));
331  err2:
332     return csound->PerfError(csound, &(p->h),
333                              Str("PVOC transpose too low"));
334  err3:
335     return csound->PerfError(csound, &(p->h),
336                              Str("PVOC transpose too high"));
337  err4:
338     return csound->PerfError(csound, &(p->h), Str("PVOC timpnt < 0"));
339 }
340 
341 /************************************************************/
342 /************* PVCROSS **************************************/
343 /************************************************************/
pvcrossset_(CSOUND * csound,PVCROSS * p,int32_t stringname)344 int32_t pvcrossset_(CSOUND *csound, PVCROSS *p, int32_t stringname)
345 {
346     uint32_t i;
347     char     pvfilnam[MAXNAME];
348     PVOCEX_MEMFILE  pp;
349     int32_t      frInc, chans; /* THESE SHOULD BE SAVED IN PVOC STRUCT */
350 
351     p->pp = PVOC_GetGlobals(csound);
352     p->pvbufread = p->pp->pvbufreadaddr;
353     if (UNLIKELY(p->pvbufread == NULL))
354       return csound->InitError(csound,
355                                Str("pvcross: associated pvbufread not found"));
356 
357     if (p->auxch.auxp == NULL) {              /* if no buffers yet, alloc now */
358       MYFLT *fltp;
359       csound->AuxAlloc(csound,
360                        (PVDATASIZE + PVFFTSIZE * 3 + PVWINLEN) * sizeof(MYFLT),
361                        &p->auxch);
362       fltp = (MYFLT *) p->auxch.auxp;
363       p->lastPhase = fltp;   fltp += PVDATASIZE;    /* and insert addresses */
364       p->fftBuf = fltp;      fltp += PVFFTSIZE;
365       p->dsBuf = fltp;       fltp += PVFFTSIZE;
366       p->outBuf = fltp;      fltp += PVFFTSIZE;
367       p->window = fltp;
368     }
369     if (stringname==0){
370       if (csound->ISSTRCOD(*p->ifilno))
371         strNcpy(pvfilnam,get_arg_string(csound, *p->ifilno), MAXNAME-1);
372       else csound->strarg2name(csound, pvfilnam, p->ifilno, "pvoc.",0);
373     }
374     else strNcpy(pvfilnam, ((STRINGDAT *)p->ifilno)->data, MAXNAME-1);
375 
376     if (UNLIKELY(csound->PVOCEX_LoadFile(csound, pvfilnam, &pp) != 0))
377       return csound->InitError(csound, Str("PVCROSS cannot load %s"), pvfilnam);
378 
379     p->frSiz = pp.fftsize;
380     frInc    = pp.overlap;
381     chans    = pp.chans;
382     p->asr   = pp.srate;
383     if (UNLIKELY(p->asr != CS_ESR)) {                /* & chk the data */
384       csound->Warning(csound, Str("%s's srate = %8.0f, orch's srate = %8.0f"),
385                               pvfilnam, p->asr, CS_ESR);
386     }
387     if (UNLIKELY(p->frSiz != p->pvbufread->frSiz)) {
388       return csound->InitError(csound,
389                                Str("pvcross: %s: frame size %d does not "
390                                    "match pvbufread frame size %d\n"), pvfilnam,
391                                (int32_t) p->frSiz, (int32_t) p->pvbufread->frSiz);
392     }
393     if (UNLIKELY(chans != 1)) {
394       return csound->InitError(csound, Str("%d chans (not 1) in PVOC file %s"),
395                                        (int32_t) chans, pvfilnam);
396     }
397     /* Check that pv->frSiz is a power of two too ? */
398     p->frPtr = (float*) pp.data;
399     p->baseFr = 0;  /* point to first data frame */
400     p->maxFr = pp.nframes - 1;
401     /* highest possible frame index */
402     p->frPktim = (MYFLT) CS_KSMPS / (MYFLT) frInc;
403     /* factor by which to mult expand phase diffs (ratio of samp spacings) */
404     p->frPrtim = CS_ESR / (MYFLT) frInc;
405     /* factor by which to mulitply 'real' time index to get frame index */
406     /* amplitude scale for PVOC */
407  /* p->scale = (MYFLT) pp.fftsize * ((MYFLT) pp.fftsize / (MYFLT) pp.winsize);
408   */
409     p->scale = (MYFLT) pp.fftsize * FL(0.5);
410     p->scale *= csound->GetInverseRealFFTScale(csound, pp.fftsize);
411     p->prFlg = 1;    /* true */
412     p->opBpos = 0;
413     p->lastPex = FL(1.0);   /* needs to know last pitchexp to update phase */
414     /* Set up time window */
415     memset(p->lastPhase, '\0', pvdasiz(p)*sizeof(MYFLT));
416     /* for (i = 0; i < pvdasiz(p); ++i) {      /\* or maybe pvdasiz(p) *\/ */
417     /*     p->lastPhase[i] = FL(0.0); */
418     /* } */
419     if (UNLIKELY((OPWLEN / 2 + 1) > PVWINLEN )) {
420       return csound->InitError(csound, Str("ksmps of %d needs wdw of %d, "
421                                            "max is %d for pv %s"),
422                                        CS_KSMPS, (OPWLEN / 2 + 1),
423                                        PVWINLEN, pvfilnam);
424     }
425     for (i = 0; i < OPWLEN / 2 + 1; ++i)    /* time window is OPWLEN long */
426       p->window[i] = (MYFLT) (0.5 - 0.5 * cos(TWOPI*(double)i/(double)OPWLEN));
427     /* NB: HANNING */
428     memset(p->outBuf, 0, pvfrsiz(p)*sizeof(MYFLT));
429     /* for (i = 0; i < pvfrsiz(p); ++i) */
430     /*   p->outBuf[i] = FL(0.0); */
431     MakeSinc(p->pp);                    /* sinctab is same for all instances */
432     if (p->memenv.auxp == NULL || p->memenv.size < pvdasiz(p)*sizeof(MYFLT))
433       csound->AuxAlloc(csound, pvdasiz(p) * sizeof(MYFLT), &p->memenv);
434     return OK;
435 }
436 
pvcrossset(CSOUND * csound,PVCROSS * p)437 int32_t pvcrossset(CSOUND *csound, PVCROSS *p){
438   return pvcrossset_(csound,p,0);
439 }
440 
pvcrossset_S(CSOUND * csound,PVCROSS * p)441 int32_t pvcrossset_S(CSOUND *csound, PVCROSS *p) {
442 return pvcrossset_(csound,p,1);
443 }
444 
pvcross(CSOUND * csound,PVCROSS * p)445 int32_t pvcross(CSOUND *csound, PVCROSS *p)
446 {
447     MYFLT   *ar = p->rslt;
448     MYFLT   frIndx;
449     MYFLT   *buf = p->fftBuf;
450     MYFLT   *buf2 = p->dsBuf;
451     int32_t     asize = pvdasiz(p);         /* fix */
452     int32_t     size = pvfrsiz(p);
453     int32_t     buf2Size, outlen;
454     int32_t     circBufSize = PVFFTSIZE;
455     int32_t     specwp = (int32_t) *p->ispecwp; /* spectral warping flag */
456     MYFLT   pex, scaleFac = p->scale;
457     PVBUFREAD *q = p->pvbufread;
458     int32   i, j;
459     MYFLT   ampscale1 = *p->kampscale1;
460     MYFLT   ampscale2 = *p->kampscale2;
461 
462     if (UNLIKELY(p->auxch.auxp == NULL)) goto err1;        /* RWD Fix */
463     pex = *p->kfmod;
464     outlen = (int32_t) (((MYFLT) size) / pex);
465     /* use outlen to check window/krate/transpose combinations */
466     if (UNLIKELY(outlen>PVFFTSIZE)) /* Maximum transposition down is one octave */
467                                     /* ..so we won't run into buf2Size problems */
468       goto err2;
469     if (UNLIKELY(outlen<(int32_t)(2*CS_KSMPS)))
470       goto err3;               /* minimum post-squeeze windowlength */
471     buf2Size = OPWLEN;     /* always window to same length after DS */
472     if (UNLIKELY((frIndx = *p->ktimpnt * p->frPrtim) < 0)) goto err4;
473     if (frIndx > (MYFLT) p->maxFr) {    /* not past last one */
474       frIndx = (MYFLT) p->maxFr;
475       if (p->prFlg) {
476         p->prFlg = 0;   /* false */
477         csound->Warning(csound, Str("PVOC ktimpnt truncated to last frame"));
478       }
479     }
480 
481     FetchIn(p->frPtr, buf, size, frIndx);
482 
483 /**** Apply amplitudes from pvbufread ********/
484     if (pex > FL(1.0))
485       scaleFac /= pex;
486     for (i = 0, j = 0; i <= size; i += 2, j++)
487       buf[i] = ((buf[i] * ampscale2) + (q->buf[i] * ampscale1)) * scaleFac;
488 /***************************************************/
489 
490     FrqToPhase(buf, asize, pex * (MYFLT) CS_KSMPS, p->asr,
491                (MYFLT) (0.5 * ((pex / p->lastPex) - 1)));
492     /* accumulate phase and wrap to range -PI to PI */
493     RewrapPhase(buf, asize, p->lastPhase);
494 
495     if (specwp == 0 || (p->prFlg)++ == -(int32_t) specwp) {
496       /* ?screws up when prFlg used */
497       /* specwp=0 => normal; specwp = -n => just nth frame */
498 #ifdef BETA
499      if (specwp < 0)
500         csound->Message(csound, Str("PVOC debug: one frame gets through\n"));
501 #endif
502       if (specwp > 0)
503         PreWarpSpec(buf, asize, pex, (MYFLT *)p->memenv.auxp);
504 
505       Polar2Real_PVOC(csound, buf, (int32_t) size);
506 
507       if (pex != FL(1.0))
508         UDSample(p->pp, buf,
509                  (FL(0.5) * ((MYFLT) size - pex * (MYFLT) buf2Size)), buf2,
510                  size, buf2Size, pex);
511       else
512         memcpy(buf2, buf + (int32_t) ((size - buf2Size) >> 1),
513                sizeof(MYFLT) * buf2Size);
514       if (specwp >= 0)
515         ApplyHalfWin(buf2, p->window, buf2Size);
516     }
517     else {
518       memset(buf2, 0, buf2Size*sizeof(MYFLT));
519     }
520 
521     addToCircBuf(buf2, p->outBuf, p->opBpos, CS_KSMPS, circBufSize);
522     writeClrFromCircBuf(p->outBuf, ar, p->opBpos, CS_KSMPS, circBufSize);
523     p->opBpos += CS_KSMPS;
524     if (UNLIKELY(p->opBpos > circBufSize))
525       p->opBpos -= circBufSize;
526     addToCircBuf(buf2 + CS_KSMPS, p->outBuf, p->opBpos,
527                  buf2Size - CS_KSMPS, circBufSize);
528     p->lastPex = pex;       /* needs to know last pitchexp to update phase */
529 
530     return OK;
531  err1:
532     return csound->PerfError(csound, &(p->h),
533                              Str("pvcross: not initialised"));
534  err2:
535     return csound->PerfError(csound, &(p->h),
536                              Str("PVOC transpose too low"));
537  err3:
538     return csound->PerfError(csound, &(p->h),
539                              Str("PVOC transpose too high"));
540  err4:
541     return csound->PerfError(csound, &(p->h),
542                              Str("PVOC timpnt < 0"));
543 }
544