Browse Source

cmXScore.c : Added cmXsComplexity_t related functions and added complexity measure output to MIDI file SVG plot.

master
kevin 7 years ago
parent
commit
cc334c8d7f
1 changed files with 288 additions and 8 deletions
  1. 288
    8
      app/cmXScore.c

+ 288
- 8
app/cmXScore.c View File

@@ -71,6 +71,16 @@ enum
71 71
 struct cmXsMeas_str;
72 72
 struct cmXsVoice_str;
73 73
 
74
+typedef struct cmXsComplexity_str
75
+{
76
+  unsigned sum_d_vel;
77
+  unsigned sum_d_rym;
78
+  unsigned sum_d_lpch;
79
+  unsigned sum_n_lpch;
80
+  unsigned sum_d_rpch;
81
+  unsigned sum_n_rpch;
82
+} cmXsComplexity_t;
83
+
74 84
 typedef struct cmXsNote_str
75 85
 {
76 86
   unsigned                    uid;      // unique id of this note record
@@ -107,6 +117,8 @@ typedef struct cmXsNote_str
107 117
   struct cmXsNote_str*        mlink;    // measure note list
108 118
   struct cmXsNote_str*        slink;    // time sorted event list
109 119
 
120
+  cmXsComplexity_t            cplx;
121
+
110 122
 } cmXsNote_t;
111 123
 
112 124
 typedef struct cmXsVoice_str
@@ -157,6 +169,8 @@ typedef struct
157 169
 
158 170
   cmXsSpan_t* spanL;
159 171
   unsigned    nextUid;
172
+
173
+  cmXsComplexity_t cplx_max;
160 174
 } cmXScore_t;
161 175
 
162 176
 cmXScore_t* _cmXScoreHandleToPtr( cmXsH_t h )
@@ -3050,6 +3064,200 @@ void  cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
3050 3064
   }
3051 3065
 }
3052 3066
 
3067
+typedef struct
3068
+{
3069
+  unsigned ival;
3070
+  double   fval;
3071
+  unsigned cnt;
3072
+} cmXsHist_t;
3073
+
3074
+void _cmXsHistUpdateI( cmXsHist_t* hist, unsigned histN, unsigned ival )
3075
+{
3076
+  unsigned i;
3077
+  
3078
+  for(i=0; i<histN && hist[i].cnt!=0; ++i)
3079
+    if( hist[i].ival == ival )
3080
+      break;
3081
+
3082
+  if( i==histN )
3083
+    return;
3084
+  
3085
+  hist[i].ival = ival;
3086
+  hist[i].cnt += 1;
3087
+  
3088
+}
3089
+
3090
+void _cmXsHistUpdateF( cmXsHist_t* hist, unsigned histN, double fval )
3091
+{
3092
+  unsigned i;
3093
+  
3094
+  for(i=0; i<histN && hist[i].cnt!=0; ++i)
3095
+    if( hist[i].fval == fval )
3096
+      break;
3097
+
3098
+  if( i==histN )
3099
+    return;
3100
+  
3101
+  hist[i].fval = fval;
3102
+  hist[i].cnt += 1;
3103
+}
3104
+
3105
+unsigned _cmXsHistValue( cmXsHist_t* hist, unsigned histN )
3106
+{
3107
+  unsigned i,n;
3108
+  for(i=0,n=0; i<histN && hist[i].cnt>0; ++i)
3109
+    n += 1;
3110
+
3111
+  return n;
3112
+}
3113
+
3114
+const cmXsNote_t*  _cmXsMeasComplexityInWindow( const cmXsNote_t* n0, cmXsNote_t* n1, double wndSecs )
3115
+{
3116
+  const cmXsNote_t* n2          = NULL;
3117
+  unsigned    l_pch_0     = 0;
3118
+  unsigned    l_pch_value = 0;
3119
+  unsigned    l_pch_cnt   = 0;
3120
+  unsigned    r_pch_0     = 0;
3121
+  unsigned    r_pch_value = 0;
3122
+  unsigned    r_pch_cnt   = 0;
3123
+  unsigned    i           = 0;
3124
+
3125
+
3126
+  unsigned   histN = 100;
3127
+  cmXsHist_t velHist[ histN ];
3128
+  cmXsHist_t rymHist[ histN ];
3129
+
3130
+  memset(velHist,0,sizeof(velHist));
3131
+  memset(rymHist,0,sizeof(rymHist));
3132
+
3133
+  const cmXsNote_t* n = n0;
3134
+  
3135
+  while(n!=NULL && n != n1)
3136
+  {
3137
+    // if this event is less than wndSecs behind 'n1' and is not a sounding note ...
3138
+    if( n1->secs - n->secs <= wndSecs && cmIsFlag(n->flags,kOnsetXsFl) )
3139
+    {
3140
+      // if this is not the first note in the window
3141
+      if( i > 0 )
3142
+      {
3143
+        _cmXsHistUpdateI( velHist,  histN, n->dynamics );
3144
+        _cmXsHistUpdateF( rymHist,  histN, n->rvalue );         
3145
+      
3146
+        switch( n->staff )
3147
+        {
3148
+          case 1:
3149
+            r_pch_value += r_pch_0 > n->pitch ? r_pch_0-n->pitch : n->pitch-r_pch_0;
3150
+            r_pch_cnt   += 1;
3151
+            break;
3152
+          
3153
+          case 2:
3154
+            l_pch_value += l_pch_0 > n->pitch ? l_pch_0-n->pitch : n->pitch-l_pch_0;
3155
+            r_pch_cnt   += 1;
3156
+            break;
3157
+          
3158
+          default:
3159
+            { assert(0); }
3160
+        }
3161
+      }
3162
+
3163
+      // store the pitch values to compare on the next interation
3164
+      switch( n->staff )
3165
+      {
3166
+        case 1:
3167
+          r_pch_0 = n->pitch;
3168
+          break;
3169
+        
3170
+        case 2:
3171
+          l_pch_0 = n->pitch;
3172
+          break;
3173
+        
3174
+        default:
3175
+          { assert(0); }
3176
+      }
3177
+
3178
+      // track the first note that is inside the window
3179
+      if( i == 0 )
3180
+        n2 = n;
3181
+
3182
+      // count the number of notes in the window
3183
+      i += 1;
3184
+
3185
+    }
3186
+
3187
+    cmXsMeas_t* m = n->meas;
3188
+    
3189
+    // advance th note pointer
3190
+    n  = n->slink;
3191
+
3192
+    // if we have reached the end of a measure
3193
+    if( n == NULL )
3194
+    {
3195
+      if( m != NULL )
3196
+      {
3197
+        m = m->link;
3198
+        if( m != NULL )
3199
+          n = m->noteL;
3200
+      }
3201
+    }
3202
+    
3203
+  }
3204
+
3205
+  n1->cplx.sum_d_vel  = _cmXsHistValue( velHist, histN );
3206
+  n1->cplx.sum_d_rym  = _cmXsHistValue( rymHist, histN );
3207
+  n1->cplx.sum_d_lpch = l_pch_value;
3208
+  n1->cplx.sum_n_lpch = l_pch_cnt;
3209
+  n1->cplx.sum_d_rpch = r_pch_value;
3210
+  n1->cplx.sum_n_rpch = r_pch_cnt;
3211
+  
3212
+  return n2;
3213
+}
3214
+
3215
+cmXsRC_t _cmXsMeasComplexity( cmXsH_t h, double wndSecs )
3216
+{
3217
+  cmXsRC_t    rc = kOkXsRC;
3218
+  cmXScore_t* p  = _cmXScoreHandleToPtr(h);  
3219
+  cmXsPart_t* pp = p->partL;
3220
+
3221
+  memset(&p->cplx_max,0,sizeof(p->cplx_max));
3222
+  
3223
+  const cmXsNote_t* n0 = NULL;
3224
+  
3225
+  // for each part
3226
+  for(; pp!=NULL; pp=pp->link)
3227
+  {
3228
+    cmXsMeas_t* mp = pp->measL;
3229
+
3230
+    // for each measure
3231
+    for(; mp!=NULL; mp=mp->link)
3232
+    {
3233
+      cmXsNote_t* n1 = mp->noteL;
3234
+
3235
+
3236
+      // for each note in this measure
3237
+      for(; n1!=NULL; n1=n1->slink)
3238
+        if( cmIsFlag(n1->flags,kOnsetXsFl) )
3239
+        {
3240
+          if( n0 == NULL )
3241
+            n0 = n1;
3242
+          else
3243
+            if((n0 = _cmXsMeasComplexityInWindow(n0,n1,wndSecs)) == NULL )
3244
+              n0 = n1;
3245
+
3246
+          // track the max value for all complexity values to allow 
3247
+          // eventual normalization of the complexity values
3248
+          p->cplx_max.sum_d_vel  = cmMax(p->cplx_max.sum_d_vel, n1->cplx.sum_d_vel);
3249
+          p->cplx_max.sum_d_rym  = cmMax(p->cplx_max.sum_d_rym, n1->cplx.sum_d_rym);
3250
+          p->cplx_max.sum_d_lpch = cmMax(p->cplx_max.sum_d_lpch,n1->cplx.sum_d_lpch);
3251
+          p->cplx_max.sum_n_lpch = cmMax(p->cplx_max.sum_n_lpch,n1->cplx.sum_n_lpch);
3252
+          p->cplx_max.sum_d_rpch = cmMax(p->cplx_max.sum_d_rpch,n1->cplx.sum_d_rpch);
3253
+          p->cplx_max.sum_n_rpch = cmMax(p->cplx_max.sum_n_rpch,n1->cplx.sum_n_rpch);
3254
+        
3255
+        }
3256
+    }
3257
+  }
3258
+  return rc;
3259
+}
3260
+
3053 3261
 cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
3054 3262
 {
3055 3263
   cmXsRC_t rc = kOkXsRC;
@@ -3163,6 +3371,7 @@ typedef struct cmXsSvgEvt_str
3163 3371
   unsigned         voice;     // score voice number
3164 3372
   unsigned         d0;        // MIDI d0   (barNumb)
3165 3373
   unsigned         d1;        // MIDI d1
3374
+  cmXsComplexity_t cplx;
3166 3375
   struct cmXsSvgEvt_str* link;
3167 3376
 } cmXsSvgEvt_t;
3168 3377
 
@@ -3176,6 +3385,44 @@ typedef struct cmXsMidiFile_str
3176 3385
   
3177 3386
 } cmXsMidiFile_t;
3178 3387
 
3388
+
3389
+void _cmXsWriteMidiSvgLegend( cmSvgH_t svgH, unsigned index,  const cmChar_t* label, const cmChar_t* classStr )
3390
+{
3391
+  double x = 100;
3392
+  double y = 120*10 - 20*index;
3393
+  
3394
+  cmSvgWriterText( svgH, x, y, label, "legend" );
3395
+
3396
+  x += 75;
3397
+  cmSvgWriterLine( svgH, x, y, x+125, y, classStr );
3398
+}
3399
+
3400
+void _cmXsWriteMidiSvgLinePoint( cmSvgH_t svgH, double x0, double y0, double x1, double y1, double y_max, const cmChar_t* classStr, const cmChar_t* label )
3401
+{
3402
+  int  bn = 255;
3403
+  char b[bn+1];
3404
+  double y_scale = 10;
3405
+  double y_label = y1;
3406
+  
3407
+  b[0] = 0;
3408
+  y0 = (y0/y_max) * 127.0 * y_scale;
3409
+  y1 = (y1/y_max) * 127.0 * y_scale;
3410
+  
3411
+  cmSvgWriterLine(svgH, x0, y0,  x1, y1,  classStr );
3412
+
3413
+  if( y0 != y1 )
3414
+    snprintf(b,bn,"%5.0f %s",y_label,label==NULL?"":label);
3415
+  else
3416
+  {
3417
+    if( label != NULL )
3418
+      snprintf(b,bn,"%s",label);
3419
+  }
3420
+  
3421
+  if( strlen(b) )
3422
+    cmSvgWriterText(svgH, x1, y1, b, "pt_text");
3423
+
3424
+}
3425
+
3179 3426
 cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, const cmChar_t* dir, const cmChar_t* fn )
3180 3427
 {
3181 3428
   cmXsRC_t        rc         = kOkXsRC;
@@ -3186,7 +3433,8 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
3186 3433
   const cmChar_t* svgFn      = cmFsMakeFn(dir,fn0 = cmTextAppendSS(fn0,"_midi_svg"),"html",NULL);
3187 3434
   const cmChar_t* cssFn      = cmFsMakeFn(NULL,"score_midi_svg","css",NULL);
3188 3435
   cmChar_t*       t0         = NULL;  // temporary dynamic string
3189
-
3436
+  unsigned        i          = 0;
3437
+  const cmXsSvgEvt_t* e0 = NULL;
3190 3438
   cmMemFree(fn0);
3191 3439
   
3192 3440
   // create the SVG writer
@@ -3196,6 +3444,13 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
3196 3444
     goto errLabel;
3197 3445
   }
3198 3446
 
3447
+  _cmXsWriteMidiSvgLegend( svgH, 0,  "Velocity",    "cplx_vel" );
3448
+  _cmXsWriteMidiSvgLegend( svgH, 1,  "Duration",    "cplx_rym" );
3449
+  _cmXsWriteMidiSvgLegend( svgH, 2,  "Left Pitch",  "cplx_lpch" );
3450
+  _cmXsWriteMidiSvgLegend( svgH, 3,  "Right Pitch", "cplx_rpch" );
3451
+  _cmXsWriteMidiSvgLegend( svgH, 4,  "Density",     "cplx_density" );
3452
+
3453
+  
3199 3454
   // for each MIDI file element
3200 3455
   for(; e!=NULL && rc==kOkXsRC; e=e->link)
3201 3456
   {
@@ -3220,7 +3475,25 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
3220 3475
             
3221 3476
             if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, t0, "pitch") != kOkSvgRC )
3222 3477
               rc = kSvgFailXsRC;
3478
+            else
3479
+            {
3480
+              if( e0 != NULL )
3481
+              {
3482
+                bool fl = (i % 10) == 0;
3483
+                
3484
+                _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_vel,  e->tick, e->cplx.sum_d_vel,  p->cplx_max.sum_d_vel, "cplx_vel",fl?"V":NULL);
3485
+                _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_rym,  e->tick, e->cplx.sum_d_rym,  p->cplx_max.sum_d_rym, "cplx_rym",fl?"D":NULL);
3486
+                _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_lpch, e->tick, e->cplx.sum_d_lpch, p->cplx_max.sum_d_lpch, "cplx_lpch",fl?"L":NULL);
3487
+                _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_d_rpch, e->tick, e->cplx.sum_d_rpch, p->cplx_max.sum_d_rpch, "cplx_rpch",fl?"R":NULL);
3488
+                _cmXsWriteMidiSvgLinePoint(svgH, e0->tick, e0->cplx.sum_n_lpch + e0->cplx.sum_n_rpch, e->tick, e->cplx.sum_n_lpch  + e->cplx.sum_n_rpch, p->cplx_max.sum_n_lpch + p->cplx_max.sum_n_rpch, "cplx_density",fl?"N":NULL);
3489
+              }
3490
+
3491
+              e0 = e;
3492
+            }
3493
+            
3223 3494
           }
3495
+
3496
+          i+=1;
3224 3497
         }
3225 3498
         break;
3226 3499
 
@@ -3247,7 +3520,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
3247 3520
           cmSvgWriterRect(svgH, e->tick, y, e->durTicks, noteHeight-1, classLabel);
3248 3521
         }
3249 3522
         break;
3250
-    }
3523
+    }    
3251 3524
   }
3252 3525
   
3253 3526
   if( rc != kOkXsRC )
@@ -3267,7 +3540,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
3267 3540
 }
3268 3541
 
3269 3542
 
3270
-void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 )
3543
+void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1, const cmXsComplexity_t* cplx )
3271 3544
 {
3272 3545
   cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1);
3273 3546
   e->flags    = flags;
@@ -3277,6 +3550,9 @@ void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsig
3277 3550
   e->d0       = d0;       // note=pitch bar=number pedal=ctl# metronome=BPM 
3278 3551
   e->d1       = d1;
3279 3552
   
3553
+  if( cplx != NULL )
3554
+    e->cplx     = *cplx;
3555
+  
3280 3556
   if( mf->eol != NULL )
3281 3557
     mf->eol->link = e;
3282 3558
   else
@@ -3313,7 +3589,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm
3313 3589
         if( cmIsFlag(note->flags,kMetronomeXsFl) )
3314 3590
         {
3315 3591
           // set BPM as d0
3316
-          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0);
3592
+          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0,NULL);
3317 3593
           continue;
3318 3594
           
3319 3595
         }
@@ -3329,14 +3605,14 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm
3329 3605
             for(; tn!=NULL; tn=tn->tied)
3330 3606
               durTick += tn->tied_dur;
3331 3607
           }
3332
-          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel);
3608
+          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel,&note->cplx);
3333 3609
           continue;
3334 3610
         }
3335 3611
 
3336 3612
         // if this is a bar event
3337 3613
         if( cmIsFlag(note->flags,kBarXsFl) )
3338 3614
         {
3339
-          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0);
3615
+          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0,NULL);
3340 3616
           continue;
3341 3617
         }
3342 3618
 
@@ -3344,7 +3620,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm
3344 3620
         if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) )
3345 3621
         {
3346 3622
           unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;          
3347
-          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127);
3623
+          _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127,NULL);
3348 3624
           continue;
3349 3625
         }
3350 3626
         
@@ -3407,6 +3683,10 @@ cmXsRC_t cmXScoreTest(
3407 3683
   
3408 3684
   if( midiOutFn != NULL )
3409 3685
   {
3686
+    double wndSecs = 1.0;
3687
+    _cmXsMeasComplexity(h,wndSecs);
3688
+
3689
+    
3410 3690
     cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
3411 3691
 
3412 3692
     _cmXsWriteMidiFile(ctx, h, pp->dirStr, pp->fnStr );
@@ -3417,7 +3697,7 @@ cmXsRC_t cmXScoreTest(
3417 3697
     
3418 3698
   }
3419 3699
   
3420
-  cmXScoreReport(h,&ctx->rpt,true);
3700
+  //cmXScoreReport(h,&ctx->rpt,true);
3421 3701
 
3422 3702
  errLabel:
3423 3703
   return cmXScoreFinalize(&h);

Loading…
Cancel
Save