diff --git a/src/cwtest/cfg/gutim_full/scriabin/csv_to_score.py b/src/cwtest/cfg/gutim_full/scriabin/csv_to_score.py index 08832b9..ce168d5 100644 --- a/src/cwtest/cfg/gutim_full/scriabin/csv_to_score.py +++ b/src/cwtest/cfg/gutim_full/scriabin/csv_to_score.py @@ -8,9 +8,15 @@ def parse_score( score_fn ): for r in rdr: d = dict(r) - d['src'] = "gutim" - d['loc'] = int(d['loc']) if d['loc'] else None - d['oloc'] = int(d['oloc']) if d['oloc'] else None + d['src'] = "gutim" + d['loc'] = int(d['loc']) if d['loc'] else None + d['oloc'] = int(d['oloc']) if d['oloc'] else None + d['status'] = int(d['status']) if d['status'] else None + d['d0'] = int(d['d0']) if d['d0'] else None + d['d1'] = int(d['d1']) if d['d1'] else None + d['sec'] = float(d['sec']) + d['opcode'] = d['opcode'].strip() + scoreL.append(d) @@ -36,7 +42,7 @@ def invert_velocity( d1 ): def parse_scriabin( ifn ): - ped_off = { 'dtick':0, 'sec':0, 'type':'ped', 'd0':64, 'd1':0, 'sci_pitch':None } + ped_off = { 'dtick':0, 'sec':0, 'type':'ped', 'd0':64, 'd1':0, 'sci_pitch':None, 'UID':None } noteL = [] @@ -56,7 +62,8 @@ def parse_scriabin( ifn ): 'type': type_label, 'd0': int(r['D0']), 'd1': invert_velocity(int(r['D1'])) if type_label=='non' else int(r['D1']) , - 'sci_pitch': r['sci_pitch'].strip() if 'sci_pitch' in r else None + 'sci_pitch': r['sci_pitch'].strip() if 'sci_pitch' in r else None, + 'UID': int(r['UID']) } noteL.append(x) @@ -123,6 +130,182 @@ def insert_scriabin( scoreL, noteL, src_label, insert_loc, insert_after_fl, delt return outL + +def interleave_scriabin( scoreL, noteL, src_label, beg_score_loc, cut_score_loc, end_scriabin_uid ): + # beg_score_loc: the 'loc' where the inserted scriabin begins + # cut_score_loc: the 'loc' where the inserted scriabin ends + # end_scriabin_uid: the 'uid' which syncronizes to the 'cut_score_loc'. + # + # BSL CSL + # --------------+ +------------------------------ + # | | | + # --------------+ +------------------------------ + # | | + # +-------------------------+ + # | | | + # +-------------------------+ + # ESU + + def scribian_esu_secs( noteL, end_scriabin_uid ): + if end_scriabin_uid is None: + return noteL[-1]['sec'] + + for d in noteL: + if d['UID'] == end_scriabin_uid: + return d['sec'] + assert(0) + + def score_bsl_secs( scoreL, beg_score_loc ): + for d in scoreL: + if d['loc'] == beg_score_loc: + return d['sec'] + assert(0) + + def cut_score_loc_index( scoreL, cut_score_loc ): + for i,d in enumerate(scoreL): + if d['loc'] == beg_score_loc: + return i + assert(0) + + def assign_event_id( scoreL ): + # Set a matching 'eid' for each note-on/off + # and each pedal-dn/up event. + + + def _is_note_on(d): + return d['status'] == 144 and d['d1'] is not None and d['d1']>0 + + def _is_note_off(d): + return d['status'] == 144 and d['d1'] is not None and d['d1']==0 + + def _is_pedal_dn(d): + return d['opcode'] == 'ped' and d['d1'] >= 64 + + def _is_pedal_up(d): + return d['opcode'] == 'ped' and d['d1'] < 64 + + def _set_end_evt_eid(scoreL,i,d,evt_id,func): + for n in scoreL[i:]: + if func(n) and n['d0'] == d['d0']: + n['eid'] = evt_id + return + + assert(0) + + + + evt_id = 0 + for i,d in enumerate(scoreL): + if _is_note_on(d): + _set_end_evt_eid(scoreL,i,d,evt_id,_is_note_off) + + elif _is_pedal_dn(d): + _set_end_evt_eid(scoreL,i,d,evt_id,_is_pedal_up) + + + if 'eid' not in d: + d['eid'] = evt_id; + evt_id += 1 + + + def offset_scriabin( noteL, offset_sec ): + for d in noteL: + d['sec'] += offset_sec + + def offset_score_after_cut( scoreL, csl_index, secs ): + csl_eid = scoreL[csl_index]['eid'] + + # offset all events that start on or after csl_index + # (this will not shift note-off's/pedal-ups that + # are associated with note-on/pedal-dn's which occur + # before 'csl_index'. + + sec0 = None + for d in scoreL[csl_index:]: + + if sec0 is not None: + dsec = d['sec'] - sec0 + secs += dsec + + sec0 = d['sec'] + + if d['eid'] >= csl_eid: + d['sec'] = secs + + + def scriabin_to_score( scoreL, noteL, src_label ): + + status_map = { 'non':144, 'nof':144, 'ped':176, 'ctl':176 } + type_map = { 'non':'non', 'nof':'nof', 'ped':'ctl', 'ctl':'ctl' } + + for n in noteL: + d = {} + d['meas'] = None + d['loc'] = None + d['oloc'] = None + d['sec'] = float(n['sec']) + d['opcode'] = type_map[ n['type'].strip() ] + d['status'] = status_map[n['type'].strip()] + d['d0'] = n['d0'] + d['d1'] = n['d1'] + d['tick'] = n['dtick'] + d['sci_pitch'] = n['sci_pitch'] + d['src'] = src_label + scoreL.append(d) + + return sorted(scoreL,key=lambda x:x['sec']) + + + def score_to_csv_debug(scoreL): + + fieldnames = list(scoreL[0].keys()) + with open("foo.csv","w") as f: + wtr = csv.DictWriter(f,fieldnames) + wtr.writeheader() + for r in scoreL: + wtr.writerow(r) + + + def remove_eid_field(scoreL): + for i,d in enumerate(scoreL): + if 'eid' in d: + del d['eid'] + scoreL[i] = d + + + + # Offset in seconds to be applied to the scriabin to + # sync it's start with 'beg_score_loc' + bsl_secs = score_bsl_secs( scoreL, beg_score_loc ) + + # Score index of the first score note to play + # at the end of the scriabin sequence + csl_index = cut_score_loc_index( scoreL, cut_score_loc ) + + # Assign matching note-on/off events the same evt-id + assign_event_id(scoreL) + + #score_to_csv_debug(scoreL) + + # Shift scriabin to start at the beg_score_loc + offset_scriabin(noteL,bsl_secs) + + # Start location in seconds of the second part of the score. + esu_secs = scribian_esu_secs( noteL, end_scriabin_uid ) + + # Shift the second part of score to begin at 'esu_secs' + # (while not shifting the end events (note-off/ped-up) + # for events which started before the cut) + + offset_score_after_cut( scoreL, csl_index, esu_secs ) + + scoreL = scriabin_to_score( scoreL, noteL, src_label ) + + remove_eid_field( scoreL ) + + return scoreL + + def write_meta_cfg( o_cfg_fn, name, beg_loc, end_loc ): cfg = { "player_name": name, @@ -250,32 +433,25 @@ def gen_reference( scoreL, out_dir, out_fn ): r0 = r -def insert_pedal( scoreL, locL ): +def insert_pedal( scoreL, beg_oloc, end_oloc ): def _ped_msg( secs, d1 ): return { "opcode":"ctl","loc":None,"oloc":None,"sec":secs,"status":176,"d0":64,"d1":d1,"src":"ped" } - - for dn_loc,up_loc in locL: - assert( dn_loc < up_loc ) + state = 'dn' + for d in scoreL: + if state=='dn' and d['oloc'] == beg_oloc: + outL.append( _ped_msg( d['sec'], 64 ) ) + state = 'up' + + + if state =='up' and d['oloc'] == end_oloc: + outL.append( _ped_msg( d['sec'], 0 )) + stae = None + break - outL = [] - - for i,r in enumerate(scoreL): - - if r['oloc'] == dn_loc: - outL.append(_ped_msg(r["secs"],64)) - - if r['oloc'] == up_loc: - outL.append(_ped_msg(r["secs"],0)) + return sorted(scoreL, key=lambda x:x['sec']) - - outL.append(r) - - - - - @@ -396,6 +572,19 @@ if __name__ == "__main__": { "src":"65_3", "ifn":"scriabin_etude_op65_3_molta_vivace.csv", "insert_loc": 13953, "after_fl":True, "ofn":"scriabin_65_3", "beg_loc":6780, "end_loc":6781, "delta_sec":0.0 }, + ] + + # beg_score_loc: score location which syncs w/ scriabin start + # cut_score_loc: location where score starts again + # end_scriabin_uid: uid of scriabin which syncs to 'cut_score_loc'. + fileL = [ + + # * + { "src":"74_1", "ifn":"scriabin_prelude_op74_1.csv", "beg_score_loc":1229, "cut_score_loc":1231, "end_scriabin_uid":740, "ofn":"scriabin_op74_1" }, + + # * + { "src":"74_2", "ifn":"scriabin_prelude_op74_2.csv", "beg_score_loc": 1867, "cut_score_loc":1870, "end_scriabin_uid":628, "ofn":"scriabin_op74_2" }, + ] scoreL = parse_score(score_fn) @@ -408,8 +597,11 @@ if __name__ == "__main__": noteL = parse_scriabin(f['ifn']) - scoreL = insert_scriabin(scoreL, noteL, f['src'], f['insert_loc'], f['after_fl'], f['delta_sec'] ) - + if 'beg_score_loc' in f: + scoreL = interleave_scriabin( scoreL, noteL, f['src'], f['beg_score_loc'],f['cut_score_loc'], f['end_scriabin_uid'] ) + else: + scoreL = insert_scriabin( scoreL, noteL, f['src'], f['insert_loc'], f['after_fl'], f['delta_sec'] ) + assign_loc(scoreL)