cmMidiFile.h/c : Fixed bug where tempo changes were not being applied dtick instead of atick.

Added atick,dmicro, and amicro field to cmMidiTrackMsg_t.
Replaced durTicks w/ durMicros in cmMidiChMsg_t.
Deleted cmMidiFIleTickToMicros() and cmMidiFileTickToSamples().
This commit is contained in:
kevin 2015-11-19 19:08:39 -05:00
parent acf2a85ff8
commit c8062faafe
2 changed files with 76 additions and 99 deletions

View File

@ -246,7 +246,7 @@ cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmM
tmp->byteCnt = sizeof(cmMidiChMsg_t); tmp->byteCnt = sizeof(cmMidiChMsg_t);
tmp->status = statusCh & 0xf0; tmp->status = statusCh & 0xf0;
p->ch = statusCh & 0x0f; p->ch = statusCh & 0x0f;
p->durTicks = 0; p->durMicros = 0;
unsigned byteN = cmMidiStatusToByteCount(tmp->status); unsigned byteN = cmMidiStatusToByteCount(tmp->status);
@ -416,8 +416,6 @@ cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp )
int _cmMidiFileSortFunc( const void *p0, const void* p1 ) 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 ) if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick )
return 0; 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[] // store a pointer to every trk msg in msgV[]
// and convert tick to absolute tick // and convert tick to absolute tick
mfp->nextUid = 0; mfp->nextUid = 0;
double microsPerQN = 60000000/120; // default tempo;
double microsPerTick;
unsigned i = 0; unsigned i = 0;
for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx) for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
{ {
unsigned tick = 0; unsigned tick = 0;
cmMidiTrackMsg_t* tmp = mfp->trkV[ trkIdx ].base; cmMidiTrackMsg_t* tmp = mfp->trkV[ trkIdx ].base;
microsPerTick = microsPerQN / mfp->ticksPerQN;
while( tmp != NULL ) while( tmp != NULL )
{ {
assert( i < mfp->msgN); assert( i < mfp->msgN);
@ -540,6 +546,14 @@ cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx )
tmp->atick = tick; tmp->atick = tick;
tmp->uid = mfp->nextUid++; // assign the msg uid tmp->uid = mfp->nextUid++; // assign the msg uid
mfp->msgV[i] = tmp; 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; tmp = tmp->link;
++i; ++i;
} }
@ -548,6 +562,31 @@ cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx )
// sort msgV[] in ascending order on atick // sort msgV[] in ascending order on atick
qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc ); 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; mi<mfp->msgN; ++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) //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); // 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; mi<p->msgN; ++mi) for(mi=0; mi<p->msgN; ++mi)
{ {
const cmMidiTrackMsg_t* mp = p->msgV[mi]; const cmMidiTrackMsg_t* mp = p->msgV[mi];
/*
if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
microsPerTick = mp->u.iVal / p->ticksPerQN; 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 ) if( accUSecs >= offsUSecs )
break; break;
*/
if( mp->amicro >= offsUSecs )
break;
} }
if( mi == p->msgN ) if( mi == p->msgN )
@ -1080,90 +1129,17 @@ unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned offsUSecs, unsigned* ms
double cmMidiFileDurSecs( cmMidiFileH_t h ) double cmMidiFileDurSecs( cmMidiFileH_t h )
{ {
_cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(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; mi<mfp->msgN; ++mi) if( mfp->msgN == 0 )
{ return 0;
cmMidiTrackMsg_t* mp = mfp->msgV[mi];
if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) return mfp->msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
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; mi<p->msgN; ++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);
}
}
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; mi<p->msgN; ++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 typedef struct _cmMidiVoice_str
{ {
const cmMidiTrackMsg_t* mp; const cmMidiTrackMsg_t* mp;
unsigned durTicks; unsigned durMicros;
bool sustainFl; bool sustainFl;
struct _cmMidiVoice_str* link; struct _cmMidiVoice_str* link;
} _cmMidiVoice_t; } _cmMidiVoice_t;
@ -1176,7 +1152,7 @@ void _cmMidFileCalcNoteDurationReleaseNote( _cmMidiVoice_t** listPtrPtr, _cmMidi
// store the duration of the note into the track msg // store the duration of the note into the track msg
// assoc'd with the note-on msg // assoc'd with the note-on msg
cmMidiChMsg_t* cmp = (cmMidiChMsg_t*)vp->mp->u.chMsgPtr; // cast away const 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; _cmMidiVoice_t* np = vp->link;
@ -1224,13 +1200,20 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
{ {
cmMidiTrackMsg_t* mp = p->msgV[mi]; 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 // update the duration of the sounding notes
for(vp = list; vp!=NULL; vp=vp->link) for(vp = list; vp!=NULL; vp=vp->link)
vp->durTicks += mp->dtick; vp->durMicros += d_amicro;
// update the sustain pedal duration // update the sustain pedal duration
if( sustainPedalDownMsg != NULL ) 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 // If this is sustain pedal msg
@ -1255,7 +1238,7 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
else else
{ {
sustainPedalDownMsg = mp; 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 ); _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 ) void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
{ {
//cmRptPrintf(rpt,"%5.5f ", tmp->dtick/1000000.0 ); cmRptPrintf(rpt,"%8i %8i %8i %8i : ", tmp->dtick, tmp->dmicro, tmp->atick, tmp->amicro );
cmRptPrintf(rpt,"%f ", (double)tmp->dtick );
if( tmp->status == kMetaStId ) if( tmp->status == kMetaStId )
cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId)); cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId));
@ -1502,7 +1484,7 @@ void cmMidiFileTest( const char* fn, cmCtx_t* ctx )
if( 1 ) if( 1 )
{ {
//cmMidiFileTickToMicros( h ); //cmMidiFileTickToMicros( h );
cmMidiFileTickToSamples(h,96000,false); //cmMidiFileTickToSamples(h,96000,false);
cmMidiFilePrintMsgs(h,&ctx->rpt); cmMidiFilePrintMsgs(h,&ctx->rpt);
} }

View File

@ -57,15 +57,17 @@ extern "C" {
cmMidiByte_t ch; cmMidiByte_t ch;
cmMidiByte_t d0; cmMidiByte_t d0;
cmMidiByte_t d1; cmMidiByte_t d1;
unsigned durTicks; // note duration calc'd by cmMidiFileCalcNoteDurations(); unsigned durMicros; // note duration in microseconds (corrected for tempo changes)
} cmMidiChMsg_t; } cmMidiChMsg_t;
typedef struct cmMidiTrackMsg_str typedef struct cmMidiTrackMsg_str
{ {
unsigned uid; // uid's are unique among all msg's in the file unsigned uid; // uid's are unique among all msg's in the file
unsigned dtick; // delta ticks unsigned dtick; // delta ticks between events on this track
unsigned atick; // accumulated ticks 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 status; // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
cmMidiByte_t metaId; // cmMidiByte_t metaId; //
unsigned short trkIdx; // unsigned short trkIdx; //
@ -153,13 +155,6 @@ extern "C" {
double cmMidiFileDurSecs( cmMidiFileH_t h ); 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 // Calculate Note Duration
void cmMidiFileCalcNoteDurations( cmMidiFileH_t h ); void cmMidiFileCalcNoteDurations( cmMidiFileH_t h );