Skip to content

Commit

Permalink
Add regression test cases for IIR filter (#1429)
Browse files Browse the repository at this point in the history
  • Loading branch information
derselbst authored Nov 17, 2024
1 parent dbd7832 commit 069a176
Show file tree
Hide file tree
Showing 18 changed files with 187 additions and 0 deletions.
61 changes: 61 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,64 @@ if ( LIBSNDFILE_HASVORBIS )
ADD_FLUID_TEST(test_sf3_sfont_loading)
ADD_FLUID_SF_DUMP_TEST(VintageDreamsWaves-v2.sf3)
endif ( LIBSNDFILE_HASVORBIS )


# Prepare the manual test suite down here
if(NOT DEFINED GENERAL_USER_GS2)
add_custom_target(check_manual
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red "In order to run the manual test suite, you need to set variable GENERAL_USER_GS2 to the path of the soundfont.")
else()

set(IIR_FILTER_RENDER_DIR "${CMAKE_CURRENT_BINARY_DIR}/manual/iir_filter")

if(LIBSNDFILE_SUPPORT)
set(FEXT "wav")
else()
set(FEXT "raw")
endif()

# Add an empty pseudo target
add_custom_target(check_manual)

add_custom_target(create_iir_dir
COMMAND ${CMAKE_COMMAND} -E make_directory ${IIR_FILTER_RENDER_DIR})

add_custom_target(render1415
COMMAND fluidsynth -R 0 -C 0 -g 1 -F ${IIR_FILTER_RENDER_DIR}/1415_the-nervous-filter.${FEXT} "The Nervous Filter trimmed.mid" ${GENERAL_USER_GS2}
COMMENT "Rendering testfile of issue 1415"
DEPENDS fluidsynth create_iir_dir
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/manual/iir_filter/1415_the-nervous-filter/
VERBATIM
)

add_custom_target(render1417
COMMAND fluidsynth -R 0 -C 0 -g 1 -F ${IIR_FILTER_RENDER_DIR}/1417_filter-envelope-noise.${FEXT} filter-envelope-noise.mid ${GENERAL_USER_GS2}
COMMENT "Rendering testfile of issue 1417"
DEPENDS fluidsynth create_iir_dir
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/manual/iir_filter/1417_filter-envelope-noise/
VERBATIM
)

add_custom_target(render1424
COMMAND fluidsynth -R 0 -C 0 -g 1 -F ${IIR_FILTER_RENDER_DIR}/1424_clicks-on-ModEnv-FilterFc-change.${FEXT} 1424.mid echo_drops.sf2
COMMENT "Rendering testfile of issue 1424"
DEPENDS fluidsynth create_iir_dir
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/manual/iir_filter/1424_clicks-on-ModEnv-FilterFc-change/
VERBATIM
)

add_custom_target(render1427
COMMAND fluidsynth -R 0 -C 0 -g 5 -F ${IIR_FILTER_RENDER_DIR}/1427_high-Q-note-cutoff.${FEXT} high_Q_note_cutoff_test.mid high_Q_note_cutoff_test.sf2
COMMENT "Rendering testfile of issue 1427"
DEPENDS fluidsynth create_iir_dir
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/manual/iir_filter/1427_high-Q-note-cutoff/
VERBATIM
)

# Add a dependency so that rendering targets depends on check_manual
add_dependencies(check_manual render1415)
add_dependencies(check_manual render1417)
add_dependencies(check_manual render1424)
add_dependencies(check_manual render1427)

endif()
4 changes: 4 additions & 0 deletions test/manual/iir_filter/1415_the-nervous-filter/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.ogg filter=lfs diff=lfs merge=lfs -text
*.flac filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.sf2 filter=lfs diff=lfs merge=lfs -text
Binary file not shown.
Git LFS file not shown
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
filter-envelope-noise-1345[[:space:]]reverted.flac filter=lfs diff=lfs merge=lfs -text
filter-envelope-noise-FluidSynth[[:space:]]2.4.flac filter=lfs diff=lfs merge=lfs -text
filter-envelope-noise.mid filter=lfs diff=lfs merge=lfs -text
*.flac filter=lfs diff=lfs merge=lfs -text
*.sf2 filter=lfs diff=lfs merge=lfs -text
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
echo_drops.sf2 filter=lfs diff=lfs merge=lfs -text
Binary file not shown.
Git LFS file not shown
3 changes: 3 additions & 0 deletions test/manual/iir_filter/1427_high-Q-note-cutoff/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
high_Q_note_cutoff_test.sf2 filter=lfs diff=lfs merge=lfs -text
fluidsynth_iir-tests.flac filter=lfs diff=lfs merge=lfs -text
high_Q_note_cutoff_test.mid filter=lfs diff=lfs merge=lfs -text
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
86 changes: 86 additions & 0 deletions test/manual/iir_filter/interactive_biquad_lowpass.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#
# This Matlab script implements an interactive Bode plot of fluidsynth's IIR filter.
# To run it, just call interactive_biquad_lowpass() and a window will open up, allowing
# you to adjust the cutoff frequency and Q.
# Note that Q is in linear range here!
#
function interactive_biquad_lowpass()
% Initial values
f_c = 1000; % Initial cutoff frequency in Hz
Q = 0.707; % Initial quality factor
f_s = 48000; % Sampling frequency in Hz

% Create the figure
hFig = figure('Name', 'Interactive Biquad Lowpass Filter', 'NumberTitle', 'off');

% Create axes for the magnitude and phase plots
hAxesMag = subplot(2, 1, 1, 'Parent', hFig);
hAxesPhase = subplot(2, 1, 2, 'Parent', hFig);

% Plot the initial response
plot_response(hAxesMag, hAxesPhase, f_c, Q, f_s);

% Create slider for cutoff frequency
uicontrol('Style', 'text', 'Position', [20 20 150 20], 'String', 'Cutoff Frequency (Hz)');
hSliderFc = uicontrol('Style', 'slider', 'Min', 100, 'Max', 20000, 'Value', f_c, ...
'Position', [20 40 300 20]);
hTextFc = uicontrol('Style', 'text', 'Position', [330 40 50 20], 'String', num2str(f_c));

% Create slider for quality factor
uicontrol('Style', 'text', 'Position', [20 80 150 20], 'String', 'Quality Factor (Q)');
hSliderQ = uicontrol('Style', 'slider', 'Min', 0.1, 'Max', 100, 'Value', Q, ...
'Position', [20 100 300 20]);
hTextQ = uicontrol('Style', 'text', 'Position', [330 100 50 20], 'String', num2str(Q));

% Add listeners for both sliders
addlistener(hSliderFc, 'Value', 'PreSet', @(src, event) update_plot(hAxesMag, hAxesPhase, hSliderFc, hSliderQ, f_s, hTextFc, hTextQ));
addlistener(hSliderQ, 'Value', 'PreSet', @(src, event) update_plot(hAxesMag, hAxesPhase, hSliderFc, hSliderQ, f_s, hTextFc, hTextQ));
end

function update_plot(hAxesMag, hAxesPhase, hSliderFc, hSliderQ, f_s, hTextFc, hTextQ)
% Get the current values from the sliders
f_c = get(hSliderFc, 'Value');
Q = get(hSliderQ, 'Value');

% Update the text displays
set(hTextFc, 'String', num2str(f_c, '%.1f')); % Display cutoff frequency
set(hTextQ, 'String', num2str(Q, '%.2f')); % Display quality factor

% Update the plot with the new values
plot_response(hAxesMag, hAxesPhase, f_c, Q, f_s);
end

function plot_response(hAxesMag, hAxesPhase, f_c, Q, f_s)
% Design the biquad lowpass filter
w0 = 2 * pi * f_c / f_s;
alpha = sin(w0) / (2 * Q);

b0 = (1 - cos(w0)) / 2;
b1 = 1 - cos(w0);
b2 = (1 - cos(w0)) / 2;
a0 = 1 + alpha;
a1 = -2 * cos(w0);
a2 = 1 - alpha;

% Normalize coefficients
b = [b0 / a0, b1 / a0, b2 / a0];
a = [1, a1 / a0, a2 / a0];

% Compute the frequency response
[h, w] = freqz(b, a, 1024, f_s);

% Clear the axes and plot the new response
cla(hAxesMag);
plot(hAxesMag, w, 20*log10(abs(h)));
title(hAxesMag, 'Magnitude Response');
xlabel(hAxesMag, 'Frequency (Hz)');
ylabel(hAxesMag, 'Magnitude (dB)');
grid(hAxesMag, 'on');

cla(hAxesPhase);
plot(hAxesPhase, w, angle(h) * (180/pi));
title(hAxesPhase, 'Phase Response');
xlabel(hAxesPhase, 'Frequency (Hz)');
ylabel(hAxesPhase, 'Phase (degrees)');
grid(hAxesPhase, 'on');
end

0 comments on commit 069a176

Please sign in to comment.