123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808 |
- #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
- {
- kLocSmgFl = 0x0001,
- kBarSmgFl = 0x0002, // score bar
- kNoteSmgFl = 0x0004, // score note
- kPedalSmgFl = 0x0008, // sore damper|sot pedal
- kSostSmgFl = 0x0010, // score sost pedal
- kMidiSmgFl = 0x0020, // midi msg
- kNoMatchSmgFl = 0x0040, // midi or score events that were not matched
- kPedalDnSmgFl = 0x0080 // score pedal is down
- };
-
- // Graphic box representing a score label or MIDI event
- typedef struct cmSmgBox_str
- {
- unsigned flags;
- unsigned id; // csvEventId or midiUid
- unsigned left;
- unsigned top;
- unsigned width;
- unsigned height;
- cmChar_t* text0;
- cmChar_t* text1;
- struct cmSmgBox_str* link;
- } cmSmgBox_t;
-
- // Graphic line linking secondary MIDI matches to score events
- typedef struct cmSmgLine_str
- {
- cmSmgBox_t* b0;
- cmSmgBox_t* b1;
- struct cmSmgLine_str* link;
- } cmSmgLine_t;
-
- // Score Location
- typedef struct cmSmgLoc_str
- {
- cmSmgBox_t* bV; // List of graphic boxes assigned to this score location
- } cmSmgLoc_t;
-
- // Score label
- typedef struct
- {
- unsigned type; // kBarEvtScId | kNonEvtScId | kPedalEvtScId
- unsigned barNumb;
- unsigned csvEventId;
- unsigned locIdx;
- cmSmgBox_t* box;
- } cmSmgSc_t;
-
- // Link a MIDI event to it's matched score label.
- typedef struct cmSmgMatch_str
- {
- cmSmgSc_t* score;
- struct cmSmgMatch_str* link;
- } cmSmgMatch_t;
-
- // MIDI file event
- typedef struct
- {
- double secs;
- unsigned uid;
- unsigned pitch;
- unsigned vel;
- cmSmgMatch_t* matchV; // list of matches to score events
- cmSmgBox_t* box;
- } cmSmgMidi_t;
-
-
- typedef struct
- {
- cmErr_t err;
-
- cmChar_t* scFn;
- cmSmgSc_t* scV; // scV[scN] score bars,notes, pedals
- unsigned scN;
-
- cmSmgLoc_t* locV; // locV[locN] score locations (from the score file)
- unsigned locN;
-
- cmSmgLine_t* lines; // Graphic lines used to indicate that a midi event matches to multiple score notes.
- // (Each match after the first gets a line from the box representing the midi event
- // to the matching score event)
-
- cmChar_t* mfFn; // MIDI file name
- cmSmgMidi_t* mV; // mV[mN] midi note-on events
- unsigned mN;
- double mfDurSecs; // midi file duration in seconds
-
- unsigned boxW; // graphic box width and height
- unsigned boxH;
- } 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; i<p->mN; ++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; i<p->locN; ++i)
- {
- cmSmgBox_t* b0 = p->locV[i].bV;
- cmSmgBox_t* b1 = NULL;
- while(b0!=NULL)
- {
- b1 = b0->link;
- cmMemFree(b0->text0);
- cmMemFree(b0->text1);
- 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->scFn);
- cmMemFree(p->mfFn);
- 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* text0, cmChar_t* text1 )
- {
- assert( locIdx < p->locN );
-
- cmSmgBox_t* b = cmMemAllocZ(cmSmgBox_t,1);
- b->flags = flags;
- b->id = id;
- b->text0 = text0;
- b->text1 = text1;
-
- 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->scFn = cmMemAllocStr(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; i<cmScoreLocCount(scH); ++i)
- {
- cmScoreLoc_t* l = cmScoreLoc(scH,i);
-
- // insert the location label box
- _cmSmgInsertBox(p, i, kLocSmgFl, cmInvalidId, cmTsPrintfP(NULL,"%i",i), NULL );
-
- // for each event in location i
- for(j=0; j<l->evtCnt; ++j)
- {
- const cmScoreEvt_t* e = l->evtArray[j];
- unsigned flags = kNoMatchSmgFl;
- cmChar_t* text = NULL;
-
- switch( e->type)
- {
- case kNonEvtScId:
- flags |= kNoteSmgFl;
- text = cmMemAllocStr( cmMidiToSciPitch( e->pitch, NULL, 0));
- break;
-
- case kBarEvtScId:
- flags |= kBarSmgFl;
- text = cmTsPrintfP(NULL,"%i",e->barNumb);
- break;
-
- case kPedalEvtScId:
- flags |= kPedalSmgFl;
-
- text = cmTsPrintfP(NULL,"%s", cmIsFlag(e->flags,kPedalDnScFl)?"v":"^");
-
- if( e->pitch == kSostenutoCtlMdId )
- flags |= kSostSmgFl;
-
- flags |= cmIsFlag(e->flags,kPedalDnScFl) ? kPedalDnSmgFl : 0;
-
- break;
- }
-
- // if e is a score event of interest then store a reference to it
- if( flags != kNoMatchSmgFl )
- {
- assert( k < p->scN );
-
- p->scV[k].type = e->type;
- p->scV[k].csvEventId = e->csvEventId;
- p->scV[k].locIdx = i;
- p->scV[k].barNumb = e->barNumb;
-
- p->scV[k].box = _cmSmgInsertBox(p, i, flags, e->csvEventId, text, NULL );
-
- k += 1;
- }
- }
- }
-
- p->scN = k;
-
- 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;
- p->mfDurSecs = cmMidiFileDurSecs(mfH);
- p->mfFn = cmMemAllocStr(midiFn);
-
- for(i=0,j=0; i<mN; ++i)
- if( (mV[i]!=NULL) && cmMidiIsChStatus(mV[i]->status) && cmMidiIsNoteOn(mV[i]->status) && (mV[i]->u.chMsgPtr->d1>0) )
- {
- assert(j < mN);
- p->mV[j].secs = mV[i]->amicro / 1000000.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;
-
- ++j;
- }
-
- p->mN = j;
-
- 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;
-
- p->boxW = 30;
- p->boxH = 50;
- hp->h = p;
-
- errLabel:
- if( rc != kOkSmgRC )
- _cmSmgFree(p);
-
- return rc;
- }
-
- bool cmScoreMatchGraphicIsValid( cmSmgH_t h )
- { return h.h != NULL; }
-
- 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;
-
- // if this midi event did not match any score records
- if( csvScoreEventId == cmInvalidId )
- return kOkSmgRC;
-
- assert(midiUid != cmInvalidId );
- assert(midiPitch<128 && midiVel<128);
-
- // find the midi file record which matches the event
- for(i=0; i<p->mN; ++i)
- if( p->mV[i].uid == midiUid )
- {
- // find the score record which matches the score event id
- for(j=0; j<p->scN; ++j)
- if( p->scV[j].csvEventId == csvScoreEventId )
- {
- // create a match record
- cmSmgMatch_t* m = cmMemAllocZ(cmSmgMatch_t,1);
-
- m->score = p->scV + j;
-
- // mark the box associated with this score record as 'matched' by clearing the kNoMatchSmgFl
- p->scV[j].box->flags = cmClrFlag(p->scV[j].box->flags,kNoMatchSmgFl);
-
- // insert the match record in the midi files match list
- 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 kOkSmgRC;
- }
-
- 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; i<p->mN; ++i)
- {
- // get the first match record for this MIDI event
- 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, cmTsPrintfP(NULL,"%i",p->mV[i].uid) );
-
- 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 top = p->boxH + 2*bordY;
- unsigned left = bordX;
-
- for(i=0; i<p->locN; ++i)
- {
- cmSmgLoc_t* l = p->locV + i;
- cmSmgBox_t* b = l->bV;
-
- // for each box attached to this location
- for(; b!=NULL; b=b->link)
- {
- // bar boxes are always drawn at the top of the column
- if( cmIsFlag(b->flags,kBarSmgFl) )
- b->top = bordY;
- else
- {
- b->top = top;
- top += p->boxH + bordY;
- }
-
- b->left = left;
- b->width = p->boxW;
- b->height = p->boxH;
- }
-
- left += p->boxW + bordX;
- top = p->boxH + 2*bordY;
- }
- }
-
- void _cmSmgSvgSize( cmSmg_t* p, unsigned* widthRef, unsigned* heightRef )
- {
- unsigned i;
- unsigned maxWidth = 0;
- unsigned maxHeight = 0;
-
- for(i=0; i<p->locN; ++i)
- {
- cmSmgBox_t* b = p->locV[i].bV;
- for(; b != NULL; b=b->link )
- {
- 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;
-
-
- // BUG BUG BUG : this can only be called once
- // create a box for each midi event
- _cmSmgResolveMidi( p );
-
- // layout the boxes
- _cmSmgLayout( p );
-
-
- 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);
-
- svgHeight += 10; // add a right and lower border
- svgWidth += 10;
-
- cmFilePrintf(fH,"<!DOCTYPE html>\n<html>\n<head><link rel=\"stylesheet\" type=\"text/css\" href=\"score0.css\"></head><body>\n<svg width=\"%i\" height=\"%i\">\n",svgWidth,svgHeight);
-
- for(i=0; i<p->locN; ++i)
- {
- cmSmgBox_t* b = p->locV[i].bV;
- for(; b != NULL; b=b->link )
- {
- const cmChar_t* classStr = "score";
-
- if( cmIsFlag(b->flags,kLocSmgFl) )
- classStr = "loc";
-
- 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( cmIsFlag(b->flags,kPedalSmgFl) )
- {
- if( cmIsFlag(b->flags,kSostSmgFl) )
- classStr = "sost";
- else
- classStr = "damper";
- }
-
- if( cmFilePrintf(fH,"<rect x=\"%i\" y=\"%i\" width=\"%i\" height=\"%i\" class=\"%s\"/>\n",b->left,b->top,b->width,b->height,classStr) != kOkFileRC )
- return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file rect output.");
-
- if( b->text0 != NULL )
- {
- unsigned tx = b->left + b->width/2;
- unsigned ty = b->top + 20;
-
- if( cmFilePrintf(fH,"<text x=\"%i\" y=\"%i\" text-anchor=\"middle\" class=\"stext\">%s</text>\n",tx,ty,b->text0) != kOkFileRC )
- return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file text output.");
- }
-
- if( b->text1 != NULL )
- {
- unsigned tx = b->left + b->width/2;
- unsigned ty = b->top + 20 + 20;
-
- if( cmFilePrintf(fH,"<text x=\"%i\" y=\"%i\" text-anchor=\"middle\" class=\"stext\">%s</text>\n",tx,ty,b->text1) != kOkFileRC )
- return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file text output.");
- }
-
- }
- }
-
- cmSmgLine_t* l = p->lines;
- for(; l!=NULL; l=l->link)
- {
- unsigned x0 = l->b0->left + l->b0->width/2;
- unsigned y0 = l->b0->top + l->b0->height/2;
- unsigned x1 = l->b1->left + l->b1->width/2;
- unsigned y1 = l->b1->top + l->b1->height/2;
-
- if( cmFilePrintf(fH,"<line x1=\"%i\" y1=\"%i\" x2=\"%i\" y2=\"%i\" class=\"sline\"/>\n",x0,y0,x1,y1) != kOkFileRC )
- return cmErrMsg(&p->err,kFileFailScRC,"File write failed on graphic file line output.");
- }
-
- cmFilePrint(fH,"</svg>\n</body>\n</html>\n");
-
- cmFileClose(&fH);
- return kOkSmgRC;
- }
-
-
- cmSmgRC_t cmScoreMatchGraphicGenTimeLineBars( cmSmgH_t h, const cmChar_t* fn, unsigned srate )
- {
- cmSmgRC_t rc = kOkSmgRC;
- cmFileH_t f = cmFileNullHandle;
- cmSmg_t* p = _cmSmgHandleToPtr(h);
- unsigned i = 0;
-
- if( cmFileOpen(&f,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
- return cmErrMsg(&p->err,kFileSmgRC,"The time-line bar file '%s' could not be created.",cmStringNullGuard(fn));
-
- // for each MIDI event
- for(i=0; i<p->mN; ++i)
- {
- // if this MIDI event matched a score event
- if( p->mV[i].matchV == NULL || p->mV[i].matchV->score == NULL )
- continue;
-
- // backup the score position by one location - because we are looking for bar events
- // which are just prior to note events - because for our purposes they will have the same onset.
- cmSmgSc_t* s = p->mV[i].matchV->score - 1;
- for(; s >= p->scV; s-- )
- {
- // if this is a bar event just preceding the matched MIDI event - then the bar happens at the same time as the note event
- if( s->type == kBarEvtScId )
- break;
-
- // if this is a note event - then there is no preceding bar event
- if( s->type == kNonEvtScId )
- break;
- }
-
- // if a bar was found
- if(s >= p->scV && s->type == kBarEvtScId)
- {
- unsigned bar = s->barNumb;
- unsigned offset = p->mV[i].secs * srate;
- unsigned smpCnt = p->mfDurSecs * srate - offset;
- cmFilePrintf(f,"{ label: \"%i\" type: \"mk\" ref: \"mf-0\" offset: %8i smpCnt:%8i trackId: 0 textStr: \"Bar %3i\" bar: %3i sec:\"%3i\" }\n",bar,offset,smpCnt,bar,bar,bar);
- }
- }
- cmFileClose(&f);
- return rc;
-
- }
-
- // Find the first MIDI event that matches this score event
- const cmSmgMidi_t* _cmScoreMatchGraphicScoreToMatchedMidiEvent( cmSmg_t* p, const cmSmgSc_t* sc )
- {
- unsigned i;
- for(i=0; i<p->mN; ++i)
- {
- const cmSmgMatch_t* m = p->mV[i].matchV;
-
- for(; m != NULL; m=m->link )
- if( sc->csvEventId == m->score->csvEventId )
- return p->mV + i;
-
- }
-
- return NULL;
- }
-
- cmSmgRC_t _cmScoreMatchGraphicInsertMidiMsg( cmSmg_t* p, cmMidiFileH_t mfH, bool pedalDnFl, const cmSmgSc_t* s )
- {
- const cmSmgMidi_t* m;
-
- // locate the MIDI event associated with the reference event
- if((m =_cmScoreMatchGraphicScoreToMatchedMidiEvent( p, s )) == NULL )
- return cmErrWarnMsg(&p->err,kMatchFailSmgRC,"A sostenuto pedal msg could not be aligned to a note event.");
-
-
- int dtick_offset = pedalDnFl ? 1 : -1;
- cmMidiByte_t midi_vel = pedalDnFl ? 64 : 0;
- cmMidiByte_t midi_ch = 0;
-
- //printf("pedal:%s\n",pedalDnFl?"down":"up");
-
- // insert a pedal msg relative to the reference event
- if( cmMidiFileInsertMsg(mfH, m->uid, dtick_offset, midi_ch, kCtlMdId, kSostenutoCtlMdId, midi_vel ) != kOkMfRC )
- return cmErrWarnMsg(&p->err,kMidiFileFailSmgRC,"MIDI msg insert failed.");
-
- return kOkSmgRC;
-
- }
-
- cmSmgRC_t _cmScoreMatchGraphicUpdateSostenuto( cmSmg_t* p, cmMidiFileH_t mfH )
- {
- cmSmgRC_t rc = kOkSmgRC;
- unsigned i, j = cmInvalidIdx;
- bool pedalUpFl = false;
-
- // for each score event
- for(i=0; i<p->scN; ++i)
- {
-
- switch( p->scV[i].type )
- {
- case kNonEvtScId:
- {
- // if the pedalUpFl is set then insert a sost-pedal-up msg before this note.
- if( pedalUpFl )
- {
- _cmScoreMatchGraphicInsertMidiMsg(p, mfH, false, p->scV + i );
- pedalUpFl = false;
- }
-
- j = i; // store the index of this note event (it may be needed if the next event is a sost. pedal evt.)
- }
- break;
-
- case kPedalEvtScId:
- // if this is a sost pedal event
- if( cmIsFlag(p->scV[i].box->flags,kSostSmgFl) )
- {
- if( cmIsFlag(p->scV[i].box->flags,kPedalDnSmgFl) )
- {
- assert( j != cmInvalidIdx );
- _cmScoreMatchGraphicInsertMidiMsg(p, mfH, true, p->scV + j );
- }
- else
- {
- pedalUpFl = true; // insert a pedal up message before the next note-on
- }
- }
-
- default:
- break;
- }
- }
- return rc;
- }
-
- cmSmgRC_t cmScoreMatchGraphicUpdateMidiFromScore( cmCtx_t* ctx, cmSmgH_t h, const cmChar_t* newMidiFn )
- {
- cmSmgRC_t rc = kOkSmgRC;
- cmSmg_t* p = _cmSmgHandleToPtr(h);
- unsigned i = 0;
- cmMidiFileH_t mfH = cmMidiFileNullHandle;
- cmScH_t scH = cmScNullHandle;
-
- // open the MIDI file
- if( cmMidiFileOpen(ctx, &mfH, p->mfFn ) != kOkMfRC )
- return cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI file open failed on '%s'.",cmStringNullGuard(p->mfFn));
-
- // instantiate the score from the score CSV file
- if( cmScoreInitialize(ctx,&scH,p->scFn,44100.0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
- {
- rc = cmErrMsg(&p->err,kScoreFailSmgRC,"Score initializatio failed on '%s'.",cmStringNullGuard(p->scFn));
- goto errLabel;
- }
-
- // for each MIDI note-on event
- for(i=0; i<p->mN; ++i)
- {
- cmSmgMidi_t* mr = p->mV + i;
-
- // only update midi events which were matched exactly once
- if( mr->matchV==NULL || mr->matchV->link!=NULL )
- continue;
-
- // locate the matched score event
- const cmScoreEvt_t* s= cmScoreIdToEvt( scH, mr->matchV->score->csvEventId );
- assert( s!=NULL );
-
- // assign the score velocity to the MIDI note
- if(cmMidiFileSetVelocity( mfH, mr->uid, s->vel ) != kOkMfRC )
- {
- rc = cmErrMsg(&p->err,kOpFailSmgRC,"Set velocify operation failed.");
- goto errLabel;
- }
-
- }
-
- // update the sostenuto pedal msg's in the MIDI file.
- _cmScoreMatchGraphicUpdateSostenuto(p, mfH );
-
- // write the updated MIDI file
- if( cmMidiFileWrite( mfH, newMidiFn ) != kOkMfRC )
- {
- rc = cmErrMsg(&p->err,kMidiFileFailSmgRC,"MIDI file write failed on '%s'.",cmStringNullGuard(newMidiFn));
- goto errLabel;
- }
-
-
- errLabel:
- cmMidiFileClose(&mfH);
- cmScoreFinalize(&scH);
-
- return rc;
-
- }
|