diff --git a/app/cmSdb.c b/app/cmSdb.c new file mode 100644 index 0000000..6bf4346 --- /dev/null +++ b/app/cmSdb.c @@ -0,0 +1,724 @@ +#include "cmGlobal.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmLinkedHeap.h" +#include "cmLex.h" +#include "cmCsv.h" +#include "cmSdb.h" +#include "cmText.h" + +typedef enum +{ + kUuidColIdx, + kBaseUuidColIdx, + kChIdxColIdx, + kObiColIdx, + kIbiColIdx, + kIeiColIdx, + kOeiColIdx, + kSrcColIdx, + kMidiColIdx, + kInstrColIdx, + kSrateColIdx, + kChCntColIdx, + kNotesColIdx, + kAfnColIdx, + kInvalidColIdx +} cmSdbColIdx_t; + +struct cmSdb_str; + +typedef struct cmSdbRspBlk_str +{ + unsigned* indexV; // indexV[ cmSdb_t.blkIdxAllocCnt ] + unsigned cnt; // count of indexes used + struct cmSdbRspBlk_str* link; // cmSdbRsp_t.blocks link +} cmSdbRspBlk_t; + +typedef struct cmSdbRsp_str +{ + struct cmSdb_str* p; // + cmSdbRspBlk_t* blocks; // first block ptr + cmSdbRspBlk_t* ebp; // end block ptr + unsigned cnt; // total count of indexes + struct cmSdbRsp_str* link; // cmSdb_t.responses link +} cmSdbRsp_t; + +typedef struct cmSdb_str +{ + cmCtx_t ctx; + cmLHeapH_t lhH; + cmCsvH_t csvH; + cmSdbEvent_t* eV; + unsigned eN; + unsigned blkIdxAllocCnt; + struct cmSdbRsp_str* responses; +} cmSdb_t; + +cmSdbH_t cmSdbNullHandle = cmSTATIC_NULL_HANDLE; +cmSdbResponseH_t cmSdbResponseNullHandle = cmSTATIC_NULL_HANDLE; + +cmSdb_t* _cmSdbHandleToPtr( cmSdbH_t h ) +{ + cmSdb_t* p = (cmSdb_t*)h.h; + assert( p != NULL ); + return p; +} + +cmSdbRsp_t* _cmSdbRspHandleToPtr( cmSdbResponseH_t h ) +{ + cmSdbRsp_t* p = (cmSdbRsp_t*)h.h; + assert( p != NULL ); + return p; +} + +void _cmSdbRspBlkFree( cmSdb_t* p, cmSdbRspBlk_t* bp ) +{ + cmLhFree(p->lhH, bp->indexV); + cmLhFree(p->lhH, bp); +} + + +cmSdbRspBlk_t* _cmSdbRspBlkUnlink( cmSdbRsp_t* rp, cmSdbRspBlk_t* bp ) +{ + cmSdbRspBlk_t* dp = rp->blocks; + cmSdbRspBlk_t* pp = NULL; + for(; dp!=NULL; dp=dp->link) + { + if( dp == bp ) + { + if( pp == NULL ) + rp->blocks = dp->link; + else + pp->link = dp->link; + + return bp; + } + + pp = dp; + } + + assert(0); + return NULL; +} + +void _cmSdbRspInsertIndex( cmSdb_t* p, cmSdbRsp_t* rp, unsigned evtIndex ) +{ + + if( rp->ebp == NULL || rp->ebp->cnt == p->blkIdxAllocCnt ) + { + cmSdbRspBlk_t* bp = cmLhAllocZ(p->lhH,cmSdbRspBlk_t,1); + bp->indexV = cmLhAllocZ(p->lhH,unsigned,p->blkIdxAllocCnt); + + if( rp->ebp != NULL ) + rp->ebp->link = bp; + + if( rp->blocks == NULL ) + rp->blocks = bp; + + rp->ebp = bp; + + } + + assert( rp->ebp!=NULL && rp->ebp->cnt < p->blkIdxAllocCnt ); + + rp->ebp->indexV[ rp->ebp->cnt++ ] = evtIndex; + rp->cnt += 1; +} + +void _cmSdbRspRelease( cmSdbRsp_t* rp ) +{ + while( rp->blocks != NULL ) + { + cmSdbRspBlk_t* np = rp->blocks->link; + cmSdbRspBlk_t* bp; + + if((bp = _cmSdbRspBlkUnlink(rp,rp->blocks)) != NULL ) + _cmSdbRspBlkFree(rp->p,bp); + + rp->blocks = np; + } + + cmLhFree(rp->p->lhH,rp); +} + +cmSdbRsp_t* _cmSdbRspUnlink( cmSdbRsp_t* rp ) +{ + cmSdb_t* p = rp->p; + cmSdbRsp_t* dp = p->responses; + cmSdbRsp_t* pp = NULL; + + for(; dp!=NULL; dp=dp->link) + { + if( dp == rp ) + { + if( pp == NULL ) + p->responses = dp->link; + else + pp->link = dp->link; + + return rp; + } + + pp = dp; + } + + assert( 0 ); + + return NULL; +} + + +void _cmSdbRspFree( cmSdbRsp_t* rp ) +{ + _cmSdbRspUnlink(rp); + _cmSdbRspRelease(rp); +} + +cmSdbRsp_t* _cmSdbRspAlloc( cmSdb_t* p, cmSdbResponseH_t* rhp ) +{ + if( cmSdbResponseFree(rhp) != kOkSdbRC ) + return NULL; + + cmSdbRsp_t* rp = cmLhAllocZ(p->lhH,cmSdbRsp_t,1); + rp->p = p; + rp->link = p->responses; + p->responses = rp; + + rhp->h = rp; + + return rp; +} + +cmSdbRC_t _cmSdbDestroy( cmSdb_t* p ) +{ + cmSdbRC_t rc = kOkSdbRC; + + if( cmCsvFinalize(&p->csvH) != kOkCsvRC ) + rc = cmErrMsg(&p->ctx.err,kCsvFailSdbRC,"CSV file finalize failed."); + + while( p->responses != NULL ) + _cmSdbRspRelease(p->responses); + + cmLHeapDestroy(&p->lhH); + cmMemFree(p); + return rc; +} + +cmSdbRC_t cmSdbCreate( cmCtx_t* ctx, cmSdbH_t* hp, const cmChar_t* audioDir, const cmChar_t* csvFn ) +{ + cmSdbRC_t rc; + if((rc = cmSdbDestroy(hp)) != kOkSdbRC ) + return rc; + + cmSdb_t* p = cmMemAllocZ(cmSdb_t,1); + p->ctx = *ctx; + p->blkIdxAllocCnt = 1024; + + cmErrSetup(&p->ctx.err,&ctx->rpt,"sdb"); + + if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8192,ctx)) == false ) + { + rc = cmErrMsg(&p->ctx.err,kLHeapFailSdbRC,"Linked heap mgr. allocation failed."); + goto errLabel; + } + + hp->h = p; + + if( csvFn != NULL ) + if((rc = cmSdbLoad(*hp,csvFn)) != kOkSdbRC ) + goto errLabel; + + errLabel: + if( rc != kOkSdbRC ) + _cmSdbDestroy(p); + + return rc; +} + +cmSdbRC_t cmSdbDestroy( cmSdbH_t* hp ) +{ + cmSdbRC_t rc = kOkSdbRC; + + if( hp==NULL || cmSdbIsValid(*hp)==false ) + return rc; + + cmSdb_t* p = _cmSdbHandleToPtr(*hp); + + if((rc = _cmSdbDestroy(p)) != kOkSdbRC ) + return rc; + + hp->h = NULL; + + return rc; +} + +bool cmSdbIsValid( cmSdbH_t h ) +{ return h.h != NULL; } + + +cmSdbRC_t _cmSdbSyntaxError(cmSdb_t* p, const cmChar_t* csvFn, unsigned rowIdx, unsigned colIdx, const cmChar_t* colLabel ) +{ + return cmErrMsg(&p->ctx.err,kSyntaxErrSdbRC,"A syntax error was found at row %i col %i (label:%s) in '%s'.",rowIdx+1,colIdx+1,cmStringNullGuard(colLabel),cmStringNullGuard(csvFn)); +} + + +cmSdbRC_t cmSdbLoad( cmSdbH_t h, const cmChar_t* csvFn ) +{ + cmSdbRC_t rc = kOkSdbRC; + unsigned i; + + cmSdb_t* p = _cmSdbHandleToPtr(h); + + if( cmCsvInitializeFromFile(&p->csvH, csvFn, 0, &p->ctx ) != kOkCsvRC ) + { + rc = cmErrMsg(&p->ctx.err,kCsvFailSdbRC,"CSV file load fail on '%s'.",cmStringNullGuard(csvFn)); + goto errLabel; + } + + p->eN = cmCsvRowCount(p->csvH)-1; + + // release all the memory held by the linked heap + cmLHeapClear(p->lhH,true); + + p->eV = cmLhAllocZ(p->lhH,cmSdbEvent_t,p->eN); + + + for(i=0; rc==kOkSdbRC && ieN; ++i) + { + unsigned rowIdx = i+1; + + if((p->eV[i].uuid = cmCsvCellUInt(p->csvH,rowIdx,kUuidColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kUuidColIdx,"uuid"); + + if((p->eV[i].baseUuid = cmCsvCellUInt(p->csvH,rowIdx,kBaseUuidColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kBaseUuidColIdx,"baseUuid"); + + if((p->eV[i].chIdx = cmCsvCellUInt(p->csvH,rowIdx,kChIdxColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kChIdxColIdx,"chIdx"); + else + p->eV[i].chIdx -= 1; // CSV channel index is 1 based + + if((p->eV[i].obi = cmCsvCellUInt(p->csvH,rowIdx,kObiColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kObiColIdx,"obi"); + else + p->eV[i].obi -= 1; + + if((p->eV[i].ibi = cmCsvCellUInt(p->csvH,rowIdx,kIbiColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kIbiColIdx,"ibi"); + else + p->eV[i].ibi -= 1; + + if((p->eV[i].iei = cmCsvCellUInt(p->csvH,rowIdx,kIeiColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kIeiColIdx,"obi"); + else + p->eV[i].iei -= 1; + + if((p->eV[i].oei = cmCsvCellUInt(p->csvH,rowIdx,kOeiColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kOeiColIdx,"ibi"); + else + p->eV[i].oei -= 1; + + if((p->eV[i].src = cmCsvCellText(p->csvH,rowIdx,kSrcColIdx)) == NULL ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kSrcColIdx,"src"); + + if((p->eV[i].midi = cmCsvCellInt(p->csvH,rowIdx,kMidiColIdx)) == INT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kMidiColIdx,"midi"); + + if((p->eV[i].instr = cmCsvCellText(p->csvH,rowIdx,kInstrColIdx)) == NULL ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kInstrColIdx,"instr"); + + if((p->eV[i].srate = cmCsvCellUInt(p->csvH,rowIdx,kSrateColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kSrateColIdx,"srate"); + + if((p->eV[i].chCnt = cmCsvCellUInt(p->csvH,rowIdx,kChCntColIdx)) == UINT_MAX ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kChCntColIdx,"chCnt"); + + + cmCsvCell_t* c; + if((c = cmCsvCellPtr(p->csvH,rowIdx,kNotesColIdx)) == NULL ) + { + rc = cmErrMsg(&p->ctx.err,kSyntaxErrSdbRC,"Syntax Error: No 'notes' or 'audio file name' field for row %i in '%s'.",rowIdx+1,cmStringNullGuard(csvFn)); + goto errLabel; + } + + // count the number of 'notes' + unsigned nn = 0; + for(; c->rowPtr != NULL; c=c->rowPtr) + ++nn; + + if( nn > 0 ) + { + unsigned k = 0; + + // allocate the 'notes' ptr array - the last entry is set to NULL. + p->eV[i].notesV = cmLhAllocZ(p->lhH,const cmChar_t*,nn+1); + + // read each note + for(c=cmCsvCellPtr(p->csvH,rowIdx,kNotesColIdx); c!=NULL&&c->rowPtr!=NULL; c=c->rowPtr,++k) + if(( p->eV[i].notesV[k] = cmCsvCellText(p->csvH,rowIdx,kNotesColIdx+k)) == NULL ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kNotesColIdx+k,"notes"); + + assert(k==nn); + } + + // read the audio file name + if((p->eV[i].afn = cmCsvCellText(p->csvH,rowIdx,kNotesColIdx+nn)) == NULL ) + rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kNotesColIdx+nn,"afn"); + + } + + errLabel: + + return rc; + +} + +// Compare 'label' to every string in tV[i] and return true if any comparision is a match. +// If 'subFlV[i]' is set then 'label' must only contain tV[i] as a substring to match. +// If 'negFlV[i]' is set then return true if any comparision is a mismatch. +bool _cmSdbSelectText( const cmSdbEvent_t* r, const cmChar_t** tV, const bool* subFlV, const bool* negFlV, const cmChar_t* label ) +{ + unsigned i; + + if( label == NULL ) + return false; + + if( tV == NULL ) + return true; + + for(i=0; tV[i]!=NULL; ++i) + { + bool matchFl = false; + if( subFlV[i] ) + matchFl = strstr(label,tV[i]) != NULL; + else + matchFl = strcmp(tV[i],label)==0; + + if( negFlV[i] ) + matchFl = !matchFl; + + if(matchFl) + return true; + } + + return false; +} + +unsigned _cmSdbStrVectCnt( const cmChar_t** v ) +{ + unsigned n = 0; + unsigned i = 0; + + if( v == NULL ) + return 0; + + for(i=0; v[i]!=NULL; ++i) + ++n; + return n; +} + +void _cmSdbStrVectFlags( const cmChar_t** v, bool* sV, bool* nV ) +{ + unsigned i = 0; + if( v == NULL ) + return; + + for(i=0; v[i]!=NULL; ++i) + { + nV[i] = false; + sV[i] = false; + + if( strncmp(v[i],"*!",2)==0 || strncmp(v[i],"!*",2)==0) + { + sV[i] = nV[i] = true; + v[i] += 2; + } + else + { + if( strncmp(v[i],"!",1)==0 ) + { + nV[i] = true; + v[i] += 1; + } + + if( strncmp(v[i],"*",1)==0 ) + { + sV[i] = true; + v[i] += 1; + } + } + + + } +} + +cmSdbRC_t cmSdbSelect( + cmSdbH_t h, + double srate, + const cmChar_t** instrV, + const cmChar_t** srcV, + const cmChar_t** notesV, + double minDurSec, + double maxDurSec, + unsigned minChCnt, + cmSdbResponseH_t* rhp ) +{ + cmSdbRC_t rc = kOkSdbRC; + cmSdb_t* p = _cmSdbHandleToPtr(h); + unsigned i; + + cmSdbRsp_t* rp = _cmSdbRspAlloc(p,rhp); + + // get the length of each string vector + unsigned srcN = _cmSdbStrVectCnt(srcV); + unsigned insN = _cmSdbStrVectCnt(instrV); + unsigned notN = _cmSdbStrVectCnt(notesV); + + // allocate flag vectors + bool srcSubFlV[ srcN ]; + bool srcNegFlV[ srcN ]; + bool insSubFlV[ insN ]; + bool insNegFlV[ insN ]; + bool notSubFlV[ notN ]; + bool notNegFlV[ notN ]; + + // fill the flag vectors + _cmSdbStrVectFlags(srcV, srcSubFlV,srcNegFlV); + _cmSdbStrVectFlags(instrV,insSubFlV,insNegFlV); + _cmSdbStrVectFlags(notesV,notSubFlV,notNegFlV); + + for(i=0; ieN; ++i) + { + const cmSdbEvent_t* r = p->eV + i; + double durSec = (double)r->srate * (r->oei - r->obi); + unsigned j; + + if( srate!=0 && srate!=r->srate ) + continue; + + if( durSec < minDurSec || (maxDurSec!=0 && maxDurSec < durSec) ) + continue; + + if( minChCnt!=0 && r->chCnt > minChCnt ) + continue; + + if( !_cmSdbSelectText(r,srcV,srcSubFlV,srcNegFlV,r->src) ) + continue; + + if( !_cmSdbSelectText(r,instrV,insSubFlV,insNegFlV,r->instr) ) + continue; + + if( r->notesV != NULL ) + for(j=0; r->notesV[j]!=NULL; ++j) + if( _cmSdbSelectText(r,notesV,notSubFlV,notNegFlV,r->notesV[j]) == true ) + break; + + if( r->notesV[j]==NULL ) + continue; + + + _cmSdbRspInsertIndex(p,rp,i); + } + + return rc; +} + +cmSdbRC_t cmSdbSelectChPairs( cmSdbH_t h, const cmSdbEvent_t* ep, cmSdbResponseH_t* rhp ) +{ + cmSdbRC_t rc = kOkSdbRC; + cmSdb_t* p = _cmSdbHandleToPtr(h); + cmSdbRsp_t* rp = _cmSdbRspAlloc(p,rhp); + unsigned i; + + // for each channel of this event + for(i=0; ichCnt; ++i) + { + // if i channel is not the known events channel + if( ep->chIdx != i ) + { + unsigned j; + + // examine each record + for(j=0; jeN; ++j) + // if eV[j] shares a baseUuid but is on a different channel than *ep ... + if( p->eV[j].baseUuid == ep->baseUuid && p->eV[j].chIdx==i ) + { + // .. then a match has been found + _cmSdbRspInsertIndex(p,rp,j); + break; + } + + if( j== p->eN ) + { + rc = cmErrMsg(&p->ctx.err,kChPairNotFoundSdbRC,"The channel pair associated with 'id:%i instr:%s src:%s ch index:%i could not be found.",ep->uuid,cmStringNullGuard(ep->instr),cmStringNullGuard(ep->src),ep->chIdx); + } + } + } + + return rc; + +} + + +unsigned cmSdbResponseCount( cmSdbResponseH_t rh ) +{ + cmSdbRsp_t* rp = _cmSdbRspHandleToPtr(rh); + return rp->cnt; +} + +const cmSdbEvent_t* cmSdbResponseEvent( cmSdbResponseH_t rh, unsigned index ) +{ + cmSdbRsp_t* rp = _cmSdbRspHandleToPtr(rh); + + if( index >= rp->cnt ) + return NULL; + + cmSdbRspBlk_t* bp = rp->blocks; + unsigned i; + for(i=0; bp!=NULL; i+=bp->cnt,bp=bp->link) + if( i <= index && index < (i + bp->cnt) ) + return rp->p->eV + bp->indexV[index-i]; + + cmErrMsg(&rp->p->ctx.err,kInvalidRspIdxSdbRC,"Invalid query response index=%i.",index); + return NULL; +} + +bool cmSdbResponseIsValid( cmSdbResponseH_t rh ) +{ return rh.h != NULL; } + +cmSdbRC_t cmSdbResponseFree( cmSdbResponseH_t* rhp ) +{ + cmSdbRC_t rc = kOkSdbRC; + + if( rhp == NULL || cmSdbResponseIsValid(*rhp)==false ) + return rc; + + cmSdbRsp_t* rp = _cmSdbRspHandleToPtr(*rhp); + + _cmSdbRspFree(rp); + + rhp->h = NULL; + + return rc; +} + +void cmSdbResponsePrint( cmSdbResponseH_t rh, cmRpt_t* rpt ) +{ + unsigned n = cmSdbResponseCount(rh); + unsigned i; + for(i=0; iuuid,e->baseUuid,e->chIdx,e->obi,e->ibi,e->iei,e->oei,e->midi,e->srate,e->chCnt, + cmStringNullGuard(e->src), cmStringNullGuard(e->instr) ); + } +} + +cmSdbRC_t cmSdbSyncChPairs( cmSdbH_t h ) +{ + cmSdbRC_t rc = kOkSdbRC; + cmSdb_t* p = _cmSdbHandleToPtr(h); + unsigned i; + // for each multi-channel event + for(i=0; ieN; ++i) + if(p->eV[i].chCnt > 1 ) + { + const cmSdbEvent_t* ep = p->eV + i; + unsigned iV[ep->chCnt]; + unsigned j,k; + + // load iV[] with the event indexes of the channel pairs + for(j=0,k=0; jeN && kchCnt; ++j) + if( p->eV[j].baseUuid == ep->baseUuid ) + { + assert( p->eV[j].chIdx < ep->chCnt ); + + iV[p->eV[j].chIdx] = j; + ++k; + } + + if( k != ep->chCnt ) + rc = cmErrMsg(&p->ctx.err,kChPairNotFoundSdbRC,"The channel pair associated with 'id:%i instr:%s src:%s ch index:%i could not be found.",ep->uuid,cmStringNullGuard(ep->instr),cmStringNullGuard(ep->src),ep->chIdx); + else + { + unsigned mobi = ep->obi; + unsigned mibi = ep->ibi; + unsigned miei = ep->iei; + unsigned moei = ep->oei; + + // get the min onsets and max offsets + for(j=0; jchCnt; ++j) + { + mobi = cmMin(mobi,p->eV[ iV[j] ].obi); + mibi = cmMin(mibi,p->eV[ iV[j] ].ibi); + miei = cmMax(miei,p->eV[ iV[j] ].iei); + moei = cmMax(moei,p->eV[ iV[j] ].oei); + } + + // set the onsets to the min onset / offsets to max offsets + for(j=0; jchCnt; ++j) + { + p->eV[ iV[j] ].obi = mobi; + p->eV[ iV[j] ].ibi = mibi; + p->eV[ iV[j] ].iei = miei; + p->eV[ iV[j] ].oei = moei; + } + } + } + + return rc; +} + + +cmSdbRC_t cmSdbTest( cmCtx_t* ctx ) +{ + cmSdbRC_t rc = kOkSdbRC; + cmSdbH_t h = cmSdbNullHandle; + const cmChar_t* audioDir = "/home/kevin/media/audio"; + const cmChar_t* csvFn = "/home/kevin/temp/sdb0/sdb_master.csv"; + cmErr_t err; + + cmErrSetup(&err,&ctx->rpt,"sdb test"); + + if((rc = cmSdbCreate(ctx, &h, audioDir, csvFn )) != kOkSdbRC ) + { + rc = cmErrMsg(&err,rc,"sdb create failed."); + goto errLabel; + } + + if((rc = cmSdbSyncChPairs(h)) != kOkSdbRC ) + { + rc = cmErrMsg(&err,rc,"sdb sync-ch-pairs failed."); + goto errLabel; + } + + if(0) + { + cmSdbResponseH_t rH = cmSdbResponseNullHandle; + + const cmChar_t* instrV[] = { "*viol", NULL }; + + if((rc = cmSdbSelect(h,0,instrV,NULL,NULL,0,0,0,&rH)) != kOkSdbRC ) + { + rc = cmErrMsg(&err,rc,"sdb query failed."); + goto errLabel; + } + + + cmSdbResponsePrint(rH,&ctx->rpt); + + cmSdbResponseFree(&rH); + } + + errLabel: + if((rc = cmSdbDestroy(&h)) != kOkSdbRC ) + rc = cmErrMsg(&err,rc,"sdb destroy failed."); + + return rc; +} diff --git a/app/cmSdb.h b/app/cmSdb.h new file mode 100644 index 0000000..4a8b30f --- /dev/null +++ b/app/cmSdb.h @@ -0,0 +1,169 @@ +#ifndef cmSdb_h +#define cmSdb_h + +#ifdef __cplusplus +extern "C" { +#endif + + /* + The data for this object is stored in a CSV file with the following column syntax. + + Column Name Type Description + ------ -------- ----- ----------------------------------------------------- + 1 uuid uint Unique integer identifier for this event + 2 baseUuid uint uuid of channel 0 for this event + 3 chIdx uint Channel index (stereo: 1=left 2=right) + 4 obi uint Outer onset sample index into 'afn'. + 5 ibi uint Inner onset sample index into 'afn'. + 6 iei uint Inner offset sample index into 'afn'. + 7 oei uint Outer offset sample index into 'afn'. + 8 src text Source label for this event (e.g. mcgill, ui ) + 9 midi uint MIDI pitch number or -1 if the sample is not pitched + 10 instr text Instrument label. + 11 srate uint Sample rate of audio file reference by 'afn'. + 10 chCnt uint Count of channels for this event. + * notes text 0 or more free form double quoted text notes. + * afn text File name of the audio file this event occurs in. + + Notes: + #. Each event represents a mono audio signal. If the event is drawn + from a multi-channel audio file then the 'chCnt' field will be + greater than one. If 'chCnt' is greater than one then the associated + samples can be found by collecting all events that share the + same 'baseUuid'. + + #. There may be zero or more columns of 'notes'. If there are no + notes then the 'afn' field is in column 11. + + #. The index values (chIdx,obi,ibi,iei,oei) as stored in the CSV file + are 1 based. These values are decreased by 1 by the cmSdb CSV reader + so that their cmSdb value is zero based. See cmSdbLoad(). + + */ + + enum + { + kOkSdbRC, + kLHeapFailSdbRC, + kCsvFailSdbRC, + kSyntaxErrSdbRC, + kInvalidRspIdxSdbRC, + kChPairNotFoundSdbRC + }; + + typedef cmHandle_t cmSdbH_t; + typedef cmHandle_t cmSdbResponseH_t; + + typedef cmRC_t cmSdbRC_t; + extern cmSdbH_t cmSdbNullHandle_t; + extern cmSdbResponseH_t cmSdbResponseNullHandle_t; + + typedef struct + { + unsigned uuid; // unique id of this sample + unsigned baseUuid; // uuid of channel 0 + unsigned chIdx; // channel index (0=left,1=right) + unsigned obi; // outer onset + unsigned ibi; // inner onset + unsigned iei; // inner offset + unsigned oei; // outer offset + unsigned midi; // MIDI pitch or -1 for unpitched instruments + unsigned srate; // sample rate + unsigned chCnt; // source channel count + const cmChar_t* src; // sample source (e.g. mcgill, ui ) + const cmChar_t* instr; // instrument label + const cmChar_t* afn; // audio file name + const cmChar_t** notesV; // NULL terminated list of terms describing the event. + } cmSdbEvent_t; + + + cmSdbRC_t cmSdbCreate( cmCtx_t* ctx, cmSdbH_t* hp, const cmChar_t* audioDir, const cmChar_t* csvFn ); + cmSdbRC_t cmSdbDestroy( cmSdbH_t* hp ); + + bool cmSdbIsValid( cmSdbH_t h ); + + cmSdbRC_t cmSdbLoad( cmSdbH_t h, const cmChar_t* csvFn ); + + // Select a set of events from the sample database. + // + // The possible selection criteria are: + // sample rate + // instrument label + // source label + // notes labels + // event duration + // + // In order to match an event all active criteria + // must match. In other words the match implies a + // logical AND operation on each match criteria. + // Each of the criteria can be made inactive by + // specifying particular key values. + // sample rate = 0 + // instrument label = NULL + // source label = NULL + // notes labels = NULL + // event duration = minDurSec=0 maxDurSec=0 + // + // For the text array arguments (instrV,srcV,notesV) + // each element of the array is a key which is attempts to + // match the associated field in each event record. + // By default a match is triggered if the key text is identical to the + // event field text. The match algorithm can be modified by + // specifying a '*' as the first character in the key field. + // In this case a the key need only be a substring of the + // event field to trigger a match. For example "*viol" + // will return events that match both "violin" and "viola". + // + // To specify a mismatch as a successful match + // (i.e. to return events which do not match the key text) + // prefix the key with a '!' character. + // + // Note that it is legal to specify both '!' and '*'. In + // which case a match will be triggered by fields where + // the key text is not a substring of the field text. + // + // All query response handles returned from this function + // should eventualy be released by the application via a call to + // cmSdbResponseFree(). + cmSdbRC_t cmSdbSelect( + cmSdbH_t h, + double srate, // event sample rate or 0 to ignore + const cmChar_t** instrV, // array of instrument labels to match + const cmChar_t** srcV, // array of 'src' labels to match + const cmChar_t** notesV, // array of text 'notes' to match + double minDurSec, // min event duration + double maxDurSec, // max event duration or 0 to ignore + unsigned minChCnt, // min ch cnt or 0 to ignore + cmSdbResponseH_t* rhp ); + + // Given the event 'ep' locate the channel pairs associated with that event. + // The response handle returned from this function must be released + // by a call to cmSdbResponseFree(). + cmSdbRC_t cmSdbSelectChPairs( cmSdbH_t h, const cmSdbEvent_t* ep, cmSdbResponseH_t* rhp ); + + // Return the count of events in a query response. + unsigned cmSdbResponseCount( cmSdbResponseH_t rh ); + + // Return the event at 'index' in from a query response. + // Legal 'index' range: 0<=index<=cmSdbResponseCount(). + const cmSdbEvent_t* cmSdbResponseEvent( cmSdbResponseH_t rh, unsigned index ); + + // Return true if the 'rh' is a non-NULL query response handle. + bool cmSdbResponseIsValid( cmSdbResponseH_t rh ); + + // Release the resource held by a query response. + cmSdbRC_t cmSdbResponseFree( cmSdbResponseH_t* rhp ); + void cmSdbResponsePrint( cmSdbResponseH_t rh, cmRpt_t* rpt ); + + // Time align all channel pairs by setting the onset times to + // the minimum time among all the pairs and the offset times to + // the maximum among all the pairs. + cmSdbRC_t cmSdbSyncChPairs( cmSdbH_t h ); + + cmSdbRC_t cmSdbTest( cmCtx_t* ctx ); + +#ifdef __cplusplus +} +#endif + +#endif