cmMidiFile.h/c : Added cmMidiFileInsertMsg() Also rearranged code to isolate

cmMidiTrackMsg_t allocation.  These changes are not yet tested.
This commit is contained in:
kevin 2016-06-22 17:08:47 -04:00
parent 21d13a94fd
commit 5cde332add
2 changed files with 185 additions and 72 deletions

View File

@ -19,8 +19,6 @@
#define mfSwap32(v) cmSwap32(v) #define mfSwap32(v) cmSwap32(v)
#endif #endif
typedef struct typedef struct
{ {
unsigned cnt; // count of track records unsigned cnt; // count of track records
@ -160,10 +158,25 @@ cmMfRC_t _cmMidiFileReadVarLen( _cmMidiFile_t* mfp, unsigned* p )
return kOkMfRC; return kOkMfRC;
} }
cmMfRC_t _cmMidiFileAppendTrackMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status, cmMidiTrackMsg_t** trkMsgPtrPtr ) cmMidiTrackMsg_t* _cmMidiFileAllocMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status )
{ {
cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)_cmMidiFileMalloc(mfp, sizeof(cmMidiTrackMsg_t) ); cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)_cmMidiFileMalloc(mfp, sizeof(cmMidiTrackMsg_t) );
// set the generic track record fields
tmp->dtick = dtick;
tmp->status = status;
tmp->metaId = kInvalidMetaMdId;
tmp->trkIdx = trkIdx;
tmp->byteCnt = 0;
tmp->uid = mfp->nextUid++;
return tmp;
}
cmMfRC_t _cmMidiFileAppendTrackMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status, cmMidiTrackMsg_t** trkMsgPtrPtr )
{
cmMidiTrackMsg_t* tmp = _cmMidiFileAllocMsg( mfp, trkIdx, dtick, status );
// link new record onto track record chain // link new record onto track record chain
if( mfp->trkV[trkIdx].base == NULL ) if( mfp->trkV[trkIdx].base == NULL )
mfp->trkV[trkIdx].base = tmp; mfp->trkV[trkIdx].base = tmp;
@ -174,12 +187,6 @@ cmMfRC_t _cmMidiFileAppendTrackMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, u
mfp->trkV[trkIdx].cnt++; mfp->trkV[trkIdx].cnt++;
// set the generic track record fields
tmp->dtick = dtick;
tmp->status = status;
tmp->metaId = kInvalidMetaMdId;
tmp->trkIdx = trkIdx;
tmp->byteCnt = 0;
*trkMsgPtrPtr = tmp; *trkMsgPtrPtr = tmp;
return kOkMfRC; return kOkMfRC;
@ -521,6 +528,43 @@ cmMfRC_t _cmMidiFileClose( _cmMidiFile_t* mfp )
} }
void _cmMidiFileLinearize( _cmMidiFile_t* mfp )
{
unsigned trkIdx,i,j;
// get the total trk msg count
mfp->msgN = 0;
for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
mfp->msgN += mfp->trkV[ trkIdx ].cnt;
// allocate the trk msg index vector: msgV[]
mfp->msgV = cmMemResizeZ(cmMidiTrackMsg_t*, mfp->msgV, mfp->msgN);
// store a pointer to every trk msg in msgV[]
for(i=0,j=0; i<mfp->trkN; ++i)
{
cmMidiTrackMsg_t* m = mfp->trkV[i].base;
for(; m!=NULL; m=m->link)
{
assert( j < mfp->msgN );
mfp->msgV[j++] = m;
}
}
// set the atick value in each msg
_cmMidiFileSetAccumulateTicks(mfp);
// sort msgV[] in ascending order on atick
qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc );
// set the amicro value in each msg
_cmMidiFileSetAbsoluteTime(mfp);
}
cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hPtr, const char* fn ) cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hPtr, const char* fn )
{ {
@ -528,7 +572,6 @@ cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hPtr, const char* fn )
_cmMidiFile_t* mfp = NULL; _cmMidiFile_t* mfp = NULL;
unsigned short trkIdx = 0; unsigned short trkIdx = 0;
cmErr_t err; cmErr_t err;
unsigned i,j;
if( cmMidiFileIsValid(*hPtr) ) if( cmMidiFileIsValid(*hPtr) )
if((rc = _cmMidiFileClose(_cmMidiFileHandleToPtr(*hPtr))) != kOkMfRC ) if((rc = _cmMidiFileClose(_cmMidiFileHandleToPtr(*hPtr))) != kOkMfRC )
@ -562,7 +605,7 @@ cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hPtr, const char* fn )
while( !cmFileEof(mfp->fh) && trkIdx < mfp->trkN ) while( !cmFileEof(mfp->fh) && trkIdx < mfp->trkN )
{ {
unsigned chkId=0,chkN=0; unsigned chkId = 0,chkN=0;
// read the chunk id // read the chunk id
if((rc = _cmMidiFileRead32(mfp,&chkId)) != kOkMfRC ) if((rc = _cmMidiFileRead32(mfp,&chkId)) != kOkMfRC )
@ -591,43 +634,12 @@ cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hPtr, const char* fn )
} }
} }
// get the total trk msg count // store the file name
mfp->msgN = 0;
for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
mfp->msgN += mfp->trkV[ trkIdx ].cnt;
// allocate the trk msg index vector: msgV[]
mfp->msgV = cmMemAllocZ(cmMidiTrackMsg_t*, mfp->msgN);
mfp->nextUid = 0;
// store a pointer to every trk msg in msgV[] and set 'uid'
for(i=0,j=0; i<mfp->trkN; ++i)
{
cmMidiTrackMsg_t* m = mfp->trkV[i].base;
for(; m!=NULL; m=m->link)
{
assert( j < mfp->msgN );
mfp->msgV[j++] = m;
m->uid = mfp->nextUid++;
}
}
mfp->fn = _cmMidiFileMalloc(mfp,strlen(fn)+1); mfp->fn = _cmMidiFileMalloc(mfp,strlen(fn)+1);
assert( mfp->fn != NULL ); assert( mfp->fn != NULL );
strcpy(mfp->fn,fn); strcpy(mfp->fn,fn);
_cmMidiFileLinearize(mfp);
// set the atick value in each msg
_cmMidiFileSetAccumulateTicks(mfp);
// sort msgV[] in ascending order on atick
qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc );
// set the amicro value in each msg
_cmMidiFileSetAbsoluteTime(mfp);
hPtr->h = mfp; hPtr->h = mfp;
@ -686,7 +698,7 @@ cmMfRC_t _cmMidiFileWrite24( _cmMidiFile_t* mfp, unsigned v )
unsigned mask = 0xff0000; unsigned mask = 0xff0000;
int i; int i;
for(i=2; i>=0; --i) for(i = 2; i>=0; --i)
{ {
unsigned char c = (v & mask) >> (i*8); unsigned char c = (v & mask) >> (i*8);
mask >>= 8; mask >>= 8;
@ -919,7 +931,7 @@ cmMfRC_t _cmMidiFileWriteTrack( _cmMidiFile_t* mfp, unsigned trkIdx )
cmMidiTrackMsg_t* tmp = mfp->trkV[trkIdx].base; cmMidiTrackMsg_t* tmp = mfp->trkV[trkIdx].base;
cmMidiByte_t runStatus = 0; cmMidiByte_t runStatus = 0;
for(; tmp!=NULL; tmp=tmp->link) for(; tmp != NULL; tmp=tmp->link)
{ {
// write the msg tick count // write the msg tick count
if((rc = _cmMidiFileWriteVarLen(mfp,tmp->dtick)) != kOkMfRC ) if((rc = _cmMidiFileWriteVarLen(mfp,tmp->dtick)) != kOkMfRC )
@ -968,7 +980,7 @@ cmMfRC_t cmMidiFileWrite( cmMidiFileH_t h, const char* fn )
for(i=0; i < mfp->trkN; ++i ) for(i=0; i < mfp->trkN; ++i )
{ {
unsigned chkId='MTrk'; unsigned chkId = 'MTrk';
long offs0,offs1; long offs0,offs1;
// write the track chunk id ('MTrk') // write the track chunk id ('MTrk')
@ -1131,7 +1143,7 @@ cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel
if((r = _cmMidiFileUidToMsg(mfp,uid)) == NULL ) if((r = _cmMidiFileUidToMsg(mfp,uid)) == NULL )
return cmErrMsg(&mfp->err,kUidNotFoundMfRC,"The MIDI file uid %i could not be found.",uid); return cmErrMsg(&mfp->err,kUidNotFoundMfRC,"The MIDI file uid %i could not be found.",uid);
if( cmMidiIsNoteOn(r->status)==false && cmMidiIsNoteOff(r->status,0)==false ) if( cmMidiIsNoteOn(r->status) == false && cmMidiIsNoteOff(r->status,0)==false )
return cmErrMsg(&mfp->err,kUidNotANoteMsgMfRC,"Cannot set velocity on a non-Note-On/Off msg."); return cmErrMsg(&mfp->err,kUidNotANoteMsgMfRC,"Cannot set velocity on a non-Note-On/Off msg.");
cmMidiChMsg_t* chm = (cmMidiChMsg_t*)r->u.chMsgPtr; cmMidiChMsg_t* chm = (cmMidiChMsg_t*)r->u.chMsgPtr;
@ -1141,6 +1153,103 @@ cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel
return kOkMfRC; return kOkMfRC;
} }
// Returns NULL if uid is not found or if it the first msg on the track.
cmMidiTrackMsg_t* _cmMidiFileMsgBeforeUid( _cmMidiFile_t* p, unsigned uid )
{
cmMidiTrackMsg_t* m;
if((m = _cmMidiFileUidToMsg(p,uid)) == NULL )
return NULL;
assert( m->trkIdx < p->trkN );
cmMidiTrackMsg_t* m0 = NULL;
cmMidiTrackMsg_t* m1 = p->trkV[ m->trkIdx ].base;
for(; m1!=NULL; m1 = m1->link)
{
if( m1->uid == uid )
break;
m0 = m1;
}
return m0;
}
unsigned _cmMidiFileIsMsgFirstOnTrack( _cmMidiFile_t* p, unsigned uid )
{
unsigned i;
for(i=0; i<p->trkN; ++i)
if( p->trkV[i].base!=NULL && p->trkV[i].base->uid == uid )
return i;
return cmInvalidIdx;
}
cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiByte_t ch, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 )
{
_cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
assert( mfp != NULL );
cmMidiTrackMsg_t* ref = NULL;
unsigned trkIdx = cmInvalidIdx;
// if dtick is positive ...
if( dtick >= 0 )
{
ref = _cmMidiFileUidToMsg(mfp,uid); // ... then get the ref. msg.
trkIdx = ref->trkIdx;
}
else // if dtick is negative ...
{
// ... get get the msg before the ref. msg.
if((ref = _cmMidiFileMsgBeforeUid(mfp,uid)) == NULL )
{
// ... the ref. msg was first in the track so there is no msg before it
trkIdx = _cmMidiFileIsMsgFirstOnTrack(mfp,uid);
}
}
// verify that the reference msg was found
if( trkIdx == cmInvalidIdx )
return cmErrMsg(&mfp->err,kUidNotFoundMfRC,"The UID (%i) reference note could not be located.",uid);
assert( trkIdx < mfp->trkN );
// complete the msg setup
_cmMidiTrack_t* trk = mfp->trkV + trkIdx;
cmMidiTrackMsg_t* m = _cmMidiFileAllocMsg(mfp, trkIdx, abs(dtick), status );
cmMidiChMsg_t* c = (cmMidiChMsg_t*)m->u.chMsgPtr; // cast away const
c->ch = ch;
c->d0 = d0;
c->d1 = d1;
// if 'm' is prior to the first msg in the track
if( ref == NULL )
{
// ... then make 'm' the first msg in the first msg
m->link = trk->base;
trk->base = m;
// 'm' is before ref and the track cannot be empty (because ref is in it) 'm'
// can never be the last msg in the list
}
else // ref is the msg before 'm'
{
m->link = ref->link;
ref->link = m;
// if ref was the last msg in the trk ...
if( trk->last == ref )
trk->last = m; //... then 'm' is now the last msg in the trk
}
trk->cnt += 1;
_cmMidiFileLinearize(mfp);
return kOkMfRC;
}
unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, unsigned* msgUsecsPtr, unsigned* microsPerTickPtr ) unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, unsigned* msgUsecsPtr, unsigned* microsPerTickPtr )
{ {
_cmMidiFile_t* p; _cmMidiFile_t* p;

View File

@ -161,6 +161,10 @@ extern "C" {
// Set the velocity of a note-on/off msg identified by 'uid'. // Set the velocity of a note-on/off msg identified by 'uid'.
cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel ); 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 );
// Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no // Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no
// msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning // msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning
// of the file. // of the file.