cmXScore.h/c : Added 'tied_dur' field to cmXsNote_t for use in determining the length of a MIDI note events.

This commit is contained in:
kevin 2016-09-07 17:36:15 -04:00
parent 3ef0c6fd3a
commit 84c5097a8d
2 changed files with 59 additions and 38 deletions

View File

@ -84,11 +84,13 @@ typedef struct cmXsNote_str
unsigned staff; // 1=treble 2=bass unsigned staff; // 1=treble 2=bass
unsigned tick; // unsigned tick; //
unsigned duration; // duration in ticks unsigned duration; // duration in ticks
unsigned tied_dur; // duration in ticks (including all tied notes)
double secs; // absolute time in seconds double secs; // absolute time in seconds
double dsecs; // delta time in seconds since previous event double dsecs; // delta time in seconds since previous event
unsigned locIdx; // location index (chords share the same location index) unsigned locIdx; // location index (chords share the same location index)
double rvalue; // 1/rvalue = rythmic value (1/0.5 double whole 1/1 whole 1/2 half 1/4=quarter note, 1/8=eighth note, ...) double rvalue; // 1/rvalue = rythmic value (1/0.5 double whole 1/1 whole 1/2 half 1/4=quarter note, 1/8=eighth note, ...)
const cmChar_t* tvalue; // text value const cmChar_t* tvalue; // text value
unsigned mf_uid; // MIDI file uid assigned to this event
unsigned evenGroupId; // eveness group id unsigned evenGroupId; // eveness group id
unsigned dynGroupId; // dynamics group id unsigned dynGroupId; // dynamics group id
@ -241,6 +243,7 @@ cmXsRC_t _cmXScorePushNote( cmXScore_t* p, cmXsMeas_t* meas, unsigned voiceId, c
note->voice = v; note->voice = v;
note->uid = p->nextUid++; note->uid = p->nextUid++;
note->tied_dur = note->duration;
return kOkXsRC; return kOkXsRC;
} }
@ -605,6 +608,7 @@ cmXsRC_t _cmXScorePushNonNote( cmXScore_t* p, cmXsMeas_t* meas, const cmXmlNode_
note->rvalue = rvalue; note->rvalue = rvalue;
note->tvalue = tvalue; note->tvalue = tvalue;
note->duration = duration; note->duration = duration;
note->tied_dur = duration;
note->meas = meas; note->meas = meas;
note->xmlNode = noteXmlNode; note->xmlNode = noteXmlNode;
@ -1065,6 +1069,7 @@ void _cmXScoreSpreadGraceNotes( cmXScore_t* p )
bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl ) bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, bool rptFl )
{ {
cmXsNote_t* nbp = n0p;
cmXsNote_t* nnp = n0p->slink; // begin w/ note following np cmXsNote_t* nnp = n0p->slink; // begin w/ note following np
unsigned measNumb = mp->number; unsigned measNumb = mp->number;
cmChar_t acc = n0p->alter==-1?'b' : (n0p->alter==1?'#':' '); cmChar_t acc = n0p->alter==-1?'b' : (n0p->alter==1?'#':' ');
@ -1096,6 +1101,8 @@ bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo
nnp->flags |= kTieProcXsFl; nnp->flags |= kTieProcXsFl;
nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl); nnp->flags = cmClrFlag(nnp->flags,kOnsetXsFl);
n0p->tied = nnp; n0p->tied = nnp;
nbp->tied_dur += nnp->duration;
nnp->tied_dur = 0;
if( rptFl ) if( rptFl )
printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0)); printf("---> %i %i %s ",nnp->meas->number,nnp->tick,cmMidiToSciPitch(nnp->pitch,NULL,0));
@ -1118,8 +1125,8 @@ bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo
void _cmXScoreResolveTiesAndLoc( cmXScore_t* p ) void _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
{ {
unsigned n = 0; unsigned n = 0; // count of notes which begin a tie
unsigned m = 0; unsigned m = 0; // count of tied notes that are correctly terminated.
bool rptFl = false; bool rptFl = false;
cmXsPart_t* pp = p->partL; cmXsPart_t* pp = p->partL;
@ -1227,7 +1234,7 @@ cmXsNote_t* _cmXScoreFindOverlappingNote( cmXScore_t* p, const cmXsNote_t* knp
&& cmIsFlag(np->flags,kOnsetXsFl) && cmIsFlag(np->flags,kOnsetXsFl)
&& knp->pitch == np->pitch && knp->pitch == np->pitch
&& knp->tick >= np->tick && knp->tick >= np->tick
&& knp->tick < (np->tick + np->duration) ) && knp->tick < (np->tick + np->tied_dur) )
{ {
return np; return np;
} }
@ -1256,7 +1263,7 @@ void _cmXScoreProcessOverlappingNotes( cmXScore_t* p )
if( cmIsFlag(np->flags,kOnsetXsFl) && (fnp = _cmXScoreFindOverlappingNote(p,np)) != NULL) if( cmIsFlag(np->flags,kOnsetXsFl) && (fnp = _cmXScoreFindOverlappingNote(p,np)) != NULL)
{ {
// is np entirely contained inside fnp // is np entirely contained inside fnp
bool embeddedFl = fnp->tick + fnp->duration > np->tick + np->duration; bool embeddedFl = fnp->tick + fnp->tied_dur > np->tick + np->tied_dur;
//printf("bar=%3i %4s voice:%2i %2i : %7i %7i : %7i %7i : %7i : %c \n",np->meas->number,cmMidiToSciPitch(np->pitch,NULL,0),np->voice->id,fnp->voice->id,fnp->tick,fnp->tick+fnp->duration,np->tick,np->tick+np->duration, (fnp->tick+fnp->duration) - np->tick, embeddedFl ? 'E' : 'O'); //printf("bar=%3i %4s voice:%2i %2i : %7i %7i : %7i %7i : %7i : %c \n",np->meas->number,cmMidiToSciPitch(np->pitch,NULL,0),np->voice->id,fnp->voice->id,fnp->tick,fnp->tick+fnp->duration,np->tick,np->tick+np->duration, (fnp->tick+fnp->duration) - np->tick, embeddedFl ? 'E' : 'O');
@ -1270,14 +1277,14 @@ void _cmXScoreProcessOverlappingNotes( cmXScore_t* p )
} }
else else
{ {
int d = (fnp->tick+fnp->duration) - np->tick; int d = (fnp->tick+fnp->tied_dur) - np->tick;
// shorten the first note // shorten the first note
if( d > 0 && d < fnp->duration ) if( d > 0 && d < fnp->tied_dur )
fnp->duration -= d; fnp->tied_dur -= d;
// move the second note just past it // move the second note just past it
np->tick = fnp->tick + fnp->duration + 1; np->tick = fnp->tick + fnp->tied_dur + 1;
} }
} }
} }
@ -1404,7 +1411,7 @@ cmXsRC_t _cmXScoreWriteScorePlotFile( cmXScore_t* p, const cmChar_t* fn )
{ {
if( cmIsFlag(np->flags,kMetronomeXsFl) ) if( cmIsFlag(np->flags,kMetronomeXsFl) )
{ {
double bps = np->duration / 60.0; double bps = np->tied_dur / 60.0;
// t b t // t b t
// - = - - // - = - -
@ -1418,7 +1425,7 @@ cmXsRC_t _cmXScoreWriteScorePlotFile( cmXScore_t* p, const cmChar_t* fn )
{ {
onset_secs += (np->tick - tick0) / ticks_per_sec; onset_secs += (np->tick - tick0) / ticks_per_sec;
tick0 = np->tick; tick0 = np->tick;
cmFilePrintf(fH,"n %f %f %i %s %s\n",onset_secs,np->duration/ticks_per_sec,np->uid,cmMidiToSciPitch(np->pitch,NULL,0),cmIsFlag(np->flags,kGraceXsFl)?"G":"N"); cmFilePrintf(fH,"n %f %f %i %s %s\n",onset_secs,np->tied_dur/ticks_per_sec,np->uid,cmMidiToSciPitch(np->pitch,NULL,0),cmIsFlag(np->flags,kGraceXsFl)?"G":"N");
} }
} }
} }
@ -1700,7 +1707,7 @@ cmXsRC_t _cmXScoreProcessGraceNotes( cmXScore_t* p )
// 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 * graceDurSec); np->duration = np->tied_dur = floor(ticksPerSec * graceDurSec);
gN += 1; gN += 1;
} }
@ -2938,7 +2945,7 @@ void _cmXScoreReportNote( cmRpt_t* rpt, const cmXsNote_t* note,unsigned index )
note->voice->id, note->voice->id,
note->locIdx, note->locIdx,
note->tick, note->tick,
note->duration, note->tied_dur,
note->rvalue, note->rvalue,
N,B,R,G,D,C,e,d,t,P,s,S,H,T0,T1,O); N,B,R,G,D,C,e,d,t,P,s,S,H,T0,T1,O);
@ -3072,8 +3079,11 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const
{ {
case kOnsetXsFl: case kOnsetXsFl:
{ {
if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel ) != kOkMfRC if( np->tied_dur <= 0 )
||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kNoteOffMdId, np->pitch, 0 ) != kOkMfRC ) cmErrWarnMsg(&p->err,kOkXsRC,"A zero length note was encountered bar:%i tick:%i %s",np->meas->number,np->tick,cmMidiToSciPitch(np->pitch,NULL,0));
if( cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kNoteOnMdId, np->pitch, np->vel, &np->mf_uid ) != kOkMfRC
||cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->tied_dur, kNoteOffMdId, np->pitch, 0, &np->mf_uid ) != kOkMfRC )
{ {
rc = kMidiFailXsRC; rc = kMidiFailXsRC;
} }
@ -3084,9 +3094,12 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const
case kDampUpDnXsFl: case kDampUpDnXsFl:
case kSostDnXsFl: case kSostDnXsFl:
{ {
if( np->duration <= 0 )
cmErrWarnMsg(&p->err,kOkXsRC,"A zero length pedal event was encountered bar:%i tick:%i",np->meas->number,np->tick);
cmMidiByte_t d0 = cmIsFlag(np->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId; cmMidiByte_t d0 = cmIsFlag(np->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;
if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127 ) != kOkMfRC ) if( (cmMidiFileInsertTrackChMsg(mfH, 1, np->tick, kCtlMdId, d0, 127, &np->mf_uid ) != kOkMfRC )
||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0 ) != kOkMfRC ) ) ||(cmMidiFileInsertTrackChMsg(mfH, 1, np->tick + np->duration, kCtlMdId, d0, 0, &np->mf_uid ) != kOkMfRC ) )
{ {
rc = kMidiFailXsRC; rc = kMidiFailXsRC;
} }
@ -3094,7 +3107,7 @@ cmXsRC_t _cmXsWriteMidiFile( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const
break; break;
case kMetronomeXsFl: case kMetronomeXsFl:
if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration ) != kOkMfRC ) if( cmMidFileInsertTrackTempoMsg(mfH, 0, np->tick, np->duration, &np->mf_uid ) != kOkMfRC )
rc = kMidiFailXsRC; rc = kMidiFailXsRC;
break; break;
@ -3141,6 +3154,7 @@ typedef struct cmXsSvgEvt_str
unsigned voice; // score voice number unsigned voice; // score voice number
unsigned d0; // MIDI d0 (barNumb) unsigned d0; // MIDI d0 (barNumb)
unsigned d1; // MIDI d1 unsigned d1; // MIDI d1
unsigned mf_uid;
struct cmXsSvgEvt_str* link; struct cmXsSvgEvt_str* link;
} cmXsSvgEvt_t; } cmXsSvgEvt_t;
@ -3162,7 +3176,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
unsigned noteHeight = 10; unsigned noteHeight = 10;
cmChar_t* fn0 = cmMemAllocStr( fn ); cmChar_t* fn0 = cmMemAllocStr( fn );
const cmChar_t* svgFn = cmFsMakeFn(dir,fn0 = cmTextAppendSS(fn0,"_midi_svg"),"html",NULL); const cmChar_t* svgFn = cmFsMakeFn(dir,fn0 = cmTextAppendSS(fn0,"_midi_svg"),"html",NULL);
const cmChar_t* cssFn = cmFsMakeFn(NULL,fn,"css",NULL); const cmChar_t* cssFn = cmFsMakeFn(NULL,"score_midi_svg","css",NULL);
cmChar_t* t0 = NULL; // temporary dynamic string cmChar_t* t0 = NULL; // temporary dynamic string
cmMemFree(fn0); cmMemFree(fn0);
@ -3193,9 +3207,13 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
if( cmSvgWriterRect(svgH, e->tick, e->d0 * noteHeight, e->durTicks, noteHeight-1, t0 ) != kOkSvgRC ) if( cmSvgWriterRect(svgH, e->tick, e->d0 * noteHeight, e->durTicks, noteHeight-1, t0 ) != kOkSvgRC )
rc = kSvgFailXsRC; rc = kSvgFailXsRC;
else else
if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, cmMidiToSciPitch( e->d0, NULL, 0), "pitch") != kOkSvgRC ) {
t0 = cmTsPrintfP(t0,"%s %i",cmMidiToSciPitch( e->d0, NULL, 0),e->mf_uid);
if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, t0, "pitch") != kOkSvgRC )
rc = kSvgFailXsRC; rc = kSvgFailXsRC;
} }
}
break; break;
// if this is a bar // if this is a bar
@ -3241,7 +3259,7 @@ cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, con
} }
void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 ) void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1, unsigned mf_uid )
{ {
cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1); cmXsSvgEvt_t* e = cmLhAllocZ(p->lhH,cmXsSvgEvt_t,1);
e->flags = flags; e->flags = flags;
@ -3250,6 +3268,8 @@ void _cmXsPushSvgEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsig
e->voice = voice; e->voice = voice;
e->d0 = d0; // note=pitch bar=number pedal=ctl# metronome=BPM e->d0 = d0; // note=pitch bar=number pedal=ctl# metronome=BPM
e->d1 = d1; e->d1 = d1;
e->mf_uid = mf_uid;
if( mf->eol != NULL ) if( mf->eol != NULL )
mf->eol->link = e; mf->eol->link = e;
else else
@ -3286,7 +3306,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm
if( cmIsFlag(note->flags,kMetronomeXsFl) ) if( cmIsFlag(note->flags,kMetronomeXsFl) )
{ {
// set BPM as d0 // set BPM as d0
_cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0); _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->duration,0,note->mf_uid);
continue; continue;
} }
@ -3295,21 +3315,21 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm
if( cmIsFlag(note->flags,kOnsetXsFl) ) if( cmIsFlag(note->flags,kOnsetXsFl) )
{ {
unsigned d0 = cmSciPitchToMidiPitch( note->step, note->alter, note->octave ); unsigned d0 = cmSciPitchToMidiPitch( note->step, note->alter, note->octave );
unsigned durTick = note->duration; unsigned durTick = note->tied_dur;
if( note->tied != NULL ) if( note->tied != NULL )
{ {
cmXsNote_t* tn = note->tied; cmXsNote_t* tn = note->tied;
for(; tn!=NULL; tn=tn->tied) for(; tn!=NULL; tn=tn->tied)
durTick += tn->duration; durTick += tn->tied_dur;
} }
_cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel); _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel,note->mf_uid);
continue; continue;
} }
// if this is a bar event // if this is a bar event
if( cmIsFlag(note->flags,kBarXsFl) ) if( cmIsFlag(note->flags,kBarXsFl) )
{ {
_cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0); _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0, note->mf_uid);
continue; continue;
} }
@ -3317,7 +3337,7 @@ cmXsRC_t _cmXScoreGenSvg( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cm
if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) ) if( cmIsFlag(note->flags,kDampDnXsFl|kDampUpDnXsFl|kSostDnXsFl) )
{ {
unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId; unsigned d0 = cmIsFlag(note->flags,kSostDnXsFl) ? kSostenutoCtlMdId : kSustainCtlMdId;
_cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127); _cmXsPushSvgEvent(p,&mf,note->flags,note->tick,note->duration,0,d0,127,note->mf_uid);
continue; continue;
} }
@ -3353,7 +3373,7 @@ cmXsRC_t cmXScoreTest(
// assign durations to pedal down events // assign durations to pedal down events
_cmXScoreProcessPedals(_cmXScoreHandleToPtr(h)); _cmXScoreProcessPedals(_cmXScoreHandleToPtr(h));
// remove some notes which share a pitch which are overlapped or embedded // remove some notes which share a pitch and are overlapped or embedded within another note.
_cmXScoreProcessOverlappingNotes(_cmXScoreHandleToPtr(h)); _cmXScoreProcessOverlappingNotes(_cmXScoreHandleToPtr(h));
if( csvOutFn != NULL ) if( csvOutFn != NULL )
@ -3382,15 +3402,15 @@ cmXsRC_t cmXScoreTest(
{ {
cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn); cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
_cmXScoreGenSvg( ctx, h, pp->dirStr, pp->fnStr );
_cmXsWriteMidiFile(ctx, h, pp->dirStr, pp->fnStr ); _cmXsWriteMidiFile(ctx, h, pp->dirStr, pp->fnStr );
_cmXScoreGenSvg( ctx, h, pp->dirStr, pp->fnStr );
cmFsFreePathParts(pp); cmFsFreePathParts(pp);
} }
//cmXScoreReport(h,&ctx->rpt,true); cmXScoreReport(h,&ctx->rpt,true);
errLabel: errLabel:
return cmXScoreFinalize(&h); return cmXScoreFinalize(&h);

View File

@ -19,7 +19,8 @@ extern "C" {
kMidiFailXsRC, kMidiFailXsRC,
kFileFailXsRC, kFileFailXsRC,
kSvgFailXsRC, kSvgFailXsRC,
kOverlapWarnXsRC kOverlapWarnXsRC,
kZeroLengthEventXsRC
}; };
typedef cmRC_t cmXsRC_t; typedef cmRC_t cmXsRC_t;