/* RWD version to support pvx files */ /* * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd * http://people.bath.ac.uk/masrwd * http://www.composersdesktop.com * This file is part of the CDP System. The CDP System is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The CDP System is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the CDP System; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * Sound Filing System - Buffered sound system, open/close, etc. * * Portable version * * Copyright M. C. Atkins, 1986, 1987, 1993; RWD 2007,2014,2022 * All Rights Reserved. */ /* RWD: old sfsys functions DEPRECATED and emptied */ /* RWD Dec 2019 fixed SndCloseEx for int64 comps */ /* RWD 2022 added PVX support */ #ifdef _DEBUG #include #endif #include #include #include #include #include "sffuncs.h" #ifdef unix #include #endif #ifdef ENABLE_PVX # ifdef unix # include "wavdefs.h" # include "pvdefs.h" # endif # include "pvfileio.h" # ifdef _DEBUG # include # endif #endif #ifdef _WIN32 # ifdef _MSC_VER # if _MSC_VER <=1200 static __inline int cdp_round(double fval) { int result; asm{ fld fval fistp result mov eax,result /* RWD: need this line? */ } return result; } # endif #else # ifndef __GNUWIN32__ static int cdp_round(double val) { int k; k = (int)(fabs(val)+0.5); if(val < 0.0) k = -k; return k; } # endif #endif #endif /* * This is the main structure describing a procom file */ struct sndfile { int flags; /* various flags */ int fd; /* SFfile/gemfile file descripter */ #ifdef ENABLE_PVX int pvxid; #endif char *buffer; /* pointer to the buffer */ char *next; /* next entry in buffer to access */ int bufsiz; /* the size of the buffer (in sectors) */ int remain; /* number of samples remaining in buffer */ char *endbuf; /* end of buffer (for short bufs at eof) */ int samptype; /* the type of samples in the file */ int lastread; /* number of bytes obtained from last read */ int true_bufsize; /*for 24bit alignment, etc: adjust buffer downwards*/ int scale_floats; double fscalefac; }; typedef union { int lsamp; /*float fsamp; //any use for this? */ unsigned char bytes[4]; } SND_SAMP; /* * values for sndfile.flags */ #define SNDFLG_WRITTEN (1) /* the buffer must be flushed back to disk */ #define SNDFLG_LASTWR (4) /* last i/o was a write (for sndtell) */ #define SNDFLG_TRUNC (8) /* truncate when closed */ #define SNDFLG_USERBUF (32) /* the buffer was supplied by the user */ #define SNDFLG_ATEND (64) /* the last i/o went over the end of the file */ /* * Various constants/global data */ #define SNDFDBASE (4000) /* sndfd's allocated from here */ #define SNDBUFSIZE (64) /* * global storage */ /* * The array of open sndfiles */ //#if defined ENABLE_PVX //static struct sndfile *sndfiles[MAXSNDFILES]; //#else static struct sndfile *sndfiles[MAXSNDFILES]; //#endif /* * mapping table from sample type, to size */ /* the possible sample types are now: SAMP_SHORT (0) * 16-bit short integers * SAMP_FLOAT (1) * 32-bit (IEEE) floating point * SAMP_BYTE (2) //recognize 8bit soundfiles? SAMP_LONG (3) // four most likely formats SAMP_2424 (4) SAMP_2432 (5) SAMP_2024 (6) SAMP_MASKED (7) //some weird WAVE_EX format! */ /*RWD.6.99 set this as container-size *private, but sfsys.c needs to have it */ int sampsize[] = { /* SAMP_SHORT */ sizeof(short), /* SAMP_FLOAT */ sizeof(float), /* SAMP_BYTE */ sizeof(unsigned char), /* SAMP_LONG */ sizeof(int), /* SAMP_2424 */ 3 * sizeof(unsigned char), /* SAMP_2432 */ sizeof(int), /* SAMP_2024 */ 3 * sizeof(unsigned char), /* dummy */ 0 /*for masked formats, have to calc from 1st principles...*/ }; /* * Utility routines */ #define getmem(type) ((type *)malloc(sizeof(type))) extern int _rsf_getmaxpeak(int sfd,float *peak); extern int _rsf_getbitmask(int sfd); /* * initialisation/termination routines */ static void rsndallclose(void) { int fd; for(fd = 0; fd < MAXSFNAME; fd++) if(sndfiles[fd] != 0) sndcloseEx(fd + SNDFDBASE); //RWD was plain sndclose() #if defined ENABLE_PVX pvsys_release(); #endif } static void rsndinit() { register int fd; static int inited = 0; if(inited != 0) return; inited++; for(fd = 0; fd < MAXSFNAME; fd++) sndfiles[fd] = 0; atexit(rsndallclose); //RWD if we need to be reentrant, we can't use this! } /* * map a sndfd onto a sndfiles index, * return 0 on failure */ /*RWD I want a public version of this, for use in ASSERT etc*/ static int mapsndfd(int *sfdp) { *sfdp -= SNDFDBASE; if(*sfdp < 0 || *sfdp >= MAXSNDFILES) { rsferrno = ESFBADADDR; rsferrstr = "sfd is not in valid range"; return 0; } if(sndfiles[*sfdp] == 0) { rsferrno = ESFNOTOPEN; rsferrstr = "sndfile is not open (bad sndfd)"; return 0; } return 1; } /* * do the underlying seek * NOTE * seeks to the end of an sffile are rounded down!! */ static int doseek(struct sndfile *sf, int dist, int whence) { return sfseek(sf->fd, dist, whence); } /*RWD Feb 2010 ??? */ //static long //doseekEx(struct sndfile *sf, long dist, int whence) //{ // return sfseek_buffered(sf->fd, dist, whence); //} static __int64 doseekEx(struct sndfile *sf, __int64 dist, int whence) { return sfseek_buffered(sf->fd, dist, whence); } /* * sndfilbuf - fill a buffer, for reading * return true, if an error occurs */ static int sndfilbuf(struct sndfile *sfd) { long want /* = SECSIZE * sfd->bufsiz*/ ; /*RWD: almost a const!*/ long got; int rc = 0; if(sfd->flags & SNDFLG_ATEND) return 0; want = sfd->true_bufsize; /*only difference - supports other sample sizes*/ got = sfread_buffered(sfd->fd, sfd->buffer, want); if(got < 0) { rc = 1; got = 0; } if(got < want) sfd->flags |= SNDFLG_ATEND; sfd->lastread = got; /*bytes of samps*/ sfd->flags &= ~(SNDFLG_WRITTEN|SNDFLG_LASTWR); sfd->endbuf = &sfd->buffer[got]; /*RWD NB all snd funcs think in SAMPLES, of 2 types: SHORTS or FLOATS; all 8bit stuff hidden in sf_calls*/ sfd->remain = got / sampsize[sfd->samptype]; /*RWD : adjust for 8bit files...*/ sfd->next = sfd->buffer; return rc; } /* * sndflsbuf - flush a buffer, after writing * return true, if an error occurs */ static int sndflsbuf(struct sndfile *sfd) { int toput = sfd->endbuf - sfd->buffer; int put; if(sfd->next == sfd->buffer) return 0; if(sfd->flags & SNDFLG_ATEND) return 0; sfd->next = sfd->buffer; sfd->flags &= ~SNDFLG_WRITTEN; sfd->endbuf = sfd->buffer; if(!(sfd->flags&SNDFLG_LASTWR) ) { if(doseek(sfd, -sfd->lastread, 1) < 0) { sfd->flags |= SNDFLG_LASTWR; sfd->remain = (sfd->bufsiz<samptype]; return 1; } } sfd->flags |= SNDFLG_LASTWR; sfd->remain = (sfd->bufsiz<samptype]; toput = (toput+SECSIZE-1)&~(SECSIZE-1); if((put = sfwrite(sfd->fd, sfd->buffer, toput)) < 0) return 1; if(put < toput) { sfd->flags |= SNDFLG_ATEND; sfd->remain = 0; } return 0; } static int sndflsbufEx(struct sndfile *sfd) { int toput = sfd->endbuf - sfd->buffer; int put; if(sfd->next == sfd->buffer) return 0; if(sfd->flags & SNDFLG_ATEND) return 0; sfd->next = sfd->buffer; sfd->flags &= ~SNDFLG_WRITTEN; sfd->endbuf = sfd->buffer; if(!(sfd->flags&SNDFLG_LASTWR) ) { if(doseekEx(sfd,(__int64) -sfd->lastread, 1) < 0) { sfd->flags |= SNDFLG_LASTWR; sfd->remain = sfd->true_bufsize / sampsize[sfd->samptype]; return 1; } } sfd->flags |= SNDFLG_LASTWR; sfd->remain = sfd->true_bufsize / sampsize[sfd->samptype]; if((put = sfwrite_buffered(sfd->fd, sfd->buffer, toput)) < 0) return 1; if(put < toput) { sfd->flags |= SNDFLG_ATEND; sfd->remain = 0; } return 0; } /* * free the sample buffer, if it wasn't supplied by a sndsetbuf */ static void freesndbuf(int fd) { if(sndfiles[fd]->flags & SNDFLG_USERBUF) return; free(sndfiles[fd]->buffer); } /* * free the memory for an open SFfile * used on last close, and failure of creat */ static void freesndfd(int fd) { freesndbuf(fd); free((char *)sndfiles[fd]); sndfiles[fd] = 0; return; } /* * Try to find a snd file descripter * returns -1, if there aren't any */ static int findsndfd() { register int fd = 0; while(sndfiles[fd] != 0) if(++fd >= MAXSNDFILES) { rsferrno = ESFNOSFD; rsferrstr = "Too many sndfiles are already open"; return -1; } rsferrstr = "no memory to open sndfile"; rsferrno = ESFNOMEM; if((sndfiles[fd] = getmem(struct sndfile)) == 0) return -1; if((sndfiles[fd]->buffer = (char*) malloc(SNDBUFSIZE*SECSIZE)) == 0) { free((char *)sndfiles[fd]); sndfiles[fd] = 0; return -1; } #ifdef ENABLE_PVX sndfiles[fd]->pvxid = -1; #endif sndfiles[fd]->flags = 0; sndfiles[fd]->bufsiz = SNDBUFSIZE; sndfiles[fd]->lastread = 0; sndfiles[fd]->endbuf = sndfiles[fd]->next = sndfiles[fd]->buffer; sndfiles[fd]->true_bufsize = SNDBUFSIZE * SECSIZE; /*may get rounded down for 24bit formats*/ return fd; } /* * The user-accessable routines */ /* * open a sndfile */ int sndopenEx(const char *fn,int auto_scale, int access) { register struct sndfile *sf; int fd; #ifdef ENABLE_PVX PVOCDATA pvxdata; int rc; #endif rsndinit(); if((fd = findsndfd()) < 0) return -1; sf = sndfiles[fd]; if((sf->fd = sfopenEx(fn,access)) < 0) { freesndfd(fd); return -1; } /* RWD TODO: add pvx support here_ sfopenEx can produce one */ #ifdef ENABLE_PVX if(sf_ispvx(sf->fd)){ if((rc = get_pvxfd(sf->fd ,&pvxdata )) < 0){ # ifdef _DEBUG fprintf(stderr,"sndopenEx: bad sf id\n"); # endif return -1; } else if (rc >= 0){ # ifdef _DEBUG fprintf(stderr,"sndopenEx: file %s is a pvx file!\n", fn); # endif return fd + SNDFDBASE; } } // else its a soundfile or a .ana file, carry on as usual #endif /* all the rest just for standard CDP files */ sf->remain = 0; if(sfgetprop(sf->fd, "sample type", (char *)&sf->samptype, sizeof(int)) != sizeof(int)) { rsferrno = ESFNOSTYPE; rsferrstr = "no sample type defined"; freesndfd(fd); return -1; } /*RWD only used to rescale floatsams when read into shorts (eg for playback)*/ sf->scale_floats = 0; sf->fscalefac = 1.0; if(auto_scale && (sf->samptype==SAMP_FLOAT)){ float fac = 0.0f; /* RWD 4:2002: I would prefer to rely on PEAK only for this now... */ if(sfgetprop(sf->fd,"maxamp",(char *)&fac,sizeof(float)) == sizeof(float)){ if(fac > sf->fscalefac){ sf->scale_floats = 1; sf->fscalefac = 0.99995 / (double) fac; } } /*get it from PEAK data if available*/ else if (_rsf_getmaxpeak(sf->fd,&fac) > 0){ if(fac > sf->fscalefac){ sf->scale_floats = 1; sf->fscalefac = 0.99995 / (double) fac; } } /*Could also provide a set_autoscale() func, legal only before first read (like sndsetbuf)*/ } /*adjust internal buffer if reading 24bit formats*/ if(sf->samptype==SAMP_2424 || sf->samptype == SAMP_2024) sf->true_bufsize = (sf->true_bufsize / sampsize[sf->samptype]) * sampsize[sf->samptype]; /* need superfluous seek to overcome daft OS problems reopening just-closed file */ if(sndseekEx(fd+SNDFDBASE,0,0) != 0) return -1; return fd+SNDFDBASE; } /* RWD PVX note: this is the form called by CDP for ANAL files */ /* Also, NB stype is according to the SAMP_* list, gets converted by sampsize[] for sf routines as here */ int sndcreat_formatted(const char *fn, int size, int stype,int channels, int srate,cdp_create_mode mode) { register struct sndfile *sf; int fd; rsndinit(); if((fd = findsndfd()) < 0) return -1; sf = sndfiles[fd]; sf->flags |= SNDFLG_TRUNC; sf->samptype = stype; /*RWD NB sampsize[] - no slot for 24bit size yet*/ /* ALSO: at this stage, CDP still assumes 32bits if an analysis file, no "original sampsize" info yet - we have to rely on sfputprop("original sampsize" being set by program */ /* if pvx file, sets sffile->pvxfileno (which may be 0 ), but returns sf id (which may be 1) */ if((sf->fd = sfcreat_formatted(fn, size*sampsize[stype], (__int64 *)0,channels,srate, stype,mode)) < 0) { freesndfd(fd); return -1; } // now need to set sf->pvxid, if this is a pvx file. sf->remain = sf->true_bufsize / sampsize[stype]; return fd+SNDFDBASE; } /* RWD PVX: Trouble is, we need to use this one, to get props */ /* means just one change (???!!!???) inside cdp2k, mainfunc.c */ int sndcreat_ex(const char *name, int size,SFPROPS *props,int min_header,cdp_create_mode mode) { register struct sndfile *sf; int fd; int smpsize; rsndinit(); if((fd = findsndfd()) < 0) return -1; sf = sndfiles[fd]; sf->flags |= SNDFLG_TRUNC; /* RWD.6.99 write 24bit formats, etc */ switch(props->samptype){ case(FLOAT32): sf->samptype = SAMP_FLOAT; break; case(SHORT16): sf->samptype = SAMP_SHORT; break; case(INT_32): sf->samptype = SAMP_LONG; break; case(INT2424): sf->samptype = SAMP_2424; /*adjust sndbuffer with 3-byte alignment*/ sf->true_bufsize = (sf->true_bufsize / sampsize[sf->samptype]) * sampsize[sf->samptype]; break; case(INT2024): sf->samptype = SAMP_2024; /*adjust sndbuffer with 3-byte alignment*/ sf->true_bufsize = (sf->true_bufsize / sampsize[sf->samptype]) * sampsize[sf->samptype]; break; case(SAMP_MASKED): /*don't know how to do this yet...*/ rsferrno = ESFBADPARAM; rsferrstr = "cannot write masked sample type"; return -1; default: rsferrno = ESFBADPARAM; rsferrstr = "unknown sample type"; return -1; break; } /*sf->samptype = (props->samptype == FLOAT32 ? SAMP_FLOAT : SAMP_SHORT);*/ smpsize = sampsize[sf->samptype]; /*need to return to outsize: size / smpsize - check this...*/ if((sf->fd = sfcreat_ex(name, (__int64)size *smpsize, (__int64 *)0, props,min_header,mode)) < 0) { freesndfd(fd); return -1; } sf->remain = sf->true_bufsize / smpsize; return fd+SNDFDBASE; } /* * close a sndfile */ int sndcloseEx(int fd) { register struct sndfile *sf; int rc = 0; __int64 length, pos; if(!mapsndfd(&fd)) return -1; sf = sndfiles[fd]; #ifdef ENABLE_PVX if(sf_ispvx(sf->fd)){ // if(sf_ispvx(sf->fd,0) >=0) { # ifdef _DEBUG fprintf(stderr,"calling sfclose with fd %d\n",sf->fd); # endif rc = sfclose(sf->fd); // I think..nothing else to do for pvx file! if(rc < 0) { # ifdef _DEBUG fprintf(stderr,"sfclose error, rc = %d\n",rc); # endif sfperror("sndcloseEx"); } freesndfd(fd); return rc; // } } #endif if(sf->flags&SNDFLG_TRUNC) { length = sfsize(sf->fd); pos = sndtellEx(fd + SNDFDBASE); //RWD 12-12-19 make sure of 64bit ints in calcs pos *= sampsize[sf->samptype]; if(sf->flags & SNDFLG_WRITTEN ) /* should never exec */ sndflsbufEx(sf); /* rsfsize does it! */ if((rc = sfadjust(sf->fd,pos-length)) < 0) { rsferrno = ESFWRERR; rsferrstr = "can't truncate SFfile"; } } else if(sf->flags & SNDFLG_WRITTEN ) sndflsbufEx(sf); rc |= sfclose(sf->fd); freesndfd(fd); return rc; } /*RWD.6.98 needed for Release98!*/ int sndunlink(int sndfd) { //int size; struct sndfile *sf; /* RWD PVX TODO: hopefully wouldn't need to perform this hack on pvx file....? */ (void)sndseekEx(sndfd,0,0); /*RWD.7.98 hack to fix bug when closing empty file*/ if(!mapsndfd(&sndfd)) return -1; sf = sndfiles[sndfd]; return sfunlink(sf->fd); } /* * Return the size (in samples) of a sndfile * NOTE * the first sndseek will flush buffer if necessary */ int sndsizeEx(int sfd) { int oldpos; int size; if((oldpos = sndtellEx(sfd)) < 0) /* RWD: pvx handling done */ return -1; if((size = sndseekEx(sfd, 0L, 2)) < 0) { size = -1; rsferrno = ESFNOSEEK; rsferrstr = "can't seek to end of file to find size"; } if(sndseekEx(sfd, oldpos, 0) < 0) { rsferrno = ESFNOSEEK; rsferrstr = "can't restore position after finding file size"; return -1; } return size; } /* * seek in a sndfile (of SHORTS or FLOATS) */ int sndseek(int fd, int dist, int whence) { //return sndseekEx(fd,dist,whence); return -1; } //RWD NB PVX: returns new file pos (in samples), or -1 for error int sndseekEx(int fd, int dist, int whence) { register struct sndfile *sf; long bufsize; __int64 secpos; __int64 newpos = 0; __int64 gotpos; if(!mapsndfd(&fd)) return -1; sf = sndfiles[fd]; #ifdef ENABLE_PVX if(sf_ispvx(sf->fd)){ // RWD TODO: shoukld add test for error return PVOCDATA pvxdata; // we must return currentframepos as raw floatsams int frameoffset; int curframe; int goalframe = 0; int rc = 0; int pvxid; pvxid = get_pvxfd(sf->fd,&pvxdata); # ifdef _DEBUG assert(pvxid >= 0); // eventually we will code this permanently...but is this the best place to put this test? //if(dist > 0) assert(abs(dist) % (pvxdata.nAnalysisBins * 2) == 0); # endif //RWD Oct 2025, can't divide signed val by unsigned val. No warning, but CPU goes nuts! frameoffset = dist / (int) (pvxdata.nAnalysisBins * 2); //NB: always mono stream in CDP switch(whence){ //set: go to "dist" samples: must be int multiple of framesize // seems never to be called for CDP ana routines, except for dist= 0 case SEEK_SET: rc = pvoc_seek_mcframe(pvxid, frameoffset,SEEK_SET); //or: if dist==0, use pvoc_rewind()? if(rc < 0){ return rc; } rc = pvoc_framepos(pvxid) * (pvxdata.nAnalysisBins * 2); break; //tell - Ok to use pvoc_framepos, OR can use sndtellEx below case SEEK_CUR: // not directly handled in pvfileio.c yet rc = pvoc_seek_mcframe(pvxid, frameoffset,SEEK_CUR); if(rc < 0){ return rc; } rc = pvoc_framepos(pvxid) * (pvxdata.nAnalysisBins * 2); break; //end: step back dist samps if negative. We do not allow setting beyond EOF. case SEEK_END: rc = pvoc_seek_mcframe(pvxid, frameoffset,SEEK_END); if(rc < 0){ return rc; } rc = pvoc_framepos(pvxid); //frames rc *= pvxdata.nAnalysisBins * 2; // samps. caller has to compute nframes break; } return rc; } #endif // std CDP files from here bufsize = sf->true_bufsize; if((sf->flags & SNDFLG_WRITTEN) && sndflsbufEx(sf)) return -1; switch(whence) { case 0: newpos = dist; /*in SAMPLES: NB must deal with 8bit files!*/ break; case 1: newpos = sndtellEx(fd+SNDFDBASE) + dist; break; case 2: newpos = sfsize(sf->fd); /*size-specific:SHORTSAMS, from datachunksize*/ newpos = (newpos / sampsize[sf->samptype]) + dist; /*make non-size-specific*/ break; } if(newpos < 0) newpos = 0; newpos *= sampsize[sf->samptype]; /* byte offset */ /*restore size-specific for doseek*/ /*RWD.6.99 still need to do this - not a SECSIZE calc, but just cdp_round to our buffersize*/ secpos = (newpos/bufsize)*bufsize; /* cdp_round down */ /*RWD 2007: NB for FILE64_WIN doseekEx takes and returns __int64 */ if((gotpos = doseekEx(sf, secpos, 0)) < 0)/*NB seek might be truncated */ /*gotpos = non-size-specific*/ return -1; sf->flags &= ~SNDFLG_ATEND; if(sndfilbuf(sf)) { /* if sndfilbuf fails... */ sf->next = sf->buffer; sf->remain = 0; return -1; } if(gotpos < secpos) { newpos -= sf->remain; /*RWD 2007 !!! remain supposed to count ~samples~ */ sf->remain = 0; } sf->next = &sf->buffer[(unsigned long)newpos % bufsize]; newpos /= sampsize[sf->samptype]; sf->remain = (sf->endbuf - sf->next)/sampsize[sf->samptype]; return (long) newpos; } /* * Where are we in the present sndfile * NOTE * This does not need to flush buffers, etc */ int sndtell(int fd) { return -1; } /*RWD 2007 FIXME for 64bit reads: */ /* doseekEx must return unsigned long, or __int64 value */ /* RWD PVX: this is called only once in all of CDP7/8: pvthreads.cpp, to get current framepos */ /* direct equivalent is pvoc_framepos() */ /* but sfsys expects (float) sample pos as for (mono) .ana file */ /* don't want to clone all the sf calls here (dealing with SECSIZE etc */ int sndtellEx(int fd) { struct sndfile *sf; //long off; __int64 off; if(!mapsndfd(&fd)) //strips SNDFDBASE: fd = 0 return -1; sf = sndfiles[fd]; //sf-fd = 1000 #ifdef ENABLE_PVX if(sf_ispvx(sf->fd)){ int pvxid; PVOCDATA pvxdata; pvxid = get_pvxfd(sf->fd,&pvxdata); if(pvxid >=0){ // we must return currentframepos as raw floatsams int32_t framepos; int32_t nsamps; //TODO: sf->fd is sf element, offset by SFDBASE= 1000 // need to remove that first - how? framepos = pvoc_framepos(pvxid); if(framepos < 0){ # ifdef _DEBUG fprintf(stderr,"bad return %d from pvoc_framepos\n",framepos); # endif return -1; } nsamps = framepos * (pvxdata.nAnalysisBins * 2); //of course, 1 bin = [amp,freq] duplet return (int) nsamps; } } #endif // std CDP files from here if((off = doseekEx(sf, 0L, 1)) < 0) { /*NB: must return cnt of samples * sizeof(type=SHORTS or FLOATS)*/ rsferrno = ESFNOSEEK; rsferrstr = "can't seek to find present position in file"; return -1; } if(sf->flags & SNDFLG_LASTWR) /*RWD.5.1.99 fails for short sndfiles within a buffer*/ off += sf->true_bufsize; if(sf->flags & SNDFLG_ATEND) off = sfsize(sf->fd); /*bytes, of SHORTS or FLOATS samps*/ off /= sampsize[sf->samptype]; return (int)(off - ( (sf->remain < 0) ? 0 : sf->remain)); } /* * let the user supply a larger buffer */ /*RWD.6.99 NB bufsize is in SECTORS - may need new version for new formats...*/ int sndsetbuf(int sfd, char *buf, int bufsize) { struct sndfile *sf; if(!mapsndfd(&sfd)) return -1; sf = sndfiles[sfd]; if(sf->remain != 0) { rsferrno = ESFLOSEDATA; rsferrstr = "sndsetbuf would lose data"; return -1; } freesndbuf(sfd); sf->buffer = buf; sf->bufsiz = bufsize; sf->true_bufsize = sf->bufsiz << LOGSECSIZE; sf->flags |= SNDFLG_USERBUF; return 0; } /* * snd I/O routines */ /* * fgetfloat - read the next float */ // RWD TODO: looks like several programs still call this! int fgetfloat(float *fp, int sfd) { register struct sndfile *sfp; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; if(sfp->remain < 0) return 0; if(sfp->remain-- == 0) { if(sndfilbuf(sfp)) return -1; if(sfp->remain-- <= 0) return 0; } if(sfp->samptype == SAMP_FLOAT) { *fp = *(float *)sfp->next; sfp->next += sizeof(float); } else { *fp = (float)(*(short *)sfp->next) / (float)MAXSHORT; sfp->next += sizeof(short); } return 1; } int fgetfloatEx(float *fp, int sfd,int expect_floats) { register struct sndfile *sfp; long lword = 0; long mask; /*we need signed ints*/ SND_SAMP ssamp; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; if(sfp->remain < 0) return 0; if(sfp->remain-- == 0) { if(sndfilbuf(sfp)) return -1; if(sfp->remain-- <= 0) return 0; } ssamp.lsamp = 0; switch(sfp->samptype){ case(SAMP_FLOAT): *fp = *(float *)sfp->next; sfp->next += sizeof(float); break; case(SAMP_SHORT): *fp = (float)(*(short *)sfp->next) / (float)MAXSHORT; sfp->next += sizeof(short); break; case(SAMP_LONG): if(expect_floats){ *fp = *(float *)sfp->next; sfp->next += sizeof(float); } else{ *fp = (float)((double) (*(int *)sfp->next) / (float)MAXINT); sfp->next += sizeof(int); } break; case(SAMP_2432): /*mask the word first*/ lword = *(long *)sfp->next; lword &= 0xffffff00; *fp = (float)((double)lword / MAXINT); sfp->next += sizeof(int); break; case(SAMP_2024): /*need to mask it?*/ ssamp.lsamp = 0; mask = _rsf_getbitmask(sfp->fd); if(mask==0) return -1; //#ifdef LSBFIRST ssamp.bytes[1] = sfp->next[0]; ssamp.bytes[2] = sfp->next[1]; ssamp.bytes[3] = sfp->next[2]; //#else // ssamp.bytes[0] = sfp->next[0]; // ssamp.bytes[1] = sfp->next[1]; // ssamp.bytes[2] = sfp->next[2]; //#endif *fp = (float)((double)(ssamp.lsamp & mask) / MAXINT); sfp->next += 3; break; case(SAMP_2424): //#ifdef LSBFIRST ssamp.bytes[1] = sfp->next[0]; ssamp.bytes[2] = sfp->next[1]; ssamp.bytes[3] = sfp->next[2]; //#else // ssamp.bytes[0] = sfp->next[0]; // ssamp.bytes[1] = sfp->next[1]; // ssamp.bytes[2] = sfp->next[2]; //#endif *fp = (float)((double)(ssamp.lsamp) / MAXINT); sfp->next += 3; break; default: rsferrno = ESFBADPARAM; rsferrstr = "attempted to read unknown sample format"; return -1; /*do others later...*/ break; } return 1; } /* * fputfloat - write the next float */ int fputfloat(float *fp, int sfd) { register struct sndfile *sfp; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; if(sfp->flags&SNDFLG_ATEND) return 0; sfp->flags |= SNDFLG_WRITTEN; /* sndtell checks this*/ sfp->flags |= SNDFLG_LASTWR; if(sfp->samptype == SAMP_FLOAT) { *(float *)sfp->next = *fp; sfp->next += sizeof(float); } else { /* *(short *)sfp->next = (short) floor(0.5 + *fp * MAXSHORT);*/ *(short *)sfp->next = (short) cdp_round( *fp * MAXSHORT); sfp->next += sizeof(short); } if(sfp->next > sfp->endbuf) sfp->endbuf = sfp->next; if(--sfp->remain == 0) { if(sndflsbuf(sfp)) return -1; } return 1; } /*RWD.7.99 replace floor calc with cdp_round(): more accurate!*/ int fputfloatEx(float *fp, int sfd) { register struct sndfile *sfp; SND_SAMP ssamp; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; if(sfp->flags&SNDFLG_ATEND) return 0; sfp->flags |= SNDFLG_WRITTEN; /* sndtell checks this*/ sfp->flags |= SNDFLG_LASTWR; ssamp.lsamp = 0; switch(sfp->samptype){ case(SAMP_FLOAT): *(float *)sfp->next = *fp; sfp->next += sizeof(float); break; case(SAMP_SHORT): /* *(short *)sfp->next = (short)floor(0.5 + *fp * MAXSHORT);*/ *(short *)sfp->next = (short) cdp_round( *fp * MAXSHORT); sfp->next += sizeof(short); break; case(SAMP_2024): /*ssamp.lsamp = (int) floor(0.5 + *fp * MAXINT);*/ ssamp.lsamp = (int) cdp_round(*fp * MAXINT); //#ifdef LSBFIRST sfp->next[0] = ssamp.bytes[1] & 0xf0; sfp->next[1] = ssamp.bytes[2]; sfp->next[2] = ssamp.bytes[3]; //#else // sfp->next[0] = ssamp.bytes[0] & 0xf0; // sfp->next[1] = ssamp.bytes[1]; // sfp->next[2] = ssamp.bytes[2]; //#endif sfp->next += 3; break; case(SAMP_2424): /*ssamp.lsamp = (int) floor(0.5 + *fp * MAXINT);*/ ssamp.lsamp = (int) cdp_round(*fp * MAXINT); //#ifdef LSBFIRST sfp->next[0] = ssamp.bytes[1]; sfp->next[1] = ssamp.bytes[2]; sfp->next[2] = ssamp.bytes[3]; //#else // sfp->next[0] = ssamp.bytes[0]; // sfp->next[1] = ssamp.bytes[1]; // sfp->next[2] = ssamp.bytes[2]; //#endif sfp->next += 3; break; case(SAMP_LONG): /* *(long *)sfp->next = (long) floor(0.5 + *fp * MAXINT);*/ *(int *)sfp->next = (int) cdp_round(*fp * MAXINT); sfp->next += sizeof(int); break; case(SAMP_2432): /*ssamp.lsamp = (int) floor(0.5 + *fp * MAXINT);*/ ssamp.lsamp = (int) cdp_round(*fp * MAXINT); ssamp.bytes[0] = 0; *(int *)sfp->next = ssamp.lsamp; sfp->next += sizeof(int); break; default: rsferrno = ESFBADPARAM; rsferrstr = "attempted to write unknown sample format"; return -1; /*do others later...*/ break; } if(sfp->next > sfp->endbuf) sfp->endbuf = sfp->next; if(--sfp->remain == 0) { if(sndflsbufEx(sfp)) return -1; } return 1; } /* * fgetfbuf - get a sequence of float-sams */ /*RWD.6.99 probably the tidiest way of dealing with old floatsam files * expect_float must be non-zero to trigger */ int fgetfbufEx(float *fp, int n, int sfd,int expect_floats) { register struct sndfile *sfp; int chunk; int cnt = 0; #ifdef ENABLE_PVX int rc; #endif if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; #ifdef ENABLE_PVX rc = sf_ispvx(sfp->fd); if(rc < 0){ # ifdef _DEBUG fprintf(stderr,"fgetfbufEx: bad sf id\n"); # endif return cnt; } else if(rc){ PVOCDATA pvxdata; int n_frames = 0; int binsamps; int pvxfd = get_pvxfd(sfp->fd,&pvxdata); // we absolutely have to confirm n is exact multiple of nAnalysisBins * 2) binsamps = pvxdata.nAnalysisBins * 2; if((n % binsamps) != 0){ # ifdef _DEBUG fprintf(stderr,"fgetfbufEx: buffersize(%d) must be multiple of analysis frame size\n",n); # endif cnt = 0; } n_frames = n / binsamps; // test sfp->samptype too? cnt = pvoc_getframes(pvxfd , fp, n_frames); if(cnt <= 0) cnt = 0; // EOF or error else cnt *= binsamps; return cnt; } #endif // std CDP files from here if(sfp->samptype==SAMP_FLOAT || ((sfp->samptype==INT_32) && expect_floats)){ while(cnt < n) { if(sfp->remain == 0) { if(sndfilbuf(sfp) || sfp->remain == 0) return cnt; } chunk = n - cnt; if(chunk > sfp->remain) chunk = sfp->remain; memcpy((char *)fp, sfp->next, chunk*sizeof(float)); sfp->remain -= chunk; sfp->next += chunk*sizeof(float); cnt += chunk; fp += chunk; } } else{ sfd += SNDFDBASE; while(cnt < n && (fgetfloatEx(fp++, sfd,expect_floats) > 0)) cnt++; /*return cnt;*/ } return cnt; } /* * fputfbuf - put a sequence of float-sams */ int fputfbuf(float *fp, int n, int sfd) { register struct sndfile *sfp; int chunk; int cnt = 0; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; if(sfp->samptype == SAMP_SHORT) { sfd += SNDFDBASE; while(cnt < n && fputfloat(fp++, sfd) > 0) cnt++; return cnt; } while(cnt < n) { sfp->flags |= SNDFLG_WRITTEN; /* sndtell checks this*/ sfp->flags |= SNDFLG_LASTWR; chunk = n - cnt; if(chunk > sfp->remain) chunk = sfp->remain; memcpy(sfp->next, (char *)fp, chunk*sizeof(float)); sfp->remain -= chunk; sfp->next += chunk*sizeof(float); if(sfp->next > sfp->endbuf) sfp->endbuf = sfp->next; cnt += chunk; fp += chunk; if(sfp->remain == 0) { if(sndflsbuf(sfp)) return -1; if(sfp->remain == 0) return cnt; } } return cnt; } #ifdef ENABLE_PVX // not used yet... int snd_getpvxfno(int sfd) { register struct sndfile *sfp; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; return sfp->pvxid; } #endif int fputfbufEx(float *fp, int n, int sfd) { register struct sndfile *sfp; int chunk; int cnt = 0; #ifdef ENABLE_PVX int rc; #endif if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; #ifdef ENABLE_PVX rc = sf_ispvx(sfp->fd); if(rc < 0) return -1; else if(rc) { PVOCDATA pvxdata; int n_frames = 0; int binsamps; int rc; int pvxid = -1; pvxid = get_pvxfd(sfp->fd,&pvxdata); if(pvxid < 0){ # ifdef _DEBUG fprintf(stderr,"fputfbufEx: bad pvx id %d\n",pvxid); # endif return cnt; } else { // we absolutely have to confirm n is exact multiple of nAnalysisBins * 2) # ifdef _DEBUG assert(pvxid >= 0); assert(pvxdata.nAnalysisBins > 0); # endif binsamps = pvxdata.nAnalysisBins * 2; if((n % binsamps) != 0){ # ifdef _DEBUG fprintf(stderr,"fputfbufEx: buffersize(%d) must be multiple of analysis frame size\n",n); # endif cnt = 0; } n_frames = n / binsamps; // test sfp->samptype too? cnt = pvoc_putframes(pvxid, fp, n_frames); if(cnt <= 0) cnt = 0; // EOF or error else cnt *= binsamps; return binsamps * n_frames; } } #endif // std CDP files from here if(sfp->samptype == SAMP_SHORT) { sfd += SNDFDBASE; while(cnt < n && fputfloatEx(fp++, sfd) > 0) cnt++; return cnt; } if(sfp->samptype == SAMP_FLOAT){ while(cnt < n) { sfp->flags |= SNDFLG_WRITTEN; /* sndtell checks this*/ sfp->flags |= SNDFLG_LASTWR; chunk = n - cnt; if(chunk > sfp->remain) chunk = sfp->remain; memcpy(sfp->next, (char *)fp, chunk*sizeof(float)); sfp->remain -= chunk; sfp->next += chunk*sizeof(float); if(sfp->next > sfp->endbuf) sfp->endbuf = sfp->next; cnt += chunk; fp += chunk; if(sfp->remain == 0) { if(sndflsbufEx(sfp)) return -1; if(sfp->remain == 0) return cnt; } } } else { sfd += SNDFDBASE; while(cnt < n && fputfloatEx(fp++, sfd) > 0) cnt++; return cnt; } return cnt; } /* RWD.7.99 use cdp_round() instead of floor(): later, try shoft/truncate and/or dithering! */ int fgetshortEx(short *sp, int sfd,int expect_floats) { struct sndfile *sfp; int lword = 0; int mask; SND_SAMP ssamp; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; if(sfp->remain < 0) return 0; if(sfp->remain-- == 0) { if(sndfilbuf(sfp)) return -1; if(sfp->remain-- <= 0) return 0; } ssamp.lsamp = 0; switch(sfp->samptype){ case(SAMP_FLOAT): if(sfp->scale_floats==1) /* *sp = (short)(floor(0.5 + (sfp->fscalefac * (*(float *)sfp->next * MAXSHORT))));*/ *sp = (short) cdp_round((sfp->fscalefac * (*(float *)sfp->next * MAXSHORT))); else /* *sp = (short)floor(0.5 + *(float *)sfp->next * MAXSHORT);*/ *sp = (short) cdp_round( *(float *)sfp->next * MAXSHORT); sfp->next += sizeof(float); break; case(SAMP_SHORT): *sp = *(short *)sfp->next; sfp->next += sizeof(short); break; case(SAMP_LONG): if(expect_floats){ if(sfp->scale_floats==1) /* *sp = (short)(floor(0.5 + (sfp->fscalefac * (*(float *)sfp->next * MAXSHORT))));*/ *sp = (short) cdp_round((sfp->fscalefac * (*(float *)sfp->next * MAXSHORT))); else /* *sp = (short)floor(0.5 + *(float *)sfp->next * MAXSHORT);*/ *sp = (short) cdp_round( *(float *)sfp->next * MAXSHORT); sfp->next += sizeof(float); } else{ *sp = (short)( (*(int *)sfp->next) >> 16); sfp->next += sizeof(int); } break; case(SAMP_2432): /*mask the word first*/ lword = *(int *)sfp->next; lword &= 0xffffff00; *sp = (short)(lword >> 16); sfp->next += sizeof(int); break; case(SAMP_2024): /*need to mask it?*/ mask = _rsf_getbitmask(sfp->fd); if(mask==0) return -1; ssamp.bytes[1] = sfp->next[0]; ssamp.bytes[2] = sfp->next[1]; ssamp.bytes[3] = sfp->next[2]; *sp = (short)((ssamp.lsamp & mask) >> 16); sfp->next += 3; break; case(SAMP_2424): ssamp.bytes[1] = sfp->next[0]; ssamp.bytes[2] = sfp->next[1]; ssamp.bytes[3] = sfp->next[2]; *sp = (short)(ssamp.lsamp >> 16); sfp->next += 3; break; default: rsferrno = ESFBADPARAM; rsferrstr = "attempted to read unknown sample format"; return -1; /*do others later...*/ break; } return 1; } int fputshortEx(short *sp, int sfd) { register struct sndfile *sfp; SND_SAMP ssamp; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; if(sfp->flags&SNDFLG_ATEND) return 0; sfp->flags |= SNDFLG_WRITTEN; /* sndtell checks this*/ sfp->flags |= SNDFLG_LASTWR; ssamp.lsamp = 0; switch(sfp->samptype){ case(SAMP_FLOAT): *(float *)sfp->next = (float)*sp / (float)MAXSHORT; sfp->next += sizeof(float); break; case(SAMP_SHORT): *(short *)sfp->next = *sp; sfp->next += sizeof(short); break; case(SAMP_2024): /*no need to mask; 16 bits anyway!*/ case(SAMP_2424): ssamp.lsamp = (int) ((*sp) << 16); sfp->next[0] = ssamp.bytes[1]; sfp->next[1] = ssamp.bytes[2]; sfp->next[2] = ssamp.bytes[3]; sfp->next += 3; break; case(SAMP_LONG): case(SAMP_2432): ssamp.lsamp = (int) ((*sp) << 16); *(int *)sfp->next = ssamp.lsamp; sfp->next += sizeof(int); break; default: rsferrno = ESFBADPARAM; rsferrstr = "attempted to write unknown sample format"; return -1; /*do others later...*/ break; } if(sfp->next > sfp->endbuf) sfp->endbuf = sfp->next; if(--sfp->remain == 0) { if(sndflsbufEx(sfp)) return -1; } return 1; } int fgetsbufEx(short *sp, int n, int sfd,int expect_floats) { register struct sndfile *sfp; int chunk; int cnt = 0; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; switch(sfp->samptype){ case(SAMP_SHORT): while(cnt < n) { if(sfp->remain <= 0) { /*RWD.6.99 was == 0*/ if(sndfilbuf(sfp) || (sfp->remain == 0)) return cnt; } chunk = n - cnt; if(chunk > sfp->remain) chunk = sfp->remain; memcpy((char *)sp, sfp->next, chunk*sizeof(short)); sfp->remain -= chunk; sfp->next += chunk*sizeof(short); cnt += chunk; sp += chunk; } break; default: sfd += SNDFDBASE; while(cnt < n && fgetshortEx(sp++, sfd,expect_floats) > 0) cnt++; break; } return cnt; } int fputsbufEx(short *sp, int n, int sfd) { register struct sndfile *sfp; int chunk; int cnt = 0; if(!mapsndfd(&sfd)) return -1; sfp = sndfiles[sfd]; switch(sfp->samptype){ case(SAMP_SHORT): while(cnt < n) { sfp->flags |= SNDFLG_WRITTEN; /* sndtell checks this*/ sfp->flags |= SNDFLG_LASTWR; chunk = n - cnt; if(chunk > sfp->remain) chunk = sfp->remain; memcpy(sfp->next, (char *)sp, chunk*sizeof(short)); sfp->remain -= chunk; sfp->next += chunk*sizeof(short); if(sfp->next > sfp->endbuf) sfp->endbuf = sfp->next; cnt += chunk; sp += chunk; if(sfp->remain == 0) { if(sndflsbufEx(sfp)) return -1; if(sfp->remain == 0) return cnt; } } break; default: sfd += SNDFDBASE; while(cnt < n && fputshortEx(sp++, sfd) > 0) cnt++; break; } return cnt; } /* * The property stuff, for sndfiles */ int sndgetprop(int sfd, char *prop, char *dest, int lim) { if(!mapsndfd(&sfd)) return -1; return sfgetprop(sndfiles[sfd]->fd, prop, dest, lim); } int sndputprop(int sfd, char *prop, char *src, int size) { if(!mapsndfd(&sfd)) return -1; if(strcmp(prop, "sample type") == 0) { rsferrno = ESFNOSTYPE; rsferrstr = "can't change sample type on sndfile"; return -1; } return sfputprop(sndfiles[sfd]->fd, prop, src, size); } int sndrmprop(int sfd, char *prop) { if(!mapsndfd(&sfd)) return -1; if(strcmp(prop, "sample type") == 0) { rsferrno = ESFNOSTYPE; rsferrstr = "can't remove sample type on sndfile"; return -1; } return sfrmprop(sndfiles[sfd]->fd, prop); } int snddirprop(int sfd, int (*func)(char *propname, int propsize)) { if(!mapsndfd(&sfd)) return -1; return sfdirprop(sndfiles[sfd]->fd, func); } /*RWD OCT97*/ int sndgetwordsize(int sfd) { if(!mapsndfd(&sfd)) return -1; return sfgetwordsize(sndfiles[sfd]->fd); } /*recognise shortcuts in WIN32*/ #if defined _WIN32 /*TODO: add arg for full targetname*/ int snd_is_shortcut(int sfd) { if(!mapsndfd(&sfd)) return -1; return sf_is_shortcut(sndfiles[sfd]->fd,NULL); } #endif /*RWD.5.99*/ int snd_fileformat(int sfd, fileformat *pfmt) { if(!mapsndfd(&sfd)) return -1; /* RWD 2022: this should now recognise pvx file as PVOCEX */ return sfformat(sndfiles[sfd]->fd,pfmt); } int sndgetchanmask(int sfd) { if(!mapsndfd(&sfd)) return -1; return sfgetchanmask(sndfiles[sfd]->fd); } int snd_getchanformat(int sfd, channelformat *chformat) { if(!mapsndfd(&sfd)) return -1; return sf_getchanformat(sndfiles[sfd]->fd,chformat); } int sndreadpeaks(int sfd,int channels,CHPEAK peakdata[],int *peaktime) { if(!mapsndfd(&sfd)) return -1; return sfreadpeaks(sndfiles[sfd]->fd,(int) channels,peakdata,peaktime); } int sndputpeaks(int sfd,int channels,const CHPEAK peakdata[]) { if(!mapsndfd(&sfd)) return -1; return sfputpeaks(sndfiles[sfd]->fd,channels,peakdata); } const char * snd_getfilename(int sfd) { if(!mapsndfd(&sfd)) return NULL; return sf_getfilename(sndfiles[sfd]->fd); } extern int sf_makepath(char path[], const char* sfname); int snd_makepath(char path[], const char* sfname) { return sf_makepath(path,sfname); }