├── .circleci ├── config.yml └── run-locally.sh ├── .gitignore ├── CMakeLists.txt ├── CMakeLists.txt.in ├── COPYING.MIT ├── README.md ├── build-tests.sh ├── lib ├── biquad.h ├── biquad_modified.h ├── filter_common.h ├── filter_includes.h ├── fo_apf.h ├── fo_hpf.h ├── fo_lpf.h ├── fo_shelving_high.h ├── fo_shelving_low.h ├── so_apf.h ├── so_bpf.h ├── so_bsf.h ├── so_butterworth_bpf.h ├── so_butterworth_bsf.h ├── so_butterworth_hpf.h ├── so_butterworth_lpf.h ├── so_hpf.h ├── so_linkwitz_riley_hpf.h ├── so_linkwitz_riley_lpf.h ├── so_lpf.h ├── so_parametric_cq_boost.h ├── so_parametric_cq_cut.h └── so_parametric_ncq.h └── tests ├── CMakeLists.txt ├── fo_apf_test.cpp ├── fo_hpf_test.cpp ├── fo_lpf_test.cpp ├── fo_shelving_high_test.cpp ├── fo_shelving_low_test.cpp ├── so_apf_test.cpp ├── so_bpf_test.cpp ├── so_bsf_test.cpp ├── so_butterworth_bpf_test.cpp ├── so_butterworth_bsf_test.cpp ├── so_butterworth_hpf_test.cpp ├── so_butterworth_lpf_test.cpp ├── so_hpf_test.cpp ├── so_linkwitz_riley_hpf_test.cpp ├── so_linkwitz_riley_lpf_test.cpp ├── so_lpf_test.cpp ├── so_parametric_cq_boost_test.cpp ├── so_parametric_cq_cut_test.cpp ├── so_parametric_ncq_test.cpp └── tests_common.h /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | 2 | version: 2.1 3 | jobs: 4 | build: 5 | docker: 6 | - image: rikorose/gcc-cmake:gcc-8 7 | steps: 8 | - checkout 9 | - run: 10 | name: Build and run tests 11 | command: ./build-tests.sh -------------------------------------------------------------------------------- /.circleci/run-locally.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | curl --user ${TOKEN}: \ 4 | --request POST \ 5 | --form revision=$1\ 6 | --form config=@config.yml \ 7 | --form notify=false \ 8 | https://circleci.com/api/v1.1/project/github/dimtass/DSP-Cpp-filters/tree/$2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | # Prerequisites 50 | *.d 51 | 52 | # Object files 53 | *.o 54 | *.ko 55 | *.obj 56 | *.elf 57 | 58 | # Linker output 59 | *.ilk 60 | *.map 61 | *.exp 62 | 63 | # Precompiled Headers 64 | *.gch 65 | *.pch 66 | 67 | # Libraries 68 | *.lib 69 | *.a 70 | *.la 71 | *.lo 72 | 73 | # Shared objects (inc. Windows DLLs) 74 | *.dll 75 | *.so 76 | *.so.* 77 | *.dylib 78 | 79 | # Executables 80 | *.exe 81 | *.out 82 | *.app 83 | *.i*86 84 | *.x86_64 85 | *.hex 86 | 87 | # Debug files 88 | *.dSYM/ 89 | *.su 90 | *.idb 91 | *.pdb 92 | 93 | # Kernel Module Compile Results 94 | *.mod* 95 | *.cmd 96 | .tmp_versions/ 97 | modules.order 98 | Module.symvers 99 | Mkfile.old 100 | dkms.conf 101 | 102 | # eclipse project files 103 | *.cproject 104 | *.pydevproject 105 | *.project 106 | *.settings 107 | 108 | # sublime project files 109 | *.sublime-project 110 | *.sublime-workspace 111 | 112 | # visual studio project files 113 | *.vcxproj* 114 | 115 | # Visual Studio Code project file 116 | *.vscode 117 | 118 | build/ 119 | git-multi -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # set minimum cmake version 2 | cmake_minimum_required(VERSION 3.5 FATAL_ERROR) 3 | 4 | option(BUILD_UNIT_TESTS "Build the tests" ON) 5 | 6 | if (BUILD_UNIT_TESTS) 7 | # Download and unpack googletest at configure time 8 | configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) 9 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 10 | RESULT_VARIABLE result 11 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) 12 | if(result) 13 | message(FATAL_ERROR "CMake step for googletest failed: ${result}") 14 | endif() 15 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 16 | RESULT_VARIABLE result 17 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) 18 | if(result) 19 | message(FATAL_ERROR "Build step for googletest failed: ${result}") 20 | endif() 21 | 22 | # Prevent overriding the parent project's compiler/linker 23 | # settings on Windows 24 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 25 | 26 | # Add googletest directly to our build. This defines 27 | # the gtest and gtest_main targets. 28 | add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src 29 | ${CMAKE_CURRENT_BINARY_DIR}/googletest-build 30 | EXCLUDE_FROM_ALL) 31 | 32 | # The gtest/gtest_main targets carry header search path 33 | # dependencies automatically when using CMake 2.8.11 or 34 | # later. Otherwise we have to add them here ourselves. 35 | if (CMAKE_VERSION VERSION_LESS 2.8.11) 36 | include_directories("${gtest_SOURCE_DIR}/include") 37 | endif() 38 | 39 | # Add tests sub-dir 40 | add_subdirectory(tests) 41 | endif() -------------------------------------------------------------------------------- /CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG release-1.10.0 9 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) -------------------------------------------------------------------------------- /COPYING.MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DSP filters in C++ 2 | ---- 3 | 4 | [![dimtass](https://circleci.com/gh/dimtass/DSP-Cpp-filters.svg?style=svg)](https://circleci.com/gh/dimtass/DSP-Cpp-filters) 5 | 6 | This repo contains some DSP biquad filters used in audio. I've extracted those 7 | filters from the `Designing Audio Effect Plug-Ins in C++: With Digital Audio Signal Processing Theory` 8 | book that you can find [here](https://www.amazon.com/Designing-Audio-Effect-Plug-Ins-Processing/dp/0240825152). 9 | 10 | I've also implemented a real-time testing on a Cortex-M4 MCU, using the on-chip ADC and DAC. You can find the post [here](https://www.stupid-projects.com/biquad-audio-dsp-filters-using-stm32f303cc-black-pill/). 11 | 12 | This is the formula I'm using for the [digital biquad filter](https://en.wikipedia.org/wiki/Digital_biquad_filter) 13 | in the source code: 14 | ``` 15 | y(n) = a0*x(n) + a1*x(n-1) + a2*x(n-2) - b*y(n-1) + b2*y(n-2) 16 | ``` 17 | 18 | - First order all-pass filter (fo_apf) 19 | - First order high-pass filter (fo_hpf) 20 | - First order low-pass filter (fo_lpf) 21 | - First order high-shelving filter (fo_shelving_high) 22 | - First order low-shelving filter (fo_shelving_low) 23 | - Second order all-pass filter (so_apf) 24 | - Second order band-pass filter (so_bpf) 25 | - Second order band-stop filter (so_bsf) 26 | - Second order Butterworth band-pass filter (so_butterworth_bpf) 27 | - Second order Butterworth band-stop filter (so_butterworth_bsf) 28 | - Second order Butterworth high-pass filter (so_butterworth_hpf) 29 | - Second order Butterworth low-pass filter (so_butterworth_lpf) 30 | - Second order high-pass filter (so_hpf) 31 | - Second order Linkwitz-Riley high-pass filter (so_linkwitz_riley_hpf) 32 | - Second order Linkwitz-Riley low-pass filter (so_linkwitz_riley_lpf) 33 | - Second order Low-pass filter (so_lpf) 34 | - Second order parametric/peaking boost filter with constant-Q (so_parametric_cq_boost) 35 | - Second order parametric/peaking cut filter with constant-Q (so_parametric_cq_cut) 36 | - Second order parametric/peaking filter with non-constant-Q (so_parametric_ncq) 37 | 38 | All the filters are now header files and they are located in the `lib/` folder. 39 | In order to use them just copy the `lib/` folder (and rename it if needed) into 40 | your project folder. There's an example how to build later in the README. 41 | 42 | ## Build & run tests 43 | You can use cmake to build the tests. On Linux, you can just run this: 44 | ```sh 45 | ./build_tests.sh 46 | ``` 47 | 48 | The above command will build the tests and run them. 49 | 50 | > Note: Tests are a bit naive in this case, since it doesn't make 51 | much sense for testing using unit-tests, but anyways, I've added them 52 | 53 | ## Usage: 54 | The filters can be used in your C++ code in the part where the audio sample is about to be processed. You need to include the _filter_common.h_ and _filter_includes.h_ files and the create an object with filter(s) you want to apply and calculate the coefficients with the calculate_coeffs() function. Then in the sample processing function run the filter() function with the current sample as a parameter. 55 | 56 | I've used [RackAFX](http://www.willpirkle.com/rackafx/) to test these filters. 57 | 58 | ## Code example 59 | For example, to use the so-LPF filter then first create a `main.cpp` file 60 | in the top directory of this repo. 61 | 62 | ```sh 63 | touch main.cpp 64 | ``` 65 | 66 | Then add this code inside: 67 | 68 | ```cpp 69 | #include 70 | #include 71 | #include "filter_common.h" 72 | #include "filter_includes.h" 73 | 74 | int main() { 75 | std::unique_ptr filter (new SO_LPF); 76 | 77 | auto coeffs = filter->calculate_coeffs(1.0, 5000, 96000); 78 | auto yn = filter->process(0.303); 79 | 80 | std::cout << "Coeffs: " << std::endl; 81 | std::cout << "a0: " << coeffs.a0 << std::endl; 82 | std::cout << "a1: " << coeffs.a1 << std::endl; 83 | std::cout << "a2: " << coeffs.a2 << std::endl; 84 | std::cout << "b1: " << coeffs.b1 << std::endl; 85 | std::cout << "b2: " << coeffs.b2 << std::endl; 86 | 87 | std::cout << "yn: " << yn << std::endl; 88 | return 0; 89 | } 90 | ``` 91 | 92 | Now to build the file run: 93 | ```sh 94 | g++ main.cpp -I./lib 95 | ``` 96 | 97 | And then run the executable: 98 | ```sh 99 | ./a.out 100 | ``` 101 | 102 | This is will print the filter coefficients and then will process 103 | a sample with the value `0.303`. You should see an output similar 104 | to this: 105 | 106 | ``` 107 | Coeffs: 108 | a0: 0.0228608 109 | a1: 0.0457215 110 | a2: 0.0228608 111 | b1: -1.63163 112 | b2: 0.723069 113 | yn: 0.00692681 114 | ``` 115 | -------------------------------------------------------------------------------- /build-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "Building the project in Linux environment" 3 | 4 | # Clean build directories 5 | : ${CLEANBUILD:=true} 6 | # Enable build unit-tests 7 | : ${BUILD_UNIT_TESTS:=ON} 8 | 9 | # Set default arch to stm32 10 | BUILD_DIR=build 11 | # Current working directory 12 | WORKING_DIR=$(pwd) 13 | # Compile objects in parallel, the -jN flag in make 14 | PARALLEL=$(nproc) 15 | 16 | CMAKE_FLAGS="${CMAKE_FLAGS} \ 17 | -DBUILD_UNIT_TESTS=${BUILD_UNIT_TESTS} \ 18 | " 19 | 20 | if [ "${CLEANBUILD}" == "true" ]; then 21 | echo "- removing build directory: ${BUILD_DIR}" 22 | rm -rf ${BUILD_DIR} 23 | fi 24 | 25 | mkdir -p ${BUILD_DIR} 26 | cd ${BUILD_DIR} 27 | 28 | # setup cmake 29 | cmake .. ${CMAKE_FLAGS} 30 | 31 | # build 32 | make -j${PARALLEL} --no-print-directory 33 | make check -------------------------------------------------------------------------------- /lib/biquad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * First order all-pass filter 3 | * Dimitris Tassopoulos 2016 4 | * 5 | * fc, corner frequency 6 | */ 7 | #pragma once 8 | #include "filter_common.h" 9 | 10 | class Biquad { 11 | public: 12 | 13 | #ifdef COEFF_SIZE_DOUBLE 14 | typedef double coef_size_t; 15 | #else 16 | typedef float coef_size_t; 17 | #endif 18 | 19 | Biquad() : m_xnz1(0), m_xnz2(0), m_ynz1(0), m_ynz2(0), m_offset(0), m_coeffs{0} {}; 20 | virtual ~Biquad() {}; 21 | coef_size_t process(coef_size_t sample) 22 | { 23 | coef_size_t xn = sample; 24 | coef_size_t yn = m_coeffs.a0*xn + m_coeffs.a1*m_xnz1 + m_coeffs.a2*m_xnz2 25 | - m_coeffs.b1*m_ynz1 - m_coeffs.b2*m_ynz2; 26 | m_xnz2 = m_xnz1; 27 | m_xnz1 = xn; 28 | m_ynz2 = m_ynz1; 29 | m_ynz1 = yn; 30 | return(yn + m_offset); 31 | } 32 | 33 | void set_offset(coef_size_t offset) 34 | { 35 | m_offset = offset; 36 | } 37 | 38 | coef_size_t get_offset(void) 39 | { 40 | return(m_offset); 41 | } 42 | 43 | typedef struct { 44 | coef_size_t a0; 45 | coef_size_t a1; 46 | coef_size_t a2; 47 | coef_size_t b1; 48 | coef_size_t b2; 49 | coef_size_t c0; 50 | coef_size_t d0; 51 | } tp_coeffs; 52 | 53 | protected: 54 | coef_size_t m_xnz1, m_xnz2, m_ynz1, m_ynz2, m_offset; 55 | tp_coeffs m_coeffs; 56 | }; 57 | -------------------------------------------------------------------------------- /lib/biquad_modified.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "biquad.h" 3 | 4 | class BiquadModified : public Biquad { 5 | public: 6 | coef_size_t process(coef_size_t sample) 7 | { 8 | coef_size_t xn = sample; 9 | coef_size_t ynn = m_coeffs.a0*xn + m_coeffs.a1*m_xnz1 + m_coeffs.a2*m_xnz2 10 | - m_coeffs.b1*m_ynz1 - m_coeffs.b2*m_xnz2; 11 | coef_size_t yn = m_coeffs.d0*xn + m_coeffs.c0*ynn; 12 | 13 | m_xnz2 = m_xnz1; 14 | m_xnz1 = xn; 15 | m_ynz2 = m_ynz1; 16 | m_ynz1 = yn; 17 | return(yn); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /lib/filter_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "biquad.h" 4 | #include 5 | #include 6 | 7 | #define COEFF_SIZE_DOUBLE 8 | //#define COEFF_SIZE_FLOAT 9 | 10 | #ifndef pi 11 | #define pi 3.1415926535897932384626433832795 12 | #endif 13 | 14 | #ifndef sqrt2 15 | #define sqrt2 (2.0 * 0.707106781186547524401) 16 | #endif 17 | 18 | #ifndef sqrt2over2 19 | #define sqrt2over2 0.707106781186547524401 20 | #endif 21 | -------------------------------------------------------------------------------- /lib/filter_includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "fo_apf.h" 3 | #include "fo_hpf.h" 4 | #include "fo_lpf.h" 5 | #include "fo_shelving_high.h" 6 | #include "fo_shelving_low.h" 7 | #include "so_apf.h" 8 | #include "so_bpf.h" 9 | #include "so_bsf.h" 10 | #include "so_lpf.h" 11 | #include "so_hpf.h" 12 | #include "so_butterworth_bpf.h" 13 | #include "so_butterworth_bsf.h" 14 | #include "so_butterworth_hpf.h" 15 | #include "so_butterworth_lpf.h" 16 | #include "so_linkwitz_riley_hpf.h" 17 | #include "so_linkwitz_riley_lpf.h" 18 | #include "so_parametric_cq_boost.h" 19 | #include "so_parametric_cq_cut.h" 20 | #include "so_parametric_ncq.h" -------------------------------------------------------------------------------- /lib/fo_apf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * First order all-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc, corner frequency 6 | */ 7 | #pragma once 8 | #include "filter_common.h" 9 | 10 | class FO_APF : public Biquad { 11 | public: 12 | tp_coeffs& calculate_coeffs(int fc, int fs = 44100) 13 | { 14 | coef_size_t a = (tan(pi*fc / fs) - 1.0) / (tan(pi*fc / fs) + 1.0); 15 | m_coeffs.a0 = a; 16 | m_coeffs.a1 = 1.0; 17 | m_coeffs.a2 = 0.0; 18 | m_coeffs.b1 = a; 19 | m_coeffs.b2 = 0.0; 20 | return(std::ref(m_coeffs)); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /lib/fo_hpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * First order high-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | */ 5 | #pragma once 6 | #include "filter_common.h" 7 | 8 | class FO_HPF : public Biquad { 9 | public: 10 | tp_coeffs& calculate_coeffs(int fc, int fs) 11 | { 12 | coef_size_t th = 2.0 * pi * fc / fs; 13 | coef_size_t g = cos(th) / (1.0 + sin(th)); 14 | m_coeffs.a0 = (1.0 + g) / 2.0; 15 | m_coeffs.a1 = -((1.0 + g) / 2.0); 16 | m_coeffs.a2 = 0.0; 17 | m_coeffs.b1 = -g; 18 | m_coeffs.b2 = 0.0; 19 | return(std::ref(m_coeffs)); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /lib/fo_lpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * First order low-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | */ 5 | #pragma once 6 | #include "filter_common.h" 7 | 8 | class FO_LPF : public Biquad { 9 | public: 10 | tp_coeffs& calculate_coeffs(int fc, int fs) 11 | { 12 | coef_size_t th = 2.0 * pi * fc / fs; 13 | coef_size_t g = cos(th) / (1.0 + sin(th)); 14 | m_coeffs.a0 = (1.0 - g) / 2.0; 15 | m_coeffs.a1 = (1.0 - g) / 2.0; 16 | m_coeffs.a2 = 0.0; 17 | m_coeffs.b1 = -g; 18 | m_coeffs.b2 = 0.0; 19 | return(std::ref(m_coeffs)); 20 | } 21 | }; -------------------------------------------------------------------------------- /lib/fo_shelving_high.h: -------------------------------------------------------------------------------- 1 | /** 2 | * First order high-shelving filter 3 | * Dimitris Tassopoulos 2016 4 | * 5 | * fc , high shelf frequency 6 | * Low-frequency gain/attenuation in dB 7 | * Shelving filters are used in many tone controls, especially when there are only two, bass 8 | * and treble, which are almost always implemented as shelf types. The filters have a corner 9 | * frequency and gain or attenuation value. 10 | */ 11 | #pragma once 12 | #include "filter_common.h" 13 | #include "biquad_modified.h" 14 | 15 | class FO_SHELVING_HIGH : public BiquadModified { 16 | public: 17 | tp_coeffs& calculate_coeffs(float gain_db, int fc, int fs) 18 | { 19 | coef_size_t th = 2.0 * pi * fc / fs; 20 | coef_size_t m = pow(10.0, gain_db / 20.0); 21 | coef_size_t b = (1.0 + m) / 4.0; 22 | coef_size_t d = b * tan(th / 2.0); 23 | coef_size_t g = (1.0 - d) / (1.0 + d); 24 | m_coeffs.a0 = (1.0 + g) / 2.0; 25 | m_coeffs.a1 = -((1.0 + g) / 2.0); 26 | m_coeffs.a2 = 0.0; 27 | m_coeffs.b1 = -g; 28 | m_coeffs.b2 = 0.0; 29 | m_coeffs.c0 = m - 1.0; 30 | m_coeffs.d0 = 1.0; 31 | return(std::ref(m_coeffs)); 32 | } 33 | }; -------------------------------------------------------------------------------- /lib/fo_shelving_low.h: -------------------------------------------------------------------------------- 1 | /** 2 | * First order low-shelving filter 3 | * Dimitris Tassopoulos 2016 4 | * 5 | * fc , low shelf frequency 6 | * Low-frequency gain/attenuation in dB 7 | * Shelving filters are used in many tone controls, especially when there are only two, bass 8 | * and treble, which are almost always implemented as shelf types. The filters have a corner 9 | * frequency and gain or attenuation value. 10 | */ 11 | #pragma once 12 | #include "filter_common.h" 13 | #include "biquad_modified.h" 14 | 15 | class FO_SHELVING_LOW : public BiquadModified { 16 | public: 17 | tp_coeffs& calculate_coeffs(float gain_db, int fc, int fs) 18 | { 19 | coef_size_t th = 2.0 * pi * fc / fs; 20 | coef_size_t m = pow(10.0, gain_db / 20.0); 21 | coef_size_t b = 4.0 / (1.0 + m); 22 | coef_size_t d = b * tan(th / 2.0); 23 | coef_size_t g = (1.0 - d) / (1.0 + d); 24 | m_coeffs.a0 = (1.0 - g) / 2.0; 25 | m_coeffs.a1 = (1.0 - g) / 2.0; 26 | m_coeffs.a2 = 0.0; 27 | m_coeffs.b1 = -g; 28 | m_coeffs.b2 = 0.0; 29 | m_coeffs.c0 = m - 1.0; 30 | m_coeffs.d0 = 1.0; 31 | return(std::ref(m_coeffs)); 32 | } 33 | }; -------------------------------------------------------------------------------- /lib/so_apf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order all-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc, corner frequency 6 | * Q, steepness of phase shift at fc (second-order only) 7 | */ 8 | #pragma once 9 | #include "filter_common.h" 10 | 11 | class SO_APF : public Biquad { 12 | public: 13 | tp_coeffs& calculate_coeffs(float Q, int fs) 14 | { 15 | coef_size_t a = (tan(pi*Q / fs) - 1.0) / (tan(pi*Q / fs) + 1.0); 16 | coef_size_t b = -cos(pi*Q / fs); 17 | m_coeffs.a0 = -a; 18 | m_coeffs.a1 = b*(1.0 - a); 19 | m_coeffs.a2 = 1.0; 20 | m_coeffs.b1 = m_coeffs.a1; 21 | m_coeffs.b2 = m_coeffs.a0; 22 | return(std::ref(m_coeffs)); 23 | } 24 | }; -------------------------------------------------------------------------------- /lib/so_bpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order band-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency 6 | * Q , quality factor controlling width of peak or notch = 1/BW 7 | */ 8 | #pragma once 9 | #include "filter_common.h" 10 | 11 | class SO_BPF : public Biquad { 12 | public: 13 | tp_coeffs& calculate_coeffs(float Q, int fc, int fs) 14 | { 15 | coef_size_t w = 2.0 * pi * fc / fs; 16 | coef_size_t b = 0.5*((1.0 - tan(w / (2.0*Q))) / (1.0 + tan(w / (2.0*Q)))); 17 | coef_size_t g = (0.5 + b)*cos(w); 18 | m_coeffs.a0 = 0.5 - b; 19 | m_coeffs.a1 = 0.0; 20 | m_coeffs.a2 = -(0.5 - b); 21 | m_coeffs.b1 = -2.0 * g; 22 | m_coeffs.b2 = 2.0 * b; 23 | return(std::ref(m_coeffs)); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /lib/so_bsf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order band-stop filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency 6 | * Q , quality factor controlling width of peak or notch = 1/BW 7 | */ 8 | #pragma once 9 | #include "filter_common.h" 10 | 11 | class SO_BSF : public Biquad { 12 | public: 13 | tp_coeffs& calculate_coeffs(float Q, int fc, int fs) 14 | { 15 | coef_size_t w = 2.0 * pi * fc / fs; 16 | coef_size_t b = 0.5*((1.0 - tan(w / (2.0*Q))) / (1.0 + tan(w / (2.0*Q)))); 17 | coef_size_t g = (0.5 + b)*cos(w); 18 | m_coeffs.a0 = 0.5 + b; 19 | m_coeffs.a1 = -2.0 * g; 20 | m_coeffs.a2 = 0.5 + b; 21 | m_coeffs.b1 = -2.0 * g; 22 | m_coeffs.b2 = 2.0 * b; 23 | return(std::ref(m_coeffs)); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /lib/so_butterworth_bpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order Butterworth band-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency 6 | * BW , bandwidth of peak/notch = fc/Q 7 | * Butterworth BPF and BSF are made by cascading (BPF) or paralleling (BSF) a Butterworth 8 | * LPF and Butterworth HPF. 9 | */ 10 | #pragma once 11 | #include "filter_common.h" 12 | 13 | class SO_BUTTERWORTH_BPF : public Biquad { 14 | public: 15 | tp_coeffs& calculate_coeffs(float bw, int fc, int fs) 16 | { 17 | coef_size_t c = 1.0 / (tan(pi*fc*bw / fs)); 18 | coef_size_t d = 2.0 * cos(2.0 * pi * fc / fs); 19 | m_coeffs.a0 = 1.0 / (1.0 + c); 20 | m_coeffs.a1 = 0.0; 21 | m_coeffs.a2 = - m_coeffs.a0; 22 | m_coeffs.b1 = -m_coeffs.a0 * (c * d); 23 | m_coeffs.b2 = m_coeffs.a0 * (c - 1.0); 24 | return(std::ref(m_coeffs)); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/so_butterworth_bsf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order Butterworth band-stop filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency 6 | * BW , bandwidth of peak/notch = fc/Q 7 | * Butterworth BPF and BSF are made by cascading (BPF) or paralleling (BSF) a Butterworth 8 | * LPF and Butterworth HPF. 9 | */ 10 | #pragma once 11 | #include "filter_common.h" 12 | 13 | class SO_BUTTERWORTH_BSF : public Biquad { 14 | public: 15 | tp_coeffs& calculate_coeffs(float bw, int fc, int fs) 16 | { 17 | coef_size_t c = tan(pi*fc*bw / fs); 18 | coef_size_t d = 2.0 * cos(2.0 * pi * fc / fs); 19 | m_coeffs.a0 = 1.0 / (1.0 + c); 20 | m_coeffs.a1 = -m_coeffs.a0 * d; 21 | m_coeffs.a2 = m_coeffs.a0; 22 | m_coeffs.b1 = -m_coeffs.a0 * d; 23 | m_coeffs.b2 = m_coeffs.a0 * (1.0 - c); 24 | return(std::ref(m_coeffs)); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/so_butterworth_hpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order Butterworth high-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc, corner frequency 6 | * Butterworth low-pass and high-pass filters are specialized versions of the ordinary secondorder 7 | * low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can 8 | * assume before peaking in the frequency response is observed. 9 | */ 10 | #pragma once 11 | #include "filter_common.h" 12 | 13 | class SO_BUTTERWORTH_HPF : public Biquad { 14 | public: 15 | tp_coeffs& calculate_coeffs(int fc, int fs) 16 | { 17 | coef_size_t c = tan(pi*fc / fs); 18 | m_coeffs.a0 = 1.0 / (1.0 + sqrt2*c + pow(c, 2.0)); 19 | m_coeffs.a1 = -2.0 * m_coeffs.a0; 20 | m_coeffs.a2 = m_coeffs.a0; 21 | m_coeffs.b1 = 2.0 * m_coeffs.a0*(pow(c, 2.0) - 1.0); 22 | m_coeffs.b2 = m_coeffs.a0 * (1.0 - sqrt2*c + pow(c, 2.0)); 23 | return(std::ref(m_coeffs)); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /lib/so_butterworth_lpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order Butterworth low-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc, corner frequency 6 | * Butterworth low-pass and high-pass filters are specialized versions of the ordinary secondorder 7 | * low-pass filter. Their Q values are fixed at 0.707, which is the largest value it can 8 | * assume before peaking in the frequency response is observed. 9 | */ 10 | #pragma once 11 | #include "filter_common.h" 12 | #include 13 | 14 | class SO_BUTTERWORTH_LPF : public Biquad { 15 | public: 16 | tp_coeffs& calculate_coeffs(int fc, int fs) 17 | { 18 | coef_size_t c = 1.0 / (std::tan(pi*fc / fs)); 19 | m_coeffs.a0 = 1.0 / (1.0 + sqrt2*c + std::pow(c, 2.0) ); 20 | m_coeffs.a1 = 2.0 * m_coeffs.a0; 21 | m_coeffs.a2 = m_coeffs.a0; 22 | m_coeffs.b1 = 2.0 * m_coeffs.a0*(1.0 - std::pow(c, 2.0)); 23 | m_coeffs.b2 = m_coeffs.a0 * (1.0 - sqrt2*c + std::pow(c, 2.0) ); 24 | return(std::ref(m_coeffs)); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/so_hpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order high-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency 6 | * Q , quality factor controlling resonant peaking 7 | */ 8 | #pragma once 9 | #include "filter_common.h" 10 | 11 | class SO_HPF : public Biquad { 12 | public: 13 | tp_coeffs& calculate_coeffs(float Q, int fc, int fs) 14 | { 15 | coef_size_t w = 2.0 * pi * fc / fs; 16 | coef_size_t d = 1.0 / Q; 17 | coef_size_t b = 0.5*(1.0 - (d / 2)*sin(w)) / (1.0 + (d / 2.0)*sin(w)); 18 | coef_size_t g = (0.5 + b)*cos(w); 19 | m_coeffs.a0 = (0.5 + b + g) / 2.0; 20 | m_coeffs.a1 = -(0.5 + b + g); 21 | m_coeffs.a2 = m_coeffs.a0; 22 | m_coeffs.b1 = -2.0 * g; 23 | m_coeffs.b2 = 2.0 * b; 24 | return(std::ref(m_coeffs)); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/so_linkwitz_riley_hpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order Linkwitz-Riley high-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency (-6 dB) 6 | * Second-order Linkwitz/Riley HPFs are designed to have an attenuation of -6 dB 7 | * at the corner frequency rather than the standard -3 dB When these 8 | * filters are placed in parallel with the same cutoff frequency, their outputs sum exactly and 9 | * the resulting response is flat. They are often used in crossovers. 10 | */ 11 | #pragma once 12 | #include "filter_common.h" 13 | 14 | class SO_LINKWITZ_RILEY_HPF : public Biquad { 15 | public: 16 | tp_coeffs& calculate_coeffs(int fc, int fs) 17 | { 18 | coef_size_t th = pi * fc / fs; 19 | coef_size_t Wc = pi * fc; 20 | coef_size_t k = Wc / tan(th); 21 | 22 | coef_size_t d = pow(k, 2.0) + pow(Wc, 2.0) + 2.0 * k * Wc; 23 | m_coeffs.a0 = pow(k, 2.0) / d; 24 | m_coeffs.a1 = -2.0 * pow(k, 2.0) / d; 25 | m_coeffs.a2 = m_coeffs.a0; 26 | m_coeffs.b1 = (-2.0 * pow(k, 2.0) + 2.0 * pow(Wc, 2.0)) / d; 27 | m_coeffs.b2 = (-2.0 * k * Wc + pow(k, 2.0) + pow(Wc, 2.0)) / d; 28 | return(std::ref(m_coeffs)); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /lib/so_linkwitz_riley_lpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order Linkwitz-Riley low-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency (-6 dB) 6 | * Second-order Linkwitz/Riley LPFs are designed to have an attenuation of -6 dB 7 | * at the corner frequency rather than the standard -3 dB When these 8 | * filters are placed in parallel with the same cutoff frequency, their outputs sum exactly and 9 | * the resulting response is flat. They are often used in crossovers. 10 | */ 11 | #pragma once 12 | #include "filter_common.h" 13 | 14 | class SO_LINKWITZ_RILEY_LPF : public Biquad { 15 | public: 16 | tp_coeffs& calculate_coeffs(int fc, int fs) 17 | { 18 | coef_size_t th = pi * fc / fs; 19 | coef_size_t Wc = pi * fc; 20 | coef_size_t k = Wc / tan(th); 21 | 22 | coef_size_t d = pow(k, 2.0) + pow(Wc, 2.0) + 2.0 * k * Wc; 23 | m_coeffs.a0 = pow(Wc, 2.0) / d; 24 | m_coeffs.a1 = 2.0 * pow(Wc, 2.0) / d; 25 | m_coeffs.a2 = m_coeffs.a0; 26 | m_coeffs.b1 = (-2.0 * pow(k, 2.0) + 2.0 * pow(Wc, 2.0)) / d; 27 | m_coeffs.b2 = (-2.0 * k * Wc + pow(k, 2.0) + pow(Wc, 2.0)) / d; 28 | return(std::ref(m_coeffs)); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /lib/so_lpf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order Low-pass filter 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , corner frequency 6 | * Q , quality factor controlling resonant peaking 7 | */ 8 | #pragma once 9 | #include "filter_common.h" 10 | 11 | class SO_LPF : public Biquad { 12 | public: 13 | tp_coeffs& calculate_coeffs(float Q, int fc, int fs) 14 | { 15 | coef_size_t w = 2.0 * pi * fc / fs; 16 | coef_size_t d = 1.0 / Q; 17 | coef_size_t b = 0.5*(1.0 - (d / 2)*sin(w)) / (1.0 + (d / 2.0)*sin(w)); 18 | coef_size_t g = (0.5 + b)*cos(w); 19 | m_coeffs.a0 = (0.5 + b - g) / 2.0; 20 | m_coeffs.a1 = 0.5 + b - g; 21 | m_coeffs.a2 = m_coeffs.a0; 22 | m_coeffs.b1 = -2.0 * g; 23 | m_coeffs.b2 = 2.0 * b; 24 | return(std::ref(m_coeffs)); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/so_parametric_cq_boost.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order parametric/peaking boost filter with constant-Q 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , center frequency 6 | * Q quality factor 7 | * Gain/attenuation in dB 8 | * Parametric EQs allow you to adjust the center frequency, Q and boost or cut creating any 9 | * arbitrary bumps or notches in the frequency response. The parametric EQ is a 10 | * variation on the ordinary band-pass and band-stop fi lters that generates symmetrical boost/ 11 | * cut curves and mixes in the dry signal to create the fi nal response. A true digital parametric 12 | * EQ not only has independent controls, but each control only varies one coeffi cient in the 13 | * fi lter. The parametric EQs in this section afford the same frequency response but adjustments 14 | * in any parameter require a recalculation of all the coeffi cients. These fi lters are also called 15 | * peaking fi lters. 16 | * 17 | * This design creates an almost perfect constant-Q filter with only a small amount of error 18 | * for low-boost (or cut) values. 19 | */ 20 | #pragma once 21 | #include "filter_common.h" 22 | 23 | class SO_PARAMETRIC_CQ_BOOST : public Biquad { 24 | public: 25 | tp_coeffs& calculate_coeffs(float gain_db, float Q, int fc, int fs) 26 | { 27 | coef_size_t K = 2.0 * pi * fc / fs; 28 | coef_size_t V0 = pow(10.0, gain_db / 20.0); 29 | coef_size_t d0 = 1.0 + K/Q + pow(K, 2.0); 30 | coef_size_t a = 1.0 + (V0*K)/Q + pow(K, 2.0); 31 | coef_size_t b = 2.0*(pow(K, 2.0) - 1.0); 32 | coef_size_t g = 1.0 - (V0*K)/Q + pow(K, 2.0); 33 | coef_size_t d = 1.0 - K/Q + pow(K, 2.0); 34 | m_coeffs.a0 = a/d0; 35 | m_coeffs.a1 = b/d0; 36 | m_coeffs.a2 = g/d0; 37 | m_coeffs.b1 = b/d0; 38 | m_coeffs.b2 = d/d0; 39 | m_coeffs.c0 = 1.0; 40 | m_coeffs.d0 = 0.0; 41 | return(std::ref(m_coeffs)); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /lib/so_parametric_cq_cut.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order parametric/peaking cut filter with constant-Q 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , center frequency 6 | * Q quality factor 7 | * Gain/attenuation in dB 8 | * Parametric EQs allow you to adjust the center frequency, Q and boost or cut creating any 9 | * arbitrary bumps or notches in the frequency response. The parametric EQ is a 10 | * variation on the ordinary band-pass and band-stop fi lters that generates symmetrical boost/ 11 | * cut curves and mixes in the dry signal to create the fi nal response. A true digital parametric 12 | * EQ not only has independent controls, but each control only varies one coeffi cient in the 13 | * fi lter. The parametric EQs in this section afford the same frequency response but adjustments 14 | * in any parameter require a recalculation of all the coeffi cients. These fi lters are also called 15 | * peaking fi lters. 16 | * 17 | * This design creates an almost perfect constant-Q filter with only a small amount of error 18 | * for low-boost (or cut) values. 19 | */ 20 | #pragma once 21 | #include "filter_common.h" 22 | 23 | class SO_PARAMETRIC_CQ_CUT : public Biquad { 24 | public: 25 | tp_coeffs calculate_coeffs(float gain_db, float Q, int fc, int fs) 26 | { 27 | coef_size_t K = 2.0 * pi * fc / fs; 28 | coef_size_t V0 = pow(10.0, gain_db / 20.0); 29 | coef_size_t d0 = 1.0 + K / Q + pow(K, 2.0); 30 | coef_size_t e = 1.0 + K / (V0*Q) + pow(K, 2.0); 31 | coef_size_t b = 2.0*(pow(K, 2.0) - 1.0); 32 | coef_size_t d = 1.0 - K / Q + pow(K, 2.0); 33 | coef_size_t h = 1.0 - K / (V0*Q) + pow(K, 2.0); 34 | m_coeffs.a0 = d0/e; 35 | m_coeffs.a1 = b / e; 36 | m_coeffs.a2 = d / e; 37 | m_coeffs.b1 = b / e; 38 | m_coeffs.b2 = h / e; 39 | m_coeffs.c0 = 1.0; 40 | m_coeffs.d0 = 0.0; 41 | return(m_coeffs); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /lib/so_parametric_ncq.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Second order parametric/peaking filter with non-constant-Q 3 | * Dimitris Tassopoulos 2016-2020 4 | * 5 | * fc , center frequency 6 | * Q quality factor 7 | * Gain/attenuation in dB 8 | * Parametric EQs allow you to adjust the center frequency, Q and boost or cut creating any 9 | * arbitrary bumps or notches in the frequency response. The parametric EQ is a 10 | * variation on the ordinary band-pass and band-stop fi lters that generates symmetrical boost/ 11 | * cut curves and mixes in the dry signal to create the fi nal response. A true digital parametric 12 | * EQ not only has independent controls, but each control only varies one coeffi cient in the 13 | * fi lter. The parametric EQs in this section afford the same frequency response but adjustments 14 | * in any parameter require a recalculation of all the coeffi cients. These fi lters are also called 15 | * peaking fi lters. 16 | * This parametric EQ is not constant-Q, which means the bandwidth varies depending on the 17 | * boost/cut value. Some analog fi lters have the same issue, although there is occasional debate 18 | * over whether or not this is desirable in an EQ design. 19 | */ 20 | #pragma once 21 | #include "filter_common.h" 22 | 23 | class SO_PARAMETRIC_NCQ : public Biquad { 24 | public: 25 | tp_coeffs& calculate_coeffs(float gain_db, float Q, int fc, int fs) 26 | { 27 | coef_size_t w = 2.0 * pi * fc / fs; 28 | coef_size_t m = pow(10.0, gain_db / 20.0); 29 | coef_size_t z = 4.0 / (1.0 + m); 30 | coef_size_t b = 0.5 * ((1.0 - z*tan(w / (2.0*Q)) / (1 + z*tan(w / (2.0*Q))))); 31 | coef_size_t g = (0.5 + b) * cos(w); 32 | m_coeffs.a0 = 0.5 - b; 33 | m_coeffs.a1 = 0.0; 34 | m_coeffs.a2 = -(0.5 - b); 35 | m_coeffs.b1 = -2.0*g; 36 | m_coeffs.b2 = 2.0 * b; 37 | m_coeffs.c0 = m - 1.0; 38 | m_coeffs.d0 = 1.0; 39 | return(std::ref(m_coeffs)); 40 | } 41 | }; -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../lib) 2 | 3 | enable_testing() 4 | 5 | # fo_apf 6 | add_executable(fo_apf_test fo_apf_test.cpp) 7 | target_link_libraries(fo_apf_test gtest_main) 8 | add_test(NAME fo_apf_test COMMAND fo_apf_test) 9 | 10 | # fo_hpf 11 | add_executable(fo_hpf_test fo_hpf_test.cpp) 12 | target_link_libraries(fo_hpf_test gtest_main) 13 | add_test(NAME fo_hpf_test COMMAND fo_hpf_test) 14 | 15 | # fo_lpf 16 | add_executable(fo_lpf_test fo_lpf_test.cpp) 17 | target_link_libraries(fo_lpf_test gtest_main) 18 | add_test(NAME fo_lpf_test COMMAND fo_lpf_test) 19 | 20 | # fo_shelving_high 21 | add_executable(fo_shelving_high_test fo_shelving_high_test.cpp) 22 | target_link_libraries(fo_shelving_high_test gtest_main) 23 | add_test(NAME fo_shelving_high_test COMMAND fo_shelving_high_test) 24 | 25 | # fo_shelving_low 26 | add_executable(fo_shelving_low_test fo_shelving_low_test.cpp) 27 | target_link_libraries(fo_shelving_low_test gtest_main) 28 | add_test(NAME fo_shelving_low_test COMMAND fo_shelving_low_test) 29 | 30 | # so_apf 31 | add_executable(so_apf_test so_apf_test.cpp) 32 | target_link_libraries(so_apf_test gtest_main) 33 | add_test(NAME so_apf_test COMMAND so_apf_test) 34 | 35 | # so_hpf 36 | add_executable(so_hpf_test so_hpf_test.cpp) 37 | target_link_libraries(so_hpf_test gtest_main) 38 | add_test(NAME so_hpf_test COMMAND so_hpf_test) 39 | 40 | # so_lpf 41 | add_executable(so_lpf_test so_lpf_test.cpp) 42 | target_link_libraries(so_lpf_test gtest_main) 43 | add_test(NAME so_lpf_test COMMAND so_lpf_test) 44 | 45 | # so_bpf 46 | add_executable(so_bpf_test so_bpf_test.cpp) 47 | target_link_libraries(so_bpf_test gtest_main) 48 | add_test(NAME so_bpf_test COMMAND so_bpf_test) 49 | 50 | # so_bsf 51 | add_executable(so_bsf_test so_bsf_test.cpp) 52 | target_link_libraries(so_bsf_test gtest_main) 53 | add_test(NAME so_bsf_test COMMAND so_bsf_test) 54 | 55 | # so_butterworth_bpf 56 | add_executable(so_butterworth_bpf_test so_butterworth_bpf_test.cpp) 57 | target_link_libraries(so_butterworth_bpf_test gtest_main) 58 | add_test(NAME so_butterworth_bpf_test COMMAND so_butterworth_bpf_test) 59 | 60 | # so_butterworth_bsf 61 | add_executable(so_butterworth_bsf_test so_butterworth_bsf_test.cpp) 62 | target_link_libraries(so_butterworth_bsf_test gtest_main) 63 | add_test(NAME so_butterworth_bsf_test COMMAND so_butterworth_bsf_test) 64 | 65 | # so_butterworth_hpf 66 | add_executable(so_butterworth_hpf_test so_butterworth_hpf_test.cpp) 67 | target_link_libraries(so_butterworth_hpf_test gtest_main) 68 | add_test(NAME so_butterworth_hpf_test COMMAND so_butterworth_hpf_test) 69 | 70 | # so_butterworth_lpf 71 | add_executable(so_butterworth_lpf_test so_butterworth_lpf_test.cpp) 72 | target_link_libraries(so_butterworth_lpf_test gtest_main) 73 | add_test(NAME so_butterworth_lpf_test COMMAND so_butterworth_lpf_test) 74 | 75 | # so_linkwitz_riley_hpf 76 | add_executable(so_linkwitz_riley_hpf_test so_linkwitz_riley_hpf_test.cpp) 77 | target_link_libraries(so_linkwitz_riley_hpf_test gtest_main) 78 | add_test(NAME so_linkwitz_riley_hpf_test COMMAND so_linkwitz_riley_hpf_test) 79 | 80 | # so_linkwitz_riley_lpf 81 | add_executable(so_linkwitz_riley_lpf_test so_linkwitz_riley_lpf_test.cpp) 82 | target_link_libraries(so_linkwitz_riley_lpf_test gtest_main) 83 | add_test(NAME so_linkwitz_riley_lpf_test COMMAND so_linkwitz_riley_lpf_test) 84 | 85 | # so_parametric_cq_boost 86 | add_executable(so_parametric_cq_boost_test so_parametric_cq_boost_test.cpp) 87 | target_link_libraries(so_parametric_cq_boost_test gtest_main) 88 | add_test(NAME so_parametric_cq_boost_test COMMAND so_parametric_cq_boost_test) 89 | 90 | # so_parametric_cq_cut 91 | add_executable(so_parametric_cq_cut_test so_parametric_cq_cut_test.cpp) 92 | target_link_libraries(so_parametric_cq_cut_test gtest_main) 93 | add_test(NAME so_parametric_cq_cut_test COMMAND so_parametric_cq_cut_test) 94 | 95 | # so_parametric_ncq 96 | add_executable(so_parametric_ncq_test so_parametric_ncq_test.cpp) 97 | target_link_libraries(so_parametric_ncq_test gtest_main) 98 | add_test(NAME so_parametric_ncq_test COMMAND so_parametric_ncq_test) 99 | 100 | add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} 101 | DEPENDS 102 | fo_apf_test fo_hpf_test fo_lpf_test 103 | fo_shelving_high_test fo_shelving_low_test 104 | so_apf_test so_hpf_test so_lpf_test 105 | so_bpf_test so_bsf_test 106 | so_butterworth_bpf_test so_butterworth_bsf_test 107 | so_butterworth_hpf_test so_butterworth_lpf_test 108 | so_linkwitz_riley_hpf_test so_linkwitz_riley_lpf_test 109 | so_parametric_cq_boost_test so_parametric_cq_cut_test 110 | so_parametric_ncq_test) -------------------------------------------------------------------------------- /tests/fo_apf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct fo_apf : public testing::Test { 5 | FO_APF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new FO_APF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(fo_apf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, -0.71659, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(fo_apf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(fo_apf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, -0.71659, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/fo_hpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct fo_hpf : public testing::Test { 5 | FO_HPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new FO_HPF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(fo_hpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.858295, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(fo_hpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(fo_hpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.85829, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/fo_lpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct fo_lpf : public testing::Test { 5 | FO_LPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new FO_LPF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(fo_lpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.141705, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(fo_lpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(fo_lpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.141705, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/fo_shelving_high_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct fo_shelving_high : public testing::Test { 5 | FO_SHELVING_HIGH *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new FO_SHELVING_HIGH(); 9 | m_coeffs = m_filter->calculate_coeffs(GAIN, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(fo_shelving_high, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.91946, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(fo_shelving_high, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(fo_shelving_high, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 1.11219, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/fo_shelving_low_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct fo_shelving_low : public testing::Test { 5 | FO_SHELVING_LOW *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new FO_SHELVING_LOW(); 9 | m_coeffs = m_filter->calculate_coeffs(GAIN, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(fo_shelving_low, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.237348, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(fo_shelving_low, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(fo_shelving_low, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 1.02896, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_apf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_apf : public testing::Test { 5 | SO_APF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_APF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_apf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.71659, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_apf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_apf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.71659, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_bpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_bpf : public testing::Test { 5 | SO_BPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_BPF(); 9 | m_coeffs = m_filter->calculate_coeffs(Q_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_bpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.141705, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_bpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_bpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.141705, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_bsf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_bsf : public testing::Test { 5 | SO_BSF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_BSF(); 9 | m_coeffs = m_filter->calculate_coeffs(Q_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_bsf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.858295, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_bsf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_bsf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.858295, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_butterworth_bpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_butterworth_bpf : public testing::Test { 5 | SO_BUTTERWORTH_BPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_BUTTERWORTH_BPF(); 9 | m_coeffs = m_filter->calculate_coeffs(BW_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_butterworth_bpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.211325, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_butterworth_bpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_butterworth_bpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.211325, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_butterworth_bsf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_butterworth_bsf : public testing::Test { 5 | SO_BUTTERWORTH_BSF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_BUTTERWORTH_BSF(); 9 | m_coeffs = m_filter->calculate_coeffs(BW_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_butterworth_bsf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.788675, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_butterworth_bsf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_butterworth_bsf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.788675, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_butterworth_hpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_butterworth_hpf : public testing::Test { 5 | SO_BUTTERWORTH_HPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_BUTTERWORTH_HPF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_butterworth_hpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.793181, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_butterworth_hpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_butterworth_hpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.793181, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_butterworth_lpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_butterworth_lpf : public testing::Test { 5 | SO_BUTTERWORTH_LPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_BUTTERWORTH_LPF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_butterworth_lpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.0216207, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_butterworth_lpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_butterworth_lpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.0216207, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_hpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_hpf : public testing::Test { 5 | SO_HPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_HPF(); 9 | m_coeffs = m_filter->calculate_coeffs(Q_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_hpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.838674, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_hpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_hpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.838674, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_linkwitz_riley_hpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_linkwitz_riley_hpf : public testing::Test { 5 | SO_LINKWITZ_RILEY_HPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_LINKWITZ_RILEY_HPF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_linkwitz_riley_hpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.73667, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_linkwitz_riley_hpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_linkwitz_riley_hpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.73667, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_linkwitz_riley_lpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_linkwitz_riley_lpf : public testing::Test { 5 | SO_LINKWITZ_RILEY_LPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_LINKWITZ_RILEY_LPF(); 9 | m_coeffs = m_filter->calculate_coeffs(FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_linkwitz_riley_lpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.0200803, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_linkwitz_riley_lpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_linkwitz_riley_lpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.0200803, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_lpf_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_lpf : public testing::Test { 5 | SO_LPF *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_LPF(); 9 | m_coeffs = m_filter->calculate_coeffs(Q_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_lpf, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.0228608, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_lpf, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_lpf, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.0228608, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_parametric_cq_boost_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_parametric_cq_boost : public testing::Test { 5 | SO_PARAMETRIC_CQ_BOOST *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_PARAMETRIC_CQ_BOOST(); 9 | m_coeffs = m_filter->calculate_coeffs(GAIN, Q_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_parametric_cq_boost, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 1.02784, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_parametric_cq_boost, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_parametric_cq_boost, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 1.02784, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_parametric_cq_cut_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_parametric_cq_cut : public testing::Test { 5 | SO_PARAMETRIC_CQ_CUT *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_PARAMETRIC_CQ_CUT(); 9 | m_coeffs = m_filter->calculate_coeffs(GAIN, Q_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_parametric_cq_cut, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 1.02544, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_parametric_cq_cut, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_parametric_cq_cut, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 1.02544, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/so_parametric_ncq_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tests_common.h" 2 | 3 | // Text fixture for Class-Under-Test (CUT) 4 | struct so_parametric_ncq : public testing::Test { 5 | SO_PARAMETRIC_NCQ *m_filter; 6 | Biquad::tp_coeffs m_coeffs; 7 | void SetUp() { 8 | m_filter = new SO_PARAMETRIC_NCQ(); 9 | m_coeffs = m_filter->calculate_coeffs(GAIN, Q_DEF, FC, SAMPLING_RATE); 10 | } 11 | void TearDown() { delete m_filter; } 12 | }; 13 | 14 | TEST_F(so_parametric_ncq, coeff_calculation) { 15 | // Assert 16 | EXPECT_NEAR(m_coeffs.a0, 0.118674, 0.00001); 17 | #ifdef DEBUG_OUTPUT 18 | debug_print(m_coeffs); 19 | #endif 20 | } 21 | 22 | TEST_F(so_parametric_ncq, process_0) { 23 | // Act 24 | auto yn = m_filter->process(TEST_SAMPLE_0); 25 | // Assert 26 | EXPECT_DOUBLE_EQ(yn, 0); 27 | #ifdef DEBUG_OUTPUT 28 | std::cout << "yn: " << yn << std::endl; 29 | #endif 30 | } 31 | 32 | TEST_F(so_parametric_ncq, process_1) { 33 | // Act 34 | auto yn = m_filter->process(TEST_SAMPLE_1); 35 | // Assert 36 | EXPECT_NEAR(yn, 0.118674, 0.00001); 37 | #ifdef DEBUG_OUTPUT 38 | std::cout << "yn: " << yn << std::endl; 39 | #endif 40 | } 41 | 42 | int main(int argc, char **argv) 43 | { 44 | testing::InitGoogleTest(&argc, argv); 45 | 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /tests/tests_common.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "filter_common.h" 6 | #include "filter_includes.h" 7 | 8 | // #define DEBUG_OUTPUT 9 | 10 | #define BW_DEF 1000.0 // Default bandwidth 11 | #define Q_DEF 1.0 12 | #define GAIN 1.0 13 | #define FC 5000 14 | #define SAMPLING_RATE 96000 15 | #define TEST_SAMPLE_0 0 16 | #define TEST_SAMPLE_1 1.0 17 | 18 | static inline void debug_print(Biquad::tp_coeffs &coeffs) 19 | { 20 | std::cout << "Coeffs: " << std::endl; 21 | std::cout << "\ta0: " << coeffs.a0 << std::endl; 22 | std::cout << "\ta1: " << coeffs.a1 << std::endl; 23 | std::cout << "\ta2: " << coeffs.a2 << std::endl; 24 | std::cout << "\tb1: " << coeffs.b1 << std::endl; 25 | std::cout << "\tb2: " << coeffs.b2 << std::endl; 26 | } --------------------------------------------------------------------------------