-- @version 1.082 -- @author mpl -- @changelog -- # Fix: error on empty sm table --[[ * ReaScript Name: mpl Align Takes * Lua script for Cockos REAPER * Author: Michael Pilyavskiy (mpl) * Author URI: http://forum.cockos.com/member.php?u=70694 * Licence: GPL v3 ]] --[[ * Changelog: * v1.081 (2016-06-11) # Improved graphics for knob and selector * v1.07 (2016-04-01) # Set slider to 0 when get takes * v1.06 (2016-03-31) # Fixed alg=0 error * v1.05 (2016-03-08) # save/restore window position (Reaper 5.20pre16+) * v1.04 (2016-02-17) # ReaPack changelog synthax * v1.03 (2016-02-16) + Algorithm selector, check Menu/Parameters description * v1.02 (2016-02-12) #OSX font issues * v1.01 (2016-02-11) # Some settings limits extended + Added selector RMS/FFT detection + Added RMS window knob + Added FFT size knob + Added HP/LP FFT filters cutoff knobs + Added smooth factor knob * v1.00 (2016-02-11) + Public release * v0.23 (2016-01-25) # Split from Warping tool * v0.01 (2015-09-01) + Alignment / Warping / Tempomatching tool idea --]] function bmrk() end local vrs = '1.08' ----------------------------------------------------------------------- function msg(str) if type(str) == 'boolean' then if str then str = 'true' else str = 'false' end end if type(str) == 'userdata' then str = str.get_alloc() end if str ~= nil then reaper.ShowConsoleMsg(tostring(str)..'\n') if str == "" then reaper.ShowConsoleMsg("") end else reaper.ShowConsoleMsg('nil') end end ----------------------------------------------------------------------- function fdebug(str) if debug_mode == 1 then msg(os.date()..' '..str) end end ----------------------------------------------------------------------- function MAIN_exit() reaper.atexit() gfx.quit() end ----------------------------------------------------------------------- function DEFINE_dynamic_variables() data2.custom_window = F_convert(data.custom_window_norm, 0.005, 0.2) data2.fft_size = math.floor(2^math.floor(F_convert(data.fft_size_norm,7,10))) if data2.fft_LP == nil then data2.fft_LP = data2.fft_size end data2.fft_HP = F_limit(math.floor(F_convert(data.fft_HP_norm, 1, data2.fft_size)), 1, data2.fft_LP-1) data2.fft_LP = 1+F_limit(math.floor(F_convert(data.fft_LP_norm, 1, data2.fft_size)), data2.fft_HP+1, data2.fft_size) data2.smooth = data.smooth_norm data2.filter_area = F_convert(data.filter_area_norm, 0.1,2) data2.rise_area = F_convert(data.rise_area_norm, 0.1,0.5) data2.risefall = F_convert(data.risefall_norm, 0.1,0.8) data2.risefall2 = F_convert(data.risefall2_norm, 0.05,0.8) data2.threshold = F_convert(data.threshold_norm, 0.1,0.4) data2.scaling_pow = F_convert(math.abs(1-data.scaling_pow_norm), 0.1, 0.75) data2.search_area = F_convert(data.search_area_norm, 0.05, 2) local objects = DEFINE_objects() if compact_view_trig then objects.main_h = objects.main_h + data.compact_view*objects.set_wind_h gfx.quit() gfx.init("mpl Align takes // "..vrs, objects.main_w, objects.main_h, 0, data.xpos,data.ypos ) compact_view_trig = false end char = gfx.getchar() play_pos = reaper.GetPlayPosition(0) OS = reaper.GetOS() reaper_vrs = tonumber(reaper.GetAppVersion():match('[%d%.]+')) if reaper_vrs >= 5.20 then is_docked, xpos, ypos = gfx.dock(0,data.xpos,data.ypos) if data.xpos ~= xpos or data.ypos ~= ypos then data.xpos, data.ypos = xpos, ypos ENGINE_set_ini(data, config_path) end end end ----------------------------------------------------------------------- function F_convert(val, min, max) return (max-min) *val + min end ----------------------------------------------------------------------- function F_limit(val,min,max,retnil) if val == nil or min == nil or max == nil then return 0 end local val_out = val if val == nil then val = 0 end if val < min then val_out = min if retnil then return nil end end if val > max then val_out = max if retnil then return nil end end return val_out end ----------------------------------------------------------------------- function F_Get_SSV(s) local t = {} for i in s:gmatch("[%d%.]+") do t[#t+1] = tonumber(i) / 255 end gfx.r, gfx.g, gfx.b = t[1], t[2], t[3] end ----------------------------------------------------------------------- function ENGINE_get_takes() local take_guid local take_name local takes_t = {} local count_items = reaper.CountSelectedMediaItems() if count_items ~= nil then for i =1, count_items do local item = reaper.GetSelectedMediaItem(0, i-1) if item ~= nil then local item_len = reaper.GetMediaItemInfo_Value( item, 'D_LENGTH') local item_pos = reaper.GetMediaItemInfo_Value( item, 'D_POSITION') local take = reaper.GetActiveTake(item) if not reaper.TakeIsMIDI(take) then take_guid = reaper.BR_GetMediaItemTakeGUID(take) _, take_name = reaper.GetSetMediaItemTakeInfo_String(take, 'P_NAME', '', false) local t_offs = reaper.GetMediaItemTakeInfo_Value(take, 'D_STARTOFFS') local src = reaper.GetMediaItemTake_Source(take) local rate = reaper.GetMediaSourceSampleRate(src) local vol = reaper.GetMediaItemTakeInfo_Value(take, 'D_VOL') table.insert(takes_t, {['guid']=take_guid, ['name']=take_name, ['len']=item_len, ['pos']=item_pos, ['offset']=t_offs, ['rate']=rate, ['vol']=vol}) end end end end return takes_t end ----------------------------------------------------------------------- function ENGINE_prepare_takes() local item, take local count_items = reaper.CountSelectedMediaItems() if count_items == nil or count_items < 1 then return end reaper.Main_OnCommand(41844,0) -- clear stretch markers reaper.Main_OnCommand(40652,0) -- set item rate to 1 -- check for unglued reference item/take local ref_item = reaper.GetSelectedMediaItem(0, 0) if ref_item == nil then return end local ref_track = reaper.GetMediaItemTrack(ref_item) local ref_pos = reaper.GetMediaItemInfo_Value(ref_item, 'D_POSITION') local ref_len = reaper.GetMediaItemInfo_Value(ref_item, 'D_LENGTH') for i = 2, count_items do item = reaper.GetSelectedMediaItem(0, i-1) track = reaper.GetMediaItemTrack(item) if track == ref_track then reaper.MB('Reference item/take should be glued','Warping tool', 0) return end end -- check for edges for i = 2, count_items do item = reaper.GetSelectedMediaItem(0, i-1) local pos = reaper.GetMediaItemInfo_Value(item, 'D_POSITION') local len = reaper.GetMediaItemInfo_Value(item, 'D_LENGTH') if pos < ref_pos then reaper.SetMediaItemInfo_Value(item, 'D_POSITION', ref_pos) local take = reaper.GetActiveTake(item) local take_offs = reaper.GetMediaItemTakeInfo_Value(take, 'D_STARTOFFS') reaper.SetMediaItemTakeInfo_Value(take, 'D_STARTOFFS', take_offs + ref_pos - pos) end if ref_pos + ref_len < pos+len then reaper.SetMediaItemInfo_Value(item, 'D_LENGTH', len - (pos+len - ref_pos - ref_len)) end end reaper.UpdateArrange() return 1 -- successful end ----------------------------------------------------------------------- function ENGINE_get_take_data(take_id, scaling) local st_win_cnt,end_win_cnt local aa = {} local sum_t = {} if takes_t ~= nil and takes_t[take_id] ~= nil then local take = reaper.SNM_GetMediaItemTakeByGUID(0, takes_t[take_id].guid) if take ~= nil then local item = reaper.GetMediaItemTake_Item(take) local item_len = reaper.GetMediaItemInfo_Value(item, 'D_LENGTH') aa.accessor = reaper.CreateTakeAudioAccessor(take) aa.src = reaper.GetMediaItemTake_Source(take) aa.numch = reaper.GetMediaSourceNumChannels(aa.src) aa.rate = reaper.GetMediaSourceSampleRate(aa.src) if data.mode == 0 then aa.window_sec = data2.custom_window else aa.window_sec = data2.fft_size/aa.rate -- ms end data.global_window_sec = aa.window_sec size = math.ceil(aa.window_sec*aa.rate) -- get fft_size samples buffer for read_pos = 0, item_len, aa.window_sec do aa.buffer = reaper.new_array(size*2) aa.buffer_com = reaper.new_array(size*2) reaper.GetAudioAccessorSamples( aa.accessor , --AudioAccessor aa.rate, -- samplerate 2,--aa.numch, -- numchannels read_pos, -- starttime_sec size, -- numsamplesperchannel aa.buffer) --samplebuffer -- merge buffers dy duplicating sum/2 for i = 1, size*2 - 1, 2 do aa.buffer_com[i] = (aa.buffer[i] + aa.buffer[i+1])/2 aa.buffer_com[i+1] = 0 end if data.mode == 1 then -- Get FFT sum of bins in defined range aa.buffer_com.fft(size, true, 1) aa.buffer_com_t = aa.buffer_com.table(1,size, true) sum_com = 0 for i = data2.fft_HP, data2.fft_LP do sum_com = sum_com + math.abs(aa.buffer_com_t[i]) end table.insert(sum_t, sum_com /(data2.fft_LP-data2.fft_HP)) else -- Get RMS sum in defined range aa.buffer_com_t = aa.buffer_com.table(1,size, true) sum_com = 0 for i = 1, size do sum_com = sum_com + math.abs(aa.buffer_com_t[i]) end table.insert(sum_t, sum_com) end aa.buffer.clear() aa.buffer_com.clear() end reaper.DestroyAudioAccessor(aa.accessor) else return end else return end local out_t = sum_t -- normalize table local max_com = 0 for i =1, #out_t do max_com = math.max(max_com, out_t[i]) end local com_mult = 1/max_com for i =1, #out_t do out_t[i]= out_t[i]*com_mult end -- return scaled table --for i =1, #out_t do out_t[i]= out_t[i]^scaling end -- smooth table for i = 2, #out_t do out_t[i]= out_t[i] - (out_t[i] - out_t[i-1])*0.75 end -- fill null to reference item if take_id > 1 then st_win_cnt = math.floor ((takes_t[take_id].pos - takes_t[1].pos) / data.global_window_sec) end_win_cnt = math.floor ((takes_t[1].pos + takes_t[1].len - takes_t[take_id].pos - takes_t[take_id].len) / data.global_window_sec) -- fill from start if takes_t[take_id].pos > takes_t[1].pos then for i = 1, st_win_cnt do table.insert(out_t, 1, 0) end end -- fill end if takes_t[take_id].pos + takes_t[take_id].len < takes_t[1].pos + takes_t[1].len then for i = 1, end_win_cnt do out_t[#out_t+1] = 0 end end end if out_t ~= nil and #out_t > 1 then local out_array = reaper.new_array(out_t, #out_t) return out_array end end ----------------------------------------------------------------------- function ENGINE_get_take_data_points2(inputarray, window_sec) -- micro mode --local arr_size,points if inputarray == nil then return end arr_size = inputarray.get_alloc() if arr_size <=1 then return end inputarray_scaled = reaper.new_array(arr_size) for i = 1, arr_size do inputarray_scaled[i] = inputarray[i]^data2.scaling_pow end points = reaper.new_array(arr_size) -- clear arr val for i = 1, arr_size do points[i] = 0 end -- parameters filter_area_wind = math.floor(data2.filter_area / window_sec) risearea_wind = math.floor(data2.rise_area / window_sec) ------------- ------------- -- check for rise for i = 1, arr_size - risearea_wind do arr_val_i = inputarray_scaled[i] max_val = 0 for k = i, i + risearea_wind do arr_val_k = inputarray_scaled[k] max_val = math.max(max_val,arr_val_k) if last_max_val == nil or last_max_val ~= max_val then max_val_id = k end last_max_val = max_val end if max_val - arr_val_i > data2.risefall then arr_val_i2 = inputarray[i] max_val2 = 0 for k = i, i + risearea_wind do arr_val_k2 = inputarray[k] max_val2 = math.max(max_val2,arr_val_k2) if last_max_val2 == nil or last_max_val2 ~= max_val2 then max_val_id2 = k end last_max_val2 = max_val2 end if max_val2 - arr_val_i2 > data2.risefall2 then points[max_val_id] = 1 end end end -- check for fall for i = 1, arr_size - risearea_wind do arr_val_i = inputarray_scaled[i] min_val = 1 for k = i, i + risearea_wind do arr_val_k = inputarray_scaled[k] min_val = math.min(min_val,arr_val_k) if last_min_val == nil or last_min_val ~= min_val then min_val_id = k end last_min_val = max_val end if arr_val_i - min_val > data2.risefall then arr_val_i2 = inputarray[i] min_val2 = 1 for k = i, i + risearea_wind do arr_val_k2 = inputarray[k] min_val2 = math.min(min_val2,arr_val_k2) if last_min_val2 == nil or last_min_val2 ~= min_val2 then min_val_id2 = k end last_min_val2 = max_val2 end if arr_val_i2 - min_val2 > data2.risefall2 then points[min_val_id] = 1 end end end ------------- ------------- -- filter points threshhld for i = 1, arr_size do if inputarray_scaled[i] < data2.threshold then points[i] = 0 end end -- filter points area for i = 1, arr_size-1 do if points[i] == 1 then point_i_val = inputarray_scaled[i] max_sa = i + 1 + filter_area_wind if max_sa > arr_size then max_sa = arr_size end for k = i + 1, max_sa do if points[k] == 1 then points[k] = 0 end end end end ------------- ------------- -- prepare for output for i = 2, filter_area_wind do points[i] = 0 end points[1] = 1 points[arr_size] = 1 return points end ----------------------------------------------------------------------- function F_find_arrays_com_diff(ref_array, ref_array_offset, dub_array, get_ref_block_rms) local dub_array_size = dub_array.get_alloc() local ref_array_size = ref_array.get_alloc() local endpoint,ref_rms local com_difference = 0 if ref_array_offset + dub_array_size > ref_array_size then endpoint = ref_array_size - ref_array_offset else endpoint = dub_array_size end for i = 1, endpoint do com_difference = com_difference + math.abs(ref_array[i + ref_array_offset - 1 ]-dub_array[i]) end if get_ref_block_rms ~= nil and get_ref_block_rms then ref_rms = 0 for i = 1, endpoint do ref_rms = ref_rms + math.abs(ref_array[i + ref_array_offset - 1 ]) end ref_rms = ref_rms / endpoint end return com_difference, ref_rms end ----------------------------------------------------------------------- function F_find_min_value(t) local min_val_id, min_val, min_val0 min_val0 = math.huge for i = 1, #t do min_val = math.min(min_val0, t[i]) if min_val ~= min_val0 then min_val0 = min_val min_val_id = i end end return min_val_id end ----------------------------------------------------------------------- function F_find_max_value(t) local max_val_id, max_val, max_val0 max_val0 =0 for i = 1, #t do max_val = math.max(max_val0, t[i]) if max_val ~= max_val0 then max_val0 = max_val max_val_id = i end end return max_val_id end ----------------------------------------------------------------------- function F_stretch_array(src_array, new_size) local src_array_size = src_array.get_alloc() local coeff = (src_array_size - 1) / (new_size - 1) local out_array = reaper.new_array(new_size) if new_size < src_array_size or new_size > src_array_size then for i = 0, new_size - 1 do src_idx = math.floor(i * coeff) + 1 src_idx = math.floor(F_limit(src_idx, 1, src_array_size)) out_array[i+1] = src_array[src_idx] end return out_array elseif new_size == src_array_size then out_array = src_array return out_array end return out_array end ----------------------------------------------------------------------- function F_stretch_array2(src_array, src_mid_point, stretched_point) if src_array == nil or src_mid_point == nil or stretched_point == nil then return end local src_array_size = src_array.get_alloc() local out_arr = reaper.new_array(src_array_size) local src_arr_pt1_size = src_mid_point - 1 local src_arr_pt2_size = src_array_size-src_mid_point + 1 local out_arr_pt1_size = stretched_point - 1 local out_arr_pt2_size = src_array_size-stretched_point + 1 local src_arr_pt1 = reaper.new_array(src_arr_pt1_size) local src_arr_pt2 = reaper.new_array(src_arr_pt2_size) src_arr_pt1.copy(src_array,--src, 1,--srcoffs, src_arr_pt1_size,--size, 1)--destoffs]) src_arr_pt2.copy(src_array,--src, src_mid_point,--srcoffs, src_arr_pt2_size,--size, 1)--destoffs]) local out_arr_pt1 = F_stretch_array(src_arr_pt1, out_arr_pt1_size) local out_arr_pt2 = F_stretch_array(src_arr_pt2, out_arr_pt2_size) out_arr.copy(out_arr_pt1,--src, 1,--srcoffs, out_arr_pt1_size,--size, 1)--destoffs]) out_arr.copy(out_arr_pt2,--src, 1,--srcoffs, out_arr_pt2_size,--size, out_arr_pt1_size + 1)--destoffs]) return out_arr end ----------------------------------------------------------------------- function ENGINE_compare_data2_alg1(ref_arr_orig, dub_arr_orig, points, window_sec) local st_search, end_search if ref_arr_orig == nil then return end if dub_arr_orig == nil then return end if points == nil then return end local ref_arr_size = ref_arr_orig.get_alloc() local dub_arr_size = dub_arr_orig.get_alloc() ref_arr = reaper.new_array(ref_arr_size) for i = 1, ref_arr_size do ref_arr[i] = ref_arr_orig[i]^data2.scaling_pow end dub_arr = reaper.new_array(dub_arr_size) for i = 1, dub_arr_size do dub_arr[i] = dub_arr_orig[i]^data2.scaling_pow end local sm_table = {} search_area = math.floor(data2.search_area / window_sec) -- get blocks block_ids = {} for i = 1, dub_arr_size do if points[i] == 1 then block_ids[#block_ids+1] = {['orig']=i} end end -- loop blocks for i = 1, #block_ids - 2 do -- create fixed block point1 = block_ids[i].orig point2 = block_ids[i+1].orig point3 = block_ids[i+2].orig if i >= 1 then P1_diff = 0 P2_fant = point2-point1+1 P3_fant = point3 - point1 + 1 -- arr size fantom_arr = reaper.new_array(P3_fant) fantom_arr.copy(dub_arr,--src, point1,--srcoffs, P3_fant,--size, 1)--destoffs]) end -- loop possible positions local min_block_len = 3 search_pos_start = P2_fant - search_area if search_pos_start < min_block_len then search_pos_start = min_block_len end search_pos_end = P2_fant + search_area if search_pos_end > P3_fant - min_block_len then search_pos_end = P3_fant - min_block_len end if (search_pos_end-search_pos_start+1) > min_block_len then diff = reaper.new_array(search_pos_end-search_pos_start+1) for k = search_pos_start, search_pos_end do fantom_arr_stretched = F_stretch_array2(fantom_arr, P2_fant, k) diff[k - search_pos_start+1] = F_find_arrays_com_diff(ref_arr, point1, fantom_arr_stretched) end block_ids[i+1].stretched = F_find_min_value(diff) + search_pos_start - 1 - P1_diff + point1 sm_table[#sm_table+1] = {block_ids[i+1].stretched * window_sec, (-1+block_ids[i+1].orig) * window_sec} end fantom_arr.clear() end -- end loop blocks return sm_table end ----------------------------------------------------------------------- --[[ check by every block with relative diff / k / ref_rms function ENGINE_compare_data2_alg3_test(ref_arr_orig, dub_arr_orig, points, window_sec) local st_search, end_search if ref_arr_orig == nil then return end if dub_arr_orig == nil then return end if points == nil then return end local ref_arr_size = ref_arr_orig.get_alloc() local dub_arr_size = dub_arr_orig.get_alloc() ref_arr = reaper.new_array(ref_arr_size) for i = 1, ref_arr_size do ref_arr[i] = ref_arr_orig[i]^data2.scaling_pow end dub_arr = reaper.new_array(dub_arr_size) for i = 1, dub_arr_size do dub_arr[i] = dub_arr_orig[i]^data2.scaling_pow end local sm_table = {} search_area = math.floor(data2.search_area / window_sec) -- get blocks block_ids = {} for i = 1, dub_arr_size do if points[i] == 1 then block_ids[#block_ids+1] = {['orig']= i } end end -- loop blocks block_ids[1].str = 1 for i = 2, #block_ids - 1 do block_start = block_ids[i-1].orig block_start_str = block_ids[i-1].str block_end = block_ids[i].orig block2_end = block_ids[i+1].orig fantom_arr_sz = block_end - block_start fantom_arr = reaper.new_array(fantom_arr_sz) fantom_arr.copy(dub_arr,--src, block_start,--srcoffs, fantom_arr_sz,--size, 1)--destoffs]) min_block_len = 3 search_start = fantom_arr_sz - search_area if search_start < min_block_len then search_start = min_block_len end search_end = fantom_arr_sz + (block_start-block_start_str) + search_area if search_end + block_start > dub_arr_size - min_block_len then search_end = dub_arr_size - min_block_len - block_start end diff_t = { } for k = search_start, search_end do fantom_arr_stretched = F_stretch_array(fantom_arr, k) diff, ref_rms = F_find_arrays_com_diff(ref_arr, block_start_str, fantom_arr_stretched,true) diff_t[#diff_t+1] = diff/k end --msg(table.concat(diff_t, '\n')) id_diff = F_find_min_value(diff_t) block_ids[i].str = id_diff + search_start + block_start_str sm_table[#sm_table+1] = { block_ids[i].str * window_sec, (-1+block_ids[i].orig) * window_sec } fantom_arr.clear() end return sm_table end]] ----------------------------------------------------------------------- function ENGINE_compare_data2_alg2(ref_arr_orig, dub_arr_orig, points, window_sec) local st_search, end_search if ref_arr_orig == nil then return end if dub_arr_orig == nil then return end if points == nil then return end local ref_arr_size = ref_arr_orig.get_alloc() local dub_arr_size = dub_arr_orig.get_alloc() ref_arr = reaper.new_array(ref_arr_size) for i = 1, ref_arr_size do ref_arr[i] = ref_arr_orig[i]^data2.scaling_pow end dub_arr = reaper.new_array(dub_arr_size) for i = 1, dub_arr_size do dub_arr[i] = dub_arr_orig[i]^data2.scaling_pow end local sm_table = {} search_area = math.floor(data2.search_area / window_sec) -- get blocks block_ids = {} for i = 1, dub_arr_size do if points[i] == 1 then block_ids[#block_ids+1] = {['orig']=i} end end -- loop blocks block_ids[1].str = 1 for i = 2, #block_ids - 1 do -- create fixed block point1 = block_ids[i-1].str point1_orig = block_ids[i-1].orig point2 = block_ids[i].orig point3 = block_ids[i+1].orig search_point = point2 - point1 -- form fantom array fantom_arr_sz = point3 - point1 fantom_arr = reaper.new_array(fantom_arr_sz) fantom_arr_pt1 = reaper.new_array(point2 - point1_orig) fantom_arr_pt1.copy(dub_arr,--src, point1_orig,--srcoffs, point2 - point1_orig,--size, 1)--destoffs]) fantom_arr_pt1_str = F_stretch_array(fantom_arr_pt1, point2-point1) fantom_arr.copy(fantom_arr_pt1_str,--src, 1,--srcoffs, point2-point1,--size, 1)--destoffs]) fantom_arr.copy(dub_arr,--src, point2,--srcoffs, point3-point2,--size, point2-point1+1)--destoffs]) -- loop possible positions filter_area_wind = math.floor(data2.filter_area / window_sec) min_block_len = 20 if min_block_len > filter_area_wind then min_block_len = filter_area_wind - 3 end search_start = search_point - search_area if search_start < min_block_len then search_start = min_block_len end search_end = search_point + search_area if search_end > fantom_arr_sz - min_block_len then search_end = fantom_arr_sz - min_block_len end diff_t = {} for k = search_start, search_end do fantom_arr_stretched = F_stretch_array2(fantom_arr, search_point, k) diff_t[#diff_t+1] = F_find_arrays_com_diff(ref_arr, point1, fantom_arr_stretched) end id_diff = F_find_min_value(diff_t) block_ids[i].str = id_diff + search_start + point1 sm_table[#sm_table+1] = { block_ids[i].str * window_sec, (-1+block_ids[i].orig) * window_sec } end -- loop blocks return sm_table end --[[ if i == 1 then P2_fant = point2 - point1 + 1 P3_fant = point3 - point1 + 1 -- arr size fantom_arr = reaper.new_array(P3_fant) fantom_arr.copy(dub_arr,--src, point1,--srcoffs, P3_fant,--size, 1)--destoffs]) -- min_bl_len = 3 min_search_diff = 3 search_start = P2_fant - search_area if search_start < min_bl_len then search_start = min_bl_len end search_end = P2_fant + search_area if search_end > P3_fant - min_bl_len then search_end = P3_fant - min_bl_len end if search_end-search_start+1 > min_search_diff then diff_t = {} for k = search_start, search_end do fantom_arr_stretched = F_stretch_array2(fantom_arr, P2_fant, k) diff_t[#diff_t+1] = F_find_arrays_com_diff(ref_arr, point1, fantom_arr_stretched) end id_diff = F_find_min_value(diff_t) block_ids[i+1].str = id_diff + search_start - 4 + point1 sm_table[#sm_table+1] = { block_ids[i+1].str * window_sec, (-1+block_ids[i+1].orig) * window_sec } else sm_table[#sm_table+1] = { block_ids[i+1].orig * window_sec, block_ids[i+1].orig * window_sec } block_ids[i+1].str = block_ids[i+1].orig end end -- if first block if i > 1 and i < #block_ids - 2 then last_block_diff = block_ids[i].orig - block_ids[i].str P2_fant = point2 - point1 + 1 P3_fant = point3 - point1 + 1 -- arr size P2_fant_str = P2_fant + last_block_diff P3_fant_str = P3_fant + last_block_diff fantom_arr = reaper.new_array(P3_fant_str) fantom_arr_pt1 = reaper.new_array(P2_fant) fantom_arr_pt1.copy(dub_arr,--src, point1,--srcoffs, P2_fant,--size, 1)--destoffs]) if P2_fant_str > 3 then fantom_arr_pt1_str = F_stretch_array(fantom_arr_pt1, P2_fant_str) fantom_arr.copy(fantom_arr_pt1_str,--src, 1,--srcoffs, P2_fant_str,--size, 1)--destoffs]) fantom_arr.copy(dub_arr,--src, point2,--srcoffs, point3-point2+1,--size, P2_fant_str)--destoffs]) -- loop possible positions min_bl_len = 3 min_search_diff = 3 search_start = P2_fant_str - search_area if search_start < min_bl_len then search_start = min_bl_len end search_end = P2_fant_str + search_area if search_end > P3_fant_str - min_bl_len then search_end = P3_fant_str - min_bl_len end if search_end-search_start+1 > min_search_diff then diff_t = {} if search_end > P3_fant_str - min_bl_len then search_end = P3_fant_str - min_bl_len end for k = search_start, search_end do fantom_arr_stretched = F_stretch_array2(fantom_arr, P2_fant, k) diff_t[#diff_t+1] = F_find_arrays_com_diff(ref_arr, block_ids[i].str, fantom_arr_stretched) end id_diff = F_find_min_value(diff_t) block_ids[i+1].str = id_diff + search_start + point1 -1--block_ids[i].str sm_table[#sm_table+1] = { block_ids[i+1].str * window_sec, block_ids[i+1].orig * window_sec } else sm_table[#sm_table+1] = { block_ids[i+1].orig * window_sec, block_ids[i+1].orig * window_sec } block_ids[i+1].str = block_ids[i+1].orig end else sm_table[#sm_table+1] = { block_ids[i+1].orig * window_sec, block_ids[i+1].orig * window_sec } block_ids[i+1].str = block_ids[i+1].orig end end -- other blocks ]] ----------------------------------------------------------------------- function ENGINE_set_stretch_markers2(take_id, str_mark_table, val) if str_mark_table == nil then return nil end if takes_t ~= nil and takes_t[take_id] ~= nil then local take = reaper.SNM_GetMediaItemTakeByGUID(0, takes_t[take_id].guid) if take ~= nil then reaper.DeleteTakeStretchMarkers(take, 0, #str_mark_table + 1) reaper.SetTakeStretchMarker(take, -1, 0, takes_t[take_id].offset) for i = 1, #str_mark_table do set_pos = str_mark_table[i][1]-(takes_t[take_id].pos-takes_t[1].pos) src_pos = str_mark_table[i][2]-(takes_t[take_id].pos-takes_t[1].pos) + takes_t[take_id].offset set_pos = src_pos - takes_t[take_id].offset - ((src_pos - takes_t[take_id].offset) - set_pos)*val if last_src_pos ~= nil and last_set_pos ~= nil then -- check for negative stretch markers if (src_pos - last_src_pos) / (set_pos - last_set_pos ) > 0 then reaper.SetTakeStretchMarker(take, -1, set_pos,src_pos) last_src_pos = src_pos last_set_pos = set_pos end else reaper.SetTakeStretchMarker(take, -1, set_pos,src_pos) last_src_pos = src_pos last_set_pos = set_pos end end reaper.SetTakeStretchMarker(take, -1, takes_t[take_id].len) end end reaper.UpdateArrange() end ----------------------------------------------------------------------- function GUI_item_display(objects, gui, xywh, reaperarray, is_ref, pointsarray, col_peaks) local arr_size local x=xywh[1] local y=xywh[2] local w=xywh[3] local h=xywh[4] local drawscale = 0.9 -- draw item back gradient from buf 7 gfx.a = 0.4 gfx.blit(7, 1, 0, -- backgr 0,0,objects.main_w, objects.main_h, xywh[1],xywh[2],xywh[3], xywh[4], 0,0) if reaperarray ~= nil then arr_size = reaperarray.get_alloc() -- -- draw envelope gfx.a = 0.7 F_Get_SSV(gui.color.white, true) gfx.x = x gfx.y = y for i=1, arr_size-1, 1 do local data_t_it = reaperarray[i] local data_t_it2 = reaperarray[i+1] local const = 0 local st_x = x+i*w/arr_size local end_x = x+(i+1)*w/arr_size+const if end_x > x+w then end_x = x+w end if end_x < x then end_x = x end if is_ref then gfx.a = 0.2 gfx.triangle(st_x, y+h-h*data_t_it*drawscale, end_x,y+h-h*data_t_it2*drawscale, end_x,y+h, st_x, y+h ) else gfx.a = 0.8 F_Get_SSV(gui.color.white, true) gfx.triangle(st_x, y+h*data_t_it*drawscale, end_x,y+h*data_t_it2*drawscale, end_x,y, st_x, y ) gfx.a = 0.2 F_Get_SSV(gui.color[col_peaks]) gfx.lineto(x+(i+1)*w/arr_size, y-h*data_t_it2^data2.scaling_pow*drawscale) end if is_ref then gfx.a = 0.05 gfx.triangle(st_x, y+h-h*data_t_it^data2.scaling_pow*drawscale, end_x,y+h-h*data_t_it^data2.scaling_pow*drawscale, end_x,y+h, st_x, y+h ) else gfx.a = 0.05 F_Get_SSV(gui.color.white, true) gfx.triangle(st_x, y+h*data_t_it^data2.scaling_pow*drawscale, end_x,y+h*data_t_it2^data2.scaling_pow*drawscale, end_x,y, st_x, y ) end end -- envelope building end gfx.x,gfx.y = x,y gfx.blurto(x+w,y+h) gfx.muladdrect(x-1,y,w+2,h, 1,--mul_r, 1.0,--mul_g, 1.0,--mul_b, 1.5,--mul_a, 0,--add_r, 0,--add_g, 0,--add_b, 0)--add_a) if not is_ref then -- draw sep points if pointsarray ~= nil then local pointsarr_size = reaperarray.get_alloc(pointsarray) local tri_h = 5 local tri_w = tri_h F_Get_SSV(gui.color.blue, true) gfx.a = 0.4 for i = 1, pointsarr_size-1 do if pointsarray[i] == 1 then gfx.line(x + i*w/pointsarr_size, y, x + i*w/pointsarr_size, y+h - tri_h - 1) gfx.triangle(x + i*w/pointsarr_size, y+h-tri_h, x + i*w/pointsarr_size, y+h, x + i*w/pointsarr_size + tri_w, y+h) end end end --pointsarray ~= nil then -- draw gate threshold F_Get_SSV(gui.color.black, true) gfx.a = 0.5 gfx.line(x, y+h*data2.threshold*drawscale, x+w, y+h*data2.threshold*drawscale) F_Get_SSV(gui.color.green_dark, true) gfx.a = 0.5 gfx.rect(x, y, w, h*data2.threshold*drawscale) end -- back gfx.a = 0.4 gfx.blit(3, 1, 0, --backgr 0,0,objects.main_w, objects.main_h, xywh[1],xywh[2],xywh[3],xywh[4], 0,0) end ----------------------------------------------------------------------- ----------------------------------------------------------------------- function GUI_button2(objects, gui, xywh, name, issel, font, text_alpha, color_str) local w1_sl_a gfx.y,gfx.x = 0,0 -- frame gfx.a = 0.1 F_Get_SSV(gui.color.white, true) gfx.rect(xywh[1],xywh[2],xywh[3], xywh[4], 0 , gui.aa) -- back if issel then gfx.a = 0.7 else gfx.a = 0.3 end gfx.blit(3, 1, 0, --backgr 0,0,objects.main_w, objects.main_h, xywh[1],xywh[2],xywh[3],xywh[4], 0,0) -- txt gfx.setfont(1, gui.fontname, font) gfx.a = text_alpha F_Get_SSV(gui.color[color_str], true) local measurestrname = gfx.measurestr(name) local x0 = xywh[1] + (xywh[3] - measurestrname)/2 local y0 = xywh[2] + (xywh[4] - gui.b_sel_fontsize)/2 gfx.x, gfx.y = x0,y0 gfx.drawstr(name) end ----------------------------------------------------------------------- function GUI_slider(objects, gui, xywh, val, alpha, col, takes_t) if val == nil then val = 0 end gfx.y,gfx.x = 0,0 -- frame gfx.a = 0.1 * alpha F_Get_SSV(gui.color.white, true) gfx.rect(xywh[1],xywh[2],xywh[3], xywh[4], 1 , gui.aa) -- center line gfx.a = 0.5 * alpha F_Get_SSV(gui.color[col], true) local sl_w = 3 gfx.rect(xywh[1],xywh[2]+ (xywh[4]- sl_w) / 2,xywh[3], sl_w, 1 , gui.aa) local handle_w = 20 if takes_t ~= nil and takes_t[2] ~= nil then -- blit grad local x_offs = xywh[1] + (xywh[3] - handle_w) * val gfx.a = 0.8 * alpha gfx.blit(3, 1, math.rad(180), --backgr 0,0,objects.main_w, objects.main_h, x_offs,xywh[2],handle_w/2,xywh[4], 0,0) gfx.blit(3, 1, math.rad(0), --backgr 0,0,objects.main_w, objects.main_h, x_offs+handle_w/2,xywh[2],handle_w/2,xywh[4], 0,0) end -- grid local gr_h = 20 for i = 0, 1, 0.1 do gfx.a = 0.3 * alpha F_Get_SSV(gui.color.white, true) gfx.line(handle_w/2 + xywh[1] + (xywh[3]-handle_w) * i, xywh[2] + xywh[4]/2 - gr_h*i - 1, handle_w/2 + xywh[1] + (xywh[3]-handle_w) * i, xywh[2] + xywh[4]/2 + gr_h*i-1 ) end end ----------------------------------------------------------------------- function GUI_text(xywh, gui, objects, f_name, f_size, name, has_frame) gfx.setfont(1, f_name, f_size) local measurestrname = gfx.measurestr(name) local x0 = xywh[1] + (xywh[3] - measurestrname)/2 local y0 = xywh[2]+(xywh[4]-gfx.texth)/2 if has_frame then -- text back gfx.a = 0.9 F_Get_SSV(gui.color.back, true) gfx.rect(x0-objects.x_offset,y0,measurestrname+objects.x_offset*2,gfx.texth) end -- text gfx.x, gfx.y = x0,y0 gfx.a = 0.9 F_Get_SSV(gui.color.white, true) gfx.drawstr(name) end ----------------------------------------------------------------------- function gfx_rect(x,y,w,h) gfx.x, gfx.y = x,y gfx.line(x,y, x+w-1, y) gfx.line(x+w,y, x+w, y+h-2) gfx.line(x+w,y+h-1, x-1, y+h-1) gfx.line(x-1,y+h-2, x-1, y) end ----------------------------------------------------------------------- function GUI_selector(xywh,col,val,b1,b2 ) F_Get_SSV(col, true) gfx.a = 0.3 local x,y,w,h = xywh[1], xywh[2], xywh[3], xywh[4] gfx_rect(x,y,w,h) gfx.a = 0.4 gfx.rect(xywh[1] + 2, xywh[2]+2+ (xywh[4]/2-2)*val, xywh[3]-4, (xywh[4]-4)/2,1,1) gfx.a = 1 gfx.x = xywh[1] + (xywh[3]- gfx.measurestr(b1)) /2 gfx.y = xywh[2] +2 gfx.drawstr(b1) gfx.a = 1 gfx.x = xywh[1] + (xywh[3]- gfx.measurestr(b2)) /2 gfx.y = xywh[2] + 1+ xywh[4] /2 gfx.drawstr(b2) end ----------------------------------------------------------------------- function GUI_knob(objects, xywh,gui, val, text,text_val, col, is_active) if is_active == 0 then is_active = 0.3 end if val == nil then val = 0 end x,y ,w, h = xywh[1],xywh[2],xywh[3],xywh[4] arc_r = w/2 * 0.8 ang_gr = 120 ang_val = math.rad(-ang_gr+ang_gr*2*val) ang = math.rad(ang_gr) -- back gfx.a = 0.01+0.3*is_active F_Get_SSV(gui.color[col], true) gfx.rect(x,y ,w, h, 1) gfx.a = 0.2 gfx.blit(7, 1, math.rad(180), -- backgr 0,0,objects.main_w, objects.main_h, x,y ,w, h, 0,0) -- arc back for i = 0, 3, 0.4 do if is_active then gfx.a = 0.03 else gfx.a = 0.005 end F_Get_SSV(gui.color.white) -- why THE HELL original gfx.arc() looks like SHIT? -- gfx.arc(x+w/2-1,y+h/2+1,arc_r-i, math.rad(-ang_gr),math.rad(-90), gui.aa) gfx.arc(x+w/2-1,y+h/2-1,arc_r-i, math.rad(-90),math.rad(0), gui.aa) gfx.arc(x+w/2+1,y+h/2-1,arc_r-i, math.rad(0),math.rad(90), gui.aa) gfx.arc(x+w/2+1,y+h/2+1,arc_r-i, math.rad(90),math.rad(ang_gr), gui.aa) end if is_active then gfx.a = 0.4 else gfx.a = 0.03 end local ang_val = math.rad(-ang_gr+ang_gr*2*val) for i = 0, 3, 0.4 do F_Get_SSV(gui.color[col], true) if ang_val < math.rad(-90) then gfx.arc(x+w/2-1,y+h/2+1,arc_r-i, math.rad(-ang_gr),ang_val, gui.aa) else if ang_val < math.rad(0) then gfx.arc(x+w/2-1,y+h/2+1,arc_r-i, math.rad(-ang_gr),math.rad(-90), gui.aa) gfx.arc(x+w/2-1,y+h/2-1,arc_r-i, math.rad(-90),ang_val, gui.aa) else if ang_val < math.rad(90) then gfx.arc(x+w/2-1,y+h/2+1,arc_r-i, math.rad(-ang_gr),math.rad(-90), gui.aa) gfx.arc(x+w/2-1,y+h/2-1,arc_r-i, math.rad(-90),math.rad(0), gui.aa) gfx.arc(x+w/2+1,y+h/2-1,arc_r-i, math.rad(0),ang_val, gui.aa) else if ang_val < math.rad(ang_gr) then gfx.arc(x+w/2-1,y+h/2+1,arc_r-i, math.rad(-ang_gr),math.rad(-90), gui.aa) gfx.arc(x+w/2-1,y+h/2-1,arc_r-i, math.rad(-90),math.rad(0), gui.aa) gfx.arc(x+w/2+1,y+h/2-1,arc_r-i, math.rad(0),math.rad(90), gui.aa) gfx.arc(x+w/2+1,y+h/2+1,arc_r-i, math.rad(90),ang_val, gui.aa) else gfx.arc(x+w/2-1,y+h/2+1,arc_r-i, math.rad(-ang_gr),math.rad(-90), gui.aa) gfx.arc(x+w/2-1,y+h/2-1,arc_r-i, math.rad(-90),math.rad(0), gui.aa) gfx.arc(x+w/2+1,y+h/2-1,arc_r-i, math.rad(0),math.rad(90), gui.aa) gfx.arc(x+w/2+1,y+h/2+1,arc_r-i, math.rad(90),math.rad(ang_gr), gui.aa) end end end end end -- text gfx.setfont(1, gui.fontname, gui.knob_txt) text_len = gfx.measurestr(text) gfx.x, gfx.y = x+(w-text_len)/2,y+h-gfx.texth-2 gfx.drawstr(text) -- text gfx.setfont(1, gui.fontname, gui.knob_txt-2) text_len = gfx.measurestr(text_val) gfx.x, gfx.y = x+(w-text_len)/2,y+(h-gfx.texth)/2 gfx.drawstr(text_val) end ----------------------------------------------------------------------- function DEFINE_GUI_buffers() local is_sel local objects = DEFINE_objects() update_gfx_minor = true -- GUI variables local gui = {} gui.aa = 1 gfx.mode = 0 gui.fontname = 'Calibri' gui.fontsize = 23 if OS == "OSX32" or OS == "OSX64" then gui.fontsize = gui.fontsize - 7 end -- selector buttons gui.b_sel_fontsize = gui.fontsize - 1 gui.b_sel_text_alpha = 1 gui.b_sel_text_alpha_unset = 0.7 if OS == "OSX32" or OS == "OSX64" then gui.knob_txt = gui.fontsize - 5 else gui.knob_txt = gui.fontsize - 8 end -- reg buttons gui.b_text_alpha = 0.8 gui.b3_text_alpha = 0.8 -- takenames gui.b_takenames_fontsize = gui.fontsize - 3 gui.color = {['back'] = '51 51 51', ['back2'] = '51 63 56', ['black'] = '0 0 0', ['green'] = '102 255 102', ['blue'] = '127 204 255', ['white'] = '255 255 255', ['red'] = '204 76 51', ['green_dark'] = '102 153 102', ['yellow'] = '200 200 0', ['pink'] = '200 150 200', } ----------------------------------------------------------------------- -- buffers -- 1 main back -- 2 select windows -- 3 button back gradient -- 4 wind 1 -- 5 wait window -- 6 envelopes -- 7 buffer back -- 8 about -- 9 knobs -- buf1 background if update_gfx then fdebug('DEFINE_GUI_buffers_1-mainback') gfx.dest = 1 gfx.setimgdim(1, -1, -1) gfx.setimgdim(1, objects.main_w, objects.main_h+objects.set_wind_h) gfx.a = 0.92 F_Get_SSV(gui.color.back, true) gfx.rect(0,0, objects.main_w, objects.main_h+objects.set_wind_h,1) end -- buf3 -- buttons back gradient if update_gfx then fdebug('DEFINE_GUI_buffers_3-buttons back') gfx.dest = 3 gfx.setimgdim(3, -1, -1) gfx.setimgdim(3, objects.main_w, objects.main_h) gfx.a = 1 local r,g,b,a = 0.9,0.9,1,0.6 gfx.x, gfx.y = 0,0 local drdx = 0.00001 local drdy = 0 local dgdx = 0.0001 local dgdy = 0.0003 local dbdx = 0.00002 local dbdy = 0 local dadx = 0.0003 local dady = 0.0004 gfx.gradrect(0,0,objects.main_w, objects.main_h, r,g,b,a, drdx, dgdx, dbdx, dadx, drdy, dgdy, dbdy, dady) end -- buf 4 -- general buttons / sliders / info if update_gfx_minor then gfx.dest = 4 gfx.setimgdim(4, -1, -1) gfx.setimgdim(4, objects.main_w, objects.main_h+objects.set_wind_h) if data.compact_view == 0 then mode_name = '+' else mode_name = '-' end GUI_button2(objects, gui, objects.b_setup,mode_name, mouse.context == 'w1_settings_b', gui.b_sel_fontsize, 0.7, 'white') GUI_button2(objects, gui, objects.b_get, 'Get', mouse.context == 'w1_get_b', gui.b_sel_fontsize, 0.8, 'green') GUI_button2(objects, gui, objects.pref_actions,'Menu', mouse.context == 'settings_actions', gui.b_sel_fontsize, 0.8, 'green') GUI_button2(objects, gui, objects.pref_donate,'Donate', mouse.context == 'pref_donate', gui.b_sel_fontsize, 0.8, 'green') GUI_slider(objects, gui, objects.b_slider, w1_slider, 1,'green', takes_t) if data.current_window == 4 then GUI_slider(objects, gui, objects.b_slider2, w2_slider, 1,'green', takes_t) end if takes_t ~= nil and takes_t[2] ~= nil then -- display navigation if mouse.context == 'w1_disp' then -- take names GUI_text(objects.disp_ref_text, gui, objects, gui.fontname, gui.b_takenames_fontsize, 'Reference: '..takes_t[1].name:sub(0,30), true) local d_name = 'Dub: ' if takes_t[3] ~= nil then d_name = 'Dubs ('..math.floor(data.current_take-1)..'/'..(#takes_t - 1)..'): ' end GUI_text(objects.disp_dub_text, gui, objects, gui.fontname, gui.b_takenames_fontsize, d_name..takes_t[data.current_take].name:sub(0,30), true) end -- display cursor position gfx.a = 0.9 F_Get_SSV(gui.color.red, true) gfx.line(F_limit(objects.disp[1] + objects.disp[3] / takes_t[1].len * (play_pos - takes_t[1].pos), objects.disp[1], objects.disp[1] + objects.disp[3]), objects.disp[2], F_limit(objects.disp[1] + objects.disp[3] / takes_t[1].len * (play_pos - takes_t[1].pos), objects.disp[1], objects.disp[1] + objects.disp[3]), objects.disp[2]+objects.disp[4]) end end -- item envelope gradient if update_gfx then gfx.dest = 7 gfx.setimgdim(7, -1, -1) gfx.setimgdim(7, objects.main_w, objects.main_h) gfx.gradrect(0,0, objects.main_w, objects.main_h, 1,1,1,0.9, 0,0,0,0.00001, 0,0,0,-0.005) end -- buf 6 static envelopes buttons if update_gfx then fdebug('DEFINE_GUI_buffers_6-envelopes') gfx.dest = 6 gfx.setimgdim(6, -1, -1) gfx.setimgdim(6, objects.main_w, objects.main_h) GUI_item_display (objects, gui, objects.disp_ref , takes_arrays[1], true, takes_points[1], 'green' ) GUI_item_display (objects, gui, objects.disp_dub , takes_arrays[data.current_take], false , takes_points[data.current_take], 'green') end -- buf 5 wait if trig_process ~= nil and trig_process == 1 then gfx.dest = 5 gfx.setimgdim(5, -1, -1) gfx.setimgdim(5, objects.main_w, objects.main_h+objects.set_wind_h) gfx.a = 0.93 F_Get_SSV(gui.color.back, true) gfx.rect(0,0, objects.main_w, objects.main_h+objects.set_wind_h,1) F_Get_SSV(gui.color.white, true) local str = 'Analyzing takes. Please wait...' gfx.setfont(1, gui.fontname, gui.fontsize) gfx.x = (objects.main_w - gfx.measurestr(str))/2 gfx.y = (objects.main_h-gfx.texth)/2 gfx.drawstr(str) end -- buf 9 knobs if update_gfx then fdebug('DEFINE_GUI_buffers_9-knobs') gfx.dest = 9 gfx.setimgdim(9, -1, -1) gfx.setimgdim(9, objects.main_w, objects.main_h+objects.set_wind_h) -- detect points GUI_knob(objects, objects.knob1,gui, data.scaling_pow_norm, 'Scaling', data.scaling_pow_norm, 'green',1) GUI_knob(objects, objects.knob2,gui, data.threshold_norm, 'Threshold', data2.threshold, 'green',1) GUI_knob(objects, objects.knob3,gui, data.rise_area_norm, 'Rise Area', math.floor(data2.rise_area * 1000)..'ms', 'green', 1) GUI_knob(objects, objects.knob4,gui, data.risefall_norm, 'Rise/Fall', data2.risefall, 'green', 1) GUI_knob(objects, objects.knob5,gui, data.risefall2_norm, 'Rise/Fall 2', data2.risefall2, 'green', 1) GUI_knob(objects, objects.knob6,gui, data.filter_area_norm, 'Filter Area', math.floor(data2.filter_area * 1000)..'ms', 'green', 1) gfx.a = 0.5 F_Get_SSV(gui.color.green_dark, true) gfx_rect(objects.pref_rect1[1], objects.pref_rect1[2], objects.pref_rect1[3], objects.pref_rect1[4],0) -- search area GUI_knob(objects, objects.knob7, gui, data.search_area_norm, 'Search area', math.floor(data2.search_area * 1000)..'ms', 'red', 1) gfx.a = 0.5 F_Get_SSV(gui.color.red, true) gfx_rect(objects.pref_rect2[1], objects.pref_rect2[2], objects.pref_rect2[3], objects.pref_rect2[4],0) -- selector GUI_selector(objects.selector,gui.color.blue,data.mode,'RMS', 'FFT') GUI_selector(objects.selector2,gui.color.blue,data.alg,'Algo 1','Algo 2') -- rms/fft gfx.a = 0.5 F_Get_SSV(gui.color.blue, true) gfx_rect(objects.pref_rect3[1], objects.pref_rect3[2], objects.pref_rect3[3], objects.pref_rect3[4],0) local bin = 22050/data2.fft_size GUI_knob(objects, objects.knob9,gui, data.custom_window_norm, 'RMS wind.', math.floor(data2.custom_window * 1000)..'ms', 'blue', math.abs(1-data.mode)) GUI_knob(objects, objects.knob10,gui, data.fft_size_norm, 'FFT size', data2.fft_size, 'blue', math.abs(data.mode)) GUI_knob(objects, objects.knob11,gui, data.fft_HP_norm, 'HP', math.floor((data2.fft_HP-1)*bin)..'Hz', 'blue', math.abs(data.mode)) GUI_knob(objects, objects.knob12,gui, data.fft_LP_norm, 'LP', math.floor((data2.fft_LP-1)*bin)..'Hz', 'blue', math.abs(data.mode)) GUI_knob(objects, objects.knob13,gui, data.smooth_norm, 'Smooth', (data2.smooth*100)..'%', 'blue', 1) end ------------------ -- common buf20 -- ------------------ gfx.dest = 20 gfx.setimgdim(20, -1,-1) gfx.setimgdim(20, objects.main_w, objects.main_h+objects.set_wind_h) -- common gfx.a = 1 gfx.blit(1, 1, 0, -- backgr 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0) gfx.blit(6, 1, 0, -- main window static 0,0,objects.main_w, objects.main_h, 0,0,objects.main_w, objects.main_h, 0,0) gfx.blit(4, 1, 0, -- main window dynamic 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0) gfx.blit(9, 1, 0, -- main window dynamic 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0) if trig_process ~= nil and trig_process == 1 then gfx.blit(5, 1, 0, --wait 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0,objects.main_w, objects.main_h+objects.set_wind_h, 0,0) end gfx.blit(19, 1, 0, --TEST 0,0,objects.main_w, objects.main_h, 0,0,objects.main_w, objects.main_h, 0,0) update_gfx = false end ----------------------------------------------------------------------- function GUI_DRAW() local objects = DEFINE_objects() --fdebug('GUI_DRAW') -- common buffer gfx.dest = -1 gfx.a = 1 gfx.x,gfx.y = 0,0 gfx.blit(20, 1, 0, 0,0, objects.main_w, objects.main_h+objects.set_wind_h, 0,0, objects.main_w, objects.main_h+objects.set_wind_h, 0,0) gfx.update() end ----------------------------------------------------------------------- function MOUSE_match(b) if mouse.mx > b[1] and mouse.mx < b[1]+b[3] and mouse.my > b[2] and mouse.my < b[2]+b[4] then return true end end ----------------------------------------------------------------------- function F_open_URL(url) if OS=="OSX32" or OS=="OSX64" then os.execute("open ".. url) else os.execute("start ".. url) end end ----------------------------------------------------------------------- function GUI_menu_settings() local menuret local default_data = DEFINE_global_variables() local menuret = gfx.showmenu( '#Actions' ..'|Restore defaults' ..'|Preset 1 : macro alignment' ..'|Preset 2 : tiny alignment' ..'||>Links' ..'|MPL on Cockos Forum' ..'|MPL on VK' ..'|MPL on SoundCloud' ..'|Warping tool thread on Cockos forum' ..'|<Warping tool thread on RMM forum' ..'||#Info' ..'|Parameters description' ) -- actions local act_count = 2 if menuret == 2 then -- restore defaults data = DEFINE_global_variables() ENGINE_set_ini(data, config_path) updata_gfx = true end if menuret == 3 then -- preset1 data = DEFINE_global_variables() data.filter_area_norm = 0.58 data.rise_area_norm = 0.42 data.risefall_norm = 0.115 data.risefall2_norm = 0.3 data.search_area_norm = 0.9 ENGINE_set_ini(data, config_path) updata_gfx = true end if menuret == 4 then -- preset1 data = DEFINE_global_variables() data.filter_area_norm = 0.01 data.search_area_norm = 0.01 ENGINE_set_ini(data, config_path) updata_gfx = true end -- links if menuret == 3+act_count then F_open_URL('http://forum.cockos.com/member.php?u=70694') end if menuret == 4+act_count then F_open_URL('http://vk.com/michael_pilyavskiy') end if menuret == 5+act_count then F_open_URL('http://soundcloud.com/mp57') end if menuret == 6+act_count then F_open_URL('http://forum.cockos.com/showthread.php?t=171658') end if menuret == 7+act_count then F_open_URL('http://rmmedia.ru/threads/121230/') end par_str = [[ Algorithm based on the matching RMS envelopes of 2+ takes and use stretch markers to move points of syllables start/end to make them match some reference take. Green knobs are parameters for detection points. Note, RMS envelope of course have some window, so aligning for example drums with this tool is not a good idea. Basically points added when envelope rise/fall by some value in defined area. - Scaling. Detection syllables starts/ends easier with scaled envelopes, since scaling actually compress range. When comparing data, script also use scaled envelopes and not original ones. - Threshold is linear "noise floor" for detected points. It represented on the graph. - Rise area. If signal rise/fall by value defined with Rise/Fall and Rise/Fall2 in this area, point (=stretch marker) will be created. - Rise/Fall is gain/attenuation when checking Rise area for scaled envelope. - Rise/Fall is gain/attenuation when checking Rise area for original envelope. - Filter area - is minimal space beetween detected points. Red knob is parameter of comparing part of this script. - Search area. This defines searcing area for every found point when finding best RMS fit. Blue knobs related to building envelope - First selector allow to change type envelope beetween RMS envelope and FFT (sum of bin values) envelope. - Second selector allow to change algorithm. First algo get every block and find best fit by moving center point using original block position. Second algo use same technique, but get blocks one-by one and find best fit relative to previously stretched blocks. - RMS window is how much samples taken to calculate average for every envelope point. - FFT size is number of FFT bins. - HP and LP are FFT filter cutoff controls. - Smooth knob control smoothing final envelope. ]] -- info if menuret == 9+act_count then reaper.MB(par_str, 'Parameters',0) end end ----------------------------------------------------------------------- function GUI_menu_display(takes_t) local takesstr = '' for i = 1, #takes_t do if i == 1 then takesstr = takesstr..'Reference: '..takes_t[i].name..'||' else takesstr = takesstr..'Dub #'..(i-1)..': '..takes_t[i].name..'|' end end local ret_menu = gfx.showmenu(takesstr) if ret_menu >1 then data.current_take = ret_menu update_gfx = true end end ----------------------------------------------------------------------- function ENGINE_clear_takes_data() -- clear data takes_t = {} takes_arrays = {} takes_points = {} str_markers_t = {} end ----------------------------------------------------------------------- function MOUSE_get() local objects = DEFINE_objects() local ret -- ENGINE_prepare_takes response mouse.mx = gfx.mouse_x mouse.my = gfx.mouse_y mouse.LMB_state = gfx.mouse_cap&1 == 1 mouse.RMB_state = gfx.mouse_cap&2 == 2 mouse.MMB_state = gfx.mouse_cap&64 == 64 mouse.LMB_state_doubleclick = false mouse.Ctrl_LMB_state = gfx.mouse_cap&5 == 5 mouse.Ctrl_state = gfx.mouse_cap&4 == 4 mouse.Alt_state = gfx.mouse_cap&17 == 17 -- alt + LB mouse.wheel = gfx.mouse_wheel if mouse.LMB_state and not mouse.last_LMB_state then mouse.last_mx_onclick = mouse.mx mouse.last_my_onclick = mouse.my end if mouse.last_mx_onclick ~= nil and mouse.last_my_onclick ~= nil then mouse.dx = mouse.mx - mouse.last_mx_onclick mouse.dy = mouse.my - mouse.last_my_onclick else mouse.dx, mouse.dy = 0,0 end if not mouse.LMB_state then mouse.context = nil end if not mouse.LMB_state then mouse.context2 = nil end if mouse.last_LMB_state and not mouse.LMB_state then mouse.last_touched = nil end mouse.last_mx = 0 mouse.last_my = 0 -- get takes -- display if MOUSE_match(objects.disp) then mouse.context = 'w1_disp' end if takes_t~= nil and takes_t[2] ~= nil then if MOUSE_match(objects.disp) and mouse.LMB_state and not mouse.last_LMB_state then gfx.x = mouse.mx gfx.y = mouse.my GUI_menu_display(takes_t) end end -- settings button if MOUSE_match(objects.b_setup) then mouse.context = 'w1_settings_b' end if MOUSE_match(objects.b_setup) and mouse.LMB_state and not mouse.last_LMB_state then compact_view_trig = true data.compact_view = math.abs(data.compact_view-1) ENGINE_set_ini(data, config_path) end -- actions if MOUSE_match(objects.pref_actions) then mouse.context = 'settings_actions' end if MOUSE_match(objects.pref_actions) and mouse.LMB_state and not mouse.last_LMB_state then gfx.x, gfx.y = mouse.mx, mouse.my GUI_menu_settings() end -- selector if MOUSE_match(objects.selector) and mouse.LMB_state and not mouse.last_LMB_state then data.mode = math.abs(1 - data.mode) ENGINE_set_ini(data, config_path) update_gfx = true end -- selector if MOUSE_match(objects.selector2) and mouse.LMB_state and not mouse.last_LMB_state then data.alg = math.abs(1 -data.alg) ENGINE_set_ini(data, config_path) update_gfx = true end -- donate if MOUSE_match(objects.pref_donate) then mouse.context = 'pref_donate' end if MOUSE_match(objects.pref_donate) and mouse.LMB_state and not mouse.last_LMB_state then F_open_URL('http://www.paypal.me/donate2mpl') end -- get button if MOUSE_match(objects.b_get) then mouse.context = 'w1_get_b' end if MOUSE_match(objects.b_get) and mouse.LMB_state and not mouse.last_LMB_state then if trig_process == nil then trig_process = 1 end end if trig_process ~= nil and trig_process == 1 and not mouse.LMB_state then ret = ENGINE_prepare_takes() if ret == 1 then takes_t = ENGINE_get_takes() if #takes_t ~= 1 and #takes_t >= 2 then str_markers_t = {} pos_offsets = {} rates = {} for i = 1, #takes_t do takes_arrays[i] = ENGINE_get_take_data(i, data.scaling_pow2) if i > 1 then takes_points[i] = ENGINE_get_take_data_points2(takes_arrays[i],data.global_window_sec) str_markers_t[i] = _G['ENGINE_compare_data2_alg'..data.alg](takes_arrays[1], takes_arrays[i], takes_points[i],data.global_window_sec ) end end end end update_gfx = true trig_process = nil w1_slider = 0 end -- strength / apply slider 1 if takes_t ~= nil then if MOUSE_match(objects.b_slider) and mouse.LMB_state and not mouse.last_LMB_state then mouse.context = 'w1_slider' end if mouse.context == 'w1_slider' then w1_slider = F_limit((mouse.mx - objects.b_slider[1]) / objects.b_slider[3],0,1 ) if str_markers_t then for i = 1, #takes_t do if i == 1 then ENGINE_set_stretch_markers2(i, str_markers_t[i], 0) else ENGINE_set_stretch_markers2(i, str_markers_t[i], w1_slider) end end end end end -- knobs function MOUSE_knob(objects, i, var) if MOUSE_match(objects['knob'..i]) and mouse.LMB_state and not mouse.last_LMB_state then mouse.context2 = 'knob'..i abs_var = data[var] end if mouse.context2 == 'knob'..i then data[var] = F_limit(abs_var + knob_coeff * -mouse.dy,0,1) ENGINE_set_ini(data, config_path) end end MOUSE_knob(objects, 1, 'scaling_pow_norm') MOUSE_knob(objects, 2, 'threshold_norm') MOUSE_knob(objects, 3, 'rise_area_norm') MOUSE_knob(objects, 4, 'risefall_norm') MOUSE_knob(objects, 5, 'risefall2_norm') MOUSE_knob(objects, 6, 'filter_area_norm') MOUSE_knob(objects, 7, 'search_area_norm') MOUSE_knob(objects, 9, 'custom_window_norm') MOUSE_knob(objects, 10, 'fft_size_norm') MOUSE_knob(objects, 11, 'fft_HP_norm') MOUSE_knob(objects, 12, 'fft_LP_norm') MOUSE_knob(objects, 13, 'smooth_norm') mouse.last_LMB_state = mouse.LMB_state mouse.last_RMB_state = mouse.RMB_state mouse.last_MMB_state = mouse.MMB_state mouse.last_Ctrl_LMB_state = mouse.Ctrl_LMB_state mouse.last_Ctrl_state = mouse.Ctrl_state mouse.last_wheel = mouse.wheel end ----------------------------------------------------------------------- function MAIN_defer() DEFINE_dynamic_variables() DEFINE_GUI_buffers() GUI_DRAW() MOUSE_get() if char == 27 then MAIN_exit() end --escape if char == 32 then reaper.Main_OnCommandEx(40044, 0,0) end -- space-> transport play if char ~= -1 then reaper.defer(MAIN_defer) else MAIN_exit() end end function DEFINE_objects() -- GUI global local objects = {} objects.x_offset = 5 objects.y_offset = 5 objects.main_h = 237 objects.main_w_nav = 130 -- width navigation zone objects.takes_name_h = 60 -- display H objects.takes_name_h2 = 20 -- display names objects.disp_item_h = 80 objects.slider_h = 60 objects.get_b_h = 70 objects.get_b_h2 = 50 objects.get_b_w = 100 objects.b_setup_h = 20 objects.tips_w = 100 objects.knob_count = 7 objects.knob_w = 65 objects.main_w = objects.knob_count*objects.knob_w + objects.x_offset*12 objects.knob_h = 90 objects.pref_actions_w = 400 -- GUI main window objects.disp_ref = {objects.x_offset, objects.y_offset, objects.main_w-(objects.x_offset*2), objects.disp_item_h} objects.disp_dub = {objects.x_offset, objects.y_offset+objects.disp_item_h, objects.main_w-(objects.x_offset*2), objects.disp_item_h} objects.disp = {objects.disp_ref[1], objects.disp_ref[2], objects.disp_ref[3], objects.disp_ref[4]*2} objects.b_get = {objects.x_offset, objects.disp[2]+objects.disp[4]+objects.y_offset, objects.get_b_w, objects.slider_h/2-2} objects.b_setup = {objects.x_offset, objects.b_get[2]+objects.b_get[4]+objects.y_offset, objects.get_b_w, objects.slider_h/2} objects.b_slider = {(objects.x_offset*2+objects.get_b_w), objects.b_get[2]+1, objects.main_w-(objects.x_offset*3+objects.get_b_w), objects.slider_h} objects.disp_ref_text = {objects.disp_ref[1], objects.disp_ref[2]+objects.disp_ref[4]-objects.takes_name_h2, objects.disp_ref[3], objects.takes_name_h2} objects.disp_dub_text = {objects.disp_dub[1], objects.disp_dub[2], objects.disp_dub[3], objects.takes_name_h2} -- GUI settings for i = 0, 16 do if i == 6 then offs = 2 else offs = 0 end if i > 6 then x_comp = objects.main_w-objects.x_offset*5 y_offs = objects.knob_h else x_comp = 0 y_offs = 0 end objects['knob'..(i+1)] = {objects.x_offset*2 + objects.knob_w * i + objects.x_offset*i + objects.x_offset*offs-x_comp, objects.b_setup[2]+objects.b_setup[4]+objects.y_offset*2+y_offs, objects.knob_w, objects.knob_h-objects.y_offset*3} end -- seleector1 local selector_w = objects.knob8[3] - objects.x_offset*2 local selector_h = objects.knob8[4]/2-2 objects.selector = {objects.knob8[1]+objects.x_offset, objects.knob8[2], selector_w, selector_h } objects.selector2 = {objects.knob8[1]+objects.x_offset, objects.knob8[2]+selector_h+objects.y_offset, selector_w, selector_h } -- pref rect objects.pref_rect1 = {objects.x_offset, objects.b_setup[2]+objects.b_setup[4]+objects.y_offset, objects.knob_w*6+objects.x_offset*7, objects.knob_h-objects.y_offset} objects.pref_rect2 = {objects.x_offset*9 + objects.knob_w*6, objects.b_setup[2]+objects.b_setup[4]+objects.y_offset, objects.knob_w+objects.x_offset*2, objects.knob_h-objects.y_offset} objects.pref_rect3 = {objects.x_offset, objects.pref_rect2[2]+objects.pref_rect2[4]+objects.y_offset, objects.knob_w*6+objects.x_offset*7, objects.knob_h-objects.y_offset} objects.pref_actions = {objects.x_offset*9 + objects.knob_w*6, objects.pref_rect2[2]+objects.pref_rect2[4]+objects.y_offset, objects.knob_w+objects.x_offset*2, objects.knob_h/2-objects.y_offset} objects.pref_donate = {objects.x_offset*9 + objects.knob_w*6, objects.pref_rect2[2]+objects.pref_rect2[4]+objects.knob_h/2+objects.y_offset, objects.knob_w+objects.x_offset*2, objects.knob_h/2-objects.y_offset} objects.set_wind_h = 180 return objects end ----------------------------------------------------------------------- function F_ret_ini_val2(content, ini_key, var, default_data) local out_str ,str for line in content:gmatch("[^\r\n]+") do str = line:match(ini_key..'=.*') if str ~= nil then out_str = str:gsub(ini_key..'=','') break end end if out_str == nil or tonumber(out_str) == nil then out_str = default_data[var] end data[var] = tonumber(out_str) if data.alg == 0 then data.alg = 1 end -- 1.06 strange thing but should works end ----------------------------------------------------------------------- function ENGINE_set_ini(data, config_path) -------- LINK TO INI outstr_data = '' for i,v in pairs(data) do outstr_data = outstr_data..'\n' ..i..'='..data[i] end outstr = '[MPL_Align_takes_config_current]\n'..outstr_data fdebug('ENGINE_set_ini >>>') fdebug(outstr) local file = io.open(config_path,'w') file:write(outstr) file:close() update_gfx = true end ----------------------------------------------------------------------- function ENGINE_get_ini(config_path) --local ret, str update_gfx = true local file = io.open(config_path, 'r') content = file:read('*all') file:close() fdebug('ENGINE_get_ini <<< ') fdebug(content) --msg(content) local default_data = DEFINE_global_variables() for i,v in pairs(data) do F_ret_ini_val2(content, i, i, default_data) end end ----------------------------------------------------------------------- function DEFINE_global_variables() takes_arrays = {} takes_points = {} -------- DEFINE VARS local data = {} local data2 = {} data.current_take = 2 -- show second take as dub knob_coeff = 0.01 -- knob sensivity data.mode = 0 -- 0 - RMS / 1 - FFT data.alg = 1 data.custom_window_norm = 0 -- rms window data.fft_size_norm = 0.5 data.fft_HP_norm = 0 data.fft_LP_norm = 1 data.smooth_norm = 0 data.filter_area_norm = 0.1 -- filter closer points data.rise_area_norm = 0.2 -- detect rise on this area data.risefall_norm = 0.125 -- how much envelope rise/fall in rise area - for scaled env data.risefall2_norm = 0.3 -- how much envelope rise/fall in rise area - for original env data.threshold_norm = 0.1 -- noise floor for scaled env data.scaling_pow_norm = 0.9 -- scaling - normalised RMS values scaled via power of this value (after convertion)) data.search_area_norm = 0.1 data.compact_view = 0 -- default mode data.xpos = 200 data.ypos = 200 return data,data2 end ----------------------------------------------------------------------- function MAIN_search_ini(data) fdebug('MAIN_search_ini') local reapath = reaper.GetResourcePath():gsub('\\','/') local t = debug.getinfo(1) config_path = t.source:gsub('.lua', '.ini'):sub(2) local file = io.open(config_path, 'r') if file == nil then ENGINE_set_ini(data, config_path) else ENGINE_get_ini(config_path) file:close() end end ----------------------------------------------------------------------- debug_mode = 0 if debug_mode == 1 then msg("") end mouse = {} data,data2 = DEFINE_global_variables() MAIN_search_ini(data) objects = DEFINE_objects() gfx.init("mpl Align takes // "..vrs, objects.main_w, objects.main_h, 0, data.xpos, data.ypos) objects = nil update_gfx = true compact_view_trig = true MAIN_defer()