Merge branch 'master' of klarke.webfactional.com:webapps/gitweb/repos/libcm

This commit is contained in:
kevin larke 2017-03-19 10:33:23 -04:00
commit e426f5dbd1
15 changed files with 1210 additions and 371 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
Makefile.in
.DS_Store

View File

@ -112,6 +112,14 @@ void _cmMsf_ReportMidiErrors( const _cmMsf_ScoreFollow_t* f, cmScH_t scH, const
}
}
void _cmMsf_WriteMatchFileHeader( cmFileH_t fH )
{
cmFilePrintf(fH," Score Score Score MIDI MIDI MIDI\n");
cmFilePrintf(fH," Bar UUID Pitch UUID Ptch Vel.\n");
cmFilePrintf(fH,"- ----- ----- ----- ----- ---- ----\n");
}
// Write one scScoreMatcherResult_t record to the file fH.
unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcherResult_t* r )
{
@ -130,7 +138,7 @@ unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcher
cmMidiToSciPitch(e->pitch,buf,5);
}
cmFilePrintf(fH,"m %3i %5i %4s %5i %4s %3i\n",
cmFilePrintf(fH,"m %5i %5i %5s %5i %4s %3i\n",
loc==NULL ? 0 : loc->barNumb, // score evt bar
scUid, // score event uuid
buf, // score event pitch
@ -147,17 +155,17 @@ void _cmMsf_ScoreFollowCb( struct cmScMatcher_str* p, void* arg, cmScMatcherResu
r->rV[r->rN++] = *rp;
}
cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx )
cmMsfRC_t cmMidiScoreFollowMain(
cmCtx_t* ctx,
const cmChar_t* scoreCsvFn, // score CSV file as generated from cmXScoreTest().
const cmChar_t* midiFn, // MIDI file to track
const cmChar_t* matchRptOutFn, // Score follow status report
const cmChar_t* matchSvgOutFn, // Score follow graphic report
const cmChar_t* midiOutFn, // (optional) midiFn with apply sostenuto and velocities from the score to the MIDI file
const cmChar_t* tlBarOutFn // (optional) bar positions sutiable for use in a cmTimeLine description file.
)
{
cmMsfRC_t rc = kOkMsfRC;
//const cmChar_t* scoreFn = cmFsMakeUserDirFn("src/kc/src/kc/data","mod2e.csv");
const cmChar_t* scoreFn = cmFsMakeUserDirFn("temp","a7.csv");
const cmChar_t* midiFn = cmFsMakeUserDirFn("media/projects/imag_themes/scores/gen","round1-utf8_11.mid");
const cmChar_t* outFn = cmFsMakeUserDirFn("temp","match.txt");
const cmChar_t* svgFn = cmFsMakeUserDirFn("temp","score0.html");
const cmChar_t* newMidiFn= cmFsMakeUserDirFn("temp","a7.mid");
const cmChar_t* tlBarFn = cmFsMakeUserDirFn("temp","time_line_temp.txt");
cmMsfRC_t rc = kOkMsfRC;
double srate = 96000.0;
cmScMatcher* smp = NULL;
cmScH_t scH = cmScNullHandle;
@ -179,16 +187,16 @@ cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx )
cmCtx* prCtx = cmCtxAlloc(NULL, err.rpt, cmLHeapNullHandle, cmSymTblNullHandle );
// initialize the score
if( cmScoreInitialize( ctx, &scH, scoreFn, srate, NULL, 0, NULL, NULL, cmSymTblNullHandle) != kOkScRC )
if( cmScoreInitialize( ctx, &scH, scoreCsvFn, srate, NULL, 0, NULL, NULL, cmSymTblNullHandle) != kOkScRC )
{
rc = cmErrMsg(&err,kFailMsfRC,"cmScoreInitialize() failed on %s",cmStringNullGuard(scoreFn));
rc = cmErrMsg(&err,kFailMsfRC,"cmScoreInitialize() failed on %s",cmStringNullGuard(scoreCsvFn));
goto errLabel;
}
// setup the callback record
if((sfr.rAllocN = cmScoreEvtCount( scH )*2) == 0)
{
rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreFn));
rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreCsvFn));
goto errLabel;
}
@ -229,19 +237,21 @@ cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx )
printf("MIDI notes:%i Score Events:%i\n",mN,cmScoreEvtCount(scH));
// create the output file
if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
if( cmFileOpen(&fH,matchRptOutFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
{
rc = cmErrMsg(&err,kFailMsfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn));
rc = cmErrMsg(&err,kFailMsfRC,"Unable to create the file '%s'.",cmStringNullGuard(matchRptOutFn));
goto errLabel;
}
// allocate the graphics object
if( cmScoreMatchGraphicAlloc( ctx, &smgH, scoreFn, midiFn ) != kOkSmgRC )
if( cmScoreMatchGraphicAlloc( ctx, &smgH, scoreCsvFn, midiFn ) != kOkSmgRC )
{
rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics allocation failed..");
goto errLabel;
}
// write the match report output file header
_cmMsf_WriteMatchFileHeader(fH);
// for each score follower callback record
for(i=0; i<sfr.rN; ++i)
@ -267,12 +277,14 @@ cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx )
//cmMidiFilePrintMsgs( mfH, &ctx->rpt );
// write the tracking match file as an SVG file.
cmScoreMatchGraphicWrite( smgH, svgFn );
cmScoreMatchGraphicWrite( smgH, matchSvgOutFn );
// write a cmTimeLine file which contains markers at each bar position
cmScoreMatchGraphicGenTimeLineBars(smgH, tlBarFn, srate );
if( tlBarOutFn != NULL )
cmScoreMatchGraphicGenTimeLineBars(smgH, tlBarOutFn, srate );
cmScoreMatchGraphicUpdateMidiFromScore( ctx, smgH, newMidiFn );
if( midiOutFn != NULL )
cmScoreMatchGraphicUpdateMidiFromScore( ctx, smgH, midiOutFn );
errLabel:
@ -286,12 +298,12 @@ cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx )
cmCtxFree(&prCtx);
cmFsFreeFn(scoreFn);
cmFsFreeFn(midiFn);
cmFsFreeFn(outFn);
cmFsFreeFn(svgFn);
cmFsFreeFn(newMidiFn);
cmFsFreeFn(tlBarFn);
//cmFsFreeFn(scoreCsvFn);
//cmFsFreeFn(midiFn);
//cmFsFreeFn(matchRptOutFn);
//cmFsFreeFn(matchSvgOutFn);
//cmFsFreeFn(outMidiFn);
//cmFsFreeFn(tlBarFn);
return rc;
}

View File

@ -15,7 +15,15 @@ extern "C" {
typedef cmRC_t cmMsfRC_t;
cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx );
cmMsfRC_t cmMidiScoreFollowMain(
cmCtx_t* ctx,
const cmChar_t* scoreCsvFn, // score CSV file as generated from cmXScoreTest().
const cmChar_t* midiFn, // MIDI file to track
const cmChar_t* matchRptOutFn, // Score follow status report
const cmChar_t* matchSvgOutFn, // Score follow graphic report
const cmChar_t* midiOutFn, // (optional) midiFn with apply sostenuto and velocities from the score to the MIDI file
const cmChar_t* tlBarOutFn // (optional) bar positions sutiable for use in a cmTimeLine description file.
);
#ifdef __cplusplus
}

View File

@ -154,15 +154,16 @@ cmScEvtRef_t _cmScEvtRefArray[] =
cmScEvtRef_t _cmScDynRefArray[] =
{
{ 1, 0, "pppp"},
{ 2, 0, "ppp" },
{ 3, 0, "pp" },
{ 4, 0, "p" },
{ 5, 0, "mp" },
{ 6, 0, "mf" },
{ 7, 0, "f" },
{ 8, 0, "ff" },
{ 9, 0, "fff" },
{ 1, 0, "silent"},
{ 2, 0, "pppp"},
{ 3, 0, "ppp" },
{ 4, 0, "pp" },
{ 5, 0, "p" },
{ 6, 0, "mp" },
{ 7, 0, "mf" },
{ 8, 0, "f" },
{ 9, 0, "ff" },
{ 10, 0, "fff" },
{ kInvalidDynScId,0, "***" },
};
@ -361,6 +362,22 @@ void _cmScFreeMarkList( cmScMark_t* markList )
}
}
void _cmScFreeSectList( cmSc_t* p )
{
// release the section linked list
cmScSect_t* sp = p->sectList;
cmScSect_t* np = NULL;
while(sp!=NULL)
{
np = sp->link;
cmMemFree(sp);
sp = np;
}
p->sectList = NULL;
}
void _cmScFreeSetList( cmScSet_t* setList )
{
cmScSet_t* tp = setList;
@ -412,6 +429,8 @@ cmScRC_t _cmScFinalize( cmSc_t* p )
cmMemFree(p->sets);
}
_cmScFreeSectList( p );
_cmScFreeSetList(p->setList);
_cmScFreeMarkList(p->markList);
@ -1124,9 +1143,10 @@ cmScRC_t _cmScProcSets( cmSc_t* p )
cmScRC_t _cmScProcSections( cmSc_t* p, cmScSect_t* sectList )
cmScRC_t _cmScProcSections( cmSc_t* p )
{
cmScRC_t rc = kOkScRC;
cmScSect_t* sectList = p->sectList;
unsigned i;
// count the sections
@ -1171,16 +1191,9 @@ cmScRC_t _cmScProcSections( cmSc_t* p, cmScSect_t* sectList )
}
}
// release the section linked list
sp = sectList;
cmScSect_t* np = NULL;
while(sp!=NULL)
{
np = sp->link;
cmMemFree(sp);
sp = np;
}
_cmScFreeSectList(p);
//_cmScPrintSets("Sets",p->setList );
_cmScProcSets(p);
@ -1592,7 +1605,7 @@ cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn, doubl
if((rc = _cmScInitLocArray(p)) != kOkScRC )
goto errLabel;
if((rc = _cmScProcSections(p,p->sectList)) != kOkScRC )
if((rc = _cmScProcSections(p)) != kOkScRC )
goto errLabel;
if((rc = _cmScProcMarkers(p)) != kOkScRC )
@ -2409,6 +2422,16 @@ cmScRC_t cmScoreDecode( const void* msg, unsigned msgByteCnt, cmScMsg_t* m)
return kOkScRC;
}
const cmChar_t* _cmScoreSectionLabel( cmSc_t* p, const cmScoreEvt_t* r )
{
unsigned i;
for(i=0; i<p->sectCnt; ++i)
if( p->sect[i].locPtr != NULL && p->sect[i].locPtr->index == r->locIdx && p->sect[i].begEvtIndex == r->index )
return p->sect[i].label;
return NULL;
}
void _cmScorePrintHdr( cmRpt_t* rpt )
{
cmRptPrintf(rpt,"evnt CSV bar\n");
@ -2416,12 +2439,13 @@ void _cmScorePrintHdr( cmRpt_t* rpt )
cmRptPrintf(rpt,"----- ----- ----- --- --- ----- ----- --- -------\n");
}
void _cmScorePrintEvent( const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt )
void _cmScorePrintEvent( cmSc_t* p, const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt )
{
bool eolFl = true;
switch(r->type)
{
case kBarEvtScId:
cmRptPrintf(rpt,"%5i %5i %3i bar\n",
cmRptPrintf(rpt,"%5i %5i %3i bar ",
i,
r->line,
r->barNumb );
@ -2429,7 +2453,7 @@ void _cmScorePrintEvent( const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt )
case kPedalEvtScId:
case kNonEvtScId:
cmRptPrintf(rpt,"%5i %5i %5i %3i %3i %s %5s %c%c%c %s\n",
cmRptPrintf(rpt,"%5i %5i %5i %3i %3i %s %5s %c%c%c %-7s ",
i,
r->line,
r->locIdx,
@ -2440,12 +2464,20 @@ void _cmScorePrintEvent( const cmScoreEvt_t* r, unsigned i, cmRpt_t* rpt )
cmIsFlag(r->flags,kEvenScFl) ? 'e' : ' ',
cmIsFlag(r->flags,kTempoScFl) ? 't' : ' ',
cmIsFlag(r->flags,kDynScFl) ? 'd' : ' ',
//cmIsFlag(r->flags,kDynScFl) ? 7-strlen(cmScDynIdToLabel(r->dynVal)) : 7,
cmIsFlag(r->flags,kDynScFl) ? cmScDynIdToLabel(r->dynVal) : "");
break;
default:
eolFl = false;
break;
}
const cmChar_t* sectionLabel;
if((sectionLabel = _cmScoreSectionLabel(p,r)) != NULL )
cmRptPrintf(rpt,"section:%s ",sectionLabel);
cmRptPrintf(rpt,"\n");
}
@ -2458,7 +2490,7 @@ void cmScorePrint( cmScH_t h, cmRpt_t* rpt )
_cmScorePrintHdr(rpt);
for(i=0; i<p->cnt; ++i)
_cmScorePrintEvent(p->array+i,i,rpt);
_cmScorePrintEvent(p,p->array+i,i,rpt);
}
@ -2483,7 +2515,7 @@ void cmScorePrintSets( cmScH_t h, cmRpt_t* rpt )
_cmScorePrintHdr(rpt);
for(j=0; j<s->eleCnt; ++j)
_cmScorePrintEvent(*s->eleArray+j,j,rpt);
_cmScorePrintEvent(p,*s->eleArray+j,j,rpt);
cmRptPrintf(rpt,"Targets Section: ");
for(j=0; j<s->sectCnt; ++j)
@ -2709,7 +2741,7 @@ cmScRC_t cmScoreFileFromMidi( cmCtx_t* ctx, const cmChar_t* midiFn, const c
}
void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
void cmScoreReport( cmCtx_t* ctx, const cmChar_t* fn )
{
cmScH_t h = cmScNullHandle;
if( cmScoreInitialize(ctx,&h,fn,0,NULL,0,NULL,NULL, cmSymTblNullHandle ) != kOkScRC )
@ -2720,6 +2752,11 @@ void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
cmScoreFinalize(&h);
}
void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
{
}
// 1. Fix absolute message time which was incorrect on original score file.
// 2.
void cmScoreFix( cmCtx_t* ctx )

View File

@ -280,8 +280,12 @@ extern "C" {
// Generate a new score file from a MIDI file.
cmScRC_t cmScoreFileFromMidi( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* scoreFn );
void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn );
// Print open the score file 'fn' and report the contents. This function
// simply wraps calls to cmScoreInitialize() and cmScorePrint().
void cmScoreReport( cmCtx_t* ctx, const cmChar_t* fn );
void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn );
//)
#ifdef __cplusplus

View File

@ -538,7 +538,7 @@ cmSmgRC_t cmScoreMatchGraphicWrite( cmSmgH_t h, const cmChar_t* fn )
svgHeight += 10; // add a right and lower border
svgWidth += 10;
cmFilePrintf(fH,"<!DOCTYPE html>\n<html>\n<head><link rel=\"stylesheet\" type=\"text/css\" href=\"score0.css\"></head><body>\n<svg width=\"%i\" height=\"%i\">\n",svgWidth,svgHeight);
cmFilePrintf(fH,"<!DOCTYPE html>\n<html>\n<head><link rel=\"stylesheet\" type=\"text/css\" href=\"cmScoreMatchGraphic.css\"></head><body>\n<svg width=\"%i\" height=\"%i\">\n",svgWidth,svgHeight);
for(i=0; i<p->locN; ++i)
{
@ -690,7 +690,7 @@ cmSmgRC_t _cmScoreMatchGraphicInsertMidiMsg( cmSmg_t* p, cmMidiFileH_t mfH, bool
cmMidiByte_t midi_vel = pedalDnFl ? 64 : 0;
cmMidiByte_t midi_ch = 0;
printf("pedal:%s\n",pedalDnFl?"down":"up");
//printf("pedal:%s\n",pedalDnFl?"down":"up");
// insert a pedal msg relative to the reference event
if( cmMidiFileInsertMsg(mfH, m->uid, dtick_offset, midi_ch, kCtlMdId, kSostenutoCtlMdId, midi_vel ) != kOkMfRC )
@ -798,8 +798,6 @@ cmSmgRC_t cmScoreMatchGraphicUpdateMidiFromScore( cmCtx_t* ctx, cmSmgH_t h, cons
goto errLabel;
}
cmMidiFilePrintMsgs(mfH, p->err.rpt );
errLabel:
cmMidiFileClose(&mfH);

View File

@ -134,7 +134,7 @@ cmTlMidiFile_t* _cmTlMidiFileObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
if( op==NULL || op->typeId != kMidiFileTlId )
{
if( errFl && p != NULL)
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to MIDI file failed.");
return NULL;
}
@ -147,7 +147,7 @@ cmTlMidiEvt_t* _cmTlMidiEvtObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
if( op==NULL || op->typeId != kMidiEvtTlId )
{
if( errFl && p != NULL )
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to MIDI event failed.");
return NULL;
}
@ -173,7 +173,7 @@ cmTlAudioEvt_t* _cmTlAudioEvtObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
if( op==NULL || op->typeId != kAudioEvtTlId )
{
if( errFl && p != NULL)
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to audio event failed.");
return NULL;
}
return (cmTlAudioEvt_t*)op;
@ -186,7 +186,7 @@ cmTlMarker_t* _cmTlMarkerObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
if( op==NULL || op->typeId != kMarkerTlId )
{
if( errFl && p != NULL)
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion to marker object failed.");
return NULL;
}
return (cmTlMarker_t*)op;
@ -1227,31 +1227,37 @@ _cmTlObj_t* _cmTimeLineObjAtTime( _cmTl_t* p, unsigned seqId, unsigned seqSmpIdx
_cmTlObj_t* op = p->seq[seqId].first;
_cmTlObj_t* min_op = NULL;
unsigned minDist = UINT_MAX;
bool inFl = false;
// for each object in the requested sequence
for(; op!=NULL; op=op->next)
if( typeId==cmInvalidId || op->obj->typeId == typeId )
{
// if seqSmpIdx is inside this object - then return it as the solution
bool in0Fl = false;
// if seqSmpIdx is inside this object - then the returned object must contain seqSmpIdx
// (but the ideal point to return is the one which contains seqSmpIdx and also has
// a begin or end point very close to seqSmpIdx - so this defer selecting an object
// until all objects which may contain seqSmpIdx have been examined
if((op->obj->seqSmpIdx <= seqSmpIdx && seqSmpIdx < (op->obj->seqSmpIdx + op->obj->durSmpCnt)))
return op;
{
inFl = true; // the returned object must contain seqSmpIdx
in0Fl = true; // this object contains seqSmpIdx
}
// measure the distance from seqSmpIdx to the begin and end of this object
unsigned d0 = op->obj->seqSmpIdx < seqSmpIdx ? seqSmpIdx - op->obj->seqSmpIdx : op->obj->seqSmpIdx - seqSmpIdx;
unsigned d1 = op->obj->seqSmpIdx+op->obj->durSmpCnt < seqSmpIdx ? seqSmpIdx - op->obj->seqSmpIdx+op->obj->durSmpCnt : op->obj->seqSmpIdx+op->obj->durSmpCnt - seqSmpIdx;
// d0 and d1 should decrease as the cur object approaches seqSmpIdx
// If they do not then the search is over - return the closest point.
if( d0>minDist && d1>minDist)
break;
// track the min dist and the assoc'd obj
if( d0 < minDist )
if( d0 < minDist && (inFl==false || inFl==in0Fl))
{
minDist = d0;
min_op = op;
}
if( d1 < minDist )
if( d1 < minDist && (inFl==false || inFl==in0Fl))
{
minDist = d1;
min_op = op;

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,9 @@ extern "C" {
kPedalStateErrorXsRc,
kMidiFailXsRC,
kFileFailXsRC,
kSvgFailXsRC
kSvgFailXsRC,
kOverlapWarnXsRC,
kZeroLengthEventXsRC
};
typedef cmRC_t cmXsRC_t;
@ -41,32 +43,23 @@ extern "C" {
//
// M-x load-file ~/src/emacs/proc_music_xml.el
//
// 3) How to assigned dynamic markings (they are not attached to notes). (from MIDI file?)
// 4) Tempo syntax is inconsistent (only a problem in full part2 score)
// 5) Heel is being parsed but not used.
// 6) Sostenuto pedal events are not being parsed because they are not pedal events.
// 7) What is a 'pedal-change' event vs. a 'pedal-stop' event.
// 8) Verify the colors. (done)
// 9) Remove blank bars at end (done in xml - do in score)
//10) Need to assign section targets (always default to next section)
//11) Mark tied notes for skip. (done)
//12) Determine note off locations based on ties and slurs - defer 'pedal' to player
//13) Check that the measures are given in sorted order.
//14) Current implementation assumes meter changes only occur at measure boundaries.
//15) Score Fixes: Add meter to bar 1, fix time errors (shown in voice report)
//16) The order of notes is now correct (4/6/16) after applying
// the grace note ordering changed specified in 'score_print_mk_edit.txt',
// via cmXScoreReorder() however the ticks are now incorrect - fix them.
cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn );
// Initialize an cmXScore object from a Sibelius generated MusicXML file.
// Optionally include an 'edit' file to attach additional score information.
// Note that the 'edit' file is created by marking up a file created via
// cmXScoreReport().
cmXsRC_t cmXScoreInitialize( cmCtx_t* ctx, cmXsH_t* hp, const cmChar_t* xmlFn, const cmChar_t* editFn );
cmXsRC_t cmXScoreFinalize( cmXsH_t* hp );
bool cmXScoreIsValid( cmXsH_t h );
cmXsRC_t cmXScoreWriteCsv( cmXsH_t h, const cmChar_t* csvFn );
void cmXScoreReport( cmXsH_t h, cmRpt_t* rpt, bool sortFl );
cmXsRC_t cmXScoreGenEditFile( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* outFn );
// 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 );

View File

@ -8,8 +8,11 @@
#include "cmMallocDebug.h"
#include "cmLinkedHeap.h"
#include "cmTime.h"
#include "cmText.h"
#include "cmMidi.h"
#include "cmMidiFile.h"
#include "cmSvgWriter.h"
#ifdef cmBIG_ENDIAN
#define mfSwap16(v) (v)
@ -256,7 +259,7 @@ cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmM
unsigned byteN = cmMidiStatusToByteCount(tmp->status);
if( byteN==kInvalidMidiByte || byteN > 2 )
return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i.",tmp->status,tmp->status);
return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i byte cnt:%i.",tmp->status,tmp->status,byteN);
unsigned i;
for(i=useRsFl; i<byteN; ++i)
@ -339,7 +342,7 @@ cmMfRC_t _cmMidiFileReadTrack( _cmMidiFile_t* mfp, unsigned short trkIdx )
if((rc = _cmMidiFileRead8(mfp,&status)) != kOkMfRC )
return rc;
//printf("st:%i 0x%x\n",status,status);
//printf("%5i st:%i 0x%x\n",dticks,status,status);
// append a track msg
if((rc = _cmMidiFileAppendTrackMsg( mfp, trkIdx, dticks, status, &tmp )) != kOkMfRC )
@ -677,8 +680,11 @@ cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hp, const char* fn )
rc = cmErrMsg(&p->err,kFileFailMfRC,"MIDI file close failed.");
if( rc != kOkMfRC )
{
_cmMidiFileClose(p);
hp->h = NULL;
}
return rc;
}
@ -971,12 +977,75 @@ cmMfRC_t _cmMidiFileWriteMetaMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tm
return rc;
}
cmMfRC_t _cmMidiFileInsertEotMsg( _cmMidiFile_t* p, unsigned trkIdx )
{
_cmMidiTrack_t* trk = p->trkV + trkIdx;
cmMidiTrackMsg_t* m0 = NULL;
cmMidiTrackMsg_t* m = trk->base;
// locate the current EOT msg on this track
for(; m!=NULL; m=m->link)
{
if( m->status == kMetaStId && m->metaId == kEndOfTrkMdId )
{
// If this EOT msg is the last msg in the track ...
if( m->link == NULL )
{
assert( m == trk->last );
return kOkMfRC; // ... then there is nothing else to do
}
// If this EOT msg is not the last in the track ...
if( m0 != NULL )
m0->link = m->link; // ... then unlink it
break;
}
m0 = m;
}
// if we get here then the last msg in the track was not an EOT msg
// if there was no previously allocated EOT msg
if( m == NULL )
{
m = _cmMidiFileAllocMsg(p, trkIdx, 1, kMetaStId );
m->metaId = kEndOfTrkMdId;
trk->cnt += 1;
}
// link an EOT msg as the last msg on the track
// if the track is currently empty
if( m0 == NULL )
{
trk->base = m;
trk->last = m;
}
else // link the msg as the last on on the track
{
assert( m0 == trk->last);
m0->link = m;
m->link = NULL;
trk->last = m;
}
return kOkMfRC;
}
cmMfRC_t _cmMidiFileWriteTrack( _cmMidiFile_t* mfp, unsigned trkIdx )
{
cmMfRC_t rc = kOkMfRC;
cmMidiTrackMsg_t* tmp = mfp->trkV[trkIdx].base;
cmMidiByte_t runStatus = 0;
// be sure there is a EOT msg at the end of this track
if((rc = _cmMidiFileInsertEotMsg(mfp, trkIdx )) != kOkMfRC )
return rc;
for(; tmp != NULL; tmp=tmp->link)
{
// write the msg tick count
@ -1379,8 +1448,11 @@ cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMi
assert( m1->atick >= m->atick );
m1->dtick = m1->atick - m->atick;
}
p->trkV[trkIdx].cnt += 1;
p->msgVDirtyFl = true;
return kOkMfRC;
@ -1402,6 +1474,8 @@ cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned
m.status = status & 0xf0;
m.byteCnt = sizeof(cm);
m.u.chMsgPtr = &cm;
assert( m.status >= kNoteOffMdId && m.status <= kPbendMdId );
return cmMidiFileInsertTrackMsg(h,trkIdx,&m);
}
@ -1836,6 +1910,38 @@ void cmMidiFilePrintTracks( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt )
void cmMidiFileTestPrint( void* printDataPtr, const char* fmt, va_list vl )
{ vprintf(fmt,vl); }
cmMidiFileDensity_t* cmMidiFileNoteDensity( cmMidiFileH_t h, unsigned* cntRef )
{
int msgN = cmMidiFileMsgCount(h);
const cmMidiTrackMsg_t** msgs = cmMidiFileMsgArray(h);
cmMidiFileDensity_t* dV = cmMemAllocZ(cmMidiFileDensity_t,msgN);
int i,j,k;
for(i=0,k=0; i<msgN && k<msgN; ++i)
if( msgs[i]->status == kNoteOnMdId && msgs[i]->u.chMsgPtr->d1 > 0 )
{
dV[k].uid = msgs[i]->uid;
dV[k].amicro = msgs[i]->amicro;
for(j=i; j>=0; --j)
{
if( msgs[i]->amicro - msgs[j]->amicro > 1000000 )
break;
dV[k].density += 1;
}
k += 1;
}
if( cntRef != NULL )
*cntRef = k;
return dV;
}
cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn )
{
cmMfRC_t rc = kOkMfRC;
@ -1872,6 +1978,99 @@ cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmCh
return rc;
}
cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outSvgFn, const cmChar_t* cssFn )
{
cmMfRC_t rc = kOkMfRC;
cmSvgH_t svgH = cmSvgNullHandle;
cmMidiFileH_t mfH = cmMidiFileNullHandle;
unsigned msgN = 0;
const cmMidiTrackMsg_t** msgs = NULL;
unsigned noteHeight = 10;
double micros_per_sec = 1000.0;
unsigned i;
if((rc = cmMidiFileOpen(ctx,&mfH,midiFn)) != kOkMfRC )
{
rc = cmErrMsg(&ctx->err,rc,"Unable to open the MIDI file '%s'.",cmStringNullGuard(midiFn));
goto errLabel;
}
cmMidiFileCalcNoteDurations( mfH );
msgN = cmMidiFileMsgCount(mfH);
msgs = cmMidiFileMsgArray(mfH);
if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC )
{
rc = cmErrMsg(&ctx->err,kSvgFailMfRC,"Unable to create the MIDI SVG output file '%s'.",cmStringNullGuard(outSvgFn));
goto errLabel;
}
for(i=0; i<msgN && rc==kOkMfRC; ++i)
if( msgs[i]->status == kNoteOnMdId && msgs[i]->u.chMsgPtr->d1 > 0 )
{
const cmMidiTrackMsg_t* m = msgs[i];
if( cmSvgWriterRect(svgH, m->amicro/micros_per_sec, m->u.chMsgPtr->d0 * noteHeight, m->u.chMsgPtr->durMicros/micros_per_sec, noteHeight-1, "note" ) != kOkSvgRC )
rc = kSvgFailMfRC;
const cmChar_t* t0 = cmMidiToSciPitch(m->u.chMsgPtr->d0,NULL,0);
if( cmSvgWriterText(svgH, (m->amicro + (m->u.chMsgPtr->durMicros/2)) / micros_per_sec, m->u.chMsgPtr->d0 * noteHeight, t0, "text" ) != kOkSvgRC )
rc = kSvgFailMfRC;
}
if( rc != kOkMfRC )
{
cmErrMsg(&ctx->err,rc,"SVG Shape insertion failed.");
goto errLabel;
}
unsigned dN = 0;
cmMidiFileDensity_t* dV = cmMidiFileNoteDensity( mfH, &dN );
double t0 = 0;
double y0 = 64.0;
cmChar_t* tx = NULL;
for(i=0; i<dN; ++i)
{
const cmMidiTrackMsg_t* m;
if((m = _cmMidiFileUidToMsg( _cmMidiFileHandleToPtr(mfH), dV[i].uid )) == NULL )
rc = cmErrMsg(&ctx->err,kUidNotFoundMfRC,"The MIDI msg form UID:%i was not found.",dV[i].uid);
else
{
double t1 = m->amicro / micros_per_sec;
double y1 = dV[i].density * noteHeight;
cmSvgWriterLine(svgH, t0, y0, t1, y1, "density" );
cmSvgWriterText(svgH, t1, y1, tx = cmTsPrintfP(tx,"%i",dV[i].density),"dtext");
t0 = t1;
y0 = y1;
}
}
cmMemFree(dV);
cmMemFree(tx);
if( rc == kOkMfRC )
if( cmSvgWriterWrite(svgH,cssFn,outSvgFn) != kOkSvgRC )
rc = cmErrMsg(&ctx->err,kSvgFailMfRC,"SVG file write to '%s' failed.",cmStringNullGuard(outSvgFn));
errLabel:
cmMidiFileClose(&mfH);
cmSvgWriterFree(&svgH);
return rc;
}
void cmMidiFilePrintControlNumbers( cmCtx_t* ctx, const char* fn )
{
cmMidiFileH_t h = cmMidiFileNullHandle;

View File

@ -117,7 +117,8 @@ extern "C" {
kLargeDeltaTickMfRC, // 12 (a large delta tick value was filtered)
kUidNotFoundMfRC, // 13
kUidNotANoteMsgMfRC, // 14
kInvalidTrkIndexMfRC // 15
kInvalidTrkIndexMfRC,// 15
kSvgFailMfRC // 16
};
extern cmMidiFileH_t cmMidiFileNullHandle;
@ -212,11 +213,27 @@ extern "C" {
void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt );
void cmMidiFilePrintTrack( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt );
void cmMidiFileTest( const char* fn, cmCtx_t* ctx );
typedef struct
{
unsigned uid;
unsigned long long amicro;
unsigned density;
} cmMidiFileDensity_t;
// Generate the note onset density measure for each note in the MIDI file.
// Delete the returned memory with a call to cmMemFree().
cmMidiFileDensity_t* cmMidiFileNoteDensity( cmMidiFileH_t h, unsigned* cntRef );
// Generate a piano-roll plot description file which can be displayed with cmXScore.m
cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn );
cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outSvgFn, const cmChar_t* cssFn );
void cmMidiFileTest( const char* fn, cmCtx_t* ctx );
//)

View File

@ -167,6 +167,32 @@ void _cmSvgSize( cmSvg_t* p, double* widthRef, double* heightRef )
*heightRef = max_y - min_y;
}
void _cmSvgWriterFlipY( cmSvg_t* p, unsigned height )
{
cmSvgEle_t* e = p->elist;
for(; e!=NULL; e=e->link)
{
e->y0 = (-e->y0) + height;
e->y1 = (-e->y1) + height;
if( e->id == kRectSvgId )
{
double t = e->y1;
e->y1 = e->y0;
e->y0 = t;
}
}
}
/*
"<script type=\"text/javascript\" src=\"svg-pan-zoom.min.js\"></script>\n"
"<script>\n"
" var panZoom = null;\n"
" function doOnLoad() { panZoom = svgPanZoom(document.querySelector('#mysvg'), { controlIconsEnabled:true } ) }\n"
"</script>\n"
*/
cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* outFn )
{
@ -176,15 +202,29 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t*
double svgHeight = 0;
cmSvgEle_t* e = p->elist;
cmFileH_t fH = cmFileNullHandle;
cmChar_t* s0 = NULL;
cmChar_t* s1 = NULL;
cmChar_t hdr[] =
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<meta charset=\"utf-8\">\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">\n"
"</head>\n"
"<body onload=\"doOnLoad()\">\n"
"<svg id=\"mysvg\" width=\"%f\" height=\"%f\">\n";
_cmSvgSize(p, &svgWidth, &svgHeight );
if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC )
return cmErrMsg(&p->err,kFileFailSvgRC,"SVG file create failed for '%s'.",cmStringNullGuard(outFn));
if( cmFilePrintf(fH,"<!DOCTYPE html>\n<html>\n<head><link rel=\"stylesheet\" type=\"text/css\" href=\"%s\"></head><body>\n<svg width=\"%f\" height=\"%f\">\n",svgWidth,svgHeight,cssFn) != kOkFileRC )
_cmSvgWriterFlipY( p, svgHeight );
// print the file header
if( (s0 = cmTsPrintfP(s0,hdr,cssFn,svgWidth,svgHeight)) == NULL )
{
rc = cmErrMsg(&p->err,kFileFailSvgRC,"File prefix write failed.");
rc = cmErrMsg(&p->err,kPrintFailSvgRC,"File prefix write failed.");
goto errLabel;
}
@ -193,18 +233,18 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t*
switch( e->id )
{
case kRectSvgId:
if( cmFilePrintf(fH,"<rect x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\" class=\"%s\"/>\n",e->x0,e->y0,e->x1-e->x0,e->y1-e->y0,e->cssClass) != kOkFileRC )
rc = kFileFailSvgRC;
if( (s1 = cmTsPrintfP(s1,"<rect x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\" class=\"%s\"/>\n",e->x0,e->y0,e->x1-e->x0,e->y1-e->y0,e->cssClass)) == NULL )
rc = kPrintFailSvgRC;
break;
case kLineSvgId:
if( cmFilePrintf(fH,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" class=\"%s\"/>\n",e->x0,e->y0,e->x1,e->y1,e->cssClass) != kOkFileRC )
rc = kFileFailSvgRC;
if( (s1 = cmTsPrintfP(s1,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" class=\"%s\"/>\n",e->x0,e->y0,e->x1,e->y1,e->cssClass)) == NULL )
rc = kPrintFailSvgRC;
break;
case kTextSvgId:
if( cmFilePrintf(fH,"<text x=\"%f\" y=\"%f\" class=\"%s\">%s</text>\n",e->x0,e->y0,e->cssClass,e->text) != kOkFileRC )
rc = kFileFailSvgRC;
if( (s1 = cmTsPrintfP(s1,"<text x=\"%f\" y=\"%f\" class=\"%s\">%s</text>\n",e->x0,e->y0,e->cssClass,e->text)) == NULL )
rc = kPrintFailSvgRC;
break;
default:
@ -214,21 +254,38 @@ cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t*
if( rc != kOkSvgRC )
{
rc = cmErrMsg(&p->err,kFileFailSvgRC,"Element write failed.");
rc = cmErrMsg(&p->err,kPrintFailSvgRC,"Element write failed.");
break;
}
s0 = cmTextAppendSS(s0,s1);
}
if( cmFilePrint(fH,"</svg>\n</body>\n</html>\n") != kOkFileRC )
if( (s1 = cmTsPrintfP(s1,"</svg>\n</body>\n</html>\n")) == NULL )
{
rc = cmErrMsg(&p->err,kFileFailSvgRC,"File suffix write failed.");
rc = cmErrMsg(&p->err,kPrintFailSvgRC,"File suffix write failed.");
goto errLabel;
}
if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC )
{
rc = cmErrMsg(&p->err,kFileFailSvgRC,"SVG file create failed for '%s'.",cmStringNullGuard(outFn));
goto errLabel;
}
if( cmFilePrint(fH,s0 = cmTextAppendSS(s0,s1)) != kOkFileRC )
{
rc = cmErrMsg(&p->err,kFileFailSvgRC,"File write failed.");
goto errLabel;
}
errLabel:
cmFileClose(&fH);
cmMemFree(s0);
cmMemFree(s1);
return rc;
}

View File

@ -9,6 +9,7 @@ enum
{
kOkSvgRC = cmOkRC,
kFileFailSvgRC,
kPrintFailSvgRC,
kLHeapFailSvgRC
};
@ -21,10 +22,14 @@ enum
cmSvgRC_t cmSvgWriterFree( cmSvgH_t* hp );
bool cmSvgWriterIsValid( cmSvgH_t h );
cmSvgRC_t cmSvgWriterRect( cmSvgH_t h, double x, double y, double ww, double hh, const cmChar_t* cssClassLabel );
cmSvgRC_t cmSvgWriterRect( cmSvgH_t h, double x, double y, double ww, double hh, const cmChar_t* cssClassLabel );
cmSvgRC_t cmSvgWriterLine( cmSvgH_t h, double x0, double y0, double x1, double y1, const cmChar_t* cssClassLabel );
cmSvgRC_t cmSvgWriterText( cmSvgH_t h, double x, double y, const cmChar_t* text, const cmChar_t* cssClassLabel );
// Write the SVG file. Note that header on this file references the CSS file 'cssFn'
// and the Javascript file svg-pan-zoom.min.js from https://github.com/ariutta/svg-pan-zoom.
// Both the CSS file and svg-pan-zoom.min.js should therefore be in the same directory
// as the output HTML file.
cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* outFn );
#ifdef __cplusplus

View File

@ -657,7 +657,7 @@ cmDspRC_t _cmDspTimeLineRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_
cmTlObj_t* op;
if((op = cmTimeLineIdToObj(p->tlH, cmInvalidId, markerId )) != NULL )
{
assert(op->typeId == kMarkerTlId);
assert(op->typeId == kMarkerTlId || op->typeId == kMidiEvtTlId );
p->afIdx = op->begSmpIdx;

View File

@ -201,7 +201,7 @@ void _cmDspSys_TlXformChain( cmDspSysH_t h, cmDspTlXform_t* c, unsigned preGrpS
cmDspInst_t* max_lwr_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Max Lwr"), 0.0, -1.0, 5.0, 3.0);
cmDspInst_t* min_off_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Min Off"), 0.0, 50.0, 0.1, 30.0);
cmDspInst_t* max_off_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Max Off"), 0.0, 50.0, 0.1, 30.0);
cmDspInst_t* min_wet_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Min Wet"), 0.0, 1.0, 0.01, 1.0);
cmDspInst_t* min_wet_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Min Wet"), 0.0, 1.0, 0.01, 0.0);
cmDspInst_t* max_wet_ctl = cmDspSysAllocScalarP( h,preGrpSymId, NULL, lbl("Max Wet"), 0.0, 1.0, 0.01, 1.0);