cmXScore.h,c : Began MIDI output file generation coding. Added _cmXsWriteMidiSvg().

This commit is contained in:
kevin 2016-07-14 19:08:13 -04:00
parent a4ed9ba98b
commit 38cdd3012b
2 changed files with 183 additions and 31 deletions

View File

@ -10,6 +10,7 @@
#include "cmLinkedHeap.h" #include "cmLinkedHeap.h"
#include "cmXml.h" #include "cmXml.h"
#include "cmText.h" #include "cmText.h"
#include "cmFileSys.h"
#include "cmXScore.h" #include "cmXScore.h"
#include "cmTime.h" #include "cmTime.h"
#include "cmMidi.h" #include "cmMidi.h"
@ -19,6 +20,7 @@
#include "cmSymTbl.h" #include "cmSymTbl.h"
#include "cmScore.h" #include "cmScore.h"
#include "cmFile.h" #include "cmFile.h"
#include "cmSymTbl.h" #include "cmSymTbl.h"
#include "cmAudioFile.h" #include "cmAudioFile.h"
@ -29,6 +31,8 @@
#include "cmProc2.h" #include "cmProc2.h"
#include "cmProc5.h" #include "cmProc5.h"
#include "cmSvgWriter.h"
cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE; cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE;
enum enum
@ -89,6 +93,8 @@ typedef struct cmXsNote_str
const cmXmlNode_t* xmlNode; // note xml ptr const cmXmlNode_t* xmlNode; // note xml ptr
struct cmXsNote_str* tied; // subsequent note tied to this note
struct cmXsNote_str* mlink; // measure note list struct cmXsNote_str* mlink; // measure note list
struct cmXsNote_str* slink; // time sorted event list struct cmXsNote_str* slink; // time sorted event list
@ -374,11 +380,8 @@ cmXsRC_t _cmXScoreParsePitch( cmXScore_t* p, const cmXmlNode_t* nnp, cmXsNote_t
cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL); cmXmlNodeDouble( nnp,&alter,"pitch","alter",NULL);
cmChar_t buf[3] = { *step, '0', '\0'}; int acc = alter;
unsigned midi = cmSciPitchToMidi(buf); unsigned midi = cmSciPitchToMidiPitch(*step,acc,octave);
midi += (12 * octave);
midi += alter;
np->pitch = midi; np->pitch = midi;
np->step = *step; np->step = *step;
@ -1044,10 +1047,8 @@ void _cmXScoreSpreadGraceNotes( cmXScore_t* p )
tick1 = _cmXsSpreadGraceNotes(a,ai); tick1 = _cmXsSpreadGraceNotes(a,ai);
} }
} }
} }
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* nnp = n0p->slink; // begin w/ note following np cmXsNote_t* nnp = n0p->slink; // begin w/ note following np
@ -1066,8 +1067,8 @@ bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo
mp = mp->link; mp = mp->link;
nnp = mp->noteL; nnp = mp->noteL;
// if a measure was completed and no end note was found ... then the tie is unterminated // if a measure was completed and no end note was found ... then the tie is unterminated
// (a tie must be continued in every measure which it passes through) // (a tie must be continued in every measure which it passes through)
if( mp->number > measNumb + 1 ) if( mp->number > measNumb + 1 )
break; break;
} }
@ -1080,6 +1081,7 @@ 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;
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));
@ -1088,6 +1090,8 @@ bool _cmXScoreFindTiedNote( cmXScore_t* p, cmXsMeas_t* mp, cmXsNote_t* n0p, boo
if( cmIsNotFlag(nnp->flags,kTieBegXsFl) ) if( cmIsNotFlag(nnp->flags,kTieBegXsFl) )
return true; return true;
n0p = nnp;
// record the measure number of the last note with a tie-start // record the measure number of the last note with a tie-start
measNumb = mp->number; measNumb = mp->number;
} }
@ -1134,15 +1138,19 @@ void _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
n += 1; n += 1;
} }
// Validate the tie state of the current note.
if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) ) if( cmIsFlag(np->flags,kTieEndXsFl) && cmIsFlag(np->flags,kOnsetXsFl) )
{ {
cmChar_t acc = np->alter==-1?'b' : (np->alter==1?'#':' '); cmChar_t acc = np->alter==-1?'b' : (np->alter==1?'#':' ');
cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i marked as a tied note but is also marked to sound.",np->step,acc,np->octave,mp->number); cmErrWarnMsg(&p->err,kUnterminatedTieXsRC,"The tied %c%c%i in measure %i marked as a tied note but is also marked to sound.",np->step,acc,np->octave,mp->number);
} }
// set the location //
// Set the score location of notes marked for onset and bar lines.
//
if( cmIsFlag(np->flags,kOnsetXsFl|kBarXsFl) ) if( cmIsFlag(np->flags,kOnsetXsFl|kBarXsFl) )
{ {
// if this note does not share the same location as the previous 'located' note then increment the 'loc' index
if( cmIsFlag(np->flags,kBarXsFl) || (n0!=NULL && n0->tick!=np->tick)) if( cmIsFlag(np->flags,kBarXsFl) || (n0!=NULL && n0->tick!=np->tick))
locIdx += 1; locIdx += 1;
@ -1153,7 +1161,7 @@ void _cmXScoreResolveTiesAndLoc( cmXScore_t* p )
} }
} }
printf("Found:%i Not Found:%i\n",m,n-m); printf("Tied notes found:%i Not found:%i\n",m,n-m);
} }
cmXsRC_t _cmXScoreResolveOctaveShift( cmXScore_t* p ) cmXsRC_t _cmXScoreResolveOctaveShift( cmXScore_t* p )
@ -1362,7 +1370,7 @@ void _cmXScoreFixBarLines( cmXScore_t* p )
} }
} }
cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* midiFn ) cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn )
{ {
cmXsRC_t rc = kOkXsRC; cmXsRC_t rc = kOkXsRC;
@ -2511,13 +2519,125 @@ void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl )
} }
} }
typedef struct cmXsMidiEvt_str
cmXsRC_t cmXScoreWriteMidi( cmXsH_t h, const cmChar_t* fn ) {
unsigned flags; // k???XsFl
unsigned tick; // start tick
unsigned durTicks; // dur-ticks
unsigned voice; // score voice number
unsigned d0; // MIDI d0 (barNumb)
unsigned d1; // MIDI d1
struct cmXsMidiEvt_str* link;
} cmXsMidiEvt_t;
typedef struct cmXsMidiFile_str
{
cmXsMidiEvt_t* elist;
cmXsMidiEvt_t* eol;
unsigned pitch_min;
unsigned pitch_max;
} cmXsMidiFile_t;
cmXsRC_t _cmXsWriteMidiSvg( cmCtx_t* ctx, cmXScore_t* p, cmXsMidiFile_t* mf, const cmChar_t* dir, const cmChar_t* fn )
{
cmXsRC_t rc = kOkXsRC;
cmSvgH_t svgH = cmSvgNullHandle;
cmXsMidiEvt_t* e = mf->elist;
unsigned noteHeight = 10;
const cmChar_t* svgFn = cmFsMakeFn(dir,fn,"html",NULL);
const cmChar_t* cssFn = cmFsMakeFn(NULL,fn,"css",NULL);
cmChar_t* t0 = NULL; // temporary dynamic string
// create the SVG writer
if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC )
{
rc = cmErrMsg(&p->err,kSvgFailXsRC,"Unable to create the MIDI SVG output file '%s'.",cmStringNullGuard(svgFn));
goto errLabel;
}
// for each MIDI file element
for(; e!=NULL && rc==kOkXsRC; e=e->link)
{
// if this is a note
if( cmIsFlag(e->flags,kOnsetXsFl) )
{
const cmChar_t* classLabel = "note";
t0 = cmTsPrintfP(t0,"note_%i%s",e->voice, cmIsFlag(e->flags,kGraceXsFl) ? "_g":"");
if( cmIsFlag(e->flags,kGraceXsFl) )
classLabel = "grace";
if( cmSvgWriterRect(svgH, e->tick, e->d0 * noteHeight, e->durTicks, noteHeight-1, t0 ) != kOkSvgRC )
rc = kSvgFailXsRC;
else
if( cmSvgWriterText(svgH, e->tick + e->durTicks/2, e->d0 * noteHeight + noteHeight/2, cmMidiToSciPitch( e->d0, NULL, 0), "pitch") != kOkSvgRC )
rc = kSvgFailXsRC;
}
// if this is a bar
if( cmIsFlag(e->flags,kBarXsFl) )
{
if( cmSvgWriterLine(svgH, e->tick, 0, e->tick, 127*noteHeight, "bar") != kOkSvgRC )
rc = kSvgFailXsRC;
else
{
if( cmSvgWriterText(svgH, e->tick, 10, t0 = cmTsPrintfP(t0,"%i",e->d0), "text" ) != kOkSvgRC )
rc = kSvgFailXsRC;
}
}
}
if( rc != kOkXsRC )
cmErrMsg(&p->err,kSvgFailXsRC,"SVG element insert failed.");
if( rc == kOkXsRC )
if( cmSvgWriterWrite(svgH,cssFn,svgFn) != kOkSvgRC )
rc = cmErrMsg(&p->err,kSvgFailXsRC,"SVG file write to '%s' failed.",cmStringNullGuard(svgFn));
errLabel:
cmSvgWriterFree(&svgH);
cmFsFreeFn(svgFn);
cmFsFreeFn(cssFn);
cmMemFree(t0);
return rc;
}
void _cmXsPushMidiEvent( cmXScore_t* p, cmXsMidiFile_t* mf, unsigned flags, unsigned tick, unsigned durTick, unsigned voice, unsigned d0, unsigned d1 )
{
cmXsMidiEvt_t* e = cmLhAllocZ(p->lhH,cmXsMidiEvt_t,1);
e->flags = flags;
e->tick = tick;
e->durTicks = durTick;
e->voice = voice;
e->d0 = d0;
e->d1 = d1;
if( mf->eol != NULL )
mf->eol->link = e;
else
mf->elist = e;
// track the min/max pitch
if( cmIsFlag(flags,kOnsetXsFl) )
{
mf->pitch_min = mf->eol==NULL ? d0 : cmMin(mf->pitch_min,d0);
mf->pitch_max = mf->eol==NULL ? d0 : cmMin(mf->pitch_max,d0);
}
mf->eol = e;
}
cmXsRC_t _cmXScoreWriteMidi( cmCtx_t* ctx, cmXsH_t h, const cmChar_t* dir, const cmChar_t* fn )
{ {
assert(0); // function not implemented
cmXScore_t* p = _cmXScoreHandleToPtr(h); cmXScore_t* p = _cmXScoreHandleToPtr(h);
cmXsPart_t* pp = p->partL; cmXsPart_t* pp = p->partL;
cmXsMidiFile_t mf;
memset(&mf,0,sizeof(mf));
for(; pp!=NULL; pp=pp->link) for(; pp!=NULL; pp=pp->link)
{ {
const cmXsMeas_t* meas = pp->measL; const cmXsMeas_t* meas = pp->measL;
@ -2527,39 +2647,62 @@ cmXsRC_t cmXScoreWriteMidi( cmXsH_t h, const cmChar_t* fn )
const cmXsNote_t* note = meas->noteL; const cmXsNote_t* note = meas->noteL;
for(; note!=NULL; note=note->slink) for(; note!=NULL; note=note->slink)
{ {
// if this is a sounding note
if( cmIsFlag(note->flags,kOnsetXsFl) )
{
unsigned d0 = cmSciPitchToMidiPitch( note->step, note->alter, note->octave );
unsigned durTick = note->duration;
if( note->tied != NULL )
{
cmXsNote_t* tn = note->tied;
for(; tn!=NULL; tn=tn->tied)
durTick += tn->duration;
}
_cmXsPushMidiEvent(p,&mf,note->flags,note->tick,durTick,note->voice->id,d0,note->vel);
continue;
}
// if this is a bar event
if( cmIsFlag(note->flags,kBarXsFl) )
{
_cmXsPushMidiEvent(p,&mf,note->flags,note->tick,0,0,note->meas->number,0);
continue;
}
} }
} }
} }
return _cmXsWriteMidiSvg( ctx, p, &mf, dir, fn );
} }
cmXsRC_t cmXScoreTest( cmXsRC_t cmXScoreTest(
cmCtx_t* ctx, cmCtx_t* ctx,
const cmChar_t* xmlFn, const cmChar_t* xmlFn,
const cmChar_t* midiFn, const cmChar_t* reorderFn,
const cmChar_t* outFn, const cmChar_t* csvOutFn,
const cmChar_t* reorderFn ) const cmChar_t* midiOutFn)
{ {
cmXsRC_t rc; cmXsRC_t rc;
cmXsH_t h = cmXsNullHandle; cmXsH_t h = cmXsNullHandle;
if((rc = cmXScoreInitialize( ctx, &h, xmlFn, midiFn)) != kOkXsRC ) if((rc = cmXScoreInitialize( ctx, &h, xmlFn)) != kOkXsRC )
return cmErrMsg(&ctx->err,rc,"XScore alloc failed."); return cmErrMsg(&ctx->err,rc,"XScore alloc failed.");
if( reorderFn != NULL ) if( reorderFn != NULL )
cmXScoreReorder(h,reorderFn); cmXScoreReorder(h,reorderFn);
if( outFn != NULL ) if( csvOutFn != NULL )
{ {
cmScH_t scH = cmScNullHandle; cmScH_t scH = cmScNullHandle;
double srate = 44100.0; double srate = 44100.0;
cmXScoreWriteCsv(h,outFn); cmXScoreWriteCsv(h,csvOutFn);
cmSymTblH_t stH = cmSymTblCreate(cmSymTblNullHandle, 0, ctx ); cmSymTblH_t stH = cmSymTblCreate(cmSymTblNullHandle, 0, ctx );
if( cmScoreInitialize( ctx, &scH, outFn, srate, NULL, 0, NULL, NULL, stH) != kOkScRC ) if( cmScoreInitialize( ctx, &scH, csvOutFn, srate, NULL, 0, NULL, NULL, stH) != kOkScRC )
cmErrMsg(&ctx->err,kFileFailXsRC,"The generated CSV file could not be parsed."); cmErrMsg(&ctx->err,kFileFailXsRC,"The generated CSV file could not be parsed.");
else else
{ {
@ -2570,10 +2713,19 @@ cmXsRC_t cmXScoreTest(
} }
cmSymTblDestroy(&stH); cmSymTblDestroy(&stH);
}
if( midiOutFn != NULL )
{
cmFileSysPathPart_t* pp = cmFsPathParts(midiOutFn);
_cmXScoreWriteMidi( ctx, h, pp->dirStr, pp->fnStr );
cmFsFreePathParts(pp);
} }
cmXScoreReport(h,&ctx->rpt,true); //cmXScoreReport(h,&ctx->rpt,true);
return cmXScoreFinalize(&h); return cmXScoreFinalize(&h);

View File

@ -16,7 +16,8 @@ extern "C" {
kUnterminatedSlurXsRC, kUnterminatedSlurXsRC,
kUnterminatedOctaveShiftXsrRC, kUnterminatedOctaveShiftXsrRC,
kMidiFailXsRC, kMidiFailXsRC,
kFileFailXsRC kFileFailXsRC,
kSvgFailXsRC
}; };
typedef cmRC_t cmXsRC_t; typedef cmRC_t cmXsRC_t;
@ -56,18 +57,17 @@ extern "C" {
// the grace note ordering changed specified in 'score_print_mk_edit.txt', // the grace note ordering changed specified in 'score_print_mk_edit.txt',
// via cmXScoreReorder() however the ticks are now incorrect - fix them. // via cmXScoreReorder() however the ticks are now incorrect - fix them.
cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* midiFn ); cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn );
cmXsRC_t cmXScoreFinalize( cmXsH_t* hp ); cmXsRC_t cmXScoreFinalize( cmXsH_t* hp );
bool cmXScoreIsValid( cmXsH_t h ); bool cmXScoreIsValid( cmXsH_t h );
cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn ); cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn );
cmXsRC_t cmXScoreWriteMidi( cmXsH_t h, const cmChar_t* fn );
void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl ); void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl );
cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* midiFn, const cmChar_t* outFn, const cmChar_t* reorderFn ); // Generate the CSV file suitable for use by cmScore.
cmXsRC_t cmXScoreTest( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* reorderFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn );
#ifdef __cplusplus #ifdef __cplusplus
} }