cmMidiFile.h/c : Added flags arg. to cmMidiFileCalcNoteDurations(), Added cmMidiFileIsPedalUp/Down() macro.

cmMidi.h : Fixed bug in cmMidiIsNoteOn() macro.
This commit is contained in:
kevin 2020-08-01 08:07:50 -04:00
parent 4d5e88e766
commit f3af534ad5
9 changed files with 167 additions and 48 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
@ -4102,13 +4102,13 @@ cmXsRC_t cmXScoreTest(
_cmXsIsCsvValid(ctx,h,csvOutFn);
}
if( midiOutFn != NULL )
{
// measure the score complexity
double wndSecs = 1.0;
_cmXsMeasComplexity(h,wndSecs);
if( midiOutFn != NULL )
{
cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
_cmXsWriteMidiFile(ctx, h, beginMeasNumb, beginBPM, pp->dirStr, pp->fnStr );

View File

@ -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

View File

@ -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 )

View File

@ -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; i<p->trkN; ++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(; i<n; ++i)
if( endMsgArray[i] == m )
return i;
return cmInvalidIdx;
}
bool _cmMidiFileAllEndMsgFound( cmMidiTrackMsg_t** noteMsgArray, unsigned n0, cmMidiTrackMsg_t** pedalMsgArray, unsigned n1 )
{
unsigned i=0;
for(; i<n0; ++i)
if( noteMsgArray[i] != NULL )
return false;
for(i=0; i<n1; ++i)
if( pedalMsgArray[i] != NULL )
return false;
return true;
}
double cmMidiFileDurSecs( cmMidiFileH_t h )
{
_cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
@ -1544,14 +1603,6 @@ double cmMidiFileDurSecs( cmMidiFileH_t h )
return msgV[ mfp->msgN-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,9 +1625,10 @@ 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; i<kMidiChCnt; ++i)
{
if( sustV[i]!=NULL )
sustChN += 1;
sustInstN += sustGateV[i];
if( sostV[i] != NULL )
sostChN += 1;
sostInstN += sostGateV[i] = 0;
for(j=0; j<kMidiNoteCnt; ++j)
{
noteN += noteM[ i*kMidiNoteCnt + j ] != NULL;
noteInstN += noteGateM[ i*kMidiNoteCnt + j ];
}
}
cmErrWarnMsg(&p->err,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 )
{

View File

@ -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 );
@ -200,7 +211,8 @@ extern "C" {
double cmMidiFileDurSecs( cmMidiFileH_t h );
// Calculate Note Duration
void cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
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 );
@ -219,7 +231,6 @@ extern "C" {
unsigned uid;
unsigned long long amicro;
unsigned density;
} cmMidiFileDensity_t;
// Generate the note onset density measure for each note in the MIDI file.

View File

@ -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;