Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- desc:Playhead Tearing Test
- slider1:0<0,2,1{Soft (Cubic),Soft + Hard (50/50 Blend),Hard}>Clipping Type
- slider2:0<-30,30,0.1>Input Gain (dB)
- slider3:-18<-30,0,0.1>Ceiling (dB)
- slider4:0<-30,30,0.1>Output Gain (dB)
- slider5:0<0,1,1{Off,On}>Link Input and Output Gain
- slider6:100<0,100,1>Dry/Wet (%)
- in_pin:left input
- in_pin:right input
- out_pin:left output
- out_pin:right output
- @init
- // Initialize variables
- ext_noinit = 1;
- // Track peak reduction separately for left and right channels
- peak_gr_l = 0;
- peak_gr_r = 0;
- peak_gr_readout_l = 0;
- peak_gr_readout_r = 0;
- peak_gr_readout_hold_time_l = 0;
- peak_gr_readout_hold_time_r = 0;
- max_peak_gr_readout_l = 0;
- max_peak_gr_readout_r = 0;
- peak_decay_rate = 1 - (1 - 0.999) * (48000 / srate); // 20.83....ms decay time constant
- // Meter bar hold system
- meter_hold_gr_l = 0;
- meter_hold_gr_r = 0;
- meter_hold_time_l = 0;
- meter_hold_time_r = 0;
- meter_decay_rate = 1 - (1 - 0.999) * (48000 / srate); // 20.83....ms decay time constant
- // Meter bar display values after hold processing
- meter_gr_l = 0;
- meter_gr_r = 0;
- // Playback detection for max reset
- last_play_state = 0;
- current_play_state = 0;
- last_cap = 0; // For button click detection
- // Store previous values for link controls
- prev_slider2 = 0;
- prev_slider4 = 0;
- prev_slider5 = 0;
- // Track if links are newly enabled
- input_output_link_initialized = 0;
- // Store offset values for linked controls
- input_output_offset = 0;
- // Track if a reset has occurred
- reset_occurred_input_output = 0;
- // Current values for smoothing (to prevent zipper noise)
- current_input_gain = 1;
- current_output_gain = 1;
- current_dry_wet = dry_wet = slider6 / 100;
- // Initialize the gain reduction values to 1 (no reduction) on plugin load
- gr_l = 1;
- gr_r = 1;
- // Bypass smoothing variables
- bypass_mix = 0; // 0 = effect on, 1 = bypassed
- current_bypass_mix = 0;
- // Delta mode variables
- delta_mode = 0;
- current_delta_mode = 0;
- // Convert dB to linear
- function db_to_linear(db) (
- 10^(db/20);
- );
- // Convert linear to dB
- function linear_to_db(linear) (
- 20*log10(max(linear, 0.000001));
- );
- // Soft clipping function
- function soft_clip(x) (
- abs_x = abs(x);
- abs_x < 1.5 ? (
- x - (4.0/27.0) * x * x * x; // Cubic
- ) : (
- x >= 0 ? 1.0 : -1.0; // Hard
- );
- );
- // Hard clipping function
- function hard_clip(x) (
- x >= 0 ? min(x, 1.0) : max(x, -1.0);
- );
- // Main processing function
- function process_sample(sample, ceiling, shape) (
- // Step 1: Scale UP to make clipping algorithms work at ±1.0 range
- normalized = sample / ceiling;
- // Step 2: Apply the clipping algorithms at their designed range
- soft_result = soft_clip(normalized);
- hard_result = hard_clip(normalized);
- // Step 3: Blend between algorithms
- shape_norm = shape / 100;
- blended = soft_result * (1 - shape_norm) + hard_result * shape_norm;
- // Step 4: Scale back down to ceiling level
- blended * ceiling;
- );
- @slider
- // Store original values to detect which slider changed
- last_changed = -1;
- slider2 != prev_slider2 ? last_changed = 2;
- slider4 != prev_slider4 ? last_changed = 4;
- slider5 != prev_slider5 ? last_changed = 5;
- slider2 = min(max(slider2, -30), 30); // Apply limits to Input Gain slider
- slider3 = min(max(slider3, -30), 0); // Apply limits to Ceiling slider
- slider4 = min(max(slider4, -30), 30); // Apply limits to Output Gain slider
- slider6 = min(max(slider6, 0), 100); // Apply limits to Dry/Wet slider
- // Handle linking for input/output gain
- slider5 == 1 ? (
- // Initialize the offset when link is first enabled
- prev_slider5 == 0 ? (
- input_output_offset = slider4 - (-slider2);
- input_output_link_initialized = 1;
- );
- // Check for double-click reset (value exactly 0)
- slider2 == 0 && last_changed == 2 && prev_slider2 != 0 ? (
- slider4 = 0; // Both go to zero regardless of offset
- reset_occurred_input_output = 1; // Flag that we've reset
- input_output_offset = 0; // Reset the offset
- ) :
- slider4 == 0 && last_changed == 4 && prev_slider4 != 0 ? (
- slider2 = 0; // Both go to zero regardless of offset
- reset_occurred_input_output = 1; // Flag that we've reset
- input_output_offset = 0; // Reset the offset
- ) :
- // Normal case - maintain offset with inverse relationship
- last_changed == 2 ? (
- slider4 = -slider2 + input_output_offset;
- ) :
- last_changed == 4 ? (
- slider2 = -(slider4 - input_output_offset);
- );
- ) : (
- // If linking was just disabled, store the current values
- input_output_link_initialized = 0;
- reset_occurred_input_output = 0;
- );
- // Update stored values
- prev_slider2 = slider2;
- prev_slider4 = slider4;
- prev_slider5 = slider5;
- // Convert slider values
- clipping_type = slider1 == 0 ? 0 : (slider1 == 1 ? 50 : 100); // 0% = Soft (Cubic), 50% = Soft + Hard (50/50 Blend), 100% = Hard
- input_gain = db_to_linear(slider2);
- ceiling = db_to_linear(slider3);
- output_gain = db_to_linear(slider4);
- dry_wet = slider6 / 100;
- bypass_mix = slider7 >= 0.5 ? 1 : 0; // 0 = effect on, 1 = bypassed
- delta_mode = slider8 >= 0.5 ? 1 : 0; // 0 = off, 1 = on (wet - dry)
- @block
- // Smooth Input Gain transitions
- input_gain_step = (input_gain - current_input_gain)/samplesblock;
- smoothed_input_gain = current_input_gain;
- current_input_gain = input_gain;
- // Smooth dry/wet amount transitions
- dry_wet_step = (dry_wet - current_dry_wet)/samplesblock;
- smoothed_dry_wet = current_dry_wet;
- current_dry_wet = dry_wet;
- // Smooth Output Gain transitions
- output_gain_step = (output_gain - current_output_gain)/samplesblock;
- smoothed_output_gain = current_output_gain;
- current_output_gain = output_gain;
- // Detect playback start to reset max values
- current_play_state = play_state;
- current_play_state > 0 && last_play_state == 0 ? (
- max_peak_gr_readout_l = 0;
- max_peak_gr_readout_r = 0;
- );
- last_play_state = current_play_state;
- @sample
- // Apply input gain with smoothing
- smoothed_input_gain += input_gain_step;
- spl0 *= smoothed_input_gain;
- spl1 *= smoothed_input_gain;
- // Store dry signal after input gain
- spl0_dry = spl0;
- spl1_dry = spl1;
- // Store pre-clipping levels for gain reduction calculation
- // abs() converts negative amplitudes to positive values for correct gain reduction calculations
- pre_clip_l = abs(spl0);
- pre_clip_r = abs(spl1);
- // Apply clipping
- spl0 = process_sample(spl0, ceiling, clipping_type);
- spl1 = process_sample(spl1, ceiling, clipping_type);
- // Store wet signal after clipping
- spl0_wet = spl0;
- spl1_wet = spl1;
- // === Dry/Wet Mix ===
- // Smooth dry/wet to prevent zipper noise from real-time adjustments
- smoothed_dry_wet += dry_wet_step;
- // Mix dry and wet signals
- spl0 = spl0_dry * (1 - smoothed_dry_wet) + spl0_wet * smoothed_dry_wet;
- spl1 = spl1_dry * (1 - smoothed_dry_wet) + spl1_wet * smoothed_dry_wet;
- // Store post-clipping levels after dry/wet mix for gain reduction calculation
- // abs() converts negative amplitudes to positive values for correct gain reduction calculations
- post_clip_l = abs(spl0);
- post_clip_r = abs(spl1);
- // Calculate gain reduction for each channel
- gr_l = pre_clip_l > 0.000001 ? post_clip_l / pre_clip_l : 1;
- gr_r = pre_clip_r > 0.000001 ? post_clip_r / pre_clip_r : 1;
- //-------------------------------------------------------------------------------------------------
- // Convert to dB for each channel
- current_gr_db_l = gr_l < 1 ? linear_to_db(gr_l) : 0;
- current_gr_db_r = gr_r < 1 ? linear_to_db(gr_r) : 0;
- // Left channel peak tracking with hold and decay
- current_gr_db_l < peak_gr_l ? (
- peak_gr_l = current_gr_db_l;
- peak_gr_readout_hold_time_l = srate * 0.5; // Hold for 0.5 seconds
- ) : (
- peak_gr_readout_hold_time_l > 0 ? (
- peak_gr_readout_hold_time_l -= 1;
- ) : (
- peak_gr_l *= peak_decay_rate; // Slow decay
- peak_gr_l > -0.05 ? peak_gr_l = 0; // Reset to 0 if very close
- );
- );
- // Right channel peak tracking with hold and decay
- current_gr_db_r < peak_gr_r ? (
- peak_gr_r = current_gr_db_r;
- peak_gr_readout_hold_time_r = srate * 0.5; // Hold for 0.5 seconds
- ) : (
- peak_gr_readout_hold_time_r > 0 ? (
- peak_gr_readout_hold_time_r -= 1;
- ) : (
- peak_gr_r *= peak_decay_rate; // Slow decay
- peak_gr_r > -0.05 ? peak_gr_r = 0; // Reset to 0 if very close
- );
- );
- // METER BAR PEAK REDUCTION PROCESSING WITH HOLD TIME==============================================
- // Meter bar hold system (separate from readout hold)
- //-------------------------------------------------------------------------------------------------
- // Left:
- current_gr_db_l < meter_hold_gr_l ? (
- meter_hold_gr_l = current_gr_db_l;
- meter_hold_time_l = srate * 0.09; // Hold for 90 ms
- ) : (
- meter_hold_time_l > 0 ? (
- meter_hold_time_l -= 1;
- ) : (
- meter_hold_gr_l *= meter_decay_rate; // Smooth decay
- meter_hold_gr_l > -0.05 ? meter_hold_gr_l = 0; // Reset threshold
- );
- );
- //-------------------------------------------------------------------------------------------------
- // Right:
- current_gr_db_r < meter_hold_gr_r ? (
- meter_hold_gr_r = current_gr_db_r;
- meter_hold_time_r = srate * 0.09; // Hold for 90 ms
- ) : (
- meter_hold_time_r > 0 ? (
- meter_hold_time_r -= 1;
- ) : (
- meter_hold_gr_r *= meter_decay_rate; // Smooth decay
- meter_hold_gr_r > -0.05 ? meter_hold_gr_r = 0; // Reset threshold
- );
- );
- //-------------------------------------------------------------------------------------------------
- // Copy peak reduction values to meter bar display
- meter_gr_l = meter_hold_gr_l;
- meter_gr_r = meter_hold_gr_r;
- //-------------------------------------------------------------------------------------------------
- // Update readout values (smooth transitions for easier reading)
- peak_gr_readout_l = peak_gr_readout_l * 0.9 + peak_gr_l * 0.1;
- peak_gr_readout_r = peak_gr_readout_r * 0.9 + peak_gr_r * 0.1;
- // Track maximum (most negative) gain reduction for each channel
- peak_gr_readout_l < max_peak_gr_readout_l ? max_peak_gr_readout_l = peak_gr_readout_l;
- peak_gr_readout_r < max_peak_gr_readout_r ? max_peak_gr_readout_r = peak_gr_readout_r;
- //=================================================================================================
- // Store current output for delta mode (before output gain is applied)
- current_output_l = spl0;
- current_output_r = spl1;
- // Apply output gain with smoothing
- smoothed_output_gain += output_gain_step;
- spl0 *= smoothed_output_gain;
- spl1 *= smoothed_output_gain;
- @gfx 500 250
- // Clear background
- //gfx_clear = 0xFF101010; // Alpha specified
- gfx_clear = 0x101010; // Alpha unspecified
- // Set the default drawing color to light gray
- gfx_r = 0.9; gfx_g = 0.9; gfx_b = 0.9; gfx_a = 1;
- // Vertical meter setup - centered
- meter_w = 400;
- meter_x = (gfx_w - meter_w) * 0.5; // True horizontal center
- meter_y = 45; // Moves everything down by specified pixels
- meter_h = 143; // Height for 0 to -12dB range
- channel_w = 200; // Width per channel
- // Left channel meter (dark gray background)
- left_x = meter_x;
- gfx_r = 0.3; gfx_g = 0.3; gfx_b = 0.3; gfx_a = 1;
- gfx_rect(left_x, meter_y, channel_w, meter_h);
- // Right channel meter (dark gray background)
- right_x = meter_x + channel_w + 7; // Num = gap between the left and right meter bars
- gfx_r = 0.3; gfx_g = 0.3; gfx_b = 0.3; gfx_a = 1;
- gfx_rect(right_x, meter_y, channel_w, meter_h);
- //=================================================================================================
- // Draw scale markings and labels (0 to -12)
- gfx_r = 0.6; gfx_g = 0.6; gfx_b = 0.6; gfx_a = 1;
- i = 0;
- loop(13, // 0 to -12
- db_val = -i;
- y_pos = meter_y + (i * meter_h / 12) - 1;
- // Draw tick marks
- gfx_line(left_x - 5, y_pos, left_x, y_pos);
- gfx_line(right_x + channel_w, y_pos, right_x + channel_w + 5, y_pos);
- // Draw labels (every 2dB for clarity)
- i % 2 == 0 ? (
- // Measure text to get proper positioning
- sprintf(label_text, "%d", db_val);
- gfx_measurestr(label_text, text_w, text_h);
- // Left side - Position text: right-aligned to tick mark and vertically centered
- gfx_x = left_x - 8 - text_w; // Right-align to tick mark with 3px gap
- gfx_y = y_pos - (text_h * 0.5) + 1; // Vertically center on tick mark
- gfx_drawstr(label_text);
- // Right side - Position text: left-aligned to tick mark and vertically centered
- gfx_x = right_x + channel_w + 8; // Left-align to tick mark with 3px gap
- gfx_y = y_pos - (text_h * 0.5) + 1; // Vertically center on tick mark
- gfx_drawstr(label_text);
- );
- i += 1;
- );
- //=================================================================================================
- // Draw peak reduction fill for left channel
- gr_clamped_l = max(-12, meter_gr_l);
- fill_height_l = meter_gr_l < -0.05 ? max(1, abs(gr_clamped_l) * meter_h / 12) : 0;
- gfx_r = 0.0; gfx_g = 0.8; gfx_b = 0.8; gfx_a = 1; // Cyan
- gfx_rect(left_x + 2, meter_y, channel_w - 4, fill_height_l);
- // Draw peak reduction fill for right channel
- gr_clamped_r = max(-12, meter_gr_r);
- fill_height_r = meter_gr_r < -0.05 ? max(1, abs(gr_clamped_r) * meter_h / 12) : 0;
- gfx_r = 0.0; gfx_g = 0.8; gfx_b = 0.8; gfx_a = 1; // Cyan
- gfx_rect(right_x + 2, meter_y, channel_w - 4, fill_height_r);
- //=================================================================================================
- // Calculate total visual width from leftmost label to rightmost label
- total_left_edge = left_x - 8 - 20; // Approximate left label width
- total_right_edge = right_x + channel_w + 8 + 20; // Approximate right label width
- total_meter_width = total_right_edge - total_left_edge;
- gfx_x = total_left_edge + (total_meter_width - title_w) * 0.5; // Center over entire visual area
- gfx_y = meter_y - 35; // Position above meter
- //gfx_drawstr(title_text);
Advertisement
Add Comment
Please, Sign In to add comment