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 enum
{ {
kSectionXsFl = 0x000001, // rvalue holds section number kSectionXsFl = 0x0000001, // rvalue holds section number
kBarXsFl = 0x000002, kBarXsFl = 0x0000002,
kRestXsFl = 0x000004, kRestXsFl = 0x0000004,
kGraceXsFl = 0x000008, kGraceXsFl = 0x0000008,
kDotXsFl = 0x000010, kDotXsFl = 0x0000010,
kChordXsFl = 0x000020, kChordXsFl = 0x0000020,
kDynXsFl = 0x000040, kDynXsFl = 0x0000040,
kEvenXsFl = 0x000080, kEvenXsFl = 0x0000080,
kTempoXsFl = 0x000100, kTempoXsFl = 0x0000100,
kHeelXsFl = 0x000200, kHeelXsFl = 0x0000200,
kTieBegXsFl = 0x000400, kTieBegXsFl = 0x0000400,
kTieEndXsFl = 0x000800, kTieEndXsFl = 0x0000800,
kTieProcXsFl = 0x001000, kTieProcXsFl = 0x0001000,
kDampDnXsFl = 0x002000, kDampDnXsFl = 0x0002000,
kDampUpXsFl = 0x004000, kDampUpXsFl = 0x0004000,
kDampUpDnXsFl = 0x008000, kDampUpDnXsFl = 0x0008000,
kSostDnXsFl = 0x010000, kSostDnXsFl = 0x0010000,
kSostUpXsFl = 0x020000, kSostUpXsFl = 0x0020000,
kMetronomeXsFl = 0x040000, // duration holds BPM kMetronomeXsFl = 0x0040000, // duration holds BPM
kOnsetXsFl = 0x080000, // this is a sounding note kOnsetXsFl = 0x0080000, // this is a sounding note
kBegGroupXsFl = 0x100000, kBegGroupXsFl = 0x0100000,
kEndGroupXsFl = 0x200000, kEndGroupXsFl = 0x0200000,
kBegGraceXsFl = 0x400000, // beg grace note group kBegGraceXsFl = 0x0400000, // beg grace note group
kEndGraceXsFl = 0x800000 // end 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; 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 // 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 // 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) for(; 1; ++graceGroupId)
{ {
cmXsNote_t* gnp = NULL; cmXsNote_t* gn0p = NULL;
cmXsNote_t* gn1p = NULL;
unsigned gN = 0;
cmXsPart_t* pp = p->partL; cmXsPart_t* pp = p->partL;
double ticksPerSec = 0; 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 this note is part of the grace note group we are searching for
if( np->graceGroupId == graceGroupId ) if( np->graceGroupId == graceGroupId )
{ {
// add the note to the grace note list // track the first note in the grace note list
np->grace = gnp; 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 // set each grace note to have 1/20 of a second duration
if( cmIsFlag(np->flags,kGraceXsFl) ) if( cmIsFlag(np->flags,kGraceXsFl) )
np->duration = floor(ticksPerSec / 20.0); np->duration = floor(ticksPerSec / 20.0);
gnp = np; gN += 1;
} }
}
} //
} }
} }
// no records were found for this grace id - we're done // no records were found for this grace id - we're done
if( gnp == NULL ) if( gn0p == NULL )
break; break;
cmXsNote_t* p0 = NULL; // grace note groups must have at least 3 members
cmXsNote_t* p1 = gnp; if( gN < 3 )
for(; p1!=NULL; p1=p1->grace)
{ {
if(1) rc = cmErrMsg(&p->err,kSyntaxErrorXsRC,"The grace not group ending in meas %i has fewer than 3 (%i) members.", gn1p->meas->number, gN );
break;
}
// 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;
}
// 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 )
{ {
const char* type = "g"; mp = mp->link;
if( cmIsFlag(p1->flags,kBegGraceXsFl) ) assert(mp!=NULL);
type = "b"; n1p = mp->noteL;
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);
} }
// TODO: if(1)
// position grace notes here {
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;
}
p0 = p1; 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); }
} }
@ -1648,12 +1776,12 @@ typedef struct
cmXsNote_t* note; // The cmXsNote_t* associated with this cmXsReorder_t record cmXsNote_t* note; // The cmXsNote_t* associated with this cmXsReorder_t record
unsigned dynIdx; // cmInvalidIdx=ignore otherwise index into _cmXScoreDynMarkArray[] unsigned dynIdx; // cmInvalidIdx=ignore otherwise index into _cmXScoreDynMarkArray[]
unsigned newFlags; // 0=ignore | kSostUp/DnXsFl | kDampUp/DnXsFl | kTieEndXsFl unsigned newFlags; // 0=ignore | kSostUp/DnXsFl | kDampUp/DnXsFl | kTieEndXsFl
unsigned newTick; // 0=ignore >0 new tick value 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 graceGroupId; // 0=ignore >0=grace note group id
unsigned pitch; // 0=ignore >0 new pitch unsigned pitch; // 0=ignore >0 new pitch
} cmXsReorder_t; } cmXsReorder_t;
typedef struct _cmXScoreDynMark_str typedef struct _cmXScoreDynMark_str
@ -1808,24 +1936,8 @@ cmXsRC_t _cmXScoreReorderMeas( cmXScore_t* p, unsigned measNumb, cmXsReorder_t*
if( rV[i].pitch != 0 ) if( rV[i].pitch != 0 )
rV[i].note->pitch = rV[i].pitch; rV[i].note->pitch = rV[i].pitch;
switch(rV[i].graceType) rV[i].note->flags |= rV[i].graceFlags;
{ rV[i].note->graceGroupId = rV[i].graceGroupId;
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->graceGroupId = rV[i].graceGroupId;
n0p = rV[i].note; n0p = rV[i].note;
n0p->slink = NULL; n0p->slink = NULL;
@ -2004,30 +2116,38 @@ cmXsRC_t _cmXScoreReorderParseTick(cmXScore_t* p, const cmChar_t* b, unsigned l
return rc; 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; cmXsRC_t rc = kOkXsRC;
const cmChar_t* s; const cmChar_t* s;
*graceTypeRef = 0;
if((s = strchr(b,'%')) == NULL ) if((s = strchr(b,'%')) == NULL )
return rc; return rc;
++s; ++s;
switch(*s) r->graceGroupId = *graceGroupIdRef;
{
case 'b':
case 'g':
case 'a':
case 's':
case 'f':
*graceTypeRef = *s;
break;
default: while(1)
{ assert(0); } {
switch(*s)
{
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; return rc;
@ -2165,7 +2285,7 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
goto errLabel; goto errLabel;
// parse the %grace note marker // 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; goto errLabel;
// parse the $pitch marker // 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 // process grace notes - these need to be processed separate from
// the _cmXScoreReorderMeas() because grace notes may cross measure boundaries. // the _cmXScoreReorderMeas() because grace notes may cross measure boundaries.
/*
if( r.graceType != 0 ) if( r.graceType != 0 )
{ {
r.graceGroupId = graceGroupId; r.graceGroupId = graceGroupId;
@ -2185,6 +2306,7 @@ cmXsRC_t cmXScoreReorder( cmXsH_t h, const cmChar_t* fn )
} }
} }
*/
// store the record // store the record
assert( ri < rN ); 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 // resort to force the links to be correct
_cmXScoreSort(p); _cmXScoreSort(p);
// process the grace notes.
_cmXScoreProcessGraceNotes( p ); _cmXScoreProcessGraceNotes( p );
// inserting grace notes may have left the score unsorted
_cmXScoreSort(p);
errLabel: errLabel:
cmFileClose(&fH); cmFileClose(&fH);
cmMemFree(b); cmMemFree(b);
@ -2688,6 +2814,9 @@ void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index )
if( note->tempoGroupId != 0 ) if( note->tempoGroupId != 0 )
cmRptPrintf(rpt," t=%i",note->tempoGroupId); 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); return cmXScoreFinalize(&h);