| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518 |
-
- /* pvfileio.c */
- /* pvocex format test routines*/
- /* Initial version RWD May 2000.
- * All rights reserved: work in progress!
- * Manifestly not a complete API yet!
- * In particular, error returns are kept very simplistic at the moment.
- * (and are not even very consistent yet...)
- * In time, a full set of error values and messages will be developed.
- *
- * NB: the RIFF<WAVE> functions only look for, and accept, a PVOCEX format file.
- * NB also: if windows.h is included anywhere (should be no need in this file,
- * or in pvfileio.h),
- * the WAVE_FORMAT~ symbols may need to be #ifndef-ed.
- */
- /* very simple CUSTOM window chunk:
- *
- * <PVXW><size><data>
- *
- * where size as usual gives the size of the data in bytes.
- * the size in samples much match dwWinlen (which may not be the same as N (fft length)
- * the sample type must be the same as that of the pvoc data itself
- * (only floatsams supported so far)
- * values must be normalized to peak of 1.0
- */
- //#ifdef WINDOWS
- //#include "stdafx.h"
- //#endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <memory.h>
- #include <string.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #ifdef _WIN32
- #include <io.h>
- #endif
- #ifdef unix
- #include <unistd.h>
- #define O_BINARY (0)
- #define _S_IWRITE S_IWRITE
- #define _S_IREAD S_IREAD
- #endif
- #ifdef _DEBUG
- #include <assert.h>
- #endif
- #include "pvdefs.h"
- #include "pvfileio.h"
- /* NB probably no good on 64bit platforms */
- #define REVDWBYTES(t) ( (((t)&0xff) << 24) | (((t)&0xff00) << 8) | (((t)&0xff0000) >> 8) | (((t)>>24) & 0xff) )
- #define REVWBYTES(t) ( (((t)&0xff) << 8) | (((t)>>8) &0xff) )
- #define TAG(a,b,c,d) ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )
- #ifndef WAVE_FORMAT_EXTENSIBLE
- # define WAVE_FORMAT_EXTENSIBLE (0xFFFE)
- #endif
- #ifndef WAVE_FORMAT_PCM
- #define WAVE_FORMAT_PCM (0x0001)
- #endif
- #ifndef WAVE_FORMAT_IEEE_FLOAT
- #define WAVE_FORMAT_IEEE_FLOAT (0x0003)
- #endif
- const GUID KSDATAFORMAT_SUBTYPE_PVOC = {
- 0x8312b9c2,
- 0x2e6e,
- 0x11d4,
- { 0xa8, 0x24, 0xde, 0x5b, 0x96, 0xc3, 0xab, 0x21 }
- };
- static char *pv_errstr = "";
- #define MAXFILES (16)
- /* or any desired larger number: will be dynamically allocated one day */
- typedef union {
- int32_t lsamp;
- float fsamp;
- unsigned char bytes[4];
- } SND_SAMP;
- typedef struct pvoc_file {
- WAVEFORMATEX fmtdata;
- PVOCDATA pvdata;
- /* RWD 2022 to help CDP pvocex extension, requires two-stage file creation/update */
- int32_t propsoffset;
- int32_t needsupdate; //for two-stage creation/update CDP-style
- int32_t fmtchunkoffset;
- int32_t datachunkoffset;
- int32_t nFrames; /* no of frames in file */
- int32_t FramePos; /* where we are in file */
- int32_t curpos;
- int32_t fd;
- int32_t to_delete;
- int32_t readonly;
- int32_t do_byte_reverse;
- char *name;
- float *customWindow;
-
- } PVOCFILE;
- static PVOCFILE *files[MAXFILES];
- static int32_t pvoc_writeheader(int32_t ofd);
- static int32_t pvoc_readheader(int32_t ifd,WAVEFORMATPVOCEX *pWfpx);
- int32_t pvoc_getdatasize_bytes(int32_t fd)
- {
- int32_t datachunksize, framesize;
-
- if(files[fd]==NULL)
- return -1;
- if(files[fd]->nFrames == 0)
- return 0;
- framesize = files[fd]->pvdata.nAnalysisBins * 2 * sizeof(float);
- datachunksize = files[fd]->nFrames * framesize * files[fd]->fmtdata.nChannels;
- return datachunksize;
- }
- static int32_t write_guid(int32_t fd,int32_t byterev,const GUID *pGuid)
- {
- int32_t written;
- #ifdef _DEBUG
- assert(fd >= 0);
- assert(pGuid);
- #endif
- if(byterev){
- GUID guid;
- guid.Data1 = REVDWBYTES(pGuid->Data1);
- guid.Data2 = REVWBYTES(pGuid->Data2);
- guid.Data3 = REVWBYTES(pGuid->Data3);
- memcpy((char *) (guid.Data4),(char *) (pGuid->Data4),8);
- written = write(fd,(char *) &guid,sizeof(GUID));
- }
- else
- written = write(fd,(char *) pGuid,sizeof(GUID));
- return written;
- }
- static int32_t compare_guids(const GUID *gleft, const GUID *gright)
- {
- const char *left = (const char *) gleft, *right = (const char *) gright;
- return !memcmp(left,right,sizeof(GUID));
- }
- static int32_t write_pvocdata(int32_t fd,int32_t byterev,const PVOCDATA *pData)
- {
- int32_t written;
- // int32_t dwval;
- #ifdef _DEBUG
- assert(fd >= 0);
- assert(pData);
- #endif
- if(byterev){
- // int32_t revdwval;
- PVOCDATA data;
- SND_SAMP ssamp;
- data.wWordFormat = REVWBYTES(pData->wWordFormat);
- data.wAnalFormat = REVWBYTES(pData->wAnalFormat);
- data.wSourceFormat = REVWBYTES(pData->wSourceFormat);
- data.wWindowType = REVWBYTES(pData->wWindowType);
- data.nAnalysisBins = REVDWBYTES(pData->nAnalysisBins);
- data.dwWinlen = REVDWBYTES(pData->dwWinlen);
- data.dwOverlap = REVDWBYTES(pData->dwOverlap);
- data.dwFrameAlign = REVDWBYTES(pData->dwFrameAlign);
-
- ssamp.fsamp = pData->fAnalysisRate;
- //dwval = * (int32_t *) &(pData->fAnalysisRate);
- ssamp.lsamp = REVDWBYTES(/*dwval*/ ssamp.lsamp);
- //data.fAnalysisRate = * (float *) &dwval;
- data.fAnalysisRate = ssamp.fsamp;
-
- ssamp.fsamp = pData->fWindowParam;
- //dwval = * ( int32_t *) &(pData->fWindowParam);
- ssamp.lsamp = REVDWBYTES(ssamp.lsamp);
- //dwval = REVDWBYTES(dwval);
- //data.fWindowParam = * (float *) &dwval;
- data.fWindowParam = ssamp.fsamp;
-
- written = write(fd,(char *) &data,sizeof(PVOCDATA));
- }
- else
- written = write(fd,(char *) pData,sizeof(PVOCDATA));
- return written;
- }
- static int32_t write_fmt(int fd, int byterev,const WAVEFORMATEX *pfmt)
- {
- /*NB have to write out each element, as not guaranteed alignment othewise.
- * Consider it documentation. */
- #ifdef _DEBUG
- assert(fd >=0);
- assert(pfmt);
- assert(pfmt->cbSize == 62);
- #endif
- if(byterev){
- WAVEFORMATEX fmt;
- fmt.wFormatTag = REVWBYTES(pfmt->wFormatTag);
- fmt.nChannels = REVWBYTES(pfmt->nChannels);
- fmt.nSamplesPerSec = REVDWBYTES(pfmt->nSamplesPerSec);
- fmt.nAvgBytesPerSec = REVDWBYTES(pfmt->nAvgBytesPerSec);
- fmt.nBlockAlign = REVWBYTES(pfmt->nBlockAlign);
- fmt.wBitsPerSample = REVWBYTES(pfmt->wBitsPerSample);
- fmt.cbSize = REVWBYTES(pfmt->cbSize);
- if(write(fd,(char *) &(fmt.wFormatTag),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(fmt.nChannels),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(fmt.nSamplesPerSec),sizeof(DWORD)) != sizeof(DWORD)
- || write(fd,(char *) &(fmt.nAvgBytesPerSec),sizeof(DWORD)) != sizeof(DWORD)
- || write(fd,(char *) &(fmt.nBlockAlign),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(fmt.wBitsPerSample),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(fmt.cbSize),sizeof(WORD)) != sizeof(WORD))
- return 0;
- }
- else {
- if(write(fd,(char *) &(pfmt->wFormatTag),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(pfmt->nChannels),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(pfmt->nSamplesPerSec),sizeof(DWORD)) != sizeof(DWORD)
- || write(fd,(char *) &(pfmt->nAvgBytesPerSec),sizeof(DWORD)) != sizeof(DWORD)
- || write(fd,(char *) &(pfmt->nBlockAlign),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(pfmt->wBitsPerSample),sizeof(WORD)) != sizeof(WORD)
- || write(fd,(char *) &(pfmt->cbSize),sizeof(WORD)) != sizeof(WORD))
- return 0;
- }
- return SIZEOF_WFMTEX;
- }
- static int32_t pvoc_writeWindow(int32_t fd,int32_t byterev,float *window,DWORD length)
- {
- if(byterev){
- /* don't corrupt source array! */
- DWORD i;
- int32_t lval, *lp = (int32_t *) window;
- for(i=0;i < length; i++){
- lval = *lp++;
- lval = REVDWBYTES(lval);
- if(write(fd,(char *)&lval,sizeof(int32_t)) != sizeof(int32_t))
- return 0;
- }
- }
- else{
- if(write(fd,(char *) window,length * sizeof(float)) != (int32_t)(length*sizeof(float)))
- return 0;
- }
- return (int32_t)(length * sizeof(float));
- }
- static int32_t pvoc_readWindow(int fd, int byterev, float *window,DWORD length)
- {
- if(byterev){
- DWORD i;
- int32_t got,oval,lval, *lp = (int32_t *) window;
- #ifdef SINGLE_FLOAT
- for(i=0;i < length;i++){
- if(read(fd,(char *)&lval,sizeof(int32_t)) != sizeof(int32_t))
- return 0;
- oval = REVDWBYTES(lval);
- *lp++ = oval;
- }
- #else
- /* read whole block then swap...should be faster */
- got = read(fd,(char *) window,length * sizeof(float));
- if(got != (int)(length * sizeof(float)))
- return 0;
- /* then byterev */
- for(i=0;i < length;i++){
- lval = *lp;
- oval = REVDWBYTES(lval);
- *lp++ = oval;
- }
- #endif
- }
- else{
- if(read(fd,(char *) window,length * sizeof(float)) != (int)(length * sizeof(float)))
- return 0;
- }
- return length * sizeof(float);
- }
- const char *pvoc_errorstr(void)
- {
- return (const char *) pv_errstr;
- }
- /* thanks to the SNDAN programmers for this! */
- /* return 0 for big-endian machine, 1 for little-endian machine*/
- /* probably no good for 16bit swapping though */
- static int32_t byte_order(void)
- {
- int32_t one = 1;
- char* endptr = (char *) &one;
- return (*endptr);
- }
- /***** loosely modelled on CDP sfsys ******
- * This is a static array, but coul be made dynamic in an OOP sort of way.
- * The idea is that all low-level operations and data
- * are completely hidden from the user, so that internal format changes can be made
- * with little or no disruption to the public functions.
- * But avoiding the full monty of a C++ implementation.
- *******************************************/
- int32_t init_pvsys(void)
- {
- int32_t i;
- if(files[0] != NULL) {
- pv_errstr = "\npvsys: already initialized";
- return 0;
- }
- for(i = 0;i < MAXFILES;i++)
- files[i] = NULL;
- return 1;
- }
- static void prepare_pvfmt(WAVEFORMATEX *pfmt,DWORD chans, DWORD srate,
- pv_stype stype)
- {
- #ifdef _DEBUG
- assert(pfmt);
- #endif
- pfmt->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- pfmt->nChannels = (WORD) chans;
- pfmt->nSamplesPerSec = srate;
- pfmt->nBlockAlign = 0; /* decl to please gcc */
- switch(stype){
- case(STYPE_16):
- pfmt->wBitsPerSample = (WORD)16;
- pfmt->nBlockAlign = (WORD)(chans * 2 * sizeof(char));
-
- break;
- case(STYPE_24):
- pfmt->wBitsPerSample = (WORD) 24;
- pfmt->nBlockAlign = (WORD)(chans * 3 * sizeof(char));
- break;
- case(STYPE_32):
- case(STYPE_IEEE_FLOAT):
- pfmt->wBitsPerSample = (WORD) 32;
- pfmt->nBlockAlign = (WORD)(chans * 4 * sizeof(char));
- break;
- default:
- #ifdef _DEBUG
- assert(0);
- #endif
- break;
- }
- pfmt->nAvgBytesPerSec = pfmt->nBlockAlign * srate;
- /* if we have extended WindowParam fields, or something ,will need to adjust this */
- pfmt->cbSize = 62;
- }
- /* lots of different ways of doing this! */
- /* we will need one in the form:
- * in pvoc_fmtcreate(const char *fname,PVOCDATA *p_pvfmt, WAVEFORMATEX *p_wvfmt);
- */
- /* a simple minimalist function to begin with!*/
- /*set D to 0, and/or dwWinlen to 0, to use internal default */
- /* fWindow points to userdef window, or is NULL */
- /* NB currently this does not enforce a soundfile extension; probably it should... */
- int32_t pvoc_createfile(const char *filename,
- DWORD fftlen,DWORD overlap,DWORD chans,
- DWORD format,int32_t srate,
- pv_stype stype,pv_wtype wtype,
- float wparam,float *fWindow,DWORD dwWinlen)
- {
-
- int32_t i;
- long N,D;
- char *pname;
- PVOCFILE *pfile = NULL;
- float winparam = 0.0f;
- N = fftlen; /* keep the CARL varnames for now */
- D = overlap;
- if(N == 0 || chans <=0 || filename==NULL || D > N) {
- pv_errstr = "\npvsys: bad arguments";
- return -1;
- }
- if(/*format < PVOC_AMP_FREQ ||*/ format > PVOC_COMPLEX) { /* RWD unsigned, so nothing < 0 possible */
- pv_errstr = "\npvsys: bad format parameter";
- return -1;
- }
- if(!(wtype >= PVOC_DEFAULT && wtype <= PVOC_CUSTOM)){
- pv_errstr = "\npvsys: bad window type";
- return -1;
- }
- /* load it, but ca't write until we have a PVXW chunk definition...*/
- if(wtype==PVOC_CUSTOM){
- }
- if(wtype==PVOC_DEFAULT)
- wtype = PVOC_HAMMING;
- if(wtype==PVOC_KAISER)
- if(wparam != 0.0f)
- winparam = wparam;
- /*will need an internal default for window parameters...*/
- for(i=0;i < MAXFILES;i++)
- if(files[i]==NULL)
- break;
- if(i==MAXFILES) {
- pv_errstr = "\npvsys: too many files open";
- return -1;
- }
- pfile = (PVOCFILE *) malloc(sizeof(PVOCFILE));
- if(pfile==NULL){
- pv_errstr = "\npvsys: no memory";
- return -1;
- }
- pname = (char *) malloc(strlen(filename)+1);
- if(pname == NULL){
- free(pfile);
- pv_errstr = "\npvsys: no memory";
- return -1;
- }
- pfile->customWindow = NULL;
- /* setup rendering inforamtion */
- prepare_pvfmt(&pfile->fmtdata,chans,srate,stype);
- strcpy(pname,filename);
- pfile->pvdata.wWordFormat = PVOC_IEEE_FLOAT;
- pfile->pvdata.wAnalFormat = (WORD) format;
- pfile->pvdata.wSourceFormat = (stype == STYPE_IEEE_FLOAT ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
- pfile->pvdata.wWindowType = wtype;
- pfile->pvdata.nAnalysisBins = (N>>1) + 1;
- if(dwWinlen==0)
- pfile->pvdata.dwWinlen = N;
- else
- pfile->pvdata.dwWinlen = dwWinlen;
- if(D==0)
- pfile->pvdata.dwOverlap = N/8;
- else
- pfile->pvdata.dwOverlap = D;
- pfile->pvdata.dwFrameAlign = pfile->pvdata.nAnalysisBins * 2 * sizeof(float);
- pfile->pvdata.fAnalysisRate = (float)srate / (float) pfile->pvdata.dwOverlap;
- pfile->pvdata.fWindowParam = winparam;
- pfile->to_delete = 0;
- pfile->readonly = 0;
- pfile->needsupdate = 0; //could be overruled later, but only once!
-
- if(fWindow!= NULL){
- pfile->customWindow = malloc(dwWinlen * sizeof(float));
- if(pfile->customWindow==NULL){
- pv_errstr = "\npvsys: no memory for custom window";
- return -1;
- }
- memcpy((char *)(pfile->customWindow),(char *)fWindow,dwWinlen * sizeof(float));
- }
- pfile->fd = open(filename,O_BINARY | O_CREAT | O_RDWR | O_TRUNC,_S_IWRITE | _S_IREAD);
- if(pfile->fd < 0){
- free(pname);
- if(pfile->customWindow)
- free(pfile->customWindow);
- free(pfile);
- pv_errstr = "\npvsys: unable to create file";
- return -1;
- }
- pfile->propsoffset = 0;
- pfile->datachunkoffset = 0;
- pfile->nFrames = 0;
- pfile->FramePos = 0;
- pfile->curpos = 0;
- pfile->name = pname;
- pfile->do_byte_reverse = !byte_order();
- files[i] = pfile;
- if(!pvoc_writeheader(i)) {
- close(pfile->fd);
- remove(pfile->name);
- free(pfile->name);
- if(pfile->customWindow)
- free(pfile->customWindow);
- free(pfile);
- files[i] = NULL;
- return -1;
- }
- return i;
- }
- int32_t pvoc_getpvxprops(int32_t ifd, PVOCDATA *data)
- {
- if(files[ifd]==NULL)
- return 0;
- #ifdef _DEBUG
- assert(data != NULL);
- #endif
- memcpy((char*) data,(char*) &files[ifd]->pvdata,sizeof(PVOCDATA));
- return 1;
- }
- /* tell pvsys to update pvocdata on close; return 0 for success */
- /* only other way to do this is to test initial params != 0 */
- /* I want to control this explicitly, for now */
- int32_t pvoc_set_needsupdate(int32_t ifd)
- {
- if(files[ifd]==NULL)
- return -1;
- if(files[ifd]->readonly)
- return -1;
- files[ifd]->needsupdate = 1;
- return 0;
- }
- //returns 0 for success
- int32_t pvoc_canupdate(int32_t ifd)
- {
- if(files[ifd]==NULL || files[ifd]->readonly)
- return -1;
- if(files[ifd]->needsupdate)
- return 0;
- else
- return -1;
- }
- //ONLY when creating new file using 2-stages.called last thing before file close.
- #if 0
- int32_t pvoc_updateprops(int32_t fd, const PVOCDATA *data)
- {
- DWORD pos;
-
- if(files[fd]==NULL)
- return -1;
- if(files[fd]->readonly)
- return -1;
- if(files[fd]->needsupdate==0)
- return -1;
- #ifdef _DEBUG
- // the only field that MUST be set, needed for read/write frames
- assert(files[fd]->pvdata.nAnalysisBins == data->nAnalysisBins);
- #endif
- // need to seek to propsoffset, then we can call write_pvocdata
- pos = lseek(files[fd]->fd,files[fd]->propsoffset,SEEK_SET);
- if(pos != files[fd]->propsoffset){
- pv_errstr = "\npvsys: error updating pvoc props";
- return 0;
- }
- return 1;
- }
- #endif
- int32_t pvoc_openfile(const char *filename,PVOCDATA *data,WAVEFORMATEX *fmt)
- {
- int32_t i;
- WAVEFORMATPVOCEX wfpx;
- char *pname;
- PVOCFILE *pfile = NULL;
- // long size = sizeof(WAVEFORMATPVOCEX);
-
- if(data==NULL || fmt==NULL){
- pv_errstr = "\npvsys: Internal error: NULL data arrays";
- return -1;
- }
- for(i=0;i < MAXFILES;i++)
- if(files[i]==NULL)
- break;
- if(i==MAXFILES){
- pv_errstr = "\npvsys: too many files open";
- return -1;
- }
- pfile = (PVOCFILE *) malloc(sizeof(PVOCFILE));
- if(pfile==NULL){
- pv_errstr = "\npvsys: no memory for file data";
- return -1;
- }
- pfile->customWindow = NULL;
- pname = (char *) malloc(strlen(filename)+1);
- if(pname == NULL){
- free(pfile);
- pv_errstr = "\npvsys: no memory";
- return -1;
- }
- pfile->fd = open(filename,O_BINARY | O_RDONLY,_S_IREAD);
- if(pfile->fd < 0){
- free(pname);
- free(pfile);
- pv_errstr = "\npvsys: unable to create file";
- return -1;
- }
- strcpy(pname,filename);
- pfile->datachunkoffset = 0;
- pfile->nFrames = 0;
- pfile->curpos = 0;
- pfile->FramePos = 0;
- pfile->name = pname;
- pfile->do_byte_reverse = !byte_order();
- pfile->readonly = 1;
- pfile->needsupdate = 0; // just to state it explicitly!
- pfile->to_delete = 0;
- files[i] = pfile;
- if(!pvoc_readheader(i,&wfpx)){
- close(pfile->fd);
- free(pfile->name);
- if(pfile->customWindow)
- free(pfile->customWindow);
- free(pfile);
- files[i] = NULL;
- return -1;
- }
- memcpy((char *)data, (char *)&(wfpx.data),sizeof(PVOCDATA));
- memcpy((char *)fmt,(char *)&(wfpx.wxFormat.Format),SIZEOF_WFMTEX);
- files[i] = pfile;
- return i;
- }
- static int32_t pvoc_readfmt(int32_t fd,int32_t byterev,WAVEFORMATPVOCEX *pWfpx)
- {
- DWORD dword;
- WORD word;
- #ifdef _DEBUG
- assert(fd >= 0);
- assert(pWfpx);
- #endif
- if(read(fd,(char *) &(pWfpx->wxFormat.Format.wFormatTag),sizeof(WORD)) != sizeof(WORD)
- || read(fd,(char *) &(pWfpx->wxFormat.Format.nChannels),sizeof(WORD)) != sizeof(WORD)
- || read(fd,(char *) &(pWfpx->wxFormat.Format.nSamplesPerSec),sizeof(DWORD)) != sizeof(DWORD)
- || read(fd,(char *) &(pWfpx->wxFormat.Format.nAvgBytesPerSec),sizeof(DWORD)) != sizeof(DWORD)
- || read(fd,(char *) &(pWfpx->wxFormat.Format.nBlockAlign),sizeof(WORD)) != sizeof(WORD)
- || read(fd,(char *) &(pWfpx->wxFormat.Format.wBitsPerSample),sizeof(WORD)) != sizeof(WORD)
- || read(fd,(char *) &(pWfpx->wxFormat.Format.cbSize),sizeof(WORD)) != sizeof(WORD)){
- pv_errstr = "\npvsys: error reading Source format data";
- return 0;
- }
- if(byterev){
- word = pWfpx->wxFormat.Format.wFormatTag;
- pWfpx->wxFormat.Format.wFormatTag= REVWBYTES(word);
- word = pWfpx->wxFormat.Format.nChannels;
- pWfpx->wxFormat.Format.nChannels = REVWBYTES(word);
- dword = pWfpx->wxFormat.Format.nSamplesPerSec;
- pWfpx->wxFormat.Format.nSamplesPerSec = REVDWBYTES(dword);
- dword = pWfpx->wxFormat.Format.nAvgBytesPerSec;
- pWfpx->wxFormat.Format.nAvgBytesPerSec = REVDWBYTES(dword);
- word = pWfpx->wxFormat.Format.nBlockAlign;
- pWfpx->wxFormat.Format.nBlockAlign = REVWBYTES(word);
- word = pWfpx->wxFormat.Format.wBitsPerSample;
- pWfpx->wxFormat.Format.wBitsPerSample = REVWBYTES(word);
- word = pWfpx->wxFormat.Format.cbSize;
- pWfpx->wxFormat.Format.cbSize = REVWBYTES(word);
- }
- /* the first clues this is pvx format...*/
- if(pWfpx->wxFormat.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE){
- pv_errstr = "\npvsys: not a WAVE_EX file";
- return 0;
- }
- if(pWfpx->wxFormat.Format.cbSize != 62){
- pv_errstr = "\npvsys: bad size for fmt chunk";
- return 0;
- }
- if(read(fd,(char *) &(pWfpx->wxFormat.Samples.wValidBitsPerSample),sizeof(WORD)) != sizeof(WORD)
- || read(fd,(char *) &(pWfpx->wxFormat.dwChannelMask),sizeof(DWORD)) != sizeof(DWORD)
- || read(fd,(char *) &(pWfpx->wxFormat.SubFormat),sizeof(GUID)) != sizeof(GUID)){
- pv_errstr = "\npvsys: error reading Extended format data";
- return 0;
- }
- if(byterev){
- word = pWfpx->wxFormat.Samples.wValidBitsPerSample;
- pWfpx->wxFormat.Samples.wValidBitsPerSample = REVWBYTES(word);
- dword = pWfpx->wxFormat.dwChannelMask;
- pWfpx->wxFormat.dwChannelMask = REVDWBYTES(dword);
- dword = pWfpx->wxFormat.SubFormat.Data1;
- pWfpx->wxFormat.SubFormat.Data1 = REVDWBYTES(dword);
- word = pWfpx->wxFormat.SubFormat.Data2;
- pWfpx->wxFormat.SubFormat.Data2 = REVWBYTES(word);
- word = pWfpx->wxFormat.SubFormat.Data3;
- pWfpx->wxFormat.SubFormat.Data3 = REVWBYTES(word);
- /* don't need to reverse the char array */
- }
- /* ... but this is the clincher */
- if(!compare_guids(&(pWfpx->wxFormat.SubFormat),&KSDATAFORMAT_SUBTYPE_PVOC)){
- pv_errstr = "\npvsys: not a PVOCEX file";
- return 0;
- }
- if(read(fd,(char *) &(pWfpx->dwVersion),sizeof(DWORD)) != sizeof(DWORD)
- || read(fd,(char *) &(pWfpx->dwDataSize),sizeof(DWORD)) != sizeof(DWORD)
- || read(fd,(char *) &(pWfpx->data),sizeof(PVOCDATA)) != sizeof(PVOCDATA)){
- pv_errstr = "\npvsys: error reading Extended pvoc format data";
- return 0;
- }
- if(byterev){
- SND_SAMP ssamp;
- dword = pWfpx->dwVersion;
- pWfpx->dwVersion = REVDWBYTES(dword);
- /* check it now! */
- if(pWfpx->dwVersion != PVX_VERSION){
- pv_errstr = "\npvsys: unknown pvocex Version";
- return 0;
- }
- dword = pWfpx->dwDataSize;
- pWfpx->dwDataSize = REVDWBYTES(dword);
- word = pWfpx->data.wWordFormat;
- pWfpx->data.wWordFormat= REVWBYTES(word);
- word = pWfpx->data.wAnalFormat;
- pWfpx->data.wAnalFormat= REVWBYTES(word);
- word = pWfpx->data.wSourceFormat;
- pWfpx->data.wSourceFormat= REVWBYTES(word);
- word = pWfpx->data.wWindowType;
- pWfpx->data.wWindowType= REVWBYTES(word);
- dword = pWfpx->data.nAnalysisBins;
- pWfpx->data.nAnalysisBins = REVDWBYTES(dword);
- dword = pWfpx->data.dwWinlen;
- pWfpx->data.dwWinlen = REVDWBYTES(dword);
- dword = pWfpx->data.dwOverlap;
- pWfpx->data.dwOverlap = REVDWBYTES(dword);
- dword = pWfpx->data.dwFrameAlign;
- pWfpx->data.dwFrameAlign = REVDWBYTES(dword);
- ssamp.fsamp = pWfpx->data.fAnalysisRate;
- //dword = * (DWORD *)&(pWfpx->data.fAnalysisRate); /* RWD TODO: use union */
- //dword = REVDWBYTES(dword);
- ssamp.lsamp = REVDWBYTES(ssamp.lsamp);
- //pWfpx->data.fAnalysisRate = *(float *)&dword;
- pWfpx->data.fAnalysisRate = ssamp.fsamp;
-
- ssamp.fsamp = pWfpx->data.fWindowParam;
- //dword = * (DWORD *)&(pWfpx->data.fWindowParam);
- //dword = REVDWBYTES(dword);
- ssamp.lsamp = REVDWBYTES(ssamp.lsamp);
- //pWfpx->data.fWindowParam = *(float *)&dword;
- pWfpx->data.fWindowParam = ssamp.fsamp;
- }
- if(pWfpx->dwVersion != PVX_VERSION){
- pv_errstr = "\npvsys: unknown pvocex Version";
- return 0;
- }
- return 1;
- }
- static int32_t pvoc_readheader(int32_t ifd,WAVEFORMATPVOCEX *pWfpx)
- {
- DWORD tag, size,riffsize;
- int32_t fmtseen = 0, /* dataseen = 0,*/ windowseen = 0;
- // DWORD windowlength = 0;
- #ifdef _DEBUG
- assert(pWfpx);
- assert(files[ifd]);
- assert(files[ifd]->fd >= 0);
- size = sizeof(WAVEFORMATEXTENSIBLE);
- size += 2 * sizeof(DWORD);
- size += sizeof(PVOCDATA);
- #endif
- if(read(files[ifd]->fd,(char *) &tag,sizeof(DWORD)) != sizeof(DWORD)
- || read(files[ifd]->fd,(char *) &size,sizeof(DWORD)) != sizeof(DWORD)){
- pv_errstr = "\npvsys: error reading header";
- return 0;
- }
- if(files[ifd]->do_byte_reverse)
- size = REVDWBYTES(size);
- else
- tag = REVDWBYTES(tag);
- if(tag != TAG('R','I','F','F')){
- pv_errstr = "\npvsys: not a RIFF file";
- return 0;
- }
- if(size < 24 * sizeof(DWORD) + SIZEOF_FMTPVOCEX){
- pv_errstr = "\npvsys: file too small";
- return 0;
- }
- riffsize = size;
- if(read(files[ifd]->fd,(char *) &tag,sizeof(DWORD)) != sizeof(DWORD)){
- pv_errstr = "\npvsys: error reading header";
- return 0;
- }
- if(!files[ifd]->do_byte_reverse)
- tag = REVDWBYTES(tag);
- if(tag != TAG('W','A','V','E')){
- pv_errstr = "\npvsys: not a WAVE file";
- return 0;
- }
- riffsize -= sizeof(DWORD);
- /*loop for chunks */
- while(riffsize > 0){
- if(read(files[ifd]->fd,(char *) &tag,sizeof(DWORD)) != sizeof(DWORD)
- || read(files[ifd]->fd,(char *) &size,sizeof(DWORD)) != sizeof(DWORD)){
- pv_errstr = "\npvsys: error reading header";
- return 0;
- }
- if(files[ifd]->do_byte_reverse)
- size = REVDWBYTES(size);
- else
- tag = REVDWBYTES(tag);
- riffsize -= 2 * sizeof(DWORD);
- switch(tag){
- case TAG('f','m','t',' '):
- /* bail out if not a pvoc file: not trying to read all WAVE formats!*/
- if(size < SIZEOF_FMTPVOCEX){
- pv_errstr = "\npvsys: not a PVOC-EX file";
- return 0;
- }
- if(!pvoc_readfmt(files[ifd]->fd,files[ifd]->do_byte_reverse,pWfpx)){
- pv_errstr = "\npvsys: error reading format chunk";
- return 0;
- }
- riffsize -= SIZEOF_FMTPVOCEX;
- fmtseen = 1;
- memcpy((char *)&(files[ifd]->fmtdata),(char *)&(pWfpx->wxFormat),SIZEOF_WFMTEX);
- memcpy((char *)&(files[ifd]->pvdata),(char *)&(pWfpx->data),sizeof(PVOCDATA));
- break;
- case TAG('P','V','X','W'):
- if(!fmtseen){
- pv_errstr = "\npvsys: PVXW chunk found before fmt chunk.";
- return 0;
- }
- if(files[ifd]->pvdata.wWindowType!=PVOC_CUSTOM){
- /*whaddayado? can you warn the user and continue?*/
- pv_errstr = "\npvsys: PVXW chunk found but custom window not specified";
- return 0;
- }
- files[ifd]->customWindow = malloc(files[ifd]->pvdata.dwWinlen * sizeof(float));
- if(files[ifd]->customWindow == NULL){
- pv_errstr = "\npvsys: no memory for custom window data.";
- return 0;
- }
- if(pvoc_readWindow(files[ifd]->fd,files[ifd]->do_byte_reverse,
- files[ifd]->customWindow,files[ifd]->pvdata.dwWinlen)
- != (int)(files[ifd]->pvdata.dwWinlen * sizeof(float))){
- pv_errstr = "\npvsys: error reading window data.";
- return 0;
- }
- windowseen = 1;
- break;
- case TAG('d','a','t','a'):
- if(riffsize - size != 0){
- pv_errstr = "\npvsys: bad RIFF file";
- return 0;
- }
-
- if(!fmtseen){
- pv_errstr = "\npvsys: bad format, data chunk before fmt chunk";
- return 0;
- }
- if(files[ifd]->pvdata.wWindowType==PVOC_CUSTOM)
- if(!windowseen){
- pv_errstr = "\npvsys: custom window chunk PVXW not found";
- return 0;
- }
- files[ifd]->datachunkoffset = lseek(files[ifd]->fd,0,SEEK_CUR);
- files[ifd]->curpos = files[ifd]->datachunkoffset;
- /* not m/c frames, for now */
- files[ifd]->nFrames = size / files[ifd]->pvdata.dwFrameAlign;
- return 1;
- break;
- default:
- /* skip any onknown chunks */
- riffsize -= 2 * sizeof(DWORD);
- if(lseek(files[ifd]->fd,size,SEEK_CUR) < 0){
- pv_errstr = "\npvsys: error skipping unknown WAVE chunk";
- return 0;
- }
- riffsize -= size;
- break;
- }
- }
- /* if here, something very wrong!*/
- pv_errstr = "\npvsys: bad format in RIFF file";
- return 0;
- }
- static int32_t pvoc_writeheader(int ofd)
- {
- int32_t tag,size,version;
- WORD validbits;
-
- const GUID *pGuid = &KSDATAFORMAT_SUBTYPE_PVOC;
- #ifdef _DEBUG
- assert(files[ofd] != NULL);
- assert(files[ofd]->fd >=0);
- #endif
- tag = TAG('R','I','F','F');
- size = 0;
- if(files[ofd]->do_byte_reverse)
- size = REVDWBYTES(size);
- if(!files[ofd]->do_byte_reverse)
- tag = REVDWBYTES(tag);
- if(write(files[ofd]->fd,&tag,sizeof(int32_t)) != sizeof(int32_t)
- || write(files[ofd]->fd,&size,sizeof(int32_t)) != sizeof(int32_t)) {
- pv_errstr = "\npvsys: error writing header";
- return 0;
- }
- tag = TAG('W','A','V','E');
- if(!files[ofd]->do_byte_reverse)
- tag = REVDWBYTES(tag);
- if(write(files[ofd]->fd,&tag,sizeof(int32_t)) != sizeof(int32_t)){
- pv_errstr = "\npvsys: error writing header";
- return 0;
- }
- tag = TAG('f','m','t',' ');
- size = SIZEOF_WFMTEX + sizeof(WORD) +
- sizeof(DWORD)
- + sizeof(GUID)
- + 2*sizeof(DWORD)
- + sizeof(PVOCDATA);
- if(files[ofd]->do_byte_reverse)
- size = REVDWBYTES(size);
- if(!files[ofd]->do_byte_reverse)
- tag = REVDWBYTES(tag);
- if(write(files[ofd]->fd,(char *)&tag,sizeof(int32_t)) != sizeof(int32_t)
- || write(files[ofd]->fd,(char *)&size,sizeof(int32_t)) != sizeof(int32_t)) {
- pv_errstr = "\npvsys: error writing header";
- return 0;
- }
- /* we need to record where we are, as we may have to update fmt data before file close */
- files[ofd]->fmtchunkoffset = lseek(files[ofd]->fd,0,SEEK_CUR);
-
- if(write_fmt(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->fmtdata)) != SIZEOF_WFMTEX){
- pv_errstr = "\npvsys: error writing fmt chunk";
- return 0;
- }
- validbits = files[ofd]->fmtdata.wBitsPerSample; /*nothing fancy here */
- if(files[ofd]->do_byte_reverse)
- validbits = REVWBYTES(validbits);
- if(write(files[ofd]->fd,(char *) &validbits,sizeof(WORD)) != sizeof(WORD)){
- pv_errstr = "\npvsys: error writing fmt chunk";
- return 0;
- }
- /* we will take this from a WAVE_EX file, in due course */
- size = 0; /*dwChannelMask*/
- if(write(files[ofd]->fd,(char *)&size,sizeof(DWORD)) != sizeof(DWORD)){
- pv_errstr = "\npvsys: error writing fmt chunk";
- return 0;
- }
- if(write_guid(files[ofd]->fd,files[ofd]->do_byte_reverse,pGuid) != sizeof(GUID)){
- pv_errstr = "\npvsys: error writing fmt chunk";
- return 0;
- }
- version = 1;
- size = sizeof(PVOCDATA);
- if(files[ofd]->do_byte_reverse){
- version = REVDWBYTES(version);
- size = REVDWBYTES(size);
- }
- if(write(files[ofd]->fd,&version,sizeof(int32_t)) != sizeof(int32_t)
- || write(files[ofd]->fd,&size,sizeof(int32_t)) != sizeof(int32_t)){
- pv_errstr = "\npvsys: error writing fmt chunk";
- return 0;
- }
- /* RWD new 2022 so we can update all pvocex props on closing new file */
- files[ofd]->propsoffset = lseek(files[ofd]->fd,0,SEEK_CUR);
-
- if(write_pvocdata(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->pvdata)) != sizeof(PVOCDATA)){
- pv_errstr = "\npvsys: error writing fmt chunk";
- return 0;
- }
- /* VERY experimental; may not even be a good idea...*/
- if(files[ofd]->customWindow){
- tag = TAG('P','V','X','W');
- size = files[ofd]->pvdata.dwWinlen * sizeof(float);
- if(files[ofd]->do_byte_reverse)
- size = REVDWBYTES(size);
- else
- tag = REVDWBYTES(tag);
- if(write(files[ofd]->fd,(char *)&tag,sizeof(int32_t)) != sizeof(int32_t)
- || write(files[ofd]->fd,(char *)&size,sizeof(int32_t)) != sizeof(int32_t)) {
- pv_errstr = "\npvsys: error writing header";
- return 0;
- }
- if(pvoc_writeWindow(files[ofd]->fd,
- files[ofd]->do_byte_reverse,
- files[ofd]->customWindow,
- files[ofd]->pvdata.dwWinlen)!= (int)(files[ofd]->pvdata.dwWinlen * sizeof(float))){
- pv_errstr = "\npvsys: error writing window data.";
- return 0;
- }
- }
- /* no other chunks to write yet! */
- tag = TAG('d','a','t','a');
- if(!files[ofd]->do_byte_reverse)
- tag = REVDWBYTES(tag);
- if(write(files[ofd]->fd,&tag,sizeof(int32_t)) != sizeof(int32_t)){
- pv_errstr = "\npvsys: write error writing header";
- return 0;
- }
- /* we need to update size later on...*/
- size = 0;
- if(write(files[ofd]->fd,&size,sizeof(int32_t)) != sizeof(int32_t)){
- pv_errstr = "\npvsys: write error writing header";
- return 0;
- }
- files[ofd]->datachunkoffset = lseek(files[ofd]->fd,0,SEEK_CUR);
- files[ofd]->curpos = files[ofd]->datachunkoffset;
- return 1;
- }
- /* called by pvoc_closefile() */
- /* which checks "readonly", if not set, file is newly created */
- static int32_t pvoc_updateheader(int ofd)
- {
- int32_t riffsize,datasize;
- DWORD pos;
- //RWD Oct 2025 avoid singed/unsigned errors
- long longpos;
- #ifdef _DEBUG
- assert(files[ofd]);
- assert(files[ofd]->fd >= 0);
- //assert(files[ofd]->curpos == lseek(files[ofd]->fd,0,SEEK_CUR));
- assert(!files[ofd]->readonly);
- #endif
- datasize = files[ofd]->curpos - files[ofd]->datachunkoffset;
- pos = lseek(files[ofd]->fd,files[ofd]->datachunkoffset-sizeof(DWORD),SEEK_SET);
- if(pos != files[ofd]->datachunkoffset-sizeof(DWORD)){
- pv_errstr = "\npvsys: seek error updating data chunk";
- return 0;
- }
- if(files[ofd]->do_byte_reverse)
- datasize = REVDWBYTES(datasize);
- if(write(files[ofd]->fd,(char *) &datasize,sizeof(DWORD)) != sizeof(DWORD)){
- pv_errstr = "\npvsys: write error updating data chunk";
- return 0;
- }
- riffsize = files[ofd]->curpos - 2* sizeof(DWORD);
- if(files[ofd]->do_byte_reverse)
- riffsize = REVDWBYTES(riffsize);
- pos = lseek(files[ofd]->fd,sizeof(DWORD),SEEK_SET);
- if(pos != sizeof(DWORD)){
- pv_errstr = "\npvsys: seek error updating riff chunk";
- return 0;
- }
- if(write(files[ofd]->fd,(char *) &riffsize,sizeof(DWORD)) != sizeof(DWORD)){
- pv_errstr = "\npvsys: write error updating riff chunk";
- return 0;
- }
-
- // update PVOCDATA and WAVEFORMATEX fields if required
- if(files[ofd]->needsupdate){
- #ifdef _DEBUG
- fprintf(stderr,"updating in pvoc_update_header()\n");
- #endif
- WORD validbits;
-
- pos = lseek(files[ofd]->fd,files[ofd]->fmtchunkoffset,SEEK_SET);
- if(pos != (DWORD) files[ofd]->fmtchunkoffset){
- pv_errstr = "\npvsys: seek error updating fmt data";
- return 0;
- }
-
- pos = write_fmt(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->fmtdata));
- if(pos != SIZEOF_WFMTEX){
- pv_errstr = "\npvsys: write error updating fmt data";
- return 0;
- }
- // need to update validbits in case we need it, at least make it sensible
- // this should have been updated earlier
- validbits = files[ofd]->fmtdata.wBitsPerSample;
- if(files[ofd]->do_byte_reverse)
- validbits = REVWBYTES(validbits);
-
- if(write(files[ofd]->fd,(char *) &validbits,sizeof(WORD)) != sizeof(WORD)){
- pv_errstr = "\npvsys: error writing extended fmt chunk";
- return 0;
- }
- pos = lseek(files[ofd]->fd,files[ofd]->propsoffset,SEEK_SET);
- if(pos != (DWORD) files[ofd]->propsoffset){
- pv_errstr = "\npvsys: seek error updating pvx data";
- return 0;
- }
- //int32_t write_pvocdata(int32_t fd,int32_t byterev,const PVOCDATA *pData)
- pos = write_pvocdata(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->pvdata));
- if(pos != sizeof(PVOCDATA)){
- pv_errstr = "\npvsys: write error updating pvx data";
- return 0;
- }
- }
- /*pos*/ longpos = lseek(files[ofd]->fd, 0, SEEK_END);
- if(/*pos*/ longpos < 0) {
- pv_errstr = "\npvsys: seek error seeking to end of file";
- return 0;
- }
-
- return 1;
- }
- int32_t pvoc_update_closefile(int ofd, const PVOCDATA *data, const WAVEFORMATEXTENSIBLE *wfx)
- {
- if(files[ofd]==NULL){
- pv_errstr = "\npvsys: file does not exist";
- return 0;
- }
-
- if(files[ofd]->fd < 0){
- pv_errstr = "\npvsys: file not open";
- return 0;
- }
- if(files[ofd]->readonly) {
- pv_errstr = "\ncannot update readonly file"; // probably an input file
- }
- else {
- memcpy((char*) &files[ofd]->fmtdata, (char*) &wfx->Format, sizeof(WAVEFORMATEX));
- memcpy((char*) &files[ofd]->pvdata, (char*) data, sizeof(PVOCDATA));
- }
- return pvoc_closefile(ofd);
- }
- int32_t pvoc_closefile(int ofd)
- {
- if(files[ofd]==NULL){
- pv_errstr = "\npvsys: file does not exist";
- return 0;
- }
- if(files[ofd]->fd < 0){
- pv_errstr = "\npvsys: file not open";
- return 0;
- }
- if(!files[ofd]->readonly) {
- if(!pvoc_updateheader(ofd))
- return 0;
- }
- close(files[ofd]->fd);
- if(files[ofd]->to_delete && !(files[ofd]->readonly))
- remove(files[ofd]->name);
- free(files[ofd]->name);
- free(files[ofd]);
- files[ofd] = 0;
- return 1;
- }
- /* does not directly address m/c streams, or alternative numeric formats, yet
- * so for m/c files, write each frame in turn, for each channel.
- * The format requires multi-channel frames to be interleaved in the usual way:
- * if nChannels= 4, the file will contain:
- * frame[0][0],frame[0][1],frame[0][2],frame[0][3],frme[1][0],frame[1][1].....
- *
- * The idea is to offer e.g. a floats version and a longs version ONLY, but
- * independently of the underlying representation, so that the user can write a floats
- * block, even though the underlying format might be longs or doubles. Most importantly,
- * the user does not have to deal with byte-reversal, which would otherwise always be the case
- * it the user had direct access to the file.
- *
- * So these functions are the most likely to change over time!.
- *
- * return 0 for error, 1 for success. This could change....
- */
- int32_t pvoc_putframes(int ofd,const float *frame,int32_t numframes)
- {
- DWORD i;
- DWORD towrite; /* count in 'words' */
- int32_t temp,*lfp;
-
- if(files[ofd]==NULL){
- pv_errstr = "\npvsys: bad file descriptor";
- return 0;
- }
- if(files[ofd]->fd < 0){
- pv_errstr = "\npvsys: file not open";
- return 0;
- }
- /* NB doubles not supported yet */
-
- towrite = files[ofd]->pvdata.nAnalysisBins * 2 * numframes;
-
- if(files[ofd]->do_byte_reverse){
- /* do this without overwriting source data! */
- lfp = (int32_t *) frame;
- for(i=0;i < towrite; i++){
- temp = *lfp++;
- temp = REVDWBYTES(temp);
- if(write(files[ofd]->fd,(char *) &temp,sizeof(int32_t)) != sizeof(int32_t)){
- pv_errstr = "\npvsys: error writing data";
- return 0;
- }
- }
- }
- else {
- if(write(files[ofd]->fd,(char *) frame,towrite * sizeof(float)) != (int32_t)(towrite * sizeof(float))){
- pv_errstr = "\npvsys: error writing data";
- return 0;
- }
- }
- files[ofd]->FramePos += numframes;
- files[ofd]->nFrames += numframes;
- files[ofd]->curpos += towrite * sizeof(float);
- return 1;
- }
- /* Simplistic read function
- * best practice here is to read nChannels frames *
- * return -1 for error, 0 for EOF, else numframes read
- */
- int32_t pvoc_getframes(int32_t ifd,float *frames,DWORD nframes)
- {
- int32_t i;
- int32_t toread;
- int32_t oval,temp,*lfp;
- int32_t got;
- int32_t rc = -1;
- if(files[ifd]==NULL){
- pv_errstr = "\npvsys: bad file descriptor";
- return rc;
- }
- if(files[ifd]->fd < 0){
- pv_errstr = "\npvsys: file not open";
- return rc;
- }
- toread = files[ifd]->pvdata.nAnalysisBins * 2 * nframes;
- if(files[ifd]->do_byte_reverse){
- lfp = (int32_t *) frames;
- #ifdef SINGLE_FLOAT
- for(i=0;i < toread;i++){
- if((got=read(files[ifd]->fd,(char *) &temp,sizeof(int32_t))) <0){
- pv_errstr = "\npvsys: error reading data";
- return rc;
- }
- if(got < sizeof(int32_t)){
- /* file size incorrect? */
- return 0; /* assume EOF */
- }
- temp = REVDWBYTES(temp);
- *lfp++ = temp;
- }
- #else
- /* much faster on G4!!! */
- got = read(files[ifd]->fd,(char *)frames,toread * sizeof(float));
- if(got < 0){
- pv_errstr = "\npvsys: error reading data";
- return rc;
- }
- for(i=0;i < got / (int32_t) sizeof(float);i++){
- temp = *lfp;
- oval = REVDWBYTES(temp);
- *lfp++ = oval;
- }
- if(got < (int)(toread * sizeof(float))){
- /* some (user?) error in file size: return integral number of frames read*/
- toread = got / (files[ifd]->pvdata.nAnalysisBins * 2 * sizeof(float));
- /* RWD 4:2002 need to revise this too */
- nframes = toread;
- }
-
- #endif
- rc = nframes; /*RWD 4:2002 */
- }
- else{
- if((got = read(files[ifd]->fd,(char *)frames,toread * sizeof(float))) < (int32_t)(toread * sizeof(float))){
- if(got < 0){
- pv_errstr = "\npvsys: error reading data";
- return rc;
- }
- else if(got < (int)(toread * sizeof(float))){
- /* some (user?) error in file size: return integral number of frames read*/
- toread = got / (files[ifd]->pvdata.nAnalysisBins * 2 * sizeof(float));
- rc = toread;
- /* RWD 4:2002 need to revise this too */
- nframes = toread;
- }
- }
- else
- rc = nframes;
- }
- /*files[ifd]->curpos += (toread * sizeof(float));*/
- files[ifd]->curpos += got; /* RWD 4:2002 */
- files[ifd]->FramePos += nframes;
- return rc;
- }
- // return 0 for success, -1 for error
- int32_t pvoc_rewind(int32_t ifd,int32_t skip_first_frame)
- {
- int32_t rc = -1;
- int32_t fd;
- int32_t pos;
- int32_t skipsize = 0;
- int32_t skipframes = 0;
- if(files[ifd]==NULL){
- pv_errstr = "\npvsys: bad file descriptor";
- return rc;
- }
- if(files[ifd]->fd < 0){
- pv_errstr = "\npvsys: file not open";
- return rc;
- }
- skipframes = files[ifd]->fmtdata.nChannels;
- //skipsize = files[ifd]->pvdata.dwFrameAlign * skipframes;
-
-
- fd = files[ifd]->fd;
- pos = files[ifd]->datachunkoffset;
- if(skip_first_frame){
- skipsize = files[ifd]->pvdata.dwFrameAlign * skipframes;
- pos += skipsize;
- }
- if(lseek(fd,pos,SEEK_SET) != pos ) {
- pv_errstr = "\npvsys: error rewinding file";
- return rc;
- }
- files[ifd]->curpos = /* files[ifd]->datachunkoffset + skipsize */ pos;
- files[ifd]->FramePos = skipframes;
- return 0;
- }
- /* may be more to do in here later on */
- int32_t pvsys_release(void)
- {
- int32_t i;
- for(i=0;i < MAXFILES;i++){
- if(files[i]) {
- #ifdef _DEBUG
- fprintf(stderr,"\nDEBUG WARNING: file %d still open!\n",i);
- #endif
- if(!pvoc_closefile(i)){
- pv_errstr = "\npvsys: unable to close file on termination";
- return 0;
- }
- }
- }
- return 1;
- }
- /*return raw framecount: channel-agnostic for now */
- int32_t pvoc_framecount(int32_t ifd)
- {
- if(files[ifd]==NULL)
- return -1;
- return files[ifd]->nFrames;
- }
- /* RWD Jan 2014 */
- // return -1 for error, else framepos
- int32_t pvoc_framepos(int32_t ifd)
- {
- if(files[ifd]==NULL)
- return -1;
-
- return files[ifd]->FramePos;
- }
- // return -1 for error, 0 for success
- //NB framecount always disregards chans. But offset represents m/c block: offset*2 to get frames
- int32_t pvoc_seek_mcframe(int32_t ifd, int32_t offset, int32_t mode)
- {
- // DWORD mcframealign;
- int32_t rawoffset; /* RWD need to be signed to work for to and fro seeks*/
- int32_t rc = -1;
- int32_t mcframealign;
- int32_t pvxcur = 0;
- if(files[ifd]==NULL)
- return -1;
- mcframealign = files[ifd]->pvdata.dwFrameAlign * files[ifd]->fmtdata.nChannels;
- rawoffset = offset * mcframealign;
- switch (mode) {
- case SEEK_SET:
- // offset is m/c quantity, e.g. 2 * frame for stereo
- if (offset >= (files[ifd]->nFrames / files[ifd]->fmtdata.nChannels)) {
- pv_errstr = "\npvsys: seek target beyond end of file";
- break;
- }
- rawoffset += files[ifd]->datachunkoffset;
- if (lseek(files[ifd]->fd, rawoffset, SEEK_SET) != (long)rawoffset) {
- pv_errstr = "\npvsys: seek error, SEEK_SET";
- return -1;
- }
- files[ifd]->curpos = rawoffset;
- files[ifd]->FramePos = offset * files[ifd]->fmtdata.nChannels;
- rc = 0;
- break;
- case SEEK_END:
- // go to end of file + offset, offset <= 0
- if (offset > 0) {
- pv_errstr = "\npvsys: seek target before start of file, offset must be <= 0";
- break;
- }
- #ifdef _DEBUG
- fprintf(stderr, "pvoc_seek_mcframe: fd = %d\n", ifd);
- #endif
- //NB not relative to datachunkoffset in this case
- if (lseek(files[ifd]->fd, rawoffset, SEEK_END) < 0) {
- pv_errstr = "\npvsys: seek error, SEEK_END";
- return -1;
- }
- #ifdef _DEBUG
- fprintf(stderr, "pvoc_seek_mcframe: files[%d]->nFrames = %d\n", ifd, files[ifd]->nFrames);
- #endif
- files[ifd]->FramePos = files[ifd]->nFrames - (offset * files[ifd]->fmtdata.nChannels);
- files[ifd]->curpos = files[ifd]->datachunkoffset + files[ifd]->FramePos * files[ifd]->pvdata.dwFrameAlign;
- #ifdef _DEBUG
- fprintf(stderr, "pvoc_seek_mcframe: got curpos = %d\n", files[ifd]->curpos);
- #endif
- rc = 0; //success!
- break;
- case SEEK_CUR:
- pvxcur = pvoc_framepos(ifd);
- if (pvxcur + offset >= files[ifd]->nFrames) {
- pv_errstr = "\npvsys: seek target beyond end of file";
- return rc;
- }
- if (pvxcur + offset < 0) {
- pv_errstr = "\npvsys: seek target beyond start of file";
- return rc;
- }
- rawoffset = offset * mcframealign;
- if (lseek(files[ifd]->fd, rawoffset, SEEK_CUR) < 0) {
- pv_errstr = "\npvsys: seek error, SEEK_CUR";
- return -1;
- }
- files[ifd]->FramePos = pvxcur + (offset * files[ifd]->fmtdata.nChannels);
- files[ifd]->curpos = files[ifd]->datachunkoffset + files[ifd]->FramePos * files[ifd]->pvdata.dwFrameAlign;
- rc = 0;
- break;
- }
- return rc;
- }
|