Browse Source

cmProc4.h : Initial implementation of cmFrqTrk.

master
Kevin Larke 10 years ago
parent
commit
3aa247662b
2 changed files with 362 additions and 2 deletions
  1. 298
    2
      cmProc4.c
  2. 64
    0
      cmProc4.h

+ 298
- 2
cmProc4.c View File

@@ -8,6 +8,7 @@
8 8
 #include "cmLinkedHeap.h"
9 9
 #include "cmFloatTypes.h"
10 10
 #include "cmComplexTypes.h"
11
+#include "cmFile.h"
11 12
 #include "cmFileSys.h"
12 13
 #include "cmJson.h"
13 14
 #include "cmSymTbl.h"
@@ -16,11 +17,12 @@
16 17
 #include "cmProcObj.h"
17 18
 #include "cmProcTemplate.h"
18 19
 #include "cmMath.h"
19
-#include "cmProc.h"
20
-#include "cmVectOps.h"
21 20
 #include "cmTime.h"
22 21
 #include "cmMidi.h"
23 22
 #include "cmMidiFile.h"
23
+#include "cmProc.h"
24
+#include "cmProc2.h"
25
+#include "cmVectOps.h"
24 26
 #include "cmTimeLine.h"
25 27
 #include "cmScore.h"
26 28
 #include "cmProc4.h"
@@ -4546,3 +4548,297 @@ cmRC_t         cmRecdPlayExec( cmRecdPlay* p, const cmSample_t** iChs, cmSample_
4546 4548
   return cmOkRC;
4547 4549
 }
4548 4550
 
4551
+//=======================================================================================================================
4552
+
4553
+cmFrqTrk* cmFrqTrkAlloc( cmCtx* c, cmFrqTrk* p, const cmFrqTrkArgs_t* a )
4554
+{
4555
+  cmFrqTrk* op = cmObjAlloc(cmFrqTrk,c,p);
4556
+
4557
+  op->bmf = cmBinMtxFileAlloc(c,NULL,NULL);
4558
+
4559
+  if( cmFrqTrkInit(op,a) != cmOkRC )
4560
+    cmFrqTrkFree(&op);
4561
+
4562
+  return op;
4563
+
4564
+}
4565
+
4566
+cmRC_t    cmFrqTrkFree( cmFrqTrk** pp )
4567
+{
4568
+  cmRC_t rc = cmOkRC;
4569
+
4570
+  if( pp==NULL || *pp==NULL )
4571
+    return rc;
4572
+
4573
+  cmFrqTrk* p = *pp;
4574
+  if((rc = cmFrqTrkFinal(p)) != cmOkRC )
4575
+    return rc;
4576
+
4577
+  cmMemFree(p->ch);
4578
+  cmMemFree(p->dbM);
4579
+  cmMemFree(p->pkiV);
4580
+  cmMemFree(p->dbV);
4581
+  cmBinMtxFileFree(&p->bmf);
4582
+  cmObjFree(pp);
4583
+  return rc;
4584
+
4585
+}
4586
+
4587
+cmRC_t    cmFrqTrkInit( cmFrqTrk* p, const cmFrqTrkArgs_t* a )
4588
+{
4589
+  cmRC_t rc;
4590
+  if((rc = cmFrqTrkFinal(p)) != cmOkRC )
4591
+    return rc;
4592
+
4593
+  p->a         = *a;
4594
+  p->ch        = cmMemResizeZ(cmFrqTrkCh_t,p->ch,a->chCnt );
4595
+  p->hN        = cmMax(1,a->wndSecs * a->srate / a->hopSmpCnt );
4596
+  p->bN        = p->a.binCnt;
4597
+  p->dbM       = cmMemResizeZ(cmReal_t,p->dbM,p->hN*p->bN);
4598
+  p->hi        = 0;
4599
+  p->dbV       = cmMemResizeZ(cmReal_t,p->dbV,p->bN);
4600
+  p->pkiV      = cmMemResizeZ(unsigned,p->pkiV,p->bN);
4601
+  p->deadN_max = a->maxTrkDeadSec * a->srate / a->hopSmpCnt;
4602
+  p->minTrkN   = a->minTrkSec * a->srate / a->hopSmpCnt;
4603
+  p->nextTrkId = 0;
4604
+
4605
+  if( a->logFn != NULL )
4606
+  {
4607
+    if( cmBinMtxFileInit(p->bmf, a->logFn ) != cmOkRC )
4608
+       cmCtxRtCondition(&p->obj, cmSubSysFailRC, "Log file open failed on '%s'.",cmStringNullGuard(a->logFn));
4609
+  }
4610
+
4611
+  return rc;
4612
+}
4613
+
4614
+cmRC_t    cmFrqTrkFinal( cmFrqTrk* p )
4615
+{
4616
+  cmRC_t   rc = cmOkRC;
4617
+  cmBinMtxFileFinal(p->bmf);
4618
+  return rc;
4619
+}
4620
+
4621
+// Return an available channel record or NULL if all channel records are in use.
4622
+cmFrqTrkCh_t* _cmFrqTrkFindAvailCh( cmFrqTrk* p )
4623
+{
4624
+  unsigned i;
4625
+  for(i=0; i<p->a.chCnt; ++i)
4626
+    if( p->ch[i].activeFl == false )
4627
+      return p->ch + i;
4628
+
4629
+  return NULL;
4630
+}
4631
+
4632
+unsigned _cmFrqTrkActiveChCount( cmFrqTrk* p )
4633
+{
4634
+  unsigned n = 0;
4635
+  unsigned i;
4636
+  for(i=0; i<p->a.chCnt; ++i)
4637
+    if( p->ch[i].activeFl )
4638
+      ++n;
4639
+
4640
+  return n;
4641
+}
4642
+
4643
+void _cmFrqTrkWriteLog( cmFrqTrk* p )
4644
+{
4645
+  unsigned n;
4646
+
4647
+  if( cmBinMtxFileIsValid(p->bmf) == false )
4648
+    return;
4649
+
4650
+  if((n = _cmFrqTrkActiveChCount(p)) > 0 )
4651
+  {
4652
+    unsigned  i,j;
4653
+    unsigned  nn  = n*4;
4654
+    cmReal_t* v   = cmMemAllocZ(cmReal_t,nn);
4655
+    cmReal_t* idV = v + n * 0;
4656
+    cmReal_t* hzV = v + n * 1;
4657
+    cmReal_t* dbV = v + n * 2;
4658
+    cmReal_t* stV = v + n * 3;
4659
+
4660
+    for(i=0,j=0; i<p->a.chCnt; ++i)
4661
+      if( p->ch[i].activeFl )
4662
+      {
4663
+        assert(j < n);
4664
+
4665
+        idV[j] = p->ch[i].id;
4666
+        hzV[j] = p->ch[i].hz;
4667
+        dbV[j] = p->ch[i].db;
4668
+        stV[j] = p->ch[i].dN;
4669
+
4670
+        ++j;
4671
+      }
4672
+    
4673
+    cmBinMtxFileExecR(p->bmf, v, nn );
4674
+  }
4675
+}
4676
+
4677
+void _cmFrqTrkPrintChs( const cmFrqTrk* p )
4678
+{
4679
+  unsigned i;
4680
+  for(i=0; i<p->a.chCnt; ++i)
4681
+  {
4682
+    cmFrqTrkCh_t* c = p->ch + i;
4683
+    printf("%i : %i tN:%i hz:%f db:%f\n",i,c->activeFl,c->tN,c->hz,c->db);
4684
+  }
4685
+}
4686
+
4687
+// Used to sort the channels into descending dB order.
4688
+int _cmFrqTrkChCompare( const void* p0, const void* p1 )
4689
+{  return ((cmFrqTrkCh_t*)p0)->db - ((cmFrqTrkCh_t*)p1)->db; }
4690
+
4691
+
4692
+// Return the index of the peak associated with pkiV[i] which best matches the tracker 'c'
4693
+// or cmInvalidIdx if no valid peaks were found.
4694
+// pkiV[ pkN ] holds the indexes into dbV[] and hzV[] which are peaks.
4695
+// Some elements of pkiV[] may be set to cmInvalidIdx if the associated peak has already
4696
+// been selected by another tracker.
4697
+unsigned _cmFrqTrkFindPeak( cmFrqTrk* p, const cmFrqTrkCh_t* c, const cmReal_t* dbV, const cmReal_t* hzV, unsigned* pkiV, unsigned pkN )
4698
+{
4699
+  unsigned i,pki;
4700
+  cmReal_t d_max = p->a.pkThreshDb;
4701
+  unsigned d_idx = cmInvalidIdx;
4702
+
4703
+  cmReal_t hz_min = c->hz * pow(2,-p->a.stRange/12.0);
4704
+  cmReal_t hz_max = c->hz * pow(2,-p->a.stRange/12.0);
4705
+
4706
+  // find the peak with the most energy inside the frequency range hz_min to hz_max.
4707
+  for(i=0; i<pkN; ++i)
4708
+    if( ((pki = pkiV[i]) != cmInvalidIdx) && hz_min <= hzV[pki] && hzV[pki] <= hz_max && dbV[pki]>d_max )      
4709
+    {
4710
+      d_max= dbV[pki];
4711
+      d_idx = i;      
4712
+    }
4713
+
4714
+  return d_idx;
4715
+}
4716
+
4717
+// Extend the existing trackers
4718
+void _cmFrqTrkUpdateChs( cmFrqTrk* p, const cmReal_t* dbV, const cmReal_t* hzV, unsigned* pkiV, unsigned pkN )
4719
+{
4720
+  unsigned i; 
4721
+
4722
+  // sort the channels in descending order
4723
+  qsort(p->ch,p->a.chCnt,sizeof(cmFrqTrkCh_t),_cmFrqTrkChCompare);
4724
+
4725
+  // for each active channel
4726
+  for(i=0; i<p->a.chCnt; ++i)
4727
+  {
4728
+    cmFrqTrkCh_t* c = p->ch + i;
4729
+
4730
+    if( c->activeFl )
4731
+    {
4732
+      unsigned pki;
4733
+
4734
+      // if no matching peak was found to tracker 'c'.
4735
+      if((pki = _cmFrqTrkFindPeak(p,c,dbV,hzV,pkiV,pkN)) == cmInvalidIdx )
4736
+      {
4737
+        c->dN += 1;
4738
+        c->tN += 1;
4739
+
4740
+        if( c->dN >= p->deadN_max )
4741
+          c->activeFl = false;
4742
+      }
4743
+      else // ... update the tracker using the matching peak
4744
+      {
4745
+        unsigned j = pkiV[pki];
4746
+        c->dN = 0;
4747
+        c->db = dbV[ j ];
4748
+        c->hz = hzV[ j ];
4749
+        c->tN += 1;
4750
+        pkiV[pki] = cmInvalidIdx;  // mark the peak as unavailable.
4751
+      }
4752
+    }
4753
+  }
4754
+}
4755
+
4756
+// Return the index into pkiV[] of the maximum energy peak in dbV[] 
4757
+// that is also above kAtkThreshDb.
4758
+unsigned _cmFrqTrkMaxEnergyPeakIndex( const cmFrqTrk* p, const cmReal_t* dbV, const unsigned* pkiV, unsigned pkN )
4759
+{
4760
+  cmReal_t mv = p->a.pkAtkThreshDb;
4761
+  unsigned mi = cmInvalidIdx;
4762
+  unsigned i;
4763
+
4764
+  for(i=0; i<pkN; ++i)
4765
+    if( pkiV[i] != cmInvalidIdx && dbV[pkiV[i]] >= mv )
4766
+    {
4767
+      mi = i;
4768
+      mv = dbV[pkiV[i]];
4769
+    }
4770
+      
4771
+  return mi;
4772
+}
4773
+
4774
+// start new trackers
4775
+void _cmFrqTrkNewChs( cmFrqTrk* p, const cmReal_t* dbV, const cmReal_t* hzV, unsigned* pkiV, unsigned pkN )
4776
+{
4777
+
4778
+  while(1)
4779
+  {
4780
+    unsigned db_max_idx;
4781
+    cmFrqTrkCh_t* c;
4782
+
4783
+    if((c = _cmFrqTrkFindAvailCh(p)) == NULL )
4784
+      break;
4785
+    
4786
+    if((db_max_idx = _cmFrqTrkMaxEnergyPeakIndex(p,dbV,pkiV,pkN)) == cmInvalidIdx )
4787
+      break;
4788
+
4789
+    c->activeFl = true;
4790
+    c->tN       = 1;
4791
+    c->dN       = 0;
4792
+    c->hz       = hzV[ pkiV[ db_max_idx ] ];
4793
+    c->db       = dbV[ pkiV[ db_max_idx ] ];
4794
+    c->id       = p->nextTrkId++;
4795
+
4796
+    pkiV[ db_max_idx ] = cmInvalidIdx;    
4797
+  }
4798
+
4799
+}
4800
+
4801
+
4802
+cmRC_t cmFrqTrkExec( cmFrqTrk* p, const cmReal_t* magV, const cmReal_t* phsV, const cmReal_t* hzV )
4803
+{
4804
+  cmRC_t   rc = cmOkRC;
4805
+
4806
+  // convert magV to Decibels
4807
+  cmVOR_AmplitudeToDb(p->dbV,p->bN,magV);
4808
+  
4809
+  // copy p->dbV to dbM[hi,:] 
4810
+  cmVOR_CopyN(p->dbM + p->hi, p->hN, p->bN, p->dbV, 1 );
4811
+
4812
+  // increment hi
4813
+  p->hi = (p->hi + 1) % p->hN;
4814
+
4815
+  // Form the spectral magnitude profile by taking the mean over time
4816
+  // of the last hN magnitude vectors
4817
+  cmVOR_MeanM(p->dbV, p->dbM, p->hN, p->bN, 0);
4818
+
4819
+  // set the indexes of the peaks above pkThreshDb in i0[]
4820
+  unsigned pkN = cmVOR_PeakIndexes(p->pkiV, p->bN, p->dbV, p->bN, p->a.pkThreshDb );
4821
+
4822
+  // extend the existing trackers
4823
+  _cmFrqTrkUpdateChs(p, p->dbV, hzV, p->pkiV, pkN );
4824
+
4825
+  // create new trackers
4826
+  _cmFrqTrkNewChs(p,p->dbV,hzV,p->pkiV,pkN);
4827
+  
4828
+  return rc;
4829
+}
4830
+
4831
+void  cmFrqTrkPrint( cmFrqTrk* p )
4832
+{
4833
+  printf("srate:         %f\n",p->a.srate);
4834
+  printf("chCnt:         %i\n",p->a.chCnt);
4835
+  printf("binCnt:        %i\n",p->a.binCnt);
4836
+  printf("hopSmpCnt:     %i\n",p->a.hopSmpCnt);
4837
+  printf("stRange:       %f\n",p->a.stRange);
4838
+  printf("wndSecs:       %f (%i)\n",p->a.wndSecs,p->hN);
4839
+  printf("minTrkSec:     %f (%i)\n",p->a.minTrkSec,p->minTrkN);
4840
+  printf("maxTrkDeadSec: %f (%i)\n",p->a.maxTrkDeadSec,p->deadN_max);
4841
+  printf("pkThreshDb:    %f\n",p->a.pkThreshDb);
4842
+  printf("pkAtkThreshDb: %f\n",p->a.pkAtkThreshDb);
4843
+
4844
+}

+ 64
- 0
cmProc4.h View File

@@ -698,6 +698,70 @@ extern "C" {
698 698
 
699 699
   cmRC_t         cmRecdPlayExec(        cmRecdPlay* p, const cmSample_t** iChs, cmSample_t** oChs, unsigned chCnt, unsigned smpCnt );
700 700
 
701
+  //=======================================================================================================================
702
+
703
+  typedef struct
704
+  {
705
+    double      srate;          // system sample rate
706
+    unsigned    chCnt;          // tracking channel count
707
+    unsigned    binCnt;         // count of spectrum elements passed in each call to cmFrqTrkExec()
708
+    unsigned    hopSmpCnt;      // phase vocoder hop count in samples
709
+    cmReal_t    stRange;        // maximum allowable semi-tones between a tracker and a peak
710
+    cmReal_t    wndSecs;        // duration of the  
711
+    cmReal_t    minTrkSec;      // minimum track length before track is considered stable
712
+    cmReal_t    maxTrkDeadSec;  // maximum length of time a tracker may fail to connect to a peak before being declared disconnected.
713
+    cmReal_t    pkThreshDb;     // minimum amplitide in Decibels of a selected spectral peak.
714
+    cmReal_t    pkAtkThreshDb;  // minimum amplitude in Decibels for the first frame of a new track.
715
+    const char* logFn;          // log file name or NULL if no file is to be written
716
+  } cmFrqTrkArgs_t;
717
+
718
+  typedef struct
719
+  {
720
+    bool     activeFl; 
721
+    unsigned id;
722
+    unsigned tN;   // age of this track in frames
723
+    unsigned dN;   // count of consecutive times this ch has not connected 
724
+    cmReal_t hz;   // current center frequency
725
+    cmReal_t db;   // current magnitude
726
+  } cmFrqTrkCh_t;
727
+
728
+  struct cmBinMtxFile_str;
729
+
730
+  typedef struct
731
+  {
732
+    cmObj         obj;
733
+    cmFrqTrkArgs_t  a;
734
+    cmFrqTrkCh_t*  ch;  // ch[ a.chCnt ]
735
+    unsigned       hN;  // count of history frames 
736
+    unsigned       bN;  // count of bins in peak matrices
737
+    cmReal_t*     dbM;  // dbM[ hN, bN ]
738
+    unsigned       hi;  // next row of dbM to fill
739
+
740
+    cmReal_t*     dbV;
741
+    unsigned*     pkiV;
742
+    unsigned      deadN_max; // max. count of hops a tracker may fail to connect before being set to inactive
743
+    unsigned      minTrkN;   // minimum track length in hops
744
+    unsigned      nextTrkId;
745
+
746
+    struct cmBinMtxFile_str* bmf;
747
+  } cmFrqTrk;
748
+
749
+  //
750
+  // 1. Calculate the mean spectral magnitude profile over the last hN frames.
751
+  // 2. Locate the peaks in the profile.
752
+  // 3. Allow each active tracker to select the closest peak to extend its life.
753
+  //     a) The distance between the trackers current location and a given
754
+  //        peak is measured based on magnitude and frequency over time.
755
+  //     b) There is a frequency range limit outside of which a given track-peak
756
+  //        connection may not go.
757
+  //     c) There is an amplitude threshold below which a track may not fall.
758
+
759
+  cmFrqTrk* cmFrqTrkAlloc( cmCtx* c, cmFrqTrk* p, const cmFrqTrkArgs_t* a );
760
+  cmRC_t    cmFrqTrkFree( cmFrqTrk** pp );
761
+  cmRC_t    cmFrqTrkInit( cmFrqTrk* p, const cmFrqTrkArgs_t* a );
762
+  cmRC_t    cmFrqTrkFinal( cmFrqTrk* p );
763
+  cmRC_t    cmFrqTrkExec( cmFrqTrk* p, const cmReal_t* magV, const cmReal_t* phsV, const cmReal_t* hzV );
764
+  void      cmFrqTrkPrint( cmFrqTrk* p );
701 765
 
702 766
 #ifdef __cplusplus
703 767
 }

Loading…
Cancel
Save