diff --git a/cmMidiFile.c b/cmMidiFile.c index 58e04d4..2476aa1 100644 --- a/cmMidiFile.c +++ b/cmMidiFile.c @@ -246,7 +246,7 @@ cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmM tmp->byteCnt = sizeof(cmMidiChMsg_t); tmp->status = statusCh & 0xf0; p->ch = statusCh & 0x0f; - p->durTicks = 0; + p->durMicros = 0; unsigned byteN = cmMidiStatusToByteCount(tmp->status); @@ -416,8 +416,6 @@ cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp ) int _cmMidiFileSortFunc( const void *p0, const void* p1 ) { - //printf("%i %i\n",(*(cmMidiTrackMsg_t**)p0)->dticks,(*(cmMidiTrackMsg_t**)p1)->dticks); - if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick ) return 0; @@ -526,12 +524,20 @@ cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx ) // store a pointer to every trk msg in msgV[] // and convert tick to absolute tick mfp->nextUid = 0; + + double microsPerQN = 60000000/120; // default tempo; + double microsPerTick; + unsigned i = 0; for(trkIdx=0; trkIdxtrkN; ++trkIdx) { unsigned tick = 0; cmMidiTrackMsg_t* tmp = mfp->trkV[ trkIdx ].base; + + microsPerTick = microsPerQN / mfp->ticksPerQN; + + while( tmp != NULL ) { assert( i < mfp->msgN); @@ -540,6 +546,14 @@ cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx ) tmp->atick = tick; tmp->uid = mfp->nextUid++; // assign the msg uid mfp->msgV[i] = tmp; + + // track tempo changes + if( tmp->status == kMetaStId && tmp->metaId == kTempoMdId ) + microsPerTick = tmp->u.iVal / mfp->ticksPerQN; + + // convert dtick to microseconds + tmp->dmicro = round(tmp->dtick * microsPerTick); + tmp = tmp->link; ++i; } @@ -548,6 +562,31 @@ cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx ) // sort msgV[] in ascending order on atick qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc ); + // set the amicro field of each midi message to the + // absolute time offset in microseconds + unsigned mi; + unsigned amicro = 0; + microsPerTick = microsPerQN / mfp->ticksPerQN; + + for(mi=0; mimsgN; ++mi) + { + cmMidiTrackMsg_t* mp = mfp->msgV[mi]; + + // track tempo changes + if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) + microsPerTick = mp->u.iVal / mfp->ticksPerQN; + + unsigned dtick = 0; + if( mi > 0 ) + { + assert( mp->atick >= mfp->msgV[mi-1]->atick ); + dtick = mp->atick - mfp->msgV[mi-1]->atick; + } + + amicro += round(microsPerTick*dtick); + mp->amicro = amicro; + } + //for(i=0; i<25; ++i) // printf("%i 0x%x 0x%x\n",mfp->msgV[i]->tick,mfp->msgV[i]->status,mfp->msgV[i]->metaId); @@ -1054,15 +1093,25 @@ unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned offsUSecs, unsigned* ms for(mi=0; mimsgN; ++mi) { const cmMidiTrackMsg_t* mp = p->msgV[mi]; - + /* if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) microsPerTick = mp->u.iVal / p->ticksPerQN; - accUSecs += mp->dtick * microsPerTick ; + unsigned dtick = 0; + if( mi > 0 ) + { + assert( mp->atick >= p->msgV[mi-1]->atick ) + dtick = mp->atick - p->msgV[mi-1]->atick; + } + + accUSecs += dtick * microsPerTick ; if( accUSecs >= offsUSecs ) break; + */ + if( mp->amicro >= offsUSecs ) + break; } if( mi == p->msgN ) @@ -1080,90 +1129,17 @@ unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned offsUSecs, unsigned* ms double cmMidiFileDurSecs( cmMidiFileH_t h ) { _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h); - unsigned mi; - double durSecs = 0; - double r = 1.0; //1000.0/(1000-.8); - double microsPerQN = r*60000000.0/120.0; - double microsPerTick = microsPerQN / mfp->ticksPerQN; - for(mi=0; mimsgN; ++mi) - { - cmMidiTrackMsg_t* mp = mfp->msgV[mi]; - - if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) - microsPerTick = r*mp->u.iVal / mfp->ticksPerQN; - - // update the accumulated seconds - durSecs += (mp->dtick * microsPerTick) / 1000000.0; - - } - - return durSecs; -} - -void cmMidiFileTickToMicros( cmMidiFileH_t h ) -{ - _cmMidiFile_t* p; - - if((p = _cmMidiFileHandleToPtr(h)) == NULL ) - return; - - if( p->msgN == 0 ) - return; - - unsigned mi; - double microsPerQN = 60000000/120; // default tempo - double microsPerTick = microsPerQN / p->ticksPerQN; - - for(mi=0; mimsgN; ++mi) - { - cmMidiTrackMsg_t* mp = p->msgV[mi]; - - if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) - microsPerTick = mp->u.iVal / p->ticksPerQN; - - mp->dtick = round(microsPerTick*mp->dtick); - } + if( mfp->msgN == 0 ) + return 0; + return mfp->msgV[ mfp->msgN-1 ]->amicro / 1000000.0; } -void cmMidiFileTickToSamples( cmMidiFileH_t h, double srate, bool absFl ) -{ - _cmMidiFile_t* p; - - if((p = _cmMidiFileHandleToPtr(h)) == NULL ) - return; - - if( p->msgN == 0 ) - return; - - unsigned mi; - double microsPerQN = 60000000/120; // default tempo - double microsPerTick = microsPerQN / p->ticksPerQN; - double absSmp = 0; - - for(mi=0; mimsgN; ++mi) - { - cmMidiTrackMsg_t* mp = p->msgV[mi]; - - if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) - microsPerTick = mp->u.iVal / p->ticksPerQN; - - double delta = microsPerTick*mp->dtick*srate/1000000.0; - - absSmp += delta; - - mp->dtick = round(absFl ? absSmp : delta); - - } - -} - - typedef struct _cmMidiVoice_str { const cmMidiTrackMsg_t* mp; - unsigned durTicks; + unsigned durMicros; bool sustainFl; struct _cmMidiVoice_str* link; } _cmMidiVoice_t; @@ -1176,7 +1152,7 @@ void _cmMidFileCalcNoteDurationReleaseNote( _cmMidiVoice_t** listPtrPtr, _cmMidi // store the duration of the note into the track msg // assoc'd with the note-on msg cmMidiChMsg_t* cmp = (cmMidiChMsg_t*)vp->mp->u.chMsgPtr; // cast away const - cmp->durTicks = vp->durTicks; + cmp->durMicros = vp->durMicros; _cmMidiVoice_t* np = vp->link; @@ -1224,13 +1200,20 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) { cmMidiTrackMsg_t* mp = p->msgV[mi]; + unsigned d_amicro = 0; + if( mi > 0 ) + { + assert( mp->amicro >= p->msgV[mi-1]->amicro ); + d_amicro = mp->amicro - p->msgV[mi-1]->amicro; + } + // update the duration of the sounding notes for(vp = list; vp!=NULL; vp=vp->link) - vp->durTicks += mp->dtick; + vp->durMicros += d_amicro; // update the sustain pedal duration if( sustainPedalDownMsg != NULL ) - ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durTicks += mp->dtick; // cast away const + ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durMicros += d_amicro; // cast away const // // If this is sustain pedal msg @@ -1255,7 +1238,7 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ) else { sustainPedalDownMsg = mp; - ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durTicks = 0; // cast away const + ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durMicros = 0; // cast away const } _cmMidiFileCalcNoteDurationsAllocVoice( &list, mp, true ); @@ -1397,8 +1380,7 @@ void _cmMidiFilePrintHdr( const _cmMidiFile_t* mfp, cmRpt_t* rpt ) void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp ) { - //cmRptPrintf(rpt,"%5.5f ", tmp->dtick/1000000.0 ); - cmRptPrintf(rpt,"%f ", (double)tmp->dtick ); + cmRptPrintf(rpt,"%8i %8i %8i %8i : ", tmp->dtick, tmp->dmicro, tmp->atick, tmp->amicro ); if( tmp->status == kMetaStId ) cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId)); @@ -1502,7 +1484,7 @@ void cmMidiFileTest( const char* fn, cmCtx_t* ctx ) if( 1 ) { //cmMidiFileTickToMicros( h ); - cmMidiFileTickToSamples(h,96000,false); + //cmMidiFileTickToSamples(h,96000,false); cmMidiFilePrintMsgs(h,&ctx->rpt); } diff --git a/cmMidiFile.h b/cmMidiFile.h index 47b607d..7dfcd93 100644 --- a/cmMidiFile.h +++ b/cmMidiFile.h @@ -57,15 +57,17 @@ extern "C" { cmMidiByte_t ch; cmMidiByte_t d0; cmMidiByte_t d1; - unsigned durTicks; // note duration calc'd by cmMidiFileCalcNoteDurations(); + unsigned durMicros; // note duration in microseconds (corrected for tempo changes) } cmMidiChMsg_t; typedef struct cmMidiTrackMsg_str { unsigned uid; // uid's are unique among all msg's in the file - unsigned dtick; // delta ticks - unsigned atick; // accumulated ticks + unsigned dtick; // delta ticks between events on this track + unsigned dmicro; // delta microseconds between events on this track adjusted for tempo changes + unsigned atick; // global (all tracks interleaved) accumulated ticks + unsigned 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) cmMidiByte_t metaId; // unsigned short trkIdx; // @@ -153,13 +155,6 @@ extern "C" { double cmMidiFileDurSecs( cmMidiFileH_t h ); - // Convert the track message 'dtick' field to delta-microseconds. - void cmMidiFileTickToMicros( cmMidiFileH_t h ); - - // Convert the track message 'dtick' field to samples. - // If the absFl is set then the delta times are converted to absolute time. - void cmMidiFileTickToSamples( cmMidiFileH_t h, double srate, bool absFl ); - // Calculate Note Duration void cmMidiFileCalcNoteDurations( cmMidiFileH_t h );