1 /*
2     libsnd_u.c:
3 
4     Copyright (C) 2005 Barry Vercoe, John ffitch, Istvan Varga
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 "csoundCore.h"
25 #include "soundio.h"
26 #include <sndfile.h>
27 
rewriteheader(void * ofd)28 void rewriteheader(void *ofd)
29 {
30     if (LIKELY(ofd != NULL))
31       sf_command((SNDFILE *)ofd, SFC_UPDATE_HEADER_NOW, NULL, 0);
32 }
33 
34 /* Stand-Alone sndgetset() */
35 /* used by SoundAnal progs */
36 /* Returns NULL on failure */
37 
SAsndgetset(CSOUND * csound,char * infilnam,void * ap_,MYFLT * abeg_time,MYFLT * ainput_dur,MYFLT * asr,int channel)38 void *SAsndgetset(CSOUND *csound, char *infilnam, void *ap_,
39                   MYFLT *abeg_time, MYFLT *ainput_dur, MYFLT *asr,
40                   int channel)
41 {
42     SOUNDIN **ap = (SOUNDIN**) ap_;
43     SOUNDIN *p;
44     SNDFILE *infile = NULL;
45 
46     csound->esr = FL(0.0);      /* set esr 0. with no orchestra   */
47     *ap = p = (SOUNDIN*) csound->Calloc(csound, sizeof(SOUNDIN));
48     strNcpy(p->sfname, infilnam, 512); //p->sfname[511] = '\0';
49     if (UNLIKELY(channel < 1 && channel != ALLCHNLS)) {
50       csound->Message(csound, Str("channel request %d illegal\n"), channel);
51       csound->Free(csound, p);
52       *ap = NULL;
53       return NULL;
54     }
55     p->channel = channel;
56     p->analonly = 1;
57     p->sr = (int) (*asr + FL(0.5));
58     p->skiptime = *abeg_time;
59     if ((infile = sndgetset(csound, p)) == NULL)  /* open sndfil, do skiptime */
60       return(NULL);
61     if (UNLIKELY(p->framesrem < (int64_t) 0)) {
62       csound->Warning(csound, Str("undetermined file length, "
63                                   "will attempt requested duration"));
64     }
65     else {
66       if (*ainput_dur <= FL(0.0)) {         /* 0 durtim, use to EOF */
67         p->getframes = p->framesrem;
68         *ainput_dur = (MYFLT) ((double) p->getframes / (double) p->sr);
69       }
70       /* else chk that input dur is within filetime rem */
71       else {
72         p->getframes = (int64_t) ((double) p->sr * (double) *ainput_dur + 0.5);
73         if (UNLIKELY(p->getframes > p->framesrem)) {
74           p->getframes = p->framesrem;
75           csound->Warning(csound, Str("full requested duration not available"));
76         }
77       }
78       csound->Message(csound, Str("analysing %ld sample frames (%3.1f secs)"),
79                               (long) p->getframes, *ainput_dur);
80       if (*abeg_time != FL(0.0))
81         csound->Message(csound, Str(" from timepoint %3.1f\n"), *abeg_time);
82       else
83         csound->Message(csound, "\n");
84     }
85     return (void*) infile;
86 }
87 
88 /* special handling of sound input to accomodate reads thru pipes & net
89  * where nbytes rcvd can be < n requested
90  *
91  * extra arg passed for filetyp testing on POST-HEADER reads of audio samples
92  */
sreadin(CSOUND * csound,SNDFILE * infd,MYFLT * inbuf,int nsamples,SOUNDIN * p)93 static int sreadin(CSOUND *csound, SNDFILE *infd, MYFLT *inbuf,
94                    int nsamples, SOUNDIN *p)
95 {
96     /* return the number of samples read */
97     int   n, ntot = 0;
98     do {
99       n = sf_read_MYFLT(infd, inbuf + ntot, nsamples - ntot);
100       if (UNLIKELY(n < 0))
101         csound->Die(csound, Str("soundfile read error"));
102     } while (n > 0 && (ntot += n) < nsamples);
103     if (p->audrem > (int64_t) 0) {
104       if ((int64_t) ntot > p->audrem)   /*   chk haven't exceeded */
105         ntot = (int) p->audrem;         /*   limit of audio data  */
106       p->audrem -= (int64_t) ntot;
107       return ntot;
108     }
109     return 0;
110 }
111 
112 /* core of soundinset     */
113 /* called from sndinset, SAsndgetset, & gen01 */
114 /* Return NULL on failure */
115 
sndgetset(CSOUND * csound,void * p_)116 void *sndgetset(CSOUND *csound, void *p_)
117 {
118     SOUNDIN *p = (SOUNDIN*) p_;
119     int     n;
120     int     framesinbuf, skipframes;
121     char    *sfname;
122     SF_INFO sfinfo;
123 
124     sfname = &(p->sfname[0]);
125     /* IV - Feb 26 2005: should initialise sfinfo structure */
126     memset(&sfinfo, 0, sizeof(SF_INFO));
127     sfinfo.format = (p->format<0 ?        /* store default sample format, */
128                      ((int) FORMAT2SF(-p->format) | SF_FORMAT_RAW) : 0);
129     sfinfo.channels = 1;                /* number of channels, */
130     if (p->analonly)                    /* and sample rate */
131       sfinfo.samplerate = (int) p->sr;
132     else
133       sfinfo.samplerate = (int) ((double) csound->esr + 0.5);
134     if (sfinfo.samplerate < 1)
135       sfinfo.samplerate = (int) ((double) DFLT_SR + 0.5);
136     /* open with full dir paths */
137     p->fd = csound->FileOpen2(csound, &(p->sinfd), CSFILE_SND_R,
138                                      sfname, &sfinfo, "SFDIR;SSDIR",
139                                      CSFTYPE_UNKNOWN_AUDIO, 0);
140     if (UNLIKELY(p->fd == NULL)) {
141       csound->ErrorMsg(csound, Str("soundin cannot open %s: %s"),
142                        sfname, sf_strerror(NULL));
143       goto err_return;
144     }
145     /* & record fullpath filnam */
146     sfname = csound->GetFileName(p->fd);
147 
148     /* copy type from headata */
149     p->format = SF2FORMAT(sfinfo.format);
150     p->sampframsiz = (int) sfsampsize(sfinfo.format) * (int) sfinfo.channels;
151     p->nchanls = sfinfo.channels;
152     framesinbuf = (int) SNDINBUFSIZ / (int) p->nchanls;
153     p->bufsmps = framesinbuf * p->nchanls;
154     p->endfile = 0;
155     p->filetyp = SF2TYPE(sfinfo.format);
156     if (p->analonly) {                              /* anal: if sr param val */
157       if (p->sr != 0 && p->sr != sfinfo.samplerate) {   /*   use it          */
158         csound->Warning(csound, Str("-s %d overriding soundfile sr %d"),
159                                 (int) p->sr, (int) sfinfo.samplerate);
160         sfinfo.samplerate = p->sr;
161       }
162     }
163     else if (UNLIKELY(sfinfo.samplerate != (int) ((double) csound->esr + 0.5))) {
164       csound->Warning(csound,                       /* non-anal:  cmp w. esr */
165                       "%s sr = %d, orch sr = %7.1f",
166                       sfname, (int) sfinfo.samplerate, csound->esr);
167     }
168     if (UNLIKELY(p->channel != ALLCHNLS && p->channel > sfinfo.channels)) {
169       csound->ErrorMsg(csound, Str("error: req chan %d, file %s has only %d"),
170                                (int) p->channel, sfname, (int) sfinfo.channels);
171       goto err_return;
172     }
173     p->sr = (int) sfinfo.samplerate;
174     if (csound->oparms_.msglevel & 3) {
175       csound->Message(csound, Str("audio sr = %d, "), (int) p->sr);
176       switch (p->nchanls) {
177         case 1: csound->Message(csound, Str("monaural")); break;
178         case 2: csound->Message(csound, Str("stereo"));   break;
179         case 4: csound->Message(csound, Str("quad"));     break;
180         case 6: csound->Message(csound, Str("hex"));      break;
181         case 8: csound->Message(csound, Str("oct"));      break;
182         default: csound->Message(csound, Str("%d-channels"), (int) p->nchanls);
183       }
184       if (p->nchanls > 1) {
185         if (p->channel == ALLCHNLS)
186           csound->Message(csound, Str(", reading %s channels"),
187                                   (p->nchanls == 2 ? Str("both") : Str("all")));
188         else
189           csound->Message(csound, Str(", reading channel %d"),
190                                   (int) p->channel);
191       }
192       csound->Message(csound, Str("\nopening %s infile %s\n"),
193                               type2string(p->filetyp), sfname);
194     }
195     p->audrem = (int64_t) sfinfo.frames * (int64_t) sfinfo.channels;
196     p->framesrem = (int64_t) sfinfo.frames;         /*   find frames rem */
197     skipframes = (int) ((double) p->skiptime * (double) p->sr
198                         + (p->skiptime >= FL(0.0) ? 0.5 : -0.5));
199     if (skipframes < 0) {
200       n = -skipframes;
201       if (UNLIKELY(n > framesinbuf)) {
202         csound->ErrorMsg(csound, Str("soundin: invalid skip time"));
203         goto err_return;
204       }
205       n *= (int) sfinfo.channels;
206       p->inbufp = &(p->inbuf[0]);
207       p->bufend = p->inbufp;
208       do {
209         *(p->bufend++) = FL(0.0);
210       } while (--n);
211     }
212     else if (skipframes < framesinbuf) {        /* if sound within 1st buf */
213       n = sreadin(csound, p->sinfd, p->inbuf, p->bufsmps, p);
214       p->bufend = &(p->inbuf[0]) + n;
215       p->inbufp = &(p->inbuf[0]) + (skipframes * (int) sfinfo.channels);
216       if (p->inbufp >= p->bufend) {
217         p->inbufp = p->bufend;
218         p->audrem = (int64_t) 0;
219         p->endfile = 1;
220       }
221     }
222     else if ((int64_t) skipframes >= p->framesrem) {
223       n = framesinbuf * (int) sfinfo.channels;
224       p->inbufp = &(p->inbuf[0]);
225       p->bufend = p->inbufp;
226       do {
227         *(p->bufend++) = FL(0.0);
228       } while (--n);
229       p->audrem = (int64_t) 0;
230       p->endfile = 1;
231     }
232     else {                                      /* for greater skiptime: */
233       /* else seek to bndry */
234       if (UNLIKELY(sf_seek(p->sinfd, (sf_count_t) skipframes, SEEK_SET) < 0)) {
235         csound->ErrorMsg(csound, Str("soundin seek error"));
236         goto err_return;
237       }
238       /* now rd fulbuf */
239       if ((n = sreadin(csound, p->sinfd, p->inbuf, p->bufsmps, p)) == 0)
240         p->endfile = 1;
241       p->inbufp = p->inbuf;
242       p->bufend = p->inbuf + n;
243     }
244     if (p->framesrem != (int64_t) -1)
245       p->framesrem -= (int64_t) skipframes;     /* sampleframes to EOF   */
246 
247     return p->sinfd;                            /* return the active fd  */
248 
249  err_return:
250     if (p->fd != NULL)
251       csound->FileClose(csound, p->fd);
252     p->sinfd = NULL;
253     p->fd = NULL;
254     return NULL;
255 }
256 
257 /* a simplified soundin */
258 
getsndin(CSOUND * csound,void * fd_,MYFLT * fp,int nlocs,void * p_)259 int getsndin(CSOUND *csound, void *fd_, MYFLT *fp, int nlocs, void *p_)
260 {
261     SNDFILE *fd = (SNDFILE*) fd_;
262     SOUNDIN *p = (SOUNDIN*) p_;
263     int     i = 0, n;
264     MYFLT   scalefac;
265 
266     if (p->format == AE_FLOAT || p->format == AE_DOUBLE) {
267       if (p->filetyp == TYP_WAV || p->filetyp == TYP_AIFF ||
268           p->filetyp == TYP_W64)
269         scalefac = csound->e0dbfs;
270       else
271         scalefac = FL(1.0);
272       if (p->do_floatscaling)
273         scalefac *= p->fscalefac;
274     }
275     else
276       scalefac = csound->e0dbfs;
277 
278     if (p->nchanls == 1 || p->channel == ALLCHNLS) {  /* MONO or ALLCHNLS */
279       for ( ; i < nlocs; i++) {
280         if (p->inbufp >= p->bufend) {
281           if ((n = sreadin(csound, fd, p->inbuf, p->bufsmps, p)) <= 0)
282             break;
283           p->inbufp = p->inbuf;
284           p->bufend = p->inbuf + n;
285         }
286         fp[i] = *p->inbufp++ * scalefac;
287       }
288     }
289     else {                                /* MULTI-CHANNEL, SELECT ONE */
290       int   chcnt;
291       for ( ; i < nlocs; i++) {
292         if (p->inbufp >= p->bufend) {
293           if ((n = sreadin(csound, fd, p->inbuf, p->bufsmps, p)) <= 0)
294             break;
295           p->inbufp = p->inbuf;
296           p->bufend = p->inbuf + n;
297         }
298         chcnt = 0;
299         do {
300           if (++chcnt == p->channel)
301             fp[i] = *p->inbufp * scalefac;
302           p->inbufp++;
303         } while (chcnt < p->nchanls);
304       }
305     }
306 
307     n = i;
308     memset(&(fp[i]), 0, (nlocs-i)*sizeof(MYFLT)); /* if incomplete PAD */
309     /* for ( ; i < nlocs; i++)     /\* if incomplete *\/ */
310     /*   fp[i] = FL(0.0);          /\*  pad with 0's *\/ */
311     return n;
312 }
313 
dbfs_init(CSOUND * csound,MYFLT dbfs)314 void dbfs_init(CSOUND *csound, MYFLT dbfs)
315 {
316     csound->dbfs_to_float = FL(1.0) / dbfs;
317     csound->e0dbfs = dbfs;
318     /* probably want this message written just before note messages start... */
319     csound->Message(csound, Str("0dBFS level = %.1f\n"), dbfs);
320 }
321 
type2string(int x)322 char *type2string(int x)
323 {
324     switch (x) {
325       case TYP_WAV:   return "WAV";
326       case TYP_AIFF:  return "AIFF";
327       case TYP_AU:    return "AU";
328       case TYP_RAW:   return "RAW";
329       case TYP_PAF:   return "PAF";
330       case TYP_SVX:   return "SVX";
331       case TYP_NIST:  return "NIST";
332       case TYP_VOC:   return "VOC";
333       case TYP_IRCAM: return "IRCAM";
334       case TYP_W64:   return "W64";
335       case TYP_MAT4:  return "MAT4";
336       case TYP_MAT5:  return "MAT5";
337       case TYP_PVF:   return "PVF";
338       case TYP_XI:    return "XI";
339       case TYP_HTK:   return "HTK";
340       case TYP_SDS:   return "SDS";
341       case TYP_SD2:   return "SD2";
342       case TYP_FLAC:  return "FLAC";
343       case TYP_CAF:   return "CAF";
344       case TYP_WVE:   return "WVE";
345       case TYP_OGG:   return "OGG";
346       case TYP_MPC2K: return "MPC2K";
347       case TYP_RF64:  return "RF64";
348       default:        return Str("unknown");
349     }
350 }
351 
sfsampsize(int type)352 int sfsampsize(int type)
353 {
354     switch (type & SF_FORMAT_SUBMASK) {
355       case SF_FORMAT_PCM_16:  return 2;     /* Signed 16 bit data */
356       case SF_FORMAT_PCM_32:  return 4;     /* Signed 32 bit data */
357       case SF_FORMAT_FLOAT:   return 4;     /* 32 bit float data */
358       case SF_FORMAT_PCM_24:  return 3;     /* Signed 24 bit data */
359       case SF_FORMAT_DOUBLE:  return 8;     /* 64 bit float data */
360     }
361     return 1;
362 }
363 
getstrformat(int format)364 char *getstrformat(int format)  /* used here, and in sfheader.c */
365 {
366     switch (format) {
367       case  AE_UNCH:    return Str("unsigned bytes"); /* J. Mohr 1995 Oct 17 */
368       case  AE_CHAR:    return Str("signed chars");
369       case  AE_ALAW:    return Str("alaw bytes");
370       case  AE_ULAW:    return Str("ulaw bytes");
371       case  AE_SHORT:   return Str("shorts");
372       case  AE_LONG:    return Str("longs");
373       case  AE_FLOAT:   return Str("floats");
374       case  AE_DOUBLE:  return Str("double floats");
375       case  AE_24INT:   return Str("24bit ints");     /* RWD 5:2001 */
376       case  AE_VORBIS:  return Str("vorbis encoding");
377     }
378     return Str("unknown");
379 }
380 
381 /* type should be one of Csound's TYP_XXX macros,
382    encoding should be one of its AE_XXX macros. */
type2csfiletype(int type,int encoding)383 int type2csfiletype(int type, int encoding)
384 {
385     switch (type) {
386       case TYP_RAW:    return CSFTYPE_RAW_AUDIO;
387       case TYP_IRCAM:  return CSFTYPE_IRCAM;
388       case TYP_AIFF:
389         switch (encoding) {
390           case AE_CHAR:
391           case AE_SHORT:
392           case AE_24INT:
393           case AE_LONG:
394                        return CSFTYPE_AIFF;
395           default:     return CSFTYPE_AIFC;
396         }
397       case TYP_WAV:    return CSFTYPE_WAVE;
398       case TYP_AU:     return CSFTYPE_AU;
399       case TYP_W64:    return CSFTYPE_W64;
400       case TYP_WAVEX:  return CSFTYPE_WAVEX;
401       case TYP_AVR:    return CSFTYPE_AVR;
402       case TYP_HTK:    return CSFTYPE_HTK;
403       case TYP_MAT4:   return CSFTYPE_MAT4;
404       case TYP_MAT5:   return CSFTYPE_MAT5;
405       case TYP_NIST:   return CSFTYPE_NIST;
406       case TYP_PAF:    return CSFTYPE_PAF;
407       case TYP_PVF:    return CSFTYPE_PVF;
408       case TYP_SVX:    return CSFTYPE_SVX;
409       case TYP_VOC:    return CSFTYPE_VOC;
410       case TYP_XI:     return CSFTYPE_XI;
411       case TYP_SDS:    return CSFTYPE_SDS;
412       case TYP_SD2:    return CSFTYPE_SD2;
413       case TYP_FLAC:   return CSFTYPE_FLAC;
414       case TYP_CAF:    return CSFTYPE_CAF;
415       case TYP_WVE:    return CSFTYPE_WVE;
416       case TYP_OGG:    return CSFTYPE_OGG;
417       case TYP_MPC2K:  return CSFTYPE_MPC2K;
418       case TYP_RF64:   return CSFTYPE_RF64;
419       default:         return CSFTYPE_UNKNOWN_AUDIO;
420     }
421 }
422 
423 /* type should be one of libsndfile's format values. */
sftype2csfiletype(int type)424 int sftype2csfiletype(int type)
425 {
426     /* mask out the endian-ness bits */
427     int typemod = type & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK);
428     return type2csfiletype(SF2TYPE(typemod), SF2FORMAT(typemod));
429 }
430