-- $NWCUT$CONFIG: FileText $ nwcut.setlevel(2) nwcut.status = nwcut.const.rc_Report nwcut.status = nwcut.const.rc_Succes local progname = 'extract chords' local HelpMsg = [[ This tool mutes notes that are not in the normal range ]] -------------------------------------- -- function lf_n_duration -------------------------------------- local function lf_n_duration (p_text,p_frac,p_base) local lv_n_duration = 0 local lv_n_modduration = 0 if string.find(p_text,'Whole') then lv_n_duration = p_base * 4 * p_frac ; end--if if string.find(p_text,'Half') then lv_n_duration = p_base * 2 ; end--if if string.find(p_text,'4th') then lv_n_duration = p_base ; end--if if string.find(p_text,'8th') then lv_n_duration = p_base / 2 ; end--if if string.find(p_text,'16th') then lv_n_duration = p_base / 4 ; end--if if string.find(p_text,'32nd') then lv_n_duration = p_base / 8 ; end--if if string.find(p_text,'64th') then lv_n_duration = p_base / 16 ; end--if lv_n_modduration = lv_n_duration ; if string.find(p_text,'Triplet') then lv_n_modduration = lv_n_duration * 2 / 3 ; end--if if string.find(p_text,'Dotted') then lv_n_modduration = lv_n_duration * 1.5 ; end--if if string.find(p_text,'Dbl') then lv_n_modduration = lv_n_duration * 1.75 ; end--if if string.find(p_text,'Grace') then lv_n_modduration = 0 ; end--if return lv_n_modduration ; end--lf_n_duration -------------------------------------- -- function lf_n_multibar -------------------------------------- local function lf_n_multibar (p_number,p_frac,p_base) local lv_n_measures = tonumber(p_number) or 0 ; local lv_n_durmbar = p_base * 4 * p_frac * lv_n_measures ; return lv_n_durmbar ; end--lf_n_multibar -------------------------------------- -- function lf_n_timesignature -------------------------------------- local function lf_n_timesignature(p_text) local lv_n_value = 1 local lv_n_number1 = 0 local lv_n_number2 = 0 if string.find(p_text,'Common') then lv_n_value = 1 ; end if string.find(p_text,'AllaBreve') then lv_n_value = 1 ; end if string.find(p_text,'/') then lv_n_number1 = string.match(p_text,'(%d+)/') ; lv_n_number2 = string.match(p_text,'/(%d+)') ; lv_n_value = lv_n_number1 / lv_n_number2 ; end--if return lv_n_value ; end--lf_n_timesignature -------------------------------------- -- Main processing ------------------- -------------------------------------- assert(nwcut.getprop('Mode') == nwcut.const.mode_FileText, "Input type must be 'File Text'") assert(nwcut.getprop('ReturnMode') == nwcut.const.mode_FileText, "Under 'Options', check 'Returns File Text'") lv_m_playContext = nwcPlayContext.new() lv_n_CS = 0 ; -- current staff lv_n_CS = 0 before first staff lv_n_fraction = 1 ; -- default time signature (4/4) lv_n_base = 960 lv_n_ppq1tot = 0 lv_n_ppqsubtot = 0 lv_n_ppq1 = 0 lv_n_ppq2 = 0 lv_n_octaveshift = 0 lv_n_pitchshift = 0 lv_n_midipitch = 0 lv_s_name = "" lv_s_accidental = "" lv_n_octave = 0 lv_s_note = "" lv_t_origitems = {} -- needed because you can only iterate once over nwcut.items lv_t_split = {} -- table with begin/end ppq for every note/chord lv_t_usplit = {} lv_t_chord = {} -- table with type, begin, end, duration, noteinfo (midi+name) lv_t_bars = {} lv_t_ubars = {} lv_t_item = {} lv_t_flags = {} -- help table for deduplication lv_t_skips = {} lv_n_StaffTransp = 0 lv_n_InstrTransp = 0 lv_s_noteprefix = "_" lv_s_check_SkipPerc = 'Skip Percussion Staves' lv_s_check_SkipHidd = 'Skip Hidden Staves' lv_s_check_SkipMute = 'Skip Muted Staves' lv_t_promptlist = {lv_s_check_SkipPerc,lv_s_check_SkipHidd,lv_s_check_SkipMute} lv_t_prompt = nwcut.prompt("Select staff types to be skipped (cancel to abort)","&",lv_t_promptlist,lv_t_promptlist) lv_s_input1_Skip = lv_t_prompt[1] or "U" ; lv_s_input2_Skip = lv_t_prompt[2] or "U" ; lv_s_input3_Skip = lv_t_prompt[3] or "U" ; lv_b_check_SkipPerc = true ; lv_b_check_SkipHidd = true ; lv_b_check_SkipMute = true ; if lv_s_input1_Skip == lv_s_check_SkipPerc or lv_s_input2_Skip == lv_s_check_SkipPerc or lv_s_input3_Skip == lv_s_check_SkipPerc then lv_b_check_SkipPerc = true ; else lv_b_check_SkipPerc = false ; end--if if lv_s_input1_Skip == lv_s_check_SkipHidd or lv_s_input2_Skip == lv_s_check_SkipHidd or lv_s_input3_Skip == lv_s_check_SkipHidd then lv_b_check_SkipHidd = true ; else lv_b_check_SkipHidd = false ; end--if if lv_s_input1_Skip == lv_s_check_SkipMute or lv_s_input2_Skip == lv_s_check_SkipMute or lv_s_input3_Skip == lv_s_check_SkipMute then lv_b_check_SkipMute = true ; else lv_b_check_SkipMute = false ; end--if --print("##DEBUG: lv_b_check_SkipPerc = ",lv_b_check_SkipPerc) --print("##DEBUG: lv_b_check_SkipHidd = ",lv_b_check_SkipHidd) --print("##DEBUG: lv_b_check_SkipMute = ",lv_b_check_SkipMute) for item in nwcut.items() do --print (item) table.insert(lv_t_origitems,item) -- store for later print lv_m_playContext:put(item) if item:Is('AddStaff') then lv_n_CS = lv_n_CS + 1 ; lv_n_ppq1tot = 0 ; lv_n_ppqsubtot = 0 ; table.insert(lv_t_split,lv_n_ppqsubtot) ; table.insert(lv_t_skips,"N") ; end--if if item:Is('TimeSig') then lv_n_fraction = lf_n_timesignature(item.Opts.Signature) ; end--if if item:Is('Bar') then table.insert(lv_t_bars,lv_n_ppq1tot) end--if if item:Is('StaffInstrument') then lv_n_StaffTransp = item.Opts.Trans or 0 ; end--if if item:Is('StaffProperties') then lv_s_visible = item.Opts.Visible or 'U' ; if lv_s_visible == 'N' and lv_b_check_SkipHidd then lv_t_skips[lv_n_CS]="Y" ; end ; end--if if item:Is('StaffProperties') then lv_s_muted = item.Opts.Muted or 'U' ; if lv_s_muted == 'Y' and lv_b_check_SkipMute then lv_t_skips[lv_n_CS]="Y" ; end ; end--if if item:Is('StaffProperties') then lv_s_channel = item.Opts.Channel or '0' ; if lv_s_channel == '10' and lv_b_check_SkipPerc then lv_t_skips[lv_n_CS]="Y" ; end ; end--if if item:Is('Instrument') then lv_n_InstrTransp = item.Opts.Trans or 0 ; end--if if item:HasDuration() then if lv_n_StaffTransp + lv_n_InstrTransp < 0 then lv_s_noteprefix = '<' elseif lv_n_StaffTransp + lv_n_InstrTransp > 0 then lv_s_noteprefix = '>' else lv_s_noteprefix = '_' end--if lv_n_ppq1 = lf_n_duration(tostring(item.Opts.Dur ),lv_n_fraction,lv_n_base) + lf_n_multibar(tostring(item.Opts.NumBars),lv_n_fraction,lv_n_base) ; lv_n_ppq2 = lf_n_duration(tostring(item.Opts.Dur2),lv_n_fraction,lv_n_base) if lv_n_ppq1 > 0 and lv_t_skips[lv_n_CS] ~= "Y" then table.insert(lv_t_split,lv_n_ppqsubtot+lv_n_ppq1) ; end--if if lv_n_ppq2 > 0 and lv_t_skips[lv_n_CS] ~= "Y" then table.insert(lv_t_split,lv_n_ppqsubtot+lv_n_ppq2) ; end--if if lv_n_ppq2 > 0 then lv_n_ppq1tot = lv_n_ppqsubtot + math.min(lv_n_ppq1,lv_n_ppq2) ; else lv_n_ppq1tot = lv_n_ppqsubtot + lv_n_ppq1 ; end--if; lv_n_octaveshift = lv_m_playContext:GetOctaveShift() lv_n_pitchshift = lv_m_playContext:GetPlayPitchShift() if not item:Is('RestChord') and not item:Is('Rest') and not item:Is('RestMultiBar') then -- decode chord positions to notes for part 1 for i, notepos in ipairs(item.Opts.Pos) do lv_n_midipitch = lv_m_playContext:GetNoteMidiPitch(notepos) lv_s_name = lv_m_playContext:GetNoteName(notepos) lv_s_accidental = lv_m_playContext:GetNoteAccidental(notepos) ; if lv_s_accidental == "n" then lv_s_accidental = "_" ; end--if lv_n_octave = lv_m_playContext:GetScientificPitchOctave(notepos) lv_t_item = {itemtype = "SOLONOTE ", start = lv_n_ppqsubtot, duration = lv_n_ppq1, finish = lv_n_ppqsubtot+lv_n_ppq1, note = 1000+lv_n_midipitch+lv_n_StaffTransp+lv_n_InstrTransp.."_"..lv_s_noteprefix..lv_s_accidental..lv_s_name..lv_n_octave } ; if lv_t_skips[lv_n_CS] ~= "Y" then table.insert(lv_t_chord,lv_t_item) ; end--if end--for "Pos" else -- no chord positions for part 1 / decode as rest lv_s_note="9999__RST" lv_t_item = {itemtype = "REST ", start = lv_n_ppqsubtot, duration = lv_n_ppq1, finish = lv_n_ppqsubtot+lv_n_ppq1, note = lv_s_note } ; if lv_t_skips[lv_n_CS] ~= "Y" then table.insert(lv_t_chord,lv_t_item) ; end--if end--if not rests -- -- calc notes for dur2/pos2 chord if lv_n_ppq2 > 0 then -- decode chord positions to notes for part 2 for i, notepos in ipairs(item.Opts.Pos2) do lv_n_midipitch = lv_m_playContext:GetNoteMidiPitch(notepos) lv_s_name = lv_m_playContext:GetNoteName(notepos) lv_s_accidental = lv_m_playContext:GetNoteAccidental(notepos) lv_n_octave = lv_m_playContext:GetScientificPitchOctave(notepos) lv_t_item = {itemtype = "SOLONOTE ", start = lv_n_ppqsubtot, duration = lv_n_ppq2, finish = lv_n_ppqsubtot+lv_n_ppq2, note = 1000+lv_n_midipitch+lv_n_StaffTransp+lv_n_InstrTransp.."_"..lv_s_noteprefix..lv_s_accidental..lv_s_name..lv_n_octave } ; if lv_t_skips[lv_n_CS] ~= "Y" then table.insert(lv_t_chord,lv_t_item) ; end--if end--for "Pos2" end--if lv_n_ppq2 lv_n_ppqsubtot = lv_n_ppq1tot end--if end--for --for i=1,#lv_t_skips do print ('###DEBUG: skipped staff:',i,lv_t_skips[i]) ; end--for table.insert(lv_t_bars,lv_n_ppq1tot) -- add "bar" at end of staff -- Main part - deduplicate lv_t_bars (into lv_t_ubars) table.sort(lv_t_bars, function (k1, k2) return k1 < k2 end) lv_t_flags = {} lv_t_ubars = {} -- unique bartable for i=1,#lv_t_bars do if not lv_t_flags[lv_t_bars[i]] then table.insert(lv_t_ubars,lv_t_bars[i]) ; lv_t_flags[lv_t_bars[i]] = true ; end end--for -- Main part - deduplicate lv_t_split (into lv_t_usplit) + sort lv_t_flags = {} lv_t_usplit = {} -- unique lv_t_split for i=1,#lv_t_split do if not lv_t_flags[lv_t_split[i]] then table.insert(lv_t_usplit,lv_t_split[i]) ; lv_t_flags[lv_t_split[i]] = true ; end end--for table.sort(lv_t_usplit, function (k1, k2) return k1 < k2 end) -- gestorteed op start -- Main part - sort lv_t_chord = table with all notes in the score table.sort(lv_t_chord, function (k1, k2) return (k1.start < k2.start) or (k1.start == k2.start and k1.finish < k2.finish) end) -- sort on start and then on finish -- ----------------------------------------------------------------- lv_t_sorteddurations = { 60, 90, 105, 120, 180, 210, 240, 360, 420, 480, 720, 840, 960,1440,1680,1920,2880,3360,3840,5760,6720} lv_t_sorteddurnames = { "64th","64th,Dotted","64th,DblDotted","32nd","32nd,Dotted","32nd,DblDotted","16th","16th,Dotted","16th,DblDotted","8th","8th,Dotted","8th,DblDotted","4th","4th,Dotted","4th,DblDotted","Half","Half,Dotted","Half,DblDotted","Whole","Whole,Dotted","Whole,DblDotted" } for key,value in pairs(lv_t_origitems) do print(value) end--for lv_t_chordinfo = {} lv_t_chordinfo[1] = "|Lyric8|Text:" lv_t_chordinfo[2] = "|Lyric7|Text:" lv_t_chordinfo[3] = "|Lyric6|Text:" lv_t_chordinfo[4] = "|Lyric5|Text:" lv_t_chordinfo[5] = "|Lyric4|Text:" lv_t_chordinfo[6] = "|Lyric3|Text:" lv_t_chordinfo[7] = "|Lyric2|Text:" lv_t_chordinfo[8] = "|Lyric1|Text:" lv_t_chordinfo[9] = "|Lyric9|Text:" lv_t_chordinfo[10] = "|Lyric10|Text:" lv_t_chordinfo[11] = "|Lyric11|Text:" lv_t_chordinfo[12] = {} lv_t_tempstaff = {} lv_t_utempstaff = {} lv_n_currentindex = 0 lv_n_chordindex = 1 lv_n_barindex = 1 lv_n_delta = 0 lv_n_previous = 0 lv_n_calcdur = 0 lv_s_calcname = "" for i = 1, #lv_t_usplit do lv_t_tempstaff = {} lv_t_utempstaff = {} lv_t_flags = {} if lv_t_usplit[i]-lv_n_previous > 0 then lv_n_adapteddur = lv_t_usplit[i]-lv_n_previous + lv_n_delta lv_n_previous = lv_t_usplit[i] j = 1 while j <= #lv_t_sorteddurations and lv_t_sorteddurations[j] <= lv_n_adapteddur do lv_n_calcdur = lv_t_sorteddurations[j] lv_s_calcname= lv_t_sorteddurnames [j] lv_n_delta = lv_n_adapteddur - lv_n_calcdur j = j + 1 end--while table.insert(lv_t_chordinfo[12],"|REST|Opts:Lyric=Always|Dur:"..lv_s_calcname) end--if if lv_t_ubars[lv_n_barindex] == lv_t_usplit[i] then table.insert(lv_t_chordinfo[12], "|Bar") ; lv_n_barindex = lv_n_barindex + 1 ; end--if lv_n_currentindex = lv_n_chordindex CONT=true while CONT do if lv_t_usplit[i] >= lv_t_chord[lv_n_currentindex].start and (lv_t_usplit[i+1] or 100000000000000)<= lv_t_chord[lv_n_currentindex].finish then table.insert(lv_t_tempstaff,lv_t_chord[lv_n_currentindex].note) end--if if ( lv_t_chord[lv_n_currentindex].start >= (lv_t_usplit[i+1] or 100000000000000) or lv_n_currentindex == #lv_t_chord) then CONT = false end--if; lv_n_currentindex = lv_n_currentindex + 1 ; end--while for i=1,#lv_t_tempstaff do if not lv_t_flags[lv_t_tempstaff[i]] then table.insert(lv_t_utempstaff,lv_t_tempstaff[i]) ; lv_t_flags[lv_t_tempstaff[i]] = true ; end end--for table.sort(lv_t_utempstaff, function (k1, k2) return k1 < k2 end) -- gestorteed op note (only field) lv_t_suffix5 = {} lv_t_suffix8 = {} for i = 1,8 do lv_t_suffix5[i] = "." ; lv_t_suffix8[i] = "." ; end--for for i = 1,8 do if not (lv_t_utempstaff[i] == nil) and not (lv_t_utempstaff[i] == "9999__RST" ) then for j = i+1,8 do if not (lv_t_utempstaff[j] == nil) and not (lv_t_utempstaff[j] == "9999__RST" ) then if ( string.sub(lv_t_utempstaff[j],1,4)-string.sub(lv_t_utempstaff[i],1,4) ) == 7 then lv_t_suffix5[i] = "F" lv_t_suffix5[j] = "f" end--if if ( string.sub(lv_t_utempstaff[j],1,4)-string.sub(lv_t_utempstaff[i],1,4) ) == 12 then lv_t_suffix8[i] = "O" lv_t_suffix8[j] = "o" end--if end--if end-- for j end--if end--for for k = 1,8 do if not (lv_t_utempstaff[k] == nil) and not (lv_t_utempstaff[k] == "9999__RST" ) then lv_t_chordinfo[k]=lv_t_chordinfo[k] .. " " .. string.sub(lv_t_utempstaff[k],5).."_["..lv_t_suffix5[k]..lv_t_suffix8[k].."]" ; else lv_t_chordinfo[k]=lv_t_chordinfo[k] .. " ________" ; end--if end--for while lv_t_chord[lv_n_chordindex].finish <= lv_t_usplit[i] and lv_n_chordindex < #lv_t_chord do lv_n_chordindex = lv_n_chordindex + 1 ; end--while end--for -- ADD CHORD STAFF lv_t_chordinfonew = {} table.insert(lv_t_chordinfonew,"|AddStaff|Name:CHORDNOTES|Label:CHORDNOTES|Group:CREATED_BY_TOOL") table.insert(lv_t_chordinfonew,"|StaffProperties|Visible:Y|Color:Default|Lines:1|WithNextStaff:Bracket,ConnectBars|BoundaryBottom:10|BoundaryTop:50") table.insert(lv_t_chordinfonew,"|Lyrics|Align:Standard Rules|Offset:0|Placement:Top") table.insert(lv_t_chordinfonew,lv_t_chordinfo[1]) table.insert(lv_t_chordinfonew,lv_t_chordinfo[2]) table.insert(lv_t_chordinfonew,lv_t_chordinfo[3]) table.insert(lv_t_chordinfonew,lv_t_chordinfo[4]) table.insert(lv_t_chordinfonew,lv_t_chordinfo[5]) table.insert(lv_t_chordinfonew,lv_t_chordinfo[6]) table.insert(lv_t_chordinfonew,lv_t_chordinfo[7]) table.insert(lv_t_chordinfonew,lv_t_chordinfo[8]) for key,chordinfovalue in pairs(lv_t_chordinfonew) do print(chordinfovalue) end--for for key,chordinfovalue in pairs(lv_t_chordinfo[12]) do print(chordinfovalue) end--for