--[[ * ReaScript Name: Advanced Tempo Tool: Fit Bars In Time Selection * Description: See title. * Instructions: Run * Screenshot: http://i.giphy.com/l3nWpXT6Irpe4jDYQ.gif * Author: swiiscompos, very awesome GUI: JRENG! * Author URI: * Repository: * Repository * File URI: * Licence: GPL v3 * Forum Thread: Advanced tempo tool: fit bars in time selection * Forum Thread URI: http://forum.cockos.com/showthread.php?t=170987 * REAPER: 5.0 * Extensions: None * Version: 1.1 --]] --[[ * Changelog: * v1.1 (2016-01-14) + moved "- BEAT" button to the left + added some header metadatas # fixed a bug with +/- BEAT buttons * v1.2 (14-Jan-2014) ------------ JRENG! # bug fix textbox curState # add key TAB to cyle texboxes # add key ENTER to apply --]] function mouseGUI () mouseHold,cmd,opt,ctrl,shift,mouseClick,lastCap=gfx.mouse_cap&1,gfx.mouse_cap&4,gfx.mouse_cap&16,gfx.mouse_cap&32,gfx.mouse_cap&8,(mouseHold-lastCap),mouseHold end function setcolor(i) gfx.set(((i>>16)&0xFF)/0xFF, ((i>>8)&0xFF)/0xFF, (i&0xFF)/0xFF) end function collision_rect(_) return gfx.mouse_x>=_.x and gfx.mouse_x<=(_.x+_.w) and gfx.mouse_y>=_.y and gfx.mouse_y<=(_.y+_.h) end function textBox_draw(_) setcolor(_.bgcol) gfx.rect(_.x,_.y,_.w,_.h,true) setcolor(_.hasfocus and _.fgfcol or _.fgcol) gfx.rect(_.x,_.y,_.w,_.h,false) gfx.setfont(_.font) setcolor(_.txtcol) local w,h=gfx.measurestr(_.text) local w,h=gfx.measurestr(_.text) local x,y=_.x+_.l,_.y+(_.h-h)/2 gfx.x,gfx.y=x,y gfx.drawstr(_.text) if _.sel ~= 0 then local sc,ec=_.caret,_.caret+_.sel if sc > ec then sc,ec=ec,sc end local sx=gfx.measurestr(string.sub(_.text,0,sc)) local ex=gfx.measurestr(string.sub(_.text,0,ec)) setcolor(_.txtcol) gfx.rect(ox+sx,oy,ex-sx,sc+1,ec) setcolor(_.bgcol) gfx.x,gfx.y=ox+sx,oy gfx.drawstr(string.sub(_.text,sc+1,ec)) end if _.hasfocus then local blink=25 if _.curState < blink/2 then setcolor(_.curscol) w=gfx.measurestr(string.sub(_.text,0,_.caret)) gfx.line(_.x+_.l+w,_.y+2,_.x+_.l+w,_.y+_.h-_.l) end _.curState=(_.curState+1)%blink end textBox_mouse(_) end function textBox_caret(_) local len=string.len(_.text) for i=1, len do w=gfx.measurestr(string.sub(_.text,1,i)) if gfx.mouse_x<_.x+_.l+w then return i-1 end end return len end function textBox_mouseDown(_) _.hasfocus=collision_rect(_) if _.hasfocus then _.caret=textBox_caret(_) _.curState=0 end _.sel=0 end function textBox_doubleClick(_) local len=string.len(_.text) _.caret=len ; _.sel=-len end function textBox_mouseMove(_) _.sel=textBox_caret(_)-_.caret end function textBox_char (_,c) if _.sel ~=0 then local sc,ec=_.caret,_.caret+_.sel if sc>ec then sc,ec=ec,sc end _.text=string.sub(_.text,1,sc)..string.sub(_.text,ec+1) _.sel=0 end if c == 0x6C656674 then -- left arrow if _.caret > 0 then _.caret=_.caret-1 end elseif c == 0x72676874 then -- right arrow if _.caret < string.len(_.text) then _.caret=_.caret+1 end elseif c == 8 then -- backspace if _.caret > 0 then _.text=string.sub(_.text,1,_.caret-1)..string.sub(_.text,_.caret+1) _.caret=_.caret-1 end elseif c >= 46 and c <= 57 and string.len(_.text) < _.maxlen then _.text=string.format("%s%c%s", string.sub(_.text,1,_.caret), c, string.sub(_.text,_.caret+1)) _.caret=_.caret+1 end end function mouseDown(_) textBox_mouseDown(_) _.mouseDown=true ; _.mouseCapcnt=0 _.ox,_.oy=gfx.mouse_x,gfx.mouse_y end function mouseDoubleClick(_) if _.hasfocus then textBox_doubleClick(_) end end function mouseMove(_) if _.hasfocus then textBox_mouseMove(_) end _.lx,_.ly=gfx.mouse_x,gfx.mouse_y _.mouseCapcnt=_.mouseCapcnt+1 end function mouseUp(_) _.mouseDown=false _.mouseUptime=os.clock() end function textBox_mouse(_) if mouseHold == 1 then if not _.mouseDown then mouseDown(_) if _.mouseUptime and os.clock()-_.mouseUptime < 0.25 then mouseDoubleClick(_) end elseif gfx.mouse_x ~= _.lx or gfx.mouse_y ~= _.ly then mouseMove(_) end elseif _.mouseDown then mouseUp(_) end end function mouseToggleButton (_) if collision_rect (_) and mouseClick==1 then _.val=_.val+1 _.val=_.val%2 end end function mouseButton (_) if collision_rect (_) and mouseClick==1 then _.val=1 else _.val=0 end end function toggleButton (_) mouseToggleButton(_) if _.val == 1 then gfx.set(.15,1,0,0.85) gfx.rect(_.x,_.y,_.w,_.h,false) gfx.rect(_.x+2,_.y+2,_.w-4,_.h-4,true) local labelW,labelH=gfx.measurestr(_.label2) gfx.x=_.x+_.w+5 gfx.y=_.y+_.h/2-labelH/2 gfx.drawstr(_.label2) elseif _.val == 0 then gfx.set(1,1,1,0.5) gfx.a=0.5 gfx.rect(_.x,_.y,_.w,_.h,false) local labelW,labelH=gfx.measurestr(_.label1) gfx.x=_.x+_.w+5 gfx.y=_.y+_.h/2-labelH/2 gfx.drawstr(_.label1) end end function button (_) mouseButton(_) if _.val == 0 then gfx.set(.15,1,0,0.85) gfx.rect(_.x,_.y,_.w,_.h,false) gfx.rect(_.x+2,_.y+2,_.w-4,_.h-4,true) local labelW,labelH=gfx.measurestr(_.label) gfx.set(0,0,0,1) gfx.x=_.x+_.w/2-labelW/2 gfx.y=_.y+_.h/2-labelH/2 gfx.drawstr(_.label) elseif _.val == 1 then gfx.set(1,1,1,0.5) gfx.a=0.5 gfx.rect(_.x,_.y,_.w,_.h,false) local labelW,labelH=gfx.measurestr(_.label) gfx.x=_.x+_.w/2-labelW/2 gfx.y=_.y+_.h/2-labelH/2 gfx.drawstr(_.label) end end function info (_) gfx.set(.15,1,0,0.5) gfx.rect(_.x,_.y,_.w,_.h,false) gfx.a=0.15 gfx.rect(_.x,_.y,_.w,_.h,true) local labelW,labelH=gfx.measurestr(_.label) gfx.x=_.x+_.w/2-labelW/2 gfx.y=_.y+_.h/2-labelH/2 gfx.a=.75 gfx.drawstr(_.label) end function addRemoveBeats() if added_beats ~= 0 then total_beats = inc_bar + added_beats if total_beats >= 0 then inc_bar = total_beats % time_sig inc_bar_b = inc_bar * time_sig_den_b if inc_bar ~= 0 then n_bars_norm = n_bars_norm + math.floor(total_beats/time_sig) else n_bars_norm = n_bars_norm + total_beats/time_sig end elseif total_beats < 0 then inc_bar = (total_beats % time_sig) inc_bar_b = inc_bar * time_sig_den_b if inc_bar ~= 0 then n_bars_norm = n_bars_norm - 1 + math.floor(total_beats/time_sig) else n_bars_norm = n_bars_norm + total_beats/time_sig end end end end function maths1() time_sig_den_b = 4 / time_sig_den time_sig_b = time_sig * time_sig_den_b t_min = t_sec/60 q = math.floor(t_tempo * t_min / time_sig_b) r = (t_tempo * t_min - q * time_sig_b) inc_bar = math.floor(r / time_sig_den_b + 0.5) inc_bar_b = inc_bar * time_sig_den_b if a == 0 then if r<(time_sig/2) then n_bars_norm = q else n_bars_norm = q + 1 end else n_bars_norm = q end end function maths2() if a == 0 then tempo = n_bars_norm * time_sig_b / t_min else tempo = (n_bars_norm * time_sig_b + inc_bar_b) / t_min end end function deleteExistingTempoMarkers() delete_check = true while (delete_check == true) do exist_tempo_marker = reaper.FindTempoTimeSigMarker(0, end_time-1e-5) local retval, time_position, measureposOut, beatposOut, bpmOut, timesig_numOut, timesig_denomOut, lineartempoOut = reaper.GetTempoTimeSigMarker(0, exist_tempo_marker) if time_position >= start_time then delete_check = reaper.DeleteTempoTimeSigMarker(0, exist_tempo_marker) else delete_check = false end end end function insertNewTempoMarkersComplete() if a == 1 then if b == 0 then tempo_marker_pos = start_time + 60 / tempo*inc_bar_b else tempo_marker_pos = start_time end else tempo_marker_pos = start_time end reaper.AddTempoTimeSigMarker(0, tempo_marker_pos, tempo, time_sig, time_sig_den, false) reaper.AddTempoTimeSigMarker(0, end_time, tempo, time_sig, time_sig_den, false) end function insertNewTempoMarkerShorter() if b == 0 then short_tempo_marker_pos = start_time else short_tempo_marker_pos = start_time + n_bars_norm*time_sig_b*60/tempo end reaper.AddTempoTimeSigMarker(0, short_tempo_marker_pos, tempo, inc_bar, time_sig_den, false) end function createMeasure () maths1() if apply==1 then deleteExistingTempoMarkers() end if a == 1 then addRemoveBeats() maths2() if b == 0 then if apply==1 then insertNewTempoMarkerShorter() insertNewTempoMarkersComplete() end else if apply==1 then insertNewTempoMarkersComplete() insertNewTempoMarkerShorter() end end else maths2() if apply==1 then insertNewTempoMarkersComplete() end end if apply==1 then reaper.GetSet_LoopTimeRange(true, false, start_time, end_time, false) end -- JRENG! set time selection back to original selection reaper.UpdateTimeline() end function init() a = 0 -- Tick box. Allows creation of shorter bar. 1 is enabled, 0 disabled. Default is 0. When unticked, added_beats should be set to 0. b = 0 -- Switch to set shorter bar position. 0 means short bar is at the beginning, 1 at the end compound_meter = 0 --see top. t_tempo = 120.00 --text input, target tempo. When modified, added_beats should be set to 0 time_sig = 4 -- text input, time signature. Force intergers. Default is 4 time_sig_den = 4 -- text input, time signature denominator. Default is 4 added_beats = 0 -- 2 buttons. 1 to add one beat, the other one to remove one beat lastCap=1 mouseHold=0 apply=0 n_bars_old=0 gfx.init("tempo tool",440,190) focus=0 timesel_i={x=10,y=10,w=gfx.w-20,h=18,label="PLEASE CREATE TIME SELECTION, BOSS"} ts_i={x=10,y=10,w=130,h=timesel_i.h,label="TIME SIGNATURE"} ts_num={x=ts_i.x+ts_i.w+5,y=ts_i.y,w=30,h=18,l=4,maxlen=2,fgcol=0x000000,fgfcol=0x00FF00,bgcol=0x292929,txtcol=0xFFFFFF,curscol=0xFFFFFF,font=1,fontsz=14,caret=0,sel=0,curState=0,text="4",hasfocus=false} ts_denom={x=ts_num.x+ts_num.w+5,y=ts_num.y,w=ts_num.w,h=18,l=4,maxlen=2,fgcol=0x000000,fgfcol=0x00FF00,bgcol=0x292929,txtcol=0xFFFFFF,curscol=0xFFFFFF,font=1,fontsz=14,caret=0,sel=0,curState=0,text="4",hasfocus=false} tempo_i={x=ts_denom.x+ts_denom.w+10,y=ts_i.y,w=130,h=timesel_i.h,label="TARGET TEMPO"} tempot={x=tempo_i.x+tempo_i.w+5,y=ts_i.y,w=75,h=18,l=4,maxlen=7,fgcol=0x000000,fgfcol=0x00FF00,bgcol=0x292929,txtcol=0xFFFFFF,curscol=0xFFFFFF,font=1,fontsz=14,caret=0,sel=0,curState=0,text="120",hasfocus=false} short_label="ALLOW SHORTER BAR" shortbar={x=ts_i.x,y=ts_i.y+35,w=18,h=18,val=0,label1=short_label,label2=short_label} barpos_i={x=shortbar.x,y=shortbar.y+25,w=130,h=shortbar.h,label="POSITION"} barpos={x=barpos_i.x+barpos_i.w+5,y=barpos_i.y,w=18,h=timesel_i.h,val=0,label1="START",label2="END"} minus={x=barpos_i.x,y=barpos_i.y+25,w=97,h=18,val=0,label="- BEAT"} plus={x=minus.x+minus.w+5,y=minus.y,w=minus.w,h=minus.h,val=0,label="+ BEAT"} comp="COMPOUND METER" compound={x=tempo_i.x,y=tempo_i.y+30,val=0,w=shortbar.w,h=shortbar.h,label1=comp,label2=comp} calc={x=10,y=130,w=gfx.w-20,h=18,label=""} app={x=gfx.w/2-80,y=160,w=160,h=18,label="CREATE MEASURE"} end function main() gfx.set(1,1,1,.2)gfx.line(10,35,gfx.w-10,35) gfx.set(1,1,1,.2)gfx.line(10,120,gfx.w-10,120) mouseGUI() c=gfx.getchar() --- GET TIME SELECTION EDGES start_time, end_time = reaper.GetSet_LoopTimeRange(false, false, 0, 0, false) --- IF TIME SELECTION if start_time == end_time then info(timesel_i) else t_sec = end_time-start_time textBox_draw(ts_num)textBox_draw(ts_denom)textBox_draw(tempot)toggleButton(shortbar)info(ts_i)info(tempo_i)button(app)apply=app.val if shortbar.val==1 then info(barpos_i)toggleButton(barpos) a=shortbar.val b=barpos.val button(plus) button(minus) if plus.val==1 then added_beats=added_beats+1 end if minus.val==1 then added_beats=added_beats-1 end end if ts_num.hasfocus and c==9 then ts_num.hasfocus=false ts_denom.hasfocus=true elseif ts_denom.hasfocus and c==9 then ts_denom.hasfocus=false tempot.hasfocus=true elseif tempot.hasfocus and c==9 then tempot.hasfocus=false ts_num.hasfocus=true end if ts_num.hasfocus then textBox_char(ts_num, c) time_sig=tonumber(ts_num.text) end if ts_denom.hasfocus then textBox_char(ts_denom, c) time_sig_den=tonumber(ts_denom.text) end if tempot.hasfocus then textBox_char(tempot, c) t_tempo = tonumber(tempot.text) end if t_tempo~=nil and time_sig ~=nil and time_sig_den~=nil then createMeasure() if time_sig % 3 == 0 and time_sig_den == 8 then toggleButton(compound) compound_meter = compound.val if compound_meter==1 then t_tempo = tonumber(tempot.text) * 3 / 2 else t_tempo=(tempot.text) end else compound_meter = 0 end if shortbar.val == 0 or shortbar.val == 1 and inc_bar == 0 then if n_bars_norm ~= nil then calci=(n_bars_norm.." x "..time_sig.."/"..time_sig_den..", tempo: "..string.format("%.3f",tempo)) end elseif b == 0 then if inc_bar ~= nil then calci=("1 x "..inc_bar.."/"..time_sig_den.." + "..n_bars_norm.."x"..time_sig.."/"..time_sig_den..", tempo: "..string.format("%.3f",tempo)) end elseif b == 1 then if n_bars_norm ~= nil then calci=(n_bars_norm.."x"..time_sig.."/"..time_sig_den.." + 1 x "..inc_bar.."/"..time_sig_den..", tempo: "..string.format("%.3f",tempo)) end end if calci ~= nil then info(calc) calc.label=calci end if c==13 then apply=1 createMeasure() end end if apply==1 then gfx.quit() reaper.Undo_OnStateChangeEx("Create measure from time selection",-1,-1) end end if c ~=27 and apply~=1 then reaper.defer(main) else gfx.quit () end gfx.update() end --RUN init() main()