1115 lines
30 KiB
C++
1115 lines
30 KiB
C++
|
#include "cwCommon.h"
|
||
|
#include "cwLog.h"
|
||
|
#include "cwCommonImpl.h"
|
||
|
#include "cwMem.h"
|
||
|
#include "cwText.h"
|
||
|
#include "cwObject.h"
|
||
|
#include "cwMidi.h"
|
||
|
#include "cwFileSys.h"
|
||
|
#include "cwCsv.h"
|
||
|
#include "cwNumericConvert.h"
|
||
|
#include "cwTime.h"
|
||
|
#include "cwDynRefTbl.h"
|
||
|
#include "cwScoreParse.h"
|
||
|
#include "cwSfScore.h"
|
||
|
#include "cwVectOps.h"
|
||
|
|
||
|
namespace cw
|
||
|
{
|
||
|
namespace score_parse
|
||
|
{
|
||
|
typedef struct label_id_str
|
||
|
{
|
||
|
unsigned id;
|
||
|
const char* label;
|
||
|
} label_id_t;
|
||
|
|
||
|
typedef struct var_ref_str
|
||
|
{
|
||
|
unsigned flags;
|
||
|
unsigned id;
|
||
|
const char* label;
|
||
|
} var_ref_t;
|
||
|
|
||
|
typedef struct dynRef_str
|
||
|
{
|
||
|
char* label;
|
||
|
unsigned level;
|
||
|
uint8_t vel;
|
||
|
} dynRef_t;
|
||
|
|
||
|
typedef struct score_parse_str
|
||
|
{
|
||
|
csv::handle_t csvH;
|
||
|
|
||
|
double srate;
|
||
|
|
||
|
dyn_ref_tbl::handle_t dynRefH;
|
||
|
|
||
|
set_t* begSetL;
|
||
|
set_t* endSetL;
|
||
|
|
||
|
section_t* sectionL;
|
||
|
|
||
|
unsigned eventAllocN;
|
||
|
unsigned eventN;
|
||
|
event_t* eventA;
|
||
|
|
||
|
} score_parse_t;
|
||
|
|
||
|
label_id_t _opcode_ref[] = {
|
||
|
{ kBarTId, "bar" },
|
||
|
{ kSectionTId, "sec" },
|
||
|
{ kBpmTId, "bpm" },
|
||
|
{ kNoteOnTId, "non" },
|
||
|
{ kNoteOffTId, "nof" },
|
||
|
{ kPedalTId, "ped" },
|
||
|
{ kRestTId, "rst" },
|
||
|
{ kCtlTId, "ctl" },
|
||
|
{ kInvalidTId, "<inv>" }
|
||
|
};
|
||
|
|
||
|
var_ref_t _var_ref[] = {
|
||
|
{ kDynVarFl, kDynVarIdx, "d" },
|
||
|
{ kDynVarFl | kSetEndVarFl, kDynVarIdx, "D" },
|
||
|
{ kEvenVarFl, kEvenVarIdx, "e" },
|
||
|
{ kEvenVarFl | kSetEndVarFl, kEvenVarIdx, "E" },
|
||
|
{ kTempoVarFl, kTempoVarIdx, "t" },
|
||
|
{ kTempoVarFl| kSetEndVarFl, kTempoVarIdx, "T" },
|
||
|
{ 0, kInvalidIdx, "<inv>" }
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
score_parse_t* _handleToPtr( handle_t h )
|
||
|
{ return handleToPtr<handle_t,score_parse_t>(h); }
|
||
|
|
||
|
rc_t _destroy( score_parse_t* p )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
|
||
|
|
||
|
section_t* s = p->sectionL;
|
||
|
while( s != nullptr )
|
||
|
{
|
||
|
section_t* s0 = s->link;
|
||
|
mem::release(s->label);
|
||
|
mem::release(s->setA);
|
||
|
mem::release(s);
|
||
|
s = s0;
|
||
|
}
|
||
|
|
||
|
set_t* set = p->begSetL;
|
||
|
while( set!=nullptr )
|
||
|
{
|
||
|
set_t* s0 = set->link;
|
||
|
mem::release(set->eventA);
|
||
|
mem::release(set);
|
||
|
set = s0;
|
||
|
}
|
||
|
|
||
|
|
||
|
for(unsigned i=0; i<p->eventN; ++i)
|
||
|
mem::release(p->eventA[i].sciPitch);
|
||
|
|
||
|
mem::release(p->eventA);
|
||
|
mem::release(p);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_bar_row( score_parse_t* p, csv::handle_t csvH, event_t* e )
|
||
|
{
|
||
|
rc_t rc;
|
||
|
|
||
|
if((rc = getv(csvH,"bar",e->barNumb)) != kOkRC )
|
||
|
rc = cwLogError(rc,"Bar row parse failed.");
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
event_t* _hash_to_event( score_parse_t* p, unsigned hash )
|
||
|
{
|
||
|
for(unsigned i=0; i<p->eventN; ++i)
|
||
|
if( p->eventA[i].hash == hash )
|
||
|
return p->eventA + i;
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
section_t* _find_section( score_parse_t* p, const char* sectionLabel )
|
||
|
{
|
||
|
section_t* s = p->sectionL;
|
||
|
for(; s!=nullptr; s=s->link)
|
||
|
if( textIsEqual(s->label,sectionLabel) )
|
||
|
return s;
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
section_t* _find_or_create_section( score_parse_t* p, const char* sectionLabel )
|
||
|
{
|
||
|
section_t* s;
|
||
|
if((s = _find_section(p,sectionLabel)) == nullptr )
|
||
|
{
|
||
|
s = mem::allocZ<section_t>();
|
||
|
|
||
|
s->label = mem::duplStr(sectionLabel);
|
||
|
s->link = p->sectionL;
|
||
|
p->sectionL = s;
|
||
|
}
|
||
|
|
||
|
//if( s != nullptr )
|
||
|
// printf("Section:%s\n",s->label);
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_section_row( score_parse_t* p, csv::handle_t csvH, event_t* e )
|
||
|
{
|
||
|
rc_t rc;
|
||
|
const char* sectionLabel = nullptr;
|
||
|
|
||
|
if((rc = getv(csvH,"section",sectionLabel)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Section row parse failed.");
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
if((e->section = _find_or_create_section(p,sectionLabel)) == nullptr )
|
||
|
{
|
||
|
rc = cwLogError(kOpFailRC,"Section find/create failed for section: '%s'.",cwStringNullGuard(sectionLabel));
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
e->section->csvRowNumb = e->csvRowNumb;
|
||
|
|
||
|
errLabel:
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_bpm_row( score_parse_t* p, csv::handle_t csvH, event_t* e )
|
||
|
{
|
||
|
rc_t rc;
|
||
|
|
||
|
if((rc = getv(csvH,"bpm",e->bpm)) != kOkRC )
|
||
|
rc = cwLogError(rc,"BPM row parse failed.");
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_advance_to_var_label_section( const char*& text )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
|
||
|
// the label must have at least 3 characters to include both the flag character, a space, and a section identifier
|
||
|
if( textLength(text) < 3 )
|
||
|
{
|
||
|
text += textLength(text);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
if((text = nextWhiteChar(text)) != nullptr )
|
||
|
if((text = nextNonWhiteChar(text)) != nullptr)
|
||
|
if( textLength(text) > 0)
|
||
|
goto errLabel;
|
||
|
|
||
|
rc = cwLogError(kSyntaxErrorRC,"Parse of var target section failed on '%s'.",cwStringNullGuard(text));
|
||
|
|
||
|
errLabel:
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_var_label( score_parse_t* p, const char* text, unsigned varIdx, unsigned varFlag, event_t* e )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
section_t* section = nullptr;
|
||
|
|
||
|
if( textLength(text) == 0 )
|
||
|
return rc;
|
||
|
|
||
|
unsigned flags = var_char_to_flags(text);
|
||
|
|
||
|
e->varA[ varIdx ].flags = flags;
|
||
|
|
||
|
if( !cwIsFlag(flags,varFlag) )
|
||
|
{
|
||
|
rc = cwLogError(kSyntaxErrorRC,"Unknown attribute flag '%s'. Expected: '%s'.",text,var_flags_to_char(varFlag));
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
// The last set in a group will have a target section id appended to the character identifier
|
||
|
if((rc = _parse_advance_to_var_label_section(text)) != kOkRC )
|
||
|
goto errLabel;
|
||
|
|
||
|
// if this set has a target section id
|
||
|
if( textLength(text) > 0 )
|
||
|
{
|
||
|
if((section = _find_or_create_section(p, text )) == nullptr )
|
||
|
{
|
||
|
rc = cwLogError(kOpFailRC,"The var target section find/create failed.");
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
e->varA[ varIdx ].target_section = section;
|
||
|
e->varA[ varIdx ].flags |= kSetEndVarFl; // if a target section was included then this is also the 'end' id in the set.
|
||
|
}
|
||
|
errLabel:
|
||
|
return rc;
|
||
|
|
||
|
}
|
||
|
|
||
|
rc_t _parse_note_on_row( score_parse_t* p, csv::handle_t csvH, event_t* e )
|
||
|
{
|
||
|
rc_t rc,rc0,rc1,rc2;
|
||
|
const char* sciPitch = nullptr;
|
||
|
const char* dmark = nullptr;
|
||
|
const char* graceLabel = nullptr;
|
||
|
const char* tieLabel = nullptr;
|
||
|
const char* onsetLabel = nullptr;
|
||
|
const char* dynLabel = nullptr;
|
||
|
const char* evenLabel = nullptr;
|
||
|
const char* tempoLabel = nullptr;
|
||
|
const char* oloc = nullptr;
|
||
|
|
||
|
if((rc = getv(csvH,
|
||
|
"oloc", oloc,
|
||
|
"rval", e->rval,
|
||
|
"dots", e->dotCnt,
|
||
|
"sci_pitch",sciPitch,
|
||
|
"dmark",dmark,
|
||
|
"status",e->status,
|
||
|
"d0", e->d0,
|
||
|
"d1", e->d1,
|
||
|
"grace", graceLabel,
|
||
|
"tie", tieLabel,
|
||
|
"onset", onsetLabel,
|
||
|
"dyn", dynLabel,
|
||
|
"even", evenLabel,
|
||
|
"tempo", tempoLabel)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Error parsing CSV note-on row.");
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
if( textLength(oloc) > 0 )
|
||
|
if((rc = string_to_number(oloc,e->oloc)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Error converting oloc (%s) to number.",oloc);
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
rc0 = _parse_var_label(p, dynLabel, kDynVarIdx, kDynVarFl, e );
|
||
|
rc1 = _parse_var_label(p, evenLabel, kEvenVarIdx, kEvenVarFl, e );
|
||
|
rc2 = _parse_var_label(p, tempoLabel,kTempoVarIdx, kTempoVarFl, e );
|
||
|
|
||
|
if( textIsEqual(graceLabel,"g") )
|
||
|
e->flags |= kGraceFl;
|
||
|
|
||
|
if( textIsEqual(tieLabel,"t") )
|
||
|
e->flags |= kTieBegFl;
|
||
|
|
||
|
if( textIsEqual(tieLabel,"_") )
|
||
|
e->flags |= kTieContinueFl;
|
||
|
|
||
|
if( textIsEqual(tieLabel,"T") )
|
||
|
e->flags |= kTieEndFl;
|
||
|
|
||
|
if( textIsEqual(onsetLabel, "o" ))
|
||
|
e->flags |= kOnsetFl;
|
||
|
|
||
|
if( sciPitch != nullptr )
|
||
|
e->sciPitch = mem::duplStr(sciPitch);
|
||
|
|
||
|
if( e->d1 > 0 )
|
||
|
if((e->dynLevel = marker_to_level(p->dynRefH,dmark)) == kInvalidIdx )
|
||
|
{
|
||
|
rc = cwLogError(kSyntaxErrorRC,"An invalid dynamic mark (%s) was encountered.",cwStringNullGuard(dmark));
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
if( cwIsFlag(e->flags,kOnsetFl) )
|
||
|
{
|
||
|
if( cwIsFlag(e->flags,kTieContinueFl | kTieEndFl) )
|
||
|
{
|
||
|
rc = cwLogError(kSyntaxErrorRC,"The '%s' event has both an onset flag and tie continue/end flag.",cwStringNullGuard(sciPitch));
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( e->varA[kDynVarIdx].flags )
|
||
|
{
|
||
|
rc = cwLogError(kSyntaxErrorRC,"The '%s' event has no onset flag but is included in a dynamics set.",cwStringNullGuard(sciPitch));
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
errLabel:
|
||
|
return rcSelect(rc,rc0,rc1,rc2);
|
||
|
}
|
||
|
|
||
|
rc_t _parse_note_off_row( score_parse_t* p, csv::handle_t csvH, event_t* e )
|
||
|
{
|
||
|
rc_t rc;
|
||
|
|
||
|
if((rc = getv(csvH,
|
||
|
"status",e->status,
|
||
|
"d0",e->d0,
|
||
|
"d1",e->d1)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Error parsing CSV note-off row.");
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_pedal_row( score_parse_t* p, csv::handle_t csvH, event_t* e )
|
||
|
{
|
||
|
rc_t rc;
|
||
|
|
||
|
if((rc = getv(csvH,
|
||
|
"status",e->status,
|
||
|
"d0",e->d0,
|
||
|
"d1",e->d1)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Error parsing CSV pedal row.");
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_ctl_row( score_parse_t* p, csv::handle_t csvH, event_t* e )
|
||
|
{
|
||
|
rc_t rc;
|
||
|
|
||
|
if((rc = getv(csvH,
|
||
|
"status",e->status,
|
||
|
"d0",e->d0,
|
||
|
"d1",e->d1)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Error parsing CSV ctl row.");
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_events( score_parse_t* p, csv::handle_t csvH )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
section_t* cur_section = nullptr;
|
||
|
unsigned cur_bar_numb = 1;
|
||
|
unsigned bar_evt_idx = 0;
|
||
|
unsigned barPitchCntV[ midi::kMidiNoteCnt ];
|
||
|
vop::zero(barPitchCntV,midi::kMidiNoteCnt);
|
||
|
|
||
|
while((rc = next_line(csvH)) == kOkRC )
|
||
|
{
|
||
|
const char* opcodeLabel = nullptr;
|
||
|
|
||
|
if( p->eventN >= p->eventAllocN )
|
||
|
{
|
||
|
rc = cwLogError(kBufTooSmallRC,"Event array full.");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
event_t* e = p->eventA + p->eventN;
|
||
|
|
||
|
e->oloc = kInvalidIdx;
|
||
|
|
||
|
if((rc = getv(csvH,
|
||
|
"opcode",opcodeLabel,
|
||
|
"index", e->csvId,
|
||
|
"voice", e->voice,
|
||
|
"loc", e->loc,
|
||
|
"loctn", e->loctn,
|
||
|
"tick", e->tick,
|
||
|
"sec", e->sec )) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Error parsing score CSV row." );
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
e->csvRowNumb = cur_line_index(csvH) + 1;
|
||
|
e->opId = opcode_label_to_id(opcodeLabel);
|
||
|
|
||
|
switch( e->opId )
|
||
|
{
|
||
|
case kBarTId:
|
||
|
if((rc = _parse_bar_row(p, csvH, e )) == kOkRC )
|
||
|
{
|
||
|
cur_bar_numb = e->barNumb;
|
||
|
bar_evt_idx = 0;
|
||
|
vop::zero(barPitchCntV,midi::kMidiNoteCnt);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case kSectionTId:
|
||
|
if((rc = _parse_section_row(p, csvH, e )) == kOkRC )
|
||
|
{
|
||
|
if( cur_section == nullptr )
|
||
|
for(event_t* e0 = e-1; e0 >= p->eventA; --e0)
|
||
|
e0->section = e->section;
|
||
|
|
||
|
cur_section = e->section;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case kBpmTId:
|
||
|
rc = _parse_bpm_row(p,csvH, e );
|
||
|
break;
|
||
|
|
||
|
case kNoteOnTId:
|
||
|
if((rc = _parse_note_on_row(p,csvH,e)) == kOkRC )
|
||
|
{
|
||
|
e->barPitchIdx = barPitchCntV[e->d0];
|
||
|
|
||
|
unsigned hash = form_hash(e->opId,cur_bar_numb,e->d0,e->barPitchIdx);
|
||
|
if( _hash_to_event(p,hash) != nullptr )
|
||
|
{
|
||
|
rc = cwLogError(kInvalidStateRC,"The event hash '%x' is is duplicated.",hash);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
e->hash = hash;
|
||
|
barPitchCntV[e->d0] += 1;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case kNoteOffTId:
|
||
|
rc = _parse_note_off_row(p,csvH,e);
|
||
|
break;
|
||
|
|
||
|
case kPedalTId:
|
||
|
rc = _parse_ctl_row(p,csvH,e);
|
||
|
break;
|
||
|
|
||
|
case kRestTId:
|
||
|
break;
|
||
|
|
||
|
case kCtlTId:
|
||
|
rc = _parse_ctl_row(p,csvH,e);
|
||
|
break;
|
||
|
|
||
|
case kInvalidTId:
|
||
|
rc = cwLogError(kInvalidIdRC,"Invalid opocde '%s'.", cwStringNullGuard(opcodeLabel));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
rc = cwLogError(kInvalidIdRC,"Unknown opocde '%s'.", cwStringNullGuard(opcodeLabel));
|
||
|
}
|
||
|
|
||
|
if( rc != kOkRC )
|
||
|
break;
|
||
|
|
||
|
e->section = cur_section;
|
||
|
e->barNumb = cur_bar_numb;
|
||
|
e->barEvtIdx = bar_evt_idx++;
|
||
|
p->eventN += 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
errLabel:
|
||
|
switch( rc )
|
||
|
{
|
||
|
case kOkRC:
|
||
|
assert(0);
|
||
|
break;
|
||
|
|
||
|
case kEofRC:
|
||
|
rc = kOkRC;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
cwLogError(rc,"CSV parse event failed on row:%i.", cur_line_index(csvH)+1 );
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc_t _parse_csv( score_parse_t* p, const char* fname )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
const char* titleA[] = { "opcode","meas","index","voice","loc","loctn","oloc","tick","sec",
|
||
|
"dur","rval","dots","sci_pitch","dmark","dlevel","status","d0","d1",
|
||
|
"bar","section","bpm","grace","tie","onset","pedal","dyn","even","tempo" };
|
||
|
|
||
|
unsigned titleN = sizeof(titleA)/sizeof(titleA[0]);
|
||
|
csv::handle_t csvH;
|
||
|
|
||
|
// open the CSV file and validate the title row
|
||
|
if((rc = create( csvH, fname, titleA, titleN )) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Score CSV parse failed on '%s'.",fname);
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
// get the line count from the CSV file
|
||
|
if((rc = line_count(csvH,p->eventAllocN)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Score CSV line count failed.");
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
// allocate the event array
|
||
|
p->eventA = mem::allocZ<event_t>(p->eventAllocN);
|
||
|
|
||
|
// parse the events
|
||
|
if((rc = _parse_events(p, csvH )) != kOkRC )
|
||
|
goto errLabel;
|
||
|
|
||
|
errLabel:
|
||
|
if(rc != kOkRC )
|
||
|
rc = cwLogError(rc,"CSV parse failed on '%s'.",fname);
|
||
|
destroy(csvH);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
set_t* _find_set( score_parse_t* p, unsigned setId )
|
||
|
{
|
||
|
for(set_t* set=p->begSetL; set!=nullptr; set=set->link)
|
||
|
if( set->id == setId )
|
||
|
return set;
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
set_t* _find_or_create_set( score_parse_t* p, unsigned setId, unsigned varTypeId )
|
||
|
{
|
||
|
set_t* set;
|
||
|
if((set = _find_set(p,setId)) == nullptr )
|
||
|
{
|
||
|
set = mem::allocZ<set_t>();
|
||
|
set->id = setId;
|
||
|
set->varTypeId = varTypeId;
|
||
|
|
||
|
if( p->endSetL == nullptr )
|
||
|
{
|
||
|
p->endSetL = set;
|
||
|
p->begSetL = set;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p->endSetL->link = set;
|
||
|
p->endSetL = set;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
void _create_sets( score_parse_t* p )
|
||
|
{
|
||
|
set_t* cur_set = nullptr;
|
||
|
unsigned setId = 0;
|
||
|
unsigned setNoteIdx = 0;
|
||
|
unsigned endLoc = kInvalidIdx;
|
||
|
|
||
|
for(unsigned vi=0; vi<kVarCnt; ++vi)
|
||
|
for(unsigned ei=0; ei<p->eventN; ++ei)
|
||
|
{
|
||
|
event_t* e = p->eventA + ei;
|
||
|
|
||
|
// if an end evt has been located for this set
|
||
|
// and this loc is past the loc of the end event
|
||
|
// then the set is complete
|
||
|
// (this handles the case where there are multiple events
|
||
|
// on the same end set location)
|
||
|
if( endLoc != kInvalidIdx && (e->loctn > endLoc || ei==p->eventN-1) )
|
||
|
{
|
||
|
cur_set->eventA = mem::allocZ<event_t*>(cur_set->eventN);
|
||
|
setId += 1;
|
||
|
setNoteIdx = 0;
|
||
|
cur_set = nullptr;
|
||
|
endLoc = kInvalidIdx;
|
||
|
}
|
||
|
|
||
|
if( e->varA[vi].flags != 0 )
|
||
|
{
|
||
|
|
||
|
if( cur_set == nullptr )
|
||
|
cur_set = _find_or_create_set(p,setId,vi);
|
||
|
|
||
|
e->varA[vi].set = cur_set;
|
||
|
e->varA[vi].setNoteIdx = setNoteIdx++;
|
||
|
cur_set->eventN += 1;
|
||
|
|
||
|
if( cwIsFlag(e->varA[vi].flags,kSetEndVarFl) )
|
||
|
endLoc = e->loctn;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void _fill_sets( score_parse_t* p )
|
||
|
{
|
||
|
for(unsigned ei=0; ei<p->eventN; ++ei)
|
||
|
for(unsigned vi=0; vi<kVarCnt; ++vi)
|
||
|
if( p->eventA[ei].varA[vi].set != nullptr )
|
||
|
{
|
||
|
event_t* e = p->eventA + ei;
|
||
|
|
||
|
assert( e->varA[vi].setNoteIdx < e->varA[vi].set->eventN );
|
||
|
|
||
|
e->varA[vi].set->eventA[ e->varA[vi].setNoteIdx ] = e;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
rc_t _validate_sets( score_parse_t* p )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
for(set_t* set = p->begSetL; set!=nullptr; set=set->link)
|
||
|
{
|
||
|
unsigned loc[] = { kInvalidIdx, kInvalidIdx, kInvalidIdx };
|
||
|
unsigned locN = sizeof(loc)/sizeof(loc[0]);
|
||
|
unsigned loc_i = 0;
|
||
|
unsigned csvRowNumb = -1;
|
||
|
unsigned barNumb = -1;
|
||
|
|
||
|
// for each event in this set.
|
||
|
for(unsigned i=0; i<set->eventN; ++i)
|
||
|
{
|
||
|
if( set->eventA[i] == nullptr )
|
||
|
{
|
||
|
rc = cwLogError(kInvalidStateRC,"The set %i of type %i section:%s.\n", set->id, set->varTypeId, set->targetSection->label );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// track the last valid csv row numb
|
||
|
csvRowNumb = set->eventA[i]->csvRowNumb;
|
||
|
barNumb = set->eventA[i]->barNumb;
|
||
|
|
||
|
// track the number of locations the events in this set occur at
|
||
|
if( loc_i < locN )
|
||
|
{
|
||
|
unsigned j;
|
||
|
for(j=0; j<loc_i; ++j)
|
||
|
if( loc[j] == set->eventA[i]->loctn )
|
||
|
break;
|
||
|
|
||
|
if( j == loc_i )
|
||
|
loc[ loc_i++ ] = set->eventA[i]->loctn;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 'even' and 'tempo' sets must have at least three events
|
||
|
if( set->varTypeId == kEvenVarIdx && loc_i < locN )
|
||
|
{
|
||
|
rc = cwLogError(kSyntaxErrorRC,"The 'even' set %i (CSV row:%i bar:%i) of type %i does must have at least 3 events at different locations.",set->id,csvRowNumb,barNumb,set->varTypeId);
|
||
|
}
|
||
|
|
||
|
if( set->varTypeId == kTempoVarIdx && loc_i < 2 )
|
||
|
{
|
||
|
rc = cwLogError(kSyntaxErrorRC,"The 'tempo' set %i (CSV row:%i bar:%i) of type %i does must have at least 2 events at different locations.",set->id,csvRowNumb,barNumb,set->varTypeId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( rc != kOkRC )
|
||
|
rc = cwLogError(rc,"Var set validation failed.");
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
void _fill_target_sections( score_parse_t* p )
|
||
|
{
|
||
|
|
||
|
for(unsigned vi=0; vi<kVarCnt; ++vi)
|
||
|
{
|
||
|
section_t* cur_sec = nullptr;
|
||
|
|
||
|
for(int ei=(int)p->eventN; ei>=0; --ei)
|
||
|
if( p->eventA[ei].varA[vi].set != nullptr )
|
||
|
if( cwIsFlag(p->eventA[ei].varA[vi].flags,kSetEndVarFl) )
|
||
|
{
|
||
|
if( p->eventA[ei].varA[vi].target_section == nullptr )
|
||
|
p->eventA[ei].varA[vi].target_section = cur_sec;
|
||
|
else
|
||
|
cur_sec = p->eventA[ei].varA[vi].target_section;
|
||
|
|
||
|
p->eventA[ei].varA[vi].set->sectionSetIdx = cur_sec->setN;
|
||
|
p->eventA[ei].varA[vi].set->targetSection = cur_sec;
|
||
|
|
||
|
cur_sec->setN += 1;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void _fill_sections( score_parse_t* p )
|
||
|
{
|
||
|
// allocate memory to hold the set ptr arrays in each section
|
||
|
for(section_t* s = p->sectionL; s!=nullptr; s=s->link)
|
||
|
s->setA = mem::allocZ<set_t*>(s->setN);
|
||
|
|
||
|
// fill the section->setA[] ptrs
|
||
|
for(set_t* set = p->begSetL; set!=nullptr; set=set->link)
|
||
|
{
|
||
|
assert( set->sectionSetIdx < set->targetSection->setN );
|
||
|
set->targetSection->setA[ set->sectionSetIdx ] = set;
|
||
|
|
||
|
// set the section beg/end events
|
||
|
if( set->targetSection->begEvent == nullptr )
|
||
|
{
|
||
|
set->targetSection->begEvent = set->eventA[0];
|
||
|
set->targetSection->endEvent = set->eventA[ set->eventN-1 ];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( set->eventA[0]->sec < set->targetSection->begEvent->sec )
|
||
|
set->targetSection->begEvent = set->eventA[0];
|
||
|
|
||
|
if( set->eventA[set->eventN-1]->sec > set->targetSection->endEvent->sec )
|
||
|
set->targetSection->endEvent = set->eventA[ set->eventN-1 ];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool _compare_sections(const section_t* sec0, const section_t* sec1)
|
||
|
{
|
||
|
return sec0->csvRowNumb < sec1->csvRowNumb;
|
||
|
}
|
||
|
|
||
|
void _sort_sections( score_parse_t* p )
|
||
|
{
|
||
|
// get count of sections
|
||
|
unsigned secN = 0;
|
||
|
for(section_t* s=p->sectionL; s!=nullptr; s=s->link)
|
||
|
++secN;
|
||
|
|
||
|
// load secA[] with sections
|
||
|
section_t** secA = mem::allocZ<section_t*>(secN);
|
||
|
unsigned i = 0;
|
||
|
for(section_t* s=p->sectionL; s!=nullptr; s=s->link)
|
||
|
secA[i++] = s;
|
||
|
|
||
|
// sort the sections
|
||
|
std::sort( secA, secA+secN, _compare_sections);
|
||
|
|
||
|
// rebuild the section list in order
|
||
|
section_t* begSec = nullptr;
|
||
|
section_t* endSec = nullptr;
|
||
|
|
||
|
for(i=0; i<secN; ++i)
|
||
|
{
|
||
|
secA[i]->link = nullptr;
|
||
|
|
||
|
if( begSec == nullptr )
|
||
|
{
|
||
|
begSec = secA[i];
|
||
|
endSec = secA[i];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
endSec->link = secA[i];
|
||
|
endSec = secA[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p->sectionL = begSec;
|
||
|
|
||
|
mem::release(secA);
|
||
|
}
|
||
|
|
||
|
rc_t _validate_sections( score_parse_t* p )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
|
||
|
section_t* s0 = nullptr;
|
||
|
|
||
|
for(section_t* s=p->sectionL; s!=nullptr; s = s->link)
|
||
|
{
|
||
|
if( s->setN == 0 )
|
||
|
{
|
||
|
cwLogWarning("The section '%s' does not have any sets assigned to it.",cwStringNullGuard(s->label));
|
||
|
}
|
||
|
else
|
||
|
if( s->begEvent == nullptr || s->endEvent == nullptr )
|
||
|
{
|
||
|
rc = cwLogError(kInvalidStateRC,"The section '%s' does not beg/end events.",cwStringNullGuard(s->label));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if( s0 != nullptr && (textCompare( s0->label, s->label ) >= 0 || s0->csvRowNumb > s->csvRowNumb ))
|
||
|
{
|
||
|
rc = cwLogError(kInvalidStateRC,"The section label '%s' is out of order with '%s'.",cwStringNullGuard(s->label),cwStringNullGuard(s0->label));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
s0 = s;
|
||
|
}
|
||
|
|
||
|
if( rc != kOkRC )
|
||
|
rc = cwLogError(rc,"Section validation failed.");
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
void _var_print( const event_t* e, unsigned varId, char* text, unsigned textCharN )
|
||
|
{
|
||
|
if( e->varA[varId].set == nullptr )
|
||
|
snprintf(text,textCharN,"%s"," ");
|
||
|
else
|
||
|
{
|
||
|
snprintf(text,textCharN,"%3s-%03i-%02i %4s",var_flags_to_char(e->varA[varId].flags),
|
||
|
e->varA[varId].set->id,
|
||
|
e->varA[varId].setNoteIdx,
|
||
|
e->varA[varId].flags & kSetEndVarFl ? e->varA[varId].set->targetSection->label : " ");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char* cw::score_parse::opcode_id_to_label( unsigned opId )
|
||
|
{
|
||
|
for(unsigned i=0; _opcode_ref[i].id != kInvalidTId; ++i)
|
||
|
if( _opcode_ref[i].id == opId )
|
||
|
return _opcode_ref[i].label;
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
unsigned cw::score_parse::opcode_label_to_id( const char* label )
|
||
|
{
|
||
|
for(unsigned i=0; _opcode_ref[i].id != kInvalidTId; ++i)
|
||
|
if( textIsEqual(_opcode_ref[i].label,label) )
|
||
|
return _opcode_ref[i].id;
|
||
|
return kInvalidTId;
|
||
|
}
|
||
|
|
||
|
unsigned cw::score_parse::var_char_to_flags( const char* label )
|
||
|
{
|
||
|
for(unsigned i=0; _var_ref[i].flags != 0; ++i)
|
||
|
if( textIsEqual(_var_ref[i].label,label,1) )
|
||
|
return _var_ref[i].flags;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const char* cw::score_parse::var_flags_to_char( unsigned flags )
|
||
|
{
|
||
|
for(unsigned i=0; _var_ref[i].flags != 0; ++i)
|
||
|
if( _var_ref[i].flags == flags )
|
||
|
return _var_ref[i].label;
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
const char* cw::score_parse::var_index_to_char( unsigned var_idx )
|
||
|
{
|
||
|
for(unsigned i=0; _var_ref[i].flags != 0; ++i)
|
||
|
if( _var_ref[i].id == var_idx )
|
||
|
return _var_ref[i].label;
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
const char* cw::score_parse::dyn_ref_level_to_label( handle_t h, unsigned level )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
return level_to_marker( p->dynRefH, level );
|
||
|
}
|
||
|
|
||
|
unsigned cw::score_parse::dyn_ref_label_to_level( handle_t h, const char* label )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
return marker_to_level( p->dynRefH, label );
|
||
|
}
|
||
|
|
||
|
|
||
|
unsigned cw::score_parse::form_hash( unsigned op_id, unsigned bar, uint8_t midi_pitch, unsigned barPitchIdx )
|
||
|
{
|
||
|
unsigned hash = 0;
|
||
|
|
||
|
assert(barPitchIdx < 256 && bar < 0x7ff && op_id < 0xf );
|
||
|
|
||
|
hash += (barPitchIdx & 0x000000ff);
|
||
|
hash += (midi_pitch & 0x000000ff) << 8;
|
||
|
hash += (bar & 0x00000fff) << 16;
|
||
|
hash += (op_id & 0x0000000f) << 28;
|
||
|
|
||
|
return hash;
|
||
|
}
|
||
|
|
||
|
void cw::score_parse::parse_hash( unsigned hash, unsigned& op_idRef, unsigned& barRef, uint8_t& midi_pitchRef, unsigned& barPitchIdxRef )
|
||
|
{
|
||
|
barPitchIdxRef = hash & 0x000000ff;
|
||
|
midi_pitchRef = (hash & 0x0000ff00) >> 8;
|
||
|
barRef = (hash & 0x0fff0000) >> 16;
|
||
|
op_idRef = (hash & 0xf0000000) >> 28;
|
||
|
}
|
||
|
|
||
|
|
||
|
cw::rc_t cw::score_parse::create( handle_t& hRef, const char* fname, double srate, dyn_ref_tbl::handle_t dynRefH )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
score_parse_t* p = nullptr;
|
||
|
|
||
|
if((rc = destroy(hRef)) != kOkRC )
|
||
|
return rc;
|
||
|
|
||
|
p = mem::allocZ<score_parse_t>();
|
||
|
|
||
|
p->srate = srate;
|
||
|
p->dynRefH = dynRefH;
|
||
|
|
||
|
if((rc = _parse_csv(p,fname)) != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"CSV parse failed.");
|
||
|
goto errLabel;
|
||
|
}
|
||
|
|
||
|
_create_sets(p);
|
||
|
|
||
|
_fill_sets(p);
|
||
|
|
||
|
if((rc = _validate_sets(p)) != kOkRC )
|
||
|
goto errLabel;
|
||
|
|
||
|
_fill_target_sections(p);
|
||
|
|
||
|
_fill_sections(p);
|
||
|
|
||
|
_sort_sections(p);
|
||
|
|
||
|
if((rc = _validate_sections(p)) != kOkRC )
|
||
|
goto errLabel;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
hRef.set(p);
|
||
|
|
||
|
errLabel:
|
||
|
if( rc != kOkRC )
|
||
|
{
|
||
|
rc = cwLogError(rc,"Score parse failed on '%s'.",cwStringNullGuard(fname));
|
||
|
_destroy(p);
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
cw::rc_t cw::score_parse::destroy( handle_t& hRef )
|
||
|
{
|
||
|
rc_t rc = kOkRC;
|
||
|
if( !hRef.isValid() )
|
||
|
return rc;
|
||
|
|
||
|
score_parse_t* p = _handleToPtr(hRef);
|
||
|
|
||
|
if((rc = _destroy(p)) != kOkRC )
|
||
|
return rc;
|
||
|
|
||
|
hRef.clear();
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
double cw::score_parse::sample_rate( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
return p->srate;
|
||
|
}
|
||
|
|
||
|
unsigned cw::score_parse::event_count( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
return p->eventN;
|
||
|
}
|
||
|
|
||
|
const cw::score_parse::event_t* cw::score_parse::event_array( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
return p->eventA;
|
||
|
}
|
||
|
|
||
|
unsigned cw::score_parse::section_count( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
unsigned n = 0;
|
||
|
for(section_t* s = p->sectionL; s!=nullptr; s=s->link)
|
||
|
++n;
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
const cw::score_parse::section_t* cw::score_parse::section_list( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
return p->sectionL;
|
||
|
}
|
||
|
|
||
|
unsigned cw::score_parse::set_count( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
unsigned n = 0;
|
||
|
for(set_t* s = p->begSetL; s!=nullptr; s=s->link)
|
||
|
++n;
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
const cw::score_parse::set_t* cw::score_parse::set_list( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
return p->begSetL;
|
||
|
}
|
||
|
|
||
|
void cw::score_parse::report( handle_t h )
|
||
|
{
|
||
|
score_parse_t* p = _handleToPtr(h);
|
||
|
unsigned textBufN = 255;
|
||
|
char textBuf[ textBufN+1 ];
|
||
|
const char* S = "S:";
|
||
|
const char* B = "B:";
|
||
|
const char* blank = " ";
|
||
|
const unsigned flN = 6;
|
||
|
|
||
|
|
||
|
|
||
|
printf("row op section bar bei voc tick sec rval dot lo c lctn oloc flags bpm stat d0 d1 spich hash \n");
|
||
|
printf("---- --- ------- ----- --- --- ---- ------- ------ --- ----- ----- ----- ----- --- ---- ---- --- ----- --------\n");
|
||
|
|
||
|
for(unsigned i=0; i<p->eventN; ++i)
|
||
|
{
|
||
|
const event_t* e = p->eventA + i;
|
||
|
const char* secLabel = i==0 || !textIsEqual(e->section->label,p->eventA[i-1].section->label) ? S : blank;
|
||
|
const char* barLabel = i==0 || e->barNumb != p->eventA[i-1].barNumb ? B : blank;
|
||
|
|
||
|
unsigned fli = 0;
|
||
|
char flag_str[ flN ] = {0};
|
||
|
|
||
|
if( cwIsFlag(e->flags,kGraceFl)) { flag_str[fli++] = 'g'; }
|
||
|
if( cwIsFlag(e->flags,kTieBegFl)) { flag_str[fli++] = 't'; }
|
||
|
if( cwIsFlag(e->flags,kTieContinueFl)) { flag_str[fli++] = '_'; }
|
||
|
if( cwIsFlag(e->flags,kTieEndFl)) { flag_str[fli++] = 'T'; }
|
||
|
if( cwIsFlag(e->flags,kOnsetFl)) { flag_str[fli++] = 'o'; }
|
||
|
|
||
|
printf("%4i %3s %2s%4s %2s%3i %3i %3i %4i %7.3f %6.3f %3i %5i %5i %5i %5s %3i 0x%02x 0x%02x %3i %5s",
|
||
|
e->csvRowNumb,
|
||
|
opcode_id_to_label(e->opId),
|
||
|
secLabel,
|
||
|
e->section == nullptr || e->section->label==nullptr ? "" : e->section->label ,
|
||
|
barLabel,
|
||
|
e->barNumb,
|
||
|
e->barEvtIdx,
|
||
|
e->voice,
|
||
|
e->tick,
|
||
|
e->sec,
|
||
|
e->rval,
|
||
|
e->dotCnt,
|
||
|
e->loc,
|
||
|
e->loctn,
|
||
|
e->oloc,
|
||
|
flag_str,
|
||
|
e->bpm,
|
||
|
e->status,
|
||
|
e->d0,
|
||
|
e->d1,
|
||
|
e->sciPitch == nullptr ? "" : e->sciPitch);
|
||
|
|
||
|
if( e->hash != 0 )
|
||
|
printf(" %08x",e->hash);
|
||
|
|
||
|
for(unsigned vi = 0; vi<kVarCnt; ++vi)
|
||
|
{
|
||
|
_var_print(e, vi, textBuf, textBufN );
|
||
|
printf(" %s",textBuf);
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|