2012-11-15 04:01:05 +00:00
# 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 "cmFloatTypes.h"
# include "cmComplexTypes.h"
# include "cmFileSys.h"
# include "cmJson.h"
# include "cmSymTbl.h"
# include "cmAudioFile.h"
2013-01-14 00:47:50 +00:00
# include "cmText.h"
2012-11-15 04:01:05 +00:00
# include "cmProcObj.h"
# include "cmProcTemplate.h"
# include "cmMath.h"
# include "cmProc.h"
# include "cmVectOps.h"
# include "cmMidi.h"
# include "cmMidiFile.h"
# include "cmTimeLine.h"
# include "cmScore.h"
# include "cmProc4.h"
2013-01-16 01:38:21 +00:00
# include "cmTime.h"
2012-11-15 04:01:05 +00:00
2012-12-04 01:18:50 +00:00
cmScFol * cmScFolAlloc ( cmCtx * c , cmScFol * p , cmReal_t srate , cmScH_t scH , unsigned bufN , unsigned minWndLookAhead , unsigned maxWndCnt , unsigned minVel )
2012-11-15 04:01:05 +00:00
{
cmScFol * op = cmObjAlloc ( cmScFol , c , p ) ;
if ( srate ! = 0 )
2012-12-04 01:18:50 +00:00
if ( cmScFolInit ( op , srate , scH , bufN , minWndLookAhead , maxWndCnt , minVel ) ! = cmOkRC )
2012-11-15 04:01:05 +00:00
cmScFolFree ( & op ) ;
return op ;
}
cmRC_t cmScFolFree ( cmScFol * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return rc ;
cmScFol * p = * pp ;
if ( ( rc = cmScFolFinal ( p ) ) ! = cmOkRC )
return rc ;
unsigned i ;
for ( i = 0 ; i < p - > locN ; + + i )
2012-12-13 16:46:48 +00:00
cmMemFree ( p - > loc [ i ] . evtV ) ;
2012-11-15 04:01:05 +00:00
cmMemFree ( p - > loc ) ;
2012-11-27 07:08:12 +00:00
cmMemFree ( p - > bufV ) ;
2012-11-15 04:01:05 +00:00
cmObjFree ( pp ) ;
return rc ;
}
cmRC_t cmScFolFinal ( cmScFol * p )
2012-11-27 07:08:12 +00:00
{
cmMemFree ( p - > edWndMtx ) ;
return cmOkRC ;
}
2012-11-15 04:01:05 +00:00
void _cmScFolPrint ( cmScFol * p )
{
int i , j ;
for ( i = 0 ; i < p - > locN ; + + i )
{
2012-11-19 06:41:27 +00:00
printf ( " %2i %5i " , p - > loc [ i ] . barNumb , p - > loc [ i ] . scIdx ) ;
2012-12-13 16:46:48 +00:00
for ( j = 0 ; j < p - > loc [ i ] . evtCnt ; + + j )
printf ( " %s " , cmMidiToSciPitch ( p - > loc [ i ] . evtV [ j ] . pitch , NULL , 0 ) ) ;
2012-11-15 04:01:05 +00:00
printf ( " \n " ) ;
}
}
2012-12-04 01:18:50 +00:00
unsigned * _cmScFolAllocEditDistMtx ( unsigned maxN )
{
maxN + = 1 ;
unsigned * m = cmMemAllocZ ( unsigned , maxN * maxN ) ;
unsigned * p = m ;
unsigned i ;
// initialize the comparison matrix with the default costs in the
// first row and column
// (Note that this matrix is not oriented in column major order like most 'cm' matrices.)
for ( i = 0 ; i < maxN ; + + i )
{
p [ i ] = i ; // 0th row
p [ i * maxN ] = i ; // 0th col
}
return m ;
}
cmRC_t cmScFolInit ( cmScFol * p , cmReal_t srate , cmScH_t scH , unsigned bufN , unsigned minWndLookAhead , unsigned maxWndCnt , unsigned minVel )
2012-11-15 04:01:05 +00:00
{
cmRC_t rc ;
if ( ( rc = cmScFolFinal ( p ) ) ! = cmOkRC )
return rc ;
2012-12-04 01:18:50 +00:00
if ( bufN > maxWndCnt )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " The score follower buffer count (%i) must be less than the max . window length ( % i ) . " ,bufN,maxWndCnt ) ;
if ( minWndLookAhead > maxWndCnt )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " The score follower look-ahead count (%i) must be less than the max . window length ( % i ) . " ,minWndLookAhead,maxWndCnt) ;
2012-11-15 04:01:05 +00:00
p - > srate = srate ;
p - > scH = scH ;
2012-11-19 06:41:27 +00:00
p - > bufN = bufN ;
p - > bufV = cmMemResizeZ ( cmScFolBufEle_t , p - > bufV , bufN ) ;
2012-11-15 04:01:05 +00:00
p - > locN = cmScoreEvtCount ( scH ) ;
p - > loc = cmMemResizeZ ( cmScFolLoc_t , p - > loc , p - > locN ) ;
p - > sbi = cmInvalidIdx ;
p - > sei = cmInvalidIdx ;
2012-12-04 01:18:50 +00:00
p - > msln = minWndLookAhead ;
p - > mswn = maxWndCnt ;
p - > forwardCnt = 2 ;
p - > maxDist = 4 ;
p - > edWndMtx = _cmScFolAllocEditDistMtx ( p - > bufN ) ;
p - > minVel = minVel ;
p - > printFl = true ;
p - > noBackFl = true ;
2012-11-27 22:20:54 +00:00
p - > missCnt = 0 ;
p - > matchCnt = 0 ;
p - > eventIdx = 0 ;
2012-12-04 01:18:50 +00:00
p - > skipCnt = 0 ;
p - > ret_idx = cmInvalidIdx ;
2012-11-15 04:01:05 +00:00
2012-12-13 16:46:48 +00:00
// for each score location
unsigned li , ei ;
for ( li = 0 , ei = 0 ; li < cmScoreLocCount ( p - > scH ) ; + + li )
2012-11-15 04:01:05 +00:00
{
2012-12-13 16:46:48 +00:00
unsigned i , n ;
2012-11-15 04:01:05 +00:00
2012-12-13 16:46:48 +00:00
const cmScoreLoc_t * lp = cmScoreLoc ( p - > scH , li ) ;
2012-11-15 04:01:05 +00:00
2012-12-13 16:46:48 +00:00
// count the number of note events at location li
for ( n = 0 , i = 0 ; i < lp - > evtCnt ; + + i )
if ( lp - > evtArray [ i ] - > type = = kNonEvtScId )
+ + n ;
2012-11-15 04:01:05 +00:00
2012-12-13 16:46:48 +00:00
assert ( ei + n < = p - > locN ) ;
2012-11-15 04:01:05 +00:00
2012-12-13 16:46:48 +00:00
// duplicate each note at location li n times
for ( i = 0 ; i < n ; + + i )
{
unsigned j , k ;
p - > loc [ ei + i ] . evtCnt = n ;
p - > loc [ ei + i ] . evtV = cmMemAllocZ ( cmScFolEvt_t , n ) ;
p - > loc [ ei + i ] . scIdx = li ;
p - > loc [ ei + i ] . barNumb = lp - > barNumb ;
for ( j = 0 , k = 0 ; j < lp - > evtCnt ; + + j )
if ( lp - > evtArray [ j ] - > type = = kNonEvtScId )
2012-11-15 04:01:05 +00:00
{
2012-12-13 16:46:48 +00:00
p - > loc [ ei + i ] . evtV [ k ] . pitch = lp - > evtArray [ j ] - > pitch ;
p - > loc [ ei + i ] . evtV [ k ] . scEvtIdx = lp - > evtArray [ j ] - > index ;
+ + k ;
2012-11-15 04:01:05 +00:00
}
2012-12-13 16:46:48 +00:00
2012-11-15 04:01:05 +00:00
}
2012-12-13 16:46:48 +00:00
ei + = n ;
2012-11-15 04:01:05 +00:00
}
2012-12-13 16:46:48 +00:00
p - > locN = ei ;
2012-11-15 04:01:05 +00:00
//_cmScFolPrint(p);
return rc ;
}
2012-12-13 16:46:48 +00:00
cmRC_t cmScFolReset ( cmScFol * p , unsigned scEvtIdx )
2012-11-15 04:01:05 +00:00
{
2012-12-13 16:46:48 +00:00
int i , j ;
2012-11-15 04:01:05 +00:00
2012-11-19 06:41:27 +00:00
// empty the event buffer
memset ( p - > bufV , 0 , sizeof ( cmScFolBufEle_t ) * p - > bufN ) ;
2012-11-15 04:01:05 +00:00
// don't allow the score index to be prior to the first note
2012-12-13 16:46:48 +00:00
//if( scEvtIdx < p->loc[0].scIdx )
// scEvtIdx = p->loc[0].scIdx;
2012-11-19 06:41:27 +00:00
2012-12-04 01:18:50 +00:00
p - > sei = cmInvalidIdx ;
p - > sbi = cmInvalidIdx ;
p - > missCnt = 0 ;
p - > matchCnt = 0 ;
p - > eventIdx = 0 ;
p - > skipCnt = 0 ;
p - > ret_idx = cmInvalidIdx ;
2012-11-15 04:01:05 +00:00
// locate the score element in svV[] that is closest to,
2012-12-13 16:46:48 +00:00
// and possibly after, scEvtIdx.
2012-11-15 04:01:05 +00:00
for ( i = 0 ; i < p - > locN - 1 ; + + i )
2012-12-13 16:46:48 +00:00
{
for ( j = 0 ; j < p - > loc [ i ] . evtCnt ; + + j )
if ( p - > loc [ i ] . evtV [ j ] . scEvtIdx < = scEvtIdx )
p - > sbi = i ;
else
break ;
}
2012-11-15 04:01:05 +00:00
2012-11-19 06:41:27 +00:00
// locate the score element at the end of the look-ahead region
2012-12-13 16:46:48 +00:00
for ( ; i < p - > locN ; + + i )
{
for ( j = 0 ; j < p - > loc [ i ] . evtCnt ; + + j )
if ( p - > loc [ i ] . evtV [ j ] . scEvtIdx < = scEvtIdx + p - > msln )
p - > sei = i ;
}
2012-11-15 04:01:05 +00:00
return cmOkRC ;
}
bool _cmScFolIsMatch ( const cmScFolLoc_t * loc , unsigned pitch )
{
unsigned i ;
2012-12-13 16:46:48 +00:00
for ( i = 0 ; i < loc - > evtCnt ; + + i )
if ( loc - > evtV [ i ] . pitch = = pitch )
2012-11-15 04:01:05 +00:00
return true ;
return false ;
}
2012-12-04 01:18:50 +00:00
int _cmScFolMatchCost ( const cmScFolLoc_t * loc , unsigned li , const cmScFolBufEle_t * pitch , unsigned pi )
2012-11-27 22:20:54 +00:00
{
2012-12-04 01:18:50 +00:00
if ( _cmScFolIsMatch ( loc + li , pitch [ pi ] . val ) )
2012-11-27 22:20:54 +00:00
return 0 ;
if ( li > 0 & & pi > 0 )
2012-12-04 01:18:50 +00:00
if ( _cmScFolIsMatch ( loc + li - 1 , pitch [ pi ] . val ) & & _cmScFolIsMatch ( loc + li , pitch [ pi - 1 ] . val ) )
2012-11-27 22:20:54 +00:00
return 0 ;
return 1 ;
}
2012-12-04 01:18:50 +00:00
int _cmScFolDist ( unsigned mtxMaxN , unsigned * m , const cmScFolBufEle_t * s1 , const cmScFolLoc_t * s0 , int n )
2012-11-15 04:01:05 +00:00
{
mtxMaxN + = 1 ;
assert ( n < mtxMaxN ) ;
int v = 0 ;
unsigned i ;
// Note that m[maxN,maxN] is not oriented in column major order like most 'cm' matrices.
for ( i = 1 ; i < n + 1 ; + + i )
{
unsigned ii = i * mtxMaxN ; // current row
unsigned i_1 = ii - mtxMaxN ; // previous row
unsigned j ;
for ( j = 1 ; j < n + 1 ; + + j )
{
//int cost = s0[i-1] == s1[j-1] ? 0 : 1;
2012-11-27 22:20:54 +00:00
//int cost = _cmScFolIsMatch(s0 + i-1, s1[j-1]) ? 0 : 1;
int cost = _cmScFolMatchCost ( s0 , i - 1 , s1 , j - 1 ) ;
2012-11-15 04:01:05 +00:00
//m[i][j] = min( m[i-1][j] + 1, min( m[i][j-1] + 1, m[i-1][j-1] + cost ) );
m [ ii + j ] = v = cmMin ( m [ i_1 + j ] + 1 , cmMin ( m [ ii + j - 1 ] + 1 , m [ i_1 + j - 1 ] + cost ) ) ;
}
}
return v ;
}
2012-12-04 01:18:50 +00:00
void _cmScFolRpt0 ( cmScFol * p , unsigned locIdx , unsigned locN , const cmScFolBufEle_t * b , unsigned bn , unsigned min_idx )
2012-11-15 04:01:05 +00:00
{
2012-11-27 22:20:54 +00:00
unsigned i ;
int n ;
2012-11-15 04:01:05 +00:00
2012-11-27 22:20:54 +00:00
printf ( " --------------- event:%i ------------- \n " , p - > eventIdx ) ;
2012-11-15 04:01:05 +00:00
2012-11-27 22:20:54 +00:00
printf ( " loc: " ) ;
for ( i = 0 ; i < locN ; + + i )
printf ( " %4i " , i + locIdx ) ;
2012-11-15 04:01:05 +00:00
printf ( " \n " ) ;
2012-11-27 22:20:54 +00:00
for ( n = 0 , i = 0 ; i < locN ; + + i )
2012-12-13 16:46:48 +00:00
if ( p - > loc [ locIdx + i ] . evtCnt > n )
n = p - > loc [ locIdx + i ] . evtCnt ;
2012-11-15 04:01:05 +00:00
2012-11-27 22:20:54 +00:00
- - n ;
for ( ; n > = 0 ; - - n )
{
printf ( " sc%1i: " , n ) ;
for ( i = 0 ; i < locN ; + + i )
2012-11-15 04:01:05 +00:00
{
2012-12-13 16:46:48 +00:00
if ( n < p - > loc [ locIdx + i ] . evtCnt )
printf ( " %4s " , cmMidiToSciPitch ( p - > loc [ locIdx + i ] . evtV [ n ] . pitch , NULL , 0 ) ) ;
2012-11-27 22:20:54 +00:00
else
printf ( " " ) ;
2012-11-15 04:01:05 +00:00
}
2012-11-27 22:20:54 +00:00
printf ( " \n " ) ;
}
2012-11-15 04:01:05 +00:00
2012-11-27 22:20:54 +00:00
printf ( " perf: " ) ;
for ( i = 0 ; i < min_idx ; + + i )
printf ( " " ) ;
2012-11-15 04:01:05 +00:00
2012-11-27 22:20:54 +00:00
for ( i = 0 ; i < bn ; + + i )
2012-12-04 01:18:50 +00:00
printf ( " %4s " , cmMidiToSciPitch ( b [ i ] . val , NULL , 0 ) ) ;
2012-11-15 04:01:05 +00:00
2012-11-27 22:20:54 +00:00
printf ( " \n " ) ;
}
2012-11-15 04:01:05 +00:00
2012-11-27 22:20:54 +00:00
void _cmScFolRpt1 ( cmScFol * p , unsigned minDist , unsigned ret_idx , unsigned d1 , unsigned missCnt , unsigned matchCnt )
{
2012-12-04 01:18:50 +00:00
printf ( " dist:%i miss:%i match:%i skip:%i vel:%i " , minDist , missCnt , matchCnt , p - > skipCnt , d1 ) ;
if ( ret_idx ! = cmInvalidIdx )
2012-11-27 22:20:54 +00:00
printf ( " ret_idx:%i " , ret_idx ) ;
printf ( " \n " ) ;
2012-11-15 04:01:05 +00:00
}
2012-11-19 06:41:27 +00:00
unsigned cmScFolExec ( cmScFol * p , unsigned smpIdx , unsigned status , cmMidiByte_t d0 , cmMidiByte_t d1 )
{
unsigned ret_idx = cmInvalidIdx ;
2012-12-13 16:46:48 +00:00
if ( p - > sbi = = cmInvalidIdx )
{
cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " An initial score search location has not been set. " ) ;
return ret_idx ;
}
2012-11-19 06:41:27 +00:00
2012-11-27 22:20:54 +00:00
if ( status ! = kNoteOnMdId )
return ret_idx ;
+ + p - > eventIdx ;
// reject notes with very low velocity
if ( d1 < p - > minVel )
2012-12-04 01:18:50 +00:00
{
+ + p - > skipCnt ;
2012-11-19 06:41:27 +00:00
return ret_idx ;
2012-12-04 01:18:50 +00:00
}
2012-11-19 06:41:27 +00:00
// left shift bufV[] to make the right-most element available - then copy in the new element
memmove ( p - > bufV , p - > bufV + 1 , sizeof ( cmScFolBufEle_t ) * ( p - > bufN - 1 ) ) ;
p - > bufV [ p - > bufN - 1 ] . smpIdx = smpIdx ;
p - > bufV [ p - > bufN - 1 ] . val = d0 ;
p - > bufV [ p - > bufN - 1 ] . validFl = true ;
// fill in ebuf[] with the valid values in bufV[]
2012-12-04 01:18:50 +00:00
int en = cmMin ( p - > eventIdx , p - > bufN ) ;
2012-12-13 16:46:48 +00:00
int bbi = p - > eventIdx > = p - > bufN ? 0 : p - > bufN - p - > eventIdx ;
2012-11-19 06:41:27 +00:00
// en is the count of valid elements in ebuf[].
// ebuf[p->boi] is the first valid element
int j = 0 ;
int minDist = INT_MAX ;
int minIdx = cmInvalidIdx ;
int dist ;
// the score wnd must always be as long as the buffer n
// at the end of the score this may not be the case
// (once sei hits locN - at this point we must begin
// shrinking ewnd[] to contain only the last p->sei-p->sbi+1 elements)
assert ( p - > sei - p - > sbi + 1 > = en ) ;
2012-11-27 22:20:54 +00:00
for ( j = 0 ; p - > sbi + en + j - 1 < = p - > sei ; + + j )
{
// use <= minDist to choose the latest window with the lowest match
2012-12-13 16:46:48 +00:00
if ( ( dist = _cmScFolDist ( p - > bufN , p - > edWndMtx , p - > bufV + bbi , p - > loc + p - > sbi + j , en ) ) < minDist )
2012-11-19 06:41:27 +00:00
{
2012-12-04 01:18:50 +00:00
// only make an eql match if the posn is greater than the last location
if ( dist = = minDist & & p - > ret_idx ! = cmInvalidId & & p - > ret_idx > = p - > sbi + minIdx + en - 1 )
continue ;
2012-11-19 06:41:27 +00:00
minDist = dist ;
minIdx = j ;
}
2012-11-27 22:20:54 +00:00
}
2012-11-19 06:41:27 +00:00
// The best fit is on the score window: p->loc[sbi+minIdx : sbi+minIdx+en-1 ]
2012-11-27 22:20:54 +00:00
if ( p - > printFl )
2012-12-13 16:46:48 +00:00
_cmScFolRpt0 ( p , p - > sbi , p - > sei - p - > sbi + 1 , p - > bufV + bbi , en , minIdx ) ;
2012-11-27 22:20:54 +00:00
// save current missCnt for later printing
unsigned missCnt = p - > missCnt ;
2012-11-19 06:41:27 +00:00
// if a perfect match occurred
if ( minDist = = 0 )
{
2012-12-04 01:18:50 +00:00
ret_idx = p - > sbi + minIdx + en - 1 ;
p - > missCnt = 0 ;
2012-11-19 06:41:27 +00:00
// we had a perfect match - shrink the window to it's minumum size
p - > sbi + = ( en = = p - > bufN ) ? minIdx + 1 : 0 ; // move wnd begin forward to just past first match
2012-12-04 01:18:50 +00:00
p - > sei = p - > sbi + minIdx + en + p - > msln ; // move wnd end forward to lead by the min look-ahead
2012-11-27 22:20:54 +00:00
2012-11-19 06:41:27 +00:00
}
else
{
2012-12-04 01:18:50 +00:00
if ( minDist > p - > maxDist )
ret_idx = cmInvalidIdx ;
else
2012-11-19 06:41:27 +00:00
// if the last event matched - then return the match location as the current score location
2012-12-04 01:18:50 +00:00
if ( _cmScFolIsMatch ( p - > loc + ( p - > sbi + minIdx + en - 1 ) , p - > bufV [ p - > bufN - 1 ] . val ) )
2012-11-27 22:20:54 +00:00
{
ret_idx = p - > sbi + minIdx + en - 1 ;
p - > missCnt = 0 ;
2012-12-04 01:18:50 +00:00
// this is probably a pretty good match reduce the part of the window prior to
// the first match (bring the end of the window almost up to the end of the
// buffers sync position)
2012-11-27 22:20:54 +00:00
if ( en > = p - > bufN - 1 & & ( en + 2 ) < = ret_idx )
p - > sbi = ret_idx - ( en + 2 ) ;
2012-12-04 01:18:50 +00:00
2012-11-27 22:20:54 +00:00
}
2012-12-04 01:18:50 +00:00
else // the last event does not match based on the optimal edit-distance alignment
2012-11-27 22:20:54 +00:00
{
// Look backward from the closest match location for a match to the current pitch.
// The backward search scope is limited by the current value of 'missCnt'.
2012-12-13 16:46:48 +00:00
unsigned i ;
2012-11-27 22:20:54 +00:00
j = p - > sbi + minIdx + en - 2 ;
for ( i = 1 ; i + 1 < = p - > bufN & & j > = p - > sbi & & i < = p - > missCnt ; + + i , - - j )
{
// if this look-back location already matched then stop the backward search
2012-12-04 01:18:50 +00:00
if ( _cmScFolIsMatch ( p - > loc + j , p - > bufV [ p - > bufN - 1 - i ] . val ) )
2012-11-27 22:20:54 +00:00
break ;
// does this look-back location match the current pitch
2012-12-04 01:18:50 +00:00
if ( _cmScFolIsMatch ( p - > loc + j , p - > bufV [ p - > bufN - 1 ] . val ) )
2012-11-27 22:20:54 +00:00
{
ret_idx = j ;
p - > missCnt = i ; // set missCnt to the cnt of steps backward necessary for a match
break ;
}
}
// If the backward search did not find a match - look forward
if ( ret_idx = = cmInvalidIdx )
{
2012-12-13 16:46:48 +00:00
unsigned i ;
2012-11-27 22:20:54 +00:00
j = p - > sbi + minIdx + en ;
2012-12-04 01:18:50 +00:00
for ( i = 0 ; j < = p - > sei & & i < p - > forwardCnt ; + + i , + + j )
if ( _cmScFolIsMatch ( p - > loc + j , p - > bufV [ p - > bufN - 1 ] . val ) )
2012-11-27 22:20:54 +00:00
{
ret_idx = j ;
break ;
}
p - > missCnt = ret_idx = = cmInvalidIdx ? p - > missCnt + 1 : 0 ;
}
}
2012-11-19 06:41:27 +00:00
2012-12-04 01:18:50 +00:00
// Adjust the end window position (sei) based on the match location
if ( ret_idx = = cmInvalidIdx )
{
// even though we didn't match move the end of the score window forward
// this will enlarge the score window by one
p - > sei + = 1 ;
}
else
{
assert ( p - > sei > = ret_idx ) ;
// force sei to lead by min look-ahead
if ( p - > sei - ret_idx < p - > msln )
p - > sei = ret_idx + p - > msln ;
}
2012-11-19 06:41:27 +00:00
assert ( p - > sei > p - > sbi ) ;
2012-12-04 01:18:50 +00:00
// Adjust the begin window position
if ( p - > noBackFl & & ret_idx ! = cmInvalidIdx & & en > = p - > bufN & & p - > sbi > p - > bufN )
p - > sbi = ret_idx - p - > bufN ;
2012-11-19 06:41:27 +00:00
// if the score window length surpasses the max score window size
// move the beginning index forward
if ( p - > sei - p - > sbi + 1 > p - > mswn & & p - > sei > p - > mswn )
p - > sbi = p - > sei - p - > mswn + 1 ;
}
2012-11-27 22:20:54 +00:00
if ( p - > printFl )
_cmScFolRpt1 ( p , minDist , ret_idx , d1 , missCnt , p - > matchCnt ) ;
2012-12-04 01:18:50 +00:00
// don't allow the returned location to repeat or go backwards
if ( p - > noBackFl & & p - > ret_idx ! = cmInvalidIdx & & ret_idx < = p - > ret_idx )
ret_idx = cmInvalidIdx ;
// track the number of consecutive matches
2012-11-27 22:20:54 +00:00
if ( ret_idx = = cmInvalidIdx )
p - > matchCnt = 0 ;
else
2012-12-04 01:18:50 +00:00
{
2012-11-27 22:20:54 +00:00
+ + p - > matchCnt ;
2012-12-04 01:18:50 +00:00
p - > ret_idx = ret_idx ;
}
// Force the window to remain valid when it is at the end of the score
// - sbi and sei must be inside 0:locN
// - sei-sbi + 1 must be >= en
if ( p - > sei > = p - > locN )
{
p - > sei = p - > locN - 1 ;
p - > sbi = p - > sei - p - > bufN + 1 ;
}
2012-12-13 16:46:48 +00:00
if ( ret_idx ! = cmInvalidIdx )
ret_idx = p - > loc [ ret_idx ] . scIdx ;
return ret_idx ;
}
//=======================================================================================================================
cmScTrk * cmScTrkAlloc ( cmCtx * c , cmScTrk * p , cmReal_t srate , cmScH_t scH , unsigned bufN , unsigned minWndLookAhead , unsigned maxWndCnt , unsigned minVel )
{
cmScTrk * op = cmObjAlloc ( cmScTrk , c , p ) ;
op - > sfp = cmScFolAlloc ( c , NULL , srate , scH , bufN , minWndLookAhead , maxWndCnt , minVel ) ;
if ( srate ! = 0 )
if ( cmScTrkInit ( op , srate , scH , bufN , minWndLookAhead , maxWndCnt , minVel ) ! = cmOkRC )
cmScTrkFree ( & op ) ;
return op ;
}
cmRC_t cmScTrkFree ( cmScTrk * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return rc ;
cmScTrk * p = * pp ;
if ( ( rc = cmScTrkFinal ( p ) ) ! = cmOkRC )
return rc ;
cmScFolFree ( & p - > sfp ) ;
cmObjFree ( pp ) ;
return rc ;
}
void _cmScTrkPrint ( cmScTrk * p )
{
int i , j ;
for ( i = 0 ; i < p - > locN ; + + i )
{
printf ( " %2i %5i " , p - > loc [ i ] . barNumb , p - > loc [ i ] . scIdx ) ;
for ( j = 0 ; j < p - > loc [ i ] . evtCnt ; + + j )
printf ( " %s " , cmMidiToSciPitch ( p - > loc [ i ] . evtV [ j ] . pitch , NULL , 0 ) ) ;
printf ( " \n " ) ;
}
}
cmRC_t cmScTrkInit ( cmScTrk * p , cmReal_t srate , cmScH_t scH , unsigned bufN , unsigned minWndLookAhead , unsigned maxWndCnt , unsigned minVel )
{
cmRC_t rc ;
if ( ( rc = cmScTrkFinal ( p ) ) ! = cmOkRC )
return rc ;
if ( minWndLookAhead > maxWndCnt )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " The score follower look-ahead count (%i) must be less than the max . window length ( % i ) . " ,minWndLookAhead,maxWndCnt) ;
if ( ( rc = cmScFolInit ( p - > sfp , srate , scH , bufN , minWndLookAhead , maxWndCnt , minVel ) ) ! = cmOkRC )
return rc ;
p - > srate = srate ;
p - > scH = scH ;
p - > locN = cmScoreLocCount ( scH ) ;
p - > loc = cmMemResizeZ ( cmScTrkLoc_t , p - > loc , p - > locN ) ;
p - > minVel = minVel ;
p - > maxWndCnt = maxWndCnt ;
2013-01-14 00:47:50 +00:00
p - > minWndLookAhead = 4 ; //minWndLookAhead;
p - > printFl = true ;
2012-12-13 16:46:48 +00:00
p - > curLocIdx = cmInvalidIdx ;
p - > evtIndex = 0 ;
// for each score location
unsigned li ;
for ( li = 0 ; li < cmScoreLocCount ( p - > scH ) ; + + li )
{
unsigned i , j , k , n ;
const cmScoreLoc_t * lp = cmScoreLoc ( p - > scH , li ) ;
// count the number of note events at location li
for ( n = 0 , i = 0 ; i < lp - > evtCnt ; + + i )
if ( lp - > evtArray [ i ] - > type = = kNonEvtScId )
+ + n ;
p - > loc [ li ] . evtCnt = n ;
p - > loc [ li ] . evtV = cmMemAllocZ ( cmScTrkEvt_t , n ) ;
p - > loc [ li ] . scIdx = li ;
p - > loc [ li ] . barNumb = lp - > barNumb ;
for ( j = 0 , k = 0 ; j < lp - > evtCnt ; + + j )
if ( lp - > evtArray [ j ] - > type = = kNonEvtScId )
{
p - > loc [ li ] . evtV [ k ] . pitch = lp - > evtArray [ j ] - > pitch ;
p - > loc [ li ] . evtV [ k ] . scEvtIdx = lp - > evtArray [ j ] - > index ;
+ + k ;
}
}
//_cmScTrkPrint(p);
return rc ;
}
cmRC_t cmScTrkFinal ( cmScTrk * p )
{
unsigned i ;
for ( i = 0 ; i < p - > locN ; + + i )
cmMemPtrFree ( & p - > loc [ i ] . evtV ) ;
return cmOkRC ;
}
cmRC_t cmScTrkReset ( cmScTrk * p , unsigned scEvtIdx )
{
unsigned i ;
cmScFolReset ( p - > sfp , scEvtIdx ) ;
p - > curLocIdx = cmInvalidIdx ;
p - > evtIndex = 0 ;
// locate the score element in svV[] that is closest to,
// and possibly after, scEvtIdx.
for ( i = 0 ; i < p - > locN ; + + i )
{
unsigned j ;
for ( j = 0 ; j < p - > loc [ i ] . evtCnt ; + + j )
{
p - > loc [ i ] . evtV [ j ] . matchFl = false ;
// it is possible that scEvtIdx is before the first event included in p->loc[0]
// using the p->curLocIdx==cmInvalidIdx forces the first evt in p->loc[0] to be
// selected in this case
if ( p - > loc [ i ] . evtV [ j ] . scEvtIdx < = scEvtIdx | | p - > curLocIdx = = cmInvalidIdx )
p - > curLocIdx = i ;
}
}
if ( p - > curLocIdx = = cmInvalidIdx )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " The initial score search location event %i was not found. " , scEvtIdx ) ;
return cmOkRC ;
}
unsigned _cmScTrkIsMatch ( cmScTrk * p , int d , unsigned pitch )
{
if ( 0 < = p - > curLocIdx + d & & p - > curLocIdx + 1 < p - > locN )
{
unsigned i ;
const cmScTrkLoc_t * lp = p - > loc + p - > curLocIdx + d ;
for ( i = 0 ; i < lp - > evtCnt ; + + i )
if ( lp - > evtV [ i ] . pitch = = pitch & & lp - > evtV [ i ] . matchFl = = false )
return i ;
}
return cmInvalidIdx ;
}
void _cmScTrkRpt0 ( cmScTrk * p , unsigned pitch , unsigned vel , unsigned nli , unsigned nei )
{
bool missFl = nli = = cmInvalidIdx | | nei = = cmInvalidIdx ;
printf ( " ------- event:%i %s vel:%i cur:%i new:%i %s------- \n " , p - > evtIndex , cmMidiToSciPitch ( pitch , NULL , 0 ) , vel , p - > curLocIdx , nli , missFl ? " MISS " : " " ) ;
int bi = p - > curLocIdx < p - > minWndLookAhead ? 0 : p - > curLocIdx - p - > minWndLookAhead ;
int ei = cmMin ( p - > locN - 1 , p - > curLocIdx + p - > minWndLookAhead ) ;
unsigned i , n = 0 ;
for ( i = bi ; i < = ei ; + + i )
if ( p - > loc [ i ] . evtCnt > n )
n = p - > loc [ i ] . evtCnt ;
printf ( " loc " ) ;
for ( i = bi ; i < = ei ; + + i )
printf ( " %4i " , i ) ;
printf ( " \n " ) ;
for ( i = 0 ; i < n ; + + i )
{
unsigned j ;
printf ( " sc%2i " , i ) ;
for ( j = bi ; j < = ei ; + + j )
{
if ( i < p - > loc [ j ] . evtCnt )
{
char * X = p - > loc [ j ] . evtV [ i ] . matchFl ? " __ " : " " ;
if ( nli = = j & & nei = = i )
{
X = " ** " ;
assert ( p - > loc [ j ] . evtV [ i ] . pitch = = pitch ) ;
}
printf ( " %4s%s " , cmMidiToSciPitch ( p - > loc [ j ] . evtV [ i ] . pitch , NULL , 0 ) , X ) ;
}
else
printf ( " " ) ;
}
printf ( " \n " ) ;
}
}
unsigned cmScTrkExec ( cmScTrk * p , unsigned smpIdx , unsigned status , cmMidiByte_t d0 , cmMidiByte_t d1 )
{
unsigned ret_idx = cmInvalidIdx ;
//cmScFolExec(p->sfp, smpIdx, status, d0, d1);
if ( status ! = kNoteOnMdId )
return cmInvalidIdx ;
if ( p - > curLocIdx = = cmInvalidIdx )
{
cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " An initial score search location has not been set. " ) ;
return cmInvalidIdx ;
}
int i , nei , nli = cmInvalidIdx ;
// try to match curLocIdx first
if ( ( nei = _cmScTrkIsMatch ( p , 0 , d0 ) ) ! = cmInvalidIdx )
nli = p - > curLocIdx ;
for ( i = 1 ; nei = = cmInvalidIdx & & i < p - > minWndLookAhead ; + + i )
{
// go forward
if ( ( nei = _cmScTrkIsMatch ( p , i , d0 ) ) ! = cmInvalidIdx )
nli = p - > curLocIdx + i ;
else
// go backward
if ( ( nei = _cmScTrkIsMatch ( p , - i , d0 ) ) ! = cmInvalidIdx )
nli = p - > curLocIdx - i ;
}
if ( p - > printFl )
{
_cmScTrkRpt0 ( p , d0 , d1 , nli , nei ) ;
}
if ( nli ! = cmInvalidIdx )
{
p - > loc [ nli ] . evtV [ nei ] . matchFl = true ;
ret_idx = p - > loc [ nli ] . scIdx ;
if ( nli > p - > curLocIdx )
p - > curLocIdx = nli ;
}
2012-12-04 01:18:50 +00:00
2012-12-13 16:46:48 +00:00
+ + p - > evtIndex ;
2012-11-27 22:20:54 +00:00
2012-11-19 06:41:27 +00:00
return ret_idx ;
}
2013-01-14 00:47:50 +00:00
//=======================================================================================================================
//----------------------------------------------------------------------------------------
void ed_print_mtx ( ed_r * r )
{
unsigned i , j , k ;
for ( i = 0 ; i < r - > rn ; + + i )
{
for ( j = 0 ; j < r - > cn ; + + j )
{
printf ( " ( " ) ;
const ed_val * vp = r - > m + i + ( j * r - > rn ) ;
for ( k = 0 ; k < kEdCnt ; + + k )
{
printf ( " %i " , vp - > v [ k ] ) ;
if ( k < kEdCnt - 1 )
printf ( " , " ) ;
else
printf ( " " ) ;
}
printf ( " %c) " , vp - > transFl ? ' t ' : ' ' ) ;
}
printf ( " \n " ) ;
}
}
void ed_init ( ed_r * r , const char * s0 , const char * s1 )
{
unsigned i , j , k ;
r - > rn = strlen ( s0 ) + 1 ;
r - > cn = strlen ( s1 ) + 1 ;
r - > m = cmMemAllocZ ( ed_val , r - > rn * r - > cn ) ;
r - > pn = r - > rn + r - > cn ;
r - > p_mem = cmMemAllocZ ( ed_path , 2 * r - > pn ) ;
r - > p_avl = r - > p_mem ;
r - > p_cur = NULL ;
r - > p_opt = r - > p_mem + r - > pn ;
r - > s_opt = DBL_MAX ;
r - > s0 = s0 ;
r - > s1 = s1 ;
for ( i = 0 ; i < r - > rn ; + + i )
for ( j = 0 ; j < r - > cn ; + + j )
{
unsigned v [ ] = { 0 , 0 , 0 , 0 } ;
if ( i = = 0 )
{
v [ kEdMinIdx ] = j ;
v [ kEdInsIdx ] = j ;
}
else
if ( j = = 0 )
{
v [ kEdMinIdx ] = i ;
v [ kEdDelIdx ] = i ;
}
for ( k = 0 ; k < kEdCnt ; + + k )
r - > m [ i + ( j * r - > rn ) ] . v [ k ] = v [ k ] ;
}
// put pn path records on the available list
for ( i = 0 ; i < r - > pn ; + + i )
r - > p_mem [ i ] . next = i < r - > pn - 1 ? r - > p_mem + i + 1 : NULL ;
}
unsigned _ed_min ( ed_r * r , unsigned i , unsigned j )
{
assert ( i < r - > rn & & j < r - > cn ) ;
return r - > m [ i + ( j * r - > rn ) ] . v [ kEdMinIdx ] ;
}
bool _ed_is_trans ( ed_r * r , const ed_val * v1p , unsigned i , unsigned j )
{
bool fl = false ;
ed_val * v0p = r - > m + i + ( j * r - > rn ) ;
if ( i > = 1 & & j > = 1 & &
v1p - > v [ kEdMinIdx ] = = v1p - > v [ kEdSubIdx ]
& & v1p - > matchFl = = false
& & v0p - > v [ kEdMinIdx ] = = v0p - > v [ kEdSubIdx ]
& & v0p - > matchFl = = false )
{
char c00 = r - > s0 [ i - 1 ] ;
char c01 = r - > s0 [ i ] ;
char c10 = r - > s1 [ j - 1 ] ;
char c11 = r - > s1 [ j ] ;
fl = c00 = = c11 & & c01 = = c10 ;
}
return fl ;
}
void ed_calc_mtx ( ed_r * r )
{
unsigned i , j ;
for ( i = 1 ; i < r - > rn ; + + i )
for ( j = 1 ; j < r - > cn ; + + j )
{
ed_val * vp = r - > m + i + ( j * r - > rn ) ;
vp - > matchFl = r - > s0 [ i - 1 ] = = r - > s1 [ j - 1 ] ;
unsigned cost = vp - > matchFl ? 0 : 1 ;
vp - > v [ kEdSubIdx ] = _ed_min ( r , i - 1 , j - 1 ) + cost ;
vp - > v [ kEdDelIdx ] = _ed_min ( r , i - 1 , j ) + 1 ;
vp - > v [ kEdInsIdx ] = _ed_min ( r , i , j - 1 ) + 1 ;
vp - > v [ kEdMinIdx ] = cmMin ( vp - > v [ kEdSubIdx ] , cmMin ( vp - > v [ kEdDelIdx ] , vp - > v [ kEdInsIdx ] ) ) ;
vp - > transFl = _ed_is_trans ( r , vp , i - 1 , j - 1 ) ;
}
}
void ed_path_push ( ed_r * r , unsigned code , unsigned ri , unsigned ci , bool matchFl , bool transFl )
{
assert ( r - > p_avl ! = NULL ) ;
ed_path * p = r - > p_avl ;
r - > p_avl = r - > p_avl - > next ;
p - > code = code ;
p - > ri = ri ;
p - > ci = ci ;
p - > matchFl = matchFl ;
p - > transFl = transFl ;
p - > next = r - > p_cur ;
r - > p_cur = p ;
}
void ed_path_pop ( ed_r * r )
{
assert ( r - > p_cur ! = NULL ) ;
ed_path * tp = r - > p_cur - > next ;
r - > p_cur - > next = r - > p_avl ;
r - > p_avl = r - > p_cur ;
r - > p_cur = tp ;
}
double ed_score_candidate ( ed_r * r )
{
ed_path * cp = r - > p_cur ;
ed_path * bp = r - > p_cur ;
ed_path * ep = NULL ;
for ( ; cp ! = NULL ; cp = cp - > next )
if ( cp - > code ! = kEdInsIdx )
{
bp = cp ;
break ;
}
for ( ; cp ! = NULL ; cp = cp - > next )
if ( cp - > code ! = kEdInsIdx )
ep = cp ;
assert ( ep ! = NULL & & bp ! = ep ) ;
unsigned n = 1 ;
for ( cp = bp ; cp ! = ep ; cp = cp - > next )
+ + n ;
double gapCnt = 0 ;
double penalty = 0 ;
bool pfl = bp - > matchFl ;
unsigned i ;
cp = bp ;
for ( i = 0 ; i < n ; + + i , cp = cp - > next )
{
// a gap is a transition from a matching subst. to an insert or deletion
//if( pc != cp->code && cp->code != kEdSubIdx && pc==kEdSubIdx && pfl==true )
if ( pfl = = true & & cp - > matchFl = = false )
+ + gapCnt ;
2013-01-16 01:38:21 +00:00
//
switch ( cp - > code )
{
case kEdSubIdx :
penalty + = cp - > matchFl ? 0 : 1 ;
penalty - = cp - > transFl ? 1 : 0 ;
break ;
case kEdDelIdx :
penalty + = 1 ;
break ;
case kEdInsIdx :
penalty + = 1 ;
break ;
}
pfl = cp - > matchFl ;
}
double score = gapCnt / n + penalty ;
printf ( " n:%i gaps:%f gap_score:%f penalty:%f score:%f \n " , n , gapCnt , gapCnt / n , penalty , score ) ;
return score ;
}
void ed_eval_candidate ( ed_r * r , double score )
{
if ( r - > s_opt = = DBL_MAX | | r - > s_opt > score )
{
// copy the p_cur to p_opt[]
ed_path * cp = r - > p_cur ;
unsigned i ;
for ( i = 0 ; cp ! = NULL & & i < r - > pn ; cp = cp - > next , + + i )
{
r - > p_opt [ i ] . code = cp - > code ;
r - > p_opt [ i ] . ri = cp - > ri ;
r - > p_opt [ i ] . ci = cp - > ci ;
r - > p_opt [ i ] . matchFl = cp - > matchFl ;
r - > p_opt [ i ] . transFl = cp - > transFl ;
}
assert ( i < r - > pn ) ;
r - > p_opt [ i ] . code = 0 ; // terminate with code=0
r - > s_opt = score ;
}
}
void ed_print_opt ( ed_r * r )
{
unsigned i ;
for ( i = 0 ; r - > p_opt [ i ] . code ! = 0 ; + + i )
{
ed_path * cp = r - > p_opt + i ;
char c0 = cp - > matchFl ? ' m ' : ' ' ;
char c1 = cp - > transFl ? ' t ' : ' ' ;
printf ( " %2i code:%i ri:%2i ci:%2i %c%c \n " , i , cp - > code , cp - > ri , cp - > ci , c0 , c1 ) ;
}
printf ( " score:%f \n " , r - > s_opt ) ;
}
void ed_print_candidate ( ed_r * r )
{
ed_path * cp = r - > p_cur ;
unsigned pn = r - > pn ;
unsigned i ;
char s0 [ pn + 1 ] ;
char s1 [ pn + 1 ] ;
char s2 [ pn + 1 ] ;
char s3 [ pn + 1 ] ;
s0 [ pn ] = 0 ;
s1 [ pn ] = 0 ;
s2 [ pn ] = 0 ;
s3 [ pn ] = 0 ;
for ( i = 0 ; i < pn & & cp ! = NULL ; + + i , cp = cp - > next )
{
switch ( cp - > code )
{
case kEdSubIdx : // subst
assert ( 0 < = cp - > ri & & cp - > ri < = r - > rn ) ;
assert ( 0 < = cp - > ci & & cp - > ci < = r - > cn ) ;
s0 [ i ] = r - > s0 [ cp - > ri ] ;
s1 [ i ] = r - > s1 [ cp - > ci ] ;
s2 [ i ] = ' s ' ;
s3 [ i ] = cp - > matchFl ? ' m ' : ' ' ;
break ;
case kEdDelIdx : // delete
assert ( 0 < = cp - > ri & & cp - > ri < = r - > rn ) ;
s0 [ i ] = r - > s0 [ cp - > ri ] ;
s1 [ i ] = ' ' ;
s2 [ i ] = ' d ' ;
s3 [ i ] = ' ' ;
break ;
case kEdInsIdx : // insert
assert ( 0 < = cp - > ci & & cp - > ci < = r - > cn ) ;
s0 [ i ] = ' ' ;
s1 [ i ] = r - > s1 [ cp - > ci ] ;
s2 [ i ] = ' i ' ;
s3 [ i ] = ' ' ;
break ;
}
}
if ( i < pn )
{
s0 [ i ] = 0 ;
s1 [ i ] = 0 ;
s2 [ i ] = 0 ;
s3 [ i ] = 0 ;
}
printf ( " \n s0:%s \n " , s0 ) ;
printf ( " s1:%s \n " , s1 ) ;
printf ( " s2:%s \n " , s2 ) ;
printf ( " s3:%s \n " , s3 ) ;
}
// traverse the solution matrix from the lower-right to
// the upper-left.
void ed_node ( ed_r * r , int i , int j )
{
unsigned m ;
// stop when the upper-right is encountered
if ( i = = 0 & & j = = 0 )
{
ed_print_candidate ( r ) ;
ed_eval_candidate ( r , ed_score_candidate ( r ) ) ;
return ;
}
ed_val * vp = r - > m + i + ( j * r - > rn ) ;
// for each possible dir: up,left,up-left
for ( m = 1 ; m < kEdCnt ; + + m )
if ( vp - > v [ m ] = = vp - > v [ kEdMinIdx ] )
{
unsigned ii = i - 1 ;
unsigned jj = j - 1 ;
switch ( m )
{
case kEdSubIdx :
break ;
case kEdDelIdx :
jj = j ;
break ;
case kEdInsIdx :
ii = i ;
break ;
}
// prepend to the current candidate path: r->p_cur
ed_path_push ( r , m , ii , jj , vp - > matchFl , vp - > transFl ) ;
// recurse!
ed_node ( r , ii , jj ) ;
// remove the first element from the current path
ed_path_pop ( r ) ;
}
}
void ed_align ( ed_r * r )
{
int i = r - > rn - 1 ;
int j = r - > cn - 1 ;
unsigned m = r - > m [ i + ( j * r - > rn ) ] . v [ kEdMinIdx ] ;
if ( m = = cmMax ( r - > rn , r - > cn ) )
printf ( " Edit distance is at max: %i. No Match. \n " , m ) ;
else
ed_node ( r , i , j ) ;
}
void ed_free ( ed_r * r )
{
cmMemFree ( r - > m ) ;
cmMemFree ( r - > p_mem ) ;
}
void ed_main ( )
{
const char * s0 = " YHCQPGK " ;
const char * s1 = " LAHYQQKPGKA " ;
s0 = " ABCDE " ;
s1 = " ABDCE " ;
//s1 = "FGHIJK";
ed_r r ;
ed_init ( & r , s0 , s1 ) ;
ed_calc_mtx ( & r ) ;
ed_print_mtx ( & r ) ;
ed_align ( & r ) ;
ed_print_opt ( & r ) ;
ed_free ( & r ) ;
}
//=======================================================================================================================
cmScMatch * cmScMatchAlloc ( cmCtx * c , cmScMatch * p , cmScH_t scH , unsigned maxScWndN , unsigned maxMidiWndN )
{
cmScMatch * op = cmObjAlloc ( cmScMatch , c , p ) ;
if ( cmScoreIsValid ( scH ) )
if ( cmScMatchInit ( op , scH , maxScWndN , maxMidiWndN ) ! = cmOkRC )
cmScMatchFree ( & op ) ;
return op ;
}
cmRC_t cmScMatchFree ( cmScMatch * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return rc ;
cmScMatch * p = * pp ;
if ( ( rc = cmScMatchFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemFree ( p - > loc ) ;
cmMemFree ( p - > m ) ;
cmMemFree ( p - > p_mem ) ;
cmObjFree ( pp ) ;
return rc ;
}
void _cmScMatchInitLoc ( cmScMatch * p )
{
unsigned li , ei ;
p - > locN = cmScoreEvtCount ( p - > scH ) ;
p - > loc = cmMemResizeZ ( cmScMatchLoc_t , p - > loc , p - > locN ) ;
// for each score location
for ( li = 0 , ei = 0 ; li < cmScoreLocCount ( p - > scH ) ; + + li )
{
unsigned i , n ;
const cmScoreLoc_t * lp = cmScoreLoc ( p - > scH , li ) ;
// count the number of note events at location li
for ( n = 0 , i = 0 ; i < lp - > evtCnt ; + + i )
if ( lp - > evtArray [ i ] - > type = = kNonEvtScId )
+ + n ;
assert ( ei + n < = p - > locN ) ;
// duplicate each note at location li n times
for ( i = 0 ; i < n ; + + i )
{
unsigned j , k ;
p - > loc [ ei + i ] . evtCnt = n ;
p - > loc [ ei + i ] . evtV = cmMemAllocZ ( cmScMatchEvt_t , n ) ;
p - > loc [ ei + i ] . scLocIdx = li ;
p - > loc [ ei + i ] . barNumb = lp - > barNumb ;
for ( j = 0 , k = 0 ; j < lp - > evtCnt ; + + j )
if ( lp - > evtArray [ j ] - > type = = kNonEvtScId )
{
p - > loc [ ei + i ] . evtV [ k ] . pitch = lp - > evtArray [ j ] - > pitch ;
2013-01-18 17:28:33 +00:00
p - > loc [ ei + i ] . evtV [ k ] . scEvtIdx = lp - > evtArray [ j ] - > index ;
2013-01-16 01:38:21 +00:00
+ + k ;
}
}
ei + = n ;
}
assert ( ei < = p - > locN ) ;
p - > locN = ei ;
}
cmRC_t cmScMatchInit ( cmScMatch * p , cmScH_t scH , unsigned maxScWndN , unsigned maxMidiWndN )
{
unsigned i ;
cmRC_t rc ;
if ( ( rc = cmScMatchFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > scH = scH ;
p - > mrn = maxMidiWndN + 1 ;
p - > mcn = maxScWndN + 1 ;
p - > mmn = maxMidiWndN ;
p - > msn = maxScWndN ;
_cmScMatchInitLoc ( p ) ;
p - > m = cmMemResizeZ ( cmScMatchVal_t , p - > m , p - > mrn * p - > mcn ) ;
p - > pn = p - > mrn + p - > mcn ;
p - > p_mem = cmMemResizeZ ( cmScMatchPath_t , p - > p_mem , 2 * p - > pn ) ;
p - > p_avl = p - > p_mem ;
p - > p_cur = NULL ;
p - > p_opt = p - > p_mem + p - > pn ;
// put pn path records on the available list
for ( i = 0 ; i < p - > pn ; + + i )
{
p - > p_mem [ i ] . next = i < p - > pn - 1 ? p - > p_mem + i + 1 : NULL ;
p - > p_opt [ i ] . next = i < p - > pn - 1 ? p - > p_opt + i + 1 : NULL ;
}
return rc ;
}
cmRC_t cmScMatchFinal ( cmScMatch * p )
{
unsigned i ;
if ( p ! = NULL )
for ( i = 0 ; i < p - > locN ; + + i )
cmMemPtrFree ( & p - > loc [ i ] . evtV ) ;
return cmOkRC ;
}
cmRC_t _cmScMatchInitMtx ( cmScMatch * p , unsigned rn , unsigned cn )
{
if ( rn > p - > mrn & & cn > p - > mcn )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " MIDI sequence length must be less than %i. Score sequence length must be less than %i. " , p - > mmn , p - > msn ) ;
// if the size of the mtx is not changing then there is nothing to do
if ( rn = = p - > rn & & cn = = p - > cn )
return cmOkRC ;
// update the mtx size
p - > rn = rn ;
p - > cn = cn ;
// fill in the default values for the first row
// and column of the DP matrix
unsigned i , j , k ;
for ( i = 0 ; i < rn ; + + i )
for ( j = 0 ; j < cn ; + + j )
{
unsigned v [ ] = { 0 , 0 , 0 , 0 } ;
if ( i = = 0 )
{
v [ kSmMinIdx ] = j ;
v [ kSmInsIdx ] = j ;
}
else
if ( j = = 0 )
{
v [ kSmMinIdx ] = i ;
v [ kSmDelIdx ] = i ;
}
2013-01-30 19:56:56 +00:00
// zero the value field
2013-01-16 01:38:21 +00:00
for ( k = 0 ; k < kSmCnt ; + + k )
p - > m [ i + ( j * rn ) ] . v [ k ] = v [ k ] ;
}
return cmOkRC ;
}
cmScMatchVal_t * _cmScMatchValPtr ( cmScMatch * p , unsigned i , unsigned j , unsigned rn , unsigned cn )
{
assert ( i < rn & & j < cn ) ;
return p - > m + i + ( j * rn ) ;
}
2013-01-30 19:56:56 +00:00
unsigned _cmScMatchIsMatchIndex ( const cmScMatchLoc_t * loc , unsigned pitch )
2013-01-16 01:38:21 +00:00
{
unsigned i ;
for ( i = 0 ; i < loc - > evtCnt ; + + i )
if ( loc - > evtV [ i ] . pitch = = pitch )
2013-01-30 19:56:56 +00:00
return i ;
return cmInvalidIdx ;
2013-01-16 01:38:21 +00:00
}
2013-01-30 19:56:56 +00:00
bool _cmScMatchIsMatch ( const cmScMatchLoc_t * loc , unsigned pitch )
{ return _cmScMatchIsMatchIndex ( loc , pitch ) ! = cmInvalidIdx ; }
bool _cmScMatchIsTrans ( cmScMatch * p , const cmScMatchMidi_t * midiV , const cmScMatchVal_t * v1p , unsigned bsi , unsigned i , unsigned j , unsigned rn , unsigned cn )
2013-01-16 01:38:21 +00:00
{
bool fl = false ;
cmScMatchVal_t * v0p = _cmScMatchValPtr ( p , i , j , rn , cn ) ;
if ( i > = 1 & & j > = 1
& & v1p - > v [ kSmMinIdx ] = = v1p - > v [ kSmSubIdx ]
& & cmIsNotFlag ( v1p - > flags , kSmMatchFl )
& & v0p - > v [ kSmMinIdx ] = = v0p - > v [ kSmSubIdx ]
& & cmIsNotFlag ( v0p - > flags , kSmMatchFl )
)
{
2013-01-30 19:56:56 +00:00
unsigned c00 = midiV [ i - 1 ] . pitch ;
unsigned c01 = midiV [ i ] . pitch ;
2013-01-16 01:38:21 +00:00
cmScMatchLoc_t * c10 = p - > loc + bsi + j - 1 ;
cmScMatchLoc_t * c11 = p - > loc + bsi + j ;
fl = _cmScMatchIsMatch ( c11 , c00 ) & & _cmScMatchIsMatch ( c10 , c01 ) ;
}
return fl ;
}
unsigned _cmScMatchMin ( cmScMatch * p , unsigned i , unsigned j , unsigned rn , unsigned cn )
{
return _cmScMatchValPtr ( p , i , j , rn , cn ) - > v [ kSmMinIdx ] ;
}
// Return false if bsi + cn > p->locN
// pitchV[rn-1]
2013-01-30 19:56:56 +00:00
bool _cmScMatchCalcMtx ( cmScMatch * p , unsigned bsi , const cmScMatchMidi_t * midiV , unsigned rn , unsigned cn )
2013-01-16 01:38:21 +00:00
{
// loc[begScanLocIdx:begScanLocIdx+cn-1] must be valid
if ( bsi + cn > p - > locN )
return false ;
unsigned i , j ;
for ( j = 1 ; j < cn ; + + j )
for ( i = 1 ; i < rn ; + + i )
{
cmScMatchLoc_t * loc = p - > loc + bsi + j - 1 ;
2013-01-30 19:56:56 +00:00
unsigned pitch = midiV [ i - 1 ] . pitch ;
2013-01-16 01:38:21 +00:00
cmScMatchVal_t * vp = _cmScMatchValPtr ( p , i , j , rn , cn ) ;
2013-01-30 19:56:56 +00:00
unsigned idx = _cmScMatchIsMatchIndex ( loc , pitch ) ;
vp - > flags = idx = = cmInvalidIdx ? 0 : kSmMatchFl ;
vp - > scEvtIdx = idx = = cmInvalidIdx ? cmInvalidIdx : loc - > evtV [ idx ] . scEvtIdx ;
2013-01-16 01:38:21 +00:00
unsigned cost = cmIsFlag ( vp - > flags , kSmMatchFl ) ? 0 : 1 ;
vp - > v [ kSmSubIdx ] = _cmScMatchMin ( p , i - 1 , j - 1 , rn , cn ) + cost ;
vp - > v [ kSmDelIdx ] = _cmScMatchMin ( p , i - 1 , j , rn , cn ) + 1 ;
vp - > v [ kSmInsIdx ] = _cmScMatchMin ( p , i , j - 1 , rn , cn ) + 1 ;
vp - > v [ kSmMinIdx ] = cmMin ( vp - > v [ kSmSubIdx ] , cmMin ( vp - > v [ kSmDelIdx ] , vp - > v [ kSmInsIdx ] ) ) ;
2013-01-30 19:56:56 +00:00
vp - > flags | = _cmScMatchIsTrans ( p , midiV , vp , bsi , i - 1 , j - 1 , rn , cn ) ? kSmTransFl : 0 ;
2013-01-16 01:38:21 +00:00
}
return true ;
}
void _cmScMatchPrintMtx ( cmScMatch * r , unsigned rn , unsigned cn )
{
unsigned i , j , k ;
for ( i = 0 ; i < rn ; + + i )
{
for ( j = 0 ; j < cn ; + + j )
{
printf ( " ( " ) ;
const cmScMatchVal_t * vp = _cmScMatchValPtr ( r , i , j , rn , cn ) ;
for ( k = 0 ; k < kSmCnt ; + + k )
{
printf ( " %i " , vp - > v [ k ] ) ;
if ( k < kSmCnt - 1 )
printf ( " , " ) ;
else
printf ( " " ) ;
}
printf ( " %c%c) " , cmIsFlag ( vp - > flags , kSmMatchFl ) ? ' m ' : ' ' , cmIsFlag ( vp - > flags , kSmTransFl ) ? ' t ' : ' ' ) ;
}
printf ( " \n " ) ;
}
}
2013-01-30 19:56:56 +00:00
void _cmScMatchPathPush ( cmScMatch * r , unsigned code , unsigned ri , unsigned ci , unsigned flags , unsigned scEvtIdx )
2013-01-16 01:38:21 +00:00
{
assert ( r - > p_avl ! = NULL ) ;
cmScMatchPath_t * p = r - > p_avl ;
r - > p_avl = r - > p_avl - > next ;
p - > code = code ;
p - > ri = ri ;
p - > ci = ci ;
p - > flags = code = = kSmSubIdx & & cmIsFlag ( flags , kSmMatchFl ) ? kSmMatchFl : 0 ;
2013-01-16 23:12:54 +00:00
p - > flags | = cmIsFlag ( flags , kSmTransFl ) ? kSmTransFl : 0 ;
2013-01-30 19:56:56 +00:00
p - > scEvtIdx = scEvtIdx ;
2013-01-16 01:38:21 +00:00
p - > next = r - > p_cur ;
r - > p_cur = p ;
}
void _cmScMatchPathPop ( cmScMatch * r )
{
assert ( r - > p_cur ! = NULL ) ;
cmScMatchPath_t * tp = r - > p_cur - > next ;
r - > p_cur - > next = r - > p_avl ;
r - > p_avl = r - > p_cur ;
r - > p_cur = tp ;
}
2013-02-09 07:48:54 +00:00
2013-01-16 01:38:21 +00:00
double _cmScMatchCalcCandidateCost ( cmScMatch * r )
{
cmScMatchPath_t * cp = r - > p_cur ;
cmScMatchPath_t * bp = r - > p_cur ;
cmScMatchPath_t * ep = NULL ;
// skip leading inserts
for ( ; cp ! = NULL ; cp = cp - > next )
if ( cp - > code ! = kSmInsIdx )
{
bp = cp ;
break ;
}
// skip to trailing inserts
for ( ; cp ! = NULL ; cp = cp - > next )
if ( cp - > code ! = kSmInsIdx )
ep = cp ;
// count remaining path length
2013-01-30 19:56:56 +00:00
assert ( ep ! = NULL ) ;
2013-01-16 01:38:21 +00:00
unsigned n = 1 ;
for ( cp = bp ; cp ! = ep ; cp = cp - > next )
+ + n ;
double gapCnt = 0 ;
double penalty = 0 ;
bool pfl = cmIsFlag ( bp - > flags , kSmMatchFl ) ;
unsigned i ;
cp = bp ;
for ( i = 0 ; i < n ; + + i , cp = cp - > next )
{
// a gap is a transition from a matching subst. to an insert or deletion
//if( pc != cp->code && cp->code != kSmSubIdx && pc==kSmSubIdx && pfl==true )
if ( pfl = = true & & cmIsFlag ( cp - > flags , kSmMatchFl ) = = false )
+ + gapCnt ;
//
switch ( cp - > code )
{
case kSmSubIdx :
penalty + = cmIsFlag ( cp - > flags , kSmMatchFl ) ? 0 : 1 ;
penalty - = cmIsFlag ( cp - > flags , kSmTransFl ) ? 1 : 0 ;
break ;
case kSmDelIdx :
penalty + = 1 ;
break ;
case kSmInsIdx :
penalty + = 1 ;
break ;
}
pfl = cmIsFlag ( cp - > flags , kSmMatchFl ) ;
}
double cost = gapCnt / n + penalty ;
//printf("n:%i gaps:%f gap_score:%f penalty:%f score:%f\n",n,gapCnt,gapCnt/n,penalty,score);
return cost ;
}
double _cmScMatchEvalCandidate ( cmScMatch * r , double min_cost , double cost )
{
if ( min_cost = = DBL_MAX | | cost < min_cost )
{
// copy the p_cur to p_opt[]
cmScMatchPath_t * cp = r - > p_cur ;
unsigned i ;
for ( i = 0 ; cp ! = NULL & & i < r - > pn ; cp = cp - > next , + + i )
{
r - > p_opt [ i ] . code = cp - > code ;
r - > p_opt [ i ] . ri = cp - > ri ;
r - > p_opt [ i ] . ci = cp - > ci ;
r - > p_opt [ i ] . flags = cp - > flags ;
2013-01-30 19:56:56 +00:00
r - > p_opt [ i ] . scEvtIdx = cp - > scEvtIdx ;
2013-01-16 01:38:21 +00:00
r - > p_opt [ i ] . next = cp - > next = = NULL ? NULL : r - > p_opt + i + 1 ;
}
assert ( i < r - > pn ) ;
r - > p_opt [ i ] . code = 0 ; // terminate with code=0
min_cost = cost ;
}
return min_cost ;
}
2013-01-16 23:12:54 +00:00
// NOTE: IF THE COST CALCULATION WAS BUILT INTO THE RECURSION THEN
// THIS FUNCTION COULD BE MADE MORE EFFICIENT BECAUSE PATHS WHICH
// EXCEEDED THE min_cost COULD BE SHORT CIRCUITED.
//
2013-01-16 01:38:21 +00:00
// traverse the solution matrix from the lower-right to
// the upper-left.
double _cmScMatchGenPaths ( cmScMatch * r , int i , int j , unsigned rn , unsigned cn , double min_cost )
{
unsigned m ;
// stop when the upper-right is encountered
if ( i = = 0 & & j = = 0 )
return _cmScMatchEvalCandidate ( r , min_cost , _cmScMatchCalcCandidateCost ( r ) ) ;
cmScMatchVal_t * vp = _cmScMatchValPtr ( r , i , j , rn , cn ) ;
// for each possible dir: up,left,up-left
for ( m = 1 ; m < kSmCnt ; + + m )
if ( vp - > v [ m ] = = vp - > v [ kSmMinIdx ] )
{
// prepend to the current candidate path: r->p_cur
2013-01-30 19:56:56 +00:00
_cmScMatchPathPush ( r , m , i , j , vp - > flags , vp - > scEvtIdx ) ;
2013-01-16 01:38:21 +00:00
int ii = i - 1 ;
int jj = j - 1 ;
switch ( m )
{
case kSmSubIdx :
break ;
case kSmDelIdx :
jj = j ;
break ;
case kSmInsIdx :
ii = i ;
break ;
default :
{ assert ( 0 ) ; }
}
// recurse!
min_cost = _cmScMatchGenPaths ( r , ii , jj , rn , cn , min_cost ) ;
// remove the first element from the current path
_cmScMatchPathPop ( r ) ;
}
return min_cost ;
}
double _cmScMatchAlign ( cmScMatch * p , unsigned rn , unsigned cn , double min_cost )
{
int i = rn - 1 ;
int j = cn - 1 ;
unsigned m = _cmScMatchMin ( p , i , j , rn , cn ) ;
if ( m = = cmMax ( rn , cn ) )
printf ( " Edit distance is at max: %i. No Match. \n " , m ) ;
else
min_cost = _cmScMatchGenPaths ( p , i , j , rn , cn , min_cost ) ;
return min_cost ;
}
2013-01-30 19:56:56 +00:00
cmRC_t cmScMatchExec ( cmScMatch * p , unsigned locIdx , unsigned locN , const cmScMatchMidi_t * midiV , unsigned midiN , double min_cost )
2013-01-16 01:38:21 +00:00
{
cmRC_t rc ;
2013-01-30 19:56:56 +00:00
unsigned rn = midiN + 1 ;
unsigned cn = locN + 1 ;
2013-01-16 01:38:21 +00:00
// set the DP matrix default values
if ( ( rc = _cmScMatchInitMtx ( p , rn , cn ) ) ! = cmOkRC )
return rc ;
// _cmScMatchCalcMtx() returns false if the score window exceeds the length of the score
2013-01-30 19:56:56 +00:00
if ( ! _cmScMatchCalcMtx ( p , locIdx , midiV , rn , cn ) )
2013-01-16 01:38:21 +00:00
return cmEofRC ;
//_cmScMatchPrintMtx(p,rn,cn);
2013-01-16 23:12:54 +00:00
2013-01-16 01:38:21 +00:00
// locate the path through the DP matrix with the lowest edit distance (cost)
p - > opt_cost = _cmScMatchAlign ( p , rn , cn , min_cost ) ;
return rc ;
}
2013-01-30 19:56:56 +00:00
// Traverse the least cost path and:
// 1) Set p->esi to the score location index of the last MIDI note
// which has a positive match with the score and assign
// the internal score index to cp->locIdx.
//
// 2) Set cmScAlignPath_t.locIdx - index into p->loc[] associated
// with each path element that is a 'substitute' or an 'insert'.
//
// 3) Set p->missCnt: the count of trailing non-positive matches.
// p->missCnt is eventually used in cmScAlignStep() to track the number
// of consecutive trailing missed notes.
//
// i_opt is index into p->loc[] of p->p_opt.
unsigned cmScMatchDoSync ( cmScMatch * p , unsigned i_opt , cmScMatchMidi_t * midiBuf , unsigned midiN , unsigned * missCntPtr )
{
cmScMatchPath_t * cp = p - > p_opt ;
unsigned missCnt = 0 ;
unsigned esi = cmInvalidIdx ;
unsigned i ;
for ( i = 0 ; cp ! = NULL ; cp = cp - > next )
{
// there is no MIDI note associated with 'inserts'
if ( cp - > code ! = kSmInsIdx )
{
assert ( cp - > ri > 0 ) ;
midiBuf [ cp - > ri - 1 ] . locIdx = cmInvalidIdx ;
}
switch ( cp - > code )
{
case kSmSubIdx :
midiBuf [ cp - > ri - 1 ] . locIdx = i_opt + i ;
midiBuf [ cp - > ri - 1 ] . scEvtIdx = cp - > scEvtIdx ;
if ( cmIsFlag ( cp - > flags , kSmMatchFl ) )
{
esi = i_opt + i ;
missCnt = 0 ;
}
else
{
+ + missCnt ;
}
// fall through
case kSmInsIdx :
cp - > locIdx = i_opt + i ;
+ + i ;
break ;
case kSmDelIdx :
cp - > locIdx = cmInvalidIdx ;
+ + missCnt ;
break ;
}
}
if ( missCntPtr ! = NULL )
* missCntPtr = missCnt ;
return esi ;
}
void _cmScMatchMidiEvtFlags ( cmScMatch * p , const cmScMatchLoc_t * lp , unsigned evtIdx , char * s , unsigned sn )
{
const cmScoreLoc_t * slp = cmScoreLoc ( p - > scH , lp - > scLocIdx ) ;
assert ( evtIdx < slp - > evtCnt ) ;
const cmScoreEvt_t * ep = slp - > evtArray [ evtIdx ] ;
unsigned i = 0 ;
s [ 0 ] = 0 ;
if ( cmIsFlag ( ep - > flags , kEvenScFl ) )
s [ i + + ] = ' e ' ;
if ( cmIsFlag ( ep - > flags , kTempoScFl ) )
s [ i + + ] = ' t ' ;
if ( cmIsFlag ( ep - > flags , kDynScFl ) )
s [ i + + ] = ' d ' ;
if ( cmIsFlag ( ep - > flags , kGraceScFl ) )
s [ i + + ] = ' g ' ;
s [ i + + ] = 0 ;
assert ( i < = sn ) ;
}
void _cmScMatchPrintPath ( cmScMatch * p , cmScMatchPath_t * cp , unsigned bsi , const cmScMatchMidi_t * midiV )
2013-01-16 01:38:21 +00:00
{
assert ( bsi ! = cmInvalidIdx ) ;
cmScMatchPath_t * pp = cp ;
int polyN = 0 ;
int i ;
printf ( " loc: " ) ;
// get the polyphony count for the score window
for ( i = 0 ; pp ! = NULL ; pp = pp - > next )
{
cmScMatchLoc_t * lp = p - > loc + bsi + pp - > ci ;
if ( pp - > code ! = kSmDelIdx )
{
if ( lp - > evtCnt > polyN )
polyN = lp - > evtCnt ;
2013-01-30 19:56:56 +00:00
printf ( " %4i%4s " , bsi + i , " " ) ;
2013-01-16 01:38:21 +00:00
+ + i ;
}
else
2013-01-30 19:56:56 +00:00
printf ( " %4s%4s " , " " , " " ) ;
2013-01-16 01:38:21 +00:00
}
printf ( " \n " ) ;
// print the score notes
for ( i = polyN ; i > 0 ; - - i )
{
printf ( " %3i: " , i ) ;
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
2013-01-30 19:56:56 +00:00
if ( pp - > code ! = kSmDelIdx )
{
int locIdx = bsi + pp - > ci - 1 ;
assert ( 0 < = locIdx & & locIdx < = p - > locN ) ;
cmScMatchLoc_t * lp = p - > loc + locIdx ;
if ( lp - > evtCnt > = i )
{
unsigned sn = 6 ;
char s [ sn ] ;
_cmScMatchMidiEvtFlags ( p , lp , i - 1 , s , sn ) ;
printf ( " %4s%-4s " , cmMidiToSciPitch ( lp - > evtV [ i - 1 ] . pitch , NULL , 0 ) , s ) ;
}
else
printf ( " %4s%4s " , " " , " " ) ;
}
else
printf ( " %4s%4s " , ( pp - > code = = kSmDelIdx ? " - " : " " ) , " " ) ;
/*
2013-01-16 01:38:21 +00:00
int locIdx = bsi + pp - > ci - 1 ;
assert ( 0 < = locIdx & & locIdx < = p - > locN ) ;
cmScMatchLoc_t * lp = p - > loc + locIdx ;
if ( pp - > code ! = kSmDelIdx & & lp - > evtCnt > = i )
printf ( " %4s " , cmMidiToSciPitch ( lp - > evtV [ i - 1 ] . pitch , NULL , 0 ) ) ;
else
printf ( " %4s " , pp - > code = = kSmDelIdx ? " - " : " " ) ;
2013-01-30 19:56:56 +00:00
*/
2013-01-16 01:38:21 +00:00
}
printf ( " \n " ) ;
}
printf ( " mid: " ) ;
// print the MIDI buffer
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
if ( pp - > code ! = kSmInsIdx )
2013-01-30 19:56:56 +00:00
printf ( " %4s%4s " , cmMidiToSciPitch ( midiV [ pp - > ri - 1 ] . pitch , NULL , 0 ) , " " ) ;
2013-01-16 01:38:21 +00:00
else
2013-01-30 19:56:56 +00:00
printf ( " %4s%4s " , pp - > code = = kSmInsIdx ? " - " : " " , " " ) ;
}
printf ( " \n vel: " ) ;
// print the MIDI velocity
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
if ( pp - > code ! = kSmInsIdx )
printf ( " %4i%4s " , midiV [ pp - > ri - 1 ] . vel , " " ) ;
else
printf ( " %4s%4s " , pp - > code = = kSmInsIdx ? " - " : " " , " " ) ;
2013-01-16 01:38:21 +00:00
}
printf ( " \n mni: " ) ;
// print the MIDI buffer index (mni)
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
if ( pp - > code ! = kSmInsIdx )
2013-01-30 19:56:56 +00:00
printf ( " %4i%4s " , midiV [ pp - > ri - 1 ] . mni , " " ) ;
2013-01-16 01:38:21 +00:00
else
2013-01-30 19:56:56 +00:00
printf ( " %4s%4s " , pp - > code = = kSmInsIdx ? " - " : " " , " " ) ;
2013-01-16 01:38:21 +00:00
}
printf ( " \n op: " ) ;
// print the substitute/insert/delete operation
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
char c = ' ' ;
switch ( pp - > code )
{
case kSmSubIdx : c = ' s ' ; break ;
case kSmDelIdx : c = ' d ' ; break ;
case kSmInsIdx : c = ' i ' ; break ;
default :
{ assert ( 0 ) ; }
}
2013-01-30 19:56:56 +00:00
printf ( " %4c%4s " , c , " " ) ;
2013-01-16 01:38:21 +00:00
}
printf ( " \n " ) ;
// give substitute attribute (match or transpose)
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
cmChar_t s [ 3 ] ;
int k = 0 ;
if ( cmIsFlag ( pp - > flags , kSmMatchFl ) )
s [ k + + ] = ' m ' ;
if ( cmIsFlag ( pp - > flags , kSmTransFl ) )
s [ k + + ] = ' t ' ;
s [ k ] = 0 ;
2013-01-30 19:56:56 +00:00
printf ( " %4s%4s " , s , " " ) ;
2013-01-16 01:38:21 +00:00
}
printf ( " \n scl: " ) ;
// print the stored location index
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
if ( pp - > locIdx = = cmInvalidIdx )
2013-01-30 19:56:56 +00:00
printf ( " %4s%4s " , " " , " " ) ;
else
printf ( " %4i%4s " , p - > loc [ pp - > locIdx ] . scLocIdx , " " ) ;
}
printf ( " \n bar: " ) ;
// print the stored location index
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
if ( pp - > locIdx = = cmInvalidIdx | | pp - > scEvtIdx = = cmInvalidIdx )
printf ( " %4s%4s " , " " , " " ) ;
else
{
const cmScoreEvt_t * ep = cmScoreEvt ( p - > scH , pp - > scEvtIdx ) ;
printf ( " %4i%4s " , ep - > barNumb , " " ) ;
}
}
printf ( " \n sec: " ) ;
// print seconds
unsigned begSmpIdx = cmInvalidIdx ;
for ( pp = cp ; pp ! = NULL ; pp = pp - > next )
{
if ( pp - > code ! = kSmInsIdx )
{
if ( begSmpIdx = = cmInvalidIdx )
begSmpIdx = midiV [ pp - > ri - 1 ] . smpIdx ;
printf ( " %2.2f%4s " , ( double ) ( midiV [ pp - > ri - 1 ] . smpIdx - begSmpIdx ) / 96000.0 , " " ) ;
}
2013-01-16 01:38:21 +00:00
else
2013-01-30 19:56:56 +00:00
printf ( " %4s%4s " , pp - > code = = kSmInsIdx ? " - " : " " , " " ) ;
2013-01-16 01:38:21 +00:00
}
2013-01-30 19:56:56 +00:00
2013-01-16 01:38:21 +00:00
printf ( " \n \n " ) ;
2013-01-30 19:56:56 +00:00
2013-01-16 01:38:21 +00:00
}
//=======================================================================================================================
2013-01-31 01:33:44 +00:00
cmScMatcher * cmScMatcherAlloc ( cmCtx * c , cmScMatcher * p , double srate , cmScH_t scH , unsigned scWndN , unsigned midiWndN , cmScMatcherCb_t cbFunc , void * cbArg )
2013-01-16 01:38:21 +00:00
{
cmScMatcher * op = cmObjAlloc ( cmScMatcher , c , p ) ;
if ( op ! = NULL )
op - > mp = cmScMatchAlloc ( c , NULL , cmScNullHandle , 0 , 0 ) ;
if ( srate ! = 0 )
{
2013-01-31 01:33:44 +00:00
if ( cmScMatcherInit ( op , srate , scH , scWndN , midiWndN , cbFunc , cbArg ) ! = cmOkRC )
2013-01-16 01:38:21 +00:00
cmScMatcherFree ( & op ) ;
}
return op ;
}
cmRC_t cmScMatcherFree ( cmScMatcher * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return rc ;
cmScMatcher * p = * pp ;
if ( ( rc = cmScMatcherFinal ( p ) ) ! = cmOkRC )
return rc ;
cmScMatchFree ( & p - > mp ) ;
cmMemFree ( p - > midiBuf ) ;
cmMemFree ( p - > res ) ;
cmObjFree ( pp ) ;
return rc ;
}
2013-01-31 01:33:44 +00:00
cmRC_t cmScMatcherInit ( cmScMatcher * p , double srate , cmScH_t scH , unsigned scWndN , unsigned midiWndN , cmScMatcherCb_t cbFunc , void * cbArg )
2013-01-16 01:38:21 +00:00
{
cmRC_t rc ;
if ( ( rc = cmScMatcherFinal ( p ) ) ! = cmOkRC )
return rc ;
if ( midiWndN > scWndN )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " The score alignment MIDI event buffer length (%i) must be less than the score window length ( % i ) . " ,midiWndN,scWndN) ;
if ( ( rc = cmScMatchInit ( p - > mp , scH , scWndN , midiWndN ) ) ! = cmOkRC )
return rc ;
2013-01-31 01:33:44 +00:00
p - > cbFunc = cbFunc ;
p - > cbArg = cbArg ;
2013-01-16 01:38:21 +00:00
p - > mn = midiWndN ;
2013-02-09 07:48:54 +00:00
p - > midiBuf = cmMemResizeZ ( cmScMatchMidi_t , p - > midiBuf , p - > mn ) ;
2013-01-16 01:38:21 +00:00
p - > stepCnt = 3 ;
p - > maxMissCnt = p - > stepCnt + 1 ;
p - > rn = 2 * cmScoreEvtCount ( scH ) ;
p - > res = cmMemResizeZ ( cmScMatcherResult_t , p - > res , p - > rn ) ;
2013-01-31 01:33:44 +00:00
p - > printFl = false ;
cmScMatcherReset ( p , 0 ) ;
2013-01-16 01:38:21 +00:00
return rc ;
}
cmRC_t cmScMatcherFinal ( cmScMatcher * p )
{
return cmScMatchFinal ( p - > mp ) ;
}
2013-01-31 01:33:44 +00:00
cmRC_t cmScMatcherReset ( cmScMatcher * p , unsigned scLocIdx )
2013-01-16 01:38:21 +00:00
{
p - > mbi = p - > mp - > mmn ;
p - > mni = 0 ;
p - > begSyncLocIdx = cmInvalidIdx ;
p - > s_opt = DBL_MAX ;
p - > missCnt = 0 ;
p - > scanCnt = 0 ;
p - > ri = 0 ;
2013-01-31 01:33:44 +00:00
p - > eli = cmInvalidIdx ;
p - > ili = 0 ;
// convert scLocIdx to an index into p->mp->loc[]
unsigned i ;
for ( i = 0 ; i < p - > mp - > locN ; + + i )
if ( p - > mp - > loc [ i ] . scLocIdx = = scLocIdx )
p - > ili = i ;
return cmOkRC ;
2013-01-16 01:38:21 +00:00
}
bool cmScMatcherInputMidi ( cmScMatcher * p , unsigned smpIdx , unsigned status , cmMidiByte_t d0 , cmMidiByte_t d1 )
{
if ( status ! = kNoteOnMdId )
return false ;
unsigned mi = p - > mn - 1 ;
//printf("%3i %5.2f %4s\n",p->mni,(double)smpIdx/p->srate,cmMidiToSciPitch(d0,NULL,0));
// shift the new MIDI event onto the end of the MIDI buffer
2013-01-30 19:56:56 +00:00
memmove ( p - > midiBuf , p - > midiBuf + 1 , sizeof ( cmScMatchMidi_t ) * mi ) ;
p - > midiBuf [ mi ] . locIdx = cmInvalidIdx ;
p - > midiBuf [ mi ] . scEvtIdx = cmInvalidIdx ;
p - > midiBuf [ mi ] . mni = p - > mni + + ;
p - > midiBuf [ mi ] . smpIdx = smpIdx ;
p - > midiBuf [ mi ] . pitch = d0 ;
p - > midiBuf [ mi ] . vel = d1 ;
2013-01-16 01:38:21 +00:00
if ( p - > mbi > 0 )
- - p - > mbi ;
return true ;
}
2013-01-30 19:56:56 +00:00
void _cmScMatcherStoreResult ( cmScMatcher * p , unsigned locIdx , unsigned scEvtIdx , unsigned flags , const cmScMatchMidi_t * mp )
2013-01-16 01:38:21 +00:00
{
// don't store missed score note results
assert ( mp ! = NULL ) ;
2013-01-16 23:12:54 +00:00
bool matchFl = cmIsFlag ( flags , kSmMatchFl ) ;
bool tpFl = locIdx ! = cmInvalidIdx & & matchFl ;
bool fpFl = locIdx = = cmInvalidIdx | | matchFl = = false ;
cmScMatcherResult_t * rp = NULL ;
2013-01-16 01:38:21 +00:00
unsigned i ;
2013-01-31 01:33:44 +00:00
cmScMatcherResult_t r ;
2013-01-16 01:38:21 +00:00
assert ( tpFl = = false | | ( tpFl = = true & & locIdx ! = cmInvalidIdx ) ) ;
// it is possible that the same MIDI event is reported more than once
// (due to step->scan back tracking) - try to find previous result records
// associated with this MIDI event
for ( i = 0 ; i < p - > ri ; + + i )
if ( p - > res [ i ] . mni = = mp - > mni )
{
2013-01-16 23:12:54 +00:00
// if this is not the first time this note was reported and it is a true positive
2013-01-16 01:38:21 +00:00
if ( tpFl )
{
rp = p - > res + i ;
break ;
}
// a match was found but this was not a true-pos so ignore it
return ;
}
if ( rp = = NULL )
{
2013-01-31 01:33:44 +00:00
if ( p - > ri > = p - > rn )
{
rp = & r ;
memset ( rp , 0 , sizeof ( r ) ) ;
}
else
{
rp = p - > res + p - > ri ;
+ + p - > ri ;
}
2013-01-16 01:38:21 +00:00
}
2013-01-30 19:56:56 +00:00
rp - > locIdx = locIdx ;
rp - > scEvtIdx = scEvtIdx ;
rp - > mni = mp - > mni ;
rp - > smpIdx = mp - > smpIdx ;
rp - > pitch = mp - > pitch ;
rp - > vel = mp - > vel ;
rp - > flags = flags | ( tpFl ? kSmTruePosFl : 0 ) | ( fpFl ? kSmFalsePosFl : 0 ) ;
2013-01-16 01:38:21 +00:00
2013-01-18 17:28:33 +00:00
if ( p - > cbFunc ! = NULL )
p - > cbFunc ( p , p - > cbArg , rp ) ;
2013-01-16 01:38:21 +00:00
}
2013-01-16 23:12:54 +00:00
void cmScMatcherPrintPath ( cmScMatcher * p )
{
2013-01-30 19:56:56 +00:00
_cmScMatchPrintPath ( p - > mp , p - > mp - > p_opt , p - > begSyncLocIdx , p - > midiBuf ) ;
2013-01-16 23:12:54 +00:00
}
2013-01-31 01:33:44 +00:00
unsigned cmScMatcherScan ( cmScMatcher * p , unsigned bli , unsigned scanCnt )
2013-01-16 01:38:21 +00:00
{
assert ( p - > mp ! = NULL & & p - > mp - > mmn > 0 ) ;
2013-01-16 23:12:54 +00:00
unsigned i_opt = cmInvalidIdx ;
2013-01-16 01:38:21 +00:00
double s_opt = DBL_MAX ;
2013-01-31 01:33:44 +00:00
cmRC_t rc = cmOkRC ;
2013-01-16 01:38:21 +00:00
unsigned i ;
// initialize the internal values set by this function
p - > missCnt = 0 ;
2013-01-31 01:33:44 +00:00
p - > eli = cmInvalidIdx ;
2013-01-16 01:38:21 +00:00
p - > s_opt = DBL_MAX ;
// if the MIDI buf is not full
if ( p - > mbi ! = 0 )
return cmInvalidIdx ;
// calc the edit distance from pitchV[] to a sliding score window
for ( i = 0 ; rc = = cmOkRC & & ( scanCnt = = cmInvalidCnt | | i < scanCnt ) ; + + i )
{
2013-01-31 01:33:44 +00:00
rc = cmScMatchExec ( p - > mp , bli + i , p - > mp - > msn , p - > midiBuf , p - > mp - > mmn , s_opt ) ;
2013-01-16 01:38:21 +00:00
switch ( rc )
{
case cmOkRC : // normal result
if ( p - > mp - > opt_cost < s_opt )
{
s_opt = p - > mp - > opt_cost ;
2013-01-31 01:33:44 +00:00
i_opt = bli + i ;
2013-01-16 01:38:21 +00:00
}
break ;
case cmEofRC : // score window encountered the end of the score
break ;
default : // error state
return cmInvalidIdx ;
}
}
// store the cost assoc'd with i_opt
p - > s_opt = s_opt ;
if ( i_opt = = cmInvalidIdx )
return cmInvalidIdx ;
2013-01-30 19:56:56 +00:00
// set the locIdx field in midiBuf[], trailing miss count and
// return the latest positive-match locIdx
2013-01-31 01:33:44 +00:00
p - > eli = cmScMatchDoSync ( p - > mp , i_opt , p - > midiBuf , p - > mp - > mmn , & p - > missCnt ) ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
// if no positive matches were found
2013-01-31 01:33:44 +00:00
if ( p - > eli = = cmInvalidIdx )
2013-01-16 01:38:21 +00:00
i_opt = cmInvalidIdx ;
else
{
2013-01-30 19:56:56 +00:00
cmScMatchPath_t * cp ;
2013-01-16 01:38:21 +00:00
// record result
for ( cp = p - > mp - > p_opt ; cp ! = NULL ; cp = cp - > next )
if ( cp - > code ! = kSmInsIdx )
2013-01-30 19:56:56 +00:00
_cmScMatcherStoreResult ( p , cp - > locIdx , cp - > scEvtIdx , cp - > flags , p - > midiBuf + cp - > ri - 1 ) ;
2013-01-16 01:38:21 +00:00
}
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
return i_opt ;
2013-01-14 00:47:50 +00:00
}
2013-01-16 01:38:21 +00:00
cmRC_t cmScMatcherStep ( cmScMatcher * p )
2013-01-14 00:47:50 +00:00
{
2013-01-16 01:38:21 +00:00
int i ;
2013-01-16 23:12:54 +00:00
unsigned pitch = p - > midiBuf [ p - > mn - 1 ] . pitch ;
unsigned locIdx = cmInvalidIdx ;
2013-01-30 19:56:56 +00:00
unsigned pidx = cmInvalidIdx ;
2013-01-31 01:33:44 +00:00
2013-01-16 01:38:21 +00:00
// the tracker must be sync'd to step
2013-01-31 01:33:44 +00:00
if ( p - > eli = = cmInvalidIdx )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " The p->eli value must be valid to perform a step operation. " ) ;
2013-01-16 01:38:21 +00:00
// if the end of the score has been reached
2013-01-31 01:33:44 +00:00
if ( p - > eli + 1 > = p - > mp - > locN )
2013-01-16 01:38:21 +00:00
return cmEofRC ;
// attempt to match to next location first
2013-01-31 01:33:44 +00:00
if ( ( pidx = _cmScMatchIsMatchIndex ( p - > mp - > loc + p - > eli + 1 , pitch ) ) ! = cmInvalidIdx )
2013-01-14 00:47:50 +00:00
{
2013-01-31 01:33:44 +00:00
locIdx = p - > eli + 1 ;
2013-01-16 01:38:21 +00:00
}
else
{
//
for ( i = 2 ; i < p - > stepCnt ; + + i )
2013-01-14 00:47:50 +00:00
{
2013-01-16 01:38:21 +00:00
// go forward
2013-01-31 01:33:44 +00:00
if ( p - > eli + i < p - > mp - > locN & & ( pidx = _cmScMatchIsMatchIndex ( p - > mp - > loc + p - > eli + i , pitch ) ) ! = cmInvalidIdx )
2013-01-16 01:38:21 +00:00
{
2013-01-31 01:33:44 +00:00
locIdx = p - > eli + i ;
2013-01-16 01:38:21 +00:00
break ;
}
// go backward
2013-01-31 01:33:44 +00:00
if ( p - > eli > = ( i - 1 ) & & ( pidx = _cmScMatchIsMatchIndex ( p - > mp - > loc + p - > eli - ( i - 1 ) , pitch ) ) ! = cmInvalidIdx )
2013-01-16 01:38:21 +00:00
{
2013-01-31 01:33:44 +00:00
locIdx = p - > eli - ( i - 1 ) ;
2013-01-16 01:38:21 +00:00
break ;
}
2013-01-14 00:47:50 +00:00
}
}
2013-01-30 19:56:56 +00:00
unsigned scEvtIdx = locIdx = = cmInvalidIdx ? cmInvalidIdx : p - > mp - > loc [ locIdx ] . evtV [ pidx ] . scEvtIdx ;
p - > midiBuf [ p - > mn - 1 ] . locIdx = locIdx ;
p - > midiBuf [ p - > mn - 1 ] . scEvtIdx = scEvtIdx ;
2013-01-16 01:38:21 +00:00
if ( locIdx = = cmInvalidIdx )
+ + p - > missCnt ;
else
2013-01-14 00:47:50 +00:00
{
2013-01-16 01:38:21 +00:00
p - > missCnt = 0 ;
2013-01-31 01:33:44 +00:00
p - > eli = locIdx ;
2013-01-14 00:47:50 +00:00
}
2013-01-16 01:38:21 +00:00
// store the result
2013-01-30 19:56:56 +00:00
_cmScMatcherStoreResult ( p , locIdx , scEvtIdx , locIdx ! = cmInvalidIdx ? kSmMatchFl : 0 , p - > midiBuf + p - > mn - 1 ) ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
if ( p - > missCnt > = p - > maxMissCnt )
2013-01-14 00:47:50 +00:00
{
2013-01-31 01:33:44 +00:00
unsigned begScanLocIdx = p - > eli > p - > mn ? p - > eli - p - > mn : 0 ;
2013-01-16 01:38:21 +00:00
p - > s_opt = DBL_MAX ;
2013-01-31 01:33:44 +00:00
unsigned bli = cmScMatcherScan ( p , begScanLocIdx , p - > mn * 2 ) ;
2013-01-16 01:38:21 +00:00
+ + p - > scanCnt ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
// if the scan failed find a match
2013-01-31 01:33:44 +00:00
if ( bli = = cmInvalidIdx )
2013-01-16 01:38:21 +00:00
return cmCtxRtCondition ( & p - > obj , cmSubSysFailRC , " Scan resync. failed. " ) ;
2013-01-14 00:47:50 +00:00
}
2013-01-16 01:38:21 +00:00
return cmOkRC ;
2013-01-14 00:47:50 +00:00
}
2013-01-31 01:33:44 +00:00
cmRC_t cmScMatcherExec ( cmScMatcher * p , unsigned smpIdx , unsigned status , cmMidiByte_t d0 , cmMidiByte_t d1 , unsigned * scLocIdxPtr )
2013-01-16 01:38:21 +00:00
{
2013-01-31 01:33:44 +00:00
bool fl = p - > mbi > 0 ;
cmRC_t rc = cmOkRC ;
unsigned eli = p - > eli ;
if ( scLocIdxPtr ! = NULL )
* scLocIdxPtr = cmInvalidIdx ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
// update the MIDI buffer with the incoming note
2013-01-31 01:33:44 +00:00
if ( cmScMatcherInputMidi ( p , smpIdx , status , d0 , d1 ) = = false )
return rc ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
// if the MIDI buffer transitioned to full then perform an initial scan sync.
if ( fl & & p - > mbi = = 0 )
{
2013-01-31 01:33:44 +00:00
if ( ( p - > begSyncLocIdx = cmScMatcherScan ( p , p - > ili , cmInvalidCnt ) ) = = cmInvalidIdx )
2013-01-16 01:38:21 +00:00
rc = cmInvalidArgRC ; // signal init. scan sync. fail
else
{
2013-01-30 19:56:56 +00:00
//cmScMatcherPrintPath(p);
2013-01-16 01:38:21 +00:00
}
}
else
{
// if the MIDI buffer is full then perform a step sync.
if ( ! fl & & p - > mbi = = 0 )
rc = cmScMatcherStep ( p ) ;
}
2013-01-14 00:47:50 +00:00
2013-01-31 01:33:44 +00:00
if ( scLocIdxPtr ! = NULL & & p - > eli ! = eli )
* scLocIdxPtr = p - > mp - > loc [ p - > eli ] . scLocIdx ;
2013-01-14 00:47:50 +00:00
2013-01-31 01:33:44 +00:00
return rc ;
2013-01-14 00:47:50 +00:00
}
2013-01-16 01:38:21 +00:00
double cmScMatcherFMeas ( cmScMatcher * p )
2013-01-14 00:47:50 +00:00
{
2013-01-16 01:38:21 +00:00
unsigned bli = p - > mp - > locN ;
unsigned eli = 0 ;
unsigned scNoteCnt = 0 ; // total count of score notes
unsigned matchCnt = 0 ; // count of matched notes (true positives)
unsigned wrongCnt = 0 ; // count of incorrect notes (false positives)
unsigned missCnt = 0 ; // count of missed score notes (false negatives)
unsigned i ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
for ( i = 0 ; i < p - > ri ; + + i )
if ( p - > res [ i ] . locIdx ! = cmInvalidIdx )
{
bli = cmMin ( bli , p - > res [ i ] . locIdx ) ;
eli = cmMax ( eli , p - > res [ i ] . locIdx ) ;
2013-01-16 23:12:54 +00:00
if ( cmIsFlag ( p - > res [ i ] . flags , kSmTruePosFl ) )
2013-01-16 01:38:21 +00:00
+ + matchCnt ;
2013-01-14 00:47:50 +00:00
2013-01-16 23:12:54 +00:00
if ( cmIsFlag ( p - > res [ i ] . flags , kSmFalsePosFl ) )
2013-01-16 01:38:21 +00:00
+ + wrongCnt ;
}
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
scNoteCnt = eli - bli + 1 ;
missCnt = scNoteCnt - matchCnt ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
double prec = ( double ) 2.0 * matchCnt / ( matchCnt + wrongCnt ) ;
double rcal = ( double ) 2.0 * matchCnt / ( matchCnt + missCnt ) ;
double fmeas = prec * rcal / ( prec + rcal ) ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
//printf("total:%i match:%i wrong:%i miss:%i\n",scNoteCnt,matchCnt,wrongCnt,missCnt);
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
return fmeas ;
2013-01-14 00:47:50 +00:00
}
2013-01-16 23:12:54 +00:00
typedef struct cmScMatcherPrint_str
{
unsigned flags ;
unsigned scLocIdx ;
unsigned mni ;
unsigned pitch ;
unsigned vel ;
} cmScMatcherPrint_t ;
void _cmScMatcherInsertPrint ( cmScMatcherPrint_t * a , unsigned i , unsigned * anp , unsigned aan , const cmScMatcherResult_t * rp , unsigned scLocIdx )
{
assert ( * anp + 1 < = aan ) ;
memmove ( a + i + 1 , a + i , ( * anp - i ) * sizeof ( cmScMatcherPrint_t ) ) ;
memset ( a + i , 0 , sizeof ( cmScMatcherPrint_t ) ) ;
* anp + = 1 ;
a [ i ] . flags = rp - > flags ;
a [ i ] . scLocIdx = scLocIdx ;
a [ i ] . mni = rp - > mni ;
a [ i ] . pitch = rp - > pitch ;
a [ i ] . vel = rp - > vel ;
}
void cmScMatcherPrint ( cmScMatcher * p )
{
unsigned bsli = cmScoreEvtCount ( p - > mp - > scH ) ;
unsigned esli = 0 ;
unsigned i , j , k ;
// get first/last scLocIdx from res[]
for ( i = 0 ; i < p - > ri ; + + i )
if ( p - > res [ i ] . locIdx ! = cmInvalidIdx )
{
bsli = cmMin ( bsli , p - > mp - > loc [ p - > res [ i ] . locIdx ] . scLocIdx ) ;
esli = cmMax ( esli , p - > mp - > loc [ p - > res [ i ] . locIdx ] . scLocIdx ) ;
}
unsigned an = 0 ;
unsigned aan = p - > ri ;
// calc the count of score events between bsli and esli.
for ( i = bsli ; i < = esli ; + + i )
{
cmScoreLoc_t * lp = cmScoreLoc ( p - > mp - > scH , i ) ;
assert ( lp ! = NULL ) ;
aan + = lp - > evtCnt ;
}
// allocate an array off 'aan' print records
cmScMatcherPrint_t * a = cmMemAllocZ ( cmScMatcherPrint_t , aan ) ;
2013-01-30 19:56:56 +00:00
// fill a[] note and bar events from cmScoreLoc()
2013-01-16 23:12:54 +00:00
for ( i = bsli ; i < = esli ; + + i )
{
unsigned scLocIdx = i ;
cmScoreLoc_t * lp = cmScoreLoc ( p - > mp - > scH , scLocIdx ) ;
for ( j = 0 ; j < lp - > evtCnt ; + + j )
{
assert ( an < aan ) ;
cmScoreEvt_t * ep = lp - > evtArray [ j ] ;
cmScMatcherPrint_t * pp = a + an ;
an + = 1 ;
switch ( ep - > type )
{
case kBarEvtScId :
pp - > flags = kSmBarFl ;
break ;
case kNonEvtScId :
pp - > flags = kSmNoteFl ;
break ;
}
pp - > scLocIdx = scLocIdx ;
pp - > mni = cmInvalidIdx ;
pp - > pitch = ep - > pitch ;
pp - > vel = kInvalidMidiVelocity ;
}
}
// for each result record
for ( i = 0 ; i < p - > ri ; + + i )
{
cmScMatcherResult_t * rp = p - > res + i ;
// if this result recd matched a score event
if ( cmIsFlag ( rp - > flags , kSmTruePosFl ) )
{
// locate the matching score event
for ( k = 0 ; k < an ; + + k )
if ( a [ k ] . scLocIdx = = p - > mp - > loc [ rp - > locIdx ] . scLocIdx & & a [ k ] . pitch = = rp - > pitch )
{
a [ k ] . mni = rp - > mni ;
a [ k ] . vel = rp - > vel ;
a [ k ] . flags | = kSmMatchFl ;
break ;
}
}
// if this result did not match a score event
if ( cmIsFlag ( rp - > flags , kSmFalsePosFl ) )
{
unsigned d_min ;
cmScMatcherPrint_t * dp = NULL ;
unsigned scLocIdx = cmInvalidIdx ;
// if this result does not have a valid locIdx
// (e.g. errant MIDI notes: scan:'delete' note or a step:mis-match note)
if ( rp - > locIdx = = cmInvalidIdx )
{
// find the print recd with the closet 'mni'
for ( k = 0 ; k < an ; + + k )
if ( a [ k ] . mni ! = cmInvalidIdx )
{
unsigned d ;
if ( a [ k ] . mni > rp - > mni )
d = a [ k ] . mni - rp - > mni ;
else
d = rp - > mni - a [ k ] . mni ;
if ( dp = = NULL | | d < d_min )
{
dp = a + k ;
d_min = d ;
}
}
k = dp - a ;
assert ( k < an ) ;
scLocIdx = p - > mp - > loc [ k ] . scLocIdx ;
if ( a [ k ] . mni < rp - > mni )
+ + k ;
}
else // result w/ a valid locIdx (e.g. scan 'substitute' with no match)
{
scLocIdx = p - > mp - > loc [ rp - > locIdx ] . scLocIdx ;
// find the print recd with the closest scIdx
for ( k = 0 ; k < an ; + + k )
if ( a [ k ] . scLocIdx ! = cmInvalidIdx )
{
unsigned d ;
if ( a [ k ] . scLocIdx > scLocIdx )
d = a [ k ] . scLocIdx - scLocIdx ;
else
d = scLocIdx - a [ k ] . scLocIdx ;
if ( dp = = NULL | | d < d_min )
{
dp = a + k ;
d_min = d ;
}
}
k = dp - a ;
assert ( k < an ) ;
if ( a [ k ] . scLocIdx < scLocIdx )
+ + k ;
}
// create a new print recd to represent the false-positive result recd
assert ( dp ! = NULL ) ;
_cmScMatcherInsertPrint ( a , k , & an , aan , rp , scLocIdx ) ;
}
}
for ( i = 0 ; i < an ; + + i )
{
printf ( " %4i %4i %4s %c%c%c \n " , a [ i ] . scLocIdx , a [ i ] . mni ,
cmIsFlag ( a [ i ] . flags , kSmBarFl ) ? " | " : cmMidiToSciPitch ( a [ i ] . pitch , NULL , 0 ) ,
cmIsFlag ( a [ i ] . flags , kSmNoteFl ) ? ' n ' : ' ' ,
cmIsFlag ( a [ i ] . flags , kSmMatchFl ) ? ' m ' : ( cmIsFlag ( a [ i ] . flags , kSmTransFl ) ? ' t ' : ' ' ) ,
cmIsFlag ( a [ i ] . flags , kSmFalsePosFl ) ? ' * ' : ' '
) ;
}
}
2013-01-18 17:28:33 +00:00
2013-01-14 00:47:50 +00:00
//=======================================================================================================================
2013-01-30 19:56:56 +00:00
cmScMeas * cmScMeasAlloc ( cmCtx * c , cmScMeas * p , cmScH_t scH , double srate , const unsigned * dynRefArray , unsigned dynRefCnt )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
cmScMeas * op = cmObjAlloc ( cmScMeas , c , p ) ;
op - > mp = cmScMatchAlloc ( c , NULL , cmScNullHandle , 0 , 0 ) ;
if ( cmScoreIsValid ( scH ) )
2013-01-30 19:56:56 +00:00
if ( cmScMeasInit ( op , scH , srate , dynRefArray , dynRefCnt ) ! = cmOkRC )
2013-01-18 17:28:33 +00:00
cmScMeasFree ( & op ) ;
2013-01-14 00:47:50 +00:00
return op ;
}
2013-01-18 17:28:33 +00:00
cmRC_t cmScMeasFree ( cmScMeas * * pp )
2013-01-14 00:47:50 +00:00
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return rc ;
2013-01-18 17:28:33 +00:00
cmScMeas * p = * pp ;
if ( ( rc = cmScMeasFinal ( p ) ) ! = cmOkRC )
2013-01-14 00:47:50 +00:00
return rc ;
2013-01-18 17:28:33 +00:00
cmScMatchFree ( & p - > mp ) ;
2013-01-14 00:47:50 +00:00
cmMemFree ( p - > midiBuf ) ;
2013-01-18 17:28:33 +00:00
cmMemFree ( p - > set ) ;
2013-01-30 19:56:56 +00:00
cmMemFree ( p - > dynRef ) ;
2013-01-14 00:47:50 +00:00
cmObjFree ( pp ) ;
return rc ;
}
2013-01-18 17:28:33 +00:00
void _cmScMeasPrint ( cmScMeas * p )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
unsigned i ;
for ( i = 0 ; i < p - > sn ; + + i )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
cmScMeasSet_t * sp = p - > set + i ;
printf ( " %4i: sli:%4i %4i li:%4i %4i \n " , i , sp - > bsli , sp - > esli , sp - > bli , sp - > eli ) ;
2013-01-14 00:47:50 +00:00
}
2013-01-18 17:28:33 +00:00
2013-01-14 00:47:50 +00:00
}
2013-01-30 19:56:56 +00:00
int _cmScMeasSortFunc ( const void * p0 , const void * p1 )
{
const cmScMeasSet_t * s0 = ( const cmScMeasSet_t * ) p0 ;
const cmScMeasSet_t * s1 = ( const cmScMeasSet_t * ) p1 ;
return s0 - > esli - s1 - > esli ;
}
cmRC_t cmScMeasInit ( cmScMeas * p , cmScH_t scH , double srate , const unsigned * dynRefArray , unsigned dynRefCnt )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
cmRC_t rc ;
unsigned i , j ;
unsigned si ;
unsigned maxScWndN = 0 ;
if ( ( rc = cmScMeasFinal ( p ) ) ! = cmOkRC )
2013-01-14 00:47:50 +00:00
return rc ;
2013-01-30 19:56:56 +00:00
p - > mii = 0 ;
2013-01-18 17:28:33 +00:00
p - > mn = 2 * cmScoreEvtCount ( scH ) ;
2013-01-30 19:56:56 +00:00
p - > midiBuf = cmMemResizeZ ( cmScMatchMidi_t , p - > midiBuf , p - > mn ) ;
2013-01-18 17:28:33 +00:00
p - > sn = cmScoreSetCount ( scH ) ;
2013-02-09 07:48:54 +00:00
p - > set = cmMemResizeZ ( cmScMeasSet_t , p - > set , p - > sn ) ;
p - > dynRef = cmMemResizeZ ( unsigned , p - > dynRef , dynRefCnt ) ;
2013-01-30 19:56:56 +00:00
p - > dn = dynRefCnt ;
p - > srate = srate ;
memcpy ( p - > dynRef , dynRefArray , sizeof ( dynRefArray [ 0 ] ) * dynRefCnt ) ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
unsigned n = cmScoreLocCount ( scH ) ;
2013-01-14 00:47:50 +00:00
// for each score location
2013-01-18 17:28:33 +00:00
for ( i = 0 , si = 0 ; i < n ; + + i )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
cmScoreLoc_t * lp = cmScoreLoc ( scH , i ) ;
cmScoreSet_t * sp = lp - > setList ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
// for each set that ends on this score location
for ( ; sp ! = NULL ; sp = sp - > llink , + + si )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
assert ( si < p - > sn ) ;
2013-01-14 23:15:53 +00:00
2013-01-18 17:28:33 +00:00
cmScMeasSet_t * msp = p - > set + si ;
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
msp - > sp = sp ;
2013-01-18 17:28:33 +00:00
msp - > bsli = cmScoreLocCount ( scH ) ;
msp - > esli = 0 ;
msp - > bsei = cmScoreEvtCount ( scH ) ;
msp - > esei = 0 ;
msp - > bli = cmInvalidIdx ;
msp - > eli = cmInvalidIdx ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
for ( j = 0 ; j < sp - > eleCnt ; + + j )
{
msp - > bsli = cmMin ( msp - > bsli , sp - > eleArray [ j ] - > locIdx ) ;
msp - > esli = cmMax ( msp - > esli , sp - > eleArray [ j ] - > locIdx ) ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
msp - > bsei = cmMin ( msp - > bsei , sp - > eleArray [ j ] - > index ) ;
msp - > esei = cmMax ( msp - > esei , sp - > eleArray [ j ] - > index ) ;
}
}
2013-01-14 00:47:50 +00:00
}
2013-01-18 17:28:33 +00:00
// initialize p->mp so that mp->loc[] is loaded - use dummy scWndN and midiN
if ( ( rc = cmScMatchInit ( p - > mp , scH , 11 , 10 ) ) ! = cmOkRC )
return rc ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
// assign set[].bli and set[].eli
for ( j = 0 ; j < p - > sn ; + + j )
{
cmScMeasSet_t * msp = p - > set + j ;
for ( i = 0 ; i < p - > mp - > locN ; + + i )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
if ( msp - > bli = = cmInvalidIdx & & msp - > bsli = = p - > mp - > loc [ i ] . scLocIdx )
msp - > bli = i ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
if ( msp - > esli = = p - > mp - > loc [ i ] . scLocIdx )
msp - > eli = i ;
}
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
assert ( msp - > eli > msp - > bli ) ;
maxScWndN = cmMax ( maxScWndN , msp - > eli - msp - > bli + 1 ) ;
2013-01-14 00:47:50 +00:00
}
2013-01-18 17:28:33 +00:00
// setup the match
if ( ( rc = cmScMatchInit ( p - > mp , scH , 2 * maxScWndN + 1 , 2 * maxScWndN ) ) ! = cmOkRC )
return rc ;
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
// sort set[] on cmScMeasSet_t.esli
qsort ( p - > set , p - > sn , sizeof ( cmScMeasSet_t ) , _cmScMeasSortFunc ) ;
2013-01-18 17:28:33 +00:00
//_cmScMeasPrint(p);
2013-01-14 23:15:53 +00:00
2013-01-31 01:33:44 +00:00
cmScMeasReset ( p ) ;
2013-01-14 00:47:50 +00:00
return rc ;
}
2013-01-18 17:28:33 +00:00
cmRC_t cmScMeasFinal ( cmScMeas * p )
{ return cmScMatchFinal ( p - > mp ) ; }
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
cmRC_t cmScMeasReset ( cmScMeas * p )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
cmRC_t rc = cmOkRC ;
2013-01-30 19:56:56 +00:00
p - > mii = 0 ;
p - > nsi = cmInvalidIdx ;
2013-01-31 01:33:44 +00:00
p - > vsi = cmInvalidIdx ;
2013-01-30 19:56:56 +00:00
p - > nsli = cmInvalidIdx ;
2013-01-31 01:33:44 +00:00
unsigned i ;
for ( i = 0 ; i < p - > sn ; + + i )
p - > set [ i ] . value = DBL_MAX ;
2013-01-18 17:28:33 +00:00
return rc ;
2013-01-14 00:47:50 +00:00
}
2013-01-30 19:56:56 +00:00
typedef struct
2013-01-14 00:47:50 +00:00
{
2013-01-30 19:56:56 +00:00
unsigned scLocIdx ; // score loc index
double frac ; // score based fraction of beat
unsigned smpIdx ; // time of assoc'd MIDI event
unsigned cnt ; //
double val ; //
} _cmScMeasTimeEle_t ;
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
typedef struct
{
unsigned setN ; // set length
unsigned midiN ; // count of MIDI events to match to score
unsigned alignN ; // count of score events in the alignment (<= setN)
unsigned matchN ; // count of positive matches
double tempo ;
double value ;
} _cmScMeasResult_t ;
double _cmScMeasCalcTempo ( const _cmScMeasTimeEle_t * b , unsigned bn , double srate )
{
assert ( bn > = 2 ) ;
assert ( b [ bn - 1 ] . smpIdx ! = cmInvalidIdx ) ;
assert ( b [ 0 ] . smpIdx ! = cmInvalidIdx ) ;
double durSmpCnt = b [ bn - 1 ] . smpIdx - b [ 0 ] . smpIdx ;
double beats = 0 ;
unsigned i ;
for ( i = 0 ; i < bn ; + + i )
beats + = b [ i ] . frac ;
assert ( beats ! = 0 ) ;
return beats / ( durSmpCnt / ( srate * 60.0 ) ) ;
}
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
// Note: On successful completion (return!=0) the first
// and last record returned in c[cn] will be matched records.
unsigned _cmScMeasTimeAlign ( cmScMeas * p , cmScMeasSet_t * sp , cmScMatchMidi_t * m , unsigned mn , _cmScMeasTimeEle_t * c , unsigned cn , _cmScMeasResult_t * rp )
{
int i , j , k ;
int an = sp - > sp - > eleCnt ;
_cmScMeasTimeEle_t * b = NULL ;
int bn = 0 ;
bool tempoFl = false ;
unsigned matchN = 0 ;
assert ( an ! = 0 ) ;
// alloc a 'score set' element array
_cmScMeasTimeEle_t a [ an ] ;
// get the scLocIdx of each set element from the score
for ( i = 0 , j = 0 ; i < an ; + + i )
if ( i = = 0 | | sp - > sp - > eleArray [ i - 1 ] - > locIdx ! = sp - > sp - > eleArray [ i ] - > locIdx )
{
assert ( sp - > sp - > eleArray [ i ] - > locIdx ! = cmInvalidIdx ) ;
a [ j ] . scLocIdx = sp - > sp - > eleArray [ i ] - > locIdx ;
a [ j ] . frac = sp - > sp - > eleArray [ i ] - > frac ;
a [ j ] . smpIdx = cmInvalidIdx ;
a [ j ] . cnt = 0 ;
+ + j ;
}
an = j ; // set the count of unique score locations (new length of a[])
// collect the 'smpIdx' for each MIDI event which matches a set element
for ( i = 0 ; i < mn ; + + i )
if ( m [ i ] . locIdx ! = cmInvalidIdx )
{
for ( j = 0 ; j < an ; + + j )
if ( p - > mp - > loc [ m [ i ] . locIdx ] . scLocIdx = = a [ j ] . scLocIdx )
{
a [ j ] . smpIdx + = m [ i ] . smpIdx ;
a [ j ] . cnt + = 1 ;
if ( a [ j ] . cnt = = 1 )
matchN + = 1 ; // only cnt one match per sc loc.
break ;
}
}
// remove leading missing values
for ( i = 0 ; i < an ; + + i )
if ( a [ i ] . smpIdx ! = cmInvalidIdx )
{
b = a + i ;
bn = an - i ;
break ;
}
// remove trailing missing values
for ( i = bn - 1 ; i > = 0 ; - - i , - - bn )
if ( b [ i ] . smpIdx ! = cmInvalidIdx )
break ;
// can't measure evenness against less than 2 values
if ( bn < 2 )
{
return 0 ;
}
assert ( b [ 0 ] . smpIdx ! = cmInvalidIdx & & b [ bn - 1 ] . smpIdx ! = cmInvalidIdx ) ;
2013-02-09 07:48:54 +00:00
// calc avg. smpIdx, insert missing values, and convert b[].smpIdx to delta smp index
2013-01-30 19:56:56 +00:00
for ( i = 0 , j = 0 ; i < bn ; + + i )
2013-01-14 00:47:50 +00:00
{
2013-01-30 19:56:56 +00:00
if ( b [ i ] . cnt > 1 )
b [ i ] . smpIdx / = b [ i ] . cnt ;
if ( b [ i ] . smpIdx = = cmInvalidIdx )
+ + j ; // incr missing value count
else
{
if ( i > 0 )
{
// fill in missing values
+ + j ;
unsigned d = ( b [ i ] . smpIdx - b [ i - j ] . smpIdx ) / j ;
for ( k = 0 ; k < j ; + + k )
b [ i - j + k ] . val = d ;
}
j = 0 ;
}
if ( b [ i ] . frac ! = 0 )
tempoFl = true ;
}
rp - > setN = an ;
rp - > midiN = mn ;
rp - > alignN = bn ;
rp - > matchN = matchN ;
rp - > tempo = 0 ;
rp - > value = 0 ;
// calculate tempo
if ( tempoFl )
rp - > tempo = _cmScMeasCalcTempo ( b , bn , p - > srate ) ;
assert ( bn < = cn ) ;
2013-01-31 01:33:44 +00:00
// TODO: this copy should be eliminated
2013-01-30 19:56:56 +00:00
// copy to output
for ( i = 0 ; i < bn & & i < cn ; + + i )
c [ i ] = b [ i ] ;
return bn ;
}
double _cmScMeasEven ( cmScMeas * p , cmScMeasSet_t * sp , cmScMatchMidi_t * m , unsigned mn , _cmScMeasResult_t * rp )
{
unsigned bn = sp - > sp - > eleCnt ;
_cmScMeasTimeEle_t b [ bn ] ;
unsigned i ;
if ( ( bn = _cmScMeasTimeAlign ( p , sp , m , mn , b , bn , rp ) ) = = 0 )
return DBL_MAX ;
// calc avg. delta time
double d_avg = 0 ;
for ( i = 0 ; i < bn - 1 ; + + i )
d_avg + = b [ i ] . val ;
d_avg / = ( bn - 1 ) ;
// calc std-dev of delta time
double d_sd = 0 ;
for ( i = 0 ; i < bn - 1 ; + + i )
2013-02-09 07:48:54 +00:00
d_sd + = ( b [ i ] . val - d_avg ) * ( b [ i ] . val - d_avg ) ;
2013-01-30 19:56:56 +00:00
// if there is no deviation then we can't compute a z-score
// (this will happen if we fill in all the missing values based on 2 values)
if ( d_sd = = 0 )
return 1.0 ;
d_sd = sqrt ( d_sd / ( bn - 1 ) ) ;
// calc avg. z-score
double z = 0 ;
for ( i = 0 ; i < bn - 1 ; + + i )
2013-02-09 07:48:54 +00:00
z + = fabs ( b [ i ] . val - d_avg ) / d_sd ;
2013-01-30 19:56:56 +00:00
double val = z / ( bn - 1 ) ;
rp - > value = val ;
return val ;
}
// return Tempo estimation in BPM
double _cmScMeasTempo ( cmScMeas * p , cmScMeasSet_t * sp , cmScMatchMidi_t * m , unsigned mn , _cmScMeasResult_t * rp )
{
unsigned bn = sp - > sp - > eleCnt ;
_cmScMeasTimeEle_t b [ bn ] ;
if ( ( bn = _cmScMeasTimeAlign ( p , sp , m , mn , b , bn , rp ) ) = = 0 )
return DBL_MAX ;
return rp - > tempo ;
}
double _cmScMeasDyn ( cmScMeas * p , cmScMeasSet_t * sp , cmScMatchMidi_t * m , unsigned mn , _cmScMeasResult_t * rp )
{
typedef struct
{
unsigned scEvtIdx ;
unsigned vel ;
double val ;
} ele_t ;
int i , j ;
int n = sp - > sp - > eleCnt ;
double vv = 0 ;
unsigned vn = 0 ;
unsigned matchN = 0 ;
unsigned alignN = 0 ;
assert ( n ! = 0 ) ;
ele_t a [ n ] ;
// get the scEvtIdx of each set element
for ( i = 0 ; i < n ; + + i )
{
cmScoreEvt_t * ep = cmScoreEvt ( p - > mp - > scH , sp - > sp - > eleArray [ i ] - > index ) ;
assert ( ep ! = NULL ) ;
a [ i ] . scEvtIdx = sp - > sp - > eleArray [ i ] - > index ;
a [ i ] . vel = 0 ;
a [ i ] . val = ep - > dynVal ;
}
// set the performed vel. of each note in the set
// (if a note was not played it's a[].vel is left at 0)
for ( i = 0 ; i < mn ; + + i )
if ( m [ i ] . scEvtIdx ! = cmInvalidIdx )
{
alignN + = 1 ;
for ( j = 0 ; j < n ; + + j )
if ( m [ i ] . scEvtIdx = = a [ j ] . scEvtIdx )
{
matchN + = 1 ;
a [ j ] . vel = m [ i ] . vel ;
break ;
}
}
// assign a dynamics category to each note in the set
for ( i = 0 ; i < n ; + + i )
if ( a [ i ] . vel > 0 )
{
unsigned mnv = 0 ; // lower bound for first dyn's category
for ( j = 0 ; j < p - > dn ; + + j )
{
if ( mnv < = a [ i ] . vel & & a [ i ] . vel < p - > dynRef [ j ] )
{
// accum. the diff. between the ref. and performed dyn. category
vv + = fabs ( a [ i ] . val - j ) ;
vn + = 1 ;
break ;
}
mnv = p - > dynRef [ j ] ;
}
assert ( j < p - > dn ) ;
}
rp - > setN = n ;
rp - > midiN = mn ;
rp - > alignN = alignN ;
rp - > matchN = matchN ;
rp - > tempo = 0 ;
rp - > value = vn = = 0 ? DBL_MAX : vv / vn ;
return rp - > value ;
}
2013-01-31 01:33:44 +00:00
2013-01-30 19:56:56 +00:00
unsigned MEAS_MATCH_CNT = 0 ;
2013-01-31 01:33:44 +00:00
void _cmScMeasPrintResult ( cmScMeas * p , cmScMeasSet_t * sp , _cmScMeasResult_t * rp , unsigned bli , const cmScMatchMidi_t * mb )
{
const char * label = " <none> " ;
switch ( sp - > sp - > varId )
{
case kEvenVarScId :
label = " even " ;
break ;
case kDynVarScId :
label = " dyn " ;
break ;
case kTempoVarScId :
label = " tempo " ;
break ;
}
const cmChar_t * msg = " " ;
if ( rp - > value = = DBL_MAX )
{
msg = " Measure FAILED. " ;
sp - > value = 0 ;
}
printf ( " %i set:%i %s bsli:%i esli:%i [set:%i match:%i] cost:%f val:%f %s " , MEAS_MATCH_CNT , p - > nsi , label , sp - > bsli , sp - > esli , rp - > setN , rp - > matchN , p - > mp - > opt_cost , sp - > value , msg ) ;
if ( rp - > tempo ! = 0 )
printf ( " tempo:%f " , rp - > tempo ) ;
printf ( " \n " ) ;
_cmScMatchPrintPath ( p - > mp , p - > mp - > p_opt , bli , mb ) ;
}
void _cmScMeasCalcVal ( cmScMeas * p , cmScMeasSet_t * sp , int n_mii )
2013-01-30 19:56:56 +00:00
{
2013-01-31 01:33:44 +00:00
unsigned mn = 0 ;
2013-01-30 19:56:56 +00:00
int i ;
if ( n_mii = = 0 )
return ;
// Determine the count of MIDI notes to match to the set score
// by searching from the MIDI note just recieved (midiBuf[n_mii]
// back toward the beginning until a MIDI event which occurs just
// prior to the set's begScLocIdx.
for ( i = n_mii ; i > = 0 ; - - i )
{
+ + mn ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
if ( p - > midiBuf [ i ] . locIdx ! = cmInvalidIdx )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
unsigned scLocIdx = p - > mp - > loc [ p - > midiBuf [ i ] . locIdx ] . scLocIdx ;
2013-01-30 19:56:56 +00:00
if ( scLocIdx < sp - > bsli )
2013-01-18 17:28:33 +00:00
break ;
2013-01-14 00:47:50 +00:00
}
2013-01-18 17:28:33 +00:00
}
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
i = cmMax ( 0 , i ) ;
assert ( mn > 0 ) ;
// Create a copy of the the MIDI buffer to prevent the
// p->midiBuf[].locIdx from being overwritten by cmScMatchDoSync().
cmScMatchMidi_t mb [ mn ] ;
unsigned j ;
for ( j = 0 ; j < mn ; + + j )
{
mb [ j ] = p - > midiBuf [ i + j ] ;
mb [ j ] . locIdx = cmInvalidIdx ;
}
2013-01-18 17:28:33 +00:00
2013-01-30 19:56:56 +00:00
// In general the first and last MIDI event should be assigned
// to a score location - it's possible however that no MIDI
// event's prior to the one at p->midiBuf[n_mii] were assigned.
2013-01-31 01:33:44 +00:00
assert ( ( i = = 0 | | p - > midiBuf [ i ] . locIdx ! = cmInvalidIdx ) & & p - > midiBuf [ i + mn - 1 ] . locIdx ! = cmInvalidIdx ) ;
2013-01-30 19:56:56 +00:00
2013-01-31 01:33:44 +00:00
unsigned bli = p - > midiBuf [ i ] . locIdx ;
unsigned ln = p - > midiBuf [ i + mn - 1 ] . locIdx - bli + 1 ;
double min_cost = DBL_MAX ;
_cmScMeasResult_t r ;
memset ( & r , 0 , sizeof ( r ) ) ;
2013-01-30 19:56:56 +00:00
// match MIDI to score
if ( cmScMatchExec ( p - > mp , bli , ln , mb , mn , min_cost ) ! = cmOkRC )
return ;
// sync the score and MIDI based on the match information
if ( cmScMatchDoSync ( p - > mp , bli , mb , mn , NULL ) = = cmInvalidIdx )
return ;
switch ( sp - > sp - > varId )
{
case kEvenVarScId :
2013-01-31 01:33:44 +00:00
sp - > value = _cmScMeasEven ( p , sp , mb , mn , & r ) ;
2013-01-30 19:56:56 +00:00
break ;
case kDynVarScId :
2013-01-31 01:33:44 +00:00
sp - > value = _cmScMeasDyn ( p , sp , mb , mn , & r ) ;
2013-01-30 19:56:56 +00:00
break ;
case kTempoVarScId :
2013-01-31 01:33:44 +00:00
sp - > value = _cmScMeasTempo ( p , sp , mb , mn , & r ) ;
2013-01-30 19:56:56 +00:00
break ;
2013-01-31 01:33:44 +00:00
default :
{ assert ( 0 ) ; }
2013-01-30 19:56:56 +00:00
}
2013-01-31 01:33:44 +00:00
sp - > tempo = r . tempo ;
2013-01-14 00:47:50 +00:00
2013-01-31 01:33:44 +00:00
// print the result
_cmScMeasPrintResult ( p , sp , & r , bli , mb ) ;
2013-01-30 19:56:56 +00:00
2013-01-31 01:33:44 +00:00
MEAS_MATCH_CNT + + ;
}
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
cmRC_t cmScMeasExec ( cmScMeas * p , unsigned mni , unsigned locIdx , unsigned scEvtIdx , unsigned flags , unsigned smpIdx , unsigned pitch , unsigned vel )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
cmRC_t rc = cmOkRC ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
// if the midi buffer is full
2013-01-30 19:56:56 +00:00
if ( p - > mii > = p - > mn )
2013-01-18 17:28:33 +00:00
return cmCtxRtCondition ( & p - > obj , cmEofRC , " The MIDI buffer is full. " ) ;
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
int n_mii = cmInvalidIdx ;
2013-02-09 07:48:54 +00:00
2013-01-31 01:33:44 +00:00
// locate the MIDI event assoc'd with 'mni' ...
2013-01-30 19:56:56 +00:00
if ( p - > mii > 0 & & mni < = p - > midiBuf [ p - > mii - 1 ] . mni )
{
if ( locIdx ! = cmInvalidIdx )
{
for ( n_mii = p - > mii - 1 ; n_mii > = 0 ; - - n_mii )
if ( p - > midiBuf [ n_mii ] . mni = = mni )
break ;
if ( n_mii < 0 )
n_mii = cmInvalidIdx ;
}
}
2013-01-31 01:33:44 +00:00
else // ... or push a new record onto p->midiBuf[]
2013-01-30 19:56:56 +00:00
{
n_mii = p - > mii ;
+ + p - > mii ;
}
2013-01-18 17:28:33 +00:00
// store the MIDI event
2013-01-30 19:56:56 +00:00
p - > midiBuf [ n_mii ] . mni = mni ;
p - > midiBuf [ n_mii ] . locIdx = locIdx ;
p - > midiBuf [ n_mii ] . scEvtIdx = scEvtIdx ;
p - > midiBuf [ n_mii ] . smpIdx = smpIdx ;
p - > midiBuf [ n_mii ] . pitch = pitch ;
p - > midiBuf [ n_mii ] . vel = vel ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
if ( locIdx = = cmInvalidIdx )
return cmOkRC ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
//
unsigned scLocIdx = p - > mp - > loc [ locIdx ] . scLocIdx ;
unsigned maxScLocIdx = cmScoreLocCount ( p - > mp - > scH ) - 1 ;
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
// if this cmScMeas object has not yet synchronized to the cmScMatcher
// (if p->nsli is not valid)
if ( p - > nsli = = cmInvalidIdx )
{
unsigned i ;
for ( i = 0 ; i < p - > sn ; + + i )
if ( p - > set [ i ] . esli + 1 = = scLocIdx )
{
p - > nsli = scLocIdx ;
p - > nsi = i ;
break ;
}
if ( i = = p - > sn )
return rc ;
}
2013-01-31 01:33:44 +00:00
p - > vsi = p - > nsi ;
2013-02-09 07:48:54 +00:00
p - > vsli = p - > nsli ;
2013-01-31 01:33:44 +00:00
2013-01-30 19:56:56 +00:00
// for each cmScore location between p->nsli and scLocIdx
2013-01-18 17:28:33 +00:00
for ( ; p - > nsli < = scLocIdx & & p - > nsi < p - > sn ; + + p - > nsli )
{
2013-01-30 19:56:56 +00:00
// if this score location index (p->nsli) is one score location
// ahead of the next sets ending location.
2013-01-18 17:28:33 +00:00
while ( cmMin ( maxScLocIdx , p - > set [ p - > nsi ] . esli + 1 ) = = p - > nsli )
2013-01-14 00:47:50 +00:00
{
2013-01-31 01:33:44 +00:00
// calculate the value assoc'd with p->set[p->nsi]
_cmScMeasCalcVal ( p , p - > set + p - > nsi , n_mii ) ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
// advance the set index
+ + p - > nsi ;
2013-01-14 00:47:50 +00:00
}
}
2013-01-18 17:28:33 +00:00
return rc ;
2013-01-14 00:47:50 +00:00
}
2013-01-18 17:28:33 +00:00
//=======================================================================================================================
cmRC_t cmScAlignScanToTimeLineEvent ( cmScMatcher * p , cmTlH_t tlH , cmTlObj_t * top , unsigned endSmpIdx )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
assert ( top ! = NULL ) ;
cmTlMidiEvt_t * mep = NULL ;
cmRC_t rc = cmOkRC ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
// as long as more MIDI events are available get the next MIDI msg
while ( rc = = cmOkRC & & ( mep = cmTlNextMidiEvtObjPtr ( tlH , top , top - > seqId ) ) ! = NULL )
2013-01-14 00:47:50 +00:00
{
2013-01-18 17:28:33 +00:00
top = & mep - > obj ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
// if the msg falls after the end of the marker then we are through
if ( mep - > obj . seqSmpIdx ! = cmInvalidIdx & & mep - > obj . seqSmpIdx > endSmpIdx )
break ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
// if the time line MIDI msg a note-on
if ( mep - > msg - > status = = kNoteOnMdId )
2013-01-14 00:47:50 +00:00
{
2013-01-31 01:33:44 +00:00
rc = cmScMatcherExec ( p , mep - > obj . seqSmpIdx , mep - > msg - > status , mep - > msg - > u . chMsgPtr - > d0 , mep - > msg - > u . chMsgPtr - > d1 , NULL ) ;
2013-01-14 00:47:50 +00:00
2013-01-18 17:28:33 +00:00
switch ( rc )
2013-01-14 00:47:50 +00:00
{
2013-02-09 07:48:54 +00:00
case cmOkRC : // continue processing MIDI events
2013-01-14 00:47:50 +00:00
break ;
2013-02-09 07:48:54 +00:00
case cmEofRC : // end of the score was encountered
2013-01-14 00:47:50 +00:00
break ;
2013-01-31 01:33:44 +00:00
case cmInvalidArgRC : // p->eli was not set correctly
2013-01-14 23:15:53 +00:00
break ;
case cmSubSysFailRC : // scan resync failed
break ;
2013-01-31 01:33:44 +00:00
default :
{ assert ( 0 ) ; }
2013-01-14 23:15:53 +00:00
}
}
2013-01-14 00:47:50 +00:00
}
2013-01-14 23:15:53 +00:00
if ( rc = = cmEofRC )
rc = cmOkRC ;
2013-01-14 00:47:50 +00:00
2013-01-14 23:15:53 +00:00
return rc ;
}
2013-01-14 00:47:50 +00:00
2013-01-30 19:56:56 +00:00
// This callback connects/feeds the cmScMeas object from the cmScMatcher object.
// (See _cmScMatcherStoreResult().)
2013-01-18 17:28:33 +00:00
void cmScMatcherCb ( cmScMatcher * p , void * arg , cmScMatcherResult_t * rp )
2013-01-14 00:47:50 +00:00
{
2013-01-30 19:56:56 +00:00
cmScMeas * mp = ( cmScMeas * ) arg ;
cmScMeasExec ( mp , rp - > mni , rp - > locIdx , rp - > scEvtIdx , rp - > flags , rp - > smpIdx , rp - > pitch , rp - > vel ) ;
2013-01-14 00:47:50 +00:00
}
void cmScAlignScanMarkers ( cmRpt_t * rpt , cmTlH_t tlH , cmScH_t scH )
{
2013-01-18 17:28:33 +00:00
unsigned i ;
2013-01-30 19:56:56 +00:00
double srate = cmTimeLineSampleRate ( tlH ) ;
2013-01-18 17:28:33 +00:00
unsigned midiN = 7 ;
unsigned scWndN = 10 ;
unsigned markN = 291 ;
2013-01-30 19:56:56 +00:00
unsigned dynRefArray [ ] = { 14 , 28 , 42 , 56 , 71 , 85 , 99 , 113 , 128 } ;
unsigned dynRefCnt = sizeof ( dynRefArray ) / sizeof ( dynRefArray [ 0 ] ) ;
2013-01-18 17:28:33 +00:00
cmCtx * ctx = cmCtxAlloc ( NULL , rpt , cmLHeapNullHandle , cmSymTblNullHandle ) ;
2013-01-30 19:56:56 +00:00
cmScMeas * mp = cmScMeasAlloc ( ctx , NULL , scH , srate , dynRefArray , dynRefCnt ) ;
2013-01-31 01:33:44 +00:00
cmScMatcher * p = cmScMatcherAlloc ( ctx , NULL , srate , scH , scWndN , midiN , cmScMatcherCb , mp ) ;
2013-01-18 17:28:33 +00:00
double scoreThresh = 0.5 ;
unsigned candCnt = 0 ;
unsigned initFailCnt = 0 ;
unsigned otherFailCnt = 0 ;
unsigned scoreFailCnt = 0 ;
bool printFl = false ;
unsigned markCharCnt = 31 ;
cmChar_t markText [ markCharCnt + 1 ] ;
2013-01-16 01:38:21 +00:00
cmTimeSpec_t t0 , t1 ;
2013-01-14 23:15:53 +00:00
2013-01-16 01:38:21 +00:00
cmTimeGet ( & t0 ) ;
2013-01-14 00:47:50 +00:00
// for each marker
for ( i = 0 ; i < markN ; + + i )
{
// form the marker text
snprintf ( markText , markCharCnt , " Mark %i " , i ) ;
// locate the marker
2013-01-30 19:56:56 +00:00
cmTlMarker_t * tlmp = cmTimeLineMarkerFind ( tlH , markText ) ;
if ( tlmp = = NULL )
2013-01-14 00:47:50 +00:00
{
2013-01-16 23:12:54 +00:00
if ( printFl )
printf ( " The marker '%s' was not found. \n \n " , markText ) ;
2013-01-14 00:47:50 +00:00
continue ;
}
// skip markers which do not contain text
2013-01-30 19:56:56 +00:00
if ( cmTextIsEmpty ( tlmp - > text ) )
2013-01-14 00:47:50 +00:00
{
2013-01-16 23:12:54 +00:00
if ( printFl )
printf ( " The marker '%s' is being skipped because it has no text. \n \n " , markText ) ;
2013-01-14 00:47:50 +00:00
continue ;
}
2013-01-30 19:56:56 +00:00
printf ( " =================== MARKER:%s =================== \n " , markText ) ;
2013-01-31 01:33:44 +00:00
cmScMatcherReset ( p , 0 ) ; // reset the score follower to the beginnig of the score
2013-01-30 19:56:56 +00:00
cmScMeasReset ( mp ) ;
2013-01-14 00:47:50 +00:00
2013-01-14 23:15:53 +00:00
+ + candCnt ;
2013-01-14 00:47:50 +00:00
// scan to the beginning of the marker
2013-01-30 19:56:56 +00:00
cmRC_t rc = cmScAlignScanToTimeLineEvent ( p , tlH , & tlmp - > obj , tlmp - > obj . seqSmpIdx + tlmp - > obj . durSmpCnt ) ;
2013-01-14 23:15:53 +00:00
bool pfl = true ;
if ( rc ! = cmOkRC | | p - > begSyncLocIdx = = cmInvalidIdx )
{
2013-01-30 19:56:56 +00:00
bool fl = printFl ;
printFl = true ;
// if a no alignment was found
2013-01-14 23:15:53 +00:00
if ( p - > begSyncLocIdx = = cmInvalidIdx )
rc = cmInvalidArgRC ;
if ( p - > mni = = 0 )
{
2013-01-16 23:12:54 +00:00
if ( printFl )
printf ( " mark:%i midi:%i Not enough MIDI notes to fill the scan buffer. \n " , i , p - > mni ) ;
2013-01-14 23:15:53 +00:00
pfl = false ;
}
else
{
switch ( rc )
{
case cmInvalidArgRC :
2013-01-16 23:12:54 +00:00
if ( printFl )
printf ( " mark:%i INITIAL SYNC FAIL \n " , i ) ;
2013-01-14 23:15:53 +00:00
+ + initFailCnt ;
pfl = false ;
break ;
case cmSubSysFailRC :
2013-01-16 23:12:54 +00:00
if ( printFl )
printf ( " mark:%i SCAN RESYNC FAIL \n " , i ) ;
2013-01-14 23:15:53 +00:00
+ + otherFailCnt ;
break ;
default :
2013-01-16 23:12:54 +00:00
if ( printFl )
printf ( " mark:%i UNKNOWN FAIL \n " , i ) ;
2013-01-14 23:15:53 +00:00
+ + otherFailCnt ;
}
}
2013-01-30 19:56:56 +00:00
printFl = fl ;
2013-01-14 23:15:53 +00:00
}
if ( pfl )
2013-01-14 00:47:50 +00:00
{
2013-01-16 01:38:21 +00:00
double fmeas = cmScMatcherFMeas ( p ) ;
2013-01-14 00:47:50 +00:00
2013-01-16 23:12:54 +00:00
if ( printFl )
2013-01-30 19:56:56 +00:00
printf ( " mark:%i midi:%i loc:%i bar:%i cost:%f f-meas:%f text:%s \n " , i , p - > mni , p - > begSyncLocIdx , p - > mp - > loc [ p - > begSyncLocIdx ] . barNumb , p - > s_opt , fmeas , tlmp - > text ) ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
if ( fmeas < scoreThresh )
2013-01-14 23:15:53 +00:00
+ + scoreFailCnt ;
2013-01-14 00:47:50 +00:00
}
2013-01-14 23:15:53 +00:00
2013-01-18 17:28:33 +00:00
//print score and match for entire marker
2013-01-16 23:12:54 +00:00
//cmScMatcherPrint(p);
2013-01-18 17:28:33 +00:00
// ONLY USE ONE MARKER DURING TESTING
2013-01-30 19:56:56 +00:00
// break;
2013-01-14 00:47:50 +00:00
2013-01-16 23:12:54 +00:00
if ( printFl )
printf ( " \n " ) ;
2013-01-14 00:47:50 +00:00
2013-01-14 23:15:53 +00:00
}
2013-01-14 00:47:50 +00:00
2013-01-14 23:15:53 +00:00
printf ( " cand:%i fail:%i - init:%i score:%i other:%i \n \n " , candCnt , initFailCnt + scoreFailCnt + otherFailCnt , initFailCnt , scoreFailCnt , otherFailCnt ) ;
2013-01-14 00:47:50 +00:00
2013-01-16 01:38:21 +00:00
cmTimeGet ( & t1 ) ;
printf ( " elapsed:%f \n " , ( double ) cmTimeElapsedMicros ( & t0 , & t1 ) / 1000000.0 ) ;
cmScMatcherFree ( & p ) ;
2013-01-18 17:28:33 +00:00
cmScMeasFree ( & mp ) ;
2013-01-14 00:47:50 +00:00
cmCtxFree ( & ctx ) ;
}
2013-02-09 07:48:54 +00:00
//=======================================================================================================================
cmScModulator * cmScModulatorAlloc ( cmCtx * c , cmScModulator * p , cmCtx_t * ctx , cmSymTblH_t stH , double srate , unsigned samplesPerCycle , const cmChar_t * fn , const cmChar_t * modLabel , cmScModCb_t cbFunc , void * cbArg )
{
cmScModulator * op = cmObjAlloc ( cmScModulator , c , p ) ;
if ( ctx ! = NULL )
if ( cmScModulatorInit ( op , ctx , stH , srate , samplesPerCycle , fn , modLabel , cbFunc , cbArg ) ! = cmOkRC )
cmScModulatorFree ( & op ) ;
return op ;
}
cmRC_t cmScModulatorFree ( cmScModulator * * pp )
{
cmRC_t rc = cmOkRC ;
if ( pp = = NULL | | * pp = = NULL )
return rc ;
cmScModulator * p = * pp ;
if ( ( rc = cmScModulatorFinal ( p ) ) ! = cmOkRC )
return rc ;
cmMemFree ( p - > earray ) ;
cmObjFree ( pp ) ;
return rc ;
}
typedef struct
{
unsigned typeId ;
unsigned minArgCnt ;
const cmChar_t * label ;
} _cmScModTypeMap_t ;
_cmScModTypeMap_t _cmScModTypeArray [ ] =
{
{ kSetModTId , 1 , " set " } ,
{ kLineModTId , 2 , " line " } ,
{ kSetLineModTId , 3 , " sline " } ,
{ kInvalidModTId , 0 , " <invalid> " }
} ;
const _cmScModTypeMap_t * _cmScModTypeLabelToMap ( const cmChar_t * label )
{
unsigned i ;
for ( i = 0 ; _cmScModTypeArray [ i ] . typeId ! = kInvalidModTId ; + + i )
if ( strcmp ( _cmScModTypeArray [ i ] . label , label ) = = 0 )
return _cmScModTypeArray + i ;
return NULL ;
}
cmScModVar_t * _cmScModulatorInsertValue ( cmScModulator * p , unsigned varSymId )
{
cmScModVar_t * vp = p - > vlist ;
for ( ; vp ! = NULL ; vp = vp - > vlink )
if ( varSymId = = vp - > varSymId )
return vp ;
vp = cmMemAllocZ ( cmScModVar_t , 1 ) ;
vp - > varSymId = varSymId ;
vp - > vlink = p - > vlist ;
p - > vlist = vp ;
return vp ;
}
cmRC_t _cmScModulatorInsertEntry ( cmScModulator * p , unsigned idx , unsigned scLocIdx , unsigned modSymId , unsigned varSymId , unsigned typeId , const double * av , unsigned an )
{
assert ( idx < p - > en ) ;
if ( p - > modSymId ! = modSymId )
return cmOkRC ;
p - > earray [ idx ] . scLocIdx = scLocIdx ;
p - > earray [ idx ] . typeId = typeId ;
p - > earray [ idx ] . parray = an = = 0 ? NULL : cmMemAllocZ ( double , an ) ;
p - > earray [ idx ] . pn = an ;
p - > earray [ idx ] . valPtr = _cmScModulatorInsertValue ( p , varSymId ) ;
unsigned i ;
for ( i = 0 ; i < an ; + + i )
p - > earray [ idx ] . parray [ i ] = av [ i ] ;
return cmOkRC ;
}
/*
{
[
{ loc : 123 , mod : modlabel , var : varlabel , param : [ ] }
]
}
*/
cmRC_t _cmScModulatorParse ( cmScModulator * p , cmCtx_t * ctx , cmSymTblH_t stH , const cmChar_t * fn )
{
cmRC_t rc = cmOkRC ;
cmJsonNode_t * jnp = NULL ;
cmJsonH_t jsH = cmJsonNullHandle ;
unsigned i ;
// read the JSON file
if ( cmJsonInitializeFromFile ( & jsH , fn , ctx ) ! = kOkJsRC )
return cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " JSON file parse failed on the modulator file: %s. " , cmStringNullGuard ( fn ) ) ;
jnp = cmJsonRoot ( jsH ) ;
// validate that the first child as an array
if ( jnp = = NULL | | ( ( jnp = cmJsonNodeMemberValue ( jnp , " array " ) ) = = NULL ) | | cmJsonIsArray ( jnp ) = = false )
{
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Modulator file header syntax error in file:%s " , cmStringNullGuard ( fn ) ) ;
goto errLabel ;
}
// allocate the entry array
p - > en = cmJsonChildCount ( jnp ) ;
p - > earray = cmMemResizeZ ( cmScModEntry_t , p - > earray , p - > en ) ;
for ( i = 0 ; i < p - > en ; + + i )
{
cmJsRC_t jsRC ;
const char * errLabelPtr = NULL ;
unsigned scLocIdx = cmInvalidIdx ;
const cmChar_t * modLabel = NULL ;
const cmChar_t * varLabel = NULL ;
const cmChar_t * typeLabel = NULL ;
cmJsonNode_t * onp = cmJsonArrayElement ( jnp , i ) ;
cmJsonNode_t * dnp = NULL ;
const _cmScModTypeMap_t * map = NULL ;
if ( ( jsRC = cmJsonMemberValues ( onp , & errLabelPtr ,
" loc " , kIntTId , & scLocIdx ,
" mod " , kStringTId , & modLabel ,
" var " , kStringTId , & varLabel ,
" type " , kStringTId , & typeLabel ,
NULL ) ) ! = kOkJsRC )
{
if ( errLabelPtr = = NULL )
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Error:%s on record at index %i in file:%s " , errLabelPtr , i , cmStringNullGuard ( fn ) ) ;
else
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Synax error in Modulator record at index %i in file:%s " , i , cmStringNullGuard ( fn ) ) ;
goto errLabel ;
}
// validate the entry type label
if ( ( map = _cmScModTypeLabelToMap ( typeLabel ) ) = = NULL )
{
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Unknown entry type '%s' in Modulator record at index %i in file:%s " , cmStringNullGuard ( typeLabel ) , i , cmStringNullGuard ( fn ) ) ;
goto errLabel ;
}
// get a data pointer to the data node
if ( ( dnp = cmJsonNodeMemberValue ( onp , " data " ) ) = = NULL )
{
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Synax error in Modulator 'data' record at index %i in file:%s " , i , cmStringNullGuard ( fn ) ) ;
goto errLabel ;
}
unsigned modSymId = cmSymTblRegisterSymbol ( stH , modLabel ) ;
unsigned varSymId = cmSymTblRegisterSymbol ( stH , varLabel ) ;
// the data may be an array of doubles ....
if ( cmJsonIsArray ( dnp ) )
{
unsigned an = cmJsonChildCount ( dnp ) ;
double av [ an ] ;
unsigned j ;
// read each element in the data array
for ( j = 0 ; j < an ; + + j )
if ( cmJsonRealValue ( cmJsonArrayElement ( dnp , j ) , av + j ) ! = kOkJsRC )
{
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Error parsing in Modulator 'data' record at index %i value index %i in file:%s " , i , j , cmStringNullGuard ( fn ) ) ;
goto errLabel ;
}
_cmScModulatorInsertEntry ( p , i , scLocIdx , modSymId , varSymId , map - > typeId , av , an ) ;
}
else // ... or a scalar
{
double v ;
if ( cmJsonRealValue ( dnp , & v ) ! = kOkJsRC )
{
rc = cmCtxRtCondition ( & p - > obj , cmInvalidArgRC , " Error paring in Modulator 'data' on record index %i. " , i , cmStringNullGuard ( fn ) ) ;
goto errLabel ;
}
_cmScModulatorInsertEntry ( p , i , scLocIdx , modSymId , varSymId , map - > typeId , & v , 1 ) ;
}
}
errLabel :
// release the JSON tree
if ( cmJsonIsValid ( jsH ) )
cmJsonFinalize ( & jsH ) ;
return rc ;
}
cmRC_t cmScModulatorInit ( cmScModulator * p , cmCtx_t * ctx , cmSymTblH_t stH , double srate , unsigned samplesPerCycle , const cmChar_t * fn , const cmChar_t * modLabel , cmScModCb_t cbFunc , void * cbArg )
{
cmRC_t rc ;
if ( ( rc = cmScModulatorFinal ( p ) ) ! = cmOkRC )
return rc ;
p - > modSymId = cmSymTblRegisterSymbol ( stH , modLabel ) ;
p - > cbFunc = cbFunc ;
p - > cbArg = cbArg ;
p - > samplesPerCycle = samplesPerCycle ;
p - > srate = srate ;
if ( ( rc = _cmScModulatorParse ( p , ctx , stH , fn ) ) ! = cmOkRC )
goto errLabel ;
errLabel :
if ( rc ! = cmOkRC )
cmScModulatorFinal ( p ) ;
else
cmScModulatorReset ( p , 0 ) ;
return rc ;
}
cmRC_t cmScModulatorFinal ( cmScModulator * p )
{
unsigned i ;
// release each value record
cmScModVar_t * vp = p - > vlist ;
while ( vp ! = NULL )
{
cmScModVar_t * np = vp - > vlink ;
cmMemFree ( vp ) ;
vp = np ;
}
// release each entry record
for ( i = 0 ; i < p - > en ; + + i )
cmMemFree ( p - > earray [ i ] . parray ) ;
return cmOkRC ;
}
unsigned cmScModulatorVarCount ( cmScModulator * p )
{
unsigned n = 0 ;
const cmScModVar_t * vp = p - > vlist ;
for ( ; vp ! = NULL ; vp = vp - > vlink )
+ + n ;
return n ;
}
cmScModVar_t * cmScModulatorVar ( cmScModulator * p , unsigned idx )
{
unsigned n = 0 ;
cmScModVar_t * vp = p - > vlist ;
for ( ; vp ! = NULL ; vp = vp - > vlink , + + n )
if ( n = = idx )
return vp ;
assert ( 0 ) ;
return NULL ;
}
cmRC_t cmScModulatorReset ( cmScModulator * p , unsigned scLocIdx )
{
p - > alist = NULL ;
p - > nei = 0 ;
return cmScModulatorExec ( p , scLocIdx ) ;
}
void _cmScModUnlink ( cmScModulator * p , cmScModVar_t * vp , cmScModVar_t * pp )
{
if ( pp = = NULL )
p - > alist = vp - > alink ;
else
pp - > alink = vp - > alink ;
vp - > flags = 0 ;
vp - > alink = NULL ;
vp - > entry = NULL ;
}
// Type specific variable activation
cmRC_t _cmScModActivate ( cmScModulator * p , cmScModEntry_t * ep )
{
cmRC_t rc = cmOkRC ;
cmScModVar_t * vp = ep - > valPtr ;
switch ( ep - > typeId )
{
case kSetModTId :
break ;
case kLineModTId :
vp - > v0 = vp - > value ;
vp - > phase = 0 ;
break ;
case kSetLineModTId :
vp - > value = ep - > parray [ 0 ] ;
vp - > v0 = ep - > parray [ 0 ] ;
vp - > phase = 0 ;
ep - > parray [ 0 ] = ep - > parray [ 1 ] ;
ep - > parray [ 1 ] = ep - > parray [ 2 ] ;
break ;
default :
{ assert ( 0 ) ; }
}
return rc ;
}
// Return true if vp should be deactivated otherwise return false.
bool _cmScModExec ( cmScModulator * p , cmScModVar_t * vp )
{
bool fl = false ;
switch ( vp - > entry - > typeId )
{
case kSetModTId :
p - > cbFunc ( p - > cbArg , vp - > varSymId , vp - > entry - > parray [ 0 ] ) ;
fl = true ;
break ;
case kSetLineModTId :
case kLineModTId :
{
double v1 = vp - > entry - > parray [ 0 ] ;
double v = vp - > value + ( v1 - vp - > v0 ) * ( vp - > phase * p - > samplesPerCycle ) / ( p - > srate * vp - > entry - > parray [ 1 ] ) ;
if ( ( fl = ( vp - > value < = v1 & & v > = v1 ) | | ( vp - > value > = v1 & & v < = v1 ) ) = = true )
v = v1 ;
vp - > phase + = 1 ;
vp - > value = v ;
p - > cbFunc ( p - > cbArg , vp - > varSymId , v ) ;
}
break ;
default :
{ assert ( 0 ) ; }
}
return fl ;
}
cmRC_t cmScModulatorExec ( cmScModulator * p , unsigned scLocIdx )
{
cmRC_t trc ;
cmRC_t rc = cmOkRC ;
// trigger entries that have expired since the last call to this function
for ( ; p - > nei < p - > en & & p - > earray [ p - > nei ] . scLocIdx < = scLocIdx ; + + p - > nei )
{
cmScModEntry_t * ep = p - > earray + p - > nei ;
// if the variable assoc'd with this entry is not on the active list ...
if ( cmIsFlag ( ep - > valPtr - > flags , kActiveModFl ) = = false )
{
// ... then push it onto the front of the active list ...
ep - > valPtr - > flags = kActiveModFl ;
ep - > valPtr - > alink = p - > alist ;
p - > alist = ep - > valPtr ;
}
// do type specific activation
if ( ( trc = _cmScModActivate ( p , ep ) ) ! = cmOkRC )
rc = trc ;
ep - > valPtr - > entry = ep ;
}
cmScModVar_t * pp = NULL ;
cmScModVar_t * vp = p - > alist ;
for ( ; vp ! = NULL ; vp = vp - > alink )
{
if ( _cmScModExec ( p , vp ) )
_cmScModUnlink ( p , vp , pp ) ;
else
pp = vp ;
}
return rc ;
}