app/cmSdb.h/c : Sample database reader object. Initial commit.

This commit is contained in:
kevin 2014-01-25 16:13:42 -05:00
parent e25a9abaac
commit f95d93a4f8
2 changed files with 893 additions and 0 deletions

724
app/cmSdb.c Normal file
View File

@ -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 && i<p->eN; ++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; i<p->eN; ++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; i<ep->chCnt; ++i)
{
// if i channel is not the known events channel
if( ep->chIdx != i )
{
unsigned j;
// examine each record
for(j=0; j<p->eN; ++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; i<n; ++i)
{
const cmSdbEvent_t* e = cmSdbResponseEvent(rh,i);
if( e != NULL )
cmRptPrintf(rpt,"%6i %6i %2i %12i %12i %12i %12i %2i %6i %2i %10s %15s\n",
e->uuid,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; i<p->eN; ++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; j<p->eN && k<ep->chCnt; ++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; j<ep->chCnt; ++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; j<ep->chCnt; ++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;
}

169
app/cmSdb.h Normal file
View File

@ -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