cmXScore.c : Completed 'add' and 'subtract' based grace note positioning.

This commit is contained in:
kevin 2016-08-03 17:03:17 -04:00
parent 83734d9949
commit d2a3d0d32e

View File

@ -37,30 +37,33 @@ cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
enum
{
kSectionXsFl = 0x000001, // rvalue holds section number
kBarXsFl = 0x000002,
kRestXsFl = 0x000004,
kGraceXsFl = 0x000008,
kDotXsFl = 0x000010,
kChordXsFl = 0x000020,
kDynXsFl = 0x000040,
kEvenXsFl = 0x000080,
kTempoXsFl = 0x000100,
kHeelXsFl = 0x000200,
kTieBegXsFl = 0x000400,
kTieEndXsFl = 0x000800,
kTieProcXsFl = 0x001000,
kDampDnXsFl = 0x002000,
kDampUpXsFl = 0x004000,
kDampUpDnXsFl = 0x008000,
kSostDnXsFl = 0x010000,
kSostUpXsFl = 0x020000,
kMetronomeXsFl = 0x040000, // duration holds BPM
kOnsetXsFl = 0x080000, // this is a sounding note
kBegGroupXsFl = 0x100000,
kEndGroupXsFl = 0x200000,
kBegGraceXsFl = 0x400000, // beg grace note group
kEndGraceXsFl = 0x800000 // end grace note group
kSectionXsFl = 0x0000001, // rvalue holds section number
kBarXsFl = 0x0000002,
kRestXsFl = 0x0000004,
kGraceXsFl = 0x0000008,
kDotXsFl = 0x0000010,
kChordXsFl = 0x0000020,
kDynXsFl = 0x0000040,
kEvenXsFl = 0x0000080,
kTempoXsFl = 0x0000100,
kHeelXsFl = 0x0000200,
kTieBegXsFl = 0x0000400,
kTieEndXsFl = 0x0000800,
kTieProcXsFl = 0x0001000,
kDampDnXsFl = 0x0002000,
kDampUpXsFl = 0x0004000,
kDampUpDnXsFl = 0x0008000,
kSostDnXsFl = 0x0010000,
kSostUpXsFl = 0x0020000,
kMetronomeXsFl = 0x0040000, // duration holds BPM
kOnsetXsFl = 0x0080000, // this is a sounding note
kBegGroupXsFl = 0x0100000,
kEndGroupXsFl = 0x0200000,
kBegGraceXsFl = 0x0400000, // beg grace note group
kEndGraceXsFl = 0x0800000, // end grace note group
kAddGraceXsFl = 0x1000000, // end grace note group operator flag - add time
kSubGraceXsFl = 0x2000000, // " " " " " " - subtract time
kFirstGraceXsFl = 0x4000000, // " " " " " " - sync to first note
};
@ -1464,6 +1467,57 @@ cmXsRC_t _cmXScoreProcessPedals( cmXScore_t* p )
return rc;
}
void _cmXScoreInsertTime( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* np, unsigned expand_ticks )
{
for(; mp!=NULL; mp=mp->link)
{
if( np == NULL )
np = mp->noteL;
for(; np!=NULL; np=np->slink)
np->tick += expand_ticks;
}
}
// Insert the grace notes in between the first and last note in the group
// by inserting time between the first and last note.
void _cmXScoreGraceInsertTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN )
{
cmXsNote_t* np = NULL;
unsigned expand_ticks = 0;
unsigned ticks = aV[aN-1]->tick;
unsigned i;
for(i=0; i<aN; ++i)
if( cmIsFlag(aV[i]->flags,kGraceXsFl) && aV[i]->graceGroupId == graceGroupId )
{
aV[i]->tick = ticks;
ticks += aV[i]->duration;
expand_ticks += aV[i]->duration;
np = aV[i];
}
np = np->slink;
if( np != NULL )
_cmXScoreInsertTime(p,np->meas,np,expand_ticks);
}
// Insert the grace notes in between the first and last note in the group
// but do not insert any additional time betwee the first and last note.
// In effect time is removed from the first note and taken by the grace notes.
void _cmXScoreGraceOverlayTime( cmXScore_t* p, unsigned graceGroupId, cmXsNote_t* aV[], unsigned aN )
{
assert(aN >= 3 );
unsigned t = aV[aN-1]->tick;
int i = (int)aN-2;
for(; i>0; --i)
if( cmIsFlag(aV[i]->flags,kGraceXsFl) && aV[i]->graceGroupId == graceGroupId )
{
aV[i]->tick = t - aV[i]->duration;
t = aV[i]->tick;
}
}
// Adjust the locations of grace notes. Note that this must be done
// after reordering so that we can be sure that the order in time of
@ -1476,7 +1530,9 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p )
for(; 1; ++graceGroupId)
{
cmXsNote_t* gnp = NULL;
cmXsNote_t* gn0p = NULL;
cmXsNote_t* gn1p = NULL;
unsigned gN = 0;
cmXsPart_t* pp = p->partL;
double ticksPerSec = 0;
@ -1498,47 +1554,119 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p )
// if this note is part of the grace note group we are searching for
if( np->graceGroupId == graceGroupId )
{
// add the note to the grace note list
np->grace = gnp;
// track the first note in the grace note list
if( gn0p == NULL )
gn0p = np;
// add the note to the end of the grace note list
if( gn1p != NULL )
gn1p->grace = np;
// track the last note in the grace note list
gn1p = np;
// set each grace note to have 1/20 of a second duration
if( cmIsFlag(np->flags,kGraceXsFl) )
np->duration = floor(ticksPerSec / 20.0);
gnp = np;
}
gN += 1;
}
} //
}
}
// no records were found for this grace id - we're done
if( gnp == NULL )
if( gn0p == NULL )
break;
cmXsNote_t* p0 = NULL;
cmXsNote_t* p1 = gnp;
for(; p1!=NULL; p1=p1->grace)
// grace note groups must have at least 3 members
if( gN < 3 )
{
if(1)
{
const char* type = "g";
if( cmIsFlag(p1->flags,kBegGraceXsFl) )
type = "b";
if( cmIsFlag(p1->flags,kEndGraceXsFl) )
type = "i";
bool fl = p0 != NULL && p0->tick < p1->tick;
printf("%3i %s %i %5i %i\n",p1->graceGroupId,type,p1->tick,p1->duration,fl);
rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The grace not group ending in meas %i has fewer than 3 (%i) members.", gn1p->meas->number, gN );
break;
}
// TODO:
// position grace notes here
// gn0p is now set to the first note in th group
// gn1p is now set to the last note in the group
// verify that the first note is marked with kBegGraceXsFl
if( cmIsNotFlag(gn0p->flags,kBegGraceXsFl) )
{
rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The first note in a grace note group in meas %i is not marked with a 'b'.", gn0p->meas->number );
break;
}
p0 = p1;
// verify that the last note is marked with kEndGraceXsFl
if( cmIsNotFlag(gn1p->flags,kEndGraceXsFl) )
{
rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The last note in a grace note group in meas %i is not marked with a valid operator character.", gn1p->meas->number );
break;
}
// count the total number of events between gn0p and gn1p
cmXsNote_t* n0p = NULL;
cmXsNote_t* n1p = gn0p;
cmXsMeas_t* mp = gn0p->meas;
unsigned aN = 0;
for(; n0p != gn1p; n1p=n1p->slink )
{
// if we are crossing a measure boundary
if( n1p == NULL )
{
mp = mp->link;
assert(mp!=NULL);
n1p = mp->noteL;
}
if(1)
{
bool fl = n0p != NULL && n0p->tick < n1p->tick;
unsigned type = n1p->flags & (kBegGraceXsFl|kEndGraceXsFl|kAddGraceXsFl|kSubGraceXsFl|kFirstGraceXsFl);
printf("%3i 0x%08x %i %5i %i\n",n1p->graceGroupId,type,n1p->tick,n1p->duration,fl);
}
++aN;
n0p = n1p;
}
// create a vector of pointers to all events between gn0p and gn1p
cmXsNote_t* aV[ aN ];
unsigned i;
n1p = gn0p;
n0p = NULL;
mp = gn0p->meas;
for(i=0; n0p != gn1p; n1p=n1p->slink )
{
// if we are crossing a measure boundardy
if( n1p == NULL )
{
mp = mp->link;
assert(mp!=NULL);
n1p = mp->noteL;
}
assert(i<aN);
aV[i++] = n1p;
n0p = n1p;
}
switch( gn1p->flags & (kAddGraceXsFl | kSubGraceXsFl | kFirstGraceXsFl) )
{
case kAddGraceXsFl:
_cmXScoreGraceInsertTime(p, graceGroupId, aV, aN );
break;
case kSubGraceXsFl:
_cmXScoreGraceOverlayTime(p, graceGroupId, aV, aN );
break;
case kFirstGraceXsFl:
break;
default:
{ assert(0); }
}
@ -1651,7 +1779,7 @@ typedef struct
unsigned dynIdx; // cmInvalidIdx=ignore otherwise index into _cmXScoreDynMarkArray[]
unsigned newFlags; // 0=ignore | kSostUp/DnXsFl | kDampUp/DnXsFl | kTieEndXsFl
unsigned newTick; // 0=ignore >0 new tick value
char graceType; // 0=ignore g=grace note i=anchor note
unsigned graceFlags; // 0=ignore See kXXXGraceXsFl
unsigned graceGroupId; // 0=ignore >0=grace note group id
unsigned pitch; // 0=ignore >0 new pitch
} cmXsReorder_t;
@ -1808,23 +1936,7 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t*
if( rV[i].pitch != 0 )
rV[i].note->pitch = rV[i].pitch;
switch(rV[i].graceType)
{
case 'b':
rV[i].note->flags = cmSetFlag(rV[i].note->flags,kBegGraceXsFl);
break;
case 'g':
break;
case 'a':
case 's':
case 'f':
rV[i].note->flags = cmSetFlag(rV[i].note->flags,kEndGraceXsFl);
break;
}
rV[i].note->flags |= rV[i].graceFlags;
rV[i].note->graceGroupId = rV[i].graceGroupId;
n0p = rV[i].note;
@ -2004,32 +2116,40 @@ cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned l
return rc;
}
cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, char* graceTypeRef )
cmXsRC_t _cmXScoreReorderParseGrace(cmXScore_t* p, const cmChar_t* b, unsigned line, cmXsReorder_t* r, unsigned* graceGroupIdRef )
{
cmXsRC_t rc = kOkXsRC;
const cmChar_t* s;
*graceTypeRef = 0;
if((s = strchr(b,'%')) == NULL )
return rc;
++s;
r->graceGroupId = *graceGroupIdRef;
while(1)
{
switch(*s)
{
case 'b':
case 'g':
case 'a':
case 's':
case 'f':
*graceTypeRef = *s;
break;
case 'b': r->graceFlags |= kBegGraceXsFl; break;
case 'a': r->graceFlags |= kAddGraceXsFl | kEndGraceXsFl; break;
case 's': r->graceFlags |= kSubGraceXsFl | kEndGraceXsFl; break;
case 'f': r->graceFlags |= kFirstGraceXsFl | kEndGraceXsFl; break;
case 'g': break;
case '%':
*graceGroupIdRef += 1;
++s;
continue;
default:
{ assert(0); }
}
break;
}
return rc;
}
@ -2165,7 +2285,7 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
goto errLabel;
// parse the %grace note marker
if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &r.graceType)) != kOkXsRC )
if((rc = _cmXScoreReorderParseGrace(p, b, ln+1, &r, &graceGroupId)) != kOkXsRC )
goto errLabel;
// parse the $pitch marker
@ -2174,6 +2294,7 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
// process grace notes - these need to be processed separate from
// the _cmXScoreReorderMeas() because grace notes may cross measure boundaries.
/*
if( r.graceType != 0 )
{
r.graceGroupId = graceGroupId;
@ -2185,6 +2306,7 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
}
}
*/
// store the record
assert( ri < rN );
@ -2228,8 +2350,12 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
// resort to force the links to be correct
_cmXScoreSort(p);
// process the grace notes.
_cmXScoreProcessGraceNotes( p );
// inserting grace notes may have left the score unsorted
_cmXScoreSort(p);
errLabel:
cmFileClose(&fH);
cmMemFree(b);
@ -2688,6 +2814,9 @@ void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index )
if( note->tempoGroupId != 0 )
cmRptPrintf(rpt," t=%i",note->tempoGroupId);
if( note->graceGroupId != 0)
cmRptPrintf(rpt," g=%i",note->graceGroupId);
}
@ -3107,7 +3236,7 @@ cmXsRC_t cmXScoreTest(
}
//cmXScoreReport(h,&ctx->rpt,false);
cmXScoreReport(h,&ctx->rpt,true);
return cmXScoreFinalize(&h);