1 /*
2     ugens8.c:
3 
4     Copyright (C) 1991, 1998, 2000 Dan Ellis, Richard Karpen, Richard Dobson
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 #include "pvoc.h"         /*      UGENS8.C        */
25 #include <math.h>
26 
27 /* RWD 10:9:2000 read pvocex file format */
28 #include "pvfileio.h"
29 static int32_t pvx_loadfile(CSOUND *, const char *, PVOC *);
30 
31 /********************************************/
32 /* Originated by Dan Ellis, MIT             */
33 /* Spectral Extraction and Amplitude Gating */
34 /* added by Richard Karpen, University      */
35 /* of Washington, Seattle 1998              */
36 /********************************************/
37 
38 #define WLN   1                         /* time window is WLN*2*ksmps long  */
39 #define OPWLEN (2*WLN*CS_KSMPS)    /* manifest used for final time wdw */
40 
pvset_(CSOUND * csound,PVOC * p,int32_t stringname)41 int32_t pvset_(CSOUND *csound, PVOC *p, int32_t stringname)
42 {
43     uint32_t      i;
44     int32    memsize;
45     char     pvfilnam[MAXNAME];
46     int32_t      size;      /* THESE SHOULD BE SAVED IN PVOC STRUCT */
47     FUNC     *AmpGateFunc = NULL;
48 
49     p->pp = PVOC_GetGlobals(csound);
50 
51      if (stringname==0){
52       if (csound->ISSTRCOD(*p->ifilno))
53         strNcpy(pvfilnam,get_arg_string(csound, *p->ifilno), MAXNAME-1);
54       else csound->strarg2name(csound, pvfilnam, p->ifilno, "pvoc.",0);
55     }
56     else strNcpy(pvfilnam, ((STRINGDAT *)p->ifilno)->data, MAXNAME-1);
57 
58     if (UNLIKELY(pvx_loadfile(csound, pvfilnam, p) != OK))
59       return NOTOK;
60 
61     memsize = (int32) (PVDATASIZE + PVFFTSIZE * 3 + PVWINLEN);
62     if (*p->imode == 1 || *p->imode == 2) {
63       int32  n = (int32) ((p->frSiz + 2L) * (p->maxFr + 2L));
64 #ifdef USE_DOUBLE
65       n = (n + 1L) * (int32) sizeof(float) / (int32) sizeof(double);
66 #endif
67       memsize += n;
68     }
69 
70     if (p->auxch.auxp == NULL || memsize != p->mems) {
71       MYFLT *fltp;
72       csound->AuxAlloc(csound, (memsize * sizeof(MYFLT)), &p->auxch);
73       fltp = (MYFLT *) p->auxch.auxp;
74       p->lastPhase = fltp;   fltp += PVDATASIZE;    /* and insert addresses */
75       p->fftBuf = fltp;      fltp += PVFFTSIZE;
76       p->dsBuf = fltp;       fltp += PVFFTSIZE;
77       p->outBuf = fltp;      fltp += PVFFTSIZE;
78       p->window = fltp;
79       if (*p->imode == 1 || *p->imode == 2) {
80         fltp += PVWINLEN;
81         p->pvcopy = (float*) ((void*) fltp);
82       }
83     }
84     p->mems = memsize;
85     p->frPktim = ((MYFLT)CS_KSMPS)/((MYFLT) p->frInc);
86     /* factor by which to mult expand phase diffs (ratio of samp spacings) */
87     p->frPrtim = CS_ESR/((MYFLT) p->frInc);
88     /* factor by which to mulitply 'real' time index to get frame index */
89     size = pvfrsiz(p);          /* size used in def of OPWLEN ? */
90     /* 2*incr/OPWLEN scales down for win ovlp, windo'd 1ce (but 2ce?) */
91     /* 1/frSiz is the required scale down before (i)FFT */
92     p->prFlg = 1;    /* true */
93     p->opBpos = 0;
94     p->lastPex = FL(1.0);     /* needs to know last pitchexp to update phase */
95     /* Set up time window */
96     memset(p->lastPhase, 0, sizeof(MYFLT)*pvdasiz(p));
97     /* for (i=0; i < pvdasiz(p); ++i) {  /\* or maybe pvdasiz(p) *\/ */
98     /*   p->lastPhase[i] = FL(0.0); */
99     /* } */
100     if (UNLIKELY((OPWLEN/2 + 1)>PVWINLEN )) {
101       return csound->InitError(csound, Str("ksmps of %d needs wdw of %d, "
102                                            "max is %d for pv %s"),
103                                        CS_KSMPS, (OPWLEN/2 + 1), PVWINLEN,
104                                        pvfilnam);
105     }
106 
107     if (*p->igatefun > 0)
108       if (UNLIKELY((AmpGateFunc = csound->FTnp2Find(csound, p->igatefun)) == NULL))
109         return NOTOK;
110     p->AmpGateFunc = AmpGateFunc;
111 
112     if (*p->igatefun > 0)
113       p->PvMaxAmp = PvocMaxAmp(p->frPtr, size, p->maxFr);
114 
115     if (*p->imode == 1 || *p->imode == 2) {
116       SpectralExtract(p->frPtr, p->pvcopy, size, p->maxFr,
117                       (int32_t) *p->imode, *p->ifreqlim);
118       p->frPtr = p->pvcopy;
119     }
120 
121     for (i=0; i < OPWLEN / 2 + 1; ++i)  /* time window is OPWLEN long */
122       p->window[i] = (FL(0.5) - FL(0.5) * COS(TWOPI_F*(MYFLT)i/(MYFLT)OPWLEN));
123     /* NB: HANNING */
124     memset(p->outBuf, 0, sizeof(MYFLT)*pvfrsiz(p));
125     /* for (i=0; i< pvfrsiz(p); ++i) */
126     /*   p->outBuf[i] = FL(0.0); */
127     MakeSinc(p->pp);                    /* sinctab is same for all instances */
128 
129     if (p->memenv.auxp == NULL || p->memenv.size < pvdasiz(p)*sizeof(MYFLT))
130         csound->AuxAlloc(csound, pvdasiz(p) * sizeof(MYFLT), &p->memenv);
131 
132     return OK;
133 }
134 
pvset(CSOUND * csound,PVOC * p)135 int32_t pvset(CSOUND *csound, PVOC *p){
136   return pvset_(csound,p,0);
137 }
138 
pvset_S(CSOUND * csound,PVOC * p)139 int32_t pvset_S(CSOUND *csound, PVOC *p){
140   return pvset_(csound,p,1);
141 }
142 
143 
pvoc(CSOUND * csound,PVOC * p)144 int32_t pvoc(CSOUND *csound, PVOC *p)
145 {
146     MYFLT  *ar = p->rslt;
147     MYFLT  frIndx;
148     MYFLT  *buf = p->fftBuf;
149     MYFLT  *buf2 = p->dsBuf;
150     int32_t    asize = pvdasiz(p);  /* new */
151     int32_t    size = pvfrsiz(p);
152     int32_t    buf2Size, outlen;
153     int32_t    circBufSize = PVFFTSIZE;
154     int32_t    specwp = (int32_t)*p->ispecwp;   /* spectral warping flag */
155     MYFLT  pex, scaleFac;
156     uint32_t offset = p->h.insdshead->ksmps_offset;
157     uint32_t early  = p->h.insdshead->ksmps_no_end;
158     uint32_t i, nsmps = CS_KSMPS;
159 
160     if (UNLIKELY(p->auxch.auxp == NULL)) goto err1;
161     pex = *p->kfmod;
162     outlen = (int32_t) (((MYFLT) size) / pex);
163     /* use outlen to check window/krate/transpose combinations */
164     if (UNLIKELY(outlen>PVFFTSIZE))  /* Maximum transposition down is one octave */
165       goto err2;           /* ..so we won't run into buf2Size problems */
166     if (UNLIKELY(outlen<(int32_t)(2*nsmps))) /* minimum post-squeeze windowlength */
167       goto err3;
168     buf2Size = OPWLEN;       /* always window to same length after DS */
169     if (UNLIKELY((frIndx = *p->ktimpnt * p->frPrtim) < 0)) goto err4;
170     if (frIndx > p->maxFr) {  /* not past last one */
171       frIndx = (MYFLT)p->maxFr;
172       if (UNLIKELY(p->prFlg)) {
173         p->prFlg = 0;   /* false */
174         csound->Warning(csound, Str("PVOC ktimpnt truncated to last frame"));
175       }
176     }
177     FetchIn(p->frPtr, buf, size, frIndx);
178 
179     if (*p->igatefun > 0)
180       PvAmpGate(buf,size, p->AmpGateFunc, p->PvMaxAmp);
181 
182     FrqToPhase(buf, asize, pex * (MYFLT) nsmps, p->asr,
183                FL(0.5) * ((pex / p->lastPex) - FL(1.0)));
184     /* accumulate phase and wrap to range -PI to PI */
185     RewrapPhase(buf, asize, p->lastPhase);
186 
187     if (specwp > 0){
188       /* RWD: THIS CAUSED MASSIVE MEMORY ERROR, BUT DOESN'T WORK ANYWAY */
189         PreWarpSpec(buf, asize, pex, (MYFLT *)p->memenv.auxp);
190      }
191 
192     Polar2Real_PVOC(csound, buf, size);
193 
194     if (pex != FL(1.0))
195       UDSample(p->pp, buf, (FL(0.5) * ((MYFLT) size - pex * (MYFLT) buf2Size)),
196                buf2, size, buf2Size, pex);
197     else
198       memcpy(buf2, buf + (int32_t) ((size - buf2Size) >> 1),
199              sizeof(MYFLT) * buf2Size);
200     ApplyHalfWin(buf2, p->window, buf2Size);
201     addToCircBuf(buf2, p->outBuf, p->opBpos, nsmps, circBufSize);
202     writeClrFromCircBuf(p->outBuf, ar, p->opBpos, nsmps, circBufSize);
203     p->opBpos += nsmps;
204     if (UNLIKELY(p->opBpos > circBufSize))
205       p->opBpos -= circBufSize;
206     addToCircBuf(buf2 + nsmps, p->outBuf,
207                  p->opBpos, buf2Size - nsmps, circBufSize);
208     p->lastPex = pex;        /* needs to know last pitchexp to update phase */
209     /* scale output */
210     scaleFac = p->scale;
211     if (pex > FL(1.0))
212       scaleFac /= pex;
213     if (UNLIKELY(offset)) memset(p->rslt, '\0', offset*sizeof(MYFLT));
214     if (UNLIKELY(early)) {
215       nsmps -= early;
216       memset(&p->rslt[nsmps], '\0', early*sizeof(MYFLT));
217     }
218     for (i = offset; i < nsmps; i++)
219       p->rslt[i] *= scaleFac;
220 
221     return OK;
222  err1:
223     return csound->PerfError(csound, &(p->h), Str("pvoc: not initialised"));
224  err2:
225     return csound->PerfError(csound, &(p->h),
226                              Str("PVOC transpose too low"));
227  err3:
228     return csound->PerfError(csound, &(p->h),
229                              Str("PVOC transpose too high"));
230  err4:
231     return csound->PerfError(csound, &(p->h),
232                              Str("PVOC timpnt < 0"));
233 }
234 
235 /* RWD 8:2001: custom version of ldmemfile();
236    enables pvfileio funcs to apply byte-reversal if needed.
237 
238   this version applies scaling to match  existing  pvanal format
239  */
pvx_loadfile(CSOUND * csound,const char * fname,PVOC * p)240 static int32_t pvx_loadfile(CSOUND *csound, const char *fname, PVOC *p)
241 {
242     PVOCEX_MEMFILE  pp;
243 
244     if (UNLIKELY(csound->PVOCEX_LoadFile(csound, fname, &pp) != 0)) {
245       return csound->InitError(csound, Str("PVOC cannot load %s"), fname);
246     }
247     /* fft size must be <= PVFRAMSIZE (=8192) for Csound */
248     if (UNLIKELY(pp.fftsize > PVFRAMSIZE)) {
249       return csound->InitError(csound, Str("pvoc-ex file %s: "
250                                            "FFT size %d too large for Csound"),
251                                fname, (int32_t) pp.fftsize);
252     }
253     /* have to reject m/c files for now, until opcodes upgraded */
254     if (UNLIKELY(pp.chans > 1)) {
255       return csound->InitError(csound, Str("pvoc-ex file %s is not mono"), fname);
256     }
257     /* ignore the window spec until we can use it! */
258     p->frSiz    = pp.fftsize;
259     p->frPtr    = (float*) pp.data;
260     p->baseFr   = 0;  /* point to first data frame */
261     /* highest possible frame index */
262     p->maxFr    = pp.nframes - 1;
263     p->frInc    = pp.overlap;
264     p->chans    = pp.chans;
265     p->asr      = pp.srate;
266     /* amplitude scale for PVOC */
267  /* p->scale = (MYFLT) pp.fftsize * ((MYFLT) pp.fftsize / (MYFLT) pp.winsize);
268   */
269     p->scale = (MYFLT) pp.fftsize * FL(0.5);
270     p->scale *= csound->GetInverseRealFFTScale(csound, pp.fftsize);
271 
272     return OK;
273 }
274 
275