2024-12-01 19:35:24 +00:00
//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
2023-08-20 21:10:45 +00:00
# include "cwCommon.h"
# include "cwLog.h"
# include "cwCommonImpl.h"
2024-05-29 16:36:57 +00:00
# include "cwTest.h"
2023-08-20 21:10:45 +00:00
# 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 ;
}
2024-02-08 16:05:10 +00:00
rc_t _parse_section_stats ( score_parse_t * p , csv : : handle_t csvH , event_t * e )
{
rc_t rc ;
if ( ( rc = getv ( csvH ,
" even_min " , e - > section - > statsA [ kEvenStatIdx ] . min ,
" even_max " , e - > section - > statsA [ kEvenStatIdx ] . max ,
" even_mean " , e - > section - > statsA [ kEvenStatIdx ] . mean ,
" even_std " , e - > section - > statsA [ kEvenStatIdx ] . std ,
" dyn_min " , e - > section - > statsA [ kDynStatIdx ] . min ,
" dyn_max " , e - > section - > statsA [ kDynStatIdx ] . max ,
" dyn_mean " , e - > section - > statsA [ kDynStatIdx ] . mean ,
" dyn_std " , e - > section - > statsA [ kDynStatIdx ] . std ,
" tempo_min " , e - > section - > statsA [ kTempoStatIdx ] . min ,
" tempo_max " , e - > section - > statsA [ kTempoStatIdx ] . max ,
" tempo_mean " , e - > section - > statsA [ kTempoStatIdx ] . mean ,
" tempo_std " , e - > section - > statsA [ kTempoStatIdx ] . std ,
" cost_min " , e - > section - > statsA [ kCostStatIdx ] . min ,
" cost_max " , e - > section - > statsA [ kCostStatIdx ] . max ,
" cost_mean " , e - > section - > statsA [ kCostStatIdx ] . mean ,
" cost_std " , e - > section - > statsA [ kCostStatIdx ] . std ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error parsing CSV meas. stats field. " ) ;
goto errLabel ;
}
errLabel :
return rc ;
}
2023-08-20 21:10:45 +00:00
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 ;
}
2024-02-08 16:05:10 +00:00
//if((rc = _parse_section_stats(p,csvH,e)) != kOkRC )
// goto errLabel;
2023-08-20 21:10:45 +00:00
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 ;
2023-09-12 21:53:35 +00:00
if ( ( rc = getv ( csvH ,
" bpm " , e - > bpm ,
" rval " , e - > bpm_rval ) ) ! = kOkRC )
{
2023-08-20 21:10:45 +00:00
rc = cwLogError ( rc , " BPM row parse failed. " ) ;
2023-09-12 21:53:35 +00:00
}
2023-08-20 21:10:45 +00:00
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 )
{
2023-12-06 20:22:41 +00:00
rc_t rc , rc0 = kOkRC , rc1 = kOkRC , rc2 = kOkRC ;
2023-08-20 21:10:45 +00:00
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 ;
2023-09-12 21:53:35 +00:00
const char * oLocId = nullptr ;
2023-08-20 21:10:45 +00:00
if ( ( rc = getv ( csvH ,
2023-09-12 21:53:35 +00:00
" oloc " , oLocId ,
2023-08-20 21:10:45 +00:00
" 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 ;
}
2023-09-12 21:53:35 +00:00
if ( textLength ( oLocId ) > 0 )
if ( ( rc = string_to_number ( oLocId , e - > oLocId ) ) ! = kOkRC )
2023-08-20 21:10:45 +00:00
{
2023-09-12 21:53:35 +00:00
rc = cwLogError ( rc , " Error converting oLocId (%s) to number. " , oLocId ) ;
2023-08-20 21:10:45 +00:00
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 ;
}
2023-11-26 20:30:54 +00:00
rc_t _set_hash ( score_parse_t * p , unsigned opId , unsigned barNumb , uint8_t midiPitch , unsigned barPitchIdx , unsigned & hashRef )
{
rc_t rc = kOkRC ;
hashRef = 0 ;
unsigned hash = form_hash ( opId , barNumb , midiPitch , barPitchIdx ) ;
if ( _hash_to_event ( p , hash ) ! = nullptr )
{
rc = cwLogError ( kInvalidStateRC , " The event hash '%x' is is duplicated. " , hash ) ;
goto errLabel ;
}
hashRef = hash ;
errLabel :
return rc ;
}
2023-08-20 21:10:45 +00:00
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 ;
2023-09-12 21:53:35 +00:00
unsigned bpm = 0 ;
double bpm_rval = 0 ;
2023-08-20 21:10:45 +00:00
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 ;
2023-09-12 21:53:35 +00:00
e - > oLocId = kInvalidIdx ;
2023-08-20 21:10:45 +00:00
if ( ( rc = getv ( csvH ,
" opcode " , opcodeLabel ,
" voice " , e - > voice ,
2023-09-12 21:53:35 +00:00
" eloc " , e - > eLocId ,
2023-08-20 21:10:45 +00:00
" 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 ) ;
2023-09-12 21:53:35 +00:00
e - > index = p - > eventN ;
e - > dynLevel = kInvalidIdx ;
2023-08-20 21:10:45 +00:00
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 ) ;
2023-11-26 20:30:54 +00:00
if ( ( rc = _set_hash ( p , e - > opId , cur_bar_numb , 0 , 0 , e - > hash ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error setting event hash for bar line:%i " , cur_bar_numb ) ;
goto errLabel ;
}
//printf("%i : 0x%x\n",cur_bar_numb,e->hash);
2023-08-20 21:10:45 +00:00
}
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 :
2023-09-12 21:53:35 +00:00
if ( ( rc = _parse_bpm_row ( p , csvH , e ) ) = = kOkRC )
{
// if the cur BPM has not yet been set then go backward setting
// all events prior to this to the initial BPM
if ( bpm = = 0 & & e - > bpm ! = 0 )
std : : for_each ( p - > eventA , e , [ e ] ( auto & x ) { x . bpm = e - > bpm ; x . bpm_rval = e - > bpm_rval ; } ) ;
// if the parsed BPM is invalid ...
if ( e - > bpm = = 0 | | e - > bpm_rval = = 0 )
{
e - > bpm = bpm ; // ... then ignore it
e - > bpm_rval = bpm_rval ;
}
else
{
bpm = e - > bpm ; // ... otherwise make it the current BPM
bpm_rval = e - > bpm_rval ;
}
// Be sure that all events on this location have the same BPM
for ( event_t * e0 = e - 1 ; e0 > = p - > eventA & & e0 - > eLocId = = e - > eLocId ; - - e0 )
{
e0 - > bpm = e - > bpm ;
e0 - > bpm_rval = e - > bpm_rval ;
}
}
2023-08-20 21:10:45 +00:00
break ;
case kNoteOnTId :
if ( ( rc = _parse_note_on_row ( p , csvH , e ) ) = = kOkRC )
{
e - > barPitchIdx = barPitchCntV [ e - > d0 ] ;
2023-11-26 20:30:54 +00:00
if ( ( rc = _set_hash ( p , e - > opId , cur_bar_numb , e - > d0 , e - > barPitchIdx , e - > hash ) ) ! = kOkRC )
2023-08-20 21:10:45 +00:00
{
2023-11-26 20:30:54 +00:00
rc = cwLogError ( rc , " Error setting hash for note-on event: bar:%i pitch:%i bpi:%i " , cur_bar_numb , e - > d0 , e - > barPitchIdx ) ;
goto errLabel ;
2023-08-20 21:10:45 +00:00
}
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 + + ;
2023-09-12 21:53:35 +00:00
e - > bpm = bpm ;
e - > bpm_rval = bpm_rval ;
2023-08-20 21:10:45 +00:00
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 ;
2023-09-12 21:53:35 +00:00
const char * titleA [ ] = { " opcode " , " meas " , " index " , " voice " , " loc " , " eloc " , " oloc " , " tick " , " sec " ,
2023-08-20 21:10:45 +00:00
" 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 ;
2023-10-21 14:06:29 +00:00
unsigned endLocId = kInvalidIdx ;
2023-08-20 21:10:45 +00:00
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)
2023-10-21 14:06:29 +00:00
if ( endLocId ! = kInvalidIdx & & ( e - > eLocId > endLocId | | ei = = p - > eventN - 1 ) )
2023-08-20 21:10:45 +00:00
{
cur_set - > eventA = mem : : allocZ < event_t * > ( cur_set - > eventN ) ;
setId + = 1 ;
setNoteIdx = 0 ;
cur_set = nullptr ;
2023-10-21 14:06:29 +00:00
endLocId = kInvalidIdx ;
2023-08-20 21:10:45 +00:00
}
2023-10-21 14:06:29 +00:00
// if this event
2023-08-20 21:10:45 +00:00
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 ) )
2023-10-21 14:06:29 +00:00
endLocId = e - > eLocId ;
2023-08-20 21:10:45 +00:00
}
}
}
2023-10-21 14:06:29 +00:00
2023-08-20 21:10:45 +00:00
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 ;
}
}
2023-10-21 14:06:29 +00:00
unsigned _set_count ( score_parse_t * p )
{
unsigned n = 0 ;
for ( set_t * s = p - > begSetL ; s ! = nullptr ; s = s - > link )
+ + n ;
return n ;
}
void _order_set_ids_by_time ( score_parse_t * p )
{
typedef struct set_order_str
{
unsigned beg_evt_idx ;
set_t * set ;
} set_order_t ;
unsigned setAllocN = _set_count ( p ) ;
unsigned setN = 0 ;
set_order_t * setA = mem : : allocZ < set_order_t > ( setAllocN ) ;
for ( set_t * s = p - > begSetL ; s ! = nullptr ; s = s - > link )
{
if ( s - > eventN > 0 )
{
setA [ setN ] . beg_evt_idx = s - > eventA [ 0 ] - > index ;
setA [ setN ] . set = s ;
setN + = 1 ;
}
}
std : : sort ( setA , setA + setN , [ ] ( auto a , auto b ) { return a . beg_evt_idx < b . beg_evt_idx ; } ) ;
unsigned set_id = 0 ;
std : : for_each ( setA , setA + setN , [ & ] ( auto a ) { a . set - > id = set_id + + ; } ) ;
mem : : release ( setA ) ;
}
2023-08-20 21:10:45 +00:00
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 )
2023-09-12 21:53:35 +00:00
if ( loc [ j ] = = set - > eventA [ i ] - > eLocId )
2023-08-20 21:10:45 +00:00
break ;
if ( j = = loc_i )
2023-09-12 21:53:35 +00:00
loc [ loc_i + + ] = set - > eventA [ i ] - > eLocId ;
2023-08-20 21:10:45 +00:00
}
}
// '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 ;
}
}
}
2023-09-12 21:53:35 +00:00
void _fill_section_sets ( score_parse_t * p )
2023-08-20 21:10:45 +00:00
{
// 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 ) ;
2023-09-12 21:53:35 +00:00
2023-08-20 21:10:45 +00:00
// 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
2023-09-12 21:53:35 +00:00
if ( set - > targetSection - > begSetEvent = = nullptr )
2023-08-20 21:10:45 +00:00
{
2023-09-12 21:53:35 +00:00
set - > targetSection - > begSetEvent = set - > eventA [ 0 ] ;
set - > targetSection - > endSetEvent = set - > eventA [ set - > eventN - 1 ] ;
2023-08-20 21:10:45 +00:00
}
else
{
2023-09-12 21:53:35 +00:00
if ( set - > eventA [ 0 ] - > sec < set - > targetSection - > begSetEvent - > sec )
set - > targetSection - > begSetEvent = set - > eventA [ 0 ] ;
2023-08-20 21:10:45 +00:00
2023-09-12 21:53:35 +00:00
if ( set - > eventA [ set - > eventN - 1 ] - > sec > set - > targetSection - > endSetEvent - > sec )
set - > targetSection - > endSetEvent = set - > eventA [ set - > eventN - 1 ] ;
2023-08-20 21:10:45 +00:00
}
2023-09-12 21:53:35 +00:00
}
}
rc_t _fill_section_beg_end_evt ( score_parse_t * p )
{
rc_t rc = kOkRC ;
for ( unsigned i = 0 ; i < p - > eventN ; + + i )
{
event_t * e = p - > eventA + i ;
assert ( e - > section ! = nullptr ) ;
if ( e - > section - > begEvent = = nullptr | | e - > index < e - > section - > begEvent - > index )
e - > section - > begEvent = e ;
if ( e - > section - > endEvent = = nullptr | | e - > index > e - > section - > endEvent - > index )
e - > section - > endEvent = e ;
2023-08-20 21:10:45 +00:00
}
2023-09-12 21:53:35 +00:00
return rc ;
2023-08-20 21:10:45 +00:00
}
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 ) ;
}
2023-09-12 21:53:35 +00:00
rc_t _validate_sections ( score_parse_t * p , bool show_warnings_fl )
2023-08-20 21:10:45 +00:00
{
rc_t rc = kOkRC ;
section_t * s0 = nullptr ;
for ( section_t * s = p - > sectionL ; s ! = nullptr ; s = s - > link )
{
if ( s - > setN = = 0 )
{
2023-09-12 21:53:35 +00:00
if ( show_warnings_fl )
cwLogWarning ( " The section '%s' does not have any sets assigned to it. " , cwStringNullGuard ( s - > label ) ) ;
2023-08-20 21:10:45 +00:00
}
else
2023-09-12 21:53:35 +00:00
if ( s - > begSetEvent = = nullptr | | s - > endSetEvent = = nullptr )
2023-08-20 21:10:45 +00:00
{
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 ;
}
2023-09-12 21:53:35 +00:00
// verify that there are no event gaps between the sections
if ( p - > sectionL ! = nullptr and p - > sectionL - > link ! = nullptr )
{
s0 = p - > sectionL ;
for ( section_t * s = s0 - > link ; s ! = nullptr ; s = s - > link )
{
if ( s0 - > endEvent = = nullptr )
rc = cwLogError ( kInvalidStateRC , " The section '%s' does not have an end event. " , cwStringNullGuard ( s0 - > label ) ) ;
else
{
if ( s - > begEvent = = nullptr )
rc = cwLogError ( kInvalidStateRC , " The section '%s' does not have a begin event. " , cwStringNullGuard ( s - > label ) ) ;
else
{
if ( s0 - > endEvent - > index + 1 ! = s - > begEvent - > index )
rc = cwLogError ( kInvalidStateRC , " The sections '%s' and '%s' do not begin/end on consecutive events. " , cwStringNullGuard ( s0 - > label ) , cwStringNullGuard ( s - > label ) ) ;
}
}
s0 = s ;
}
}
2023-08-20 21:10:45 +00:00
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 ) ;
}
2023-09-12 21:53:35 +00:00
unsigned cw : : score_parse : : dyn_ref_vel_to_level ( handle_t h , uint8_t vel )
{
score_parse_t * p = _handleToPtr ( h ) ;
return velocity_to_level ( p - > dynRefH , vel ) ;
}
2023-08-20 21:10:45 +00:00
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 ;
}
2023-09-12 21:53:35 +00:00
cw : : rc_t cw : : score_parse : : create ( handle_t & hRef , const char * fname , double srate , dyn_ref_tbl : : handle_t dynRefH , bool show_warnings_fl )
2023-08-20 21:10:45 +00:00
{
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 ) ;
2023-10-21 14:06:29 +00:00
_order_set_ids_by_time ( p ) ;
2023-08-20 21:10:45 +00:00
if ( ( rc = _validate_sets ( p ) ) ! = kOkRC )
goto errLabel ;
_fill_target_sections ( p ) ;
2023-09-12 21:53:35 +00:00
_fill_section_sets ( p ) ;
2023-08-20 21:10:45 +00:00
_sort_sections ( p ) ;
2023-09-12 21:53:35 +00:00
_fill_section_beg_end_evt ( p ) ;
2023-08-20 21:10:45 +00:00
2023-09-12 21:53:35 +00:00
if ( ( rc = _validate_sections ( p , show_warnings_fl ) ) ! = kOkRC )
goto errLabel ;
2023-08-20 21:10:45 +00:00
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 ) ;
2023-10-21 14:06:29 +00:00
return _set_count ( p ) ;
2023-08-20 21:10:45 +00:00
}
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 ;
2023-09-12 21:53:35 +00:00
printf ( " row op section bpm b_rval bar bei voc tick sec rval dot eloc oloc flags bpm stat d0 d1 spich hash \n " ) ;
printf ( " ---- --- ------- --- ------ ----- --- --- ---- ------- ------ --- ----- ----- ----- --- ---- ---- --- ----- -------- \n " ) ;
2023-08-20 21:10:45 +00:00
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 ' ; }
2023-09-12 21:53:35 +00:00
printf ( " %4i %3s %2s%4s %3i %6.4f %2s%3i %3i %3i %4i %7.3f %6.3f %3i %5i %5i %5s %3i 0x%02x 0x%02x %3i %5s " ,
2023-08-20 21:10:45 +00:00
e - > csvRowNumb ,
opcode_id_to_label ( e - > opId ) ,
secLabel ,
e - > section = = nullptr | | e - > section - > label = = nullptr ? " " : e - > section - > label ,
2023-09-12 21:53:35 +00:00
e - > bpm ,
e - > bpm_rval ,
2023-08-20 21:10:45 +00:00
barLabel ,
e - > barNumb ,
e - > barEvtIdx ,
e - > voice ,
e - > tick ,
e - > sec ,
e - > rval ,
e - > dotCnt ,
2023-09-12 21:53:35 +00:00
e - > eLocId ,
e - > oLocId ,
2023-08-20 21:10:45 +00:00
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 " ) ;
}
}