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