175 lines
5.2 KiB
C++
175 lines
5.2 KiB
C++
//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
|
|
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
|
|
#include "cwCommon.h"
|
|
#include "cwLog.h"
|
|
#include "cwCommonImpl.h"
|
|
#include "cwTest.h"
|
|
#include "cwMem.h"
|
|
#include "cwFile.h"
|
|
#include "cwObject.h"
|
|
#include "cwMidi.h"
|
|
|
|
#include "cwDynRefTbl.h"
|
|
#include "cwScoreParse.h"
|
|
#include "cwSfScore.h"
|
|
#include "cwSfMatch.h"
|
|
#include "cwSfTrack.h"
|
|
#include "cwSfAnalysis.h"
|
|
|
|
namespace cw
|
|
{
|
|
namespace sf_analysis
|
|
{
|
|
enum { kSciPitchCharCnt = 7 };
|
|
|
|
typedef struct anl_evt_str
|
|
{
|
|
unsigned matchCnt; // should be equal to 1, 0 if missed, > 1 if matched multiple time
|
|
unsigned timeDirErrCnt; // should be 0, incremented every time this event was matched going backwards in time
|
|
unsigned spuriousCnt; // should be 0, incremented for every nearby spurious note
|
|
|
|
unsigned oLocId; // score event location id
|
|
unsigned barNumb; // bar number of this event
|
|
uint8_t pitch; // pitch of this event
|
|
const char* sect_label; //
|
|
char sciPitch[ kSciPitchCharCnt ]; //
|
|
} anl_event_t;
|
|
|
|
unsigned _locate_anl_evt( unsigned oLocId, uint8_t pitch, const anl_event_t* anlEvtA, unsigned anlEvtN, unsigned anlEvtIdx )
|
|
{
|
|
for(unsigned i=0; i<anlEvtN; ++i)
|
|
if( anlEvtA[i].oLocId == oLocId && anlEvtA[i].pitch == pitch )
|
|
return i;
|
|
|
|
return kInvalidIdx;
|
|
}
|
|
|
|
rc_t _write_as_csv( const anl_event_t* anlEvtA, unsigned anlEvtCnt, const char* fname )
|
|
{
|
|
file::handle_t fH;
|
|
rc_t rc = kOkRC;
|
|
|
|
if((rc = open(fH,fname,file::kWriteFl)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"File create failed on '%s'.",cwStringNullGuard(fname));
|
|
goto errLabel;
|
|
}
|
|
|
|
if((rc = printf(fH,"section,bar,oLocId,sciPitch,matchN,timeDirErrN,spuriousN\n")) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"File write failed on title line.");
|
|
goto errLabel;
|
|
}
|
|
|
|
for(const anl_event_t* e=anlEvtA; e<anlEvtA+anlEvtCnt; ++e)
|
|
{
|
|
if((rc = printf(fH,"%s,%i,%i,%s,%i,%i,%i\n",e->sect_label,e->barNumb,e->oLocId,e->sciPitch,e->matchCnt,e->timeDirErrCnt,e->spuriousCnt)) != kOkRC )
|
|
{
|
|
rc = cwLogError(rc,"File write failed on line %i.",(e-anlEvtA)+1);
|
|
goto errLabel;
|
|
|
|
}
|
|
}
|
|
errLabel:
|
|
close(fH);
|
|
|
|
if( rc != kOkRC )
|
|
rc = cwLogError(rc,"The score follow analysis CSV report file creation failed on '%s'.",cwStringNullGuard(fname));
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
cw::rc_t cw::sf_analysis::gen_analysis( sfscore::handle_t scH,
|
|
const sftrack::result_t* resultA,
|
|
unsigned resultN,
|
|
unsigned begLoc,
|
|
unsigned endLoc,
|
|
const char* fname )
|
|
{
|
|
rc_t rc = kOkRC;
|
|
unsigned anlEvtAllocN = event_count(scH);
|
|
anl_event_t* anlEvtA = mem::allocZ<anl_event_t>(anlEvtAllocN);
|
|
unsigned anlEvtCnt = 0;
|
|
unsigned anlEvtIdx = 0;
|
|
const char* sect_label0 = "<unknown>";
|
|
|
|
// fill anlEvtA[] with the score events
|
|
for(unsigned i=0; i<anlEvtAllocN; ++i)
|
|
{
|
|
const sfscore::event_t* scEvt = event(scH,i);
|
|
if( begLoc <= scEvt->oLocId && scEvt->oLocId <= endLoc )
|
|
{
|
|
anl_event_t* anlEvt = anlEvtA + anlEvtCnt++;
|
|
|
|
const sfscore::section_t* sect;
|
|
const char* sect_label = nullptr;
|
|
if((sect = event_index_to_section( scH, i )) == nullptr )
|
|
sect_label = sect_label0;
|
|
else
|
|
sect_label = sect->label;
|
|
|
|
anlEvt->oLocId = scEvt->oLocId;
|
|
anlEvt->pitch = scEvt->pitch;
|
|
anlEvt->barNumb = scEvt->barNumb;
|
|
anlEvt->sect_label = sect_label;
|
|
strncpy(anlEvt->sciPitch,scEvt->sciPitch,kSciPitchCharCnt-1);
|
|
|
|
sect_label0 = sect_label;
|
|
}
|
|
}
|
|
|
|
if( anlEvtCnt == 0)
|
|
{
|
|
rc = cwLogError(kInvalidStateRC,"The score appears to be empty.");
|
|
goto errLabel;
|
|
}
|
|
|
|
for(unsigned i=0; i<resultN; ++i)
|
|
{
|
|
const sftrack::result_t* r = resultA + i;
|
|
unsigned aei;
|
|
|
|
// if this is a spurious event
|
|
if( r->oLocId == kInvalidIdx )
|
|
{
|
|
// ignore spurious events until at least one matched event is found
|
|
if( anlEvtIdx > 0 )
|
|
anlEvtA[ anlEvtIdx ].spuriousCnt += 1;
|
|
}
|
|
else
|
|
{
|
|
// if this event is in range
|
|
if( begLoc <= r->oLocId && r->oLocId <= endLoc )
|
|
{
|
|
// locate the associated score event
|
|
if((aei = _locate_anl_evt( r->oLocId, r->pitch, anlEvtA, anlEvtCnt, anlEvtIdx )) == kInvalidIdx )
|
|
{
|
|
rc = cwLogError(kInvalidStateRC,"The score event associated with a matched performance could not be found for event:%i loc:%i.",i,r->oLocId);
|
|
goto errLabel;
|
|
}
|
|
|
|
anlEvtA[ aei ].matchCnt += 1;
|
|
|
|
anlEvtA[ aei ].timeDirErrCnt += aei < anlEvtIdx;
|
|
|
|
anlEvtIdx = aei;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rc = _write_as_csv( anlEvtA, anlEvtCnt, fname );
|
|
|
|
|
|
errLabel:
|
|
if( rc != kOkRC )
|
|
rc = cwLogError(rc,"Score follow analysis failed.");
|
|
return rc;
|
|
}
|