Guest User

tearing test

a guest
Jan 14th, 2026
14
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.62 KB | Source Code | 0 0
  1. desc:Playhead Tearing Test
  2.  
  3. slider1:0<0,2,1{Soft (Cubic),Soft + Hard (50/50 Blend),Hard}>Clipping Type
  4. slider2:0<-30,30,0.1>Input Gain (dB)
  5. slider3:-18<-30,0,0.1>Ceiling (dB)
  6. slider4:0<-30,30,0.1>Output Gain (dB)
  7. slider5:0<0,1,1{Off,On}>Link Input and Output Gain
  8. slider6:100<0,100,1>Dry/Wet (%)
  9.  
  10. in_pin:left input
  11. in_pin:right input
  12. out_pin:left output
  13. out_pin:right output
  14.  
  15. @init
  16. // Initialize variables
  17. ext_noinit = 1;
  18.  
  19. // Track peak reduction separately for left and right channels
  20. peak_gr_l = 0;
  21. peak_gr_r = 0;
  22. peak_gr_readout_l = 0;
  23. peak_gr_readout_r = 0;
  24. peak_gr_readout_hold_time_l = 0;
  25. peak_gr_readout_hold_time_r = 0;
  26. max_peak_gr_readout_l = 0;
  27. max_peak_gr_readout_r = 0;
  28. peak_decay_rate = 1 - (1 - 0.999) * (48000 / srate); // 20.83....ms decay time constant
  29.  
  30. // Meter bar hold system
  31. meter_hold_gr_l = 0;
  32. meter_hold_gr_r = 0;
  33. meter_hold_time_l = 0;
  34. meter_hold_time_r = 0;
  35. meter_decay_rate = 1 - (1 - 0.999) * (48000 / srate); // 20.83....ms decay time constant
  36.  
  37. // Meter bar display values after hold processing
  38. meter_gr_l = 0;
  39. meter_gr_r = 0;
  40.  
  41. // Playback detection for max reset
  42. last_play_state = 0;
  43. current_play_state = 0;
  44. last_cap = 0; // For button click detection
  45.  
  46. // Store previous values for link controls
  47. prev_slider2 = 0;
  48. prev_slider4 = 0;
  49. prev_slider5 = 0;
  50.  
  51. // Track if links are newly enabled
  52. input_output_link_initialized = 0;
  53.  
  54. // Store offset values for linked controls
  55. input_output_offset = 0;
  56.  
  57. // Track if a reset has occurred
  58. reset_occurred_input_output = 0;
  59.  
  60. // Current values for smoothing (to prevent zipper noise)
  61. current_input_gain = 1;
  62. current_output_gain = 1;
  63. current_dry_wet = dry_wet = slider6 / 100;
  64.  
  65. // Initialize the gain reduction values to 1 (no reduction) on plugin load
  66. gr_l = 1;
  67. gr_r = 1;
  68.  
  69. // Bypass smoothing variables
  70. bypass_mix = 0; // 0 = effect on, 1 = bypassed
  71. current_bypass_mix = 0;
  72.  
  73. // Delta mode variables
  74. delta_mode = 0;
  75. current_delta_mode = 0;
  76.  
  77. // Convert dB to linear
  78. function db_to_linear(db) (
  79. 10^(db/20);
  80. );
  81.  
  82. // Convert linear to dB
  83. function linear_to_db(linear) (
  84. 20*log10(max(linear, 0.000001));
  85. );
  86.  
  87. // Soft clipping function
  88. function soft_clip(x) (
  89. abs_x = abs(x);
  90. abs_x < 1.5 ? (
  91. x - (4.0/27.0) * x * x * x; // Cubic
  92. ) : (
  93. x >= 0 ? 1.0 : -1.0; // Hard
  94. );
  95. );
  96.  
  97. // Hard clipping function
  98. function hard_clip(x) (
  99. x >= 0 ? min(x, 1.0) : max(x, -1.0);
  100. );
  101.  
  102. // Main processing function
  103. function process_sample(sample, ceiling, shape) (
  104. // Step 1: Scale UP to make clipping algorithms work at ±1.0 range
  105. normalized = sample / ceiling;
  106.  
  107. // Step 2: Apply the clipping algorithms at their designed range
  108. soft_result = soft_clip(normalized);
  109. hard_result = hard_clip(normalized);
  110.  
  111. // Step 3: Blend between algorithms
  112. shape_norm = shape / 100;
  113. blended = soft_result * (1 - shape_norm) + hard_result * shape_norm;
  114.  
  115. // Step 4: Scale back down to ceiling level
  116. blended * ceiling;
  117. );
  118.  
  119. @slider
  120. // Store original values to detect which slider changed
  121. last_changed = -1;
  122.  
  123. slider2 != prev_slider2 ? last_changed = 2;
  124. slider4 != prev_slider4 ? last_changed = 4;
  125. slider5 != prev_slider5 ? last_changed = 5;
  126.  
  127. slider2 = min(max(slider2, -30), 30); // Apply limits to Input Gain slider
  128. slider3 = min(max(slider3, -30), 0); // Apply limits to Ceiling slider
  129. slider4 = min(max(slider4, -30), 30); // Apply limits to Output Gain slider
  130. slider6 = min(max(slider6, 0), 100); // Apply limits to Dry/Wet slider
  131.  
  132. // Handle linking for input/output gain
  133. slider5 == 1 ? (
  134. // Initialize the offset when link is first enabled
  135. prev_slider5 == 0 ? (
  136. input_output_offset = slider4 - (-slider2);
  137. input_output_link_initialized = 1;
  138. );
  139.  
  140. // Check for double-click reset (value exactly 0)
  141. slider2 == 0 && last_changed == 2 && prev_slider2 != 0 ? (
  142. slider4 = 0; // Both go to zero regardless of offset
  143. reset_occurred_input_output = 1; // Flag that we've reset
  144. input_output_offset = 0; // Reset the offset
  145. ) :
  146. slider4 == 0 && last_changed == 4 && prev_slider4 != 0 ? (
  147. slider2 = 0; // Both go to zero regardless of offset
  148. reset_occurred_input_output = 1; // Flag that we've reset
  149. input_output_offset = 0; // Reset the offset
  150. ) :
  151. // Normal case - maintain offset with inverse relationship
  152. last_changed == 2 ? (
  153. slider4 = -slider2 + input_output_offset;
  154. ) :
  155. last_changed == 4 ? (
  156. slider2 = -(slider4 - input_output_offset);
  157. );
  158. ) : (
  159. // If linking was just disabled, store the current values
  160. input_output_link_initialized = 0;
  161. reset_occurred_input_output = 0;
  162. );
  163.  
  164. // Update stored values
  165. prev_slider2 = slider2;
  166. prev_slider4 = slider4;
  167. prev_slider5 = slider5;
  168.  
  169. // Convert slider values
  170. clipping_type = slider1 == 0 ? 0 : (slider1 == 1 ? 50 : 100); // 0% = Soft (Cubic), 50% = Soft + Hard (50/50 Blend), 100% = Hard
  171. input_gain = db_to_linear(slider2);
  172. ceiling = db_to_linear(slider3);
  173. output_gain = db_to_linear(slider4);
  174. dry_wet = slider6 / 100;
  175. bypass_mix = slider7 >= 0.5 ? 1 : 0; // 0 = effect on, 1 = bypassed
  176. delta_mode = slider8 >= 0.5 ? 1 : 0; // 0 = off, 1 = on (wet - dry)
  177.  
  178. @block
  179. // Smooth Input Gain transitions
  180. input_gain_step = (input_gain - current_input_gain)/samplesblock;
  181. smoothed_input_gain = current_input_gain;
  182. current_input_gain = input_gain;
  183.  
  184. // Smooth dry/wet amount transitions
  185. dry_wet_step = (dry_wet - current_dry_wet)/samplesblock;
  186. smoothed_dry_wet = current_dry_wet;
  187. current_dry_wet = dry_wet;
  188.  
  189. // Smooth Output Gain transitions
  190. output_gain_step = (output_gain - current_output_gain)/samplesblock;
  191. smoothed_output_gain = current_output_gain;
  192. current_output_gain = output_gain;
  193.  
  194. // Detect playback start to reset max values
  195. current_play_state = play_state;
  196. current_play_state > 0 && last_play_state == 0 ? (
  197. max_peak_gr_readout_l = 0;
  198. max_peak_gr_readout_r = 0;
  199. );
  200. last_play_state = current_play_state;
  201.  
  202. @sample
  203. // Apply input gain with smoothing
  204. smoothed_input_gain += input_gain_step;
  205. spl0 *= smoothed_input_gain;
  206. spl1 *= smoothed_input_gain;
  207.  
  208. // Store dry signal after input gain
  209. spl0_dry = spl0;
  210. spl1_dry = spl1;
  211.  
  212. // Store pre-clipping levels for gain reduction calculation
  213. // abs() converts negative amplitudes to positive values for correct gain reduction calculations
  214. pre_clip_l = abs(spl0);
  215. pre_clip_r = abs(spl1);
  216.  
  217. // Apply clipping
  218. spl0 = process_sample(spl0, ceiling, clipping_type);
  219. spl1 = process_sample(spl1, ceiling, clipping_type);
  220.  
  221. // Store wet signal after clipping
  222. spl0_wet = spl0;
  223. spl1_wet = spl1;
  224.  
  225. // === Dry/Wet Mix ===
  226. // Smooth dry/wet to prevent zipper noise from real-time adjustments
  227. smoothed_dry_wet += dry_wet_step;
  228. // Mix dry and wet signals
  229. spl0 = spl0_dry * (1 - smoothed_dry_wet) + spl0_wet * smoothed_dry_wet;
  230. spl1 = spl1_dry * (1 - smoothed_dry_wet) + spl1_wet * smoothed_dry_wet;
  231.  
  232. // Store post-clipping levels after dry/wet mix for gain reduction calculation
  233. // abs() converts negative amplitudes to positive values for correct gain reduction calculations
  234. post_clip_l = abs(spl0);
  235. post_clip_r = abs(spl1);
  236.  
  237. // Calculate gain reduction for each channel
  238. gr_l = pre_clip_l > 0.000001 ? post_clip_l / pre_clip_l : 1;
  239. gr_r = pre_clip_r > 0.000001 ? post_clip_r / pre_clip_r : 1;
  240.  
  241. //-------------------------------------------------------------------------------------------------
  242. // Convert to dB for each channel
  243. current_gr_db_l = gr_l < 1 ? linear_to_db(gr_l) : 0;
  244. current_gr_db_r = gr_r < 1 ? linear_to_db(gr_r) : 0;
  245.  
  246. // Left channel peak tracking with hold and decay
  247. current_gr_db_l < peak_gr_l ? (
  248. peak_gr_l = current_gr_db_l;
  249. peak_gr_readout_hold_time_l = srate * 0.5; // Hold for 0.5 seconds
  250. ) : (
  251. peak_gr_readout_hold_time_l > 0 ? (
  252. peak_gr_readout_hold_time_l -= 1;
  253. ) : (
  254. peak_gr_l *= peak_decay_rate; // Slow decay
  255. peak_gr_l > -0.05 ? peak_gr_l = 0; // Reset to 0 if very close
  256. );
  257. );
  258.  
  259. // Right channel peak tracking with hold and decay
  260. current_gr_db_r < peak_gr_r ? (
  261. peak_gr_r = current_gr_db_r;
  262. peak_gr_readout_hold_time_r = srate * 0.5; // Hold for 0.5 seconds
  263. ) : (
  264. peak_gr_readout_hold_time_r > 0 ? (
  265. peak_gr_readout_hold_time_r -= 1;
  266. ) : (
  267. peak_gr_r *= peak_decay_rate; // Slow decay
  268. peak_gr_r > -0.05 ? peak_gr_r = 0; // Reset to 0 if very close
  269. );
  270. );
  271.  
  272. // METER BAR PEAK REDUCTION PROCESSING WITH HOLD TIME==============================================
  273. // Meter bar hold system (separate from readout hold)
  274. //-------------------------------------------------------------------------------------------------
  275. // Left:
  276. current_gr_db_l < meter_hold_gr_l ? (
  277. meter_hold_gr_l = current_gr_db_l;
  278. meter_hold_time_l = srate * 0.09; // Hold for 90 ms
  279. ) : (
  280. meter_hold_time_l > 0 ? (
  281. meter_hold_time_l -= 1;
  282. ) : (
  283. meter_hold_gr_l *= meter_decay_rate; // Smooth decay
  284. meter_hold_gr_l > -0.05 ? meter_hold_gr_l = 0; // Reset threshold
  285. );
  286. );
  287. //-------------------------------------------------------------------------------------------------
  288. // Right:
  289. current_gr_db_r < meter_hold_gr_r ? (
  290. meter_hold_gr_r = current_gr_db_r;
  291. meter_hold_time_r = srate * 0.09; // Hold for 90 ms
  292. ) : (
  293. meter_hold_time_r > 0 ? (
  294. meter_hold_time_r -= 1;
  295. ) : (
  296. meter_hold_gr_r *= meter_decay_rate; // Smooth decay
  297. meter_hold_gr_r > -0.05 ? meter_hold_gr_r = 0; // Reset threshold
  298. );
  299. );
  300. //-------------------------------------------------------------------------------------------------
  301. // Copy peak reduction values to meter bar display
  302. meter_gr_l = meter_hold_gr_l;
  303. meter_gr_r = meter_hold_gr_r;
  304. //-------------------------------------------------------------------------------------------------
  305. // Update readout values (smooth transitions for easier reading)
  306. peak_gr_readout_l = peak_gr_readout_l * 0.9 + peak_gr_l * 0.1;
  307. peak_gr_readout_r = peak_gr_readout_r * 0.9 + peak_gr_r * 0.1;
  308.  
  309. // Track maximum (most negative) gain reduction for each channel
  310. peak_gr_readout_l < max_peak_gr_readout_l ? max_peak_gr_readout_l = peak_gr_readout_l;
  311. peak_gr_readout_r < max_peak_gr_readout_r ? max_peak_gr_readout_r = peak_gr_readout_r;
  312. //=================================================================================================
  313.  
  314. // Store current output for delta mode (before output gain is applied)
  315. current_output_l = spl0;
  316. current_output_r = spl1;
  317.  
  318. // Apply output gain with smoothing
  319. smoothed_output_gain += output_gain_step;
  320. spl0 *= smoothed_output_gain;
  321. spl1 *= smoothed_output_gain;
  322.  
  323.  
  324. @gfx 500 250
  325. // Clear background
  326. //gfx_clear = 0xFF101010; // Alpha specified
  327. gfx_clear = 0x101010; // Alpha unspecified
  328.  
  329. // Set the default drawing color to light gray
  330. gfx_r = 0.9; gfx_g = 0.9; gfx_b = 0.9; gfx_a = 1;
  331.  
  332. // Vertical meter setup - centered
  333. meter_w = 400;
  334. meter_x = (gfx_w - meter_w) * 0.5; // True horizontal center
  335. meter_y = 45; // Moves everything down by specified pixels
  336. meter_h = 143; // Height for 0 to -12dB range
  337. channel_w = 200; // Width per channel
  338.  
  339. // Left channel meter (dark gray background)
  340. left_x = meter_x;
  341. gfx_r = 0.3; gfx_g = 0.3; gfx_b = 0.3; gfx_a = 1;
  342. gfx_rect(left_x, meter_y, channel_w, meter_h);
  343.  
  344. // Right channel meter (dark gray background)
  345. right_x = meter_x + channel_w + 7; // Num = gap between the left and right meter bars
  346. gfx_r = 0.3; gfx_g = 0.3; gfx_b = 0.3; gfx_a = 1;
  347. gfx_rect(right_x, meter_y, channel_w, meter_h);
  348.  
  349. //=================================================================================================
  350. // Draw scale markings and labels (0 to -12)
  351. gfx_r = 0.6; gfx_g = 0.6; gfx_b = 0.6; gfx_a = 1;
  352. i = 0;
  353. loop(13, // 0 to -12
  354. db_val = -i;
  355. y_pos = meter_y + (i * meter_h / 12) - 1;
  356.  
  357. // Draw tick marks
  358. gfx_line(left_x - 5, y_pos, left_x, y_pos);
  359. gfx_line(right_x + channel_w, y_pos, right_x + channel_w + 5, y_pos);
  360.  
  361. // Draw labels (every 2dB for clarity)
  362. i % 2 == 0 ? (
  363. // Measure text to get proper positioning
  364. sprintf(label_text, "%d", db_val);
  365. gfx_measurestr(label_text, text_w, text_h);
  366.  
  367. // Left side - Position text: right-aligned to tick mark and vertically centered
  368. gfx_x = left_x - 8 - text_w; // Right-align to tick mark with 3px gap
  369. gfx_y = y_pos - (text_h * 0.5) + 1; // Vertically center on tick mark
  370. gfx_drawstr(label_text);
  371.  
  372. // Right side - Position text: left-aligned to tick mark and vertically centered
  373. gfx_x = right_x + channel_w + 8; // Left-align to tick mark with 3px gap
  374. gfx_y = y_pos - (text_h * 0.5) + 1; // Vertically center on tick mark
  375. gfx_drawstr(label_text);
  376. );
  377.  
  378. i += 1;
  379. );
  380. //=================================================================================================
  381. // Draw peak reduction fill for left channel
  382. gr_clamped_l = max(-12, meter_gr_l);
  383. fill_height_l = meter_gr_l < -0.05 ? max(1, abs(gr_clamped_l) * meter_h / 12) : 0;
  384. gfx_r = 0.0; gfx_g = 0.8; gfx_b = 0.8; gfx_a = 1; // Cyan
  385. gfx_rect(left_x + 2, meter_y, channel_w - 4, fill_height_l);
  386.  
  387. // Draw peak reduction fill for right channel
  388. gr_clamped_r = max(-12, meter_gr_r);
  389. fill_height_r = meter_gr_r < -0.05 ? max(1, abs(gr_clamped_r) * meter_h / 12) : 0;
  390. gfx_r = 0.0; gfx_g = 0.8; gfx_b = 0.8; gfx_a = 1; // Cyan
  391. gfx_rect(right_x + 2, meter_y, channel_w - 4, fill_height_r);
  392. //=================================================================================================
  393. // Calculate total visual width from leftmost label to rightmost label
  394. total_left_edge = left_x - 8 - 20; // Approximate left label width
  395. total_right_edge = right_x + channel_w + 8 + 20; // Approximate right label width
  396. total_meter_width = total_right_edge - total_left_edge;
  397. gfx_x = total_left_edge + (total_meter_width - title_w) * 0.5; // Center over entire visual area
  398. gfx_y = meter_y - 35; // Position above meter
  399. //gfx_drawstr(title_text);
  400.  
Advertisement
Add Comment
Please, Sign In to add comment