1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-04-29 15:19:44 +02:00

Add Compressor 2 octave tests.

Signed-off-by: Max Maisel <max.maisel@posteo.de>
This commit is contained in:
Max Maisel 2020-05-31 10:49:08 +02:00
parent 1d728bec89
commit 2a6c2aaf80

View File

@ -0,0 +1,306 @@
## Audacity Compressor2 effect unit test
#
# Max Maisel
#
# This tests the Compressor effect with various pseudo-random mono and stereo
# noise sequences and sinewaves. The test sequences have different amplitudes
# per channel and sometimes a DC component.
#
# Avoid large parameters for AttackTime, ReleaseTime and LookaroundTime in
# this script as settling behaviour is different and will cause test failure.
#
pkg load signal;
pkg load image;
printf("Running Compressor effect tests.\n");
EXPORT_TEST_SIGNALS = true;
## PT1 envelope helper function for symmetric attack and release times.
function y = env_PT1(x, fs, t_ar, gain = 0)
T = 1/(t_ar*fs);
si = mean(mean(abs(x(1:fs*t_ar/20,:))));
c = (gain != 0) * gain + (gain == 0) * (1.0 + exp(t_ar/30.0));
y = c*filter(T, [1 T-1], mean(abs(x), 2), si*c);
end
## PT1 envelope helper function for asymmetric attack and release times.
# This function is much slower than the symmetric counterpart.
function y = env_PT1_asym(x, fs, t_a, t_r, gain = 0)
C_a = 1.0 / (fs*t_a);
C_r = 1.0 / (fs*t_r);
si = mean(mean(abs(x(1:fs*t_a/20,:))));
c = (gain != 0) * gain + (gain == 0) * (1.0 + exp(t_a/30.0));
x_m = mean(abs(x), 2);
y = zeros(length(x_m), 1);
level = si;
for k = 1:1:length(x_m)
if x_m(k) >= level
level = level + C_a * (x_m(k) - level);
else
level = level + C_r * (x_m(k) - level);
end
y(k) = c * level;
end
end
## Compressor gain helper function
function gain = comp_gain(env, thresh_DB, ratio, kneeW_DB, makeup)
makeupG_DB = -thresh_DB * (1-1/ratio) * makeup / 100;
env_DB = 20*log10(env);
kneeCond_DB = 2*(env_DB-thresh_DB);
belowKnee = kneeCond_DB < -kneeW_DB;
aboveKnee = kneeCond_DB >= kneeW_DB;
# & is element-wise &&
withinKnee = (kneeCond_DB >= -kneeW_DB) & (kneeCond_DB < kneeW_DB);
gain_DB = zeros(size(env));
gain_DB(belowKnee) = makeupG_DB;
gain_DB(aboveKnee) = thresh_DB + ...
(env_DB(aboveKnee) - thresh_DB) / ratio + ...
makeupG_DB - env_DB(aboveKnee);
# Prevent division by zero
kneeW_DB(kneeW_DB==0) = 0.000001;
gain_DB(withinKnee) = (1/ratio-1) * ...
(env_DB(withinKnee) - thresh_DB + kneeW_DB/2).^2 / ...
(2*kneeW_DB) + makeupG_DB;
gain = 10.^(gain_DB/20);
end
# Ignore first samples due to settling effects helper function
function y = settled(x, fs = 44100, tau = 1, both = 0)
y = x(round(3*fs*tau):length(x)-round(3*fs*tau*both),:);
end
# XXX: This Octave function is REALLY slow.
# Maximum value of n*fs < 10000
function y = lookaround_RMS(x, fs, n1, n2)
kernel = cat(1, zeros(n2*fs,1), ones(n1*fs, 1), ones(n2*fs, 1), zeros(n1*fs, 1));
y = zeros(size(x));
for i=1:1:size(x)(2)
y(:,i) = conv(x(:,i).^2, kernel, 'same')./(n1+n2)/fs;
end
y = 2*sqrt(sum(y, 2)./size(y)(2));
end
# XXX: This Octave function is REALLY slow.
# Maximum value of n*fs < 10000
function y = lookaround_max(x, fs, n1, n2)
kernel = cat(1, zeros(n2*fs,1), ones(n1*fs, 1), ones(n2*fs, 1), zeros(n1*fs, 1));
x = mean(abs(x), 2);
y = imdilate(x, kernel);
end
################################################################################
## Test Compressor, mono thresholding
CURRENT_TEST = "Compressor2, mono thresholding";
fs = 44100;
randn("seed", 1);
x1 = 0.01*randn(30*fs,1) .* sin(2*pi/fs/35*(1:1:30*fs)).';
remove_all_tracks();
x = export_to_aud(x1, fs, name = "Compressor-threshold-test.wav");
aud_do("CompressorV2: Threshold=-20 Algorithm=0 AttackTime=0.1 ReleaseTime=0.3 LookaheadTime=0 LookbehindTime=0 KneeWidth=0\n");
y = import_from_aud(1);
# All input samples are below threshold so output must be equal to input.
do_test_equ(x, y);
## Test Compressor, mono compression PT1 - no lookaround
CURRENT_TEST = "Compressor2, mono compression PT1";
x1 = x1.*10;
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("CompressorV2: Threshold=-20 Algorithm=1 CompressBy=0 Ratio=2.5 AttackTime=0.5 ReleaseTime=0.5 LookaheadTime=0.0 LookbehindTime=0.0 KneeWidth=12\n");
y = import_from_aud(1);
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(env_PT1(x, fs, 0.5, 1), fs, 1), -20, 2.5, 12, 0).* ...
settled(x, fs, 1));
## Test Compressor, mono compression PT1 - sinewave - no lookaround
CURRENT_TEST = "Compressor2, mono compression PT1 - sinewave";
x2 = sin(2*pi*300/fs*(1:1:20*fs)).';
remove_all_tracks();
x = export_to_aud(x2, fs, "Compressor-mono-sine-test.wav");
aud_do("CompressorV2: Threshold=-23 Algorithm=1 CompressBy=1 Ratio=2.5 AttackTime=0.5 ReleaseTime=0.5 LookaheadTime=0 LookbehindTime=0 KneeWidth=12\n");
y = import_from_aud(1);
# Gain factor 2 because we compress by RMS but do not use lookaround_RMS as
# lookaround is zero.
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(2*env_PT1(x, fs, 0.5), fs, 1), -23, 2.5, 12, 0).* ...
settled(x, fs, 1));
## Test Compressor, mono compression PT1 - faded sinewave - medium signal
CURRENT_TEST = "Compressor2, mono compression PT1 - faded sinewave - medium signal";
x2 = sin(2*pi*300/fs*(1:1:50*fs)).' .* horzcat(1:1:25*fs, 25*fs:-1:1).' ./ (25*fs);
remove_all_tracks();
x = export_to_aud(x2, fs, "Compressor-mono-sine-test.wav");
aud_do("CompressorV2: Threshold=-10 Algorithm=1 CompressBy=0 Ratio=100 AttackTime=0.01 ReleaseTime=0.01 LookaheadTime=0 LookbehindTime=0 KneeWidth=0\n");
y = import_from_aud(1);
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(env_PT1(x, fs, 0.01, 1), fs, 1), -10, 100, 0, 0).* ...
settled(x, fs, 1));
## Test Compressor, mono compression PT1 - faded sinewave - 50 sec signal - no lookaround
CURRENT_TEST = "Compressor2, mono compression PT1 - faded sinewave - long signal";
x2 = vertcat(x2, x2);
remove_all_tracks();
x = export_to_aud(x2, fs, "Compressor-mono-sine-test.wav");
aud_do("CompressorV2: Threshold=-10 Algorithm=1 CompressBy=0 Ratio=100 AttackTime=0.01 ReleaseTime=0.01 LookaheadTime=0 LookbehindTime=0 KneeWidth=0\n");
y = import_from_aud(1);
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(env_PT1(x, fs, 0.01, 1), fs, 1), -10, 100, 0, 0).* ...
settled(x, fs, 1));
## Test Compressor, mono compression PT1 - sinewave - no lookaround - long attack time
CURRENT_TEST = "Compressor2, mono compression PT1 - sinewave - asymetric attack / release";
x2 = sin(2*pi*300/fs*(1:1:20*fs)).';
remove_all_tracks();
x = export_to_aud(x2, fs, "Compressor-mono-sine-test.wav");
aud_do("CompressorV2: Threshold=-6 Algorithm=1 CompressBy=0 Ratio=2.0 AttackTime=1.0 ReleaseTime=0.3 LookaheadTime=0 LookbehindTime=0 KneeWidth=0 MakeupGain=0\n");
y = import_from_aud(1);
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(env_PT1_asym(x, fs, 1.0, 0.3, 1.0), fs, 1), -6, 2.0, 0, 0).*
settled(x, fs, 1));
## Test Compressor, mono lookaround max
CURRENT_TEST = "Compressor2, mono asymmetric lookaround max";
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("CompressorV2: Threshold=-17 Algorithm=1 CompressBy=0 Ratio=1.2 AttackTime=0.3 ReleaseTime=0.3 LookaheadTime=0.2 LookbehindTime=0.1 KneeWidth=5 MakeupGain=50\n");
y = import_from_aud(1);
do_test_equ(settled(y, fs, 0.6), ...
comp_gain(settled(env_PT1(lookaround_max(x, fs, 0.2, 0.1), fs, 0.3, 1), fs, 0.6), ...
-17, 1.2, 5, 50).*settled(x, fs, 0.6));
## Test Compressor, mono lookaround RMS
CURRENT_TEST = "Compressor2, mono asymmetric lookaround RMS";
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("CompressorV2: Threshold=-20 Algorithm=1 CompressBy=1 Ratio=3 AttackTime=1 ReleaseTime=1 LookaheadTime=0.1 LookbehindTime=0.2 KneeWidth=3 MakeupGain=80\n");
y = import_from_aud(1);
do_test_equ(settled(y, fs, 2), ...
comp_gain(settled(env_PT1(lookaround_RMS(x, fs, 0.1, 0.2), fs, 1), fs, 2), -20, 3, 3, 80) ...
.*settled(x, fs, 2));
## Test Compressor, mono lookaround max with selection
CURRENT_TEST = "Compressor2, mono lookaround max with selection";
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("Select: Start=2 End=5 Mode=Set\n");
aud_do("CompressorV2: Threshold=-17 Algorithm=1 CompressBy=0 Ratio=1.2 AttackTime=0.3 ReleaseTime=0.3 LookaheadTime=0.2 LookbehindTime=0.2 KneeWidth=5 MakeupGain=50\n");
y = import_from_aud(1);
x = x(2*fs+1:5*fs);
do_test_equ(settled(y, fs, 0.1), ...
comp_gain(settled(env_PT1(lookaround_max(x, fs, 0.2, 0.2), fs, 0.3, 1), fs, 0.1), ...
-17, 1.2, 5, 50).*settled(x, fs, 0.1));
## Test Compressor, mono, ultra short attack time
CURRENT_TEST = "Compressor2, mono, ultra short attack time";
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("CompressorV2: Threshold=-20 Algorithm=1 CompressBy=0 Ratio=2 AttackTime=0.00001 ReleaseTime=0.00001 LookaheadTime=0 LookbehindTime=0 KneeWidth=10\n");
y = import_from_aud(2);
# XXX: use larger epsilon due to numerical issues
# (float in audacity vs double in octave vs wav files for exchange)
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(env_PT1(x, fs, 0.00001, 1), fs), -20, 2, 10, 0) ...
.*settled(x, fs, 1), "", 0.15);
## Test Compressor, stereo compression PT1 - no lookaround
randn("seed", 2);
x1 = 0.2*randn(35*fs, 2);
x1(:,1) = x1(:,1) .* sin(2*pi/fs/35*(1:1:35*fs)).';
x1(:,2) = x1(:,2) .* (sin(2*pi/fs/75*(1:1:35*fs)).' + 0.1);
CURRENT_TEST = "Compressor2, stereo compression PT1";
remove_all_tracks();
x = export_to_aud(x1, fs, "Compressor-stereo-test.wav");
aud_do("CompressorV2: Threshold=-20 Algorithm=1 CompressBy=0 Ratio=2 AttackTime=0.5 ReleaseTime=0.5 LookaheadTime=0 LookbehindTime=0 KneeWidth=10\n");
y = import_from_aud(2);
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(env_PT1(x, fs, 0.5, 1), fs), -20, 2, 10, 0) ...
.*settled(x, fs, 1), "stereo dependent");
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("CompressorV2: Threshold=-20 Algorithm=1 Ratio=2 AttackTime=0.5 ReleaseTime=0.5 LookaheadTime=0 LookbehindTime=0 KneeWidth=10 StereoIndependent=1\n");
y = import_from_aud(2);
do_test_equ(settled(y(:,1), fs, 1), ...
comp_gain(settled(env_PT1(x(:,1), fs, 0.5, 1), fs, 1), -20, 2, 10, 0) ...
.*settled(x(:,1), fs, 1), "channel 1");
do_test_equ(settled(y(:,2), fs, 1), ...
comp_gain(settled(env_PT1(x(:,2), fs, 0.5, 1), fs, 1), -20, 2, 10, 0) ...
.*settled(x(:,2), fs, 1), "channel 2");
## Test Compressor, stereo compression PT1 - sinewave
CURRENT_TEST = "Compressor2, stereo compression PT1 - sinewave";
x2 = sin(2*pi*300/fs*(1:1:20*fs)).';
x2 = [x2, sin(2*pi*310/fs*(1:1:20*fs)).'];
remove_all_tracks();
x = export_to_aud(x2, fs, "Compressor-stereo-sine-test.wav");
aud_do("CompressorV2: Threshold=-20 Algorithm=1 CompressBy=0 Ratio=2 AttackTime=0.5 ReleaseTime=0.5 LookaheadTime=0 LookbehindTime=0 KneeWidth=10\n");
y = import_from_aud(2);
do_test_equ(settled(y, fs, 1), ...
comp_gain(settled(env_PT1(x, fs, 0.5, 1), fs, 1), -20, 2, 10, 0) ...
.*settled(x, fs, 1), "stereo dependent");
remove_all_tracks();
x = export_to_aud(x2, fs);
aud_do("CompressorV2: Threshold=-20 Algorithm=1 Ratio=2 AttackTime=0.5 ReleaseTime=0.5 LookaheadTime=0 LookbehindTime=0 KneeWidth=10 StereoIndependent=1\n");
y = import_from_aud(2);
do_test_equ(settled(y(:,1), fs, 1), ...
comp_gain(settled(env_PT1(x(:,1), fs, 0.5, 1), fs, 1), -20, 2, 10, 0) ...
.*settled(x(:,1), fs, 1), "channel 1");
do_test_equ(settled(y(:,2), fs, 1), ...
comp_gain(settled(env_PT1(x(:,2), fs, 0.5, 1), fs, 1), -20, 2, 10, 0) ...
.*settled(x(:,2), fs, 1), "channel 2");
## Test Compressor, stereo lookaround max
CURRENT_TEST = "Compressor2, stereo lookaround max";
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("CompressorV2: Threshold=-17 Algorithm=1 Ratio=1.2 AttackTime=0.3 ReleaseTime=0.3 LookaheadTime=0.2 LookbehindTime=0.2 KneeWidth=5 MakeupGain=50\n");
y = import_from_aud(2);
do_test_equ(settled(y, fs, 0.6), ...
comp_gain(settled(env_PT1(lookaround_max(x, fs, 0.2, 0.2), fs, 0.3, 1), fs, 0.6), ...
-17, 1.2, 5, 50).*settled(x, fs, 0.6));
## Test Compressor, stereo lookaround RMS
CURRENT_TEST = "Compressor2, stereo lookaround RMS";
remove_all_tracks();
x = export_to_aud(x1, fs);
aud_do("CompressorV2: Threshold=-20 Algorithm=1 Ratio=3 AttackTime=1 ReleaseTime=1 LookaheadTime=0.1 LookbehindTime=0.1 KneeWidth=3 CompressBy=1 MakeupGain=60\n");
y = import_from_aud(2);
do_test_equ(settled(y, fs, 2.5), ...
comp_gain(settled(env_PT1(lookaround_RMS(x, fs, 0.1, 0.1), fs, 1), fs, 2.5), -20, 3, 3, 60) ...
.*settled(x, fs, 2.5));