|
@@ -37,28 +37,31 @@ cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
|
37
|
37
|
|
38
|
38
|
enum
|
39
|
39
|
{
|
40
|
|
- kSectionXsFl = 0x000001, // rvalue holds section number
|
41
|
|
- kBarXsFl = 0x000002,
|
42
|
|
- kRestXsFl = 0x000004,
|
43
|
|
- kGraceXsFl = 0x000008,
|
44
|
|
- kDotXsFl = 0x000010,
|
45
|
|
- kChordXsFl = 0x000020,
|
46
|
|
- kDynXsFl = 0x000040,
|
47
|
|
- kEvenXsFl = 0x000080,
|
48
|
|
- kTempoXsFl = 0x000100,
|
49
|
|
- kHeelXsFl = 0x000200,
|
50
|
|
- kTieBegXsFl = 0x000400,
|
51
|
|
- kTieEndXsFl = 0x000800,
|
52
|
|
- kTieProcXsFl = 0x001000,
|
53
|
|
- kDampDnXsFl = 0x002000,
|
54
|
|
- kDampUpXsFl = 0x004000,
|
55
|
|
- kDampUpDnXsFl = 0x008000,
|
56
|
|
- kSostDnXsFl = 0x010000,
|
57
|
|
- kSostUpXsFl = 0x020000,
|
58
|
|
- kMetronomeXsFl = 0x040000, // duration holds BPM
|
59
|
|
- kOnsetXsFl = 0x080000, // this is a sounding note
|
60
|
|
- kBegGroupXsFl = 0x100000,
|
61
|
|
- kEndGroupXsFl = 0x200000
|
|
40
|
+ kSectionXsFl = 0x000001, // rvalue holds section number
|
|
41
|
+ kBarXsFl = 0x000002,
|
|
42
|
+ kRestXsFl = 0x000004,
|
|
43
|
+ kGraceXsFl = 0x000008,
|
|
44
|
+ kDotXsFl = 0x000010,
|
|
45
|
+ kChordXsFl = 0x000020,
|
|
46
|
+ kDynXsFl = 0x000040,
|
|
47
|
+ kEvenXsFl = 0x000080,
|
|
48
|
+ kTempoXsFl = 0x000100,
|
|
49
|
+ kHeelXsFl = 0x000200,
|
|
50
|
+ kTieBegXsFl = 0x000400,
|
|
51
|
+ kTieEndXsFl = 0x000800,
|
|
52
|
+ kTieProcXsFl = 0x001000,
|
|
53
|
+ kDampDnXsFl = 0x002000,
|
|
54
|
+ kDampUpXsFl = 0x004000,
|
|
55
|
+ kDampUpDnXsFl = 0x008000,
|
|
56
|
+ kSostDnXsFl = 0x010000,
|
|
57
|
+ kSostUpXsFl = 0x020000,
|
|
58
|
+ kMetronomeXsFl = 0x040000, // duration holds BPM
|
|
59
|
+ kOnsetXsFl = 0x080000, // this is a sounding note
|
|
60
|
+ kBegGroupXsFl = 0x100000,
|
|
61
|
+ kEndGroupXsFl = 0x200000,
|
|
62
|
+ kBegGraceXsFl = 0x400000, // beg grace note group
|
|
63
|
+ kEndGraceXsFl = 0x800000 // end grace note group
|
|
64
|
+
|
62
|
65
|
};
|
63
|
66
|
|
64
|
67
|
struct cmXsMeas_str;
|
|
@@ -87,6 +90,7 @@ typedef struct cmXsNote_str
|
87
|
90
|
unsigned evenGroupId; // eveness group id
|
88
|
91
|
unsigned dynGroupId; // dynamics group id
|
89
|
92
|
unsigned tempoGroupId; // tempo group id
|
|
93
|
+ unsigned graceGroupId; // grace note group id
|
90
|
94
|
|
91
|
95
|
struct cmXsVoice_str* voice; // voice this note belongs to
|
92
|
96
|
struct cmXsMeas_str* meas; // measure this note belongs to
|
|
@@ -94,7 +98,8 @@ typedef struct cmXsNote_str
|
94
|
98
|
const cmXmlNode_t* xmlNode; // note xml ptr
|
95
|
99
|
|
96
|
100
|
struct cmXsNote_str* tied; // subsequent note tied to this note
|
97
|
|
-
|
|
101
|
+ struct cmXsNote_str* grace; // grace note groups link backward in time from the anchor note
|
|
102
|
+
|
98
|
103
|
struct cmXsNote_str* mlink; // measure note list
|
99
|
104
|
struct cmXsNote_str* slink; // time sorted event list
|
100
|
105
|
|
|
@@ -1459,6 +1464,89 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p )
|
1459
|
1464
|
return rc;
|
1460
|
1465
|
}
|
1461
|
1466
|
|
|
1467
|
+
|
|
1468
|
+// Adjust the locations of grace notes. Note that this must be done
|
|
1469
|
+// after reordering so that we can be sure that the order in time of
|
|
1470
|
+// the notes in each group has been set prior to building the
|
|
1471
|
+// grace note groups - which must be in reverse time order.
|
|
1472
|
+cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p )
|
|
1473
|
+{
|
|
1474
|
+ cmXsRC_t rc = kOkXsRC;
|
|
1475
|
+ unsigned graceGroupId = 1;
|
|
1476
|
+
|
|
1477
|
+ for(; 1; ++graceGroupId)
|
|
1478
|
+ {
|
|
1479
|
+ cmXsNote_t* gnp = NULL;
|
|
1480
|
+ cmXsPart_t* pp = p->partL;
|
|
1481
|
+ double ticksPerSec = 0;
|
|
1482
|
+
|
|
1483
|
+ for(; pp!=NULL; pp=pp->link)
|
|
1484
|
+ {
|
|
1485
|
+ cmXsMeas_t* mp = pp->measL;
|
|
1486
|
+ for(; mp!=NULL; mp=mp->link)
|
|
1487
|
+ {
|
|
1488
|
+ cmXsNote_t* np = mp->noteL;
|
|
1489
|
+ for(; np!=NULL; np=np->slink )
|
|
1490
|
+ {
|
|
1491
|
+ // notice change of tempo
|
|
1492
|
+ if( cmIsFlag(np->flags,kMetronomeXsFl) )
|
|
1493
|
+ {
|
|
1494
|
+ // ticks/sec = ticks/qn * qn/sec
|
|
1495
|
+ ticksPerSec = mp->divisions * np->duration / 60.0;
|
|
1496
|
+ }
|
|
1497
|
+
|
|
1498
|
+ // if this note is part of the grace note group we are searching for
|
|
1499
|
+ if( np->graceGroupId == graceGroupId )
|
|
1500
|
+ {
|
|
1501
|
+ // add the note to the grace note list
|
|
1502
|
+ np->grace = gnp;
|
|
1503
|
+
|
|
1504
|
+ // set each grace note to have 1/20 of a second duration
|
|
1505
|
+ if( cmIsFlag(np->flags,kGraceXsFl) )
|
|
1506
|
+ np->duration = floor(ticksPerSec / 20.0);
|
|
1507
|
+
|
|
1508
|
+ gnp = np;
|
|
1509
|
+ }
|
|
1510
|
+ }
|
|
1511
|
+ }
|
|
1512
|
+ }
|
|
1513
|
+
|
|
1514
|
+ // no records were found for this grace id - we're done
|
|
1515
|
+ if( gnp == NULL )
|
|
1516
|
+ break;
|
|
1517
|
+
|
|
1518
|
+ cmXsNote_t* p0 = NULL;
|
|
1519
|
+ cmXsNote_t* p1 = gnp;
|
|
1520
|
+
|
|
1521
|
+ for(; p1!=NULL; p1=p1->grace)
|
|
1522
|
+ {
|
|
1523
|
+ if(1)
|
|
1524
|
+ {
|
|
1525
|
+ const char* type = "g";
|
|
1526
|
+ if( cmIsFlag(p1->flags,kBegGraceXsFl) )
|
|
1527
|
+ type = "b";
|
|
1528
|
+
|
|
1529
|
+ if( cmIsFlag(p1->flags,kEndGraceXsFl) )
|
|
1530
|
+ type = "i";
|
|
1531
|
+
|
|
1532
|
+ bool fl = p0 != NULL && p0->tick < p1->tick;
|
|
1533
|
+ printf("%3i %s %i %5i %i\n",p1->graceGroupId,type,p1->tick,p1->duration,fl);
|
|
1534
|
+ }
|
|
1535
|
+
|
|
1536
|
+ // TODO:
|
|
1537
|
+ // position grace notes here
|
|
1538
|
+
|
|
1539
|
+
|
|
1540
|
+
|
|
1541
|
+ p0 = p1;
|
|
1542
|
+ }
|
|
1543
|
+
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+ }
|
|
1547
|
+ return rc;
|
|
1548
|
+}
|
|
1549
|
+
|
1462
|
1550
|
cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn )
|
1463
|
1551
|
{
|
1464
|
1552
|
cmXsRC_t rc = kOkXsRC;
|
|
@@ -1558,11 +1646,13 @@ typedef struct
|
1558
|
1646
|
float rval; //
|
1559
|
1647
|
unsigned midi; //
|
1560
|
1648
|
|
1561
|
|
- cmXsNote_t* note; // The cmXsNode_t* associated with this cmXsReorder_t record
|
|
1649
|
+ cmXsNote_t* note; // The cmXsNote_t* associated with this cmXsReorder_t record
|
1562
|
1650
|
|
1563
|
1651
|
unsigned dynIdx; // cmInvalidIdx=ignore otherwise index into _cmXScoreDynMarkArray[]
|
1564
|
|
- unsigned newFlags; // 0=ignore | kSostUp/DnXsFl | kDampUp/DnXsFl | kTieEndXsFl
|
|
1652
|
+ unsigned newFlags; // 0=ignore | kSostUp/DnXsFl | kDampUp/DnXsFl | kTieEndXsFl
|
1565
|
1653
|
unsigned newTick; // 0=ignore >0 new tick value
|
|
1654
|
+ char graceType; // 0=ignore g=grace note i=anchor note
|
|
1655
|
+ unsigned graceGroupId; // 0=ignore >0=grace note group id
|
1566
|
1656
|
unsigned pitch; // 0=ignore >0 new pitch
|
1567
|
1657
|
} cmXsReorder_t;
|
1568
|
1658
|
|
|
@@ -1678,10 +1768,8 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t*
|
1678
|
1768
|
|
1679
|
1769
|
// set the 'note' field on each cmXsReorder_t record
|
1680
|
1770
|
for(i=0; i<rN; ++i)
|
1681
|
|
- {
|
1682
|
1771
|
if((rV[i].note = _cmXsReorderFindNote(p,measNumb,rV+i,i)) == NULL )
|
1683
|
1772
|
return kSyntaxErrorXsRC;
|
1684
|
|
- }
|
1685
|
1773
|
|
1686
|
1774
|
cmXsMeas_t* mp = rV[0].note->meas;
|
1687
|
1775
|
cmXsNote_t* n0p = NULL;
|
|
@@ -1719,6 +1807,25 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t*
|
1719
|
1807
|
// if a new note value was specified
|
1720
|
1808
|
if( rV[i].pitch != 0 )
|
1721
|
1809
|
rV[i].note->pitch = rV[i].pitch;
|
|
1810
|
+
|
|
1811
|
+ switch(rV[i].graceType)
|
|
1812
|
+ {
|
|
1813
|
+ case 'b':
|
|
1814
|
+ rV[i].note->flags = cmSetFlag(rV[i].note->flags,kBegGraceXsFl);
|
|
1815
|
+ break;
|
|
1816
|
+
|
|
1817
|
+ case 'g':
|
|
1818
|
+ break;
|
|
1819
|
+
|
|
1820
|
+ case 'a':
|
|
1821
|
+ case 's':
|
|
1822
|
+ case 'f':
|
|
1823
|
+ rV[i].note->flags = cmSetFlag(rV[i].note->flags,kEndGraceXsFl);
|
|
1824
|
+ break;
|
|
1825
|
+
|
|
1826
|
+ }
|
|
1827
|
+
|
|
1828
|
+ rV[i].note->graceGroupId = rV[i].graceGroupId;
|
1722
|
1829
|
|
1723
|
1830
|
n0p = rV[i].note;
|
1724
|
1831
|
n0p->slink = NULL;
|
|
@@ -1897,6 +2004,36 @@ cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned l
|
1897
|
2004
|
return rc;
|
1898
|
2005
|
}
|
1899
|
2006
|
|
|
2007
|
+cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, char* graceTypeRef )
|
|
2008
|
+{
|
|
2009
|
+ cmXsRC_t rc = kOkXsRC;
|
|
2010
|
+ const cmChar_t* s;
|
|
2011
|
+
|
|
2012
|
+ *graceTypeRef = 0;
|
|
2013
|
+
|
|
2014
|
+ if((s = strchr(b,'%')) == NULL )
|
|
2015
|
+ return rc;
|
|
2016
|
+
|
|
2017
|
+ ++s;
|
|
2018
|
+
|
|
2019
|
+ switch(*s)
|
|
2020
|
+ {
|
|
2021
|
+ case 'b':
|
|
2022
|
+ case 'g':
|
|
2023
|
+ case 'a':
|
|
2024
|
+ case 's':
|
|
2025
|
+ case 'f':
|
|
2026
|
+ *graceTypeRef = *s;
|
|
2027
|
+ break;
|
|
2028
|
+
|
|
2029
|
+ default:
|
|
2030
|
+ { assert(0); }
|
|
2031
|
+ }
|
|
2032
|
+
|
|
2033
|
+ return rc;
|
|
2034
|
+
|
|
2035
|
+}
|
|
2036
|
+
|
1900
|
2037
|
cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned line, unsigned* pitchRef )
|
1901
|
2038
|
{
|
1902
|
2039
|
cmXsRC_t rc = kOkXsRC;
|
|
@@ -1937,26 +2074,24 @@ cmXsRC_t _cmXScoreReorderParsePitch(cmXScore_t* p, const cmChar_t* b, unsigned
|
1937
|
2074
|
else
|
1938
|
2075
|
rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"Pitch conversion from '%s' failed on line %i.",buf,line);
|
1939
|
2076
|
|
1940
|
|
- return rc;
|
1941
|
|
-
|
|
2077
|
+ return rc;
|
1942
|
2078
|
}
|
1943
|
2079
|
|
1944
|
|
-
|
1945
|
|
-
|
1946
|
2080
|
cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
|
1947
|
2081
|
{
|
1948
|
2082
|
typedef enum { kFindMeasStId, kFindEventStId, kReadEventStId } stateId_t;
|
1949
|
2083
|
|
1950
|
|
- cmXsRC_t rc = kOkXsRC;
|
1951
|
|
- cmXScore_t* p = _cmXScoreHandleToPtr(h);
|
1952
|
|
- cmFileH_t fH = cmFileNullHandle;
|
1953
|
|
- cmChar_t* b = NULL;
|
1954
|
|
- unsigned bN = 0;
|
1955
|
|
- unsigned ln = 0;
|
1956
|
|
- stateId_t stateId = kFindMeasStId;
|
1957
|
|
- unsigned rN = 1024;
|
1958
|
|
- unsigned ri = 0;
|
1959
|
|
- unsigned measNumb = 0;
|
|
2084
|
+ cmXsRC_t rc = kOkXsRC;
|
|
2085
|
+ cmXScore_t* p = _cmXScoreHandleToPtr(h);
|
|
2086
|
+ cmFileH_t fH = cmFileNullHandle;
|
|
2087
|
+ cmChar_t* b = NULL;
|
|
2088
|
+ unsigned bN = 0;
|
|
2089
|
+ unsigned ln = 0;
|
|
2090
|
+ stateId_t stateId = kFindMeasStId;
|
|
2091
|
+ unsigned rN = 1024;
|
|
2092
|
+ unsigned ri = 0;
|
|
2093
|
+ unsigned measNumb = 0;
|
|
2094
|
+ unsigned graceGroupId = 1;
|
1960
|
2095
|
cmXsReorder_t rV[ rN ];
|
1961
|
2096
|
|
1962
|
2097
|
if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
|
|
@@ -2029,10 +2164,27 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
|
2029
|
2164
|
if((rc = _cmXScoreReorderParseTick(p, b, ln+1, &r.newTick)) != kOkXsRC )
|
2030
|
2165
|
goto errLabel;
|
2031
|
2166
|
|
|
2167
|
+ // parse the %grace note marker
|
|
2168
|
+ if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &r.graceType)) != kOkXsRC )
|
|
2169
|
+ goto errLabel;
|
|
2170
|
+
|
2032
|
2171
|
// parse the $pitch marker
|
2033
|
2172
|
if((rc = _cmXScoreReorderParsePitch(p, b, ln+1, &r.pitch )) != kOkXsRC )
|
2034
|
2173
|
goto errLabel;
|
2035
|
2174
|
|
|
2175
|
+ // process grace notes - these need to be processed separate from
|
|
2176
|
+ // the _cmXScoreReorderMeas() because grace notes may cross measure boundaries.
|
|
2177
|
+ if( r.graceType != 0 )
|
|
2178
|
+ {
|
|
2179
|
+ r.graceGroupId = graceGroupId;
|
|
2180
|
+
|
|
2181
|
+ // if this is an end of a grace note group
|
|
2182
|
+ if( r.graceType != 'g' && r.graceType != 'b' )
|
|
2183
|
+ {
|
|
2184
|
+ graceGroupId += 1;
|
|
2185
|
+ }
|
|
2186
|
+
|
|
2187
|
+ }
|
2036
|
2188
|
|
2037
|
2189
|
// store the record
|
2038
|
2190
|
assert( ri < rN );
|
|
@@ -2075,6 +2227,8 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
|
2075
|
2227
|
|
2076
|
2228
|
// resort to force the links to be correct
|
2077
|
2229
|
_cmXScoreSort(p);
|
|
2230
|
+
|
|
2231
|
+ _cmXScoreProcessGraceNotes( p );
|
2078
|
2232
|
|
2079
|
2233
|
errLabel:
|
2080
|
2234
|
cmFileClose(&fH);
|
|
@@ -2608,7 +2762,105 @@ void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
|
2608
|
2762
|
}
|
2609
|
2763
|
}
|
2610
|
2764
|
|
2611
|
|
-typedef struct cmXsMidiEvt_str
|
|
2765
|
+cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
|
|
2766
|
+{
|
|
2767
|
+ cmXsRC_t rc = kOkXsRC;
|
|
2768
|
+ cmXScore_t* p = _cmXScoreHandleToPtr(h);
|
|
2769
|
+
|
|
2770
|
+ if( p->partL==NULL || p->partL->measL == NULL )
|
|
2771
|
+ return rc;
|
|
2772
|
+
|
|
2773
|
+ cmMidiFileH_t mfH = cmMidiFileNullHandle;
|
|
2774
|
+ unsigned trkN = 2;
|
|
2775
|
+ unsigned ticksPerQN = p->partL->measL->divisions;
|
|
2776
|
+ const cmChar_t* outFn = cmFsMakeFn(dir,fn,"mid",NULL);
|
|
2777
|
+
|
|
2778
|
+ if( cmMidiFileCreate( ctx, &mfH, trkN, ticksPerQN ) != kOkMfRC )
|
|
2779
|
+ return cmErrMsg(&p->err,kMidiFailXsRC,"Unable to create the MIDI file object.");
|
|
2780
|
+
|
|
2781
|
+ cmXsPart_t* pp = p->partL;
|
|
2782
|
+
|
|
2783
|
+ // for each part
|
|
2784
|
+ for(; pp!=NULL; pp=pp->link)
|
|
2785
|
+ {
|
|
2786
|
+ cmXsMeas_t* mp = pp->measL;
|
|
2787
|
+
|
|
2788
|
+ // for each measure
|
|
2789
|
+ for(; mp!=NULL; mp=mp->link)
|
|
2790
|
+ {
|
|
2791
|
+ cmXsNote_t* np = mp->noteL;
|
|
2792
|
+
|
|
2793
|
+ if( mp->divisions != ticksPerQN )
|
|
2794
|
+ cmErrWarnMsg(&p->err,kMidiFailXsRC,"The 'tick per quarter note' (divisions) field in measure %i does not match the value in the first measure (%i).",mp->divisions,ticksPerQN);
|
|
2795
|
+
|
|
2796
|
+ // for each note in this measure
|
|
2797
|
+ for(; np!=NULL; np=np->slink)
|
|
2798
|
+ {
|
|
2799
|
+ switch( np->flags & (kOnsetXsFl|kMetronomeXsFl|kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) )
|
|
2800
|
+ {
|
|
2801
|
+ case kOnsetXsFl:
|
|
2802
|
+ if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC
|
|
2803
|
+ ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC )
|
|
2804
|
+ {
|
|
2805
|
+ rc = kMidiFailXsRC;
|
|
2806
|
+ }
|
|
2807
|
+
|
|
2808
|
+ break;
|
|
2809
|
+
|
|
2810
|
+ case kDampDnXsFl:
|
|
2811
|
+ case kDampUpDnXsFl:
|
|
2812
|
+ case kSostDnXsFl:
|
|
2813
|
+ {
|
|
2814
|
+ cmMidiByte_t d0 = cmIsFlag(np->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;
|
|
2815
|
+ if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127 ) != kOkMfRC )
|
|
2816
|
+ ||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0 ) != kOkMfRC ) )
|
|
2817
|
+ {
|
|
2818
|
+ rc = kMidiFailXsRC;
|
|
2819
|
+ }
|
|
2820
|
+ }
|
|
2821
|
+ break;
|
|
2822
|
+
|
|
2823
|
+ case kMetronomeXsFl:
|
|
2824
|
+ if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration ) != kOkMfRC )
|
|
2825
|
+ rc = kMidiFailXsRC;
|
|
2826
|
+ break;
|
|
2827
|
+
|
|
2828
|
+ case 0:
|
|
2829
|
+ break;
|
|
2830
|
+
|
|
2831
|
+ default:
|
|
2832
|
+ { assert(0); }
|
|
2833
|
+ }
|
|
2834
|
+
|
|
2835
|
+ if( rc != kOkXsRC )
|
|
2836
|
+ {
|
|
2837
|
+ rc = cmErrMsg(&p->err,rc,"MIDI message insert failed on '%s'.",cmStringNullGuard(outFn));
|
|
2838
|
+ goto errLabel;
|
|
2839
|
+ }
|
|
2840
|
+ }
|
|
2841
|
+ }
|
|
2842
|
+ }
|
|
2843
|
+
|
|
2844
|
+ if( cmMidiFileWrite(mfH,outFn) != kOkMfRC )
|
|
2845
|
+ {
|
|
2846
|
+ rc = cmErrMsg(&p->err,kMidiFailXsRC,"MIDI file write failed on '%s'.",cmStringNullGuard(outFn));
|
|
2847
|
+ goto errLabel;
|
|
2848
|
+ }
|
|
2849
|
+
|
|
2850
|
+ errLabel:
|
|
2851
|
+ cmFsFreeFn(outFn);
|
|
2852
|
+ if( cmMidiFileClose(&mfH) != kOkMfRC )
|
|
2853
|
+ {
|
|
2854
|
+ rc = cmErrMsg(&p->err,kMidiFailXsRC,"Unable to create the MIDI file object.");
|
|
2855
|
+ goto errLabel;
|
|
2856
|
+ }
|
|
2857
|
+
|
|
2858
|
+ return rc;
|
|
2859
|
+}
|
|
2860
|
+
|
|
2861
|
+
|
|
2862
|
+
|
|
2863
|
+typedef struct cmXsSvgEvt_str
|
2612
|
2864
|
{
|
2613
|
2865
|
unsigned flags; // k???XsFl
|
2614
|
2866
|
unsigned tick; // start tick
|
|
@@ -2616,13 +2868,13 @@ typedef struct cmXsMidiEvt_str
|
2616
|
2868
|
unsigned voice; // score voice number
|
2617
|
2869
|
unsigned d0; // MIDI d0 (barNumb)
|
2618
|
2870
|
unsigned d1; // MIDI d1
|
2619
|
|
- struct cmXsMidiEvt_str* link;
|
2620
|
|
-} cmXsMidiEvt_t;
|
|
2871
|
+ struct cmXsSvgEvt_str* link;
|
|
2872
|
+} cmXsSvgEvt_t;
|
2621
|
2873
|
|
2622
|
2874
|
typedef struct cmXsMidiFile_str
|
2623
|
2875
|
{
|
2624
|
|
- cmXsMidiEvt_t* elist;
|
2625
|
|
- cmXsMidiEvt_t* eol;
|
|
2876
|
+ cmXsSvgEvt_t* elist;
|
|
2877
|
+ cmXsSvgEvt_t* eol;
|
2626
|
2878
|
|
2627
|
2879
|
unsigned pitch_min;
|
2628
|
2880
|
unsigned pitch_max;
|
|
@@ -2633,7 +2885,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
|
2633
|
2885
|
{
|
2634
|
2886
|
cmXsRC_t rc = kOkXsRC;
|
2635
|
2887
|
cmSvgH_t svgH = cmSvgNullHandle;
|
2636
|
|
- cmXsMidiEvt_t* e = mf->elist;
|
|
2888
|
+ cmXsSvgEvt_t* e = mf->elist;
|
2637
|
2889
|
unsigned noteHeight = 10;
|
2638
|
2890
|
const cmChar_t* svgFn = cmFsMakeFn(dir,fn,"html",NULL);
|
2639
|
2891
|
const cmChar_t* cssFn = cmFsMakeFn(NULL,fn,"css",NULL);
|
|
@@ -2712,105 +2964,10 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
|
2712
|
2964
|
return rc;
|
2713
|
2965
|
}
|
2714
|
2966
|
|
2715
|
|
-cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, const cmChar_t* dir, const cmChar_t* fn )
|
2716
|
|
-{
|
2717
|
|
- cmXsRC_t rc = kOkXsRC;
|
2718
|
|
-
|
2719
|
|
- if( p->partL==NULL || p->partL->measL == NULL )
|
2720
|
|
- return rc;
|
2721
|
|
-
|
2722
|
|
- cmMidiFileH_t mfH = cmMidiFileNullHandle;
|
2723
|
|
- unsigned trkN = 2;
|
2724
|
|
- unsigned ticksPerQN = p->partL->measL->divisions;
|
2725
|
|
- const cmChar_t* outFn = cmFsMakeFn(dir,fn,"mid",NULL);
|
2726
|
2967
|
|
2727
|
|
- if( cmMidiFileCreate( ctx, &mfH, trkN, ticksPerQN ) != kOkMfRC )
|
2728
|
|
- return cmErrMsg(&p->err,kMidiFailXsRC,"Unable to create the MIDI file object.");
|
2729
|
|
-
|
2730
|
|
- cmXsPart_t* pp = p->partL;
|
2731
|
|
-
|
2732
|
|
- // for each part
|
2733
|
|
- for(; pp!=NULL; pp=pp->link)
|
2734
|
|
- {
|
2735
|
|
- cmXsMeas_t* mp = pp->measL;
|
2736
|
|
-
|
2737
|
|
- // for each measure
|
2738
|
|
- for(; mp!=NULL; mp=mp->link)
|
2739
|
|
- {
|
2740
|
|
- cmXsNote_t* np = mp->noteL;
|
2741
|
|
-
|
2742
|
|
- if( mp->divisions != ticksPerQN )
|
2743
|
|
- cmErrWarnMsg(&p->err,kMidiFailXsRC,"The 'tick per quarter note' (divisions) field in measure %i does not match the value in the first measure (%i).",mp->divisions,ticksPerQN);
|
2744
|
|
-
|
2745
|
|
- // for each note in this measure
|
2746
|
|
- for(; np!=NULL; np=np->slink)
|
2747
|
|
- {
|
2748
|
|
- switch( np->flags & (kOnsetXsFl|kMetronomeXsFl|kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) )
|
2749
|
|
- {
|
2750
|
|
- case kOnsetXsFl:
|
2751
|
|
- if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC
|
2752
|
|
- ||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC )
|
2753
|
|
- {
|
2754
|
|
- rc = kMidiFailXsRC;
|
2755
|
|
- }
|
2756
|
|
-
|
2757
|
|
- break;
|
2758
|
|
-
|
2759
|
|
- case kDampDnXsFl:
|
2760
|
|
- case kDampUpDnXsFl:
|
2761
|
|
- case kSostDnXsFl:
|
2762
|
|
- {
|
2763
|
|
- cmMidiByte_t d0 = cmIsFlag(np->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;
|
2764
|
|
- if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127 ) != kOkMfRC )
|
2765
|
|
- ||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0 ) != kOkMfRC ) )
|
2766
|
|
- {
|
2767
|
|
- rc = kMidiFailXsRC;
|
2768
|
|
- }
|
2769
|
|
- }
|
2770
|
|
- break;
|
2771
|
|
-
|
2772
|
|
- case kMetronomeXsFl:
|
2773
|
|
- if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration ) != kOkMfRC )
|
2774
|
|
- rc = kMidiFailXsRC;
|
2775
|
|
- break;
|
2776
|
|
-
|
2777
|
|
- case 0:
|
2778
|
|
- break;
|
2779
|
|
-
|
2780
|
|
- default:
|
2781
|
|
- { assert(0); }
|
2782
|
|
- }
|
2783
|
|
-
|
2784
|
|
- if( rc != kOkXsRC )
|
2785
|
|
- {
|
2786
|
|
- rc = cmErrMsg(&p->err,rc,"MIDI message insert failed on '%s'.",cmStringNullGuard(outFn));
|
2787
|
|
- goto errLabel;
|
2788
|
|
- }
|
2789
|
|
- }
|
2790
|
|
- }
|
2791
|
|
- }
|
2792
|
|
-
|
2793
|
|
- if( cmMidiFileWrite(mfH,outFn) != kOkMfRC )
|
2794
|
|
- {
|
2795
|
|
- rc = cmErrMsg(&p->err,kMidiFailXsRC,"MIDI file write failed on '%s'.",cmStringNullGuard(outFn));
|
2796
|
|
- goto errLabel;
|
2797
|
|
- }
|
2798
|
|
-
|
2799
|
|
- errLabel:
|
2800
|
|
- cmFsFreeFn(outFn);
|
2801
|
|
- if( cmMidiFileClose(&mfH) != kOkMfRC )
|
2802
|
|
- {
|
2803
|
|
- rc = cmErrMsg(&p->err,kMidiFailXsRC,"Unable to create the MIDI file object.");
|
2804
|
|
- goto errLabel;
|
2805
|
|
- }
|
2806
|
|
-
|
2807
|
|
- return rc;
|
2808
|
|
-}
|
2809
|
|
-
|
2810
|
|
-
|
2811
|
|
-void _cmXsPushMidiEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 )
|
|
2968
|
+void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 )
|
2812
|
2969
|
{
|
2813
|
|
- cmXsMidiEvt_t* e = cmLhAllocZ(p->lhH,cmXsMidiEvt_t,1);
|
|
2970
|
+ cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1);
|
2814
|
2971
|
e->flags = flags;
|
2815
|
2972
|
e->tick = tick;
|
2816
|
2973
|
e->durTicks = durTick;
|
|
@@ -2832,17 +2989,14 @@ void _cmXsPushMidiEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsi
|
2832
|
2989
|
mf->eol = e;
|
2833
|
2990
|
}
|
2834
|
2991
|
|
2835
|
|
-cmXsRC_t _cmXScoreGenMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
|
|
2992
|
+cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
|
2836
|
2993
|
{
|
2837
|
2994
|
cmXScore_t* p = _cmXScoreHandleToPtr(h);
|
2838
|
2995
|
cmXsPart_t* pp = p->partL;
|
2839
|
|
-
|
|
2996
|
+
|
2840
|
2997
|
cmXsMidiFile_t mf;
|
2841
|
2998
|
memset(&mf,0,sizeof(mf));
|
2842
|
2999
|
|
2843
|
|
- // assign durations to pedal down events
|
2844
|
|
- _cmXScoreProcessPedals(p);
|
2845
|
|
-
|
2846
|
3000
|
for(; pp!=NULL; pp=pp->link)
|
2847
|
3001
|
{
|
2848
|
3002
|
const cmXsMeas_t* meas = pp->measL;
|
|
@@ -2856,7 +3010,7 @@ cmXsRC_t _cmXScoreGenMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const c
|
2856
|
3010
|
if( cmIsFlag(note->flags,kMetronomeXsFl) )
|
2857
|
3011
|
{
|
2858
|
3012
|
// set BPM as d0
|
2859
|
|
- _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0);
|
|
3013
|
+ _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0);
|
2860
|
3014
|
continue;
|
2861
|
3015
|
|
2862
|
3016
|
}
|
|
@@ -2872,14 +3026,14 @@ cmXsRC_t _cmXScoreGenMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const c
|
2872
|
3026
|
for(; tn!=NULL; tn=tn->tied)
|
2873
|
3027
|
durTick += tn->duration;
|
2874
|
3028
|
}
|
2875
|
|
- _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel);
|
|
3029
|
+ _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel);
|
2876
|
3030
|
continue;
|
2877
|
3031
|
}
|
2878
|
3032
|
|
2879
|
3033
|
// if this is a bar event
|
2880
|
3034
|
if( cmIsFlag(note->flags,kBarXsFl) )
|
2881
|
3035
|
{
|
2882
|
|
- _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0);
|
|
3036
|
+ _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0);
|
2883
|
3037
|
continue;
|
2884
|
3038
|
}
|
2885
|
3039
|
|
|
@@ -2887,7 +3041,7 @@ cmXsRC_t _cmXScoreGenMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const c
|
2887
|
3041
|
if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) )
|
2888
|
3042
|
{
|
2889
|
3043
|
unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;
|
2890
|
|
- _cmXsPushMidiEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127);
|
|
3044
|
+ _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127);
|
2891
|
3045
|
continue;
|
2892
|
3046
|
}
|
2893
|
3047
|
|
|
@@ -2895,7 +3049,6 @@ cmXsRC_t _cmXScoreGenMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const c
|
2895
|
3049
|
}
|
2896
|
3050
|
}
|
2897
|
3051
|
|
2898
|
|
- _cmXsWriteMidiFile( ctx, p, &mf, dir, fn );
|
2899
|
3052
|
|
2900
|
3053
|
return _cmXsWriteMidiSvg( ctx, p, &mf, dir, fn );
|
2901
|
3054
|
|
|
@@ -2916,6 +3069,9 @@ cmXsRC_t cmXScoreTest(
|
2916
|
3069
|
|
2917
|
3070
|
if( reorderFn != NULL )
|
2918
|
3071
|
cmXScoreReorder(h,reorderFn);
|
|
3072
|
+
|
|
3073
|
+ // assign durations to pedal down events
|
|
3074
|
+ _cmXScoreProcessPedals(_cmXScoreHandleToPtr(h));
|
2919
|
3075
|
|
2920
|
3076
|
if( csvOutFn != NULL )
|
2921
|
3077
|
{
|
|
@@ -2943,7 +3099,9 @@ cmXsRC_t cmXScoreTest(
|
2943
|
3099
|
{
|
2944
|
3100
|
cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
|
2945
|
3101
|
|
2946
|
|
- _cmXScoreGenMidi( ctx, h, pp->dirStr, pp->fnStr );
|
|
3102
|
+ _cmXScoreGenSvg( ctx, h, pp->dirStr, pp->fnStr );
|
|
3103
|
+
|
|
3104
|
+ _cmXsWriteMidiFile(ctx, h, pp->dirStr, pp->fnStr );
|
2947
|
3105
|
|
2948
|
3106
|
cmFsFreePathParts(pp);
|
2949
|
3107
|
|