diff --git a/src/app/cmMidiScoreFollow.c b/src/app/cmMidiScoreFollow.c index a837159..1b32b67 100644 --- a/src/app/cmMidiScoreFollow.c +++ b/src/app/cmMidiScoreFollow.c @@ -149,6 +149,7 @@ unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcher return scUid; } +// This is the score follower callback function void _cmMsf_ScoreFollowCb( struct cmScMatcher_str* p, void* arg, cmScMatcherResult_t* rp ) { _cmMsf_ScoreFollow_t* r = (_cmMsf_ScoreFollow_t*)arg; @@ -193,7 +194,7 @@ cmMsfRC_t cmMidiScoreFollowMain( goto errLabel; } - // setup the callback record + // setup the callback record with an array that has twice as many records as there are score events if((sfr.rAllocN = cmScoreEvtCount( scH )*2) == 0) { rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreCsvFn)); diff --git a/src/app/cmTakeSeqBldr.c b/src/app/cmTakeSeqBldr.c index 4da3939..61477cb 100644 --- a/src/app/cmTakeSeqBldr.c +++ b/src/app/cmTakeSeqBldr.c @@ -960,7 +960,7 @@ cmTsbRC_t cmTakeSeqBldrLoadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid, bool ov //cmMidiFileTickToSamples( mfH, cmTimeLineSampleRate(p->tlH), false ); // calculate MIDI note and pedal durations (see cmMidiChMsg_t.durTicks) - cmMidiFileCalcNoteDurations( mfH ); + cmMidiFileCalcNoteDurations( mfH, 0 ); unsigned i = 0; unsigned n = cmMidiFileMsgCount(mfH); diff --git a/src/app/cmTimeLine.c b/src/app/cmTimeLine.c index 708f7a0..2897353 100644 --- a/src/app/cmTimeLine.c +++ b/src/app/cmTimeLine.c @@ -733,7 +733,7 @@ cmTlRC_t _cmTlAllocMidiFileRecd( _cmTl_t* p, const cmChar_t* nameStr, const cmCh //cmMidiFileTickToSamples(mfH,p->srate,false); // assign note durations to all note-on msg's - cmMidiFileCalcNoteDurations(mfH); + cmMidiFileCalcNoteDurations(mfH,0); unsigned recdByteCnt = sizeof(cmTlMidiFile_t) + strlen(fn) + 1; diff --git a/src/app/cmXScore.c b/src/app/cmXScore.c index 3e3c75c..51e0e1f 100644 --- a/src/app/cmXScore.c +++ b/src/app/cmXScore.c @@ -4084,7 +4084,7 @@ cmXsRC_t cmXScoreTest( if( editFn!=NULL && !cmFsIsFile(editFn) ) { - cmRptPrintf(&ctx->rpt,"The edit file %s does not exist. A new edit file will be created.",editFn); + cmRptPrintf(&ctx->rpt,"The edit file %s does not exist. A new edit file will be created.\n",editFn); cmXScoreGenEditFile(ctx,xmlFn,editFn); editFn = NULL; } @@ -4101,14 +4101,14 @@ cmXsRC_t cmXScoreTest( _cmXsIsCsvValid(ctx,h,csvOutFn); } + + // measure the score complexity + double wndSecs = 1.0; + + _cmXsMeasComplexity(h,wndSecs); if( midiOutFn != NULL ) - { - // measure the score complexity - double wndSecs = 1.0; - - _cmXsMeasComplexity(h,wndSecs); - + { cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn); _cmXsWriteMidiFile(ctx, h, beginMeasNumb, beginBPM, pp->dirStr, pp->fnStr ); diff --git a/src/cmDList.c b/src/cmDList.c index 831b106..29b6a63 100644 --- a/src/cmDList.c +++ b/src/cmDList.c @@ -150,7 +150,7 @@ void _cmDListIndexFree( cmDList_t* p, cmDListIndex_t* x ) // x is the first index if( x0 == NULL ) { - assert( x1 = p->indexes ); + assert( x1 == p->indexes ); p->indexes = x->link; } else diff --git a/src/cmMidi.h b/src/cmMidi.h index 653941e..b9ae113 100644 --- a/src/cmMidi.h +++ b/src/cmMidi.h @@ -88,9 +88,9 @@ extern "C" { #define cmMidiIsStatus( s ) (kNoteOffMdId <= (s) /*&& ((unsigned)(s)) <= kSysRtResetMdId*/ ) #define cmMidiIsChStatus( s ) (kNoteOffMdId <= (s) && (s) < kSysExMdId) -#define cmMidiIsNoteOn( s ) ( kNoteOnMdId <= (s) && (s) <= (kNoteOnMdId + kMidiChCnt) ) -#define cmMidiIsNoteOff( s, d1 ) ( (cmMidiIsNoteOn(s) && (d1)==0) || (kNoteOffMdId <= (s) && (s) <= (kNoteOffMdId + kMidiChCnt)) ) -#define cmMidiIsCtl( s ) ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) ) +#define cmMidiIsNoteOn( s ) ( kNoteOnMdId <= (s) && (s) < (kNoteOnMdId + kMidiChCnt) ) +#define cmMidiIsNoteOff( s, d1 ) ( (cmMidiIsNoteOn(s) && (d1)==0) || (kNoteOffMdId <= (s) && (s) < (kNoteOffMdId + kMidiChCnt)) ) +#define cmMidiIsCtl( s ) ( kCtlMdId <= (s) && (s) < (kCtlMdId + kMidiChCnt) ) #define cmMidiIsSustainPedal( s, d0 ) ( kCtlMdId <= (s) && (s) <= (kCtlMdId + kMidiChCnt) && (d0)== kSustainCtlMdId ) #define cmMidiIsSustainPedalDown( s, d0, d1) ( cmMidiIsSustainPedal(s,d0) && (d1)>=64 ) diff --git a/src/cmMidiFile.c b/src/cmMidiFile.c index bf52be2..a47f413 100644 --- a/src/cmMidiFile.c +++ b/src/cmMidiFile.c @@ -423,6 +423,34 @@ cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp ) return rc; } +void _cmMidiFileDrop( _cmMidiFile_t* p ) +{ + unsigned i; + unsigned n = 0; + for(i=0; itrkN; ++i) + { + _cmMidiTrack_t* trk = p->trkV + i; + cmMidiTrackMsg_t* m0 = NULL; + cmMidiTrackMsg_t* m = trk->base; + + for(; m!=NULL; m=m->link) + { + if( cmIsFlag(m->flags,kDropTrkMsgFl) ) + { + ++n; + if( m0 == NULL ) + trk->base = m->link; + else + m0->link = m->link; + } + else + { + m0 = m; + } + } + } +} + int _cmMidiFileSortFunc( const void *p0, const void* p1 ) { if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick ) @@ -1369,7 +1397,6 @@ cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiBy mfp->msgVDirtyFl = true; return kOkMfRC; - } cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg ) @@ -1532,6 +1559,38 @@ unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, un return mi; } +/* +1.Move closest previous tempo msg to begin. +2.The first msg in each track must be the first msg >= begin.time +3.Remove all msgs > end.time - except the 'endMsg' for each note/pedal that is active at end time. + + + */ + +unsigned _cmMidiFileIsEndMsg( cmMidiTrackMsg_t* m, cmMidiTrackMsg_t** endMsgArray, unsigned n ) +{ + unsigned i = 0; + for(; imsgN-1 ]->amicro / 1000000.0; } -typedef struct _cmMidiVoice_str -{ - const cmMidiTrackMsg_t* mp; - unsigned durMicros; - bool sustainFl; - struct _cmMidiVoice_str* link; -} _cmMidiVoice_t; - void _cmMidiFileSetDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1 ) { @@ -1574,10 +1625,11 @@ bool _cmMidiFileCalcNoteDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1, int not return true; } -void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) +void cmMidiFileCalcNoteDurations( cmMidiFileH_t h, unsigned flags ) { _cmMidiFile_t* p; - + bool warningFl = cmIsFlag(flags,kWarningsMfFl); + if((p = _cmMidiFileHandleToPtr(h)) == NULL ) return; @@ -1586,13 +1638,14 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) unsigned mi = cmInvalidId; cmMidiTrackMsg_t* noteM[ kMidiNoteCnt * kMidiChCnt ]; // ptr to note-on or NULL if the note is not sounding - cmMidiTrackMsg_t* sustV[ kMidiChCnt ]; - cmMidiTrackMsg_t* sostV[ kMidiChCnt ]; + cmMidiTrackMsg_t* sustV[ kMidiChCnt ]; // ptr to last sustain pedal down msg or NULL if susteain pedal is not down + cmMidiTrackMsg_t* sostV[ kMidiChCnt ]; // ptr to last sost. pedal down msg or NULL if sost. pedal is not down int noteGateM[ kMidiNoteCnt * kMidiChCnt ]; // true if the associated note key is depressed bool sostGateM[ kMidiNoteCnt * kMidiChCnt ]; // true if the associated note was active when the sost. pedal went down int sustGateV[ kMidiChCnt]; // true if the associated sustain pedal is down int sostGateV[ kMidiChCnt]; // true if the associated sostenuto pedal is down unsigned i,j; + unsigned n = 0; const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p); @@ -1634,12 +1687,22 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) unsigned k = ch*kMidiNoteCnt + d0; // there should be no existing sounding note instance for this pitch - //if( noteGateM[k] == 0 && noteM[k] != NULL ) - // cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-off instance for note on:%s",m->uid,cmMidiToSciPitch(d0,NULL,0)); + if( noteGateM[k] == 0 && noteM[k] != NULL ) + { + if( warningFl ) + cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-off instance for note on:%s",m->uid,cmMidiToSciPitch(d0,NULL,0)); + if( cmIsFlag(flags,kDropReattacksMfFl) ) + { + m->flags |= kDropTrkMsgFl; + n += 1; + } + + } + // if this is a re-attack if( noteM[k] != NULL ) noteGateM[k] += 1; - else + else // this is a new attack { noteM[k] = m; noteGateM[k] = 1; @@ -1676,8 +1739,8 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) if( cmMidiFileIsSustainPedalDown(m) ) { // if the sustain channel is already down - //if( sustGateV[ch] ) - // cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal went down twice with no intervening release.",m->uid); + if( warningFl && sustGateV[ch] ) + cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal went down twice with no intervening release.",m->uid); sustGateV[ch] += 1; @@ -1722,8 +1785,8 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) if( cmMidiFileIsSostenutoPedalDown(m) ) { // if the sustain channel is already down - //if( sostGateV[ch] ) - // cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal went down twice with no intervening release.",m->uid); + if( warningFl && sostGateV[ch] ) + cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal went down twice with no intervening release.",m->uid); // record the notes that are active when the sostenuto pedal went down unsigned k = ch * kMidiNoteCnt; @@ -1770,6 +1833,46 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) } } // for each midi file event + + + if( warningFl ) + { + unsigned sustChN = 0; // count of channels where the sustain pedal was left on at the end of the file + unsigned sostChN = 0; // sostenuto + unsigned sustInstN = 0; // count of sustain on with no previous sustain off + unsigned sostInstN = 0; // sostenuto on + unsigned noteN = 0; // count of notes left on at the end of the file + unsigned noteInstN = 0; // count of reattacks + + // initialize the state tracking variables + for(i=0; ierr,kEventTerminationMfRC,"note:%i inst:%i sustain: %i inst: %i sost: %i inst: %i",noteN,noteInstN,sustChN,sustInstN,sostChN,sostInstN); + } + + // drop + if( cmIsFlag(flags,kDropReattacksMfFl) ) + _cmMidiFileDrop(p); + + + } void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks ) @@ -1833,15 +1936,16 @@ void _cmMidiFilePrintHdr( const _cmMidiFile_t* mfp, cmRpt_t* rpt ) cmRptPrintf(rpt,"fmt:%i ticksPerQN:%i tracks:%i\n",mfp->fmtId,mfp->ticksPerQN,mfp->trkN); - cmRptPrintf(rpt," UID dtick atick amicro type ch D0 D1\n"); - cmRptPrintf(rpt,"----- ---------- ---------- ---------- : ---- --- --- ---\n"); + cmRptPrintf(rpt," UID trk dtick atick amicro type ch D0 D1\n"); + cmRptPrintf(rpt,"----- --- ---------- ---------- ---------- : ---- --- --- ---\n"); } void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp ) { - cmRptPrintf(rpt,"%5i %10u %10llu %10llu : ", + cmRptPrintf(rpt,"%5i %3i %10u %10llu %10llu : ", tmp->uid, + tmp->trkIdx, tmp->dtick, tmp->atick, tmp->amicro ); @@ -1980,7 +2084,7 @@ cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmCh goto errLabel; } - cmMidiFileCalcNoteDurations( mfH ); + cmMidiFileCalcNoteDurations( mfH, 0 ); if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC ) return cmErrMsg(&p->err,kFileFailMfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn)); @@ -2013,7 +2117,7 @@ cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmCha goto errLabel; } - cmMidiFileCalcNoteDurations( mfH ); + cmMidiFileCalcNoteDurations( mfH, 0 ); msgN = cmMidiFileMsgCount(mfH); msgs = cmMidiFileMsgArray(mfH); @@ -2159,7 +2263,7 @@ void cmMidiFileTest( const char* fn, cmCtx_t* ctx ) return; } - cmMidiFileCalcNoteDurations( h ); + cmMidiFileCalcNoteDurations( h, 0 ); if( 1 ) { diff --git a/src/cmMidiFile.h b/src/cmMidiFile.h index 6775f31..b5428e2 100644 --- a/src/cmMidiFile.h +++ b/src/cmMidiFile.h @@ -63,11 +63,16 @@ extern "C" { struct cmMidiTrackMsg_str* end; // note-off or pedal-up message } cmMidiChMsg_t; + enum + { + kDropTrkMsgFl = 0x01 + }; typedef struct cmMidiTrackMsg_str { + unsigned flags; // see k???TrkMsgFl unsigned uid; // uid's are unique among all msg's in the file - unsigned dtick; // delta ticks between events on this track + unsigned dtick; // delta ticks between events on this track (ticks between this event and the previous event on this track) unsigned long long atick; // global (all tracks interleaved) accumulated ticks unsigned long long amicro; // global (all tracks interleaved) accumulated microseconds adjusted for tempo changes cmMidiByte_t status; // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch) @@ -91,9 +96,12 @@ extern "C" { } u; } cmMidiTrackMsg_t; -#define cmMidiFileIsNoteOn(m) (cmMidiIsNoteOn((m)->status) && (m)->u.chMsgPtr->d1>0) +#define cmMidiFileIsNoteOn(m) (cmMidiIsNoteOn((m)->status) && ((m)->u.chMsgPtr->d1>0)) #define cmMidiFileIsNoteOff(m) (cmMidiIsNoteOff((m)->status,(m)->u.chMsgPtr->d1)) - + +#define cmMidiFileIsPedalUp(m) (cmMidiIsPedalUp( (m)->status, (m)->u.chMsgPtr->d0, (m)->u.chMsgPtr->d1) ) +#define cmMidiFileIsPedalDown(m) (cmMidiIsPedalDown( (m)->status, (m)->u.chMsgPtr->d0, (m)->u.chMsgPtr->d1) ) + #define cmMidiFileIsSustainPedalUp(m) (cmMidiIsSustainPedalUp( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1)) #define cmMidiFileIsSustainPedalDown(m) (cmMidiIsSustainPedalDown( (m)->status,(m)->u.chMsgPtr->d0,(m)->u.chMsgPtr->d1)) @@ -118,7 +126,9 @@ extern "C" { kUidNotFoundMfRC, // 13 kUidNotANoteMsgMfRC, // 14 kInvalidTrkIndexMfRC,// 15 - kSvgFailMfRC // 16 + kSvgFailMfRC, // 16 + kMsgNotFoundMfRC, // 17 + kEventTerminationMfRC // 18 }; extern cmMidiFileH_t cmMidiFileNullHandle; @@ -172,6 +182,7 @@ extern "C" { // Set the velocity of a note-on/off msg identified by 'uid'. cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel ); + // Insert a MIDI message relative to the reference msg identified by 'uid'. // If dtick is positive/negative then the new msg is inserted after/before the reference msg. cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiByte_t ch, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 ); @@ -199,8 +210,9 @@ extern "C" { double cmMidiFileDurSecs( cmMidiFileH_t h ); - // Calculate Note Duration - void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ); + // Calculate Note Duration + enum { kWarningsMfFl=0x01, kDropReattacksMfFl=0x02 }; + void cmMidiFileCalcNoteDurations( cmMidiFileH_t h, unsigned flags ); // Set the delay prior to the first non-zero msg. void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks ); @@ -218,8 +230,7 @@ extern "C" { { unsigned uid; unsigned long long amicro; - unsigned density; - + unsigned density; } cmMidiFileDensity_t; // Generate the note onset density measure for each note in the MIDI file. diff --git a/src/cmXml.c b/src/cmXml.c index cfbd27d..05399ee 100644 --- a/src/cmXml.c +++ b/src/cmXml.c @@ -650,6 +650,9 @@ cmXmlRC_t _cmXmlReadNode( cmXml_t* p, cmXmlNode_t* parent ) return rc; } + if( np==NULL && p->stack==NULL) + break; + // if an end-tag was just read or node was created but closed then pop the stack if( np==NULL || (np==p->stack && cmIsFlag(np->flags,kClosedXmlFl)) ) p->stack = p->stack->parent;