5249 lines
134 KiB
C
5249 lines
134 KiB
C
#include "cmPrefix.h"
|
|
#include "cmGlobal.h"
|
|
#include "cmRpt.h"
|
|
#include "cmErr.h"
|
|
#include "cmCtx.h"
|
|
#include "cmMem.h"
|
|
#include "cmMallocDebug.h"
|
|
#include "cmLinkedHeap.h"
|
|
#include "cmSymTbl.h"
|
|
#include "cmFloatTypes.h"
|
|
#include "cmComplexTypes.h"
|
|
#include "cmFileSys.h"
|
|
#include "cmProcObj.h"
|
|
#include "cmProcTemplate.h"
|
|
#include "cmAudioFile.h"
|
|
#include "cmMath.h"
|
|
#include "cmProc.h"
|
|
#include "cmVectOps.h"
|
|
#include "cmKeyboard.h"
|
|
#include "cmGnuPlot.h"
|
|
|
|
#include <time.h> // time()
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
void cmFloatPointExceptHandler( int signo, siginfo_t* info, void* context )
|
|
{
|
|
char* cp = "<Type Unknown>";
|
|
switch( info->si_code )
|
|
{
|
|
case FPE_INTDIV: cp = "integer divide by zero"; break;
|
|
case FPE_INTOVF: cp = "integer overflow"; break;
|
|
case FPE_FLTDIV: cp = "divide by zero"; break;
|
|
case FPE_FLTUND: cp = "underflow"; break;
|
|
case FPE_FLTRES: cp = "inexact result"; break;
|
|
case FPE_FLTINV: cp = "invalid operation"; break;
|
|
case FPE_FLTSUB: cp = "subscript range error"; break;
|
|
}
|
|
|
|
fprintf(stderr,"Floating point exception: Type: %s\n",cp);
|
|
exit(1);
|
|
}
|
|
|
|
// set 'orgSaPtr' to NULL to discard the current signal action state
|
|
void cmSetupFloatPointExceptHandler( struct sigaction* orgSaPtr )
|
|
{
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = SIG_DFL;
|
|
sa.sa_flags = SA_SIGINFO;
|
|
sa.sa_sigaction = cmFloatPointExceptHandler;
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
// set all FP except flags excetp: FE_INEXACT
|
|
|
|
#ifdef OS_OSX
|
|
// we don't yet support FP exceptions on OSX
|
|
// for an example of how to make this work with the linux interface as below
|
|
// see: http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c
|
|
assert(0);
|
|
#else
|
|
// int flags = FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID;
|
|
// feenableexcept(flags);
|
|
|
|
assert(0);
|
|
|
|
#endif
|
|
sigaction( SIGFPE, &sa, orgSaPtr );
|
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmAudioFileRd* cmAudioFileRdAlloc( cmCtx* c, cmAudioFileRd* p, unsigned procSmpCnt, const cmChar_t* fn, unsigned chIdx, unsigned begFrmIdx, unsigned endFrmIdx )
|
|
{
|
|
cmAudioFileRd* op = cmObjAlloc( cmAudioFileRd, c, p );
|
|
|
|
if( fn != NULL )
|
|
if( cmAudioFileRdOpen( op, procSmpCnt, fn, chIdx, begFrmIdx, endFrmIdx ) != cmOkRC )
|
|
cmAudioFileRdFree(&op);
|
|
|
|
return op;
|
|
}
|
|
|
|
cmRC_t cmAudioFileRdFree( cmAudioFileRd** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
|
|
if( pp != NULL && *pp != NULL )
|
|
{
|
|
cmAudioFileRd* p = *pp;
|
|
|
|
if((rc = cmAudioFileRdClose(p)) == cmOkRC )
|
|
{
|
|
cmMemPtrFree(&p->outV);
|
|
cmMemPtrFree(&p->fn);
|
|
cmObjFree(pp);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmAudioFileRdOpen( cmAudioFileRd* p, unsigned procSmpCnt, const cmChar_t* fn, unsigned chIdx, unsigned begFrmIdx, unsigned endFrmIdx )
|
|
{
|
|
cmRC_t rc;
|
|
cmRC_t afRC;
|
|
|
|
if((rc = cmAudioFileRdClose(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->h = cmAudioFileNewOpen( fn, &p->info, &afRC, p->obj.err.rpt );
|
|
|
|
if( afRC != kOkAfRC )
|
|
return cmCtxRtCondition( &p->obj, afRC, "Unable to open the audio file:'%s'", fn );
|
|
|
|
p->chIdx = chIdx;
|
|
p->outN = endFrmIdx==cmInvalidIdx ? p->info.frameCnt : procSmpCnt;
|
|
p->outV = cmMemResizeZ( cmSample_t, p->outV, p->outN );
|
|
p->fn = cmMemResizeZ( cmChar_t, p->fn, strlen(fn)+1 );
|
|
strcpy(p->fn,fn);
|
|
//p->mfp = cmCtxAllocDebugFile( p->obj.ctx,"audioFile");
|
|
p->lastReadFrmCnt = 0;
|
|
p->eofFl = false;
|
|
p->begFrmIdx = begFrmIdx;
|
|
p->endFrmIdx = endFrmIdx==0 ? p->info.frameCnt : endFrmIdx;
|
|
p->curFrmIdx = p->begFrmIdx;
|
|
|
|
if( p->begFrmIdx > 0 )
|
|
rc = cmAudioFileRdSeek(p,p->begFrmIdx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmAudioFileRdClose( cmAudioFileRd* p )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmRC_t afRC;
|
|
|
|
if( p == NULL )
|
|
return cmOkRC;
|
|
|
|
//cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
if( cmAudioFileIsOpen(p->h) == false )
|
|
return cmOkRC;
|
|
|
|
if((afRC = cmAudioFileDelete(&p->h)) != cmOkRC )
|
|
rc = cmCtxRtCondition( &p->obj, afRC, "An attempt to close the audio file'%s' failed.", p->fn );
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmAudioFileRdRead( cmAudioFileRd* p )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmRC_t afRC;
|
|
|
|
if(p->eofFl || ((p->eofFl = cmAudioFileIsEOF(p->h)) == true) )
|
|
return cmEofRC;
|
|
|
|
unsigned n = p->endFrmIdx==cmInvalidIdx ? p->outN : cmMin( p->outN, p->endFrmIdx - p->curFrmIdx );
|
|
|
|
if((afRC = cmAudioFileReadSample( p->h, n, p->chIdx, 1, &p->outV, &p->lastReadFrmCnt )) != kOkAfRC )
|
|
rc = cmCtxRtCondition( &p->obj, afRC, "Audio file read failed on:'%s'.", p->fn);
|
|
|
|
p->curFrmIdx += p->lastReadFrmCnt;
|
|
|
|
if( n < p->outN )
|
|
{
|
|
cmVOS_Zero(p->outV + p->lastReadFrmCnt, p->outN - p->lastReadFrmCnt);
|
|
p->eofFl = true;
|
|
}
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileSmpExec( p->mfp, p->outV, p->outN );
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmAudioFileRdSeek( cmAudioFileRd* p, unsigned frmIdx )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmRC_t afRC;
|
|
|
|
if((afRC = cmAudioFileSeek( p->h, frmIdx )) != kOkAfRC )
|
|
rc = cmCtxRtCondition( &p->obj, afRC, "Audio file read failed on:'%s'.", p->fn);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmAudioFileRdMinMaxMean( cmAudioFileRd* p, unsigned chIdx, cmSample_t* minPtr, cmSample_t* maxPtr, cmSample_t* meanPtr )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmRC_t afRC;
|
|
|
|
if(( afRC = cmAudioFileMinMaxMean( p->h, chIdx, minPtr, maxPtr, meanPtr )) != kOkAfRC )
|
|
rc = cmCtxRtCondition( &p->obj, afRC, "Audio file min, max, and mean calculation failed on '%s'", p->fn );
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmShiftBuf* cmShiftBufAlloc( cmCtx* c, cmShiftBuf* p, unsigned procSmpCnt, unsigned wndSmpCnt, unsigned hopSmpCnt )
|
|
{
|
|
cmShiftBuf* op = cmObjAlloc( cmShiftBuf, c, p );
|
|
|
|
if( procSmpCnt > 0 && wndSmpCnt > 0 && hopSmpCnt > 0 )
|
|
if( cmShiftBufInit(op, procSmpCnt, wndSmpCnt, hopSmpCnt ) != cmOkRC)
|
|
cmShiftBufFree(&op);
|
|
|
|
return op;
|
|
}
|
|
|
|
cmRC_t cmShiftBufFree( cmShiftBuf** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
|
|
if( pp != NULL && *pp != NULL )
|
|
{
|
|
if((rc = cmShiftBufFinal(*pp)) == cmOkRC )
|
|
{
|
|
cmMemPtrFree(&(*pp)->bufV);
|
|
cmObjFree(pp);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmShiftBufInit( cmShiftBuf* p, unsigned procSmpCnt, unsigned wndSmpCnt, unsigned hopSmpCnt )
|
|
{
|
|
cmRC_t rc;
|
|
|
|
if( hopSmpCnt > wndSmpCnt )
|
|
return cmCtxRtAssertFailed( &p->obj, cmArgAssertRC, "The window sample count (%i) must be greater than or equal to the hop sample count (%i).", wndSmpCnt, hopSmpCnt );
|
|
|
|
if((rc = cmShiftBufFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
// The worst case storage requirement is where there are wndSmpCnt-1 samples in outV[] and procSmpCnt new samples arrive.
|
|
p->bufSmpCnt = wndSmpCnt + procSmpCnt;
|
|
p->bufV = cmMemResizeZ( cmSample_t, p->outV, p->bufSmpCnt );
|
|
p->outV = p->bufV;
|
|
p->outN = wndSmpCnt;
|
|
p->wndSmpCnt = wndSmpCnt;
|
|
p->procSmpCnt = procSmpCnt;
|
|
p->hopSmpCnt = hopSmpCnt;
|
|
p->inPtr = p->outV;
|
|
p->fl = false;
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmShiftBufFinal( cmShiftBuf* p )
|
|
{
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
// This function should be called in a loop until it returns false.
|
|
// Note that 'sp' and 'sn' are ignored except p->fl == false.
|
|
bool cmShiftBufExec( cmShiftBuf* p, const cmSample_t* sp, unsigned sn )
|
|
{
|
|
assert( sn <= p->procSmpCnt );
|
|
|
|
// The active samples are in outV[wndSmpCnt]
|
|
// Stored samples are between outV + wndSmpCnt and inPtr.
|
|
|
|
// if the previous call to this function returned true then the buffer must be
|
|
// shifted by hopSmpCnt samples - AND sp[] is ignored.
|
|
if( p->fl )
|
|
{
|
|
// shift the output buffer to the left to remove expired samples
|
|
p->outV += p->hopSmpCnt;
|
|
|
|
// if there are not wndSmpCnt samples left in the buffer
|
|
if( p->inPtr - p->outV < p->wndSmpCnt )
|
|
{
|
|
// then copy the remaining active samples (between outV and inPtr)
|
|
// to the base of the physicalbuffer
|
|
unsigned n = p->inPtr - p->outV;
|
|
memmove( p->bufV, p->outV, n * sizeof(cmSample_t));
|
|
|
|
p->inPtr = p->bufV + n; // update the input and output positions
|
|
p->outV = p->bufV;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if the previous call to this function returned false then sp[sn] should not be ignored
|
|
assert( p->inPtr + sn <= p->outV + p->bufSmpCnt );
|
|
// copy the incoming samples into the buffer
|
|
cmVOS_Copy(p->inPtr,sn,sp);
|
|
p->inPtr += sn;
|
|
}
|
|
|
|
// if there are at least wndSmpCnt available samples in outV[]
|
|
p->fl = p->inPtr - p->outV >= p->wndSmpCnt;
|
|
|
|
return p->fl;
|
|
}
|
|
|
|
|
|
void cmShiftBufTest( cmCtx* c )
|
|
{
|
|
unsigned smpCnt = 48;
|
|
unsigned procSmpCnt = 5;
|
|
|
|
unsigned hopSmpCnt = 6;
|
|
unsigned wndSmpCnt = 7;
|
|
unsigned i;
|
|
|
|
cmShiftBuf* b = cmShiftBufAlloc(c,NULL,procSmpCnt,wndSmpCnt,hopSmpCnt );
|
|
|
|
cmSample_t x[ smpCnt ];
|
|
cmVOS_Seq(x,smpCnt,1,1);
|
|
|
|
//cmVOS_Print( rptFuncPtr, 1, smpCnt, x );
|
|
|
|
for(i=0; i<smpCnt; i+=procSmpCnt)
|
|
{
|
|
while( cmShiftBufExec( b, x + i, procSmpCnt ) )
|
|
{
|
|
cmVOS_Print( c->obj.err.rpt, 1, wndSmpCnt, b->outV );
|
|
}
|
|
}
|
|
|
|
|
|
cmShiftBufFree(&b);
|
|
|
|
}
|
|
|
|
/*
|
|
bool cmShiftBufExec( cmShiftBuf* p, const cmSample_t* sp, unsigned sn )
|
|
{
|
|
bool retFl = false;
|
|
|
|
if( sn > p->procSmpCnt )
|
|
{
|
|
cmCtxRtAssertFailed( p->obj.ctx, cmArgAssertRC, "The input sample count (%i) must be less than or equal to the proc sample count (%i).", sn, p->procSmpCnt);
|
|
return false;
|
|
}
|
|
|
|
assert( sn <= p->procSmpCnt );
|
|
|
|
cmSample_t* dbp = p->outV;
|
|
cmSample_t* dep = p->outV + (p->outN - sn);
|
|
cmSample_t* sbp = p->outV + sn;
|
|
|
|
// shift the last bufCnt-shiftCnt samples over the first shiftCnt samples
|
|
while( dbp < dep )
|
|
*dbp++ = *sbp++;
|
|
|
|
// copy in the new samples
|
|
dbp = dep;
|
|
dep = dbp + sn;
|
|
while( dbp < dep )
|
|
*dbp++ = *sp++;
|
|
|
|
// if any space remains at the end of the buffer then zero it
|
|
dep = p->outV + p->outN;
|
|
while( dbp < dep )
|
|
*dbp++ = 0;
|
|
|
|
|
|
if( p->firstPtr > p->outV )
|
|
p->firstPtr = cmMax( p->outV, p->firstPtr - p->procSmpCnt);
|
|
|
|
p->curHopSmpCnt += sn;
|
|
|
|
if( p->curHopSmpCnt >= p->hopSmpCnt )
|
|
{
|
|
p->curHopSmpCnt -= p->hopSmpCnt;
|
|
retFl = true;
|
|
}
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileSmpExec(p->mfp,p->outV,p->outN);
|
|
|
|
return retFl;
|
|
}
|
|
*/
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmWndFunc* cmWndFuncAlloc( cmCtx* c, cmWndFunc* p, unsigned wndId, unsigned wndSmpCnt, double kaiserSideLobeRejectDb )
|
|
{
|
|
cmWndFunc* op = cmObjAlloc( cmWndFunc, c, p );
|
|
|
|
if( wndId != kInvalidWndId )
|
|
if( cmWndFuncInit(op,wndId,wndSmpCnt,kaiserSideLobeRejectDb ) != cmOkRC )
|
|
cmWndFuncFree(&op);
|
|
|
|
return op;
|
|
}
|
|
|
|
cmRC_t cmWndFuncFree( cmWndFunc** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
if( pp != NULL && *pp != NULL )
|
|
{
|
|
|
|
cmWndFunc* p = *pp;
|
|
if((rc = cmWndFuncFinal(p)) == cmOkRC )
|
|
{
|
|
cmMemPtrFree(&p->wndV);
|
|
cmMemPtrFree(&p->outV);
|
|
cmObjFree(pp);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmWndFuncInit( cmWndFunc* p, unsigned wndId, unsigned wndSmpCnt, double kslRejectDb )
|
|
{
|
|
cmRC_t rc;
|
|
|
|
if( wndId == (p->wndId | p->flags) && wndSmpCnt == p->outN && kslRejectDb == p->kslRejectDb )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmWndFuncFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->wndV = cmMemResize( cmSample_t, p->wndV, wndSmpCnt );
|
|
p->outV = cmMemResize( cmSample_t, p->outV, wndSmpCnt );
|
|
p->outN = wndSmpCnt;
|
|
p->wndId = wndId;
|
|
p->kslRejectDb = kslRejectDb;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"wndFunc");
|
|
p->flags = wndId & (~kWndIdMask);
|
|
|
|
switch( wndId & kWndIdMask )
|
|
{
|
|
case kHannWndId: cmVOS_Hann( p->wndV, p->outN ); break;
|
|
case kHannMatlabWndId: cmVOS_HannMatlab( p->wndV, p->outN ); break;
|
|
case kHammingWndId: cmVOS_Hamming( p->wndV, p->outN ); break;
|
|
case kTriangleWndId: cmVOS_Triangle( p->wndV, p->outN ); break;
|
|
case kUnityWndId: cmVOS_Fill( p->wndV, p->outN, 1.0 ); break;
|
|
case kKaiserWndId:
|
|
{
|
|
double beta;
|
|
|
|
if( cmIsFlag(wndId,kSlRejIsBetaWndFl) )
|
|
beta = kslRejectDb;
|
|
else
|
|
beta = cmVOS_KaiserBetaFromSidelobeReject(fabs(kslRejectDb));
|
|
|
|
cmVOS_Kaiser( p->wndV,p->outN, beta);
|
|
}
|
|
break;
|
|
case kInvalidWndId: break;
|
|
|
|
default:
|
|
{ assert(0); }
|
|
}
|
|
|
|
cmSample_t den = 0;
|
|
cmSample_t num = 1;
|
|
if( cmIsFlag(p->flags,kNormBySumWndFl) )
|
|
{
|
|
den = cmVOS_Sum(p->wndV, p->outN);
|
|
num = wndSmpCnt;
|
|
}
|
|
|
|
if( cmIsFlag(p->flags,kNormByLengthWndFl) )
|
|
den += wndSmpCnt;
|
|
|
|
if( den > 0 )
|
|
{
|
|
cmVOS_MultVS(p->wndV,p->outN,num);
|
|
cmVOS_DivVS(p->wndV,p->outN,den);
|
|
}
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmWndFuncFinal( cmWndFunc* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
|
|
cmRC_t cmWndFuncExec( cmWndFunc* p, const cmSample_t* sp, unsigned sn )
|
|
{
|
|
if( sn > p->outN )
|
|
return cmCtxRtAssertFailed( &p->obj, cmArgAssertRC, "The length of the input vector (%i) is greater thean the length of the window function (%i).", sn, p->outN );
|
|
|
|
if( p->wndId != kInvalidWndId )
|
|
cmVOS_MultVVV( p->outV, sn, sp, p->wndV );
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileSmpExec(p->mfp,p->outV,p->outN);
|
|
|
|
return cmOkRC;
|
|
|
|
}
|
|
|
|
void cmWndFuncTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH )
|
|
{
|
|
|
|
unsigned wndCnt = 5;
|
|
double kaiserSideLobeRejectDb = 30;
|
|
|
|
cmCtx c;
|
|
cmCtxAlloc(&c,rpt,lhH,stH);
|
|
cmWndFunc* p = cmWndFuncAlloc(&c,NULL,kHannWndId,wndCnt, 0 );
|
|
cmVOS_Print( rpt, 1, wndCnt, p->wndV );
|
|
|
|
cmWndFuncInit(p,kHammingWndId ,wndCnt, 0 );
|
|
cmVOS_Print( rpt, 1, wndCnt, p->wndV );
|
|
|
|
cmWndFuncInit(p,kTriangleWndId ,wndCnt, 0 );
|
|
cmVOS_Print( rpt, 1, wndCnt, p->wndV );
|
|
|
|
cmWndFuncInit(p,kKaiserWndId ,wndCnt, kaiserSideLobeRejectDb );
|
|
cmVOS_Print( rpt, 1, wndCnt, p->wndV );
|
|
|
|
cmSample_t wV[ wndCnt ];
|
|
cmVOS_HannMatlab(wV,wndCnt);
|
|
cmVOS_Print( rpt, 1, wndCnt, wV);
|
|
|
|
cmWndFuncFree(&p);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmSpecDelay* cmSpecDelayAlloc( cmCtx* c, cmSpecDelay* ap, unsigned maxDelayCnt, unsigned binCnt )
|
|
{
|
|
cmSpecDelay* p = cmObjAlloc( cmSpecDelay, c, ap );
|
|
|
|
|
|
if( maxDelayCnt > 0 && binCnt > 0 )
|
|
if( cmSpecDelayInit(p,maxDelayCnt,binCnt) != cmOkRC )
|
|
cmSpecDelayFree(&p);
|
|
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmSpecDelayFree( cmSpecDelay** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
|
|
if( pp != NULL && *pp != NULL )
|
|
{
|
|
cmSpecDelay* p = *pp;
|
|
if((rc=cmSpecDelayFinal(p)) == cmOkRC )
|
|
{
|
|
cmMemPtrFree(&p->bufPtr);
|
|
cmObjFree(pp);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmRC_t cmSpecDelayInit( cmSpecDelay* p, unsigned maxDelayCnt, unsigned binCnt )
|
|
{
|
|
cmRC_t rc;
|
|
if((rc = cmSpecDelayFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->bufPtr = cmMemResizeZ( cmSample_t, p->bufPtr, binCnt * maxDelayCnt );
|
|
p->maxDelayCnt = maxDelayCnt;
|
|
p->outN = binCnt;
|
|
p->inIdx = 0;
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSpecDelayFinal(cmSpecDelay* p )
|
|
{ return cmOkRC; }
|
|
|
|
cmRC_t cmSpecDelayExec( cmSpecDelay* p, const cmSample_t* sp, unsigned sn )
|
|
{
|
|
cmSample_t* dp = p->bufPtr + (p->inIdx * p->outN);
|
|
cmVOS_Copy( dp, cmMin(sn,p->outN), sp);
|
|
p->inIdx = (p->inIdx+1) % p->maxDelayCnt;
|
|
return cmOkRC;
|
|
}
|
|
|
|
const cmSample_t* cmSpecDelayOutPtr( cmSpecDelay* p, unsigned delayCnt )
|
|
{
|
|
assert( delayCnt < p->maxDelayCnt );
|
|
|
|
int i = p->inIdx - delayCnt;
|
|
if( i < 0 )
|
|
i = p->maxDelayCnt + i;
|
|
|
|
return p->bufPtr + (i * p->outN);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmFilter* cmFilterAlloc( cmCtx* c, cmFilter* ap, const cmReal_t* b, unsigned bn, const cmReal_t* a, unsigned an, unsigned procSmpCnt, const cmReal_t* d )
|
|
{
|
|
cmRC_t rc;
|
|
cmFilter* p = cmObjAlloc(cmFilter,c,ap);
|
|
|
|
if( (bn > 0 || an > 0) && procSmpCnt > 0 )
|
|
if( (rc = cmFilterInit( p, b, bn, a, an, procSmpCnt, d)) != cmOkRC )
|
|
cmFilterFree(&p);
|
|
|
|
return p;
|
|
}
|
|
|
|
cmFilter* cmFilterAllocEllip( cmCtx* c, cmFilter* ap, cmReal_t srate, cmReal_t passHz, cmReal_t stopHz, cmReal_t passDb, cmReal_t stopDb, unsigned procSmpCnt, const cmReal_t* d )
|
|
{
|
|
cmRC_t rc;
|
|
cmFilter* p = cmObjAlloc(cmFilter,c,ap);
|
|
|
|
if( srate > 0 && passHz > 0 && procSmpCnt > 0 )
|
|
if( (rc = cmFilterInitEllip( p, srate, passHz, stopHz, passDb, stopDb, procSmpCnt, d)) != cmOkRC )
|
|
cmFilterFree(&p);
|
|
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmFilterFree( cmFilter** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
|
|
if( pp != NULL && *pp != NULL )
|
|
{
|
|
cmFilter* p = *pp;
|
|
if((rc = cmFilterFinal(p)) == cmOkRC )
|
|
{
|
|
cmMemPtrFree(&p->a);
|
|
cmMemPtrFree(&p->b);
|
|
cmMemPtrFree(&p->d);
|
|
cmMemPtrFree(&p->outSmpV);
|
|
cmObjFree(pp);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmFilterInit( cmFilter* p, const cmReal_t* b, unsigned bn, const cmReal_t* a, unsigned an, unsigned procSmpCnt, const cmReal_t* d )
|
|
{
|
|
assert( bn >= 1 );
|
|
assert( an >= 1 && a[0] != 0 );
|
|
|
|
cmRC_t rc;
|
|
if((rc = cmFilterFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
int cn = cmMax(an,bn) - 1;
|
|
|
|
// The output vector may be used as either cmReal_t or cmSample_t.
|
|
// Find the larger of the two possible types.
|
|
if( sizeof(cmReal_t) > sizeof(cmSample_t) )
|
|
{
|
|
p->outRealV = cmMemResizeZ( cmReal_t, p->outRealV, procSmpCnt );
|
|
p->outSmpV = (cmSample_t*)p->outRealV;
|
|
}
|
|
else
|
|
{
|
|
p->outSmpV = cmMemResizeZ( cmSample_t, p->outSmpV, procSmpCnt );
|
|
p->outRealV = (cmReal_t*)p->outRealV;
|
|
|
|
}
|
|
|
|
p->a = cmMemResizeZ( cmReal_t, p->a, cn );
|
|
p->b = cmMemResizeZ( cmReal_t, p->b, cn );
|
|
p->d = cmMemResizeZ( cmReal_t, p->d, cn+1 );
|
|
//p->outV = cmMemResizeZ( cmSample_t, p->outV, procSmpCnt );
|
|
p->outN = procSmpCnt;
|
|
p->an = an;
|
|
p->bn = bn;
|
|
p->cn = cn;
|
|
p->di = 0;
|
|
p->b0 = b[0] / a[0];
|
|
|
|
int i;
|
|
for(i=0; i<an-1; ++i)
|
|
p->a[i] = a[i+1] / a[0];
|
|
|
|
for(i=0; i<bn-1; ++i)
|
|
p->b[i] = b[i+1] / a[0];
|
|
|
|
if( d != NULL )
|
|
cmVOR_Copy(p->d,cn,d);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
// initialize an elliptic lowpass filter with the given characteristics
|
|
// ref: Parks & Burrus, Digital Filter Design, sec. 7.2.7 - 7.2.8
|
|
cmRC_t cmFilterInitEllip( cmFilter* p, cmReal_t srate, cmReal_t passHz, cmReal_t stopHz, cmReal_t passDb, cmReal_t stopDb, unsigned procSmpCnt, const cmReal_t* d )
|
|
{
|
|
assert( srate > 0 );
|
|
assert( passHz > 0 && stopHz > passHz && srate/2 > stopHz );
|
|
|
|
cmReal_t Wp, Ws, ep, v0,
|
|
k, kc, k1, k1c,
|
|
K, Kc, K1, K1c,
|
|
sn, cn, dn,
|
|
sm, cm, dm,
|
|
zr, zi, pr, pi;
|
|
|
|
unsigned N, L, j;
|
|
|
|
// prewarp Wp and Ws, calculate k
|
|
Wp = 2 * srate * tan(M_PI * passHz / srate);
|
|
Ws = 2 * srate * tan(M_PI * stopHz / srate);
|
|
k = Wp / Ws;
|
|
|
|
// calculate ep and k1 from passDb and stopDb
|
|
ep = sqrt(pow(10, passDb/10) - 1);
|
|
k1 = ep / sqrt(pow(10, stopDb/10) - 1);
|
|
|
|
// calculate complimentary moduli
|
|
kc = sqrt(1-k*k);
|
|
k1c = sqrt(1-k1*k1);
|
|
|
|
// calculate complete elliptic integrals
|
|
K = cmEllipK( kc );
|
|
Kc = cmEllipK( k );
|
|
K1 = cmEllipK( k1c );
|
|
K1c = cmEllipK( k1 );
|
|
|
|
// calculate minimum integer filter order N
|
|
N = ceil(K*K1c/Kc/K1);
|
|
|
|
// recalculate k and kc from chosen N
|
|
// Ws is minimized while other specs held constant
|
|
k = cmEllipDeg( K1c/K1/N );
|
|
kc = sqrt(1-k*k);
|
|
K = cmEllipK( kc );
|
|
Kc = cmEllipK( k );
|
|
Ws = Wp / k;
|
|
|
|
// initialize temporary coefficient arrays
|
|
cmReal_t b[N+1], a[N+1];
|
|
a[0] = b[0] = 1;
|
|
memset(b+1, 0, N*sizeof(cmReal_t));
|
|
memset(a+1, 0, N*sizeof(cmReal_t));
|
|
|
|
// intermediate value needed for determining poles
|
|
v0 = K/K1/N * cmEllipArcSc( 1/ep, k1 );
|
|
cmEllipJ( v0, k, &sm, &cm, &dm );
|
|
|
|
for( L=1-N%2; L<N; L+=2 )
|
|
{
|
|
// find the next pole and zero on s-plane
|
|
cmEllipJ( K*L/N, kc, &sn, &cn, &dn );
|
|
zr = 0;
|
|
zi = L ? Ws/sn : 1E25;
|
|
pr = -Wp*sm*cm*cn*dn/(1-pow(dn*sm,2));
|
|
pi = Wp*dm*sn/(1-pow(dn*sm,2));
|
|
|
|
// convert pole and zero to z-plane using bilinear transform
|
|
cmBlt( 1, srate, &zr, &zi );
|
|
cmBlt( 1, srate, &pr, &pi );
|
|
|
|
if( L == 0 )
|
|
{
|
|
// first order section
|
|
b[1] = -zr;
|
|
a[1] = -pr;
|
|
}
|
|
else
|
|
{
|
|
// replace complex root and its conjugate with 2nd order section
|
|
zi = zr*zr + zi*zi;
|
|
zr *= -2;
|
|
pi = pr*pr + pi*pi;
|
|
pr *= -2;
|
|
|
|
// combine with previous sections to obtain filter coefficients
|
|
for( j = L+1; j >= 2; j-- )
|
|
{
|
|
b[j] = b[j] + zr*b[j-1] + zi*b[j-2];
|
|
a[j] = a[j] + pr*a[j-1] + pi*a[j-2];
|
|
}
|
|
|
|
b[1] += zr;
|
|
a[1] += pr;
|
|
}
|
|
}
|
|
|
|
// scale b coefficients s.t. DC gain is 0 dB
|
|
cmReal_t sumB = 0, sumA = 0;
|
|
for( j = 0; j < N+1; j++ )
|
|
{
|
|
sumB += b[j];
|
|
sumA += a[j];
|
|
}
|
|
sumA /= sumB;
|
|
for( j = 0; j < N+1; j++ )
|
|
b[j] *= sumA;
|
|
|
|
return cmFilterInit( p, b, N+1, a, N+1, procSmpCnt, d );
|
|
}
|
|
|
|
cmRC_t cmFilterFinal( cmFilter* p )
|
|
{ return cmOkRC; }
|
|
|
|
|
|
cmRC_t cmFilterExecS( cmFilter* p, const cmSample_t* x, unsigned xn, cmSample_t* yy, unsigned yn )
|
|
{
|
|
|
|
cmSample_t* y;
|
|
|
|
if( yy == NULL || yn==0 )
|
|
{
|
|
y = p->outSmpV;
|
|
yn = p->outN;
|
|
}
|
|
else
|
|
{
|
|
y = yy;
|
|
}
|
|
|
|
cmVOS_Filter( y, yn, x, xn, p->b0, p->b, p->a, p->d, p->cn );
|
|
return cmOkRC;
|
|
|
|
/*
|
|
|
|
int i,j;
|
|
cmSample_t y0 = 0;
|
|
cmSample_t* y;
|
|
unsigned n;
|
|
|
|
if( yy == NULL || yn==0 )
|
|
{
|
|
n = cmMin(p->outN,xn);
|
|
y = p->outSmpV;
|
|
yn = p->outN;
|
|
}
|
|
else
|
|
{
|
|
n = cmMin(yn,xn);
|
|
y = yy;
|
|
}
|
|
|
|
// This is a direct form II algorithm based on the MATLAB implmentation
|
|
// http://www.mathworks.com/access/helpdesk/help/techdoc/ref/filter.html#f83-1015962
|
|
|
|
for(i=0; i<n; ++i)
|
|
{
|
|
//cmSample_t x0 = x[i];
|
|
|
|
y[i] = (x[i] * p->b0) + p->d[0];
|
|
|
|
y0 = y[i];
|
|
|
|
for(j=0; j<p->cn; ++j)
|
|
p->d[j] = (p->b[j] * x[i]) - (p->a[j] * y0) + p->d[j+1];
|
|
|
|
}
|
|
|
|
|
|
// if fewer input samples than output samples - zero the end of the output buffer
|
|
if( yn > xn )
|
|
cmVOS_Fill(y+i,yn-i,0);
|
|
|
|
return cmOkRC;
|
|
*/
|
|
}
|
|
|
|
cmRC_t cmFilterExecR( cmFilter* p, const cmReal_t* x, unsigned xn, cmReal_t* yy, unsigned yn )
|
|
{
|
|
|
|
cmReal_t* y;
|
|
|
|
if( yy == NULL || yn==0 )
|
|
{
|
|
y = p->outRealV;
|
|
yn = p->outN;
|
|
}
|
|
else
|
|
{
|
|
//n = cmMin(yn,xn);
|
|
y = yy;
|
|
}
|
|
|
|
cmVOR_Filter( y, yn, x, xn, p->b0, p->b, p->a, p->d, p->cn );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmFilterSignal( cmCtx* c, const cmReal_t b[], unsigned bn, const cmReal_t a[], unsigned an, const cmSample_t* x, unsigned xn, cmSample_t* y, unsigned yn )
|
|
{
|
|
int procSmpCnt = cmMin(1024,xn);
|
|
cmFilter* p = cmFilterAlloc(c,NULL,b,bn,a,an,procSmpCnt,NULL);
|
|
|
|
int i,n;
|
|
|
|
for(i=0; i<xn && i<yn; i+=n)
|
|
{
|
|
n = cmMin(procSmpCnt,cmMin(yn-i,xn-i));
|
|
cmFilterExecS(p,x+i,n,y+i,n);
|
|
}
|
|
|
|
if( i < yn )
|
|
cmVOS_Fill(y+i,yn-i,0);
|
|
|
|
cmFilterFree(&p);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmFilterFilterS(cmCtx* c, const cmReal_t bb[], unsigned bn, const cmReal_t aa[], unsigned an, const cmSample_t* x, unsigned xn, cmSample_t* y, unsigned yn )
|
|
{
|
|
cmFilter* f = cmFilterAlloc(c,NULL,NULL,0,NULL,0,0,NULL);
|
|
cmVOS_FilterFilter( f, cmFilterExecS, bb,bn,aa,an,x,xn,y,yn);
|
|
cmFilterFree(&f);
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmFilterFilterR(cmCtx* c, const cmReal_t bb[], unsigned bn, const cmReal_t aa[], unsigned an, const cmReal_t* x, unsigned xn, cmReal_t* y, unsigned yn )
|
|
{
|
|
cmFilter* f = cmFilterAlloc(c,NULL,NULL,0,NULL,0,0,NULL);
|
|
cmVOR_FilterFilter( f, cmFilterExecR, bb,bn,aa,an,x,xn,y,yn);
|
|
cmFilterFree(&f);
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
void cmFilterTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH )
|
|
{
|
|
cmCtx c;
|
|
cmCtxAlloc(&c, rpt, lhH, stH );
|
|
|
|
cmReal_t b[] = { 0.16, 0.32, 0.16 };
|
|
unsigned bn = sizeof(b)/sizeof(cmReal_t);
|
|
|
|
cmReal_t a[] = {1 , -.5949, .2348 };
|
|
unsigned an = sizeof(a)/sizeof(cmReal_t);
|
|
|
|
cmReal_t x[] = { 1,0,0,0,1,0,0,0 };
|
|
unsigned xn = sizeof(x)/sizeof(cmReal_t);
|
|
|
|
cmReal_t d[] = { .5, -.25};
|
|
|
|
// 0.1600 0.4152 0.3694 0.1223 0.1460 0.3781 0.3507 0.1198
|
|
// -0.0111 -0.0281
|
|
|
|
cmFilter* p = cmFilterAlloc(&c,NULL,b,bn,a,an,xn,d);
|
|
|
|
cmFilterExecR(p,x,xn,NULL,0);
|
|
|
|
cmVOR_Print( rpt, 1, xn, p->outRealV );
|
|
|
|
cmVOR_Print( rpt, 1, p->cn, p->d );
|
|
|
|
cmFilterFree(&p);
|
|
|
|
cmObjFreeStatic( cmCtxFree, cmCtx, c );
|
|
|
|
/*
|
|
cmReal_t b[] = { 0.16, 0.32, 0.16 };
|
|
unsigned bn = sizeof(b)/sizeof(cmReal_t);
|
|
|
|
cmReal_t a[] = { 1, -.5949, .2348};
|
|
unsigned an = sizeof(a)/sizeof(cmReal_t);
|
|
|
|
cmSample_t x[] = { 1,0,0,0,0,0,0,0,0,0 };
|
|
unsigned xn = sizeof(x)/sizeof(cmSample_t);
|
|
|
|
cmFilter* p = cmFilterAlloc(&c,NULL,b,bn,a,an,xn);
|
|
|
|
cmFilterExec(&c,p,x,xn,NULL,0);
|
|
cmVOS_Print( vReportFunc, 1, xn, p->outV );
|
|
|
|
cmVOR_Print( vReportFunc, 1, p->cn, p->d );
|
|
|
|
cmFilterExec(&c,p,x,xn,NULL,0);
|
|
cmVOS_Print( vReportFunc, 1, xn, p->outV );
|
|
|
|
cmFilterFree(&p);
|
|
*/
|
|
|
|
}
|
|
|
|
void cmFilterFilterTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH )
|
|
{
|
|
cmCtx c;
|
|
cmCtxAlloc(&c, rpt, lhH, stH );
|
|
|
|
cmReal_t b[] = { 0.36, 0.32, 0.36 };
|
|
unsigned bn = sizeof(b)/sizeof(cmReal_t);
|
|
|
|
cmReal_t a[] = {1 , -.5949, .2348 };
|
|
unsigned an = sizeof(a)/sizeof(cmReal_t);
|
|
|
|
cmReal_t x[] = { 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0 };
|
|
unsigned xn = sizeof(x)/sizeof(cmReal_t);
|
|
|
|
unsigned yn = xn;
|
|
cmReal_t y[yn];
|
|
memset(y,0,sizeof(y));
|
|
|
|
cmFilterFilterR(&c, b,bn,a,an,x,xn,y,yn );
|
|
|
|
cmVOR_Print( rpt, 1, yn, y );
|
|
|
|
cmObjFreeStatic( cmCtxFree, cmCtx, c );
|
|
|
|
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmComplexDetect* cmComplexDetectAlloc(cmCtx* c, cmComplexDetect* p, unsigned binCnt )
|
|
{
|
|
cmComplexDetect* op = cmObjAlloc( cmComplexDetect, c, p );
|
|
|
|
cmSpecDelayAlloc(c,&op->phsDelay,0,0);
|
|
cmSpecDelayAlloc(c,&op->magDelay,0,0);
|
|
|
|
if( binCnt > 0 )
|
|
if( cmComplexDetectInit(op,binCnt) != cmOkRC && p == NULL )
|
|
cmComplexDetectFree(&op);
|
|
|
|
return op;
|
|
}
|
|
|
|
cmRC_t cmComplexDetectFree( cmComplexDetect** pp )
|
|
{
|
|
cmRC_t rc;
|
|
if( pp != NULL && *pp != NULL )
|
|
{
|
|
cmComplexDetect* p = *pp;
|
|
|
|
if((rc = cmComplexDetectFinal(p)) == cmOkRC )
|
|
{
|
|
cmSpecDelay* sdp;
|
|
sdp = &p->phsDelay;
|
|
cmSpecDelayFree(&sdp);
|
|
sdp = &p->magDelay;
|
|
cmSpecDelayFree(&sdp);
|
|
|
|
cmObjFree(pp);
|
|
}
|
|
}
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmComplexDetectInit( cmComplexDetect* p, unsigned binCnt )
|
|
{
|
|
cmRC_t rc;
|
|
if((rc = cmComplexDetectFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmSpecDelayInit(&p->phsDelay,2,binCnt);
|
|
cmSpecDelayInit(&p->magDelay,1,binCnt);
|
|
p->binCnt = binCnt;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"complexDetect");
|
|
//p->cdfSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kCDF_FId, 1 );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmComplexDetectFinal( cmComplexDetect* p)
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmComplexDetectExec( cmComplexDetect* p, const cmSample_t* magV, const cmSample_t* phsV, unsigned binCnt )
|
|
{
|
|
p->out = cmVOS_ComplexDetect( magV, cmSpecDelayOutPtr(&p->magDelay,0), phsV, cmSpecDelayOutPtr(&p->phsDelay,1), cmSpecDelayOutPtr(&p->phsDelay,0), binCnt);
|
|
p->out /= 10000000;
|
|
|
|
cmSpecDelayExec(&p->magDelay,magV,binCnt);
|
|
cmSpecDelayExec(&p->phsDelay,phsV,binCnt);
|
|
|
|
//if( p->mfp != NULL )
|
|
// cmMtxFileSmpExec( p->mfp, &p->out, 1 );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmSample_t _cmComplexOnsetMedian( const cmSample_t* sp, unsigned sn, void* userPtr )
|
|
{ return cmVOS_Median(sp,sn); }
|
|
|
|
cmComplexOnset* cmComplexOnsetAlloc( cmCtx* c, cmComplexOnset* p, unsigned procSmpCnt, double srate, unsigned medFiltWndSmpCnt, double threshold, unsigned frameCnt )
|
|
{
|
|
cmComplexOnset* op = cmObjAlloc( cmComplexOnset, c, p );
|
|
|
|
if( procSmpCnt > 0 && srate > 0 && medFiltWndSmpCnt > 0 )
|
|
if( cmComplexOnsetInit( op, procSmpCnt, srate, medFiltWndSmpCnt, threshold, frameCnt ) != cmOkRC )
|
|
cmComplexOnsetFree(&op);
|
|
|
|
return op;
|
|
}
|
|
|
|
cmRC_t cmComplexOnsetFree( cmComplexOnset** pp)
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmComplexOnset* p = *pp;
|
|
|
|
if( pp==NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmComplexOnsetFinal(*pp)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->df);
|
|
cmMemPtrFree(&p->fdf);
|
|
cmObjFree(pp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmComplexOnsetInit( cmComplexOnset* p, unsigned procSmpCnt, double srate, unsigned medFiltWndSmpCnt, double threshold, unsigned frameCnt )
|
|
{
|
|
|
|
cmRC_t rc;
|
|
if(( rc = cmComplexOnsetFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->frmCnt = frameCnt;
|
|
p->dfi = 0;
|
|
p->df = cmMemResizeZ( cmSample_t, p->df, frameCnt );
|
|
p->fdf = cmMemResizeZ( cmSample_t, p->fdf, frameCnt );
|
|
p->onrate = 0;
|
|
p->threshold = threshold;
|
|
p->medSmpCnt = medFiltWndSmpCnt;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"complexOnset");
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmComplexOnsetFinal( cmComplexOnset* p)
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
cmRC_t cmComplexOnsetExec( cmComplexOnset* p, cmSample_t cdf )
|
|
{
|
|
p->df[p->dfi++] = cdf;
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmComplexOnsetCalc( cmComplexOnset* p )
|
|
{
|
|
// df -= mean(df)
|
|
cmVOS_SubVS(p->df,p->frmCnt,cmVOS_Mean(p->df,p->frmCnt));
|
|
|
|
// low pass forward/backward filter df[] into fdf[]
|
|
double d = 2 + sqrt(2);
|
|
cmReal_t b[] = {1/d, 2/d, 1/d};
|
|
unsigned bn = sizeof(b)/sizeof(b[0]);
|
|
cmReal_t a[] = {1, 0, 7-2*d};
|
|
unsigned an = sizeof(a)/sizeof(a[0]);
|
|
cmFilterFilterS(p->obj.ctx,b,bn,a,an,p->df,p->frmCnt,p->fdf,p->frmCnt);
|
|
|
|
// median filter to low-passed filtered fdf[] into df[]
|
|
cmVOS_FnThresh(p->fdf,p->frmCnt,p->medSmpCnt,p->df,1,NULL);
|
|
|
|
// subtract med filtered signal from the low passed signal.
|
|
// fdf[] -= df[];
|
|
cmVOS_SubVV(p->fdf,p->frmCnt,p->df);
|
|
cmVOS_SubVS(p->fdf,p->frmCnt,p->threshold);
|
|
cmSample_t *bp = p->df,
|
|
*ep = bp + p->frmCnt - 1,
|
|
*dp = p->fdf + 1;
|
|
*bp++ = *ep = 0;
|
|
for( ; bp<ep; bp++,dp++)
|
|
{
|
|
*bp = (*dp > *(dp-1) && *dp > *(dp+1) && *dp > 0) ? 1 : 0;
|
|
p->onrate += *bp;
|
|
}
|
|
|
|
p->onrate /= p->frmCnt;
|
|
|
|
/*
|
|
if( p->mfp != NULL )
|
|
{
|
|
bp = p->df;
|
|
ep = bp + p->frmCnt;
|
|
while( bp < ep )
|
|
cmMtxFileSmpExec( p->mfp, bp++, 1 );
|
|
}
|
|
*/
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmMfcc* cmMfccAlloc( cmCtx* c, cmMfcc* ap, double srate, unsigned melBandCnt, unsigned dctCoeffCnt, unsigned binCnt )
|
|
{
|
|
cmMfcc* p = cmObjAlloc( cmMfcc, c, ap );
|
|
|
|
if( melBandCnt > 0 && binCnt > 0 && dctCoeffCnt > 0 )
|
|
if( cmMfccInit( p, srate, melBandCnt, dctCoeffCnt, binCnt ) != cmOkRC )
|
|
cmMfccFree(&p);
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmMfccFree( cmMfcc** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
|
|
if( pp==NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
cmMfcc* p = *pp;
|
|
|
|
if( (rc = cmMfccFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->melM);
|
|
cmMemPtrFree(&p->dctM);
|
|
cmMemPtrFree(&p->outV);
|
|
cmObjFree(pp);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmMfccInit( cmMfcc* p, double srate, unsigned melBandCnt, unsigned dctCoeffCnt, unsigned binCnt )
|
|
{
|
|
cmRC_t rc;
|
|
if((rc = cmMfccFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->melM = cmMemResize( cmReal_t, p->melM, melBandCnt * binCnt );
|
|
p->dctM = cmMemResize( cmReal_t, p->dctM, dctCoeffCnt * melBandCnt );
|
|
p->outV = cmMemResize( cmReal_t, p->outV, dctCoeffCnt );
|
|
|
|
// each row of the matrix melp contains a mask
|
|
cmVOR_MelMask( p->melM, melBandCnt, binCnt, srate, kShiftMelFl );
|
|
|
|
// each row contains melBandCnt elements
|
|
cmVOR_DctMatrix(p->dctM, dctCoeffCnt, melBandCnt );
|
|
|
|
p->melBandCnt = melBandCnt;
|
|
p->dctCoeffCnt = dctCoeffCnt;
|
|
p->binCnt = binCnt;
|
|
p->outN = dctCoeffCnt;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"mfcc");
|
|
|
|
//if( p->obj.ctx->statsProcPtr != NULL )
|
|
// p->mfccSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kMFCC_FId, p->outN );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmMfccFinal( cmMfcc* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmMfccExecPower( cmMfcc* p, const cmReal_t* magPowV, unsigned binCnt )
|
|
{
|
|
assert( binCnt == p->binCnt );
|
|
|
|
cmReal_t t[ p->melBandCnt ];
|
|
|
|
// apply the mel filter mask to the power spectrum
|
|
cmVOR_MultVMV( t, p->melBandCnt, p->melM, binCnt, magPowV );
|
|
|
|
// convert mel bands to dB
|
|
cmVOR_PowerToDb( t, p->melBandCnt, t );
|
|
|
|
// decorellate the bands with a DCT
|
|
cmVOR_MultVMV( p->outV, p->dctCoeffCnt, p->dctM, p->melBandCnt, t );
|
|
|
|
|
|
/*
|
|
cmPlotSelectSubPlot(0,0);
|
|
cmPlotClear();
|
|
//cmPlotLineS( "power", NULL, magPowV, NULL, 35, NULL, kSolidPlotLineId );
|
|
cmPlotLineS( "mel", NULL, t0, NULL, p->melBandCnt, NULL, kSolidPlotLineId );
|
|
|
|
cmPlotSelectSubPlot(1,0);
|
|
cmPlotClear();
|
|
//cmPlotLineS( "meldb", NULL, t1, NULL, p->melBandCnt, NULL, kSolidPlotLineId );
|
|
cmPlotLineS( "mfcc", NULL, p->outV+1, NULL, p->dctCoeffCnt-1, NULL, kSolidPlotLineId );
|
|
*/
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileRealExec(p->mfp,p->outV, p->outN);
|
|
|
|
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmMfccExecAmplitude( cmMfcc* p, const cmReal_t* magAmpV, unsigned binCnt )
|
|
{
|
|
cmReal_t t[ binCnt ];
|
|
|
|
cmVOR_MultVVV( t,binCnt, magAmpV, magAmpV );
|
|
|
|
cmMfccExecPower(p,t,binCnt);
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileRealExec(p->mfp,p->outV, p->outN);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
enum { cmSonesEqlConBinCnt = kEqualLoudBandCnt, cmSonesEqlConCnt=13 };
|
|
|
|
cmSones* cmSonesAlloc( cmCtx* c, cmSones* ap, double srate, unsigned barkBandCnt, unsigned binCnt, unsigned flags )
|
|
{
|
|
cmSones* p = cmObjAlloc( cmSones, c, ap );
|
|
|
|
if( srate > 0 && barkBandCnt > 0 && binCnt > 0 )
|
|
if( cmSonesInit(p,srate,barkBandCnt,binCnt,flags) != cmOkRC )
|
|
cmSonesFree(&p);
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmSonesFree( cmSones** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmSones* p = *pp;
|
|
|
|
if( pp==NULL || *pp==NULL)
|
|
return cmOkRC;
|
|
|
|
if((rc = cmSonesFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->ttmV);
|
|
cmMemPtrFree(&p->sfM);
|
|
cmMemPtrFree(&p->barkIdxV);
|
|
cmMemPtrFree(&p->barkCntV);
|
|
cmMemPtrFree(&p->outV);
|
|
cmObjFree(pp);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmSonesInit( cmSones* p, double srate, unsigned barkBandCnt, unsigned binCnt, unsigned flags )
|
|
{
|
|
p->ttmV = cmMemResize( cmReal_t, p->ttmV, binCnt);
|
|
p->sfM = cmMemResize( cmReal_t, p->sfM, binCnt*barkBandCnt);
|
|
p->barkIdxV = cmMemResize( unsigned, p->barkIdxV, barkBandCnt);
|
|
p->barkCntV = cmMemResize( unsigned, p->barkCntV, barkBandCnt);
|
|
p->outV = cmMemResize( cmReal_t, p->outV, barkBandCnt);
|
|
|
|
// calc outer ear filter
|
|
cmVOR_TerhardtThresholdMask( p->ttmV, binCnt, srate, kNoTtmFlags );
|
|
|
|
// calc shroeder spreading function
|
|
cmVOR_ShroederSpreadingFunc(p->sfM, barkBandCnt, srate);
|
|
|
|
// calc the bin to bark maps
|
|
p->barkBandCnt = cmVOR_BarkMap( p->barkIdxV, p->barkCntV, barkBandCnt, binCnt, srate );
|
|
|
|
//unsigned i = 0;
|
|
//for(; i<barkBandCnt; ++i)
|
|
// printf("%i %i %i\n", i+1, barkIdxV[i]+1, barkCntV[i]);
|
|
|
|
bool elFl = cmIsFlag(p->flags, kUseEqlLoudSonesFl);
|
|
|
|
p->binCnt = binCnt;
|
|
p->outN = elFl ? cmSonesEqlConCnt : p->barkBandCnt;
|
|
p->overallLoudness = 0;
|
|
p->flags = flags;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"sones");
|
|
//p->sonesSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kSones_FId, p->outN );
|
|
//p->loudSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kLoud_FId, 1 );
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSonesFinal( cmSones* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
|
|
cmRC_t cmSonesExec( cmSones* p, const cmReal_t* magPowV, unsigned binCnt )
|
|
{
|
|
assert( binCnt == p->binCnt );
|
|
|
|
// Equal-loudness and phon map from: Y. Wonho, 1999, EMBSD: an objective speech quality measure based on audible distortion,
|
|
|
|
// equal-loudness contours
|
|
double eqlcon[cmSonesEqlConCnt][cmSonesEqlConBinCnt] =
|
|
{
|
|
{12,7,4,1,0,0,0,-0.5,-2,-3,-7,-8,-8.5,-8.5,-8.5},
|
|
{20,17,14,12,10,9.5,9,8.5,7.5,6.5,4,3,2.5,2,2.5},
|
|
{29,26,23,21,20,19.5,19.5,19,18,17,15,14,13.5,13,13.5},
|
|
{36,34,32,30,29,28.5,28.5,28.5,28,27.5,26,25,24.5,24,24.5},
|
|
{45,43,41,40,40,40,40,40,40,39.5,38,37,36.5,36,36.5},
|
|
{53,51,50,49,48.5,48.5,49,49,49,49,48,47,46.5,45.5,46},
|
|
{62,60,59,58,58,58.5,59,59,59,59,58,57.5,57,56,56},
|
|
{70,69,68,67.5,67.5,68,68,68,68,68,67,66,65.5,64.5,64.5},
|
|
{79,79,79,79,79,79,79,79,78,77.5,76,75,74.5,73,73},
|
|
{89,89,89,89.5,90,90,90,89.5,89,88.5,87,86,85.5,84,83.5},
|
|
{100,100,100,100,100,99.5,99,99,98.5,98,96,95,94.5,93.5,93},
|
|
{112,112,112,112,111,110.5,109.5,109,108.5,108,106,105,104.5,103,102.5},
|
|
{122,122,121,121,120.5,120,119,118,117,116.5,114.5,113.5,113,111, 110.5}
|
|
};
|
|
|
|
// loudness levels (phone scales)
|
|
double phons[cmSonesEqlConCnt]= {0.0,10.0,20.0,30.0,40.0,50.0,60.0,70.0,80.0,90.0,100.0,110.0,120.0};
|
|
|
|
unsigned i,j;
|
|
|
|
cmReal_t t0[ binCnt ];
|
|
cmReal_t t1[ p->barkBandCnt ];
|
|
cmReal_t t2[ p->barkBandCnt ];
|
|
|
|
unsigned* idxV = p->barkIdxV;
|
|
unsigned* cntV = p->barkCntV;
|
|
cmReal_t* sfM = p->sfM;
|
|
|
|
// apply the outer ear filter
|
|
cmVOR_MultVVV( t0, binCnt, magPowV, p->ttmV);
|
|
|
|
|
|
// apply the bark frequency warping
|
|
for(i=0; i<p->barkBandCnt; ++i)
|
|
{
|
|
if( cntV[i] == 0 )
|
|
t1[i] = 0;
|
|
else
|
|
{
|
|
t1[i] = t0[ idxV[i] ];
|
|
|
|
for(j=1; j<cntV[i]; ++j)
|
|
t1[i] += t0[ idxV[i] + j ];
|
|
}
|
|
}
|
|
|
|
|
|
// apply the spreading filters
|
|
cmVOR_MultVMV( t2, p->barkBandCnt, sfM, p->barkBandCnt, t1 );
|
|
|
|
bool elFl = cmIsFlag(p->flags, kUseEqlLoudSonesFl);
|
|
unsigned bandCnt = elFl ? cmMin(p->barkBandCnt,cmSonesEqlConBinCnt) : p->barkBandCnt;
|
|
|
|
//p->outN = elFl ? cmSonesEqlConCnt : p->barkBandCnt;
|
|
p->overallLoudness = 0;
|
|
|
|
for( i = 0; i <bandCnt; i++ )
|
|
{
|
|
// if using the equal-loudness contours begin with the third bark band
|
|
// and end with the 18th bark band
|
|
unsigned k = elFl ? i+3 : i;
|
|
|
|
if( k < p->barkBandCnt )
|
|
{
|
|
double v = t2[k];
|
|
|
|
// convert to db
|
|
v = 10*log10( v<1 ? 1 : v );
|
|
|
|
if( elFl )
|
|
{
|
|
// db to phons
|
|
// see: Y. Wonho, 1999, EMBSD: an objective speech quality measure based on audible distortion,
|
|
|
|
j = 0;
|
|
|
|
// find the equal loudness curve for this frequency and db level
|
|
while( v >= eqlcon[j][i] )
|
|
++j;
|
|
|
|
if( j == cmSonesEqlConCnt )
|
|
{
|
|
cmCtxRtAssertFailed( &p->obj, cmArgAssertRC, "Bark band %i is out of range during equal-loudness mapping.",j );
|
|
continue;
|
|
}
|
|
|
|
// convert db to phons
|
|
if( j == 0 )
|
|
v = phons[0];
|
|
else
|
|
{
|
|
double t1 = ( v - eqlcon[j-1][i] ) / ( eqlcon[j][i] - eqlcon[j-1][i] );
|
|
v = phons[j-1] + t1 * (phons[j] - phons[j-1]);
|
|
}
|
|
}
|
|
|
|
// convert to sones
|
|
// bladon and lindblom, 1981, JASA, modelling the judment of vowel quality differences
|
|
|
|
if( v >= 40 )
|
|
p->outV[i] = pow(2,(v-40)/10);
|
|
else
|
|
p->outV[i] = pow(v/40,2.642);
|
|
|
|
p->overallLoudness += p->outV[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileRealExec( p->mfp, p->outV, p->outN );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
void cmSonesTest()
|
|
{
|
|
cmKbRecd kb;
|
|
double srate = 44100;
|
|
unsigned bandCnt = 23;
|
|
unsigned binCnt = 513;
|
|
cmSample_t tv[ binCnt ];
|
|
cmSample_t sm[ bandCnt * bandCnt ];
|
|
cmSample_t t[ bandCnt * bandCnt ];
|
|
unsigned binIdxV[ bandCnt ];
|
|
unsigned cntV[ bandCnt ];
|
|
unsigned i;
|
|
|
|
cmPlotSetup("Sones",1,1);
|
|
|
|
cmVOS_TerhardtThresholdMask(tv,binCnt,srate, kModifiedTtmFl );
|
|
|
|
cmVOS_ShroederSpreadingFunc(sm, bandCnt, srate);
|
|
|
|
cmVOS_Transpose( t, sm, bandCnt, bandCnt );
|
|
|
|
bandCnt = cmVOS_BarkMap(binIdxV,cntV, bandCnt, binCnt, srate );
|
|
|
|
for(i=0; i<bandCnt; ++i)
|
|
printf("%i %i %i\n", i, binIdxV[i], cntV[i] );
|
|
|
|
for(i=0; i<bandCnt; ++i )
|
|
{
|
|
cmPlotLineS( NULL, NULL, t+(i*bandCnt), NULL, bandCnt, NULL, kSolidPlotLineId );
|
|
}
|
|
|
|
//cmPlotLineS( NULL, NULL, tv, NULL, binCnt, NULL, kSolidPlotLineId );
|
|
|
|
cmPlotDraw();
|
|
cmKeyPress(&kb);
|
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmAudioOffsetScale* cmAudioOffsetScaleAlloc( cmCtx* c, cmAudioOffsetScale* ap, unsigned procSmpCnt, double srate, cmSample_t offset, double rmsWndSecs, double dBref, unsigned flags )
|
|
{
|
|
cmAudioOffsetScale* p = cmObjAlloc( cmAudioOffsetScale, c, ap );
|
|
|
|
|
|
if( procSmpCnt > 0 && srate > 0 )
|
|
if( cmAudioOffsetScaleInit( p, procSmpCnt, srate, offset, rmsWndSecs, dBref, flags ) != cmOkRC )
|
|
cmAudioOffsetScaleFree(&p);
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmAudioOffsetScaleFree( cmAudioOffsetScale** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmAudioOffsetScale* p = *pp;
|
|
|
|
if( pp == NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
|
|
if((rc = cmAudioOffsetScaleFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->cBufPtr);
|
|
cmMemPtrFree(&p->cCntPtr);
|
|
cmMemPtrFree(&p->outV);
|
|
|
|
cmObjFree(pp);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmAudioOffsetScaleInit( cmAudioOffsetScale* p, unsigned procSmpCnt, double srate, cmSample_t offset, double rmsWndSecs, double dBref, unsigned flags )
|
|
{
|
|
assert( procSmpCnt > 0 && srate > 0);
|
|
|
|
cmRC_t rc;
|
|
if((rc = cmAudioOffsetScaleFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->cBufCnt = 0;
|
|
|
|
if( cmIsFlag(flags, kRmsAudioScaleFl) )
|
|
{
|
|
if( rmsWndSecs > 0 )
|
|
{
|
|
unsigned rmsSmpCnt = srate * rmsWndSecs;
|
|
p->cBufCnt = (unsigned)ceil( rmsSmpCnt / procSmpCnt );
|
|
|
|
if( p->cBufCnt > 0 )
|
|
{
|
|
p->cBufPtr = cmMemResizeZ( cmReal_t, p->cBufPtr, p->cBufCnt );
|
|
p->cCntPtr = cmMemResizeZ( unsigned, p->cCntPtr, p->cBufCnt );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p->cBufCnt = 0;
|
|
p->cBufPtr = NULL;
|
|
p->cCntPtr = NULL;
|
|
}
|
|
}
|
|
|
|
p->cBufIdx = 0;
|
|
p->cBufCurCnt = 0;
|
|
p->cBufSum = 0;
|
|
p->cCntSum = 0;
|
|
p->outV = cmMemResize( cmSample_t, p->outV, procSmpCnt );
|
|
p->outN = procSmpCnt;
|
|
p->offset = offset;
|
|
p->dBref = dBref;
|
|
p->flags = flags;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"audioOffsetScale");
|
|
return cmOkRC;
|
|
|
|
}
|
|
|
|
cmRC_t cmAudioOffsetScaleFinal( cmAudioOffsetScale* p )
|
|
{
|
|
//if( p != NULL)
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmAudioOffsetScaleExec( cmAudioOffsetScale* p, const cmSample_t* sp, unsigned sn )
|
|
{
|
|
|
|
double Pref = 20.0 / 1000000; // 20 micro Pascals
|
|
cmSample_t* dbp = p->outV;
|
|
const cmSample_t* dep = dbp + sn;
|
|
double scale = 0;
|
|
|
|
|
|
// if no scaling was requested then add offset only
|
|
if( cmIsFlag(p->flags, kNoAudioScaleFl) )
|
|
{
|
|
while( dbp < dep )
|
|
*dbp++ = *sp++ + p->offset;
|
|
goto doneLabel;
|
|
}
|
|
|
|
// if fixed scaling
|
|
if( cmIsFlag(p->flags, kFixedAudioScaleFl) )
|
|
{
|
|
if( scale == 0 )
|
|
scale = pow(10,p->dBref/20);
|
|
|
|
while( dbp < dep )
|
|
*dbp++ = (*sp++ + p->offset) * scale;
|
|
|
|
}
|
|
else // if RMS scaling
|
|
{
|
|
double sum = 0;
|
|
double rms = 0;
|
|
|
|
|
|
while( dbp < dep )
|
|
{
|
|
double v = (*sp++ + p->offset) / Pref;
|
|
|
|
sum += v*v;
|
|
|
|
*dbp++ = v;
|
|
}
|
|
|
|
// if there is no RMS buffer calc RMS on procSmpCnt samles
|
|
if( p->cBufCnt == 0 )
|
|
rms = sqrt( sum / sn );
|
|
else
|
|
{
|
|
p->cBufSum -= p->cBufPtr[ p->cBufIdx ];
|
|
p->cBufSum += sum;
|
|
p->cCntSum -= p->cCntPtr[ p->cBufIdx ];
|
|
p->cCntSum += sn;
|
|
p->cBufIdx = (p->cBufIdx+1) % p->cBufCnt;
|
|
p->cBufCurCnt = cmMin( p->cBufCurCnt+1, p->cBufCnt );
|
|
|
|
assert( p->cCntSum > 0 );
|
|
|
|
rms = sqrt( p->cBufSum / p->cCntSum );
|
|
}
|
|
|
|
double sigSPL = 20*log10(rms);
|
|
|
|
scale = pow(10,(p->dBref - sigSPL)/20);
|
|
|
|
dbp = p->outV;
|
|
while( dbp < dep )
|
|
*dbp++ *= scale;
|
|
|
|
}
|
|
|
|
doneLabel:
|
|
dbp = p->outV + sn;
|
|
dep = p->outV + p->outN;
|
|
while( dbp < dep )
|
|
*dbp++ = 0;
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileSmpExec(p->mfp,p->outV,p->outN);
|
|
|
|
return cmOkRC;
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
cmSpecMeas* cmSpecMeasAlloc( cmCtx* c, cmSpecMeas* ap, double srate, unsigned binCnt, unsigned wndFrmCnt, unsigned flags )
|
|
{
|
|
cmSpecMeas* p = cmObjAlloc( cmSpecMeas, c, ap );
|
|
|
|
if( srate > 0 && binCnt > 0 )
|
|
if( cmSpecMeasInit( p, srate, binCnt, wndFrmCnt, flags ) != cmOkRC )
|
|
cmSpecMeasFree(&p);
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmSpecMeasFree( cmSpecMeas** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmSpecMeas* p = *pp;
|
|
|
|
if( pp == NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmSpecMeasFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->rmsV);
|
|
cmMemPtrFree(&p->hfcV);
|
|
cmMemPtrFree(&p->scnV);
|
|
cmObjFree(pp);
|
|
return rc;
|
|
|
|
}
|
|
|
|
cmRC_t cmSpecMeasInit( cmSpecMeas* p, double srate, unsigned binCnt, unsigned wndFrmCnt, unsigned flags )
|
|
{
|
|
cmRC_t rc;
|
|
if((rc = cmSpecMeasFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
if( cmIsFlag(flags, kUseWndSpecMeasFl) )
|
|
{
|
|
p->rmsV = cmMemResizeZ( cmReal_t, p->rmsV, wndFrmCnt );
|
|
p->hfcV = cmMemResizeZ( cmReal_t, p->hfcV, wndFrmCnt );
|
|
p->scnV = cmMemResizeZ( cmReal_t, p->scnV, wndFrmCnt );
|
|
}
|
|
|
|
p->rmsSum = 0;
|
|
p->hfcSum = 0;
|
|
p->scnSum = 0;
|
|
|
|
p->binCnt = binCnt;
|
|
p->flags = flags;
|
|
p->wndFrmCnt = wndFrmCnt;
|
|
p->frameCnt = 0;
|
|
p->frameIdx = 0;
|
|
p->binHz = srate / ((binCnt-1) * 2);
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"specMeas");
|
|
|
|
//p->rmsSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kRMS_FId, 1);
|
|
//p->hfcSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kHFC_FId, 1);
|
|
//p->scSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kSC_FId, 1);
|
|
//p->ssSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kSS_FId, 1);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSpecMeasFinal( cmSpecMeas* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSpecMeasExec( cmSpecMeas* p, const cmReal_t* magPowV, unsigned binCnt )
|
|
{
|
|
assert( binCnt == p->binCnt );
|
|
|
|
unsigned i = 0;
|
|
const cmReal_t* sbp = magPowV;
|
|
const cmReal_t* sep = sbp + binCnt;
|
|
cmReal_t rmsSum = 0;
|
|
cmReal_t hfcSum = 0;
|
|
cmReal_t scnSum = 0;
|
|
cmReal_t ssSum = 0;
|
|
|
|
for(i=0; sbp < sep; ++i, ++sbp )
|
|
{
|
|
rmsSum += *sbp;
|
|
hfcSum += *sbp * i;
|
|
scnSum += *sbp * i * p->binHz;
|
|
}
|
|
|
|
p->frameCnt++;
|
|
|
|
if( cmIsFlag(p->flags, kUseWndSpecMeasFl) )
|
|
{
|
|
p->frameCnt = cmMin( p->frameCnt, p->wndFrmCnt );
|
|
|
|
cmReal_t* rmsV = p->rmsV + p->frameIdx;
|
|
cmReal_t* hfcV = p->hfcV + p->frameIdx;
|
|
cmReal_t* scnV = p->scnV + p->frameIdx;
|
|
|
|
p->rmsSum -= *rmsV;
|
|
p->hfcSum -= *hfcV;
|
|
p->scnSum -= *scnV;
|
|
|
|
*rmsV = rmsSum;
|
|
*hfcV = hfcSum;
|
|
*scnV = scnSum;
|
|
|
|
p->frameIdx = (p->frameIdx+1) % p->frameCnt;
|
|
|
|
}
|
|
|
|
p->rmsSum += rmsSum;
|
|
p->hfcSum += hfcSum;
|
|
p->scnSum += scnSum;
|
|
|
|
|
|
p->rms = sqrt(p->rmsSum / (p->binCnt * p->frameCnt) );
|
|
p->hfc = p->hfcSum / ( p->binCnt * p->frameCnt );
|
|
p->sc = p->scnSum / cmMax( cmReal_EPSILON, p->rmsSum );
|
|
|
|
|
|
sbp = magPowV;
|
|
for(i=0; sbp < sep; ++i )
|
|
{
|
|
cmReal_t t = (i*p->binHz) - p->sc;
|
|
ssSum += *sbp++ * (t*t);
|
|
}
|
|
|
|
p->ss = sqrt(ssSum / cmMax( cmReal_EPSILON, p->rmsSum ));
|
|
|
|
if( p->mfp != NULL )
|
|
{
|
|
cmReal_t a[4] = { p->rms, p->hfc, p->sc, p->ss };
|
|
cmMtxFileRealExec( p->mfp, a, 4 );
|
|
}
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmSigMeas* cmSigMeasAlloc( cmCtx* c, cmSigMeas* ap, double srate, unsigned procSmpCnt, unsigned measSmpCnt )
|
|
{
|
|
cmSigMeas* p = cmObjAlloc( cmSigMeas, c, ap );
|
|
|
|
p->sbp = cmShiftBufAlloc(c,&p->shiftBuf,0,0,0);
|
|
|
|
if( srate > 0 && procSmpCnt > 0 && measSmpCnt > 0 )
|
|
if( cmSigMeasInit( p, srate, procSmpCnt, measSmpCnt ) != cmOkRC )
|
|
cmSigMeasFree(&p);
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmSigMeasFree( cmSigMeas** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmSigMeas* p = *pp;
|
|
|
|
if( pp==NULL || *pp==NULL)
|
|
return cmOkRC;
|
|
|
|
if((rc = cmSigMeasFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmShiftBufFree(&p->sbp);
|
|
|
|
cmObjFree(pp);
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmSigMeasInit( cmSigMeas* p, double srate, unsigned procSmpCnt, unsigned measSmpCnt )
|
|
{
|
|
cmRC_t rc;
|
|
|
|
if((rc = cmSigMeasFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
if( procSmpCnt != measSmpCnt )
|
|
cmShiftBufInit( p->sbp, procSmpCnt, measSmpCnt, procSmpCnt );
|
|
|
|
p->zcrDelay = 0;
|
|
p->srate = srate;
|
|
p->measSmpCnt = measSmpCnt;
|
|
p->procSmpCnt = procSmpCnt;
|
|
//p->zcrSpRegId = cmStatsProcReg( p->obj.ctx->statsProcPtr, kZCR_FId, 1 );
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"sigMeas");
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSigMeasFinal( cmSigMeas* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSigMeasExec( cmSigMeas* p, const cmSample_t* sp, unsigned sn )
|
|
{
|
|
if( p->procSmpCnt != p->measSmpCnt )
|
|
{
|
|
cmShiftBufExec( p->sbp, sp, sn );
|
|
sp = p->sbp->outV;
|
|
sn = p->sbp->wndSmpCnt;
|
|
assert( p->sbp->wndSmpCnt == p->measSmpCnt );
|
|
}
|
|
|
|
unsigned zcn = cmVOS_ZeroCrossCount( sp, sn, NULL );
|
|
p->zcr = (cmReal_t)zcn * p->srate / p->measSmpCnt;
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileRealExec( p->mfp, &p->zcr, 1 );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmSRC* cmSRCAlloc( cmCtx* c, cmSRC* ap, double srate, unsigned procSmpCnt, unsigned upFact, unsigned dnFact )
|
|
{
|
|
cmSRC* p = cmObjAlloc( cmSRC, c,ap );
|
|
|
|
cmFilterAlloc(c,&p->filt,NULL,0,NULL,0,0,NULL);
|
|
|
|
if( srate > 0 && procSmpCnt > 0 )
|
|
if( cmSRCInit( p, srate, procSmpCnt, upFact, dnFact ) != cmOkRC )
|
|
cmSRCFree(&p);
|
|
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmSRCFree( cmSRC** pp )
|
|
{
|
|
cmRC_t rc;
|
|
|
|
if( pp != NULL && *pp != NULL )
|
|
{
|
|
cmSRC* p = *pp;
|
|
|
|
if((rc = cmSRCFinal( p )) == cmOkRC )
|
|
{
|
|
cmFilter* fp = &p->filt;
|
|
cmFilterFree(&fp);
|
|
cmMemPtrFree(&p->outV);
|
|
cmObjFree(pp);
|
|
}
|
|
}
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSRCInit( cmSRC* p, double srate, unsigned procSmpCnt, unsigned upFact, unsigned dnFact )
|
|
{
|
|
cmRC_t rc;
|
|
|
|
if((rc = cmSRCFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
double hiRate = upFact * srate;
|
|
double loRate = hiRate / dnFact;
|
|
double minRate= cmMin( loRate, srate );
|
|
double fcHz = minRate/2;
|
|
double dHz = (fcHz * .1); // transition band is 5% of min sample rate
|
|
double passHz = fcHz-dHz;
|
|
double stopHz = fcHz;
|
|
double passDb = 0.001;
|
|
double stopDb = 20;
|
|
|
|
cmFilterInitEllip( &p->filt, hiRate, passHz, stopHz, passDb, stopDb, procSmpCnt, NULL );
|
|
|
|
//printf("CoeffCnt:%i dHz:%f passHz:%f stopHz:%f passDb:%f stopDb:%f\n", p->fir.coeffCnt, dHz, passHz, stopHz, passDb, stopDb );
|
|
|
|
p->outN = (unsigned)ceil(procSmpCnt * upFact / dnFact);
|
|
p->outV = cmMemResize( cmSample_t, p->outV, p->outN );
|
|
p->upi = 0;
|
|
p->dni = 0;
|
|
p->upFact = upFact;
|
|
p->dnFact = dnFact;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"src");
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSRCFinal( cmSRC* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmSRCExec( cmSRC* p, const cmSample_t* sp, unsigned sn )
|
|
{
|
|
const cmSample_t* sep = sp + sn;
|
|
cmSample_t* op = p->outV;
|
|
const cmSample_t* oep = op + p->outN;
|
|
unsigned iN = sn * p->upFact;
|
|
unsigned i,j;
|
|
|
|
// run the filter at the upsampled rate ...
|
|
for(i=0; i<iN; ++i)
|
|
{
|
|
assert( p->upi!=0 || sp<sep );
|
|
|
|
cmSample_t x0 = p->upi==0 ? *sp++ : 0,
|
|
y0 = x0 * p->filt.b0 + p->filt.d[0];
|
|
|
|
// ... but output at the down sampled rate
|
|
if( p->dni==0 )
|
|
{
|
|
assert( op < oep );
|
|
*op++ = y0;
|
|
}
|
|
|
|
// advance the filter delay line
|
|
for(j=0; j<p->filt.cn; ++j)
|
|
p->filt.d[j] = p->filt.b[j]*x0 - p->filt.a[j]*y0 + p->filt.d[j+1];
|
|
|
|
// update the input/output clocks
|
|
p->upi = (p->upi + 1) % p->upFact;
|
|
p->dni = (p->dni + 1) % p->dnFact;
|
|
|
|
}
|
|
|
|
p->outN = op - p->outV;
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileSmpExec(p->mfp,p->outV,p->outN );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmConstQ* cmConstQAlloc( cmCtx* c, cmConstQ* ap, double srate, unsigned minMidiPitch, unsigned maxMidiPitch, unsigned binsPerOctave, double thresh )
|
|
{
|
|
cmConstQ* p = cmObjAlloc( cmConstQ, c, ap );
|
|
|
|
if( srate >0 )
|
|
if( cmConstQInit(p,srate,minMidiPitch,maxMidiPitch,binsPerOctave,thresh) != cmOkRC )
|
|
cmConstQFree(&p);
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmConstQFree( cmConstQ** pp )
|
|
{
|
|
cmRC_t rc;
|
|
cmConstQ* p = *pp;
|
|
|
|
if( pp==NULL || *pp==NULL)
|
|
return cmOkRC;
|
|
|
|
if((rc = cmConstQFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->fiV);
|
|
cmMemPtrFree(&p->foV);
|
|
cmMemPtrFree(&p->skM);
|
|
cmMemPtrFree(&p->outV);
|
|
cmMemPtrFree(&p->magV);
|
|
cmMemPtrFree(&p->skBegV);
|
|
cmMemPtrFree(&p->skEndV);
|
|
cmObjFree(pp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
|
|
cmRC_t cmConstQInit( cmConstQ* p, double srate, unsigned minMidiPitch, unsigned maxMidiPitch, unsigned binsPerOctave, double thresh )
|
|
{
|
|
cmRC_t rc;
|
|
if((rc = cmConstQFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmReal_t minHz = cmMidiToHz(minMidiPitch);
|
|
cmReal_t maxHz = cmMidiToHz(maxMidiPitch);
|
|
cmReal_t Q = 1.0/(pow(2,(double)1.0/binsPerOctave)-1);
|
|
unsigned K = (unsigned)ceil( binsPerOctave * log2(maxHz/minHz) );
|
|
unsigned fftN = cmNextPowerOfTwo( (unsigned)ceil(Q*srate/minHz) );
|
|
unsigned k = 0;
|
|
p->fiV = cmMemResize(cmComplexR_t, p->fiV, fftN);
|
|
p->foV = cmMemResize(cmComplexR_t, p->foV, fftN);
|
|
|
|
cmFftPlanR_t plan = cmFft1dPlanAllocR(fftN, p->fiV, p->foV, FFTW_FORWARD, FFTW_ESTIMATE );
|
|
|
|
p->wndSmpCnt = fftN;
|
|
p->constQBinCnt = K;
|
|
p->binsPerOctave= binsPerOctave;
|
|
p->skM = cmMemResizeZ( cmComplexR_t, p->skM, p->wndSmpCnt * p->constQBinCnt);
|
|
p->outV = cmMemResizeZ( cmComplexR_t, p->outV, p->constQBinCnt);
|
|
p->magV = cmMemResizeZ( cmReal_t, p->magV, p->constQBinCnt);
|
|
p->skBegV = cmMemResizeZ( unsigned, p->skBegV, p->constQBinCnt);
|
|
p->skEndV = cmMemResizeZ( unsigned, p->skEndV, p->constQBinCnt);
|
|
|
|
|
|
//p->mfp = cmCtxAllocDebugFile( p->obj.ctx, "constQ");
|
|
|
|
//printf("hz:%f %f bpo:%i sr:%f thresh:%f Q:%f K%i (cols) fftN:%i (rows)\n", minHz,maxHz,binsPerOctave,srate,thresh,Q,K,fftN);
|
|
|
|
double* hamm = NULL;
|
|
|
|
// note that the bands are created in reverse order
|
|
for(k=0; k<K; ++k)
|
|
{
|
|
unsigned iN = ceil( Q * srate / (minHz * pow(2,(double)k/binsPerOctave)));
|
|
unsigned start = fftN/2;
|
|
//double hamm[ iN ];
|
|
hamm = cmMemResizeZ(double,hamm,iN);
|
|
|
|
memset( p->fiV, 0, fftN * sizeof(cmComplexR_t));
|
|
memset( p->foV, 0, fftN * sizeof(cmComplexR_t));
|
|
|
|
cmVOD_Hamming( hamm, iN );
|
|
|
|
cmVOD_DivVS( hamm, iN, iN );
|
|
|
|
if( cmIsEvenU(iN) )
|
|
start -= iN/2;
|
|
else
|
|
start -= (iN+1)/2;
|
|
|
|
//printf("k:%i iN:%i start:%i %i\n",k,iN,start,start+iN-1);
|
|
|
|
unsigned i = start;
|
|
for(; i<=start+iN-1; ++i)
|
|
{
|
|
double arg = 2.0*M_PI*Q*(i-start)/iN;
|
|
double mag = hamm[i-start];
|
|
|
|
p->fiV[i-1] = (mag * cos(arg)) + (mag * I * sin(arg));
|
|
}
|
|
|
|
cmFftExecuteR(plan);
|
|
|
|
// since the bands are created in reverse order they are also stored in reverse order
|
|
// (i.e column k-1 is stored first and column 0 is stored last)
|
|
i=0;
|
|
unsigned minIdx = -1;
|
|
unsigned maxIdx = 0;
|
|
for(; i<fftN; ++i)
|
|
{
|
|
bool fl = cabs(p->foV[i]) <= thresh;
|
|
|
|
p->skM[ (k*p->wndSmpCnt) + i ] = fl ? 0 : p->foV[i]/fftN;
|
|
|
|
if( fl==false && minIdx == -1 )
|
|
minIdx = i;
|
|
|
|
if( fl==false && i>maxIdx )
|
|
maxIdx = i;
|
|
}
|
|
|
|
p->skBegV[k] = minIdx;
|
|
p->skEndV[k] = maxIdx;
|
|
|
|
}
|
|
|
|
cmMemPtrFree(&hamm);
|
|
|
|
cmFftPlanFreeR(plan);
|
|
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmConstQFinal( cmConstQ* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmConstQExec( cmConstQ* p, const cmComplexR_t* ftV, unsigned srcBinCnt )
|
|
{
|
|
//acVORC_MultVVM( p->outV, p->constQBinCnt,ftV,p->wndSmpCnt, p->skM );
|
|
|
|
cmReal_t* mbp = p->magV;
|
|
cmComplexR_t* dbp = p->outV;
|
|
const cmComplexR_t* dep = p->outV + p->constQBinCnt;
|
|
|
|
unsigned i = 0;
|
|
|
|
for(; dbp < dep; ++dbp,++i,++mbp )
|
|
{
|
|
const cmComplexR_t* sbp = ftV + p->skBegV[i];
|
|
const cmComplexR_t* kp = p->skM + (i*p->wndSmpCnt) + p->skBegV[i];
|
|
const cmComplexR_t* ep = kp + (p->skEndV[i] - p->skBegV[i]) + 1;
|
|
|
|
*dbp = 0;
|
|
while( kp < ep )
|
|
*dbp += *sbp++ * *kp++;
|
|
|
|
*mbp = cmCabsR(*dbp);
|
|
}
|
|
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileComplexExec( p->mfp, p->outV, p->constQBinCnt, 1 );
|
|
|
|
return cmOkRC;
|
|
|
|
}
|
|
|
|
void cmConstQTest( cmConstQ* p )
|
|
{
|
|
cmKbRecd kb;
|
|
|
|
unsigned i,j;
|
|
cmSample_t* t = cmMemAlloc( cmSample_t, p->wndSmpCnt );
|
|
|
|
for(i=0; i<p->constQBinCnt; ++i)
|
|
{
|
|
for(j=0; j<p->wndSmpCnt; ++j)
|
|
t[j] = cabs( p->skM[ (i*p->wndSmpCnt) + j ]);
|
|
|
|
|
|
//cmPlotClear();
|
|
cmPlotLineS( NULL, NULL, t, NULL, 500, NULL, kSolidPlotLineId );
|
|
}
|
|
|
|
cmPlotDraw();
|
|
cmKeyPress(&kb);
|
|
|
|
cmMemPtrFree(&t);
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmHpcp* cmTunedHpcpAlloc( cmCtx* c, cmHpcp* ap, unsigned binsPerOctave, unsigned constQBinCnt, unsigned cqMinMidiPitch, unsigned frameCnt, unsigned medFiltOrder )
|
|
{
|
|
cmHpcp* p = cmObjAlloc( cmHpcp, c, ap );
|
|
|
|
if( binsPerOctave > 0 && constQBinCnt >> 0 )
|
|
if( cmTunedHpcpInit( p, binsPerOctave, constQBinCnt, cqMinMidiPitch, frameCnt, medFiltOrder ) != cmOkRC)
|
|
cmTunedHpcpFree(&p);
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmTunedHpcpFree( cmHpcp** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmHpcp* p = *pp;
|
|
|
|
if(pp==NULL || *pp==NULL)
|
|
return cmOkRC;
|
|
|
|
if((rc = cmTunedHpcpFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->hpcpM);
|
|
cmMemPtrFree(&p->fhpcpM);
|
|
cmMemPtrFree(&p->histV);
|
|
cmMemPtrFree(&p->outM);
|
|
cmMemPtrFree(&p->meanV);
|
|
cmMemPtrFree(&p->varV);
|
|
|
|
cmObjFree(pp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmTunedHpcpInit( cmHpcp* p, unsigned binsPerOctave, unsigned constQBinCnt, unsigned cqMinMidiPitch, unsigned frameCnt, unsigned medFiltOrder )
|
|
{
|
|
assert( binsPerOctave % kStPerOctave == 0 );
|
|
assert( cmIsOddU( binsPerOctave / kStPerOctave ) );
|
|
|
|
cmRC_t rc;
|
|
if((rc = cmTunedHpcpFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->histN = binsPerOctave/kStPerOctave;
|
|
p->hpcpM = cmMemResizeZ( cmReal_t, p->hpcpM, frameCnt*binsPerOctave );
|
|
p->fhpcpM = cmMemResizeZ( cmReal_t, p->fhpcpM, binsPerOctave*frameCnt );
|
|
p->histV = cmMemResizeZ( unsigned, p->histV, p->histN );
|
|
p->outM = cmMemResizeZ( cmReal_t, p->outM, kStPerOctave * frameCnt );
|
|
p->meanV = cmMemResizeZ( cmReal_t, p->meanV, kStPerOctave );
|
|
p->varV = cmMemResizeZ( cmReal_t, p->varV, kStPerOctave );
|
|
|
|
p->constQBinCnt = constQBinCnt;
|
|
p->binsPerOctave = binsPerOctave;
|
|
p->frameCnt = frameCnt;
|
|
p->frameIdx = 0;
|
|
p->cqMinMidiPitch= cqMinMidiPitch;
|
|
p->medFiltOrder = medFiltOrder;
|
|
//p->mf0p = cmCtxAllocDebugFile(p->obj.ctx,"hpcp");
|
|
//p->mf1p = cmCtxAllocDebugFile(p->obj.ctx,"fhpcp");
|
|
//p->mf2p = cmCtxAllocDebugFile(p->obj.ctx,"chroma");
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmTunedHpcpFinal( cmHpcp* p )
|
|
{
|
|
/*
|
|
if( p != NULL )
|
|
{
|
|
cmCtxFreeDebugFile(p->obj.ctx,&p->mf0p);
|
|
cmCtxFreeDebugFile(p->obj.ctx,&p->mf1p);
|
|
cmCtxFreeDebugFile(p->obj.ctx,&p->mf2p);
|
|
}
|
|
*/
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmTunedHpcpExec( cmHpcp* p, const cmComplexR_t* cqp, unsigned cqn )
|
|
{
|
|
assert( cqn == p->constQBinCnt );
|
|
|
|
// if there is no space to store the output then do nothing
|
|
if( p->frameIdx >= p->frameCnt )
|
|
return cmOkRC;
|
|
|
|
unsigned octCnt = (unsigned)floor(p->constQBinCnt / p->binsPerOctave);
|
|
unsigned i;
|
|
cmReal_t hpcpV[ p->binsPerOctave + 2 ];
|
|
unsigned idxV[ p->binsPerOctave ];
|
|
unsigned binsPerSt = p->binsPerOctave / kStPerOctave;
|
|
|
|
|
|
// Notice that the first and last elements of p->hpcp are reserved for
|
|
// use in producing the appeareance of circularity for the peak picking
|
|
// algorithm. The cmtual hpcp[] data begins on the index 1 (not 0) and
|
|
// ends on p->binsPerOctave (not p->binsPerOctave-1).
|
|
|
|
// sum the constQBinCnt constant Q bins into binsPerOctave bins to form the HPCP
|
|
for(i=0; i<p->binsPerOctave; ++i)
|
|
{
|
|
cmReal_t sum = 0;
|
|
const cmComplexR_t* sbp = cqp + i;
|
|
const cmComplexR_t* sep = cqp + (octCnt * p->binsPerOctave);
|
|
|
|
for(; sbp < sep; sbp += p->binsPerOctave)
|
|
sum += cmCabsR(*sbp);
|
|
|
|
hpcpV[i+1] = sum;
|
|
}
|
|
|
|
// shift the lowest ST center bin to (binsPerSt+1)/2 such that an equal number of
|
|
// flat and sharp bins are above an below it
|
|
int rotateCnt = ((binsPerSt+1)/2) - 1;
|
|
|
|
// shift pitch class C to the lowest semitone boundary
|
|
rotateCnt -= ( 48-(int)p->cqMinMidiPitch) * binsPerSt;
|
|
|
|
// perform the shift
|
|
cmVOR_Rotate(hpcpV+1, p->binsPerOctave, rotateCnt);
|
|
|
|
// duplicate the first and last bin to produce circularity in the hpcp
|
|
hpcpV[0] = hpcpV[ p->binsPerOctave ];
|
|
hpcpV[ p->binsPerOctave+1 ] = hpcpV[1];
|
|
|
|
// locate the indexes of the positive peaks in the hpcp
|
|
unsigned pkN = cmVOR_PeakIndexes( idxV, p->binsPerOctave, hpcpV, p->binsPerOctave, 0 );
|
|
|
|
// Convert the peak indexes to values in the range 0 to binsPerSet-1
|
|
// If stPerBin == 3 : 0=flat 1=in tune 2=sharp
|
|
cmVOU_Mod( idxV, pkN, binsPerSt );
|
|
|
|
// Form a histogram to keep count of the number of flat,in-tune and sharp peaks
|
|
cmVOU_Hist( p->histV, binsPerSt, idxV, pkN );
|
|
|
|
// store the hpcpV[] to the row p->hpcpM[p->frameIdx,:]
|
|
cmVOR_CopyN( p->hpcpM + p->frameIdx, p->binsPerOctave, p->frameCnt, hpcpV+1, 1 );
|
|
|
|
// write the hpcp debug file
|
|
if( p->mf0p != NULL )
|
|
cmMtxFileRealExecN( p->mf0p, p->hpcpM + p->frameIdx, p->binsPerOctave, p->frameCnt );
|
|
|
|
p->frameIdx++;
|
|
|
|
return cmOkRC;
|
|
|
|
}
|
|
|
|
cmRC_t cmTunedHpcpTuneAndFilter( cmHpcp* p)
|
|
{
|
|
// note: p->frameIdx now holds the cmtual count of frames in p->hpcpA[].
|
|
// p->frameCnt holds the allocated count of frames in p->hpcpA[].
|
|
|
|
unsigned i,j;
|
|
|
|
// filter each column of hpcpA[] into each row of fhpcpA[]
|
|
for(i=0; i<p->binsPerOctave; ++i)
|
|
{
|
|
cmVOR_MedianFilt( p->hpcpM + (i * p->frameCnt), p->frameIdx, p->medFiltOrder, p->fhpcpM + i, p->binsPerOctave );
|
|
|
|
// write the fhpcp[i,:] to the debug file
|
|
if( p->mf1p != NULL )
|
|
cmMtxFileRealExecN( p->mf1p, p->fhpcpM + i, p->frameIdx, p->binsPerOctave );
|
|
|
|
}
|
|
|
|
unsigned binsPerSt = p->histN;
|
|
|
|
assert( (binsPerSt > 0) && (cmIsOddU(binsPerSt)) );
|
|
|
|
|
|
unsigned maxIdx = cmVOU_MaxIndex(p->histV,binsPerSt,1);
|
|
int tuneShift = -(maxIdx - ((binsPerSt+1)/2));
|
|
cmReal_t gaussWndV[ binsPerSt ];
|
|
|
|
// generate a gaussian window
|
|
cmVOR_GaussWin( gaussWndV, binsPerSt, 2.5 );
|
|
|
|
// Rotate the window to apply tuning via the weighted sum operation below
|
|
// (the result will be equivalent to rotating p->fhpcpM[] prior to reducing the )
|
|
cmVOR_Rotate(gaussWndV, binsPerSt, tuneShift);
|
|
|
|
// zero the meanV[] before summing into it
|
|
cmVOR_Fill(p->meanV,kStPerOctave,0);
|
|
|
|
// for each frame
|
|
for(i=0; i<p->frameIdx; ++i)
|
|
{
|
|
// for each semitone
|
|
for(j=0; j<kStPerOctave; ++j)
|
|
{
|
|
// reduce each semitone to a single value by forming a weighted sum of all the assoc'd bins
|
|
cmReal_t sum = cmVOR_MultSumVV( gaussWndV, p->fhpcpM + (i*p->binsPerOctave) + (j*binsPerSt), binsPerSt );
|
|
|
|
// store time-series output to the ith column
|
|
p->outM[ (i*kStPerOctave) + j ] = sum;
|
|
|
|
// calc the sum of all chroma values in bin j.
|
|
p->meanV[ j ] += sum;
|
|
}
|
|
|
|
// write the chroma debug file
|
|
if( p->mf2p != NULL )
|
|
cmMtxFileRealExec( p->mf2p, p->outM + (i*kStPerOctave), kStPerOctave );
|
|
|
|
}
|
|
|
|
// form the chroma mean from the sum calc'd above
|
|
cmVOR_DivVS( p->meanV, kStPerOctave, p->frameIdx );
|
|
|
|
// variance
|
|
for(j=0; j<kStPerOctave; ++j)
|
|
p->varV[j] = cmVOR_VarianceN( p->outM + j, p->frameIdx, kStPerOctave, p->meanV + j );
|
|
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
/*
|
|
cmStatsProc* cmStatsProcAlloc( cmCtx* c, cmStatsProc* p, unsigned wndEleCnt, unsigned flags )
|
|
{
|
|
cmStatsProc* op = cmObjAlloc(cmStatsProc,c,p);
|
|
|
|
if( wndEleCnt > 0 )
|
|
if( cmStatsProcInit(op, wndEleCnt, flags ) != cmOkRC )
|
|
cmStatsProcFree(&op);
|
|
|
|
if( op != NULL )
|
|
cmCtxSetStatsProc(c,op);
|
|
|
|
return op;
|
|
}
|
|
|
|
|
|
cmRC_t cmStatsProcFree( cmStatsProc** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmStatsProc* p = *pp;
|
|
|
|
if( pp == NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmStatsProcFinal(p) ) != cmOkRC )
|
|
return rc;
|
|
|
|
cmCtxSetStatsProc(p->obj.ctx,NULL); // complement to cmSetStatsProc() in cmStatsProcAlloc()
|
|
|
|
cmMemPtrFree(&p->regArrayV);
|
|
cmMemPtrFree(&p->m);
|
|
cmMemPtrFree(&p->sumV);
|
|
cmMemPtrFree(&p->meanV);
|
|
cmMemPtrFree(&p->varV);
|
|
cmObjFree(pp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmStatsProcInit( cmStatsProc* p, unsigned wndEleCnt, unsigned flags )
|
|
{
|
|
cmRC_t rc;
|
|
|
|
if((rc = cmStatsProcFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->wndEleCnt = wndEleCnt;
|
|
p->dimCnt = 0;
|
|
p->curIdx = 0;
|
|
p->curCnt = 1;
|
|
p->flags = flags;
|
|
p->execCnt = 0;
|
|
p->regArrayN = 0;
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"statsProc");
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmStatsProcFinal( cmStatsProc* p )
|
|
{
|
|
if( p != NULL )
|
|
cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
unsigned cmStatsProcReg( cmStatsProc* p, unsigned featId, unsigned featEleCnt )
|
|
{
|
|
cmStatsProcRecd r;
|
|
|
|
r.featId = featId;
|
|
r.idx = p->dimCnt;
|
|
r.cnt = featEleCnt;
|
|
|
|
p->dimCnt += featEleCnt;
|
|
|
|
//unsigned i;
|
|
// printf("B:\n");
|
|
//for(i=0; i<p->regArrayN; ++i)
|
|
// printf("fid:%i idx:%i cnt:%i : fid:%i cnt:%i\n", p->regArrayV[i].featId,p->regArrayV[i].idx,p->regArrayV[i].cnt,featId,featEleCnt);
|
|
//printf("\nA:\n");
|
|
|
|
p->regArrayV = cmMemResizePZ( cmStatsProcRecd, p->regArrayV, p->regArrayN+1 );
|
|
p->regArrayV[ p->regArrayN ] = r;
|
|
p->regArrayN++;
|
|
|
|
|
|
//for(i=0; i<p->regArrayN; ++i)
|
|
// printf("fid:%i idx:%i cnt:%i : fid:%i cnt:%i\n", p->regArrayV[i].featId,p->regArrayV[i].idx,p->regArrayV[i].cnt,featId,featEleCnt);
|
|
//printf("\n");
|
|
|
|
return p->regArrayN-1; // return the index of the new reg recd
|
|
}
|
|
|
|
const cmStatsProcRecd* cmStatsProcRecdPtr( cmStatsProc* p, unsigned regId )
|
|
{
|
|
assert( regId < p->regArrayN );
|
|
return p->regArrayV + regId;
|
|
}
|
|
|
|
cmRC_t cmStatsProcExecD( cmStatsProc* p, unsigned regId, const double v[], unsigned vCnt )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
|
|
// on the first pass allcoate the storage buffer (m) and vectors (sumV,meanV and varV)
|
|
if( p->execCnt == 0 )
|
|
{
|
|
p->sumV = cmMemResizeZ( double, p->sumV, p->dimCnt);
|
|
p->meanV = cmMemResizeZ( double, p->meanV, p->dimCnt );
|
|
p->varV = cmMemResizeZ( double, p->varV, p->dimCnt );
|
|
p->m = cmMemResizeZ( double, p->m, p->dimCnt * p->wndEleCnt );
|
|
}
|
|
|
|
// if the storage matrix is full
|
|
if( p->curIdx == p->wndEleCnt )
|
|
return rc;
|
|
|
|
// get the pointer to this data source reg recd
|
|
assert( regId < p->regArrayN);
|
|
cmStatsProcRecd* r = p->regArrayV + regId;
|
|
|
|
// the dimensionality of the incoming data must be <= the registered dimensionality
|
|
assert( r->cnt <= vCnt );
|
|
|
|
unsigned dimIdx = r->idx;
|
|
bool updateFl = cmIsFlag(p->flags,kUpdateOnExecStatProcFl);
|
|
double* sbp = p->sumV + dimIdx; // sum base ptr
|
|
|
|
// mbp point to a segment (mbp[vCnt]) in column p->curIdx
|
|
double* mbp = p->m + (p->curIdx * p->dimCnt) + dimIdx; // mem col base ptr
|
|
|
|
const double* mep = p->m + p->dimCnt * p->wndEleCnt;
|
|
|
|
// decr the current col segment from the sum
|
|
if( updateFl )
|
|
cmVOD_SubVV( sbp, vCnt, mbp );
|
|
|
|
assert( p->m <= mbp && mbp < mep && p->m <= mbp+vCnt && mbp+vCnt <= mep );
|
|
|
|
// copy in the incoming values to mem col segment
|
|
cmVOD_Copy( mbp, vCnt, v );
|
|
|
|
if( updateFl )
|
|
{
|
|
// incr the sum from the incoming value
|
|
cmVOD_AddVV( sbp, vCnt, mbp );
|
|
|
|
// use the new sum to compute new mean values
|
|
cmVOD_DivVVS( p->meanV + dimIdx, vCnt, sbp, p->curCnt );
|
|
|
|
|
|
// update the variance - cmross each row
|
|
unsigned di;
|
|
|
|
for(di=dimIdx; di<dimIdx+vCnt; ++di )
|
|
p->varV[di] = cmVOD_VarianceN( p->m + dimIdx, p->curCnt, p->dimCnt, p->meanV + dimIdx );
|
|
}
|
|
|
|
++p->execCnt;
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
cmRC_t cmStatsProcExecF( cmStatsProc* p, unsigned regId, const float v[], unsigned vCnt )
|
|
{
|
|
double dv[ vCnt ];
|
|
cmVOD_CopyF(dv,vCnt,v);
|
|
cmStatsProcExecD(p,regId,dv,vCnt);
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmStatsProcCalc(cmStatsProc* p )
|
|
{
|
|
unsigned colCnt = cmMin(p->curCnt,p->wndEleCnt);
|
|
unsigned i = 0;
|
|
|
|
cmVOD_Fill(p->sumV,p->dimCnt,0);
|
|
|
|
// sum the ith col of p->m[] into p->sumV[i]
|
|
for(; i<colCnt; ++i)
|
|
{
|
|
cmVOD_AddVV( p->sumV, p->dimCnt, p->m + (i * p->dimCnt) );
|
|
if( p->mfp != NULL )
|
|
cmMtxFileDoubleExec( p->mfp, p->sumV, p->dimCnt, 1 );
|
|
}
|
|
|
|
// calc the mean of each row
|
|
cmVOD_DivVVS( p->meanV, p->dimCnt, p->sumV, colCnt );
|
|
|
|
// calc the variance cmross each row
|
|
for(i=0; i<p->dimCnt; ++i)
|
|
p->varV[i] = cmVOD_VarianceN(p->m + i, colCnt, p->dimCnt, p->meanV + i );
|
|
|
|
return cmOkRC;
|
|
|
|
}
|
|
|
|
cmRC_t cmStatsProcAdvance( cmStatsProc* p )
|
|
{
|
|
++p->curIdx;
|
|
if( p->curIdx > p->wndEleCnt )
|
|
p->curIdx = 0;
|
|
|
|
p->curCnt = cmMin(p->curCnt+1,p->wndEleCnt);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
void cmStatsProcTest( cmVReportFuncPtr_t vReportFunc )
|
|
{
|
|
enum
|
|
{
|
|
wndEleCnt = 7,
|
|
dDimCnt = 3,
|
|
fDimCnt = 2,
|
|
dimCnt = dDimCnt + fDimCnt,
|
|
|
|
kTypeId0 = 0,
|
|
kTypeId1 = 1
|
|
};
|
|
|
|
unsigned flags = 0;
|
|
unsigned i;
|
|
|
|
double dd[ dDimCnt * wndEleCnt ] =
|
|
{
|
|
0, 1, 2,
|
|
3, 4, 5,
|
|
6, 7, 8,
|
|
9, 10, 11,
|
|
12, 13, 14,
|
|
15, 16, 17,
|
|
18, 19, 20
|
|
};
|
|
|
|
float fd[ 14 ] =
|
|
{
|
|
0, 1,
|
|
2, 3,
|
|
4, 5,
|
|
6, 7,
|
|
8, 9,
|
|
10, 11,
|
|
12, 13
|
|
};
|
|
|
|
cmCtx c;
|
|
cmCtxInit(&c, vReportFunc, vReportFunc, NULL );
|
|
cmStatsProc* p = cmStatsProcAlloc( &c, NULL, wndEleCnt, flags );
|
|
|
|
unsigned regId0 = cmStatsProcReg( p, kTypeId0, dDimCnt );
|
|
unsigned regId1 = cmStatsProcReg( p, kTypeId1, fDimCnt );
|
|
|
|
|
|
for(i=0; i<wndEleCnt; ++i)
|
|
{
|
|
cmStatsProcExecD( p, regId0, dd + (i*dDimCnt), dDimCnt );
|
|
cmStatsProcExecF( p, regId1, fd + (i*fDimCnt), fDimCnt );
|
|
cmStatsProcAdvance(p);
|
|
}
|
|
|
|
cmStatsProcCalc( p);
|
|
cmVOD_PrintE( vReportFunc, 1, p->dimCnt, p->meanV );
|
|
cmVOD_PrintE( vReportFunc, 1, p->dimCnt, p->varV );
|
|
|
|
cmStatsProcFree(&p);
|
|
|
|
}
|
|
*/
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmBeatHist* cmBeatHistAlloc( cmCtx* c, cmBeatHist* ap, unsigned frmCnt )
|
|
{
|
|
cmBeatHist* p = cmObjAlloc(cmBeatHist,c,ap);
|
|
|
|
p->fft = cmFftAllocRR(c,NULL,NULL,0,kToPolarFftFl);
|
|
p->ifft = cmIFftAllocRR(c,NULL,0);
|
|
|
|
if( frmCnt > 0 )
|
|
if( cmBeatHistInit(p,frmCnt) != cmOkRC )
|
|
cmBeatHistFree(&p);
|
|
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmBeatHistFree( cmBeatHist** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmBeatHist* p = *pp;
|
|
|
|
if( pp == NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmBeatHistFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->m);
|
|
cmMemPtrFree(&p->H);
|
|
cmMemPtrFree(&p->df);
|
|
cmMemPtrFree(&p->fdf);
|
|
cmMemPtrFree(&p->histV);
|
|
cmFftFreeRR(&p->fft);
|
|
cmIFftFreeRR(&p->ifft);
|
|
|
|
cmObjFree(&p);
|
|
return rc;
|
|
|
|
}
|
|
|
|
void _cmBeatHistInitH( cmReal_t* H, unsigned hrn, unsigned hcn, unsigned ri, unsigned c0, unsigned c1 )
|
|
{
|
|
unsigned ci;
|
|
for(ci=c0; ci<=c1; ++ci)
|
|
H[ (ci*hrn) + ri ] = 1;
|
|
}
|
|
|
|
cmRC_t cmBeatHistInit( cmBeatHist* p, unsigned frmCnt )
|
|
{
|
|
cmRC_t rc;
|
|
unsigned i,j,k;
|
|
enum { kLagFact = 4, kHistBinCnt=15, kHColCnt=128 };
|
|
|
|
if((rc = cmBeatHistFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
p->frmCnt = frmCnt;
|
|
p->maxLagCnt = (unsigned)floor(p->frmCnt / kLagFact);
|
|
p->histBinCnt= kHistBinCnt;
|
|
p->hColCnt = kHColCnt;
|
|
p->dfi = 0;
|
|
|
|
unsigned cfbMemN = p->frmCnt * p->maxLagCnt;
|
|
unsigned hMemN = p->histBinCnt * kHColCnt;
|
|
|
|
//cmArrayResizeVZ(p->obj.ctx,&p->cfbMem, cmReal_t, &p->m, cfbMemN, &p->H, hMemN, NULL);
|
|
p->m = cmMemResizeZ( cmReal_t, p->m, cfbMemN );
|
|
p->H = cmMemResizeZ( cmReal_t, p->H, hMemN );
|
|
|
|
//p->df = cmArrayResizeZ(c,&p->dfMem, 2*p->frmCnt + kHistBinCnt, cmReal_t);
|
|
//p->fdf = p->df + p->frmCnt;
|
|
//p->histV = p->fdf + p->frmCnt;
|
|
//cmArrayResizeVZ(p->obj.ctx, &p->dfMem, cmReal_t, &p->df, p->frmCnt, &p->fdf, p->frmCnt, &p->histV, kHistBinCnt, NULL );
|
|
p->df = cmMemResizeZ( cmReal_t, p->df, p->frmCnt );
|
|
p->fdf = cmMemResizeZ( cmReal_t, p->fdf, p->frmCnt );
|
|
p->histV = cmMemResizeZ( cmReal_t, p->histV, kHistBinCnt );
|
|
|
|
cmFftInitRR( p->fft,NULL,cmNextPowerOfTwo(2*frmCnt),kToPolarFftFl);
|
|
cmIFftInitRR(p->ifft,p->fft->binCnt);
|
|
|
|
// initialize H
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 0, 103, 127 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 1, 86, 102 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 2, 73, 85 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 3, 64, 72 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 4, 57, 63 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 5, 51, 56 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 6, 46, 50 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 7, 43, 45 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 8, 39, 42 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 9, 36, 38 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 10,32, 35 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 11,28, 31 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 12,25, 27 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 13,21, 24 );
|
|
_cmBeatHistInitH( p->H, p->histBinCnt, p->hColCnt, 14,11, 20 );
|
|
|
|
// for each column
|
|
for(i=0; i<p->maxLagCnt; ++i)
|
|
{
|
|
|
|
// for each lag group
|
|
for(j=0; j<kLagFact; ++j)
|
|
{
|
|
for(k=0; k<=2*j; ++k)
|
|
{
|
|
unsigned idx = (i*p->frmCnt) + (i*j) + i + k;
|
|
if( idx < cfbMemN )
|
|
p->m[ idx ] = 1.0/(2*j+1);
|
|
}
|
|
}
|
|
}
|
|
|
|
double g[ p->maxLagCnt ];
|
|
double g_max = 0;
|
|
double b = 43;
|
|
|
|
b = b*b;
|
|
for(i=0; i<p->maxLagCnt; ++i)
|
|
{
|
|
double n = i+1;
|
|
g[i] = n/b * exp(-(n*n) / (2*b));
|
|
if( g[i] > g_max )
|
|
g_max = g[i];
|
|
}
|
|
|
|
// normalize g[]
|
|
cmVOD_DivVS( g, p->maxLagCnt, g_max );
|
|
|
|
// for each column of p->m[]
|
|
for(i=0; i<p->maxLagCnt; ++i)
|
|
{
|
|
double gg = g[i];
|
|
|
|
k = i*p->frmCnt;
|
|
|
|
for(j=0; j<p->frmCnt; ++j)
|
|
p->m[ k + j ] *= gg;
|
|
}
|
|
|
|
//p->mfp = cmCtxAllocDebugFile(p->obj.ctx,"beatHist");
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmBeatHistFinal( cmBeatHist* p )
|
|
{
|
|
//if( p != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmBeatHistExec( cmBeatHist* p, cmSample_t df )
|
|
{
|
|
if( p->dfi < p->frmCnt )
|
|
p->df[p->dfi++] = df;
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmBeatHistCalc( cmBeatHist* p )
|
|
{
|
|
unsigned i;
|
|
|
|
// df -= mean(df)
|
|
cmVOR_SubVS(p->df,p->frmCnt,cmVOR_Mean(p->df,p->frmCnt));
|
|
|
|
//cmPlotLineR( "dfm", NULL, p->df, NULL, p->frmCnt, NULL, kSolidPlotLineId );
|
|
|
|
// take alpha norm of df
|
|
double alpha = 9;
|
|
cmVOR_DivVS(p->df,p->frmCnt, cmVOR_AlphaNorm(p->df,p->frmCnt,alpha));
|
|
|
|
|
|
//cmPlotLineS( "dfd", NULL, p->df, NULL, p->frmCnt, NULL, kSolidPlotLineId );
|
|
|
|
// low pass forward/backward filter df[] into fdf[]
|
|
cmReal_t b[] = {0.1600, 0.3200, 0.1600};
|
|
unsigned bn = sizeof(b)/sizeof(b[0]);
|
|
cmReal_t a[] = {1.0000, -0.5949, 0.2348};
|
|
unsigned an = sizeof(a)/sizeof(a[0]);
|
|
cmFilterFilterR(p->obj.ctx,b,bn,a,an,p->df,p->frmCnt,p->fdf,p->frmCnt);
|
|
|
|
//cmPlotLineS( "fdf", NULL, p->fdf, NULL, p->frmCnt, NULL, kSolidPlotLineId );
|
|
|
|
// median filter to low-passed filtered fdf[] into df[]
|
|
cmVOR_FnThresh(p->fdf,p->frmCnt,16,p->df,1,NULL);
|
|
|
|
// subtract med filtered signal from the low pa1ssed signal.
|
|
// fdf[] -= df[];
|
|
cmVOR_SubVV(p->fdf,p->frmCnt,p->df);
|
|
|
|
// half-wave rectify fdf[] = set all negative values in fdf[] to zero.
|
|
cmVOR_HalfWaveRectify(p->fdf,p->frmCnt,p->fdf);
|
|
|
|
//cmPlotLineS( "meddf", NULL, p->fdf, NULL, p->frmCnt, NULL, kSolidPlotLineId );
|
|
|
|
// take FT of fdf[]
|
|
cmFftExecRR(p->fft,p->fdf,p->frmCnt);
|
|
|
|
// square FT magn.
|
|
cmVOR_PowVS(p->fft->magV,p->fft->binCnt,2);
|
|
|
|
//cmPlotLineS( "mag", NULL, p->fft->magV, NULL, p->fft->binCnt, NULL, kSolidPlotLineId );
|
|
|
|
// take the IFFT of the squared magnitude vector.
|
|
cmVOR_Fill(p->fft->phsV,p->fft->binCnt,0);
|
|
cmIFftExecRectRR(p->ifft,p->fft->magV,p->fft->phsV);
|
|
|
|
// Matlab automatically provides this scaling as part of the IFFT.
|
|
cmVOR_DivVS(p->ifft->outV,p->ifft->outN,p->ifft->outN);
|
|
|
|
// remove bias for short periods from CMF
|
|
for(i=0; i<p->frmCnt; ++i)
|
|
p->ifft->outV[i] /= (p->frmCnt - i);
|
|
|
|
//cmPlotLineS( "cm", NULL, p->ifft->outV, NULL, p->frmCnt, NULL, kSolidPlotLineId );
|
|
|
|
// apply comb filter to the CMF and store result in df[maxLagCnt]
|
|
cmVOR_MultVMtV(p->df,p->maxLagCnt,p->m,p->frmCnt,p->ifft->outV);
|
|
|
|
//acPlotLineS( "cfb", NULL, p->df, NULL, p->maxLagCnt, NULL, kSolidPlotLineId );
|
|
|
|
//acVOR_Print(p->obj.err.rpt,1,p->maxLagCnt,p->df);
|
|
|
|
assert( p->maxLagCnt == p->hColCnt );
|
|
cmVOR_MultVMV(p->histV,p->histBinCnt,p->H,p->hColCnt,p->df);
|
|
|
|
cmReal_t bins[] = { 25, 17, 13, 9, 7, 6, 5, 3, 4, 3, 4, 4, 3, 4, 10};
|
|
|
|
cmVOR_DivVV( p->histV, p->histBinCnt, bins );
|
|
|
|
//cmPlotLineS( "cfb", NULL, p->histV, NULL, p->histBinCnt, NULL, kSolidPlotLineId );
|
|
|
|
if( p->mfp != NULL )
|
|
cmMtxFileRealExec( p->mfp, p->histV, p->histBinCnt );
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmGmm_t* cmGmmAlloc( cmCtx* c, cmGmm_t* ap, unsigned K, unsigned D, const cmReal_t* gM, const cmReal_t* uM, const cmReal_t* sMM, unsigned uflags )
|
|
{
|
|
cmGmm_t* p = cmObjAlloc( cmGmm_t, c, ap );
|
|
|
|
if( K > 0 && D > 0 )
|
|
if( cmGmmInit(p,K,D,gM,uM,sMM,uflags) != cmOkRC )
|
|
cmGmmFree(&p);
|
|
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmGmmFree( cmGmm_t** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmGmm_t* p = *pp;
|
|
|
|
if( pp == NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmGmmFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->gV);
|
|
cmMemPtrFree(&p->uM);
|
|
cmMemPtrFree(&p->sMM);
|
|
cmMemPtrFree(&p->isMM);
|
|
cmMemPtrFree(&p->uMM);
|
|
cmMemPtrFree(&p->logDetV);
|
|
cmMemPtrFree(&p->t);
|
|
cmObjFree(pp);
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t _cmGmmUpdateCovar( cmGmm_t* p, const cmReal_t* sMM )
|
|
{
|
|
unsigned i;
|
|
|
|
if( sMM == NULL )
|
|
return cmOkRC;
|
|
|
|
unsigned De2 = p->D*p->D;
|
|
unsigned KDe2 = p->K*De2;
|
|
|
|
cmVOR_Copy(p->sMM, KDe2, sMM);
|
|
cmVOR_Copy(p->isMM,KDe2, sMM);
|
|
cmVOR_Copy(p->uMM, KDe2, sMM);
|
|
|
|
//if( cmIsFlag(p->uflags,cmGmmCovarNoProcFl) )
|
|
// return cmOkRC;
|
|
|
|
// for each component
|
|
for(i=0; i<p->K; ++i)
|
|
{
|
|
cmReal_t* is = p->isMM + i*De2;
|
|
cmReal_t* u = p->uMM + i*De2;
|
|
cmReal_t* r;
|
|
|
|
// if the covariance matrix is diagnal
|
|
if( cmIsFlag(p->uflags,cmGmmDiagFl))
|
|
{
|
|
p->logDetV[i] = cmVOR_LogDetDiagM(is,p->D); // calc the det. of diag. covar. mtx
|
|
r = cmVOR_InvDiagM(is,p->D); // calc the inverse in place
|
|
}
|
|
else
|
|
{
|
|
p->logDetV[i] = cmVOR_LogDetM(is,p->D); // calc the det. of covar mtx
|
|
r = cmVOR_InvM(is,p->D); // calc the inverse in place
|
|
}
|
|
|
|
if( fabs(p->logDetV[i]) < 1e-20 )
|
|
{
|
|
cmCtxPrint(p->obj.ctx,"%i\n",i);
|
|
cmVOR_PrintLE("DANGER SM:\n",p->obj.err.rpt,p->D,p->D,p->sMM);
|
|
}
|
|
|
|
|
|
if( cmVOR_CholZ(u,p->D) == NULL )
|
|
{
|
|
return cmCtxRtCondition(&p->obj, cmSingularMtxRC, "A singular covariance matrix (Cholesky factorization failed.) was encountered in _cmGmmUpdateCovar().");
|
|
}
|
|
|
|
if( p->logDetV[i] == 0 )
|
|
{
|
|
cmGmmPrint(p,true);
|
|
return cmCtxRtCondition(&p->obj, cmSingularMtxRC, "A singular covariance matrix (det==0) was encountered in _cmGmmUpdateCovar().");
|
|
}
|
|
|
|
if( r == NULL )
|
|
{
|
|
//cmCtxPrint(c,"%i\n",i);
|
|
//cmVOR_PrintLE("DANGER SM:\n",p->obj.err.rpt,p->D,p->D,p->sMM);
|
|
|
|
return cmCtxRtCondition(&p->obj, cmSingularMtxRC, "A singular covariance matrix (inversion failed) was encountered in _cmGmmUpdateCovar().");
|
|
}
|
|
|
|
}
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmGmmInit( cmGmm_t* p, unsigned K, unsigned D, const cmReal_t* gV, const cmReal_t* uM, const cmReal_t* sMM, unsigned uflags )
|
|
{
|
|
cmRC_t rc;
|
|
if((rc = cmGmmFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
// gM[K] uM[DK] sMM[DDK] isMM[DDK]+ uMM[DDK] logDetV[K] t[DD] fact[K]
|
|
/*
|
|
unsigned n = K + (D*K) + (D*D*K) + (D*D*K) + (D*D*K) + K + (D*D) + K;
|
|
p->gV = cmArrayResizeZ(c,&p->memA, n, cmReal_t );
|
|
p->uM = p->gV + K;
|
|
p->sMM = p->uM + (D*K);
|
|
p->isMM = p->sMM + (D*D*K);
|
|
p->uMM = p->isMM + (D*D*K);
|
|
p->logDetV = p->uMM + (D*D*K);
|
|
p->t = p->logDetV + K;
|
|
*/
|
|
|
|
//cmArrayResizeVZ(c, &p->memA, cmReal_t, &p->gV,K, &p->uM,D*K, &p->sMM,D*D*K,
|
|
//&p->isMM,D*D*K, &p->uMM,D*D*K, &p->logDetV,K, &p->t,D*D, NULL );
|
|
|
|
p->gV = cmMemResizeZ( cmReal_t, p->gV, K );
|
|
p->uM = cmMemResizeZ( cmReal_t, p->uM, D*K);
|
|
p->sMM = cmMemResizeZ( cmReal_t, p->sMM, D*D*K);
|
|
p->isMM = cmMemResizeZ( cmReal_t, p->isMM, D*D*K);
|
|
p->uMM = cmMemResizeZ( cmReal_t, p->uMM, D*D*K);
|
|
p->logDetV = cmMemResizeZ( cmReal_t, p->logDetV, K);
|
|
p->t = cmMemResizeZ( cmReal_t, p->t, D*D );
|
|
|
|
p->K = K;
|
|
p->D = D;
|
|
p->uflags = uflags;
|
|
|
|
if( gV != NULL )
|
|
cmVOR_Copy(p->gV,K,gV);
|
|
|
|
if( uM != NULL )
|
|
cmVOR_Copy(p->uM,D*K,uM);
|
|
|
|
return _cmGmmUpdateCovar(p,sMM );
|
|
|
|
}
|
|
|
|
|
|
cmRC_t cmGmmFinal( cmGmm_t* p )
|
|
{ return cmOkRC; }
|
|
|
|
|
|
typedef struct
|
|
{
|
|
cmGmm_t* p;
|
|
const cmReal_t* xM;
|
|
unsigned colCnt;
|
|
} _cmGmmRdFuncData_t;
|
|
|
|
const cmReal_t* _cmGmmReadFunc( void* userPtr, unsigned colIdx )
|
|
{
|
|
assert(colIdx < ((const _cmGmmRdFuncData_t*)userPtr)->colCnt);
|
|
return ((const _cmGmmRdFuncData_t*)userPtr)->xM + (colIdx * ((const _cmGmmRdFuncData_t*)userPtr)->p->D);
|
|
}
|
|
|
|
|
|
// xM[D,xN]
|
|
// yV[xN]
|
|
// yM[xN,K]
|
|
cmRC_t cmGmmEval( cmGmm_t* p, const cmReal_t* xM, unsigned xN, cmReal_t* yV, cmReal_t* yM )
|
|
{
|
|
_cmGmmRdFuncData_t r;
|
|
r.colCnt = xN;
|
|
r.p = p;
|
|
r.xM = xM;
|
|
|
|
return cmGmmEval2(p,_cmGmmReadFunc,&r,xN,yV,yM);
|
|
}
|
|
|
|
|
|
cmRC_t cmGmmEval2( cmGmm_t* p, cmGmmReadFunc_t readFunc, void* userFuncPtr, unsigned xN, cmReal_t* yV, cmReal_t* yM)
|
|
{
|
|
cmReal_t tV[xN];
|
|
unsigned k;
|
|
|
|
//cmVOR_PrintL("cV: ",p->obj.err.rpt, 1, p->K, p->gV);
|
|
//cmVOR_PrintL("uM:\n",p->obj.err.rpt, p->D, p->K, p->uM );
|
|
|
|
//
|
|
cmVOR_Fill(yV,xN,0);
|
|
|
|
// for each component PDF
|
|
for(k=0; k<p->K; k++)
|
|
{
|
|
const cmReal_t* meanV = p->uM + (k*p->D);
|
|
const cmReal_t* isM = p->isMM + (k*p->D*p->D);
|
|
cmReal_t* pV;
|
|
|
|
if( yM == NULL )
|
|
pV = tV;
|
|
else
|
|
pV = yM + (k*xN);
|
|
|
|
|
|
// evaluate the kth component PDF with xM[1:T]
|
|
|
|
//cmVOR_MultVarGaussPDF2( pV, xM, meanV, isM, p->logDetV[k], p->D, xN, cmIsFlag(p->uflags,cmGmmDiagFl) );
|
|
cmVOR_MultVarGaussPDF3( pV, readFunc, userFuncPtr, meanV, isM, p->logDetV[k], p->D, xN, cmIsFlag(p->uflags,cmGmmDiagFl) );
|
|
|
|
|
|
// apply the kth component weight
|
|
cmVOR_MultVS( pV, xN, p->gV[k] );
|
|
|
|
// sum the result into the output vector
|
|
cmVOR_AddVV( yV, xN, pV );
|
|
|
|
}
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
|
|
// Evaluate each component for a single data point
|
|
// xV[D] - observed data point
|
|
// yV[K] - output contains the evaluation for each component
|
|
cmRC_t cmGmmEval3( cmGmm_t* p, const cmReal_t* xV, cmReal_t* yV )
|
|
{
|
|
unsigned k;
|
|
|
|
for(k=0; k<p->K; ++k)
|
|
{
|
|
const cmReal_t* meanV = p->uM + (k*p->D);
|
|
const cmReal_t* isM = p->isMM + (k*p->D*p->D);
|
|
|
|
// evaluate the kth component PDF with xM[1:T]
|
|
cmVOR_MultVarGaussPDF2( yV + k, xV, meanV, isM, p->logDetV[k], p->D, 1, cmIsFlag(p->uflags,cmGmmDiagFl) );
|
|
|
|
// apply the kth component weight
|
|
yV[k] *= p->gV[k];
|
|
|
|
}
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmReal_t _cmGmmKmeansDistFunc( void* userPtr, const cmReal_t* v0, const cmReal_t* v1, unsigned vn )
|
|
{ return cmVOR_EuclidDistance(v0,v1,vn); }
|
|
|
|
cmRC_t cmGmmRandomize2( cmGmm_t* p, cmGmmReadFunc_t readFunc, void* funcUserPtr, unsigned xN, const cmReal_t* uM, const bool* roFlV )
|
|
{
|
|
unsigned k;
|
|
unsigned iV[ p->K ];
|
|
|
|
if( uM == NULL )
|
|
roFlV = NULL;
|
|
|
|
// randomize the mixture coefficients
|
|
cmVOR_Random( p->gV, p->K, 0.0, 1.0 );
|
|
cmVOR_NormalizeProbability(p->gV,p->K);
|
|
|
|
// fill iV with random integers between 0 and xN-1
|
|
cmVOU_Random( iV, p->K, xN-1 );
|
|
|
|
|
|
// for each component
|
|
for(k=0; k<p->K; ++k)
|
|
{
|
|
cmReal_t r[ p->D ];
|
|
|
|
// if this component's mean is not read-only
|
|
if( roFlV==NULL || roFlV[k]==false )
|
|
{
|
|
const cmReal_t* xV = NULL;
|
|
|
|
if( uM == NULL )
|
|
xV = readFunc( funcUserPtr, iV[k] ); // read a random frame index
|
|
else
|
|
xV = uM + (k*p->D); // select a user supplied mean vector
|
|
|
|
assert( xV != NULL );
|
|
|
|
// set the random feature vector as this components mean value
|
|
cmVOR_Copy(p->uM+(k*p->D),p->D,xV);
|
|
}
|
|
|
|
cmReal_t* sM = p->sMM+(k*p->D*p->D);
|
|
|
|
// create a random covariance mtx
|
|
if( cmIsFlag(p->uflags,cmGmmDiagFl) )
|
|
{
|
|
// create a random diag. covar mtx
|
|
cmVOR_Random(r,p->D,0.0,1.0);
|
|
cmVOR_Diag(sM,p->D,r);
|
|
}
|
|
else
|
|
{
|
|
// create a random symetric positive definite matrix
|
|
cmVOR_RandSymPosDef(sM,p->D,p->t);
|
|
}
|
|
}
|
|
|
|
unsigned* classIdxV = cmMemAllocZ(unsigned, xN );
|
|
|
|
// if some components have read-only mean's
|
|
if( uM != NULL && roFlV != NULL )
|
|
{
|
|
assert( xN >= p->K );
|
|
|
|
for(k=0; k<p->K; ++k)
|
|
classIdxV[k] = roFlV[k];
|
|
}
|
|
|
|
// use kmeans clustering to move the means closer to their center values
|
|
if( cmIsFlag( p->uflags, cmGmmSkipKmeansFl) == false )
|
|
cmVOR_Kmeans2( classIdxV, p->uM, p->K, readFunc, p->D, xN, funcUserPtr, _cmGmmKmeansDistFunc, NULL, -1, 0 );
|
|
|
|
cmMemPtrFree(&classIdxV);
|
|
|
|
return _cmGmmUpdateCovar(p,p->sMM);
|
|
|
|
}
|
|
|
|
cmRC_t cmGmmRandomize( cmGmm_t* p, const cmReal_t* xM, unsigned xN )
|
|
{
|
|
_cmGmmRdFuncData_t r;
|
|
r.colCnt = xN;
|
|
r.p = p;
|
|
r.xM = xM;
|
|
|
|
return cmGmmRandomize2(p,_cmGmmReadFunc,&r,xN,NULL,NULL);
|
|
}
|
|
|
|
|
|
// xM[D,xN]
|
|
cmRC_t cmGmmTrain( cmGmm_t* p, const cmReal_t* xM, unsigned xN, unsigned* iterCntPtr )
|
|
{
|
|
unsigned i,k;
|
|
cmRC_t rc;
|
|
|
|
if((rc = cmGmmRandomize(p,xM,xN)) != cmOkRC )
|
|
return rc;
|
|
|
|
//cmGmmPrint(c,p);
|
|
|
|
// wM[xN,K]
|
|
cmReal_t wM[ xN * p->K ]; // wM[N,K] soft assignment mtx
|
|
unsigned wV[ xN ]; // wV[N] hard assignment vector
|
|
unsigned stopCnt = 0;
|
|
unsigned curStopCnt = 0;
|
|
|
|
if( iterCntPtr != NULL )
|
|
{
|
|
stopCnt = *iterCntPtr;
|
|
*iterCntPtr = 0;
|
|
}
|
|
else
|
|
{
|
|
// BUG BUG BUG
|
|
// stopCnt is used uninitialized when iterCntPtr == NULL
|
|
assert( 0 );
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
|
|
//cmVOR_NormalizeProbability(p->gV,p->K);
|
|
|
|
cmCtxPrint(p->obj.ctx,"iter:%i --------------------------------------------\n",*iterCntPtr );
|
|
|
|
cmVOR_PrintL("uM:\n", p->obj.err.rpt, p->D, p->K, p->uM );
|
|
cmVOR_PrintL("gV:\n", p->obj.err.rpt, 1, p->K, p->gV );
|
|
|
|
//cmGmmPrint(c,p);
|
|
|
|
for(k=0; k<p->K; ++k)
|
|
{
|
|
cmReal_t* wp = wM + (k*xN);
|
|
|
|
// calc the prob that each data point in xM[] was generated by the kth gaussian
|
|
// and store as a column vector in wM[:,k]
|
|
cmVOR_MultVarGaussPDF2( wp, xM, p->uM + (k*p->D), p->isMM + (k*p->D*p->D), p->logDetV[k], p->D, xN, cmIsFlag(p->uflags,cmGmmDiagFl) );
|
|
|
|
|
|
// scale the prob by the gain coeff for gaussian k
|
|
cmVOR_MultVS( wp, xN, p->gV[k]);
|
|
|
|
}
|
|
|
|
|
|
|
|
//cmVOR_PrintL("wM:\n",p->obj.err.rpt,xN,p->K,wM);
|
|
|
|
bool doneFl = true;
|
|
for(i=0; i<xN; ++i)
|
|
{
|
|
|
|
// form a probability for the ith data point weights
|
|
cmVOR_NormalizeProbabilityN( wM + i, p->K, xN);
|
|
|
|
// select the cluster to which the ith data point is most likely to belong
|
|
unsigned mi = cmVOR_MaxIndex(wM + i, p->K, xN);
|
|
|
|
// if the ith data point changed clusters
|
|
if( mi != wV[i] )
|
|
{
|
|
doneFl = false;
|
|
wV[i] = mi;
|
|
}
|
|
}
|
|
|
|
curStopCnt = doneFl ? curStopCnt+1 : 0;
|
|
|
|
|
|
// if no data points changed owners then the clustering is complete
|
|
if( curStopCnt == stopCnt )
|
|
{
|
|
//cmVOU_PrintL("wV: ",p->obj.err.rpt,xN,1,wV);
|
|
break;
|
|
}
|
|
|
|
// for each cluster
|
|
for(k=0; k<p->K; ++k)
|
|
{
|
|
cmReal_t* uV = p->uM + (k*p->D); // meanV[k]
|
|
cmReal_t* sM = p->sMM + (k*p->D*p->D); // covarM[k]
|
|
|
|
cmReal_t sw = cmVOR_Sum(wM + (k*xN), xN );
|
|
|
|
|
|
// update the kth weight
|
|
p->gV[k] = sw / xN;
|
|
|
|
|
|
// form a sum of all data points weighted by their soft assignment to cluster k
|
|
cmReal_t sumV[p->D];
|
|
cmVOR_MultVMV( sumV, p->D, xM, xN, wM + k*xN );
|
|
|
|
// update the mean[k]
|
|
cmVOR_DivVVS(uV, p->D, sumV, sw );
|
|
|
|
|
|
// update the covar[k]
|
|
// sM += ( W(i,k) .* ((X(:,i) - uV) * (X(:,i) - uV)'));
|
|
cmVOR_Fill(sM,p->D*p->D,0);
|
|
for(i=0; i<xN; ++i)
|
|
{
|
|
cmReal_t duV[ p->D ];
|
|
|
|
cmVOR_SubVVV( duV, p->D, xM + (i*p->D), uV ); // duV[] = xM[:,i] - uV[];
|
|
cmVOR_MultMMM( p->t, p->D, p->D, duV, duV, 1 ); // t[,] = duV[] * duV[]'
|
|
cmVOR_MultVS( p->t, p->D*p->D, wM[ k * xN + i ]); // t[,] *= wM[i,k]
|
|
cmVOR_AddVV( sM, p->D*p->D, p->t ); // sM[,] += t[,];
|
|
}
|
|
|
|
cmVOR_DivVS( sM, p->D*p->D, sw ); // sM[,] ./ sw;
|
|
}
|
|
|
|
// update the inverted covar mtx and covar det.
|
|
if((rc = _cmGmmUpdateCovar(p,p->sMM )) != cmOkRC )
|
|
return rc;
|
|
|
|
if( iterCntPtr != NULL )
|
|
*iterCntPtr += 1;
|
|
}
|
|
return cmOkRC;
|
|
}
|
|
|
|
// xM[D,xN]
|
|
cmRC_t cmGmmTrain2( cmGmm_t* p, cmGmmReadFunc_t readFunc, void* userFuncPtr, unsigned xN, unsigned* iterCntPtr, const cmReal_t* uM, const bool* roFlV, int maxIterCnt )
|
|
{
|
|
unsigned i,j,k;
|
|
cmRC_t rc;
|
|
|
|
// if uM[] is not set then ignore roFlV[]
|
|
if( uM == NULL )
|
|
roFlV=NULL;
|
|
|
|
if((rc = cmGmmRandomize2(p,readFunc,userFuncPtr,xN,uM,roFlV)) != cmOkRC )
|
|
return rc;
|
|
|
|
//cmGmmPrint(c,p);
|
|
|
|
// wM[xN,K] soft assignment mtx
|
|
cmReal_t* wM = cmMemAlloc(cmReal_t, xN * p->K );
|
|
|
|
// wV[N] hard assignment vector
|
|
unsigned* wV = cmMemAlloc(unsigned, xN );
|
|
|
|
unsigned stopCnt = 0;
|
|
unsigned curStopCnt = 0;
|
|
|
|
if( iterCntPtr != NULL )
|
|
{
|
|
stopCnt = *iterCntPtr;
|
|
*iterCntPtr = 0;
|
|
}
|
|
else
|
|
{
|
|
// BUG BUG BUG
|
|
// stopCnt is used uninitialized when iterCntPtr == NULL
|
|
assert( 0 );
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
|
|
//cmCtxPrint(p->obj.ctx,"iter:%i --------------------------------------------\n",*iterCntPtr );
|
|
//cmVOR_PrintL("uM:\n", p->obj.err.rpt, p->D, p->K, p->uM );
|
|
cmVOR_PrintL("gV:\n", p->obj.err.rpt, 1, p->K, p->gV );
|
|
//cmGmmPrint(c,p);
|
|
|
|
for(k=0; k<p->K; ++k)
|
|
{
|
|
cmReal_t* wp = wM + (k*xN);
|
|
|
|
// calc the prob that each data point in xM[] was generated by the kth gaussian
|
|
// and store as a column vector in wM[:,k]
|
|
cmVOR_MultVarGaussPDF3( wp, readFunc, userFuncPtr, p->uM + (k*p->D), p->isMM + (k*p->D*p->D), p->logDetV[k], p->D, xN, cmIsFlag(p->uflags,cmGmmDiagFl) );
|
|
|
|
|
|
// scale the prob by the gain coeff for gaussian k
|
|
cmVOR_MultVS( wp, xN, p->gV[k]);
|
|
|
|
}
|
|
|
|
//cmVOR_PrintL("wM:\n",p->obj.err.rpt,xN,p->K,wM);
|
|
|
|
bool doneFl = true;
|
|
unsigned changeCnt = 0;
|
|
for(i=0; i<xN; ++i)
|
|
{
|
|
|
|
// form a probability for the ith data point weights
|
|
cmVOR_NormalizeProbabilityN( wM + i, p->K, xN);
|
|
|
|
// select the cluster to which the ith data point is most likely to belong
|
|
unsigned mi = cmVOR_MaxIndex(wM + i, p->K, xN);
|
|
|
|
// if the ith data point changed clusters
|
|
if( mi != wV[i] )
|
|
{
|
|
++changeCnt;
|
|
doneFl = false;
|
|
wV[i] = mi;
|
|
}
|
|
}
|
|
|
|
curStopCnt = doneFl ? curStopCnt+1 : 0;
|
|
|
|
printf("%i stopCnt:%i changeCnt:%i\n",*iterCntPtr,curStopCnt,changeCnt);
|
|
|
|
// if no data points changed owners then the clustering is complete
|
|
if( curStopCnt == stopCnt )
|
|
{
|
|
//cmVOU_PrintL("wV: ",p->obj.err.rpt,xN,1,wV);
|
|
break;
|
|
}
|
|
|
|
// if a maxIterCnt was given and the cur iter cnt exceeds the max iter cnt then stop
|
|
if( maxIterCnt>=1 && *iterCntPtr >= maxIterCnt )
|
|
break;
|
|
|
|
// form a sum of all data points weighted by their soft assignment to cluster k
|
|
// NOTE: cmGmmTrain() performs this step more efficiently because it use
|
|
// an LAPACK matrix multiply.
|
|
cmReal_t sumM[ p->D * p->K ];
|
|
|
|
cmVOR_Zero(sumM,p->D*p->K);
|
|
|
|
for(i=0; i<xN; ++i)
|
|
{
|
|
const cmReal_t* xV = readFunc( userFuncPtr, i );
|
|
|
|
assert( xV != NULL );
|
|
|
|
for(k=0; k<p->K; ++k)
|
|
{
|
|
cmReal_t weight = wM[ i + (k*xN)];
|
|
|
|
for(j=0; j<p->D; ++j)
|
|
sumM[ j + (k*p->D) ] += xV[j] * weight;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// for each cluster that is not marked as read-only
|
|
for(k=0; k<p->K; ++k)
|
|
{
|
|
cmReal_t* uV = p->uM + (k*p->D); // meanV[k]
|
|
cmReal_t* sM = p->sMM + (k*p->D*p->D); // covarM[k]
|
|
|
|
cmReal_t sw = cmVOR_Sum(wM + (k*xN), xN );
|
|
|
|
|
|
// update the kth weight
|
|
p->gV[k] = sw / xN;
|
|
|
|
|
|
// if this component's mean is not read-only
|
|
if( (roFlV==NULL || roFlV[k]==false) && sw != 0)
|
|
{
|
|
// get vector of all data points weighted by their soft assignment to cluster k
|
|
cmReal_t* sumV = sumM + (k*p->D); // sumV[p->D];
|
|
|
|
// update the mean[k]
|
|
cmVOR_DivVVS(uV, p->D, sumV, sw );
|
|
}
|
|
|
|
// update the covar[k]
|
|
// sM += ( W(i,k) .* ((X(:,i) - uV) * (X(:,i) - uV)'));
|
|
cmVOR_Fill(sM,p->D*p->D,0);
|
|
for(i=0; i<xN; ++i)
|
|
{
|
|
cmReal_t duV[ p->D ];
|
|
|
|
const cmReal_t* xV = readFunc( userFuncPtr, i );
|
|
|
|
assert( xV != NULL );
|
|
|
|
|
|
cmVOR_SubVVV( duV, p->D, xV, uV ); // duV[] = xM[:,i] - uV[];
|
|
cmVOR_MultMMM( p->t, p->D, p->D, duV, duV, 1 ); // t[,] = duV[] * duV[]'
|
|
cmVOR_MultVS( p->t, p->D*p->D, wM[ k * xN + i ]); // t[,] *= wM[i,k]
|
|
cmVOR_AddVV( sM, p->D*p->D, p->t ); // sM[,] += t[,];
|
|
}
|
|
|
|
if( sw != 0 )
|
|
cmVOR_DivVS( sM, p->D*p->D, sw ); // sM[,] ./ sw;
|
|
}
|
|
|
|
// update the inverted covar mtx and covar det.
|
|
if((rc = _cmGmmUpdateCovar(p,p->sMM )) != cmOkRC )
|
|
goto errLabel;
|
|
|
|
if( iterCntPtr != NULL )
|
|
*iterCntPtr += 1;
|
|
}
|
|
|
|
cmMemPtrFree(&wM);
|
|
cmMemPtrFree(&wV);
|
|
|
|
errLabel:
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmGmmTrain3( cmGmm_t* p, const cmReal_t* xM, unsigned xN, unsigned* iterCntPtr )
|
|
{
|
|
_cmGmmRdFuncData_t r;
|
|
r.colCnt = xN;
|
|
r.p = p;
|
|
r.xM = xM;
|
|
|
|
return cmGmmTrain2(p,_cmGmmReadFunc,&r,xN,iterCntPtr,NULL,NULL,-1);
|
|
|
|
}
|
|
|
|
|
|
cmRC_t cmGmmGenerate( cmGmm_t* p, cmReal_t* yM, unsigned yN )
|
|
{
|
|
unsigned i=0;
|
|
|
|
unsigned kV[yN];
|
|
|
|
// use weighted random selection to choose the component for each output value
|
|
cmVOR_WeightedRandInt(kV,yN, p->gV, p->K );
|
|
|
|
// cmVOU_Print(p->obj.err.rpt,1,yN,kV);
|
|
|
|
for(i=0; i<yN; ++i)
|
|
{
|
|
const cmReal_t* uV = p->uM + (kV[i] * p->D);
|
|
unsigned idx = kV[i] * p->D * p->D;
|
|
|
|
//cmVOR_PrintL("sM\n",p->obj.err.rpt,p->D,p->D,sM);
|
|
|
|
if( cmIsFlag(p->uflags,cmGmmDiagFl) )
|
|
{
|
|
const cmReal_t* sM = p->sMM + idx;
|
|
cmVOR_RandomGaussDiagM( yM + (i*p->D), p->D, 1, uV, sM );
|
|
}
|
|
else
|
|
{
|
|
const cmReal_t* uM = p->uMM + idx;
|
|
cmVOR_RandomGaussNonDiagM2(yM + (i*p->D), p->D, 1, uV, uM );
|
|
}
|
|
|
|
}
|
|
return cmOkRC;
|
|
}
|
|
|
|
void cmGmmPrint( cmGmm_t* p, bool fl )
|
|
{
|
|
unsigned k;
|
|
//cmCtx* c = p->obj.ctx;
|
|
|
|
cmVOR_PrintL("gV: ", p->obj.err.rpt, 1, p->K, p->gV );
|
|
cmVOR_PrintL("mM:\n", p->obj.err.rpt, p->D, p->K, p->uM );
|
|
|
|
for(k=0; k<p->K; ++k)
|
|
cmVOR_PrintL("sM:\n", p->obj.err.rpt, p->D, p->D, p->sMM + (k*p->D*p->D));
|
|
|
|
if( fl )
|
|
{
|
|
for(k=0; k<p->K; ++k)
|
|
cmVOR_PrintL("isM:\n", p->obj.err.rpt, p->D, p->D, p->isMM + (k*p->D*p->D));
|
|
|
|
for(k=0; k<p->K; ++k)
|
|
cmVOR_PrintL("uM:\n", p->obj.err.rpt, p->D, p->D, p->uMM + (k*p->D*p->D));
|
|
|
|
cmVOR_PrintL("logDetV:\n", p->obj.err.rpt, 1, p->K, p->logDetV);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void cmGmmTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH )
|
|
{
|
|
cmCtx* c = cmCtxAlloc(NULL,rpt,lhH,stH);
|
|
|
|
unsigned K = 2;
|
|
unsigned D = 2;
|
|
cmReal_t gV[ 2 ] = { .5, .5 };
|
|
cmReal_t uM[ 4 ] = { .3, .3, .8, .8 };
|
|
cmReal_t sMM[ 8 ] = { .1, 0, 0, .1, .1, 0, 0, .1 };
|
|
unsigned flags = cmGmmDiagFl ;
|
|
|
|
unsigned M = 100;
|
|
cmReal_t xM[ D*M ];
|
|
cmReal_t yV[ M ];
|
|
unsigned i,j;
|
|
|
|
cmPlotSetup("MultDimGauss Test",1,1);
|
|
|
|
cmGmm_t* p = cmGmmAlloc(c, NULL, K, D, gV, uM, sMM, flags );
|
|
|
|
if(0)
|
|
{
|
|
cmGmmPrint(p,true);
|
|
|
|
for(i=0; i<10; i++)
|
|
for(j=0; j<20; j+=2)
|
|
{
|
|
xM[(i*20)+j] = .1 * i;;
|
|
xM[(i*20)+j + 1] = .1 * (j/2);;
|
|
}
|
|
|
|
// octave equivalent
|
|
// x0= .1 * ones(1,10);
|
|
// x = [ 0*x0 1*x0 2*x0 3*x0 4*x0 5*x0 6*x0 7*x0 8*x0 9*x0];
|
|
// x = [x; repmat([0:.1:1],1,10)];
|
|
// y = mvnpdf(x',[.3 .3],[.1 0; 0 .1]); plot(y);
|
|
|
|
cmGmmEval(p,xM,M,yV,NULL);
|
|
|
|
|
|
//cmVOR_PrintL( "xM\n", rpt, D, M, xM );
|
|
cmVOR_PrintL( "yV\n", rpt, 10, 10, yV );
|
|
|
|
//printf("y:%f\n",yV[0]);
|
|
|
|
|
|
cmPlotLineD( NULL, NULL, yV, NULL, M, NULL, kSolidPlotLineId );
|
|
|
|
|
|
}
|
|
|
|
if(0)
|
|
{
|
|
cmReal_t yM[ D*M ];
|
|
cmReal_t yMt[ M*D ];
|
|
cmReal_t uMt[ p->K*D];
|
|
unsigned iterCnt = 10;
|
|
|
|
//srand( time(NULL) );
|
|
|
|
cmGmmGenerate( p, yM, M );
|
|
|
|
p->uflags = 0; // turn off diagonal condition
|
|
|
|
if( cmGmmTrain3( p, yM, M, &iterCnt ) != cmOkRC )
|
|
return;
|
|
|
|
cmCtxPrint(c,"iterCnt:%i\n",iterCnt);
|
|
|
|
cmGmmPrint( p, true );
|
|
|
|
cmVOR_Transpose(yMt, yM, D, M );
|
|
|
|
//cmVOR_PrintL("yMt\n",vReportFunc,M,D,yMt);
|
|
|
|
cmPlotLineD(NULL, yMt, yMt+M, NULL, M, "blue", kAsteriskPlotPtId );
|
|
|
|
cmVOR_Transpose( uMt, p->uM, D, p->K);
|
|
|
|
cmVOR_PrintL("uMt:\n", p->obj.err.rpt, p->K, p->D, uMt );
|
|
|
|
cmPlotLineD(NULL, uMt, uMt+p->K, NULL, p->D, "red", kXPlotPtId );
|
|
|
|
}
|
|
|
|
if(1)
|
|
{
|
|
cmGmmFree(&p);
|
|
|
|
|
|
cmReal_t cV0[] = { .7, .3 };
|
|
cmReal_t uM0[] = { .2, .1, .1, .2 };
|
|
cmReal_t sMM0[] = { .01, 0, 0, .01, .01, 0, 0, .01 };
|
|
unsigned flags = 0;
|
|
|
|
K = 2;
|
|
D = 2;
|
|
|
|
cmGmm_t* p = cmGmmAlloc(c,NULL, K, D, cV0, uM0, sMM0, flags );
|
|
|
|
xM[0] = 0.117228;
|
|
xM[1] = 0.110079;
|
|
|
|
cmGmmEval(p,xM,1,yV,NULL);
|
|
|
|
cmCtxPrint(c,"y: %f\n",yV[0]);
|
|
|
|
}
|
|
|
|
cmPlotDraw();
|
|
cmGmmFree(&p);
|
|
cmCtxFree(&c);
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmChmm_t* cmChmmAlloc( cmCtx* c, cmChmm_t* ap, unsigned stateN, unsigned mixN, unsigned dimN, const cmReal_t* iV, const cmReal_t* aM )
|
|
{
|
|
cmChmm_t* p = cmObjAlloc(cmChmm_t,c,ap);
|
|
|
|
if( stateN >0 && dimN > 0 )
|
|
if( cmChmmInit(p,stateN,mixN,dimN,iV,aM) != cmOkRC )
|
|
cmChmmFree(&p);
|
|
|
|
return p;
|
|
}
|
|
|
|
cmRC_t cmChmmFree( cmChmm_t** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmChmm_t* p = *pp;
|
|
|
|
if( pp == NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmChmmFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmMemPtrFree(&p->iV);
|
|
cmMemPtrFree(&p->aM);
|
|
cmMemPtrFree(&p->bV);
|
|
cmMemPtrFree(&p->bM);
|
|
cmObjFree(pp);
|
|
return cmOkRC;
|
|
|
|
}
|
|
|
|
cmRC_t cmChmmInit( cmChmm_t* p, unsigned stateN, unsigned mixN, unsigned dimN, const cmReal_t* iV, const cmReal_t* aM )
|
|
{
|
|
cmRC_t rc;
|
|
unsigned i;
|
|
|
|
if((rc = cmChmmFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
// iV[] aM
|
|
/*
|
|
unsigned n = stateN + (stateN*stateN);
|
|
p->iV = cmArrayResizeZ(c, &p->memA, n, cmReal_t );
|
|
p->aM = p->iV + stateN;
|
|
*/
|
|
|
|
//cmArrayResizeVZ(c,&p->memA, cmReal_t, &p->iV,stateN, &p->aM, stateN*stateN, NULL );
|
|
|
|
p->iV = cmMemResizeZ( cmReal_t, p->iV, stateN );
|
|
p->aM = cmMemResizeZ( cmReal_t, p->aM, stateN * stateN );
|
|
p->bV = cmMemResizeZ( cmGmm_t*, p->bV, stateN );
|
|
p->N = stateN;
|
|
p->K = mixN;
|
|
p->D = dimN;
|
|
|
|
if( iV != NULL )
|
|
cmVOR_Copy(p->iV,p->N,iV);
|
|
|
|
if( aM != NULL )
|
|
cmVOR_Copy(p->aM,p->N*p->N,aM);
|
|
|
|
for(i=0; i<p->N; ++i)
|
|
p->bV[i] = cmGmmAlloc( p->obj.ctx, NULL, p->K, p->D, NULL, NULL, NULL, 0 );
|
|
|
|
//p->mfp = cmCtxAllocDebugFile( p->obj.ctx,"chmm");
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmChmmFinal( cmChmm_t* p )
|
|
{
|
|
if( p != NULL )
|
|
{
|
|
unsigned i;
|
|
|
|
for(i=0; i<p->N; ++i)
|
|
cmGmmFree( &p->bV[i] );
|
|
|
|
cmMemPtrFree(&p->bM);
|
|
|
|
//if( p->mfp != NULL )
|
|
// cmCtxFreeDebugFile(p->obj.ctx,&p->mfp);
|
|
|
|
}
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmRC_t cmChmmRandomize( cmChmm_t* p, const cmReal_t* oM, unsigned T )
|
|
{
|
|
cmRC_t rc;
|
|
unsigned i;
|
|
unsigned N = p->N;
|
|
|
|
// randomize the initial state probabilities
|
|
cmVOR_Random( p->iV, N, 0.0, 1.0 );
|
|
cmVOR_NormalizeProbability( p->iV, N );
|
|
|
|
// randomize the state transition matrix
|
|
cmVOR_Random( p->aM, N*N, 0.0, 1.0 );
|
|
|
|
for(i=0; i<N; ++i)
|
|
{
|
|
cmVOR_NormalizeProbabilityN( p->aM + i, N, N ); // rows of aM must sum to 1.0
|
|
|
|
if((rc = cmGmmRandomize( p->bV[i], oM, T )) != cmOkRC) // randomize the GMM assoc'd with state i
|
|
return rc;
|
|
}
|
|
|
|
cmMemPtrFree(&p->bM); // force bM[] to be recalculated
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
cmReal_t _cmChmmKmeansDist( void* userPtr, const cmReal_t* v0, const cmReal_t* v1, unsigned vn )
|
|
{ return cmVOR_EuclidDistance(v0,v1,vn); }
|
|
|
|
cmRC_t cmChmmSegKMeans( cmChmm_t* p, const cmReal_t* oM, unsigned T, cmReal_t threshProb, unsigned maxIterCnt, unsigned iterCnt )
|
|
{
|
|
cmCtx* c = p->obj.ctx;
|
|
cmRC_t rc = cmOkRC;
|
|
unsigned i,j,k,t;
|
|
|
|
unsigned N = p->N;
|
|
unsigned K = p->K;
|
|
unsigned D = p->D;
|
|
|
|
//unsigned qV[T];
|
|
//cmReal_t alphaM[N*T];
|
|
//unsigned clusterIdxV[T];
|
|
//cmReal_t centroidM[D*N];
|
|
|
|
/*
|
|
unsigned sz = 2*ALIGN_B(T,unsigned) +
|
|
ALIGN_B(N*T,cmReal_t) +
|
|
ALIGN_B(D*N,cmReal_t);
|
|
cmArray mem;
|
|
cmArrayAlloc(c, &mem);
|
|
|
|
unsigned* qV = (unsigned*) cmArrayResize(c, &mem, sz, char);
|
|
cmReal_t* alphaM = (cmReal_t*) (qV + ALIGN_T(T, unsigned));
|
|
unsigned* clusterIdxV = (unsigned*) (alphaM + ALIGN_T(N*T,cmReal_t));
|
|
cmReal_t* centroidM = (cmReal_t*) (clusterIdxV + ALIGN_T(T, unsigned));
|
|
*/
|
|
|
|
unsigned* qV = cmMemAlloc( unsigned, T );
|
|
cmReal_t* alphaM = cmMemAlloc( cmReal_t, N*T);
|
|
unsigned* clusterIdxV = cmMemAlloc( unsigned, T );
|
|
cmReal_t* centroidM = cmMemAlloc( cmReal_t, D*N);
|
|
|
|
|
|
cmReal_t logPr = 0;
|
|
bool reportFl = true;
|
|
|
|
cmChmmRandomize(p,oM,T);
|
|
|
|
// cluster the observations into N groups
|
|
cmVOR_Kmeans( qV, centroidM, N, oM, D, T, NULL, 0, false, _cmChmmKmeansDist, NULL );
|
|
|
|
for(i=0; i<maxIterCnt; ++i)
|
|
{
|
|
unsigned jnV[N];
|
|
|
|
if( reportFl )
|
|
cmCtxPrint(c,"SegKM: ----------------------------------------------------%i\n",i);
|
|
|
|
// get the count of data points in each state
|
|
cmVOU_Fill(jnV,N,0);
|
|
for(t=0; t<T; ++t)
|
|
++jnV[ qV[t] ];
|
|
|
|
// for each state
|
|
for(j=0; j<N; ++j)
|
|
{
|
|
cmGmm_t* g = p->bV[j];
|
|
|
|
// cluster all datapoints which were assigned to state j
|
|
cmVOR_Kmeans( clusterIdxV, g->uM, K, oM, D, T, qV, j, false, _cmChmmKmeansDist, NULL );
|
|
|
|
// for each cluster
|
|
for(k=0; k<K; ++k)
|
|
{
|
|
unsigned kN = 0;
|
|
|
|
// kN is count of data points assigned to cluster k
|
|
for(t=0; t<T; ++t)
|
|
if( clusterIdxV[t] == k )
|
|
++kN;
|
|
|
|
g->gV[k] = (cmReal_t)kN/jnV[j];
|
|
|
|
|
|
// the covar of the kth component is the sample covar of cluster k
|
|
cmVOR_GaussCovariance(g->sMM + (k*D*D), D, oM, T, g->uM + (k*D), clusterIdxV, k );
|
|
|
|
}
|
|
|
|
if((rc = _cmGmmUpdateCovar(g, g->sMM )) != cmOkRC )
|
|
goto errLabel;
|
|
}
|
|
|
|
if( i== 0 )
|
|
{
|
|
// count transitions from i to j
|
|
for(t=0; t<T-1; ++t)
|
|
p->aM[ (qV[t+1]*N) + qV[t] ] += 1.0;
|
|
|
|
for(j=0; j<N; ++j)
|
|
{
|
|
// normalize state transitions by dividing by times in each state
|
|
for(k=0; k<N; k++)
|
|
p->aM[ (k*N) + j ] /= jnV[j];
|
|
|
|
cmVOR_NormalizeProbabilityN(p->aM + j, N, N);
|
|
|
|
cmGmmEval( p->bV[j], oM, 1, p->iV + j, NULL );
|
|
}
|
|
}
|
|
|
|
|
|
if((rc = cmChmmTrain(p, oM, T, iterCnt,0,0 )) != cmOkRC )
|
|
goto errLabel;
|
|
|
|
// calculate the prob. that the new model generated the data
|
|
cmReal_t logPr0 = cmChmmForward(p,oM,T,alphaM,NULL);
|
|
cmReal_t dLogPr = logPr0 - logPr;
|
|
|
|
if( reportFl )
|
|
cmCtxPrint(c,"pr:%f d:%f\n",logPr0,dLogPr);
|
|
|
|
if( (dLogPr > 0) && (dLogPr < threshProb) )
|
|
break;
|
|
|
|
logPr = logPr0;
|
|
|
|
|
|
|
|
// fill qV[] with the state at each time t
|
|
cmChmmDecode(p,oM,T,qV);
|
|
|
|
}
|
|
|
|
errLabel:
|
|
|
|
cmMemPtrFree(&qV);
|
|
cmMemPtrFree(&alphaM);
|
|
cmMemPtrFree(&clusterIdxV);
|
|
cmMemPtrFree(¢roidM);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
cmRC_t cmChmmSetGmm( cmChmm_t* p, unsigned i, const cmReal_t* wV, const cmReal_t* uM, const cmReal_t* sMM, unsigned flags )
|
|
{
|
|
assert( i < p->N);
|
|
cmMemPtrFree(&p->bM); // force bM[] to be recalculated
|
|
return cmGmmInit(p->bV[i],p->K,p->D,wV,uM,sMM,flags);
|
|
}
|
|
|
|
|
|
// Return the probability of the observation for each state.
|
|
// oV[D] - multi-dim. observation data point
|
|
// pV[N] - probability of this observation for each state
|
|
void cmChmmObsProb( const cmChmm_t* p, const cmReal_t* oV, cmReal_t* prV )
|
|
{
|
|
unsigned i;
|
|
for(i=0; i<p->N; ++i)
|
|
cmGmmEval( p->bV[i], oV, 1, prV + i, NULL );
|
|
}
|
|
|
|
// oM[D,T] - observation matrix
|
|
// alphaM[N,T] - prob of being in each state and observtin oM(:,t)
|
|
// bM[N,T] - (optional) state-observation probability matrix
|
|
// logPrV[T] - (optional) record the log prob of the data given the model at each time step
|
|
// Returns sum(logPrV[T])
|
|
cmReal_t cmChmmForward( const cmChmm_t* p, const cmReal_t* oM, unsigned T, cmReal_t* alphaM, cmReal_t* logPrV )
|
|
{
|
|
unsigned t;
|
|
|
|
cmReal_t logPr = 0;
|
|
|
|
// calc the prob of starting in each state
|
|
if( p->bM == NULL )
|
|
cmChmmObsProb( p, oM, alphaM );
|
|
else
|
|
cmVOR_Copy( alphaM, p->N*T, p->bM );
|
|
|
|
|
|
cmVOR_MultVV( alphaM, p->N, p->iV );
|
|
|
|
cmReal_t s = cmVOR_Sum(alphaM,p->N);
|
|
|
|
cmVOR_DivVS(alphaM,p->N,s);
|
|
//cmVOR_PrintL("alpha:\n",p->obj.err.rpt,p->N,1,alphaM);
|
|
|
|
for(t=1; t<T; ++t)
|
|
{
|
|
cmReal_t tmp[p->N];
|
|
|
|
cmReal_t* alphaV = alphaM + t*p->N;
|
|
|
|
// calc the prob of the observation for each state
|
|
if( p->bM == NULL )
|
|
cmChmmObsProb(p,oM + (t*p->D), alphaV );
|
|
|
|
// calc. the prob. of transitioning to each state at time t, from each state at t-1
|
|
cmVOR_MultVVM(tmp,p->N, alphaM + ((t-1)*p->N), p->N, p->aM );
|
|
|
|
// calc the joint prob of transitioning from each state to each state and observing O(t).
|
|
cmVOR_MultVV(alphaV, p->N, tmp );
|
|
|
|
// scale the probabilities to prevent underflow
|
|
s = cmVOR_Sum(alphaV,p->N);
|
|
cmVOR_DivVS(alphaV,p->N,s);
|
|
|
|
// track the log prob. of the model having generated the data up to time t.
|
|
cmReal_t pr = log(s);
|
|
|
|
if( logPrV != NULL )
|
|
logPrV[t] = pr;
|
|
|
|
logPr += pr;
|
|
}
|
|
|
|
return logPr;
|
|
|
|
}
|
|
|
|
// oM[D,T]
|
|
// betaM[N,T]
|
|
void cmChmmBackward( const cmChmm_t* p, const cmReal_t* oM, unsigned T, cmReal_t* betaM )
|
|
{
|
|
cmVOR_Fill(betaM,p->N*T,1.0);
|
|
|
|
assert(T >= 2 );
|
|
|
|
int t = (int)T - 2;
|
|
for(; t>=0; --t)
|
|
{
|
|
cmReal_t tmp[p->N];
|
|
|
|
if( p->bM == NULL )
|
|
cmChmmObsProb(p,oM+((t+1)*p->D), tmp );
|
|
else
|
|
cmVOR_Copy(tmp,p->N,p->bM + ((t+1)*p->N));
|
|
|
|
cmVOR_MultVV(tmp,p->N,betaM + ((t+1)*p->N));
|
|
|
|
cmVOR_MultVMV(betaM+(t*p->N),p->N, p->aM, p->N, tmp );
|
|
|
|
cmVOR_NormalizeProbability(betaM+(t*p->N),p->N );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmReal_t cmChmmCompare( const cmChmm_t* p0, const cmChmm_t* p1, unsigned T )
|
|
{
|
|
assert(p0->D == p1->D);
|
|
assert(p0->N == p1->N);
|
|
|
|
cmReal_t oM[p0->D*T];
|
|
cmReal_t alphaM[p0->N*T];
|
|
|
|
cmChmmGenerate(p0,oM,T,NULL);
|
|
cmReal_t logPr00 = cmChmmForward(p0,oM,T,alphaM,NULL);
|
|
cmReal_t logPr01 = cmChmmForward(p1,oM,T,alphaM,NULL);
|
|
|
|
cmChmmGenerate(p1,oM,T,NULL);
|
|
cmReal_t logPr10 = cmChmmForward(p0,oM,T,alphaM,NULL);
|
|
cmReal_t logPr11 = cmChmmForward(p1,oM,T,alphaM,NULL);
|
|
|
|
cmReal_t d0 = (logPr01-logPr00)/T;
|
|
cmReal_t d1 = (logPr10-logPr11)/T;
|
|
|
|
return (d0+d1)/2;
|
|
|
|
}
|
|
|
|
cmRC_t cmChmmGenerate( const cmChmm_t* p, cmReal_t* oM, unsigned T, unsigned* sV )
|
|
{
|
|
unsigned i,si;
|
|
|
|
// use weighted random selection to choose an intitial state
|
|
cmVOR_WeightedRandInt(&si, 1, p->iV, p->N );
|
|
|
|
for(i=0; i<T; ++i)
|
|
{
|
|
if( sV != NULL )
|
|
sV[i] = si;
|
|
|
|
// generate a random value using the GMM assoc'd with the current state
|
|
cmGmmGenerate( p->bV[si], oM + (i*p->D), 1 );
|
|
|
|
// choose the next state using the transition weights from the current state
|
|
cmVOR_WeightedRandInt(&si, 1, p->aM + (si*p->N), p->N );
|
|
}
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
cmRC_t cmChmmDecode( cmChmm_t* p, const cmReal_t* oM, unsigned T, unsigned* yV )
|
|
{
|
|
int i,j,t;
|
|
unsigned N = p->N;
|
|
|
|
//unsigned psiM[N*T];
|
|
//cmReal_t delta[N];
|
|
/*
|
|
unsigned sz = ALIGN_B(N*T,unsigned) + ALIGN_B(N,cmReal_t);
|
|
|
|
cmArray mem;
|
|
cmArrayAlloc(c, &mem);
|
|
|
|
unsigned* psiM = (unsigned*) cmArrayResize(c, &mem, sz, char);
|
|
cmReal_t* delta = (cmReal_t*) (psiM + ALIGN_T(N*T,unsigned));
|
|
*/
|
|
|
|
unsigned* psiM = cmMemAlloc( unsigned, N*T );
|
|
cmReal_t* delta= cmMemAlloc( cmReal_t, N );
|
|
|
|
// get the prob of starting in each state
|
|
if( p->bM == NULL )
|
|
cmChmmObsProb( p, oM, delta );
|
|
else
|
|
cmVOR_Copy( delta, N, p->bM );
|
|
|
|
cmVOR_MultVV( delta, p->N, p->iV );
|
|
cmVOR_NormalizeProbability(delta, p->N);
|
|
|
|
for(t=1; t<T; ++t)
|
|
{
|
|
cmReal_t mV[N];
|
|
|
|
const cmReal_t* ap = p->aM;
|
|
|
|
// calc. the most likely new state given the most likely prev state
|
|
// and the transition matrix
|
|
for(i=0; i<N; ++i)
|
|
{
|
|
const cmReal_t* dp = delta;
|
|
unsigned psiIdx = t*N + i;
|
|
|
|
mV[i] = *ap++ * *dp++;
|
|
psiM[ psiIdx ] = 0;
|
|
|
|
// find max value of: delta .* A(:,i)
|
|
for(j=1; j<N; ++j )
|
|
{
|
|
cmReal_t v = *ap++ * *dp++;
|
|
|
|
if( v > mV[i] )
|
|
{
|
|
mV[i] = v;
|
|
psiM[ psiIdx ] = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
// mV[] now holds the prob. of the max likelihood state at time t
|
|
// for each possible state at t-1
|
|
|
|
// psiM[:,t] holds the index of the max likelihood state
|
|
|
|
// condition the most likely new state on the observations
|
|
if( p->bM == NULL )
|
|
cmChmmObsProb(p,oM + (t*p->D), delta);
|
|
else
|
|
cmVOR_Copy(delta, N, p->bM + (t*p->N) );
|
|
|
|
cmVOR_MultVV(delta, N, mV ); // condition it on the max. like current states
|
|
cmVOR_NormalizeProbability( delta, N ); // normalize the prob.
|
|
}
|
|
|
|
// unwind psiM[] to form the max. likelihood state sequence
|
|
yV[T-1] = cmVOR_MaxIndex(delta,N,1);
|
|
|
|
for(t=T-2; t>=0; --t)
|
|
yV[t] = psiM[ ((t+1)*N) + yV[t+1] ];
|
|
|
|
|
|
cmMemPtrFree(&psiM);
|
|
cmMemPtrFree(&delta);
|
|
|
|
return cmOkRC;
|
|
}
|
|
|
|
|
|
cmRC_t cmChmmTrain( cmChmm_t* p, const cmReal_t* oM, unsigned T, unsigned iterCnt, cmReal_t thresh, unsigned flags )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
unsigned i,j,k,t,d;
|
|
unsigned iter;
|
|
unsigned N = p->N;
|
|
unsigned K = p->K;
|
|
unsigned D = p->D;
|
|
unsigned De2 = D * D;
|
|
bool mixFl = !cmIsFlag(flags,kNoTrainMixCoeffChmmFl);
|
|
bool meanFl = !cmIsFlag(flags,kNoTrainMeanChmmFl);
|
|
bool covarFl = !cmIsFlag(flags,kNoTrainCovarChmmFl);
|
|
bool bFl = mixFl | meanFl | covarFl;
|
|
bool calcBFl = true;
|
|
bool progFl = false;
|
|
bool timeProgFl = false;
|
|
cmReal_t progInc = 0.1;
|
|
cmReal_t progFrac = 0;
|
|
cmReal_t logProb = 0;
|
|
|
|
//cmReal_t alphaM[N*T]; // alpha[N,T]
|
|
//cmReal_t betaM[N*T]; // betaM[N,T]
|
|
//cmReal_t logPrV[T];
|
|
//cmReal_t EpsM[N*N];
|
|
//cmReal_t BK[N*K*T];
|
|
//cmReal_t gamma_jk[N*K];
|
|
//cmReal_t uM[K*D*N];
|
|
//cmReal_t sMM[K*De2*N];
|
|
/*
|
|
unsigned sz = ALIGN_T(N*T, cmReal_t) +
|
|
ALIGN_T(N*T, cmReal_t) +
|
|
ALIGN_T(T, cmReal_t) +
|
|
ALIGN_T(N*N, cmReal_t) +
|
|
ALIGN_T(N*K*T, cmReal_t) +
|
|
ALIGN_T(N*K, cmReal_t) +
|
|
ALIGN_T(K*D*N, cmReal_t) +
|
|
ALIGN_T(K*De2*N,cmReal_t);
|
|
cmArray mem;
|
|
cmArrayAlloc(c, &mem);
|
|
|
|
cmReal_t* alphaM = cmArrayResize(c, &mem, sz, cmReal_t); // alpha[N,T]
|
|
cmReal_t* betaM = alphaM + ALIGN_T(N*T, cmReal_t); // betaM[N,T]
|
|
cmReal_t* logPrV = betaM + ALIGN_T(N*T, cmReal_t);
|
|
cmReal_t* EpsM = logPrV + ALIGN_T(T, cmReal_t);
|
|
cmReal_t* BK = EpsM + ALIGN_T(N*N, cmReal_t);
|
|
cmReal_t* gamma_jk = BK + ALIGN_T(N*K*T,cmReal_t);
|
|
cmReal_t* uM = gamma_jk + ALIGN_T(N*K, cmReal_t);
|
|
cmReal_t* sMM = uM + ALIGN_T(K*D*N,cmReal_t);
|
|
*/
|
|
|
|
cmReal_t* alphaM = cmMemAlloc( cmReal_t, N*T );
|
|
cmReal_t* betaM = cmMemAlloc( cmReal_t, N*T );
|
|
cmReal_t* logPrV = cmMemAlloc( cmReal_t, T );
|
|
cmReal_t* EpsM = cmMemAlloc( cmReal_t, N*N );
|
|
cmReal_t* BK = cmMemAlloc( cmReal_t, N*K*T );
|
|
cmReal_t* gamma_jk = cmMemAlloc( cmReal_t, N*K );
|
|
cmReal_t* uM = cmMemAlloc( cmReal_t, K*D*N );
|
|
cmReal_t* sMM = cmMemAlloc( cmReal_t, K*De2*N );
|
|
|
|
if( thresh <=0 )
|
|
thresh = 0.0001;
|
|
|
|
//cmArrayResizeZ(c,&p->memC,N*T,cmReal_t);
|
|
p->bM = cmMemResizeZ( cmReal_t, p->bM, N*T);
|
|
|
|
for(iter=0; iter<iterCnt; ++iter)
|
|
{
|
|
// zero the mean and covar summation arrays
|
|
cmVOR_Fill(uM, K*D *N,0);
|
|
cmVOR_Fill(sMM, K*De2*N,0);
|
|
cmVOR_Fill(EpsM,N*N, 0);
|
|
cmVOR_Fill(gamma_jk,N*K,0);
|
|
|
|
//
|
|
// B[i,t] The prob that state i generated oM(:,t)
|
|
// BK[i,k,t] The prob that state i component k generated oM(:,t)
|
|
// Note: B[i,t] = sum(BK(i,k,:))
|
|
//
|
|
if( calcBFl || bFl )
|
|
{
|
|
calcBFl = false;
|
|
for(t=0; t<T; ++t)
|
|
{
|
|
// prob. that state i generated objservation O[t]
|
|
for(i=0; i<N; ++i )
|
|
cmGmmEval( p->bV[i], oM + (t*D), 1, p->bM + (t*N) + i, BK + (t*N*K) + (i*K) );
|
|
}
|
|
}
|
|
|
|
// alpha[N,T] is prob. of transitioning forward to each state given the observed data
|
|
cmReal_t logProb0 = cmChmmForward( p, oM, T, alphaM, logPrV );
|
|
|
|
// check for convergence
|
|
cmReal_t dLogProb = labs(logProb0-logProb) / ((labs(logProb0)+labs(logProb)+cmReal_EPSILON)/2);
|
|
if( dLogProb < thresh )
|
|
break;
|
|
|
|
logProb = logProb0;
|
|
|
|
// betaM[N,T] is prob of transitioning backward from each state given the observed data
|
|
cmChmmBackward(p, oM, T, betaM );
|
|
|
|
|
|
if(progFl)
|
|
cmCtxPrint(p->obj.ctx,"%i (%f) ",iter+1, dLogProb );
|
|
|
|
if(timeProgFl)
|
|
progFrac = progInc;
|
|
|
|
|
|
// for each time step
|
|
for(t=0; t<T-1; ++t)
|
|
{
|
|
// oV[D] is the observation at step t
|
|
const cmReal_t* oV = oM + (t*D);
|
|
|
|
|
|
//
|
|
// Update EpsM[N,N] (6.37)
|
|
// (prob. of being in state i at time t and transitioning
|
|
// to state j at time t+1)
|
|
//
|
|
cmReal_t E[N*N];
|
|
|
|
// for each possible state transition
|
|
for(i=0; i<N; ++i)
|
|
for(j=0; j<N; ++j)
|
|
{
|
|
|
|
E[ i + (j*N) ]
|
|
= exp(log(alphaM[ (t*N) + i ])
|
|
+ log(p->aM[ i + (j*N) ])
|
|
+ log(p->bM[ ((t+1)*N) + j ])
|
|
+ log(betaM[ ((t+1)*N) + j ]));
|
|
}
|
|
|
|
|
|
cmVOR_NormalizeProbability( E, N*N );
|
|
cmVOR_AddVV( EpsM, N*N, E );
|
|
|
|
|
|
// If t==0 then update the initial state prob's
|
|
if( t == 0 )
|
|
{
|
|
for(i=0; i<N; ++i)
|
|
p->iV[i] = cmVOR_SumN(EpsM+i, N, N);
|
|
|
|
assert( cmVOR_IsNormal(p->iV,N) );
|
|
}
|
|
|
|
if( bFl )
|
|
{
|
|
//
|
|
// Calculate gamma_jk[]
|
|
//
|
|
cmReal_t gtjk[N*K]; // gamma_jk[N,K] at time t
|
|
cmReal_t abV[N]; //
|
|
|
|
|
|
// (alphaM[j,t] * betaM[j:t]) / (sum(alphaM[:,t] * betaM[:,t]))
|
|
cmVOR_MultVVV(abV,N,alphaM + t*N, betaM+t*N);
|
|
cmReal_t abSum = cmVOR_Sum(abV,N);
|
|
|
|
if( abSum<=0 )
|
|
assert(abSum>0);
|
|
|
|
cmVOR_DivVS(abV,N,abSum);
|
|
|
|
|
|
for(j=0; j<N; ++j)
|
|
{
|
|
cmReal_t bkSum = cmVOR_Sum(BK + (t*N*K) + (j*K), K );
|
|
|
|
for(k=0; k<K; ++k)
|
|
gtjk[ (k*N)+j ] = abV[j] * (BK[ (t*N*K) + (j*K) + k ] / bkSum);
|
|
}
|
|
|
|
|
|
// sum gtjk[N,K] into gamma_jk (integrate gamma over time)
|
|
cmVOR_AddVV( gamma_jk, N*K, gtjk );
|
|
|
|
|
|
// update the mean and covar numerators
|
|
for(j=0; j<N; ++j)
|
|
{
|
|
cmReal_t* uV = uM + (j*D*K);
|
|
cmReal_t* sV = sMM + (j*De2*K);
|
|
|
|
for(k=0; k<K; ++k,uV+=D,sV+=De2)
|
|
{
|
|
cmReal_t c = gtjk[ (k*N)+j ];
|
|
|
|
if( covarFl )
|
|
{
|
|
cmReal_t dV[D];
|
|
cmReal_t dM[D*D];
|
|
|
|
// covar numerator b[j].sM[k]
|
|
cmVOR_SubVVV(dV, D, oV, p->bV[j]->uM + (k*D));
|
|
cmVOR_MultMMM( dM, D, D, dV, dV, 1 );
|
|
cmVOR_MultVS( dM, De2, c );
|
|
cmVOR_AddVV( sV, De2, dM );
|
|
}
|
|
|
|
if( meanFl )
|
|
{
|
|
// mean numerator b[j].uM[k]
|
|
for(d=0; d<D; ++d)
|
|
uV[d] += c * oV[ d ];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( timeProgFl && (t >= floor(T*progFrac)) )
|
|
{
|
|
cmCtxPrint(p->obj.ctx,"%i ", (unsigned)round(progFrac*100) );
|
|
progFrac+=progInc;
|
|
}
|
|
|
|
} // end time loop
|
|
|
|
|
|
for(i=0; i<N; ++i)
|
|
{
|
|
|
|
// update the state transition matrix
|
|
cmReal_t den = cmVOR_SumN(EpsM + i, N, N );
|
|
|
|
assert(den != 0 );
|
|
|
|
for(j=0; j<N; ++j)
|
|
p->aM[ i + (j*N) ] = EpsM[ i + (j*N) ] / den;
|
|
|
|
if( bFl )
|
|
{
|
|
// update the mean, covariance and mix coefficient
|
|
cmGmm_t* g = p->bV[i];
|
|
const cmReal_t* uV = uM + (i*D*K);
|
|
const cmReal_t* sMV = sMM + (i*De2*K);
|
|
|
|
for(k=0; k<K; ++k,uV+=D,sMV+=De2)
|
|
{
|
|
cmReal_t gjk = gamma_jk[ (k*N) + i ];
|
|
|
|
if( meanFl )
|
|
cmVOR_DivVVS(g->uM + (k*D), D, uV, gjk );
|
|
|
|
if( covarFl )
|
|
cmVOR_DivVVS(g->sMM + (k*De2), De2, sMV, gjk );
|
|
|
|
if( mixFl )
|
|
g->gV[k] = gjk / cmVOR_SumN( gamma_jk + i, K, N );
|
|
}
|
|
|
|
if((rc = _cmGmmUpdateCovar(g,g->sMM)) != cmOkRC )
|
|
goto errLabel;
|
|
}
|
|
}
|
|
|
|
assert( cmVOR_IsNormalZ(p->aM,N*N) );
|
|
|
|
if( timeProgFl )
|
|
cmCtxPrint(p->obj.ctx,"\n");
|
|
|
|
} // end iter loop
|
|
|
|
if( progFl)
|
|
cmCtxPrint(p->obj.ctx,"\n");
|
|
|
|
if( p->mfp != NULL )
|
|
{
|
|
// first line is iV[N]
|
|
cmMtxFileRealExec(p->mfp,p->iV,p->N);
|
|
|
|
// next N lines are aM[N,N]
|
|
for(i=0; i<p->N; ++i)
|
|
cmMtxFileRealExecN(p->mfp,p->aM + i,p->N,p->N);
|
|
|
|
// next T lines are bM[T,N]
|
|
if( p->bM != NULL )
|
|
for(i=0; i<T; ++i)
|
|
cmMtxFileRealExec(p->mfp, p->bM + (i*p->N),p->N);
|
|
}
|
|
|
|
errLabel:
|
|
cmMemPtrFree(&alphaM);
|
|
cmMemPtrFree(&betaM);
|
|
cmMemPtrFree(&logPrV);
|
|
cmMemPtrFree(&EpsM);
|
|
cmMemPtrFree(&BK);
|
|
cmMemPtrFree(&gamma_jk);
|
|
cmMemPtrFree(&uM);
|
|
cmMemPtrFree(&sMM);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
void cmChmmPrint( cmChmm_t* p )
|
|
{
|
|
unsigned i;
|
|
cmCtxPrint(p->obj.ctx,"======================================== \n");
|
|
cmVOR_PrintL("iV: ", p->obj.err.rpt, 1, p->N, p->iV);
|
|
cmVOR_PrintL("aM:\n", p->obj.err.rpt, p->N, p->N, p->aM);
|
|
|
|
for(i=0; i<p->N; ++i)
|
|
{
|
|
cmCtxPrint(p->obj.ctx,"bV[%i] ----------------- %i \n",i,i);
|
|
cmGmmPrint(p->bV[i],false);
|
|
}
|
|
}
|
|
|
|
|
|
void cmChmmTestForward( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH )
|
|
{
|
|
cmReal_t oM[] = {
|
|
0.117228, 0.110079,
|
|
0.154646, 0.210436,
|
|
0.947468, 0.558136,
|
|
0.202023, 0.138123,
|
|
0.929933, 0.456102,
|
|
0.897566, 0.685078,
|
|
0.945177, 0.663145,
|
|
0.272399, 0.055107,
|
|
0.863386, 0.621546,
|
|
0.217545, 0.274709,
|
|
0.838777, 0.650038,
|
|
0.134966, 0.159472,
|
|
0.053990, 0.264051,
|
|
0.884269, 0.550019,
|
|
0.764787, 0.554484,
|
|
0.114771, 0.077518,
|
|
0.835121, 0.606137,
|
|
0.070733, 0.120015,
|
|
0.819814, 0.588482,
|
|
0.105511, 0.197699,
|
|
0.824778, 0.533047,
|
|
0.945223, 0.511411,
|
|
0.126971, 0.050083,
|
|
0.869497, 0.567737,
|
|
0.144866, 0.197363,
|
|
0.985726, 0.590402,
|
|
0.181094, 0.192827,
|
|
0.162179, 0.155297,
|
|
1.034691, 0.513413,
|
|
0.220708, 0.036158,
|
|
0.750061, 0.671224,
|
|
0.246971, 0.093246,
|
|
0.997567, 0.680491,
|
|
0.916887, 0.530981,
|
|
0.022328, 0.121969,
|
|
0.794031, 0.618081,
|
|
0.845066, 0.625512,
|
|
0.174731, 0.094773,
|
|
0.968665, 0.652435,
|
|
0.932484, 0.388081,
|
|
0.202732, 0.148710,
|
|
0.911307, 0.637139,
|
|
0.211127, 0.201362,
|
|
0.138152, 0.057290,
|
|
0.819132, 0.579888,
|
|
0.135625, 0.176140,
|
|
0.146017, 0.157853,
|
|
0.950319, 0.624150,
|
|
0.285064, 0.038825,
|
|
0.716844, 0.575189,
|
|
0.907433, 0.504946,
|
|
0.219772, 0.129993,
|
|
0.076507, 0.193079,
|
|
0.808906, 0.548409,
|
|
0.880892, 0.523950,
|
|
0.758099, 0.636729,
|
|
1.014017, 0.557120,
|
|
0.277888, 0.181492,
|
|
0.877588, 0.508634,
|
|
0.251266, 0.225890,
|
|
0.990904, 0.482949,
|
|
0.999899, 0.534579,
|
|
0.904179, 0.707349,
|
|
0.952879, 0.617955,
|
|
0.172068, 0.151984,
|
|
1.026262, 0.662600,
|
|
0.812003, 0.430856,
|
|
0.173393, 0.017885,
|
|
0.099370, 0.146661,
|
|
0.785785, 0.564333,
|
|
0.698222, 0.449299,
|
|
0.276539, 0.225314,
|
|
0.799271, 0.618159,
|
|
0.098813, 0.090839,
|
|
0.883666, 0.554150,
|
|
0.274934, 0.185403,
|
|
0.200419, 0.109972,
|
|
0.925076, 0.608610,
|
|
0.864486, 0.348689,
|
|
0.176733, 0.136235,
|
|
0.967278, 0.656875,
|
|
0.986994, 0.659877,
|
|
1.015618, 0.596549,
|
|
0.689903, 0.528107,
|
|
0.978238, 0.630989,
|
|
0.269847, 0.144358,
|
|
0.092303, 0.139894,
|
|
0.168185, 0.095327,
|
|
0.897767, 0.584203,
|
|
0.068316, 0.018452,
|
|
0.953395, 0.530545,
|
|
0.266405, 0.173987,
|
|
0.233845, 0.205276,
|
|
0.900060, 0.477108,
|
|
0.052909, 0.053077,
|
|
0.885850, 0.496546,
|
|
0.268494, 0.104785,
|
|
1.041405, 0.655079,
|
|
1.055915, 0.697988,
|
|
0.181569, 0.146840
|
|
};
|
|
|
|
unsigned i;
|
|
cmReal_t iV[] = { .5 , .5};
|
|
cmReal_t A[] = { .3, .6, .7, .4 };
|
|
|
|
cmReal_t cV0[] = { .7, .3 };
|
|
cmReal_t uM0[] = { .2, .1, .1, .2 };
|
|
cmReal_t sMM0[]= { .01, 0, 0, .01, .01, 0, 0, .01 };
|
|
|
|
cmReal_t cV1[] = { .2, .8 };
|
|
cmReal_t uM1[] = { .8, .9, .9, .5 };
|
|
cmReal_t sMM1[]= { .01, 0, 0, .01, .01, 0, 0, .01 };
|
|
|
|
|
|
unsigned T = 100;
|
|
unsigned N = 2;
|
|
unsigned K = 2;
|
|
unsigned D = 2;
|
|
|
|
cmReal_t alphaM[N*T];
|
|
cmReal_t betaM[N*T];
|
|
cmReal_t logPrV[T];
|
|
unsigned qV[T];
|
|
unsigned sV[T];
|
|
cmReal_t oMt[T*D];
|
|
|
|
// scale covariance
|
|
cmVOR_MultVS(sMM0,D*D*K,1);
|
|
cmVOR_MultVS(sMM1,D*D*K,1);
|
|
|
|
|
|
cmCtx c;
|
|
cmCtxAlloc(&c,rpt,lhH,stH);
|
|
|
|
cmChmm_t* p = cmChmmAlloc(&c,NULL,N,K,D,iV,A);
|
|
|
|
cmChmmSetGmm(p,0,cV0,uM0,sMM0,0);
|
|
cmChmmSetGmm(p,1,cV1,uM1,sMM1,0);
|
|
|
|
cmChmmPrint(p);
|
|
|
|
cmChmmGenerate(p, oM, T, sV );
|
|
|
|
cmChmmForward( p, oM, T, alphaM,logPrV );
|
|
|
|
//cmVOR_PrintL("logPrV:\n",rpt,1,T,logPrV);
|
|
|
|
cmCtxPrint(&c,"log prob:%f\n", cmVOR_Sum(logPrV,T));
|
|
|
|
cmChmmBackward( p, oM, T, betaM );
|
|
|
|
//cmVOR_PrintL("beta:\n",rpt,N,T,betaM);
|
|
|
|
cmChmmDecode(p,oM,T,qV);
|
|
|
|
cmVOU_PrintL("sV:\n",rpt,1,T,sV);
|
|
cmVOU_PrintL("qV:\n",rpt,1,T,qV);
|
|
|
|
unsigned d=0;
|
|
for(i=0; i<T; ++i)
|
|
d += sV[i] != qV[i];
|
|
|
|
cmCtxPrint(&c,"Diff:%i\n",d);
|
|
|
|
|
|
cmPlotSetup("Chmm Forward Test",1,1);
|
|
cmVOR_Transpose(oMt,oM,D,T);
|
|
cmPlotLineD(NULL, oMt, oMt+T, NULL, T, "blue", kXPlotPtId );
|
|
cmPlotDraw();
|
|
|
|
cmChmmFree(&p);
|
|
|
|
}
|
|
|
|
void cmChmmTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH )
|
|
{
|
|
|
|
time_t t = time(NULL); //0x4b9e82aa; //time(NULL);
|
|
srand( t );
|
|
printf("TIME: 0x%x\n",(unsigned)t);
|
|
|
|
//cmChmmTestForward(vReportFunc);
|
|
//return;
|
|
|
|
unsigned i;
|
|
cmReal_t iV[] = { 1.0/3.0, 1.0/3.0, 1.0/3.0 };
|
|
cmReal_t A[] = { .1, .4, .7, .4, .2, .2 };
|
|
|
|
cmReal_t cV0[] = { .7, .3 };
|
|
cmReal_t uM0[] = { .2, .1, .1, .2 };
|
|
cmReal_t sMM0[] = { .01, 0, 0, .01, .01, 0, 0, .01 };
|
|
|
|
cmReal_t cV1[] = { .2, .8 };
|
|
cmReal_t uM1[] = { .8, .9, .9, .8 };
|
|
cmReal_t sMM1[] = { .01, 0, 0, .01, .01, 0, 0, .01 };
|
|
|
|
cmReal_t cV2[] = { .5, .5 };
|
|
cmReal_t uM2[] = { .5, .5, .5, .5 };
|
|
cmReal_t sMM2[] = { .01, 0, 0, .01, .01, 0, 0, .01 };
|
|
|
|
|
|
cmReal_t kmThreshProb = 0.001;
|
|
unsigned kmMaxIterCnt = 10;
|
|
unsigned iterCnt = 20;
|
|
|
|
|
|
unsigned N = sizeof(iV) / sizeof(iV[0]);
|
|
unsigned K = sizeof(cV0) / sizeof(cV0[0]);
|
|
unsigned D = sizeof(uM0) / sizeof(uM0[0]) / K;
|
|
unsigned T = 100;
|
|
|
|
cmReal_t alphaM[N*T];
|
|
cmReal_t oM[D*T];
|
|
unsigned sV[T];
|
|
unsigned qV[T];
|
|
|
|
cmCtx c;
|
|
cmCtxAlloc(&c,rpt,lhH,stH);
|
|
|
|
cmCtxPrint(&c,"N:%i K:%i D:%i\n",N,K,D);
|
|
|
|
cmChmm_t* p = cmChmmAlloc(&c,NULL,N,K,D,iV,A);
|
|
|
|
cmChmmSetGmm(p,0,cV0,uM0,sMM0,0);
|
|
cmChmmSetGmm(p,1,cV1,uM1,sMM1,0);
|
|
cmChmmSetGmm(p,2,cV2,uM2,sMM2,0);
|
|
|
|
// generate data using the parameters above
|
|
cmChmmGenerate(p,oM,T,sV);
|
|
|
|
cmVOU_PrintL("sV: ",rpt,1,T,sV);
|
|
|
|
cmChmmRandomize(p,oM,T);
|
|
|
|
if(cmChmmSegKMeans(p,oM,T,kmThreshProb,kmMaxIterCnt,iterCnt) != cmOkRC )
|
|
goto errLabel;
|
|
|
|
|
|
if( cmChmmTrain(p,oM,T,iterCnt,0,0) != cmOkRC )
|
|
goto errLabel;
|
|
|
|
//cmChmmPrint(p);
|
|
|
|
cmChmmDecode(p,oM,T,qV);
|
|
|
|
cmReal_t pr = cmChmmForward(p,oM,T,alphaM,NULL);
|
|
|
|
cmCtxPrint(&c,"pr:%f\n",pr);
|
|
cmVOU_PrintL("sV:\n",rpt,1,T,sV);
|
|
cmVOU_PrintL("qV:\n",rpt,1,T,qV);
|
|
|
|
unsigned d=0;
|
|
for(i=0; i<T; ++i)
|
|
d += sV[i] != qV[i];
|
|
|
|
cmCtxPrint(&c,"Diff:%i\n",d);
|
|
|
|
errLabel:
|
|
|
|
|
|
cmChmmFree(&p);
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
cmChord* cmChordAlloc( cmCtx* c, cmChord* ap, const cmReal_t* chromaM, unsigned T )
|
|
{
|
|
unsigned i,j;
|
|
unsigned S = 6;
|
|
unsigned N = 24;
|
|
unsigned D = 12;
|
|
cmChord* p = cmObjAlloc(cmChord,c,ap);
|
|
|
|
p->h = cmChmmAlloc( p->obj.ctx, NULL, 0, 0, 0, NULL, NULL );
|
|
|
|
if( chromaM != NULL && T > 0 )
|
|
if( cmChordInit(p,chromaM,T) != cmOkRC )
|
|
cmChordFree(&p);
|
|
|
|
p->N = N;
|
|
p->D = D;
|
|
p->S = kTonalSpaceDimCnt;
|
|
|
|
/*
|
|
// iv[N] aM[N*N] uM[D*N] sMM[D*D*N] phiM[D*S] tsxxxV[S]
|
|
unsigned n = ALIGN_T(N, cmReal_t) +
|
|
ALIGN_T(N*N, cmReal_t) +
|
|
ALIGN_T(D*N, cmReal_t) +
|
|
ALIGN_T(D*D*N,cmReal_t) +
|
|
ALIGN_T(D*S, cmReal_t) +
|
|
2*ALIGN_T(S, cmReal_t);
|
|
|
|
p->iV = cmArrayResizeZ(c, &p->memA, n, cmReal_t);
|
|
p->aM = p->iV + ALIGN_T(N, cmReal_t);
|
|
p->uM = p->aM + ALIGN_T(N*N, cmReal_t);
|
|
p->sMM = p->uM + ALIGN_T(D*N, cmReal_t);
|
|
p->phiM = p->sMM + ALIGN_T(D*D*N,cmReal_t);
|
|
p->tsMeanV = p->phiM + ALIGN_T(D*S, cmReal_t);
|
|
p->tsVarV = p->tsMeanV + ALIGN_T(S, cmReal_t);
|
|
*/
|
|
|
|
p->iV = cmMemAllocZ( cmReal_t, N );
|
|
p->aM = cmMemAllocZ( cmReal_t, N*N);
|
|
p->uM = cmMemAllocZ( cmReal_t, D*N);
|
|
p->sMM = cmMemAllocZ( cmReal_t, D*D*N );
|
|
p->phiM = cmMemAllocZ( cmReal_t, D*S);
|
|
p->tsMeanV = cmMemAllocZ( cmReal_t, S );
|
|
p->tsVarV = cmMemAllocZ( cmReal_t, S );
|
|
|
|
|
|
// initialize iV[N] (HMM initial state probabilities)
|
|
cmVOR_Fill(p->iV,N,1.0/N);
|
|
|
|
|
|
// initialize aM[N,N] (HMM transition matrix)
|
|
cmReal_t epsilon = 0.01;
|
|
cmReal_t CMaj2any[] = { 12, 2, 8, 6, 4, 10, 0, 10, 4, 6, 8, 2, 5, 5, 9, 1, 11, 3, 7, 7, 3, 11, 1, 9 };
|
|
|
|
for(i=0; i<N; ++i)
|
|
{
|
|
cmVOR_Copy( p->aM+(i*N), N, CMaj2any );
|
|
cmVOR_Rotate( CMaj2any, N, 1 );
|
|
}
|
|
|
|
cmVOR_AddVS(p->aM, N*N, epsilon);
|
|
cmVOR_DivVS(p->aM, N*N, ( (N/2)*(N/2) ) + (N*epsilon) );
|
|
|
|
//cmVOR_PrintL("A:\n",p->obj.err.rpt,N,N,A);
|
|
|
|
|
|
// initialize sMM[D*D,N] (HMM covariance matrices)
|
|
cmReal_t diagMV[] = { 1, 0.2, 0.2, 0.2, 1.0, 0.2, 0.2, 1.0, 0.2, 0.2, 0.2, 0.2 };
|
|
cmReal_t diagmV[] = { 1, 0.2, 0.2, 1.0, 0.2, 0.2, 0.2, 1.0, 0.2, 0.2, 0.2, 0.2 };
|
|
cmReal_t Maj[D*D];
|
|
cmReal_t Min[D*D];
|
|
cmVOR_DiagZ(Maj,D,diagMV);
|
|
Maj[ (4*D) + 0 ] = 0.6; Maj[ (0*D) + 4 ] = 0.6;
|
|
Maj[ (7*D) + 0 ] = 0.8; Maj[ (0*D) + 7 ] = 0.8;
|
|
Maj[ (7*D) + 4 ] = 0.8; Maj[ (4*D) + 7 ] = 0.8;
|
|
|
|
|
|
cmVOR_DiagZ(Min,D,diagmV);
|
|
Min[ (3*D) + 0 ] = 0.6; Min[ (0*D) + 3 ] = 0.6;
|
|
Min[ (7*D) + 0 ] = 0.8; Min[ (0*D) + 7 ] = 0.8;
|
|
Min[ (7*D) + 3 ] = 0.8; Min[ (3*D) + 7 ] = 0.8;
|
|
|
|
cmReal_t* sM = p->sMM;
|
|
for(i=0; i<N/2; ++i,sM+=D*D)
|
|
cmVOR_RotateM( sM, D, D, Maj, i, i );
|
|
|
|
for(i=0; i<N/2; ++i,sM+=D*D)
|
|
cmVOR_RotateM( sM, D, D, Min, i, i );
|
|
|
|
|
|
/*
|
|
cmVOR_PrintL("Maj:\n",p->obj.err.rpt,D,D,Maj);
|
|
cmVOR_PrintL("Min:\n",p->obj.err.rpt,D,D,Min);
|
|
|
|
for(i=0; i<N; ++i)
|
|
{
|
|
cmCtxPrint(c,"%i----\n",i);
|
|
cmVOR_PrintL("sM:\n",p->obj.err.rpt,D,D,sMM + (i*D*D));
|
|
}
|
|
*/
|
|
|
|
// initialize uM[D,N] (HMM GMM mean vectors)
|
|
cmVOR_Fill(p->uM,D*N,0);
|
|
for(i=0; i<D; ++i)
|
|
{
|
|
unsigned dom = (i+7) % D;
|
|
unsigned medM = (i+4) % D;
|
|
unsigned medm = (i+3) % D;
|
|
|
|
p->uM[ (i * D) + i ] = 1;
|
|
p->uM[ (i * D) + medM ] = 1;
|
|
p->uM[ (i * D) + dom ] = 1;
|
|
|
|
p->uM[ ((i+D) * D) + i ] = 1;
|
|
p->uM[ ((i+D) * D) + medm ] = 1;
|
|
p->uM[ ((i+D) * D) + dom ] = 1;
|
|
}
|
|
|
|
cmVOR_AddVS(p->uM,D*N,0.01);
|
|
|
|
for(i=0; i<N; ++i)
|
|
cmVOR_NormalizeProbability( p->uM + (i*D), D);
|
|
|
|
|
|
// initialize phiM[D,S]
|
|
cmReal_t phi[D*S];
|
|
for(i=0,j=0; i<D; ++i,++j)
|
|
phi[j] = sin( M_PI*7.0*i/6.0 );
|
|
|
|
for(i=0; i<D; ++i,++j)
|
|
phi[j] = cos( M_PI*7.0*i/6.0 );
|
|
|
|
for(i=0; i<D; ++i,++j)
|
|
phi[j] = sin( M_PI*3.0*i/2.0 );
|
|
|
|
for(i=0; i<D; ++i,++j)
|
|
phi[j] = cos( M_PI*3.0*i/2.0 );
|
|
|
|
for(i=0; i<D; ++i,++j)
|
|
phi[j] = 0.5 * sin( M_PI*2.0*i/3.0 );
|
|
|
|
for(i=0; i<D; ++i,++j)
|
|
phi[j] = 0.5 * cos( M_PI*2.0*i/3.0 );
|
|
|
|
cmVOR_Transpose(p->phiM,phi,D,S);
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
cmRC_t cmChordFree( cmChord** pp )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
cmChord* p = *pp;
|
|
|
|
if( pp == NULL || *pp == NULL )
|
|
return cmOkRC;
|
|
|
|
if((rc = cmChordFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
cmChmmFree( &p->h );
|
|
|
|
cmMemPtrFree(&p->iV);
|
|
cmMemPtrFree(&p->aM);
|
|
cmMemPtrFree(&p->uM);
|
|
cmMemPtrFree(&p->sMM);
|
|
cmMemPtrFree(&p->phiM);
|
|
cmMemPtrFree(&p->tsMeanV);
|
|
cmMemPtrFree(&p->tsVarV);
|
|
|
|
cmMemPtrFree(&p->chromaM);
|
|
cmMemPtrFree(&p->tsM);
|
|
cmMemPtrFree(&p->cdtsV);
|
|
|
|
|
|
cmObjFree(pp);
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmChordInit( cmChord* p, const cmReal_t* chromaM, unsigned T )
|
|
{
|
|
cmRC_t rc = cmOkRC;
|
|
unsigned i;
|
|
unsigned N = p->N; // count of states
|
|
unsigned K = 1; // count of components per mixture
|
|
unsigned D = p->D; // dimensionality of the observation vector
|
|
unsigned S = p->S; //
|
|
cmReal_t alpha = 6.63261; // alpha norm coeff
|
|
|
|
if((rc = cmChordFinal(p)) != cmOkRC )
|
|
return rc;
|
|
|
|
// Create the hidden markov model
|
|
cmChmmInit( p->h, N, K, D, p->iV, p->aM);
|
|
|
|
// load the GMM's for each markov state
|
|
cmReal_t mixCoeff = 1.0;
|
|
bool diagFl = false;
|
|
for(i=0; i<N; ++i)
|
|
if((rc = cmChmmSetGmm(p->h, i, &mixCoeff, p->uM + (i*D), p->sMM+(i*D*D), diagFl )) != cmOkRC )
|
|
return rc;
|
|
|
|
|
|
// Allocate memory
|
|
// chromaM[D,T] tsM[S,T] cdtsV[T]
|
|
/*
|
|
unsigned n = ALIGN_T(D*T,cmReal_t) + ALIGN_T(S*T,cmReal_t) + ALIGN_T(T,cmReal_t);
|
|
p->chromaM = cmArrayResizeZ(c, &p->memB, n, cmReal_t);
|
|
p->tsM = p->chromaM + ALIGN_T(D*T,cmReal_t);
|
|
p->cdtsV = p->tsM + ALIGN_T(S*T,cmReal_t);
|
|
p->T = T;
|
|
*/
|
|
|
|
p->chromaM = cmMemResizeZ( cmReal_t, p->chromaM, p->D*T );
|
|
p->tsM = cmMemResizeZ( cmReal_t, p->tsM, p->S*T );
|
|
p->cdtsV = cmMemResizeZ( cmReal_t, p->cdtsV, p->D*T );
|
|
p->T = T;
|
|
|
|
// Allocate local memory
|
|
// qV[], triadIntV[] triadSeqV[] tsNormsV[]
|
|
/*
|
|
n = 2*ALIGN_B(T,unsigned) + ALIGN_B(T,int) + ALIGN_B(T,cmReal_t);
|
|
cmArray mem;
|
|
cmArrayAlloc(c, &mem);
|
|
|
|
unsigned* qV = (unsigned*) cmArrayResize(c, &mem, n, char);
|
|
unsigned* triadSeqV = (unsigned*) (qV + ALIGN_T(T,unsigned));
|
|
int* triadIntV = (int*) (triadSeqV + ALIGN_T(T,unsigned));
|
|
cmReal_t* tsNormsV = (cmReal_t*) (triadIntV + ALIGN_T(T,int));
|
|
*/
|
|
|
|
//unsigned qV[T];
|
|
//unsigned triadSeqV[T];
|
|
//int triadIntV[T];
|
|
//cmReal_t tsNormsV[T];
|
|
|
|
|
|
unsigned* qV = cmMemAlloc( unsigned, T );
|
|
unsigned* triadSeqV = cmMemAlloc( unsigned, T );
|
|
int* triadIntV = cmMemAlloc( int, T );
|
|
cmReal_t* tsNormsV = cmMemAlloc( cmReal_t, T );
|
|
|
|
|
|
// Take the alpha norm of chroma and store the result in p->chromaM[]
|
|
for(i=0; i<T; ++i)
|
|
p->chromaM[i] = cmVOR_AlphaNorm( chromaM + (i*D), D, alpha);
|
|
|
|
cmVOR_DivVVS(p->chromaM,D*T,chromaM, cmVOR_AlphaNorm(p->chromaM,T,alpha));
|
|
|
|
|
|
// Train the HMM iniital state prob. p->h->iV[] and transition matrix p->h->aM[]
|
|
unsigned flags = kNoTrainMixCoeffChmmFl | kNoTrainMeanChmmFl | kNoTrainCovarChmmFl;
|
|
unsigned iterCnt = 40;
|
|
if( chromaM != NULL && T > 0 )
|
|
if((rc = cmChmmTrain(p->h, p->chromaM, p->T, iterCnt, 0, flags )) != cmOkRC )
|
|
goto errLabel;
|
|
|
|
// Find the most likely chords using a Viterbi decoding of the chroma.
|
|
cmChmmDecode(p->h,p->chromaM,T,qV);
|
|
|
|
|
|
// Reorder the chord sequence cmcording to circle of fifths
|
|
unsigned map[] = {0, 14, 4, 18, 8, 22, 12, 2, 16, 6, 20, 10, 17, 7, 21, 11, 1, 15, 5, 19, 9, 23, 13, 3 };
|
|
|
|
for(i=0; i<T; ++i)
|
|
qV[i] = map[ qV[i] ];
|
|
|
|
//cmVOU_PrintL("qV:\n",p->obj.err.rpt,1,T,qV);
|
|
|
|
// Smooth the chord sequence with a median filter.
|
|
cmVOU_MedianFilt(qV,T,3,triadSeqV,1);
|
|
|
|
//cmVOU_PrintL("triadSeqV:\n",p->obj.err.rpt,1,T,triadSeqV);
|
|
|
|
// Calculate the chord change distance on the circle of fifths.
|
|
int d = 0;
|
|
for(i=0; i<T; ++i)
|
|
{
|
|
int v = abs(d);
|
|
|
|
assert(v<N);
|
|
|
|
v = (v<=11) ? v : -(12-(v-12));
|
|
|
|
if( i > 0 )
|
|
triadIntV[i-1] = (d < 0 ? -1 : 1) * v;
|
|
|
|
if( i + 1 < T)
|
|
d = triadSeqV[i+1] - triadSeqV[i];
|
|
}
|
|
|
|
// Project chroma into a 6D tonal space.
|
|
cmVOR_MultMMM( p->tsM,S,T,p->phiM,p->chromaM,D);
|
|
|
|
// Find the norm of p->tsM[6,T]
|
|
cmVOR_Fill(tsNormsV,T,0);
|
|
for(i=0; i<T; ++i)
|
|
tsNormsV[i] = cmVOR_MultSumVV( p->tsM + (i*S), p->tsM + (i*S), S );
|
|
|
|
cmVOR_PowVS(tsNormsV,T,0.5);
|
|
|
|
// Take the cosine distance.
|
|
p->cdtsV[0] = 1;
|
|
for(i=1; i<T; ++i)
|
|
p->cdtsV[i] = cmVOR_MultSumVV( p->tsM + ((i-1)*S), p->tsM + (i*S), S );
|
|
|
|
for(i=0; i<T-1; ++i)
|
|
p->cdtsV[i+1] /= tsNormsV[i] * tsNormsV[i+1];
|
|
|
|
//cmVOR_PrintL("tsNormsV:\n",p->obj.err.rpt,1,T,tsNormsV);
|
|
//cmVOR_PrintL("CDTS:\n", p->obj.err.rpt,1,T,p->cdtsV);
|
|
|
|
|
|
p->triadSeqMode = cmVOU_Mode(triadSeqV,T);
|
|
p->triadSeqVar = cmVOU_Variance(triadSeqV,T,NULL);
|
|
p->triadIntMean = cmVOI_Mean(triadIntV,T);
|
|
p->triadIntVar = cmVOI_Variance(triadIntV,T,&p->triadIntMean);
|
|
|
|
|
|
cmVOR_MeanM( p->tsMeanV, p->tsM, S, T, 1 );
|
|
cmVOR_VarianceM( p->tsVarV, p->tsM, S, T, p->tsMeanV, 1);
|
|
|
|
p->cdtsMean = cmVOR_Mean( p->cdtsV, T );
|
|
p->cdtsVar = cmVOR_Variance( p->cdtsV, T, &p->cdtsMean );
|
|
|
|
/*
|
|
cmReal_t tsMt[T*S];
|
|
cmKbRecd kb;
|
|
cmPlotInitialize(NULL);
|
|
cmPlotSetup("Chords",1,1);
|
|
cmCtxPrint(c,"%s\n","press any key");
|
|
//cmPlotLineD( NULL, NULL, p->cdtsV, NULL, T, NULL, kSolidPlotLineId );
|
|
cmVOR_Transpose(tsMt,p->tsM,S,T);
|
|
cmPlotLineMD(NULL, tsMt, NULL, T, S, kSolidPlotLineId);
|
|
cmPlotDraw();
|
|
cmKeyPress(&kb);
|
|
cmPlotFinalize();
|
|
*/
|
|
|
|
errLabel:
|
|
cmMemPtrFree(&qV);
|
|
cmMemPtrFree(&triadSeqV);
|
|
cmMemPtrFree(&triadIntV);
|
|
cmMemPtrFree(&tsNormsV);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmRC_t cmChordFinal( cmChord* p )
|
|
{ return cmOkRC; }
|
|
|
|
void cmChordTest( cmRpt_t* rpt, cmLHeapH_t lhH, cmSymTblH_t stH )
|
|
{
|
|
cmCtx c;
|
|
cmCtxAlloc(&c,rpt,lhH,stH);
|
|
|
|
cmChord* p = cmChordAlloc(&c,NULL,NULL,0);
|
|
|
|
cmChordFree(&p);
|
|
}
|
|
|
|
|