#include "cmGlobal.h" #include "cmFloatTypes.h" #include "cmRpt.h" #include "cmErr.h" #include "cmCtx.h" #include "cmMem.h" #include "cmMallocDebug.h" #include "cmAudioFile.h" #include "cmVectOpsTemplateMain.h" #include "cmAudioFileMgr.h" struct cmAfm_str; typedef struct { cmSample_t* minV; // minV[summN] cmSample_t* maxV; // maxV[summN] unsigned summN; // lenght of minV[] and maxV[] } cmAfmSummary_t; typedef struct cmAfmFile_str { unsigned id; cmAudioFileH_t afH; cmAudioFileInfo_t afInfo; unsigned smpPerSummPt; cmAfmSummary_t* summArray; // summArray[ afInfo.chCnt ] cmSample_t* summMem; // memory used by summArray[] vectors struct cmAfm_str* p; struct cmAfmFile_str* next; struct cmAfmFile_str* prev; } cmAfmFile_t; typedef struct cmAfm_str { cmErr_t err; cmAfmFile_t* list; } cmAfm_t; cmAfmH_t cmAfmNullHandle = cmSTATIC_NULL_HANDLE; cmAfmFileH_t cmAfmFileNullHandle = cmSTATIC_NULL_HANDLE; cmAfm_t* _cmAfmHandleToPtr( cmAfmH_t h ) { cmAfm_t* p = (cmAfm_t*)h.h; assert(p!=NULL); return p; } cmAfmFile_t* _cmAfmFileHandleToPtr( cmAfmFileH_t fh ) { cmAfmFile_t* fp = (cmAfmFile_t*)fh.h; assert(fp!=NULL); return fp; } cmAfmRC_t _cmAfmFileClose( cmAfmFile_t* fp ) { cmAfmRC_t rc = kOkAfmRC; if( cmAudioFileIsValid( fp->afH ) ) if( cmAudioFileDelete( &fp->afH) != kOkAfRC ) return cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file close failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH))); if( fp->next != NULL ) fp->next->prev = fp->prev; if( fp->prev != NULL ) fp->prev->next = fp->next; if( fp->p->list == fp ) fp->p->list = fp->next; cmMemFree(fp->summArray); cmMemFree(fp->summMem); cmMemFree(fp); return rc; } cmAfmRC_t cmAfmFileOpen( cmAfmH_t h, cmAfmFileH_t* fhp, const cmChar_t* audioFn, unsigned id, cmAudioFileInfo_t* afInfo ) { cmAfmRC_t rc; cmRC_t afRC; if((rc = cmAfmFileClose(fhp)) != kOkAfmRC ) return rc; cmAfmFile_t* fp = cmMemAllocZ(cmAfmFile_t,1); fp->p = _cmAfmHandleToPtr(h); // open the audio file if( cmAudioFileIsValid(fp->afH = cmAudioFileNewOpen(audioFn, &fp->afInfo, &afRC, fp->p->err.rpt )) == false ) { rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"The audio file '%s' could not be opened.",cmStringNullGuard(audioFn)); goto errLabel; } // prepend the new file to the mgr's file list if( fp->p->list != NULL ) fp->p->list->prev = fp; fp->next = fp->p->list; fp->p->list = fp; fp->id = id; fhp->h = fp; if( afInfo != NULL ) *afInfo = fp->afInfo; errLabel: if( rc != kOkAfmRC ) _cmAfmFileClose(fp); return rc; } cmAfmRC_t cmAfmFileClose( cmAfmFileH_t* fhp ) { cmAfmRC_t rc = kOkAfmRC; if( fhp==NULL || cmAfmFileIsValid(*fhp)==false) return rc; cmAfmFile_t* fp = _cmAfmFileHandleToPtr( *fhp ); if((rc = _cmAfmFileClose(fp)) != kOkAfmRC ) return rc; fhp->h = NULL; return rc; } bool cmAfmFileIsValid( cmAfmFileH_t fh ) { return fh.h != NULL; } unsigned cmAfmFileId( cmAfmFileH_t fh ) { cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh ); return fp->id; } cmAudioFileH_t cmAfmFileHandle( cmAfmFileH_t fh ) { cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh ); return fp->afH; } const cmAudioFileInfo_t* cmAfmFileInfo( cmAfmFileH_t fh ) { cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh ); return &fp->afInfo; } cmAfmRC_t cmAfmFileSummarize( cmAfmFileH_t fh, unsigned smpPerSummPt ) { cmAfmFile_t* fp = _cmAfmFileHandleToPtr(fh); cmAfmRC_t rc = kOkAfmRC; unsigned chCnt = fp->afInfo.chCnt; // summary points per channel per vector unsigned summN = (unsigned)ceil((double)fp->afInfo.frameCnt / smpPerSummPt ); // total summary points in all channels and vectors unsigned n = chCnt*2*summN; // Calc the number of summary points per audio file read unsigned ptsPerRd = cmMax(1,cmMax(smpPerSummPt,8192) / smpPerSummPt); // Calc the number samples per audio file read as an integer multiple of ptsPerRd. unsigned frmCnt = ptsPerRd * smpPerSummPt; unsigned actualFrmCnt = 0; cmSample_t* chBuf[ chCnt ]; cmSample_t buf[ frmCnt * chCnt ]; unsigned i; // allocate the summary record array if( fp->summArray == NULL ) fp->summArray = cmMemAllocZ( cmAfmSummary_t, chCnt ); // allocate the summary vector memory for all channels fp->summMem = cmMemResizeZ( cmSample_t, fp->summMem, n); fp->smpPerSummPt = smpPerSummPt; // setup the summary record array and audio file read buffer for(i=0; isummArray[i].minV = fp->summMem + i * summN * 2; fp->summArray[i].maxV = fp->summArray[i].minV + summN; fp->summArray[i].summN = summN; // setup the audio file reading channel buffer chBuf[i] = buf + (i*frmCnt); } // read the entire file and calculate the summary vectors i = 0; do { unsigned chIdx = 0; unsigned j,k; // read the next frmCnt samples from the if( cmAudioFileReadSample(fp->afH, frmCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC ) { rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH))); goto errLabel; } // for each summary point for(k=0; ksummArray[j].minV[i] = cmVOS_Min(chBuf[j]+k,m,1); fp->summArray[j].maxV[i] = cmVOS_Max(chBuf[j]+k,m,1); } } }while( i= outCnt ); double smpPerOut = (double)smpCnt/outCnt; double summPerOut = smpPerOut/fp->smpPerSummPt; unsigned i; for(i=0; ismpPerSummPt; // starting summary pt index double fsei = fsbi + summPerOut; // endiing summary pt index unsigned si = (unsigned)floor(fsbi); unsigned sn = (unsigned)floor(fsei - fsbi + 1); if( si > fp->summArray[chIdx].summN ) { minV[i] = 0; maxV[i] = 0; } else { if( si + sn > fp->summArray[chIdx].summN ) sn = fp->summArray[chIdx].summN - si; if( sn == 0 ) { minV[i] = 0; maxV[i] = 0; } else { minV[i] = cmVOS_Min(fp->summArray[chIdx].minV+si,sn,1); maxV[i] = cmVOS_Max(fp->summArray[chIdx].maxV+si,sn,1); } } } return kOkAfmRC; } // Downsample the audio data to produce the output. cmAfmRC_t _cmAfmFileGetDownAudio( cmAfmFile_t* fp, unsigned chIdx, unsigned begSmpIdx, unsigned smpCnt, cmSample_t* minV, cmSample_t* maxV, unsigned outCnt ) { assert( smpCnt >= outCnt ); cmAfmRC_t rc = kOkAfmRC; unsigned actualFrmCnt = 0; unsigned chCnt = 1; unsigned i; cmSample_t buf[ smpCnt ]; cmSample_t* chBuf[] = { buf }; // seek to the read location if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC ) { rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH))); goto errLabel; } // read 'smpCnt' samples into chBuf[][] if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC ) { rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH))); goto errLabel; } double smpPerOut = (double)smpCnt/outCnt; for(i=0; i smpCnt ) { minV[i] = 0; maxV[i] = 0; } if( si + sn > smpCnt ) sn = smpCnt - si; minV[i] = cmVOS_Min(chBuf[chIdx]+si,sn,1); maxV[i] = cmVOS_Max(chBuf[chIdx]+si,sn,1); } errLabel: return rc; } // If there is one or less summary points per output cmAfmRC_t _cmAfmFileGetUpSummary( cmAfmFile_t* fp, unsigned chIdx, unsigned begSmpIdx, unsigned smpCnt, cmSample_t* minV, cmSample_t* maxV, unsigned outCnt ) { assert( outCnt >= smpCnt ); cmAfmRC_t rc = kOkAfmRC; unsigned actualFrmCnt = 0; unsigned chCnt = 1; unsigned i; cmSample_t buf[ smpCnt ]; cmSample_t* chBuf[] = { buf }; if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC ) { rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH))); goto errLabel; } if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC ) { rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH))); goto errLabel; } for(i=0; iafInfo.srate < maxHiResDurSecs ) rc = _cmAfmFileGetDownAudio( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt ); else rc = _cmAfmFileGetDownSummary( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt ); } return rc; } //---------------------------------------------------------------------------- // Audio File Manager //---------------------------------------------------------------------------- cmAfmRC_t _cmAfmDestroy( cmAfm_t* p ) { cmAfmRC_t rc = kOkAfmRC; while( p->list != NULL ) { if((rc = _cmAfmFileClose(p->list)) != kOkAfmRC ) goto errLabel; } cmMemFree(p); errLabel: return rc; } cmAfmRC_t cmAfmCreate( cmCtx_t* ctx, cmAfmH_t* hp ) { cmAfmRC_t rc; if((rc = cmAfmDestroy(hp)) != kOkAfmRC ) return rc; cmAfm_t* p = cmMemAllocZ(cmAfm_t,1); cmErrSetup(&p->err,&ctx->rpt,"Audio File Mgr"); hp->h = p; return rc; } cmAfmRC_t cmAfmDestroy( cmAfmH_t* hp ) { cmAfmRC_t rc = kOkAfmRC; if( hp==NULL || cmAfmIsValid(*hp)==false) return rc; cmAfm_t* p = _cmAfmHandleToPtr(*hp); if((rc = _cmAfmDestroy(p)) != kOkAfmRC ) return rc; hp->h = NULL; return rc; } bool cmAfmIsValid( cmAfmH_t h ) { return h.h != NULL; } cmAfmFileH_t cmAfmIdToHandle( cmAfmH_t h, unsigned fileId ) { cmAfm_t* p = _cmAfmHandleToPtr(h); cmAfmFile_t* fp = p->list; cmAfmFileH_t fh = cmAfmFileNullHandle; for(; fp!=NULL; fp=fp->next) if( fp->id == fileId ) { fh.h = fp; break; } return fh; }