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:
parent
acf2a85ff8
commit
c8062faafe
160
cmMidiFile.c
160
cmMidiFile.c
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
cmMidiFile.h
15
cmMidiFile.h
@ -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 );
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user