|
@@ -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);
|