Browse Source

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

master
kevin 7 years ago
parent
commit
49943bd43e
2 changed files with 116 additions and 49 deletions
  1. 96
    44
      cmMidiFile.c
  2. 20
    5
      cmMidiFile.h

+ 96
- 44
cmMidiFile.c View File

@@ -40,12 +40,14 @@ typedef struct
40 40
   char*              fn;                 // file name or NULL if this object did not originate from a file
41 41
   unsigned           msgN;               // count of msg's in msgV[]
42 42
   cmMidiTrackMsg_t** msgV;               // sorted msg list
43
+  bool               msgVDirtyFl;        // msgV[] needs to be refreshed from trkV[] because new msg's were inserted.
43 44
   unsigned           nextUid;            // next available msg uid
44 45
 } _cmMidiFile_t;
45 46
 
46 47
 
47 48
 cmMidiFileH_t cmMidiFileNullHandle = cmSTATIC_NULL_HANDLE;
48 49
 
50
+const cmMidiTrackMsg_t** _cmMidiFileMsgArray( _cmMidiFile_t* p  );
49 51
 
50 52
 _cmMidiFile_t* _cmMidiFileHandleToPtr( cmMidiFileH_t h )
51 53
 {
@@ -435,9 +437,9 @@ void _cmMidiFileSetAccumulateTicks( _cmMidiFile_t* p )
435 437
   unsigned          i;
436 438
   bool              fl = true;
437 439
   
438
-  // iniitalize nextTrkTick[] and nextTrkMsg[].
440
+  // iniitalize nextTrkTick[] and nextTrkMsg[] to the first msg in each track
439 441
   for(i=0; i<p->trkN; ++i)
440
-    if((nextTrkMsg[i]  =  p->trkV[i].base) != NULL )
442
+    if((nextTrkMsg[i] =  p->trkV[i].base) != NULL )
441 443
       nextTrkMsg[i]->atick = nextTrkMsg[i]->dtick;
442 444
 
443 445
   while(1)
@@ -474,21 +476,23 @@ void _cmMidiFileSetAccumulateTicks( _cmMidiFile_t* p )
474 476
 
475 477
 void _cmMidiFileSetAbsoluteTime( _cmMidiFile_t* mfp )
476 478
 {
477
-  double             microsPerQN   = 60000000/120; // default tempo;
478
-  double             microsPerTick = microsPerQN / mfp->ticksPerQN;
479
-  unsigned long long amicro        = 0;
480
-  unsigned           i;
479
+  const cmMidiTrackMsg_t** msgV          = _cmMidiFileMsgArray(mfp);
480
+  double                   microsPerQN   = 60000000/120; // default tempo;
481
+  double                   microsPerTick = microsPerQN / mfp->ticksPerQN;
482
+  unsigned long long       amicro        = 0;
483
+  unsigned                 i;
484
+
481 485
 
482 486
   for(i=0; i<mfp->msgN; ++i)
483 487
   {
484
-    cmMidiTrackMsg_t* mp    = mfp->msgV[i];
488
+    cmMidiTrackMsg_t* mp    = (cmMidiTrackMsg_t*)msgV[i]; // cast away const
485 489
     unsigned          dtick = 0;
486 490
     
487 491
     if( i > 0 )
488 492
     {
489 493
       // atick must have already been set and sorted
490
-      assert( mp->atick >= mfp->msgV[i-1]->atick );
491
-      dtick = mp->atick -  mfp->msgV[i-1]->atick;
494
+      assert( mp->atick >= msgV[i-1]->atick );
495
+      dtick = mp->atick -  msgV[i-1]->atick;
492 496
     }
493 497
 
494 498
     amicro     += microsPerTick * dtick;
@@ -524,9 +528,13 @@ cmMfRC_t _cmMidiFileClose( _cmMidiFile_t* mfp )
524 528
   
525 529
 }
526 530
 
531
+
527 532
 void _cmMidiFileLinearize( _cmMidiFile_t* mfp )
528 533
 {
529 534
   unsigned trkIdx,i,j;
535
+
536
+  if( mfp->msgVDirtyFl == false )
537
+    return;
530 538
   
531 539
   // get the total trk msg count
532 540
   mfp->msgN = 0;
@@ -558,9 +566,23 @@ void _cmMidiFileLinearize( _cmMidiFile_t* mfp )
558 566
 
559 567
   // set the amicro value in each msg
560 568
   _cmMidiFileSetAbsoluteTime(mfp);
569
+
570
+  mfp->msgVDirtyFl = false;
561 571
   
562 572
 }
563 573
 
574
+// Note that p->msgV[] should always be accessed through this function
575
+// to guarantee that the p->msgVDirtyFl is checked and msgV[] is updated
576
+// in case msgV[] is out of sync (due to inserted msgs (see cmMidiFileInsertTrackMsg())
577
+// with trkV[].
578
+const cmMidiTrackMsg_t** _cmMidiFileMsgArray( _cmMidiFile_t* p  )
579
+{
580
+  _cmMidiFileLinearize(p);
581
+  
582
+  // this cast is needed to eliminate an apparently needless 'incompatible type' warning
583
+  return (const cmMidiTrackMsg_t**)p->msgV;
584
+}
585
+
564 586
 cmMfRC_t _cmMidiFileCreate( cmCtx_t* ctx, cmMidiFileH_t* hp )
565 587
 {
566 588
   cmMfRC_t rc = kOkMfRC;
@@ -644,10 +666,10 @@ cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hp, const char* fn )
644 666
   p->fn          = cmLhAllocZ(p->lhH,char,strlen(fn)+1);
645 667
   assert( p->fn != NULL );
646 668
   strcpy(p->fn,fn);
647
-  
669
+
670
+  p->msgVDirtyFl = true;
648 671
   _cmMidiFileLinearize(p);
649 672
   
650
-
651 673
  errLabel:
652 674
 
653 675
   if( cmFileClose(&p->fh) != kOkFileRC )
@@ -1133,6 +1155,7 @@ unsigned              cmMidiFileMsgCount( cmMidiFileH_t h )
1133 1155
   return mfp->msgN;
1134 1156
 }
1135 1157
 
1158
+
1136 1159
 const cmMidiTrackMsg_t** cmMidiFileMsgArray(    cmMidiFileH_t h )
1137 1160
 {
1138 1161
   _cmMidiFile_t* mfp;
@@ -1140,18 +1163,18 @@ const cmMidiTrackMsg_t** cmMidiFileMsgArray(    cmMidiFileH_t h )
1140 1163
   if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
1141 1164
     return NULL;
1142 1165
 
1143
-  // this cast is needed to eliminate an apparently needless 'incompatible type' warning
1144
-  return (const cmMidiTrackMsg_t**)mfp->msgV;
1166
+  return _cmMidiFileMsgArray(mfp); 
1145 1167
 }
1146 1168
 
1147 1169
 
1148 1170
 cmMidiTrackMsg_t*  _cmMidiFileUidToMsg( _cmMidiFile_t* mfp, unsigned uid )
1149 1171
 {
1150 1172
   unsigned i;
1173
+  const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
1151 1174
 
1152 1175
   for(i=0; i<mfp->msgN; ++i)
1153
-    if( mfp->msgV[i]->uid == uid )
1154
-      return mfp->msgV[i];
1176
+    if( msgV[i]->uid == uid )
1177
+      return (cmMidiTrackMsg_t*)msgV[i];
1155 1178
 
1156 1179
   return NULL;
1157 1180
 }
@@ -1272,18 +1295,12 @@ cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiBy
1272 1295
 
1273 1296
   trk->cnt += 1;
1274 1297
 
1275
-  _cmMidiFileLinearize(mfp);
1298
+  mfp->msgVDirtyFl = true;
1276 1299
 
1277 1300
   return kOkMfRC;
1278 1301
 
1279 1302
 }
1280 1303
 
1281
-// Only set
1282
-//   atick    - used to position the msg in the track
1283
-//   status   - this field is always set (Note that channel information must stripped from the status byte and included in the channel msg data)
1284
-//   metaId   - this field is optional depending on the msg type
1285
-//   byteCnt  - used to allocate storage for the data element in 'cmMidiTrackMsg_t.u'
1286
-//   u        - the message data
1287 1304
 cmMfRC_t  cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg )
1288 1305
 {
1289 1306
   _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
@@ -1311,13 +1328,14 @@ cmMfRC_t  cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi
1311 1328
     memcpy((void*)m->u.voidPtr,msg->u.voidPtr,msg->byteCnt);
1312 1329
   }
1313 1330
 
1314
-  cmMidiTrackMsg_t* m0 = NULL;
1315
-  cmMidiTrackMsg_t* m1 = p->trkV[trkIdx].base;
1331
+  cmMidiTrackMsg_t* m0 = NULL;                  // msg before insertion
1332
+  cmMidiTrackMsg_t* m1 = p->trkV[trkIdx].base;  // msg after insertion
1316 1333
 
1317
-  // locate the track record before and after the new msg
1334
+  // locate the track record before and after the new msg based on 'atick' value
1318 1335
   for(; m1!=NULL; m1=m1->link)
1336
+  {
1319 1337
     if( m1->atick > m->atick )
1320
-    {
1338
+    {      
1321 1339
       if( m0 == NULL )
1322 1340
         p->trkV[trkIdx].base = m;
1323 1341
       else
@@ -1326,17 +1344,42 @@ cmMfRC_t  cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi
1326 1344
       m->link = m1;
1327 1345
       break;
1328 1346
     }
1347
+    
1348
+    m0 = m1;    
1349
+  }
1329 1350
 
1330
-  // the new track record is the last msg
1351
+  // if the new track record was not inserted then it is the last msg
1331 1352
   if( m1 == NULL )
1332 1353
   {
1333
-    m1                   = p->trkV[trkIdx].last;
1334
-    m1->link             = m;
1354
+    assert(m0 == p->trkV[trkIdx].last);
1355
+    
1356
+    // link in the new msg
1357
+    if( m0 != NULL )
1358
+      m0->link = m;
1359
+
1360
+    // the new msg always becomes the last msg
1335 1361
     p->trkV[trkIdx].last = m;
1362
+    
1363
+    // if the new msg is the first msg inserted in this track
1336 1364
     if( p->trkV[trkIdx].base == NULL )
1337
-      p->trkV[trkIdx].base = m;
1365
+      p->trkV[trkIdx].base = m;    
1366
+  }
1367
+
1368
+  // set the dtick field of the new msg
1369
+  if( m0 != NULL )
1370
+  {
1371
+    assert( m->atick >= m0->atick );
1372
+    m->dtick = m->atick - m0->atick;
1338 1373
   }
1339 1374
 
1375
+  // update the dtick field of the msg following the new msg
1376
+  if( m1 != NULL )
1377
+  {
1378
+    assert( m1->atick >= m->atick );
1379
+    m1->dtick = m1->atick - m->atick;
1380
+  }
1381
+  
1382
+  p->msgVDirtyFl = true;
1340 1383
   
1341 1384
   return kOkMfRC;
1342 1385
    
@@ -1387,14 +1430,15 @@ unsigned  cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, un
1387 1430
   if( p->msgN == 0 )
1388 1431
     return cmInvalidIdx;
1389 1432
 
1390
-  unsigned mi;
1391
-  double microsPerQN   = 60000000.0/120.0;
1392
-  double microsPerTick = microsPerQN / p->ticksPerQN;
1393
-  double accUSecs      = 0;
1394
-
1433
+  unsigned                 mi;
1434
+  double                   microsPerQN   = 60000000.0/120.0;
1435
+  double                   microsPerTick = microsPerQN / p->ticksPerQN;
1436
+  double                   accUSecs      = 0;
1437
+  const cmMidiTrackMsg_t** msgV          = _cmMidiFileMsgArray(p);
1438
+  
1395 1439
   for(mi=0; mi<p->msgN; ++mi)
1396 1440
   {
1397
-    const cmMidiTrackMsg_t* mp = p->msgV[mi];
1441
+    const cmMidiTrackMsg_t* mp = msgV[mi];
1398 1442
 
1399 1443
     if( mp->amicro >= offsUSecs )
1400 1444
       break;
@@ -1414,12 +1458,14 @@ unsigned  cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, un
1414 1458
 
1415 1459
 double  cmMidiFileDurSecs( cmMidiFileH_t h )
1416 1460
 {
1417
-  _cmMidiFile_t* mfp           = _cmMidiFileHandleToPtr(h);
1461
+  _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
1418 1462
 
1419 1463
   if( mfp->msgN == 0 )
1420 1464
     return 0;
1465
+
1466
+  const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
1421 1467
   
1422
-  return mfp->msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
1468
+  return msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
1423 1469
 }
1424 1470
 
1425 1471
 typedef struct _cmMidiVoice_str
@@ -1471,7 +1517,9 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
1471 1517
   int               sustGateV[ kMidiChCnt];                  // true if the associated sustain pedal is down
1472 1518
   int               sostGateV[ kMidiChCnt];                  // true if the associated sostenuto pedal is down
1473 1519
   unsigned          i,j;
1474
-
1520
+  
1521
+  const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
1522
+  
1475 1523
   // initialize the state tracking variables
1476 1524
   for(i=0; i<kMidiChCnt; ++i)
1477 1525
   {
@@ -1492,10 +1540,10 @@ void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
1492 1540
   // for each midi event
1493 1541
   for(mi=0; mi<p->msgN; ++mi)
1494 1542
   {
1495
-    cmMidiTrackMsg_t* m = p->msgV[mi];
1543
+    cmMidiTrackMsg_t* m = (cmMidiTrackMsg_t*)msgV[mi]; // cast away const
1496 1544
 
1497 1545
     // verify that time is also incrementing
1498
-    assert(  mi==0 || (mi>0 &&  m->amicro >= p->msgV[mi-1]->amicro) );
1546
+    assert(  mi==0 || (mi>0 &&  m->amicro >= msgV[mi-1]->amicro) );
1499 1547
 
1500 1548
     // ignore all non-channel messages
1501 1549
     if(  !cmMidiIsChStatus( m->status ) )
@@ -1656,12 +1704,14 @@ void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks )
1656 1704
   if((p = _cmMidiFileHandleToPtr(h)) == NULL )
1657 1705
     return;
1658 1706
 
1707
+  const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
1708
+
1659 1709
   if( p->msgN == 0 )
1660 1710
     return;
1661 1711
 
1662 1712
   for(mi=0; mi<p->msgN; ++mi)
1663 1713
   {
1664
-    cmMidiTrackMsg_t* mp = p->msgV[mi];
1714
+    cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)msgV[mi]; // cast away const
1665 1715
     
1666 1716
     // locate the first msg which has a non-zero delta tick
1667 1717
     if( mp->dtick > 0 )
@@ -1742,14 +1792,16 @@ void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
1742 1792
 
1743 1793
 void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt )
1744 1794
 {
1745
-  const _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
1795
+  _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
1746 1796
   unsigned mi;
1747 1797
   
1748 1798
   _cmMidiFilePrintHdr(p,rpt);
1749 1799
 
1800
+  const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
1801
+
1750 1802
   for(mi=0; mi<p->msgN; ++mi)
1751 1803
   {
1752
-    cmMidiTrackMsg_t* mp = p->msgV[mi];
1804
+    const cmMidiTrackMsg_t* mp = msgV[mi];
1753 1805
 
1754 1806
     if( mp != NULL )
1755 1807
       _cmMidiFilePrintMsg(rpt,mp);

+ 20
- 5
cmMidiFile.h View File

@@ -136,17 +136,20 @@ extern "C" {
136 136
   // Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
137 137
   unsigned              cmMidiFileType( cmMidiFileH_t h );
138 138
 
139
-  // Returns ticks per quarter note or kInvalidMidiByte if 'h' is invalid or 0 if file uses SMPTE ticks per frame time base.
139
+  // Returns ticks per quarter note or kInvalidMidiByte if 'h' is
140
+  // invalid or 0 if file uses SMPTE ticks per frame time base.
140 141
   unsigned              cmMidiFileTicksPerQN( cmMidiFileH_t h );
141 142
 
142 143
   // The file name used in an earlier call to midiFileOpen() or NULL if this 
143 144
   // midi file did not originate from an actual file.
144 145
   const char*           cmMidiFileName( cmMidiFileH_t h );
145 146
 
146
-  // Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
147
+  // Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is
148
+  // invalid or 0 if file uses ticks per quarter note time base.
147 149
   cmMidiByte_t          cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
148 150
 
149
-  // Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
151
+  // Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0
152
+  // if file uses ticks per quarter note time base.
150 153
   cmMidiByte_t          cmMidiFileSmpteFormatId( cmMidiFileH_t h );
151 154
 
152 155
   // Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
@@ -155,11 +158,13 @@ extern "C" {
155 158
   // Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
156 159
   const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
157 160
 
158
-  // Returns the total count of records in the midi file and the number in the array returned by cmMidiFileMsgArray().
161
+  // Returns the total count of records in the midi file and the
162
+  // number in the array returned by cmMidiFileMsgArray(). 
159 163
   // Return kInvalidCnt if 'h' is invalid.
160 164
   unsigned              cmMidiFileMsgCount( cmMidiFileH_t h );
161 165
 
162
-  // Returns a pointer to the base of an array of pointers to each record in the file sorted in ascending time order.
166
+  // Returns a pointer to the base of an array of pointers to all records
167
+  // in the file sorted in ascending time order. 
163 168
   // Returns NULL if 'h' is invalid.
164 169
   const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
165 170
 
@@ -170,6 +175,16 @@ extern "C" {
170 175
   // If dtick is positive/negative then the new msg is inserted after/before the reference msg.  
171 176
   cmMfRC_t             cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiByte_t ch, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 );
172 177
 
178
+  //
179
+  // Insert a new cmMidiTrackMsg_t into the MIDI file on the specified track.
180
+  //
181
+  // Only the following fields need be set in 'msg'.
182
+  //   atick    - used to position the msg in the track
183
+  //   status   - this field is always set (Note that channel information must stripped from the status byte and included in the channel msg data)
184
+  //   metaId   - this field is optional depending on the msg type
185
+  //   byteCnt  - used to allocate storage for the data element in 'cmMidiTrackMsg_t.u'
186
+  //   u        - the message data
187
+  //
173 188
   cmMfRC_t             cmMidiFileInsertTrackMsg(     cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg );
174 189
   cmMfRC_t             cmMidiFileInsertTrackChMsg(   cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 );
175 190
   cmMfRC_t             cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm );

Loading…
Cancel
Save