Переглянути джерело

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

master
kevin 7 роки тому
джерело
коміт
38cdd3012b
2 змінених файлів з 182 додано та 30 видалено
  1. 177
    25
      app/cmXScore.c
  2. 5
    5
      app/cmXScore.h

+ 177
- 25
app/cmXScore.c Переглянути файл

10
 #include "cmLinkedHeap.h"
10
 #include "cmLinkedHeap.h"
11
 #include "cmXml.h"
11
 #include "cmXml.h"
12
 #include "cmText.h"
12
 #include "cmText.h"
13
+#include "cmFileSys.h"
13
 #include "cmXScore.h"
14
 #include "cmXScore.h"
14
 #include "cmTime.h"
15
 #include "cmTime.h"
15
 #include "cmMidi.h"
16
 #include "cmMidi.h"
19
 #include "cmSymTbl.h"
20
 #include "cmSymTbl.h"
20
 #include "cmScore.h"
21
 #include "cmScore.h"
21
 
22
 
23
+
22
 #include "cmFile.h"
24
 #include "cmFile.h"
23
 #include "cmSymTbl.h"
25
 #include "cmSymTbl.h"
24
 #include "cmAudioFile.h"
26
 #include "cmAudioFile.h"
29
 #include "cmProc2.h"
31
 #include "cmProc2.h"
30
 #include "cmProc5.h"
32
 #include "cmProc5.h"
31
 
33
 
34
+#include "cmSvgWriter.h"
35
+
32
 cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
36
 cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
33
 
37
 
34
 enum
38
 enum
89
 
93
 
90
   const cmXmlNode_t*          xmlNode;  // note xml ptr
94
   const cmXmlNode_t*          xmlNode;  // note xml ptr
91
 
95
 
96
+  struct cmXsNote_str*        tied;     // subsequent note tied to this note
97
+
92
   struct cmXsNote_str*        mlink;    // measure note list
98
   struct cmXsNote_str*        mlink;    // measure note list
93
   struct cmXsNote_str*        slink;    // time sorted event list
99
   struct cmXsNote_str*        slink;    // time sorted event list
94
 
100
 
103
 
109
 
104
 typedef struct cmXsSpan_str
110
 typedef struct cmXsSpan_str
105
 {
111
 {
106
-  unsigned             staff;
112
+  unsigned             staff;     
107
   unsigned             number;
113
   unsigned             number;
108
   struct cmXsMeas_str* meas;
114
   struct cmXsMeas_str* meas;
109
   unsigned             tick0;
115
   unsigned             tick0;
374
 
380
 
375
   cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
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
   np->pitch  = midi;
386
   np->pitch  = midi;
384
   np->step   = *step;
387
   np->step   = *step;
1044
          tick1 = _cmXsSpreadGraceNotes(a,ai);
1047
          tick1 = _cmXsSpreadGraceNotes(a,ai);
1045
     }
1048
     }
1046
   }
1049
   }
1047
-
1048
 }
1050
 }
1049
 
1051
 
1050
-
1051
 bool  _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl )
1052
 bool  _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl )
1052
 {
1053
 {
1053
   cmXsNote_t* nnp       = n0p->slink;  // begin w/ note following np
1054
   cmXsNote_t* nnp       = n0p->slink;  // begin w/ note following np
1066
       mp  = mp->link;
1067
       mp  = mp->link;
1067
       nnp = mp->noteL;
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
       if( mp->number > measNumb + 1 )
1072
       if( mp->number > measNumb + 1 )
1072
         break;
1073
         break;
1073
     }
1074
     }
1080
       {
1081
       {
1081
         nnp->flags |= kTieProcXsFl;
1082
         nnp->flags |= kTieProcXsFl;
1082
         nnp->flags  = cmClrFlag(nnp->flags,kOnsetXsFl);
1083
         nnp->flags  = cmClrFlag(nnp->flags,kOnsetXsFl);
1084
+        n0p->tied   = nnp;
1083
 
1085
 
1084
         if( rptFl )
1086
         if( rptFl )
1085
           printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
1087
           printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
1088
         if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
1090
         if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
1089
           return true;
1091
           return true;
1090
 
1092
 
1093
+        n0p = nnp;
1094
+        
1091
         // record the measure number of the last note with a tie-start
1095
         // record the measure number of the last note with a tie-start
1092
         measNumb = mp->number;
1096
         measNumb = mp->number;
1093
       }
1097
       }
1134
           n += 1;
1138
           n += 1;
1135
         }
1139
         }
1136
 
1140
 
1141
+        // Validate the tie state of the current note.
1137
         if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) )
1142
         if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) )
1138
         {
1143
         {
1139
           cmChar_t    acc  = np->alter==-1?'b' : (np->alter==1?'#':' ');
1144
           cmChar_t    acc  = np->alter==-1?'b' : (np->alter==1?'#':' ');
1140
           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);
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
         if( cmIsFlag(np->flags,kOnsetXsFl|kBarXsFl) )
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
           if( cmIsFlag(np->flags,kBarXsFl) || (n0!=NULL && n0->tick!=np->tick))
1154
           if( cmIsFlag(np->flags,kBarXsFl) || (n0!=NULL && n0->tick!=np->tick))
1147
             locIdx += 1;
1155
             locIdx += 1;
1148
 
1156
 
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
 cmXsRC_t  _cmXScoreResolveOctaveShift( cmXScore_t* p )
1167
 cmXsRC_t  _cmXScoreResolveOctaveShift( 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
   cmXsRC_t rc = kOkXsRC;
1375
   cmXsRC_t rc = kOkXsRC;
1368
 
1376
 
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
   cmXScore_t* p  = _cmXScoreHandleToPtr(h);
2635
   cmXScore_t* p  = _cmXScoreHandleToPtr(h);
2519
   cmXsPart_t* pp = p->partL;
2636
   cmXsPart_t* pp = p->partL;
2520
 
2637
 
2638
+  cmXsMidiFile_t mf;
2639
+  memset(&mf,0,sizeof(mf));
2640
+
2521
   for(; pp!=NULL; pp=pp->link)
2641
   for(; pp!=NULL; pp=pp->link)
2522
   {
2642
   {
2523
     const cmXsMeas_t* meas = pp->measL;
2643
     const cmXsMeas_t* meas = pp->measL;
2527
       const cmXsNote_t* note = meas->noteL;
2647
       const cmXsNote_t* note = meas->noteL;
2528
       for(; note!=NULL; note=note->slink)
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
 cmXsRC_t cmXScoreTest(
2680
 cmXsRC_t cmXScoreTest(
2537
   cmCtx_t* ctx,
2681
   cmCtx_t* ctx,
2538
   const cmChar_t* xmlFn,
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
   cmXsRC_t rc;
2687
   cmXsRC_t rc;
2544
   cmXsH_t h = cmXsNullHandle;
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
     return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
2691
     return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
2548
 
2692
 
2549
   if( reorderFn != NULL )
2693
   if( reorderFn != NULL )
2550
     cmXScoreReorder(h,reorderFn);
2694
     cmXScoreReorder(h,reorderFn);
2551
   
2695
   
2552
-  if( outFn != NULL )
2696
+  if( csvOutFn != NULL )
2553
   {
2697
   {
2554
     cmScH_t scH = cmScNullHandle;
2698
     cmScH_t scH = cmScNullHandle;
2555
     double srate = 44100.0;
2699
     double srate = 44100.0;
2556
     
2700
     
2557
-    cmXScoreWriteCsv(h,outFn);
2558
-
2701
+    cmXScoreWriteCsv(h,csvOutFn);
2559
 
2702
 
2560
     cmSymTblH_t stH = cmSymTblCreate(cmSymTblNullHandle, 0, ctx );
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
       cmErrMsg(&ctx->err,kFileFailXsRC,"The generated CSV file could not be parsed.");
2706
       cmErrMsg(&ctx->err,kFileFailXsRC,"The generated CSV file could not be parsed.");
2564
     else
2707
     else
2565
     {
2708
     {
2569
       cmScoreFinalize(&scH);
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
   return cmXScoreFinalize(&h);
2730
   return cmXScoreFinalize(&h);
2579
 
2731
 

+ 5
- 5
app/cmXScore.h Переглянути файл

16
     kUnterminatedSlurXsRC,
16
     kUnterminatedSlurXsRC,
17
     kUnterminatedOctaveShiftXsrRC,
17
     kUnterminatedOctaveShiftXsrRC,
18
     kMidiFailXsRC,
18
     kMidiFailXsRC,
19
-    kFileFailXsRC
19
+    kFileFailXsRC,
20
+    kSvgFailXsRC
20
   };
21
   };
21
 
22
 
22
   typedef cmRC_t     cmXsRC_t;
23
   typedef cmRC_t     cmXsRC_t;
56
   //    the grace note ordering changed specified in 'score_print_mk_edit.txt',
57
   //    the grace note ordering changed specified in 'score_print_mk_edit.txt',
57
   //    via cmXScoreReorder() however the ticks are now incorrect - fix them.
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
   cmXsRC_t cmXScoreFinalize( cmXsH_t* hp );
61
   cmXsRC_t cmXScoreFinalize( cmXsH_t* hp );
61
 
62
 
62
   bool     cmXScoreIsValid( cmXsH_t h );
63
   bool     cmXScoreIsValid( cmXsH_t h );
63
 
64
 
64
   cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn );
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
   void     cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl );
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
 #ifdef __cplusplus
72
 #ifdef __cplusplus
73
 }
73
 }

Завантаження…
Відмінити
Зберегти