Browse Source

cmXScore.h,c : Began MIDI output file generation coding. Added _cmXsWriteMidiSvg().

master
kevin 7 years ago
parent
commit
38cdd3012b
2 changed files with 182 additions and 30 deletions
  1. 177
    25
      app/cmXScore.c
  2. 5
    5
      app/cmXScore.h

+ 177
- 25
app/cmXScore.c View File

@@ -10,6 +10,7 @@
10 10
 #include "cmLinkedHeap.h"
11 11
 #include "cmXml.h"
12 12
 #include "cmText.h"
13
+#include "cmFileSys.h"
13 14
 #include "cmXScore.h"
14 15
 #include "cmTime.h"
15 16
 #include "cmMidi.h"
@@ -19,6 +20,7 @@
19 20
 #include "cmSymTbl.h"
20 21
 #include "cmScore.h"
21 22
 
23
+
22 24
 #include "cmFile.h"
23 25
 #include "cmSymTbl.h"
24 26
 #include "cmAudioFile.h"
@@ -29,6 +31,8 @@
29 31
 #include "cmProc2.h"
30 32
 #include "cmProc5.h"
31 33
 
34
+#include "cmSvgWriter.h"
35
+
32 36
 cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
33 37
 
34 38
 enum
@@ -89,6 +93,8 @@ typedef struct cmXsNote_str
89 93
 
90 94
   const cmXmlNode_t*          xmlNode;  // note xml ptr
91 95
 
96
+  struct cmXsNote_str*        tied;     // subsequent note tied to this note
97
+
92 98
   struct cmXsNote_str*        mlink;    // measure note list
93 99
   struct cmXsNote_str*        slink;    // time sorted event list
94 100
 
@@ -103,7 +109,7 @@ typedef struct cmXsVoice_str
103 109
 
104 110
 typedef struct cmXsSpan_str
105 111
 {
106
-  unsigned             staff;
112
+  unsigned             staff;     
107 113
   unsigned             number;
108 114
   struct cmXsMeas_str* meas;
109 115
   unsigned             tick0;
@@ -374,11 +380,8 @@ cmXsRC_t  _cmXScoreParsePitch( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t
374 380
 
375 381
   cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
376 382
 
377
-  cmChar_t buf[3] = { *step, '0', '\0'};
378
-  unsigned midi = cmSciPitchToMidi(buf);
379
-
380
-  midi      += (12 * octave);
381
-  midi      += alter;
383
+  int acc = alter;
384
+  unsigned midi = cmSciPitchToMidiPitch(*step,acc,octave);
382 385
 
383 386
   np->pitch  = midi;
384 387
   np->step   = *step;
@@ -1044,10 +1047,8 @@ void _cmXScoreSpreadGraceNotes( cmXScore_t* p )
1044 1047
          tick1 = _cmXsSpreadGraceNotes(a,ai);
1045 1048
     }
1046 1049
   }
1047
-
1048 1050
 }
1049 1051
 
1050
-
1051 1052
 bool  _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl )
1052 1053
 {
1053 1054
   cmXsNote_t* nnp       = n0p->slink;  // begin w/ note following np
@@ -1066,8 +1067,8 @@ bool  _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo
1066 1067
       mp  = mp->link;
1067 1068
       nnp = mp->noteL;
1068 1069
 
1069
-    // if a measure was completed and no end note was found ... then the tie is unterminated
1070
-    // (a tie must be continued in every measure which it passes through)
1070
+      // if a measure was completed and no end note was found ... then the tie is unterminated
1071
+      // (a tie must be continued in every measure which it passes through)
1071 1072
       if( mp->number > measNumb + 1 )
1072 1073
         break;
1073 1074
     }
@@ -1080,6 +1081,7 @@ bool  _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo
1080 1081
       {
1081 1082
         nnp->flags |= kTieProcXsFl;
1082 1083
         nnp->flags  = cmClrFlag(nnp->flags,kOnsetXsFl);
1084
+        n0p->tied   = nnp;
1083 1085
 
1084 1086
         if( rptFl )
1085 1087
           printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
@@ -1088,6 +1090,8 @@ bool  _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo
1088 1090
         if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
1089 1091
           return true;
1090 1092
 
1093
+        n0p = nnp;
1094
+        
1091 1095
         // record the measure number of the last note with a tie-start
1092 1096
         measNumb = mp->number;
1093 1097
       }
@@ -1134,15 +1138,19 @@ void  _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
1134 1138
           n += 1;
1135 1139
         }
1136 1140
 
1141
+        // Validate the tie state of the current note.
1137 1142
         if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) )
1138 1143
         {
1139 1144
           cmChar_t    acc  = np->alter==-1?'b' : (np->alter==1?'#':' ');
1140 1145
           cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i marked as a tied note but is also marked to sound.",np->step,acc,np->octave,mp->number);
1141 1146
         }
1142 1147
 
1143
-        // set the location
1148
+        //
1149
+        // Set the score location of notes marked for onset and bar lines.
1150
+        //
1144 1151
         if( cmIsFlag(np->flags,kOnsetXsFl|kBarXsFl) )
1145 1152
         {
1153
+          // if this note does not share the same location as the previous 'located' note then increment the 'loc' index
1146 1154
           if( cmIsFlag(np->flags,kBarXsFl) || (n0!=NULL && n0->tick!=np->tick))
1147 1155
             locIdx += 1;
1148 1156
 
@@ -1153,7 +1161,7 @@ void  _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
1153 1161
     }
1154 1162
   }
1155 1163
 
1156
-  printf("Found:%i Not Found:%i\n",m,n-m);
1164
+  printf("Tied notes found:%i Not found:%i\n",m,n-m);
1157 1165
 }
1158 1166
 
1159 1167
 cmXsRC_t  _cmXScoreResolveOctaveShift( cmXScore_t* p )
@@ -1362,7 +1370,7 @@ void _cmXScoreFixBarLines( cmXScore_t* p )
1362 1370
   }
1363 1371
 }
1364 1372
 
1365
-cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* midiFn )
1373
+cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn )
1366 1374
 {
1367 1375
   cmXsRC_t rc = kOkXsRC;
1368 1376
 
@@ -2511,13 +2519,125 @@ void  cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
2511 2519
   }
2512 2520
 }
2513 2521
 
2522
+typedef struct cmXsMidiEvt_str
2523
+{
2524
+  unsigned         flags;     // k???XsFl
2525
+  unsigned         tick;      // start tick
2526
+  unsigned         durTicks;  // dur-ticks
2527
+  unsigned         voice;     // score voice number
2528
+  unsigned         d0;        // MIDI d0   (barNumb)
2529
+  unsigned         d1;        // MIDI d1
2530
+  struct cmXsMidiEvt_str* link;
2531
+} cmXsMidiEvt_t;
2532
+
2533
+typedef struct cmXsMidiFile_str
2534
+{
2535
+  cmXsMidiEvt_t* elist;
2536
+  cmXsMidiEvt_t* eol;
2537
+
2538
+  unsigned pitch_min;
2539
+  unsigned pitch_max;
2540
+  
2541
+} cmXsMidiFile_t;
2542
+
2543
+cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, const cmChar_t* dir, const cmChar_t* fn )
2544
+{
2545
+  cmXsRC_t        rc         = kOkXsRC;
2546
+  cmSvgH_t        svgH       = cmSvgNullHandle;
2547
+  cmXsMidiEvt_t*  e          = mf->elist;
2548
+  unsigned        noteHeight = 10;
2549
+  const cmChar_t* svgFn      = cmFsMakeFn(dir,fn,"html",NULL);
2550
+  const cmChar_t* cssFn      = cmFsMakeFn(NULL,fn,"css",NULL);
2551
+  cmChar_t*       t0          = NULL;  // temporary dynamic string
2552
+  // create the SVG writer
2553
+  if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC )
2554
+  {
2555
+    rc = cmErrMsg(&p->err,kSvgFailXsRC,"Unable to create the MIDI SVG output file '%s'.",cmStringNullGuard(svgFn));
2556
+    goto errLabel;
2557
+  }
2558
+
2559
+  // for each MIDI file element
2560
+  for(; e!=NULL && rc==kOkXsRC; e=e->link)
2561
+  {
2562
+    // if this is a note
2563
+    if( cmIsFlag(e->flags,kOnsetXsFl) )
2564
+    {
2565
+      const cmChar_t* classLabel = "note";
2566
+      
2567
+
2568
+      t0 = cmTsPrintfP(t0,"note_%i%s",e->voice, cmIsFlag(e->flags,kGraceXsFl) ? "_g":"");
2569
+      
2570
+      if( cmIsFlag(e->flags,kGraceXsFl) )
2571
+        classLabel = "grace";
2572
+      
2573
+      if( cmSvgWriterRect(svgH, e->tick, e->d0 * noteHeight,  e->durTicks,  noteHeight-1, t0 ) != kOkSvgRC )
2574
+        rc = kSvgFailXsRC;
2575
+      else
2576
+        if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, cmMidiToSciPitch( e->d0, NULL, 0), "pitch") != kOkSvgRC )
2577
+          rc = kSvgFailXsRC;
2578
+    }
2579
+
2580
+    // if this is a bar
2581
+    if( cmIsFlag(e->flags,kBarXsFl) )
2582
+    {
2583
+      if( cmSvgWriterLine(svgH, e->tick, 0, e->tick, 127*noteHeight, "bar") != kOkSvgRC )
2584
+        rc = kSvgFailXsRC;
2585
+      else
2586
+      {
2587
+        if( cmSvgWriterText(svgH, e->tick, 10, t0 = cmTsPrintfP(t0,"%i",e->d0), "text" ) != kOkSvgRC )
2588
+          rc = kSvgFailXsRC;
2589
+      }
2590
+    }
2591
+  }
2592
+
2593
+  if( rc != kOkXsRC )
2594
+    cmErrMsg(&p->err,kSvgFailXsRC,"SVG element insert failed.");
2595
+
2596
+  if( rc == kOkXsRC )
2597
+    if( cmSvgWriterWrite(svgH,cssFn,svgFn) != kOkSvgRC )
2598
+      rc = cmErrMsg(&p->err,kSvgFailXsRC,"SVG file write to '%s' failed.",cmStringNullGuard(svgFn));
2599
+  
2600
+ errLabel:
2601
+  cmSvgWriterFree(&svgH);
2602
+  cmFsFreeFn(svgFn);
2603
+  cmFsFreeFn(cssFn);
2604
+  cmMemFree(t0);
2605
+  
2606
+  return rc;
2607
+}
2608
+
2609
+void _cmXsPushMidiEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 )
2610
+{
2611
+  cmXsMidiEvt_t* e = cmLhAllocZ(p->lhH,cmXsMidiEvt_t,1);
2612
+  e->flags    = flags;
2613
+  e->tick     = tick;
2614
+  e->durTicks = durTick;
2615
+  e->voice    = voice;
2616
+  e->d0       = d0;
2617
+  e->d1       = d1;
2618
+  if( mf->eol != NULL )
2619
+    mf->eol->link = e;
2620
+  else
2621
+    mf->elist = e;
2622
+
2623
+  // track the min/max pitch
2624
+  if( cmIsFlag(flags,kOnsetXsFl) )
2625
+  {
2626
+    mf->pitch_min = mf->eol==NULL ? d0 : cmMin(mf->pitch_min,d0);
2627
+    mf->pitch_max = mf->eol==NULL ? d0 : cmMin(mf->pitch_max,d0);
2628
+  }
2629
+  
2630
+  mf->eol = e;
2631
+}
2514 2632
 
2515
-cmXsRC_t cmXScoreWriteMidi( cmXsH_t h, const cmChar_t* fn )
2633
+cmXsRC_t _cmXScoreWriteMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
2516 2634
 {
2517
-  assert(0); // function not implemented
2518 2635
   cmXScore_t* p  = _cmXScoreHandleToPtr(h);
2519 2636
   cmXsPart_t* pp = p->partL;
2520 2637
 
2638
+  cmXsMidiFile_t mf;
2639
+  memset(&mf,0,sizeof(mf));
2640
+
2521 2641
   for(; pp!=NULL; pp=pp->link)
2522 2642
   {
2523 2643
     const cmXsMeas_t* meas = pp->measL;
@@ -2527,39 +2647,62 @@ cmXsRC_t cmXScoreWriteMidi( cmXsH_t h, const cmChar_t* fn )
2527 2647
       const cmXsNote_t* note = meas->noteL;
2528 2648
       for(; note!=NULL; note=note->slink)
2529 2649
       {
2650
+        // if this is a sounding note
2651
+        if( cmIsFlag(note->flags,kOnsetXsFl) )
2652
+        {
2653
+          unsigned d0      =  cmSciPitchToMidiPitch( note->step, note->alter, note->octave );
2654
+          unsigned durTick = note->duration;
2655
+          if( note->tied != NULL )
2656
+          {
2657
+            cmXsNote_t* tn = note->tied;
2658
+            for(; tn!=NULL; tn=tn->tied)
2659
+              durTick += tn->duration;
2660
+          }
2661
+          _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel);
2662
+          continue;
2663
+        }
2530 2664
 
2665
+        // if this is a bar event
2666
+        if( cmIsFlag(note->flags,kBarXsFl) )
2667
+        {
2668
+          _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0);
2669
+          continue;
2670
+        }
2671
+        
2531 2672
       }
2532 2673
     }
2533 2674
   }
2675
+
2676
+  return _cmXsWriteMidiSvg( ctx, p, &mf, dir, fn );
2677
+
2534 2678
 }
2535 2679
 
2536 2680
 cmXsRC_t cmXScoreTest(
2537 2681
   cmCtx_t* ctx,
2538 2682
   const cmChar_t* xmlFn,
2539
-  const cmChar_t* midiFn,
2540
-  const cmChar_t* outFn,
2541
-  const cmChar_t* reorderFn )
2683
+  const cmChar_t* reorderFn,
2684
+  const cmChar_t* csvOutFn,
2685
+  const cmChar_t* midiOutFn)
2542 2686
 {
2543 2687
   cmXsRC_t rc;
2544 2688
   cmXsH_t h = cmXsNullHandle;
2545 2689
 
2546
-  if((rc = cmXScoreInitialize( ctx, &h, xmlFn, midiFn)) != kOkXsRC )
2690
+  if((rc = cmXScoreInitialize( ctx, &h, xmlFn)) != kOkXsRC )
2547 2691
     return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
2548 2692
 
2549 2693
   if( reorderFn != NULL )
2550 2694
     cmXScoreReorder(h,reorderFn);
2551 2695
   
2552
-  if( outFn != NULL )
2696
+  if( csvOutFn != NULL )
2553 2697
   {
2554 2698
     cmScH_t scH = cmScNullHandle;
2555 2699
     double srate = 44100.0;
2556 2700
     
2557
-    cmXScoreWriteCsv(h,outFn);
2558
-
2701
+    cmXScoreWriteCsv(h,csvOutFn);
2559 2702
 
2560 2703
     cmSymTblH_t stH = cmSymTblCreate(cmSymTblNullHandle, 0, ctx );
2561 2704
     
2562
-    if( cmScoreInitialize( ctx, &scH, outFn, srate, NULL, 0, NULL, NULL, stH) != kOkScRC )
2705
+    if( cmScoreInitialize( ctx, &scH, csvOutFn, srate, NULL, 0, NULL, NULL, stH) != kOkScRC )
2563 2706
       cmErrMsg(&ctx->err,kFileFailXsRC,"The generated CSV file could not be parsed.");
2564 2707
     else
2565 2708
     {
@@ -2569,11 +2712,20 @@ cmXsRC_t cmXScoreTest(
2569 2712
       cmScoreFinalize(&scH);
2570 2713
     }
2571 2714
 
2572
-    cmSymTblDestroy(&stH);
2715
+    cmSymTblDestroy(&stH); 
2716
+  }
2717
+
2718
+  if( midiOutFn != NULL )
2719
+  {
2720
+    cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
2721
+    
2722
+    _cmXScoreWriteMidi( ctx, h, pp->dirStr, pp->fnStr );
2723
+
2724
+    cmFsFreePathParts(pp);
2573 2725
     
2574 2726
   }
2575 2727
   
2576
-  cmXScoreReport(h,&ctx->rpt,true);
2728
+  //cmXScoreReport(h,&ctx->rpt,true);
2577 2729
 
2578 2730
   return cmXScoreFinalize(&h);
2579 2731
 

+ 5
- 5
app/cmXScore.h View File

@@ -16,7 +16,8 @@ extern "C" {
16 16
     kUnterminatedSlurXsRC,
17 17
     kUnterminatedOctaveShiftXsrRC,
18 18
     kMidiFailXsRC,
19
-    kFileFailXsRC
19
+    kFileFailXsRC,
20
+    kSvgFailXsRC
20 21
   };
21 22
 
22 23
   typedef cmRC_t     cmXsRC_t;
@@ -56,18 +57,17 @@ extern "C" {
56 57
   //    the grace note ordering changed specified in 'score_print_mk_edit.txt',
57 58
   //    via cmXScoreReorder() however the ticks are now incorrect - fix them.
58 59
   
59
-  cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* midiFn );
60
+  cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn );
60 61
   cmXsRC_t cmXScoreFinalize( cmXsH_t* hp );
61 62
 
62 63
   bool     cmXScoreIsValid( cmXsH_t h );
63 64
 
64 65
   cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn );
65 66
 
66
-  cmXsRC_t cmXScoreWriteMidi( cmXsH_t h, const cmChar_t* fn );
67
-
68 67
   void     cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl );
69 68
 
70
-  cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* midiFn, const cmChar_t* outFn, const cmChar_t* reorderFn );
69
+  // Generate the CSV file suitable for use by cmScore.
70
+  cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* reorderFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn );
71 71
   
72 72
 #ifdef __cplusplus
73 73
 }

Loading…
Cancel
Save