From 033204dd04c68f43426e093e662e3f3e0b965669 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 13 Oct 2020 15:12:19 -0400 Subject: [PATCH] cmXScore.h : Added cmXScoreMergedEditFiles(). --- src/app/cmXScore.c | 638 ++++++++++++++++++++++++++++++++++++++++----- src/app/cmXScore.h | 5 +- 2 files changed, 579 insertions(+), 64 deletions(-) diff --git a/src/app/cmXScore.c b/src/app/cmXScore.c index 5892a42..c98abb6 100644 --- a/src/app/cmXScore.c +++ b/src/app/cmXScore.c @@ -110,12 +110,14 @@ 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 + const cmChar_t* editStr; // merged manual edit string unsigned evenGroupId; // eveness group id unsigned dynGroupId; // dynamics group id unsigned tempoGroupId; // tempo group id unsigned graceGroupId; // grace note group id + struct cmXsVoice_str* voice; // voice this note belongs to struct cmXsMeas_str* meas; // measure this note belongs to @@ -230,6 +232,33 @@ cmXsVoice_t* _cmXScoreIdToVoice( cmXsMeas_t* meas, unsigned voiceId ) return NULL; } +cmXsMeas_t* _cmXsFindMeas( cmXsPart_t* part, unsigned measNumb ) +{ + cmXsMeas_t* m = part->measL; + for(; m!=NULL; m=m->link) + if( m->number == measNumb ) + return m; + + return NULL; +} + +cmXsNote_t* _cmXsFindNote( cmXsMeas_t* m, unsigned idx, unsigned midi, double rval, unsigned durtn, unsigned* idxRef ) +{ + unsigned i; + cmXsNote_t* np = m->noteL; + for(i=0; np!=NULL; np=np->slink,++i) + { + //printf("idx:%i %i midi:%i %i rval:%f %f durtn:%i %i\n", i,idx, np->pitch,midi, np->rvalue,rval, np->tied_dur,durtn); + if( i>=idx && np->pitch==midi && np->rvalue==rval && np->tied_dur==durtn ) + { + *idxRef = i; + return np; + } + } + + return NULL; +} + cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, cmXsNote_t* note ) { cmXsVoice_t* v; @@ -612,6 +641,7 @@ cmXsRC_t _cmXScoreParseNote(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_t* cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1); unsigned voiceId; + note->pitch = kInvalidMidiPitch; note->meas = meas; note->xmlNode = nnp; @@ -686,6 +716,7 @@ cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_ cmXsNote_t* note = cmLhAllocZ(p->lhH,cmXsNote_t,1); unsigned voiceId = 0; // non-note's are always assigned to voiceId=0; + note->pitch = kInvalidMidiPitch; note->tick = tick; note->staff = staff; note->flags = flags; @@ -798,6 +829,11 @@ cmXsRC_t _cmXScoreParseDirection(cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNo flags = kSectionXsFl; } + else + { + // we only care about 'words' in 'enclosures' + pushFl = false; + } } else @@ -2028,7 +2064,7 @@ cmXsRC_t _cmXScoreProcessDynamicForks( cmXScore_t* p ) //------------------------------------------------------------------------------------------- -typedef struct +typedef struct _cmXsReorder_str { unsigned idx; // Fields from the reordering input file which are unsigned voice; // used to match the reorder record to @@ -2046,8 +2082,26 @@ typedef struct unsigned graceFlags; // 0=ignore See kXXXGraceXsFl unsigned graceGroupId; // 0=ignore >0=grace note group id unsigned pitch; // 0=ignore >0 new pitch + + const char* editStr; + + struct _cmXsReorder_str* link; } cmXsReorder_t; +typedef struct _cmXsReorderMeas_str +{ + unsigned measNumb; + cmXsReorder_t* beg; + cmXsReorder_t* end; + struct _cmXsReorderMeas_str* link; +} cmXsReorderMeas_t; + +typedef struct +{ + cmXsReorderMeas_t* beg; + cmXsReorderMeas_t* end; +} cmXsReorderFile_t; + typedef struct _cmXScoreDynMark_str { const cmChar_t* mark; // @@ -2088,6 +2142,90 @@ _cmXScoreDynMark_t _cmXScoreDynMarkArray[] = }; +cmXsReorderMeas_t* _cmXsReorderFileAllocMeas( cmXScore_t* p, cmXsReorderFile_t* rfp, unsigned measNumb ) +{ + cmXsReorderMeas_t* m = cmLhAllocZ(p->lhH,cmXsReorderMeas_t,1); + m->measNumb = measNumb; + + if( rfp->end == NULL ) + { + rfp->beg = m; + rfp->end = m; + } + else + { + rfp->end->link = m; + rfp->end = m; + } + return m; +} + +cmXsReorderMeas_t* _cmXsReorderFileFindMeas( cmXsReorderFile_t* rfp, unsigned measNumb ) +{ + cmXsReorderMeas_t* m = rfp->beg; + for(; m!=NULL; m=m->link) + if( m->measNumb == measNumb ) + return m; + + return NULL; +} + + +cmXsReorder_t* _cmXsReorderMeasAllocEvent( cmXScore_t* p, cmXsReorderMeas_t* m ) +{ + cmXsReorder_t* r = cmLhAllocZ(p->lhH, cmXsReorder_t,1); + + r->midi = kInvalidMidiPitch; + + if( m->end == NULL ) + { + m->beg = r; + m->end = r; + } + else + { + m->end->link = r; + m->end = r; + } + + return r; +} + +// find key in meas (m) by searching after event idx0. +cmXsReorder_t* _cmXsReorderFindEvent( cmXsReorderMeas_t* m, unsigned idx0, cmXsReorder_t* key ) +{ + cmXsReorder_t* r; + for(r=m->beg; r!=NULL; r=r->link) + if( r->idx >= idx0 && r->midi==key->midi && r->rval==key->rval && r->durtn==key->durtn ) + return r; + + return NULL; +} + +cmXsReorder_t* _cmXsReorderMeasPop( cmXsReorderMeas_t* m ) +{ + cmXsReorder_t* r0 = NULL; + cmXsReorder_t* r = NULL; + for(r=m->beg; r!=NULL; r=r->link) + { + if( r == m->end ) + break; + r0 = r; + } + + if( r0 == NULL ) + { + m->beg = NULL; + m->end = NULL; + } + else + { + m->end = r0; + m->end->link = NULL; + } + + return r; +} cmXsNote_t* _cmXsReorderFindNote( cmXScore_t* p, unsigned measNumb, const cmXsReorder_t* r, unsigned iii ) { @@ -2138,7 +2276,8 @@ void _cmXScoreInsertPedalEvent( cmXScore_t* p, const cmXsReorder_t* r, unsigned { // Create a new score event record cmXsNote_t* nn = cmLhAllocZ(p->lhH,cmXsNote_t,1); - + + nn->pitch = kInvalidMidiPitch; nn->uid = p->nextUid++; nn->voice = r->note->voice; nn->meas = r->note->meas; @@ -2165,7 +2304,108 @@ void _cmXScoreInsertPedalEvent( cmXScore_t* p, const cmXsReorder_t* r, unsigned } -cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* rV, unsigned rN ) +cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, cmXsReorderMeas_t* m ) +{ + unsigned i; + cmXsReorder_t* r; + + if( m->beg == NULL ) + return kOkXsRC; + + // set the 'note' field on each cmXsReorder_t record + for(r=m->beg,i=0; r!=NULL; r=r->link,++i) + if((r->note = _cmXsReorderFindNote(p,m->measNumb,r,i)) == NULL ) + return kSyntaxErrorXsRC; + + // remove deleted notes + for(r=m->beg; r!=NULL; r=r->link) + if( cmIsFlag(r->newFlags,kDeleteXsFl) ) + if( _cmXScoreRemoveNote( r->note ) != kOkXsRC ) + return cmErrMsg(&p->err,kSyntaxErrorXsRC,"Event marked to skip was not found in measure: %i",m->measNumb); + + cmXsMeas_t* mp = m->beg->note->meas; + cmXsNote_t* n0p = NULL; + + assert( mp->number == m->measNumb ); + + // Reassign the slink of the cmXsNote_t records in this measure + // according to their order in rV[]. + for(r=m->beg; r!=NULL; r=r->link) + { + + if( cmIsFlag(r->newFlags,kDeleteXsFl) ) + continue; + + if( n0p == NULL ) + mp->noteL = r->note; + else + n0p->slink = r->note; + + // if a new tick was specified + if( r->newTick != 0 ) + r->note->tick = r->newTick; + + // if a dynamic or velocity mark was included + if( r->dynIdx != cmInvalidIdx ) + { + r->note->dynamics = _cmXScoreDynMarkArray[ r->dynIdx ].dyn; + r->note->vel = _cmXScoreDynMarkArray[ r->dynIdx ].vel; + } + + // Set the dynamic fork begin/end flags for later _cmXScoreProcessDynamicForks() + if( cmIsFlag(r->newFlags,kDynBegForkXsFl) ) + r->note->flags = cmSetFlag(r->note->flags,kDynBegForkXsFl); + + if( cmIsFlag(r->newFlags,kDynEndForkXsFl) ) + r->note->flags = cmSetFlag(r->note->flags,kDynEndForkXsFl); + + + // if the tie end flag was set + if( cmIsFlag(r->newFlags,kTieEndXsFl) ) + { + r->note->flags |= kTieEndXsFl; + r->note->flags = cmClrFlag( r->note->flags, kOnsetXsFl ); + r->newFlags = cmClrFlag( r->newFlags, kTieEndXsFl); + } + + // if a new note value was specified + if( r->pitch != 0 ) + r->note->pitch = r->pitch; + + r->note->flags |= r->graceFlags; + r->note->graceGroupId = r->graceGroupId; + + + + n0p = r->note; + n0p->slink = NULL; + } + + + // Insert new note records for pedal up/dn events. + for(r=m->beg; r!=NULL; r=r->link) + { + if( r->newFlags != 0 ) + { + if( cmIsFlag(r->newFlags,kDampDnXsFl ) ) + _cmXScoreInsertPedalEvent(p,r,kDampDnXsFl); + + if( cmIsFlag(r->newFlags,kSostDnXsFl ) ) + _cmXScoreInsertPedalEvent(p,r,kSostDnXsFl); + + if( cmIsFlag(r->newFlags,kDampUpXsFl ) ) + _cmXScoreInsertPedalEvent(p,r,kDampUpXsFl); + + if( cmIsFlag(r->newFlags,kSostUpXsFl ) ) + _cmXScoreInsertPedalEvent(p,r,kSostUpXsFl); + } + } + + return kOkXsRC; + +} + +cmXsRC_t _cmXScoreReorderMeas0( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* rV, unsigned rN ) { unsigned i; @@ -2266,19 +2506,22 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t* } -cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lineNumb, unsigned* dynIdxRef, unsigned* flagsRef, int measNumb ) -{ - cmXsRC_t rc = kOkXsRC; - const cmChar_t* s = NULL; - bool begForkFl = false; - bool endForkFl = false; - +cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lineNumb, char** s0, unsigned* dynIdxRef, unsigned* flagsRef, int measNumb ) +{ + cmXsRC_t rc = kOkXsRC; + cmChar_t* s = NULL; + bool begForkFl = false; + bool endForkFl = false; + *dynIdxRef = cmInvalidIdx; // locate the '!' which indicates the start of a dynamic marking if( (s = strchr(b,'!')) == NULL ) return rc; + + if( *s0==NULL || s<*s0 ) + *s0 = s; ++s; // increment past the '!' @@ -2373,19 +2616,22 @@ cmXsRC_t _cmXScoreReorderParseDyn(cmXScore_t* p, const cmChar_t* b, unsigned lin } -cmXsRC_t _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* newFlagsRef ) +cmXsRC_t _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* newFlagsRef ) { - cmXsRC_t rc = kOkXsRC; - const cmChar_t* s; - bool doneFl = false; - unsigned i = 0; - + cmXsRC_t rc = kOkXsRC; + cmChar_t* s; + bool doneFl = false; + unsigned i = 0; + *newFlagsRef = 0; // tilde indicates a pedal event if((s = strchr(b,'~')) == NULL ) return rc; + if( *s0==NULL || s<*s0) + *s0 = s; + do { ++s; @@ -2434,14 +2680,17 @@ cmXsRC_t _cmXScoreReorderParseFlags(cmXScore_t* p, const cmChar_t* b, unsigned return rc; } -cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* tickRef ) +cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* tickRef ) { cmXsRC_t rc = kOkXsRC; - const cmChar_t* s; + cmChar_t* s; if((s = strchr(b,'@')) == NULL ) return rc; + if( *s0 == NULL || s<*s0 ) + *s0 = s; + ++s; if(!isdigit(*s)) @@ -2453,14 +2702,17 @@ cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned l return rc; } -cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, cmXsReorder_t* r, unsigned* graceGroupIdRef ) +cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, cmXsReorder_t* r, unsigned* graceGroupIdRef ) { - cmXsRC_t rc = kOkXsRC; - const cmChar_t* s; + cmXsRC_t rc = kOkXsRC; + cmChar_t* s; if((s = strchr(b,'%')) == NULL ) return rc; + if( *s0==NULL || s<*s0 ) + *s0 = s; + ++s; r->graceGroupId = *graceGroupIdRef; @@ -2502,12 +2754,12 @@ cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned } -cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* pitchRef ) +cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned line, char** s0, unsigned* pitchRef ) { - cmXsRC_t rc = kOkXsRC; + cmXsRC_t rc = kOkXsRC; cmChar_t* s; - cmChar_t buf[4]; - unsigned i,j; + cmChar_t buf[4]; + unsigned i,j; memset(buf,0,sizeof(buf)); *pitchRef = 0; @@ -2515,6 +2767,9 @@ cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned if((s = strchr(b,'$')) == NULL ) return rc; + if( *s0==NULL || s<*s0) + *s0 = s; + ++s; j=2; @@ -2546,7 +2801,232 @@ cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned return rc; } +cmXsRC_t _cmXsReadEditFile( cmXScore_t* p, const cmChar_t* fn, unsigned* graceGroupIdPtr, cmXsReorderFile_t* rfp ) +{ + typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t; + + cmXsRC_t rc = kOkXsRC; + cmFileH_t fH = cmFileNullHandle; + cmChar_t* b = NULL; + unsigned bN = 0; + unsigned ln = 0; + stateId_t stateId = kFindMeasStId; + cmXsReorderMeas_t* curMeas = NULL; + + *graceGroupIdPtr = 1; + + if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC ) + { + rc = cmErrMsg(&p->err,kFileFailXsRC,"The reordering file '%s' could not be opened.",cmStringNullGuard(fn)); + return rc; + } + + for(; cmFileGetLineAuto(fH,&b,&bN) == kOkFileRC; ++ln) + { + switch( stateId ) + { + case kFindEventStId: // scanning past labels to an event line + { + unsigned voice,loc; + if( sscanf(b,"%i %i",&voice,&loc) != 2 ) + continue; + + stateId = kReadEventStId; + } + // fall through + + case kReadEventStId: + { + cmXsReorder_t* r = _cmXsReorderMeasAllocEvent(p, curMeas ); + char pitchStr[4]; + char* s0 = NULL; + + // parse an event line + if( sscanf(b,"%i %i %i %i %i %f",&r->idx,&r->voice,&r->locIdx,&r->tick,&r->durtn,&r->rval) == 6 ) + { + assert( strlen(b)>=52); + int PC = 39; // text file column where first pitch char occurs + + if( b[PC] == ' ') + r->midi = kInvalidMidiPitch; + else + { + pitchStr[0] = b[PC+0]; + pitchStr[1] = b[PC+1]; + pitchStr[2] = b[PC+2]; + pitchStr[3] = 0; + + if( !isdigit(pitchStr[2]) ) + r->midi = kInvalidMidiPitch; + else + { + if( pitchStr[1] == ' ') + { + pitchStr[1] = pitchStr[2]; + pitchStr[2] = 0; + } + + r->midi = cmSciPitchToMidi(pitchStr); + + //printf("%i %i %s %s\n",curMeas->measNumb,r->midi,pitchStr,fn); + } + } + + // parse the flag edits following a '~' + if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &s0, &r->newFlags)) != kOkXsRC ) + goto errLabel; + + // parse the dynamic marking following a '!' + if((rc = _cmXScoreReorderParseDyn(p,b,ln+1, &s0, &r->dynIdx, &r->newFlags, curMeas->measNumb)) != kOkXsRC ) + goto errLabel; + + // parse the @newtick marker + if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &s0, &r->newTick)) != kOkXsRC ) + goto errLabel; + + // parse the %grace note marker + if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &s0, r, graceGroupIdPtr)) != kOkXsRC ) + goto errLabel; + + // parse the $pitch marker + if((rc = _cmXScoreReorderParsePitch(p, b, ln+1, &s0, &r->pitch )) != kOkXsRC ) + goto errLabel; + + if( s0 != NULL ) + r->editStr = cmTextTrimEnd(cmLhAllocStrN( p->lhH, s0, strlen(s0)+1 )); + + continue; + } + + // remove the last reorder record because it was not filled + _cmXsReorderMeasPop(curMeas); + + stateId = kFindMeasStId; + // fall through + } + + case kFindMeasStId: // scanning for a bar-line + { + char colon; + unsigned measNumb = 0; + + if( sscanf(b,"%i %c",&measNumb,&colon) == 2 && colon == ':' ) + { + curMeas = _cmXsReorderFileAllocMeas( p, rfp, measNumb ); + stateId = kFindEventStId; + } + } + break; + } + + } + errLabel: + cmMemFree(b); + cmFileClose(&fH); + return rc; +} + cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) +{ + cmXsRC_t rc = kOkXsRC; + unsigned graceGroupId = 1; + cmXsReorderFile_t rf; + cmXsReorderMeas_t* m; + memset(&rf,0,sizeof(rf)); + + if((rc = _cmXsReadEditFile( p, fn, &graceGroupId, &rf )) != kOkXsRC ) + return rc; + + // reorder each measure + for(m=rf.beg; m!=NULL; m=m->link) + if((rc = _cmXScoreReorderMeas(p, m)) != kOkXsRC ) + goto errLabel; + + // the ticks may have changed so the 'secs' and 'dsecs' must be updated + _cmXScoreSetAbsoluteTime( p ); + + // the bar lines should be the first event in the measure + _cmXScoreFixBarLines(p); + + // resort to force the links to be correct + _cmXScoreSort(p); + + // process the grace notes. + _cmXScoreProcessGraceNotes( p, graceGroupId ); + + // inserting grace notes may have left the score unsorted + _cmXScoreSort(p); + + // process the dynamic forks + _cmXScoreProcessDynamicForks(p); + + //_cmXScoreReport(p, NULL, true ); + + errLabel: + return rc; +} + + + +cmXsRC_t _cmXsMergeEditFiles( cmXScore_t* p, unsigned measNumb0, const cmChar_t* keyEditFn, unsigned keyMeasNumb, const cmChar_t* outFn ) +{ + cmXsRC_t rc = kOkXsRC; + unsigned graceGroup1Id = 1; + unsigned measNumb1 = keyMeasNumb; + cmXsReorderFile_t rf1; + memset(&rf1,0,sizeof(rf1)); + + if((rc = _cmXsReadEditFile( p, keyEditFn, &graceGroup1Id, &rf1 )) != kOkXsRC ) + return rc; + + while(1) + { + cmXsMeas_t* m0 = _cmXsFindMeas( p->partL, measNumb0 ); + cmXsReorderMeas_t* m1 = _cmXsReorderFileFindMeas( &rf1, measNumb1 ); + cmXsReorder_t* key = NULL; + unsigned idx0 = 0; + + if( m1==NULL ) + { + rc = cmErrMsg(&p->err,kEventNotFoundXsRC,"The measure %i was not found in the key edit file '%s'.",keyMeasNumb,cmStringNullGuard(keyEditFn)); + break; + } + + key = m1->beg; + + for(; key!=NULL; key=key->link) + { + unsigned idx1 = cmInvalidIdx; + cmXsNote_t* np = _cmXsFindNote( m0, idx0, key->midi, key->rval, key->durtn, &idx1); + + if( np==NULL ) + { + if( key->editStr != NULL ) + { + const char* sciPitch = key->midi!=kInvalidMidiPitch ? cmMidiToSciPitch(key->midi,NULL,0) : ""; + cmErrWarnMsg(&p->err,kEventNotFoundXsRC,"Sync error: meas: ref:%i key:%i index:%i %s (midi:%i) edit:%s did not match to the reference edit file.", measNumb0,m1->measNumb,key->idx, sciPitch, key->midi, key->editStr); + } + } + else + { + np->editStr = key->editStr; + + if( key->editStr != NULL ) + idx0 = idx1; + } + } + + ++measNumb0; + ++measNumb1; + + } + + return rc; +} + + + +cmXsRC_t _cmXsApplyEditFile0( cmXScore_t* p, const cmChar_t* fn ) { typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t; @@ -2586,6 +3066,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) { cmXsReorder_t r; char pitchStr[4]; + char* s0 = NULL; memset(&r,0,sizeof(r)); @@ -2596,7 +3077,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) int PC = 39; // text file column where first pitch char occurs if( b[PC] == ' ') - r.midi = 0; + r.midi = kInvalidMidiPitch; else { pitchStr[0] = b[PC+0]; @@ -2605,7 +3086,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) pitchStr[3] = 0; if( !isdigit(pitchStr[2]) ) - r.midi = 0; + r.midi = kInvalidMidiPitch; else { if( pitchStr[1] == ' ') @@ -2621,40 +3102,24 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) // parse the flag edits following a '~' - if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &r.newFlags)) != kOkXsRC ) + if((rc = _cmXScoreReorderParseFlags(p,b,ln+1, &s0, &r.newFlags)) != kOkXsRC ) goto errLabel; // parse the dynamic marking following a '!' - if((rc = _cmXScoreReorderParseDyn(p,b,ln+1,&r.dynIdx, &r.newFlags, measNumb)) != kOkXsRC ) + if((rc = _cmXScoreReorderParseDyn(p,b,ln+1,&s0, &r.dynIdx, &r.newFlags, measNumb)) != kOkXsRC ) goto errLabel; // parse the @newtick marker - if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &r.newTick)) != kOkXsRC ) + if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &s0, &r.newTick)) != kOkXsRC ) goto errLabel; // parse the %grace note marker - if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &r, &graceGroupId)) != kOkXsRC ) + if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &s0, &r, &graceGroupId)) != kOkXsRC ) goto errLabel; // parse the $pitch marker - if((rc = _cmXScoreReorderParsePitch(p, b, ln+1, &r.pitch )) != kOkXsRC ) + if((rc = _cmXScoreReorderParsePitch(p, b, ln+1, &s0, &r.pitch )) != kOkXsRC ) goto errLabel; - - // process grace notes - these need to be processed separate from - // the _cmXScoreReorderMeas() because grace notes may cross measure boundaries. - /* - if( r.graceType != 0 ) - { - r.graceGroupId = graceGroupId; - - // if this is an end of a grace note group - if( r.graceType != 'g' && r.graceType != 'b' ) - { - graceGroupId += 1; - } - - } - */ // store the record assert( ri < rN ); @@ -2666,7 +3131,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) // the end of the measure was encountered - // reorder the measure based on the cmXsReorder_t in rV[ri] - if((rc = _cmXScoreReorderMeas(p, measNumb, rV, ri )) != kOkXsRC ) + if((rc = _cmXScoreReorderMeas0(p, measNumb, rV, ri )) != kOkXsRC ) goto errLabel; ri = 0; @@ -2692,7 +3157,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) // If reorder records remain to be processed if( ri > 0 ) - if((rc = _cmXScoreReorderMeas(p, measNumb, rV, ri )) != kOkXsRC ) + if((rc = _cmXScoreReorderMeas0(p, measNumb, rV, ri )) != kOkXsRC ) goto errLabel; @@ -2724,8 +3189,7 @@ cmXsRC_t _cmXsApplyEditFile( cmXScore_t* p, const cmChar_t* fn ) } - -cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn, bool damperRptFl ) +cmXsRC_t cmXScoreAlloc( cmCtx_t* ctx, cmXsH_t* hp ) { cmXsRC_t rc = kOkXsRC; @@ -2740,6 +3204,21 @@ cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, c if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false ) return cmErrMsg(&p->err,kLHeapFailXsRC,"Lheap create failed."); + hp->h = p; + + return rc; +} + +cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn, bool damperRptFl ) +{ + cmXsRC_t rc; + cmXScore_t* p = NULL; + + if((rc = cmXScoreAlloc(ctx,hp)) != kOkXsRC ) + goto errLabel; + + p = _cmXScoreHandleToPtr(*hp); + // open the music xml file if( cmXmlAlloc(ctx, &p->xmlH, xmlFn) != kOkXmlRC ) { @@ -2798,9 +3277,8 @@ cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, c errLabel: if( rc != kOkXsRC ) - _cmXScoreFinalize(p); - else - hp->h = p; + cmXScoreFinalize(hp); + return rc; } @@ -3321,6 +3799,9 @@ void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index ) if( note->dynamics != 0) cmRptPrintf(rpt," dyn=%i %i",note->dynamics,note->vel); + + if( note->editStr != NULL ) + cmRptPrintf(rpt, " %s", note->editStr ); /* if( cmIsFlag(note->flags,kBegGraceXsFl) ) @@ -3371,6 +3852,7 @@ void _cmXScoreReport( cmXScore_t* p, cmRpt_t* rpt, bool sortFl ) t1 = note->slink==NULL ? note->tick : note->slink->tick; + // check that this note is in tick order if( !(t0 <= note->tick && note->tick <= t1) ) { cmRptPrintf(rpt," +"); @@ -3427,32 +3909,41 @@ void _cmXScoreGenEditFileWrite( void* arg, const cmChar_t* text ) } } -cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn, bool damperRptFl ) +cmXsRC_t _cmXScoreEditFileRpt( cmCtx_t* ctx, cmXScore_t* p, const cmChar_t* outFn, bool damperRptFl ) { - cmXsH_t xsH = cmXsNullHandle; - cmFileH_t fH = cmFileNullHandle; cmXsRC_t rc = kOkXsRC; cmErr_t err; cmRpt_t rpt; + cmFileH_t fH = cmFileNullHandle; cmErrSetup(&err,&ctx->rpt,"cmXScoreGenEditFile"); cmRptSetup(&rpt,_cmXScoreGenEditFileWrite,_cmXScoreGenEditFileWrite,&fH); - - if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL,damperRptFl)) != 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); + _cmXScoreReport(p,&rpt,true); errLabel: if( cmFileClose(&fH) != kOkFileRC ) rc = cmErrMsg(&err,kFileFailXsRC,"File close failed on '%s'.",cmStringNullGuard(outFn)); + + return rc; +} + +cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn, bool damperRptFl ) +{ + cmXsH_t xsH = cmXsNullHandle; + cmXsRC_t rc = kOkXsRC; + + if((rc = cmXScoreInitialize(ctx,&xsH,xmlFn,NULL,damperRptFl)) != kOkXsRC ) + return rc; + + rc = _cmXScoreEditFileRpt( ctx, _cmXScoreHandleToPtr(xsH), outFn, damperRptFl ); cmXScoreFinalize(&xsH); @@ -4148,3 +4639,24 @@ cmXsRC_t cmXScoreTest( return cmXScoreFinalize(&h); } + + +cmXsRC_t cmXScoreMergeEditFiles( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* refEditFn, unsigned refBegMeasNumb, const cmChar_t* editFn, unsigned keyMeasNumb, const cmChar_t* outFn ) +{ + cmXsH_t h = cmXsNullHandle; + cmXsRC_t rc; + bool damperRptFl = false; + + if((rc = cmXScoreInitialize(ctx, &h, xmlFn, refEditFn, damperRptFl )) == kOkXsRC ) + { + cmXScore_t* p = _cmXScoreHandleToPtr(h); + + if((rc = _cmXsMergeEditFiles(p, refBegMeasNumb, editFn, keyMeasNumb, outFn )) == kOkXsRC ) + rc = _cmXScoreEditFileRpt( ctx, p, outFn, damperRptFl ); + + + cmXScoreFinalize(&h); + } + + return rc; +} diff --git a/src/app/cmXScore.h b/src/app/cmXScore.h index c412368..df7a352 100644 --- a/src/app/cmXScore.h +++ b/src/app/cmXScore.h @@ -20,7 +20,8 @@ extern "C" { kFileFailXsRC, kSvgFailXsRC, kOverlapWarnXsRC, - kZeroLengthEventXsRC + kZeroLengthEventXsRC, + kEventNotFoundXsRC }; typedef cmRC_t cmXsRC_t; @@ -73,6 +74,8 @@ extern "C" { // Set begMeasNumb to the first measure the to be written to the output csv, MIDI and SVG files. // Set begBPM to 0 to use the tempo from the score otherwise set it to the tempo at begMeasNumb. cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* reorderFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn, const cmChar_t* svgOutFn, bool reportFl, int begMeasNumb, int begBPM, bool svgStandAloneFl, bool svgPanZoomFl, bool damperRptFl ); + + cmXsRC_t cmXScoreMergeEditFiles( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* refEditFn, unsigned refBegMeasNumb, const cmChar_t* editFn, unsigned keyMeasNumb, const cmChar_t* outFn ); #ifdef __cplusplus }