cmMidiFile.h/c : Completed cmMidiFileInsertTrackMsg() and added _cmMidiFile_t.msgVDirtyFl and associated processing.

This commit is contained in:
kevin 2016-07-27 19:03:42 -04:00
parent 198aa41b98
commit 49943bd43e
2 changed files with 121 additions and 54 deletions

View File

@ -40,12 +40,14 @@ typedef struct
char* fn; // file name or NULL if this object did not originate from a file
unsigned msgN; // count of msg's in msgV[]
cmMidiTrackMsg_t** msgV; // sorted msg list
bool msgVDirtyFl; // msgV[] needs to be refreshed from trkV[] because new msg's were inserted.
unsigned nextUid; // next available msg uid
} _cmMidiFile_t;
cmMidiFileH_t cmMidiFileNullHandle = cmSTATIC_NULL_HANDLE;
const cmMidiTrackMsg_t** _cmMidiFileMsgArray( _cmMidiFile_t* p );
_cmMidiFile_t* _cmMidiFileHandleToPtr( cmMidiFileH_t h )
{
@ -435,9 +437,9 @@ void _cmMidiFileSetAccumulateTicks( _cmMidiFile_t* p )
unsigned i;
bool fl = true;
// iniitalize nextTrkTick[] and nextTrkMsg[].
// iniitalize nextTrkTick[] and nextTrkMsg[] to the first msg in each track
for(i=0; i<p->trkN; ++i)
if((nextTrkMsg[i] = p->trkV[i].base) != NULL )
if((nextTrkMsg[i] = p->trkV[i].base) != NULL )
nextTrkMsg[i]->atick = nextTrkMsg[i]->dtick;
while(1)
@ -474,21 +476,23 @@ void _cmMidiFileSetAccumulateTicks( _cmMidiFile_t* p )
void _cmMidiFileSetAbsoluteTime( _cmMidiFile_t* mfp )
{
double microsPerQN = 60000000/120; // default tempo;
double microsPerTick = microsPerQN / mfp->ticksPerQN;
unsigned long long amicro = 0;
unsigned i;
const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
double microsPerQN = 60000000/120; // default tempo;
double microsPerTick = microsPerQN / mfp->ticksPerQN;
unsigned long long amicro = 0;
unsigned i;
for(i=0; i<mfp->msgN; ++i)
{
cmMidiTrackMsg_t* mp = mfp->msgV[i];
cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)msgV[i]; // cast away const
unsigned dtick = 0;
if( i > 0 )
{
// atick must have already been set and sorted
assert( mp->atick >= mfp->msgV[i-1]->atick );
dtick = mp->atick - mfp->msgV[i-1]->atick;
assert( mp->atick >= msgV[i-1]->atick );
dtick = mp->atick - msgV[i-1]->atick;
}
amicro += microsPerTick * dtick;
@ -524,9 +528,13 @@ cmMfRC_t _cmMidiFileClose( _cmMidiFile_t* mfp )
}
void _cmMidiFileLinearize( _cmMidiFile_t* mfp )
{
unsigned trkIdx,i,j;
if( mfp->msgVDirtyFl == false )
return;
// get the total trk msg count
mfp->msgN = 0;
@ -558,9 +566,23 @@ void _cmMidiFileLinearize( _cmMidiFile_t* mfp )
// set the amicro value in each msg
_cmMidiFileSetAbsoluteTime(mfp);
mfp->msgVDirtyFl = false;
}
// Note that p->msgV[] should always be accessed through this function
// to guarantee that the p->msgVDirtyFl is checked and msgV[] is updated
// in case msgV[] is out of sync (due to inserted msgs (see cmMidiFileInsertTrackMsg())
// with trkV[].
const cmMidiTrackMsg_t** _cmMidiFileMsgArray( _cmMidiFile_t* p )
{
_cmMidiFileLinearize(p);
// this cast is needed to eliminate an apparently needless 'incompatible type' warning
return (const cmMidiTrackMsg_t**)p->msgV;
}
cmMfRC_t _cmMidiFileCreate( cmCtx_t* ctx, cmMidiFileH_t* hp )
{
cmMfRC_t rc = kOkMfRC;
@ -644,10 +666,10 @@ cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hp, const char* fn )
p->fn = cmLhAllocZ(p->lhH,char,strlen(fn)+1);
assert( p->fn != NULL );
strcpy(p->fn,fn);
p->msgVDirtyFl = true;
_cmMidiFileLinearize(p);
errLabel:
if( cmFileClose(&p->fh) != kOkFileRC )
@ -1133,6 +1155,7 @@ unsigned cmMidiFileMsgCount( cmMidiFileH_t h )
return mfp->msgN;
}
const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h )
{
_cmMidiFile_t* mfp;
@ -1140,18 +1163,18 @@ const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h )
if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
return NULL;
// this cast is needed to eliminate an apparently needless 'incompatible type' warning
return (const cmMidiTrackMsg_t**)mfp->msgV;
return _cmMidiFileMsgArray(mfp);
}
cmMidiTrackMsg_t* _cmMidiFileUidToMsg( _cmMidiFile_t* mfp, unsigned uid )
{
unsigned i;
const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
for(i=0; i<mfp->msgN; ++i)
if( mfp->msgV[i]->uid == uid )
return mfp->msgV[i];
if( msgV[i]->uid == uid )
return (cmMidiTrackMsg_t*)msgV[i];
return NULL;
}
@ -1272,18 +1295,12 @@ cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiBy
trk->cnt += 1;
_cmMidiFileLinearize(mfp);
mfp->msgVDirtyFl = true;
return kOkMfRC;
}
// Only set
// atick - used to position the msg in the track
// status - this field is always set (Note that channel information must stripped from the status byte and included in the channel msg data)
// metaId - this field is optional depending on the msg type
// byteCnt - used to allocate storage for the data element in 'cmMidiTrackMsg_t.u'
// u - the message data
cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg )
{
_cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
@ -1311,13 +1328,14 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi
memcpy((void*)m->u.voidPtr,msg->u.voidPtr,msg->byteCnt);
}
cmMidiTrackMsg_t* m0 = NULL;
cmMidiTrackMsg_t* m1 = p->trkV[trkIdx].base;
cmMidiTrackMsg_t* m0 = NULL; // msg before insertion
cmMidiTrackMsg_t* m1 = p->trkV[trkIdx].base; // msg after insertion
// locate the track record before and after the new msg
// locate the track record before and after the new msg based on 'atick' value
for(; m1!=NULL; m1=m1->link)
{
if( m1->atick > m->atick )
{
{
if( m0 == NULL )
p->trkV[trkIdx].base = m;
else
@ -1326,17 +1344,42 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi
m->link = m1;
break;
}
// the new track record is the last msg
if( m1 == NULL )
{
m1 = p->trkV[trkIdx].last;
m1->link = m;
p->trkV[trkIdx].last = m;
if( p->trkV[trkIdx].base == NULL )
p->trkV[trkIdx].base = m;
m0 = m1;
}
// if the new track record was not inserted then it is the last msg
if( m1 == NULL )
{
assert(m0 == p->trkV[trkIdx].last);
// link in the new msg
if( m0 != NULL )
m0->link = m;
// the new msg always becomes the last msg
p->trkV[trkIdx].last = m;
// if the new msg is the first msg inserted in this track
if( p->trkV[trkIdx].base == NULL )
p->trkV[trkIdx].base = m;
}
// set the dtick field of the new msg
if( m0 != NULL )
{
assert( m->atick >= m0->atick );
m->dtick = m->atick - m0->atick;
}
// update the dtick field of the msg following the new msg
if( m1 != NULL )
{
assert( m1->atick >= m->atick );
m1->dtick = m1->atick - m->atick;
}
p->msgVDirtyFl = true;
return kOkMfRC;
@ -1387,14 +1430,15 @@ unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, un
if( p->msgN == 0 )
return cmInvalidIdx;
unsigned mi;
double microsPerQN = 60000000.0/120.0;
double microsPerTick = microsPerQN / p->ticksPerQN;
double accUSecs = 0;
unsigned mi;
double microsPerQN = 60000000.0/120.0;
double microsPerTick = microsPerQN / p->ticksPerQN;
double accUSecs = 0;
const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
for(mi=0; mi<p->msgN; ++mi)
{
const cmMidiTrackMsg_t* mp = p->msgV[mi];
const cmMidiTrackMsg_t* mp = msgV[mi];
if( mp->amicro >= offsUSecs )
break;
@ -1414,12 +1458,14 @@ unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, un
double cmMidiFileDurSecs( cmMidiFileH_t h )
{
_cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
_cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
if( mfp->msgN == 0 )
return 0;
const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
return mfp->msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
return msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
}
typedef struct _cmMidiVoice_str
@ -1471,7 +1517,9 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
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;
const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
// initialize the state tracking variables
for(i=0; i<kMidiChCnt; ++i)
{
@ -1492,10 +1540,10 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
// for each midi event
for(mi=0; mi<p->msgN; ++mi)
{
cmMidiTrackMsg_t* m = p->msgV[mi];
cmMidiTrackMsg_t* m = (cmMidiTrackMsg_t*)msgV[mi]; // cast away const
// verify that time is also incrementing
assert( mi==0 || (mi>0 && m->amicro >= p->msgV[mi-1]->amicro) );
assert( mi==0 || (mi>0 && m->amicro >= msgV[mi-1]->amicro) );
// ignore all non-channel messages
if( !cmMidiIsChStatus( m->status ) )
@ -1656,12 +1704,14 @@ void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks )
if((p = _cmMidiFileHandleToPtr(h)) == NULL )
return;
const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
if( p->msgN == 0 )
return;
for(mi=0; mi<p->msgN; ++mi)
{
cmMidiTrackMsg_t* mp = p->msgV[mi];
cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)msgV[mi]; // cast away const
// locate the first msg which has a non-zero delta tick
if( mp->dtick > 0 )
@ -1742,14 +1792,16 @@ void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt )
{
const _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
_cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
unsigned mi;
_cmMidiFilePrintHdr(p,rpt);
const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
for(mi=0; mi<p->msgN; ++mi)
{
cmMidiTrackMsg_t* mp = p->msgV[mi];
const cmMidiTrackMsg_t* mp = msgV[mi];
if( mp != NULL )
_cmMidiFilePrintMsg(rpt,mp);

View File

@ -136,17 +136,20 @@ extern "C" {
// Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
unsigned cmMidiFileType( cmMidiFileH_t h );
// Returns ticks per quarter note or kInvalidMidiByte if 'h' is invalid or 0 if file uses SMPTE ticks per frame time base.
// Returns ticks per quarter note or kInvalidMidiByte if 'h' is
// invalid or 0 if file uses SMPTE ticks per frame time base.
unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h );
// The file name used in an earlier call to midiFileOpen() or NULL if this
// midi file did not originate from an actual file.
const char* cmMidiFileName( cmMidiFileH_t h );
// Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
// Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is
// invalid or 0 if file uses ticks per quarter note time base.
cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
// Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
// Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0
// if file uses ticks per quarter note time base.
cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h );
// Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
@ -155,11 +158,13 @@ extern "C" {
// Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
// Returns the total count of records in the midi file and the number in the array returned by cmMidiFileMsgArray().
// Returns the total count of records in the midi file and the
// number in the array returned by cmMidiFileMsgArray().
// Return kInvalidCnt if 'h' is invalid.
unsigned cmMidiFileMsgCount( cmMidiFileH_t h );
// Returns a pointer to the base of an array of pointers to each record in the file sorted in ascending time order.
// Returns a pointer to the base of an array of pointers to all records
// in the file sorted in ascending time order.
// Returns NULL if 'h' is invalid.
const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
@ -170,6 +175,16 @@ extern "C" {
// 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 );
//
// Insert a new cmMidiTrackMsg_t into the MIDI file on the specified track.
//
// Only the following fields need be set in 'msg'.
// atick - used to position the msg in the track
// status - this field is always set (Note that channel information must stripped from the status byte and included in the channel msg data)
// metaId - this field is optional depending on the msg type
// byteCnt - used to allocate storage for the data element in 'cmMidiTrackMsg_t.u'
// u - the message data
//
cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg );
cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 );
cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm );