Skip to content

Commit a1ec44f

Browse files
authored
Release Perfect Timing! - Audio Quantizer v0.41 (#1703)
- Improved Visualizer performance when zoomed out on long waveforms - Fixed additional cases of incorrect grid drawing in the Visualizer - Simplified gridscan logic from "choose closest and most powerful transient" to "choose closest transient"
1 parent f726bbf commit a1ec44f

1 file changed

Lines changed: 107 additions & 88 deletions

File tree

Items Editing/80icio_Perfect Timing! - Audio Quantizer.lua

Lines changed: 107 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
-- @description Perfect Timing! - Audio Quantizer
22
-- @author 80icio
3-
-- @version 0.40
3+
-- @version 0.41
44
-- @changelog
5-
-- - Improved transient detection with more accurate peak and RMS measurements,
6-
-- DC offset correction, and smarter retrigger behavior
7-
-- - Major speed improvements for filters (lowcut, hicut, transient, gain) -
8-
-- up to 2x faster processing
9-
-- - Fixed incorrect grid drawing in the internal visualizer
5+
-- - Improved Visualizer performance when zoomed out on long waveforms
6+
-- - Fixed additional cases of incorrect grid drawing in the Visualizer
7+
-- - Simplified gridscan logic from "choose closest and most powerful transient" to "choose closest transient"
108
-- @link Forum thread https://forum.cockos.com/showthread.php?t=288964
119
-- @about
1210
-- # PERFECT TIMING!
@@ -1453,7 +1451,7 @@ local output ={}
14531451

14541452
for i=1, h do
14551453
self.grid_diff_temp ={}
1456-
self.rms_battle = {}
1454+
--self.rms_battle = {}
14571455
local idtotal ={}
14581456
if IsEven(i)==false then
14591457
for c = 1, #input, 2 do
@@ -1462,14 +1460,15 @@ local output ={}
14621460

14631461
if diff <= grid_dist then
14641462
self.grid_diff_temp[#self.grid_diff_temp+1] = diff
1465-
self.rms_battle[#self.rms_battle+1] = input[c+1][1]
1463+
--self.rms_battle[#self.rms_battle+1] = input[c+1][1]
14661464
idtotal[#idtotal+1] = c
14671465
end
14681466
end
14691467
end
14701468

1471-
id = getHighest(self.rms_battle)
1472-
1469+
--id = getHighest(self.rms_battle)
1470+
id = getLowest(self.grid_diff_temp)
1471+
14731472
if idtotal[id] then
14741473
output[#output+1] = input[idtotal[id]]
14751474
output[#output+1] = input[(idtotal[id])+1]
@@ -1529,7 +1528,7 @@ function Create_Grid_table()
15291528

15301529
local blocklineQN_check = abs(blocklineQN - floor(blocklineQN))
15311530

1532-
if blocklineQN_check <1e-13 or blocklineQN_check > 1-1e-13 then
1531+
if blocklineQN_check < 1e-10 or blocklineQN_check > 1-1e-10 then
15331532
Grid_blocks_Ruler_thickness[h] = 2
15341533
else
15351534
Grid_blocks_Ruler_thickness[h] = 1
@@ -2664,11 +2663,12 @@ function thresh_histogram() ---------- draw Threshold PEAK Histogram
26642663
end
26652664
local LRarrow = 0
26662665
function waveform_visualizer()
2667-
2666+
2667+
local total_samples = #Wave.out_buf
26682668
local w = m_window_width - ImGui.StyleVar_CellPadding-2
26692669
local y_pos = 0
26702670
local y_neg = 0
2671-
local max_zoom_h = floor(#Wave.out_buf/(m_window_width*2))
2671+
local max_zoom_h = floor(total_samples/(m_window_width*2))
26722672

26732673
local i_array = 1
26742674

@@ -2702,105 +2702,124 @@ function waveform_visualizer()
27022702

27032703
local mouse_wheel_v, mouse_wheel_h = ImGui.GetMouseWheel( ctx )
27042704

2705-
if ImGui.IsMouseClicked( ctx, 0 ) or mouse_wheel_v~=0 or mouse_wheel_h~=0 then -------- zoom when click left
2706-
new_zoom_pos_range = (1/waveform_zoom_h)
2707-
new_zoom_pos_start = (startsample/#Wave.out_buf)
2708-
local x_mouse, _ = r.GetMousePosition(ctx)
2709-
x_mouse = x_mouse-histo_x
2710-
x_mouse = x_mouse/histo_w
2711-
if x_mouse>1 then x_mouse=1 end
2712-
if x_mouse<0 then x_mouse=0 end
2713-
local x_mouse_rel = (x_mouse * new_zoom_pos_range) + new_zoom_pos_start
2714-
h_zoom_center = x_mouse_rel
2715-
h_zoom_absolute_center = x_mouse
2716-
samplesdifference = h_zoom_center-h_zoom_absolute_center
2717-
end
2718-
2719-
2720-
if ImGui.IsMouseDragging( ctx, 1 ) then ------right click dragging
2721-
-- ImGui.SetMouseCursor( ctx, ImGui.MouseCursor_Hand )
2722-
local x_delta_rc, _ = ImGui.GetMouseDelta( ctx )
2723-
h_zoom_center = h_zoom_center - x_delta_rc/w/waveform_zoom_h
2724-
2725-
right_click_dragging = true
2726-
else
2727-
right_click_dragging = false
2728-
end
2729-
2730-
if h_zoom_center>1 then h_zoom_center = 1 end
2731-
if h_zoom_center<0 then h_zoom_center = 0 end
2705+
if ImGui.IsMouseClicked(ctx, 0) or mouse_wheel_v~=0 or mouse_wheel_h~=0 then -------- zoom when click left and drag down
2706+
new_zoom_pos_range = 1 / waveform_zoom_h
2707+
new_zoom_pos_start = startsample / total_samples
2708+
2709+
local x_mouse = r.GetMousePosition(ctx) - histo_x
2710+
x_mouse = x_mouse / histo_w
2711+
2712+
if x_mouse > 1 then x_mouse = 1 end
2713+
if x_mouse < 0 then x_mouse = 0 end
2714+
2715+
local x_mouse_rel = (x_mouse * new_zoom_pos_range) + new_zoom_pos_start
2716+
h_zoom_center = x_mouse_rel
2717+
h_zoom_absolute_center = x_mouse
2718+
samplesdifference = x_mouse_rel - x_mouse
2719+
end
27322720

27332721

2734-
if ImGui.IsMouseDragging( ctx, 0 ) or mouse_wheel_v~=0 or mouse_wheel_h~=0 then -------------------get horizontal zoom value
2735-
h_zoom_dragging = true
2736-
local x_delta, y_delta = ImGui.GetMouseDelta( ctx )
2737-
mouse_wheel_h = (mouse_wheel_h^3)*-1
2738-
mouse_wheel_h=min(mouse_wheel_h,600)
2739-
mouse_wheel_h=max(mouse_wheel_h,-600)
2740-
2741-
local zoom_factor_y = (y_delta/10) / histo_h
2742-
local zoom_factor_wheel = mouse_wheel_v / histo_h
2743-
waveform_zoom_h = waveform_zoom_h + (zoom_factor_y + zoom_factor_wheel) * (max_zoom_h / 2)
2744-
--waveform_zoom_h = waveform_zoom_h^2/2
2745-
--waveform_zoom_h = waveform_zoom_h + (y_delta/histo_h)*(max_zoom_h/2) + (mouse_wheel_v/histo_h)*(max_zoom_h/2)
2746-
moving_h = floor(((x_delta^3)/waveform_zoom_h) + (mouse_wheel_h*100/waveform_zoom_h)) + moving_h
2747-
2748-
if waveform_zoom_h>max_zoom_h then waveform_zoom_h=max_zoom_h end
2749-
if waveform_zoom_h<min_visualzer_rangeview then waveform_zoom_h=min_visualzer_rangeview end
2750-
else
2751-
h_zoom_dragging = false
2752-
moving_h = 0
2753-
end
2722+
if ImGui.IsMouseDragging(ctx, 0) or mouse_wheel_v~=0 or mouse_wheel_h~=0 then
2723+
h_zoom_dragging = true
2724+
local x_delta, y_delta = ImGui.GetMouseDelta(ctx)
2725+
2726+
-- Process horizontal wheel (cube, negate, clamp)
2727+
mouse_wheel_h = mouse_wheel_h * mouse_wheel_h * mouse_wheel_h * -1
2728+
if mouse_wheel_h > 600 then mouse_wheel_h = 600 end
2729+
if mouse_wheel_h < -600 then mouse_wheel_h = -600 end
2730+
2731+
-- Cache inverse values
2732+
local inv_histo_h = 1 / histo_h
2733+
local inv_zoom_h = 1 / waveform_zoom_h
2734+
2735+
2736+
local delta_scaling = 0.01 * (total_samples/zoomedsamples)^0.5
2737+
2738+
-- Calculate zoom
2739+
local zoom_factor_y = (y_delta * delta_scaling) * inv_histo_h
2740+
local zoom_factor_wheel = mouse_wheel_v * delta_scaling * inv_histo_h
2741+
waveform_zoom_h = waveform_zoom_h + (zoom_factor_y + zoom_factor_wheel) * (max_zoom_h * 0.5)
2742+
2743+
-- Calculate movement
2744+
local x_cubed = x_delta * x_delta * x_delta
2745+
moving_h = floor((x_cubed * inv_zoom_h) + (mouse_wheel_h * 100 * inv_zoom_h)) + moving_h
2746+
2747+
-- Clamp zoom
2748+
if waveform_zoom_h > max_zoom_h then waveform_zoom_h = max_zoom_h end
2749+
if waveform_zoom_h < min_visualzer_rangeview then waveform_zoom_h = min_visualzer_rangeview end
2750+
else
2751+
h_zoom_dragging = false
2752+
moving_h = 0
2753+
end
27542754

27552755

27562756
else
27572757
ImGui.SetMouseCursor( ctx, ImGui.MouseCursor_Arrow )
27582758
end
2759-
2759+
27602760
if w_check ~= w or cyclecount==1 or h_zoom_dragging or LRarrow~=0 or right_click_dragging then ---or v_zoom_dragging then
27612761
line_array_pos = {}
27622762
line_array_neg = {}
27632763

2764-
zoomedsamples = floor(#Wave.out_buf/waveform_zoom_h)
2764+
zoomedsamples = floor(total_samples/waveform_zoom_h)
27652765

2766-
startsample = floor(#Wave.out_buf*((1-(1/waveform_zoom_h))*h_zoom_center+(samplesdifference/waveform_zoom_h)))
2766+
startsample = floor(total_samples*((1-(1/waveform_zoom_h))*h_zoom_center+(samplesdifference/waveform_zoom_h)))
27672767
startsample = startsample+moving_h
27682768

27692769
if startsample<0 then startsample = 0 end
27702770

27712771
local endsample = zoomedsamples+startsample
27722772

2773-
if endsample>#Wave.out_buf then
2774-
endsample=#Wave.out_buf ; startsample = #Wave.out_buf-zoomedsamples
2773+
if endsample>total_samples then
2774+
endsample=total_samples ; startsample = total_samples-zoomedsamples
27752775
end
27762776

27772777
local w_zoom = (w)/(zoomedsamples)
2778+
2779+
local sample_count = endsample - startsample
2780+
local threshold = 5000000 -- adjust this based on testing
27782781

2779-
for i = 1+startsample, endsample do
2780-
local x_poly = floor(i*w_zoom)
2781-
local x_poly_next = floor((i+1)*w_zoom)
2782-
2783-
if x_poly == x_poly_next then
2784-
2785-
if Wave.out_buf[i]>=(y_pos) then
2786-
y_pos = Wave.out_buf[i]
2787-
end
2788-
if Wave.out_buf[i]<=(y_neg) then
2789-
y_neg = Wave.out_buf[i]
2790-
end
2791-
2792-
else
2793-
2794-
line_array_pos[i_array] = y_pos
2795-
2796-
line_array_neg[i_array] = y_neg
2797-
2798-
y_pos = 0
2799-
y_neg = 0
2800-
i_array = i_array+1
2782+
if sample_count < threshold then
2783+
-- DETAILED MODE: Your current precise function
2784+
local x_poly = floor(startsample * w_zoom)
2785+
for i = 1+startsample, endsample do
2786+
local x_poly_next = floor((i+1)*w_zoom)
2787+
local val = Wave.out_buf[i]
28012788

2789+
if x_poly == x_poly_next then
2790+
if val > y_pos then y_pos = val end
2791+
if val < y_neg then y_neg = val end
2792+
else
2793+
line_array_pos[i_array] = y_pos
2794+
line_array_neg[i_array] = y_neg
2795+
i_array = i_array + 1
2796+
y_pos, y_neg = 0, 0
2797+
x_poly = x_poly_next
2798+
end
28022799
end
2803-
end
2800+
2801+
else
2802+
-- FAST MODE: Same logic as detailed mode but with stride
2803+
local stride = max(10, floor(sample_count / 100000)) -- adjust divisor for speed vs quality
2804+
2805+
local x_poly = floor(startsample * w_zoom)
2806+
for i = 1+startsample, endsample, stride do
2807+
local x_poly_next = floor((i+1)*w_zoom)
2808+
local val = Wave.out_buf[i]
2809+
2810+
if x_poly == x_poly_next then
2811+
if val > y_pos then y_pos = val end
2812+
if val < y_neg then y_neg = val end
2813+
else
2814+
line_array_pos[i_array] = y_pos
2815+
line_array_neg[i_array] = y_neg
2816+
i_array = i_array + 1
2817+
y_pos, y_neg = 0, 0
2818+
x_poly = x_poly_next
2819+
end
2820+
end
2821+
end
2822+
28042823
end
28052824

28062825
w_check = w

0 commit comments

Comments
 (0)