From fdfe4d404a5954e75b8e8dcd78c0da481bafdf4e Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 17 Aug 2016 18:58:00 -0400 Subject: [PATCH 01/31] cmScore.c : Added _cmScFreeSectList() to fix memory on free when CSV parse fails. --- app/cmScore.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/app/cmScore.c b/app/cmScore.c index a88f032..b51cd91 100644 --- a/app/cmScore.c +++ b/app/cmScore.c @@ -361,6 +361,22 @@ void _cmScFreeMarkList( cmScMark_t* markList ) } } +void _cmScFreeSectList( cmSc_t* p ) +{ + + // release the section linked list + cmScSect_t* sp = p->sectList; + cmScSect_t* np = NULL; + while(sp!=NULL) + { + np = sp->link; + cmMemFree(sp); + sp = np; + } + + p->sectList = NULL; +} + void _cmScFreeSetList( cmScSet_t* setList ) { cmScSet_t* tp = setList; @@ -412,6 +428,8 @@ cmScRC_t _cmScFinalize( cmSc_t* p ) cmMemFree(p->sets); } + _cmScFreeSectList( p ); + _cmScFreeSetList(p->setList); _cmScFreeMarkList(p->markList); @@ -1119,9 +1137,10 @@ cmScRC_t _cmScProcSets( cmSc_t* p ) -cmScRC_t _cmScProcSections( cmSc_t* p, cmScSect_t* sectList ) +cmScRC_t _cmScProcSections( cmSc_t* p ) { cmScRC_t rc = kOkScRC; + cmScSect_t* sectList = p->sectList; unsigned i; // count the sections @@ -1166,16 +1185,9 @@ cmScRC_t _cmScProcSections( cmSc_t* p, cmScSect_t* sectList ) } } - // release the section linked list - sp = sectList; - cmScSect_t* np = NULL; - while(sp!=NULL) - { - np = sp->link; - cmMemFree(sp); - sp = np; - } + _cmScFreeSectList(p); + //_cmScPrintSets("Sets",p->setList ); _cmScProcSets(p); @@ -1587,7 +1599,7 @@ cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn, doubl if((rc = _cmScInitLocArray(p)) != kOkScRC ) goto errLabel; - if((rc = _cmScProcSections(p,p->sectList)) != kOkScRC ) + if((rc = _cmScProcSections(p)) != kOkScRC ) goto errLabel; if((rc = _cmScProcMarkers(p)) != kOkScRC ) From be6dc19ade7ca28652b91aadab8e5fbb4f417e62 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 17 Aug 2016 18:58:21 -0400 Subject: [PATCH 02/31] cmXScore.c : Added _cmXScoreGraceInsertTimeXXX(). --- app/cmXScore.c | 178 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 120 insertions(+), 58 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 40f81ce..6ab4c4b 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -37,33 +37,34 @@ cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE; enum { - kSectionXsFl = 0x0000001, // rvalue holds section number - kBarXsFl = 0x0000002, - kRestXsFl = 0x0000004, - kGraceXsFl = 0x0000008, - kDotXsFl = 0x0000010, - kChordXsFl = 0x0000020, - kDynXsFl = 0x0000040, - kEvenXsFl = 0x0000080, - kTempoXsFl = 0x0000100, - kHeelXsFl = 0x0000200, - kTieBegXsFl = 0x0000400, - kTieEndXsFl = 0x0000800, - kTieProcXsFl = 0x0001000, - kDampDnXsFl = 0x0002000, - kDampUpXsFl = 0x0004000, - kDampUpDnXsFl = 0x0008000, - kSostDnXsFl = 0x0010000, - kSostUpXsFl = 0x0020000, - kMetronomeXsFl = 0x0040000, // duration holds BPM - kOnsetXsFl = 0x0080000, // this is a sounding note - kBegGroupXsFl = 0x0100000, - kEndGroupXsFl = 0x0200000, - kBegGraceXsFl = 0x0400000, // beg grace note group - kEndGraceXsFl = 0x0800000, // end grace note group - kAddGraceXsFl = 0x1000000, // end grace note group operator flag - add time - kSubGraceXsFl = 0x2000000, // " " " " " " - subtract time - kFirstGraceXsFl = 0x4000000, // " " " " " " - sync to first note + kSectionXsFl = 0x00000001, // rvalue holds section number + kBarXsFl = 0x00000002, + kRestXsFl = 0x00000004, + kGraceXsFl = 0x00000008, + kDotXsFl = 0x00000010, + kChordXsFl = 0x00000020, + kDynXsFl = 0x00000040, + kEvenXsFl = 0x00000080, + kTempoXsFl = 0x00000100, + kHeelXsFl = 0x00000200, + kTieBegXsFl = 0x00000400, + kTieEndXsFl = 0x00000800, + kTieProcXsFl = 0x00001000, + kDampDnXsFl = 0x00002000, + kDampUpXsFl = 0x00004000, + kDampUpDnXsFl = 0x00008000, + kSostDnXsFl = 0x00010000, + kSostUpXsFl = 0x00020000, + kMetronomeXsFl = 0x00040000, // duration holds BPM + kOnsetXsFl = 0x00080000, // this is a sounding note + kBegGroupXsFl = 0x00100000, + kEndGroupXsFl = 0x00200000, + kBegGraceXsFl = 0x00400000, // (b) beg grace note group + kEndGraceXsFl = 0x00800000, // end grace note group + kAddGraceXsFl = 0x01000000, // (a) end grace note group operator flag - add time + kSubGraceXsFl = 0x02000000, // (s) " " " " " " - subtract time + kAFirstGraceXsFl = 0x04000000, // (A) add time after first note + kNFirstGraceXsFl = 0x08000000 // (n) grace notes start as soon as possible after first note and add time }; @@ -1464,6 +1465,9 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p ) cmErrWarnMsg(&p->err,kPedalStateErrorXsRc,"Sostenuto left down at the end of a part."); } + + _cmXScoreSort(p); + return rc; } @@ -1479,21 +1483,30 @@ void _cmXScoreInsertTime( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* np, unsigne } } -// Insert the grace notes in between the first and last note in the group -// by inserting time between the first and last note. -void _cmXScoreGraceInsertTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN ) +void _cmXScoreGraceInsertTimeBase( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN, unsigned initTick ) { - cmXsNote_t* np = NULL; - unsigned expand_ticks = 0; - unsigned ticks = aV[aN-1]->tick; - unsigned i; + cmXsNote_t* np = NULL; + unsigned expand_ticks = 0; + unsigned ticks = initTick; + + unsigned t0 = 0; + unsigned i; + for(i=0; iflags,kGraceXsFl) && aV[i]->graceGroupId == graceGroupId ) { - aV[i]->tick = ticks; - ticks += aV[i]->duration; - expand_ticks += aV[i]->duration; - np = aV[i]; + // if this grace note falls on the same tick as the previous grace note + if( np != NULL && aV[i]->tick == t0 ) + aV[i]->tick = np->tick; + else + { + t0 = aV[i]->tick; // store the unmodified tick value of this note + aV[i]->tick = ticks; // set the new tick value + ticks += aV[i]->duration; // calc the next grace not location + expand_ticks += aV[i]->duration; // track how much we are expanding time by + } + + np = aV[i]; } np = np->slink; @@ -1501,24 +1514,59 @@ void _cmXScoreGraceInsertTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* _cmXScoreInsertTime(p,np->meas,np,expand_ticks); } +// Insert the grace notes in between the first and last note in the group +// by inserting time between the first and last note. +// Note that in effect his means that the last note is pushed back +// in time by the total duration of the grace notes. +void _cmXScoreGraceInsertTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN ) +{ + _cmXScoreGraceInsertTimeBase( p, graceGroupId,aV,aN, aV[aN-1]->tick ); +} + // Insert the grace notes in between the first and last note in the group // but do not insert any additional time betwee the first and last note. // In effect time is removed from the first note and taken by the grace notes. +// The time position of the last note is therefore unchanged. void _cmXScoreGraceOverlayTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN ) { assert(aN >= 3 ); - unsigned t = aV[aN-1]->tick; - int i = (int)aN-2; + int i = (int)aN-2; + cmXsNote_t* np = aV[aN-1]; + unsigned t0 = -1; for(; i>0; --i) if( cmIsFlag(aV[i]->flags,kGraceXsFl) && aV[i]->graceGroupId == graceGroupId ) { - aV[i]->tick = t - aV[i]->duration; - t = aV[i]->tick; + if( aV[i]->tick == t0) + aV[i]->tick = np->tick; + else + { + t0 = aV[i]->tick; + aV[i]->tick = np->tick - aV[i]->duration; + } + + np = aV[i]; } } +// Play the first grace at the time of the first note in the group (which is a non-grace note) +// and then expand time while inserting the other grace notes. +void _cmXScoreGraceInsertAfterFirst( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN ) +{ + _cmXScoreGraceInsertTimeBase( p, graceGroupId,aV,aN, aV[0]->tick ); +} + + +// Play the first grace not shortly (one grace note duration) after the first note +// in the group (which is a non-grace note) and then expand time while inserting the other +// grace notes. +void _cmXScoreGraceInsertSoonAfterFirst( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN ) +{ + _cmXScoreGraceInsertTimeBase( p, graceGroupId,aV,aN, aV[0]->tick + aV[1]->duration ); +} + + // Adjust the locations of grace notes. Note that this must be done // after reordering so that we can be sure that the order in time of // the notes in each group has been set prior to building the @@ -1528,14 +1576,18 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) cmXsRC_t rc = kOkXsRC; unsigned graceGroupId = 1; double graceDurSec = 1.0/15.0; // duration of all grace notes in seconds - + for(; 1; ++graceGroupId) { - cmXsNote_t* gn0p = NULL; - cmXsNote_t* gn1p = NULL; + cmXsNote_t* gn0p = NULL; // first note in the grace group + cmXsNote_t* gn1p = NULL; // last note in the grace group unsigned gN = 0; cmXsPart_t* pp = p->partL; double ticksPerSec = 0; + + // Build a note chain, using cmXsNote_t.grace, between gn0p and + // gn1p containing all the grace notes with + // cmXsNote_t.graceGroupId == graceGroupId. for(; pp!=NULL; pp=pp->link) { @@ -1571,11 +1623,11 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) np->duration = floor(ticksPerSec * graceDurSec); gN += 1; - } + } - } // - } - } + } // for each note in this meassure + } // for each measure + } // for each part // no records were found for this grace id - we're done if( gn0p == NULL ) @@ -1587,7 +1639,6 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The grace not group ending in meas %i has fewer than 3 (%i) members.", gn1p->meas->number, gN ); break; } - // gn0p is now set to the first note in th group // gn1p is now set to the last note in the group @@ -1606,7 +1657,7 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) break; } - // count the total number of events between gn0p and gn1p + // Count the total number of events between gn0p and gn1p cmXsNote_t* n0p = NULL; cmXsNote_t* n1p = gn0p; cmXsMeas_t* mp = gn0p->meas; @@ -1624,8 +1675,8 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) if(1) { bool fl = n0p != NULL && n0p->tick < n1p->tick; - unsigned type = n1p->flags & (kBegGraceXsFl|kEndGraceXsFl|kAddGraceXsFl|kSubGraceXsFl|kFirstGraceXsFl); - printf("%3i 0x%08x %i %5i %i\n",n1p->graceGroupId,type,n1p->tick,n1p->duration,fl); + unsigned type = n1p->flags & (kBegGraceXsFl|kEndGraceXsFl|kAddGraceXsFl|kSubGraceXsFl|kAFirstGraceXsFl|kNFirstGraceXsFl); + printf("%3i 0x%08x %i %3i %5i %i\n",n1p->graceGroupId,type,n1p->meas->number,n1p->tick,n1p->duration,fl); } ++aN; @@ -1653,7 +1704,7 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) n0p = n1p; } - switch( gn1p->flags & (kAddGraceXsFl | kSubGraceXsFl | kFirstGraceXsFl) ) + switch( gn1p->flags & (kAddGraceXsFl | kSubGraceXsFl | kAFirstGraceXsFl | kNFirstGraceXsFl ) ) { case kAddGraceXsFl: _cmXScoreGraceInsertTime(p, graceGroupId, aV, aN ); @@ -1663,7 +1714,12 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) _cmXScoreGraceOverlayTime(p, graceGroupId, aV, aN ); break; - case kFirstGraceXsFl: + case kAFirstGraceXsFl: + _cmXScoreGraceInsertAfterFirst(p,graceGroupId,aV,aN); + break; + + case kNFirstGraceXsFl: + _cmXScoreGraceInsertSoonAfterFirst(p,graceGroupId,aV,aN); break; default: @@ -2136,7 +2192,8 @@ cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned case 'b': r->graceFlags |= kBegGraceXsFl; break; case 'a': r->graceFlags |= kAddGraceXsFl | kEndGraceXsFl; break; case 's': r->graceFlags |= kSubGraceXsFl | kEndGraceXsFl; break; - case 'f': r->graceFlags |= kFirstGraceXsFl | kEndGraceXsFl; break; + case 'A': r->graceFlags |= kAFirstGraceXsFl| kEndGraceXsFl; break; + case 'n': r->graceFlags |= kNFirstGraceXsFl| kEndGraceXsFl; break; case 'g': break; case '%': @@ -3015,7 +3072,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con { cmXsRC_t rc = kOkXsRC; cmSvgH_t svgH = cmSvgNullHandle; - cmXsSvgEvt_t* e = mf->elist; + cmXsSvgEvt_t* e = mf->elist; unsigned noteHeight = 10; const cmChar_t* svgFn = cmFsMakeFn(dir,fn,"html",NULL); const cmChar_t* cssFn = cmFsMakeFn(NULL,fn,"css",NULL); @@ -3198,7 +3255,11 @@ cmXsRC_t cmXScoreTest( return cmErrMsg(&ctx->err,rc,"XScore alloc failed."); if( reorderFn != NULL ) - cmXScoreReorder(h,reorderFn); + if((rc = cmXScoreReorder(h,reorderFn)) != kOkXsRC ) + { + cmErrMsg(&ctx->err,rc,"XScore reorder failed."); + goto errLabel; + } // assign durations to pedal down events _cmXScoreProcessPedals(_cmXScoreHandleToPtr(h)); @@ -3239,6 +3300,7 @@ cmXsRC_t cmXScoreTest( cmXScoreReport(h,&ctx->rpt,true); + errLabel: return cmXScoreFinalize(&h); } From bd1d645e81af5a117af99309374a0ee205961db2 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 17 Aug 2016 18:58:42 -0400 Subject: [PATCH 03/31] cmSvgWriter.c : Added _cmSgWriterFlipY(). --- cmSvgWriter.c | 20 ++++++++++++++++++++ cmSvgWriter.h | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/cmSvgWriter.c b/cmSvgWriter.c index d854932..5533bfb 100644 --- a/cmSvgWriter.c +++ b/cmSvgWriter.c @@ -167,6 +167,24 @@ void _cmSvgSize( cmSvg_t* p, double* widthRef, double* heightRef ) *heightRef = max_y - min_y; } +void _cmSvgWriterFlipY( cmSvg_t* p, unsigned height ) +{ + cmSvgEle_t* e = p->elist; + for(; e!=NULL; e=e->link) + { + e->y0 = (-e->y0) + height; + e->y1 = (-e->y1) + height; + + if( e->id == kRectSvgId ) + { + double t = e->y1; + e->y1 = e->y0; + e->y0 = t; + } + + } +} + cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* outFn ) { @@ -178,6 +196,8 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* cmFileH_t fH = cmFileNullHandle; _cmSvgSize(p, &svgWidth, &svgHeight ); + + _cmSvgWriterFlipY( p, svgHeight ); if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC ) return cmErrMsg(&p->err,kFileFailSvgRC,"SVG file create failed for '%s'.",cmStringNullGuard(outFn)); diff --git a/cmSvgWriter.h b/cmSvgWriter.h index aca0a43..83470d0 100644 --- a/cmSvgWriter.h +++ b/cmSvgWriter.h @@ -21,7 +21,7 @@ enum cmSvgRC_t cmSvgWriterFree( cmSvgH_t* hp ); bool cmSvgWriterIsValid( cmSvgH_t h ); - cmSvgRC_t cmSvgWriterRect( cmSvgH_t h, double x, double y, double ww, double hh, const cmChar_t* cssClassLabel ); + cmSvgRC_t cmSvgWriterRect( cmSvgH_t h, double x, double y, double ww, double hh, const cmChar_t* cssClassLabel ); cmSvgRC_t cmSvgWriterLine( cmSvgH_t h, double x0, double y0, double x1, double y1, const cmChar_t* cssClassLabel ); cmSvgRC_t cmSvgWriterText( cmSvgH_t h, double x, double y, const cmChar_t* text, const cmChar_t* cssClassLabel ); From caffeb4b0a9e3794732917ab59ca820f16565c26 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 24 Aug 2016 13:59:03 -0400 Subject: [PATCH 04/31] cmTimeLine.c : _cmTimeLineObjAtTime() changed such that when an object contains the search point that the returned object also contains the search point. The returned object will be the one containing the search point whose begin or end point is closest to the search point. --- app/cmTimeLine.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/app/cmTimeLine.c b/app/cmTimeLine.c index bc381d0..9ebc521 100644 --- a/app/cmTimeLine.c +++ b/app/cmTimeLine.c @@ -134,7 +134,7 @@ cmTlMidiFile_t* _cmTlMidiFileObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl ) if( op==NULL || op->typeId != kMidiFileTlId ) { if( errFl && p != NULL) - cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed."); + cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to MIDI file failed."); return NULL; } @@ -147,7 +147,7 @@ cmTlMidiEvt_t* _cmTlMidiEvtObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl ) if( op==NULL || op->typeId != kMidiEvtTlId ) { if( errFl && p != NULL ) - cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed."); + cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to MIDI event failed."); return NULL; } @@ -173,7 +173,7 @@ cmTlAudioEvt_t* _cmTlAudioEvtObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl ) if( op==NULL || op->typeId != kAudioEvtTlId ) { if( errFl && p != NULL) - cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed."); + cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to audio event failed."); return NULL; } return (cmTlAudioEvt_t*)op; @@ -186,7 +186,7 @@ cmTlMarker_t* _cmTlMarkerObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl ) if( op==NULL || op->typeId != kMarkerTlId ) { if( errFl && p != NULL) - cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed."); + cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to marker object failed."); return NULL; } return (cmTlMarker_t*)op; @@ -1227,31 +1227,37 @@ _cmTlObj_t* _cmTimeLineObjAtTime( _cmTl_t* p, unsigned seqId, unsigned seqSmpIdx _cmTlObj_t* op = p->seq[seqId].first; _cmTlObj_t* min_op = NULL; unsigned minDist = UINT_MAX; - + bool inFl = false; + + // for each object in the requested sequence for(; op!=NULL; op=op->next) if( typeId==cmInvalidId || op->obj->typeId == typeId ) { - // if seqSmpIdx is inside this object - then return it as the solution + bool in0Fl = false; + + // if seqSmpIdx is inside this object - then the returned object must contain seqSmpIdx + // (but the ideal point to return is the one which contains seqSmpIdx and also has + // a begin or end point very close to seqSmpIdx - so this defer selecting an object + // until all objects which may contain seqSmpIdx have been examined if((op->obj->seqSmpIdx <= seqSmpIdx && seqSmpIdx < (op->obj->seqSmpIdx + op->obj->durSmpCnt))) - return op; - + { + inFl = true; // the returned object must contain seqSmpIdx + in0Fl = true; // this object contains seqSmpIdx + } + // measure the distance from seqSmpIdx to the begin and end of this object unsigned d0 = op->obj->seqSmpIdx < seqSmpIdx ? seqSmpIdx - op->obj->seqSmpIdx : op->obj->seqSmpIdx - seqSmpIdx; unsigned d1 = op->obj->seqSmpIdx+op->obj->durSmpCnt < seqSmpIdx ? seqSmpIdx - op->obj->seqSmpIdx+op->obj->durSmpCnt : op->obj->seqSmpIdx+op->obj->durSmpCnt - seqSmpIdx; - // d0 and d1 should decrease as the cur object approaches seqSmpIdx - // If they do not then the search is over - return the closest point. - if( d0>minDist && d1>minDist) - break; // track the min dist and the assoc'd obj - if( d0 < minDist ) + if( d0 < minDist && (inFl==false || inFl==in0Fl)) { minDist = d0; min_op = op; } - if( d1 < minDist ) + if( d1 < minDist && (inFl==false || inFl==in0Fl)) { minDist = d1; min_op = op; From 1d1681eefd6b8a38ededb3c320c6dbaba77565ac Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 24 Aug 2016 13:59:52 -0400 Subject: [PATCH 05/31] cmDspKr.c : In _cmDspTimeLineRecv() allow the selected time line object to be a kMidiEvtTlId type. --- dsp/cmDspKr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/cmDspKr.c b/dsp/cmDspKr.c index 7252ffe..3f43eff 100644 --- a/dsp/cmDspKr.c +++ b/dsp/cmDspKr.c @@ -411,7 +411,7 @@ cmDspRC_t _cmDspTimeLineRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_ cmTlObj_t* op; if((op = cmTimeLineIdToObj(p->tlH, cmInvalidId, markerId )) != NULL ) { - assert(op->typeId == kMarkerTlId); + assert(op->typeId == kMarkerTlId || op->typeId == kMidiEvtTlId ); p->afIdx = op->begSmpIdx; From 4ac92aef52154c383a48ec74e6de35e640ad635a Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 24 Aug 2016 18:38:04 -0400 Subject: [PATCH 06/31] cmXScore.c: _cmXScoreSetAbsoluteTime() is now automatically called from _cmXScoreSort(). --- app/cmXScore.c | 70 ++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 6ab4c4b..a89dfce 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -882,32 +882,6 @@ cmXsNote_t* _cmXScoreInsertSortedNote( cmXsNote_t* s0, cmXsNote_t* np ) return s0; } -void _cmXScoreSort( cmXScore_t* p ) -{ - // for each part - cmXsPart_t* pp = p->partL; - for(; pp!=NULL; pp=pp->link) - { - // for each measure in this part - cmXsMeas_t* mp = pp->measL; - for(; mp!=NULL; mp=mp->link) - { - // explicitely set noteL to NULL to in case we are re-sorting - mp->noteL = NULL; - - // for each voice in this measure - cmXsVoice_t* vp = mp->voiceL; - for(; vp!=NULL; vp=vp->link) - { - // for each note in this measure - cmXsNote_t* np = vp->noteL; - for(; np!=NULL; np=np->mlink) - mp->noteL = _cmXScoreInsertSortedNote(mp->noteL,np); - } - } - } -} - // Set the cmXsNode_t.secs and dsecs. void _cmXScoreSetAbsoluteTime( cmXScore_t* p ) { @@ -960,6 +934,36 @@ void _cmXScoreSetAbsoluteTime( cmXScore_t* p ) } } + +void _cmXScoreSort( cmXScore_t* p ) +{ + // for each part + cmXsPart_t* pp = p->partL; + for(; pp!=NULL; pp=pp->link) + { + // for each measure in this part + cmXsMeas_t* mp = pp->measL; + for(; mp!=NULL; mp=mp->link) + { + // explicitely set noteL to NULL to in case we are re-sorting + mp->noteL = NULL; + + // for each voice in this measure + cmXsVoice_t* vp = mp->voiceL; + for(; vp!=NULL; vp=vp->link) + { + // for each note in this measure + cmXsNote_t* np = vp->noteL; + for(; np!=NULL; np=np->mlink) + mp->noteL = _cmXScoreInsertSortedNote(mp->noteL,np); + } + } + } + + // The order of events may have changed update the absolute time. + _cmXScoreSetAbsoluteTime( p ); +} + // All notes in a[aN] are on the same tick unsigned _cmXsSpreadGraceNotes( cmXsNote_t** a, unsigned aN ) { @@ -1514,7 +1518,7 @@ void _cmXScoreGraceInsertTimeBase( cmXScore_t* p, unsigned graceGroupId, cmXsNot _cmXScoreInsertTime(p,np->meas,np,expand_ticks); } -// Insert the grace notes in between the first and last note in the group +// (a) Insert the grace notes in between the first and last note in the group // by inserting time between the first and last note. // Note that in effect his means that the last note is pushed back // in time by the total duration of the grace notes. @@ -1523,7 +1527,7 @@ void _cmXScoreGraceInsertTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* _cmXScoreGraceInsertTimeBase( p, graceGroupId,aV,aN, aV[aN-1]->tick ); } -// Insert the grace notes in between the first and last note in the group +// (s) Insert the grace notes in between the first and last note in the group // but do not insert any additional time betwee the first and last note. // In effect time is removed from the first note and taken by the grace notes. // The time position of the last note is therefore unchanged. @@ -1550,7 +1554,7 @@ void _cmXScoreGraceOverlayTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t } } -// Play the first grace at the time of the first note in the group (which is a non-grace note) +// (A) Play the first grace at the time of the first note in the group (which is a non-grace note) // and then expand time while inserting the other grace notes. void _cmXScoreGraceInsertAfterFirst( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN ) { @@ -1558,7 +1562,7 @@ void _cmXScoreGraceInsertAfterFirst( cmXScore_t* p, unsigned graceGroupId, cmXsN } -// Play the first grace not shortly (one grace note duration) after the first note +// (n) Play the first grace not shortly (one grace note duration) after the first note // in the group (which is a non-grace note) and then expand time while inserting the other // grace notes. void _cmXScoreGraceInsertSoonAfterFirst( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN ) @@ -1672,7 +1676,7 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) n1p = mp->noteL; } - if(1) + if(0) { bool fl = n0p != NULL && n0p->tick < n1p->tick; unsigned type = n1p->flags & (kBegGraceXsFl|kEndGraceXsFl|kAddGraceXsFl|kSubGraceXsFl|kAFirstGraceXsFl|kNFirstGraceXsFl); @@ -1773,8 +1777,6 @@ cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn ) _cmXScoreSort(p); - _cmXScoreSetAbsoluteTime(p); - _cmXScoreResolveTiesAndLoc(p); _cmXScoreRemoveDuplicateNotes(p); @@ -3298,7 +3300,7 @@ cmXsRC_t cmXScoreTest( } - cmXScoreReport(h,&ctx->rpt,true); + //cmXScoreReport(h,&ctx->rpt,true); errLabel: return cmXScoreFinalize(&h); From ca40492412c3c66c540813fa710896e1a9d53e05 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 24 Aug 2016 18:39:03 -0400 Subject: [PATCH 07/31] cmSvgWriter.h/c : Added calls to use svg-pan-zoom.js to HTML header. --- cmSvgWriter.c | 63 +++++++++++++++++++++++++++++++++++++++------------ cmSvgWriter.h | 5 ++++ 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/cmSvgWriter.c b/cmSvgWriter.c index 5533bfb..cfb630e 100644 --- a/cmSvgWriter.c +++ b/cmSvgWriter.c @@ -186,6 +186,7 @@ void _cmSvgWriterFlipY( cmSvg_t* p, unsigned height ) } + cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* outFn ) { cmSvgRC_t rc = kOkSvgRC; @@ -194,17 +195,34 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* double svgHeight = 0; cmSvgEle_t* e = p->elist; cmFileH_t fH = cmFileNullHandle; + cmChar_t* s0 = NULL; + cmChar_t* s1 = NULL; + + cmChar_t hdr[] = + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + + _cmSvgSize(p, &svgWidth, &svgHeight ); _cmSvgWriterFlipY( p, svgHeight ); - - if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC ) - return cmErrMsg(&p->err,kFileFailSvgRC,"SVG file create failed for '%s'.",cmStringNullGuard(outFn)); - if( cmFilePrintf(fH,"\n\n\n\n",svgWidth,svgHeight,cssFn) != kOkFileRC ) + // print the file header + if( (s0 = cmTsPrintfP(s0,hdr,cssFn,svgWidth,svgHeight)) == NULL ) { - rc = cmErrMsg(&p->err,kFileFailSvgRC,"File prefix write failed."); + rc = cmErrMsg(&p->err,kPrintFailSvgRC,"File prefix write failed."); goto errLabel; } @@ -213,18 +231,18 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* switch( e->id ) { case kRectSvgId: - if( cmFilePrintf(fH,"\n",e->x0,e->y0,e->x1-e->x0,e->y1-e->y0,e->cssClass) != kOkFileRC ) - rc = kFileFailSvgRC; + if( (s1 = cmTsPrintfP(s1,"\n",e->x0,e->y0,e->x1-e->x0,e->y1-e->y0,e->cssClass)) == NULL ) + rc = kPrintFailSvgRC; break; case kLineSvgId: - if( cmFilePrintf(fH,"\n",e->x0,e->y0,e->x1,e->y1,e->cssClass) != kOkFileRC ) - rc = kFileFailSvgRC; + if( (s1 = cmTsPrintfP(s1,"\n",e->x0,e->y0,e->x1,e->y1,e->cssClass)) == NULL ) + rc = kPrintFailSvgRC; break; case kTextSvgId: - if( cmFilePrintf(fH,"%s\n",e->x0,e->y0,e->cssClass,e->text) != kOkFileRC ) - rc = kFileFailSvgRC; + if( (s1 = cmTsPrintfP(s1,"%s\n",e->x0,e->y0,e->cssClass,e->text)) == NULL ) + rc = kPrintFailSvgRC; break; default: @@ -234,21 +252,38 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* if( rc != kOkSvgRC ) { - rc = cmErrMsg(&p->err,kFileFailSvgRC,"Element write failed."); + rc = cmErrMsg(&p->err,kPrintFailSvgRC,"Element write failed."); break; } + + s0 = cmTextAppendSS(s0,s1); + } - if( cmFilePrint(fH,"\n\n\n") != kOkFileRC ) + if( (s1 = cmTsPrintfP(s1,"\n\n\n")) == NULL ) { - rc = cmErrMsg(&p->err,kFileFailSvgRC,"File suffix write failed."); + rc = cmErrMsg(&p->err,kPrintFailSvgRC,"File suffix write failed."); goto errLabel; } + if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC ) + { + rc = cmErrMsg(&p->err,kFileFailSvgRC,"SVG file create failed for '%s'.",cmStringNullGuard(outFn)); + goto errLabel; + } + + if( cmFilePrint(fH,s0 = cmTextAppendSS(s0,s1)) != kOkFileRC ) + { + rc = cmErrMsg(&p->err,kFileFailSvgRC,"File write failed."); + goto errLabel; + } errLabel: cmFileClose(&fH); + cmMemFree(s0); + cmMemFree(s1); + return rc; } diff --git a/cmSvgWriter.h b/cmSvgWriter.h index 83470d0..01214cf 100644 --- a/cmSvgWriter.h +++ b/cmSvgWriter.h @@ -9,6 +9,7 @@ enum { kOkSvgRC = cmOkRC, kFileFailSvgRC, + kPrintFailSvgRC, kLHeapFailSvgRC }; @@ -25,6 +26,10 @@ enum cmSvgRC_t cmSvgWriterLine( cmSvgH_t h, double x0, double y0, double x1, double y1, const cmChar_t* cssClassLabel ); cmSvgRC_t cmSvgWriterText( cmSvgH_t h, double x, double y, const cmChar_t* text, const cmChar_t* cssClassLabel ); + // Write the SVG file. Note that header on this file references the CSS file 'cssFn' + // and the Javascript file svg-pan-zoom.min.js from https://github.com/ariutta/svg-pan-zoom. + // Both the CSS file and svg-pan-zoom.min.js should therefore be in the same directory + // as the output HTML file. cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* outFn ); #ifdef __cplusplus From ee9ba9270e4a2ffa115f0aba21457b2b6a7f2aa6 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 30 Aug 2016 16:43:54 -0400 Subject: [PATCH 08/31] cmMidiScoreFollow.h/c : Added filename parameters to cmMidiScoreFollowMain(). --- app/cmMidiScoreFollow.c | 64 ++++++++++++++++++++++++----------------- app/cmMidiScoreFollow.h | 10 ++++++- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/app/cmMidiScoreFollow.c b/app/cmMidiScoreFollow.c index 5b38ad7..803d63a 100644 --- a/app/cmMidiScoreFollow.c +++ b/app/cmMidiScoreFollow.c @@ -112,6 +112,14 @@ void _cmMsf_ReportMidiErrors( const _cmMsf_ScoreFollow_t* f, cmScH_t scH, const } } +void _cmMsf_WriteMatchFileHeader( cmFileH_t fH ) +{ + cmFilePrintf(fH," Score Score Score MIDI MIDI MIDI\n"); + cmFilePrintf(fH," Bar UUID Pitch UUID Ptch Vel.\n"); + cmFilePrintf(fH,"- ----- ----- ----- ----- ---- ----\n"); +} + + // Write one scScoreMatcherResult_t record to the file fH. unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcherResult_t* r ) { @@ -130,7 +138,7 @@ unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcher cmMidiToSciPitch(e->pitch,buf,5); } - cmFilePrintf(fH,"m %3i %5i %4s %5i %4s %3i\n", + cmFilePrintf(fH,"m %5i %5i %5s %5i %4s %3i\n", loc==NULL ? 0 : loc->barNumb, // score evt bar scUid, // score event uuid buf, // score event pitch @@ -147,17 +155,17 @@ void _cmMsf_ScoreFollowCb( struct cmScMatcher_str* p, void* arg, cmScMatcherResu r->rV[r->rN++] = *rp; } -cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx ) +cmMsfRC_t cmMidiScoreFollowMain( + cmCtx_t* ctx, + const cmChar_t* scoreCsvFn, // score CSV file as generated from cmXScoreTest(). + const cmChar_t* midiFn, // MIDI file to track + const cmChar_t* matchRptOutFn, // Score follow status report + const cmChar_t* matchSvgOutFn, // Score follow graphic report + const cmChar_t* midiOutFn, // (optional) midiFn with apply sostenuto and velocities from the score to the MIDI file + const cmChar_t* tlBarOutFn // (optional) bar positions sutiable for use in a cmTimeLine description file. +) { - cmMsfRC_t rc = kOkMsfRC; - //const cmChar_t* scoreFn = cmFsMakeUserDirFn("src/kc/src/kc/data","mod2e.csv"); - const cmChar_t* scoreFn = cmFsMakeUserDirFn("temp","a7.csv"); - const cmChar_t* midiFn = cmFsMakeUserDirFn("media/projects/imag_themes/scores/gen","round1-utf8_11.mid"); - const cmChar_t* outFn = cmFsMakeUserDirFn("temp","match.txt"); - const cmChar_t* svgFn = cmFsMakeUserDirFn("temp","score0.html"); - const cmChar_t* newMidiFn= cmFsMakeUserDirFn("temp","a7.mid"); - const cmChar_t* tlBarFn = cmFsMakeUserDirFn("temp","time_line_temp.txt"); - + cmMsfRC_t rc = kOkMsfRC; double srate = 96000.0; cmScMatcher* smp = NULL; cmScH_t scH = cmScNullHandle; @@ -179,16 +187,16 @@ cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx ) cmCtx* prCtx = cmCtxAlloc(NULL, err.rpt, cmLHeapNullHandle, cmSymTblNullHandle ); // initialize the score - if( cmScoreInitialize( ctx, &scH, scoreFn, srate, NULL, 0, NULL, NULL, cmSymTblNullHandle) != kOkScRC ) + if( cmScoreInitialize( ctx, &scH, scoreCsvFn, srate, NULL, 0, NULL, NULL, cmSymTblNullHandle) != kOkScRC ) { - rc = cmErrMsg(&err,kFailMsfRC,"cmScoreInitialize() failed on %s",cmStringNullGuard(scoreFn)); + rc = cmErrMsg(&err,kFailMsfRC,"cmScoreInitialize() failed on %s",cmStringNullGuard(scoreCsvFn)); goto errLabel; } // setup the callback record if((sfr.rAllocN = cmScoreEvtCount( scH )*2) == 0) { - rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreFn)); + rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreCsvFn)); goto errLabel; } @@ -229,19 +237,21 @@ cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx ) printf("MIDI notes:%i Score Events:%i\n",mN,cmScoreEvtCount(scH)); // create the output file - if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC ) + if( cmFileOpen(&fH,matchRptOutFn,kWriteFileFl,&ctx->rpt) != kOkFileRC ) { - rc = cmErrMsg(&err,kFailMsfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn)); + rc = cmErrMsg(&err,kFailMsfRC,"Unable to create the file '%s'.",cmStringNullGuard(matchRptOutFn)); goto errLabel; } // allocate the graphics object - if( cmScoreMatchGraphicAlloc( ctx, &smgH, scoreFn, midiFn ) != kOkSmgRC ) + if( cmScoreMatchGraphicAlloc( ctx, &smgH, scoreCsvFn, midiFn ) != kOkSmgRC ) { rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics allocation failed.."); goto errLabel; } + // write the match report output file header + _cmMsf_WriteMatchFileHeader(fH); // for each score follower callback record for(i=0; irpt ); // write the tracking match file as an SVG file. - cmScoreMatchGraphicWrite( smgH, svgFn ); + cmScoreMatchGraphicWrite( smgH, matchSvgOutFn ); // write a cmTimeLine file which contains markers at each bar position - cmScoreMatchGraphicGenTimeLineBars(smgH, tlBarFn, srate ); + if( tlBarOutFn != NULL ) + cmScoreMatchGraphicGenTimeLineBars(smgH, tlBarOutFn, srate ); - cmScoreMatchGraphicUpdateMidiFromScore( ctx, smgH, newMidiFn ); + if( midiOutFn != NULL ) + cmScoreMatchGraphicUpdateMidiFromScore( ctx, smgH, midiOutFn ); errLabel: @@ -286,12 +298,12 @@ cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx ) cmCtxFree(&prCtx); - cmFsFreeFn(scoreFn); - cmFsFreeFn(midiFn); - cmFsFreeFn(outFn); - cmFsFreeFn(svgFn); - cmFsFreeFn(newMidiFn); - cmFsFreeFn(tlBarFn); + //cmFsFreeFn(scoreCsvFn); + //cmFsFreeFn(midiFn); + //cmFsFreeFn(matchRptOutFn); + //cmFsFreeFn(matchSvgOutFn); + //cmFsFreeFn(outMidiFn); + //cmFsFreeFn(tlBarFn); return rc; } diff --git a/app/cmMidiScoreFollow.h b/app/cmMidiScoreFollow.h index 5e0fc39..838c728 100644 --- a/app/cmMidiScoreFollow.h +++ b/app/cmMidiScoreFollow.h @@ -15,7 +15,15 @@ extern "C" { typedef cmRC_t cmMsfRC_t; - cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx ); + cmMsfRC_t cmMidiScoreFollowMain( + cmCtx_t* ctx, + const cmChar_t* scoreCsvFn, // score CSV file as generated from cmXScoreTest(). + const cmChar_t* midiFn, // MIDI file to track + const cmChar_t* matchRptOutFn, // Score follow status report + const cmChar_t* matchSvgOutFn, // Score follow graphic report + const cmChar_t* midiOutFn, // (optional) midiFn with apply sostenuto and velocities from the score to the MIDI file + const cmChar_t* tlBarOutFn // (optional) bar positions sutiable for use in a cmTimeLine description file. + ); #ifdef __cplusplus } From 4d6bff0ecf38cb8b234ba71f6e45e899370e9b6b Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 30 Aug 2016 16:44:38 -0400 Subject: [PATCH 09/31] cmScoreMatchGraphic.c : Removed call to cmMidiFilePrintMsgs() from cmScoreMatchGraphicUpdateMidiFromScore(). --- app/cmScoreMatchGraphic.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/cmScoreMatchGraphic.c b/app/cmScoreMatchGraphic.c index d1a3a4a..595a108 100644 --- a/app/cmScoreMatchGraphic.c +++ b/app/cmScoreMatchGraphic.c @@ -798,8 +798,6 @@ cmSmgRC_t cmScoreMatchGraphicUpdateMidiFromScore( cmCtx_t* ctx, cmSmgH_t h, cons goto errLabel; } - cmMidiFilePrintMsgs(mfH, p->err.rpt ); - errLabel: cmMidiFileClose(&mfH); From 227bf8954ba607409fc02459d380f76142d1b454 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 30 Aug 2016 16:46:35 -0400 Subject: [PATCH 10/31] cmMidiFile.c : Added _cmMidiFileInsertEotMsg(). Fixed bug where _cmMidiFileTrk.cnt was not incremented in cmMidiFileInsertTrkMsg(). --- cmMidiFile.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/cmMidiFile.c b/cmMidiFile.c index 0071468..06f07fc 100644 --- a/cmMidiFile.c +++ b/cmMidiFile.c @@ -256,7 +256,7 @@ cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmM unsigned byteN = cmMidiStatusToByteCount(tmp->status); if( byteN==kInvalidMidiByte || byteN > 2 ) - return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i.",tmp->status,tmp->status); + return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i byte cnt:%i.",tmp->status,tmp->status,byteN); unsigned i; for(i=useRsFl; ierr,kFileFailMfRC,"MIDI file close failed."); if( rc != kOkMfRC ) + { _cmMidiFileClose(p); - + hp->h = NULL; + } + return rc; } @@ -971,12 +974,75 @@ cmMfRC_t _cmMidiFileWriteMetaMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tm return rc; } +cmMfRC_t _cmMidiFileInsertEotMsg( _cmMidiFile_t* p, unsigned trkIdx ) +{ + _cmMidiTrack_t* trk = p->trkV + trkIdx; + cmMidiTrackMsg_t* m0 = NULL; + cmMidiTrackMsg_t* m = trk->base; + + // locate the current EOT msg on this track + for(; m!=NULL; m=m->link) + { + if( m->status == kMetaStId && m->metaId == kEndOfTrkMdId ) + { + // If this EOT msg is the last msg in the track ... + if( m->link == NULL ) + { + assert( m == trk->last ); + return kOkMfRC; // ... then there is nothing else to do + } + + // If this EOT msg is not the last in the track ... + if( m0 != NULL ) + m0->link = m->link; // ... then unlink it + + break; + } + + m0 = m; + } + + // if we get here then the last msg in the track was not an EOT msg + + // if there was no previously allocated EOT msg + if( m == NULL ) + { + m = _cmMidiFileAllocMsg(p, trkIdx, 1, kMetaStId ); + m->metaId = kEndOfTrkMdId; + trk->cnt += 1; + + } + + // link an EOT msg as the last msg on the track + + // if the track is currently empty + if( m0 == NULL ) + { + trk->base = m; + trk->last = m; + } + else // link the msg as the last on on the track + { + assert( m0 == trk->last); + m0->link = m; + m->link = NULL; + trk->last = m; + } + + return kOkMfRC; + +} + cmMfRC_t _cmMidiFileWriteTrack( _cmMidiFile_t* mfp, unsigned trkIdx ) { cmMfRC_t rc = kOkMfRC; cmMidiTrackMsg_t* tmp = mfp->trkV[trkIdx].base; cmMidiByte_t runStatus = 0; + // be sure there is a EOT msg at the end of this track + if((rc = _cmMidiFileInsertEotMsg(mfp, trkIdx )) != kOkMfRC ) + return rc; + for(; tmp != NULL; tmp=tmp->link) { // write the msg tick count @@ -1379,7 +1445,8 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi assert( m1->atick >= m->atick ); m1->dtick = m1->atick - m->atick; } - + + p->trkV[trkIdx].cnt += 1; p->msgVDirtyFl = true; return kOkMfRC; @@ -1402,6 +1469,8 @@ cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned m.status = status & 0xf0; m.byteCnt = sizeof(cm); m.u.chMsgPtr = &cm; + + assert( m.status >= kNoteOffMdId && m.status <= kPbendMdId ); return cmMidiFileInsertTrackMsg(h,trkIdx,&m); } From bea6d075688d1fd67963f65ac1b76bfbf86af9da Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 31 Aug 2016 13:38:07 -0400 Subject: [PATCH 11/31] cmSCoreMatchGraphic.c : Removed printf() from _cmScoreMatchGraphicInsertMidiMsg(). --- app/cmScoreMatchGraphic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cmScoreMatchGraphic.c b/app/cmScoreMatchGraphic.c index 595a108..79799fb 100644 --- a/app/cmScoreMatchGraphic.c +++ b/app/cmScoreMatchGraphic.c @@ -690,7 +690,7 @@ cmSmgRC_t _cmScoreMatchGraphicInsertMidiMsg( cmSmg_t* p, cmMidiFileH_t mfH, bool cmMidiByte_t midi_vel = pedalDnFl ? 64 : 0; cmMidiByte_t midi_ch = 0; - printf("pedal:%s\n",pedalDnFl?"down":"up"); + //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 ) From d17b30831d221fb4531bc41d63a5132690d3990a Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 31 Aug 2016 13:38:38 -0400 Subject: [PATCH 12/31] cmXScore.h/c : Added _cmXScoreProcessOverlappingNotes(). --- app/cmXScore.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++--- app/cmXScore.h | 3 +- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index a89dfce..00dfc04 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -1208,6 +1208,83 @@ cmXsRC_t _cmXScoreResolveOctaveShift( cmXScore_t* p ) return kOkXsRC; } +cmXsNote_t* _cmXScoreFindOverlappingNote( cmXScore_t* p, const cmXsNote_t* knp ) +{ + cmXsPart_t* pp = p->partL; + + // for each part + for(; pp!=NULL; pp=pp->link) + { + cmXsMeas_t* mp = pp->measL; + + // for each measure + for(; mp!=NULL; mp=mp->link) + { + cmXsNote_t* np = mp->noteL; + + // for each note in this measure + for(; np!=NULL; np=np->slink) + if( np->uid != knp->uid + && cmIsFlag(np->flags,kOnsetXsFl) + && knp->pitch == np->pitch + && knp->tick >= np->tick + && knp->tick < (np->tick + np->duration) ) + { + return np; + } + } + } + return NULL; +} + +void _cmXScoreProcessOverlappingNotes( cmXScore_t* p ) +{ + cmXsPart_t* pp = p->partL; + + // for each part + for(; pp!=NULL; pp=pp->link) + { + cmXsMeas_t* mp = pp->measL; + + // for each measure + for(; mp!=NULL; mp=mp->link) + { + cmXsNote_t* np = mp->noteL; + cmXsNote_t* fnp; + + // for each note in this measure + for(; np!=NULL; np=np->slink) + if( cmIsFlag(np->flags,kOnsetXsFl) && (fnp = _cmXScoreFindOverlappingNote(p,np)) != NULL) + { + // is np entirely contained inside fnp + bool embeddedFl = fnp->tick + fnp->duration > np->tick + np->duration; + + //printf("bar=%3i %4s voice:%2i %2i : %7i %7i : %7i %7i : %7i : %c \n",np->meas->number,cmMidiToSciPitch(np->pitch,NULL,0),np->voice->id,fnp->voice->id,fnp->tick,fnp->tick+fnp->duration,np->tick,np->tick+np->duration, (fnp->tick+fnp->duration) - np->tick, embeddedFl ? 'E' : 'O'); + + // turn off embedded notes + if( embeddedFl ) + { + if( np->voice->id == fnp->voice->id ) + cmErrWarnMsg(&p->err,kOverlapWarnXsRC,"A time embedded note (bar=%i %s) was removed even though it overlapped with a note in the same voice.",np->meas->number,cmMidiToSciPitch(np->pitch,NULL,0)); + + np->flags = cmClrFlag(np->flags,kOnsetXsFl); + } + else + { + int d = (fnp->tick+fnp->duration) - np->tick; + + // shorten the first note + if( d > 0 && d < fnp->duration ) + fnp->duration -= d; + + // move the second note just past it + np->tick = fnp->tick + fnp->duration + 1; + } + } + } + } +} + // The identical pitch may be notated to play simultaneously on different voices. // As performed on the piano this will equate to a single sounding note. @@ -2426,7 +2503,7 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn ) -/* +/* CSV score columns kMidiFileIdColScIdx= 0, kTypeLabelColScIdx = 3, kDSecsColScIdx = 4, @@ -2719,7 +2796,6 @@ cmXsRC_t _cmXScoreWriteCsvRow( errLabel: return rc; - } cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn ) @@ -3076,9 +3152,12 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con cmSvgH_t svgH = cmSvgNullHandle; cmXsSvgEvt_t* e = mf->elist; unsigned noteHeight = 10; - const cmChar_t* svgFn = cmFsMakeFn(dir,fn,"html",NULL); + cmChar_t* fn0 = cmMemAllocStr( fn ); + const cmChar_t* svgFn = cmFsMakeFn(dir,fn0 = cmTextAppendSS(fn0,"_midi_svg"),"html",NULL); const cmChar_t* cssFn = cmFsMakeFn(NULL,fn,"css",NULL); cmChar_t* t0 = NULL; // temporary dynamic string + + cmMemFree(fn0); // create the SVG writer if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC ) @@ -3265,7 +3344,10 @@ cmXsRC_t cmXScoreTest( // assign durations to pedal down events _cmXScoreProcessPedals(_cmXScoreHandleToPtr(h)); - + + // remove some notes which share a pitch which are overlapped or embedded + _cmXScoreProcessOverlappingNotes(_cmXScoreHandleToPtr(h)); + if( csvOutFn != NULL ) { cmScH_t scH = cmScNullHandle; @@ -3287,7 +3369,7 @@ cmXsRC_t cmXScoreTest( cmSymTblDestroy(&stH); } - + if( midiOutFn != NULL ) { cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn); diff --git a/app/cmXScore.h b/app/cmXScore.h index af9a556..1c47f38 100644 --- a/app/cmXScore.h +++ b/app/cmXScore.h @@ -18,7 +18,8 @@ extern "C" { kPedalStateErrorXsRc, kMidiFailXsRC, kFileFailXsRC, - kSvgFailXsRC + kSvgFailXsRC, + kOverlapWarnXsRC }; typedef cmRC_t cmXsRC_t; From 382b9a9d2eabc74adba615fd0d55fac9c249fe7b Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 31 Aug 2016 18:09:50 -0400 Subject: [PATCH 13/31] cmScore.c : Added 'silent' note category to _cmScDynRefArray[]. --- app/cmScore.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/cmScore.c b/app/cmScore.c index b51cd91..d7ca324 100644 --- a/app/cmScore.c +++ b/app/cmScore.c @@ -154,15 +154,16 @@ cmScEvtRef_t _cmScEvtRefArray[] = cmScEvtRef_t _cmScDynRefArray[] = { - { 1, 0, "pppp"}, - { 2, 0, "ppp" }, - { 3, 0, "pp" }, - { 4, 0, "p" }, - { 5, 0, "mp" }, - { 6, 0, "mf" }, - { 7, 0, "f" }, - { 8, 0, "ff" }, - { 9, 0, "fff" }, + { 1, 0, "silent"}, + { 2, 0, "pppp"}, + { 3, 0, "ppp" }, + { 4, 0, "pp" }, + { 5, 0, "p" }, + { 6, 0, "mp" }, + { 7, 0, "mf" }, + { 8, 0, "f" }, + { 9, 0, "ff" }, + { 10, 0, "fff" }, { kInvalidDynScId,0, "***" }, }; From 1da843a380dd26703da5b4b928608923cbb1a1c0 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 31 Aug 2016 18:13:58 -0400 Subject: [PATCH 14/31] cmXScore.c : Added 'silent' note dynamic category. Fixed bug in cmXScoreReorder() where last measure was not processed. --- app/cmXScore.c | 66 ++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 00dfc04..4497431 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -76,7 +76,6 @@ typedef struct cmXsNote_str unsigned uid; // unique id of this note record unsigned flags; // See k???XsFl unsigned pitch; // midi pitch - unsigned velocity; // midi velocity unsigned dynamics; // dynamic level 1=pppp 9=fff unsigned vel; // score specified MIDI velocity cmChar_t step; // A-G @@ -1931,30 +1930,31 @@ typedef struct _cmXScoreDynMark_str _cmXScoreDynMark_t _cmXScoreDynMarkArray[] = { - {"pppp-", 1, 1, -1, 3}, - {"pppp", 2, 1, 0, 10}, - {"pppp+", 3, 1, 1, 22}, - {"ppp-", 3, 2, -1, 22}, - {"ppp", 4, 2, 0, 29}, - {"ppp+", 5, 2, 1, 36}, - {"pp-", 5, 3, -1, 36}, - {"pp", 6, 3, 0, 43}, - {"pp+", 7, 3, 1, 50}, - {"p-", 7, 4, -1, 50}, - {"p", 8, 4, 0, 57}, - {"p+", 9, 4, 1, 64}, - {"mp-", 9, 5, -1, 64}, - {"mp", 10, 5, 0, 71}, - {"mp+", 11, 5, 1, 78}, - {"mf-", 11, 6, -1, 78}, - {"mf", 12, 6, 0, 85}, - {"mf+", 13, 6, 1, 92}, - {"f-", 13, 7, -1, 92}, - {"f", 14, 7, 0, 99}, - {"f+", 15, 7, 1, 106}, - {"ff", 16, 8, 0, 113}, - {"ff+", 17, 8, 1, 120}, - {"fff", 18, 9, 0, 127}, + {"s", 1, 0, 0, 1}, // silent note + {"pppp-", 2, 1, -1, 3}, + {"pppp", 3, 1, 0, 10}, + {"pppp+", 4, 1, 1, 22}, + {"ppp-", 4, 2, -1, 22}, + {"ppp", 5, 2, 0, 29}, + {"ppp+", 6, 2, 1, 36}, + {"pp-", 6, 3, -1, 36}, + {"pp", 7, 3, 0, 43}, + {"pp+", 8, 3, 1, 50}, + {"p-", 8, 4, -1, 50}, + {"p", 9, 4, 0, 57}, + {"p+", 10, 4, 1, 64}, + {"mp-", 10, 5, -1, 64}, + {"mp", 11, 5, 0, 71}, + {"mp+", 12, 5, 1, 78}, + {"mf-", 12, 6, -1, 78}, + {"mf", 13, 6, 0, 85}, + {"mf+", 14, 6, 1, 92}, + {"f-", 14, 7, -1, 92}, + {"f", 15, 7, 0, 99}, + {"f+", 16, 7, 1, 106}, + {"ff", 17, 8, 0, 113}, + {"ff+", 18, 8, 1, 120}, + {"fff", 19, 9, 0, 127}, {NULL,0,0,0,0} }; @@ -2139,6 +2139,7 @@ cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lin { switch(s[i]) { + case 's': case 'm': case 'p': case 'f': @@ -2478,6 +2479,12 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn ) } + // If reorder records remain to be processed + if( ri > 0 ) + if((rc = _cmXScoreReorderMeas(p, measNumb, rV, ri )) != kOkXsRC ) + goto errLabel; + + // the ticks may have changed so the 'secs' and 'dsecs' must be updated _cmXScoreSetAbsoluteTime( p ); @@ -3064,12 +3071,13 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const switch( np->flags & (kOnsetXsFl|kMetronomeXsFl|kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) ) { case kOnsetXsFl: - if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC - ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC ) { - rc = kMidiFailXsRC; + if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC + ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC ) + { + rc = kMidiFailXsRC; + } } - break; case kDampDnXsFl: From 3ef0c6fd3ae5256c7c15467ad51f93ee5d32fcf4 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 7 Sep 2016 17:35:01 -0400 Subject: [PATCH 15/31] cmMidiFile.h/c : In cmMIdiFileInsertTrack???() return the 'uid' of the new event record. --- cmMidiFile.c | 18 +++++++++++++----- cmMidiFile.h | 6 +++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cmMidiFile.c b/cmMidiFile.c index 06f07fc..a7aaf36 100644 --- a/cmMidiFile.c +++ b/cmMidiFile.c @@ -1368,10 +1368,13 @@ cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiBy } -cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg ) +cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg, unsigned* uidRef ) { _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h); + if( uidRef != NULL ) + *uidRef = cmInvalidId; + // validate the track index if( trkIdx >= p->trkN ) return cmErrMsg(&p->err,kInvalidTrkIndexMfRC,"The track index (%i) is invalid.",trkIdx); @@ -1395,6 +1398,9 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi memcpy((void*)m->u.voidPtr,msg->u.voidPtr,msg->byteCnt); } + if( uidRef != NULL ) + *uidRef = m->uid; + cmMidiTrackMsg_t* m0 = NULL; // msg before insertion cmMidiTrackMsg_t* m1 = p->trkV[trkIdx].base; // msg after insertion @@ -1448,12 +1454,14 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi p->trkV[trkIdx].cnt += 1; p->msgVDirtyFl = true; + + return kOkMfRC; } -cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 ) +cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1, unsigned* uidRef ) { cmMidiTrackMsg_t m; cmMidiChMsg_t cm; @@ -1472,10 +1480,10 @@ cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned assert( m.status >= kNoteOffMdId && m.status <= kPbendMdId ); - return cmMidiFileInsertTrackMsg(h,trkIdx,&m); + return cmMidiFileInsertTrackMsg(h,trkIdx,&m,uidRef); } -cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm ) +cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm, unsigned* uidRef ) { cmMidiTrackMsg_t m; @@ -1486,7 +1494,7 @@ cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsign m.metaId = kTempoMdId; m.u.iVal = 60000000/bpm; // convert BPM to microsPerQN - return cmMidiFileInsertTrackMsg(h,trkIdx,&m); + return cmMidiFileInsertTrackMsg(h,trkIdx,&m,uidRef); } diff --git a/cmMidiFile.h b/cmMidiFile.h index 55b8ef7..efc5976 100644 --- a/cmMidiFile.h +++ b/cmMidiFile.h @@ -185,9 +185,9 @@ extern "C" { // byteCnt - used to allocate storage for the data element in 'cmMidiTrackMsg_t.u' // u - the message data // - cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg ); - cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 ); - cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm ); + cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg, unsigned* uidRef ); + cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1, unsigned* uidRef ); + cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm, unsigned* uidRef ); // Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no // msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning From 84c5097a8db1db8ca3f8439b3c0ceeb573744908 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 7 Sep 2016 17:36:15 -0400 Subject: [PATCH 16/31] cmXScore.h/c : Added 'tied_dur' field to cmXsNote_t for use in determining the length of a MIDI note events. --- app/cmXScore.c | 94 ++++++++++++++++++++++++++++++-------------------- app/cmXScore.h | 3 +- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 4497431..9567c65 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -84,11 +84,13 @@ typedef struct cmXsNote_str unsigned staff; // 1=treble 2=bass unsigned tick; // unsigned duration; // duration in ticks + unsigned tied_dur; // duration in ticks (including all tied notes) double secs; // absolute time in seconds double dsecs; // delta time in seconds since previous event unsigned locIdx; // location index (chords share the same location index) double rvalue; // 1/rvalue = rythmic value (1/0.5 double whole 1/1 whole 1/2 half 1/4=quarter note, 1/8=eighth note, ...) const cmChar_t* tvalue; // text value + unsigned mf_uid; // MIDI file uid assigned to this event unsigned evenGroupId; // eveness group id unsigned dynGroupId; // dynamics group id @@ -239,8 +241,9 @@ cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, c n->mlink = note; } - note->voice = v; - note->uid = p->nextUid++; + note->voice = v; + note->uid = p->nextUid++; + note->tied_dur = note->duration; return kOkXsRC; } @@ -605,6 +608,7 @@ cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_ note->rvalue = rvalue; note->tvalue = tvalue; note->duration = duration; + note->tied_dur = duration; note->meas = meas; note->xmlNode = noteXmlNode; @@ -1065,6 +1069,7 @@ void _cmXScoreSpreadGraceNotes( cmXScore_t* p ) bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl ) { + cmXsNote_t* nbp = n0p; cmXsNote_t* nnp = n0p->slink; // begin w/ note following np unsigned measNumb = mp->number; cmChar_t acc = n0p->alter==-1?'b' : (n0p->alter==1?'#':' '); @@ -1093,9 +1098,11 @@ bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo // if this note is tied to the originating note (np) if( nnp->voice->id == n0p->voice->id && nnp->step == n0p->step && nnp->octave == n0p->octave ) { - nnp->flags |= kTieProcXsFl; - nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl); - n0p->tied = nnp; + nnp->flags |= kTieProcXsFl; + nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl); + n0p->tied = nnp; + nbp->tied_dur += nnp->duration; + nnp->tied_dur = 0; if( rptFl ) printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0)); @@ -1118,10 +1125,10 @@ bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo void _cmXScoreResolveTiesAndLoc( cmXScore_t* p ) { - unsigned n = 0; - unsigned m = 0; - bool rptFl = false; - cmXsPart_t* pp = p->partL; + unsigned n = 0; // count of notes which begin a tie + unsigned m = 0; // count of tied notes that are correctly terminated. + bool rptFl = false; + cmXsPart_t* pp = p->partL; // for each part for(; pp!=NULL; pp=pp->link) @@ -1227,7 +1234,7 @@ cmXsNote_t* _cmXScoreFindOverlappingNote( cmXScore_t* p, const cmXsNote_t* knp && cmIsFlag(np->flags,kOnsetXsFl) && knp->pitch == np->pitch && knp->tick >= np->tick - && knp->tick < (np->tick + np->duration) ) + && knp->tick < (np->tick + np->tied_dur) ) { return np; } @@ -1256,7 +1263,7 @@ void _cmXScoreProcessOverlappingNotes( cmXScore_t* p ) if( cmIsFlag(np->flags,kOnsetXsFl) && (fnp = _cmXScoreFindOverlappingNote(p,np)) != NULL) { // is np entirely contained inside fnp - bool embeddedFl = fnp->tick + fnp->duration > np->tick + np->duration; + bool embeddedFl = fnp->tick + fnp->tied_dur > np->tick + np->tied_dur; //printf("bar=%3i %4s voice:%2i %2i : %7i %7i : %7i %7i : %7i : %c \n",np->meas->number,cmMidiToSciPitch(np->pitch,NULL,0),np->voice->id,fnp->voice->id,fnp->tick,fnp->tick+fnp->duration,np->tick,np->tick+np->duration, (fnp->tick+fnp->duration) - np->tick, embeddedFl ? 'E' : 'O'); @@ -1270,14 +1277,14 @@ void _cmXScoreProcessOverlappingNotes( cmXScore_t* p ) } else { - int d = (fnp->tick+fnp->duration) - np->tick; + int d = (fnp->tick+fnp->tied_dur) - np->tick; // shorten the first note - if( d > 0 && d < fnp->duration ) - fnp->duration -= d; + if( d > 0 && d < fnp->tied_dur ) + fnp->tied_dur -= d; // move the second note just past it - np->tick = fnp->tick + fnp->duration + 1; + np->tick = fnp->tick + fnp->tied_dur + 1; } } } @@ -1404,7 +1411,7 @@ cmXsRC_t _cmXScoreWriteScorePlotFile( cmXScore_t* p, const cmChar_t* fn ) { if( cmIsFlag(np->flags,kMetronomeXsFl) ) { - double bps = np->duration / 60.0; + double bps = np->tied_dur / 60.0; // t b t // - = - - @@ -1418,7 +1425,7 @@ cmXsRC_t _cmXScoreWriteScorePlotFile( cmXScore_t* p, const cmChar_t* fn ) { onset_secs += (np->tick - tick0) / ticks_per_sec; tick0 = np->tick; - cmFilePrintf(fH,"n %f %f %i %s %s\n",onset_secs,np->duration/ticks_per_sec,np->uid,cmMidiToSciPitch(np->pitch,NULL,0),cmIsFlag(np->flags,kGraceXsFl)?"G":"N"); + cmFilePrintf(fH,"n %f %f %i %s %s\n",onset_secs,np->tied_dur/ticks_per_sec,np->uid,cmMidiToSciPitch(np->pitch,NULL,0),cmIsFlag(np->flags,kGraceXsFl)?"G":"N"); } } } @@ -1700,7 +1707,7 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) // set each grace note to have 1/20 of a second duration if( cmIsFlag(np->flags,kGraceXsFl) ) - np->duration = floor(ticksPerSec * graceDurSec); + np->duration = np->tied_dur = floor(ticksPerSec * graceDurSec); gN += 1; } @@ -2938,7 +2945,7 @@ void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index ) note->voice->id, note->locIdx, note->tick, - note->duration, + note->tied_dur, note->rvalue, N,B,R,G,D,C,e,d,t,P,s,S,H,T0,T1,O); @@ -3072,8 +3079,11 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const { case kOnsetXsFl: { - if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC - ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC ) + if( np->tied_dur <= 0 ) + cmErrWarnMsg(&p->err,kOkXsRC,"A zero length note was encountered bar:%i tick:%i %s",np->meas->number,np->tick,cmMidiToSciPitch(np->pitch,NULL,0)); + + if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel, &np->mf_uid ) != kOkMfRC + ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->tied_dur, kNoteOffMdId, np->pitch, 0, &np->mf_uid ) != kOkMfRC ) { rc = kMidiFailXsRC; } @@ -3084,9 +3094,12 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const case kDampUpDnXsFl: case kSostDnXsFl: { + if( np->duration <= 0 ) + cmErrWarnMsg(&p->err,kOkXsRC,"A zero length pedal event was encountered bar:%i tick:%i",np->meas->number,np->tick); + cmMidiByte_t d0 = cmIsFlag(np->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId; - if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127 ) != kOkMfRC ) - ||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0 ) != kOkMfRC ) ) + if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127, &np->mf_uid ) != kOkMfRC ) + ||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0, &np->mf_uid ) != kOkMfRC ) ) { rc = kMidiFailXsRC; } @@ -3094,7 +3107,7 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const break; case kMetronomeXsFl: - if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration ) != kOkMfRC ) + if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration, &np->mf_uid ) != kOkMfRC ) rc = kMidiFailXsRC; break; @@ -3141,6 +3154,7 @@ typedef struct cmXsSvgEvt_str unsigned voice; // score voice number unsigned d0; // MIDI d0 (barNumb) unsigned d1; // MIDI d1 + unsigned mf_uid; struct cmXsSvgEvt_str* link; } cmXsSvgEvt_t; @@ -3162,7 +3176,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con unsigned noteHeight = 10; cmChar_t* fn0 = cmMemAllocStr( fn ); const cmChar_t* svgFn = cmFsMakeFn(dir,fn0 = cmTextAppendSS(fn0,"_midi_svg"),"html",NULL); - const cmChar_t* cssFn = cmFsMakeFn(NULL,fn,"css",NULL); + const cmChar_t* cssFn = cmFsMakeFn(NULL,"score_midi_svg","css",NULL); cmChar_t* t0 = NULL; // temporary dynamic string cmMemFree(fn0); @@ -3193,8 +3207,12 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con if( cmSvgWriterRect(svgH, e->tick, e->d0 * noteHeight, e->durTicks, noteHeight-1, t0 ) != kOkSvgRC ) rc = kSvgFailXsRC; else - if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, cmMidiToSciPitch( e->d0, NULL, 0), "pitch") != kOkSvgRC ) + { + t0 = cmTsPrintfP(t0,"%s %i",cmMidiToSciPitch( e->d0, NULL, 0),e->mf_uid); + + if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, t0, "pitch") != kOkSvgRC ) rc = kSvgFailXsRC; + } } break; @@ -3241,7 +3259,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con } -void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 ) +void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1, unsigned mf_uid ) { cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1); e->flags = flags; @@ -3250,6 +3268,8 @@ void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsig e->voice = voice; e->d0 = d0; // note=pitch bar=number pedal=ctl# metronome=BPM e->d1 = d1; + e->mf_uid = mf_uid; + if( mf->eol != NULL ) mf->eol->link = e; else @@ -3286,7 +3306,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm if( cmIsFlag(note->flags,kMetronomeXsFl) ) { // set BPM as d0 - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0,note->mf_uid); continue; } @@ -3295,21 +3315,21 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm if( cmIsFlag(note->flags,kOnsetXsFl) ) { unsigned d0 = cmSciPitchToMidiPitch( note->step, note->alter, note->octave ); - unsigned durTick = note->duration; + unsigned durTick = note->tied_dur; if( note->tied != NULL ) { cmXsNote_t* tn = note->tied; for(; tn!=NULL; tn=tn->tied) - durTick += tn->duration; + durTick += tn->tied_dur; } - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel,note->mf_uid); continue; } // if this is a bar event if( cmIsFlag(note->flags,kBarXsFl) ) { - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0, note->mf_uid); continue; } @@ -3317,7 +3337,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) ) { unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId; - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127,note->mf_uid); continue; } @@ -3353,7 +3373,7 @@ cmXsRC_t cmXScoreTest( // assign durations to pedal down events _cmXScoreProcessPedals(_cmXScoreHandleToPtr(h)); - // remove some notes which share a pitch which are overlapped or embedded + // remove some notes which share a pitch and are overlapped or embedded within another note. _cmXScoreProcessOverlappingNotes(_cmXScoreHandleToPtr(h)); if( csvOutFn != NULL ) @@ -3381,16 +3401,16 @@ cmXsRC_t cmXScoreTest( if( midiOutFn != NULL ) { cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn); - - _cmXScoreGenSvg( ctx, h, pp->dirStr, pp->fnStr ); _cmXsWriteMidiFile(ctx, h, pp->dirStr, pp->fnStr ); + + _cmXScoreGenSvg( ctx, h, pp->dirStr, pp->fnStr ); cmFsFreePathParts(pp); } - //cmXScoreReport(h,&ctx->rpt,true); + cmXScoreReport(h,&ctx->rpt,true); errLabel: return cmXScoreFinalize(&h); diff --git a/app/cmXScore.h b/app/cmXScore.h index 1c47f38..7359b89 100644 --- a/app/cmXScore.h +++ b/app/cmXScore.h @@ -19,7 +19,8 @@ extern "C" { kMidiFailXsRC, kFileFailXsRC, kSvgFailXsRC, - kOverlapWarnXsRC + kOverlapWarnXsRC, + kZeroLengthEventXsRC }; typedef cmRC_t cmXsRC_t; From 1a042843c8f8c1b7d0c96646dd45a6668c5c7d5c Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 8 Sep 2016 16:17:15 -0400 Subject: [PATCH 17/31] cmXScore.c : Added error checking to section marker text-to-number conversion. --- app/cmXScore.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 9567c65..3b72e99 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -1376,8 +1376,18 @@ void _cmXScoreSetMeasGroups( cmXScore_t* p, unsigned flag ) } if( cmIsFlag(np->flags,kSectionXsFl) ) - sectionId = np->tvalue==NULL ? 0 : strtol(np->tvalue,NULL,10); - + { + sectionId = 0; + + if( np->tvalue == NULL ) + cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"An apparent section label in measure '%i' is blank.",np->meas->number); + else + if( cmTextToUInt( np->tvalue, §ionId, NULL ) != kOkTxRC ) + cmErrWarnMsg(&p->err,kSyntaxErrorXsRC,"The section label '%s' is not an integer.",np->tvalue); + + //sectionId = np->tvalue==NULL ? 0 : strtol(np->tvalue,NULL,10); + } + n0 = NULL; } } From bd63a0eeebf51d10d2f803fa209cd295d551fffa Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 14 Sep 2016 13:47:19 -0400 Subject: [PATCH 18/31] cmScore.h/c : Added cmScoreReport(). Added 'section' number to report output. --- app/cmScore.c | 36 ++++++++++++++++++++++++++++++------ app/cmScore.h | 6 +++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/app/cmScore.c b/app/cmScore.c index d7ca324..a01eb4a 100644 --- a/app/cmScore.c +++ b/app/cmScore.c @@ -2417,6 +2417,16 @@ cmScRC_t cmScoreDecode( const void* msg, unsigned msgByteCnt, cmScMsg_t* m) return kOkScRC; } +const cmChar_t* _cmScoreSectionLabel( cmSc_t* p, const cmScoreEvt_t* r ) +{ + unsigned i; + for(i=0; isectCnt; ++i) + if( p->sect[i].locPtr != NULL && p->sect[i].locPtr->index == r->locIdx && p->sect[i].begEvtIndex == r->index ) + return p->sect[i].label; + + return NULL; +} + void _cmScorePrintHdr( cmRpt_t* rpt ) { cmRptPrintf(rpt,"evnt CSV bar\n"); @@ -2424,12 +2434,13 @@ void _cmScorePrintHdr( cmRpt_t* rpt ) cmRptPrintf(rpt,"----- ----- ----- --- --- ----- ----- --- -------\n"); } -void _cmScorePrintEvent( const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt ) +void _cmScorePrintEvent( cmSc_t* p, const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt ) { + bool eolFl = true; switch(r->type) { case kBarEvtScId: - cmRptPrintf(rpt,"%5i %5i %3i bar\n", + cmRptPrintf(rpt,"%5i %5i %3i bar ", i, r->line, r->barNumb ); @@ -2437,7 +2448,7 @@ void _cmScorePrintEvent( const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt ) case kPedalEvtScId: case kNonEvtScId: - cmRptPrintf(rpt,"%5i %5i %5i %3i %3i %s %5s %c%c%c %s\n", + cmRptPrintf(rpt,"%5i %5i %5i %3i %3i %s %5s %c%c%c %-7s ", i, r->line, r->locIdx, @@ -2448,12 +2459,20 @@ void _cmScorePrintEvent( const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt ) cmIsFlag(r->flags,kEvenScFl) ? 'e' : ' ', cmIsFlag(r->flags,kTempoScFl) ? 't' : ' ', cmIsFlag(r->flags,kDynScFl) ? 'd' : ' ', + //cmIsFlag(r->flags,kDynScFl) ? 7-strlen(cmScDynIdToLabel(r->dynVal)) : 7, cmIsFlag(r->flags,kDynScFl) ? cmScDynIdToLabel(r->dynVal) : ""); break; default: + eolFl = false; break; } + + const cmChar_t* sectionLabel; + if((sectionLabel = _cmScoreSectionLabel(p,r)) != NULL ) + cmRptPrintf(rpt,"section:%s ",sectionLabel); + + cmRptPrintf(rpt,"\n"); } @@ -2466,7 +2485,7 @@ void cmScorePrint( cmScH_t h, cmRpt_t* rpt ) _cmScorePrintHdr(rpt); for(i=0; icnt; ++i) - _cmScorePrintEvent(p->array+i,i,rpt); + _cmScorePrintEvent(p,p->array+i,i,rpt); } @@ -2491,7 +2510,7 @@ void cmScorePrintSets( cmScH_t h, cmRpt_t* rpt ) _cmScorePrintHdr(rpt); for(j=0; jeleCnt; ++j) - _cmScorePrintEvent(*s->eleArray+j,j,rpt); + _cmScorePrintEvent(p,*s->eleArray+j,j,rpt); cmRptPrintf(rpt,"Targets Section: "); for(j=0; jsectCnt; ++j) @@ -2717,7 +2736,7 @@ cmScRC_t cmScoreFileFromMidi( cmCtx_t* ctx, const cmChar_t* midiFn, const c } -void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn ) +void cmScoreReport( cmCtx_t* ctx, const cmChar_t* fn ) { cmScH_t h = cmScNullHandle; if( cmScoreInitialize(ctx,&h,fn,0,NULL,0,NULL,NULL, cmSymTblNullHandle ) != kOkScRC ) @@ -2728,6 +2747,11 @@ void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn ) cmScoreFinalize(&h); } +void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn ) +{ +} + + // 1. Fix absolute message time which was incorrect on original score file. // 2. void cmScoreFix( cmCtx_t* ctx ) diff --git a/app/cmScore.h b/app/cmScore.h index 8b94537..f41965a 100644 --- a/app/cmScore.h +++ b/app/cmScore.h @@ -280,8 +280,12 @@ extern "C" { // Generate a new score file from a MIDI file. cmScRC_t cmScoreFileFromMidi( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* scoreFn ); - void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn ); + // Print open the score file 'fn' and report the contents. This function + // simply wraps calls to cmScoreInitialize() and cmScorePrint(). + void cmScoreReport( cmCtx_t* ctx, const cmChar_t* fn ); + void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn ); + //) #ifdef __cplusplus From d14744ed4d333bd55262594f3088fa10b9cdba33 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 14 Sep 2016 15:15:04 -0400 Subject: [PATCH 19/31] cmXScore.c : Removed use of MIDI file event UID's. --- app/cmXScore.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 3b72e99..3a23dc6 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -90,7 +90,6 @@ typedef struct cmXsNote_str unsigned locIdx; // location index (chords share the same location index) double rvalue; // 1/rvalue = rythmic value (1/0.5 double whole 1/1 whole 1/2 half 1/4=quarter note, 1/8=eighth note, ...) const cmChar_t* tvalue; // text value - unsigned mf_uid; // MIDI file uid assigned to this event unsigned evenGroupId; // eveness group id unsigned dynGroupId; // dynamics group id @@ -3092,8 +3091,8 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const if( np->tied_dur <= 0 ) cmErrWarnMsg(&p->err,kOkXsRC,"A zero length note was encountered bar:%i tick:%i %s",np->meas->number,np->tick,cmMidiToSciPitch(np->pitch,NULL,0)); - if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel, &np->mf_uid ) != kOkMfRC - ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->tied_dur, kNoteOffMdId, np->pitch, 0, &np->mf_uid ) != kOkMfRC ) + if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC + ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->tied_dur, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC ) { rc = kMidiFailXsRC; } @@ -3108,8 +3107,8 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmErrWarnMsg(&p->err,kOkXsRC,"A zero length pedal event was encountered bar:%i tick:%i",np->meas->number,np->tick); cmMidiByte_t d0 = cmIsFlag(np->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId; - if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127, &np->mf_uid ) != kOkMfRC ) - ||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0, &np->mf_uid ) != kOkMfRC ) ) + if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127 ) != kOkMfRC ) + ||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0 ) != kOkMfRC ) ) { rc = kMidiFailXsRC; } @@ -3117,7 +3116,7 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const break; case kMetronomeXsFl: - if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration, &np->mf_uid ) != kOkMfRC ) + if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration ) != kOkMfRC ) rc = kMidiFailXsRC; break; @@ -3164,7 +3163,6 @@ typedef struct cmXsSvgEvt_str unsigned voice; // score voice number unsigned d0; // MIDI d0 (barNumb) unsigned d1; // MIDI d1 - unsigned mf_uid; struct cmXsSvgEvt_str* link; } cmXsSvgEvt_t; @@ -3218,7 +3216,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con rc = kSvgFailXsRC; else { - t0 = cmTsPrintfP(t0,"%s %i",cmMidiToSciPitch( e->d0, NULL, 0),e->mf_uid); + t0 = cmTsPrintfP(t0,"%s",cmMidiToSciPitch( e->d0, NULL, 0)); if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, t0, "pitch") != kOkSvgRC ) rc = kSvgFailXsRC; @@ -3269,7 +3267,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con } -void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1, unsigned mf_uid ) +void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 ) { cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1); e->flags = flags; @@ -3278,7 +3276,6 @@ void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsig e->voice = voice; e->d0 = d0; // note=pitch bar=number pedal=ctl# metronome=BPM e->d1 = d1; - e->mf_uid = mf_uid; if( mf->eol != NULL ) mf->eol->link = e; @@ -3316,7 +3313,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm if( cmIsFlag(note->flags,kMetronomeXsFl) ) { // set BPM as d0 - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0,note->mf_uid); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0); continue; } @@ -3332,14 +3329,14 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm for(; tn!=NULL; tn=tn->tied) durTick += tn->tied_dur; } - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel,note->mf_uid); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel); continue; } // if this is a bar event if( cmIsFlag(note->flags,kBarXsFl) ) { - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0, note->mf_uid); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0); continue; } @@ -3347,7 +3344,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) ) { unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId; - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127,note->mf_uid); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127); continue; } From 19b90719aa85f1849720448db4fe1cf58e39fb56 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 14 Sep 2016 15:15:48 -0400 Subject: [PATCH 20/31] cmMidiFile.c : Removed UID return value from cmMidiFileInsertTrackMsg(). --- cmMidiFile.c | 16 +++++----------- cmMidiFile.h | 6 +++--- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/cmMidiFile.c b/cmMidiFile.c index a7aaf36..fb26f63 100644 --- a/cmMidiFile.c +++ b/cmMidiFile.c @@ -1368,13 +1368,10 @@ cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiBy } -cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg, unsigned* uidRef ) +cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg ) { _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h); - if( uidRef != NULL ) - *uidRef = cmInvalidId; - // validate the track index if( trkIdx >= p->trkN ) return cmErrMsg(&p->err,kInvalidTrkIndexMfRC,"The track index (%i) is invalid.",trkIdx); @@ -1398,9 +1395,6 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi memcpy((void*)m->u.voidPtr,msg->u.voidPtr,msg->byteCnt); } - if( uidRef != NULL ) - *uidRef = m->uid; - cmMidiTrackMsg_t* m0 = NULL; // msg before insertion cmMidiTrackMsg_t* m1 = p->trkV[trkIdx].base; // msg after insertion @@ -1461,7 +1455,7 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi } -cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1, unsigned* uidRef ) +cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 ) { cmMidiTrackMsg_t m; cmMidiChMsg_t cm; @@ -1480,10 +1474,10 @@ cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned assert( m.status >= kNoteOffMdId && m.status <= kPbendMdId ); - return cmMidiFileInsertTrackMsg(h,trkIdx,&m,uidRef); + return cmMidiFileInsertTrackMsg(h,trkIdx,&m); } -cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm, unsigned* uidRef ) +cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm ) { cmMidiTrackMsg_t m; @@ -1494,7 +1488,7 @@ cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsign m.metaId = kTempoMdId; m.u.iVal = 60000000/bpm; // convert BPM to microsPerQN - return cmMidiFileInsertTrackMsg(h,trkIdx,&m,uidRef); + return cmMidiFileInsertTrackMsg(h,trkIdx,&m); } diff --git a/cmMidiFile.h b/cmMidiFile.h index efc5976..55b8ef7 100644 --- a/cmMidiFile.h +++ b/cmMidiFile.h @@ -185,9 +185,9 @@ extern "C" { // byteCnt - used to allocate storage for the data element in 'cmMidiTrackMsg_t.u' // u - the message data // - cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg, unsigned* uidRef ); - cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1, unsigned* uidRef ); - cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm, unsigned* uidRef ); + cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg ); + cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 ); + cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm ); // Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no // msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning From 780abd5e74f88583e1b83670dacec9edc573efbe Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 14 Sep 2016 15:47:13 -0400 Subject: [PATCH 21/31] cmMidiFile.h/c : Added cmMidiFileNoteTimeDensity(). --- cmMidiFile.c | 32 ++++++++++++++++++++++++++++++++ cmMidiFile.h | 13 ++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cmMidiFile.c b/cmMidiFile.c index fb26f63..8a18e8a 100644 --- a/cmMidiFile.c +++ b/cmMidiFile.c @@ -1907,6 +1907,38 @@ void cmMidiFilePrintTracks( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt ) void cmMidiFileTestPrint( void* printDataPtr, const char* fmt, va_list vl ) { vprintf(fmt,vl); } +cmMidiFileDensity_t* cmMidiFileNoteDensity( cmMidiFileH_t h, unsigned* cntRef ) +{ + int msgN = cmMidiFileMsgCount(h); + const cmMidiTrackMsg_t** msgs = cmMidiFileMsgArray(h); + cmMidiFileDensity_t* dV = cmMemAllocZ(cmMidiFileDensity_t,msgN); + + int i,j,k; + for(i=0,k=0; istatus == kNoteOnMdId && msgs[i]->u.chMsgPtr->d1 > 0 ) + { + dV[k].uid = msgs[i]->uid; + dV[k].amicro = msgs[i]->amicro; + + for(j=i; j>=0; --j) + { + if( msgs[i]->amicro - msgs[j]->amicro > 1000000 ) + break; + + dV[k].density += 1; + } + + k += 1; + + } + + if( cntRef != NULL ) + *cntRef = k; + + return dV; +} + + cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn ) { cmMfRC_t rc = kOkMfRC; diff --git a/cmMidiFile.h b/cmMidiFile.h index 55b8ef7..d54d1be 100644 --- a/cmMidiFile.h +++ b/cmMidiFile.h @@ -212,11 +212,22 @@ extern "C" { void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt ); void cmMidiFilePrintTrack( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt ); - void cmMidiFileTest( const char* fn, cmCtx_t* ctx ); + + typedef struct + { + unsigned uid; + unsigned long long amicro; + unsigned density; + + } cmMidiFileDensity_t; + cmMidiFileDensity_t* cmMidiFileNoteTimeDensity( cmMidiFileH_t h, unsigned* cntRef ); // Generate a piano-roll plot description file which can be displayed with cmXScore.m cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn ); + void cmMidiFileTest( const char* fn, cmCtx_t* ctx ); + + //) From 88fdfd998d734a927b7b095e9f4dfb587f484ed7 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 14 Sep 2016 18:11:00 -0400 Subject: [PATCH 22/31] cmSvgWriter.c : Improved formatting of HTML content. --- cmSvgWriter.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cmSvgWriter.c b/cmSvgWriter.c index cfb630e..28aa541 100644 --- a/cmSvgWriter.c +++ b/cmSvgWriter.c @@ -199,19 +199,19 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* cmChar_t* s1 = NULL; cmChar_t hdr[] = - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n"; From a37e2e2c71e9f288dbc7def67a223494de673f9e Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 14 Sep 2016 18:11:30 -0400 Subject: [PATCH 23/31] cmMidiFile.h/c : Added cmMidiFileGenSvgFile() --- cmMidiFile.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ cmMidiFile.h | 10 ++++-- 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/cmMidiFile.c b/cmMidiFile.c index 8a18e8a..ce6e6e8 100644 --- a/cmMidiFile.c +++ b/cmMidiFile.c @@ -8,8 +8,11 @@ #include "cmMallocDebug.h" #include "cmLinkedHeap.h" #include "cmTime.h" +#include "cmText.h" #include "cmMidi.h" #include "cmMidiFile.h" +#include "cmSvgWriter.h" + #ifdef cmBIG_ENDIAN #define mfSwap16(v) (v) @@ -1975,6 +1978,99 @@ cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmCh return rc; } +cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outSvgFn, const cmChar_t* cssFn ) +{ + cmMfRC_t rc = kOkMfRC; + cmSvgH_t svgH = cmSvgNullHandle; + cmMidiFileH_t mfH = cmMidiFileNullHandle; + unsigned msgN = 0; + const cmMidiTrackMsg_t** msgs = NULL; + unsigned noteHeight = 10; + double micros_per_sec = 1000.0; + unsigned i; + + if((rc = cmMidiFileOpen(ctx,&mfH,midiFn)) != kOkMfRC ) + { + rc = cmErrMsg(&ctx->err,rc,"Unable to open the MIDI file '%s'.",cmStringNullGuard(midiFn)); + goto errLabel; + } + + cmMidiFileCalcNoteDurations( mfH ); + + msgN = cmMidiFileMsgCount(mfH); + msgs = cmMidiFileMsgArray(mfH); + + + if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC ) + { + rc = cmErrMsg(&ctx->err,kSvgFailMfRC,"Unable to create the MIDI SVG output file '%s'.",cmStringNullGuard(outSvgFn)); + goto errLabel; + } + + + for(i=0; istatus == kNoteOnMdId && msgs[i]->u.chMsgPtr->d1 > 0 ) + { + const cmMidiTrackMsg_t* m = msgs[i]; + + + if( cmSvgWriterRect(svgH, m->amicro/micros_per_sec, m->u.chMsgPtr->d0 * noteHeight, m->u.chMsgPtr->durMicros/micros_per_sec, noteHeight-1, "note" ) != kOkSvgRC ) + rc = kSvgFailMfRC; + + const cmChar_t* t0 = cmMidiToSciPitch(m->u.chMsgPtr->d0,NULL,0); + + if( cmSvgWriterText(svgH, (m->amicro + (m->u.chMsgPtr->durMicros/2)) / micros_per_sec, m->u.chMsgPtr->d0 * noteHeight, t0, "text" ) != kOkSvgRC ) + rc = kSvgFailMfRC; + + } + + if( rc != kOkMfRC ) + { + cmErrMsg(&ctx->err,rc,"SVG Shape insertion failed."); + goto errLabel; + } + + unsigned dN = 0; + cmMidiFileDensity_t* dV = cmMidiFileNoteDensity( mfH, &dN ); + double t0 = 0; + double y0 = 64.0; + cmChar_t* tx = NULL; + + for(i=0; ierr,kUidNotFoundMfRC,"The MIDI msg form UID:%i was not found.",dV[i].uid); + else + { + double t1 = m->amicro / micros_per_sec; + double y1 = dV[i].density * noteHeight; + + cmSvgWriterLine(svgH, t0, y0, t1, y1, "density" ); + cmSvgWriterText(svgH, t1, y1, tx = cmTsPrintfP(tx,"%i",dV[i].density),"dtext"); + + t0 = t1; + y0 = y1; + + } + } + + cmMemFree(dV); + cmMemFree(tx); + + if( rc == kOkMfRC ) + if( cmSvgWriterWrite(svgH,cssFn,outSvgFn) != kOkSvgRC ) + rc = cmErrMsg(&ctx->err,kSvgFailMfRC,"SVG file write to '%s' failed.",cmStringNullGuard(outSvgFn)); + + + errLabel: + cmMidiFileClose(&mfH); + cmSvgWriterFree(&svgH); + + return rc; +} + void cmMidiFilePrintControlNumbers( cmCtx_t* ctx, const char* fn ) { cmMidiFileH_t h = cmMidiFileNullHandle; diff --git a/cmMidiFile.h b/cmMidiFile.h index d54d1be..f77e51a 100644 --- a/cmMidiFile.h +++ b/cmMidiFile.h @@ -117,7 +117,8 @@ extern "C" { kLargeDeltaTickMfRC, // 12 (a large delta tick value was filtered) kUidNotFoundMfRC, // 13 kUidNotANoteMsgMfRC, // 14 - kInvalidTrkIndexMfRC // 15 + kInvalidTrkIndexMfRC,// 15 + kSvgFailMfRC // 16 }; extern cmMidiFileH_t cmMidiFileNullHandle; @@ -220,11 +221,16 @@ extern "C" { unsigned density; } cmMidiFileDensity_t; - cmMidiFileDensity_t* cmMidiFileNoteTimeDensity( cmMidiFileH_t h, unsigned* cntRef ); + + // Generate the note onset density measure for each note in the MIDI file. + // Delete the returned memory with a call to cmMemFree(). + cmMidiFileDensity_t* cmMidiFileNoteDensity( cmMidiFileH_t h, unsigned* cntRef ); // Generate a piano-roll plot description file which can be displayed with cmXScore.m cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn ); + cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outSvgFn, const cmChar_t* cssFn ); + void cmMidiFileTest( const char* fn, cmCtx_t* ctx ); From cc334c8d7f37afa6a5e261b11ed8f783da42362b Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 15 Sep 2016 16:55:14 -0400 Subject: [PATCH 24/31] cmXScore.c : Added cmXsComplexity_t related functions and added complexity measure output to MIDI file SVG plot. --- app/cmXScore.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 288 insertions(+), 8 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 3a23dc6..532f3c3 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -71,6 +71,16 @@ enum struct cmXsMeas_str; struct cmXsVoice_str; +typedef struct cmXsComplexity_str +{ + unsigned sum_d_vel; + unsigned sum_d_rym; + unsigned sum_d_lpch; + unsigned sum_n_lpch; + unsigned sum_d_rpch; + unsigned sum_n_rpch; +} cmXsComplexity_t; + typedef struct cmXsNote_str { unsigned uid; // unique id of this note record @@ -107,6 +117,8 @@ typedef struct cmXsNote_str struct cmXsNote_str* mlink; // measure note list struct cmXsNote_str* slink; // time sorted event list + cmXsComplexity_t cplx; + } cmXsNote_t; typedef struct cmXsVoice_str @@ -157,6 +169,8 @@ typedef struct cmXsSpan_t* spanL; unsigned nextUid; + + cmXsComplexity_t cplx_max; } cmXScore_t; cmXScore_t* _cmXScoreHandleToPtr( cmXsH_t h ) @@ -3050,6 +3064,200 @@ void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl ) } } +typedef struct +{ + unsigned ival; + double fval; + unsigned cnt; +} cmXsHist_t; + +void _cmXsHistUpdateI( cmXsHist_t* hist, unsigned histN, unsigned ival ) +{ + unsigned i; + + for(i=0; i0; ++i) + n += 1; + + return n; +} + +const cmXsNote_t* _cmXsMeasComplexityInWindow( const cmXsNote_t* n0, cmXsNote_t* n1, double wndSecs ) +{ + const cmXsNote_t* n2 = NULL; + unsigned l_pch_0 = 0; + unsigned l_pch_value = 0; + unsigned l_pch_cnt = 0; + unsigned r_pch_0 = 0; + unsigned r_pch_value = 0; + unsigned r_pch_cnt = 0; + unsigned i = 0; + + + unsigned histN = 100; + cmXsHist_t velHist[ histN ]; + cmXsHist_t rymHist[ histN ]; + + memset(velHist,0,sizeof(velHist)); + memset(rymHist,0,sizeof(rymHist)); + + const cmXsNote_t* n = n0; + + while(n!=NULL && n != n1) + { + // if this event is less than wndSecs behind 'n1' and is not a sounding note ... + if( n1->secs - n->secs <= wndSecs && cmIsFlag(n->flags,kOnsetXsFl) ) + { + // if this is not the first note in the window + if( i > 0 ) + { + _cmXsHistUpdateI( velHist, histN, n->dynamics ); + _cmXsHistUpdateF( rymHist, histN, n->rvalue ); + + switch( n->staff ) + { + case 1: + r_pch_value += r_pch_0 > n->pitch ? r_pch_0-n->pitch : n->pitch-r_pch_0; + r_pch_cnt += 1; + break; + + case 2: + l_pch_value += l_pch_0 > n->pitch ? l_pch_0-n->pitch : n->pitch-l_pch_0; + r_pch_cnt += 1; + break; + + default: + { assert(0); } + } + } + + // store the pitch values to compare on the next interation + switch( n->staff ) + { + case 1: + r_pch_0 = n->pitch; + break; + + case 2: + l_pch_0 = n->pitch; + break; + + default: + { assert(0); } + } + + // track the first note that is inside the window + if( i == 0 ) + n2 = n; + + // count the number of notes in the window + i += 1; + + } + + cmXsMeas_t* m = n->meas; + + // advance th note pointer + n = n->slink; + + // if we have reached the end of a measure + if( n == NULL ) + { + if( m != NULL ) + { + m = m->link; + if( m != NULL ) + n = m->noteL; + } + } + + } + + n1->cplx.sum_d_vel = _cmXsHistValue( velHist, histN ); + n1->cplx.sum_d_rym = _cmXsHistValue( rymHist, histN ); + n1->cplx.sum_d_lpch = l_pch_value; + n1->cplx.sum_n_lpch = l_pch_cnt; + n1->cplx.sum_d_rpch = r_pch_value; + n1->cplx.sum_n_rpch = r_pch_cnt; + + return n2; +} + +cmXsRC_t _cmXsMeasComplexity( cmXsH_t h, double wndSecs ) +{ + cmXsRC_t rc = kOkXsRC; + cmXScore_t* p = _cmXScoreHandleToPtr(h); + cmXsPart_t* pp = p->partL; + + memset(&p->cplx_max,0,sizeof(p->cplx_max)); + + const cmXsNote_t* n0 = NULL; + + // for each part + for(; pp!=NULL; pp=pp->link) + { + cmXsMeas_t* mp = pp->measL; + + // for each measure + for(; mp!=NULL; mp=mp->link) + { + cmXsNote_t* n1 = mp->noteL; + + + // for each note in this measure + for(; n1!=NULL; n1=n1->slink) + if( cmIsFlag(n1->flags,kOnsetXsFl) ) + { + if( n0 == NULL ) + n0 = n1; + else + if((n0 = _cmXsMeasComplexityInWindow(n0,n1,wndSecs)) == NULL ) + n0 = n1; + + // track the max value for all complexity values to allow + // eventual normalization of the complexity values + p->cplx_max.sum_d_vel = cmMax(p->cplx_max.sum_d_vel, n1->cplx.sum_d_vel); + p->cplx_max.sum_d_rym = cmMax(p->cplx_max.sum_d_rym, n1->cplx.sum_d_rym); + p->cplx_max.sum_d_lpch = cmMax(p->cplx_max.sum_d_lpch,n1->cplx.sum_d_lpch); + p->cplx_max.sum_n_lpch = cmMax(p->cplx_max.sum_n_lpch,n1->cplx.sum_n_lpch); + p->cplx_max.sum_d_rpch = cmMax(p->cplx_max.sum_d_rpch,n1->cplx.sum_d_rpch); + p->cplx_max.sum_n_rpch = cmMax(p->cplx_max.sum_n_rpch,n1->cplx.sum_n_rpch); + + } + } + } + return rc; +} + cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn ) { cmXsRC_t rc = kOkXsRC; @@ -3163,6 +3371,7 @@ typedef struct cmXsSvgEvt_str unsigned voice; // score voice number unsigned d0; // MIDI d0 (barNumb) unsigned d1; // MIDI d1 + cmXsComplexity_t cplx; struct cmXsSvgEvt_str* link; } cmXsSvgEvt_t; @@ -3176,6 +3385,44 @@ typedef struct cmXsMidiFile_str } cmXsMidiFile_t; + +void _cmXsWriteMidiSvgLegend( cmSvgH_t svgH, unsigned index, const cmChar_t* label, const cmChar_t* classStr ) +{ + double x = 100; + double y = 120*10 - 20*index; + + cmSvgWriterText( svgH, x, y, label, "legend" ); + + x += 75; + cmSvgWriterLine( svgH, x, y, x+125, y, classStr ); +} + +void _cmXsWriteMidiSvgLinePoint( cmSvgH_t svgH, double x0, double y0, double x1, double y1, double y_max, const cmChar_t* classStr, const cmChar_t* label ) +{ + int bn = 255; + char b[bn+1]; + double y_scale = 10; + double y_label = y1; + + b[0] = 0; + y0 = (y0/y_max) * 127.0 * y_scale; + y1 = (y1/y_max) * 127.0 * y_scale; + + cmSvgWriterLine(svgH, x0, y0, x1, y1, classStr ); + + if( y0 != y1 ) + snprintf(b,bn,"%5.0f %s",y_label,label==NULL?"":label); + else + { + if( label != NULL ) + snprintf(b,bn,"%s",label); + } + + if( strlen(b) ) + cmSvgWriterText(svgH, x1, y1, b, "pt_text"); + +} + cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, const cmChar_t* dir, const cmChar_t* fn ) { cmXsRC_t rc = kOkXsRC; @@ -3186,7 +3433,8 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con const cmChar_t* svgFn = cmFsMakeFn(dir,fn0 = cmTextAppendSS(fn0,"_midi_svg"),"html",NULL); const cmChar_t* cssFn = cmFsMakeFn(NULL,"score_midi_svg","css",NULL); cmChar_t* t0 = NULL; // temporary dynamic string - + unsigned i = 0; + const cmXsSvgEvt_t* e0 = NULL; cmMemFree(fn0); // create the SVG writer @@ -3196,6 +3444,13 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con goto errLabel; } + _cmXsWriteMidiSvgLegend( svgH, 0, "Velocity", "cplx_vel" ); + _cmXsWriteMidiSvgLegend( svgH, 1, "Duration", "cplx_rym" ); + _cmXsWriteMidiSvgLegend( svgH, 2, "Left Pitch", "cplx_lpch" ); + _cmXsWriteMidiSvgLegend( svgH, 3, "Right Pitch", "cplx_rpch" ); + _cmXsWriteMidiSvgLegend( svgH, 4, "Density", "cplx_density" ); + + // for each MIDI file element for(; e!=NULL && rc==kOkXsRC; e=e->link) { @@ -3220,7 +3475,25 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, t0, "pitch") != kOkSvgRC ) rc = kSvgFailXsRC; + else + { + if( e0 != NULL ) + { + bool fl = (i % 10) == 0; + + _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_vel, e->tick, e->cplx.sum_d_vel, p->cplx_max.sum_d_vel, "cplx_vel",fl?"V":NULL); + _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_rym, e->tick, e->cplx.sum_d_rym, p->cplx_max.sum_d_rym, "cplx_rym",fl?"D":NULL); + _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_lpch, e->tick, e->cplx.sum_d_lpch, p->cplx_max.sum_d_lpch, "cplx_lpch",fl?"L":NULL); + _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_rpch, e->tick, e->cplx.sum_d_rpch, p->cplx_max.sum_d_rpch, "cplx_rpch",fl?"R":NULL); + _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_n_lpch + e0->cplx.sum_n_rpch, e->tick, e->cplx.sum_n_lpch + e->cplx.sum_n_rpch, p->cplx_max.sum_n_lpch + p->cplx_max.sum_n_rpch, "cplx_density",fl?"N":NULL); + } + + e0 = e; + } + } + + i+=1; } break; @@ -3247,7 +3520,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con cmSvgWriterRect(svgH, e->tick, y, e->durTicks, noteHeight-1, classLabel); } break; - } + } } if( rc != kOkXsRC ) @@ -3267,7 +3540,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con } -void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 ) +void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1, const cmXsComplexity_t* cplx ) { cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1); e->flags = flags; @@ -3277,6 +3550,9 @@ void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsig e->d0 = d0; // note=pitch bar=number pedal=ctl# metronome=BPM e->d1 = d1; + if( cplx != NULL ) + e->cplx = *cplx; + if( mf->eol != NULL ) mf->eol->link = e; else @@ -3313,7 +3589,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm if( cmIsFlag(note->flags,kMetronomeXsFl) ) { // set BPM as d0 - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0,NULL); continue; } @@ -3329,14 +3605,14 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm for(; tn!=NULL; tn=tn->tied) durTick += tn->tied_dur; } - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel,¬e->cplx); continue; } // if this is a bar event if( cmIsFlag(note->flags,kBarXsFl) ) { - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0,NULL); continue; } @@ -3344,7 +3620,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) ) { unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId; - _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127); + _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127,NULL); continue; } @@ -3407,6 +3683,10 @@ cmXsRC_t cmXScoreTest( if( midiOutFn != NULL ) { + double wndSecs = 1.0; + _cmXsMeasComplexity(h,wndSecs); + + cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn); _cmXsWriteMidiFile(ctx, h, pp->dirStr, pp->fnStr ); @@ -3417,7 +3697,7 @@ cmXsRC_t cmXScoreTest( } - cmXScoreReport(h,&ctx->rpt,true); + //cmXScoreReport(h,&ctx->rpt,true); errLabel: return cmXScoreFinalize(&h); From b43b6af50992c8f6c382341033bf49e7847d86b6 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 15 Sep 2016 16:55:58 -0400 Subject: [PATCH 25/31] cmSvgWriter.c : Removed pan-zoom code from HTML header. --- cmSvgWriter.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cmSvgWriter.c b/cmSvgWriter.c index 28aa541..7845955 100644 --- a/cmSvgWriter.c +++ b/cmSvgWriter.c @@ -185,7 +185,14 @@ void _cmSvgWriterFlipY( cmSvg_t* p, unsigned height ) } } - +/* + "\n" + "\n" + + */ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* outFn ) { @@ -204,11 +211,6 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* "\n" "\n" "\n" - "\n" - "\n" "\n" "\n" "\n"; From 7eac59b3cae889287bebecbddb0538c83800c3c9 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 15 Sep 2016 16:57:06 -0400 Subject: [PATCH 26/31] .gitignore : Ignore .DS_Store. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 70845e0..d07bba3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ Makefile.in +.DS_Store + From 85878f995bfd8edc6791b4414e5cf43fdab56ce1 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 15 Sep 2016 18:00:04 -0400 Subject: [PATCH 27/31] cmXScore.c : Minor improvements and bug-fixes for _cmXsMeasComplexity() --- app/cmXScore.c | 63 +++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 532f3c3..91d2592 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -3111,13 +3111,15 @@ unsigned _cmXsHistValue( cmXsHist_t* hist, unsigned histN ) return n; } +// Measure the score complexity for the the time window 'wndSecs' seconds +// prior to the note n1 and following n0. const cmXsNote_t* _cmXsMeasComplexityInWindow( const cmXsNote_t* n0, cmXsNote_t* n1, double wndSecs ) { const cmXsNote_t* n2 = NULL; unsigned l_pch_0 = 0; unsigned l_pch_value = 0; - unsigned l_pch_cnt = 0; - unsigned r_pch_0 = 0; + unsigned l_pch_cnt = n1->staff==1 ? 0 : 1; + unsigned r_pch_0 = n1->staff==1 ? 1 : 0; unsigned r_pch_value = 0; unsigned r_pch_cnt = 0; unsigned i = 0; @@ -3137,44 +3139,35 @@ const cmXsNote_t* _cmXsMeasComplexityInWindow( const cmXsNote_t* n0, cmXsNote_t // if this event is less than wndSecs behind 'n1' and is not a sounding note ... if( n1->secs - n->secs <= wndSecs && cmIsFlag(n->flags,kOnsetXsFl) ) { - // if this is not the first note in the window - if( i > 0 ) - { - _cmXsHistUpdateI( velHist, histN, n->dynamics ); - _cmXsHistUpdateF( rymHist, histN, n->rvalue ); - - switch( n->staff ) - { - case 1: - r_pch_value += r_pch_0 > n->pitch ? r_pch_0-n->pitch : n->pitch-r_pch_0; - r_pch_cnt += 1; - break; - - case 2: - l_pch_value += l_pch_0 > n->pitch ? l_pch_0-n->pitch : n->pitch-l_pch_0; - r_pch_cnt += 1; - break; - - default: - { assert(0); } - } - } + _cmXsHistUpdateI( velHist, histN, n->dynamics ); + _cmXsHistUpdateF( rymHist, histN, n->rvalue ); - // store the pitch values to compare on the next interation switch( n->staff ) { - case 1: + case 1: // treble cleff + if( i > 0 ) + { + r_pch_value += r_pch_0 > n->pitch ? r_pch_0-n->pitch : n->pitch-r_pch_0; + r_pch_cnt += 1; + } + r_pch_0 = n->pitch; break; - - case 2: - l_pch_0 = n->pitch; + + case 2: // bass cleff + if( i > 0 ) + { + l_pch_value += l_pch_0 > n->pitch ? l_pch_0-n->pitch : n->pitch-l_pch_0; + l_pch_cnt += 1; + } + + l_pch_0 = n->pitch; break; - + default: { assert(0); } } - + // track the first note that is inside the window if( i == 0 ) n2 = n; @@ -3202,6 +3195,7 @@ const cmXsNote_t* _cmXsMeasComplexityInWindow( const cmXsNote_t* n0, cmXsNote_t } + // update the cplx record in n1 with the results of this window analysis n1->cplx.sum_d_vel = _cmXsHistValue( velHist, histN ); n1->cplx.sum_d_rym = _cmXsHistValue( rymHist, histN ); n1->cplx.sum_d_lpch = l_pch_value; @@ -3212,6 +3206,8 @@ const cmXsNote_t* _cmXsMeasComplexityInWindow( const cmXsNote_t* n0, cmXsNote_t return n2; } +// Measure the score complexity and fill in the cmXsComplexity_t record associated +// with the cmXsNote_t record of each sounding note. cmXsRC_t _cmXsMeasComplexity( cmXsH_t h, double wndSecs ) { cmXsRC_t rc = kOkXsRC; @@ -3361,8 +3357,6 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const return rc; } - - typedef struct cmXsSvgEvt_str { unsigned flags; // k???XsFl @@ -3630,7 +3624,6 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm return _cmXsWriteMidiSvg( ctx, p, &mf, dir, fn ); - } cmXsRC_t cmXScoreTest( @@ -3683,6 +3676,8 @@ cmXsRC_t cmXScoreTest( if( midiOutFn != NULL ) { + + // measure the score complexity double wndSecs = 1.0; _cmXsMeasComplexity(h,wndSecs); From 8e4f32c0c1d2bb48898585cd9817babaa143448e Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 15 Sep 2016 18:28:30 -0400 Subject: [PATCH 28/31] cmXScore.h/c : Moved edit file processing into cmXScoreInitialize(). --- app/cmXScore.c | 207 +++++++++++++++++++++++++------------------------ app/cmXScore.h | 25 ++---- 2 files changed, 113 insertions(+), 119 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index 91d2592..d0e9c68 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -1842,89 +1842,6 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p ) return rc; } -cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn ) -{ - cmXsRC_t rc = kOkXsRC; - - if((rc = cmXScoreFinalize(hp)) != kOkXsRC ) - return rc; - - cmXScore_t* p = cmMemAllocZ(cmXScore_t,1); - - cmErrSetup(&p->err,&ctx->rpt,"XScore"); - - // create a local linked heap - if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false ) - return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed."); - - // open the music xml file - if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC ) - { - rc = cmErrMsg(&p->err,kXmlFailXsRC,"Unable to open the MusicXML file '%s'.",cmStringNullGuard(xmlFn)); - goto errLabel; - } - - //cmXmlPrint(p->xmlH,&ctx->rpt); - - // parse the part-list - if((rc = _cmXScoreParsePartList( p )) != kOkXsRC ) - goto errLabel; - - // parse each score 'part' - cmXsPart_t* pp = p->partL; - for(; pp!=NULL; pp=pp->link) - if((rc = _cmXScoreParsePart(p,pp)) != kOkXsRC ) - goto errLabel; - - // fill in the note->slink chain to link the notes in each measure in time order - _cmXScoreSort(p); - - _cmXScoreSpreadGraceNotes(p); - - _cmXScoreSort(p); - - _cmXScoreResolveTiesAndLoc(p); - - _cmXScoreRemoveDuplicateNotes(p); - - _cmXScoreSetMeasGroups(p,kEvenXsFl); - _cmXScoreSetMeasGroups(p,kDynXsFl); - _cmXScoreSetMeasGroups(p,kTempoXsFl); - - //_cmXScoreResolveOctaveShift(p); - - // CSV output initialize failed. - if( cmCsvInitialize(&p->csvH,ctx) != kOkCsvRC ) - rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output object create failed."); - - errLabel: - if( rc != kOkXsRC ) - _cmXScoreFinalize(p); - else - hp->h = p; - - return rc; -} - -cmXsRC_t cmXScoreFinalize( cmXsH_t* hp ) -{ - cmXsRC_t rc = kOkXsRC; - - if( hp == NULL || cmXScoreIsValid(*hp)==false ) - return kOkXsRC; - - cmXScore_t* p = _cmXScoreHandleToPtr(*hp); - - if((rc = _cmXScoreFinalize(p)) != kOkXsRC ) - return rc; - - hp->h = NULL; - - return rc; -} - -bool cmXScoreIsValid( cmXsH_t h ) -{ return h.h != NULL; } //------------------------------------------------------------------------------------------- @@ -2365,12 +2282,11 @@ cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned return rc; } -cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn ) +cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) { typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t; cmXsRC_t rc = kOkXsRC; - cmXScore_t* p = _cmXScoreHandleToPtr(h); cmFileH_t fH = cmFileNullHandle; cmChar_t* b = NULL; unsigned bN = 0; @@ -2538,6 +2454,107 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn ) +cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn ) +{ + cmXsRC_t rc = kOkXsRC; + + if((rc = cmXScoreFinalize(hp)) != kOkXsRC ) + return rc; + + cmXScore_t* p = cmMemAllocZ(cmXScore_t,1); + + cmErrSetup(&p->err,&ctx->rpt,"XScore"); + + // create a local linked heap + if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false ) + return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed."); + + // open the music xml file + if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC ) + { + rc = cmErrMsg(&p->err,kXmlFailXsRC,"Unable to open the MusicXML file '%s'.",cmStringNullGuard(xmlFn)); + goto errLabel; + } + + //cmXmlPrint(p->xmlH,&ctx->rpt); + + // parse the part-list + if((rc = _cmXScoreParsePartList( p )) != kOkXsRC ) + goto errLabel; + + // parse each score 'part' + cmXsPart_t* pp = p->partL; + for(; pp!=NULL; pp=pp->link) + if((rc = _cmXScoreParsePart(p,pp)) != kOkXsRC ) + goto errLabel; + + // fill in the note->slink chain to link the notes in each measure in time order + _cmXScoreSort(p); + + _cmXScoreSpreadGraceNotes(p); + + _cmXScoreSort(p); + + _cmXScoreResolveTiesAndLoc(p); + + _cmXScoreRemoveDuplicateNotes(p); + + _cmXScoreSetMeasGroups(p,kEvenXsFl); + _cmXScoreSetMeasGroups(p,kDynXsFl); + _cmXScoreSetMeasGroups(p,kTempoXsFl); + + //_cmXScoreResolveOctaveShift(p); + + // CSV output initialize failed. + if( cmCsvInitialize(&p->csvH,ctx) != kOkCsvRC ) + rc = cmErrMsg(&p->err,kCsvFailXsRC,"CSV output object create failed."); + + if( editFn != NULL ) + { + if((rc = _cmXsApplyEditFile(p,editFn)) != kOkXsRC ) + { + cmErrMsg(&ctx->err,rc,"XScore reorder failed."); + goto errLabel; + } + } + + // assign durations to pedal down events + _cmXScoreProcessPedals(p); + + // remove some notes which share a pitch and are overlapped or embedded within another note. + _cmXScoreProcessOverlappingNotes(p); + + + errLabel: + if( rc != kOkXsRC ) + _cmXScoreFinalize(p); + else + hp->h = p; + + return rc; +} + +cmXsRC_t cmXScoreFinalize( cmXsH_t* hp ) +{ + cmXsRC_t rc = kOkXsRC; + + if( hp == NULL || cmXScoreIsValid(*hp)==false ) + return kOkXsRC; + + cmXScore_t* p = _cmXScoreHandleToPtr(*hp); + + if((rc = _cmXScoreFinalize(p)) != kOkXsRC ) + return rc; + + hp->h = NULL; + + return rc; +} + +bool cmXScoreIsValid( cmXsH_t h ) +{ return h.h != NULL; } + + /* CSV score columns @@ -3626,32 +3643,21 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm return _cmXsWriteMidiSvg( ctx, p, &mf, dir, fn ); } + cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, - const cmChar_t* reorderFn, + const cmChar_t* editFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn) { cmXsRC_t rc; cmXsH_t h = cmXsNullHandle; - if((rc = cmXScoreInitialize( ctx, &h, xmlFn)) != kOkXsRC ) + // Parse the XML file and apply the changes in editFn. + if((rc = cmXScoreInitialize( ctx, &h, xmlFn,editFn)) != kOkXsRC ) return cmErrMsg(&ctx->err,rc,"XScore alloc failed."); - if( reorderFn != NULL ) - if((rc = cmXScoreReorder(h,reorderFn)) != kOkXsRC ) - { - cmErrMsg(&ctx->err,rc,"XScore reorder failed."); - goto errLabel; - } - - // assign durations to pedal down events - _cmXScoreProcessPedals(_cmXScoreHandleToPtr(h)); - - // remove some notes which share a pitch and are overlapped or embedded within another note. - _cmXScoreProcessOverlappingNotes(_cmXScoreHandleToPtr(h)); - if( csvOutFn != NULL ) { cmScH_t scH = cmScNullHandle; @@ -3694,7 +3700,6 @@ cmXsRC_t cmXScoreTest( //cmXScoreReport(h,&ctx->rpt,true); - errLabel: return cmXScoreFinalize(&h); } diff --git a/app/cmXScore.h b/app/cmXScore.h index 7359b89..3b28c25 100644 --- a/app/cmXScore.h +++ b/app/cmXScore.h @@ -43,26 +43,15 @@ extern "C" { // // M-x load-file ~/src/emacs/proc_music_xml.el // - // 3) How to assigned dynamic markings (they are not attached to notes). (from MIDI file?) - // 4) Tempo syntax is inconsistent (only a problem in full part2 score) - // 5) Heel is being parsed but not used. - // 6) Sostenuto pedal events are not being parsed because they are not pedal events. - // 7) What is a 'pedal-change' event vs. a 'pedal-stop' event. - // 8) Verify the colors. (done) - // 9) Remove blank bars at end (done in xml - do in score) - //10) Need to assign section targets (always default to next section) - //11) Mark tied notes for skip. (done) - //12) Determine note off locations based on ties and slurs - defer 'pedal' to player - //13) Check that the measures are given in sorted order. - //14) Current implementation assumes meter changes only occur at measure boundaries. - //15) Score Fixes: Add meter to bar 1, fix time errors (shown in voice report) - //16) The order of notes is now correct (4/6/16) after applying - // the grace note ordering changed specified in 'score_print_mk_edit.txt', - // via cmXScoreReorder() however the ticks are now incorrect - fix them. - - cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn ); + + // Initialize an cmXScore object from a Sibelius generated MusicXML file. + // Optionally include an 'edit' file to attach additional score information. + // Note that the 'edit' file is created by marking up a file created via + // cmXScoreReport(). + cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn ); cmXsRC_t cmXScoreFinalize( cmXsH_t* hp ); + bool cmXScoreIsValid( cmXsH_t h ); cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn ); From 1b2054bf10abe6e40cf94bc27a862a04a8467d96 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 21 Sep 2016 19:16:54 -0400 Subject: [PATCH 29/31] cmScoreMatchGraphic.c : Changed score0.css to cmScoreMatchGraphic.css. --- app/cmScoreMatchGraphic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cmScoreMatchGraphic.c b/app/cmScoreMatchGraphic.c index 79799fb..b013c97 100644 --- a/app/cmScoreMatchGraphic.c +++ b/app/cmScoreMatchGraphic.c @@ -538,7 +538,7 @@ cmSmgRC_t cmScoreMatchGraphicWrite( cmSmgH_t h, const cmChar_t* fn ) svgHeight += 10; // add a right and lower border svgWidth += 10; - cmFilePrintf(fH,"\n\n\n\n",svgWidth,svgHeight); + cmFilePrintf(fH,"\n\n\n\n",svgWidth,svgHeight); for(i=0; ilocN; ++i) { From 5272aaeb52a1b13235160ea6c018350df2318da4 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 21 Sep 2016 19:18:01 -0400 Subject: [PATCH 30/31] cmXScore.h/c : Added cmXScoreGenEditFile(). --- app/cmXScore.c | 80 +++++++++++++++++++++++++++++++++++++------------- app/cmXScore.h | 2 ++ 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/app/cmXScore.c b/app/cmXScore.c index d0e9c68..c94ddab 100644 --- a/app/cmXScore.c +++ b/app/cmXScore.c @@ -71,14 +71,15 @@ enum struct cmXsMeas_str; struct cmXsVoice_str; +// Values measured for each sounding note in the preceding time window.... typedef struct cmXsComplexity_str { - unsigned sum_d_vel; - unsigned sum_d_rym; - unsigned sum_d_lpch; - unsigned sum_n_lpch; - unsigned sum_d_rpch; - unsigned sum_n_rpch; + unsigned sum_d_vel; // sum of first order difference of cmXsNote_t.dynamics + unsigned sum_d_rym; // sum of first order difference of cmXsNote_t.rvalue + unsigned sum_d_lpch; // sum of first order difference of cmXsNote_t.pitch of note assigned to the bass cleff + unsigned sum_n_lpch; // count of notes assigned to the bass cleff + unsigned sum_d_rpch; // sum of first order difference of cmXsNote_t.pitch of note assigned to the treble cleff + unsigned sum_n_rpch; // count of notes assigned to the treble cleff } cmXsComplexity_t; typedef struct cmXsNote_str @@ -2011,8 +2012,8 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* if( cmIsFlag(rV[i].newFlags,kTieEndXsFl) ) { rV[i].note->flags |= kTieEndXsFl; - rV[i].note->flags = cmClrFlag(rV[i].note->flags, kOnsetXsFl); - rV[i].newFlags = cmClrFlag(rV[i].newFlags,kTieEndXsFl ); + rV[i].note->flags = cmClrFlag( rV[i].note->flags, kOnsetXsFl ); + rV[i].newFlags = cmClrFlag( rV[i].newFlags, kTieEndXsFl); } // if a new note value was specified @@ -2032,7 +2033,6 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* { if( rV[i].newFlags != 0 ) { - if( cmIsFlag(rV[i].newFlags,kDampDnXsFl ) ) _cmXScoreInsertPedalEvent(p,rV + i,kDampDnXsFl); @@ -2043,8 +2043,7 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* _cmXScoreInsertPedalEvent(p,rV + i,kDampUpXsFl); if( cmIsFlag(rV[i].newFlags,kSostUpXsFl ) ) - _cmXScoreInsertPedalEvent(p,rV + i,kSostUpXsFl); - + _cmXScoreInsertPedalEvent(p,rV + i,kSostUpXsFl); } } @@ -2603,6 +2602,7 @@ cmXsRC_t _cmXScoreWriteCsvHdr( cmXScore_t* p ) return kOkXsRC; } + cmXsRC_t _cmXScoreWriteCsvBlankCols( cmXScore_t* p, unsigned cnt, cmCsvCell_t** leftCellPtrPtr ) { unsigned i; @@ -3081,6 +3081,47 @@ void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl ) } } +void _cmXScoreGenEditFileWrite( void* arg, const cmChar_t* text ) +{ + if( text != NULL && arg != NULL ) + { + cmFileH_t* hp = (cmFileH_t*)arg; + cmFilePrint(*hp,text); + } +} + +cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn ) +{ + cmXsH_t xsH = cmXsNullHandle; + cmFileH_t fH = cmFileNullHandle; + cmXsRC_t rc = kOkXsRC; + cmErr_t err; + cmRpt_t rpt; + + cmErrSetup(&err,&ctx->rpt,"cmXScoreGenEditFile"); + cmRptSetup(&rpt,_cmXScoreGenEditFileWrite,_cmXScoreGenEditFileWrite,&fH); + + if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL)) != kOkXsRC ) + return rc; + + if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC ) + { + cmErrMsg(&err,kFileFailXsRC,"Unable to open the output file '%s'.",cmStringNullGuard(outFn)); + goto errLabel; + } + + cmXScoreReport(xsH,&rpt,true); + + errLabel: + + if( cmFileClose(&fH) != kOkFileRC ) + rc = cmErrMsg(&err,kFileFailXsRC,"File close failed on '%s'.",cmStringNullGuard(outFn)); + + cmXScoreFinalize(&xsH); + + return rc; +} + typedef struct { unsigned ival; @@ -3376,13 +3417,13 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const typedef struct cmXsSvgEvt_str { - unsigned flags; // k???XsFl - unsigned tick; // start tick - unsigned durTicks; // dur-ticks - unsigned voice; // score voice number - unsigned d0; // MIDI d0 (barNumb) - unsigned d1; // MIDI d1 - cmXsComplexity_t cplx; + unsigned flags; // k???XsFl + unsigned tick; // start tick + unsigned durTicks; // dur-ticks + unsigned voice; // score voice number + unsigned d0; // MIDI d0 (barNumb) + unsigned d1; // MIDI d1 + cmXsComplexity_t cplx; struct cmXsSvgEvt_str* link; } cmXsSvgEvt_t; @@ -3638,8 +3679,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm } } } - - + return _cmXsWriteMidiSvg( ctx, p, &mf, dir, fn ); } diff --git a/app/cmXScore.h b/app/cmXScore.h index 3b28c25..a47cfa1 100644 --- a/app/cmXScore.h +++ b/app/cmXScore.h @@ -58,6 +58,8 @@ extern "C" { void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl ); + cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn ); + // Generate the CSV file suitable for use by cmScore. cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* reorderFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn ); From bbcd065cb1f0e99513d4254d6dba0350ab97ff4a Mon Sep 17 00:00:00 2001 From: kevin Date: Sun, 26 Feb 2017 14:10:17 -0500 Subject: [PATCH 31/31] cmDspPgmKrChain.c : Fixed 'Min Wet' to default to 0.0 (was set to 1.0) --- dsp/cmDspPgmKrChain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/cmDspPgmKrChain.c b/dsp/cmDspPgmKrChain.c index 18eeeaf..8f97eb1 100644 --- a/dsp/cmDspPgmKrChain.c +++ b/dsp/cmDspPgmKrChain.c @@ -198,7 +198,7 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c, unsigned preGrpS cmDspInst_t* max_lwr_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Max Lwr"), 0.0, -1.0, 5.0, 3.0); cmDspInst_t* min_off_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Min Off"), 0.0, 50.0, 0.1, 30.0); cmDspInst_t* max_off_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Max Off"), 0.0, 50.0, 0.1, 30.0); - cmDspInst_t* min_wet_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Min Wet"), 0.0, 1.0, 0.01, 1.0); + cmDspInst_t* min_wet_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Min Wet"), 0.0, 1.0, 0.01, 0.0); cmDspInst_t* max_wet_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Max Wet"), 0.0, 1.0, 0.01, 1.0);