1 /*
2     memfiles.c:
3 
4     Copyright (C) 1991, 2001 Barry Vercoe, John ffitch, Richard Dobson
5               (C) 2005 Istvan Varga
6 
7     This file is part of Csound.
8 
9     The Csound Library is free software; you can redistribute it
10     and/or modify it under the terms of the GNU Lesser General Public
11     License as published by the Free Software Foundation; either
12     version 2.1 of the License, or (at your option) any later version.
13 
14     Csound is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU Lesser General Public License for more details.
18 
19     You should have received a copy of the GNU Lesser General Public
20     License along with Csound; if not, write to the Free Software
21     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22     02110-1301 USA
23 */
24 
25 #include "csoundCore.h"     /*                              MEMFILES.C      */
26 #include "soundio.h"
27 #include "pvfileio.h"
28 #include "convolve.h"
29 #include "lpc.h"
30 #include "pstream.h"
31 #include "namedins.h"
32 #include <sndfile.h>
33 #include <string.h>
34 #include <inttypes.h>
35 
Load_Het_File_(CSOUND * csound,const char * filnam,char ** allocp,int32 * len)36 static int Load_Het_File_(CSOUND *csound, const char *filnam,
37                           char **allocp, int32 *len)
38 {
39     FILE *f;
40     int length = 1024;
41     int i = 0;
42     int cc;
43     int16 x;
44     char *all;
45     char buffer[16];
46     //void *dummy = 0;
47     f = fopen(filnam, "r");
48     if (f==NULL) {
49       csound->Warning(csound, Str("failed to open file %s\n"), filnam);
50       return NOTOK;
51     }
52     csoundNotifyFileOpened(csound, filnam, CSFTYPE_HETRO, 0, 0);
53     all = (char *)csound->Malloc(csound, (size_t) length);
54     if (6!=fread(buffer, 1, 6, f)) { /* Skip HETRO */
55       csound->Warning(csound, Str("failed to read file %s\n"), filnam);
56       fclose(f);
57       return NOTOK;
58     }
59     //for (i=0; i<6; i++) getc(f); /* Skip HETRO */
60     ignore_value(fgets(buffer, 10, f));         /* number of partials */
61     x = atoi(buffer);
62     memcpy(&all[0], &x, sizeof(int16));
63     /* Read data until end, pack as int16 */
64     for (i=sizeof(int16);;i+=sizeof(int16)) {
65       int p = 0;
66       while ((cc=getc(f))!=',' && cc!='\n' && p<15) {
67         if (cc == EOF) {
68           goto out;
69         }
70         buffer[p++] = cc;
71       }
72       buffer[p]='\0';
73       /* Expand as necessary */
74       if (UNLIKELY(i>=length-4)) all = csound->ReAlloc(csound, all, length+=1024);
75       x = atoi(buffer);
76       memcpy(&all[i], &x, sizeof(int16));
77     }
78  out:
79     fclose(f);                                  /*   and close it      */
80     *len = i;
81     all = csound->ReAlloc(csound, all, i);
82     *allocp = all;
83     return 0;                                   /*   return 0 for OK   */
84 }
85 
read_ieee(FILE * f,int * end)86 static MYFLT read_ieee(FILE* f, int *end)
87 {
88     char buff[120];
89     double x;
90     char *p = fgets(buff, 120, f);
91 
92     if (p==NULL || feof(f)) {
93       *end = 1;
94       return FL(0.0);
95     }
96     x = cs_strtod(buff, NULL);
97     return (MYFLT)x;
98     /* union { */
99     /*   double d; */
100     /*   struct {int  j,k;}  n; */
101     /*   int64_t  i; */
102     /* } x; */
103     /* int sign=1, ex; */
104     /* int64_t man; */
105     /* int64_t bit = 1; */
106     /* char buff[32]; */
107     /* char *p; */
108     /* bit <<= 62; */
109     /* p = fgets(buff, 32, f); */
110     /* printf("... %s", buff); */
111     /* if (p==NULL || feof(f)) { */
112     /*   printf("ending\n"); */
113     /*   *end = 1; */
114     /*   return FL(0.0); */
115     /* } */
116     /* if (strstr(p, "0x0p+0")) { */
117     /*   return FL(0.0); */
118     /* } */
119     /* if (buff[0]=='-') sign=-1; */
120     /* p = strchr(buff, '.')+1; */
121     /* sscanf(p, "%lxp%d", &man, &ex); */
122     /* x.i = man; */
123     /* if (man!=(int64_t)0) x.i |= bit; */
124     /* x.d = ldexp(x.d, ex-1); */
125     /* if (sign<0) x.d =-x.d; */
126     /* return (MYFLT)x.d; */
127 }
128 
Load_CV_File_(CSOUND * csound,const char * filnam,char ** allocp,int32 * len)129 static int Load_CV_File_(CSOUND *csound, const char *filnam,
130                           char **allocp, int32 *len)
131 {
132     FILE *f;
133     int length = 4096;
134     unsigned int i = 0;
135     int          j = 0;
136     MYFLT x;
137     char *all;
138     CVSTRUCT cvh = {0,0,0,0,0.0,0,0,0,0,{0}};
139     char buff[120];
140     char *p;
141     //void *dummy = 0;
142 
143     f = fopen(filnam, "r");
144     csoundNotifyFileOpened(csound, filnam, CSFTYPE_CVANAL, 0, 0);
145     all = (char *)csound->Malloc(csound, (size_t) length);
146     ignore_value(fgets(buff, 120, f)); /* Skip CVANAL */
147     cvh.magic = CVMAGIC;
148     p = fgets(buff, 120, f);
149     if (UNLIKELY(p==NULL)) {
150       fclose(f);
151       return csoundInitError(csound, Str("Ill-formed CV file\n"));
152     }
153     cvh.headBsize = strtol(p, &p, 10);
154     cvh.dataBsize = strtol(p, &p, 10);
155     cvh.dataFormat = strtol(p, &p, 10);
156     cvh.samplingRate = (MYFLT)cs_strtod(p, &p);
157     cvh.src_chnls = strtol(p, &p, 10);
158     cvh.channel = strtol(p, &p, 10);
159     cvh.Hlen = strtol(p, &p, 10);
160     cvh.Format = strtol(p, &p, 10);
161     /* fscanf(f, "%d %d %d %g %d %d %d %d\n",  */
162     /*        &cvh.headBsize, &cvh.dataBsize, &cvh.dataFormat, */
163     /*        &cvh.samplingRate, &cvh.src_chnls, &cvh.channel, */
164     /*        &cvh.Hlen, &cvh.Format); */
165     cvh.headBsize = sizeof(int32)*8 + sizeof(MYFLT);
166     memcpy(&all[0], &cvh, sizeof(CVSTRUCT));
167 
168     /* Read data until end, pack as MYFLTs */
169     for (i=sizeof(CVSTRUCT);;i+=sizeof(MYFLT)) {
170       /* Expand as necessary */
171       if (UNLIKELY(i>=length-sizeof(MYFLT)-4)) {
172         //printf("expanding from %p[%d] to\n", all, length);
173         all = csound->ReAlloc(csound, all, length+=4096);
174         //printf("i=%d                     %p[%d]\n", i, all, length);
175       }      x = read_ieee(f, &j);
176       if (j) break;
177       memcpy(&all[i], &x, sizeof(MYFLT));
178     }
179     fclose(f);                                  /*   and close it      */
180     //printf("length=%d i=%d\n", length, i);
181     *len = i;
182     all = csound->ReAlloc(csound, all, i);
183     *allocp = all;
184     return 0;                                   /*   return 0 for OK   */
185 }
186 
Load_LP_File_(CSOUND * csound,const char * filnam,char ** allocp,int32 * len)187 static int Load_LP_File_(CSOUND *csound, const char *filnam,
188                           char **allocp, int32 *len)
189 {
190     FILE *f;
191     int length = 4096;
192     unsigned int i = 0;
193     int          j = 0;
194     MYFLT x;
195     char *all, *p;
196     LPHEADER lph = {0,0,0,0,0.0,0.0,0.0,{0}};
197     char buff[120];
198     //void *dummy = 0;
199 
200     f = fopen(filnam, "r");
201     csoundNotifyFileOpened(csound, filnam, CSFTYPE_LPC, 0, 0);
202     all = (char *)csound->Malloc(csound, (size_t) length);
203     for (i=0; i<6; i++) fgetc(f); /* Skip LPANAL */
204     if (UNLIKELY(4!=fscanf(f, "%d %d %d %d\n",
205                        &lph.headersize, &lph.lpmagic, &lph.npoles, &lph.nvals))) {
206       fclose(f);
207       return csound->InitError(csound, Str("Ill-formed LPC file\n"));
208     }
209     ignore_value(fgets(buff, 120, f));
210     lph.framrate = (MYFLT)cs_strtod(buff, &p);
211     lph.srate = (MYFLT)cs_strtod(p, &p);
212     lph.duration = (MYFLT)cs_strtod(p, &p);
213     /* lph.text[0] = (char)strtol(p, &p, 0); */
214     /* lph.text[1] = (char)strtol(p, &p, 0); */
215     /* lph.text[2] = (char)strtol(p, &p, 0); */
216     /* lph.text[3] = (char)strtol(p, &p, 0); */
217     /* printf("LPHeader %d %d %d %d\n%f %f %f\n", */
218     /*        lph.headersize, lph.lpmagic, lph.npoles, lph.nvals, */
219     /*        lph.framrate, lph.srate, lph.duration); */
220     /* fscanf(f, "%f %f %f %.2x %.2x %.2x %.2x\n", */
221     /*        &lph.framrate, &lph.srate, &lph.duration, */
222     /*        &lph.text[0], &lph.text[1], &lph.text[2], &lph.text[3]); */
223     // This needs surgery if in/out different MYFLT sizes *** FIX ME ***
224     lph.headersize = sizeof(int32)*4+sizeof(MYFLT)*3;
225     memcpy(&all[0], &lph, lph.headersize);
226 
227     /* Read data until end, pack as MYFLTs */
228     for (i=lph.headersize;;i+=sizeof(MYFLT)) {
229       /* Expand as necessary */
230       if (UNLIKELY(i>=length-sizeof(MYFLT)-8)) {
231         //printf("expanding from %p[%d] to\n", all, length);
232         all = csound->ReAlloc(csound, all, length+=4096);
233         //printf("i=%d                     %p[%d]\n", i, all, length);
234       }
235       x = read_ieee(f, &j);
236       if (j) break;
237       memcpy(&all[i], &x, sizeof(MYFLT));
238     }
239     fclose(f);                                  /*   and close it      */
240     printf("length=%d i=%u\n", length, i);
241     *len = i;
242     all = csound->ReAlloc(csound, all, i);
243     *allocp = all;
244     return 0;                                   /*   return 0 for OK   */
245 }
246 
Load_File_(CSOUND * csound,const char * filnam,char ** allocp,int32 * len,int csFileType)247 static int Load_File_(CSOUND *csound, const char *filnam,
248                        char **allocp, int32 *len, int csFileType)
249 {
250     FILE *f;
251     //void *dummy = 0;
252     *allocp = NULL;
253     f = fopen(filnam, "rb");
254     if (UNLIKELY(f == NULL))                    /* if cannot open the file */
255       return 1;                                 /*    return 1             */
256     if (csFileType==CSFTYPE_HETRO) {
257       char buff[8];
258       ignore_value(fgets(buff, 6, f));
259       if (strcmp(buff, "HETRO")==0) {
260         fclose(f);
261         return Load_Het_File_(csound, filnam, allocp, len);
262       }
263     }
264     else if (csFileType==CSFTYPE_CVANAL) {
265       char buff[8];
266       ignore_value(fgets(buff, 7, f));
267       if (strcmp(buff, "CVANAL")==0) {
268         fclose(f);
269         return Load_CV_File_(csound, filnam, allocp, len);
270       }
271     }
272     else if (csFileType==CSFTYPE_LPC) {
273       char buff[8];
274       ignore_value(fgets(buff, 7, f));
275       if (strcmp(buff, "LPANAL")==0) {
276         fclose(f);
277         return Load_LP_File_(csound, filnam, allocp, len);
278       }
279     }
280     /* notify the host if it asked */
281     csoundNotifyFileOpened(csound, filnam, csFileType, 0, 0);
282     fseek(f, 0L, SEEK_END);                     /* then get its length     */
283     *len = (int32) ftell(f);
284     fseek(f, 0L, SEEK_SET);
285     if (UNLIKELY(*len < 1L))
286       goto err_return;
287     *allocp = csound->Malloc(csound, (size_t) (*len + 1)); /*   alloc as reqd     */
288     if (UNLIKELY(fread(*allocp, (size_t) 1,     /*   read file in      */
289                        (size_t) (*len), f) != (size_t) (*len)))
290       goto err_return;
291     fclose(f);                                  /*   and close it      */
292     (*allocp)[*len] = '\0';                     /*   add sentinel      */
293     return 0;                                   /*   return 0 for OK   */
294  err_return:
295     if (*allocp != NULL) {
296       csound->Free(csound, *allocp);
297       *allocp = NULL;
298     }
299     fclose(f);
300     return 1;
301 }
302 
303 /* Backwards-compatible wrapper for ldmemfile2().
304    Please use ldmemfile2() or ldmemfile2withCB() in all new code instead.
305 MEMFIL *ldmemfile(CSOUND *csound, const char *filnam)
306 {
307     return ldmemfile2withCB(csound, filnam, CSFTYPE_UNKNOWN, NULL);
308 }
309 */
310 /* Takes an additional parameter specifying the type of the file being opened.
311    The type constants are defined in the enumeration CSOUND_FILETYPES.
312    Use ldmemfile2() to load file without additional processing.
313 MEMFIL *ldmemfile2(CSOUND *csound, const char *filnam, int csFileType)
314 {
315     return ldmemfile2withCB(csound, filnam, csFileType, NULL);
316 }
317 */
318 
319 /* This version of ldmemfile2 allows you to specify a callback procedure
320    to process the file's data after it is loaded.  This method ensures that
321    your procedure is only called once even if the file is "loaded" multiple
322    times by several opcodes.  callback can be NULL.
323 
324    Callback signature:     int myfunc(CSOUND* csound, MEMFIL* mfp)
325    Callback return value:  OK (0) or NOTOK (-1)
326  */
ldmemfile2withCB(CSOUND * csound,const char * filnam,int csFileType,int (* callback)(CSOUND *,MEMFIL *))327 MEMFIL *ldmemfile2withCB(CSOUND *csound, const char *filnam, int csFileType,
328                          int (*callback)(CSOUND*, MEMFIL*))
329 {                               /* read an entire file into memory and log it */
330     MEMFIL  *mfp, *last = NULL; /* share the file with all subsequent requests*/
331     char    *allocp = NULL;     /* if not fullpath, look in current directory,*/
332     int32    len = 0;           /*   then SADIR (if defined).                 */
333     char    *pathnam;           /* Used by adsyn, pvoc, and lpread            */
334 
335     mfp = csound->memfiles;
336     while (mfp != NULL) {                               /* Checking chain */
337       if (strcmp(mfp->filename, filnam) == 0)           /*   if match     */
338         return mfp;                                     /*   we have it   */
339       last = mfp;
340       mfp = mfp->next;
341     }
342     /* Add new file description */
343     mfp = (MEMFIL*) csound->Calloc(csound, sizeof(MEMFIL));
344     if (last != NULL)
345       last->next = mfp;
346     else
347       csound->memfiles = mfp;
348     mfp->next = NULL;
349     strNcpy(mfp->filename, filnam, 256);
350 
351     pathnam = csoundFindInputFile(csound, filnam, "SADIR");
352     if (UNLIKELY(pathnam == NULL)) {
353       csoundMessage(csound, Str("cannot load %s\n"), filnam);
354       delete_memfile(csound, filnam);
355       return NULL;
356     }
357     if (UNLIKELY(Load_File_(csound, pathnam, &allocp, &len, csFileType) != 0)) {
358       /* loadfile */
359       csoundMessage(csound, Str("cannot load %s, or SADIR undefined\n"),
360                             pathnam);
361       csound->Free(csound, pathnam);
362       delete_memfile(csound, filnam);
363       return NULL;
364     }
365     /* init the struct */
366     mfp->beginp = allocp;
367     mfp->endp = allocp + len;
368     mfp->length = len;
369     if (callback != NULL) {
370       if (callback(csound, mfp) != OK) {
371         csoundMessage(csound, Str("error processing file %s\n"), filnam);
372         csound->Free(csound, pathnam);
373         delete_memfile(csound, filnam);
374         return NULL;
375       }
376     }
377     csoundMessage(csound, Str("file %s (%ld bytes) loaded into memory\n"),
378                   pathnam, (long) len);
379     csound->Free(csound, pathnam);
380     return mfp;                                          /* rtn new slotadr */
381 }
382 
383 /* clear the memfile array, & free all allocated space */
384 
rlsmemfiles(CSOUND * csound)385 void rlsmemfiles(CSOUND *csound)
386 {
387     MEMFIL  *mfp = csound->memfiles, *nxt;
388 
389     while (mfp != NULL) {
390       nxt = mfp->next;
391       csound->Free(csound, mfp->beginp);       /*   free the space */
392       csound->Free(csound, mfp);
393       mfp = nxt;
394     }
395     csound->memfiles = NULL;
396 }
397 
delete_memfile(CSOUND * csound,const char * filnam)398 int delete_memfile(CSOUND *csound, const char *filnam)
399 {
400     MEMFIL  *mfp, *prv;
401 
402     prv = NULL;
403     mfp = csound->memfiles;
404     while (mfp != NULL) {
405       if (strcmp(mfp->filename, filnam) == 0)
406         break;
407       prv = mfp;
408       mfp = mfp->next;
409     }
410     if (mfp == NULL)
411       return -1;
412     if (prv == NULL)
413       csound->memfiles = mfp->next;
414     else
415       prv->next = mfp->next;
416     csound->Free(csound, mfp->beginp);
417     csound->Free(csound, mfp);
418     return 0;
419 }
420 
421  /* ------------------------------------------------------------------------ */
422 
423 /*  despite basic parity in analysis and synthesis,
424     we still have to rescale the amplitudes
425     by 32768 to fit Csound's notion of 0dBFS.
426     Note we do NOT try to rescale to match the old .pv format.
427 */
428 
429 /* custom version of ldmemfile();
430    enables pvfileio funcs to apply byte-reversal if needed.
431    NB: filename size in MEMFIL struct was only 64; now 256...
432 */
433 
434 /* RWD NB PVOCEX format always 32bit, so no MYFLTs here! */
435 
pvx_err_msg(CSOUND * csound,const char * fmt,...)436 static int pvx_err_msg(CSOUND *csound, const char *fmt, ...)
437 {
438     va_list args;
439     va_start(args, fmt);
440     csound->ErrMsgV(csound, Str("PVOCEX_LoadFile(): error:\n    "), fmt, args);
441     va_end(args);
442     return -1;
443 }
444 
PVOCEX_LoadFile(CSOUND * csound,const char * fname,PVOCEX_MEMFILE * p)445 int PVOCEX_LoadFile(CSOUND *csound, const char *fname, PVOCEX_MEMFILE *p)
446 {
447     PVOCDATA      pvdata;
448     WAVEFORMATEX  fmt;
449     PVOCEX_MEMFILE  *pp;
450     int           i, j, rc = 0, pvx_id, hdr_size, name_size;
451     int32          mem_wanted;
452     int32          totalframes, framelen;
453     float         *pFrame;
454 
455     if (UNLIKELY(fname == NULL || fname[0] == '\0')) {
456       memset(p, 0, sizeof(PVOCEX_MEMFILE));
457       return pvx_err_msg(csound, Str("Empty or NULL file name"));
458     }
459     /* is this file already loaded ? */
460     pp = csound->pvx_memfiles;
461     while (pp != NULL && strcmp(pp->filename, fname) != 0)
462       pp = pp->nxt;
463     if (pp != NULL) {
464       memcpy(p, pp, sizeof(PVOCEX_MEMFILE));
465       return 0;
466     }
467 
468     hdr_size = ((int) sizeof(PVOCEX_MEMFILE) + 7) & (~7);
469     name_size = ((int) strlen(fname) + 8) & (~7);
470     memset(p, 0, sizeof(PVOCEX_MEMFILE));
471     memset(&pvdata, 0, sizeof(PVOCDATA));
472     memset(&fmt, 0, sizeof(WAVEFORMATEX));
473     pvx_id = csound->PVOC_OpenFile(csound, fname, &pvdata, &fmt);
474     if (UNLIKELY(pvx_id < 0)) {
475       return pvx_err_msg(csound, Str("unable to open pvocex file %s: %s"),
476                                  fname, csound->PVOC_ErrorString(csound));
477     }
478     framelen = 2 * pvdata.nAnalysisBins;
479     /* also, accept only 32bit floats for now */
480     if (UNLIKELY(pvdata.wWordFormat != PVOC_IEEE_FLOAT)) {
481       return pvx_err_msg(csound, Str("pvoc-ex file %s is not 32bit floats"),
482                                  fname);
483     }
484     /* FOR NOW, accept only PVOC_AMP_FREQ: later, we can convert */
485     /* NB Csound knows no other: frameFormat is not read anywhere! */
486     if (UNLIKELY(pvdata.wAnalFormat != PVOC_AMP_FREQ)) {
487       return pvx_err_msg(csound, Str("pvoc-ex file %s not in AMP_FREQ format"),
488                                  fname);
489     }
490     /* ignore the window spec until we can use it! */
491     totalframes = csound->PVOC_FrameCount(csound, pvx_id);
492     if (UNLIKELY(totalframes <= 0)) {
493       return pvx_err_msg(csound, Str("pvoc-ex file %s is empty!"), fname);
494     }
495     mem_wanted = totalframes * 2 * pvdata.nAnalysisBins * sizeof(float);
496     /* try for the big block first! */
497     pp = (PVOCEX_MEMFILE*) csound->Malloc(csound, (size_t) (hdr_size + name_size)
498                                            + (size_t) mem_wanted);
499     memset((void*) pp, 0, (size_t) (hdr_size + name_size));
500     pp->filename = (char*) ((uintptr_t) pp + (uintptr_t) hdr_size);
501     pp->nxt = csound->pvx_memfiles;
502     pp->data = (float*) ((uintptr_t) pp + (uintptr_t) (hdr_size + name_size));
503     strcpy(pp->filename, fname);
504     /* despite using pvocex infile, and pvocex-style resynth, we ~still~
505        have to rescale to Csound's internal range! This is because all pvocex
506        calculations assume +-1 floatsam i/o.
507        It seems preferable to do this here, rather than force the user
508        to do so. Csound might change one day...
509      */
510     for (pFrame = pp->data, i = 0; i < totalframes; i++) {
511       rc = csound->PVOC_GetFrames(csound, pvx_id, pFrame, 1);
512       if (UNLIKELY(rc != 1))
513         break;          /* read error, but may still have something to use */
514       /* scale amps to Csound range, to fit fsig */
515       for (j = 0; j < framelen; j += 2) {
516         pFrame[j] *= (float) csound->e0dbfs;
517       }
518       pFrame += framelen;
519     }
520     csound->PVOC_CloseFile(csound, pvx_id);
521     if (UNLIKELY(rc < 0)) {
522       csound->Free(csound, pp);
523       return pvx_err_msg(csound, Str("error reading pvoc-ex file %s"), fname);
524     }
525     if (UNLIKELY(i < totalframes)) {
526       csound->Free(csound, pp);
527       return pvx_err_msg(csound, Str("error reading pvoc-ex file %s "
528                                      "after %d frames"), fname, i);
529     }
530     pp->srate = (MYFLT) fmt.nSamplesPerSec;
531     if (UNLIKELY(pp->srate != csound->esr)) {             /* & chk the data */
532       csound->Warning(csound, Str("%s's srate = %8.0f, orch's srate = %8.0f"),
533                               fname, pp->srate, csound->esr);
534     }
535     pp->nframes = (uint32) totalframes;
536     pp->format  = PVS_AMP_FREQ;
537     pp->fftsize = 2 * (pvdata.nAnalysisBins - 1);
538     pp->overlap = pvdata.dwOverlap;
539     pp->winsize = pvdata.dwWinlen;
540     pp->chans   = fmt.nChannels;
541     switch ((pv_wtype) pvdata.wWindowType) {
542       case PVOC_HAMMING:
543         pp->wintype = PVS_WIN_HAMMING;
544         break;
545       case PVOC_HANN:
546         pp->wintype = PVS_WIN_HANN;
547         break;
548       case PVOC_KAISER:
549         pp->wintype = PVS_WIN_KAISER;
550         break;
551       default:
552         /* deal with all other possibilities later! */
553         pp->wintype = PVS_WIN_HAMMING;
554         break;
555     }
556 
557     /* link into PVOC-EX memfile chain */
558     csound->pvx_memfiles = pp;
559     csound->Message(csound, Str("file %s (%"PRIi32" bytes) loaded into memory\n"),
560                             fname, mem_wanted);
561 
562     memcpy(p, pp, sizeof(PVOCEX_MEMFILE));
563     return 0;
564 }
565 
566  /* ------------------------------------------------------------------------ */
567 
568 /**
569  * Load an entire sound file into memory.
570  * 'fileName' is the file name (searched in the current directory first,
571  * then search path defined by SSDIR, then SFDIR), and sfinfo (optional,
572  * may be NULL) stores the default parameters for opening a raw file.
573  * On success, a pointer to an SNDMEMFILE structure (see csoundCore.h) is
574  * returned, and sound file parameters are stored in sfinfo (assuming that
575  * it is not NULL).
576  * Multiple calls of csoundLoadSoundFile() with the same file name will
577  * share the same SNDMEMFILE structure, and the file is loaded only once
578  * from disk.
579  * The return value is NULL if an error occurs (the contents of sfinfo may
580  * be undefined in this case).
581  */
582 
csoundLoadSoundFile(CSOUND * csound,const char * fileName,void * sfi)583 SNDMEMFILE *csoundLoadSoundFile(CSOUND *csound, const char *fileName, void *sfi)
584 {
585     SF_INFO       *sfinfo = sfi;
586     SNDFILE       *sf;
587     void          *fd;
588     SNDMEMFILE    *p = NULL;
589     SF_INFO       tmp;
590 
591 
592     if (UNLIKELY(fileName == NULL || fileName[0] == '\0'))
593       return NULL;
594 
595     /* check if file is already loaded */
596     if (csound->sndmemfiles != NULL) {
597       p = cs_hash_table_get(csound, csound->sndmemfiles, (char*)fileName);
598     }
599     else {
600       csound->sndmemfiles = cs_hash_table_create(csound);
601     }
602 
603     if (p != NULL) {
604       /* if file was loaded earlier: */
605       if (sfinfo != NULL) {
606         memset(sfinfo, 0, sizeof(SF_INFO));
607         sfinfo->frames = (sf_count_t) p->nFrames;
608         sfinfo->samplerate = ((int) p->sampleRate + 0.5);
609         sfinfo->channels = p->nChannels;
610         sfinfo->format = FORMAT2SF(p->sampleFormat) | TYPE2SF(p->fileType);
611       }
612       return p;
613     }
614     /* open file */
615     if (sfinfo == NULL) {
616       memset(&tmp, 0, sizeof(SF_INFO));
617       sfinfo = &tmp;
618     }
619     fd = csound->FileOpen2(csound, &sf, CSFILE_SND_R, fileName, sfinfo,
620                             "SFDIR;SSDIR", CSFTYPE_UNKNOWN_AUDIO, 0);
621     if (UNLIKELY(fd == NULL)) {
622       csound->ErrorMsg(csound,
623                        Str("csoundLoadSoundFile(): failed to open '%s' %s"),
624                        fileName, Str(sf_strerror(NULL)));
625       return NULL;
626     }
627     p = (SNDMEMFILE*)
628             csound->Malloc(csound, sizeof(SNDMEMFILE)
629                            + (size_t)  sfinfo->frames * sizeof(float));
630     /* set parameters */
631     p->name = (char*) csound->Malloc(csound, strlen(fileName) + 1);
632     strcpy(p->name, fileName);
633     p->fullName = (char*) csound->Malloc(csound,
634                                          strlen(csound->GetFileName(fd)) + 1);
635     strcpy(p->fullName, csound->GetFileName(fd));
636     p->sampleRate = (double) sfinfo->samplerate;
637     p->nFrames = (size_t) sfinfo->frames;
638     p->nChannels = sfinfo->channels;
639     p->sampleFormat = SF2FORMAT(sfinfo->format);
640     p->fileType = SF2TYPE(sfinfo->format);
641     /* set defaults for sampler information */
642     p->loopMode = 0;
643     p->startOffs = 0.0;
644     p->loopStart = 0.0;
645     p->loopEnd = 0.0;
646     p->baseFreq = 1.0;
647     p->scaleFac = 1.0;
648     {
649       SF_INSTRUMENT lpd;
650       if (sf_command(sf, SFC_GET_INSTRUMENT, &lpd, sizeof(SF_INSTRUMENT))
651           != 0) {
652         if (lpd.loop_count > 0 && lpd.loops[0].mode != SF_LOOP_NONE) {
653           /* set loop mode and loop points */
654           p->loopMode = (lpd.loops[0].mode == SF_LOOP_FORWARD ?
655                          2 : (lpd.loops[0].mode == SF_LOOP_BACKWARD ? 3 : 4));
656           p->loopStart = (double) lpd.loops[0].start;
657           p->loopEnd = (double) lpd.loops[0].end;
658         }
659         else {
660           /* loop mode: off */
661           p->loopMode = 1;
662         }
663         p->baseFreq = pow(2.0, (double) (((int) lpd.basenote - 69) * 100
664                                          + (int) lpd.detune) / 1200.0) * csound->A4;
665         p->scaleFac = pow(10.0, (double) lpd.gain * 0.05);
666       }
667     }
668     if (UNLIKELY((size_t) sf_readf_float(sf, &(p->data[0]), (sf_count_t) p->nFrames)
669                  != p->nFrames)) {
670       csound->FileClose(csound, fd);
671       csound->Free(csound, p->name);
672       csound->Free(csound, p->fullName);
673       csound->Free(csound, p);
674       csound->ErrorMsg(csound, Str("csoundLoadSoundFile(): error reading '%s'"),
675                                fileName);
676       return NULL;
677     }
678     p->data[p->nFrames] = 0.0f;
679     csound->FileClose(csound, fd);
680     csound->Message(csound, "%s '%s' (sr = %d Hz, %d %s, %" PRId64 " %s) %s",
681                     Str("File"), p->fullName, sfinfo->samplerate,
682                     sfinfo->channels, Str("channel(s)"), (int64_t)sfinfo->frames,
683                     Str("sample frames"),
684                     Str("loaded into memory\n"));
685 
686     /* link into database */
687     cs_hash_table_put(csound, csound->sndmemfiles, (char*)fileName, p);
688 
689     /* return with pointer to file structure */
690     return p;
691 }
692