Browse Source

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

master
kevin 10 years ago
parent
commit
f95d93a4f8
2 changed files with 893 additions and 0 deletions
  1. 724
    0
      app/cmSdb.c
  2. 169
    0
      app/cmSdb.h

+ 724
- 0
app/cmSdb.c View File

@@ -0,0 +1,724 @@
1
+#include "cmGlobal.h"
2
+#include "cmRpt.h"
3
+#include "cmErr.h"
4
+#include "cmCtx.h"
5
+#include "cmMem.h"
6
+#include "cmMallocDebug.h"
7
+#include "cmLinkedHeap.h"
8
+#include "cmLex.h"
9
+#include "cmCsv.h"
10
+#include "cmSdb.h"
11
+#include "cmText.h"
12
+
13
+typedef enum
14
+{
15
+  kUuidColIdx,
16
+  kBaseUuidColIdx,
17
+  kChIdxColIdx,
18
+  kObiColIdx,
19
+  kIbiColIdx,
20
+  kIeiColIdx,
21
+  kOeiColIdx,
22
+  kSrcColIdx,
23
+  kMidiColIdx,
24
+  kInstrColIdx,
25
+  kSrateColIdx,
26
+  kChCntColIdx,
27
+  kNotesColIdx,
28
+  kAfnColIdx,
29
+  kInvalidColIdx
30
+} cmSdbColIdx_t;
31
+
32
+struct cmSdb_str;
33
+
34
+typedef struct cmSdbRspBlk_str
35
+{
36
+  unsigned*               indexV;  // indexV[ cmSdb_t.blkIdxAllocCnt ]
37
+  unsigned                cnt;     // count of indexes used
38
+  struct cmSdbRspBlk_str* link;    // cmSdbRsp_t.blocks link
39
+} cmSdbRspBlk_t;
40
+
41
+typedef struct cmSdbRsp_str
42
+{
43
+  struct cmSdb_str*    p;       // 
44
+  cmSdbRspBlk_t*       blocks;  // first block ptr
45
+  cmSdbRspBlk_t*       ebp;     // end block ptr
46
+  unsigned             cnt;     // total count of indexes
47
+  struct cmSdbRsp_str* link;    // cmSdb_t.responses link
48
+} cmSdbRsp_t;
49
+
50
+typedef struct cmSdb_str
51
+{
52
+  cmCtx_t              ctx;
53
+  cmLHeapH_t           lhH;
54
+  cmCsvH_t             csvH;
55
+  cmSdbEvent_t*        eV;
56
+  unsigned             eN;
57
+  unsigned             blkIdxAllocCnt;
58
+  struct cmSdbRsp_str* responses;
59
+} cmSdb_t;
60
+
61
+cmSdbH_t         cmSdbNullHandle         = cmSTATIC_NULL_HANDLE;
62
+cmSdbResponseH_t cmSdbResponseNullHandle = cmSTATIC_NULL_HANDLE;
63
+
64
+cmSdb_t* _cmSdbHandleToPtr( cmSdbH_t h )
65
+{
66
+  cmSdb_t* p = (cmSdb_t*)h.h;
67
+  assert( p != NULL );
68
+  return p;
69
+}
70
+
71
+cmSdbRsp_t* _cmSdbRspHandleToPtr( cmSdbResponseH_t h )
72
+{
73
+  cmSdbRsp_t* p = (cmSdbRsp_t*)h.h;
74
+  assert( p != NULL );
75
+  return p;
76
+}
77
+
78
+void _cmSdbRspBlkFree( cmSdb_t* p, cmSdbRspBlk_t* bp )
79
+{
80
+  cmLhFree(p->lhH, bp->indexV);
81
+  cmLhFree(p->lhH, bp);
82
+}
83
+
84
+
85
+cmSdbRspBlk_t*  _cmSdbRspBlkUnlink( cmSdbRsp_t* rp, cmSdbRspBlk_t* bp )
86
+{
87
+  cmSdbRspBlk_t* dp = rp->blocks;
88
+  cmSdbRspBlk_t* pp = NULL;
89
+  for(; dp!=NULL; dp=dp->link)
90
+  {
91
+    if( dp == bp )
92
+    {
93
+      if( pp == NULL )
94
+        rp->blocks = dp->link;
95
+      else
96
+        pp->link = dp->link;
97
+
98
+      return bp;
99
+    }
100
+
101
+    pp = dp;
102
+  }
103
+
104
+  assert(0);
105
+  return NULL;
106
+}
107
+
108
+void _cmSdbRspInsertIndex( cmSdb_t* p, cmSdbRsp_t* rp, unsigned evtIndex )
109
+{
110
+
111
+  if( rp->ebp == NULL || rp->ebp->cnt == p->blkIdxAllocCnt )
112
+  {
113
+    cmSdbRspBlk_t* bp = cmLhAllocZ(p->lhH,cmSdbRspBlk_t,1);
114
+    bp->indexV = cmLhAllocZ(p->lhH,unsigned,p->blkIdxAllocCnt);
115
+
116
+    if( rp->ebp != NULL )
117
+      rp->ebp->link = bp;
118
+
119
+    if( rp->blocks == NULL )
120
+      rp->blocks = bp;
121
+
122
+    rp->ebp  = bp;
123
+
124
+  }
125
+
126
+  assert( rp->ebp!=NULL && rp->ebp->cnt < p->blkIdxAllocCnt );
127
+
128
+  rp->ebp->indexV[ rp->ebp->cnt++ ] = evtIndex;
129
+  rp->cnt += 1;
130
+}
131
+
132
+void _cmSdbRspRelease( cmSdbRsp_t* rp )
133
+{
134
+  while( rp->blocks != NULL )
135
+  {
136
+    cmSdbRspBlk_t* np = rp->blocks->link;
137
+    cmSdbRspBlk_t* bp;
138
+
139
+    if((bp = _cmSdbRspBlkUnlink(rp,rp->blocks)) != NULL )
140
+      _cmSdbRspBlkFree(rp->p,bp);
141
+    
142
+    rp->blocks = np;
143
+  }
144
+
145
+  cmLhFree(rp->p->lhH,rp);
146
+}
147
+
148
+cmSdbRsp_t*  _cmSdbRspUnlink( cmSdbRsp_t* rp )
149
+{
150
+  cmSdb_t*    p  = rp->p;
151
+  cmSdbRsp_t* dp = p->responses;
152
+  cmSdbRsp_t* pp = NULL;
153
+
154
+  for(; dp!=NULL; dp=dp->link)
155
+  {
156
+    if( dp == rp )
157
+    {
158
+      if( pp == NULL )
159
+        p->responses = dp->link;
160
+      else
161
+        pp->link = dp->link;
162
+
163
+      return rp;
164
+    }
165
+
166
+    pp = dp;
167
+  }
168
+
169
+  assert( 0 );
170
+
171
+  return NULL;
172
+}
173
+
174
+
175
+void _cmSdbRspFree( cmSdbRsp_t* rp )
176
+{
177
+  _cmSdbRspUnlink(rp);
178
+  _cmSdbRspRelease(rp);
179
+}
180
+
181
+cmSdbRsp_t* _cmSdbRspAlloc( cmSdb_t* p, cmSdbResponseH_t* rhp )
182
+{
183
+  if( cmSdbResponseFree(rhp) != kOkSdbRC )
184
+    return NULL;
185
+
186
+  cmSdbRsp_t* rp = cmLhAllocZ(p->lhH,cmSdbRsp_t,1);
187
+  rp->p = p;
188
+  rp->link = p->responses;
189
+  p->responses = rp;
190
+
191
+  rhp->h = rp;
192
+
193
+  return rp;
194
+}
195
+
196
+cmSdbRC_t _cmSdbDestroy( cmSdb_t* p )
197
+{
198
+  cmSdbRC_t rc = kOkSdbRC;
199
+
200
+  if( cmCsvFinalize(&p->csvH) != kOkCsvRC )
201
+    rc = cmErrMsg(&p->ctx.err,kCsvFailSdbRC,"CSV file finalize failed.");
202
+
203
+  while( p->responses != NULL )
204
+    _cmSdbRspRelease(p->responses);
205
+
206
+  cmLHeapDestroy(&p->lhH);
207
+  cmMemFree(p);
208
+  return rc;
209
+}
210
+
211
+cmSdbRC_t cmSdbCreate( cmCtx_t* ctx,  cmSdbH_t* hp, const cmChar_t* audioDir, const cmChar_t* csvFn )
212
+{
213
+  cmSdbRC_t rc;
214
+  if((rc = cmSdbDestroy(hp)) != kOkSdbRC )
215
+    return rc;
216
+
217
+  cmSdb_t* p = cmMemAllocZ(cmSdb_t,1);
218
+  p->ctx = *ctx;
219
+  p->blkIdxAllocCnt  = 1024;
220
+
221
+  cmErrSetup(&p->ctx.err,&ctx->rpt,"sdb");
222
+
223
+  if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8192,ctx)) == false )
224
+  {
225
+    rc = cmErrMsg(&p->ctx.err,kLHeapFailSdbRC,"Linked heap mgr. allocation failed.");
226
+    goto errLabel;
227
+  }
228
+
229
+  hp->h = p;
230
+
231
+  if( csvFn != NULL )
232
+    if((rc = cmSdbLoad(*hp,csvFn)) != kOkSdbRC )
233
+      goto errLabel;
234
+
235
+ errLabel:
236
+  if( rc != kOkSdbRC )
237
+    _cmSdbDestroy(p);
238
+
239
+  return rc;
240
+}
241
+
242
+cmSdbRC_t cmSdbDestroy( cmSdbH_t* hp )
243
+{
244
+  cmSdbRC_t rc = kOkSdbRC;
245
+
246
+  if( hp==NULL || cmSdbIsValid(*hp)==false )
247
+    return rc;
248
+
249
+  cmSdb_t* p = _cmSdbHandleToPtr(*hp);
250
+
251
+  if((rc = _cmSdbDestroy(p)) != kOkSdbRC )
252
+    return rc;
253
+
254
+  hp->h = NULL;
255
+
256
+  return rc;
257
+}
258
+
259
+bool     cmSdbIsValid( cmSdbH_t h )
260
+{ return h.h != NULL; }
261
+
262
+
263
+cmSdbRC_t _cmSdbSyntaxError(cmSdb_t* p, const cmChar_t* csvFn, unsigned rowIdx, unsigned colIdx, const cmChar_t* colLabel )
264
+{
265
+  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));
266
+}
267
+
268
+
269
+cmSdbRC_t cmSdbLoad( cmSdbH_t h, const cmChar_t* csvFn )
270
+{
271
+  cmSdbRC_t rc   = kOkSdbRC;
272
+  unsigned  i;
273
+
274
+  cmSdb_t* p = _cmSdbHandleToPtr(h);
275
+
276
+  if( cmCsvInitializeFromFile(&p->csvH, csvFn, 0, &p->ctx ) != kOkCsvRC )
277
+  {
278
+    rc = cmErrMsg(&p->ctx.err,kCsvFailSdbRC,"CSV file load fail on '%s'.",cmStringNullGuard(csvFn));
279
+    goto errLabel;
280
+  }
281
+
282
+  p->eN = cmCsvRowCount(p->csvH)-1;
283
+  
284
+  // release all the memory held by the linked heap
285
+  cmLHeapClear(p->lhH,true);
286
+
287
+  p->eV = cmLhAllocZ(p->lhH,cmSdbEvent_t,p->eN);
288
+
289
+
290
+  for(i=0; rc==kOkSdbRC && i<p->eN; ++i)
291
+  {
292
+    unsigned rowIdx = i+1;
293
+
294
+    if((p->eV[i].uuid = cmCsvCellUInt(p->csvH,rowIdx,kUuidColIdx)) == UINT_MAX )
295
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kUuidColIdx,"uuid");
296
+
297
+    if((p->eV[i].baseUuid = cmCsvCellUInt(p->csvH,rowIdx,kBaseUuidColIdx)) == UINT_MAX )
298
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kBaseUuidColIdx,"baseUuid");
299
+
300
+    if((p->eV[i].chIdx = cmCsvCellUInt(p->csvH,rowIdx,kChIdxColIdx)) == UINT_MAX )
301
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kChIdxColIdx,"chIdx");
302
+    else
303
+      p->eV[i].chIdx -= 1; // CSV channel index is 1 based
304
+
305
+    if((p->eV[i].obi = cmCsvCellUInt(p->csvH,rowIdx,kObiColIdx)) == UINT_MAX )
306
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kObiColIdx,"obi");
307
+    else
308
+      p->eV[i].obi -= 1;
309
+
310
+    if((p->eV[i].ibi = cmCsvCellUInt(p->csvH,rowIdx,kIbiColIdx)) == UINT_MAX )
311
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kIbiColIdx,"ibi");
312
+    else
313
+      p->eV[i].ibi -= 1;
314
+
315
+    if((p->eV[i].iei = cmCsvCellUInt(p->csvH,rowIdx,kIeiColIdx)) == UINT_MAX )
316
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kIeiColIdx,"obi");
317
+    else
318
+      p->eV[i].iei -= 1;
319
+
320
+    if((p->eV[i].oei = cmCsvCellUInt(p->csvH,rowIdx,kOeiColIdx)) == UINT_MAX )
321
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kOeiColIdx,"ibi");
322
+    else
323
+      p->eV[i].oei -= 1;
324
+
325
+    if((p->eV[i].src = cmCsvCellText(p->csvH,rowIdx,kSrcColIdx)) == NULL )
326
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kSrcColIdx,"src");
327
+
328
+    if((p->eV[i].midi = cmCsvCellInt(p->csvH,rowIdx,kMidiColIdx)) == INT_MAX )
329
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kMidiColIdx,"midi");
330
+
331
+    if((p->eV[i].instr = cmCsvCellText(p->csvH,rowIdx,kInstrColIdx)) == NULL )
332
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kInstrColIdx,"instr");
333
+
334
+    if((p->eV[i].srate = cmCsvCellUInt(p->csvH,rowIdx,kSrateColIdx)) == UINT_MAX )
335
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kSrateColIdx,"srate");
336
+
337
+    if((p->eV[i].chCnt = cmCsvCellUInt(p->csvH,rowIdx,kChCntColIdx)) == UINT_MAX )
338
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kChCntColIdx,"chCnt");
339
+
340
+     
341
+    cmCsvCell_t* c;
342
+    if((c = cmCsvCellPtr(p->csvH,rowIdx,kNotesColIdx)) == NULL )
343
+    {
344
+      rc = cmErrMsg(&p->ctx.err,kSyntaxErrSdbRC,"Syntax Error: No 'notes' or 'audio file name' field for row %i in '%s'.",rowIdx+1,cmStringNullGuard(csvFn));
345
+      goto errLabel;
346
+    }
347
+    
348
+    // count the number of 'notes'
349
+    unsigned nn = 0;
350
+    for(; c->rowPtr != NULL; c=c->rowPtr)
351
+      ++nn;
352
+
353
+    if( nn > 0 )
354
+    {
355
+      unsigned k  = 0;
356
+
357
+      // allocate the 'notes' ptr array - the last entry is set to NULL.
358
+      p->eV[i].notesV = cmLhAllocZ(p->lhH,const cmChar_t*,nn+1);
359
+
360
+      // read each note
361
+      for(c=cmCsvCellPtr(p->csvH,rowIdx,kNotesColIdx); c!=NULL&&c->rowPtr!=NULL; c=c->rowPtr,++k)
362
+        if(( p->eV[i].notesV[k] = cmCsvCellText(p->csvH,rowIdx,kNotesColIdx+k)) == NULL )
363
+          rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kNotesColIdx+k,"notes");
364
+
365
+      assert(k==nn);
366
+    }
367
+
368
+    // read the audio file name
369
+    if((p->eV[i].afn = cmCsvCellText(p->csvH,rowIdx,kNotesColIdx+nn)) == NULL )
370
+      rc = _cmSdbSyntaxError(p,csvFn,rowIdx,kNotesColIdx+nn,"afn");
371
+
372
+  }
373
+
374
+ errLabel:
375
+
376
+  return rc;
377
+
378
+}
379
+
380
+// Compare 'label' to every string in tV[i] and return true if any comparision is a match.
381
+// If 'subFlV[i]' is set then 'label' must only contain tV[i] as a substring to match.
382
+// If 'negFlV[i]' is set then return true if any comparision is a mismatch.
383
+bool _cmSdbSelectText( const cmSdbEvent_t* r, const cmChar_t** tV, const bool* subFlV, const bool* negFlV, const cmChar_t* label )
384
+{
385
+  unsigned        i;
386
+
387
+  if( label == NULL )
388
+    return false;
389
+
390
+  if( tV == NULL )
391
+    return true;
392
+
393
+  for(i=0; tV[i]!=NULL; ++i)
394
+  {
395
+    bool matchFl = false;
396
+    if( subFlV[i] )
397
+      matchFl = strstr(label,tV[i]) != NULL;
398
+    else
399
+      matchFl = strcmp(tV[i],label)==0;
400
+
401
+    if( negFlV[i] )
402
+      matchFl = !matchFl;
403
+
404
+    if(matchFl)
405
+      return true;
406
+  }
407
+
408
+  return false;
409
+}
410
+
411
+unsigned _cmSdbStrVectCnt( const cmChar_t** v )
412
+{
413
+  unsigned n = 0;
414
+  unsigned i = 0;
415
+
416
+  if( v == NULL )
417
+    return 0;
418
+
419
+  for(i=0; v[i]!=NULL; ++i)
420
+    ++n;
421
+  return n;
422
+}
423
+
424
+void _cmSdbStrVectFlags( const cmChar_t** v, bool* sV, bool* nV )
425
+{
426
+  unsigned i = 0;
427
+  if( v == NULL )
428
+    return;
429
+
430
+  for(i=0; v[i]!=NULL; ++i)
431
+  {
432
+    nV[i] = false;
433
+    sV[i] = false;
434
+
435
+    if( strncmp(v[i],"*!",2)==0 || strncmp(v[i],"!*",2)==0)
436
+    {
437
+      sV[i] = nV[i] = true;
438
+      v[i] += 2;
439
+    }
440
+    else
441
+    {
442
+      if( strncmp(v[i],"!",1)==0 )
443
+      {
444
+        nV[i] = true;
445
+        v[i] += 1;
446
+      }
447
+
448
+      if( strncmp(v[i],"*",1)==0 )
449
+      {
450
+        sV[i] = true;
451
+        v[i] += 1;
452
+      }
453
+    }
454
+
455
+    
456
+  }
457
+}
458
+
459
+cmSdbRC_t cmSdbSelect( 
460
+    cmSdbH_t         h,
461
+    double           srate,
462
+    const cmChar_t** instrV,
463
+    const cmChar_t** srcV,
464
+    const cmChar_t** notesV,
465
+    double           minDurSec,
466
+    double           maxDurSec,
467
+    unsigned         minChCnt,
468
+    cmSdbResponseH_t* rhp )
469
+{
470
+  cmSdbRC_t rc = kOkSdbRC;
471
+  cmSdb_t*  p  = _cmSdbHandleToPtr(h);
472
+  unsigned  i;
473
+
474
+  cmSdbRsp_t* rp   = _cmSdbRspAlloc(p,rhp);
475
+
476
+  // get the length of each string vector
477
+  unsigned    srcN = _cmSdbStrVectCnt(srcV);
478
+  unsigned    insN = _cmSdbStrVectCnt(instrV);
479
+  unsigned    notN = _cmSdbStrVectCnt(notesV);
480
+
481
+  // allocate flag vectors
482
+  bool srcSubFlV[ srcN ];
483
+  bool srcNegFlV[ srcN ];
484
+  bool insSubFlV[ insN ];
485
+  bool insNegFlV[ insN ];
486
+  bool notSubFlV[ notN ];
487
+  bool notNegFlV[ notN ];
488
+  
489
+  // fill the flag vectors
490
+  _cmSdbStrVectFlags(srcV,  srcSubFlV,srcNegFlV);
491
+  _cmSdbStrVectFlags(instrV,insSubFlV,insNegFlV);
492
+  _cmSdbStrVectFlags(notesV,notSubFlV,notNegFlV);
493
+
494
+  for(i=0; i<p->eN; ++i)
495
+  {
496
+    const cmSdbEvent_t* r      = p->eV + i;
497
+    double              durSec = (double)r->srate * (r->oei - r->obi);
498
+    unsigned            j;
499
+
500
+    if( srate!=0 && srate!=r->srate )
501
+      continue;
502
+
503
+    if( durSec < minDurSec || (maxDurSec!=0 && maxDurSec < durSec) )
504
+      continue;
505
+
506
+    if( minChCnt!=0 && r->chCnt > minChCnt )
507
+      continue;
508
+
509
+    if( !_cmSdbSelectText(r,srcV,srcSubFlV,srcNegFlV,r->src) )
510
+      continue;
511
+    
512
+    if( !_cmSdbSelectText(r,instrV,insSubFlV,insNegFlV,r->instr) )
513
+      continue;
514
+
515
+    if( r->notesV != NULL )
516
+      for(j=0; r->notesV[j]!=NULL; ++j)
517
+        if( _cmSdbSelectText(r,notesV,notSubFlV,notNegFlV,r->notesV[j]) == true )
518
+          break;
519
+
520
+    if( r->notesV[j]==NULL )
521
+      continue;
522
+
523
+    
524
+    _cmSdbRspInsertIndex(p,rp,i);
525
+  }
526
+  
527
+  return rc;
528
+}
529
+
530
+cmSdbRC_t cmSdbSelectChPairs( cmSdbH_t h, const cmSdbEvent_t* ep, cmSdbResponseH_t* rhp )
531
+{
532
+  cmSdbRC_t   rc = kOkSdbRC;
533
+  cmSdb_t*    p  = _cmSdbHandleToPtr(h);
534
+  cmSdbRsp_t* rp = _cmSdbRspAlloc(p,rhp);
535
+  unsigned    i;
536
+
537
+  // for each channel of this event
538
+  for(i=0; i<ep->chCnt; ++i)
539
+  {
540
+     // if i channel is not the known events channel
541
+    if( ep->chIdx != i )
542
+    {
543
+      unsigned j;
544
+
545
+      // examine each record
546
+      for(j=0; j<p->eN; ++j)
547
+        // if eV[j] shares a baseUuid but is on a different channel than *ep  ...
548
+        if( p->eV[j].baseUuid == ep->baseUuid && p->eV[j].chIdx==i )
549
+        {
550
+          // .. then a match has been found
551
+          _cmSdbRspInsertIndex(p,rp,j);
552
+          break;
553
+        }
554
+
555
+      if( j== p->eN )
556
+      {
557
+        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);
558
+      }
559
+    }
560
+  }
561
+
562
+  return rc;
563
+
564
+}
565
+
566
+
567
+unsigned cmSdbResponseCount( cmSdbResponseH_t rh )
568
+{
569
+  cmSdbRsp_t* rp = _cmSdbRspHandleToPtr(rh);
570
+  return rp->cnt;
571
+}
572
+
573
+const cmSdbEvent_t* cmSdbResponseEvent( cmSdbResponseH_t rh, unsigned index )
574
+{
575
+  cmSdbRsp_t* rp = _cmSdbRspHandleToPtr(rh);
576
+
577
+  if( index >= rp->cnt )
578
+    return NULL;
579
+
580
+  cmSdbRspBlk_t* bp = rp->blocks;
581
+  unsigned i;
582
+  for(i=0; bp!=NULL; i+=bp->cnt,bp=bp->link)
583
+    if( i <= index && index < (i + bp->cnt) )
584
+      return rp->p->eV + bp->indexV[index-i];
585
+  
586
+  cmErrMsg(&rp->p->ctx.err,kInvalidRspIdxSdbRC,"Invalid query response index=%i.",index);
587
+  return NULL;
588
+}
589
+
590
+bool cmSdbResponseIsValid( cmSdbResponseH_t rh )
591
+{ return rh.h != NULL; }
592
+
593
+cmSdbRC_t cmSdbResponseFree( cmSdbResponseH_t* rhp )
594
+{
595
+  cmSdbRC_t rc = kOkSdbRC;
596
+
597
+  if( rhp == NULL || cmSdbResponseIsValid(*rhp)==false )
598
+    return rc;
599
+
600
+  cmSdbRsp_t* rp = _cmSdbRspHandleToPtr(*rhp);
601
+
602
+  _cmSdbRspFree(rp);
603
+
604
+  rhp->h = NULL;
605
+
606
+  return rc;
607
+}
608
+
609
+void   cmSdbResponsePrint( cmSdbResponseH_t rh, cmRpt_t* rpt )
610
+{
611
+  unsigned n = cmSdbResponseCount(rh);
612
+  unsigned i;
613
+  for(i=0; i<n; ++i)
614
+  {
615
+    const cmSdbEvent_t* e = cmSdbResponseEvent(rh,i);
616
+    if( e != NULL )
617
+      cmRptPrintf(rpt,"%6i %6i %2i %12i %12i %12i %12i %2i %6i %2i %10s %15s\n",
618
+        e->uuid,e->baseUuid,e->chIdx,e->obi,e->ibi,e->iei,e->oei,e->midi,e->srate,e->chCnt,
619
+        cmStringNullGuard(e->src), cmStringNullGuard(e->instr) );
620
+  }
621
+}
622
+
623
+cmSdbRC_t  cmSdbSyncChPairs( cmSdbH_t h )
624
+{
625
+  cmSdbRC_t rc = kOkSdbRC;
626
+  cmSdb_t* p = _cmSdbHandleToPtr(h);
627
+  unsigned i;
628
+  // for each multi-channel event
629
+  for(i=0; i<p->eN; ++i)
630
+    if(p->eV[i].chCnt > 1 )
631
+    {
632
+      const cmSdbEvent_t* ep = p->eV + i;
633
+      unsigned iV[ep->chCnt];
634
+      unsigned j,k;
635
+
636
+      // load iV[] with the event indexes of the channel pairs
637
+      for(j=0,k=0; j<p->eN && k<ep->chCnt; ++j)
638
+        if( p->eV[j].baseUuid == ep->baseUuid )
639
+        {
640
+          assert( p->eV[j].chIdx < ep->chCnt );
641
+
642
+          iV[p->eV[j].chIdx] = j;
643
+          ++k;
644
+        }
645
+
646
+      if( k != ep->chCnt )
647
+        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);
648
+      else
649
+      {
650
+        unsigned mobi = ep->obi;
651
+        unsigned mibi = ep->ibi;
652
+        unsigned miei = ep->iei;
653
+        unsigned moei = ep->oei;
654
+
655
+        // get the min onsets and max offsets
656
+        for(j=0; j<ep->chCnt; ++j)
657
+        {
658
+          mobi = cmMin(mobi,p->eV[ iV[j] ].obi);
659
+          mibi = cmMin(mibi,p->eV[ iV[j] ].ibi);
660
+          miei = cmMax(miei,p->eV[ iV[j] ].iei);
661
+          moei = cmMax(moei,p->eV[ iV[j] ].oei);
662
+        }
663
+
664
+        // set the onsets to the min onset / offsets to max offsets 
665
+        for(j=0; j<ep->chCnt; ++j)
666
+        {
667
+          p->eV[ iV[j] ].obi = mobi;
668
+          p->eV[ iV[j] ].ibi = mibi;
669
+          p->eV[ iV[j] ].iei = miei;
670
+          p->eV[ iV[j] ].oei = moei;
671
+        }
672
+      }
673
+    }
674
+ 
675
+  return rc;
676
+}
677
+
678
+
679
+cmSdbRC_t cmSdbTest( cmCtx_t* ctx )
680
+{
681
+  cmSdbRC_t       rc       = kOkSdbRC;
682
+  cmSdbH_t        h        = cmSdbNullHandle;
683
+  const cmChar_t* audioDir = "/home/kevin/media/audio";
684
+  const cmChar_t* csvFn    = "/home/kevin/temp/sdb0/sdb_master.csv";
685
+  cmErr_t         err;
686
+
687
+  cmErrSetup(&err,&ctx->rpt,"sdb test");
688
+
689
+  if((rc = cmSdbCreate(ctx,  &h, audioDir, csvFn )) != kOkSdbRC )
690
+  {
691
+    rc = cmErrMsg(&err,rc,"sdb create failed.");
692
+    goto errLabel;
693
+  }
694
+
695
+  if((rc = cmSdbSyncChPairs(h)) != kOkSdbRC )
696
+  {
697
+    rc = cmErrMsg(&err,rc,"sdb sync-ch-pairs failed.");
698
+    goto errLabel;
699
+  }
700
+
701
+  if(0)
702
+  {
703
+    cmSdbResponseH_t rH = cmSdbResponseNullHandle;
704
+
705
+    const cmChar_t* instrV[] = { "*viol", NULL };
706
+
707
+    if((rc = cmSdbSelect(h,0,instrV,NULL,NULL,0,0,0,&rH)) != kOkSdbRC )
708
+    {
709
+      rc = cmErrMsg(&err,rc,"sdb query failed.");
710
+      goto errLabel;
711
+    }
712
+
713
+
714
+    cmSdbResponsePrint(rH,&ctx->rpt);
715
+
716
+    cmSdbResponseFree(&rH);
717
+  }
718
+
719
+ errLabel:
720
+  if((rc = cmSdbDestroy(&h)) != kOkSdbRC )
721
+    rc = cmErrMsg(&err,rc,"sdb destroy failed.");
722
+
723
+  return rc;
724
+}

+ 169
- 0
app/cmSdb.h View File

@@ -0,0 +1,169 @@
1
+#ifndef cmSdb_h
2
+#define cmSdb_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  /*
9
+    The data for this object is stored in a CSV file with the following column syntax.
10
+    
11
+    Column Name     Type  Description
12
+    ------ -------- ----- -----------------------------------------------------
13
+       1   uuid     uint  Unique integer identifier for this event
14
+       2   baseUuid uint  uuid of channel 0 for this event
15
+       3   chIdx    uint  Channel index (stereo: 1=left 2=right)
16
+       4   obi      uint  Outer onset sample index into 'afn'.
17
+       5   ibi      uint  Inner onset sample index into 'afn'.
18
+       6   iei      uint  Inner offset sample index into 'afn'.
19
+       7   oei      uint  Outer offset sample index into 'afn'.
20
+       8   src      text  Source label for this event (e.g. mcgill, ui )       
21
+       9   midi     uint  MIDI pitch number or -1 if the sample is not pitched
22
+      10   instr    text  Instrument label.
23
+      11   srate    uint  Sample rate of audio file reference by 'afn'.
24
+      10   chCnt    uint  Count of channels for this event.
25
+      *    notes    text  0 or more free form double quoted text notes.
26
+      *    afn      text  File name of the audio file this event occurs in.
27
+
28
+     Notes:
29
+     #. Each event represents a mono audio signal.  If the event is drawn
30
+        from a multi-channel audio file then the 'chCnt' field will be
31
+        greater than one.  If 'chCnt' is greater than one then the associated
32
+        samples can be found by collecting all events that share the
33
+        same 'baseUuid'.
34
+
35
+     #. There may be zero or more columns of 'notes'. If there are no
36
+        notes then the 'afn' field is in column 11.
37
+
38
+     #. The index values (chIdx,obi,ibi,iei,oei) as stored in the CSV file
39
+         are 1 based. These values are decreased by 1 by the cmSdb CSV reader
40
+        so that their cmSdb value is zero based.  See cmSdbLoad().
41
+ 
42
+   */
43
+
44
+  enum
45
+  {
46
+    kOkSdbRC,
47
+    kLHeapFailSdbRC,
48
+    kCsvFailSdbRC,
49
+    kSyntaxErrSdbRC,
50
+    kInvalidRspIdxSdbRC,
51
+    kChPairNotFoundSdbRC
52
+  };
53
+
54
+  typedef cmHandle_t cmSdbH_t;
55
+  typedef cmHandle_t cmSdbResponseH_t;
56
+
57
+  typedef cmRC_t     cmSdbRC_t;
58
+  extern cmSdbH_t cmSdbNullHandle_t;
59
+  extern cmSdbResponseH_t cmSdbResponseNullHandle_t;
60
+  
61
+  typedef struct
62
+  {
63
+    unsigned         uuid;     // unique id of this sample
64
+    unsigned         baseUuid; // uuid of channel 0
65
+    unsigned         chIdx;   // channel index (0=left,1=right)
66
+    unsigned         obi;     // outer onset
67
+    unsigned         ibi;     // inner onset
68
+    unsigned         iei;     // inner offset
69
+    unsigned         oei;     // outer offset
70
+    unsigned         midi;    // MIDI pitch or -1 for unpitched instruments
71
+    unsigned         srate;   // sample rate
72
+    unsigned         chCnt;   // source channel count
73
+    const cmChar_t*  src;     // sample source (e.g. mcgill, ui )
74
+    const cmChar_t*  instr;   // instrument label
75
+    const cmChar_t*  afn;     // audio file name 
76
+    const cmChar_t** notesV;  // NULL terminated list of terms describing the event.
77
+  } cmSdbEvent_t;
78
+
79
+
80
+  cmSdbRC_t cmSdbCreate( cmCtx_t* ctx,  cmSdbH_t* hp, const cmChar_t* audioDir, const cmChar_t* csvFn );
81
+  cmSdbRC_t cmSdbDestroy( cmSdbH_t* hp );
82
+
83
+  bool      cmSdbIsValid( cmSdbH_t h );
84
+
85
+  cmSdbRC_t cmSdbLoad( cmSdbH_t h, const cmChar_t* csvFn );
86
+
87
+  // Select a set of events from the sample database.
88
+  //
89
+  // The possible selection criteria are:
90
+  //   sample rate
91
+  //   instrument label
92
+  //   source label
93
+  //   notes labels
94
+  //   event duration
95
+  //
96
+  // In order to match an event all active criteria
97
+  // must match.  In other words the match implies a
98
+  // logical AND operation on each match criteria.
99
+  // Each of the criteria can be made inactive by 
100
+  // specifying particular key values.
101
+  //   sample rate      = 0
102
+  //   instrument label = NULL
103
+  //   source label     = NULL
104
+  //   notes labels     = NULL
105
+  //   event duration   = minDurSec=0 maxDurSec=0
106
+  //
107
+  // For the text array arguments (instrV,srcV,notesV) 
108
+  // each element of the array is a key which is attempts to
109
+  // match the associated field in each event record.
110
+  // By default a match is triggered if the key text is identical to the 
111
+  // event field text.  The match algorithm can be modified by
112
+  // specifying a '*' as the first character in the key field.
113
+  // In this case a the key need only be a substring of the
114
+  // event field to trigger a match.  For example "*viol" 
115
+  // will return events that match both "violin" and "viola".
116
+  // 
117
+  // To specify a mismatch as a successful match 
118
+  // (i.e. to return events which do not match the key text) 
119
+  // prefix the key with a '!' character. 
120
+  // 
121
+  // Note that it is legal to specify both '!' and '*'. In
122
+  // which case a match will be triggered by fields where
123
+  // the key text is not a substring of the field text.
124
+  //
125
+  // All query response handles returned from this function
126
+  // should eventualy be released by the application via a call to
127
+  // cmSdbResponseFree().  
128
+  cmSdbRC_t cmSdbSelect( 
129
+    cmSdbH_t         h,
130
+    double           srate,      // event sample rate or 0 to ignore
131
+    const cmChar_t** instrV,     // array of instrument labels to match
132
+    const cmChar_t** srcV,       // array of 'src' labels to match
133
+    const cmChar_t** notesV,     // array of text 'notes' to match
134
+    double           minDurSec,  // min event duration
135
+    double           maxDurSec,  // max event duration or 0 to ignore
136
+    unsigned         minChCnt,   // min ch cnt or 0 to ignore
137
+    cmSdbResponseH_t* rhp );
138
+
139
+  // Given the event 'ep' locate the channel pairs associated with that event.
140
+  // The response handle returned from this function must be released
141
+  // by a call to cmSdbResponseFree().
142
+  cmSdbRC_t cmSdbSelectChPairs( cmSdbH_t h, const cmSdbEvent_t* ep, cmSdbResponseH_t* rhp );
143
+  
144
+  // Return the count of events in a query response.
145
+  unsigned            cmSdbResponseCount(   cmSdbResponseH_t  rh );
146
+
147
+  // Return the event at 'index' in from a query response.
148
+  // Legal 'index' range: 0<=index<=cmSdbResponseCount().
149
+  const cmSdbEvent_t* cmSdbResponseEvent(   cmSdbResponseH_t  rh, unsigned index );
150
+
151
+  // Return true if the 'rh' is a non-NULL query response handle.
152
+  bool                cmSdbResponseIsValid( cmSdbResponseH_t  rh );
153
+
154
+  // Release the resource held by a query response.  
155
+  cmSdbRC_t           cmSdbResponseFree(    cmSdbResponseH_t* rhp );
156
+  void                cmSdbResponsePrint(   cmSdbResponseH_t  rh, cmRpt_t* rpt );
157
+    
158
+  // Time align all channel pairs by setting the onset times to 
159
+  // the minimum time among all the pairs and the offset times to
160
+  // the maximum among all the pairs.
161
+  cmSdbRC_t cmSdbSyncChPairs( cmSdbH_t h );
162
+
163
+  cmSdbRC_t cmSdbTest( cmCtx_t* ctx );
164
+
165
+#ifdef __cplusplus
166
+}
167
+#endif
168
+
169
+#endif

Loading…
Cancel
Save