From 01bf7de3bd45c989b01eab8b286776960dc4c48f Mon Sep 17 00:00:00 2001 From: Kevin Larke Date: Thu, 24 Mar 2016 12:12:54 -0400 Subject: [PATCH] cmScoreMatchGraphic.h/c, Makefile.am : Initial commit. --- Makefile.am | 7 +- app/cmScoreMatchGraphic.c | 488 ++++++++++++++++++++++++++++++++++++++ app/cmScoreMatchGraphic.h | 32 +++ 3 files changed, 522 insertions(+), 5 deletions(-) create mode 100644 app/cmScoreMatchGraphic.c create mode 100644 app/cmScoreMatchGraphic.h diff --git a/Makefile.am b/Makefile.am index 21eb218..f456779 100644 --- a/Makefile.am +++ b/Makefile.am @@ -73,9 +73,6 @@ cmHDR += src/libcm/cmDList.h src/libcm/cmDListTpl.h cmHDR += src/libcm/cmProcObj.h src/libcm/cmProc.h src/libcm/cmProc2.h src/libcm/cmProc3.h src/libcm/cmProc4.h src/libcm/cmProc5.h src/libcm/cmProcTest.h cmSRC += src/libcm/cmProcObj.c src/libcm/cmProc.c src/libcm/cmProc2.c src/libcm/cmProc3.c src/libcm/cmProc4.c src/libcm/cmProc5.c src/libcm/cmProcTest.c - -cmHDR += src/libcm/app/cmOnset.h src/libcm/app/cmTimeLine.h src/libcm/app/cmScore.h src/libcm/app/cmScoreProc.h - cmHDR += src/libcm/cmErr.h src/libcm/cmCtx.h src/libcm/cmRpt.h src/libcm/cmGlobal.h src/libcm/cmComplexTypes.h src/libcm/cmFloatTypes.h src/libcm/cmPrefix.h cmSRC += src/libcm/cmErr.c src/libcm/cmCtx.c src/libcm/cmRpt.c src/libcm/cmGlobal.c src/libcm/cmComplexTypes.c @@ -147,8 +144,8 @@ cmHDR += src/libcm/cmProcObj.h src/libcm/cmProc.h src/libcm/cmProc2.h src/libcm/ cmSRC += src/libcm/cmProcObj.c src/libcm/cmProc.c src/libcm/cmProc2.c src/libcm/cmProc3.c src/libcm/cmProc4.c src/libcm/cmProc5.c src/libcm/cmProcTest.c -cmHDR += src/libcm/app/cmOnset.h src/libcm/app/cmTimeLine.h src/libcm/app/cmScore.h src/libcm/app/cmScoreProc.h src/libcm/app/cmXScore.h -cmSRC += src/libcm/app/cmOnset.c src/libcm/app/cmTimeLine.c src/libcm/app/cmScore.c src/libcm/app/cmScoreProc.c src/libcm/app/cmXScore.c +cmHDR += src/libcm/app/cmOnset.h src/libcm/app/cmTimeLine.h src/libcm/app/cmScore.h src/libcm/app/cmScoreProc.h src/libcm/app/cmXScore.h src/libcm/app/cmScoreMatchGraphic.h +cmSRC += src/libcm/app/cmOnset.c src/libcm/app/cmTimeLine.c src/libcm/app/cmScore.c src/libcm/app/cmScoreProc.c src/libcm/app/cmXScore.c src/libcm/app/cmScoreMatchGraphic.c cmHDR += src/libcm/app/cmSdb.h src/libcm/app/cmTakeSeqBldr.h src/libcm/app/cmDspPgmJsonToDot.h cmSRC += src/libcm/app/cmSdb.c src/libcm/app/cmTakeSeqBldr.c src/libcm/app/cmDspPgmJsonToDot.c diff --git a/app/cmScoreMatchGraphic.c b/app/cmScoreMatchGraphic.c new file mode 100644 index 0000000..5df4445 --- /dev/null +++ b/app/cmScoreMatchGraphic.c @@ -0,0 +1,488 @@ +#include "cmPrefix.h" +#include "cmGlobal.h" +#include "cmFloatTypes.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmLinkedHeap.h" +#include "cmTime.h" +#include "cmMidi.h" +#include "cmLex.h" +#include "cmCsv.h" +#include "cmSymTbl.h" +#include "cmMidiFile.h" +#include "cmAudioFile.h" +#include "cmTimeLine.h" +#include "cmText.h" +#include "cmFile.h" +#include "cmScore.h" +#include "cmScoreMatchGraphic.h" + +enum +{ + kBarSmgFl = 0x0001, + kNoteSmgFl = 0x0002, + kMidiSmgFl = 0x0004, + kNoMatchSmgFl = 0x0008 +}; + +typedef struct cmSmgBox_str +{ + unsigned flags; + unsigned id; // csvEventId or midiUid + unsigned left; + unsigned top; + unsigned width; + unsigned height; + cmChar_t* text; + struct cmSmgBox_str* link; +} cmSmgBox_t; + +typedef struct cmSmgLine_str +{ + cmSmgBox_t* b0; + cmSmgBox_t* b1; + struct cmSmgLine_str* link; +} cmSmgLine_t; + +typedef struct cmSmgLoc_str +{ + cmSmgBox_t* bV; +} cmSmgLoc_t; + +typedef struct +{ + unsigned type; + unsigned csvEventId; + unsigned locIdx; + cmSmgBox_t* box; +} cmSmgSc_t; + +typedef struct cmSmgMatch_str +{ + cmSmgSc_t* score; + struct cmSmgMatch_str* link; +} cmSmgMatch_t; + +typedef struct +{ + unsigned uid; + unsigned pitch; + unsigned vel; + cmSmgMatch_t* matchV; + cmSmgBox_t* box; +} cmSmgMidi_t; + + +typedef struct +{ + cmErr_t err; + cmSmgSc_t* scV; + unsigned scN; + cmSmgMidi_t* mV; + unsigned mN; + cmSmgLoc_t* locV; + unsigned locN; + cmSmgLine_t* lines; +} cmSmg_t; + +cmSmgH_t cmSmgNullHandle = cmSTATIC_NULL_HANDLE; + +cmSmg_t* _cmSmgHandleToPtr( cmSmgH_t h ) +{ + cmSmg_t* p = (cmSmg_t*)h.h; + assert(p!=NULL); + return p; +} + +cmSmgRC_t _cmSmgFree( cmSmg_t* p ) +{ + unsigned i; + + for(i=0; imN; ++i) + { + cmSmgMatch_t* m0 = p->mV[i].matchV; + cmSmgMatch_t* m1 = NULL; + while(m0!=NULL) + { + m1 = m0->link; + cmMemFree(m0); + m0 = m1; + } + } + + for(i=0; ilocN; ++i) + { + cmSmgBox_t* b0 = p->locV[i].bV; + cmSmgBox_t* b1 = NULL; + while(b0!=NULL) + { + b1 = b0->link; + cmMemFree(b0->text); + cmMemFree(b0); + b0 = b1; + } + } + + cmSmgLine_t* l0 = p->lines; + cmSmgLine_t* l1 = NULL; + while( l0 != NULL ) + { + l1 = l0->link; + cmMemFree(l0); + l0 = l1; + } + + cmMemFree(&p->scV); + cmMemFree(&p->mV); + cmMemFree(&p->locV); + cmMemFree(p); + return kOkSmgRC; +} + +cmSmgBox_t* _cmSmgInsertBox( cmSmg_t* p, unsigned locIdx, unsigned flags, unsigned id, cmChar_t* text ) +{ + assert( locIdx < p->locN ); + + cmSmgBox_t* b = cmMemAllocZ(cmSmgBox_t,1); + b->flags = flags; + b->id = id; + b->text = text; + + if( p->locV[locIdx].bV == NULL ) + p->locV[locIdx].bV = b; + else + { + cmSmgBox_t* b0 = p->locV[locIdx].bV; + while( b0->link!=NULL ) + b0 = b0->link; + + b0->link = b; + } + + return b; +} + +cmSmgRC_t _cmSmgInitFromScore( cmCtx_t* ctx, cmSmg_t* p, const cmChar_t* scoreFn ) +{ + cmSmgRC_t rc = kOkSmgRC; + cmScH_t scH = cmScNullHandle; + unsigned i,j,k; + + if( cmScoreInitialize(ctx,&scH,scoreFn,44100.0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC ) + return cmErrMsg(&p->err,kScoreFailSmgRC,"Score initializatio failed on '%s'.",cmStringNullGuard(scoreFn)); + + p->scN = cmScoreEvtCount(scH); + p->scV = cmMemAllocZ(cmSmgSc_t,p->scN); + + p->locN = cmScoreLocCount(scH); + p->locV = cmMemAllocZ(cmSmgLoc_t,p->locN); + + // for each score location + for(i=0,k=0; ievtCnt; ++j) + { + const cmScoreEvt_t* e = l->evtArray[j]; + + switch( e->type) + { + case kBarEvtScId: + case kNonEvtScId: + { + unsigned flags = e->type==kNonEvtScId ? kNoteSmgFl : kBarSmgFl; + cmChar_t* text = NULL; + + assert( k < p->scN ); + + p->scV[k].type = e->type; + p->scV[k].csvEventId = e->csvEventId; + p->scV[k].locIdx = i; + + if( e->type == kBarEvtScId ) + text = cmTsPrintfP(NULL,"%i",e->barNumb); + else + text = cmMemAllocStr( cmMidiToSciPitch( e->pitch, NULL, 0)); + + p->scV[k].box = _cmSmgInsertBox(p, i, flags, e->csvEventId, text ); + + k += 1; + } + break; + } + } + } + + cmScoreFinalize(&scH); + + return rc; +} + +cmSmgRC_t _cmSmgInitFromMidi( cmCtx_t* ctx, cmSmg_t* p, const cmChar_t* midiFn ) +{ + cmSmgRC_t rc = kOkSmgRC; + cmMidiFileH_t mfH = cmMidiFileNullHandle; + unsigned i,j; + + if( cmMidiFileOpen(ctx, &mfH, midiFn ) != kOkMfRC ) + return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI file open failed on '%s'.",cmStringNullGuard(midiFn)); + + const cmMidiTrackMsg_t** mV = cmMidiFileMsgArray(mfH); + unsigned mN = cmMidiFileMsgCount(mfH); + + p->mV = cmMemAllocZ(cmSmgMidi_t,mN); + p->mN = mN; + + for(i=0,j=0; istatus) && cmMidiIsNoteOn(mV[i]->status) && (mV[i]->u.chMsgPtr->d1>0) ) + { + p->mV[j].uid = mV[i]->uid; + p->mV[j].pitch = mV[i]->u.chMsgPtr->d0; + p->mV[j].vel = mV[i]->u.chMsgPtr->d1; + } + + cmMidiFileClose(&mfH); + + return rc; +} + +cmSmgRC_t cmScoreMatchGraphicAlloc( cmCtx_t* ctx, cmSmgH_t* hp, const cmChar_t* scoreFn, const cmChar_t* midiFn ) +{ + cmSmgRC_t rc; + if((rc = cmScoreMatchGraphicFree(hp)) != kOkSmgRC ) + return rc; + + cmSmg_t* p = cmMemAllocZ(cmSmg_t,1); + cmErrSetup(&p->err,&ctx->rpt,"ScoreMatchGraphic"); + + if((rc = _cmSmgInitFromScore(ctx,p,scoreFn)) != kOkSmgRC ) + goto errLabel; + + if((rc = _cmSmgInitFromMidi(ctx,p,midiFn)) != kOkSmgRC ) + goto errLabel; + + hp->h = p; + + errLabel: + if( rc != kOkSmgRC ) + _cmSmgFree(p); + + return rc; +} + +cmSmgRC_t cmScoreMatchGraphicFree( cmSmgH_t* hp ) +{ + cmSmgRC_t rc = kOkSmgRC; + + if(hp==NULL || cmScoreMatchGraphicIsValid(*hp)==false) + return kOkSmgRC; + + cmSmg_t* p = _cmSmgHandleToPtr(*hp); + + if((rc = _cmSmgFree(p)) != kOkSmgRC ) + return rc; + + hp->h = NULL; + + return rc; +} + +bool cmScoreMatchGraphic( cmSmgH_t h ) +{ return h.h != NULL; } + +cmSmgRC_t cmScoreMatchGraphicInsertMidi( cmSmgH_t h, unsigned midiUid, unsigned midiPitch, unsigned midiVel, unsigned csvScoreEventId ) +{ + cmSmg_t* p = _cmSmgHandleToPtr(h); + unsigned i,j; + + // for each MIDI event + for(i=0; imN; ++i) + if( p->mV[i].uid == midiUid ) + { + // for each score record + for(j=0; jscN; ++j) + if( p->scV[j].csvEventId == csvScoreEventId ) + { + cmSmgMatch_t* m = cmMemAllocZ(cmSmgMatch_t,1); + + m->score = p->scV + j; + + if( p->mV[i].matchV == NULL ) + p->mV[i].matchV = m; + else + { + cmSmgMatch_t* m0 = p->mV[i].matchV; + while( m0->link != NULL ) + m0 = m0->link; + + m0->link = m; + } + } + + return cmErrMsg(&p->err,kScoreFailSmgRC,"The score csv event id %i not found,",csvScoreEventId); + } + + return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI uid %i not found.",midiUid); +} + +// Create a box for each MIDI event and a line for each +// match beyond the first. +void _cmSmgResolveMidi( cmSmg_t* p ) +{ + unsigned prevLocIdx = 0; + unsigned i; + + // for each midi record + for(i=0; imN; ++i) + { + const cmSmgMatch_t* m = p->mV[i].matchV; + + // get the score location for this midi event + unsigned locIdx = m==NULL ? prevLocIdx : m->score->locIdx; + + unsigned flags = kMidiSmgFl | (m==NULL ? kNoMatchSmgFl : 0); + + // set the text label for this event + cmChar_t* text = cmMemAllocStr( cmMidiToSciPitch( p->mV[i].pitch, NULL, 0)); + + // insert a box to represent this midi event + cmSmgBox_t* box = _cmSmgInsertBox( p, locIdx, flags, p->mV[i].uid, text ); + + prevLocIdx = locIdx; + + // if this midi event matched to multiple score positions + if( m != NULL && m->link != NULL ) + { + // insert a line for each match after the first + m = m->link; + for(; m!=NULL; m=m->link ) + { + cmSmgLine_t* l = cmMemAllocZ(cmSmgLine_t,1); + l->b0 = box; + l->b1 = m->score->box; + + l->link = p->lines; + p->lines = l; + } + } + } +} + +void _cmSmgLayout( cmSmg_t* p ) +{ + unsigned i; + unsigned bordX = 5; + unsigned bordY = 5; + unsigned boxH = 30; + unsigned boxW = 30; + unsigned top = boxH + bordY; + unsigned left = bordX; + + for(i=0; ilocN; ++i) + { + cmSmgLoc_t* l = p->locV + i; + cmSmgBox_t* b = l->bV; + + for(; b!=NULL; b=b->link) + { + if( cmIsFlag(b->flags,kBarSmgFl) ) + b->top = bordY; + else + { + b->top = top; + top += boxH + bordY; + } + + b->left = left; + b->width = boxW; + b->height = boxH; + } + + left += boxW + bordX; + } +} + +void _cmSmgSvgSize( cmSmg_t* p, unsigned* widthRef, unsigned* heightRef ) +{ + unsigned i; + unsigned maxWidth = 0; + unsigned maxHeight = 0; + + for(i=0; ilocN; ++i) + { + cmSmgBox_t* b = p->locV[i].bV; + while( b != NULL ) + { + if( b->left + b->width > maxWidth ) + maxWidth = b->left + b->width; + + if( b->top + b->height > maxHeight ) + maxHeight = b->top + b->height; + } + } + + *widthRef = maxWidth; + *heightRef = maxHeight; +} + +cmSmgRC_t cmScoreMatchGraphicWrite( cmSmgH_t h, const cmChar_t* fn ) +{ + cmSmg_t* p = _cmSmgHandleToPtr(h); + cmFileH_t fH = cmFileNullHandle; + unsigned svgHeight = 0; + unsigned svgWidth = 0; + unsigned i; + + if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC ) + return cmErrMsg(&p->err,kFileFailScRC,"Graphic file create failed for '%s'.",cmStringNullGuard(fn)); + + _cmSmgSvgSize(p,&svgWidth,&svgHeight); + + cmFilePrintf(fH,"\n\n\n\n",svgWidth,svgHeight); + + for(i=0; ilocN; ++i) + { + cmSmgBox_t* b = p->locV[i].bV; + while( b != NULL ) + { + const cmChar_t* classStr = "score"; + + if( cmIsFlag(b->flags,kMidiSmgFl) ) + classStr = "midi"; + + if( cmIsFlag(b->flags,kNoMatchSmgFl) ) + if( cmIsFlag(b->flags,kMidiSmgFl) ) + classStr = "midi_miss"; + + if( cmIsFlag(b->flags,kNoMatchSmgFl) ) + if( cmIsFlag(b->flags,kNoteSmgFl) ) + classStr = "score_miss"; + + if( cmIsFlag(b->flags,kBarSmgFl) ) + classStr = "bar"; + + if( cmFilePrintf(fH,"\n",b->left,b->top,b->width,b->height,classStr) != kOkFileRC ) + return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file output."); + + if( b->text != NULL ) + { + unsigned tx = b->left + b->width/2; + unsigned ty = b->top + 20; //g->height/2; + + if( cmFilePrintf(fH,"%s\n",tx,ty,b->text) != kOkFileRC ) + return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file output."); + } + + } + } + + cmFilePrint(fH,"\n\n\n"); + + cmFileClose(&fH); + return kOkSmgRC; +} diff --git a/app/cmScoreMatchGraphic.h b/app/cmScoreMatchGraphic.h new file mode 100644 index 0000000..4a8b317 --- /dev/null +++ b/app/cmScoreMatchGraphic.h @@ -0,0 +1,32 @@ +#ifndef cmScoreMatchGraphic_h +#define cmScoreMatchGraphic_h + +#ifdef __cplusplus +extern "C" { +#endif + + enum + { + kOkSmgRC = cmOkRC, + kFileSmgRC, + kScoreFailSmgRC, + kMidiFileFailSmgRC + }; + + typedef cmRC_t cmSmgRC_t; + typedef cmHandle_t cmSmgH_t; + + extern cmSmgH_t cmSmgNullHandle; + + cmSmgRC_t cmScoreMatchGraphicAlloc( cmCtx_t* ctx, cmSmgH_t* hp, const cmChar_t* scoreFn, const cmChar_t* midiFn ); + cmSmgRC_t cmScoreMatchGraphicFree( cmSmgH_t* hp ); + bool cmScoreMatchGraphicIsValid( cmSmgH_t h ); + cmSmgRC_t cmScoreMatchGraphicInsertMidi( cmSmgH_t h, unsigned midiUid, unsigned midiPitch, unsigned midiVel, unsigned csvScoreEventId ); + cmSmgRC_t cmScoreMatchGraphicWrite( cmSmgH_t h, const cmChar_t* fn ); + + +#ifdef __cplusplus +} +#endif + +#endif