-- @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' ..'|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()