Browse Source

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().
master
kevin 8 years ago
parent
commit
c8062faafe
2 changed files with 76 additions and 99 deletions
  1. 71
    89
      cmMidiFile.c
  2. 5
    10
      cmMidiFile.h

+ 71
- 89
cmMidiFile.c View File

246
   tmp->byteCnt = sizeof(cmMidiChMsg_t);
246
   tmp->byteCnt = sizeof(cmMidiChMsg_t);
247
   tmp->status  = statusCh & 0xf0;
247
   tmp->status  = statusCh & 0xf0;
248
   p->ch        = statusCh & 0x0f;
248
   p->ch        = statusCh & 0x0f;
249
-  p->durTicks  = 0;
249
+  p->durMicros = 0;
250
 
250
 
251
   unsigned byteN = cmMidiStatusToByteCount(tmp->status);
251
   unsigned byteN = cmMidiStatusToByteCount(tmp->status);
252
   
252
   
416
 
416
 
417
 int _cmMidiFileSortFunc( const void *p0, const void* p1 )
417
 int _cmMidiFileSortFunc( const void *p0, const void* p1 )
418
 {  
418
 {  
419
-  //printf("%i %i\n",(*(cmMidiTrackMsg_t**)p0)->dticks,(*(cmMidiTrackMsg_t**)p1)->dticks);
420
-
421
   if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick )
419
   if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick )
422
     return 0;
420
     return 0;
423
 
421
 
526
   // store a pointer to every trk msg in msgV[] 
524
   // store a pointer to every trk msg in msgV[] 
527
   // and convert tick to absolute tick
525
   // and convert tick to absolute tick
528
   mfp->nextUid = 0;
526
   mfp->nextUid = 0;
527
+
528
+  double microsPerQN  = 60000000/120; // default tempo;
529
+  double microsPerTick;
530
+  
529
   unsigned i = 0;
531
   unsigned i = 0;
530
   for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
532
   for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
531
   {
533
   {
532
     unsigned        tick = 0;
534
     unsigned        tick = 0;
533
     cmMidiTrackMsg_t* tmp  = mfp->trkV[ trkIdx ].base;
535
     cmMidiTrackMsg_t* tmp  = mfp->trkV[ trkIdx ].base;
534
 
536
 
537
+    
538
+    microsPerTick = microsPerQN / mfp->ticksPerQN;
539
+  
540
+    
535
     while( tmp != NULL )
541
     while( tmp != NULL )
536
     {
542
     {
537
       assert( i < mfp->msgN);
543
       assert( i < mfp->msgN);
540
       tmp->atick     = tick;
546
       tmp->atick     = tick;
541
       tmp->uid       = mfp->nextUid++; // assign the msg uid
547
       tmp->uid       = mfp->nextUid++; // assign the msg uid
542
       mfp->msgV[i]   = tmp;
548
       mfp->msgV[i]   = tmp;
549
+
550
+      // track tempo changes
551
+      if( tmp->status == kMetaStId && tmp->metaId == kTempoMdId )
552
+        microsPerTick = tmp->u.iVal / mfp->ticksPerQN;
553
+
554
+      // convert dtick to microseconds
555
+      tmp->dmicro = round(tmp->dtick * microsPerTick);
556
+      
543
       tmp            = tmp->link;
557
       tmp            = tmp->link;
544
       ++i;
558
       ++i;
545
     }  
559
     }  
548
   // sort msgV[] in ascending order on atick
562
   // sort msgV[] in ascending order on atick
549
   qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc );
563
   qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc );
550
 
564
 
565
+  // set the amicro field of each midi message to the
566
+  // absolute time offset in microseconds
567
+  unsigned mi;
568
+  unsigned amicro = 0;
569
+  microsPerTick = microsPerQN / mfp->ticksPerQN;
570
+
571
+  for(mi=0; mi<mfp->msgN; ++mi)
572
+  {
573
+    cmMidiTrackMsg_t* mp = mfp->msgV[mi];
574
+
575
+    // track tempo changes
576
+    if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
577
+      microsPerTick = mp->u.iVal / mfp->ticksPerQN;
578
+
579
+    unsigned dtick = 0;
580
+    if( mi > 0 )
581
+    {
582
+      assert( mp->atick >= mfp->msgV[mi-1]->atick );
583
+      dtick = mp->atick -  mfp->msgV[mi-1]->atick;
584
+    }
585
+    
586
+    amicro += round(microsPerTick*dtick);
587
+    mp->amicro = amicro;
588
+  }
589
+  
551
   //for(i=0; i<25; ++i)
590
   //for(i=0; i<25; ++i)
552
   //  printf("%i 0x%x 0x%x\n",mfp->msgV[i]->tick,mfp->msgV[i]->status,mfp->msgV[i]->metaId);
591
   //  printf("%i 0x%x 0x%x\n",mfp->msgV[i]->tick,mfp->msgV[i]->status,mfp->msgV[i]->metaId);
553
 
592
 
1054
   for(mi=0; mi<p->msgN; ++mi)
1093
   for(mi=0; mi<p->msgN; ++mi)
1055
   {
1094
   {
1056
     const cmMidiTrackMsg_t* mp = p->msgV[mi];
1095
     const cmMidiTrackMsg_t* mp = p->msgV[mi];
1057
-
1096
+    /*
1058
     if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
1097
     if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
1059
       microsPerTick = mp->u.iVal / p->ticksPerQN;
1098
       microsPerTick = mp->u.iVal / p->ticksPerQN;
1060
 
1099
 
1061
-    accUSecs += mp->dtick * microsPerTick ;
1100
+    unsigned dtick = 0;
1101
+    if( mi > 0 )
1102
+    {
1103
+      assert( mp->atick >= p->msgV[mi-1]->atick )
1104
+      dtick = mp->atick - p->msgV[mi-1]->atick;
1105
+    }
1106
+    
1107
+    accUSecs += dtick * microsPerTick ;
1062
 
1108
 
1063
     if( accUSecs >= offsUSecs )
1109
     if( accUSecs >= offsUSecs )
1064
       break;
1110
       break;
1111
+    */
1065
 
1112
 
1113
+    if( mp->amicro >= offsUSecs )
1114
+      break;
1066
   }
1115
   }
1067
   
1116
   
1068
   if( mi == p->msgN )
1117
   if( mi == p->msgN )
1080
 double  cmMidiFileDurSecs( cmMidiFileH_t h )
1129
 double  cmMidiFileDurSecs( cmMidiFileH_t h )
1081
 {
1130
 {
1082
   _cmMidiFile_t* mfp           = _cmMidiFileHandleToPtr(h);
1131
   _cmMidiFile_t* mfp           = _cmMidiFileHandleToPtr(h);
1083
-  unsigned       mi;
1084
-  double         durSecs       = 0;
1085
-  double         r             = 1.0; //1000.0/(1000-.8);
1086
-  double         microsPerQN   = r*60000000.0/120.0;
1087
-  double         microsPerTick = microsPerQN / mfp->ticksPerQN;
1088
-
1089
-  for(mi=0; mi<mfp->msgN; ++mi)
1090
-  {
1091
-    cmMidiTrackMsg_t* mp = mfp->msgV[mi];
1092
-
1093
-    if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
1094
-      microsPerTick = r*mp->u.iVal / mfp->ticksPerQN;
1095
-
1096
-    // update the accumulated seconds
1097
-    durSecs += (mp->dtick * microsPerTick) / 1000000.0;
1098
 
1132
 
1099
-  }
1100
-
1101
-  return durSecs;
1102
-}
1103
-
1104
-void cmMidiFileTickToMicros( cmMidiFileH_t h )
1105
-{
1106
-  _cmMidiFile_t* p;
1107
-
1108
-  if((p = _cmMidiFileHandleToPtr(h)) == NULL )
1109
-    return;
1110
-
1111
-  if( p->msgN == 0 )
1112
-    return;
1113
-
1114
-  unsigned mi;
1115
-  double microsPerQN   = 60000000/120; // default tempo
1116
-  double microsPerTick = microsPerQN / p->ticksPerQN;
1117
-
1118
-  for(mi=0; mi<p->msgN; ++mi)
1119
-  {
1120
-    cmMidiTrackMsg_t* mp = p->msgV[mi];
1121
-
1122
-    if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
1123
-      microsPerTick = mp->u.iVal / p->ticksPerQN;
1124
-    
1125
-    mp->dtick = round(microsPerTick*mp->dtick);
1126
-  }
1127
-  
1128
-}
1129
-
1130
-void cmMidiFileTickToSamples( cmMidiFileH_t h, double srate, bool absFl )
1131
-{
1132
-  _cmMidiFile_t* p;
1133
-
1134
-  if((p = _cmMidiFileHandleToPtr(h)) == NULL )
1135
-    return;
1136
-
1137
-  if( p->msgN == 0 )
1138
-    return;
1139
-
1140
-  unsigned mi;
1141
-  double microsPerQN   = 60000000/120; // default tempo
1142
-  double microsPerTick = microsPerQN / p->ticksPerQN;
1143
-  double absSmp = 0;
1144
-
1145
-  for(mi=0; mi<p->msgN; ++mi)
1146
-  {
1147
-    cmMidiTrackMsg_t* mp = p->msgV[mi];
1148
-
1149
-    if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
1150
-      microsPerTick = mp->u.iVal / p->ticksPerQN;
1151
-
1152
-    double delta = microsPerTick*mp->dtick*srate/1000000.0;
1153
-
1154
-    absSmp += delta;
1155
-
1156
-    mp->dtick  = round(absFl ? absSmp : delta);
1157
-
1158
-  }
1133
+  if( mfp->msgN == 0 )
1134
+    return 0;
1159
   
1135
   
1136
+  return mfp->msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
1160
 }
1137
 }
1161
 
1138
 
1162
-
1163
 typedef struct _cmMidiVoice_str
1139
 typedef struct _cmMidiVoice_str
1164
 {
1140
 {
1165
   const  cmMidiTrackMsg_t*  mp;
1141
   const  cmMidiTrackMsg_t*  mp;
1166
-  unsigned                  durTicks;
1142
+  unsigned                  durMicros;
1167
   bool                      sustainFl;
1143
   bool                      sustainFl;
1168
   struct _cmMidiVoice_str*  link;
1144
   struct _cmMidiVoice_str*  link;
1169
 } _cmMidiVoice_t;
1145
 } _cmMidiVoice_t;
1176
   // store the duration of the note into the track msg 
1152
   // store the duration of the note into the track msg 
1177
   // assoc'd with the note-on msg
1153
   // assoc'd with the note-on msg
1178
   cmMidiChMsg_t* cmp = (cmMidiChMsg_t*)vp->mp->u.chMsgPtr; // cast away const
1154
   cmMidiChMsg_t* cmp = (cmMidiChMsg_t*)vp->mp->u.chMsgPtr; // cast away const
1179
-  cmp->durTicks = vp->durTicks;
1155
+  cmp->durMicros = vp->durMicros;
1180
 
1156
 
1181
   _cmMidiVoice_t* np = vp->link;
1157
   _cmMidiVoice_t* np = vp->link;
1182
 
1158
 
1224
   {
1200
   {
1225
     cmMidiTrackMsg_t* mp    = p->msgV[mi];
1201
     cmMidiTrackMsg_t* mp    = p->msgV[mi];
1226
 
1202
 
1203
+    unsigned d_amicro = 0;
1204
+    if( mi > 0 )
1205
+    {
1206
+      assert(    mp->amicro >= p->msgV[mi-1]->amicro );
1207
+      d_amicro = mp->amicro -  p->msgV[mi-1]->amicro;
1208
+    }
1209
+    
1227
     // update the duration of the sounding notes
1210
     // update the duration of the sounding notes
1228
     for(vp = list; vp!=NULL; vp=vp->link)
1211
     for(vp = list; vp!=NULL; vp=vp->link)
1229
-      vp->durTicks += mp->dtick;    
1212
+      vp->durMicros += d_amicro;    
1230
 
1213
 
1231
     // update the sustain pedal duration
1214
     // update the sustain pedal duration
1232
     if( sustainPedalDownMsg != NULL )
1215
     if( sustainPedalDownMsg != NULL )
1233
-      ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durTicks += mp->dtick;  // cast away const
1216
+      ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durMicros += d_amicro;  // cast away const
1234
 
1217
 
1235
     //
1218
     //
1236
     // If this is sustain pedal msg
1219
     // If this is sustain pedal msg
1255
         else
1238
         else
1256
         {
1239
         {
1257
           sustainPedalDownMsg = mp;
1240
           sustainPedalDownMsg = mp;
1258
-          ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durTicks = 0;  // cast away const
1241
+          ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durMicros = 0;  // cast away const
1259
         }
1242
         }
1260
 
1243
 
1261
         _cmMidiFileCalcNoteDurationsAllocVoice( &list, mp, true );
1244
         _cmMidiFileCalcNoteDurationsAllocVoice( &list, mp, true );
1397
 
1380
 
1398
 void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
1381
 void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
1399
 {
1382
 {
1400
-  //cmRptPrintf(rpt,"%5.5f ", tmp->dtick/1000000.0 );
1401
-  cmRptPrintf(rpt,"%f ",    (double)tmp->dtick );
1383
+  cmRptPrintf(rpt,"%8i %8i %8i %8i : ",   tmp->dtick, tmp->dmicro, tmp->atick, tmp->amicro );
1402
 
1384
 
1403
   if( tmp->status == kMetaStId )
1385
   if( tmp->status == kMetaStId )
1404
     cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId)); 
1386
     cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId)); 
1502
   if( 1 )
1484
   if( 1 )
1503
   {
1485
   {
1504
     //cmMidiFileTickToMicros( h );
1486
     //cmMidiFileTickToMicros( h );
1505
-    cmMidiFileTickToSamples(h,96000,false);
1487
+    //cmMidiFileTickToSamples(h,96000,false);
1506
     cmMidiFilePrintMsgs(h,&ctx->rpt);
1488
     cmMidiFilePrintMsgs(h,&ctx->rpt);
1507
   }
1489
   }
1508
 
1490
 

+ 5
- 10
cmMidiFile.h View File

57
     cmMidiByte_t ch;
57
     cmMidiByte_t ch;
58
     cmMidiByte_t d0;
58
     cmMidiByte_t d0;
59
     cmMidiByte_t d1;
59
     cmMidiByte_t d1;
60
-    unsigned     durTicks; // note duration calc'd by cmMidiFileCalcNoteDurations();
60
+    unsigned     durMicros;  // note duration in microseconds (corrected for tempo changes)
61
   } cmMidiChMsg_t;
61
   } cmMidiChMsg_t;
62
 
62
 
63
 
63
 
64
   typedef struct cmMidiTrackMsg_str
64
   typedef struct cmMidiTrackMsg_str
65
   {
65
   {
66
     unsigned                   uid;     // uid's are unique among all msg's in the file
66
     unsigned                   uid;     // uid's are unique among all msg's in the file
67
-    unsigned                   dtick;   // delta ticks
68
-    unsigned                   atick;   // accumulated ticks
67
+    unsigned                   dtick;   // delta ticks between events on this track
68
+    unsigned                   dmicro;  // delta microseconds between events on this track adjusted for tempo changes
69
+    unsigned                   atick;   // global (all tracks interleaved) accumulated ticks
70
+    unsigned                   amicro;  // global (all tracks interleaved) accumulated microseconds adjusted for tempo changes
69
     cmMidiByte_t               status;  // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
71
     cmMidiByte_t               status;  // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
70
     cmMidiByte_t               metaId;  //
72
     cmMidiByte_t               metaId;  //
71
     unsigned short             trkIdx;  //  
73
     unsigned short             trkIdx;  //  
153
 
155
 
154
   double                cmMidiFileDurSecs( cmMidiFileH_t h );
156
   double                cmMidiFileDurSecs( cmMidiFileH_t h );
155
 
157
 
156
-  // Convert the track message 'dtick' field to delta-microseconds.
157
-  void                  cmMidiFileTickToMicros( cmMidiFileH_t h );
158
-
159
-  // Convert the track message 'dtick' field to samples.
160
-  // If the absFl is set then the delta times are converted to absolute time.
161
-  void                  cmMidiFileTickToSamples( cmMidiFileH_t h, double srate, bool absFl );
162
-
163
   // Calculate Note Duration 
158
   // Calculate Note Duration 
164
   void                  cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
159
   void                  cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
165
 
160
 

Loading…
Cancel
Save