cmXScore.c : Completed 'add' and 'subtract' based grace note positioning.
This commit is contained in:
parent
83734d9949
commit
d2a3d0d32e
285
app/cmXScore.c
285
app/cmXScore.c
@ -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;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 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;
|
||||||
@ -1808,23 +1936,7 @@ 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;
|
||||||
{
|
|
||||||
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;
|
rV[i].note->graceGroupId = rV[i].graceGroupId;
|
||||||
|
|
||||||
n0p = rV[i].note;
|
n0p = rV[i].note;
|
||||||
@ -2004,32 +2116,40 @@ 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;
|
||||||
|
|
||||||
|
r->graceGroupId = *graceGroupIdRef;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
switch(*s)
|
switch(*s)
|
||||||
{
|
{
|
||||||
case 'b':
|
case 'b': r->graceFlags |= kBegGraceXsFl; break;
|
||||||
case 'g':
|
case 'a': r->graceFlags |= kAddGraceXsFl | kEndGraceXsFl; break;
|
||||||
case 'a':
|
case 's': r->graceFlags |= kSubGraceXsFl | kEndGraceXsFl; break;
|
||||||
case 's':
|
case 'f': r->graceFlags |= kFirstGraceXsFl | kEndGraceXsFl; break;
|
||||||
case 'f':
|
case 'g': break;
|
||||||
*graceTypeRef = *s;
|
|
||||||
break;
|
case '%':
|
||||||
|
*graceGroupIdRef += 1;
|
||||||
|
++s;
|
||||||
|
continue;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{ assert(0); }
|
{ 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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user