diff --git a/.travis.yml b/.travis.yml index 39fced1..1e231e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: python python: - "3.7" -dist: bionic +dist: focal before_install: - sudo apt-get install -y libfftw3-dev diff --git a/CMakeLists.txt b/CMakeLists.txt index 7667d0f..9d9b7c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,136 @@ -cmake_minimum_required(VERSION 2.8) -project(kissfft) +file(READ Makefile _MAKEFILE_CONTENTS) +string(REGEX MATCH "KFVER=([0-9]+)\n" KFVER_MATCH "${_MAKEFILE_CONTENTS}") +if(NOT KFVER_MATCH) + message(FATAL_ERROR "Cannot extract version from Makefile") +endif() +set(MAKEFILE_EXTRACTED_VERSION "${CMAKE_MATCH_1}") + +cmake_minimum_required(VERSION 3.13) +project(kissfft VERSION "${MAKEFILE_EXTRACTED_VERSION}") + +option(KISSFFT_SIMD "Build kissfft with SIMD" OFF) +option(KISSFFT_FLOAT "Build kissfft with float type" ON) +option(KISSFFT_OPENMP "Build kissfft with openmp" OFF) + +if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + add_compile_options(-ffast-math -fomit-frame-pointer + -W -Wall -Waggregate-return -Wcast-align -Wcast-qual -Wshadow -Wwrite-strings + "$<$:-Wstrict-prototypes;-Wmissing-prototypes;-Wnested-externs;-Wbad-function-cast>") +endif() + +set(KISSFFT_FIXED_WIDTH_VALID 16 32) +set(KISSFFT_FIXED_WIDTH "16" CACHE STRING "Width of kissfft fixed integer (16/32)") +set_property(CACHE KISSFFT_FIXED_WIDTH PROPERTY STRINGS ${KISSFFT_FIXED_WIDTH_VALID}) +if(NOT KISSFFT_FIXED_WIDTH IN_LIST KISSFFT_FIXED_WIDTH_VALID) + message(FATAL_ERROR "KISSFFT_FIXED_WIDTH must be 16 or 32") +endif() + +set(KISSFFT_FLOAT_TYPE_VALID float double) +set(KISSFFT_FLOAT_TYPE "float" CACHE STRING "Floating type of kissfft (float/double)") +set_property(CACHE KISSFFT_FLOAT_TYPE PROPERTY STRINGS ${KISSFFT_FLOAT_TYPE_VALID}) +if(NOT KISSFFT_FLOAT_TYPE IN_LIST KISSFFT_FLOAT_TYPE_VALID) + message(FATAL_ERROR "KISSFFT_FLOAT_TYPE must be float or double") +endif() add_library(kissfft kiss_fft.c) target_include_directories(kissfft PUBLIC - $ - $) + $ + $) + +set(KISSFFT_COMPILE_DEFINITIONS) +if(KISSFFT_SIMD) + list(APPEND KISSFFT_COMPILE_DEFINITIONS USE_SIMD) + set(KISSFFT_DATATYPE "simd") +else() + if(KISSFFT_FLOAT) + list(APPEND KISSFFT_COMPILE_DEFINITIONS kiss_fft_scalar=${KISSFFT_FLOAT_TYPE}) + set(KISSFFT_DATATYPE "${KISSFFT_FLOAT_TYPE}") + else() + list(APPEND KISSFFT_COMPILE_DEFINITIONS KISSFFT_FIXED_POINT=${KISSFFT_FIXED_WIDTH}) + set(KISSFFT_DATATYPE "int${KISSFFT_FIXED_WIDTH}") + endif() +endif() +if(KISSFFT_OPENMP) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + target_compile_options(kissfft PRIVATE -fopenmp) + target_link_options(kissfft PUBLIC -fopenmp) + else() + message(FATAL_ERROR "Don't know how to enable openmp for this compiler") + endif() +endif() +message(STATUS "Building KissFFT with datatype=${KISSFFT_DATATYPE}") + +if(BUILD_SHARED_LIBS) + list(APPEND KISSFFT_COMPILE_DEFINITIONS KISS_FFT_SHARED) + set_target_properties(kissfft PROPERTIES + C_VISIBILITY_PRESET hidden) + set(KISSFFT_EXPORT_SUFFIX "-shared") +else() + set(KISSFFT_EXPORT_SUFFIX "-static") +endif() +target_compile_definitions(kissfft PUBLIC ${KISSFFT_COMPILE_DEFINITIONS}) +set(KISSFFT_OUTPUT_NAME "kissfft_${KISSFFT_DATATYPE}") +set_target_properties(kissfft PROPERTIES + OUTPUT_NAME "${KISSFFT_OUTPUT_NAME}" + DEFINE_SYMBOL KISS_FFT_BUILD + EXPORT_NAME "kissfft-${KISSFFT_DATATYPE}" + VERSION ${PROJECT_VERSION}) +add_library(kissfft::kissfft ALIAS kissfft) +add_library(kissfft::kissff-${KISSFFT_DATATYPE} ALIAS kissfft) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(kissfft PRIVATE m) +endif() + +function(add_kissfft_executable NAME) + add_executable(${NAME} ${ARGN}) + target_link_libraries(${NAME} PRIVATE kissfft::kissfft) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(${NAME} PRIVATE m) + endif() + set_target_properties(${NAME} PROPERTIES + OUTPUT_NAME "${NAME}_${KISSFFT_DATATYPE}") +endfunction() + +option(KISSFFT_TOOLS "Build kissfft tools" ON) +if(KISSFFT_TOOLS) + add_subdirectory(tools) +endif() + +option(KISSFFT_TEST "Build and enable kissfft tests" ON) +if(KISSFFT_TEST) + enable_testing() + add_subdirectory(test) +endif() + +option(KISSFFT_INSTALL "Enable kissfft install" ON) +if (KISSFFT_INSTALL) + include(GNUInstallDirs) + install(TARGETS kissfft EXPORT kissfft + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + install(FILES "kiss_fft.h" "kissfft.hh" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + + set(KISSFFT_INSTALL_CMAKE "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE FILEPATH "Install destination of kissfft cmake modules") + mark_as_advanced(KISSFFT_INSTALL_CMAKE) + + install(EXPORT kissfft DESTINATION "${KISSFFT_INSTALL_CMAKE}" + NAMESPACE "kissfft::" + FILE "${PROJECT_NAME}-${KISSFFT_DATATYPE}${KISSFFT_EXPORT_SUFFIX}-targets.cmake") + include(CMakePackageConfigHelpers) + configure_package_config_file(kissfft-config.cmake.in kissfft-config.cmake + INSTALL_DESTINATION "${KISSFFT_INSTALL_CMAKE}") + write_basic_package_version_file(kissfft-config-version.cmake COMPATIBILITY AnyNewerVersion) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kissfft-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/kissfft-config-version.cmake" + DESTINATION "${KISSFFT_INSTALL_CMAKE}") + + set(PKG_KISSFFT_DEFS) + foreach(_def ${KISSFFT_COMPILE_DEFINITIONS}) + set(PKG_KISSFFT_DEFS "${PKG_KISSFFT_DEFS} -D${_def}") + endforeach() + configure_file(kissfft.pc.in "kissfft-${KISSFFT_DATATYPE}.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kissfft-${KISSFFT_DATATYPE}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif() diff --git a/kiss_fft.h b/kiss_fft.h index 25bb720..dce1034 100644 --- a/kiss_fft.h +++ b/kiss_fft.h @@ -14,6 +14,21 @@ #include #include +// Define KISS_FFT_SHARED macro to properly export symbols +#ifdef KISS_FFT_SHARED +# ifdef _WIN32 +# ifdef KISS_FFT_BUILD +# define KISS_FFT_API __declspec(dllexport) +# else +# define KISS_FFT_API __declspec(dllimport) +# endif +# else +# define KISS_FFT_API __attribute__ ((visibility ("default"))) +# endif +#else +# define KISS_FFT_API +#endif + #ifdef __cplusplus extern "C" { #endif @@ -99,7 +114,7 @@ typedef struct kiss_fft_state* kiss_fft_cfg; * buffer size in *lenmem. * */ -kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); +kiss_fft_cfg KISS_FFT_API kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); /* * kiss_fft(cfg,in_out_buf) @@ -111,12 +126,12 @@ kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) * Note that each element is complex and can be accessed like f[k].r and f[k].i * */ -void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); +void KISS_FFT_API kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); /* A more generic version of the above function. It reads its input from every Nth sample. * */ -void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); +void KISS_FFT_API kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); /* If kiss_fft_alloc allocated a buffer, it is one contiguous buffer and can be simply free()d when no longer needed*/ @@ -126,13 +141,13 @@ void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up your compiler output to call this before you exit. */ -void kiss_fft_cleanup(void); +void KISS_FFT_API kiss_fft_cleanup(void); /* * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) */ -int kiss_fft_next_fast_size(int n); +int KISS_FFT_API kiss_fft_next_fast_size(int n); /* for real ffts, we need an even size */ #define kiss_fftr_next_fast_size_real(n) \ diff --git a/kissfft-config.cmake.in b/kissfft-config.cmake.in new file mode 100644 index 0000000..cd7139a --- /dev/null +++ b/kissfft-config.cmake.in @@ -0,0 +1,82 @@ +# kissfft-config.ccmake accept the following components: +# +# SHARED/STATIC: +# This components allows one to choose a shared/static kissfft library. +# The default is selected by BUILD_SHARED_LIBS. +# They are to be used exclusively. Using them together is an error. +# +# example: +# find_package(kissfft CONFIG REQUIRED COMPONENTS STATIC) +# +# simd/int16/int32/float/double: +# This components allows one to choose the datatype. +# When using this component, the target kissfft::kissfft becomes available. +# When not using this component, you will have to choose the correct kissfft target. +# +# example: +# find_package(kissfft CONFIG REQUIRED) +# # - kissfft::kissfft-float, kissfft::kissfft-int32_t/ ... are available (if they are installed) +# # - kissfft::kissfft is not available, +# +# find_package(kissfft CONFIG REQUIRED COMPONENTS int32_t) +# # - kissfft::kissfft-float, kissfft::kissfft-int32_t/ ... are available (if they are installed) +# # - kissfft::kissfft is available (as an alias for kissfft::kissfft-int32_t), + +@PACKAGE_INIT@ + +cmake_minimum_required(VERSION 3.3) + +# Set include glob of config files using SHARED/static component, BUILD_SHARED_LIBS by default +set(_kissfft_shared_detected OFF) +set(_kissfft_shared ${BUILD_SHARED_LIBS}) +if("SHARED" IN_LIST kissfft_FIND_COMPONENTS) + set(_kissfft_shared_detected ON) + set(_kissfft_shared ON) +endif() +if("STATIC" IN_LIST kissfft_FIND_COMPONENTS) + if(_kissfft_shared_detected) + message(FATAL_ERROR "SHARED and STATIC components cannot be used together") + endif() + set(_kissfft_shared_detected ON) + set(_kissfft_shared OFF) +endif() + +if(_kissfft_shared) + set(_kissfft_config_glob "kissfft-*-shared-targets.cmake") +else() + set(_kissfft_config_glob "kissfft-*-static-targets.cmake") +endif() + +# Load information for all configured kissfft +get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +file(GLOB CONFIG_FILES "${_DIR}/${_kissfft_config_glob}") +foreach(f ${CONFIG_FILES}) + include(${f}) +endforeach() + +# If a datatype component is passed, create kissfft::kissfft +set(_kissfft_datatype_detected) +foreach(_kissfft_datatype simd int16 int32 float double) + if(_kissfft_datatype IN_LIST kissfft_FIND_COMPONENTS) + if(_kissfft_datatype_detected) + message(FATAL_ERROR "Cannot define datatype COMPONENT twice: ${_kissfft_datatype_detected} and ${_kissfft_datatype}") + endif() + set(_kissfft_datatype_detected ${_kissfft_datatype}) + endif() +endforeach() + +if(_kissfft_datatype_detected) + if(NOT TARGET kissfft::kissfft-${_kissfft_datatype_detected}) + message(FATAL_ERROR "kissfft with datatype=${_kissfft_datatype_detected} is not installed") + endif() + if(TARGET kissfft::kissfft) + message(SEND_ERROR "kissfft::kissfft already exists. You cannot use 2 find_package's with datatype that are visible to eachother.") + else() + add_library(kissfft::kissfft INTERFACE IMPORTED) + set_property(TARGET kissfft::kissfft PROPERTY INTERFACE_LINK_LIBRARIES kissfft::kissfft-${_kissfft_datatype_detected}) + endif() +endif() + +set(kissfft_FOUND ON) +set(KISSFFT_VERSION @kissfft_VERSION@) + diff --git a/kissfft.pc.in b/kissfft.pc.in new file mode 100644 index 0000000..0de9c3a --- /dev/null +++ b/kissfft.pc.in @@ -0,0 +1,9 @@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: kissfft +Description: A Fast Fourier Transform (FFT) library that tries to Keep it Simple, Stupid +Version: @kissfft_VERSION@ + +Libs: -L${libdir} -l@KISSFFT_OUTPUT_NAME@ +Cflags: -I${includedir} @PKG_KISSFFT_DEFS@ \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..5655e01 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,49 @@ +add_library(kissfft_help_library OBJECT + pstats.c + ../tools/kfc.c + ../tools/kiss_fftnd.c + ../tools/kiss_fftr.c + ../tools/kiss_fftndr.c +) +target_link_libraries(kissfft_help_library PRIVATE kissfft::kissfft) +target_include_directories(kissfft_help_library PRIVATE ../tools) + +function(add_kissfft_test_executable NAME) + add_kissfft_executable(${NAME} ${ARGN} $) + target_include_directories(${NAME} PRIVATE ../tools) + add_test(NAME ${NAME} COMMAND ${NAME}) + set_tests_properties(${NAME} PROPERTIES TIMEOUT 10) +endfunction() + +set(KISSFFT_TEST_NUMFFTS 10000) + +add_kissfft_test_executable(bm_kiss benchkiss.c) +# add_test(NAME benchmar COMMAND ${NAME}) +# set_tests_properties(${NAME} PROPERTIES TIMEOUT 10) + +include(FindPkgConfig) +if(KISSFFT_FLOAT) + set(fftw3_pkg fftw3f) +else() + set(fftw3_pkg fftw3) +endif() +pkg_check_modules(fftw3 REQUIRED IMPORTED_TARGET ${fftw3_pkg}) +add_kissfft_test_executable(bm_fftw benchfftw.c) +target_link_libraries(bm_fftw PRIVATE PkgConfig::fftw3) + +add_kissfft_test_executable(st twotonetest.c) + +add_kissfft_test_executable(tkfc twotonetest.c) +target_compile_definitions(tkfc PRIVATE KFC_TEST) + +add_kissfft_test_executable(ffr twotonetest.c) +add_kissfft_test_executable(tr test_real.c) + +add_kissfft_test_executable(testcpp testcpp.cc) + +find_package(PythonInterp REQUIRED) +add_test(NAME testkiss.py COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/testkiss.py") +set_tests_properties(testkiss.py PROPERTIES + TIMEOUT 20 + ENVIRONMENT "DATATYPE=${KISSFFT_DATATYPE}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..2ed4aa2 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,18 @@ +add_kissfft_executable(fastconvr kiss_fastfir.c kiss_fftr.c) +target_compile_definitions(fastconvr PRIVATE REAL_FASTFIR FAST_FILT_UTIL) + +add_kissfft_executable(fastconv kiss_fastfir.c) +target_compile_definitions(fastconv PRIVATE FAST_FILT_UTIL) + +add_kissfft_executable(fft fftutil.c kiss_fftnd.c kiss_fftr.c kiss_fftndr.c) + +# psdpng does not build with "simd" datatype +if(NOT KISSFFT_DATATYPE MATCHES "simd") + include(FindPkgConfig) + pkg_check_modules(libpng REQUIRED IMPORTED_TARGET libpng) + add_kissfft_executable(psdpng psdpng.c kiss_fftr.c) + target_link_libraries(psdpng PRIVATE PkgConfig::libpng) +endif() + +#FIXME: dumphdr.c is not available +#add_kissfft_executable(dumphdr dumphdr.c)