diff --git a/.clang-tidy b/.clang-tidy index a342e25c..9b369447 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,31 +1,28 @@ --- +# cppcoreguidelines-avoid-magic-numbers are deactivated since it is an alias of readability-magic-numbers Checks: > - -*, - clang-diagnostic-*, - clang-analyzer-*, - cppcoreguidelines-*, - cert-*, - bugprone-*, - google-*, - misc-*, - modernize-*, - performance-*, - portability-*, - readability-*, + *, + -abseil-*, + -altera-*, + -android-*, + -fuchsia-*, + -linuxkernel-*, -google-readability-namespace-comments, -google-runtime-int, -google-runtime-references, + -llvmlibc-*, + -hicpp-no-array-decay, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-avoid-magic-numbers FormatStyle: 'file' HeaderFilterRegex: '**/*\.(hpp|h)' CheckOptions: - { key: bugprone-argument-comment.StrictMode, value: true } - { key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic, value: true } - - { key: readability-magic-numbers.IgnoredIntegerValues, value: 0;1;2;3;4;5;6;7;8;9;10 } - - { key: cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues, value: "0;1;2;3;4;5;6;7;8;9;10;100" } + - { key: readability-magic-numbers.IgnoredIntegerValues, value: 0;1;2;3;4;5;6;7;8;9;10;100;1000 } - { key: readability-magic-numbers.IgnorePowersOf2IntegerValues, value: true } - - { key: readability-magic-numbers.IgnoredFloatingPointValues, value: 0.0;0.33;0.5;0.75;1.0;2.0;10.0;25.0;20.0;50.0;100.0;360.0;20000.0 } - - { key: cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues, value: "0.0;0.33;0.5;0.75;1.0;2.0;10.0;25.0;20.0;50.0;100.0;360.0;20000.0" } - - { key: readability-identifier-naming.NamespaceCase, value: lower_case } + - { key: readability-magic-numbers.IgnoredFloatingPointValues, value: 0.0;0.33;0.5;0.75;1.0;2.0;10.0;25.0;20.0;50.0;100.0;360.0;1000.0,20000.0 } + - { key: readability-identifier-naming.NamespaceCase, value: CamelCase } - { key: readability-identifier-naming.ClassCase, value: CamelCase } - { key: readability-identifier-naming.StructCase, value: CamelCase } - { key: readability-identifier-naming.UnionCase, value: CamelCase } @@ -33,11 +30,11 @@ CheckOptions: - { key: readability-identifier-naming.FunctionCase, value: camelBack } - { key: readability-identifier-naming.MemberCase, value: camelBack } - { key: readability-identifier-naming.ParameterCase, value: camelBack } - - { key: readability-identifier-naming.VariableCase, value: camelBack } + - { key: readability-identifier-naming.VariableCase, value: camel_Snake_Case } - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } - { key: readability-identifier-naming.EnumConstantCase, value: Camel_Snake_Case } - - { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase } - - { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase } + - { key: readability-identifier-naming.ConstexprVariableCase, value: UPPER_CASE } + - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } - { key: readability-identifier-naming.MemberConstantCase, value: CamelCase } - { key: readability-identifier-naming.StaticConstantCase, value: CamelCase } ... diff --git a/.gitignore b/.gitignore index 3f73a836..81b2903f 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,6 @@ ObjectStore/ *.log *.jar *.iml -**/*.iml \ No newline at end of file +**/*.iml +performancelog.json +Testing/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 646e4be4..6467f8ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,45 +14,40 @@ cmake_minimum_required(VERSION 3.15) # `project()` command. `project()` sets up some helpful variables that describe source/binary # directories, and the current project version. This is a standard CMake command. -project(speclet VERSION 0.0.1 LANGUAGES CXX) +# Set message log level to VERBOSE to see more details about what CMake is doing. +#set(CMAKE_MESSAGE_LOG_LEVEL VERBOSE) -# Fix behavior of CMAKE_CXX_STANDARD when targeting macOS. -if (POLICY CMP0025) - cmake_policy(SET CMP0025 NEW) -endif () +# Name of the project and the plugin. Avoid spaces and special characters. +set(PROJECT_NAME "Speclet") +set(CURRENT_VERSION "1.0.0") -# Set C++ Language Standard to C++17 -set (CMAKE_CXX_STANDARD 17) +message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}") -# Generates a JSON file containing compile info like include paths, defines, etc. -# Reference: https://stackoverflow.com/questions/39455090/clang-tidy-cant-find-my-header-files -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# Enable diagnostic colors in CMake output -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - add_compile_options(-fdiagnostics-color=always) -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - add_compile_options(-fcolor-diagnostics) +set(PRODUCT_NAME_POSTFIX "") +string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPERCASE) +if (NOT "${CMAKE_BUILD_TYPE_UPPERCASE}" STREQUAL "RELEASE") + set(PRODUCT_NAME_POSTFIX "-${CMAKE_BUILD_TYPE}") + message(STATUS "PRODUCT_NAME_POSTFIX: ${PRODUCT_NAME_POSTFIX}") endif() -# Download "cpm", the setup-free CMake dependency management -# https://github.com/cpm-cmake/CPM.cmake -set(CPM_DOWNLOAD_VERSION 0.35.0) -set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") -if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) - message(STATUS "Downloading CPM.cmake") - file(DOWNLOAD https://github.com/TheLartians/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake ${CPM_DOWNLOAD_LOCATION}) -endif() -include(${CPM_DOWNLOAD_LOCATION}) +# For simplilicity, the name of the project is also the name of the targe. +project(${PROJECT_NAME} VERSION ${CURRENT_VERSION}) + +include(Environment.cmake) + +# Build Options +option(JUCE_BUILD_EXTRAS "Build JUCE Extras" OFF) +option(JUCE_BUILD_EXAMPLES "Build JUCE Examples" OFF) +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) # needed for fftw to not depend on dll # Import dependencies -CPMAddPackage("gh:juce-framework/JUCE#6.1.5") -#CPMAddPackage("https://math.ryerson.ca/~lkolasa/xxx.tar.gz") #Online not available anymore +#CPMAddPackage("https://math.ryerson.ca/~lkolasa/xxx.tar.gz") # Not online anymore (2022). Now directly included in this repository. +CPMAddPackage("gh:juce-framework/JUCE#6.1.6") CPMAddPackage("https://www.fftw.org/fftw-3.3.10.tar.gz#MD5=8ccbf6a5ea78a16dbc3e1306e234cc5c") CPMAddPackage("gh:dmoulding/vld#v1.9h") +CPMAddPackage("gh:tcbrindle/span#117fbada0f888e1535e3db20c7c9ddd86db129e2") +# CPMAddPackage("gh:Neargye/magic_enum#v0.7.3") # simplifies enum handling, not yet needed -# Import test dependencies -CPMAddPackage("gh:catchorg/Catch2@2.13.8") # If you've installed JUCE somehow (via a package manager, or directly using the CMake install # target), you'll need to tell this project that it depends on the installed copy of JUCE. If you've # included JUCE directly in your source tree (perhaps as a submodule), you'll need to tell CMake to @@ -73,23 +68,26 @@ CPMAddPackage("gh:catchorg/Catch2@2.13.8") # up by default. As well as this shared code static library, this function adds targets for each of # the formats specified by the FORMATS arguments. This function accepts many optional arguments. # Check the readme at `docs/CMake API.md` in the JUCE repo for the full list. - -juce_add_plugin(Speclet - # VERSION ... # Set this if the plugin version is different to the project version - # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone - # ICON_SMALL ... - # COMPANY_NAME ... # Specify the name of the plugin's author - # IS_SYNTH TRUE/FALSE # Is this a synth or an effect? - # NEEDS_MIDI_INPUT TRUE/FALSE # Does the plugin need midi input? - # NEEDS_MIDI_OUTPUT TRUE/FALSE # Does the plugin need midi output? - # IS_MIDI_EFFECT TRUE/FALSE # Is this plugin a MIDI effect? - # EDITOR_WANTS_KEYBOARD_FOCUS TRUE/FALSE # Does the editor need keyboard focus? - # COPY_PLUGIN_AFTER_BUILD TRUE/FALSE # Should the plugin be installed to a default location after building? - PLUGIN_MANUFACTURER_CODE JohT # A four-character manufacturer id with at least one upper-case character - PLUGIN_CODE JTSL # A unique four-character plugin id with exactly one upper-case character +juce_add_plugin("${PROJECT_NAME}" + PRODUCT_NAME "${PROJECT_NAME}${PRODUCT_NAME_POSTFIX}" # The name of the final executable, which can differ from the target name + COMPANY_NAME "Johannes Troppacher" # Specify the name of the plugin's author + DESCRIPTION "Audio Spectrum Analyzer using Fourier- and Wavelet-Transformation (v${CURRENT_VERSION})" # A short description of the plugin + # VERSION 0.9.0 # Set this if the plugin version is different to the project version + FORMATS VST3 AU Standalone # The formats to build. Other valid formats are: AAX Unity VST AU AUv3 + + PLUGIN_MANUFACTURER_CODE "JohT" # A four-character manufacturer id with at least one upper-case character + PLUGIN_CODE "SpcJ" # A unique four-character plugin id with exactly one upper-case character # GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case - FORMATS VST3 Standalone # The formats to build. Other valid formats are: AAX Unity VST AU AUv3 - PRODUCT_NAME "Speclet") # The name of the final executable, which can differ from the target name + # ICON_SMALL ... + ICON_BIG "${CMAKE_CURRENT_SOURCE_DIR}/image/SpecletBlueIcon.png" + IS_SYNTH FALSE # Is this a synth or an effect? + NEEDS_MIDI_INPUT FALSE # Does the plugin need midi input? + NEEDS_MIDI_OUTPUT FALSE # Does the plugin need midi output? + IS_MIDI_EFFECT FALSE # Is this plugin a MIDI effect? + EDITOR_WANTS_KEYBOARD_FOCUS FALSE # Does the editor need keyboard focus? + # COPY_PLUGIN_AFTER_BUILD TRUE # Should the plugin be installed to a default location after building? + VST3_CATEGORIES "Analyzer" # VST3 Plugin Category +) # `juce_generate_juce_header` will create a JuceHeader.h for a given target, which will be generated # into your build tree. This should be included with `#include `. The include path for @@ -97,17 +95,52 @@ juce_add_plugin(Speclet # include all your JUCE module headers; if you're happy to include module headers directly, you # probably don't need to call this. -juce_generate_juce_header(Speclet) +juce_generate_juce_header("${PROJECT_NAME}") + +# Compile Project with C++20 Standard +target_compile_features("${PROJECT_NAME}" PRIVATE cxx_std_20) +message(VERBOSE "C++ Language Standard set to C++20 for Target ${PROJECT_NAME}") + +# Get all source files in the src directory and store them in the project_sources variable. +# GLOB_RECURSE is not recommended but used here for simplicity: https://cmake.org/cmake/help/latest/command/file.html?highlight=CONFIGURE_DEPENDS#filesystem +FILE(GLOB_RECURSE project_sources CONFIGURE_DEPENDS "src/*.cpp") +message(VERBOSE "Sources in ${CMAKE_CURRENT_SOURCE_DIR}/src:") +foreach(source ${project_sources}) + message(VERBOSE "'${source}'") +endforeach() # `target_sources` adds source files to a target. We pass the target that needs the sources as the # first argument, then a visibility parameter for the sources which should normally be PRIVATE. # Finally, we supply a list of source files that will be built into the target. This is a standard # CMake command. +target_sources("${PROJECT_NAME}" PRIVATE ${project_sources}) + +# Import fftw3 api headers to include with #include "fftw3.h" +if (fftw_ADDED) + include_directories("${PROJECT_NAME}" PRIVATE "${fftw_SOURCE_DIR}/api") + message(VERBOSE "FFTW api (headers) directory included for all targets: ${fftw_SOURCE_DIR}/api") + # Get all include directories for the target. + get_target_property(PROJECT_INCLUDES "${PROJECT_NAME}" INCLUDE_DIRECTORIES) + foreach(dir ${PROJECT_INCLUDES}) + message(VERBOSE "Project_include_dir='${dir}'") + endforeach() +endif() -target_sources(Speclet - PRIVATE - src/PluginEditor.cpp - src/PluginProcessor.cpp) +# Import span header to include with #include "tcb/span.hpp" +if (span_ADDED) + include_directories("${PROJECT_NAME}" PRIVATE "${span_SOURCE_DIR}/include") + message(VERBOSE "tcb span headers directory included for all targets: ${span_SOURCE_DIR}/include") +endif() + +# Import magic_enum header to include with #include "magic_enum.hpp" +#if (magic_enum_ADDED) +# include_directories("${PROJECT_NAME}" PRIVATE "${magic_enum_SOURCE_DIR}/include") +# message(VERBOSE "magic_enum headers directory included for all targets: ${magic_enum_SOURCE_DIR}/include") +#endif() + +# Add libraries to the target that needed to be included inside the repository: +add_subdirectory(lib/wave++) +include_directories("${PROJECT_NAME}" PRIVATE "lib/wave++/source") # `target_compile_definitions` adds some preprocessor definitions to our target. In a Projucer # project, these might be passed in the 'Preprocessor Definitions' field. JUCE modules also make use @@ -115,13 +148,38 @@ target_sources(Speclet # need that's not on by default, check the module header for the correct flag to set here. These # definitions will be visible both to your code, and also the JUCE module code, so for new # definitions, pick unique names that are unlikely to collide! This is a standard CMake command. - -target_compile_definitions(Speclet +target_compile_definitions("${PROJECT_NAME}" PUBLIC # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them. + JUCE_DISPLAY_SPLASH_SCREEN=0 # Plugins with GPL3 License can disable splash screen + JUCE_REPORT_APP_USAGE=0 JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call - JUCE_VST3_CAN_REPLACE_VST2=0) + JUCE_VST3_CAN_REPLACE_VST2=0 + $<$:LOG_PERFORMANCE> # Create performancelog.json in Debug build for google chrome tracing + _USE_MATH_DEFINES +) + +# Sets compile options for the target: +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Using GNU or Clang compiler + set(GNUCLANG_COMPILE_OPTIONS "-m64;-fPIC") + set(GNUCLANG_COMPILE_DEBUG_OPTIONS "${GNUCLANG_COMPILE_OPTIONS};-g;-O0") + set(GNUCLANG_COMPILE_RELEASE_OPTIONS "${GNUCLANG_COMPILE_OPTIONS};-O3") + target_compile_options("${PROJECT_NAME}" PUBLIC "$<$:${GNUCLANG_COMPILE_DEBUG_OPTIONS}>") + target_compile_options("${PROJECT_NAME}" PUBLIC "$<$:${GNUCLANG_COMPILE_RELEASE_OPTIONS}>") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Using Visual Studio C++ + set(MSVC_COMPILE_OPTIONS "/MP;/Gy;/nologo;/EHsc") + set(MSVC_COMPILE_DEBUG_OPTIONS "${MSVC_COMPILE_OPTIONS};/Od") + set(MSVC_COMPILE_RELEASE_OPTIONS "${MSVC_COMPILE_OPTIONS};/O2") + target_compile_options("${PROJECT_NAME}" PUBLIC "$<$:${MSVC_COMPILE_DEBUG_OPTIONS}>") + target_compile_options("${PROJECT_NAME}" PUBLIC "$<$:${MSVC_COMPILE_RELEASE_OPTIONS}>") +endif() + +# Displays the chosen target's compile options: +get_target_property(MAIN_TARGET_COMPILE_OPTIONS "${PROJECT_NAME}" COMPILE_OPTIONS) +message(STATUS "Compiler definitions added to target ${PROJECT_NAME} for compiler ${CMAKE_CXX_COMPILER_ID} and build type ${CMAKE_BUILD_TYPE}: ${MAIN_TARGET_COMPILE_OPTIONS}") # If your target needs extra binary assets, you can add them here. The first argument is the name of # a new static library target that will include all the binary resources. There is an optional @@ -138,12 +196,19 @@ target_compile_definitions(Speclet # linked automatically. If we'd generated a binary data target above, we would need to link to it # here too. This is a standard CMake command. -target_link_libraries(Speclet +target_link_libraries("${PROJECT_NAME}" PRIVATE # AudioPluginData # If we'd created a binary data target, we'd link to it here + juce::juce_core juce::juce_dsp juce::juce_audio_utils + juce::juce_gui_basics + fftw3 + wave++ PUBLIC juce::juce_recommended_config_flags juce::juce_recommended_lto_flags - juce::juce_recommended_warning_flags) \ No newline at end of file + juce::juce_recommended_warning_flags) + +# Add tests +add_subdirectory(test) \ No newline at end of file diff --git a/COMMANDS.md b/COMMANDS.md index 7a60c414..05c0ebf7 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -26,4 +26,64 @@ cmake -Bbuild -DJUCE_BUILD_EXTRAS=ON -DJUCE_BUILD_EXAMPLES=ON -DCMAKE_OSX_ARCHIT ```shell cmake --build build +``` + +## Build AudioPluginHost to test the plugin: + +```shell +cmake.exe --build build --config Debug --target AudioPluginHost +``` + +## Run Unit-Tests + +From this directory start the unit test with the following command. + +```shell +ctest --test-dir build/test +``` + +[CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) + + +## Build for x86_64 on Mac with M1 (not working yet) + + + +### Build x86 on Mac M1 (not working) + +These are the references and commands used to build x86_64 on a mac m1 (arm). After trying these and several other things it didn't work. + +- [Run x86 Terminal Apps (Like Homebrew) on Your New M1 Mac](https://medium.com/swlh/run-x86-terminal-apps-like-homebrew-on-your-new-m1-mac-73bdc9b0f343) +- [StackTrace - x86 installation of brew](https://stackoverflow.com/questions/67386941/using-x86-libraries-and-openmp-on-macos-arm64-architecture#67418208) +- [StackTrace - proper way to build for macOS-x86_64 using cmake on Apple M1](https://stackoverflow.com/questions/69803659/what-is-the-proper-way-to-build-for-macos-x86-64-using-cmake-on-apple-m1-arm) + +```shell +# launch x86_64 shell +arch -x86_64 zsh +# install x86_64 variant of brew +arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" +# install x86_64 variant of clang +arch -x86_64 /usr/local/bin/brew install llvm +# compile using x86_64 variant of clang +/usr/local/opt/llvm/bin/clang++ -arch x86_64 omp_ex.cpp +``` + +```shell +# arm64 (default) location +/opt/homebrew/bin/brew +# x86_64 location +/usr/local/bin/brew +``` + +```shell +# Apple arm64 (default) location +/usr/bin/clang +# brew arm64 location +/opt/homebrew/opt/llvm/bin/clang +# brew x86_64 location +/usr/local/opt/llvm/bin/clang +``` + +```shell +arch -x86_64 /usr/local/opt/cmake/bin/cmake --no-warn-unused-cli -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_BUILD_TYPE:STRING=Debug -DJUCE_BUILD_EXTRAS=OFF -DJUCE_BUILD_EXAMPLES=OFF -DCMAKE_C_COMPILER:FILEPATH=/usr/local/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER:FILEPATH=/usr/local/opt/llvm/bin/clang++ -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY -S/Users/johnny/Repositories/git/speclet -B/Users/johnny/Repositories/git/speclet/build -G Ninja ``` \ No newline at end of file diff --git a/Environment.cmake b/Environment.cmake new file mode 100644 index 00000000..7c45c9bc --- /dev/null +++ b/Environment.cmake @@ -0,0 +1,41 @@ +message(STATUS "Environment setup started") + +# Fix behavior of CMAKE_CXX_STANDARD when targeting macOS. +if (POLICY CMP0025) + cmake_policy(SET CMP0025 NEW) +endif () + +# Set C++ Language Standard to C++20 +set(CMAKE_CXX_STANDARD 20) + +# Generates a JSON file containing compile info like include paths, defines, etc. for clangd (IDE support e.g. VSCode) +# Reference: https://stackoverflow.com/questions/39455090/clang-tidy-cant-find-my-header-files +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Download "cpm", the setup-free CMake dependency management +# https://github.com/cpm-cmake/CPM.cmake +set(CPM_DOWNLOAD_VERSION 0.35.0) +set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) + message(STATUS "Downloading CPM.cmake") + file(DOWNLOAD https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake ${CPM_DOWNLOAD_LOCATION}) +endif() +include(${CPM_DOWNLOAD_LOCATION}) + +#Minimum MacOS target, set globally +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version" FORCE) + +option(UniversalBinary "Build universal binary for mac" OFF) + +if (UniversalBinary) + set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "") + message(STATUS "Building universal binary for mac") +endif() + +# Set runtime library for windows when using Microsoft Visual Studio (Build Tools) +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + +message(VERBOSE "CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}") +message(VERBOSE "CMAKE_FIND_LIBRARY_SUFFIXES: ${CMAKE_FIND_LIBRARY_SUFFIXES}") + +message(STATUS "Environment setup finished") \ No newline at end of file diff --git a/JuceAudioPluginHost.filtergraph b/JuceAudioPluginHost.filtergraph index 1459750d..96876d4b 100644 --- a/JuceAudioPluginHost.filtergraph +++ b/JuceAudioPluginHost.filtergraph @@ -1,7 +1,7 @@ - + - + - 642.hAGaoMGcv.C1AHv.DTfAGfPBJr.Gc3wGgvUag4VclE1XzUmbkIGUjEFcg8EDarVPUAkbkMWYzAUYxMWZyQWYtQ2TzEFcksTY4c0b0IFc4AWYWYWYxMWZu4FUzkGbk8EDOXVZrUVKxUlYkIWYtMVYyQkag0VYRDFbvwFPgxvzM3vCPDwEXYTZrUlSg0VYWIUYmk1atMmWIM2TkwVYiQWYjYTZrU1WPP4KUMWYxM2Kp8FZt4VduPzaiUWak4Fcy8RS0MWZq8xQk4VYxEFco8lafLDagMGZuL0XnwVXmoWY0cVKBUVXxIVYoQWctcFHg4FZg4FYfTVZtU1bfDTcyM2Xn4VZzQ2bubTYtUlbgQWZu41PrE1bnQjb001bEQVZz0hQ041ZPElbz0BNsrTZislLtQVQwMzasAmKsA2LgJA0SPQEVbAFWngWRU1Yo8laD8VYywzauAmVRU1Yo8laDEFcg8EDPjzbSUFakMFckQlTkcVZu4lVRU1Yo8laNEVakkvSP.G..........PRlw0V.....XjYbsE.......bY8......B.........vjYbsE.....NZFWaA....D....P...........................................................P5Y.......InkSkcGHRU1Yo8laIHQXlAGaP.fDgU2YtEMHPXkQowVYfDCVU4FcoQGakQF.H.PF.XB.q.PR.DE.YAfW..G.0Afd.rG.8A.g.zH.UB.oArS.8DfQATU.fEvbA3W.+Ef7ALe.9Gv+BPf.FHvBB3f.U........HP..........HB..................HfG + 589.hAGaoMGcv.C1AHv.DTfAGfPBJr.Gc3wGgvUag4VclE1XzUmbkIGUjEFcg8EDarVPUAkbkMWYzAUYxMWZyQWYtQ2TzEFcksTY4c0b0IFc4AWYWYWYxMWZu4FUzkGbk8EDOXVZrUVKxUlYkIWYtMVYyQkag0VYRDFbvwFPgxvzM3vCPDQFXYTZrUlSg0VYWIUYmk1atMmWIM2TkwVYiQWYjYTZrU1WP70KUMWYxM2Kp8FZt4VduPzaiUWak4Fcy8RS0MWZq8xTuYFc2Elbk8hUugWYtc1auX0a3Ulam8FHTUzSTUDHTU1bz8hUugWYtc1aTUzSTUTKOYlYsPjb00Fau8FbtzFbyDpDTOAEUXwEXjQFZIUYmk1at4TXsUlVRU1Yo8laDEFcg8EDPjzbSUFakMFckQlTkcVZu4lWRU1Yo8laD8VYywzauAmVNU1cfHUYmk1at8DDvA..............BC................D....P..............fVepWyRdB1UvghURUbI1juA....fYSCZI.......................................9f.C...........3q....3yATBIjfDgYFbrAA.RDVcm4Vzf.gUFkFakARLXUkazkFcrUFY.f..Y.fI.rB.IAPT.jE.dA.b.TG.5Avd.zG.DBPi.TI.jFfAAfP.QD.GAbR.5DPRAPU.GG.xAjb.JGvyADc.VGP1A.N.......f.A.........fH..................P.oC @@ -75,13 +75,13 @@ - - - 890.VMjLgD2....O+fWarAhckI2bo8la8HRLt.iHfTlai8FYo41Y8HRUTYTK3HxO9.BOVMEUy.Ea0cVZtMEcgQWY9vSRC8Vav8lak4Fc9TCNy3BTEwlXmAiUYoWUrIVdA4hKAImKTETRUAUSAAkKBolQY4BQPQTQDoVXmcmUjUSUrIlYToVXmkjQgsVPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hK2rhKtPUPIUETMEDTtHjZFkkKD4BQEYFUZ01Yw.ELQcDRBs1QhcVSxHlKXcEVxU0UY4BQPIDQt3hKt3hKt3hKt3hKtTETRUDUS4BQl4xaQYjKAwTTAgzZwjkaMQ0X5EDZQgWUVIFLUwVXos1QtDSQFEFLUYjKAolKA4hKt3hKt3BZSMDQt.UQpQUPvPjKAgDTZoVPP4xST4hTuMlQZMTUGMlYLUTX0EzUY4BVWgkbUcUVtPDTBQjKt3hKt3hKt3hKt3hKUAkTEQ0TtPjYt7VTF4RP2.UPLgSLiMTUGMlYHQEY1UTLhkWPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hKt3hKtPUPIUETMEDTtHjZFkkKDYFQEYmdgISSTMldAgVT3UkUhASUrEVZqcjKwTjQgASUF4RPp4RPt3hKt3hKt3hdtPjKPUjZTEDLD4RPHAkVpEDTt3DUtLUciICTvD0QHM0cwDlcUYjKwTjQgASUF4RPp4RPt3hKt3hKt3hKt3hKPUjZTEDLD4RPHAkVpEDTtzDUtP0ZEEiVlgDUjYWQwHVdAY1XmcmUisVPP4RRP4hKt3hKt3hKt3hKt3BUAkTUP0TPP4hPpYTVtPjKDUjKUk0YyYDRFkzUYcWUWkEcMYEYtf0UXIWUWkkKDAkPD4hKt3hKt3hcigDTt3RUPIUQTMkKDYlKuEkQtDjbPEDTUYEVwEDdQc1ZrElKXcEVxU0UY4BQPIDQt3hKt3hKt3hKt3hKtTETRUDUS4BQl4xaQYjKAMCTAAUUVgUbAIEUvTjQg8VTWQlKXcEVxU0UY4BQPIDQt3hKt3hKt3hKq7jKt3hKt3hKt3hKt3hRUACTEEzZh8VVWgkdUYTTmE0UX4BQP4hPqcjXm0jLh4BQP4xPt.0Qt3hKt3hKt3hKtQUUCUEQTg2ZrM1YQcUVDUjQicVP77RRC8Vav8lak4Fc9vyKVMEUy.Ea0cVZtMEcgQWY9.. + + + 855.VMjLg3z....O+fWarAhckI2bo8la8HRLt.iHfTlai8FYo41Y8HRUTYTK3HxO9.BOVMEUy.Ea0cVZtMEcgQWY9vSRC8Vav8lak4Fc9TSM23BTEwlXmAiUYoWUrIVdA4hKAImKTETRUAUSAAkKBolQY4BQ1ITQLESXxgCahMGNFk0ZAY1XmcmUisVPP4RRP4hKt3hKt3hKtHjKA4BUAkTUP0TPP4hPpYTVtPjcBUjXVkEcUwlXmEkLggWPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hKt3hKtPUPIUETMEDTtHjZFkkKD4RQEIlUYQWUrI1YQISX3kEahsVQWM1ZzDCVzDjYic1cVM1ZAAkKIAkKt3hKt3hKt.kUtDjKTETRUAUSAAkKBolQY4BQlMTQ1ESXskEahsVQWM1ZzDCVzDjYic1cVM1ZAAkKIAkKt3hKt3hKt3xMq3hKTETRUAUSAAkKBolQY4BQlMTQ1ESXsAiUX0FMVokdUcTVqEjYic1cVM1ZAAkKIAkKt3hKt3hKt3xMq3hKTETRUAUSAAkKBolQY4BQtLTQHcUV4giQgASTWoUczXjKwTjQgASUF4RPp4RPt3hKt3hKt3BRtPjKPUjZTEDLD4RPHAkVpEDTtjDUlIVcUczXuQSLY4BVWgkbUcUVtPDTBQjKt3hKt3hKt3RPP4hKUAkTEQ0TtPjYt7VTF4RPxAUP5kzUXQWSskUcIcUXtf0UXIWUWkkKDAkPD4hKt3hKt3hKtrxSt3RUPIUQTMkKDYlKuEkQtDjZPEjLEw1XqcmUYoWPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hKL4RPtPUPIUETMEDTtHjZFkkKD4RQEI1UXESUFE1ZQcjXm0TLZsVTsg0YMckV4EjYic1cVM1ZAAkKIAkKt3hKt3hKt3hKtDjKTETRUAUSAAkKBolQY4BQ1ITQhckVzEULgIyZrEVaAY1XmcmUisVPP4RRP4hKt3hKt3hKt3hKA4hKt3hKt3hKt3hKtQUUCUEQTg2ZrM1YQcUVDUjQicVPP4RPHQEY1UTLhkWPP4RPL4hKi4hKt3hKt3hKtXlTU0DUQAURWoULEYzXqEEUXoWQFwyKIMzasA2atUlaz4COuX0TTMCTrU2Yo41TzEFck4C. @@ -91,8 +91,6 @@ - - - - + + diff --git a/JuceAudioPluginHostRelWithDebInfo.filtergraph b/JuceAudioPluginHostRelWithDebInfo.filtergraph new file mode 100644 index 00000000..d316ad70 --- /dev/null +++ b/JuceAudioPluginHostRelWithDebInfo.filtergraph @@ -0,0 +1,96 @@ + + + + + + 0. + + + + + + + + + + + + 0. + + + + + + + + + + + + 0. + + + + + + + + + + + + 0. + + + + + + + + + + + + 589.hAGaoMGcv.C1AHv.DTfAGfPBJr.Gc3wGgvUag4VclE1XzUmbkIGUjEFcg8EDarVPUAkbkMWYzAUYxMWZyQWYtQ2TzEFcksTY4c0b0IFc4AWYWYWYxMWZu4FUzkGbk8EDOXVZrUVKxUlYkIWYtMVYyQkag0VYRDFbvwFPgxvzM3vCPDQFXYTZrUlSg0VYWIUYmk1atMmWIM2TkwVYiQWYjYTZrU1WP70KUMWYxM2Kp8FZt4VduPzaiUWak4Fcy8RS0MWZq8xTuYFc2Elbk8hUugWYtc1auX0a3Ulam8FHTUzSTUDHTU1bz8hUugWYtc1aTUzSTUTKOYlYsPjb00Fau8FbtzFbyDpDTOAEUXwEXjQFZIUYmk1at4TXsUlVRU1Yo8laDEFcg8EDPjzbSUFakMFckQlTkcVZu4lWRU1Yo8laD8VYywzauAmVNU1cfHUYmk1at8DDvA..............BC................D....P..............fVepWyRdB1UvghURUbI1juA....fYSCZI.......................................9f.C...........3q....3yATBIjfDgYFbrAA.RDVcm4Vzf.gUFkFakARLXUkazkFcrUFY.f..Y.fI.rB.IAPT.jE.dA.b.TG.5Avd.zG.DBPi.TI.jFfAAfP.QD.GAbR.5DPRAPU.GG.xAjb.JGvyADc.VGP1A.N.......f.A.........fH..................P.oC + + + + + + + + + + 855.VMjLg3z....O+fWarAhckI2bo8la8HRLt.iHfTlai8FYo41Y8HRUTYTK3HxO9.BOVMEUy.Ea0cVZtMEcgQWY9vSRC8Vav8lak4Fc9TSM23BTEwlXmAiUYoWUrIVdA4hKAImKTETRUAUSAAkKBolQY4BQ1ITQLESXxgCahMGNFk0ZAY1XmcmUisVPP4RRP4hKt3hKt3hKtHjKA4BUAkTUP0TPP4hPpYTVtPjcBUjXVkEcUwlXmEkLggWPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hKt3hKtPUPIUETMEDTtHjZFkkKD4RQEIlUYQWUrI1YQISX3kEahsVQWM1ZzDCVzDjYic1cVM1ZAAkKIAkKt3hKt3hKt.kUtDjKTETRUAUSAAkKBolQY4BQlMTQ1ESXskEahsVQWM1ZzDCVzDjYic1cVM1ZAAkKIAkKt3hKt3hKt3xMq3hKTETRUAUSAAkKBolQY4BQlMTQ1ESXsAiUX0FMVokdUcTVqEjYic1cVM1ZAAkKIAkKt3hKt3hKt3xMq3hKTETRUAUSAAkKBolQY4BQtLTQHcUV4giQgASTWoUczXjKwTjQgASUF4RPp4RPt3hKt3hKt3BRtPjKPUjZTEDLD4RPHAkVpEDTtjDUlIVcUczXuQSLY4BVWgkbUcUVtPDTBQjKt3hKt3hKt3hKt3hKUAkTEQ0TtPjYt7VTF4RPxAUP5kzUXQWSskUcIcUXtf0UXIWUWkkKDAkPD4hKt3hKt3hKt3hKt3RUPIUQTMkKDYlKuEkQtDjZPEjLEw1XqcmUYoWPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hKL4RPtPUPIUETMEDTtHjZFkkKD4RQEI1UXESUFE1ZQcjXm0TLZsVTsg0YMckV4EjYic1cVM1ZAAkKIAkKt3hKt3hKt3hKtDjKTETRUAUSAAkKBolQY4BQ1ITQhckVzEULgIyZrEVaAY1XmcmUisVPP4RRP4hKt3hKt3hKt3hKA4hKt3hKt3hKt3hKtQUUCUEQTg2ZrM1YQcUVDUjQicVPP4RPHQEY1UTLhkWPP4RPL4hKi4hKt3hKt3hKtXlTU0DUQAURWoULEYzXqEEUXoWQFwyKIMzasA2atUlaz4COuX0TTMCTrU2Yo41TzEFck4C. + + + + + + + + + + + + diff --git a/JuceAudioPluginHostRelease.filtergraph b/JuceAudioPluginHostRelease.filtergraph new file mode 100644 index 00000000..13791e53 --- /dev/null +++ b/JuceAudioPluginHostRelease.filtergraph @@ -0,0 +1,96 @@ + + + + + + 0. + + + + + + + + + + + + 0. + + + + + + + + + + + + 0. + + + + + + + + + + + + 0. + + + + + + + + + + + + 589.hAGaoMGcv.C1AHv.DTfAGfPBJr.Gc3wGgvUag4VclE1XzUmbkIGUjEFcg8EDarVPUAkbkMWYzAUYxMWZyQWYtQ2TzEFcksTY4c0b0IFc4AWYWYWYxMWZu4FUzkGbk8EDOXVZrUVKxUlYkIWYtMVYyQkag0VYRDFbvwFPgxvzM3vCPDQFXYTZrUlSg0VYWIUYmk1atMmWIM2TkwVYiQWYjYTZrU1WP70KUMWYxM2Kp8FZt4VduPzaiUWak4Fcy8RS0MWZq8xTuYFc2Elbk8hUugWYtc1auX0a3Ulam8FHTUzSTUDHTU1bz8hUugWYtc1aTUzSTUTKOYlYsPjb00Fau8FbtzFbyDpDTOAEUXwEXjQFZIUYmk1at4TXsUlVRU1Yo8laDEFcg8EDPjzbSUFakMFckQlTkcVZu4lWRU1Yo8laD8VYywzauAmVNU1cfHUYmk1at8DDvA..............BC................D....P..............fVepWyRdB1UvghURUbI1juA....fYSCZI.......................................9f.C...........3q....3yATBIjfDgYFbrAA.RDVcm4Vzf.gUFkFakARLXUkazkFcrUFY.f..Y.fI.rB.IAPT.jE.dA.b.TG.5Avd.zG.DBPi.TI.jFfAAfP.QD.GAbR.5DPRAPU.GG.xAjb.JGvyADc.VGP1A.N.......f.A.........fH..................P.oC + + + + + + + + + + 855.VMjLg3z....O+fWarAhckI2bo8la8HRLt.iHfTlai8FYo41Y8HRUTYTK3HxO9.BOVMEUy.Ea0cVZtMEcgQWY9vSRC8Vav8lak4Fc9TSM23BTEwlXmAiUYoWUrIVdA4hKAImKTETRUAUSAAkKBolQY4BQ1ITQLESXxgCahMGNFk0ZAY1XmcmUisVPP4RRP4hKt3hKt3hKtHjKA4BUAkTUP0TPP4hPpYTVtPjcBUjXVkEcUwlXmEkLggWPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hKt3hKtPUPIUETMEDTtHjZFkkKD4RQEIlUYQWUrI1YQISX3kEahsVQWM1ZzDCVzDjYic1cVM1ZAAkKIAkKt3hKt3hKtvjVtDjKTETRUAUSAAkKBolQY4BQlMTQ1ESXskEahsVQWM1ZzDCVzDjYic1cVM1ZAAkKIAkKt3hKt3hKt3xMq3hKTETRUAUSAAkKBolQY4BQlMTQ1ESXsAiUX0FMVokdUcTVqEjYic1cVM1ZAAkKIAkKt3hKt3hKt3xMq3hKTETRUAUSAAkKBolQY4BQtLTQHcUV4giQgASTWoUczXjKwTjQgASUF4RPp4RPt3hKt3hKt3BTtPjKPUjZTEDLD4RPHAkVpEDTtjDUlIVcUczXuQSLY4BVWgkbUcUVtPDTBQjKt3hKt3hKt3RPP4hKUAkTEQ0TtPjYt7VTF4RPxAUP5kzUXQWSskUcIcUXtf0UXIWUWkkKDAkPD4hKt3hKt3hKt3hKt3RUPIUQTMkKDYlKuEkQtDjZPEjLEw1XqcmUYoWPlM1Y2Y0XqEDTtjDTt3hKt3hKt3hKL4RPtPUPIUETMEDTtHjZFkkKD4RQEI1UXESUFE1ZQcjXm0TLZsVTsg0YMckV4EjYic1cVM1ZAAkKIAkKt3hKt3hKt3BQtDjKTETRUAUSAAkKBolQY4BQ1ITQhckVzEULgIyZrEVaAY1XmcmUisVPP4RRP4hKt3hKt3hKt3hKA4hKt3hKt3hKt3hKtQUUCUEQTg2ZrM1YQcUVDUjQicVPP4RPHQEY1UTLhkWPP4RPL4hKi4hKt3hKt3hKtXlTU0DUQAURWoULEYzXqEEUXoWQFwyKIMzasA2atUlaz4COuX0TTMCTrU2Yo41TzEFck4C. + + + + + + + + + + + + diff --git a/README.md b/README.md index e30383cb..d5bc66fd 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,103 @@ # Speclet -(c)2011 by Johannes Troppacher (Austria) -For graduation at HS Mittweida - university of applied science (Germany) +VST Audio Spectrum Analyzer Plugin using Fourier- and Wavelet-Transformation. -## This is not working yet +- (c)2011 by Johannes Troppacher (Austria) +- For graduation at [Hochschule Mittweida - University of Applied Science](https://www.hs-mittweida.de/en) (Germany) +- Paper (german): [Echtzeitspektralanalyse auf Basis der Fourier- und Wavelet-Transformation implementiert als VST-Plugin](https://monami.hs-mittweida.de/frontdoor/deliver/index/docId/3216/file/J.Troppacher_2011_Diplomarbeit.pdf) +- License: [GNU GENERAL PUBLIC LICENSE v3](./LICENSE) +- For prototyping and experimentation without programming visit [JoWa Wavelet Library in Native Instruments Reaktor User Library](https://www.native-instruments.com/en/reaktor-community/reaktor-user-library/entry/show/11541) + +## User Interface + + + +## User Guide +Have a look at the [User Guide](./USERGUIDE.md) for further screenshots and to get started using the analyzer. + +## Build the Project + +### Prerequisites +- [Installing GIT](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [Installing CMake](https://cmake.org/install/) +- [Download Ninja](https://github.com/ninja-build/ninja/releases) +- (Windows only) [Install Visual Studio Build Tools](https://visualstudio.microsoft.com/de/downloads/?q=build+tools) with Visual Studio Installer, select Desktop Development for C++ and add all optional "Clang" components +- (Optional) [Download Visual Studio Code](https://code.visualstudio.com/download) + +### Clone +Download the project as [zip](https://github.com/JohT/speclet/archive/refs/heads/main.zip) +or clone it using GIT: +```shell +git clone https://github.com/JohT/speclet.git +``` + +### Command Line + +See [COMMANDS.md](./COMMANDS.md) if you prefer to use the command line. + +### Visual Studio Code (MacOS) +- [Using Clang in Visual Studio Code](https://code.visualstudio.com/docs/cpp/config-clang-mac) +- Install recommended extensions specified in [speclet.code-workspace](./speclet.code-workspace) +- Select the kit that fits to your machine (`STRG+SHIFT+P`, Type "CMAKE kit") + +### Visual Studio Code (Windows) +- Install [Visual Studio Build Tools](https://visualstudio.microsoft.com/de/downloads/?q=build+tools) with Visual Studio Installer, select Desktop Development for C++ and add all optional Clang Features +- Install recommended extensions specified in [speclet.code-workspace](./speclet.code-workspace) +- Select the kit that fits your machine (e.g. amd64) (`STRG+SHIFT+P`, Type "CMAKE kit"). +Successfully tested with "Visual Studio Build Tools 2022 Release - amd64". + +## Testing + +### Run Unit Tests within your command line interface (CLI): +```shell +ctest --test-dir build/test +``` + +### Run Unit Tests in Visual Studio Code +Build the project and use extension "matepek.vscode-catch2-test-adapter" to show all the Catch2 CTest cases +under "Testing" (left "activity bar", below extensions button). + +### Build AudioPluginHost for exploratory testing +```shell +cmake.exe --build build --config Debug --target AudioPluginHost +``` + +## Used Tools + +Modernized 2022 using the following tools: + +- [The LLVM Compiler Infrastructure](https://github.com/llvm/llvm-project) +- [Ninja](https://github.com/ninja-build/ninja) +- [CMake](https://gitlab.kitware.com/cmake/cmake) +- [Setup-free CMake dependency management](https://github.com/cpm-cmake/CPM.cmake) +- [Plugin Validator](https://github.com/Tracktion/pluginval) This version is not ready yet. It contains the code from back then, that doesn't compile any more. It also contains the plugin made with the tutorial [Learn Modern C++ by Building an Audio Plugin (w/ JUCE Framework) - Full Course YouTube](https://www.youtube.com/watch?v=i_Iq4_Kd7Rc&list=PLi4rQ_T_X31Gd4pyUbvPltTVSyw8v_yYT&index=5&t=1051s) as a template to start the modernization. -### Used Libraries -This plugin was made using the following frameworks and libraries: +## Used Libraries + +This plugin was made in 2011 (modernized 2022) using the following frameworks and libraries: + +- VST SDK 2.4 rev2 (2011) +by Steinberg +http://www.steinberg.net/en/company/developer.html +visit the link above for licensing -- VST SDK 2.4 rev2 +- VST 3 Audio Plug-Ins SDK (2022) by Steinberg http://www.steinberg.net/en/company/developer.html -visit the link above for licensing issues and license agreement +visit the link above for licensing - JUCE -by Raw Material Software -http://www.rawmaterialsoftware.com/juce -GPL (Gnu Public License) +by Raw Material Software (2011) +http://www.rawmaterialsoftware.com/juce (2011) +License: https://github.com/juce-framework/JUCE/blob/master/LICENSE.md (2022) - FFTW -by MIT (Matteo Frigo and Steven G. Johnson) +by MIT (Matteo Frigo and Steven G. Johnson) http://www.fftw.org GNU General Public License @@ -43,4 +116,65 @@ allowed by permission of the copyright holders. - Visual Leak Detector (VLD) (c) 2005-2011 Dan Moulding, Arkadiy Shapkin, Laurent Lessieux https://github.com/dmoulding/vld -GNU Lesser General Public License v2.1 \ No newline at end of file +GNU Lesser General Public License v2.1 + +- Implementation of C++20's std::span for older compilers +(c) 2019 by Tristan Brindle +https://github.com/tcbrindle/span +Boost Software License 1.0 + +## Trademarks + +- Steinberg is a registered trademarks of Steinberg Media Technologies GmbH. + +## References + +### Digital Signal Processing +- [⭐ Window Functions and Their Applications in Signal Processing (PDF)](https://library.oapen.org/handle/20.500.12657/41686) +- [Understanding FFT Windows (PDF, 2013)](https://www.egr.msu.edu/classes/me451/me451_labs/Fall_2013/Understanding_FFT_Windows.pdf) +- [Comparison of Matching Pursuit Algorithm with Other Signal Processing Techniques for Computation of the Time-Frequency Power Spectrum of Brain Signals](https://www.jneurosci.org/content/36/12/3399) +- [Fourier uncertainty principle](https://www.johndcook.com/blog/2021/03/17/fourier-uncertainty-principle) +- [JoWa Wavelet Library in Native Instruments Reaktor User Library](https://www.native-instruments.com/en/reaktor-community/reaktor-user-library/entry/show/11541) + +### Templates and Examples +- [⭐ Pamplejuce](https://github.com/sudara/pamplejuce) +- [Cross-platform CI for JUCE audio plugins with Github Actions](https://github.com/maxwellpollack/juce-plugin-ci) +- [JUCE CMake Repo Prototype](https://github.com/eyalamirmusic/JUCECmakeRepoPrototype/blob/master/CMakeLists.txt) +- [Example using CPM package manager with JUCE](https://github.com/robbert-vdh/diopser/blob/master/CMakeLists.txt) +- [CLAP CLever Audio Plugin](https://github.com/free-audio/clap) +- [Integration of Catch, CMake and CMake CodeCoverage](https://github.com/fkromer/catch_cmake_coverage) +- [The Stochas Sequencer](https://github.com/surge-synthesizer/stochas) + +### Performance Tracing/Logging (Profiling, Instrumentation) +- [Google Chrome Browser Trace Log Viewer](chrome://tracing/) +- [The Cherno - VISUAL BENCHMARKING in C++ (YouTube)](https://www.youtube.com/watch?v=xlAH4dbMVnU&t=197s) +- [⭐ Instrumentor.h (Code Snippet)](https://gist.github.com/TheCherno/31f135eea6ee729ab5f26a6908eb3a5e) +- [Instrumentor.h David Churchill Mod (Code Snippet)](https://pastebin.com/qw5Neq4U) +- [Instrumentor.h Mattias Aronsson Mod](https://gist.github.com/maronsson/073840bf94e4d6df94c5f294a6e96e03) + +### Testing +- [⭐ CMake's CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) +- [⭐ Catch2 CMake Integration](https://github.com/catchorg/Catch2/blob/devel/docs/cmake-integration.md) +- [⭐ Plugin Validator](https://github.com/Tracktion/pluginval) +- [Catch2 Data Generators](https://github.com/catchorg/Catch2/blob/devel/docs/generators.md) +- [Coverage for tests with cmake and catch2](https://stackoverflow.com/questions/65603144/how-to-get-coverage-for-tests-with-cmake-and-catch2) +- [Findcodecov.cmake](https://github.com/catchorg/Catch2/blob/devel/CMake/Findcodecov.cmake) + +### Windows Build +- [Visual Studio Build Tools Download](https://visualstudio.microsoft.com/de/downloads/?q=build+tools) + +### Installation +- [VST plug-in locations on Windows](https://helpcenter.steinberg.de/hc/en-us/articles/115000177084-VST-plug-in-locations-on-Windows) +- [VST plug-in locations on Mac OS X and macOS](https://helpcenter.steinberg.de/hc/en-us/articles/115000171310-VST-plug-in-locations-on-Mac-OS-X-and-macOS) +- [Where are third-party Audio Units plug-ins installed on Mac?](https://support.apple.com/en-al/HT201532) + +### Other +- [⭐ std::span implementation for C++11 and later](https://github.com/tcbrindle/span) +- [C++ Singleton](https://stackoverflow.com/questions/1008019/c-singleton-design-pattern) +- [10 Cmake Tips & Tricks](https://medium.com/codex/10-cmake-tips-tricks-7f00d407923d) +- [Reference to non-static member function must be called](https://stackoverflow.com/questions/26331628/reference-to-non-static-member-function-must-be-called) +- [Pointer-to-Member Function](http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm) +- [Commit Message Guidelines](https://gist.github.com/robertpainsi/b632364184e70900af4ab688decf6f53) +- [Undefined symbol _kIOMainPortDefault](https://forum.juce.com/t/juce-6-1-3-undefined-symbol-kiomainportdefault/49335/9) +- [CheckTypeSize (FFTW failed for multiple architectures)](https://cmake.org/cmake/help/latest/module/CheckTypeSize.html) +- [Article CMake Ninja Combo: The Gist](https://www.incredibuild.com/blog/cmake-ninja-combo-the-gist) diff --git a/USERGUIDE.md b/USERGUIDE.md new file mode 100644 index 00000000..dbecfab3 --- /dev/null +++ b/USERGUIDE.md @@ -0,0 +1,73 @@ +# Speclet User Guide + +**Speclet** is a VST Audio Spectrum Analyzer Plugin using Fourier- and Wavelet-Transformation. It was written in 2011 for graduation at [Hochschule Mittweida - University of Applied Science](https://www.hs-mittweida.de/en) in Germany. + +**Speclet** shows the difference between FFT and Wavelet spectral transformation, their variations and parameters. It might not keep up with other analyzers currently available regarding features, but might help to get an overview of the fourier- and wavelet-transform and how to implement an analyzer with it. + +> **Speclet** is for the curious folks getting familiar with spectrum transformation and audio plugin development. + +## Installation + +### VST3 on Windows +Copy the `.vst3` file into `C:\Program Files\Common Files\VST3` as described in +[VST plug-in locations on Windows](https://helpcenter.steinberg.de/hc/en-us/articles/115000177084-VST-plug-in-locations-on-Windows). + +### VST3 on Mac +Copy the `.vst3` file into `/Library/Audio/Plug-ins/VST3` (for all users) or into the user directory `~/Library/Audio/Plug-Ins/VST3` as described in [VST plug-in locations on Mac OS X and macOS](https://helpcenter.steinberg.de/hc/en-us/articles/115000171310-VST-plug-in-locations-on-Mac-OS-X-and-macOS). + +### Audio Unit Plugins on Mac +Copy the audio unit `.component` file into `/Library/Audio/Plug-Ins/Components` (for all users) or into the user directory `~/Library/Audio/Plug-Ins/Components` as described in [Where are third-party Audio Units plug-ins installed on Mac?](https://support.apple.com/en-al/HT201532). + +## Transformation Methods + +All transformation methods are implemented so that the audio samples are processed in blocks. The block size is a power of two (e.g. 212=4096) for efficient calculation. + +The selected window function "rounds the edges" at the beginning and end of each audio block to avoid hard cuts (rectangular window) that would affect the displayed spectrum ("smearing"). + +### Fast Fourier Transform (FFT) +The (Short-Time) Fast Fourier Transform (FFT) is probably the most used, widely known and most efficient implemented transform. + +A higher block size leads to a better frequency resolution at the cost of a lower time resolution, since it is not possible to tell where the frequency occurred within the block of samples. It is not possible to use a higher frequency resolution for low frequencies and a higher time resolution for high frequencies. The frequency lines are spread evenly/linear (every x Hz) across the spectrum. + +
+ +### Fast Wavelet Transform (FWT) +The (dyadic) Fast Wavelet Transform (FWT) uses filters to split the audio spectrum in two halves. Every half is then down-sampled ("fully decimated") by removing ever second sample. The process is repeated for the lower half. + +This leads to a higher frequency resolution for low frequencies (multiple filtered) and a higher time resolution for high frequencies. The logarithmic approach better suit the way audio is perceived. For example is the difference between a 100Hz and a 200Hz tone more apparent than the same difference 10kHz higher between 10100Hz and 10200Hz. The opposite (frequency wise) applies to time resolution. A higher time resolution at higher frequencies is especially advantageous for percussive signals that start with a short high frequency part ("transient") that highly affect how they are perceived. + +The wavelet functions are implemented as high pass FIR filters that extract the "detail" of the signal. The scaling function is derived from the wavelet function and is implemented as the matching low pass FIR filter that extract the "scale" of the signal. Higher order filters with more coefficients lead to a higher latency and need more processing power. However, they reduce aliasing effects and lead to "sharper" results. + +The fully decimated, down-sampled wavelet transform makes calculation more efficient. +The drawback is that this leads to aliasing that effects the displayed spectrum. Additionally, the lack of translation-invariance that comes with the fully decimated wavelet transform leads to a deviation in the spectrum for time shifts. An offset of a couple of samples at the input varies the spectrum even when the signal content is the same. + + + +### Fast Wavelet Packet Transform (WPT) +The Fast Wavelet Packet Transform does - in comparison to the [FWT](#Fast-Wavelet-Transform-FWT) - also splits up the result of the high pass filter. +This leads to a higher frequency resolution and lower time resolution very similar to the [FFT](#Fast-Fourier-Transform-FFT). The additional filters raise calculation demands and latency. + +#### About swapped frequency bands +When the output of a high pass filter is decimated (taking every second sample to divide the sampling rate) and then further split into two frequency band, the resulting bands need to be swapped. + +Since the spectrum is folded at the Nyquist frequency, a down sampled signal, that mostly contains frequencies above the nyquist frequency (half the sampling rate) will be "folded back" so that the low frequencies appear on the higher half of the frequency band and vice versa. This is why the results of the Fast Wavelet Packet Transform need to be sorted/swapped before they can be displayed. + + + +### Fast Wavelet Packet Best-Basis Transform +Fast Wavelet Packet Best-Basis Transform is a special form of the [WPT](#Fast-Wavelet-Packet-Transform-WPT) that fully utilizes the possible to decide which frequency band should be split further (more frequency details) or not (more time details). The filters are structured in a binary tree shape. Every "leaf" can be divided further on demand. The dyadic [FWT](#Fast-Wavelet-Transform-FWT) is special case of that. + +A cost function is used to decide for each band, if it is worth "the cost" to get further divided. When the spectrum doesn't change much over time, the band will be most likely divided further to get a higher frequency resolution. When the spectral content varies significantly over a short period of time, the band will most likely not be divided to preserve the higher time resolution. + +This transformation is able to adapt its frequency- and time resolution dynamically and optimized for the processed signal and shows the capabilities and flexibility of the wavelet transform. This can be best seen at discontinuities of the input signal. + + + +## Frequency- and Time-Resolution +The following figure shows how the frequency- and time-resolution looks like for different transformations: +
+Reference: +[Comparison of Matching Pursuit Algorithm with Other Signal Processing Techniques for Computation of the Time-Frequency Power Spectrum of Brain Signals](https://www.jneurosci.org/content/36/12/3399) + +A higher frequency resolution will lead to a lower possible time resolution and vice versa. +For more details look for the "Fourier uncertainty principle", e.g. [this article by John D. Cook](https://www.johndcook.com/blog/2021/03/17/fourier-uncertainty-principle). \ No newline at end of file diff --git a/doc/image/Speclet-FastFourierTransform-Windowing.png b/doc/image/Speclet-FastFourierTransform-Windowing.png new file mode 100644 index 00000000..cf410446 Binary files /dev/null and b/doc/image/Speclet-FastFourierTransform-Windowing.png differ diff --git a/doc/image/Speclet-FastWaveletTransform.png b/doc/image/Speclet-FastWaveletTransform.png new file mode 100644 index 00000000..2be15094 Binary files /dev/null and b/doc/image/Speclet-FastWaveletTransform.png differ diff --git a/doc/image/Speclet-WaveletPacketBestBasisTransform.png b/doc/image/Speclet-WaveletPacketBestBasisTransform.png new file mode 100644 index 00000000..beed115c Binary files /dev/null and b/doc/image/Speclet-WaveletPacketBestBasisTransform.png differ diff --git a/doc/image/Speclet-WaveletPacketTransform.png b/doc/image/Speclet-WaveletPacketTransform.png new file mode 100644 index 00000000..70ca6ac7 Binary files /dev/null and b/doc/image/Speclet-WaveletPacketTransform.png differ diff --git a/doc/image/WaveletResolutionPlane-www.jneurosci.org-content-jneuro-36-12-3399.jpg b/doc/image/WaveletResolutionPlane-www.jneurosci.org-content-jneuro-36-12-3399.jpg new file mode 100644 index 00000000..75c94da4 Binary files /dev/null and b/doc/image/WaveletResolutionPlane-www.jneurosci.org-content-jneuro-36-12-3399.jpg differ diff --git a/image/SpecletBlueIcon.png b/image/SpecletBlueIcon.png new file mode 100644 index 00000000..498e2256 Binary files /dev/null and b/image/SpecletBlueIcon.png differ diff --git a/lib/wave++/CMakeLists.txt b/lib/wave++/CMakeLists.txt new file mode 100644 index 00000000..5d10b9f7 --- /dev/null +++ b/lib/wave++/CMakeLists.txt @@ -0,0 +1,51 @@ +# This file was added later to the library +# and is not part of the original library. +# The same license as the original library is applied to this file. +cmake_minimum_required(VERSION 3.15) +set(wavelet_library_target_name "wave++") +project("${wavelet_library_target_name}") + +# add headers to the library +# GLOB_RECURSE is not recommended but used here for simplicity: https://cmake.org/cmake/help/latest/command/file.html?highlight=CONFIGURE_DEPENDS#filesystem +FILE(GLOB_RECURSE wavelet_library_headers CONFIGURE_DEPENDS "source/*.h") + +message(VERBOSE "${wavelet_library_target_name} library headers added:") +foreach(header ${wavelet_library_headers}) + message(VERBOSE "'${header}'") +endforeach() + +# add sources (and headers) to the library +# GLOB_RECURSE is not recommended but used here for simplicity: https://cmake.org/cmake/help/latest/command/file.html?highlight=CONFIGURE_DEPENDS#filesystem +FILE(GLOB_RECURSE library_sources CONFIGURE_DEPENDS "source/*.cc") + +add_library("${wavelet_library_target_name}" STATIC ${library_sources} ${wavelet_library_headers}) + +message(VERBOSE "${wavelet_library_target_name} library sources added:") +foreach(source ${library_sources}) + message(VERBOSE "'${source}'") +endforeach() + +# Sets compile options for the target +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Using GNU or Clang compiler + set(GNUCLANG_COMPILE_OPTIONS "-m64;-fPIC") + set(GNUCLANG_COMPILE_DEBUG_OPTIONS "${GNUCLANG_COMPILE_OPTIONS};-g;-O0") + set(GNUCLANG_COMPILE_RELEASE_OPTIONS "${GNUCLANG_COMPILE_OPTIONS};-O3") + target_compile_options("${wavelet_library_target_name}" PRIVATE "$<$:${GNUCLANG_COMPILE_DEBUG_OPTIONS}>") + target_compile_options("${wavelet_library_target_name}" PRIVATE "$<$:${GNUCLANG_COMPILE_RELEASE_OPTIONS}>") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Using Visual Studio C++ + set(MSVC_COMPILE_OPTIONS "/MP;/Gy;/nologo;/EHsc") + set(MSVC_COMPILE_DEBUG_OPTIONS "${MSVC_COMPILE_OPTIONS};/Od") + set(MSVC_COMPILE_RELEASE_OPTIONS "${MSVC_COMPILE_OPTIONS};/O2") + target_compile_options("${wavelet_library_target_name}" PRIVATE "$<$:${MSVC_COMPILE_DEBUG_OPTIONS}>") + target_compile_options("${wavelet_library_target_name}" PRIVATE "$<$:${MSVC_COMPILE_RELEASE_OPTIONS}>") +endif() + +# Displays the chosen target's compile options +get_target_property(WAVEPP_TARGET_COMPILE_OPTIONS "${wavelet_library_target_name}" COMPILE_OPTIONS) +message(STATUS "Compiler definitions added to target ${wavelet_library_target_name} for compiler ${CMAKE_CXX_COMPILER_ID} and build type ${CMAKE_BUILD_TYPE}: ${WAVEPP_TARGET_COMPILE_OPTIONS}") + +target_compile_definitions("${wavelet_library_target_name}" PUBLIC _USE_MATH_DEFINES) +install(TARGETS "${wavelet_library_target_name}" DESTINATION lib) +install(FILES ${wavelet_library_headers} DESTINATION include) \ No newline at end of file diff --git a/lib/wave++/README.txt b/lib/wave++/README.txt new file mode 100644 index 00000000..a2e2e503 --- /dev/null +++ b/lib/wave++/README.txt @@ -0,0 +1,54 @@ +//README file for release of Wave++ Library. January 2,000. + +Welcome to Release 1.0 of Wave++ !!! + +1) After having executed the commands for unpacking + +gunzip nameOfFile.tar.gz + +tar xvf nameOfFile.tar + +The directory wave++ should then have been created. This directory contains +the following subdirectories: + +1a) source +1b) demos +1c) documentation + +To get a quick idea if Wave++ may be useful for you we suggest +to read the short PostScript file "documentation/wave++.ps". + +Each directory contains a README file that you should read +for instructions and information. To compile and run the +code in "demos" you need first to create the library. This is +done following the instructions in the "source" directory. + +2) The execution of the Makefile in "source" will create a +library archive in that same directory. Path dependencies +in the directory "demos" point to the "source" location +of the library archive. That is to say, our distribution +creates files locally with respect to the wave++ directory. +The user can move the relevant library file and header file to more +standard directories if he/she wishes. + + +3) The directory "documentation" contains PostScript files +explaining features of the library and some background to understand +the demos. + + + +All program code and documentation in this directory is +copyright (c) 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic + +Permission is granted for anyone to copy, use, or modify these +programs and documents for purposes of research or education, +provided this copyright notice is retained, and note is made of +any changes that have been made. Use for commercial applications is only +allowed by permission of the copyright holders. + +These programs and documents are distributed without any warranty, +express or implied. As the programs were written for research +purposes only, they have not been tested to the degree that would +be advisable in any important application. All use of these +programs is entirely at the user's own risk. diff --git a/lib/wave++/demos/Makefile b/lib/wave++/demos/Makefile new file mode 100644 index 00000000..dbef6d5a --- /dev/null +++ b/lib/wave++/demos/Makefile @@ -0,0 +1,54 @@ +#Makefile for directory demos +CFLAGS =# -g #debugging disabled + +OBJDEMOWAV = demoWav.o input_maker.o rms_error.o demoTools.o +OBJDEMOWAVPACK = demoWavPack.o input_maker.o rms_error.o demoTools.o +OBJDEMOMP = demoMP.o input_maker.o +LINKS = -L./ranlib -L../source -lranlib -lm -lw + + +demo: demoWav demoWavPack demoMP + + +demoWav: $(OBJDEMOWAV) + g++ $(CFLAGS) -o demoWav $(OBJDEMOWAV) $(LINKS) + +demoWav.o: demoWav.cc + g++ $(CFLAGS) -c demoWav.cc + +demoWavPack: $(OBJDEMOWAVPACK) + g++ $(CFLAGS) -o demoWavPack $(OBJDEMOWAVPACK) $(LINKS) + +demoWavPack.o: demoWavPack.cc + g++ $(CFLAGS) -c demoWavPack.cc + + +demoTools.o: demoTools.cc demoTools.h + g++ $(CFLAGS) -c demoTools.cc + + +demoMP: $(OBJDEMOMP) + g++ $(CFLAGS) -o demoMP $(OBJDEMOMP) $(LINKS) + + +demoMP.o: demoMP.cc + g++ $(CFLAGS) -c demoMP.cc + +input_maker.o: ./input_maker.cc ./input_maker.h + g++ $(CFLAGS) -c input_maker.cc + +rms_error.o: ./rms_error.h ./rms_error.cc + g++ $(CFLAGS) -c rms_error.cc + +clean: + -rm demoWav demoWavPack demoMP *.o *~ + + + + + + + + + + diff --git a/lib/wave++/demos/README.txt b/lib/wave++/demos/README.txt new file mode 100644 index 00000000..71cb1963 --- /dev/null +++ b/lib/wave++/demos/README.txt @@ -0,0 +1,47 @@ +//README file for directory "wave++/demos" + +To create the demos type + +make + +this will create the executables for the following demos + + +1) demoWavPack + +The mathematical background for the algorithm implemented in demoWavPack.cc +is in the file "documentation/demoDenoising.ps". Essentially, +a thresholding is applied to the noisy wavelet coefficients +with respect to the best wavelet basis. This is done for many +dictionaries of wavelet packets. See the comments in demoWav.cc +for more details. The program will pause (stop();) execution +and will wait for the user to type "enter" +every time after a new best basis is found and displayed on the screen +(reconstruction errors are also displayed). + +2) demoWav + +Same as above except we use only the Wavelet transform instead of the +full Wavelet Packet Transform. This demo is a good example of how to +use the Wavelet Transform and its inverse. + +3) demoMP + +The mathematical background and the basics of our implementation of +the matching pursuit (MP) algorithm can be found in +"documentation/mpTechReport.ps". Running demoMP will run both versions +of our implementation of MP and display errors for the reconstruction. +It is easy to see that different errors are to be expected from the two +implementations. The excutable demoMP takes (command-line) arguments +and outputs a file, this features should be self explanatory after +inspecting the file demoMP.cc. + + + + + +NOTE: The inclusion of the library "ranlib" is only needed +to generate random numbers with Gaussian distribution. + + + diff --git a/lib/wave++/demos/demoMP.cc b/lib/wave++/demos/demoMP.cc new file mode 100644 index 00000000..f25a742a --- /dev/null +++ b/lib/wave++/demos/demoMP.cc @@ -0,0 +1,89 @@ +//file: demoMP.cc +//it will run both implementations of MP up +// a max_iter number of components or quiting +//by epsilon. The functionality of the code +//below should be clear once the MP is understood. +#include "../source/libw.h" + +#include +#include "input_maker.h" +#include + +int main( int argc, char* argv[]) +{ + if( argc != 2 ) + { + std::cout << "Must give parameter m to main. E.g. type: main 7\n" + << "Alternatively, if you want results to go to a file with \n " + << "a different suffix, say .temp, then type : main 7.temp \n" + << "In this case results will go to file results_for_m=7.temp" + << std::endl; + exit(1); + } + + int m = atoi( argv[1] ); + std::cout << "m = " << m << std::endl; + int dim = 1< G, G1; + std::vector Gcoef, Gcoef1; + Interval f_approx(0, dim-1), f_approx1(0, dim-1); + RealGabor Gtemp, Gtemp1; + Partition Part(dim, 2.0); + + error = RunShiftGaborMP( max_iter, epsilon, CleanSignal, Part, + RecSignal, Rf, G, Gcoef); + std::cout << "error for Shift, a = 2 : " << error << std::endl; + + error1 = RunFFTGaborMP(max_iter, epsilon, CleanSignal, + RecSignal1,Rf1, G1, Gcoef1); + std::cout << "error for FFT" << " : " << error1 << std::endl; + + outfile << "********* INPUT_MAKER " << inp << " *********" << std::endl; + outfile << "number of iterations performed " << G.size() + << " error = " << error << std::endl; + for(int z=0; z < G.size(); z++) + { + Gtemp = G[z]; + coef = Gcoef[z]; + Gtemp1 = G1[z]; + coef1 = Gcoef1[z]; + outfile << "iteration " << z << std::endl; + outfile << "\t\tcoeficient: "<< coef < +//last parameter not used, needed only for compatibility and keep +//the demo brief +real_number OracCost( const real_number *data, const integer_number &n, + const real_number &sigma,const integer_number &k) +{ + real_number cost=0; + real_number var=sigma*sigma; + real_number temp; + + for(int i=0;i=var*Factor*Factor) cost += var; + else cost += temp; + + + } + return cost; +} + +void stop(){ + while(getchar()==0) { + getchar();} +} + + + + + + + + + + + diff --git a/lib/wave++/demos/demoTools.h b/lib/wave++/demos/demoTools.h new file mode 100644 index 00000000..fb817395 --- /dev/null +++ b/lib/wave++/demos/demoTools.h @@ -0,0 +1,32 @@ +//file: demoTools.h +#ifndef TESTTOOLS_H +#define TESTTOOLS_H + + +#include "../source/libw.h" + +//defined in demoWavPack.cc +extern real_number Factor; + +real_number OracCost( const real_number *data, const integer_number &n, + const real_number &sigma,const integer_number &k); +void stop(); +#endif + + + + + + + + + + + + + + + + + + diff --git a/lib/wave++/demos/demoWav.cc b/lib/wave++/demos/demoWav.cc new file mode 100644 index 00000000..17e8fa1a --- /dev/null +++ b/lib/wave++/demos/demoWav.cc @@ -0,0 +1,94 @@ +//file: demoWav.cc +//Shows some of the functionality of the Wavelet Library, namely: +//we denoise a noisy signal using the nonlinear thresholding algorithm +//of Donoho and Johnstone (D&J). This is done in the context +//of a single wavelet basis (contrast this with what is +//done in demoWavPack.cc) +//math. background for algo. is in /documentation/demoDenoising.ps +//Remark: The function stop() waits for keyboard "enter" +//to resume execution +#include "demoTools.h" +#include "./ranlib/ranlib.h" +#include "../source/libw.h" +#include "input_maker.h" +#include "rms_error.h" + + +int MAXLEVEL= 10; +int DIM = (1< variance*Factor*Factor) Delta[i]=1; + } + for(i=0; i variance*Factor*Factor) Delta[i]=1; + } + for(i=0; i 0) norm = (1.0)/sqrt(norm); + for(int i=In.beg; i<=In.end; i++) In.origin[i] *= norm; + + return; +} + +//////////////////////////////////////////////////////// +int signum(real_number x) +{ + int j; + j = (x>0 ? 1:(x<0 ? -1:0)); + return j; +} +////////////////////////////////////////////////////////// +real_number funct(real_number x) +{ + return 1.0/(1.0 + x*x*x*x); +} +////////////////////////////////////////////////////////// + diff --git a/lib/wave++/demos/input_maker.h b/lib/wave++/demos/input_maker.h new file mode 100644 index 00000000..ee932507 --- /dev/null +++ b/lib/wave++/demos/input_maker.h @@ -0,0 +1,40 @@ +// input_maker.h + +#include "../source/libw.h" + +#ifndef INPUTMAKER_H +#define INPUTMAKER_H + +void Input_maker(int type, Interval &In); // function for making 6 different + // clean signals + // type is an integer between 1 and 6 + // on input In is predefined all 0 interval so that + // In.length = desired length of the signal + // on output In is filled up with a signal + +int signum(real_number x); // utility + +const real_number Pi = 3.141592654; + +real_number funct(real_number x); // utility + +#endif + + + + + + + + + + + + + + + + + + + diff --git a/lib/ranlib/com.cpp b/lib/wave++/demos/ranlib/com.c similarity index 96% rename from lib/ranlib/com.cpp rename to lib/wave++/demos/ranlib/com.c index dabfe21d..04924f68 100644 --- a/lib/ranlib/com.cpp +++ b/lib/wave++/demos/ranlib/com.c @@ -50,15 +50,15 @@ void getsd(long *iseed1,long *iseed2) ********************************************************************** void getsd(long *iseed1,long *iseed2) GET SeeD - Returns the value of two integer seeds of the current generator + Returns the value of two integer_number seeds of the current generator This is a transcription from Pascal to Fortran of routine Get_State from the paper L'Ecuyer, P. and Cote, S. "Implementing a Random Number Package with Splitting Facilities." ACM Transactions on Mathematical Software, 17:98-111 (1991) Arguments - iseed1 <- First integer seed of generator G - iseed2 <- Second integer seed of generator G + iseed1 <- First integer_number seed of generator G + iseed2 <- Second integer_number seed of generator G ********************************************************************** */ { @@ -87,7 +87,7 @@ long ignlgi(void) ********************************************************************** long ignlgi(void) GeNerate LarGe Integer - Returns a random integer following a uniform distribution over + Returns a random integer_number following a uniform distribution over (1, 2147483562) using the current generator. This is a transcription from Pascal to Fortran of routine Random from the paper @@ -252,8 +252,8 @@ void setall(long iseed1,long iseed2) with Splitting Facilities." ACM Transactions on Mathematical Software, 17:98-111 (1991) Arguments - iseed1 -> First of two integer seeds - iseed2 -> Second of two integer seeds + iseed1 -> First of two integer_number seeds + iseed2 -> Second of two integer_number seeds ********************************************************************** */ { @@ -343,8 +343,8 @@ void setsd(long iseed1,long iseed2) with Splitting Facilities." ACM Transactions on Mathematical Software, 17:98-111 (1991) Arguments - iseed1 -> First integer seed - iseed2 -> Second integer seed + iseed1 -> First integer_number seed + iseed2 -> Second integer_number seed ********************************************************************** */ { diff --git a/lib/ranlib/linpack.cpp b/lib/wave++/demos/ranlib/linpack.c similarity index 100% rename from lib/ranlib/linpack.cpp rename to lib/wave++/demos/ranlib/linpack.c diff --git a/lib/wave++/demos/ranlib/makefile b/lib/wave++/demos/ranlib/makefile new file mode 100644 index 00000000..e0f08364 --- /dev/null +++ b/lib/wave++/demos/ranlib/makefile @@ -0,0 +1,21 @@ +#makefile to create archive of ranlib code + +CC= g++ +CFLAGS= -O2 -Wall +CLIBS= -lm -liostream + +all: ranlib + +ranlib: + $(CC) $(CFLAGS) -c com.c + $(CC) $(CFLAGS) -c linpack.c + $(CC) $(CFLAGS) -c ranlib.c + ar rcs libranlib.a \ + com.o \ + linpack.o \ + ranlib.o + +clean: + rm -f *.o *~ + +# End \ No newline at end of file diff --git a/lib/ranlib/ranlib.cpp b/lib/wave++/demos/ranlib/ranlib.c similarity index 98% rename from lib/ranlib/ranlib.cpp rename to lib/wave++/demos/ranlib/ranlib.c index d456d258..5dae7387 100644 --- a/lib/ranlib/ranlib.cpp +++ b/lib/wave++/demos/ranlib/ranlib.c @@ -3,8 +3,8 @@ #include #include #define ABS(x) ((x) >= 0 ? (x) : -(x)) -#define ranlib_min(a,b) ((a) <= (b) ? (a) : (b)) -#define ranlib_max(a,b) ((a) >= (b) ? (a) : (b)) +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define max(a,b) ((a) >= (b) ? (a) : (b)) void ftnstop(char*); float genbet(float aa,float bb) /* @@ -45,14 +45,14 @@ static long qsame; olda = aa; oldb = bb; S20: - if(!(ranlib_min(aa,bb) > 1.0)) goto S100; + if(!(min(aa,bb) > 1.0)) goto S100; /* Alborithm BB Initialize */ if(qsame) goto S30; - a = ranlib_min(aa,bb); - b = ranlib_max(aa,bb); + a = min(aa,bb); + b = max(aa,bb); alpha = a+b; beta = sqrt((alpha-2.0)/(2.0*a*b-alpha)); gamma = a+1.0/beta; @@ -70,7 +70,7 @@ static long qsame; S50: w = a*exp(v); S60: - z = pow((double)u1,2.0)*u2; + z = pow(u1,2.0)*u2; r = gamma*v-1.3862944; s = a+r-w; /* @@ -103,8 +103,8 @@ static long qsame; Initialize */ if(qsame) goto S110; - a = ranlib_max(aa,bb); - b = ranlib_min(aa,bb); + a = max(aa,bb); + b = min(aa,bb); alpha = a+b; beta = 1.0/b; delta = 1.0+a-b; @@ -129,7 +129,7 @@ static long qsame; /* Step 3 */ - z = pow((double)u1,2.0)*u2; + z = pow(u1,2.0)*u2; if(!(z <= 0.25)) goto S160; v = beta*log(u1/(1.0-u1)); if(!(v > expmax)) goto S140; @@ -442,7 +442,7 @@ static float gennch; fprintf(stderr,"Value of DF: %16.6E Value of XNONC%16.6E\n",df,xnonc); exit(1); S10: - gennch = genchi(df-1.0)+pow((double)gennor(sqrt(xnonc),1.0),2.0); + gennch = genchi(df-1.0)+pow(gennor(sqrt(xnonc),1.0),2.0); return gennch; } float gennf(float dfn,float dfd,float xnonc) @@ -551,10 +551,10 @@ float genunf(float low,float high) float genunf(float low,float high) GeNerate Uniform Real between LOW and HIGH Function - Generates a real uniformly distributed between LOW and HIGH. + Generates a real_number uniformly distributed between LOW and HIGH. Arguments low --> Low bound (exclusive) on real value to be generated - high --> High bound (exclusive) on real value to be generated + high --> High bound (exclusive) on real_number value to be generated ********************************************************************** */ { @@ -747,7 +747,7 @@ static float al,alv,amaxp,c,f,f1,f2,ffm,fm,g,p,p1,p2,p3,p4,q,qn,r,u,v,w,w2,x,x1, *****SETUP, PERFORM ONLY WHEN PARAMETERS CHANGE */ psave = pp; - p = ranlib_min(psave,1.0-psave); + p = min(psave,1.0-psave); q = 1.0-p; S20: xnp = n*p; @@ -864,7 +864,7 @@ static float al,alv,amaxp,c,f,f1,f2,ffm,fm,g,p,p1,p2,p3,p4,q,qn,r,u,v,w,w2,x,x1, /* INVERSE CDF LOGIC FOR MEAN LESS THAN 30 */ - qn = pow((double)q,(double)n); + qn = pow(q,(double)n); r = p/q; g = r*(n+1); S150: @@ -1094,7 +1094,7 @@ static float b1,b2,c,c0,c1,c2,c3,d,del,difmuk,e,fk,fx,fy,g,omega,p,p0,px,py,q,s, */ if(ignpoi >= 10) goto S80; px = -mu; - py = pow((double)mu,(double)ignpoi)/ *(fact+ignpoi); + py = pow(mu,(double)ignpoi)/ *(fact+ignpoi); goto S110; S80: /* @@ -1126,7 +1126,7 @@ static float b1,b2,c,c0,c1,c2,c3,d,del,difmuk,e,fk,fx,fy,g,omega,p,p0,px,py,q,s, muprev = 0.0; if(mu == muold) goto S130; muold = mu; - m = ranlib_max(1L,(long) (mu)); + m = max(1L,(long) (mu)); l = 0; p = exp(-mu); q = p0 = p; @@ -1144,7 +1144,7 @@ static float b1,b2,c,c0,c1,c2,c3,d,del,difmuk,e,fk,fx,fy,g,omega,p,p0,px,py,q,s, */ if(l == 0) goto S150; j = 1; - if(u > 0.458) j = ranlib_min(l,m); + if(u > 0.458) j = min(l,m); for(k=j; k<=l; k++) { if(u <= *(pp+k-1)) goto S180; } @@ -1175,10 +1175,10 @@ long ignuin(long low,long high) long ignuin(long low,long high) GeNerate Uniform INteger Function - Generates an integer uniformly distributed between LOW and HIGH. + Generates an integer_number uniformly distributed between LOW and HIGH. Arguments low --> Low bound (inclusive) on integer value to be generated - high --> High bound (inclusive) on integer value to be generated + high --> High bound (inclusive) on integer_number value to be generated Note If (HIGH-LOW) > 2,147,483,561 prints error message on * unit and stops the program. diff --git a/lib/ranlib/ranlib.h b/lib/wave++/demos/ranlib/ranlib.h similarity index 100% rename from lib/ranlib/ranlib.h rename to lib/wave++/demos/ranlib/ranlib.h diff --git a/lib/wave++/demos/rms_error.cc b/lib/wave++/demos/rms_error.cc new file mode 100644 index 00000000..b7d3b4ff --- /dev/null +++ b/lib/wave++/demos/rms_error.cc @@ -0,0 +1,26 @@ +//file: rms_error.cc This returns ||in - out||/||in|| in l^2-norm + +#include +#include "rms_error.h" + +real_number RMS_error(const Interval &input, const Interval &output ) +{ + assert(input.beg==output.beg && input.end==output.end); + real_number error = 0, temp = 0; + real_number temp2; + for(int i=input.beg; i<=input.end; i++) + { + temp2 = input[i]-output[i]; + error += temp2*temp2; + temp += input[i]*input[i]; + } + error /= temp; + error = sqrt(error); + return error; +} + + + + + + diff --git a/lib/wave++/demos/rms_error.h b/lib/wave++/demos/rms_error.h new file mode 100644 index 00000000..d702477e --- /dev/null +++ b/lib/wave++/demos/rms_error.h @@ -0,0 +1,9 @@ +//file: rms_error.h +#include "../source/libw.h" + +#ifndef RMS_H +#define RMS_H + +real_number RMS_error(const Interval &input, const Interval &output ); + +#endif diff --git a/lib/wave++/documentation/CppWavelets.pdf b/lib/wave++/documentation/CppWavelets.pdf new file mode 100644 index 00000000..cca6c369 Binary files /dev/null and b/lib/wave++/documentation/CppWavelets.pdf differ diff --git a/lib/wave++/documentation/README.txt b/lib/wave++/documentation/README.txt new file mode 100644 index 00000000..0e887997 --- /dev/null +++ b/lib/wave++/documentation/README.txt @@ -0,0 +1,21 @@ +//README file for directory "wave++/documentation". January 2000 + +In this directory you will find the following PostScrip files + +0) wave++.ps +This brief document describes the main features and system requirements of +Wave++ + +1) usersGuideCppWavelets.ps +This is the user's guide (preliminary version) for the part +of wave++ that deals with wavelets and the Best Basis Algorithm. + +2) mpTechReport.ps +This a technical report describing some background on the Matching +Pursuit algorithm plus details of our implementation of this algorithm. + +3) demoDenoising.ps +This is a short note describing the mathematical +background to understand the +algorithm implemented in "wave++/demos/demoWavPack.cc" + diff --git a/lib/wave++/documentation/demoDenoising.pdf b/lib/wave++/documentation/demoDenoising.pdf new file mode 100644 index 00000000..8788bb26 Binary files /dev/null and b/lib/wave++/documentation/demoDenoising.pdf differ diff --git a/lib/wave++/documentation/mpTechReport.pdf b/lib/wave++/documentation/mpTechReport.pdf new file mode 100644 index 00000000..23c69f8e Binary files /dev/null and b/lib/wave++/documentation/mpTechReport.pdf differ diff --git a/lib/wave++/documentation/ps/CppWavelets.ps b/lib/wave++/documentation/ps/CppWavelets.ps new file mode 100644 index 00000000..efed1412 Binary files /dev/null and b/lib/wave++/documentation/ps/CppWavelets.ps differ diff --git a/lib/wave++/documentation/ps/demoDenoising.ps b/lib/wave++/documentation/ps/demoDenoising.ps new file mode 100644 index 00000000..40085cbb Binary files /dev/null and b/lib/wave++/documentation/ps/demoDenoising.ps differ diff --git a/lib/wave++/documentation/ps/mpTechReport.ps b/lib/wave++/documentation/ps/mpTechReport.ps new file mode 100644 index 00000000..c004baba Binary files /dev/null and b/lib/wave++/documentation/ps/mpTechReport.ps differ diff --git a/lib/wave++/documentation/ps/wave++.ps b/lib/wave++/documentation/ps/wave++.ps new file mode 100644 index 00000000..9b826524 Binary files /dev/null and b/lib/wave++/documentation/ps/wave++.ps differ diff --git a/lib/wave++/documentation/wave++.pdf b/lib/wave++/documentation/wave++.pdf new file mode 100644 index 00000000..d604c5bf Binary files /dev/null and b/lib/wave++/documentation/wave++.pdf differ diff --git a/lib/wave++/includes/Gabor.h b/lib/wave++/includes/Gabor.h deleted file mode 100644 index 5fa2ec7d..00000000 --- a/lib/wave++/includes/Gabor.h +++ /dev/null @@ -1,33 +0,0 @@ -// Gabor.h -// contains classes reprezenting real_DWT and complex_DWT Gabor functions -#ifndef GABOR_H -#define GABOR_H - -#include "Interval.h" - -class RealGabor -{ - public: - RealGabor() : s(1), u(0), v(0), w(0) {}; - RealGabor(const real_DWT &S, const real_DWT &U, const real_DWT &V, - const real_DWT &W); - // set s, u, v, w, no sample created - RealGabor(const real_DWT &S, const real_DWT &U, const real_DWT &V, - const real_DWT &W, const Interval & I); - // set s, u, v, w, Sample created by evaluating on I - ~RealGabor() {}; - void Set(const real_DWT &S, const real_DWT &U, const real_DWT &V, const real_DWT &W); - void Set(const real_DWT &S, const real_DWT &U, const real_DWT &V, const real_DWT &W, - const Interval &I); - real_DWT evaluate( const real_DWT &t ) const; // return g((t-u)/s) * cos(vt + w) - // where g(x) = exp(-Pi*x*x) - void createSample(const Interval &I); - // create normalized sample on I, i.e. its L2norm is 1 - - real_DWT s, u, v, w; - Interval Sample; -}; - -real_DWT g( const real_DWT &x ); - -#endif diff --git a/lib/wave++/sources/ArrayTree.cpp b/lib/wave++/source/ArrayTree.cc similarity index 73% rename from lib/wave++/sources/ArrayTree.cpp rename to lib/wave++/source/ArrayTree.cc index 319b933f..84df0b45 100644 --- a/lib/wave++/sources/ArrayTree.cpp +++ b/lib/wave++/source/ArrayTree.cc @@ -9,25 +9,25 @@ /******************************* ArrayTreePer *******************************/ -ArrayTreePer::ArrayTreePer(integer MAXLEVEL) : maxlevel(MAXLEVEL) +ArrayTreePer::ArrayTreePer(integer_number MAXLEVEL) : maxlevel(MAXLEVEL) { assert(maxlevel>0); dim = 1< 0 && ((dim>>maxlevel)<> (L) )); } ///////////////////////////////////////////////////////////////////////////// -integer ArrayTreePer::block_length(const integer &L) const +integer_number ArrayTreePer::block_length(const integer_number &L) const { assert(0<=L && L<=maxlevel); return (dim) >> (L); // 2^(maxlevel-L) } ///////////////////////////////////////////////////////////////////////////// -real_DWT *ArrayTreePer::left_child(const integer &L, const integer &B) const +real_number *ArrayTreePer::left_child(const integer_number &L, const integer_number &B) const { assert( 0<=L && L>L); // block_start(L+1, 2*B) } ///////////////////////////////////////////////////////////////////////////// -real_DWT *ArrayTreePer::right_child(const integer &L, const integer &B) const +real_number *ArrayTreePer::right_child(const integer_number &L, const integer_number &B) const { assert( 0<=L && L>L) + (dim>>(L+1)); @@ -100,7 +100,7 @@ real_DWT *ArrayTreePer::right_child(const integer &L, const integer &B) const /******************************* ArrayTreeAper ******************************/ -ArrayTreeAper::ArrayTreeAper(integer MAXLEVEL) : maxlevel(MAXLEVEL) +ArrayTreeAper::ArrayTreeAper(integer_number MAXLEVEL) : maxlevel(MAXLEVEL) { assert(maxlevel>0); size = ((1)<<(maxlevel+1)) - 1; @@ -116,7 +116,7 @@ ArrayTreeAper::ArrayTreeAper(const ArrayTreeAper &Rhs) : { root = new Interval [size]; assert(root); - for(integer i=0; i class BinTreeComplete : public BinTree { public: - BinTreeComplete() : maxlevel(0) { BinTree::BinTree(); } + BinTreeComplete() : maxlevel(0) { } - BinTreeComplete(integer MAXLEVEL, const CONTENTYPE &c); + BinTreeComplete(integer_number MAXLEVEL, const CONTENTYPE &c); // set maxlevel=MAXLEVEL and construct tree with contents of all // nodes equal c. @@ -60,20 +60,20 @@ class BinTreeComplete : public BinTree BinTreeComplete& operator=(const BinTreeComplete &); - Node *block(const integer &L, const integer &B); + Node *block(const integer_number &L, const integer_number &B); // return pointer to B-th node at level L Node *blockHelp(Node *pos, - const integer &L, const integer &B) const; + const integer_number &L, const integer_number &B) const; - void SetConst(Node **ptr, const integer &L, const CONTENTYPE &c); + void SetConst(Node **ptr, const integer_number &L, const CONTENTYPE &c); // attach to *ptr a tree with levels 0...L, filled up with constant c - integer maxlevel; // levels numbered from 0 to maxlevel + integer_number maxlevel; // levels numbered from 0 to maxlevel }; // end of BinTreeComplete class definition -typedef BinTreeComplete Tree; +typedef BinTreeComplete Tree; /////////////////////////////////////////////////////////////////////////////// /*********************** Implementation **************************************/ @@ -142,12 +142,12 @@ void BinTree::DestroyTreeHelp(Node* &ptr) /////////////////////////////////////////////////////////////////////////////// template -BinTreeComplete::BinTreeComplete(integer MAXLEVEL, +BinTreeComplete::BinTreeComplete(integer_number MAXLEVEL, const CONTENTYPE &c) : maxlevel(MAXLEVEL) { assert(maxlevel>0); - SetConst(&root, maxlevel, c); + SetConst(&BinTree::root, maxlevel, c); } /////////////////////////////////////////////////////////////////////////////// @@ -155,7 +155,7 @@ template BinTreeComplete::BinTreeComplete( const BinTreeComplete &Rhs) { - BinTree::CopyTree(Rhs.root, root); + BinTree::CopyTree(Rhs.root, BinTree::root); } /////////////////////////////////////////////////////////////////////////////// @@ -169,17 +169,17 @@ BinTreeComplete& BinTreeComplete::operator= /////////////////////////////////////////////////////////////////////////////// template -Node *BinTreeComplete::block(const integer &L, - const integer &B) +Node *BinTreeComplete::block(const integer_number &L, + const integer_number &B) { assert( 0<=L && L<=maxlevel && 0<=B && B<=(1<::root, L, B); } /////////////////////////////////////////////////////////////////////////////// template Node *BinTreeComplete::blockHelp(Node *pos, - const integer &L, const integer &B) const + const integer_number &L, const integer_number &B) const { Node *ptr; if( L && pos) @@ -196,7 +196,7 @@ Node *BinTreeComplete::blockHelp(Node *pos, /////////////////////////////////////////////////////////////////////////////// template void BinTreeComplete::SetConst(Node **ptr, - const integer &L, const CONTENTYPE &c) + const integer_number &L, const CONTENTYPE &c) { if( L > 0 ) { diff --git a/lib/wave++/sources/ConvDec.cpp b/lib/wave++/source/ConvDec.cc similarity index 79% rename from lib/wave++/sources/ConvDec.cpp rename to lib/wave++/source/ConvDec.cc index 49221fc7..e37ae199 100644 --- a/lib/wave++/sources/ConvDec.cpp +++ b/lib/wave++/source/ConvDec.cc @@ -7,14 +7,14 @@ #include #include -void ConvDecPer( const real_DWT *In, real_DWT *Out, integer q, const PQMF &F ) +void ConvDecPer( const real_number *In, real_number *Out, integer_number q, const PQMF &F ) { int j, offset, i = 0; - real_DWT *In_end, *In_End, *Coef; - real_DWT *Initial = const_cast(In) - F.beg; // Input array offset to + real_number *In_end, *In_End, *Coef; + real_number *Initial = const_cast(In) - F.beg; // Input array offset to // match the coefficients - real_DWT *Final = const_cast(In) + q - 1; // End of the input array - real_DWT *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array + real_number *Final = const_cast(In) + q - 1; // End of the input array + real_number *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array if (q > (F.end - F.beg)) // Long input array { @@ -79,7 +79,7 @@ void ConvDecPer( const real_DWT *In, real_DWT *Out, integer q, const PQMF &F ) { j = 0; Coef = F.pcoef[Q]; - In_end = const_cast(In) + i; + In_end = const_cast(In) + i; while (j <= i) { @@ -97,14 +97,14 @@ void ConvDecPer( const real_DWT *In, real_DWT *Out, integer q, const PQMF &F ) } } /////////////////////////////////////////////////////////////////// -void ConvDecPer( const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F ) +void ConvDecPer( const real_number *In, real_number *Out, integer_number q, const GPQMF &F ) { int j, offset, i = 0; - real_DWT *In_end, *In_End, *Coef; - real_DWT *Initial = const_cast(In) - F.beg; // Input array offset to match + real_number *In_end, *In_End, *Coef; + real_number *Initial = const_cast(In) - F.beg; // Input array offset to match // the coefficients - real_DWT *Final = const_cast(In) + q - 1; // End of the input array - real_DWT *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array + real_number *Final = const_cast(In) + q - 1; // End of the input array + real_number *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array if (q > (F.end - F.beg)) // Long input array { @@ -169,7 +169,7 @@ void ConvDecPer( const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F ) { j = 0; Coef = F.pcoef[Q]; - In_end = const_cast(In) + i; + In_end = const_cast(In) + i; while (j <= i) { @@ -187,10 +187,10 @@ void ConvDecPer( const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F ) } } /////////////////////////////////////////////////////////////////// -void ConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q, const PQMF &F) +void ConvDecV2Per(const real_number* In, real_number* Out, integer_number q, const PQMF &F) { int i, j, q1 = q-1, q2i; - real_DWT *Coef; + real_number *Coef; if( q > (F.end - F.beg) ) { @@ -225,10 +225,10 @@ void ConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q, const PQMF &F) } } /////////////////////////////////////////////////////////////////// -void ConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q, const GPQMF &F) +void ConvDecV2Per(const real_number* In, real_number* Out, integer_number q, const GPQMF &F) { int i, j, q1 = q-1, q2i; - real_DWT *Coef; + real_number *Coef; if( q > (F.end - F.beg) ) { @@ -264,13 +264,13 @@ void ConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q, const GPQMF &F) } /////////////////////////////////////////////////////////////// -void AdjConvDecPer( const real_DWT *In, real_DWT *Out, integer q2, const PQMF &F ) +void AdjConvDecPer( const real_number *In, real_number *Out, integer_number q2, const PQMF &F ) { int j, offset, i = 0, q = 2*q2; - real_DWT *Out_end, *Out_End, *Coef; - real_DWT *Initial = Out - F.beg; // Output array offset to match the coefficients - real_DWT *Final = Out + q - 1; // End of the Output array - real_DWT *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array + real_number *Out_end, *Out_End, *Coef; + real_number *Initial = Out - F.beg; // Output array offset to match the coefficients + real_number *Final = Out + q - 1; // End of the Output array + real_number *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array if (q > (F.end - F.beg)) // Long input array { @@ -353,13 +353,13 @@ void AdjConvDecPer( const real_DWT *In, real_DWT *Out, integer q2, const PQMF &F } } /////////////////////////////////////////////////////////////// -void AdjConvDecPer( const real_DWT *In, real_DWT *Out, integer q2, const GPQMF &F ) +void AdjConvDecPer( const real_number *In, real_number *Out, integer_number q2, const GPQMF &F ) { int j, offset, i = 0, q = 2*q2; - real_DWT *Out_end, *Out_End, *Coef; - real_DWT *Initial = Out - F.beg; // Output array offset to match the coefficients - real_DWT *Final = Out + q - 1; // End of the Output array - real_DWT *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array + real_number *Out_end, *Out_End, *Coef; + real_number *Initial = Out - F.beg; // Output array offset to match the coefficients + real_number *Final = Out + q - 1; // End of the Output array + real_number *CoefBegin = F.coef + F.beg; // Actual beginning of coefficient array if (q > (F.end - F.beg)) // Long input array { @@ -442,10 +442,10 @@ void AdjConvDecPer( const real_DWT *In, real_DWT *Out, integer q2, const GPQMF & } } /////////////////////////////////////////////////////////////////// -void AdjConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q2, const PQMF &F) +void AdjConvDecV2Per(const real_number* In, real_number* Out, integer_number q2, const PQMF &F) { int i, j, q = 2*q2, q1 = q-1, q2i; - real_DWT *Coef; + real_number *Coef; if( q > (F.end - F.beg) ) { @@ -480,10 +480,10 @@ void AdjConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q2, const PQMF & } } /////////////////////////////////////////////////////////////////// -void AdjConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q2, const GPQMF &F) +void AdjConvDecV2Per(const real_number* In, real_number* Out, integer_number q2, const GPQMF &F) { int i, j, q = 2*q2, q1 = q-1, q2i; - real_DWT *Coef; + real_number *Coef; if( q > (F.end - F.beg) ) { @@ -520,11 +520,11 @@ void AdjConvDecV2Per(const real_DWT* In, real_DWT* Out, integer q2, const GPQMF /////////////////////////////////////////////////////////////// void ConvDecAper(const Interval &In, Interval &Out, const QMF &F) { - integer alpha=ICH(In.beg + F.beg), omega=IFH(In.end + F.end); + integer_number alpha=ICH(In.beg + F.beg), omega=IFH(In.end + F.end); assert(Out.beg <= alpha); assert(Out.end >= omega ); - integer i, Begin, End, j; - integer i2; + integer_number i, Begin, End, j; + integer_number i2; for(i=alpha; i<=omega; i++) { i2 = 2 * i; @@ -537,7 +537,7 @@ void ConvDecAper(const Interval &In, Interval &Out, const QMF &F) ////////////////////////////////////////////////////////////////// void AdjConvDecAper(const Interval &In, Interval &Out, const QMF &F) { - integer j = Out.beg, Begin, End, i; + integer_number j = Out.beg, Begin, End, i; while(j <= Out.end) { Begin = maximum( In.beg, ICH(j+F.beg) ); diff --git a/lib/wave++/includes/ConvDec.h b/lib/wave++/source/ConvDec.h similarity index 73% rename from lib/wave++/includes/ConvDec.h rename to lib/wave++/source/ConvDec.h index 1ef9e32a..fe259f6b 100644 --- a/lib/wave++/includes/ConvDec.h +++ b/lib/wave++/source/ConvDec.h @@ -8,8 +8,8 @@ #include "QMF.h" #include "Interval.h" -void ConvDecPer(const real_DWT *In, real_DWT *Out, integer q, const PQMF &F ); -void ConvDecPer(const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F ); +void ConvDecPer(const real_number *In, real_number *Out, integer_number q, const PQMF &F ); +void ConvDecPer(const real_number *In, real_number *Out, integer_number q, const GPQMF &F ); // Periodic convolution-decimation. Fast version. // It is assumed that F.beg <= 0 <= F.end. Else use version 2. @@ -17,8 +17,8 @@ void ConvDecPer(const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F ); // Out predefined so that Out[0].... Out[q/2-1] exist. // Output superimposed, so if assignment wanted all elts of Out should be 0's -void ConvDecV2Per(const real_DWT *In, real_DWT *Out, integer q, const PQMF &F); -void ConvDecV2Per(const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F); +void ConvDecV2Per(const real_number *In, real_number *Out, integer_number q, const PQMF &F); +void ConvDecV2Per(const real_number *In, real_number *Out, integer_number q, const GPQMF &F); // Periodic convolution-decimation version 2. // It is NOT assumed that F.beg <= 0 <= F.end. Slower algorithm. @@ -26,8 +26,8 @@ void ConvDecV2Per(const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F); // Out predefined so that Out[0].... Out[q/2-1] exist. // Output superimposed, so if assignment wanted all elts of Out should be 0's -void AdjConvDecPer(const real_DWT *In, real_DWT *Out, integer q, const PQMF &F ); -void AdjConvDecPer(const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F ); +void AdjConvDecPer(const real_number *In, real_number *Out, integer_number q, const PQMF &F ); +void AdjConvDecPer(const real_number *In, real_number *Out, integer_number q, const GPQMF &F ); // Adjoint periodic convolution-decimation. Fast version. // It is assumed that F.beg <= 0 <= F.end. Else use version 2. @@ -35,8 +35,8 @@ void AdjConvDecPer(const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F // Out predefined so that Out[0].... Out[2*q-1] exist // Output superimposed, so if assignment wanted all elts of Out should be 0's -void AdjConvDecV2Per(const real_DWT *In, real_DWT *Out, integer q, const PQMF &F ); -void AdjConvDecV2Per(const real_DWT *In, real_DWT *Out, integer q, const GPQMF &F ); +void AdjConvDecV2Per(const real_number *In, real_number *Out, integer_number q, const PQMF &F ); +void AdjConvDecV2Per(const real_number *In, real_number *Out, integer_number q, const GPQMF &F ); // Adjoint periodic convolution-decimation. // It is NOT assumed that F.beg <= 0 <= F.end. Slower algorithm. diff --git a/lib/wave++/sources/FFTGaborMP.cpp b/lib/wave++/source/FFTGaborMP.cc similarity index 75% rename from lib/wave++/sources/FFTGaborMP.cpp rename to lib/wave++/source/FFTGaborMP.cc index 491b9196..929ea9d4 100644 --- a/lib/wave++/sources/FFTGaborMP.cpp +++ b/lib/wave++/source/FFTGaborMP.cc @@ -1,47 +1,45 @@ -#define _USE_MATH_DEFINES +#include "FFTGaborMP.h" +#include #include #include -#include #include "fft.h" -#include "FFTGaborMP.h" - // declare bunch of global variables. They get updated as you run // RunFFTGaborMP -static real_DWT coef, product, a, b, a1, b1, v, phase, Pnorm2, Qnorm2, PprodQ; +static real_number coef, product, a, b, a1, b1, v, phase, Pnorm2, Qnorm2, PprodQ; static RealGabor Gtemp; -static integer s, u; +static integer_number s, u; -real_DWT RunFFTGaborMP(int max_iter, // maximal number of iterations - real_DWT epsilon, // desired precision | Rf | < epsilon +real_number RunFFTGaborMP(int max_iter, // maximal number of iterations + real_number epsilon, // desired precision | Rf | < epsilon const Interval &f, // signal to be approximated - // assumption : f sampled on integers starting with 0 + // assumption : f sampled on integer_numbers starting with 0 // i.e. f.beg must be 0. No constraints on f.length. Interval &f_approx, // MP approximation of f // f_approx is a linear combination of Gabors Interval &Rf, // final residual: Rf = f - f_approx // error which is returned equals | Rf | - std::vector &G, // std::vector of Gabors chosen for + std::vector &G, // vector of Gabors chosen for // f_approx - std::vector & Gcoef // coeficinets in the linear + std::vector & Gcoef // coeficinets in the linear // combination, corresponding to Gabors in G // f_approx = sum( Gcoef[i] * G[i] ), where number of i's // depends on epsilon and max_iter ) { - integer dim = f.length; + integer_number dim = f.length; assert( f.beg == 0 && dim > 0 && is_pow_of_2(dim)); Interval I(0, dim-1); for(int i=0; i dim, i.e. s=dim=2^m, s=2^(m-1) and s=2^(m-2) for(s = dim; s >= (1<<(m-2)); s>>=1) @@ -112,26 +110,26 @@ void getOptimalFFTGabor(const Interval &f) } } /////////////////////////////////////////////////////////////// -void proces1(const integer &N, const real_DWT *fptr) +void proces1(const integer_number &N, const real_number *fptr) { Interval X(0, N-1), Y(0, N-1), U(0, N-1), V(0, N-1); - real_DWT temp; - real_DWT *Xptr = X.origin, *Yptr = Y.origin; - for(integer t=0; t < N; t++) + real_number temp; + real_number *Xptr = X.origin, *Yptr = Y.origin; + for(integer_number t=0; t < N; t++) { - temp = g((real_DWT)(t-u)/(real_DWT)s); + temp = g((real_number)(t-u)/(real_number)s); *Xptr++ = (*fptr++) * temp; *Yptr++ = temp * temp; } - complex_DWT *E1, *E2, *O1, *O2; - E1 = (complex_DWT *) calloc(N, sizeof(complex_DWT)); + complex_number *E1, *E2, *O1, *O2; + E1 = (complex_number *) calloc(N, sizeof(complex_number)); assert(E1); - E2 = (complex_DWT *) calloc(N, sizeof(complex_DWT)); + E2 = (complex_number *) calloc(N, sizeof(complex_number)); assert(E2); - for(integer t=0; t>1); k++) + real_number c = Y.origin[0]; + for(integer_number k = 0; k < (N>>1); k++) { Pnorm2 = (c + Y.origin[2*k]) / 2; Qnorm2 = (c - Y.origin[2*k]) / 2; @@ -171,28 +169,28 @@ void proces1(const integer &N, const real_DWT *fptr) } } ///////////////////////////////////////////////////////////////////////////// -void proces2(const integer &N, const integer &stop, const real_DWT *fptr) +void proces2(const integer_number &N, const integer_number &stop, const real_number *fptr) { Interval X(0, N-1); Interval Y(0, N-1); Interval U(0, N-1), V(0, N-1); - real_DWT temp; - real_DWT *Xptr = X.origin, *Yptr = Y.origin; - integer d = u - 4*s; - for(integer t=0; t <= stop; t++) + real_number temp; + real_number *Xptr = X.origin, *Yptr = Y.origin; + integer_number d = u - 4*s; + for(integer_number t=0; t <= stop; t++) { - temp = g((real_DWT)(t-4*s)/(real_DWT)s); + temp = g((real_number)(t-4*s)/(real_number)s); *Xptr++ = (*fptr++) * temp; *Yptr++ = temp * temp; } - complex_DWT *E1, *E2, *O1, *O2; - E1 = (complex_DWT *) calloc(N, sizeof(complex_DWT)); + complex_number *E1, *E2, *O1, *O2; + E1 = (complex_number *) calloc(N, sizeof(complex_number)); assert(E1); - E2 = (complex_DWT *) calloc(N, sizeof(complex_DWT)); + E2 = (complex_number *) calloc(N, sizeof(complex_number)); assert(E2); - for(integer t=0; t>1); k++) + real_number co = cos(alpha), si = sin(alpha); + real_number x, y, uu, vv; + for(integer_number k = 0; k < (N>>1); k++) { y = Y.origin[2*k]; vv = V.origin[2*k]; Pnorm2 = (c + y * cos2al + vv * sin2al) / 2; @@ -266,7 +264,7 @@ void update() else // P = 0 or a1 = 0 { product = -b / sqrt(Qnorm2) ; phase = M_PI * 0.5; } } - // std::cout << "product " << product << std::endl; + // cout << "product " << product << endl; if ( absval(product) > absval(coef) ) { coef = product; diff --git a/lib/wave++/includes/FFTGaborMP.h b/lib/wave++/source/FFTGaborMP.h similarity index 75% rename from lib/wave++/includes/FFTGaborMP.h rename to lib/wave++/source/FFTGaborMP.h index c808f4a8..46090183 100644 --- a/lib/wave++/includes/FFTGaborMP.h +++ b/lib/wave++/source/FFTGaborMP.h @@ -2,6 +2,7 @@ #define FFTGABORMP_H #include "Gabor.h" +#include "common.h" #include // Functions which runs MP algorithm until epsilon precision is achieved @@ -9,8 +10,8 @@ // It makes repeated calls to getOptimalFFTGabor function. // Partitionis fixed (Mallat). // Works only for signals whose length is power of 2 -real_DWT RunFFTGaborMP(int max_iter, // maximal number of iterations - real_DWT epsilon, // desired precision | Rf | < epsilon +real_number RunFFTGaborMP(int max_iter, // maximal number of iterations + real_number epsilon, // desired precision | Rf | < epsilon const Interval &f, // signal to be approximated // assumption : f sampled on integers starting with 0 // i.e. f.beg must be 0 @@ -19,9 +20,9 @@ real_DWT RunFFTGaborMP(int max_iter, // maximal number of iterations // f_approx is a linear combination of Gabors Interval &Rf, // final residual: Rf = f - f_approx // error which is returned equals | Rf | - std::vector &G, // std::vector of Gabors chosen for + std::vector &G, // vector of Gabors chosen for // f_approx - std::vector & Gcoef // coeficinets in the linear + std::vector & Gcoef // coeficinets in the linear // combination, corresponding to Gabors in G // f_approx = sum( Gcoef[i] * G[i] ), where number of i's // depends on epsilon and max_iter @@ -34,10 +35,10 @@ real_DWT RunFFTGaborMP(int max_iter, // maximal number of iterations void getOptimalFFTGabor(const Interval &f); // utility function -void proces1(const integer &N, const real_DWT *fptr); +void proces1(const integer_number &N, const real_number *fptr); // utility function -void proces2(const integer &N, const integer &stop, const real_DWT *fptr); +void proces2(const integer_number &N, const integer_number &stop, const real_number *fptr); // utility for updating global variables void update(); diff --git a/lib/wave++/sources/Gabor.cpp b/lib/wave++/source/Gabor.cc similarity index 52% rename from lib/wave++/sources/Gabor.cpp rename to lib/wave++/source/Gabor.cc index 57d4fc0e..540f507d 100644 --- a/lib/wave++/sources/Gabor.cpp +++ b/lib/wave++/source/Gabor.cc @@ -1,32 +1,32 @@ // Gabor.cc -// Implementation of real_DWT Gabor functions +// Implementation of real Gabor functions #include "Gabor.h" - -#define _USE_MATH_DEFINES -#include #include +#include +#include +#include -RealGabor::RealGabor(const real_DWT &S, const real_DWT &U, const real_DWT &V, - const real_DWT &W) : s(S), u(U),v(V), w(W) +RealGabor::RealGabor(const real_number &S, const real_number &U, const real_number &V, + const real_number &W) : s(S), u(U),v(V), w(W) { assert(s>0); } ////////////////////////////////////////////////////////////////////////////// -RealGabor::RealGabor(const real_DWT &S, const real_DWT &U, const real_DWT &V, - const real_DWT &W, const Interval &I) : s(S), u(U),v(V), w(W) +RealGabor::RealGabor(const real_number &S, const real_number &U, const real_number &V, + const real_number &W, const Interval &I) : s(S), u(U),v(V), w(W) { assert(s>0); createSample(I); } ////////////////////////////////////////////////////////////////////////////// -void RealGabor::Set(const real_DWT &S, const real_DWT &U, const real_DWT &V, const real_DWT &W) +void RealGabor::Set(const real_number &S, const real_number &U, const real_number &V, const real_number &W) { assert(S>0); s=S; u=U; v=V; w=W; } ////////////////////////////////////////////////////////////////////////////// -void RealGabor::Set(const real_DWT &S, const real_DWT &U, const real_DWT &V, const real_DWT &W, +void RealGabor::Set(const real_number &S, const real_number &U, const real_number &V, const real_number &W, const Interval &I) { assert(S>0); @@ -34,7 +34,7 @@ void RealGabor::Set(const real_DWT &S, const real_DWT &U, const real_DWT &V, con createSample(I); } ////////////////////////////////////////////////////////////////////////////// -real_DWT RealGabor::evaluate(const real_DWT &t) const +real_number RealGabor::evaluate(const real_number &t) const { return exp(-M_PI*(t-u)*(t-u)/(s*s)) * cos(v*t + w); } @@ -42,9 +42,9 @@ real_DWT RealGabor::evaluate(const real_DWT &t) const void RealGabor::createSample(const Interval &I) { Sample.Set(I.beg, I.end); - real_DWT norm = 0; - real_DWT *Iptr = I.origin + I.beg, *Sptr = Sample.origin + Sample.beg; - integer j; + real_number norm = 0; + real_number *Iptr = I.origin + I.beg, *Sptr = Sample.origin + Sample.beg; + integer_number j; for(j=0; j0 && num_of_levels>0); - levels=new integer [num_of_levels]; + levels=new integer_number [num_of_levels]; assert(levels); for(int i=0; i>p)<> (levels[i]) ); // dim/number of blocks at i-th level } ////////////////////////////////////////////////////////////////////////////// -real_DWT *HedgePer::block_start(const integer &i) const +real_number *HedgePer::block_start(const integer_number &i) const { assert(origin); assert(0<=i && i> (levels[j]); + integer_number len=0; + for(integer_number j=0; j> (levels[j]); return (origin + len); } /********************** Hedge Aperiodic **************************************/ -HedgeAper::HedgeAper(const integer &n, const integer *Levs) : +HedgeAper::HedgeAper(const integer_number &n, const integer_number *Levs) : num_of_levels(n) { assert(num_of_levels>0); - levels=new integer [num_of_levels]; + levels=new integer_number [num_of_levels]; assert(levels); for(int i=0; i>(std::istream &input, Interval &I) { if(I.origin) { - for(integer j=I.beg; j<=I.end; j++) + for(integer_number j=I.beg; j<=I.end; j++) { if(!input.eof()) input >> I.origin[j]; else @@ -66,7 +66,7 @@ std::ostream &operator<<(std::ostream &output, const HedgePer &H) { if(H.origin) { - for(integer i=0; i < H.dim; i++) + for(integer_number i=0; i < H.dim; i++) output << H.origin[i] << std::endl; } else std::cout << "HedgePer empty. No output produced." << std::endl; @@ -76,7 +76,7 @@ std::ostream &operator<<(std::ostream &output, const HedgePer &H) /////////////////////////////////////////////////////////////////////////////// std::istream &operator>>(std::istream &input, HedgePer &H) { - for(integer j=0; j < H.dim; j++) + for(integer_number j=0; j < H.dim; j++) { if(!input.eof()) input >> H.origin[j]; else @@ -117,7 +117,7 @@ void ReadFromFile(HedgePer &H, const char *filename) void coutLevels(const HedgePer &H) { if(H.levels) - for(integer i=0; i>(std::istream &input, HedgeAper &H) { - for(integer j=0; j < H.num_of_levels; j++) input >> H.root[j]; + for(integer_number j=0; j < H.num_of_levels; j++) input >> H.root[j]; return input; } // enables cin >> x >> y @@ -183,7 +183,7 @@ void ReadFromFile(HedgeAper &H, const char *filename) void coutLevels(const HedgeAper &H) { if(H.levels) - for(integer i=0; i>(std::istream &input, ArrayTreePer &A) { - for(integer j=0; j < A.dim * (A.maxlevel+1); j++) + for(integer_number j=0; j < A.dim * (A.maxlevel+1); j++) { if(!input.eof()) input >> A.origin[j]; else @@ -259,7 +259,7 @@ std::ostream &operator<<(std::ostream &output, const ArrayTreeAper &A) { if(A.root) { - for(integer i=0; i < A.size; i++) + for(integer_number i=0; i < A.size; i++) output << A.root[i]; } else std::cout << "ArrayTreeAper empty. No output produced." << std::endl; @@ -269,7 +269,7 @@ std::ostream &operator<<(std::ostream &output, const ArrayTreeAper &A) /////////////////////////////////////////////////////////////////////////////// std::istream &operator>>(std::istream &input, ArrayTreeAper &A) { - for(integer j=0; j < A.size; j++) input >> A.root[j]; + for(integer_number j=0; j < A.size; j++) input >> A.root[j]; return input; } // enables cin >> x >> y diff --git a/lib/wave++/includes/InOutUtil.h b/lib/wave++/source/InOutUtil.h similarity index 99% rename from lib/wave++/includes/InOutUtil.h rename to lib/wave++/source/InOutUtil.h index 57a4f542..a510c42c 100644 --- a/lib/wave++/includes/InOutUtil.h +++ b/lib/wave++/source/InOutUtil.h @@ -8,6 +8,7 @@ #include "Interval.h" #include "Hedge.h" #include "ArrayTree.h" +#include /************************** Interval *****************************************/ diff --git a/lib/wave++/sources/Interval.cpp b/lib/wave++/source/Interval.cc similarity index 72% rename from lib/wave++/sources/Interval.cpp rename to lib/wave++/source/Interval.cc index 7415c726..5c8202df 100644 --- a/lib/wave++/sources/Interval.cpp +++ b/lib/wave++/source/Interval.cc @@ -6,17 +6,17 @@ #include #include #include -#include +#include #include "Interval.h" -Interval::Interval(const integer &alpha, const integer &omega, const real_DWT *data) : origin(0) +Interval::Interval(const integer_number &alpha, const integer_number &omega, const real_number *data) : origin(0) { if(data == 0) Set( alpha, omega ); else Set( alpha, omega, data ); } /////////////////////////////////////////////////////////////////////////////// -Interval::Interval(const integer &p, const real_DWT *data) : origin(0) +Interval::Interval(const integer_number &p, const real_number *data) : origin(0) { if(data==0) Set(0, (1< 0) { - origin=new real_DWT [length]; + origin=new real_number [length]; assert(origin); origin -= beg; - if(data == 0) for(integer i=beg; i<=end; i++) origin[i]=0; - else for(integer i=0; i -Partition::Partition(integer dim, real_DWT a) +Partition::Partition(integer_number dim, real_number a) { - integer largest_pow = ((integer)(floor((log((double)dim) / log(a)) + 0.5 ))); + integer_number largest_pow = ((integer_number)(floor((log(dim) / log(a)) + 0.5 ))); // largest_pow = nearest integer to log(dim)/log(a), i.e. it is // the largest integer power of a which is <= dim // In this implementation for the smallest power od a we choose 1. @@ -14,13 +13,13 @@ Partition::Partition(integer dim, real_DWT a) s.Set(1, largest_pow); dv.Set(s.beg, s.end); du.Set(s.beg, s.end); - for(integer j = s.beg; j <= s.end; j++) + for(integer_number j = s.beg; j <= s.end; j++) { s.origin[j] = pow(a, j); // s[j] = a^j dv.origin[j] = M_PI / s.origin[j]; // dv[j] = Pi / s[j] du.origin[j] = s.origin[j] / 2; // du[j] = s[j]/2; - if( (integer)(du.origin[j]) <= 0 ) + if( (integer_number)(du.origin[j]) <= 0 ) { // in this case take du=1 du.origin[j] = 1.0; diff --git a/lib/wave++/includes/Partition.h b/lib/wave++/source/Partition.h similarity index 61% rename from lib/wave++/includes/Partition.h rename to lib/wave++/source/Partition.h index 5d8e3318..9cb306cf 100644 --- a/lib/wave++/includes/Partition.h +++ b/lib/wave++/source/Partition.h @@ -4,17 +4,18 @@ #define PARTITION_H #include "Interval.h" +#include "common.h" class Partition { public: - Partition(integer dim, real_DWT a); + Partition(integer_number dim, real_number a); Interval s; // scale Interval du; Interval dv; - integer lmu, rmu; - real_DWT rmv; + integer_number lmu, rmu; + real_number rmv; }; diff --git a/lib/wave++/sources/QMF.cpp b/lib/wave++/source/QMF.cc similarity index 70% rename from lib/wave++/sources/QMF.cpp rename to lib/wave++/source/QMF.cc index 36ebef28..7c8d702f 100644 --- a/lib/wave++/sources/QMF.cpp +++ b/lib/wave++/source/QMF.cc @@ -12,7 +12,7 @@ QMF::QMF() : beg(0), end(-1), center(0), dev(0), coef(0) { } -QMF::QMF(const real_DWT *f, integer alpha, integer omega) +QMF::QMF(const real_number *f, integer_number alpha, integer_number omega) { Set(f, alpha, omega); } @@ -22,10 +22,10 @@ QMF::QMF(const QMF &Rhs) : { if(Rhs.coef) { - coef=new real_DWT [end-beg+1]; + coef=new real_number [end-beg+1]; assert(coef); coef -= beg; - for(integer i=beg; i<=end; i++) coef[i]=Rhs.coef[i]; + for(integer_number i=beg; i<=end; i++) coef[i]=Rhs.coef[i]; } else coef=0; } @@ -36,7 +36,7 @@ QMF::~QMF() coef=0; } -void QMF::Set(const real_DWT *f, integer alpha, integer omega) +void QMF::Set(const real_number *f, integer_number alpha, integer_number omega) { if(alpha > 0 || omega < 0) std::cout << "Standard convention QMF.beg <= 0 and QMF.end >= 0 not satisfied." @@ -46,11 +46,11 @@ void QMF::Set(const real_DWT *f, integer alpha, integer omega) beg = alpha; end = omega; assert ( (end-beg+1)>0 ); - coef = new real_DWT [end-beg+1]; + coef = new real_number [end-beg+1]; assert(coef); coef -= beg; - real_DWT energy = 0; - for(integer i=beg; i<=end; i++) + real_number energy = 0; + for(integer_number i=beg; i<=end; i++) { coef[i] = f[i-beg]; energy += coef[i]*coef[i]; @@ -60,10 +60,10 @@ void QMF::Set(const real_DWT *f, integer alpha, integer omega) { center /= energy; int sgn=-1; - for(integer j=1; j<=(end-beg)/2; j++) + for(integer_number j=1; j<=(end-beg)/2; j++) { - real_DWT y=0; - for(integer k=j+beg; k<=end-j; k++) y += k*coef[k-j]*coef[k+j]; + real_number y=0; + for(integer_number k=j+beg; k<=end-j; k++) y += k*coef[k-j]*coef[k+j]; dev += sgn*y; sgn = -sgn; } @@ -79,30 +79,30 @@ GPQMF::GPQMF(): QMF() pcoef_size=0; } -GPQMF::GPQMF(const real_DWT *f, integer alpha, integer omega) +GPQMF::GPQMF(const real_number *f, integer_number alpha, integer_number omega) { Set(f, alpha, omega); } -GPQMF::GPQMF(const GPQMF &Rhs) : QMF(Rhs) +GPQMF::GPQMF(const GPQMF &Rhs) : QMF::QMF(Rhs) { int N = Rhs.end + 1 - Rhs.beg; N = ( ((N&1)==0) ? N : N+1 ); pcoef_size = Rhs.pcoef_size; if(Rhs.pcoef) { - pcoef = new real_DWT*[Rhs.pcoef_size]; + pcoef = new real_number*[Rhs.pcoef_size]; assert(pcoef); for(int m = 0; m < pcoef_size-1; m++) { int index = pcoef_size-1-m; N -= 2; - pcoef[index] = new real_DWT[N]; + pcoef[index] = new real_number[N]; assert(pcoef[index]); for(int i=0; i < N; i++) pcoef[index][i] = Rhs.pcoef[index][i]; } - pcoef[0] = new real_DWT[1]; + pcoef[0] = new real_number[1]; assert(pcoef[0]); pcoef[0][0] = Rhs.pcoef[0][0]; } @@ -122,32 +122,32 @@ GPQMF::~GPQMF() pcoef = 0; } -void GPQMF::Set(const real_DWT *f, integer alpha, integer omega) +void GPQMF::Set(const real_number *f, integer_number alpha, integer_number omega) { QMF::Set(f, alpha, omega); - real_DWT *fq; + real_number *fq; int N = end+1-beg; N = ( ( (N&1)==0 ) ? N : N+1 ); // make sure N is even pcoef_size = N/2; - pcoef = new real_DWT*[pcoef_size]; + pcoef = new real_number*[pcoef_size]; assert(pcoef); for(int m = 0; m < pcoef_size-1; m++) { N -= 2; - fq = new real_DWT [N]; + fq = new real_number [N]; assert(fq); periodize(fq, N); pcoef[pcoef_size-1-m] = fq; } - fq = new real_DWT [1]; + fq = new real_number [1]; assert(fq); periodize(fq,1); pcoef[0] = fq; } -void GPQMF::periodize(real_DWT *fq, integer q) +void GPQMF::periodize(real_number *fq, integer_number q) { - real_DWT *pointer; + real_number *pointer; int counter; int index = ( (beg%q + q)%q ); int j = 0; @@ -168,23 +168,23 @@ PQMF::PQMF(): QMF() pcoef_size=0; } -PQMF::PQMF(const real_DWT *f, integer alpha, integer omega) +PQMF::PQMF(const real_number *f, integer_number alpha, integer_number omega) { Set(f, alpha, omega); } -PQMF::PQMF(const PQMF &Rhs) : QMF(Rhs) +PQMF::PQMF(const PQMF &Rhs) : QMF::QMF(Rhs) { int N; pcoef_size = Rhs.pcoef_size; if(Rhs.pcoef) { - pcoef = new real_DWT*[Rhs.pcoef_size]; + pcoef = new real_number*[Rhs.pcoef_size]; assert(pcoef); for(int m = 0; m < pcoef_size; m++) { N = 1 << m; - pcoef[m] = new real_DWT[N]; + pcoef[m] = new real_number[N]; assert(pcoef[m]); for(int i=0; i < N; i++) pcoef[m][i] = Rhs.pcoef[m][i]; @@ -206,31 +206,31 @@ PQMF::~PQMF() pcoef = 0; } -void PQMF::Set(const real_DWT *f, integer alpha, integer omega) +void PQMF::Set(const real_number *f, integer_number alpha, integer_number omega) { QMF::Set(f, alpha, omega); - real_DWT *fq; + real_number *fq; pcoef_size = 0; int N = end+1-beg; while( (1 << pcoef_size) < N ) pcoef_size++; - pcoef = new real_DWT*[pcoef_size]; + pcoef = new real_number*[pcoef_size]; assert(pcoef); - for(integer m=0; m < pcoef_size; m++) + for(integer_number m=0; m < pcoef_size; m++) { N = (1 << m); - fq = new real_DWT [N]; + fq = new real_number [N]; assert(fq); periodize(fq, N); pcoef[m] = fq; } } -void PQMF::periodize(real_DWT *fq, integer q) +void PQMF::periodize(real_number *fq, integer_number q) { - real_DWT *pointer; + real_number *pointer; int counter; int index = ( (beg%q + q)%q ); diff --git a/lib/wave++/includes/QMF.h b/lib/wave++/source/QMF.h similarity index 50% rename from lib/wave++/includes/QMF.h rename to lib/wave++/source/QMF.h index 91deb941..238ade55 100644 --- a/lib/wave++/includes/QMF.h +++ b/lib/wave++/source/QMF.h @@ -10,27 +10,27 @@ class QMF { // base class : aperiodic quadratic mirror filter public: QMF(); // default constructor - QMF(const real_DWT *f, integer alpha, integer omega); // construct QMF + QMF(const real_number *f, integer_number alpha, integer_number omega); // construct QMF QMF(const QMF &); // copy constructor virtual ~QMF(); // destructor - virtual void Set(const real_DWT *f, integer alpha, integer omega); - integer beg; // least index of coef array - integer end; // final index of coef array - real_DWT center; // center of energy of the filter - real_DWT dev; // deviation - real_DWT *coef; // array of coefficients : from coef[beg] to coef[end] + virtual void Set(const real_number *f, integer_number alpha, integer_number omega); + integer_number beg; // least index of coef array + integer_number end; // final index of coef array + real_number center; // center of energy of the filter + real_number dev; // deviation + real_number *coef; // array of coefficients : from coef[beg] to coef[end] }; class GPQMF : public QMF { // derived class : periodized qmf public: // All even length periodizations GPQMF(); - GPQMF(const real_DWT *f, integer alpha, integer omega); + GPQMF(const real_number *f, integer_number alpha, integer_number omega); GPQMF(const GPQMF &); virtual ~GPQMF(); - virtual void Set(const real_DWT *f, integer alpha, integer omega); - void periodize(real_DWT *fq, integer q); // utilty used by Set + virtual void Set(const real_number *f, integer_number alpha, integer_number omega); + void periodize(real_number *fq, integer_number q); // utilty used by Set - real_DWT **pcoef; // periodized coefficient array + real_number **pcoef; // periodized coefficient array int pcoef_size; // number of arrays of periodized coefficients }; @@ -38,13 +38,13 @@ class GPQMF : public QMF { // derived class : periodized qmf class PQMF : public QMF { // derived class : periodized quadratic mirror filter public: // periodizations of length a power of 2 PQMF(); - PQMF(const real_DWT *f, integer alpha, integer omega); + PQMF(const real_number *f, integer_number alpha, integer_number omega); PQMF(const PQMF &); virtual ~PQMF(); - virtual void Set(const real_DWT *f, integer alpha, integer omega); - void periodize(real_DWT *fq, integer q); // utilty used by Set + virtual void Set(const real_number *f, integer_number alpha, integer_number omega); + void periodize(real_number *fq, integer_number q); // utilty used by Set - real_DWT **pcoef; // periodized coefficient array + real_number **pcoef; // periodized coefficient array int pcoef_size; // number of arrays of periodized coefficients }; diff --git a/lib/wave++/source/README.txt b/lib/wave++/source/README.txt new file mode 100644 index 00000000..970744c8 --- /dev/null +++ b/lib/wave++/source/README.txt @@ -0,0 +1,14 @@ +//README file for "wave++/source" directory. January 25, 2000. + +1) This directory ("wave++/source") contains all the source files that +are necessary to build the wave++ library. To do this just type the command + +make + +this will create the archive file (libw.a) that you will need +to link against when using wave++ in your programs. You will +also need to include libw.h in your applications. To see an +example of how this can be acomplished see the directory "wave++/demos". + + + diff --git a/lib/wave++/sources/ShiftGaborMP.cpp b/lib/wave++/source/ShiftGaborMP.cc similarity index 77% rename from lib/wave++/sources/ShiftGaborMP.cpp rename to lib/wave++/source/ShiftGaborMP.cc index 6ad4e38e..ac3901c4 100644 --- a/lib/wave++/sources/ShiftGaborMP.cpp +++ b/lib/wave++/source/ShiftGaborMP.cc @@ -1,10 +1,10 @@ -#define _USE_MATH_DEFINES + #include "ShiftGaborMP.h" #include #include -real_DWT RunShiftGaborMP(int max_iter, // maximal number of iterations - real_DWT epsilon, // desired precision | Rf | < epsilon +real_number RunShiftGaborMP(int max_iter, // maximal number of iterations + real_number epsilon, // desired precision | Rf | < epsilon const Interval &f, // signal to be approximated // assumption : f sampled on integers starting with 0 // i.e. f.beg must be 0. No constraints on f.length. @@ -14,28 +14,28 @@ real_DWT RunShiftGaborMP(int max_iter, // maximal number of iterations // f_approx is a linear combination of Gabors Interval &Rf, // final residual: Rf = f - f_approx // error which is returned equals | Rf | - std::vector &G, // std::vector of Gabors chosen for + std::vector &G, // vector of Gabors chosen for // f_approx - std::vector & Gcoef // coeficinets in the linear + std::vector & Gcoef // coeficinets in the linear // combination, corresponding to Gabors in G // f_approx = sum( Gcoef[i] * G[i] ), where number of i's // depends on epsilon and max_iter ) { - integer dim = f.length; + integer_number dim = f.length; assert( f.beg == 0 && dim > 0); Interval I(0, dim-1); for(int i=0; i + real_number &coef // ) { - integer dim = f.length; - integer c = dim / 2; // midpoint or left midpoint of the interval [0, dim-1] + integer_number dim = f.length; + integer_number c = dim / 2; // midpoint or left midpoint of the interval [0, dim-1] // depending on whether dim is odd or even - integer t, ddu; // ddu will hold (integer)du[j] - real_DWT ddv; // ddv will hold dv[j] - integer lmshift = Part.lmu - c, rmshift = Part.rmu - c; // leftmost and rightmost + integer_number t, ddu; // ddu will hold (integer)du[j] + real_number ddv; // ddv will hold dv[j] + integer_number lmshift = Part.lmu - c, rmshift = Part.rmu - c; // leftmost and rightmost // shifts with respect to the central position c - real_DWT codv, sidv, covshift, sivshift; // codv will hold cos( ddv ) + real_number codv, sidv, covshift, sivshift; // codv will hold cos( ddv ) // covshift will hold cos( v * shift ) Interval CO, SI; // CO[t] will hold cos( t * v ) - integer beg, end; + integer_number beg, end; Interval P, Q; - real_DWT a, b, phase, Pnorm2, Qnorm2, PprodQ, a1, b1, product; - real_DWT atemp, btemp, Pnorm2temp, Qnorm2temp, PprodQtemp; - real_DWT *Pptr, *Qptr, *fptr, *COptr, *SIptr; - real_DWT covlms, sivlms, covdu, sivdu, codvlms, sidvlms, codvdu, sidvdu, temp; + real_number a, b, phase, Pnorm2, Qnorm2, PprodQ, a1, b1, product; + real_number atemp, btemp, Pnorm2temp, Qnorm2temp, PprodQtemp; + real_number *Pptr, *Qptr, *fptr, *COptr, *SIptr; + real_number covlms, sivlms, covdu, sivdu, codvlms, sidvlms, codvdu, sidvdu, temp; // covlms will hold cos(v * lmshift), codvlms will hold cos(ddv * lmshift) // covdu will hold cos( v * ddu ), codvdu will hold cos(ddv * ddu) coef = 0; - for( integer j = Part.s.beg; j <= Part.s.end; j++ ) + for( integer_number j = Part.s.beg; j <= Part.s.end; j++ ) { // set up ddv = Part.dv.origin[j]; - ddu = (integer)(Part.du.origin[j]); // effectively floor + ddu = (integer_number)(Part.du.origin[j]); // effectively floor - beg = maximum(c - 4 * (integer)(Part.s.origin[j]), c - dim); - end = minimum(c + 4 * (integer)(Part.s.origin[j]), c + dim); + beg = maximum(c - 4 * (integer_number)(Part.s.origin[j]), c - dim); + end = minimum(c + 4 * (integer_number)(Part.s.origin[j]), c + dim); P.Set(beg, end); Q.Set(beg, end); CO.Set(beg, end); @@ -106,11 +106,11 @@ void getOptimalShiftGabor(const Interval &f, // signal Pptr = P.origin + beg; COptr = CO.origin + beg; SIptr = SI.origin + beg; // do first elements separately - *Pptr++ = g((real_DWT)(beg - c) / Part.s.origin[j]); + *Pptr++ = g((real_number)(beg - c) / Part.s.origin[j]); *COptr++ = cos( beg * ddv ); *SIptr++ = sin( beg * ddv ); for(t = beg + 1; t <= end; t++) { - *Pptr++ = g((real_DWT)(t - c)/Part.s.origin[j]); + *Pptr++ = g((real_number)(t - c)/Part.s.origin[j]); *COptr = *(COptr - 1) * codv - *(SIptr - 1) * sidv; *SIptr = *(COptr - 1) * sidv + *(SIptr - 1) * codv; COptr++; SIptr++; @@ -120,7 +120,7 @@ void getOptimalShiftGabor(const Interval &f, // signal covlms = covdu = 1; sivlms = sivdu = 0; // by now everything is set up for v=0 // Now do v=0 separately - for(integer shift = lmshift; shift <= rmshift; shift += ddu) + for(integer_number shift = lmshift; shift <= rmshift; shift += ddu) { // covshift = cos(v*shift); sivshift = sin(v*shift) beg = maximum(0, P.beg + shift); @@ -143,7 +143,7 @@ void getOptimalShiftGabor(const Interval &f, // signal } } // end of shift loop for case v = 0 // Now do the rest of v's - for(real_DWT v=ddv; v < Part.rmv; v += ddv) + for(real_number v=ddv; v < Part.rmv; v += ddv) { // update P, Q for this new value of v for( t = P.beg; t <= P.end; t++) @@ -161,7 +161,7 @@ void getOptimalShiftGabor(const Interval &f, // signal sivdu = temp * sidvdu + sivdu * codvdu; // initialize covshift with cos( v * lmshift ) covshift = covlms; sivshift = sivlms; - for(integer shift = lmshift; shift <= rmshift; shift += ddu) + for(integer_number shift = lmshift; shift <= rmshift; shift += ddu) { // covshift = cos(v*shift); sivshift = sin(v*shift) beg = maximum(0, P.beg + shift); @@ -204,7 +204,7 @@ void getOptimalShiftGabor(const Interval &f, // signal else // P = 0 or a1 = 0 { product = -b / sqrt(Qnorm2) ; phase = M_PI * 0.5; } } - // std::cout << "product " << product << std::endl; + // cout << "product " << product << endl; if ( absval(product) > absval(coef) ) { coef = product; diff --git a/lib/wave++/includes/ShiftGaborMP.h b/lib/wave++/source/ShiftGaborMP.h similarity index 82% rename from lib/wave++/includes/ShiftGaborMP.h rename to lib/wave++/source/ShiftGaborMP.h index 1a73f879..7efb8b3c 100644 --- a/lib/wave++/includes/ShiftGaborMP.h +++ b/lib/wave++/source/ShiftGaborMP.h @@ -11,8 +11,8 @@ // i.e. |Rf| &G, // std::vector of Gabors chosen for + std::vector &G, // vector of Gabors chosen for // f_approx - std::vector & Gcoef // coeficinets in the linear + std::vector & Gcoef // coeficinets in the linear // combination, corresponding to Gabors in G // f_approx = sum( Gcoef[i] * G[i] ), where number of i's // depends on epsilon and max_iter @@ -39,7 +39,7 @@ real_DWT RunShiftGaborMP(int max_iter, // maximal number of iterations void getOptimalShiftGabor(const Interval &f, // signal const Partition &Part, RealGabor &G, // optimal Gabor, the one closest to f - real_DWT &coef // + real_number &coef // ); diff --git a/lib/wave++/source/Utility.cc b/lib/wave++/source/Utility.cc new file mode 100644 index 00000000..06242022 --- /dev/null +++ b/lib/wave++/source/Utility.cc @@ -0,0 +1,202 @@ +//***************************************************************************** +// Utility.cc Implementations of utility functions +//***************************************************************************** + +#include +#include +#include +#include +#include "Utility.h" + +/***************************** Periodic case *********************************/ + +void ExtractHedge(HedgePer &H, const ArrayTreePer &A) +{ + assert( H.dim == A.dim ); + assert( H.maxlevel() <= A.maxlevel ); + integer_number j, shift=0; + real_number *Hptr, *Aptr; + for(integer_number i=0; i < H.num_of_levels; i++) + { + Hptr = H.block_start(i); + Aptr = A.block_start(H.levels[i], 0) + shift; + for(j=0; j < H.block_length(i); j++) Hptr[j] = Aptr[j]; + shift += H.block_length(i); + } +} + +/////////////////////////////////////////////////////////////////////////////// +void SuperposeHedge(const HedgePer &H, ArrayTreePer &A) +{ + assert( H.dim == A.dim ); + assert( H.maxlevel() <= A.maxlevel ); + integer_number j, shift=0; + real_number *Hptr, *Aptr; + for(integer_number i=0; i < H.num_of_levels; i++) + { + Hptr = H.block_start(i); + Aptr = A.block_start(H.levels[i], 0) + shift; + for(j=0; j < H.block_length(i); j++) Aptr[j] += Hptr[j] ; + shift += H.block_length(i); + } +} + +///////////////////////////////////////////////////////////////////////////// +void GetCosts(const ArrayTreePer &A, Tree &T, costFun F, const real_number &sigma) +{ + GetCostsHelp( A, &(T.root), F, sigma, 0, 0); + T.maxlevel = A.maxlevel; +} + +//////////////////////////////////////////////////////////////////////////// +void GetCostsHelp(const ArrayTreePer &A, Node **ptr, + costFun F, const real_number &sigma, + const integer_number &L, const integer_number &B ) +{ + if( L <= A.maxlevel ) + { + real_number cost = F( A.block_start(L, B), A.block_length(L), sigma, A.dim ); + (*ptr) = new Node(cost, 0, 0); + assert( *ptr ); + if( L < A.maxlevel ) + { + GetCostsHelp( A, &((*ptr)->left), F, sigma, L+1, B<<1 ); + GetCostsHelp( A, &((*ptr)->right), F, sigma, L+1, (B<<1)|1 ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +void BestBasis(HedgePer &H, const Tree &B) +{ + assert(B.root != 0); + integer_number *Levs; + Levs = new integer_number [1<<(B.maxlevel)]; // start with maximal space for levels + integer_number n = 0; + BestBasisHelp(Levs, n, B.root, 0); + H.num_of_levels = n; + H.levels = new integer_number [n]; + for (integer_number i=0; i *ptr, + const integer_number &L) +{ + assert(ptr); + if( !ptr->left && !ptr->right ) // if at the bottom row of the tree + { + Levs[n] = L; + n++; + return (ptr->content); + } + else + { + integer_number blocks = n; + real_number cost=0; + if(ptr->left) cost += BestBasisHelp(Levs, n, ptr->left, L+1); + if(ptr->right) cost += BestBasisHelp(Levs, n, ptr->right, L+1); + + if((ptr->content) > cost) return cost; + else + { + n = blocks; + Levs[n] = L; + n++; + return (ptr->content); + } + } +} + +//////////////////////////////////////////////////////////////////////////// +void ExtractBestBasis(const ArrayTreePer &A, HedgePer &H, costFun F, + const double &sigma) +{ + assert( A.origin ); + Tree B; + GetCosts(A, B, F, sigma); + H.dim = A.dim; + BestBasis(H, B); + H.origin = new real_number [H.dim]; + ExtractHedge(H, A); +} +//////////////////////////////////////////////////////////////////////////// +/***************************** Aperiodic case ********************************/ + +void ExtractHedge(HedgeAper &H, const ArrayTreeAper &A) +{ + assert( H.maxlevel() <= A.maxlevel ); + H.root[0] = *(A.block(H.levels[0], 0)); + integer_number B=0; + for(integer_number i=1; i>(H.levels[i-1]); + H.root[i] = *(A.block(H.levels[i], B)); + } +} + +//////////////////////////////////////////////////////////////////////////// +void SuperposeHedge(const HedgeAper &H, ArrayTreeAper &A) +{ + assert( H.maxlevel() <= A.maxlevel ); + *(A.block(H.levels[0], 0)) += H.root[0]; + integer_number B=0; + for(integer_number i=1; i>(H.levels[i-1]); + *(A.block(H.levels[i], B)) += H.root[i]; + } +} + +//////////////////////////////////////////////////////////////////////////// +void GetCosts(const ArrayTreeAper &A, Tree &B, costFun F, + const real_number &sigma) +{ + GetCostsHelp(A, &(B.root), F, sigma, 0, 0); + B.maxlevel = A.maxlevel; +} + +void GetCostsHelp(const ArrayTreeAper &A, Node **ptr, + costFun F, const real_number &sigma, + const integer_number &L, const integer_number &B ) +{ + if( L <= A.maxlevel ) + { + Interval *ip = A.block(L, B); + real_number cost = F( ip->origin + ip->beg, ip->length, sigma, A.root->length); + (*ptr) = new Node(cost, 0, 0); + assert( *ptr ); + if( L < A.maxlevel ) + { + GetCostsHelp(A, &((*ptr)->left), F, sigma, L+1, B<<1); + GetCostsHelp(A, &((*ptr)->right), F, sigma, L+1, (B<<1)|1); + } + } +} + +//////////////////////////////////////////////////////////////////////////// +void BestBasis(HedgeAper &H, const Tree &B) +{ + assert( B.root ); + integer_number *Levs; + Levs = new integer_number [1<<(B.maxlevel)]; // start with maximal space for levels + integer_number n=0; + BestBasisHelp(Levs, n, B.root, 0); + H.num_of_levels = n; + H.levels = new integer_number [n]; + for (integer_number i=0; i **ptr, - costFun F, const real_DWT &sigma, - const integer &L, const integer &B ); + +void GetCostsHelp(const ArrayTreePer &A, Node **ptr, + costFun F, const real_number &sigma, + const integer_number &L, const integer_number &B ); // utility -void GetCostsHelp(const ArrayTreePer &A, Node **ptr, - costFunAdv F, const real_DWT &sigma, const real_DWT &factor, - const integer &L, const integer &B ); - // utility - // advanced function to integrate the cost function used in "demoWavPack.cpp" - // implemented by Johannes Troppacher - void BestBasis(HedgePer &H, const Tree &B); // get cost minimizing basis in bin tree B // assumption: on input H is default empty hedge // on output H.num_of_levels and H.levels are completely defined // but H.origin is still 0 -real_DWT BestBasisHelp(integer *Levs, integer &n, Node *ptr, - const integer &L); +real_number BestBasisHelp(integer_number *Levs, integer_number &n, Node *ptr, + const integer_number &L); // utility void ExtractBestBasis(const ArrayTreePer &A, HedgePer &H, costFun F, @@ -70,20 +54,6 @@ void ExtractBestBasis(const ArrayTreePer &A, HedgePer &H, costFun F, // find best basis which minimizes cost. On input H is an empty hedge, // on output H completely filled up and contains the best basis -void ExtractBestBasis(const ArrayTreePer &A, HedgePer &H, const double &sigma, const double &factor); - // wrapping function: start with array bin tree A, calculate costs - // using F and sigma. Temporarily store costs in a bin tree and then - // find best basis which minimizes cost. On input H is an empty hedge, - // on output H completely filled up and contains the best basis - // advanced function to integrate the cost function used in "demoWavPack.cpp" - // implemented by Johannes Troppacher - -real_DWT OracCostAdv(const real_DWT *data, const integer &n, - const real_DWT &sigma, const real_DWT &factor, const integer &k); - // advanced function to integrate the cost function used in "demoWavPack.cpp" - // and former defined in "demoTools.cpp" - // implemented by Johannes Troppacher - /*************************** Aperiodic case **********************************/ void ExtractHedge(HedgeAper &H, const ArrayTreeAper &A); @@ -97,14 +67,14 @@ void SuperposeHedge(const HedgeAper &H, ArrayTreeAper &A); // assumption: both H and A previously completely defined. void GetCosts(const ArrayTreeAper &A, Tree &B, costFun F, - const real_DWT &sigma); + const real_number &sigma); // using functional F calculate costs from a given array bin tree A // assumption : on input B is empty tree // on output B filled up with costs -void GetCostsHelp(const ArrayTreeAper &A, Node **ptr, - costFun F, const real_DWT &sigma, - const integer &L, const integer &B ); +void GetCostsHelp(const ArrayTreeAper &A, Node **ptr, + costFun F, const real_number &sigma, + const integer_number &L, const integer_number &B ); // utility void BestBasis(HedgeAper &H, const Tree &B); diff --git a/lib/wave++/sources/WavePacket.cpp b/lib/wave++/source/WavePacket.cc similarity index 92% rename from lib/wave++/sources/WavePacket.cpp rename to lib/wave++/source/WavePacket.cc index 5e24e35c..17140b7f 100644 --- a/lib/wave++/sources/WavePacket.cpp +++ b/lib/wave++/source/WavePacket.cc @@ -13,8 +13,8 @@ void Analysis(const Interval &In, ArrayTreePer &A, { assert(In.length == A.dim && A.origin); assert( (1<<(A.maxlevel)) == A.dim ); // check pow of 2 case - for(integer i=0; i=0; L--) // go through all levels starting { // from maxlevel-1 up to 0 for(B=0; B < (1<=0; L--) // go through all levels starting { // from maxlevel-1 up to 0 for(B=0; B < (1<=0; L--) // go through all levels from P-1 up to 0 { for(B=0; B < (1<= In.end); assert( is_pow_of_2(In.length) ); - real_DWT *in=In.origin, *out=Out.origin, *work=Work.origin; - integer k=In.length; + real_number *in=In.origin, *out=Out.origin, *work=Work.origin; + integer_number k=In.length; while( k > 1) { con_dec(in, out + k/2, k, G); @@ -52,11 +52,11 @@ void InvWaveTrans(const Interval &In, Interval &Out, Interval &Work, { assert( In.beg == 0 && Out.beg <= 0 && Out.end >= In.end); assert( is_pow_of_2(In.length) ); - real_DWT *in=In.origin, *out=Out.origin, *work=Work.origin; - integer n=In.length; + real_number *in=In.origin, *out=Out.origin, *work=Work.origin; + integer_number n=In.length; if( n > 1 ) { - integer k=1; + integer_number k=1; work += k; work[0] = in[0]; in += 1; @@ -76,7 +76,7 @@ void InvWaveTrans(const Interval &In, Interval &Out, Interval &Work, /****************************** Periodic case m*2^L *************************/ -void WaveTrans(const Interval &In, Interval &Out, integer L, +void WaveTrans(const Interval &In, Interval &Out, integer_number L, const GPQMF &H, const GPQMF &G, cdgpType con_dec) { Interval Work(0, In.beg); // all elemnts of Work are 0's @@ -84,13 +84,13 @@ void WaveTrans(const Interval &In, Interval &Out, integer L, } /////////////////////////////////////////////////////////////////////////////// -void WaveTrans(const Interval &In, Interval &Out, Interval &Work, integer L, +void WaveTrans(const Interval &In, Interval &Out, Interval &Work, integer_number L, const GPQMF &H, const GPQMF &G, cdgpType con_dec) { - integer k=In.length; + integer_number k=In.length; assert( In.beg == 0 && Out.beg <= 0 && Out.end >= In.end); assert( ((k>>L)< 0 ) { con_dec(in, out + k/2, k, G); @@ -102,7 +102,7 @@ void WaveTrans(const Interval &In, Interval &Out, Interval &Work, integer L, } ////////////////////////////////////////////////////////////////////////////// -void InvWaveTrans(const Interval &In, Interval &Out, integer L, +void InvWaveTrans(const Interval &In, Interval &Out, integer_number L, const GPQMF &H, const GPQMF &G, cdgpType adj_con_dec) { Interval Work(0, In.end); @@ -110,19 +110,19 @@ void InvWaveTrans(const Interval &In, Interval &Out, integer L, } ////////////////////////////////////////////////////////////////////////////// -void InvWaveTrans(const Interval &In, Interval &Out, Interval &Work, integer L, +void InvWaveTrans(const Interval &In, Interval &Out, Interval &Work, integer_number L, const GPQMF &H, const GPQMF &G, cdgpType adj_con_dec) { assert(L>0); - integer k, n=In.length; + integer_number k, n=In.length; assert( In.beg == 0 && Out.beg <= 0 && Out.end >= In.end); assert( ((n>>L)< 0 ) { k = n>>L; work += k; - for(integer j=0; j0); @@ -151,12 +151,12 @@ void WaveTrans(const Interval &In, Interval *Out, integer L, /////////////////////////////////////////////////////////////////////////////// void WaveTrans(const Interval &In, Interval *Out, Interval *Work, - integer L, const QMF &H, const QMF &G, cdaType con_dec) + integer_number L, const QMF &H, const QMF &G, cdaType con_dec) { assert(L > 0); Interval *Temp; Temp = const_cast (&In); - for(integer k=L-1; k>=0; k--) + for(integer_number k=L-1; k>=0; k--) { Work[k].Set(ICH((Temp->beg)+H.beg), IFH((Temp->end)+H.end)); // all 0's Out[k+1].Set(ICH((Temp->beg)+G.beg), IFH((Temp->end)+G.end)); // all 0's @@ -168,7 +168,7 @@ void WaveTrans(const Interval &In, Interval *Out, Interval *Work, } /////////////////////////////////////////////////////////////////////////////// -void InvWaveTrans(const Interval *In, Interval &Out, integer L, +void InvWaveTrans(const Interval *In, Interval &Out, integer_number L, const QMF &H, const QMF &G, cdaType adj_con_dec) { assert(L>0); @@ -179,12 +179,12 @@ void InvWaveTrans(const Interval *In, Interval &Out, integer L, /////////////////////////////////////////////////////////////////////////////// void InvWaveTrans(const Interval *In, Interval &Out, Interval *Work, - integer L, const QMF &H, const QMF &G, cdaType adj_con_dec) + integer_number L, const QMF &H, const QMF &G, cdaType adj_con_dec) { assert(L>0); Work[0] = In[0]; - integer alpha, omega; - for(integer k=1; k #include -using namespace std; - -integer Log2(integer r) +integer_number Log2(integer_number r) { - integer temp=0; + integer_number temp=0; r >>= 1; while(r > 0) { @@ -17,21 +15,21 @@ integer Log2(integer r) return temp; } ////////////////////////////////////////////////////////////////////////////// -int is_pow_of_2( integer r ) +bool is_pow_of_2( integer_number r ) { return ( ((r>>Log2(r))<> x; diff --git a/lib/wave++/includes/common.h b/lib/wave++/source/common.h similarity index 65% rename from lib/wave++/includes/common.h rename to lib/wave++/source/common.h index 4bcf5bf0..eae49cc1 100644 --- a/lib/wave++/includes/common.h +++ b/lib/wave++/source/common.h @@ -5,8 +5,8 @@ #ifndef COMMON_H #define COMMON_H -typedef double real_DWT; -typedef long integer; +using real_number = double; +using integer_number = long; #define maximum(x,y) ((x)>(y) ? (x):(y)) #define minimum(x,y) ((x)<(y) ? (x):(y)) @@ -17,10 +17,9 @@ typedef long integer; #define LEAST(I,F) (2*(I.beg) - (F.beg) - 1) // least after adjoint conv-dec #define FINAL(I,F) (2*(I.end) - (F.end) + 1) // final after adjoint conv-dec -integer Log2( integer r ); // log base 2 of integer r -int is_pow_of_2( integer r ); // check if r is power of 2 - -integer NumOfData(const char *filename); - // count total=number of real_DWTs in file, return total +integer_number Log2( integer_number r ); // log base 2 of integer r +bool is_pow_of_2( integer_number r ); // check if r is power of 2 +integer_number NumOfData(const char *filename); + // count total=number of reals in file, return total #endif diff --git a/lib/wave++/sources/fft.cpp b/lib/wave++/source/fft.c similarity index 80% rename from lib/wave++/sources/fft.cpp rename to lib/wave++/source/fft.c index 243bf50c..4f56084e 100644 --- a/lib/wave++/sources/fft.cpp +++ b/lib/wave++/source/fft.c @@ -1,14 +1,12 @@ -#define _USE_MATH_DEFINES -#include "fft.h" #include #include #include -#include +#include "fft.h" -#define fftbitrev(out,in,q) bitrevd(out,in,q,sizeof(complex_DWT)) -#define fftbrinpl(x,q) bitrevi(x,q,sizeof(complex_DWT)) +#define fftbitrev(out,in,q) bitrevd(out,in,q,sizeof(complex_number)) +#define fftbrinpl(x,q) bitrevi(x,q,sizeof(complex_number)) -extern /*"C"*/ int +extern "C" int br( int n, /* Nonnegative integer to bit-reverse. */ int log2len) /* Reverse this many bits. */ @@ -28,7 +26,7 @@ extern /*"C"*/ int return(u); } -extern /*"C"*/ void +extern "C" void bitrevd( void *out, /* Pointer to base of the output array. */ const void *in, /* Pointer to base of the input array. */ @@ -45,12 +43,12 @@ extern /*"C"*/ void for(n=0; n<(1< n ) { - xu = (char*)x+u*size; - xn = (char*)x+n*size; + xu = x+u*size; + xn = x+n*size; memcpy( temp, xu, size ); memcpy( xu, xn, size ); memcpy( xn, temp, size ); @@ -80,15 +78,15 @@ extern /*"C"*/ void return; } -extern /*"C"*/ void +extern "C" void fftproduct( /* Apply sparse matrix product. */ - complex_DWT *f, /* Input and output vector. */ + complex_number *f, /* Input and output vector. */ int q, /* Length of `f[]' is N=1<=0; k--) @@ -144,32 +142,32 @@ extern /*"C"*/ void return; } -extern /*"C"*/ complex_DWT * +extern "C" complex_number * fftomega( /* Return exp(-M_PI*i*n/M), */ int M) /* for n=0,1,2,...,|M|-1. */ { - complex_DWT *W; + complex_number *W; double factor, theta; int n; factor = -M_PI/(double)M; M = abs(M); - W = (complex_DWT *)malloc(M*sizeof(complex_DWT)); + W = (complex_number *)malloc(M*sizeof(complex_number)); theta = 0.0; for(n=0; n -/*#define M_SQRT2 (1.41421356237309504880) /* sqrt(2.0) */ +/*#define M_SQRT2 (1.4142135623730950488) *//* sqrt(2.0) */ #define SR3 (1.7320508075688772935) /* sqrt(3.0) */ #define SR10 (3.1622776601683793320) /* sqrt(10.0) */ #define SR15 (3.8729833462074168852) /* sqrt(15.0) */ @@ -26,7 +25,7 @@ /******************* Beylkin 18 ********************/ -static real_DWT +static real_number b18soqf[18] = { 9.93057653743539270E-02, 4.24215360812961410E-01, @@ -51,7 +50,7 @@ static int b18salpha = 0, b18somega = 17; -static real_DWT +static real_number b18doqf[18] = { 6.40485328521245350E-04, 2.73603162625860610E-03, @@ -80,7 +79,7 @@ static int /******************* Coifman 6 ********************/ -static real_DWT +static real_number c06soqf[6] = { ((SR15-3.0)/32.0)*M_SQRT2, /* 3.85807777478867490E-02, */ ((1.0-SR15)/32.0)*M_SQRT2, /* -1.26969125396205200E-01, */ @@ -93,7 +92,7 @@ static int c06salpha = 0, c06somega = 5; -static real_DWT +static real_number c06doqf[6] = { ((9.0-SR15)/32.0)*M_SQRT2, /* 2.26584265197068560E-01, */ (-(SR15+13.0)/32.0)*M_SQRT2, /* -7.45687558934434280E-01, */ @@ -108,7 +107,7 @@ static int /******************* Coifman 12 ********************/ -static real_DWT +static real_number c12soqf[12] = { 1.63873364631797850E-02, -4.14649367819664850E-02, @@ -127,7 +126,7 @@ static int c12salpha = 0, c12somega = 11; -static real_DWT +static real_number c12doqf[12] = { -7.20549445368115120E-04, 1.82320887091009920E-03, @@ -148,7 +147,7 @@ static int /******************* Coifman 18 ********************/ -static real_DWT +static real_number c18soqf[18] = { -3.79351286437787590E-03, 7.78259642567078690E-03, @@ -173,7 +172,7 @@ static int c18salpha = 0, c18somega = 17; -static real_DWT +static real_number c18doqf[18] = { -3.45997731974026950E-05, 7.09833025057049280E-05, @@ -200,7 +199,7 @@ static int /******************* Coifman 24 ********************/ -static real_DWT +static real_number c24soqf[24] = { 8.92313668220275710E-04, -1.62949201311084900E-03, @@ -231,7 +230,7 @@ static int c24salpha = 0, c24somega = 23; -static real_DWT +static real_number c24doqf[24] = { -1.78498455869993380E-06, 3.25968044485761290E-06, @@ -264,7 +263,7 @@ static int /******************* Coifman 30 ********************/ -static real_DWT +static real_number c30soqf[30] = { -2.12080863336306810E-04, 3.58589677255698600E-04, @@ -301,7 +300,7 @@ static int c30salpha = 0, c30somega = 29; -static real_DWT +static real_number c30doqf[30] = { -9.51579170468293560E-08, 1.67408293749300630E-07, @@ -339,7 +338,7 @@ static int c30domega = 29; /******************* Daubechies 2 ********************/ -static real_DWT +static real_number d02soqf[2] = { (0.5)*M_SQRT2, /* 0.707106781186547, */ (0.5)*M_SQRT2, /* 0.707106781186547 */ @@ -348,7 +347,7 @@ static int d02salpha = 0, d02somega = 1; -static real_DWT +static real_number d02doqf[2] = { (0.5)*M_SQRT2, /* 0.707106781186547, */ (-0.5)*M_SQRT2, /* -0.707106781186547 */ @@ -359,7 +358,7 @@ static int /******************* Daubechies 4 ********************/ -static real_DWT +static real_number d04soqf[4] = { (1.0+SR3)/(4.0*M_SQRT2), /* 4.82962913144534160E-01, */ (3.0+SR3)/(4.0*M_SQRT2), /* 8.36516303737807940E-01, */ @@ -370,7 +369,7 @@ static int d04salpha = 0, d04somega = 3; -static real_DWT +static real_number d04doqf[4] = { (1.0-SR3)/(4.0*M_SQRT2), /* -1.29409522551260370E-01, */ (SR3-3.0)/(4.0*M_SQRT2), /* -2.24143868042013390E-01, */ @@ -383,7 +382,7 @@ static int /******************* Daubechies 6 ********************/ -static real_DWT +static real_number d06soqf[6] = { 0.125*A, /* 3.32670552950082630E-01, */ 0.125*(M_SQRT2+2.0*A-B), /* 8.06891509311092550E-01, */ @@ -396,7 +395,7 @@ static int d06salpha = 0, d06somega = 5; -static real_DWT +static real_number d06doqf[6] = { 0.125*B, /* 3.52262918857095330E-02, */ 0.125*(A-M_SQRT2-2.0*B), /* 8.54412738820266580E-02, */ @@ -411,7 +410,7 @@ static int /******************* Daubechies 8 ********************/ -static real_DWT +static real_number d08soqf[8] = { 2.30377813309000010E-01, 7.14846570553000050E-01, /* Maximum absolute value */ @@ -426,7 +425,7 @@ static int d08salpha = 0, d08somega = 7; -static real_DWT +static real_number d08doqf[8] = { -1.05974017850000000E-02, -3.28830116670000010E-02, @@ -443,7 +442,7 @@ static int /******************* Daubechies 10 ********************/ -static real_DWT +static real_number d10soqf[10] = { 1.60102397974000000E-01, 6.03829269797000020E-01, @@ -460,7 +459,7 @@ static int d10salpha = 0, d10somega = 9; -static real_DWT +static real_number d10doqf[10] = { 3.33572528500000010E-03, 1.25807519990000000E-02, @@ -479,7 +478,7 @@ static int /******************* Daubechies 12 ********************/ -static real_DWT +static real_number d12soqf[12] = { 1.11540743350000000E-01, 4.94623890397999980E-01, @@ -498,7 +497,7 @@ static int d12salpha = 0, d12somega = 11; -static real_DWT +static real_number d12doqf[12] = { -1.07730108500000000E-03, -4.77725751100000020E-03, @@ -519,7 +518,7 @@ static int /******************* Daubechies 14 ********************/ -static real_DWT +static real_number d14soqf[14] = { 7.78520540849999970E-02, 3.96539319482000000E-01, @@ -540,7 +539,7 @@ static int d14salpha = 0, d14somega = 13; -static real_DWT +static real_number d14doqf[14] = { 3.53713800000000020E-04, 1.80164070400000000E-03, @@ -563,7 +562,7 @@ static int /******************* Daubechies 16 ********************/ -static real_DWT +static real_number d16soqf[16] = { 5.44158422430000010E-02, 3.12871590914000020E-01, @@ -586,7 +585,7 @@ static int d16salpha = 0, d16somega = 15; -static real_DWT +static real_number d16doqf[16] = { -1.17476784000000000E-04, -6.75449405999999950E-04, @@ -611,7 +610,7 @@ static int /******************* Daubechies 18 ********************/ -static real_DWT +static real_number d18soqf[18] = { 3.80779473639999980E-02, 2.43834674613000010E-01, @@ -636,7 +635,7 @@ static int d18salpha = 0, d18somega = 17; -static real_DWT +static real_number d18doqf[18] = { 3.93473200000000030E-05, 2.51963189000000020E-04, @@ -663,7 +662,7 @@ static int /******************* Daubechies 20 ********************/ -static real_DWT +static real_number d20soqf[20] = { 2.66700579010000010E-02, 1.88176800078000000E-01, @@ -690,7 +689,7 @@ static int d20salpha = 0, d20somega = 19; -static real_DWT +static real_number d20doqf[20] = { -1.32642030000000010E-05, -9.35886700000000050E-05, @@ -719,7 +718,7 @@ static int /******************* Vaidyanathan 24 ********************/ -static real_DWT +static real_number v24soqf[24] = { -6.29061181907475230E-05, 3.43631904821029190E-04, @@ -750,7 +749,7 @@ static int v24salpha = 0, v24somega = 23; -static real_DWT +static real_number v24doqf[24] = { 4.57993341109767180E-02, -2.50184129504662180E-01, @@ -782,12 +781,11 @@ static int v24domega = 23; -/*#undef M_SQRT2 */ +#undef M_SQRT2 #undef SR3 #undef SR10 #undef SR15 #undef A #undef B -#undef _USE_MATH_DEFINES #endif diff --git a/lib/wave++/sources/Utility.cpp b/lib/wave++/sources/Utility.cpp deleted file mode 100644 index 33ab3b7c..00000000 --- a/lib/wave++/sources/Utility.cpp +++ /dev/null @@ -1,253 +0,0 @@ -//***************************************************************************** -// Utility.cc Implementations of utility functions -//***************************************************************************** - -#include -#include -#include -#include -#include "Utility.h" - -/***************************** Periodic case *********************************/ - -void ExtractHedge(HedgePer &H, const ArrayTreePer &A) -{ - assert( H.dim == A.dim ); - assert( H.maxlevel() <= A.maxlevel ); - integer j, shift=0; - real_DWT *Hptr, *Aptr; - for(integer i=0; i < H.num_of_levels; i++) - { - Hptr = H.block_start(i); - Aptr = A.block_start(H.levels[i], 0) + shift; - for(j=0; j < H.block_length(i); j++) Hptr[j] = Aptr[j]; - shift += H.block_length(i); - } -} - -/////////////////////////////////////////////////////////////////////////////// -void SuperposeHedge(const HedgePer &H, ArrayTreePer &A) -{ - assert( H.dim == A.dim ); - assert( H.maxlevel() <= A.maxlevel ); - integer j, shift=0; - real_DWT *Hptr, *Aptr; - for(integer i=0; i < H.num_of_levels; i++) - { - Hptr = H.block_start(i); - Aptr = A.block_start(H.levels[i], 0) + shift; - for(j=0; j < H.block_length(i); j++) Aptr[j] += Hptr[j] ; - shift += H.block_length(i); - } -} - -///////////////////////////////////////////////////////////////////////////// -void GetCosts(const ArrayTreePer &A, Tree &T, costFun F, const real_DWT &sigma) -{ - GetCostsHelp( A, &(T.root), F, sigma, 0, 0); - T.maxlevel = A.maxlevel; -} - -///////////////////////////////////////////////////////////////////////////// -void GetCosts(const ArrayTreePer &A, Tree &T, costFunAdv F, const real_DWT &sigma, const real_DWT &factor) -{ - GetCostsHelp( A, &(T.root), F, sigma, factor, 0, 0); - T.maxlevel = A.maxlevel; -} - -//////////////////////////////////////////////////////////////////////////// -void GetCostsHelp(const ArrayTreePer &A, Node **ptr, - costFun F, const real_DWT &sigma, - const integer &L, const integer &B ) -{ - if( L <= A.maxlevel ) - { - real_DWT cost = F( A.block_start(L, B), A.block_length(L), sigma, A.dim ); - (*ptr) = new Node(cost, 0, 0); - assert( *ptr ); - if( L < A.maxlevel ) - { - GetCostsHelp( A, &((*ptr)->left), F, sigma, L+1, B<<1 ); - GetCostsHelp( A, &((*ptr)->right), F, sigma, L+1, (B<<1)|1 ); - } - } -} - -//////////////////////////////////////////////////////////////////////////// -void GetCostsHelp(const ArrayTreePer &A, Node **ptr, - costFunAdv F, const real_DWT &sigma, const real_DWT &factor, - const integer &L, const integer &B ) -{ - if( L <= A.maxlevel ) - { - real_DWT cost = F( A.block_start(L, B), A.block_length(L), sigma, factor, A.dim ); - (*ptr) = new Node(cost, 0, 0); - assert( *ptr ); - if( L < A.maxlevel ) - { - GetCostsHelp( A, &((*ptr)->left), F, sigma, factor, L+1, B<<1 ); - GetCostsHelp( A, &((*ptr)->right), F, sigma, factor, L+1, (B<<1)|1 ); - } - } -} - -real_DWT OracCostAdv(const real_DWT *data, const integer &n, const real_DWT &sigma, const real_DWT &factor,const integer &k) -{ - // real_DWT factor= (1.0+sqrt((double)2*log((double)mDWT_maxLevel*mResolution)));//D&J Best Wavelet (BWB) - real_DWT cost=0; - real_DWT var=sigma*sigma; - real_DWT temp; - - for(int i=0;i=var*factor*factor) cost += var; - else cost += temp; - } - return cost; -} -/////////////////////////////////////////////////////////////////////////////// -void BestBasis(HedgePer &H, const Tree &B) -{ - assert(B.root != 0); - integer *Levs; - Levs = new integer [1<<(B.maxlevel)]; // start with maximal space for levels - integer n = 0; - BestBasisHelp(Levs, n, B.root, 0); - H.num_of_levels = n; - H.levels = new integer [n]; - for (integer i=0; i *ptr, - const integer &L) -{ - assert(ptr); - if( !ptr->left && !ptr->right ) // if at the bottom row of the tree - { - Levs[n] = L; - n++; - return (ptr->content); - } - else - { - integer blocks = n; - real_DWT cost=0; - if(ptr->left) cost += BestBasisHelp(Levs, n, ptr->left, L+1); - if(ptr->right) cost += BestBasisHelp(Levs, n, ptr->right, L+1); - - if((ptr->content) > cost) return cost; - else - { - n = blocks; - Levs[n] = L; - n++; - return (ptr->content); - } - } -} - -//////////////////////////////////////////////////////////////////////////// -void ExtractBestBasis(const ArrayTreePer &A, HedgePer &H, costFun F, - const double &sigma) -{ - assert( A.origin ); - Tree B; - GetCosts(A, B, F, sigma); - H.dim = A.dim; - BestBasis(H, B); - H.origin = new real_DWT [H.dim]; - ExtractHedge(H, A); -} -//////////////////////////////////////////////////////////////////////////// -void ExtractBestBasis(const ArrayTreePer &A, HedgePer &H, const double &sigma, const double &factor) -{ - assert( A.origin ); - Tree B; - GetCosts(A, B, OracCostAdv, sigma, factor); - H.dim = A.dim; - BestBasis(H, B); - H.origin = new real_DWT [H.dim]; - ExtractHedge(H, A); -} -//////////////////////////////////////////////////////////////////////////// -/***************************** Aperiodic case ********************************/ - -void ExtractHedge(HedgeAper &H, const ArrayTreeAper &A) -{ - assert( H.maxlevel() <= A.maxlevel ); - H.root[0] = *(A.block(H.levels[0], 0)); - integer B=0; - for(integer i=1; i>(H.levels[i-1]); - H.root[i] = *(A.block(H.levels[i], B)); - } -} - -//////////////////////////////////////////////////////////////////////////// -void SuperposeHedge(const HedgeAper &H, ArrayTreeAper &A) -{ - assert( H.maxlevel() <= A.maxlevel ); - *(A.block(H.levels[0], 0)) += H.root[0]; - integer B=0; - for(integer i=1; i>(H.levels[i-1]); - *(A.block(H.levels[i], B)) += H.root[i]; - } -} - -//////////////////////////////////////////////////////////////////////////// -void GetCosts(const ArrayTreeAper &A, Tree &B, costFun F, - const real_DWT &sigma) -{ - GetCostsHelp(A, &(B.root), F, sigma, 0, 0); - B.maxlevel = A.maxlevel; -} - -void GetCostsHelp(const ArrayTreeAper &A, Node **ptr, - costFun F, const real_DWT &sigma, - const integer &L, const integer &B ) -{ - if( L <= A.maxlevel ) - { - Interval *ip = A.block(L, B); - real_DWT cost = F( ip->origin + ip->beg, ip->length, sigma, A.root->length); - (*ptr) = new Node(cost, 0, 0); - assert( *ptr ); - if( L < A.maxlevel ) - { - GetCostsHelp(A, &((*ptr)->left), F, sigma, L+1, B<<1); - GetCostsHelp(A, &((*ptr)->right), F, sigma, L+1, (B<<1)|1); - } - } -} - -//////////////////////////////////////////////////////////////////////////// -void BestBasis(HedgeAper &H, const Tree &B) -{ - assert( B.root ); - integer *Levs; - Levs = new integer [1<<(B.maxlevel)]; // start with maximal space for levels - integer n=0; - BestBasisHelp(Levs, n, B.root, 0); - H.num_of_levels = n; - H.levels = new integer [n]; - for (integer i=0; i -#include - - -void SimpleEQLookAndFeel::drawRotarySlider( - Graphics &graphics, - int x, - int y, - int width, - int height, - float sliderPosProportional, - float rotaryStartAngle, - float rotaryEndAngle, - Slider &slider) { - - jassert(rotaryStartAngle < rotaryEndAngle); - - using namespace juce; - - auto bounds = Rectangle(static_cast(x), static_cast(y), static_cast(width), static_cast(height)); - auto enabled = slider.isEnabled(); - - graphics.setColour(enabled ? Colour(18, 10, 90) : Colours::darkgrey); - graphics.fillEllipse(bounds); - - graphics.setColour(enabled ? Colour(95, 75, 255) : Colours::grey); - graphics.drawEllipse(bounds, 2.F); - - if (auto *rotarySliderWithLabels = dynamic_cast(&slider)) { - auto center = bounds.getCentre(); - - Path path; - - auto sliderTextHeight = static_cast(RotarySlidersWithLabels::getTextHeight()); - - Rectangle rectangle; - rectangle.setLeft(center.getX() - 2); - rectangle.setRight(center.getX() + 2); - rectangle.setTop(bounds.getY()); - rectangle.setBottom(center.toFloat().getY() - sliderTextHeight * 1.5F); - - path.addRoundedRectangle(rectangle, 2.F); - path.addRectangle(rectangle); - - auto sliderAngleRadiants = jmap(sliderPosProportional, 0.F, 1.F, rotaryStartAngle, rotaryEndAngle); - - path.applyTransform(AffineTransform().rotated(sliderAngleRadiants, center.getX(), center.getY())); - - graphics.fillPath(path); - - graphics.setFont(sliderTextHeight); - auto text = rotarySliderWithLabels->getDisplayString(); - auto stringWidth = static_cast(graphics.getCurrentFont().getStringWidth(text)); - - rectangle.setSize(stringWidth + 4, sliderTextHeight + 2); - rectangle.setCentre(bounds.getCentre()); - - graphics.setColour(enabled ? Colours::black : Colours::darkgrey); - graphics.fillRect(rectangle); - - graphics.setColour(enabled ? Colours::white : Colours::lightgrey); - graphics.drawFittedText(text, rectangle.toNearestInt(), Justification::centred, 1); - } -} - -void SimpleEQLookAndFeel::drawToggleButton(juce::Graphics &graphics, juce::ToggleButton &toggleButton, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) { - using namespace juce; - - if (auto *powerButton = dynamic_cast(&toggleButton)) { - Path powerButtonPath; - - auto bounds = toggleButton.getLocalBounds(); - auto size = juce::jmin(bounds.getWidth(), bounds.getHeight()) - 6; - auto rectangle = bounds.withSizeKeepingCentre(size, size).toFloat(); - - float const angle = 30.0F; - - size -= 6; - - powerButtonPath.addCentredArc( - rectangle.getCentreX(), - rectangle.getCentreY(), - static_cast(size * 0.5), - static_cast(size * 0.5), - 0.F, - degreesToRadians(angle), - degreesToRadians(360.0F - angle), - true); - powerButtonPath.startNewSubPath(rectangle.getCentreX(), rectangle.getY()); - powerButtonPath.lineTo(rectangle.getCentre()); - - PathStrokeType curvedStrokeType(2.0F, PathStrokeType::JointStyle::curved); - auto color = toggleButton.getToggleState() ? Colours::dimgrey : Colour(95, 75, 255); - graphics.setColour(color); - graphics.strokePath(powerButtonPath, curvedStrokeType); - graphics.drawEllipse(rectangle, 2.0F); - } else if (auto *analyzerButton = dynamic_cast(&toggleButton)) { - auto color = !toggleButton.getToggleState() ? Colours::dimgrey : Colour(95, 75, 255); - graphics.setColour(color); - - auto bounds = toggleButton.getLocalBounds(); - graphics.drawRect(bounds); - - graphics.strokePath(analyzerButton->randomPath, PathStrokeType(1.0F)); - } -} - -//============================================================================== - -void RotarySlidersWithLabels::paint(juce::Graphics &graphics) { - using namespace juce; - - auto startAngle = degreesToRadians(180.0f + 45.0f); - auto endAngle = degreesToRadians(180.0f - 45.0f) + MathConstants::twoPi; - - auto range = getRange(); - - auto sliderBounds = getSliderBounds(); - - // Helper rectangles for layout design purposes: - // graphics.setColour(Colours::red); - // graphics.drawRect(getLocalBounds()); - // graphics.setColour(Colours::yellow); - // graphics.drawRect(sliderBounds); - - auto sliderPosProportional = static_cast(jmap(getValue(), range.getStart(), range.getEnd(), 0.0, 1.0)); - getLookAndFeel().drawRotarySlider( - graphics, - sliderBounds.getX(), - sliderBounds.getY(), - sliderBounds.getWidth(), - sliderBounds.getHeight(), - sliderPosProportional, - startAngle, - endAngle, - *this); - - auto center = sliderBounds.toFloat().getCentre(); - auto radius = sliderBounds.toFloat().getWidth() * 0.5F; - auto textHeight = static_cast(getTextHeight()); - - graphics.setColour(Colour(95, 75, 255)); - graphics.setFont(textHeight); - - auto numberOfChoices = labels.size(); - for (int i = 0; i < numberOfChoices; ++i) { - auto position = labels[i].position; - jassert(0.0F <= position); - jassert(1.0F >= position); - - auto angle = jmap(position, 0.0F, 1.0F, startAngle, endAngle); - - auto labelCenter = center.getPointOnCircumference(radius + textHeight * 0.5F + 1, angle); - - Rectangle labelRectangle; - auto string = labels[i].label; - labelRectangle.setSize(static_cast(graphics.getCurrentFont().getStringWidth(string)), textHeight); - labelRectangle.setCentre(labelCenter); - labelRectangle.setY(labelRectangle.getY() + textHeight); - - graphics.drawFittedText(string, labelRectangle.toNearestInt(), Justification::centred, 1); - } -} - -juce::Rectangle RotarySlidersWithLabels::getSliderBounds() const { - auto bounds = getLocalBounds(); - - auto size = juce::jmin(bounds.getWidth(), bounds.getHeight()); - size -= getTextHeight() * 2; - - juce::Rectangle rectangle; - rectangle.setSize(size, size); - rectangle.setCentre(bounds.getCentreX(), 0); - rectangle.setY(2); - - return rectangle; -} - -juce::String RotarySlidersWithLabels::getDisplayString() const { - - if (auto *choiceParameter = dynamic_cast(parameter)) { - return choiceParameter->getCurrentChoiceName(); - } - - juce::String string; - bool inKiloHertz = false; - - if (auto *choiceParameter = dynamic_cast(parameter)) { - auto value = getValue(); - - if (value > 999.0f) { - value /= 1000.0f; - inKiloHertz = true; - } - - string = juce::String(value, inKiloHertz ? 2 : 0); - } else { - jassertfalse; - } - - if (unitSuffix.isNotEmpty()) { - string << " "; - if (inKiloHertz) { - string << "k"; - } - string << unitSuffix; - } - - return string; -} - -//============================================================================== - -ResponseCurveComponent::ResponseCurveComponent(SimpleEQAudioProcessor &referencedAudioProcessor) : audioProcessor(referencedAudioProcessor), - leftChannelPathProducer(referencedAudioProcessor.leftChannelQueue), - rightChannelPathProducer(referencedAudioProcessor.rightChannelQueue) { - const auto ¶meters = referencedAudioProcessor.getParameters(); - for (auto *parameter : parameters) { - parameter->addListener(this); - } - - updateChain(); - startTimerHz(60); -} - -ResponseCurveComponent::~ResponseCurveComponent() -{ - const auto& parameters = audioProcessor.getParameters(); - for (auto *parameter : parameters) { - parameter->removeListener(this); - } -} - -void ResponseCurveComponent::parameterValueChanged(int, float) { - parametersChanged.set(true); -} - -void PathProducer::process(juce::Rectangle fftBounds, double sampleRate) { - juce::AudioBuffer tempIncomingBuffer; - const auto lowestLevelToAnalyzeInDB = -48.0F; - - while (leftChannelQueue->getNumCompleteBuffersAvailable() > 0) { - if (leftChannelQueue->getAudioBuffer(tempIncomingBuffer)) { - auto size = tempIncomingBuffer.getNumSamples(); - auto sizeToMove = monoBuffer.getNumSamples() - size; - - juce::FloatVectorOperations::copy(monoBuffer.getWritePointer(0, 0), - monoBuffer.getReadPointer(0, size), - sizeToMove); - juce::FloatVectorOperations::copy(monoBuffer.getWritePointer(0, sizeToMove), - tempIncomingBuffer.getReadPointer(0, 0), - size); - leftChannelFFTDataGenerator.produceFFTDataForRendering(monoBuffer, lowestLevelToAnalyzeInDB); - } - } - const auto fftSize = leftChannelFFTDataGenerator.getFFTSize(); - const auto fftBinWidth = sampleRate / static_cast(fftSize); - - while (leftChannelFFTDataGenerator.getNumAvailableFFTDataBlocks() > 0) { - std::vector fftData; - if (leftChannelFFTDataGenerator.getFFTData(fftData)) { - leftChannelPathGenerator.generatePath(fftData, fftBounds, fftSize, static_cast(fftBinWidth), lowestLevelToAnalyzeInDB); - } - } - - // while there are patchs that can be pulled then pull as many as we can and display the most recent path - while (leftChannelPathGenerator.getNumPathsAvailable() > 0) { - leftChannelPathGenerator.getPath(leftChannelFFTPath); - } -} - -void ResponseCurveComponent::timerCallback() { - - if (shouldShowFFTAnalysis) { - auto fftBounds = getAnalysisArea().toFloat(); - auto sampleRate = audioProcessor.getSampleRate(); - leftChannelPathProducer.process(fftBounds, sampleRate); - rightChannelPathProducer.process(fftBounds, sampleRate); - } - - if (parametersChanged.compareAndSetBool(false, true)) { - updateChain(); - } - repaint(); -} - -void ResponseCurveComponent::updateChain() { - auto chainSettings = getChainSettings(audioProcessor.parameters); - - monoChain.setBypassed(chainSettings.lowCutBypass); - monoChain.setBypassed(chainSettings.peakBypass); - monoChain.setBypassed(chainSettings.highCutBypass); - - auto peakCoefficients = makePeakFilter(chainSettings, audioProcessor.getSampleRate()); - updateCoefficients(monoChain.get().coefficients, peakCoefficients); - - auto lowCutCoefficients = makeLowCutFilter(chainSettings, audioProcessor.getSampleRate()); - updateCutFilter(monoChain.get(), lowCutCoefficients, chainSettings.lowCutSlope); - - auto highCutCoefficients = makeHighCutFilter(chainSettings, audioProcessor.getSampleRate()); - updateCutFilter(monoChain.get(), highCutCoefficients, chainSettings.highCutSlope); -} - -void ResponseCurveComponent::paint(juce::Graphics &graphics) { - using namespace juce; - // Our component is opaque, so we must completely fill the background with a solid colour. - // g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); - graphics.fillAll(Colours::black); - - graphics.drawImage(background, getLocalBounds().toFloat()); - - auto responseArea = getAnalysisArea(); - - auto width = responseArea.toDouble().getWidth(); - - auto &lowCut = monoChain.get(); - auto &peak = monoChain.get(); - auto &highCut = monoChain.get(); - - auto sampleRate = audioProcessor.getSampleRate(); - - std::vector magnitudes; - - magnitudes.resize(static_cast(lrint(width))); - - for (unsigned long i = 0; i < magnitudes.size(); ++i) { - double magnitude = 1.0F; - auto frequency = mapToLog10(static_cast(i) / width, 20.0, 20000.0); - - if (!monoChain.isBypassed()) { - magnitude *= peak.coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - - if (!monoChain.isBypassed()) { - if (!lowCut.isBypassed<0>()) { - magnitude *= lowCut.get<0>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - if (!lowCut.isBypassed<1>()) { - magnitude *= lowCut.get<1>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - if (!lowCut.isBypassed<2>()) { - magnitude *= lowCut.get<2>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - if (!lowCut.isBypassed<3>()) { - magnitude *= lowCut.get<3>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - } - if (!monoChain.isBypassed()) { - if (!highCut.isBypassed<0>()) { - magnitude *= highCut.get<0>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - if (!highCut.isBypassed<1>()) { - magnitude *= highCut.get<1>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - if (!highCut.isBypassed<2>()) { - magnitude *= highCut.get<2>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - if (!highCut.isBypassed<3>()) { - magnitude *= highCut.get<3>().coefficients->getMagnitudeForFrequency(frequency, sampleRate); - } - } - magnitudes[i] = Decibels::gainToDecibels(magnitude); - } - - Path responseCurve; - - const double outputMin = responseArea.getBottom(); - const double outputMax = responseArea.getY(); - auto map = [outputMin, outputMax](double input) { - return jmap(input, -24.0, 24.0, outputMin, outputMax); - }; - - responseCurve.startNewSubPath(responseArea.toFloat().getX(), static_cast(map(magnitudes.front()))); - - for (size_t i = 1; i < magnitudes.size(); ++i) { - responseCurve.lineTo(responseArea.toFloat().getX() + static_cast(i), static_cast(map(magnitudes[i]))); - } - - if (shouldShowFFTAnalysis) { - auto leftChannelFFTPath = leftChannelPathProducer.getPath(); - leftChannelFFTPath.applyTransform(AffineTransform::translation(responseArea.toFloat().getX(), responseArea.toFloat().getY())); - graphics.setColour(Colours::skyblue); - graphics.strokePath(leftChannelFFTPath, PathStrokeType(1.0F)); - - auto rightChannelFFTPath = rightChannelPathProducer.getPath(); - rightChannelFFTPath.applyTransform(AffineTransform::translation(responseArea.toFloat().getX(), responseArea.toFloat().getY())); - graphics.setColour(Colours::lightyellow); - graphics.strokePath(rightChannelFFTPath, PathStrokeType(1.0F)); - } - graphics.setColour(Colours::orange); - graphics.drawRoundedRectangle(getRenderArea().toFloat(), 4.0F, 1.0F); - - graphics.setColour(Colours::white); - graphics.strokePath(responseCurve, PathStrokeType(2.0F)); -} - -void ResponseCurveComponent::resized() { - - using namespace juce; - background = Image(Image::PixelFormat::RGB, getWidth(), getHeight(), true); - - Graphics graphics(background); - - graphics.setColour(Colours::dimgrey); - - // Draw vertical frequency lines - Array frequencies{ - 20, 30, 40, 50, 100, - 200, 300, 400, 500, 1000, - 2000, 3000, 4000, 5000, 10000, - 20000}; - - auto renderArea = getAnalysisArea(); - auto left = renderArea.toFloat().getX(); - auto right = renderArea.toFloat().getRight(); - auto top = renderArea.toFloat().getY(); - auto bottom = renderArea.toFloat().getBottom(); - auto width = renderArea.toFloat().getWidth(); - - Array frequencyLineXPositions; - for (auto frequency : frequencies) { - auto frequencyLineXPosition = mapFromLog10(frequency, 20.F, 20000.0F); - frequencyLineXPositions.add(static_cast(std::lrint(left + width * frequencyLineXPosition))); - } - - for (auto frequencyLineXPosition : frequencyLineXPositions) { - graphics.drawVerticalLine(frequencyLineXPosition, top, bottom); - } - - // Draw horizontal gain lines - Array gainValues{-24.0F, -12.0F, 0.0F, 12.0F, 24.0F}; - for (auto gainValue : gainValues) { - auto gainLineYPosition = static_cast(std::lrint(jmap(gainValue, -24.0F, 24.0F, bottom, top))); - graphics.setColour(gainValue == 0.0F ? Colour(95, 75, 255) : Colours::dimgrey); - graphics.drawHorizontalLine(gainLineYPosition, left, right); - } - - // Draw frequency labels - graphics.setColour(Colours::lightgrey); - const int magnitudeLabelsFontSize = 10; - graphics.setFont(magnitudeLabelsFontSize); - - Array frequencyLabelsToPrint{ - true, false, false, true, true, - true, false, false, true, true, - true, false, false, true, true, - true}; - - for (int i = 0; i < frequencies.size(); ++i) { - if (!frequencyLabelsToPrint[i]) { - continue; - } - auto frequency = frequencies[i]; - auto frequencyLineXPosition = frequencyLineXPositions[i]; - - bool inKiloHertz = false; - if (frequency >= 999.0F) { - inKiloHertz = true; - frequency /= 1000.0F; - } - - String frequencyLabelText; - frequencyLabelText << frequency; - if (inKiloHertz) { - frequencyLabelText << "kHz"; - } else { - frequencyLabelText << "Hz"; - } - - auto textWidth = graphics.getCurrentFont().getStringWidth(frequencyLabelText); - - Rectangle rectangle; - rectangle.setSize(textWidth, magnitudeLabelsFontSize); - rectangle.setCentre(frequencyLineXPosition, 0); - rectangle.setY(1); - - graphics.drawFittedText(frequencyLabelText, rectangle, juce::Justification::centred, 1); - } - - // Draw gain labels - for (auto gainValue : gainValues) { - auto gainLineYPosition = static_cast(std::lrint(jmap(gainValue, -24.0F, 24.0F, bottom, top))); - - String gainLabelText; - if (gainValue > 0) { - gainLabelText << "+"; - } - gainLabelText << gainValue; - - auto textWidth = graphics.getCurrentFont().getStringWidth(gainLabelText); - - Rectangle rectangle; - rectangle.setSize(textWidth, magnitudeLabelsFontSize); - rectangle.setX(getWidth() - textWidth); - rectangle.setCentre(rectangle.getCentreX(), gainLineYPosition); - graphics.setColour(gainValue == 0.0F ? Colour(95, 75, 255) : Colours::lightgrey); - - graphics.drawFittedText(gainLabelText, rectangle, juce::Justification::centred, 1); - - // Analyzer level label - gainLabelText.clear(); - gainLabelText << (gainValue - 24.0F); - - rectangle.setX(1); - textWidth = graphics.getCurrentFont().getStringWidth(gainLabelText); - rectangle.setSize(textWidth, magnitudeLabelsFontSize); - graphics.setColour(Colours::lightgrey); - - graphics.drawFittedText(gainLabelText, rectangle, juce::Justification::centred, 1); - } -} - -juce::Rectangle ResponseCurveComponent::getRenderArea() const { - auto bounds = getLocalBounds(); - - bounds.removeFromTop(12); - bounds.removeFromBottom(2); - bounds.removeFromLeft(20); - bounds.removeFromRight(20); - - return bounds; -} - -juce::Rectangle ResponseCurveComponent::getAnalysisArea() const { - auto bounds = getRenderArea(); - - bounds.removeFromTop(4); - bounds.removeFromBottom(4); - - return bounds; -} - -//============================================================================== -SimpleEQAudioProcessorEditor::SimpleEQAudioProcessorEditor(SimpleEQAudioProcessor &p) - : AudioProcessorEditor(&p), audioProcessor(p), - peakFrequencySlider(*audioProcessor.parameters.getParameter("Peak Frequency"), "Hz"), - peakGainSlider(*audioProcessor.parameters.getParameter("Peak Gain"), "dB"), - peakQualitySlider(*audioProcessor.parameters.getParameter("Peak Quality"), ""), - lowCutFrequencySlider(*audioProcessor.parameters.getParameter("LowCut Frequency"), "Hz"), - highCutFrequencySlider(*audioProcessor.parameters.getParameter("HighCut Frequency"), "Hz"), - lowCutSlopeSlider(*audioProcessor.parameters.getParameter("LowCut Slope"), "dB/Oct"), - highCutSlopeSlider(*audioProcessor.parameters.getParameter("HighCut Slope"), "dB/Oct"), - - responseCurveComponent(audioProcessor), - peakFrequencyAttachment(audioProcessor.parameters, "Peak Frequency", peakFrequencySlider), - peakGainAttachment(audioProcessor.parameters, "Peak Gain", peakGainSlider), - peakQualityAttachment(audioProcessor.parameters, "Peak Quality", peakQualitySlider), - lowCutFrequencyAttachment(audioProcessor.parameters, "LowCut Frequency", lowCutFrequencySlider), - highCutFrequencyAttachment(audioProcessor.parameters, "HighCut Frequency", highCutFrequencySlider), - lowCutSlopeAttachment(audioProcessor.parameters, "LowCut Slope", lowCutSlopeSlider), - highCutSlopeAttachment(audioProcessor.parameters, "HighCut Slope", highCutSlopeSlider), - - lowCutBypassAttachment(audioProcessor.parameters, "LowCut Bypass", lowCutBypassButton), - peakBypassAttachment(audioProcessor.parameters, "Peak Bypass", peakBypassButton), - highCutBypassAttachment(audioProcessor.parameters, "HighCut Bypass", highCutBypassButton), - analyzerEnableAttachment(audioProcessor.parameters, "Analyzer Enable", analyzerEnableButton) { - - juce::ignoreUnused(audioProcessor); - // Make sure that before the constructor has finished, you've set the - // editor's size to whatever you need it to be. - - peakFrequencySlider.labels.add({0.0F, "20Hz"}); - peakFrequencySlider.labels.add({1.0F, "20kHz"}); - - peakGainSlider.labels.add({0.0F, "-24dB"}); - peakGainSlider.labels.add({1.0F, "+24dB"}); - - peakQualitySlider.labels.add({0.0F, "0.1"}); - peakQualitySlider.labels.add({1.0F, "10.0"}); - - lowCutFrequencySlider.labels.add({0.0F, "20Hz"}); - lowCutFrequencySlider.labels.add({1.0F, "20kHz"}); - - highCutFrequencySlider.labels.add({0.0F, "20Hz"}); - highCutFrequencySlider.labels.add({1.0F, "20kHz"}); - - lowCutSlopeSlider.labels.add({0.0F, "12"}); - lowCutSlopeSlider.labels.add({1.0F, "48"}); - - highCutSlopeSlider.labels.add({0.0F, "12"}); - highCutSlopeSlider.labels.add({1.0F, "48"}); - - for (auto *component : getComponents()) { - addAndMakeVisible(component); - } - - lowCutBypassButton.setLookAndFeel(&lookAndFeel); - peakBypassButton.setLookAndFeel(&lookAndFeel); - highCutBypassButton.setLookAndFeel(&lookAndFeel); - analyzerEnableButton.setLookAndFeel(&lookAndFeel); - - auto safeEditorPointer = juce::Component::SafePointer(this); - peakBypassButton.onClick = [safeEditorPointer]() { - if (auto *component = safeEditorPointer.getComponent()) { - auto bypassed = component->peakBypassButton.getToggleState(); - component->peakFrequencySlider.setEnabled(!bypassed); - component->peakGainSlider.setEnabled(!bypassed); - component->peakQualitySlider.setEnabled(!bypassed); - } - }; - lowCutBypassButton.onClick = [safeEditorPointer]() { - if (auto *component = safeEditorPointer.getComponent()) { - auto bypassed = component->lowCutBypassButton.getToggleState(); - component->lowCutFrequencySlider.setEnabled(!bypassed); - component->lowCutSlopeSlider.setEnabled(!bypassed); - } - }; - highCutBypassButton.onClick = [safeEditorPointer]() { - if (auto *component = safeEditorPointer.getComponent()) { - auto bypassed = component->highCutBypassButton.getToggleState(); - component->highCutFrequencySlider.setEnabled(!bypassed); - component->highCutSlopeSlider.setEnabled(!bypassed); - } - }; - - analyzerEnableButton.onClick = [safeEditorPointer]() { - if (auto *component = safeEditorPointer.getComponent()) { - auto enabled = component->analyzerEnableButton.getToggleState(); - component->responseCurveComponent.setEnabled(enabled); - } - }; - - setSize(600, 480); -} - -SimpleEQAudioProcessorEditor::~SimpleEQAudioProcessorEditor() { - lowCutBypassButton.setLookAndFeel(nullptr); - peakBypassButton.setLookAndFeel(nullptr); - highCutBypassButton.setLookAndFeel(nullptr); - analyzerEnableButton.setLookAndFeel(nullptr); -} - -//============================================================================== -void SimpleEQAudioProcessorEditor::paint(juce::Graphics &graphics) { - using namespace juce; - // (Our component is opaque, so we must completely fill the background with a solid colour) - //g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); - graphics.fillAll(Colours::black); -} - -void SimpleEQAudioProcessorEditor::resized() { - // This is generally where you'll want to lay out the positions of any - // subcomponents in your editor.. - - auto bounds = getLocalBounds(); - - auto analyzerEnableArea = bounds.removeFromTop(25); - analyzerEnableArea.setWidth(100); - analyzerEnableArea.setX(5); - analyzerEnableArea.removeFromTop(2); - - analyzerEnableButton.setBounds(analyzerEnableArea); - - bounds.removeFromTop(5); - - // JUCE_LIVE_CONSTANT lets us adjust the size of the sliders without having to recompile - // float heightRatio = JUCE_LIVE_CONSTANT(25) / 100.0F; - auto const heightRatio = 25.0 / 100.0F; - - auto responseArea = bounds.removeFromTop(static_cast(lrint(bounds.getHeight() * heightRatio))); - - responseCurveComponent.setBounds(responseArea); - - bounds.removeFromTop(5); - auto lowCutArea = bounds.removeFromLeft(static_cast(lrint(bounds.getWidth() * 0.33))); - auto highCutArea = bounds.removeFromRight(static_cast(lrint(bounds.getWidth() * 0.5))); - - auto const bypassButtonsTopPadding = 25; - lowCutBypassButton.setBounds(lowCutArea.removeFromTop(bypassButtonsTopPadding)); - lowCutFrequencySlider.setBounds(lowCutArea.removeFromTop(static_cast(lrint(lowCutArea.getHeight() * 0.5)))); - lowCutSlopeSlider.setBounds(lowCutArea); - - highCutBypassButton.setBounds(highCutArea.removeFromTop(bypassButtonsTopPadding)); - highCutFrequencySlider.setBounds(highCutArea.removeFromTop(static_cast(lrint(highCutArea.getHeight() * 0.5)))); - highCutSlopeSlider.setBounds(highCutArea); - - peakBypassButton.setBounds(bounds.removeFromTop(bypassButtonsTopPadding)); - peakFrequencySlider.setBounds(bounds.removeFromTop(static_cast(lrint(bounds.getHeight() * 0.33)))); - peakGainSlider.setBounds(bounds.removeFromTop(static_cast(lrint(bounds.getHeight() * 0.5)))); - peakQualitySlider.setBounds(bounds); -} - -auto SimpleEQAudioProcessorEditor::getComponents() -> std::vector { - return { - &peakFrequencySlider, - &peakGainSlider, - &peakQualitySlider, - &lowCutFrequencySlider, - &highCutFrequencySlider, - &lowCutSlopeSlider, - &highCutSlopeSlider, - &responseCurveComponent, - - &lowCutBypassButton, - &peakBypassButton, - &highCutBypassButton, - &analyzerEnableButton}; -} \ No newline at end of file diff --git a/src/PluginEditor.h b/src/PluginEditor.h deleted file mode 100644 index db4e6b2b..00000000 --- a/src/PluginEditor.h +++ /dev/null @@ -1,304 +0,0 @@ -#pragma once - -#include "PluginProcessor.h" -#include -#include - -// Copied (and slightly adapted) from https://github.com/matkatmusic/SimpleEQ/blob/master/Source/PluginEditor.h -enum FFTOrder { - Order_2048 = 11, - Order_4096 = 12, - Order_8192 = 13 -}; - -// Copied (and slightly adapted) from https://github.com/matkatmusic/SimpleEQ/blob/master/Source/PluginEditor.h -template -struct FFTDataGenerator { - /** - produces the FFT data from an audio buffer. - */ - void produceFFTDataForRendering(const juce::AudioBuffer &audioData, const float negativeInfinity) { - const auto fftSize = getFFTSize(); - - fftData.assign(fftData.size(), 0); - const auto *readIndex = audioData.getReadPointer(0); - std::copy(readIndex, readIndex + fftSize, fftData.begin()); - - // first apply a windowing function to our data - window->multiplyWithWindowingTable(fftData.data(), fftSize);// [1] - - // then render our FFT data.. - forwardFFT->performFrequencyOnlyForwardTransform(fftData.data());// [2] - - int numBins = std::rintl(fftSize / 2); - - //normalize the fft values. - for (int i = 0; i < numBins; ++i) { - auto v = fftData[i]; - if (!std::isinf(v) && !std::isnan(v)) { - v /= float(numBins); - } else { - v = 0.0F; - } - fftData[i] = v; - } - - //convert them to decibels - for (int i = 0; i < numBins; ++i) { - fftData[i] = juce::Decibels::gainToDecibels(fftData[i], negativeInfinity); - } - - fftDataFifo.push(fftData); - } - - void changeOrder(FFTOrder newOrder) { - //when you change order, recreate the window, forwardFFT, fifo, fftData - //also reset the fifoIndex - //things that need recreating should be created on the heap via std::make_unique<> - - order = newOrder; - auto fftSize = getFFTSize(); - - forwardFFT = std::make_unique(order); - window = std::make_unique>(fftSize, juce::dsp::WindowingFunction::blackmanHarris); - - fftData.clear(); - fftData.resize(fftSize * 2, 0); - - fftDataFifo.prepare(fftData.size()); - } - //============================================================================== - [[nodiscard]] auto getFFTSize() const -> int { return 1 << order; } - [[nodiscard]] auto getNumAvailableFFTDataBlocks() const -> int { return fftDataFifo.getNumAvailableForReading(); } - //============================================================================== - auto getFFTData(BlockType &fftData) -> bool { return fftDataFifo.pull(fftData); } - -private: - FFTOrder order; - BlockType fftData; - std::unique_ptr forwardFFT; - std::unique_ptr> window; - - Fifo fftDataFifo; -}; - -// Copied (and slightly adapted) from https://github.com/matkatmusic/SimpleEQ/blob/master/Source/PluginEditor.h -template -struct AnalyzerPathGenerator { - /* - converts 'renderData[]' into a juce::Path - */ - void generatePath(const std::vector &renderData, - juce::Rectangle fftBounds, - int fftSize, - float binWidth, - float negativeInfinity) { - auto top = fftBounds.getY(); - auto bottom = fftBounds.getHeight(); - auto width = fftBounds.getWidth(); - - int numBins = fftSize / 2; - - PathType p; - p.preallocateSpace(3 * static_cast(fftBounds.getWidth())); - - auto map = [bottom, top, negativeInfinity](float v) { - return juce::jmap(v, - negativeInfinity, 0.0F, - float(bottom + 10), top); - }; - - auto y = map(renderData[0]); - - // jassert( !std::isnan(y) && !std::isinf(y) ); - if (std::isnan(y) || std::isinf(y)) { - y = bottom; - } - - p.startNewSubPath(0, y); - - const int pathResolution = 2;//you can draw line-to's every 'pathResolution' pixels. - - for (int binNum = 1; binNum < numBins; binNum += pathResolution) { - y = map(renderData[binNum]); - - // jassert( !std::isnan(y) && !std::isinf(y) ); - - if (!std::isnan(y) && !std::isinf(y)) { - auto binFreq = binNum * binWidth; - auto normalizedBinX = juce::mapFromLog10(binFreq, 20.0F, 20000.0F); - int binX = std::floor(normalizedBinX * width); - p.lineTo(binX, y); - } - } - - pathFifo.push(p); - } - - [[nodiscard]] auto getNumPathsAvailable() const -> int { - return pathFifo.getNumAvailableForReading(); - } - - auto getPath(PathType &path) -> bool { - return pathFifo.pull(path); - } - -private: - Fifo pathFifo; -}; - -//============================================================================== - -struct SimpleEQLookAndFeel : juce::LookAndFeel_V4 { - void drawRotarySlider(juce::Graphics &graphics, int x, int y, int width, int height, float sliderPosProportional, - float rotaryStartAngle, float rotaryEndAngle, juce::Slider &slider) override; - void drawToggleButton(juce::Graphics &graphics, juce::ToggleButton &button, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override; -}; - -struct RotarySlidersWithLabels : juce::Slider { - RotarySlidersWithLabels(juce::RangedAudioParameter &newParamter, juce::String newUnitSuffix) - : juce::Slider(juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag, juce::Slider::TextEntryBoxPosition::NoTextBox), - parameter(&newParamter), - unitSuffix(std::move(newUnitSuffix)) { - setLookAndFeel(&lookAndFeel); - } - - ~RotarySlidersWithLabels() override { setLookAndFeel(nullptr); } - - struct LabelPosition { - float position; - juce::String label; - }; - - juce::Array labels; - - void paint(juce::Graphics &graphics) override; - auto getSliderBounds() const -> juce::Rectangle; - static int getTextHeight() { return 14; } - auto getDisplayString() const -> juce::String; - -private: - SimpleEQLookAndFeel lookAndFeel; - - juce::RangedAudioParameter *parameter; - juce::String unitSuffix; -}; - -struct PathProducer { - -public: - explicit PathProducer(SingleChannelSampleFifo &singleChannelQueue) : leftChannelQueue(&singleChannelQueue) { - leftChannelFFTDataGenerator.changeOrder(FFTOrder::Order_2048); - monoBuffer.setSize(1, leftChannelFFTDataGenerator.getFFTSize()); - } - void process(juce::Rectangle fftBounds, double sampleRate); - auto getPath() -> juce::Path { return leftChannelFFTPath; } - -private: - SingleChannelSampleFifo *leftChannelQueue; - - juce::AudioBuffer monoBuffer; - - FFTDataGenerator> leftChannelFFTDataGenerator; - AnalyzerPathGenerator leftChannelPathGenerator; - juce::Path leftChannelFFTPath; -}; - -struct ResponseCurveComponent : juce::Component, - juce::AudioProcessorParameter::Listener, - juce::Timer { -public: - explicit ResponseCurveComponent(SimpleEQAudioProcessor &); - ~ResponseCurveComponent() override; - - void parameterValueChanged(int, float) override; - void parameterGestureChanged(int, bool) override {} - - void timerCallback() override; - - void paint(juce::Graphics &graphics) override; - void resized() override; - - void setEnabled(bool value) { - shouldShowFFTAnalysis = value; - } - -private: - // This reference is provided as a quick way for your editor to - // access the processor object that created it. - SimpleEQAudioProcessor &audioProcessor; - - juce::Atomic parametersChanged{false}; - - MonoChain monoChain; - - void updateChain(); - - juce::Image background; - - auto getRenderArea() const -> juce::Rectangle; - auto getAnalysisArea() const -> juce::Rectangle; - - PathProducer leftChannelPathProducer, rightChannelPathProducer; - - bool shouldShowFFTAnalysis = true; -}; - -//============================================================================== - -struct PowerButton : juce::ToggleButton {}; -struct AnalyzerButton : juce::ToggleButton { - - void resized() override { - auto bounds = getLocalBounds(); - auto insetRectangle = bounds.reduced(4).toFloat(); - - randomPath.clear(); - - Random random; - auto insetBottom = insetRectangle.getBottom(); - randomPath.startNewSubPath(insetRectangle.getX(), insetBottom * random.nextFloat()); - - for (auto x = insetRectangle.toNearestInt().getX() + 1; x < insetRectangle.toNearestInt().getRight(); x += 2) { - randomPath.lineTo(static_cast(x), insetBottom * random.nextFloat()); - } - }; - - juce::Path randomPath; -}; - -class SimpleEQAudioProcessorEditor : public juce::AudioProcessorEditor { -public: - explicit SimpleEQAudioProcessorEditor(SimpleEQAudioProcessor &); - ~SimpleEQAudioProcessorEditor() override; - - //============================================================================== - void paint(juce::Graphics &graphics) override; - void resized() override; - - private: - // This reference is provided as a quick way for your editor to - // access the processor object that created it. - SimpleEQAudioProcessor &audioProcessor; - - RotarySlidersWithLabels peakFrequencySlider, peakGainSlider, peakQualitySlider; - RotarySlidersWithLabels lowCutFrequencySlider, highCutFrequencySlider, lowCutSlopeSlider, highCutSlopeSlider; - - ResponseCurveComponent responseCurveComponent; - - using ParameterAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; - ParameterAttachment peakFrequencyAttachment, peakGainAttachment, peakQualityAttachment; - ParameterAttachment lowCutFrequencyAttachment, highCutFrequencyAttachment, lowCutSlopeAttachment, highCutSlopeAttachment; - - PowerButton lowCutBypassButton, peakBypassButton, highCutBypassButton; - AnalyzerButton analyzerEnableButton; - - using ButtonAttachment = juce::AudioProcessorValueTreeState::ButtonAttachment; - ButtonAttachment lowCutBypassAttachment, peakBypassAttachment, highCutBypassAttachment, analyzerEnableAttachment; - - auto getComponents() -> std::vector; - - SimpleEQLookAndFeel lookAndFeel; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SimpleEQAudioProcessorEditor) -}; diff --git a/src/PluginProcessor.cpp b/src/PluginProcessor.cpp deleted file mode 100644 index b969b810..00000000 --- a/src/PluginProcessor.cpp +++ /dev/null @@ -1,332 +0,0 @@ -#include "PluginProcessor.h" -#include "PluginEditor.h" -#include -#include - -//============================================================================== -SimpleEQAudioProcessor::SimpleEQAudioProcessor() - : AudioProcessor(BusesProperties() -#if !JucePlugin_IsMidiEffect -#if !JucePlugin_IsSynth - .withInput("Input", juce::AudioChannelSet::stereo(), true) -#endif - .withOutput("Output", juce::AudioChannelSet::stereo(), true) -#endif - ) { -} - -SimpleEQAudioProcessor::~SimpleEQAudioProcessor() { -} - -//============================================================================== -auto SimpleEQAudioProcessor::getName() const -> const juce::String { - return JucePlugin_Name; -} - -auto SimpleEQAudioProcessor::acceptsMidi() const -> bool { -#if JucePlugin_WantsMidiInput - return true; - #else - return false; - #endif -} - -auto SimpleEQAudioProcessor::producesMidi() const -> bool { -#if JucePlugin_ProducesMidiOutput - return true; - #else - return false; - #endif -} - -auto SimpleEQAudioProcessor::isMidiEffect() const -> bool { -#if JucePlugin_IsMidiEffect - return true; - #else - return false; - #endif -} - -auto SimpleEQAudioProcessor::getTailLengthSeconds() const -> double { - return 0.0; -} - -auto SimpleEQAudioProcessor::getNumPrograms() -> int { - return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, - // so this should be at least 1, even if you're not really implementing programs. -} - -auto SimpleEQAudioProcessor::getCurrentProgram() -> int { - return 0; -} - -void SimpleEQAudioProcessor::setCurrentProgram(int index) { - juce::ignoreUnused (index); -} - -auto SimpleEQAudioProcessor::getProgramName(int index) -> const juce::String { - juce::ignoreUnused (index); - return {}; -} - -void SimpleEQAudioProcessor::changeProgramName(int index, const juce::String &newName) { - juce::ignoreUnused (index, newName); -} - -//============================================================================== -void SimpleEQAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) { - // Use this method as the place to do any pre-playback - // initialisation that you need.. - juce::dsp::ProcessSpec specification{}; - - specification.maximumBlockSize = static_cast(samplesPerBlock); - specification.numChannels = 1; - specification.sampleRate = sampleRate; - - leftChain.prepare(specification); - rightChain.prepare(specification); - - updateFilters(); - - leftChannelQueue.prepare(samplesPerBlock); - rightChannelQueue.prepare(samplesPerBlock); - - oscillator.initialise([](float x) { return std::sin(x); }); - - specification.numChannels = static_cast(getTotalNumOutputChannels()); - oscillator.prepare(specification); - oscillator.setFrequency(200); -} - -void SimpleEQAudioProcessor::releaseResources() { - // When playback stops, you can use this as an opportunity to free up any - // spare memory, etc. -} - -auto SimpleEQAudioProcessor::isBusesLayoutSupported(const BusesLayout &layouts) const -> bool { -#if JucePlugin_IsMidiEffect - juce::ignoreUnused (layouts); - return true; - #else - // This is the place where you check if the layout is supported. - // In this template code we only support mono or stereo. - // Some plugin hosts, such as certain GarageBand versions, will only - // load plugins that support stereo bus layouts. - if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() - && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) { - return false; - } - - // This checks if the input layout matches the output layout - #if ! JucePlugin_IsSynth - if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) { - return false; - } - #endif - - return true; - #endif -} - -void SimpleEQAudioProcessor::processBlock(juce::AudioBuffer &buffer, - juce::MidiBuffer &midiMessages) { - juce::ignoreUnused (midiMessages); - - juce::ScopedNoDenormals noDenormals; - auto totalNumInputChannels = getTotalNumInputChannels(); - auto totalNumOutputChannels = getTotalNumOutputChannels(); - - // In case we have more outputs than inputs, this code clears any output - // channels that didn't contain input data, (because these aren't - // guaranteed to be empty - they may contain garbage). - // This is here to avoid people getting screaming feedback - // when they first compile a plugin, but obviously you don't need to keep - // this code if your algorithm always overwrites all the output channels. - for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) { - buffer.clear (i, 0, buffer.getNumSamples()); - } - - updateFilters(); - - juce::dsp::AudioBlock block(buffer); - - // Oscillator for testing - // buffer.clear(); - // juce::dsp::ProcessContextReplacing stereoContext(block); - // oscillator.process(stereoContext); - - auto leftBlock = block.getSingleChannelBlock(0); - auto rightBlock = block.getSingleChannelBlock(1); - - juce::dsp::ProcessContextReplacing leftContext(leftBlock); - juce::dsp::ProcessContextReplacing rightContext(rightBlock); - - leftChain.process(leftContext); - rightChain.process(rightContext); - - leftChannelQueue.update(buffer); - rightChannelQueue.update(buffer); -} - -//============================================================================== -auto SimpleEQAudioProcessor::hasEditor() const -> bool { - return true; // (change this to false if you choose to not supply an editor) -} - -auto SimpleEQAudioProcessor::createEditor() -> juce::AudioProcessorEditor * { - return new SimpleEQAudioProcessorEditor(*this); - // return new juce::GenericAudioProcessorEditor(*this); // generic editor for prototyping without GUI -} - -//============================================================================== -void SimpleEQAudioProcessor::getStateInformation(juce::MemoryBlock &destData) { - // You should use this method to store your parameters in the memory block. - // You could do that either as raw data, or use the XML or ValueTree classes - // as intermediaries to make it easy to save and load complex data. - juce::MemoryOutputStream memoryOutputStream(destData, true); - parameters.state.writeToStream(memoryOutputStream); -} - -void SimpleEQAudioProcessor::setStateInformation(const void *data, int sizeInBytes) { - // You should use this method to restore your parameters from this memory block, - // whose contents will have been created by the getStateInformation() call. - auto tree = juce::ValueTree::readFromData(data, static_cast(sizeInBytes)); - if (tree.isValid()) { - parameters.replaceState(tree); - updateFilters(); - } -} - -//============================================================================== -// This creates new instances of the plugin.. -auto JUCE_CALLTYPE createPluginFilter() -> juce::AudioProcessor* -{ - return new SimpleEQAudioProcessor(); -} - -auto getChainSettings(const AudioProcessorValueTreeState ¶meters) -> ChainSettings -{ - ChainSettings settings; - - settings.lowCutFrequency = parameters.getRawParameterValue("LowCut Frequency")->load(); - settings.highCutFrequency = parameters.getRawParameterValue("HighCut Frequency")->load(); - settings.peakFrequency = parameters.getRawParameterValue("Peak Frequency")->load(); - settings.peakGainInDecibels = parameters.getRawParameterValue("Peak Gain")->load(); - settings.peakQuality = parameters.getRawParameterValue("Peak Quality")->load(); - settings.lowCutSlope = static_cast(parameters.getRawParameterValue("LowCut Slope")->load()); - settings.highCutSlope = static_cast(parameters.getRawParameterValue("HighCut Slope")->load()); - - settings.lowCutBypass = parameters.getRawParameterValue("LowCut Bypass")->load() > 0.5F; - settings.highCutBypass = parameters.getRawParameterValue("HighCut Bypass")->load() > 0.5F; - settings.peakBypass = parameters.getRawParameterValue("Peak Bypass")->load() > 0.5F; - - return settings; -} - -void updateCoefficients(Coefficients &old, const Coefficients &replacements) { - *old = *replacements; -} - -auto makePeakFilter(const ChainSettings& chainSettings, double sampleRate) -> Coefficients { - auto peakGain = juce::Decibels::decibelsToGain(chainSettings.peakGainInDecibels); - return juce::dsp::IIR::Coefficients::makePeakFilter( - sampleRate, - chainSettings.peakFrequency, - chainSettings.peakQuality, - peakGain); -} - -void SimpleEQAudioProcessor::updatePeakFilter(const ChainSettings &chainSettings) { - auto peakCoefficients = makePeakFilter(chainSettings, getSampleRate()); - - leftChain.setBypassed(chainSettings.peakBypass); - rightChain.setBypassed(chainSettings.peakBypass); - - updateCoefficients(leftChain.get().coefficients, peakCoefficients); - updateCoefficients(rightChain.get().coefficients, peakCoefficients); -} - -void SimpleEQAudioProcessor::updateLowCutFilters(const ChainSettings &chainSettings) { - auto lowCutCoefficients = makeLowCutFilter(chainSettings, getSampleRate()); - auto &leftLowCut = leftChain.get(); - auto &rightLowCut = rightChain.get(); - - leftChain.setBypassed(chainSettings.lowCutBypass); - rightChain.setBypassed(chainSettings.lowCutBypass); - - updateCutFilter(leftLowCut, lowCutCoefficients, chainSettings.lowCutSlope); - updateCutFilter(rightLowCut, lowCutCoefficients, chainSettings.lowCutSlope); -} - -void SimpleEQAudioProcessor::updateHighCutFilters(const ChainSettings &chainSettings) { - auto highCutCoefficients = makeHighCutFilter(chainSettings, getSampleRate()); - auto& leftHighCut = leftChain.get(); - auto& rightHighCut = rightChain.get(); - - leftChain.setBypassed(chainSettings.highCutBypass); - rightChain.setBypassed(chainSettings.highCutBypass); - - updateCutFilter(leftHighCut, highCutCoefficients, chainSettings.highCutSlope); - updateCutFilter(rightHighCut, highCutCoefficients, chainSettings.highCutSlope); -} - -void SimpleEQAudioProcessor::updateFilters() { - auto chainSettings = getChainSettings(parameters); - updateLowCutFilters(chainSettings); - updatePeakFilter(chainSettings); - updateHighCutFilters(chainSettings); -} - -auto SimpleEQAudioProcessor::createParameterLayout() -> juce::AudioProcessorValueTreeState::ParameterLayout { - juce::AudioProcessorValueTreeState::ParameterLayout layout; - - const auto minFilterFrequency = 20.0F; - const auto maxFilterFrequency = 20000.0F; - const auto defaultPeakFilterFrequency = 750.0F; - - const auto filterGainLimit = 24.0F; - const auto minFilterQuality = 0.1F; - const auto maxFilterQuality = 10.0F; - - const auto frequencyStepSize = 1.0F; - const auto gainStepSize = 0.5F; - const auto qualityStepSize = 0.05F; - - const auto frequencySkewFactor = 0.25F; - - layout.add(std::make_unique( - "LowCut Frequency", "LowCut Frequency", - juce::NormalisableRange(minFilterFrequency, maxFilterFrequency, frequencyStepSize, frequencySkewFactor), - minFilterFrequency)); - - layout.add(std::make_unique( - "HighCut Frequency", "HighCut Frequency", - juce::NormalisableRange(minFilterFrequency, maxFilterFrequency, 1.0F, frequencySkewFactor), - maxFilterFrequency)); - - layout.add(std::make_unique( - "Peak Frequency", "Peak Frequency", - juce::NormalisableRange(minFilterFrequency, maxFilterFrequency, 1.0F, frequencySkewFactor), - defaultPeakFilterFrequency)); - - layout.add(std::make_unique( - "Peak Gain", "Peak Gain", - juce::NormalisableRange(-filterGainLimit, filterGainLimit, gainStepSize, 1.0F), - 0.0F)); - - layout.add(std::make_unique( - "Peak Quality", "Peak Quality", - juce::NormalisableRange(minFilterQuality, maxFilterQuality, qualityStepSize, 1.0F), - 1.0F)); - - juce::StringArray filterSlopeOptions = {"12 dB/Octave", "24 dB/Octave", "36 dB/Octave", "48 dB/Octave"}; - layout.add(std::make_unique("LowCut Slope", "LowCut Slope", filterSlopeOptions, 0)); - layout.add(std::make_unique("HighCut Slope", "HighCut Slope", filterSlopeOptions, 0)); - - layout.add(std::make_unique("LowCut Bypass", "LowCut Bypass", false)); - layout.add(std::make_unique("Peak Bypass", "Peak Bypass", false)); - layout.add(std::make_unique("HighCut Bypass", "HighCut Bypass", false)); - layout.add(std::make_unique("Analyzer Enable", "Analyzer Enable", true)); - - return layout; -} \ No newline at end of file diff --git a/src/PluginProcessor.h b/src/PluginProcessor.h deleted file mode 100644 index 27bcaf8a..00000000 --- a/src/PluginProcessor.h +++ /dev/null @@ -1,269 +0,0 @@ -#pragma once - -#include -#include - -enum Channel { - Left = 0, - Right = 1 -}; - -enum Slope { - Slope_12dB, - Slope_24dB, - Slope_36dB, - Slope_48dB -}; - -//============================================================================== - -//Copied from https://github.com/matkatmusic/SimpleEQ/blob/master/src/PluginProcessor.h -template -struct Fifo { - void prepare(int numChannels, int numSamples) { - static_assert(std::is_same_v>, - "prepare(numChannels, numSamples) should only be used when the Fifo is holding juce::AudioBuffer"); - for (auto &buffer : buffers) { - buffer.setSize(numChannels, - numSamples, - false,//clear everything? - true, //including the extra space? - true);//avoid reallocating if you can? - buffer.clear(); - } - } - - void prepare(size_t numElements) { - static_assert(std::is_same_v>, - "prepare(numElements) should only be used when the Fifo is holding std::vector"); - for (auto &buffer : buffers) { - buffer.clear(); - buffer.resize(numElements, 0); - } - } - - auto push(const T &t) -> bool { - auto write = fifo.write(1); - if (write.blockSize1 > 0) { - buffers[write.startIndex1] = t; - return true; - } - - return false; - } - - auto pull(T &t) -> bool { - auto read = fifo.read(1); - if (read.blockSize1 > 0) { - t = buffers[read.startIndex1]; - return true; - } - - return false; - } - - auto getNumAvailableForReading() const -> int { - return fifo.getNumReady(); - } - -private: - static constexpr int Capacity = 30; - std::array buffers; - juce::AbstractFifo fifo{Capacity}; -}; - -//Copied from https://github.com/matkatmusic/SimpleEQ/blob/master/src/PluginProcessor.h -template -struct SingleChannelSampleFifo { - explicit SingleChannelSampleFifo(Channel ch) : channelToUse(ch) { - prepared.set(false); - } - - void update(const BlockType &buffer) { - jassert(prepared.get()); - jassert(buffer.getNumChannels() > channelToUse); - auto *channelPtr = buffer.getReadPointer(channelToUse); - - for (int i = 0; i < buffer.getNumSamples(); ++i) { - pushNextSampleIntoFifo(channelPtr[i]); - } - } - - void prepare(int bufferSize) { - prepared.set(false); - size.set(bufferSize); - - bufferToFill.setSize(1, //channel - bufferSize,//num samples - false, //keepExistingContent - true, //clear extra space - true); //avoid reallocating - audioBufferFifo.prepare(1, bufferSize); - fifoIndex = 0; - prepared.set(true); - } - //============================================================================== - auto getNumCompleteBuffersAvailable() const -> int { return audioBufferFifo.getNumAvailableForReading(); } - auto isPrepared() const -> bool { return prepared.get(); } - auto getSize() const -> int { return size.get(); } - //============================================================================== - auto getAudioBuffer(BlockType &buf) -> bool { return audioBufferFifo.pull(buf); } - -private: - Channel channelToUse; - int fifoIndex = 0; - Fifo audioBufferFifo; - BlockType bufferToFill; - juce::Atomic prepared = false; - juce::Atomic size = 0; - - void pushNextSampleIntoFifo(float sample) { - if (fifoIndex == bufferToFill.getNumSamples()) { - auto ok = audioBufferFifo.push(bufferToFill); - - juce::ignoreUnused(ok); - - fifoIndex = 0; - } - - bufferToFill.setSample(0, fifoIndex, sample); - ++fifoIndex; - } -}; - -//============================================================================== - -struct ChainSettings { - float peakFrequency{0}, peakGainInDecibels{0}, peakQuality{1.0F}; - float lowCutFrequency{0}, highCutFrequency{0}; - - Slope lowCutSlope{Slope::Slope_12dB}, highCutSlope{Slope::Slope_12dB}; - - bool lowCutBypass{false}, peakBypass{false}, highCutBypass{false}; -}; - -auto getChainSettings(const AudioProcessorValueTreeState& parameters) -> ChainSettings; - -using Filter = juce::dsp::IIR::Filter; -using CutFilter = juce::dsp::ProcessorChain; -using MonoChain = juce::dsp::ProcessorChain; - -enum ChainPositions { LowCut, Peak, HighCut }; - -using Coefficients = Filter::CoefficientsPtr; -void updateCoefficients(Coefficients& old, const Coefficients& replacements); - -auto makePeakFilter(const ChainSettings& chainSettings, double sampleRate) -> Coefficients; - -template -void update(ChainType &chain, const CoefficientType &coefficients) { - updateCoefficients(chain.template get().coefficients, - coefficients[Index]); - chain.template setBypassed(false); -} - -template -void updateCutFilter(ChainType &chain, const CoefficientType &cutCoefficients, - const Slope &slope) { - chain.template setBypassed<0>(true); - chain.template setBypassed<1>(true); - chain.template setBypassed<2>(true); - chain.template setBypassed<3>(true); - - // 12dB/octave = slope 0 = order 2 - // 24dB/octave = slope 1 = order 4 - // 36dB/octave = slope 2 = order 6 - // 48dB/octave = slope 3 = order 8 - switch (slope) { - case Slope_48dB: { - update<3>(chain, cutCoefficients); - } - case Slope_36dB: { - update<2>(chain, cutCoefficients); - } - case Slope_24dB: { - update<1>(chain, cutCoefficients); - } - case Slope_12dB: { - update<0>(chain, cutCoefficients); - } - } -} - -inline auto makeLowCutFilter(const ChainSettings& chainSettings, double sampleRate) { - auto lowCutOrder = (chainSettings.lowCutSlope + 1) * 2; - auto highestPossibleFrequency = static_cast(sampleRate * 0.5) - 1; - return dsp::FilterDesign::designIIRHighpassHighOrderButterworthMethod( - std::min(chainSettings.lowCutFrequency, highestPossibleFrequency), - sampleRate, lowCutOrder); -} - -inline auto makeHighCutFilter(const ChainSettings& chainSettings, double sampleRate) { - auto highCutOrder = (chainSettings.highCutSlope + 1) * 2; - auto highestPossibleFrequency = static_cast(sampleRate * 0.5) - 1; - return dsp::FilterDesign::designIIRLowpassHighOrderButterworthMethod( - std::min(chainSettings.highCutFrequency, highestPossibleFrequency), - sampleRate, highCutOrder); -} - -//============================================================================== -class SimpleEQAudioProcessor : public juce::AudioProcessor { -public: - //============================================================================== - SimpleEQAudioProcessor(); - ~SimpleEQAudioProcessor() override; - - //============================================================================== - void prepareToPlay (double sampleRate, int samplesPerBlock) override; - void releaseResources() override; - - auto isBusesLayoutSupported (const BusesLayout& layouts) const -> bool override; - - void processBlock (juce::AudioBuffer&, juce::MidiBuffer&) override; - using AudioProcessor::processBlock; - - //============================================================================== - auto createEditor() -> juce::AudioProcessorEditor* override; - auto hasEditor() const -> bool override; - - //============================================================================== - auto getName() const -> const juce::String override; - - auto acceptsMidi() const -> bool override; - auto producesMidi() const -> bool override; - auto isMidiEffect() const -> bool override; - auto getTailLengthSeconds() const -> double override; - - //============================================================================== - auto getNumPrograms() -> int override; - auto getCurrentProgram() -> int override; - void setCurrentProgram (int index) override; - auto getProgramName (int index) -> const juce::String override; - void changeProgramName (int index, const juce::String& newName) override; - - //============================================================================== - void getStateInformation (juce::MemoryBlock& destData) override; - void setStateInformation (const void* data, int sizeInBytes) override; - - juce::AudioProcessorValueTreeState parameters {*this, nullptr, "Parameters", createParameterLayout()}; - - using BlockType = juce::AudioBuffer; - SingleChannelSampleFifo leftChannelQueue{Channel::Left}; - SingleChannelSampleFifo rightChannelQueue{Channel::Right}; - -private: - static auto createParameterLayout() -> juce::AudioProcessorValueTreeState::ParameterLayout; - - MonoChain leftChain, rightChain; - - void updatePeakFilter(const ChainSettings& chainSettings); - void updateLowCutFilters(const ChainSettings& chainSettings); - void updateHighCutFilters(const ChainSettings& chainSettings); - - void updateFilters(); - - juce::dsp::Oscillator oscillator; - - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SimpleEQAudioProcessor) -}; diff --git a/src/SpecletPluginProcessor.cpp b/src/SpecletPluginProcessor.cpp new file mode 100644 index 00000000..9e302326 --- /dev/null +++ b/src/SpecletPluginProcessor.cpp @@ -0,0 +1,294 @@ +#include "SpecletPluginProcessor.h" +#include "dsp/SignalGenerator.h" +#include "dsp/transformations/AbstractWaveletTransformation.h" +#include "dsp/transformations/TransformationFactory.h" +#include "juce_core/system/juce_PlatformDefs.h" +#include "parameter/SpecletParameters.h" +#include "ui/ColourGradients.h" +#include "ui/SpecletMainUI.h" +#include "utilities/PerformanceLogger.h" +#include +#include +#include + + +#define DEFAULT_SAMPLINGRATE 44100 + +//============================================================================== +SpecletAudioProcessor::SpecletAudioProcessor() + : AudioProcessor(BusesProperties() +#if !JucePlugin_IsMidiEffect +#if !JucePlugin_IsSynth + .withInput("Input", juce::AudioChannelSet::stereo(), true) +#endif + .withOutput("Output", juce::AudioChannelSet::stereo(), true) +#endif + ), + parameters(*this), + parameterRouting(parameters.getRouting()) { + + LOG_PERFORMANCE_BEGIN("SpecletAudioProcessor"); +#if _LOGTOFILE + juce::Logger::setCurrentLogger(new juce::FileLogger(juce::File("c:/temp/speclet.log"), "Speclet LogFile"), true); +#endif + + //registers itself as a listener for parameter-changes + parameters.addListener(this); + DBG("SpecletAudioProcessor as parameter listener added"); +} + +SpecletAudioProcessor::~SpecletAudioProcessor() { + parameters.removeListener(this); + DBG("SpecletAudioProcessor as parameter listener removed"); + currentTransformation = nullptr; + + TransformationFactory::getSingletonInstance().destruct(); + +#if _LOGTOFILE + juce::Logger::setCurrentLogger(0, true); +#endif + LOG_PERFORMANCE_END(); +} + +//============================================================================== +auto SpecletAudioProcessor::getName() const -> const juce::String { + return JucePlugin_Name; +} + +auto SpecletAudioProcessor::acceptsMidi() const -> bool { +#if JucePlugin_WantsMidiInput + return true; +#else + return false; +#endif +} + +auto SpecletAudioProcessor::producesMidi() const -> bool { +#if JucePlugin_ProducesMidiOutput + return true; +#else + return false; +#endif +} + +auto SpecletAudioProcessor::isMidiEffect() const -> bool { +#if JucePlugin_IsMidiEffect + return true; +#else + return false; +#endif +} + +auto SpecletAudioProcessor::getTailLengthSeconds() const -> double { + return 0.0; +} + +auto SpecletAudioProcessor::getNumPrograms() -> int { + return 1;// NB: some hosts don't cope very well if you tell them there are 0 programs, + // so this should be at least 1, even if you're not really implementing programs. +} + +auto SpecletAudioProcessor::getCurrentProgram() -> int { + return 0; +} + +void SpecletAudioProcessor::setCurrentProgram(int index) { + juce::ignoreUnused(index); +} + +auto SpecletAudioProcessor::getProgramName(int index) -> const juce::String { + juce::ignoreUnused(index); + return {}; +} + +void SpecletAudioProcessor::changeProgramName(int index, const juce::String &newName) { + juce::ignoreUnused(index, newName); +} + +void SpecletAudioProcessor::parameterChanged(const juce::String& parameterID, float newValue) { + const juce::ScopedLock myScopedLock(criticalSection); + DBG("SpecletAudioProcessor::parameterChanged: " + parameterID); + + if (SpecletParameters::isTransformationParameter(parameterID)) { + updateTransformation(); + } + if (parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_ROUTING)) { + parameterRouting = parameters.getRouting(); + } + if ((parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_GENERATOR))// + || (parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_GENERATORFREQUENCY))) { + updateSignalGenerator(); + } +} + +//============================================================================== +void SpecletAudioProcessor::prepareToPlay(double sampleRate, int /*samplesPerBlock*/) { + // Use this method as the place to do any pre-playback + // initialisation that you need.. + if (currentTransformation == nullptr) { + updateTransformation(); + } + if (signalGenerator.getSamplingRate() != sampleRate) { + signalGenerator = SignalGenerator(sampleRate, static_cast(parameters.getGenerator()), parameters.getGeneratorFrequency()); + } +} + +void SpecletAudioProcessor::releaseResources() { + // When playback stops, you can use this as an opportunity to free up any + // spare memory, etc. +} + +auto SpecletAudioProcessor::isBusesLayoutSupported(const BusesLayout &layouts) const -> bool { +#if JucePlugin_IsMidiEffect + juce::ignoreUnused(layouts); + return true; +#else + // This is the place where you check if the layout is supported. + // In this template code we only support mono or stereo. + // Some plugin hosts, such as certain GarageBand versions, will only + // load plugins that support stereo bus layouts. + if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) { + return false; + } + + // This checks if the input layout matches the output layout +#if !JucePlugin_IsSynth + if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) { + return false; + } +#endif + + return true; +#endif +} + +void SpecletAudioProcessor::processBlock(juce::AudioBuffer &buffer, + juce::MidiBuffer &midiMessages) { + juce::ignoreUnused(midiMessages); + + juce::ScopedNoDenormals noDenormals; + auto totalNumInputChannels = getTotalNumInputChannels(); + auto totalNumOutputChannels = getTotalNumOutputChannels(); + + // In case we have more outputs than inputs, this code clears any output + // channels that didn't contain input data, (because these aren't + // guaranteed to be empty - they may contain garbage). + // This is here to avoid people getting screaming feedback + // when they first compile a plugin, but obviously you don't need to keep + // this code if your algorithm always overwrites all the output channels. + for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) { + buffer.clear(i, 0, buffer.getNumSamples()); + } + + const juce::ScopedLock myScopedLock(criticalSection); + parameters.blockParameterChanges(); + + const int numSamples = buffer.getNumSamples(); + const int numChannels = totalNumInputChannels; + + const float *inR = nullptr; + const float *inL = nullptr; + + if (numChannels <= 0) { + return; + } + if (numChannels == 1) { + inR = buffer.getReadPointer(0); + inL = buffer.getReadPointer(0); + } + if (numChannels >= 2) { + inR = buffer.getReadPointer(0); + inL = buffer.getReadPointer(1); + } + + for (int s = 0; s < numSamples; ++s, ++inL, ++inR) { + if (currentTransformation != nullptr) { + currentTransformation->setNextInputSample(getSampleFromRouting(inL, inR)); + } + } + // In case we have more outputs than inputs, we'll clear any output + // channels that didn't contain input data, (because these aren't + // guaranteed to be empty - they may contain garbage). + for (int i = numChannels; i < numChannels; ++i) { + buffer.clear(i, 0, numSamples); + } + + parameters.unblockParameterChanges(); +} + +//============================================================================== +auto SpecletAudioProcessor::hasEditor() const -> bool { + return true;// (change this to false if you choose to not supply an editor) +} + +auto SpecletAudioProcessor::createEditor() -> juce::AudioProcessorEditor * { + return new SpecletMainUI(*this); + // return new juce::GenericAudioProcessorEditor(*this); // generic editor for prototyping without GUI +} + +//============================================================================== +void SpecletAudioProcessor::getStateInformation(juce::MemoryBlock &destData) { + // You should use this method to store your parameters in the memory block. + // You could do that either as raw data, or use the XML or ValueTree classes + // as intermediaries to make it easy to save and load complex data. + parameters.getStateInformation(destData); +} + +void SpecletAudioProcessor::setStateInformation(const void *data, int sizeInBytes) { + // You should use this method to restore your parameters from this memory block, + // whose contents will have been created by the getStateInformation() call. + auto tree = juce::ValueTree::readFromData(data, static_cast(sizeInBytes)); + parameters.setStateInformation(tree); + if (tree.isValid() && (currentTransformation == nullptr)) { + updateTransformation(); + } +} + +//============================================================================== + +void SpecletAudioProcessor::updateTransformation() { + const juce::ScopedLock myScopedLock(criticalSection); + DBG("SpecletAudioProcessor::updateTransformation()"); + parameters.blockParameterChanges(); + + currentTransformation = nullptr; + double sampleRate = (getSampleRate() <= 100) ? DEFAULT_SAMPLINGRATE : getSampleRate(); + + currentTransformation = TransformationFactory::getSingletonInstance().createTransformation( + static_cast(parameters.getTransformation()), + sampleRate, + parameters.getResolution(), + static_cast(parameters.getWindowing()), + static_cast(parameters.getWavelet()), + static_cast(parameters.getWaveletPacketBasis())); + + parameters.unblockParameterChanges(); +} + +void SpecletAudioProcessor::updateSignalGenerator() { + double sampleRate = (getSampleRate() <= 100) ? DEFAULT_SAMPLINGRATE : getSampleRate(); + signalGenerator = SignalGenerator(sampleRate, static_cast(parameters.getGenerator()), parameters.getGeneratorFrequency()); +} + +auto SpecletAudioProcessor::getSampleFromRouting(const float *inL, const float *inR) -> float { + switch (parameterRouting) { + case SpecletParameters::ROUTING_SIDE: + return *inL - *inR; + case SpecletParameters::ROUTING_MID: + return static_cast((*inL + *inR) / 2.0); + case SpecletParameters::ROUTING_RIGHT: + return *inR; + case SpecletParameters::ROUTING_LEFT: + return *inL; + case SpecletParameters::ROUTING_GENERATOR: + return static_cast(signalGenerator.getNextSample()); + default: + return static_cast((*inL + *inR) / 2.0); + } +} + +//============================================================================== +// This creates new instances of the plugin.. +auto JUCE_CALLTYPE createPluginFilter() -> juce::AudioProcessor * { + return new SpecletAudioProcessor(); +} \ No newline at end of file diff --git a/src/SpecletPluginProcessor.h b/src/SpecletPluginProcessor.h new file mode 100644 index 00000000..6a966e1a --- /dev/null +++ b/src/SpecletPluginProcessor.h @@ -0,0 +1,97 @@ +/* + ============================================================================== + This file is part of the VST spectrum analyzer plugin "speclet" (working title) + Copyright 2011 by Johannes Troppacher + ------------------------------------------------------------------------------ + This file may use parts of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + ------------------------------------------------------------------------------ + This file may use parts of the fftw library + Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology + ------------------------------------------------------------------------------ + This file may use parts of the wave++ library + Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic + ============================================================================== +*/ +#pragma once + +#include "dsp/SignalGenerator.h" +#include "dsp/transformations/TransformationFactory.h" +#include "parameter/SpecletParameters.h" +#include +#include +#include + + + +//============================================================================== +class SpecletAudioProcessor : public juce::AudioProcessor, public juce::AudioProcessorValueTreeState::Listener +{ +public: + //============================================================================== + SpecletAudioProcessor(); + ~SpecletAudioProcessor() override; + + //============================================================================== + void prepareToPlay(double sampleRate, int samplesPerBlock) override; + void releaseResources() override; + + auto isBusesLayoutSupported(const BusesLayout &layouts) const -> bool override; + + void processBlock(juce::AudioBuffer &, juce::MidiBuffer &) override; + using AudioProcessor::processBlock; + + //============================================================================== + auto createEditor() -> juce::AudioProcessorEditor * override; + auto hasEditor() const -> bool override; + + //============================================================================== + auto getName() const -> const juce::String override; + + auto acceptsMidi() const -> bool override; + auto producesMidi() const -> bool override; + auto isMidiEffect() const -> bool override; + auto getTailLengthSeconds() const -> double override; + + //============================================================================== + auto getNumPrograms() -> int override; + auto getCurrentProgram() -> int override; + void setCurrentProgram(int index) override; + auto getProgramName(int index) -> const juce::String override; + void changeProgramName(int index, const juce::String &newName) override; + + //============================================================================== + //these methods are called, when parameter changes were recognised + void parameterChanged (const juce::String& parameterID, float newValue) override; + //============================================================================== + void getStateInformation(juce::MemoryBlock &destData) override; + void setStateInformation(const void *data, int sizeInBytes) override; + //============================================================================== + void updateTransformation(); + void updateSignalGenerator(); + //============================================================================== + // these are used to persist the UI's size - the values are stored along with the + // filter's other parameters, and the UI component will update them when it gets + // resized. + int lastUIWidth = 800; //NOLINT(readability-magic-numbers) + int lastUIHeight = 360;//NOLINT(readability-magic-numbers) + //============================================================================== + + auto getSpecletParameters() -> SpecletParameters & { return parameters; } + +private: + SpecletParameters parameters; + + //Some parameter need to be kept local (as copy), + //since they are called in critical sections + //e.g. during Audioprocessing on every sample + int parameterRouting; + Transformation *currentTransformation = nullptr; + SignalGenerator signalGenerator = SignalGenerator(); + juce::CriticalSection criticalSection; + //============================================================================== + auto getSampleFromRouting(const float *inL, const float *inR) -> float; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SpecletAudioProcessor) +}; diff --git a/src/config/AppConfig.h b/src/config/AppConfig.h deleted file mode 100644 index 8dba4ef6..00000000 --- a/src/config/AppConfig.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - - IMPORTANT! This file is auto-generated by the Jucer each time you save your - project - if you alter its contents, your changes may be overwritten! - - If you want to change any of these values, use the Jucer to do so, rather than - editing this file directly! - - Any commented-out settings will fall back to using the default values that - they are given in juce_Config.h - -*/ - -/* NOTE: These configs aren't available when you're linking to the juce library statically! - If you need to set a configuration that differs from the default, you'll need - to include the amalgamated Juce files. -*/ - -//#define JUCE_FORCE_DEBUG -//#define JUCE_LOG_ASSERTIONS -//#define JUCE_ASIO -//#define JUCE_WASAPI -//#define JUCE_DIRECTSOUND -//#define JUCE_ALSA -#define JUCE_QUICKTIME 0 -//#define JUCE_OPENGL -//#define JUCE_DIRECT2D -//#define JUCE_USE_FLAC -//#define JUCE_USE_OGGVORBIS -//#define JUCE_USE_CDBURNER -//#define JUCE_USE_CDREADER -//#define JUCE_USE_CAMERA -//#define JUCE_ENABLE_REPAINT_DEBUGGING -//#define JUCE_USE_XINERAMA -//#define JUCE_USE_XSHM -//#define JUCE_USE_XRENDER -//#define JUCE_USE_XCURSOR -//#define JUCE_PLUGINHOST_VST -//#define JUCE_PLUGINHOST_AU -//#define JUCE_ONLY_BUILD_CORE_LIBRARY -//#define JUCE_WEB_BROWSER -//#define JUCE_SUPPORT_CARBON -//#define JUCE_CHECK_MEMORY_LEAKS -//#define JUCE_CATCH_UNHANDLED_EXCEPTIONS - - diff --git a/src/config/JucePluginCharacteristics.h b/src/config/JucePluginCharacteristics.h deleted file mode 100644 index d604d0dc..00000000 --- a/src/config/JucePluginCharacteristics.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - - IMPORTANT! This file is auto-generated by the Jucer each time you save your - project - if you alter its contents, your changes may be overwritten! - - This header file contains configuration options for the plug-in. If you need to change any of - these, it'd be wise to do so using the Jucer, rather than editing this file directly... - -*/ - -#ifndef __PLUGINCHARACTERISTICS_D4EFFF1A__ -#define __PLUGINCHARACTERISTICS_D4EFFF1A__ - -#define JucePlugin_Build_VST 1 // (If you change this value, you'll also need to re-export the projects using the Jucer) -#define JucePlugin_Build_AU 0 // (If you change this value, you'll also need to re-export the projects using the Jucer) -#define JucePlugin_Build_RTAS 0 // (If you change this value, you'll also need to re-export the projects using the Jucer) - -#if NDEBUG -#define JucePlugin_Name "Speclet (built 11.6.)" -#else -#define JucePlugin_Name "Speclet (built 11.6. debug)" -#endif -#define JucePlugin_Desc "Spectrum Analyzer" -#define JucePlugin_Manufacturer "Johannes Troppacher" -#define JucePlugin_ManufacturerCode 'TroJ' -#define JucePlugin_PluginCode 'SpcJ' -#define JucePlugin_MaxNumInputChannels 2 -#define JucePlugin_MaxNumOutputChannels 2 -#define JucePlugin_PreferredChannelConfigurations {1, 1}, {2, 2} -#define JucePlugin_IsSynth 0 -#define JucePlugin_WantsMidiInput 0 -#define JucePlugin_ProducesMidiOutput 0 -#define JucePlugin_SilenceInProducesSilenceOut 0 -#define JucePlugin_TailLengthSeconds 0 -#define JucePlugin_EditorRequiresKeyboardFocus 1 -#define JucePlugin_VersionCode 0x09000 -#define JucePlugin_VersionString "1.0.0" -#define JucePlugin_VSTUniqueID JucePlugin_PluginCode -#define JucePlugin_VSTCategory kPlugCategEffect -#define JucePlugin_AUMainType kAudioUnitType_Effect -#define JucePlugin_AUSubType JucePlugin_PluginCode -#define JucePlugin_AUExportPrefix JuceDemoProjectAU -#define JucePlugin_AUExportPrefixQuoted "SpecletAU" -#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode -#define JucePlugin_CFBundleIdentifier com.rawmaterialsoftware.JuceDemoPlugin -#define JucePlugin_AUCocoaViewClassName JuceDemoProjectAU_V1 -#define JucePlugin_RTASCategory ePlugInCategory_None -#define JucePlugin_RTASManufacturerCode JucePlugin_ManufacturerCode -#define JucePlugin_RTASProductId JucePlugin_PluginCode -#define JUCE_USE_VSTSDK_2_4 1 - -#endif // __PLUGINCHARACTERISTICS_D4EFFF1A__ diff --git a/src/data/SpectralDataBuffer.cpp b/src/data/SpectralDataBuffer.cpp index d911d1d2..74c4e814 100644 --- a/src/data/SpectralDataBuffer.cpp +++ b/src/data/SpectralDataBuffer.cpp @@ -1,83 +1,90 @@ #include "SpectralDataBuffer.h" -#include "../utilities/PerformanceManager.h" +#include "../utilities/PerformanceLogger.h" +#include "juce_core/juce_core.h" +#include -SpectralDataBuffer::SpectralDataBuffer() { - mWriteAccess = false; - mItemSize = 0; - sizeCheckCounter = 0; - buffer = new std::list(); +SpectralDataBuffer::SpectralDataBuffer() + : buffer(new std::list()), mWriteAccess(false), sizeCheckCounter(0) { } SpectralDataBuffer::~SpectralDataBuffer() { - if (buffer) delete buffer; - buffer = 0; + delete buffer; + buffer = nullptr; } -void SpectralDataBuffer::write(ItemType item) { -// const ScopedLock myScopedLock (criticalSection); - mWriteAccess = true; - PerformanceManager::getSingletonInstance()->start(T("bufferWrite")); - - //if there are too many buffer entries, - //delete the old ones to avoid memory problems - if (sizeCheckCounter > SIZECHECKCOUNT) { - sizeCheckCounter = 0; - unsigned int size = buffer->size(); - if (size > CAPACITY) { - if (buffer) buffer->clear(); - DBG(T("SpectralDataBuffer::write: buffer had to be cleared, size was " ) + juce::String(size)); - } - } - sizeCheckCounter++; - - if (buffer) buffer->push_back(item); - PerformanceManager::getSingletonInstance()->stop(T("bufferWrite")); - mWriteAccess = false; +void SpectralDataBuffer::write(const ItemType &item) { + // const ScopedLock myScopedLock (criticalSection); + LOG_PERFORMANCE_OF_SCOPE("SpectralDataBuffer write"); + mWriteAccess = true; + + //if there are too many buffer entries, + //delete the old ones to avoid memory problems + if (sizeCheckCounter > SIZECHECKCOUNT) { + sizeCheckCounter = 0; + auto size = buffer->size(); + if (size > CAPACITY) { + if (buffer != nullptr) { + buffer->clear(); + } + DBG("SpectralDataBuffer::write: buffer had to be cleared, size was " + juce::String(size)); + } + } + sizeCheckCounter++; + + if (buffer != nullptr) { + buffer->push_back(item); + } + mWriteAccess = false; } -void SpectralDataBuffer::read(ItemType* pItem) { - PerformanceManager::getSingletonInstance()->start(T("bufferRead")); - - assert(pItem); - if (mWriteAccess) return; - if (!buffer) return; - - pItem->assign(buffer->front().begin(), buffer->front().end()); - if (!buffer->empty()) buffer->pop_front(); - //TODO faster buffer? threadsafe? - - PerformanceManager::getSingletonInstance()->stop(T("bufferRead")); +void SpectralDataBuffer::read(ItemType *pItem) { + LOG_PERFORMANCE_OF_SCOPE("SpectralDataBuffer read"); + + assert(pItem); + if (mWriteAccess) { + return; + } + if (buffer == nullptr) { + return; + } + + pItem->assign(buffer->front().begin(), buffer->front().end()); + if (!buffer->empty()) { + buffer->pop_front(); + } + //TODO(JohT) faster buffer? threadsafe? use juce framework solution? } -SpectralDataBuffer::ItemSizeType SpectralDataBuffer::size(void) { - if (!buffer) return 0; - return buffer->size(); +auto SpectralDataBuffer::size() -> SpectralDataBuffer::ItemSizeType { + if (buffer == nullptr) { + return 0; + } + return buffer->size(); } -SpectralDataBuffer::ItemSizeType SpectralDataBuffer::unread(void) { - if (!buffer) return 0; - return buffer->size(); +auto SpectralDataBuffer::unread() -> SpectralDataBuffer::ItemSizeType { + if (buffer == nullptr) { + return 0; + } + return buffer->size(); } -SpectralDataBuffer::ItemStatisticsType SpectralDataBuffer::getStatistics(ItemType* pItem) { - assert(pItem); - ItemStatisticsType statistics; - - double value_sum = 0.0; - - for (unsigned int i = 0; i < pItem->size(); i++) { - float value = pItem->at(i); - - if (i==0) { - statistics.min = value; - statistics.max = value; - } - if (value > statistics.max) statistics.max = value; - if (value < statistics.min) statistics.min = value; - - value_sum = value_sum + value; - } - statistics.avg = value_sum / (float)pItem->size(); - - return statistics; +SpectralDataBuffer::ItemStatisticsType::ItemStatisticsType(ItemType &item) { + if (item.empty()) { + return; + } + min = item.front(); + max = item.front(); + double sum = 0.0; + + for (ValueType &value : item) { + if (value < min) { + min = value; + } + if (value > max) { + max = value; + } + sum = sum + value; + } + avg = static_cast(sum / static_cast(item.size())); } \ No newline at end of file diff --git a/src/data/SpectralDataBuffer.h b/src/data/SpectralDataBuffer.h index 7171c587..1b1aebd6 100644 --- a/src/data/SpectralDataBuffer.h +++ b/src/data/SpectralDataBuffer.h @@ -15,55 +15,45 @@ */ #pragma once -#include -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" #include "../data/SpectralDataInfo.h" +#include #include -#include "assert.h" class SpectralDataBuffer { public: - static const long CAPACITY = 5000; //50000 - static const int MAXHISTORYELEMENTS = 50; - static const int SIZECHECKCOUNT = 500; + static const long CAPACITY = 5000;//50000 + static const int MAXHISTORYELEMENTS = 50; + static const int SIZECHECKCOUNT = 500; - typedef std::vector ItemType; - typedef std::vector::size_type ItemSizeType; - typedef std::vector::iterator ItemIteratorType; + using ValueType = float; + using ItemType = std::vector; + using ItemSizeType = std::vector::size_type; - //types for per spectrum statistic - typedef struct { - float min; - float max; - float avg; - } ItemStatisticsType; + //types for per spectrum statistic + struct ItemStatisticsType { + ValueType min = 0.0; + ValueType max = 0.0; + ValueType avg = 0.0; -public: - SpectralDataBuffer(); - ~SpectralDataBuffer(); + ItemStatisticsType(ItemType &item); + }; - void write(ItemType item); - void read(ItemType* pItem); + SpectralDataBuffer(); + ~SpectralDataBuffer(); + SpectralDataBuffer(const SpectralDataBuffer &) = delete; //No copy contructor + SpectralDataBuffer(SpectralDataBuffer &&) = delete; //No move contructor + auto operator=(const SpectralDataBuffer &) -> SpectralDataBuffer & = delete;//No copy assignment + auto operator=(SpectralDataBuffer &&) -> SpectralDataBuffer & = delete; //No move assignment - ItemSizeType size(void); - ItemSizeType unread(void); + void write(const ItemType &item); + void read(ItemType *pItem); - ItemStatisticsType getStatistics(ItemType* pItem); + auto size() -> ItemSizeType; + auto unread() -> ItemSizeType; private: - SpectralDataBuffer(const SpectralDataBuffer&); // no copy constructor - SpectralDataBuffer& operator = (const SpectralDataBuffer&); // no assign operator + std::list *buffer; - #if __USE_BOOST - Bounded_Buffer* buffer; - #else - std::list* buffer; - //CriticalSection criticalSection; - #endif - - ItemSizeType mItemSize; - bool mWriteAccess; - int sizeCheckCounter; + bool mWriteAccess; + int sizeCheckCounter; }; - - diff --git a/src/data/SpectralDataInfo.cpp b/src/data/SpectralDataInfo.cpp index b9fb7f99..7af80cf7 100644 --- a/src/data/SpectralDataInfo.cpp +++ b/src/data/SpectralDataInfo.cpp @@ -1,97 +1,52 @@ #include "SpectralDataInfo.h" #include "../dsp/transformations/Transformation.h" -#include #include -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" SpectralDataInfo::SpectralDataInfo( - double samplingRate, - long resolution, - long frequencyResolution, - long timeResolution, - double frequencyPartitionSize) - -: mSamplingFrequency(samplingRate), - mResolution(resolution), - mFrequencyResolution(frequencyResolution), - mTimeResolution(timeResolution), - mFrequencyPartitionSize(frequencyPartitionSize), - mMaxFrequency(samplingRate / 2.0), - mTimeResolutionMs(mResolution / mTimeResolution / mSamplingFrequency * 1000) -{ - if (frequencyResolution == 0) { - //default for frequencyResolution (fits for FFT) - mFrequencyResolution = (long)((samplingRate/2.0)+1.0); - } - if (frequencyPartitionSize == 0) { - //default for frequencyResolution (fits for FFT) - mFrequencyPartitionSize = 1.0/(double)frequencyResolution; - } - if (mTimeResolution > Transformation::TIME_RESOLUTION_LIMIT) { - mTimeResolution = Transformation::TIME_RESOLUTION_LIMIT; - } - - assert(samplingRate > 100); - assert(frequencyResolution >= 1); - assert(mFrequencyPartitionSize > 0.0000001); - - DBG(T("SpectralDataInfo constructed with res=") + - juce::String(resolution) + - T(",fres=") + juce::String(frequencyResolution) + - T(",tres=") + juce::String(timeResolution) + - T(",part=") + juce::String(mFrequencyPartitionSize) - ); -}; - -SpectralDataInfo::~SpectralDataInfo(void) { + double newSamplingRate, + ResolutionType newResolution, + ResolutionType newFrequencyResolution, + ResolutionType newTimeResolution, + double newFrequencyPartitionSize) + + : samplingFrequency(newSamplingRate), + resolution(newResolution), + frequencyResolution(newFrequencyResolution), + timeResolution(newTimeResolution), + frequencyPartitionSize(newFrequencyPartitionSize), + maxFrequency(newSamplingRate / 2.0), + timeResolutionMs(static_cast(newResolution) / static_cast(newTimeResolution) / samplingFrequency * 1000) { + + if (newFrequencyResolution == 0) { + //default for frequencyResolution (fits for FFT) + frequencyResolution = static_cast(lrint((newSamplingRate / 2.0) + 1.0)); + } + if (newFrequencyPartitionSize == 0) { + //default for frequencyResolution (fits for FFT) + frequencyPartitionSize = 1.0 / static_cast(newFrequencyResolution); + } + if (timeResolution > Transformation::TIME_RESOLUTION_LIMIT) { + timeResolution = Transformation::TIME_RESOLUTION_LIMIT; + } + const auto minFrequencyPartitionSize = 0.0000001; + + assert(newSamplingRate > 100); + assert(resolution > 0); + assert(newFrequencyResolution >= 1); + assert(frequencyPartitionSize > minFrequencyPartitionSize); + + DBG("SpectralDataInfo constructed" + toString()); } -bool SpectralDataInfo::operator == (SpectralDataInfo& compareObject) { - if (!&compareObject) return false; - if (mSamplingFrequency != compareObject.getSamplingFrequency()) return false; - if (mResolution != compareObject.getResolution()) return false; - if (mFrequencyResolution != compareObject.getFrequencyResolution()) return false; - if (mTimeResolution != compareObject.getTimeResolution()) return false; - if (mFrequencyPartitionSize != compareObject.getFrequencyPartitionSize()) return false; - - return true; +//TODO (JohT) Granular partition size not yet implemented +auto SpectralDataInfo::constgetSpectralLineFrequencyPartitionSize(ResolutionType /*spectralLineNr*/) const -> double { + return frequencyPartitionSize; } -double SpectralDataInfo::getSamplingFrequency(void) { - return mSamplingFrequency; -}; - -double SpectralDataInfo::getMaxFrequency(void) { - return mMaxFrequency; -}; - -long SpectralDataInfo::getResolution(void) { - return mResolution; -}; - -//gets the spectral line count within mSamplingFrequency/2 Hz -long SpectralDataInfo::getFrequencyResolution(void) { - return mFrequencyResolution; -}; - -//gets the spectral line count within 1/mResolution ms -long SpectralDataInfo::getTimeResolution(void) { - return mTimeResolution; -}; - -//gets the time resolution in ms (best resolution in case of non lin time res.) -double SpectralDataInfo::getTimeResolutionMs(void) { - return mTimeResolutionMs; -}; - -//gets the freq. partition (e.g. 1/32 -> partition [Hz] = mSamplingFrequency/2 split into 1/32) -double SpectralDataInfo::getFrequencyPartitionSize(void) { - return mFrequencyPartitionSize; -}; - -double SpectralDataInfo::getSpectralLineFrequencyPartitionSize (long spectralLineNr) { -//e.g. FFT mFrequencyPartitionSize=1/16: 1/16, 1/16, 1/16.... -//e.g. DWT mFrequencyPartitionSize=1/16, Progression=2: 1/16, 1/16, 1/8, 1/4, 1/2 - assert(spectralLineNr >= 0); - return mFrequencyPartitionSize; +auto SpectralDataInfo::toString() const -> std::string { + return "SpectralDataInfo(samplingFrequency=" + std::to_string(samplingFrequency) + + ",resolution=" + std::to_string(resolution) + + ",frequency resolution=" + std::to_string(frequencyResolution) + + ",time resolution=" + std::to_string(timeResolution) + + ",partition size=" + std::to_string(frequencyPartitionSize) + ")"; } \ No newline at end of file diff --git a/src/data/SpectralDataInfo.h b/src/data/SpectralDataInfo.h index cf0aab09..36897a37 100644 --- a/src/data/SpectralDataInfo.h +++ b/src/data/SpectralDataInfo.h @@ -14,42 +14,90 @@ ============================================================================== */ #pragma once -#include + +#include class SpectralDataInfo { -public: - SpectralDataInfo( - double samplingRate, - long resolution, - long frequencyResolution = 0, - long timeResolution = 1, - double frequencyPartitionRatio = 0.0 - ); - ~SpectralDataInfo(void); - bool operator == (SpectralDataInfo& compareObject); - - double getSamplingFrequency(void); - double getMaxFrequency(void); - long getResolution(void); - long getFrequencyResolution(void); //spectral line count within mSamplingFrequency/2 Hz ("vertical resolution") - long getTimeResolution(void); //spectral line count within 1/mResolution ms ("horizontal resolution") - double getTimeResolutionMs(void); //time resolution in ms (best resolution in case of non lin time res.) - double getFrequencyPartitionSize(void); //(e.g. 1/32 -> partition [Hz] = mSamplingFrequency/2 split into 1/32) - double getSpectralLineFrequencyPartitionSize (long spectralLineNr); +public: + using ResolutionType = unsigned long; + + SpectralDataInfo( + double newSamplingRate, + ResolutionType newResolution, + ResolutionType newFrequencyResolution = 0, + ResolutionType newTimeResolution = 1, + double newFrequencyPartitionSize = 0.0); + + SpectralDataInfo() = delete; // no default constructor + ~SpectralDataInfo() = default;// default destructor + + SpectralDataInfo(const SpectralDataInfo &) = default; // default copy constructor + auto operator=(const SpectralDataInfo &) -> SpectralDataInfo & = default;// default assignment operator + SpectralDataInfo(SpectralDataInfo &&) = default; // default move constructor + auto operator=(SpectralDataInfo &&) -> SpectralDataInfo & = default; // default move assignment operator + + auto operator==(SpectralDataInfo &compareObject) -> bool; + + [[nodiscard]] auto getSamplingFrequency() const -> double { + return samplingFrequency; + } + [[nodiscard]] auto getMaxFrequency() const -> double { + return maxFrequency; + } + [[nodiscard]] auto getResolution() const -> ResolutionType { + return resolution; + } + /** + * @brief Gets the spectral line count within mSamplingFrequency/2 Hz ("vertical resolution") + * + * @return ResolutionType + */ + [[nodiscard]] auto getFrequencyResolution() const -> ResolutionType { + return frequencyResolution; + } + /** + * @brief Gets the spectral line count within 1/mResolution ms ("horizontal resolution") + * + * @return ResolutionType + */ + [[nodiscard]] auto getTimeResolution() const -> ResolutionType { + return timeResolution; + } + /** + * @brief Gets the time resolution in ms (best resolution in case of non linear time resolution) + * + * @return double + */ + [[nodiscard]] auto getTimeResolutionMs() const -> double { + return timeResolutionMs; + } + /** + * @brief Gets the frequency partition (e.g. 1/32 -> partition [Hz] = mSamplingFrequency/2 split into 1/32) + * + * @return double + */ + [[nodiscard]] auto getFrequencyPartitionSize() const -> double { + return frequencyPartitionSize; + } + /** + * @brief Gets the relative frequency partition size for the specified spectral line number. + * Example 1: Fast Fourier Transformation FFT: mFrequencyPartitionSize=1/16: 1/16, 1/16, 1/16.... + * Example 2: Discrete (Decimated) Wavelet Transform DWT: mFrequencyPartitionSize=1/16, Progression=2: 1/16, 1/16, 1/8, 1/4, 1/2 + * @param spectralLineNr ResolutionType + * @return double + */ + [[nodiscard]] auto constgetSpectralLineFrequencyPartitionSize(ResolutionType spectralLineNr) const -> double; + + [[nodiscard]] auto toString() const -> std::string; private: - SpectralDataInfo(const SpectralDataInfo&); // no copy constructor - SpectralDataInfo& operator = (const SpectralDataInfo&); // no assign operator - SpectralDataInfo(void) {}; // no default constructor - - double mSamplingFrequency; - long mResolution; - long mFrequencyResolution; - long mTimeResolution; - //fields for non constant frequency partitions - double mFrequencyPartitionSize; - //calculated fields - double mMaxFrequency; - double mTimeResolutionMs; + double samplingFrequency; + ResolutionType resolution; + ResolutionType frequencyResolution; + ResolutionType timeResolution; + //fields for non constant frequency partitions + double frequencyPartitionSize; + //calculated fields + double maxFrequency; + double timeResolutionMs; }; - diff --git a/src/dsp/SignalGenerator.cpp b/src/dsp/SignalGenerator.cpp index f65055dc..d3aade61 100644 --- a/src/dsp/SignalGenerator.cpp +++ b/src/dsp/SignalGenerator.cpp @@ -1,114 +1,109 @@ #include "SignalGenerator.h" -#include "ranlib.h" - -#include -#include -#define PIx2 6.2831853071795865 - -SignalGenerator::SignalGenerator(short signalType, double signalFrequency, double samplingFrequency) { - mSignalType = signalType; - mSignalFrequency = signalFrequency; - mSamplingFrequency = samplingFrequency; - mLastSignalGeneratorArgument = 0.0; - mLastSignalGeneratorSample = 0.0; - - if (signalType == SpectronParameters::GENERATOR_NOISE) { - long iseed1 =333; - long iseed2 = (long) (.987654321 * (double)iseed1); - setall(iseed1,(long) iseed2); - } +#include "SignalGeneratorParameters.h" +#include +#include + +SignalGenerator::SignalGenerator(double newSamplingFrequency, SignalGeneratorParameters::Waveform newWaveform, double newSignalFrequency) + : waveform(newWaveform), + signalFrequency(newSignalFrequency), + samplingFrequency(newSamplingFrequency), + randomMersenneTwisterEngine(std::random_device{}()), + randomDistribution(std::uniform_real_distribution(-1.0, std::nextafter(1.0, DBL_MAX))) { } -SignalGenerator::~SignalGenerator(void){ +auto SignalGenerator::getNextSample() -> double { + switch (waveform) { + case SignalGeneratorParameters::Waveform::SINE: + return generateSine(); + case SignalGeneratorParameters::Waveform::TRIANGLE: + return generateTriangle(); + case SignalGeneratorParameters::Waveform::RAMP: + return generateRamp(); + case SignalGeneratorParameters::Waveform::SQUARE: + return generateSquare(); + case SignalGeneratorParameters::Waveform::NOISE: + return generateNoise(); + case SignalGeneratorParameters::Waveform::NUMBER_OF_OPTIONS: + default: { + //ignore if wrong: use sine as default + return generateSine(); + } + } } -double SignalGenerator::getNextSample(void) { - switch (mSignalType) { - case SpectronParameters::GENERATOR_SINE: return generateSine(); - case SpectronParameters::GENERATOR_TRANGLE: return generateTriangle(); - case SpectronParameters::GENERATOR_RAMP: return generateRamp(); - case SpectronParameters::GENERATOR_SQUARE: return generateSquare(); - case SpectronParameters::GENERATOR_NOISE: return generateNoise(); - default: { - //ignore if wrong: use sine as default - return generateSine(); - } - } -} +auto SignalGenerator::generateSine() -> double { + double value = sin(lastSignalGeneratorArgument); + lastSignalGeneratorSample = value; + + lastSignalGeneratorArgument = lastSignalGeneratorArgument + (PI_TIMES_2 / samplingFrequency * signalFrequency); + if (lastSignalGeneratorArgument > PI_TIMES_2) { + lastSignalGeneratorArgument -= PI_TIMES_2; + } -double SignalGenerator::generateSine (void) { - double value = sin(mLastSignalGeneratorArgument); - mLastSignalGeneratorSample = value; - - mLastSignalGeneratorArgument = mLastSignalGeneratorArgument + (PIx2 / mSamplingFrequency * mSignalFrequency); - if (mLastSignalGeneratorArgument > PIx2) mLastSignalGeneratorArgument-=PIx2; - - return value; + return value; } -double SignalGenerator::generateTriangle (void) { - if (mLastSignalGeneratorArgument == 0.0) { - //first call - short cut - mLastSignalGeneratorArgument = 4.0*mSignalFrequency/mSamplingFrequency; - mLastSignalGeneratorSample = 0.0; - return 0.0; - } - - double value = mLastSignalGeneratorSample + mLastSignalGeneratorArgument; - - if ((mLastSignalGeneratorArgument >= 0.0) - && (value > 1.0)) { - mLastSignalGeneratorArgument*=-1.0; - value = mLastSignalGeneratorSample + mLastSignalGeneratorArgument; - } - if ((mLastSignalGeneratorArgument < 0.0) - && (value < -1.0)) { - mLastSignalGeneratorArgument*=-1.0; - value = mLastSignalGeneratorSample + mLastSignalGeneratorArgument; - } - - mLastSignalGeneratorSample = value; - return value; +auto SignalGenerator::generateTriangle() -> double { + if (lastSignalGeneratorArgument == 0.0) { + //first call - short cut + lastSignalGeneratorArgument = 4.0 * signalFrequency / samplingFrequency; + lastSignalGeneratorSample = 0.0; + return 0.0; + } + + double value = lastSignalGeneratorSample + lastSignalGeneratorArgument; + + if ((lastSignalGeneratorArgument >= 0.0) && (value > 1.0)) { + lastSignalGeneratorArgument *= -1.0; + value = lastSignalGeneratorSample + lastSignalGeneratorArgument; + } + if ((lastSignalGeneratorArgument < 0.0) && (value < -1.0)) { + lastSignalGeneratorArgument *= -1.0; + value = lastSignalGeneratorSample + lastSignalGeneratorArgument; + } + + lastSignalGeneratorSample = value; + return value; } -double SignalGenerator::generateRamp (void) { - if (mLastSignalGeneratorArgument == 0.0) { - //first call - short cut - mLastSignalGeneratorArgument = 2.0*mSignalFrequency/mSamplingFrequency; - mLastSignalGeneratorSample = -1.0; - return -1.0; - } +auto SignalGenerator::generateRamp() -> double { + if (lastSignalGeneratorArgument == 0.0) { + //first call - short cut + lastSignalGeneratorArgument = 2.0 * signalFrequency / samplingFrequency; + lastSignalGeneratorSample = -1.0; + return -1.0; + } - double value = mLastSignalGeneratorSample + mLastSignalGeneratorArgument; + double value = lastSignalGeneratorSample + lastSignalGeneratorArgument; - if (value > 1.0) { - value = -1.0; - } + if (value > 1.0) { + value = -1.0; + } - mLastSignalGeneratorSample = value; - return value; + lastSignalGeneratorSample = value; + return value; } -double SignalGenerator::generateSquare (void) { - double value = 0.0; - - if (mLastSignalGeneratorArgument <= (0.5 / mSignalFrequency)) { - value = 1.0; - mLastSignalGeneratorArgument = mLastSignalGeneratorArgument + 1.0 / mSamplingFrequency; - } else { - if (mLastSignalGeneratorArgument < (1.0 / mSignalFrequency)) { - value = -1.0; - mLastSignalGeneratorArgument = mLastSignalGeneratorArgument + 1.0 / mSamplingFrequency; - } else { - value = 1.0; - mLastSignalGeneratorArgument = 0.0; - } - } - - mLastSignalGeneratorSample = value; - return value; +auto SignalGenerator::generateSquare() -> double { + double value = 0.0; + + if (lastSignalGeneratorArgument <= (0.5 / signalFrequency)) { + value = 1.0; + lastSignalGeneratorArgument = lastSignalGeneratorArgument + 1.0 / samplingFrequency; + } else { + if (lastSignalGeneratorArgument < (1.0 / signalFrequency)) { + value = -1.0; + lastSignalGeneratorArgument = lastSignalGeneratorArgument + 1.0 / samplingFrequency; + } else { + value = 1.0; + lastSignalGeneratorArgument = 0.0; + } + } + + lastSignalGeneratorSample = value; + return value; } -double SignalGenerator::generateNoise (void) { - return (double)gennor(0,1); -} +auto SignalGenerator::generateNoise() -> double { + return randomDistribution(randomMersenneTwisterEngine); +} \ No newline at end of file diff --git a/src/dsp/SignalGenerator.h b/src/dsp/SignalGenerator.h index d29e15a8..c1764e49 100644 --- a/src/dsp/SignalGenerator.h +++ b/src/dsp/SignalGenerator.h @@ -14,31 +14,39 @@ ============================================================================== */ #pragma once -#include "..\plugin\SpectronParameters.h" +#include "../parameter/SpecletParameters.h" +#include "SignalGeneratorParameters.h" +#include class SignalGenerator { public: - SignalGenerator( - short signalType = SpectronParameters::GENERATOR_DEFAULT, - double signalFrequency = 441.0, - double samplingFrequency = 44100.0 - ); - ~SignalGenerator(void); - - double getNextSample(void); + explicit SignalGenerator( + double newSamplingFrequency = DEFAULT_SAMPLING_FREQUENCY, + SignalGeneratorParameters::Waveform newWaveform = SignalGeneratorParameters::Waveform::DEFAULT, + double newSignalFrequency = DEFAULT_GENERATOR_FREQUENCY); -private: - double generateSine (void); - double generateTriangle (void); - double generateRamp (void); - double generateSquare (void); - double generateNoise (void); + auto getNextSample() -> double; + auto getSamplingRate() const -> double { + return samplingFrequency; + } private: - double mLastSignalGeneratorArgument; - double mLastSignalGeneratorSample; - int mSignalType; - double mSignalFrequency; - double mSamplingFrequency; -}; + constexpr static const double DEFAULT_GENERATOR_FREQUENCY = 441.0; + constexpr static const double DEFAULT_SAMPLING_FREQUENCY = 44100.0; + constexpr static const double PI_TIMES_2 = 6.283185307179586476925286766559; + + double lastSignalGeneratorArgument = 0.0F; + double lastSignalGeneratorSample = 0.0F; + SignalGeneratorParameters::Waveform waveform; + double signalFrequency; + double samplingFrequency; + std::mt19937 randomMersenneTwisterEngine; + std::uniform_real_distribution randomDistribution; + + auto generateSine() -> double; + auto generateTriangle() -> double; + auto generateRamp() -> double; + auto generateSquare() -> double; + auto generateNoise() -> double; +}; diff --git a/src/dsp/SignalGeneratorParameters.h b/src/dsp/SignalGeneratorParameters.h new file mode 100644 index 00000000..c15c56e4 --- /dev/null +++ b/src/dsp/SignalGeneratorParameters.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace SignalGeneratorParameters { + + enum class Waveform { + SINE = 1, + TRIANGLE, + RAMP, + SQUARE, + NOISE, + + NUMBER_OF_OPTIONS, + DEFAULT = SINE + }; + + struct WaveformNames { + static std::map createMap() { + return { + {Waveform::SINE, "Sine"}, + {Waveform::TRIANGLE, "Triangle"}, + {Waveform::RAMP, "Sawtooth"}, + {Waveform::SQUARE, "Rectangle"}, + {Waveform::NOISE, "Noise"}, + }; + } + static const std::map map; + }; + + inline const std::map WaveformNames::map = WaveformNames::createMap(); +}// namespace SignalGeneratorParameters \ No newline at end of file diff --git a/src/dsp/WindowFunctions.cpp b/src/dsp/WindowFunctions.cpp deleted file mode 100644 index 367f455a..00000000 --- a/src/dsp/WindowFunctions.cpp +++ /dev/null @@ -1,233 +0,0 @@ -//Most of the window function implementations in this class had been taken out of the fft c program -//from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) -#include "WindowFunctions.h" -#include "..\plugin\SpectronParameters.h" - -#include -#include -#include - -#define PI 3,14159265358979323846 // 1 * PI -#define PIx2 6.28318530717958647692 // 2 * PI - -//------------------------------------------------------------------------------------// -// Interface as abstract class // -//------------------------------------------------------------------------------------// -WindowFunction::WindowFunction(long resolution) { - mResolution = resolution; - windowFunctionFactors = NULL; -} - -WindowFunction::~WindowFunction(void) { - if (windowFunctionFactors) free(windowFunctionFactors); -} - -double WindowFunction::getFactor(int index) { - assert(index >= 0); - assert(index < mResolution); - - return getWindow()[index]; -} - -double* WindowFunction::getWindow() { - if (!windowFunctionFactors) { - windowFunctionFactors = (double*) malloc (sizeof(double) * mResolution); - - for (int i = 0; i < mResolution; i++) { - windowFunctionFactors[i] = calculateFactor(i); - } - } - return windowFunctionFactors; -} - -//------------------------------------------------------------------------------------// -// Factory // -//------------------------------------------------------------------------------------// -// Singleton instance variable (only one instance of this class) -WindowFunctionsFactory* WindowFunctionsFactory::pSingletonInstance = 0; - -WindowFunctionsFactory* WindowFunctionsFactory::getSingletonInstance() { -// Method to get the single instance of this class (Singleton) - if (WindowFunctionsFactory::pSingletonInstance == 0) { - WindowFunctionsFactory::pSingletonInstance = new WindowFunctionsFactory(); - } - return WindowFunctionsFactory::pSingletonInstance; -} - -void WindowFunctionsFactory::destruct() { -//deletes singleton instance - can be reactivated by calling getSingletonInstance - if (!pSingletonInstance) return; - - pSingletonInstance->deleteWindowFunctionsMap(); - - delete(pSingletonInstance); - pSingletonInstance = 0; -} - -WindowFunction* WindowFunctionsFactory::createWindowFunction(int windowFunctionNr, long resolution) { - WindowFunction* result = readWindowFunctionFromMap(windowFunctionNr, resolution); - if (result) return result; - - switch (windowFunctionNr) { - case SpectronParameters::WINDOWING_BARTLETT :{ result = (WindowFunction*)(new WindowBartlett(resolution)); break;} - case SpectronParameters::WINDOWING_BLACKMAN :{ result = (WindowFunction*)(new WindowBlackman(resolution)); break;} - case SpectronParameters::WINDOWING_BLACKMAN_HARRIS:{ result = (WindowFunction*)(new WindowBlackmanHarris(resolution));break;} - case SpectronParameters::WINDOWING_HAMMING :{ result = (WindowFunction*)(new WindowHamming(resolution)); break;} - case SpectronParameters::WINDOWING_HANNING :{ result = (WindowFunction*)(new WindowHanning(resolution)); break;} - case SpectronParameters::WINDOWING_PARZEN :{ result = (WindowFunction*)(new WindowParzen(resolution)); break;} - case SpectronParameters::WINDOWING_SQUARE :{ result = (WindowFunction*)(new WindowSquare(resolution)); break;} - case SpectronParameters::WINDOWING_WELCH :{ result = (WindowFunction*)(new WindowWelch(resolution)); break;} - default : {bool windowing_function_unknown = false; assert(windowing_function_unknown);} - } - - writeWindowFunctionIntoMap(windowFunctionNr, resolution, result); - return result; -} - -WindowFunction* WindowFunctionsFactory::readWindowFunctionFromMap(int windowFunctionNr, long resolution) { - TWindowFunctionsMapIterator windowingFunctionsIterator; - TResolutionsMapIterator resolutionsIterator; - - //Get the windowing function - windowingFunctionsIterator = windowingFunctions.find(windowFunctionNr); - if (windowingFunctionsIterator == windowingFunctions.end()) return NULL; - - //Get the resolutions map that belongs to the windowing function - TResolutionsMap resolutions = windowingFunctionsIterator->second; - - //Get the pointer to the windowing function class that belongs to that specific window function and resolution - resolutionsIterator = resolutions.find(resolution); - if (resolutionsIterator == resolutions.end()) return NULL; - return resolutionsIterator->second; -} - -void WindowFunctionsFactory::writeWindowFunctionIntoMap(int windowFunctionNr, long resolution, WindowFunction* pWindowFunction) { - TWindowFunctionsMapIterator windowingFunctionsIterator; - TResolutionsMapIterator resolutionsIterator; - - //Get the windowing function - windowingFunctionsIterator = windowingFunctions.find(windowFunctionNr); - - if (windowingFunctionsIterator == windowingFunctions.end()) { - //window function not found: new resolution map + new window function map entry - TResolutionsKeyValue resolutionMapEntry(resolution, pWindowFunction); - TResolutionsMap resolutions; - resolutions.insert(resolutionMapEntry); - - //insert the new window function entry incl. the resolution - TWindowFunctionsKeyValue windowFunctionsMapEntry(windowFunctionNr, resolutions); - windowingFunctions.insert(windowFunctionsMapEntry); - } else { - //window function found. does resolution exist in resolutionMap? - TResolutionsMap resolutions = windowingFunctionsIterator->second; - - resolutionsIterator = resolutions.find(windowFunctionNr); - if (resolutionsIterator == resolutions.end()) { - //resolution entry not found - TResolutionsKeyValue resolutionsKeyValue(resolution, pWindowFunction); - resolutions.insert(resolutionsKeyValue); - - //erase old windowing function entry to get space for the new - //one with one more resolution entry - windowingFunctions.erase(windowingFunctionsIterator); - - //insert the new window function entry incl. the old and new resolutions - TWindowFunctionsKeyValue windowFunctionsMapEntry(windowFunctionNr, resolutions); - windowingFunctions.insert(windowFunctionsMapEntry); - } else { - //resolution entry also exists, so there is nothing to be done - } - } -} - -//delete all cached windowing functions -void WindowFunctionsFactory::deleteWindowFunctionsMap() { - TWindowFunctionsMap windowFunctions = WindowFunctionsFactory::pSingletonInstance->windowingFunctions; - TWindowFunctionsMapIterator windowingFunctionsIterator = windowFunctions.begin(); - - while (windowingFunctionsIterator != windowFunctions.end()) { - TResolutionsMap resolutions = windowingFunctionsIterator->second; - TResolutionsMapIterator resolutionsIterator = resolutions.begin(); - while (resolutionsIterator != resolutions.end()) { - delete (resolutionsIterator->second); - resolutionsIterator++; - } - windowingFunctionsIterator++; - } -} - - -//------------------------------------------------------------------------------------// -// Interface implementations // -//------------------------------------------------------------------------------------// -/* See Oppenheim & Schafer, Digital Signal Processing, p. 241 (1st ed.) */ -double WindowBartlett::calculateFactor(int index) { - double a = 2.0/(mResolution-1), w; - - if ((w = index*a) > 1.0) w = 2.0 - w; - - return (w); -}; - -/* See Oppenheim & Schafer, Digital Signal Processing, p. 242 (1st ed.) */ -double WindowBlackman::calculateFactor(int index) { - double a = PIx2/(mResolution-1), w; - - w = 0.42 - 0.5*cos(a*index) + 0.08*cos(2*a*index); - return (w); -}; - -/* See Harris, F.J., "On the use of windows for harmonic analysis with the -discrete Fourier transform", Proc. IEEE, Jan. 1978 */ -double WindowBlackmanHarris::calculateFactor(int index) { - double a = PIx2/(mResolution-1), w; - - w = 0.35875 - 0.48829*cos(a*index) + 0.14128*cos(2*a*index) - 0.01168*cos(3*a*index); - return (w); -}; - -/* See Oppenheim & Schafer, Digital Signal Processing, p. 242 (1st ed.) */ -double WindowHamming::calculateFactor(int index) { - double a = PIx2/(mResolution-1), w; - - w = 0.54 - 0.46*cos(a*index); - return (w); -}; - -/* See Oppenheim & Schafer, Digital Signal Processing, p. 242 (1st ed.) -The second edition of Numerical Recipes calls this the "Hann" window. */ -double WindowHanning::calculateFactor(int index) { - double a = PIx2/(mResolution-1), w; - - w = 0.5 - 0.5*cos(a*index); - return (w); -}; - -/* See Press, Flannery, Teukolsky, & Vetterling, Numerical Recipes in C, -p. 442 (1st ed.) */ -double WindowParzen::calculateFactor(int index) { - double a = (mResolution-1)/2.0, w; - - if ((w = (index-a)/(a+1)) > 0.0) { - w = 1 - w; - } else { - w = 1 + w; - } - return (w); -}; - -/* See any of the above references. */ -double WindowSquare::calculateFactor(int index) { - return (1.0); -}; - -/* See Press, Flannery, Teukolsky, & Vetterling, Numerical Recipes in C, -p. 442 (1st ed.) or p. 554 (2nd ed.) */ -double WindowWelch::calculateFactor(int index) { - double a = (mResolution-1)/2.0, w; - - w = (index-a)/(a+1); - w = 1 - w*w; - - return (w); -}; \ No newline at end of file diff --git a/src/dsp/WindowFunctions.h b/src/dsp/WindowFunctions.h deleted file mode 100644 index 55270739..00000000 --- a/src/dsp/WindowFunctions.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - ============================================================================== - This file is part of the VST spectrum analyzer plugin "speclet" (working title) - Copyright 2011 by Johannes Troppacher - ------------------------------------------------------------------------------ - This file may use parts of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - ------------------------------------------------------------------------------ - This file may use parts of the fftw library - Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology - ------------------------------------------------------------------------------ - This file may use parts of the wave++ library - Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic - ------------------------------------------------------------------------------ - Most of the window function implementations in this class had been taken out of the fft c program - from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) - ============================================================================== -*/ -#pragma once -#include -using namespace std; - -class WindowFunction; - -//------------------------------------------------------------------------------------// -// Factory // -//------------------------------------------------------------------------------------// -class WindowFunctionsFactory { -public: - static WindowFunctionsFactory* getSingletonInstance(); - WindowFunction* createWindowFunction(int windowFunctionNr, long resolution); - void destruct(); - -private: - static WindowFunctionsFactory* pSingletonInstance; - - WindowFunctionsFactory(void) {}; - ~WindowFunctionsFactory(void) {}; - WindowFunctionsFactory(const WindowFunctionsFactory&); - - typedef map TResolutionsMap; - typedef map ::const_iterator TResolutionsMapIterator; - typedef pair TResolutionsKeyValue; - typedef map TWindowFunctionsMap; - typedef map ::const_iterator TWindowFunctionsMapIterator; - typedef pair TWindowFunctionsKeyValue; - - TWindowFunctionsMap windowingFunctions; - - WindowFunction* readWindowFunctionFromMap(int windowFunctionNr, long resolution); - void writeWindowFunctionIntoMap(int windowFunctionNr, long resolution, WindowFunction* pWindowFunction); - void WindowFunctionsFactory::deleteWindowFunctionsMap(); -}; - - - -//------------------------------------------------------------------------------------// -// Interface as abstract class // -//------------------------------------------------------------------------------------// -class WindowFunction abstract { -public: - WindowFunction(long resolution); - ~WindowFunction(void); - - double getFactor(int index); - double* getWindow(); - virtual char* getName(void) = 0; - -protected: - virtual double calculateFactor(int index) = 0; //will be redefined by the derived windowing function - - long mResolution; //also known as N or FFT-size or length of signal piece array - double* windowFunctionFactors; - -private: - WindowFunction() {} //explicit no default contructor; - WindowFunction(const WindowFunction &) {} //explicit no copy contructor; -}; - - - -//------------------------------------------------------------------------------------// -// Interface implementations // -//------------------------------------------------------------------------------------// -class WindowBartlett : WindowFunction { -public: - WindowBartlett(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //bartlett windowing function - char* getName(void) {return "Barlett";}; -}; - -class WindowBlackman : WindowFunction { -public: - WindowBlackman(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //blackman windowing function - char* getName(void) {return "Blackman";}; -}; - -class WindowBlackmanHarris : WindowFunction { -public: - WindowBlackmanHarris(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //blackman_harris windowing function - char* getName(void) {return "Blackman Harris";}; -}; - -class WindowHamming : WindowFunction { -public: - WindowHamming(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //hamming windowing function - char* getName() {return "Hamming";}; -}; - -class WindowHanning : WindowFunction { -public: - WindowHanning(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //hanning windowing function - char* getName(void) {return "Hanning";}; -}; - -class WindowParzen : WindowFunction { -public: - WindowParzen(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //parzen windowing function - char* getName(void) {return "Parzen";}; -}; - -class WindowSquare : WindowFunction { -public: - WindowSquare(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //square windowing function - char* getName(void) {return "Square";}; -}; - -class WindowWelch : WindowFunction { -public: - WindowWelch(long resolution) : WindowFunction(resolution) {}; - double calculateFactor(int index); //welch windowing function - char* getName(void) {return "Welch";}; -}; \ No newline at end of file diff --git a/src/dsp/transformations/AbstractWaveletTransformation.cpp b/src/dsp/transformations/AbstractWaveletTransformation.cpp index 5707c0e7..adc685f6 100644 --- a/src/dsp/transformations/AbstractWaveletTransformation.cpp +++ b/src/dsp/transformations/AbstractWaveletTransformation.cpp @@ -1,160 +1,155 @@ #include "AbstractWaveletTransformation.h" -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" -#include "../../utilities/PerformanceManager.h" - -AbstractWaveletTransformation::AbstractWaveletTransformation(double samplingRate, long resolution, int windowFunctionNr, int waveletBaseTypeNr) - : Transformation(samplingRate, resolution, windowFunctionNr) -{ - mDWT_Input = NULL; - mConstantLevelsHedge = NULL; - mDWTLevelsHedge = NULL; - - mWaveletBaseTypeNr = waveletBaseTypeNr; - mDWT_maxLevel = getMaxLevel(resolution); - mDWT_Input = new Interval(0, resolution-1); //wavelet transformation input data - - setWaveletBase(mWaveletBaseTypeNr); - updateConstantLevelsHedge(mDWT_maxLevel / 2); - updateDWTLevelsHedge(); - - DBG(T("AbstractWaveletTransformation::initialize done with waveletNr=") - + juce::String(mWaveletBaseTypeNr) + T("maxLevel=") + juce::String(mDWT_maxLevel)); -}; +#include "../../utilities/PerformanceLogger.h" +#include +#include +#include +#include + +AbstractWaveletTransformation::AbstractWaveletTransformation(double newSamplingRate, ResolutionType newResolution, TransformationParameters::Type newTransformationType, WindowParameters::WindowFunction newWindowFunction, WaveletParameters::WaveletBase newWaveletBase) + : Transformation(newSamplingRate, newResolution, newTransformationType, newWindowFunction), + waveletFilterTreeMaxLevel(getMaxLevel(newResolution)), + dwtInput(Interval(0, static_cast(newResolution - 1))) { + + setWaveletBase(newWaveletBase); + updateConstantLevelsHedge(waveletFilterTreeMaxLevel / 2); + updateDWTLevelsHedge(); + + DBG("AbstractWaveletTransformation::initialize done with waveletNr=" + std::string(WaveletParameters::WaveletBaseNames::map.at(newWaveletBase)) + "maxLevel=" + std::to_string(waveletFilterTreeMaxLevel)); +} AbstractWaveletTransformation::~AbstractWaveletTransformation() { - if (mDWT_Input) delete (mDWT_Input); - if (mConstantLevelsHedge) delete (mConstantLevelsHedge); - if (mDWTLevelsHedge) delete (mDWTLevelsHedge); - - mDWT_Input = NULL; - mConstantLevelsHedge = NULL; - mDWTLevelsHedge = NULL; + constantLevelsHedge = nullptr; + dWTLevelsHedge = nullptr; - DBG(T("AbstractWaveletTransformation destructed")); + DBG("AbstractWaveletTransformation destructed"); } -int AbstractWaveletTransformation::getMaxLevel(int dimension) { - int maxlevel = (int)(log((double)dimension) / log((double)2.0)); -// DBG(T("AbstractWaveletTransformation::getMaxLevel calc: logDim/log(2)", log((double)dimension), log((double)2.0)); -// DBG(T("AbstractWaveletTransformation::getMaxLevel result: dim/maxLevel=", dimension, maxlevel); - return maxlevel; +auto AbstractWaveletTransformation::getMaxLevel(ResolutionType resolution) -> WaveletLevelType { + assert(resolution > 0); + auto maxlevel = log(resolution) / log(2.0F); + DBG("AbstractWaveletTransformation::getMaxLevel resolution " + juce::String(resolution) + " leads to max level " + juce::String(maxlevel)); + return static_cast(lrint(maxlevel)); } -int AbstractWaveletTransformation::getMinLevel(const HedgePer &bestBasis) { - if ((&bestBasis == NULL) || (bestBasis.num_of_levels <= 0)) return 1; - //return 1; - - int minBestBasisLevel, level = 0; - - for(int levelNr = 0; levelNr < bestBasis.num_of_levels; levelNr++) { - level = bestBasis.levels[levelNr]; - if (level == 1) return 1; - if ((levelNr == 0) || (level < minBestBasisLevel)) { - minBestBasisLevel = level; - } - } - return minBestBasisLevel; +auto AbstractWaveletTransformation::getMinLevel(const HedgePer &bestBasis) -> WaveletLevelType { + if (bestBasis.num_of_levels <= 0) { + return 1; + } + long minBestBasisLevel = 0; + + auto levels = tcb::span(bestBasis.levels, static_cast(bestBasis.num_of_levels)); + for (auto &level : levels) { + assert(level >= 0); + if (level == 1) { + return 1; + } + if ((minBestBasisLevel == 0) || (level < minBestBasisLevel)) { + minBestBasisLevel = level; + } + } + return static_cast(minBestBasisLevel); } -void AbstractWaveletTransformation::setWaveletBase(int waveletBaseNr) { - ready = false; - - switch (waveletBaseNr) { - case SpectronParameters::WAVELET_DAUBECHIES_02: { - mDWT_filter_G.Set(d02doqf, d02dalpha, d02domega); - mDWT_filter_H.Set(d02soqf, d02salpha, d02somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_04: { - mDWT_filter_G.Set(d04doqf, d04dalpha, d04domega); - mDWT_filter_H.Set(d04soqf, d04salpha, d04somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_06: { - mDWT_filter_G.Set(d06doqf, d06dalpha, d06domega); - mDWT_filter_H.Set(d06soqf, d06salpha, d06somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_08: { - mDWT_filter_G.Set(d08doqf, d08dalpha, d08domega); - mDWT_filter_H.Set(d08soqf, d08salpha, d08somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_10: { - mDWT_filter_G.Set(d10doqf, d10dalpha, d10domega); - mDWT_filter_H.Set(d10soqf, d10salpha, d10somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_12: { - mDWT_filter_G.Set(d12doqf, d12dalpha, d12domega); - mDWT_filter_H.Set(d12soqf, d12salpha, d12somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_14: { - mDWT_filter_G.Set(d14doqf, d14dalpha, d14domega); - mDWT_filter_H.Set(d14soqf, d14salpha, d14somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_16: { - mDWT_filter_G.Set(d16doqf, d16dalpha, d16domega); - mDWT_filter_H.Set(d16soqf, d16salpha, d16somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_18: { - mDWT_filter_G.Set(d18doqf, d18dalpha, d18domega); - mDWT_filter_H.Set(d18soqf, d18salpha, d18somega); - break; - } - case SpectronParameters::WAVELET_DAUBECHIES_20: { - mDWT_filter_G.Set(d20doqf, d20dalpha, d20domega); - mDWT_filter_H.Set(d20soqf, d20salpha, d20somega); - break; - } - case SpectronParameters::WAVELET_COIFMAN_06: { - mDWT_filter_G.Set(c06doqf, c06dalpha, c06domega); - mDWT_filter_H.Set(c06soqf, c06salpha, c06somega); - break; - } - case SpectronParameters::WAVELET_COIFMAN_12: { - mDWT_filter_G.Set(c12doqf, c12dalpha, c12domega); - mDWT_filter_H.Set(c12soqf, c12salpha, c12somega); - break; - } - case SpectronParameters::WAVELET_COIFMAN_18: { - mDWT_filter_G.Set(c18doqf, c18dalpha, c18domega); - mDWT_filter_H.Set(c18soqf, c18salpha, c18somega); - break; - } - case SpectronParameters::WAVELET_COIFMAN_24: { - mDWT_filter_G.Set(c24doqf, c24dalpha, c24domega); - mDWT_filter_H.Set(c24soqf, c24salpha, c24somega); - break; - } - case SpectronParameters::WAVELET_COIFMAN_30: { - mDWT_filter_G.Set(c30doqf, c30dalpha, c30domega); - mDWT_filter_H.Set(c30soqf, c30salpha, c30somega); - break; - } - case SpectronParameters::WAVELET_BEYLKIN_18: { - mDWT_filter_G.Set(b18doqf, b18dalpha, b18domega); - mDWT_filter_H.Set(b18soqf, b18salpha, b18somega); - break; - } - case SpectronParameters::WAVELET_VAIDYANATHAN_18: { - mDWT_filter_G.Set(v24doqf, v24dalpha, v24domega); - mDWT_filter_H.Set(v24soqf, v24salpha, v24somega); - break; - } - default: { - bool unbekanntes_wavelet = false; - assert(unbekanntes_wavelet); - } - } - - DBG(T("AbstractWaveletTransformation::setWaveletBase done with waveletBaseNr=") + - juce::String(waveletBaseNr) + ",coeffSize=" + juce::String(mDWT_filter_G.pcoef_size)); - - ready = true; +void AbstractWaveletTransformation::setWaveletBase(const WaveletParameters::WaveletBase &newWaveletBase) { + setReady(false); + + switch (newWaveletBase) { + case WaveletParameters::WaveletBase::DAUBECHIES_02: { + mDwtFilterG.Set(d02doqf, d02dalpha, d02domega); + mDwtFilterH.Set(d02soqf, d02salpha, d02somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_04: { + mDwtFilterG.Set(d04doqf, d04dalpha, d04domega); + mDwtFilterH.Set(d04soqf, d04salpha, d04somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_06: { + mDwtFilterG.Set(d06doqf, d06dalpha, d06domega); + mDwtFilterH.Set(d06soqf, d06salpha, d06somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_08: { + mDwtFilterG.Set(d08doqf, d08dalpha, d08domega); + mDwtFilterH.Set(d08soqf, d08salpha, d08somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_10: { + mDwtFilterG.Set(d10doqf, d10dalpha, d10domega); + mDwtFilterH.Set(d10soqf, d10salpha, d10somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_12: { + mDwtFilterG.Set(d12doqf, d12dalpha, d12domega); + mDwtFilterH.Set(d12soqf, d12salpha, d12somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_14: { + mDwtFilterG.Set(d14doqf, d14dalpha, d14domega); + mDwtFilterH.Set(d14soqf, d14salpha, d14somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_16: { + mDwtFilterG.Set(d16doqf, d16dalpha, d16domega); + mDwtFilterH.Set(d16soqf, d16salpha, d16somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_18: { + mDwtFilterG.Set(d18doqf, d18dalpha, d18domega); + mDwtFilterH.Set(d18soqf, d18salpha, d18somega); + break; + } + case WaveletParameters::WaveletBase::DAUBECHIES_20: { + mDwtFilterG.Set(d20doqf, d20dalpha, d20domega); + mDwtFilterH.Set(d20soqf, d20salpha, d20somega); + break; + } + case WaveletParameters::WaveletBase::COIFMAN_06: { + mDwtFilterG.Set(c06doqf, c06dalpha, c06domega); + mDwtFilterH.Set(c06soqf, c06salpha, c06somega); + break; + } + case WaveletParameters::WaveletBase::COIFMAN_12: { + mDwtFilterG.Set(c12doqf, c12dalpha, c12domega); + mDwtFilterH.Set(c12soqf, c12salpha, c12somega); + break; + } + case WaveletParameters::WaveletBase::COIFMAN_18: { + mDwtFilterG.Set(c18doqf, c18dalpha, c18domega); + mDwtFilterH.Set(c18soqf, c18salpha, c18somega); + break; + } + case WaveletParameters::WaveletBase::COIFMAN_24: { + mDwtFilterG.Set(c24doqf, c24dalpha, c24domega); + mDwtFilterH.Set(c24soqf, c24salpha, c24somega); + break; + } + case WaveletParameters::WaveletBase::COIFMAN_30: { + mDwtFilterG.Set(c30doqf, c30dalpha, c30domega); + mDwtFilterH.Set(c30soqf, c30salpha, c30somega); + break; + } + case WaveletParameters::WaveletBase::BEYLKIN_18: { + mDwtFilterG.Set(b18doqf, b18dalpha, b18domega); + mDwtFilterH.Set(b18soqf, b18salpha, b18somega); + break; + } + case WaveletParameters::WaveletBase::VAIDYANATHAN_18: { + mDwtFilterG.Set(v24doqf, v24dalpha, v24domega); + mDwtFilterH.Set(v24soqf, v24salpha, v24somega); + break; + } + case WaveletParameters::WaveletBase::NUMBER_OF_OPTIONS: + default: { + bool unknownWavelet = false; + assert(unknownWavelet); + } + } + + DBG("AbstractWaveletTransformation::setWaveletBase done with waveletBaseNr=" + + std::string(WaveletParameters::WaveletBaseNames::map.at(newWaveletBase)) + ",coeffSize=" + std::to_string(mDwtFilterG.pcoef_size)); + + setReady(true); } //--------------------------------------------------------------------// @@ -163,192 +158,224 @@ void AbstractWaveletTransformation::setWaveletBase(int waveletBaseNr) { //Copying every single sample from input-queue to wavelet input-array void AbstractWaveletTransformation::fillDWTInput() { - if (!mDWT_Input) { - DBG(T("AbstractWaveletTransformation::fillDWTInput: mDWT_Input = null !")); - return; - } - for (long i = 0; i < mResolution; i++) { - double nextSample = mInputQueue->front(); - (*mDWT_Input)[i] = nextSample * mWindowFunction->getFactor(i); - mInputQueue->pop(); - } + LOG_PERFORMANCE_OF_SCOPE("AbstractWaveletTransformation fillDWTInput"); + auto *windowFunction = getWindowFunction(); + for (unsigned int i = 0; i < getResolution(); i++) { + auto nextSample = getInputQueue().front(); + dwtInput[i] = nextSample * windowFunction->getFactor(i); + getInputQueue().pop(); + } } -// sorts the tree by descending scale (ascending frequency) -void AbstractWaveletTransformation::sortDWPTTreeByScaleDescending(const ArrayTreePer &tree) { - if (!tree.origin) return; +void AbstractWaveletTransformation::sortWaveletFilterTreeByScaleDescending(const ArrayTreePer &tree) { + LOG_PERFORMANCE_OF_SCOPE("AbstractWaveletTransformation sortWaveletFilterTreeByScaleDescending"); + if (tree.origin == nullptr) { + return; + } + assert(tree.dim > 0); + for (unsigned int level = 1; level <= (tree.maxlevel - 1); level++) { + auto blockLength = tree.block_length(level); + assert(blockLength > 0); + auto blocksCount = tree.dim / blockLength; + + for (unsigned int block = 1; block < blocksCount; block += 2) { + //process only the odd elements (=right element, =results from G wavelet hipass filter) + swapWaveletFilterTreeChilds(tree, level, block); + } + } +} - for (int level = 1; level <= (tree.maxlevel - 1); level++) { - int blocksCount = tree.dim / tree.block_length(level); +void AbstractWaveletTransformation::swapWaveletFilterTreeChilds(const ArrayTreePer &tree, const WaveletLevelType &level, const unsigned int &block) { + assert(0 <= level); + assert(level < tree.maxlevel); + assert(0 <= block); + assert(block < (1U << level)); + + assert(tree.dim > 0); + auto treeDimension = static_cast(tree.dim); + + assert(tree.block_length(level + 1) > 0); + auto blocklength = static_cast(tree.block_length(level + 1)); + + auto maxSubLevel = static_cast(tree.maxlevel - level); + auto maxIndex = static_cast(blocklength + maxSubLevel * treeDimension); + + auto *leftChild = tree.left_child(level, block); + auto *rightChild = tree.right_child(level, block); + auto leftChildSpan = tcb::span(leftChild, maxIndex); + auto rightChildSpan = tcb::span(rightChild, maxIndex); + + //swap child elements and underlying sublevel-elements + for (unsigned long sublevel = 0; sublevel < maxSubLevel; sublevel++) { + for (unsigned long element = 0; element < blocklength; element++) { + auto index = element + sublevel * treeDimension; + std::swap(leftChildSpan[index], rightChildSpan[index]); + } + } +} - for (int block = 1; block < blocksCount; block+=2) { - //process only the odd elements (=right elemet, =results from G wavelet hipass filter) - swapDWPTTreeChilds(tree, level, block); - } - } +void AbstractWaveletTransformation::analyse(ArrayTreePer &analysisResult) const { + Analysis(getDwtInput(), analysisResult, mDwtFilterH, mDwtFilterG, ConvDecPer); } -// swaps the right and the left child of a wavelet packet transform tree node ("block") -void AbstractWaveletTransformation::swapDWPTTreeChilds(const ArrayTreePer &tree, const integer &L, const integer &B) { - assert( 0<=L && L 0); - extractSpectrum(TRANSFORM_RESULT_CLASS_INTERVAL, out_DWT.origin, *mDWTLevelsHedge); +void AbstractWaveletTransformation::extractSpectrum(const Interval &outDWT) { + assert(outDWT.length > 0); + assert(outDWT.origin != nullptr); + auto waveletTransformOutput = tcb::span(outDWT.origin, static_cast(outDWT.length)); + extractSpectrum(TRANSFORM_RESULT_CLASS_INTERVAL, waveletTransformOutput, *dWTLevelsHedge); } -void AbstractWaveletTransformation::extractSpectrum(const ArrayTreePer &out_DWPT, const HedgePer &levelsHedge) { - assert(&out_DWPT); - assert(out_DWPT.maxlevel > 0); - extractSpectrum(TRANSFORM_RESULT_CLASS_ARRAYTREE, out_DWPT.origin, levelsHedge); +void AbstractWaveletTransformation::extractSpectrum(const ArrayTreePer &outWaveletPacketTree) { + extractSpectrum(outWaveletPacketTree, *constantLevelsHedge); } -void AbstractWaveletTransformation::extractSpectrum(int transformResultClass, real_DWT* origin, const HedgePer &levelsHedge) { - PerformanceManager::getSingletonInstance()->start(T("waveletExtract")); - - SpectralDataBuffer::ItemType spectrum; - - int blocksize = 0; - int blockpos = 0; - int blockpos_end = 0; - int blocknr = 0; - int basisPos = 0; - int freqDuplicats = 0; - int minBestBasisLevel = getMinLevel(levelsHedge); - long timeResolution = (long)1<<(mDWT_maxLevel - minBestBasisLevel); - int timeStepSize = timeResolution / TIME_RESOLUTION_LIMIT; - float value = 0.0; - float realToFullResolution = (float)mFrequencyResolution / (float)mResolution; - - for(int time = 0; time < timeResolution; time++) { - basisPos = 0; - spectrum.clear(); - - for(int levelNr = 0; levelNr < levelsHedge.num_of_levels; levelNr++) { - int level = levelsHedge.levels[levelNr]; - blocksize = 1<<(mDWT_maxLevel - level); - blockpos = (int)((float)blocksize / (float)timeResolution * (float)time); - blockpos_end = (int)((float)blocksize / (float)timeResolution * (float)(time + timeStepSize)); - blocknr = basisPos / blocksize ; - freqDuplicats = (int)(realToFullResolution * (float)blocksize); - - //value = getValue(transformResultClass, origin, level, blocknr, blockpos); - value = getAvgValue(transformResultClass, origin, level, blocknr, blockpos, blockpos_end); - - for(int freqDuplicateNr = 0; freqDuplicateNr < freqDuplicats; freqDuplicateNr++) { - spectrum.push_back(value*value); - } - - basisPos += blocksize; - } - getSpectralDataBuffer()->write(spectrum); - - //DBG(T("AbstractWaveletTransformation::extractSpectrum spectrum-size=",(int)spectrum.size()); - //(*1) since the full time resolution causes serious performance problems - //and is to big for the display, - //it must be currently limited for high settings. - if (timeResolution > TIME_RESOLUTION_LIMIT) time+=timeStepSize - 1; - } - - PerformanceManager::getSingletonInstance()->stop(T("waveletExtract")); +void AbstractWaveletTransformation::extractSpectrum(const ArrayTreePer &outWaveletPacketTree, const HedgePer &levelsHedge) { + assert(outWaveletPacketTree.maxlevel > 0); + assert(outWaveletPacketTree.dim > 0); + assert(outWaveletPacketTree.origin != nullptr); + auto dataLength = outWaveletPacketTree.dim * (outWaveletPacketTree.maxlevel + 1); + auto waveletTransformOutput = tcb::span(outWaveletPacketTree.origin, static_cast(dataLength)); + extractSpectrum(TRANSFORM_RESULT_CLASS_ARRAYTREE, waveletTransformOutput, levelsHedge); +} + +void AbstractWaveletTransformation::extractSpectrum(int transformResultClass, tcb::span origin, const HedgePer &levelsHedge) { + LOG_PERFORMANCE_OF_SCOPE("AbstractWaveletTransformation extractSpectrum"); + + SpectralDataBuffer::ItemType spectrum; + + unsigned int blockSize = 0; + unsigned long blockPosition = 0; + unsigned long blockPositionEnd = 0; + unsigned long blockNumber = 0; + long basisPosition = 0; + long freqDuplicates = 0; + + WaveletLevelType minBestBasisLevel = getMinLevel(levelsHedge); + auto timeResolution = 1U << (waveletFilterTreeMaxLevel - minBestBasisLevel); + auto timeStepSize = timeResolution / TIME_RESOLUTION_LIMIT; + auto frequencyResolution = getSpectralDataInfo().getFrequencyResolution(); + auto realToFullResolution = static_cast(frequencyResolution) / static_cast(getResolution()); + auto levels = tcb::span(levelsHedge.levels, static_cast(levelsHedge.num_of_levels)); + + double value = 0.0; + double timeResolutionPerSample = 0.0; + for (unsigned int time = 0; time < timeResolution; time++) { + basisPosition = 0; + spectrum.clear(); + + for (auto level : levels) { + assert(waveletFilterTreeMaxLevel >= level); + assert(level > 0); + blockSize = 1U << static_cast(waveletFilterTreeMaxLevel - level); + timeResolutionPerSample = static_cast(blockSize) / static_cast(timeResolution); + blockPosition = static_cast(lrint(timeResolutionPerSample * static_cast(time))); + blockPositionEnd = static_cast(lrint(timeResolutionPerSample * static_cast(time + timeStepSize))); + blockNumber = static_cast(lrint(static_cast(basisPosition) / static_cast(blockSize))); + + value = getAvgValue(transformResultClass, origin, static_cast(level), blockNumber, blockPosition, blockPositionEnd); + + freqDuplicates = lrint(realToFullResolution * static_cast(blockSize)); + for (int freqDuplicateNr = 0; freqDuplicateNr < freqDuplicates; freqDuplicateNr++) { + spectrum.push_back(static_cast(value * value)); + } + + basisPosition += blockSize; + } + getSpectralDataBuffer()->write(spectrum); + + //(*1) since the full time resolution causes serious performance problems + //and is too big for the display, + //it must be currently limited for high settings. + if (timeResolution > TIME_RESOLUTION_LIMIT) { + time += timeStepSize - 1; + } + } } //returns the average value of the specified result tree positions -float AbstractWaveletTransformation::getAvgValue( - int transformResultClass, - real_DWT* origin, - int level, - int blocknr, - int blockpos_start, - int blockpos_end) -{ - if (!origin) return 0.0f; - - int stepSize = 2; //skips every nth value. no mathematically exact averaging, but necessary for performance - int count = 0; - double averageValue = 0.0; - real_DWT* values = NULL; - - if (transformResultClass == TRANSFORM_RESULT_CLASS_ARRAYTREE) { - values = origin + (level * mResolution + blocknr * ( (mResolution) >> (level) )); - } - if (transformResultClass == TRANSFORM_RESULT_CLASS_INTERVAL) { - values = origin + (1<<(mDWT_maxLevel - level)) + (blocknr - 1); - } - if ((blockpos_start + stepSize - 1) >= blockpos_end) return *(values + blockpos_start); - - for (int blockpos = blockpos_start; blockpos < blockpos_end; blockpos+=stepSize) { - averageValue += abs(*(values + blockpos)); - count++; - } - return (float)(averageValue / count); +auto AbstractWaveletTransformation::getAvgValue( + int transformResultClass, + tcb::span origin, + WaveletLevelType level, + unsigned long blockNumber, + unsigned long blockposStart, + unsigned long blockposEnd) const -> double { + + LOG_PERFORMANCE_OF_SCOPE("AbstractWaveletTransformation getAvgValue"); + + auto count = 0; + auto averageValue = 0.0; + tcb::span values; + + if (transformResultClass == TRANSFORM_RESULT_CLASS_ARRAYTREE) { + auto resolution = getResolution(); + auto offset = (level * resolution + blockNumber * (resolution >> level)); + values = origin.subspan(offset); + } + if (transformResultClass == TRANSFORM_RESULT_CLASS_INTERVAL) { + auto offset = (1U << (waveletFilterTreeMaxLevel - level)) + (blockNumber - 1); + values = origin.subspan(offset); + } + if (blockposEnd >= values.size()) { + blockposEnd = values.size() - 1; + } + if (blockposStart >= blockposEnd) { + return values[blockposEnd]; + } + for (auto blockpos = blockposStart; blockpos < blockposEnd; blockpos++) { + averageValue += abs(values[blockpos]); + count++; + } + return averageValue / count; } //returns the value of the specified result tree position -float AbstractWaveletTransformation::getValue(int transformResultClass, real_DWT* origin, int level, int blocknr, int blockpos) { - if (transformResultClass == TRANSFORM_RESULT_CLASS_ARRAYTREE) { - return (origin)? *(origin + (level * mResolution + blocknr * ( (mResolution) >> (level) )) + blockpos) : 0.0f; - } - if (transformResultClass == TRANSFORM_RESULT_CLASS_INTERVAL) { - return (origin)? *(origin + (1<<(mDWT_maxLevel - level)) + (blocknr - 1) + blockpos) : 0.0f; - } - DBG(T("AbstractWaveletTransformation::getValue(..): Unkown transformResultClass")); - return 0.0; +auto AbstractWaveletTransformation::getValue(int transformResultClass, tcb::span origin, WaveletLevelType level, unsigned long blockNumber, unsigned long blockPosition) const -> double { + if (transformResultClass == TRANSFORM_RESULT_CLASS_ARRAYTREE) { + auto resolution = getResolution(); + auto offset = (level * resolution + blockNumber * (resolution >> level)) + blockPosition; + return origin[offset]; + } + if (transformResultClass == TRANSFORM_RESULT_CLASS_INTERVAL) { + auto offset = (1U << (waveletFilterTreeMaxLevel - level)) + (blockNumber - 1) + blockPosition; + return origin[offset]; + } + DBG("AbstractWaveletTransformation::getValue(..): Unkown transformResultClass"); + return 0.0; } -//Updates the member "mConstantLevelsHedge" for a given level (e.g. 4,4,4,4) -void AbstractWaveletTransformation::updateConstantLevelsHedge(int level) { - assert(level > 0); - assert(level <= mDWT_maxLevel); - - long levelCount = (long)1<<(level); +void AbstractWaveletTransformation::updateConstantLevelsHedge(WaveletLevelType level) { + assert(level > 0); + assert(level <= waveletFilterTreeMaxLevel); - integer *levels = new integer [levelCount]; - for (int i = 0; i < levelCount; i++) { - levels[i] = level; - } + auto levelCount = 1U << level; - if (mConstantLevelsHedge) delete (mConstantLevelsHedge); - mConstantLevelsHedge = new HedgePer(mResolution, levelCount, levels); - assert(mConstantLevelsHedge); + auto levels = std::vector(levelCount); + for (unsigned int i = 0; i < levelCount; i++) { + levels[i] = level; + } - delete [] (levels); + constantLevelsHedge = std::make_unique(getResolution(), levelCount, levels.data()); - DBG(T("AbstractWaveletTransformation::updateConstantLevelsHedge done with level=") + - juce::String(level) + ",count=" + juce::String(levelCount)); + DBG("AbstractWaveletTransformation::updateConstantLevelsHedge done with level=" + + juce::String(level) + ",count=" + juce::String(levelCount)); } -//Updates/fills the member "mDWTLevelsHedge" with the levels, that are in use when a DWT is applied -void AbstractWaveletTransformation::updateDWTLevelsHedge(void) { - integer* levels = new integer [mDWT_maxLevel + 1]; +void AbstractWaveletTransformation::updateDWTLevelsHedge() { + assert(waveletFilterTreeMaxLevel > 0); + + auto levels = std::vector(waveletFilterTreeMaxLevel + 1); + levels[0] = waveletFilterTreeMaxLevel; - levels[0] = mDWT_maxLevel; - for (int i = 0; i < mDWT_maxLevel; i++) { - levels[i+1] = mDWT_maxLevel - i; - } + for (unsigned int i = 0; i < static_cast(waveletFilterTreeMaxLevel); i++) { + levels[i + 1] = waveletFilterTreeMaxLevel - i; + } - if (mDWTLevelsHedge) delete (mDWTLevelsHedge); - mDWTLevelsHedge = new HedgePer(mResolution, mDWT_maxLevel + 1, levels); - assert(mDWTLevelsHedge); + dWTLevelsHedge = std::make_unique(getResolution(), waveletFilterTreeMaxLevel + 1, levels.data()); - delete [] (levels); - DBG(T("AbstractWaveletTransformation::updateDWTLevelsHedge done with count" + juce::String(mDWT_maxLevel))); + DBG("AbstractWaveletTransformation::updateDWTLevelsHedge done with mDwtMaxLevel " + juce::String(waveletFilterTreeMaxLevel)); } \ No newline at end of file diff --git a/src/dsp/transformations/AbstractWaveletTransformation.h b/src/dsp/transformations/AbstractWaveletTransformation.h index 137d5149..ebf3e605 100644 --- a/src/dsp/transformations/AbstractWaveletTransformation.h +++ b/src/dsp/transformations/AbstractWaveletTransformation.h @@ -14,51 +14,111 @@ ============================================================================== */ #pragma once -#include "Transformation.h" -#include "..\..\plugin\SpectronParameters.h" -#include "..\..\utilities\RenderingHelper.h" #include "libw.h" +#include "../../utilities/RenderingHelper.h" +#include "Transformation.h" +#include "WaveletParameters.h" +#include "TransformationParameters.h" +#include +#include "tcb/span.hpp" class AbstractWaveletTransformation : public Transformation { public: - AbstractWaveletTransformation( - double samplingRate, - long resolution, - int windowFunctionNr = SpectronParameters::WINDOWING_DEFAULT, - int waveletBaseTypeNr = SpectronParameters::WAVELET_DEFAULT - ); - virtual ~AbstractWaveletTransformation(void); + AbstractWaveletTransformation( + double newSamplingRate, + ResolutionType newResolution, + TransformationParameters::Type newTransformationType, + WindowParameters::WindowFunction newWindowFunction = WindowParameters::WindowFunction::DEFAULT, + WaveletParameters::WaveletBase newWaveletBase = WaveletParameters::WaveletBase::DEFAULT); + ~AbstractWaveletTransformation() override; + + AbstractWaveletTransformation(const AbstractWaveletTransformation &) = delete; //No copy contructor + AbstractWaveletTransformation(AbstractWaveletTransformation &&) = delete; //No move contructor + auto operator=(const AbstractWaveletTransformation &) -> AbstractWaveletTransformation & = delete;//No copy assignment + auto operator=(AbstractWaveletTransformation &&) -> AbstractWaveletTransformation & = delete; //No move assignment - void setWaveletBase(int waveletBasNr); + void setWaveletBase(const WaveletParameters::WaveletBase &newWaveletBase); + using WaveletLevelType = unsigned int; protected: - virtual int getMaxLevel (int dimension); - virtual int getMinLevel (const HedgePer &bestBasis); - virtual void fillDWTInput (void); - virtual void sortDWPTTreeByScaleDescending (const ArrayTreePer &tree); - virtual void swapDWPTTreeChilds (const ArrayTreePer &tree, const integer &L, const integer &B); - - virtual void extractSpectrum(const Interval &out_DWT); - virtual void extractSpectrum(const ArrayTreePer &out_DWPT, const HedgePer &levelsHedge); - virtual void updateConstantLevelsHedge (int resolutionRatioDWPT = 0); - virtual void updateDWTLevelsHedge (void); - - int mWaveletBaseTypeNr; //Waveletbase-type, see enum WAVELET_BASE_NR - int mDWT_maxLevel; //Wavelet dimension (resolution = 2^DWT_MAX_LEVEL) - PQMF mDWT_filter_H; //DWT/DWPT lowpass filter coeffs (result=scaling function); - PQMF mDWT_filter_G; //DWT/DWPT hipass filter coeffs (result=wavelet function); - Interval* mDWT_Input; //Pointer to wave++'s wavelet transformation input data - - HedgePer* mConstantLevelsHedge;//Contains constant levels as hedge for a given level (e.g. 4,4,4,4) - HedgePer* mDWTLevelsHedge; //Contains falling levels (=DWT levels) as hedge (e.g. 8,7,6,5,4,3,2,1) - RenderingHelper renderingHelper; + void fillDWTInput(); + + /** + * @brief Sorts the tree by descending scale (ascending frequency) + * + * @param tree + */ + static void sortWaveletFilterTreeByScaleDescending(const ArrayTreePer &tree); + + /** + * @brief Executes the wavelet packet transform analysis. + * + * @param analysisResult + */ + void analyse(ArrayTreePer &analysisResult) const; + + /** + * @brief Executes the dyadic wavelet transform analysis. + * + * @param analysisResult + */ + void analyse(Interval &analysisResult) const; + + void extractSpectrum(const Interval &outDWT); // For "classic" (dyadic) Discrete Wavelet Transform + void extractSpectrum(const ArrayTreePer &outWaveletPacketTree); // For Discrete Wavelet Packet Transform + void extractSpectrum(const ArrayTreePer &outWaveletPacketTree, const HedgePer &levelsHedge);// For Discrete Wavelet Packet Transform with dynamic (best) basis + + /** + * @brief Updates the member "constantLevelsHedge" for a given level (e.g. 4,4,4,4). + * @param level WaveletLevelType + */ + void updateConstantLevelsHedge(WaveletLevelType level); + + auto getDwtInput() const -> const Interval & { + return dwtInput; + } + auto getWaveletFilterTreeMaxLevel() const -> WaveletLevelType { + return waveletFilterTreeMaxLevel; + } private: - static enum TRANSFORM_RESULT_CLASS { - TRANSFORM_RESULT_CLASS_INTERVAL = 0, - TRANSFORM_RESULT_CLASS_ARRAYTREE - }; - void extractSpectrum(int transformResultClass, real_DWT* origin, const HedgePer &bestBasis); - float getValue(int transformResultClass, real_DWT* origin, int level, int blocknr, int blockpos); - float getAvgValue(int transformResultClass, real_DWT* origin, int level, int blocknr, int blockpos_start, int blockpos_end); + enum TRANSFORM_RESULT_CLASS { + TRANSFORM_RESULT_CLASS_INTERVAL = 0, + TRANSFORM_RESULT_CLASS_ARRAYTREE + }; + + WaveletLevelType waveletFilterTreeMaxLevel;//Wavelet dimension (resolution = 2^DWT_MAX_LEVEL) + Interval dwtInput; //wavelet transformation input data of wave++ library + + PQMF mDwtFilterH; //Wavelet lowpass filter coefficients (result=scaling function); + PQMF mDwtFilterG; //Wavelet highpass filter coefficients (result=wavelet function); + + std::unique_ptr constantLevelsHedge = {};//Contains constant levels as hedge for a given level (e.g. 4,4,4,4) + std::unique_ptr dWTLevelsHedge = {}; //Contains falling levels (=DWT levels) as hedge (e.g. 8,7,6,5,4,3,2,1) + + static auto getMaxLevel(ResolutionType resolution) -> WaveletLevelType; + static auto getMinLevel(const HedgePer &bestBasis) -> WaveletLevelType; + + /** + * @brief Updates/fills the member "dWTLevelsHedge" with the levels, that are in use when a dyadic decimated discrete wavelet transform (DWT) is applied. + */ + void updateDWTLevelsHedge(); + + /** + * @brief Swaps the right and the left child of a wavelet packet transform tree node ("block"). + + * This is necessary to get the correct order of frequencies in the wavelet packet transform tree. + * The "classic" dyadic decimated discrete wavelet transform only takes the result of the previous low pass filter and splits it into two parts. + * The wavelet packet transform also splits the result of the high pass filter (where specified). + * Splitting it into two parts leads to a swapped order of frequencies. The highpass result contains the lower frequency an vice versa. + * Therefore these need to be swapped. + * @param tree + * @param level + * @param block + */ + static void swapWaveletFilterTreeChilds(const ArrayTreePer &tree, const WaveletLevelType &level, const unsigned int &block); + + void extractSpectrum(int transformResultClass, tcb::span origin, const HedgePer &levelsHedge); + auto getValue(int transformResultClass, tcb::span origin, WaveletLevelType level, unsigned long blockNumber, unsigned long blockPosition) const -> double; + auto getAvgValue(int transformResultClass, tcb::span origin, WaveletLevelType level, unsigned long blockNumber, unsigned long blockposStart, unsigned long blockposEnd) const -> double; }; diff --git a/src/dsp/transformations/FourierTransformation.cpp b/src/dsp/transformations/FourierTransformation.cpp index bb5c17a9..71d69741 100644 --- a/src/dsp/transformations/FourierTransformation.cpp +++ b/src/dsp/transformations/FourierTransformation.cpp @@ -1,56 +1,69 @@ #include "FourierTransformation.h" -#include "../../utilities/PerformanceManager.h" +#include "../../utilities/PerformanceLogger.h" +#include "TransformationParameters.h" +#include +#include -FourierTransformation::FourierTransformation(double samplingRate, long resolution, int windowFunctionNr) - : Transformation(samplingRate, resolution, windowFunctionNr) -{ - long frequencyResolution = (long)((mResolution/2.0)+1.0); - mSpectralDataInfo = new SpectralDataInfo(samplingRate, resolution, frequencyResolution, 1); +FourierTransformation::FourierTransformation(double newSamplingRate, ResolutionType newResolution, WindowParameters::WindowFunction newWindowFunction) + : Transformation(newSamplingRate, newResolution, TransformationParameters::Type::FAST_FOURIER_TRANSFORM, newWindowFunction), + in(static_cast(fftw_malloc(sizeof(double) * newResolution))), + out(static_cast(fftw_malloc(sizeof(fftw_complex) * ((newResolution / 2) + 1)))), + spectralDataInfo(newSamplingRate, newResolution, (newResolution / 2 + 1), 1) { - in = (double*) fftw_malloc(sizeof(double) * mResolution); - out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * ((mResolution/2)+1)); - plan = fftw_plan_dft_r2c_1d(mResolution, in, out, FFTW_ESTIMATE); + plan = fftw_plan_dft_r2c_1d(static_cast(newResolution), in, out, FFTW_ESTIMATE); - assert(plan); + assert(plan); - DBG(T("FourierTransformation::initialize done with fs/res=") + - juce::String(mSamplingRate) + ",resolution=" + juce::String(mResolution)); + DBG("FourierTransformation::initialize done with fs/res=" + juce::String(newSamplingRate) + ",resolution=" + juce::String(newResolution)); - ready = true; - calculated = true; -}; + setReady(); + setCalculated(); +} FourierTransformation::~FourierTransformation() { - ready = false; + setReady(false); - if (in) fftw_free(in); - if (out) fftw_free(out); - if (plan) fftw_destroy_plan(plan); + if (in != nullptr) { + fftw_free(in); + } + if (out != nullptr) { + fftw_free(out); + } + if (plan != nullptr) { + fftw_destroy_plan(plan); + } - DBG(T("TransformFFT destructed")); + DBG("TransformFFT destructed"); } void FourierTransformation::calculate() { - //Loop for copying every single sample from input-queue to fft inputarray - for (int i = 0; i < mResolution; i++) { - double nextSamplePerChannel = mInputQueue->front(); - *(in + i) = nextSamplePerChannel * mWindowFunction->getFactor(i); - - mInputQueue->pop(); - } - - PerformanceManager::getSingletonInstance()->start(T("fftw_execute")); - fftw_execute(plan); - PerformanceManager::getSingletonInstance()->stop(T("fftw_execute")); - - SpectralDataBuffer::ItemType spectrum; - - for (int i = 0; i < ((mResolution / 2) + 1); i++) { - //Loop for copying every single fft result data into the output-queue - double realValue = *(out + i)[0]; - double imagValue = *(out + i)[1]; - double magnitude = sqrt(realValue*realValue + imagValue*imagValue) / mResolution; /*1/N = Normalisation*/ - spectrum.push_back(magnitude); - } - getSpectralDataBuffer()->write(spectrum); + //Loop for copying every single sample from input-queue to fft inputarray + auto resolution = getResolution(); + { + LOG_PERFORMANCE_OF_SCOPE("FourierTransformation calculate fftInputCopy"); + for (std::size_t i = 0; i < resolution; i++) { + auto nextSamplePerChannel = getInputQueue().front(); + *(in + i) = nextSamplePerChannel * getWindowFunction()->getFactor(i); + + getInputQueue().pop(); + } + } + { + LOG_PERFORMANCE_OF_SCOPE("FourierTransformation calculate fftw_execute"); + fftw_execute(plan); + } + { + LOG_PERFORMANCE_OF_SCOPE("FourierTransformation calculate fftOutputCopy"); + SpectralDataBuffer::ItemType spectrum; + + for (std::size_t i = 0; i < ((resolution / 2) + 1); i++) { + //Loop for copying every single fft result data into the output-queue + double realValue = *(out + i)[0]; + double imaginaryValue = *(out + i)[1]; + double magnitude = sqrt(realValue * realValue + imaginaryValue * imaginaryValue); + double normalizedMagnitude = magnitude / static_cast(resolution);// 1/N = Normalisation + spectrum.push_back(static_cast(normalizedMagnitude)); + } + getSpectralDataBuffer()->write(spectrum); + } } \ No newline at end of file diff --git a/src/dsp/transformations/FourierTransformation.h b/src/dsp/transformations/FourierTransformation.h index 1c64c62e..e486abf5 100644 --- a/src/dsp/transformations/FourierTransformation.h +++ b/src/dsp/transformations/FourierTransformation.h @@ -14,24 +14,37 @@ ============================================================================== */ #pragma once +#include "../../parameter/SpecletParameters.h" +#include "../windowing/WindowParameters.h" +#include "Transformation.h" #include "TransformationFactory.h" -#include "..\..\plugin\SpectronParameters.h" #include "fftw3.h" + class FourierTransformation : public Transformation { public: - FourierTransformation( - double samplingRate, - long resolution, - int windowFunctionNr = SpectronParameters::WINDOWING_DEFAULT - ); - virtual ~FourierTransformation(void); + FourierTransformation( + double newSamplingRate, + ResolutionType newResolution, + WindowParameters::WindowFunction newWindowFunction = WindowParameters::WindowFunction::DEFAULT); + ~FourierTransformation() override; + FourierTransformation(FourierTransformation &) = delete; //No copy contructor + FourierTransformation(FourierTransformation &&) = delete; //No move contructor + auto operator=(FourierTransformation &) -> FourierTransformation & = delete; //No copy assignment + auto operator=(FourierTransformation &&) -> FourierTransformation & = delete;//No move assignment + + auto getName() -> const char * override { return "Fast Fourier Transformation"; } protected: - virtual void calculate(); + void calculate() override; + auto getSpectralDataInfo() -> const SpectralDataInfo & override { + return spectralDataInfo; + } private: - fftw_plan plan; //Plan holds prepared (optimized) fft - double *in; //Pointer to input data - fftw_complex *out; //Pointer to output data + fftw_plan plan; //Plan holds prepared (optimized) fft + double *in; //Pointer to input data + fftw_complex *out;//Pointer to output data + + SpectralDataInfo spectralDataInfo; }; diff --git a/src/dsp/transformations/Transformation.cpp b/src/dsp/transformations/Transformation.cpp index 8da90ca5..c8120cb7 100644 --- a/src/dsp/transformations/Transformation.cpp +++ b/src/dsp/transformations/Transformation.cpp @@ -1,146 +1,127 @@ #include "Transformation.h" -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" -#include "../../source/utilities/PerformanceManager.h" - -using namespace std; -#ifndef __WINDOW_FACTORY__ - #define WINDOW_FACTORY WindowFunctionsFactory::getSingletonInstance() -#endif - -Transformation::Transformation(double samplingRate, long resolution, int windowFunctionNr) - : mInputQueue(0), - mOutputBuffer(0), - mSpectralDataInfo(0), - mWindowFunction(0), - mTransformResultsListener(0) -{ - ready = false; - calculated = false; - - mSamplingRate = samplingRate; - mResolution = resolution; - mInputQueue = new queue(); - mOutputBuffer = new SpectralDataBuffer(); - waitForDestruction= new juce::WaitableEvent(true); - waitForDestruction->signal(); - mTransformTypeNr = 0; - - setWindowFunction(windowFunctionNr); - - DBG(T("Transformation::initialize done with fs=") + - juce::String(mSamplingRate) + - T(",res=") + juce::String(mResolution) + - T(",fres=") + juce::String(mFrequencyResolution) - ); +#include "../windowing/WindowFunctionFactory.h" +#include "TransformationParameters.h" +#include "juce_core/juce_core.h" +#include "../../utilities/PerformanceLogger.h" +#include +#include + +Transformation::Transformation(double newSamplingRate, ResolutionType newResolution, TransformationParameters::Type newTransformationType, WindowParameters::WindowFunction newWindowFunction) + : transformationType(newTransformationType), + resolution(newResolution) { + assert(newResolution >= 256); + waitForDestruction.signal(); + + setWindowFunction(newWindowFunction); + + DBG("Transformation::initialize done with sampling rate=" + juce::String(newSamplingRate) + ", resolution/blockSize=" + juce::String(newResolution)); } -//destructor: waits, until a possibly within another thread currently running +//Destructor waits until a possibly within another thread currently running //calculation ends and deletes then all allocated objects Transformation::~Transformation() { - PerformanceManager::getSingletonInstance()->start(T("waitForDestruction")); - bool timeoutDuringWait = waitForDestruction->wait(3000); - PerformanceManager::getSingletonInstance()->stop(T("waitForDestruction")); - if (!timeoutDuringWait) DBG(T("Transformation destruction: Timeout during wait!")); - - ready = false; - waitForDestruction->signal(); - - if (mInputQueue) delete(mInputQueue); - if (mOutputBuffer) delete(mOutputBuffer); - if (mSpectralDataInfo) delete(mSpectralDataInfo); - if (waitForDestruction) delete(waitForDestruction); - - mInputQueue = 0; - mOutputBuffer = 0; - mSpectralDataInfo = 0; - waitForDestruction = 0; - - DBG(T("Transform destructed")); + { + LOG_PERFORMANCE_OF_SCOPE("Transformation waitForDestruction"); + bool timeoutDuringWait = waitForDestruction.wait(WAIT_FOR_DESTRUCTION_TIMEOUT); + if (!timeoutDuringWait) { + DBG("Transformation destruction: Timeout during wait!"); + } + } + ready = false; + waitForDestruction.signal(); + DBG("Transform destructed"); } -//loads or replaces the window function with the given number (see class WindowFunctionsFactory) -void Transformation::setWindowFunction(int windowFunctionNr) { - ready = false; - - mWindowFunction = WINDOW_FACTORY->createWindowFunction(windowFunctionNr, mResolution); - assert(mWindowFunction); - DBG(T("Transformation::setWindowFunction done with windowFunctionNr=") + juce::String(windowFunctionNr)); - - ready = true; +auto Transformation::getWindowFunction() const -> WindowFunction * { + return windowFunction.get(); } -//reads the next input sample -void Transformation::setNextInputSample(double sample) { - if (!ready) return; - if (!mInputQueue) return; - mInputQueue->push(sample); +void Transformation::setWindowFunction(const WindowParameters::WindowFunction& newWindowFunction) { + setReady(false); + windowFunction = WindowFunctionFactory::getSingletonInstance().getWindow(newWindowFunction, resolution); + assert(windowFunction); + auto windowFunctionName = std::string(WindowParameters::WindowFunctionNames::map.find(newWindowFunction)->second); + DBG("Transformation::setWindowFunction done with windowFunctionNr=" + windowFunctionName); + setReady(true); +} - if (!mWindowFunction) return; - if (!calculated) return; +auto Transformation::getInputQueue() -> std::queue & { + return inputQueue; +} - calculationFrame(); +void Transformation::setNextInputSample(const double &sample) { + if (!ready) { + return; + } + if (std::isinf(sample) || std::isnan(sample)) { + return; + } + inputQueue.push(sample); + + if (windowFunction == nullptr) { + return; + } + if (!calculated) { + return; + } + + calculationFrame(); } -//this method is called on every new input sample and -//contains the sequence of actions associated with the calculation, e.g.: -//enough data? - ready? - calculate - informListeners,... void Transformation::calculationFrame() { - calculated = false; - - assert(mResolution > 0); - if (mInputQueue->size() < mResolution) { - //Calculation only with at least N samples possible - calculated = true; - return; - } - if (!ready) return; - - { - //begin of critical section: only one thread per time ------------------------------ - PerformanceManager::getSingletonInstance()->start(T("calculationFrame")); - const ScopedLock myScopedLock (criticalSection); - waitForDestruction->reset(); - - calculate(); - if (isOutputAvailable()) informListenersAboutTransformResults(); - - calculated = true; - waitForDestruction->signal(); - PerformanceManager::getSingletonInstance()->stop(T("calculationFrame")); - //end of critical section --------------------------------- - } + calculated = false; + if (inputQueue.size() < resolution) { + //Calculation only with at least N samples possible + calculated = true; + return; + } + if (!ready) { + return; + } + + { + //begin of critical section: only one thread per time ------------------------------ + LOG_PERFORMANCE_OF_SCOPE("Transformation calculationFrameTimer"); + const juce::ScopedLock myScopedLock(criticalSection); + waitForDestruction.reset(); + + calculate(); + if (isOutputAvailable()) { + informListenersAboutTransformResults(); + } + + calculated = true; + waitForDestruction.signal(); + //end of critical section --------------------------------- + } } //returns true, if spectral data is available -bool Transformation::isOutputAvailable() { - if (!ready) return false; - if (!mOutputBuffer) return false; - return (getSpectralDataBuffer()->unread() > 0); +auto Transformation::isOutputAvailable() -> bool { + return ready && (outputBuffer.unread() > 0); } -SpectralDataBuffer* Transformation::getSpectralDataBuffer(void) { - return mOutputBuffer; -}; - -void Transformation::getNextSpectrum (SpectralDataBuffer::ItemType* item) { - getSpectralDataBuffer()->read(item); +auto Transformation::getSpectralDataBuffer() -> SpectralDataBuffer * { + return &outputBuffer; } -SpectralDataBuffer::ItemStatisticsType Transformation::getSpectrumStatistics (SpectralDataBuffer::ItemType* item) { - return getSpectralDataBuffer()->getStatistics(item); +void Transformation::getNextSpectrum(SpectralDataBuffer::ItemType *item) { + outputBuffer.read(item); } //simple single-listener, could be extend using arrays... -void Transformation::setTransformResultListener(TransformationListener* value) { - mTransformResultsListener = value; +void Transformation::setTransformResultListener(TransformationListener *value) { + transformResultsListener = value; } //since there is just one listener, only one call has to be done void Transformation::informListenersAboutTransformResults() { - PerformanceManager::getSingletonInstance()->start(T("informListeners")); - - if (!ready) return; - if (mTransformResultsListener) mTransformResultsListener->onTransformationEvent(this); - - PerformanceManager::getSingletonInstance()->stop(T("informListeners")); + LOG_PERFORMANCE_OF_SCOPE("Transformation informListenersAboutTransformResults"); + + if (!ready) { + return; + } + if (transformResultsListener != nullptr) { + transformResultsListener->onTransformationEvent(this); + } } \ No newline at end of file diff --git a/src/dsp/transformations/Transformation.h b/src/dsp/transformations/Transformation.h index 86405a33..bddeeccf 100644 --- a/src/dsp/transformations/Transformation.h +++ b/src/dsp/transformations/Transformation.h @@ -15,67 +15,129 @@ */ #pragma once -#include -#include -#include +#include "../../data/SpectralDataBuffer.h" #include "../../data/SpectralDataInfo.h" -#include "../../data/SpectralDataBuffer.h" -#include "../WindowFunctions.h" -#include "../../plugin/SpectronParameters.h" +#include "../windowing/WindowParameters.h" +#include "../windowing/WindowFunctions.h" +#include "TransformationParameters.h" +#include "juce_core/juce_core.h" +#include "juce_dsp/juce_dsp.h" +#include +#include class TransformationListener; +class TransformationResult { +public: + virtual ~TransformationResult() = default; + virtual auto isOutputAvailable() -> bool = 0; + virtual auto getSpectralDataBuffer() -> SpectralDataBuffer * = 0; + virtual void getNextSpectrum(SpectralDataBuffer::ItemType *item) = 0; + /** + * @brief Get the spectral data info that contains amongst others details about the time and frequency resolution. + * + * @return const SpectralDataInfo& + */ + virtual auto getSpectralDataInfo() -> const SpectralDataInfo & = 0; +}; -class Transformation abstract { -friend class TransformationFactory; +class Transformation : public TransformationResult { public: - static enum Constants { - TIME_RESOLUTION_LIMIT = 8 - }; - Transformation(double samplingRate, long resolution, int windowFunctionNr = SpectronParameters::WINDOWING_DEFAULT); - virtual ~Transformation(void); - - void setWindowFunction (int windowFunctionNr); - void setNextInputSample (double sample); - bool isOutputAvailable (void); - SpectralDataBuffer* getSpectralDataBuffer(void); - SpectralDataInfo* getSpectralDataInfo (void) {return mSpectralDataInfo;}; - int getTransformationNr (void) {return mTransformTypeNr;}; - void setTransformationNr (int transformTypeNr) {mTransformTypeNr = transformTypeNr;}; - void setTransformResultListener (TransformationListener* value); - - void getNextSpectrum (SpectralDataBuffer::ItemType* item); - SpectralDataBuffer::ItemStatisticsType getSpectrumStatistics (SpectralDataBuffer::ItemType* item); + enum Constants { + TIME_RESOLUTION_LIMIT = 8, + WAIT_FOR_DESTRUCTION_TIMEOUT = 3000 + }; -private: - void informListenersAboutTransformResults(); - void calculationFrame(); + using ResolutionType = unsigned long; + + Transformation() = delete; //No default contructor + Transformation(Transformation &) = delete; //No copy contructor + Transformation(Transformation &&) = delete; //No move contructor + auto operator=(Transformation &) -> Transformation & = delete; //No copy assignment + auto operator=(Transformation &&) -> Transformation & = delete;//No move assignment + + Transformation( + double newSamplingRate, + ResolutionType newResolution, + TransformationParameters::Type newTransformationType, + WindowParameters::WindowFunction newWindowFunction = WindowParameters::WindowFunction::DEFAULT); + virtual ~Transformation(); + + auto getWindowFunction() const -> WindowFunction *; + + /** + * @brief Loads or replaces the window function with the given number (see class WindowFunctionsFactory) + * + * @param newWindowFunction + */ + void setWindowFunction(const WindowParameters::WindowFunction& newWindowFunction); + + /** + * @brief Get the input queue containing the next samples to be transformed + * + * @return const std::queue& + */ + auto getInputQueue() -> std::queue &; + + /** + * @brief Gathers the next input sample + * + * @param sample + */ + void setNextInputSample(const double &sample); + auto isOutputAvailable() -> bool; + auto getSpectralDataBuffer() -> SpectralDataBuffer *; + auto getTransformationType() const -> auto { return transformationType; } + void setTransformResultListener(TransformationListener *value); + void getNextSpectrum(SpectralDataBuffer::ItemType *item); - Transformation(void); //No default contructor - Transformation(Transformation &); //No copy contructor - TransformationListener* mTransformResultsListener; - juce::CriticalSection criticalSection; - juce::WaitableEvent* waitForDestruction; + /** + * @brief Gets the name of the transformation + * + * @return const char* + */ + virtual auto getName() -> const char * = 0; protected: - virtual void calculate()= 0; //abstract: must be implemented by inherited class! - - long mResolution; - long mFrequencyResolution; - long mTimeResolution; - double mSamplingRate; - int mTransformTypeNr; - - bool ready; //Signalizes internally "ready for new calculation" - bool calculated; //Signalizes internally "calculation finished" - - std::queue* mInputQueue; - SpectralDataBuffer* mOutputBuffer; - SpectralDataInfo* mSpectralDataInfo; - WindowFunction* mWindowFunction; //Windowfunction-Interface for hanning, hamming, kaiser,... + /** + * @brief applies the transformation to the samples in the input queue and stores the result in the output queue + */ + virtual void calculate() = 0;//abstract: must be implemented by inherited class! + + auto getResolution() const -> ResolutionType { + return resolution; + } + + void setReady(bool value = true) { ready = value; } + void setCalculated(bool value = true) { calculated = value; } + +private: + TransformationParameters::Type transformationType; + std::shared_ptr windowFunction;//Windowfunction-Interface for hanning, hamming, kaiser,... + + ResolutionType resolution; + + std::queue inputQueue; + SpectralDataBuffer outputBuffer; + + bool ready = false; //Signalizes internally "ready for new calculation" + bool calculated = false;//Signalizes internally "calculation finished" + + TransformationListener *transformResultsListener = nullptr; + juce::CriticalSection criticalSection; + juce::WaitableEvent waitForDestruction{true}; + + void informListenersAboutTransformResults(); + + /** + * @brief Meant to be called on every new sample to be transformed. + * Contains the sequence of actions associated with the calculation, + * e.g.: enough data? - ready? - calculate - informListeners,... + */ + void calculationFrame(); }; -class TransformationListener abstract -{ +class TransformationListener { public: - virtual void onTransformationEvent(Transformation* value) = 0; //abstract -}; \ No newline at end of file + virtual ~TransformationListener() = default; + virtual void onTransformationEvent(TransformationResult *result) = 0; +}; diff --git a/src/dsp/transformations/TransformationFactory.cpp b/src/dsp/transformations/TransformationFactory.cpp index 88ed9ccc..a08c1107 100644 --- a/src/dsp/transformations/TransformationFactory.cpp +++ b/src/dsp/transformations/TransformationFactory.cpp @@ -1,106 +1,104 @@ #include "TransformationFactory.h" +#include "AbstractWaveletTransformation.h" +#include "FourierTransformation.h" +#include "Transformation.h" +#include "TransformationParameters.h" +#include "WaveletPacketBestBasisTransformation.h" +#include "WaveletPacketTransformation.h" +#include "WaveletTransformation.h" +#include +#include #ifndef __LOGGER__ - #include "../../libs/juce/JuceLibraryCode/JuceHeader.h" - #define LOGGER CLogger::getSingletonInstance() +#include "juce_core/juce_core.h" +#define LOGGER CLogger::getSingletonInstance() #endif -// Singleton instance variable (only one instance of this class) -TransformationFactory* TransformationFactory::singletonInstance = 0; +auto TransformationFactory::getSingletonInstance() -> TransformationFactory & { + static TransformationFactory singletonInstance;// Guaranteed to be destroyed. Instantiated on first use. + return singletonInstance; +} TransformationFactory::TransformationFactory() { - transformation = NULL; - listenerToHandOverToEveryNewTransformation = NULL; - transformationType = SpectronParameters::TRANSFORM_OFF; - DBG(T("TransformationFactory constructed")); + DBG("TransformationFactory constructed"); } -TransformationFactory::~TransformationFactory(void) { - deleteTransformation(); - transformation = NULL; - listenerToHandOverToEveryNewTransformation = NULL; - DBG(T("TransformationFactory deconstructed")); +TransformationFactory::~TransformationFactory() { + deleteTransformation(); + currentTransformation = nullptr; + listenerToHandOverToEveryNewTransformation = nullptr; + DBG("TransformationFactory deconstructed"); } void TransformationFactory::destruct() { - if (!singletonInstance) return; - - listenerToHandOverToEveryNewTransformation = 0; - singletonInstance->deleteTransformation(); - - delete(singletonInstance); - singletonInstance = 0; + listenerToHandOverToEveryNewTransformation = nullptr; } -TransformationFactory* TransformationFactory::getSingletonInstance() { -// Method to get the single instance of this class (Singleton) - if (TransformationFactory::singletonInstance == 0) { - TransformationFactory::singletonInstance = new TransformationFactory(); - } - return TransformationFactory::singletonInstance; -} +auto TransformationFactory::createTransformation( + TransformationParameters::Type newTransformationType, + double samplingRate, + Transformation::ResolutionType resolution, + WindowParameters::WindowFunction newWindowFunction, + WaveletParameters::WaveletBase waveletBase, + WaveletParameters::ResolutionRatioOption resolutionRatio) -> Transformation * { -Transformation* TransformationFactory::createTransformation( - int transformationTypeNr, - double samplingRate, - long resolution, - int windowFunction, - int waveletBaseTypeNr, - int resolutionRatioDWPT) -{ - DBG(T("TransformationFactory::createTransformation started. transformationNr=") + - juce::String(transformationTypeNr) + - T(",ptr=") + juce::String((int)transformation) - ); + auto newTransformationTypeName = std::string(TransformationParameters::TypeNames::map.find(newTransformationType)->second); + DBG("TransformationFactory::createTransformation started. transformationType=" + newTransformationTypeName + + ", old transformation=" + (currentTransformation != nullptr ? currentTransformation->getName() : "does not exist")); - deleteTransformation(); - - switch (transformationTypeNr) { - case SpectronParameters::TRANSFORM_FFT : { - transformation = new FourierTransformation(samplingRate, resolution, windowFunction); - assert(transformation); - break; - } - case SpectronParameters::TRANSFORM_FWT : { - transformation = new WaveletTransformation(samplingRate, resolution, windowFunction, waveletBaseTypeNr); - assert(transformation); - break; - } - case SpectronParameters::TRANSFORM_FWPT : { - transformation = new WaveletPacketTransformation(samplingRate, resolution, windowFunction, waveletBaseTypeNr, resolutionRatioDWPT); - assert(transformation); - break; - } - case SpectronParameters::TRANSFORM_FWPT_BB : { - transformation = new WaveletPacketBestBasisTransformation(samplingRate, resolution, windowFunction, waveletBaseTypeNr); - assert(transformation); - break; - } - case SpectronParameters::TRANSFORM_OFF : { - break; - } - default : { - bool transformation_unknown_error = false; - assert(transformation_unknown_error); - } - } + deleteTransformation(); - if (transformation) { - transformation->setTransformationNr(transformationTypeNr); - transformation->setTransformResultListener(listenerToHandOverToEveryNewTransformation); - } - transformationType = transformationTypeNr; + switch (newTransformationType) { + case TransformationParameters::Type::FAST_FOURIER_TRANSFORM: { + currentTransformation = new FourierTransformation(samplingRate, resolution, newWindowFunction); + assert(currentTransformation); + break; + } + case TransformationParameters::Type::FAST_WAVELET_TRANSFORM: { + currentTransformation = new WaveletTransformation(samplingRate, resolution, newWindowFunction, waveletBase); + assert(currentTransformation); + break; + } + case TransformationParameters::Type::FAST_WAVELET_PACKET_TRANSFORM: { + currentTransformation = new WaveletPacketTransformation(samplingRate, resolution, newWindowFunction, waveletBase, resolutionRatio); + assert(currentTransformation); + break; + } + case TransformationParameters::Type::FAST_WAVELET_PACKET_BEST_BASIS_TRANSFORM: { + currentTransformation = new WaveletPacketBestBasisTransformation(samplingRate, resolution, newWindowFunction, waveletBase); + assert(currentTransformation); + break; + } + case TransformationParameters::Type::BYPASS: { + break; + } + case TransformationParameters::Type::NUMBER_OF_OPTIONS: + default: { + bool transformationUnknownError = false; + assert(transformationUnknownError); + } + } - DBG(T("TransformationFactory::createTransformation done. transformationNr=") + - juce::String(transformationTypeNr) + - T(",ptr=") + juce::String((int)transformation) - ); + if (currentTransformation != nullptr) { + currentTransformation->setTransformResultListener(listenerToHandOverToEveryNewTransformation); + } + transformationType = newTransformationType; + + DBG("TransformationFactory::createTransformation done. transformationType=" + newTransformationTypeName + + ", current transformation=" + (currentTransformation != nullptr ? currentTransformation->getName() : "does not exist")); + + return currentTransformation; +} - return transformation; +void TransformationFactory::registerForTransformationResults(TransformationListener *value) { + listenerToHandOverToEveryNewTransformation = value; + if (currentTransformation != nullptr) { + currentTransformation->setTransformResultListener(listenerToHandOverToEveryNewTransformation); + } } -void TransformationFactory::deleteTransformation(void) { - if (transformation) delete transformation; - transformation = 0; - transformationType = SpectronParameters::TRANSFORM_OFF; +void TransformationFactory::deleteTransformation() { + delete currentTransformation; + currentTransformation = nullptr; + transformationType = TransformationParameters::Type::BYPASS; } \ No newline at end of file diff --git a/src/dsp/transformations/TransformationFactory.h b/src/dsp/transformations/TransformationFactory.h index 3aa698a6..d958c9f7 100644 --- a/src/dsp/transformations/TransformationFactory.h +++ b/src/dsp/transformations/TransformationFactory.h @@ -15,42 +15,43 @@ */ #pragma once +#include "../../parameter/SpecletParameters.h" +#include "../windowing/WindowParameters.h" #include "Transformation.h" -#include "FourierTransformation.h" -#include "WaveletTransformation.h" -#include "WaveletPacketTransformation.h" -#include "WaveletPacketBestBasisTransformation.h" -#include "../../dsp/WindowFunctions.h" -#include "../../plugin/SpectronParameters.h" +#include "TransformationParameters.h" +#include "WaveletParameters.h" //This special factory-variant does not only create different types of "Transformation"-implementing-objects by ID, //it is also a singleton and therefore made to exists only once. //Furthermore it holds a pointer to the transformation, that had been created the last time class TransformationFactory { public: - static TransformationFactory* getSingletonInstance(); - void destruct(); - - Transformation* createTransformation( - int transformationTypeNr, - double samplingRate, - long resolution, - int windowFunction = SpectronParameters::WINDOWING_DEFAULT, - int waveletBaseTypeNr = SpectronParameters::WAVELET_DEFAULT, - int resolutionRatioDWPT = SpectronParameters::RESOLUTION_RATIO_DEFAULT - ); - Transformation* getCurrentTransformation (void) {return transformation;}; - void registerForTransformationResults(TransformationListener* value) {listenerToHandOverToEveryNewTransformation = value;}; + static auto getSingletonInstance() -> TransformationFactory &; + + // Copy-constructors and move- and assignment-operator are deleted, because this class is a singleton. + TransformationFactory(TransformationFactory const &) = delete; + TransformationFactory(TransformationFactory &&) = delete; + auto operator=(TransformationFactory const &) -> TransformationFactory & = delete; + auto operator=(TransformationFactory const &&) -> TransformationFactory & = delete; + + void destruct(); + + auto createTransformation( + TransformationParameters::Type newTransformationType, + double samplingRate, + Transformation::ResolutionType resolution, + WindowParameters::WindowFunction newWindowFunction = WindowParameters::WindowFunction::DEFAULT, + WaveletParameters::WaveletBase waveletBase = WaveletParameters::WaveletBase::DEFAULT, + WaveletParameters::ResolutionRatioOption resolutionRatio = WaveletParameters::ResolutionRatioOption::DEFAULT) -> Transformation *; + void registerForTransformationResults(TransformationListener *value); private: - static TransformationFactory* singletonInstance; - - Transformation* transformation; - TransformationListener* listenerToHandOverToEveryNewTransformation; - int transformationType; - - TransformationFactory(void); - ~TransformationFactory(void); - TransformationFactory(const TransformationFactory&); - void deleteTransformation(void); + TransformationFactory(); + ~TransformationFactory(); + + Transformation *currentTransformation = nullptr; + TransformationListener *listenerToHandOverToEveryNewTransformation = nullptr; + TransformationParameters::Type transformationType = TransformationParameters::Type::BYPASS; + + void deleteTransformation(); }; \ No newline at end of file diff --git a/src/dsp/transformations/TransformationParameters.h b/src/dsp/transformations/TransformationParameters.h new file mode 100644 index 00000000..5255ec89 --- /dev/null +++ b/src/dsp/transformations/TransformationParameters.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace TransformationParameters { + + enum class Type { + FAST_FOURIER_TRANSFORM = 1, + FAST_WAVELET_TRANSFORM, + FAST_WAVELET_PACKET_TRANSFORM, + FAST_WAVELET_PACKET_BEST_BASIS_TRANSFORM, + BYPASS, + + NUMBER_OF_OPTIONS, + DEFAULT = FAST_FOURIER_TRANSFORM + }; + struct TypeNames { + static std::map createMap() { + return { + {Type::FAST_FOURIER_TRANSFORM, "FFT"}, + {Type::FAST_WAVELET_TRANSFORM, "FWT"}, + {Type::FAST_WAVELET_PACKET_TRANSFORM, "WPT"}, + {Type::FAST_WAVELET_PACKET_BEST_BASIS_TRANSFORM, "WPT BestBasis"}, + {Type::BYPASS, "Off"}, + }; + } + static const std::map map; + }; + + inline const std::map TypeNames::map = TypeNames::createMap(); + +}// namespace TransformationParameters \ No newline at end of file diff --git a/src/dsp/transformations/WaveletPacketBestBasisTransformation.cpp b/src/dsp/transformations/WaveletPacketBestBasisTransformation.cpp index 4996e8ba..1b0d8838 100644 --- a/src/dsp/transformations/WaveletPacketBestBasisTransformation.cpp +++ b/src/dsp/transformations/WaveletPacketBestBasisTransformation.cpp @@ -1,58 +1,110 @@ #include "WaveletPacketBestBasisTransformation.h" -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" + +#include "../../utilities/PerformanceLogger.h" +#include "AbstractWaveletTransformation.h" +#include "TransformationParameters.h" +#include "juce_core/juce_core.h" +#include +#include WaveletPacketBestBasisTransformation::WaveletPacketBestBasisTransformation( - double samplingRate, - long resolution, - int windowFunctionNr, - int waveletBaseTypeNr) - : AbstractWaveletTransformation(samplingRate, resolution, windowFunctionNr, waveletBaseTypeNr) -{ - DBG(T("WaveletPacketBestBasisTransformation constructor started")); - - mSpectralDataInfo = NULL; - mFrequencyResolution = resolution; //can not be estimated, since it changes dynamically. set to best possible resolution - mTimeResolution = resolution / 2; //can not be estimated, since it changes dynamically. set to best possible resolution - mSpectralDataInfo = new SpectralDataInfo(samplingRate, resolution, mFrequencyResolution, mTimeResolution); - - DBG(T("WaveletPacketBestBasisTransformation constructor freqResolution=") + - juce::String(mFrequencyResolution) + - T(",tres=") + juce::String(mTimeResolution) + - T(",fs=") + juce::String(mSamplingRate) + - T(",res=") + juce::String(mResolution) - ); - - ready = true; - calculated = true; -}; + double newSamplingRate, + ResolutionType newResolution, + WindowParameters::WindowFunction newWindowFunction, + WaveletParameters::WaveletBase newWaveletBaseType) + : AbstractWaveletTransformation(newSamplingRate, newResolution, TransformationParameters::Type::FAST_WAVELET_PACKET_BEST_BASIS_TRANSFORM, newWindowFunction, newWaveletBaseType), + //Time and frequency resolution can't be estimated since they change dynamically. Assume same values as for the DWT. + spectralDataInfo(SpectralDataInfo(newSamplingRate, newResolution, newResolution, newResolution / 2)) { + + DBG("WaveletPacketBestBasisTransformation constructor: " + getSpectralDataInfo().toString()); + setReady(); + setCalculated(); +} WaveletPacketBestBasisTransformation::~WaveletPacketBestBasisTransformation() { - ready = false; - DBG(T("WaveletPacketBestBasisTransformation destructed")); + setReady(false); + DBG("WaveletPacketBestBasisTransformation destructed"); } void WaveletPacketBestBasisTransformation::calculate() { - fillDWTInput(); + fillDWTInput(); + + //to hold the result of the wavelet packet transformation (=DWPT coeffs) + ArrayTreePer outDWPT(getWaveletFilterTreeMaxLevel()); + + //DWPT (discrete wavelet packet transform), periodic + analyse(outDWPT); + sortWaveletFilterTreeByScaleDescending(outDWPT); + + //calculate noise level for a chosen SNR + int signalToNoiseRatioInDecibel = Constants::SIGNAL_TO_NOISE_RATIO; + auto resolution = static_cast(getResolution()); + double noiseLevel = sqrt(1.0F / (signalToNoiseRatioInDecibel * resolution)); + const double oracCostFactor = (1.0 + sqrt(2.0F * log(static_cast(getWaveletFilterTreeMaxLevel()) * resolution)));//D&J Best Wavelet (BWB) + + //Find the best basis + HedgePer bestBasis; + extractBestBasis(outDWPT, bestBasis, noiseLevel, oracCostFactor); + + if (bestBasis.num_of_levels <= 1) { + DBG("WaveletPacketBestBasisTransformation::calculate best basis could not be found!"); + extractSpectrum(outDWPT); + } else { + extractSpectrum(outDWPT, bestBasis); + } +} - //to hold the result of the wavelet packet transformation (=DWPT coeffs) - ArrayTreePer out_DWPT(mDWT_maxLevel); +// ---------------------------------------------------------------------------- - //DWPT (discrete wavelet packet transform), periodic - Analysis(*mDWT_Input, out_DWPT, mDWT_filter_H, mDWT_filter_G, ConvDecPer); - sortDWPTTreeByScaleDescending(out_DWPT); +void WaveletPacketBestBasisTransformation::getCosts(const ArrayTreePer &a, Tree &t, const costFunAdv& costFunction, const real_number &sigma, const real_number &factor) { + getCostsHelp(a, &(t.root), costFunction, sigma, factor, 0, 0); + t.maxlevel = a.maxlevel; +} + + +void WaveletPacketBestBasisTransformation::getCostsHelp(const ArrayTreePer &a, Node **ptr, + const costFunAdv& costFunction, const real_number &sigma, const real_number &factor, + const integer_number &level, const integer_number &b) { + if (level <= a.maxlevel) { + real_number cost = (this->*costFunction)(a.block_start(level, b), a.block_length(level), sigma, factor, a.dim); + (*ptr) = new Node(cost, nullptr, nullptr); + assert(*ptr); + if (level < a.maxlevel) { + getCostsHelp(a, &((*ptr)->left), costFunction, sigma, factor, level + 1, b << 1U); + getCostsHelp(a, &((*ptr)->right), costFunction, sigma, factor, level + 1, (b << 1U) | 1); + } + } +} + +auto WaveletPacketBestBasisTransformation::oracCostAdv(const real_number *data, const integer_number &n, const real_number &sigma, const real_number &factor, const integer_number & /* k */) -> real_number { + real_number cost = 0; + real_number var = sigma * sigma; + real_number temp = 0; + + for (int i = 0; i < n; i++) { + temp = data[i] * data[i]; + + if (temp >= var * factor * factor) { + cost += var; + } else { + cost += temp; + } + } + return cost; +} - //calculate noise level for a chosen SNR //TODO should be provided in a better way e.g. by measurement... - int SNR = 48; - double noise_level = sqrt(1.0/(SNR*mResolution)); - double oracCostFactor = (1.0+sqrt((double)2*log((double)mDWT_maxLevel*mResolution)));//D&J Best Wavelet (BWB) +void WaveletPacketBestBasisTransformation::extractBestBasis(const ArrayTreePer &a, HedgePer &h, const double &sigma, const double &factor) { + LOG_PERFORMANCE_OF_SCOPE("WaveletPacketBestBasisTransformation extractBestBasis"); + assert(a.origin); + Tree b; - //Find the best basis - HedgePer bestBasis; - ExtractBestBasis(out_DWPT, bestBasis, noise_level, oracCostFactor); + // Get a member function reference to the cost function + real_number (WaveletPacketBestBasisTransformation::*costFunction)(const real_number *data, const integer_number &n, const real_number &sigma, const real_number &factor, const integer_number &k); + costFunction = &WaveletPacketBestBasisTransformation::oracCostAdv; - if (bestBasis.num_of_levels <= 1) { - DBG(T("WaveletPacketBestBasisTransformation::calculate best basis could not be found!")); - bestBasis = *mConstantLevelsHedge; - } - extractSpectrum(out_DWPT, bestBasis); + getCosts(a, b, costFunction, sigma, factor); + h.dim = a.dim; + BestBasis(h, b); + h.origin = new real_number[static_cast(h.dim)]; + ExtractHedge(h, a); } \ No newline at end of file diff --git a/src/dsp/transformations/WaveletPacketBestBasisTransformation.h b/src/dsp/transformations/WaveletPacketBestBasisTransformation.h index e968662c..1367cf8c 100644 --- a/src/dsp/transformations/WaveletPacketBestBasisTransformation.h +++ b/src/dsp/transformations/WaveletPacketBestBasisTransformation.h @@ -15,18 +15,100 @@ */ #pragma once #include "AbstractWaveletTransformation.h" -#include "..\..\plugin\SpectronParameters.h" class WaveletPacketBestBasisTransformation : public AbstractWaveletTransformation { public: - WaveletPacketBestBasisTransformation( - double samplingRate, - long resolution, - int windowFunctionNr = SpectronParameters::WINDOWING_DEFAULT, - int waveletBaseTypeNr = SpectronParameters::WAVELET_DEFAULT - ); - virtual ~WaveletPacketBestBasisTransformation(void); + enum Constants { + //TODO (JohT) could this be provided dynamically e.g. by measurement? + SIGNAL_TO_NOISE_RATIO = 48, + }; + + WaveletPacketBestBasisTransformation( + double newSamplingRate, + ResolutionType newResolution, + WindowParameters::WindowFunction newWindowFunction = WindowParameters::WindowFunction::DEFAULT, + WaveletParameters::WaveletBase newWaveletBaseType = WaveletParameters::WaveletBase::DEFAULT); + ~WaveletPacketBestBasisTransformation() override; + + WaveletPacketBestBasisTransformation(const WaveletPacketBestBasisTransformation &) = delete; //No copy contructor + WaveletPacketBestBasisTransformation(WaveletPacketBestBasisTransformation &&) = delete; //No move contructor + auto operator=(const WaveletPacketBestBasisTransformation &) -> WaveletPacketBestBasisTransformation & = delete;//No copy assignment + auto operator=(WaveletPacketBestBasisTransformation &&) -> WaveletPacketBestBasisTransformation & = delete; //No move assignment + + auto getSpectralDataInfo() -> const SpectralDataInfo & override { + return spectralDataInfo; + } + + auto getName() -> const char * override { return "Fast Wavelet Packet Best Basis Transformation"; } protected: - virtual void calculate(); + void calculate() override; + +private: + SpectralDataInfo spectralDataInfo; + + //advanced cost function type with param factor + using costFunAdv = real_number (WaveletPacketBestBasisTransformation::*)(const real_number *, const integer_number &, const real_number &, const real_number &, const integer_number &); + + // + + /** + * @brief Calculates + * + * using functional F calculate costs from given tree A. + * assumption: on input B is default empty tree + * on output B filled up with costs and B.maxlevel=A.maxlevel + * advanced function to integrate the cost function used in wave++ demo "demoWavPack.cpp" + * + * @param a + * @param t + * @param costFunction + * @param sigma + * @param factor + */ + void getCosts(const ArrayTreePer &a, Tree &t, const costFunAdv& costFunction, const real_number &sigma, const real_number &factor); + + /** + * @brief Advanced function to integrate the cost function used in wave++ demo "demoWavPack.cpp" + * + * @param a + * @param ptr + * @param costFunction + * @param sigma + * @param factor + * @param level + * @param b + */ + void getCostsHelp(const ArrayTreePer &a, Node **ptr, + const costFunAdv& costFunction, const real_number &sigma, const real_number &factor, + const integer_number &level, const integer_number &b); + + /** + * @brief Finds the best wavelet packet base which minimizes the cost function. + * + * Wrapping function: start with array bin tree A, calculate costs + * using F and sigma. Temporarily store costs in a bin tree and then + * find best basis which minimizes cost. On input H is an empty hedge, + * on output H completely filled up and contains the best basis + * advanced function to integrate the cost function used in wave++ demo "demoWavPack.cpp". + + * @param a + * @param h + * @param sigma + * @param factor + */ + void extractBestBasis(const ArrayTreePer &a, HedgePer &h, const double &sigma, const double &factor); + + /** + * @brief advanced function to integrate the cost function used in "demoWavPack.cpp" and former defined in wave++ demo "demoTools.cpp" + * + * @param data + * @param n + * @param sigma + * @param factor + * @param k + * @return real_number + */ + auto oracCostAdv(const real_number *data, const integer_number &n, + const real_number &sigma, const real_number &factor, const integer_number &k) -> real_number; }; diff --git a/src/dsp/transformations/WaveletPacketTransformation.cpp b/src/dsp/transformations/WaveletPacketTransformation.cpp index bbd4cbd2..96704d09 100644 --- a/src/dsp/transformations/WaveletPacketTransformation.cpp +++ b/src/dsp/transformations/WaveletPacketTransformation.cpp @@ -1,98 +1,97 @@ #include "WaveletPacketTransformation.h" -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" +#include "../../utilities/PerformanceLogger.h" +#include "TransformationParameters.h" +#include WaveletPacketTransformation::WaveletPacketTransformation( - double samplingRate, - long resolution, - int windowFunctionNr, - int waveletBaseTypeNr, - int resolutionRatioDWPT) - : AbstractWaveletTransformation(samplingRate, resolution, windowFunctionNr, waveletBaseTypeNr) -{ - mSpectralDataInfo = NULL; - setResolutionRatioDWPT(resolutionRatioDWPT); - updateConstantLevelsHedge(mDWPT_ResultTreeLevel); + double newSamplingRate, + ResolutionType newResolution, + WindowParameters::WindowFunction newWindowFunction, + WaveletParameters::WaveletBase newWaveletBase, + WaveletParameters::ResolutionRatioOption newResolutionRatioOption) + : AbstractWaveletTransformation(newSamplingRate, newResolution, TransformationParameters::Type::FAST_WAVELET_PACKET_TRANSFORM, newWindowFunction, newWaveletBase), + samplingRate(newSamplingRate), + timeFrequencyResolutionTreeLevelOffset(toTimeFrequencyResolutionTreeLevelOffset(newResolutionRatioOption)), + resultTreeLevel(getWaveletPacketResultTreeLevel(getWaveletFilterTreeMaxLevel(), timeFrequencyResolutionTreeLevelOffset)), + spectralDataInfo(calculateSpectralDataInfo()) { - DBG(T("Wavelet initialize: N=") + - juce::String(mResolution) + - T(",fres=") + juce::String(mFrequencyResolution) + - T(",tres=") + juce::String(mTimeResolution) + - T(",fs=") + juce::String(mSamplingRate) + - T(",maxLevel=") + juce::String(mDWT_maxLevel) + - T(",resultTree=") + juce::String(mDWPT_ResultTreeLevel) - ); + setResolutionRatioOption(newResolutionRatioOption); + updateConstantLevelsHedge(resultTreeLevel); - ready = true; - calculated = true; -}; + DBG("WaveletPacketTransformation constructed: resolution=" + + juce::String(newResolution) + + ",sampling frequency=" + juce::String(newSamplingRate) + + ",maxLevel=" + juce::String(getWaveletFilterTreeMaxLevel()) + + ",resultTree=" + juce::String(resultTreeLevel)); -WaveletPacketTransformation::~WaveletPacketTransformation() { - ready = false; - DBG(T("WaveletPacketTransformation destructed")); + setReady(); + setCalculated(); } -int WaveletPacketTransformation::getFrequencyResolution (int waveletPacketResultTreeLevel) { - return (int)1<<(waveletPacketResultTreeLevel); +WaveletPacketTransformation::~WaveletPacketTransformation() { + setReady(false); + DBG("WaveletPacketTransformation destructed"); } -int WaveletPacketTransformation::getTimeResolution() { - return 1<<(mDWT_maxLevel - mDWPT_ResultTreeLevel); +auto WaveletPacketTransformation::getFrequencyResolution(WaveletLevelType waveletPacketResultTreeLevel) -> Transformation::ResolutionType { + return 1U << waveletPacketResultTreeLevel; } -int WaveletPacketTransformation::getWaveletPacketResultTreeLevel (int maxLevel, int resolutionRatioDWPT) { - //the best possible time & freq resolution ratio can be read out of the (DWPT tree-)level, - //that results in a square of blocks and block-elements (and therefor an equal freq & time resolution) - //This is only possible, if the dimension is even (see tab below). - //To get the possibility to change this toward a better frequency- or time-resolution, - //the parameter resolutionRatioDWPT can be set (+1 -> freq.Res. x2 , -2 -> time.Res. x4). - // - //--------------------------------------------------------------------------- - //N DIM Level - //256 (8) 4 (2*128, 4*64, 8*32, 16*16) - //512 (9) 5? (2*256, 4*128, 8*64, 16*32, 32*16) ! - //1024 (10) 5 (2*512, 4*256, 8*128, 16*64, 32*32) - //2048 (11) 6? (2*1024, 4*512, 8*256, 16*128, 32*64, 64*32) ! - //4096 (12) 6 (2*2048, 4*1024, 8*512, 16*256, 32*128, 64*64) - //.. - //Level = ceil_round(Dim / 2) - //--------------------------------------------------------------------------- - return (int)ceil((float)maxLevel / 2.0) + resolutionRatioDWPT; +auto WaveletPacketTransformation::getTimeResolution() const -> Transformation::ResolutionType { + return 1U << (getWaveletFilterTreeMaxLevel() - resultTreeLevel); } -void WaveletPacketTransformation::setResolutionRatioDWPT (int resolutionRatioDWPT) { - DBG(T("WaveletPacketTransformation::setResolutionRatioDWPT started")); +auto WaveletPacketTransformation::getWaveletPacketResultTreeLevel(WaveletLevelType maxLevel, int resolutionRatioOffset) -> WaveletLevelType { + //the best possible time & freq resolution ratio can be read out of the (DWPT tree-)level, + //that results in a square of blocks and block-elements (and therefor an equal freq & time resolution) + //This is only possible, if the dimension is even (see tab below). + //To get the possibility to change this toward a better frequency- or time-resolution, + //the parameter resolutionRatioDWPT can be set (+1 -> freq.Res. x2 , -2 -> time.Res. x4). + // + //--------------------------------------------------------------------------- + //N DIM Level + //256 (8) 4 (2*128, 4*64, 8*32, 16*16) + //512 (9) 5? (2*256, 4*128, 8*64, 16*32, 32*16) ! + //1024 (10) 5 (2*512, 4*256, 8*128, 16*64, 32*32) + //2048 (11) 6? (2*1024, 4*512, 8*256, 16*128, 32*64, 64*32) ! + //4096 (12) 6 (2*2048, 4*1024, 8*512, 16*256, 32*128, 64*64) + //.. + //Level = ceil_round(Dim / 2) + //--------------------------------------------------------------------------- + assert(maxLevel > 0); + assert(resolutionRatioOffset != 99); + auto waveletPacketResultTreeLevel = maxLevel / 2 + resolutionRatioOffset; + DBG("AbstractWaveletTransformation::getWaveletPacketResultTreeLevel: maxLevel=" + juce::String(maxLevel) + + ",resolutionRatio=" + juce::String(resolutionRatioOffset) + + ",WaveletPacketResultTreeLevel=" + juce::String(waveletPacketResultTreeLevel)); + return waveletPacketResultTreeLevel; +} - mResolutionRatioDWPT = resolutionRatioDWPT; - //since value 0 is no more allowed as enum, the new to "equal" corresponding value will be set manually to zero - if (mResolutionRatioDWPT == SpectronParameters::RESOLUTION_RATIO_Equal) mResolutionRatioDWPT = 0; +void WaveletPacketTransformation::setResolutionRatioOption(WaveletParameters::ResolutionRatioOption newResolutionRatio) { + DBG("WaveletPacketTransformation::setResolutionRatioOption to " + std::string(WaveletParameters::ResolutionRatioOptionNames::map.at(newResolutionRatio))); + timeFrequencyResolutionTreeLevelOffset = toTimeFrequencyResolutionTreeLevelOffset(newResolutionRatio); + resultTreeLevel = getWaveletPacketResultTreeLevel(getWaveletFilterTreeMaxLevel(), timeFrequencyResolutionTreeLevelOffset); + spectralDataInfo = calculateSpectralDataInfo(); +} - mDWPT_ResultTreeLevel = getWaveletPacketResultTreeLevel(mDWT_maxLevel, mResolutionRatioDWPT); - mFrequencyResolution = getFrequencyResolution(mDWPT_ResultTreeLevel); - mTimeResolution = getTimeResolution(); +auto WaveletPacketTransformation::calculateSpectralDataInfo() const -> SpectralDataInfo { + DBG("WaveletPacketTransformation::calculateSpectralDataInfo: waveletTreeLevel=" + juce::String(resultTreeLevel)); + return {samplingRate, getResolution(), getFrequencyResolution(resultTreeLevel), getTimeResolution()}; +} - deleteAndZero(mSpectralDataInfo); - mSpectralDataInfo = new SpectralDataInfo(mSamplingRate, mResolution, mFrequencyResolution, mTimeResolution); - assert(mSpectralDataInfo); +auto WaveletPacketTransformation::toTimeFrequencyResolutionTreeLevelOffset(const WaveletParameters::ResolutionRatioOption &resolutionRatioOption) -> int { + using ResolutionRatioOptionValueType = std::underlying_type_t; + // Option 1: Time x 4 = -2 Offset, Option 2: Time x 2 = -1 Offset, Option 3: Equal Time/Frequency = 0 Offset, ... + return static_cast(resolutionRatioOption) - 3; } void WaveletPacketTransformation::calculate() { - fillDWTInput(); - - if (!mDWT_Input) { - DBG(T("WaveletPacketTransformation::calculate: mDWT_Input = null!")); - return; - } - - //to hold the result of the wavelet packet transformation (=DWPT coeffs) - ArrayTreePer out_DWPT(mDWT_maxLevel); - //DBG(T("WaveletPacketTransformation::calculate before with mDWT_maxLevel= ") + - // juce::String(mDWT_maxLevel) + - // T(",inLen=") + juce::String(mDWT_Input->length) + - // T(",outDim=") + juce::String(out_DWPT.dim) - //); + LOG_PERFORMANCE_OF_SCOPE("WaveletPacketTransformation calculate"); + fillDWTInput(); - //DWPT (discrete wavelet packet transform), periodic - Analysis(*mDWT_Input, out_DWPT, mDWT_filter_H, mDWT_filter_G, ConvDecPer); - sortDWPTTreeByScaleDescending(out_DWPT); - extractSpectrum(out_DWPT, *mConstantLevelsHedge); + //to hold the result of the wavelet packet transformation (=DWPT coeffs) + ArrayTreePer outDWT(getWaveletFilterTreeMaxLevel()); + analyse(outDWT); + sortWaveletFilterTreeByScaleDescending(outDWT); + extractSpectrum(outDWT); } \ No newline at end of file diff --git a/src/dsp/transformations/WaveletPacketTransformation.h b/src/dsp/transformations/WaveletPacketTransformation.h index e3c5b8bc..2481072d 100644 --- a/src/dsp/transformations/WaveletPacketTransformation.h +++ b/src/dsp/transformations/WaveletPacketTransformation.h @@ -14,29 +14,51 @@ ============================================================================== */ #pragma once +#include "../../parameter/SpecletParameters.h" #include "AbstractWaveletTransformation.h" -#include "..\..\plugin\SpectronParameters.h" +#include "WaveletParameters.h" class WaveletPacketTransformation : public AbstractWaveletTransformation { public: - WaveletPacketTransformation( - double samplingRate, - long resolution, - int windowFunctionNr = SpectronParameters::WINDOWING_DEFAULT, - int waveletBaseTypeNr = SpectronParameters::WAVELET_DEFAULT, - int resolutionRatioDWPT = SpectronParameters::RESOLUTION_RATIO_DEFAULT - ); - virtual ~WaveletPacketTransformation(void); - void setResolutionRatioDWPT (int resolutionRatioDWPT); + WaveletPacketTransformation( + double newSamplingRate, + ResolutionType newResolution, + WindowParameters::WindowFunction newWindowFunction, + WaveletParameters::WaveletBase newWaveletBaseType = WaveletParameters::WaveletBase::DEFAULT, + WaveletParameters::ResolutionRatioOption newResolutionRatioOption = WaveletParameters::ResolutionRatioOption::DEFAULT); + ~WaveletPacketTransformation() override; + WaveletPacketTransformation(const WaveletPacketTransformation &) = delete; //No copy contructor + WaveletPacketTransformation(WaveletPacketTransformation &&) = delete; //No move contructor + auto operator=(const WaveletPacketTransformation &) -> WaveletPacketTransformation & = delete;//No copy assignment + auto operator=(WaveletPacketTransformation &&) -> WaveletPacketTransformation & = delete; //No move assignment -protected: - virtual void calculate(); + auto getSpectralDataInfo() -> const SpectralDataInfo & override { + return spectralDataInfo; + } + auto getName() -> const char * override { return "Fast Wavelet Packet Transform"; } - int getWaveletPacketResultTreeLevel (int maxLevel, int resolutionRatioDWPT); - int getFrequencyResolution (int maxLevel); - int getTimeResolution (void); +protected: + void calculate() override; private: - int mDWPT_ResultTreeLevel; //Wavelet packet transform result tree level, - int mResolutionRatioDWPT; //Wavelet packet transform time/freq resolution ratio (0=equal time&freq) + double samplingRate; + int timeFrequencyResolutionTreeLevelOffset;//Wavelet packet transform time/freq resolution ratio (0=equal time&freq) + WaveletLevelType resultTreeLevel; //Wavelet packet transform result tree level, + + SpectralDataInfo spectralDataInfo; + + void setResolutionRatioOption(WaveletParameters::ResolutionRatioOption newResolutionRatio); + + auto getTimeResolution() const -> ResolutionType; + auto calculateSpectralDataInfo() const -> SpectralDataInfo; + + /** + * @brief Takes a ResolutionRatioOption and returns the offset (+/- int) to the filter tree level + * + * @param resolutionRatioOption + * @return const int + */ + static auto toTimeFrequencyResolutionTreeLevelOffset(const WaveletParameters::ResolutionRatioOption &resolutionRatioOption) -> int; + static auto getFrequencyResolution(WaveletLevelType waveletPacketResultTreeLevel) -> ResolutionType; + static auto getWaveletPacketResultTreeLevel(WaveletLevelType maxLevel, int resolutionRatioOffset) -> WaveletLevelType; }; diff --git a/src/dsp/transformations/WaveletParameters.h b/src/dsp/transformations/WaveletParameters.h new file mode 100644 index 00000000..76aaf7e0 --- /dev/null +++ b/src/dsp/transformations/WaveletParameters.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include + +namespace WaveletParameters { + + enum class WaveletBase { + DAUBECHIES_02 = 1, + DAUBECHIES_04, + DAUBECHIES_06, + DAUBECHIES_08, + DAUBECHIES_10, + DAUBECHIES_12, + DAUBECHIES_14, + DAUBECHIES_16, + DAUBECHIES_18, + DAUBECHIES_20, + COIFMAN_06, + COIFMAN_12, + COIFMAN_18, + COIFMAN_24, + COIFMAN_30, + BEYLKIN_18, + VAIDYANATHAN_18, + + NUMBER_OF_OPTIONS, + HAAR = DAUBECHIES_02, + DEFAULT = VAIDYANATHAN_18 + }; + + struct WaveletBaseNames { + static std::map createMap() { + return { + {WaveletBase::HAAR, "Haar (2)"}, + {WaveletBase::DAUBECHIES_02, "Daubechies (2)"}, + {WaveletBase::DAUBECHIES_04, "Daubechies (4)"}, + {WaveletBase::DAUBECHIES_06, "Daubechies (6)"}, + {WaveletBase::DAUBECHIES_08, "Daubechies (8)"}, + {WaveletBase::DAUBECHIES_10, "Daubechies (10)"}, + {WaveletBase::DAUBECHIES_12, "Daubechies (12)"}, + {WaveletBase::DAUBECHIES_14, "Daubechies (14)"}, + {WaveletBase::DAUBECHIES_16, "Daubechies (16)"}, + {WaveletBase::DAUBECHIES_18, "Daubechies (18)"}, + {WaveletBase::DAUBECHIES_20, "Daubechies (20)"}, + {WaveletBase::COIFMAN_06, "Coifman (6)"}, + {WaveletBase::COIFMAN_12, "Coifman (12)"}, + {WaveletBase::COIFMAN_18, "Coifman (18)"}, + {WaveletBase::COIFMAN_24, "Coifman (24)"}, + {WaveletBase::COIFMAN_30, "Coifman (30)"}, + {WaveletBase::BEYLKIN_18, "Beylkin (18)"}, + {WaveletBase::VAIDYANATHAN_18, "Vaidyanathan (18)"}, + }; + } + static const std::map map; + }; + + inline const std::map WaveletBaseNames::map = WaveletBaseNames::createMap(); + + enum class ResolutionRatioOption { + TIME_X4 = 1, + TIME_X2, + EQUAL, + FREQUENCY_X2, + FREQUENCY_X4, + + NUMBER_OF_OPTIONS, + DEFAULT = EQUAL + }; + + struct ResolutionRatioOptionNames { + // Map is created in inverse enum value order using std::greater to ensure that the default value (99) is the first one followed by decreasing frequency resolutions and ascending time resolutions. + static std::map> createMap() { + return { + {ResolutionRatioOption::TIME_X4, "Time x4"}, + {ResolutionRatioOption::TIME_X2, "Time x2"}, + {ResolutionRatioOption::FREQUENCY_X2, "Freq x2"}, + {ResolutionRatioOption::FREQUENCY_X4, "Freq x4"}, + {ResolutionRatioOption::EQUAL, "Freq/Time x1"}, + }; + } + static const std::map> map; + }; + + inline const std::map> ResolutionRatioOptionNames::map = ResolutionRatioOptionNames::createMap(); + +}// namespace WaveletParameters \ No newline at end of file diff --git a/src/dsp/transformations/WaveletTransformation.cpp b/src/dsp/transformations/WaveletTransformation.cpp index f709f9fb..2abd224d 100644 --- a/src/dsp/transformations/WaveletTransformation.cpp +++ b/src/dsp/transformations/WaveletTransformation.cpp @@ -1,41 +1,39 @@ -#pragma once #include "WaveletTransformation.h" -#include "../../utilities/PerformanceManager.h" +#include "../../utilities/PerformanceLogger.h" +#include "TransformationParameters.h" +#include -WaveletTransformation::WaveletTransformation( - double samplingRate, - long resolution, - int windowFunctionNr, - int waveletBaseTypeNr) - : AbstractWaveletTransformation(samplingRate, resolution, windowFunctionNr, waveletBaseTypeNr) -{ - mFrequencyResolution = resolution; - mTimeResolution = resolution / 2; - mSpectralDataInfo = new SpectralDataInfo(samplingRate, resolution, mFrequencyResolution, mTimeResolution); - DBG(T("WaveletTransformation::initialize done with fs=") + - juce::String(mSamplingRate) + - T(",res=") + juce::String(mResolution) - ); +WaveletTransformation::WaveletTransformation( + double newSamplingRate, + ResolutionType newResolution, + WindowParameters::WindowFunction newWindowFunction, + WaveletParameters::WaveletBase newWaveletBase) + : AbstractWaveletTransformation(newSamplingRate, newResolution, TransformationParameters::Type::FAST_WAVELET_TRANSFORM, newWindowFunction, newWaveletBase), + spectralDataInfo(SpectralDataInfo(newSamplingRate, newResolution, newResolution, newResolution / 2)) { - ready = true; - calculated = true; -}; + DBG("WaveletTransformation::initialize done with fs=" + juce::String(newSamplingRate) + ",res=" + juce::String(newResolution)); + assert(newResolution <= std::numeric_limits::max());//wave++ Interval requires the resolution to be a long + setReady(); + setCalculated(); +} WaveletTransformation::~WaveletTransformation() { - ready = false; - DBG(T("WaveletTransformation destructed")); + setReady(false); + DBG("WaveletTransformation destructed"); } void WaveletTransformation::calculate() { - //fills the mDWT_Input with data from the inputQueue - fillDWTInput(); - //output data container to hold the result of the wavelet transformation ("coefficients") - Interval outDWT(0, mResolution-1); - //fast wavelet transform - PerformanceManager::getSingletonInstance()->start(T("fwt")); - WaveTrans(*mDWT_Input, outDWT, mDWT_filter_H, mDWT_filter_G, ConvDecPer); - PerformanceManager::getSingletonInstance()->stop(T("fwt")); - //fills the outputQueue with the spectral data (in a ready to draw order) - extractSpectrum(outDWT); + //fills the mDWT_Input with data from the inputQueue + fillDWTInput(); + + //output data container to hold the result of the wavelet transformation ("coefficients") + Interval outDWT(0, static_cast(getResolution() - 1)); + //fast wavelet transform + { + LOG_PERFORMANCE_OF_SCOPE("WaveletTransformation analyse"); + analyse(outDWT); + } + //fills the outputQueue with the spectral data (in a ready to draw order) + extractSpectrum(outDWT); } \ No newline at end of file diff --git a/src/dsp/transformations/WaveletTransformation.h b/src/dsp/transformations/WaveletTransformation.h index 164c3a12..fe8b30b3 100644 --- a/src/dsp/transformations/WaveletTransformation.h +++ b/src/dsp/transformations/WaveletTransformation.h @@ -14,19 +14,35 @@ ============================================================================== */ #pragma once +#include "../../parameter/SpecletParameters.h" #include "AbstractWaveletTransformation.h" -#include "..\..\plugin\SpectronParameters.h" +#include "../windowing/WindowFunctionFactory.h" +#include "WaveletParameters.h" class WaveletTransformation : public AbstractWaveletTransformation { public: - WaveletTransformation( - double samplingRate, - long resolution, - int windowFunctionNr = SpectronParameters::WINDOWING_DEFAULT, - int waveletBaseTypeNr = SpectronParameters::WAVELET_DEFAULT - ); - virtual ~WaveletTransformation(void); + WaveletTransformation() = delete;//No default contructor + WaveletTransformation( + double newSamplingRate, + ResolutionType newResolution, + WindowParameters::WindowFunction newWindowFunction = WindowParameters::WindowFunction::DEFAULT, + WaveletParameters::WaveletBase newWaveletBase = WaveletParameters::WaveletBase::DEFAULT); + + ~WaveletTransformation() override; + WaveletTransformation(WaveletTransformation &) = delete; //No copy contructor + WaveletTransformation(WaveletTransformation &&) = delete; //No move contructor + auto operator=(WaveletTransformation &) -> WaveletTransformation & = delete; //No copy assignment + auto operator=(WaveletTransformation &&) -> WaveletTransformation & = delete;//No move assignment + + auto getSpectralDataInfo() -> const SpectralDataInfo & override { + return spectralDataInfo; + } + + auto getName() -> const char * override { return "Fast Wavelet Transformation"; } protected: - virtual void calculate(); + void calculate() override; + +private: + SpectralDataInfo spectralDataInfo; }; diff --git a/src/dsp/windowing/WindowFunction.cpp b/src/dsp/windowing/WindowFunction.cpp new file mode 100644 index 00000000..b0889969 --- /dev/null +++ b/src/dsp/windowing/WindowFunction.cpp @@ -0,0 +1,24 @@ +//Most of the window function implementations in this class had been taken out of the fft c program +//from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) +#include "WindowFunction.h" +#include + +WindowFunction::WindowFunction(unsigned long newResolution) : resolution(newResolution) { +} + +auto WindowFunction::getFactor(unsigned long index) -> double { + assert(index >= 0); + assert(index < resolution); + getWindow(); + return factors[index]; +} + +auto WindowFunction::getWindow() -> double * { + if (factors.empty()) { + factors.reserve(resolution); + for (unsigned long i = 0; i < resolution; i++) { + factors.push_back(calculateFactor(i)); + } + } + return factors.data(); +} \ No newline at end of file diff --git a/src/dsp/windowing/WindowFunction.h b/src/dsp/windowing/WindowFunction.h new file mode 100644 index 00000000..e821a6f4 --- /dev/null +++ b/src/dsp/windowing/WindowFunction.h @@ -0,0 +1,51 @@ +/* + ============================================================================== + This file is part of the VST spectrum analyzer plugin "speclet" (working title) + Copyright 2011 by Johannes Troppacher + ------------------------------------------------------------------------------ + This file may use parts of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + ------------------------------------------------------------------------------ + This file may use parts of the fftw library + Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology + ------------------------------------------------------------------------------ + This file may use parts of the wave++ library + Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic + ------------------------------------------------------------------------------ + Most of the window function implementations in this class had been taken out of the fft c program + from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) + ============================================================================== +*/ +#pragma once + +//------------------------------------------------------------------------------------// +// Interface as abstract class // +//------------------------------------------------------------------------------------// +#include +class WindowFunction { +public: + explicit WindowFunction(unsigned long resolution); + + virtual ~WindowFunction() = default; + WindowFunction() = delete; //explicit no default contructor; + WindowFunction(const WindowFunction &) = default; //no copy contructor; + WindowFunction(WindowFunction &&) = delete; //no move contructor; + auto operator=(const WindowFunction &) -> WindowFunction & = default;//no copy assignment; + auto operator=(WindowFunction &&) -> WindowFunction & = delete; //no move assignment; + + auto getFactor(unsigned long index) -> double; + auto getWindow() -> double *; + virtual auto getName() -> const char * = 0; + +protected: + constexpr static const double PIx2 = 6.283185307179586476925286766559; + + virtual auto calculateFactor(unsigned long int index) -> double = 0;//will be redefined by the derived windowing function + [[nodiscard]] auto getWindowSize() const -> double { + return static_cast(resolution - 1); + } + +private: + unsigned long resolution;//also known as N or FFT-size or length of signal piece array or (window size + 1) + std::vector factors; +}; \ No newline at end of file diff --git a/src/dsp/windowing/WindowFunctionFactory.cpp b/src/dsp/windowing/WindowFunctionFactory.cpp new file mode 100644 index 00000000..8edb0d7d --- /dev/null +++ b/src/dsp/windowing/WindowFunctionFactory.cpp @@ -0,0 +1,72 @@ +//Most of the window function implementations in this class had been taken out of the fft c program +//from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) +#include "WindowFunctionFactory.h" +#include "WindowFunction.h" +#include "WindowFunctions.h" + +#include "../../parameter/SpecletParameters.h" +#include "WindowParameters.h" +#include +#include +#include +#include +#include +#include +#include + +auto WindowFunctionFactory::getSingletonInstance() -> WindowFunctionFactory & { + static WindowFunctionFactory singletonInstance;// Guaranteed to be destroyed. Instantiated on first use. + return singletonInstance; +} + +auto WindowFunctionFactory::getWindow(const WindowParameters::WindowFunction &newWindowFunction, unsigned long resolution) -> std::shared_ptr { + Key key{newWindowFunction, resolution}; + auto lowerBoundIterator = windowFunctionsCache.lower_bound(key); + auto keyComparator = windowFunctionsCache.key_comp(); + if ((lowerBoundIterator != windowFunctionsCache.end()) && !(keyComparator(key, lowerBoundIterator->first))) { + // Window function exists in cache: return it + return lowerBoundIterator->second; + } + // Window function does not exist in cache: create it. + auto newWindow = createWindow(key.windowFunction, key.resolution); + windowFunctionsCache[key] = newWindow; + return newWindow; +} + +auto WindowFunctionFactory::createWindow(const WindowParameters::WindowFunction &newWindowFunction, unsigned long resolution) -> std::shared_ptr { + switch (newWindowFunction) { + case WindowParameters::WindowFunction::BARLETT: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::BLACKMAN: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::BLACKMAN_HARRIS: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::HAMMING: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::HANN: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::PARZEN: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::RECTANGULAR: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::WELCH: { + return std::make_shared(resolution); + } + case WindowParameters::WindowFunction::NUMBER_OF_OPTIONS: + default: { + using WindowFunctionValueType = std::underlying_type::type; + throw std::invalid_argument("Unknown windowing function " + std::to_string(static_cast(newWindowFunction))); + } + } +} + +void WindowFunctionFactory::clearCache() { + windowFunctionsCache.clear(); +} \ No newline at end of file diff --git a/src/dsp/windowing/WindowFunctionFactory.h b/src/dsp/windowing/WindowFunctionFactory.h new file mode 100644 index 00000000..28bf3ab0 --- /dev/null +++ b/src/dsp/windowing/WindowFunctionFactory.h @@ -0,0 +1,58 @@ +/* + ============================================================================== + This file is part of the VST spectrum analyzer plugin "speclet" (working title) + Copyright 2011 by Johannes Troppacher + ------------------------------------------------------------------------------ + This file may use parts of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + ------------------------------------------------------------------------------ + This file may use parts of the fftw library + Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology + ------------------------------------------------------------------------------ + This file may use parts of the wave++ library + Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic + ------------------------------------------------------------------------------ + Most of the window function implementations in this class had been taken out of the fft c program + from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) + ============================================================================== +*/ +#pragma once +#include "WindowFunction.h" +#include "WindowParameters.h" +#include +#include +#include + +/** + * @brief Factory class for window functions + */ +class WindowFunctionFactory { +public: + static auto getSingletonInstance() -> WindowFunctionFactory &; + + // Copy-constructors and move- and assignment-operator are deleted, because this class is a singleton. + WindowFunctionFactory(WindowFunctionFactory const &) = delete; + WindowFunctionFactory(WindowFunctionFactory &&) = delete; + auto operator=(WindowFunctionFactory const &) -> WindowFunctionFactory & = delete; + auto operator=(WindowFunctionFactory const &&) -> WindowFunctionFactory & = delete; + + auto getWindow(const WindowParameters::WindowFunction &newWindowFunction, unsigned long resolution) -> std::shared_ptr; + void clearCache(); + +private: + WindowFunctionFactory() = default; + ~WindowFunctionFactory() = default; + + struct Key { + WindowParameters::WindowFunction windowFunction; + unsigned long resolution; + auto operator < (const Key& other) const -> bool { + return std::tie(windowFunction, resolution) < std::tie(other.windowFunction, other.resolution); + } + }; + + using WindowFunctionMapType = std::map>; + WindowFunctionMapType windowFunctionsCache; + + static auto createWindow(const WindowParameters::WindowFunction& newWindowFunction, unsigned long resolution) -> std::shared_ptr; +}; \ No newline at end of file diff --git a/src/dsp/windowing/WindowFunctions.cpp b/src/dsp/windowing/WindowFunctions.cpp new file mode 100644 index 00000000..6527a52c --- /dev/null +++ b/src/dsp/windowing/WindowFunctions.cpp @@ -0,0 +1,79 @@ +//Most of the window function implementations in this class had been taken out of the fft c program +//from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) +#include "WindowFunctions.h" +#include "../../parameter/SpecletParameters.h" + +/* See Oppenheim & Schafer, Digital Signal Processing, p. 241 (1st ed.) */ +auto WindowBartlett::calculateFactor(unsigned long index) -> double { + double a = 2.0 / getWindowSize(); + double indexDouble = static_cast(index); + double w = indexDouble * a; + if (w > 1.0) { + w = 2.0 - w; + } + return w; +} + +/* See Oppenheim & Schafer, Digital Signal Processing, p. 242 (1st ed.) */ +auto WindowBlackman::calculateFactor(unsigned long index) -> double { + double a = PIx2 / getWindowSize(); + double indexDouble = static_cast(index); + double w = 0.42 - 0.5 * cos(a * indexDouble) + 0.08 * cos(2 * a * indexDouble); + return w; +} + +/* See Harris, F.J., "On the use of windows for harmonic analysis with the +discrete Fourier transform", Proc. IEEE, Jan. 1978 */ +auto WindowBlackmanHarris::calculateFactor(unsigned long index) -> double { + double a = PIx2 / getWindowSize(); + double indexDouble = static_cast(index); + double w = 0.35875 - 0.48829 * cos(a * indexDouble) + 0.14128 * cos(2 * a * indexDouble) - 0.01168 * cos(3 * a * indexDouble); + return w; +} + +/* See Oppenheim & Schafer, Digital Signal Processing, p. 242 (1st ed.) */ +auto WindowHamming::calculateFactor(unsigned long index) -> double { + double a = PIx2 / getWindowSize(); + double indexDouble = static_cast(index); + double w = 0.54 - 0.46 * cos(a * indexDouble); + return w; +} + +/* See Oppenheim & Schafer, Digital Signal Processing, p. 242 (1st ed.) +The second edition of Numerical Recipes calls this the "Hann" window. */ +auto WindowHann::calculateFactor(unsigned long index) -> double { + double a = PIx2 / getWindowSize(); + double indexDouble = static_cast(index); + double w = 0.5 - 0.5 * cos(a * indexDouble); + return w; +} + +/* See Press, Flannery, Teukolsky, & Vetterling, Numerical Recipes in C, +p. 442 (1st ed.) */ +auto WindowParzen::calculateFactor(unsigned long index) -> double { + double a = getWindowSize() / 2.0; + double indexDouble = static_cast(index); + double w = (indexDouble - a) / (a + 1); + if (w > 0.0) { + w = 1 - w; + } else { + w = 1 + w; + } + return w; +} + +/* See any of the above references. */ +auto WindowRectangular::calculateFactor(unsigned long /* index */) -> double { + return (1.0); +} + +/* See Press, Flannery, Teukolsky, & Vetterling, Numerical Recipes in C, +p. 442 (1st ed.) or p. 554 (2nd ed.) */ +auto WindowWelch::calculateFactor(unsigned long index) -> double { + double a = getWindowSize() / 2.0, w; + double indexDouble = static_cast(index); + w = (indexDouble - a) / (a + 1); + w = 1 - w * w; + + return w; +} \ No newline at end of file diff --git a/src/dsp/windowing/WindowFunctions.h b/src/dsp/windowing/WindowFunctions.h new file mode 100644 index 00000000..0243459d --- /dev/null +++ b/src/dsp/windowing/WindowFunctions.h @@ -0,0 +1,80 @@ +/* + ============================================================================== + This file is part of the VST spectrum analyzer plugin "speclet" (working title) + Copyright 2011 by Johannes Troppacher + ------------------------------------------------------------------------------ + This file may use parts of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + ------------------------------------------------------------------------------ + This file may use parts of the fftw library + Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology + ------------------------------------------------------------------------------ + This file may use parts of the wave++ library + Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic + ------------------------------------------------------------------------------ + Most of the window function implementations in this class had been taken out of the fft c program + from George B. Moody (GNU General Public License, email: george@mit.edu, http://www.physionet.org/) + ============================================================================== +*/ +#pragma once +#include "WindowFunction.h" +#include + +//------------------------------------------------------------------------------------// +// Interface implementations // +//------------------------------------------------------------------------------------// +class WindowBartlett : public WindowFunction { +public: + explicit WindowBartlett(unsigned long newResolution) : WindowFunction(newResolution) {} + auto calculateFactor(unsigned long index) -> double override;//bartlett windowing function + auto getName() -> const char * override { return "Barlett"; } +}; + +class WindowBlackman : public WindowFunction { +public: + explicit WindowBlackman(unsigned long newResolution) : WindowFunction(newResolution) {} + auto calculateFactor(unsigned long index) -> double override;//blackman windowing function + auto getName() -> const char * override { return "Blackman"; } +}; + +class WindowBlackmanHarris : public WindowFunction { +public: + explicit WindowBlackmanHarris(unsigned long newResolution) : WindowFunction(newResolution) {} + auto calculateFactor(unsigned long index) -> double override;//blackman_harris windowing function + auto getName() -> const char * override { return "Blackman-Harris"; } +}; + +class WindowHamming : public WindowFunction { +public: + explicit WindowHamming(unsigned long newResolution) : WindowFunction(newResolution) {} + auto calculateFactor(unsigned long index) -> double override;//hamming windowing function + auto getName() -> const char * override { return "Hamming"; } +}; + +class WindowHann : public WindowFunction { +public: + explicit WindowHann(unsigned long newResolution) : WindowFunction(newResolution) {} + auto calculateFactor(unsigned long index) -> double override;//hanning windowing function + auto getName() -> const char * override { return "Hann"; } +}; + +class WindowParzen : public WindowFunction { +public: + explicit WindowParzen(unsigned long newResolution) : WindowFunction(newResolution) {} + auto calculateFactor(unsigned long index) -> double override;//parzen windowing function + auto getName() -> const char * override { return "Parzen"; } +}; + +class WindowRectangular : public WindowFunction { +public: + explicit WindowRectangular(unsigned long newResolution) : WindowFunction(newResolution){} + auto calculateFactor(unsigned long index) -> double override;//square windowing function + auto getName() -> const char * override { return "Rectangular"; } +}; + +class WindowWelch : public WindowFunction { +public: + explicit WindowWelch(unsigned long newResolution) : WindowFunction(newResolution) {} + auto calculateFactor(unsigned long index) -> double override;//welch windowing function + auto getName() -> const char * override { return "Welch"; } +}; \ No newline at end of file diff --git a/src/dsp/windowing/WindowParameters.h b/src/dsp/windowing/WindowParameters.h new file mode 100644 index 00000000..b164ed56 --- /dev/null +++ b/src/dsp/windowing/WindowParameters.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +namespace WindowParameters { + + /** + * @brief Available window functions + */ + enum class WindowFunction { + BARLETT = 1, + BLACKMAN, + BLACKMAN_HARRIS, + HAMMING, + HANN, + PARZEN, + WELCH, + RECTANGULAR, + + NUMBER_OF_OPTIONS, + DEFAULT = BLACKMAN_HARRIS + }; + + struct WindowFunctionNames { + static std::map createMap() { + return { + {WindowFunction::BARLETT, "Barlett"}, + {WindowFunction::BLACKMAN, "Blackman"}, + {WindowFunction::BLACKMAN_HARRIS, "Blackman-Harris"}, + {WindowFunction::HAMMING, "Hamming"}, + {WindowFunction::HANN, "Hann"}, + {WindowFunction::PARZEN, "Parzen"}, + {WindowFunction::WELCH, "Welch"}, + {WindowFunction::RECTANGULAR, "Rectangular"}, + }; + } + static const std::map map; + }; + + inline const std::map WindowFunctionNames::map = WindowFunctionNames::createMap(); + +}// namespace WindowParameters \ No newline at end of file diff --git a/src/parameter/SpecletParameters.cpp b/src/parameter/SpecletParameters.cpp new file mode 100644 index 00000000..95ed436f --- /dev/null +++ b/src/parameter/SpecletParameters.cpp @@ -0,0 +1,130 @@ +#include "SpecletParameters.h" +#include "../dsp/SignalGeneratorParameters.h" +#include "../dsp/transformations/TransformationParameters.h" +#include "../dsp/transformations/WaveletParameters.h" +#include "../dsp/windowing/WindowParameters.h" +#include "../ui/ColorGradientsParameters.h" +#include "../ui/SpecletDrawerParameters.h" +#include "../utilities/PerformanceLogger.h" +#include "juce_core/system/juce_PlatformDefs.h" +#include + +SpecletParameters::SpecletParameters(juce::AudioProcessor &audioProcessor) + : parameters(audioProcessor, nullptr, "Parameters", createParameterLayout()) { + + assert(getResolution() > 0); + waitForParameterChange.signal(); +} + +template +auto SpecletParameters::enumOptionToFloat(const _Tp &enumType) const -> float { + auto enumValue = static_cast>(enumType); + return static_cast(enumValue); +} + +auto SpecletParameters::isTransformationParameter(const juce::String ¶meterID) -> bool { + return parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_RESOLUTION) || parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_TRANSFORMATION) || parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_WAVELET) || parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_WAVELETPACKETBASIS) || parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_WINDOWING); +} + +auto SpecletParameters::getParameter(const juce::String &name) const -> float { + return parameters.getRawParameterValue(name)->load(); +} + +auto SpecletParameters::getParameterAsSelection(const juce::String &name) const -> int { + return static_cast(getParameter(name)) + 1; +} + +auto SpecletParameters::getParameterList(const juce::String &name) const -> juce::StringArray { + return parameters.getParameter(name)->getAllValueStrings(); +} + +auto SpecletParameters::getResolution() const -> unsigned long { + auto choice = getParameterAsSelection(PARAMETER_RESOLUTION); + //choice = 1 means resolution = 256 (2 ^ 8) so the exponent is 7 + choice. + auto resolutionShiftValue = 7 + choice; + //instead of using pow(2, resolutionShiftValue) we shift the value 1 to the left + return 1U << resolutionShiftValue; +} + +void SpecletParameters::addListener(juce::AudioProcessorValueTreeState::Listener *listener) { + for (int i = 0; i < parameters.state.getNumChildren(); i++) { + auto parameter = parameters.state.getChild(i); + if (parameter.isValid()) { + auto parameterId = parameter.getProperty(PROPERTY_ID).toString(); + parameters.addParameterListener(parameterId, listener); + } + } +} + +void SpecletParameters::removeListener(juce::AudioProcessorValueTreeState::Listener *listener) { + for (int i = 0; i < parameters.state.getNumChildren(); i++) { + auto parameter = parameters.state.getChild(i); + if (parameter.isValid()) { + auto parameterId = parameter.getProperty(PROPERTY_ID).toString(); + parameters.removeParameterListener(parameterId, listener); + } + } +} + +void SpecletParameters::getStateInformation(juce::MemoryBlock &destData) const { + // You should call this method from the audio processor's getStateInformation to store your parameters in the memory block. + juce::MemoryOutputStream memoryOutputStream(destData, true); + parameters.state.writeToStream(memoryOutputStream); +} + +void SpecletParameters::setStateInformation(const juce::ValueTree &tree) { + // You should call this method from the audio processor's setStateInformation to restore your parameters. + if (tree.isValid()) { + parameters.replaceState(tree); + } +} + +template +static void add(juce::AudioProcessorValueTreeState::ParameterLayout &group, std::unique_ptr param) { + group.add(std::move(param)); +} + +template +static Param &addToLayout(Group &layout, Ts &&...ts) { + auto param = std::make_unique(std::forward(ts)...); + auto &ref = *param; + add(layout, std::move(param)); + return ref; +} + +auto SpecletParameters::createParameterLayout() -> juce::AudioProcessorValueTreeState::ParameterLayout { + juce::AudioProcessorValueTreeState::ParameterLayout layout; + + auto routingOptions = {"Mid", "Side", "Left", "Right", "Oscillator"}; + addToLayout(layout, PARAMETER_ROUTING, PARAMETER_ROUTING, routingOptions, 0, "Audio Source"); + + auto transformationOptions = {"FFT", "FWT", "WPT", "WPT BestBasis", "Off"}; + addToLayout(layout, PARAMETER_TRANSFORMATION, PARAMETER_TRANSFORMATION, transformationOptions, 0, "Transformation"); + + auto resolutionOptions = {"256", "512", "1024", "2048", "4096", "8192", "16384", "32768", "65536"}; + addToLayout(layout, PARAMETER_RESOLUTION, PARAMETER_RESOLUTION, resolutionOptions, 3, "Resolution"); + + auto waveletPacketBasisOptions = {"Time x4", "Time x2", "Freq/Time x1", "Freq x2", "Freq x4"}; + addToLayout(layout, PARAMETER_WAVELETPACKETBASIS, PARAMETER_WAVELETPACKETBASIS, waveletPacketBasisOptions, 2, "Wavelet Packet Basis"); + + auto windowingOptions = {"Barlett", "Blackman", "Blackman-Harris", "Hamming", "Hann", "Parzen", "Welch", "Rectangular"}; + addToLayout(layout, PARAMETER_WINDOWING, PARAMETER_WINDOWING, windowingOptions, 2, "Window Function"); + + auto waveletOptions = {"Haar (2)", "Daubechies (4)", "Daubechies (6)", "Daubechies (8)", "Daubechies (10)", "Daubechies (12)", "Daubechies (14)", "Daubechies (16)", "Daubechies (18)", "Daubechies (20)", "Coifman (6)", "Coifman (12)", "Coifman (18)", "Coifman (24)", "Coifman (30)", "Beylkin (18)", "Vaidyanathan (18)"}; + addToLayout(layout, PARAMETER_WAVELET, PARAMETER_WAVELET, waveletOptions, 17, "Wavelet"); + + auto generatorFrequencyRange = juce::NormalisableRange(10.0F, 20000.0F, 1.0F, 0.25F); + addToLayout(layout, PARAMETER_GENERATORFREQUENCY, PARAMETER_GENERATORFREQUENCY, generatorFrequencyRange, 100.0f, "Oscillator Frequency"); + + auto generatorOptions = {"Sine", "Triangle", "Sawtooth", "Rectangle", "Noise"}; + addToLayout(layout, PARAMETER_GENERATOR, PARAMETER_GENERATOR, generatorOptions, 0, "Oscillator"); + + auto axisOptions = {"linear", "logarithmic"}; + addToLayout(layout, PARAMETER_LOGFREQUENCY, PARAMETER_LOGFREQUENCY, axisOptions, 1, "Frequency Scale"); + addToLayout(layout, PARAMETER_LOGMAGNITUDE, PARAMETER_LOGMAGNITUDE, axisOptions, 1, "Magnitude Scale"); + + auto colorModeOptions = {"Blue", "Green", "Rainbow", "Fire"}; + addToLayout(layout, PARAMETER_COLORMODE, PARAMETER_COLORMODE, colorModeOptions, 3, "Color Mode"); + + return layout; +} diff --git a/src/parameter/SpecletParameters.h b/src/parameter/SpecletParameters.h new file mode 100644 index 00000000..ec378302 --- /dev/null +++ b/src/parameter/SpecletParameters.h @@ -0,0 +1,166 @@ +/* + ============================================================================== + This file is part of the VST spectrum analyzer plugin "speclet" (working title) + Copyright 2011 by Johannes Troppacher + ------------------------------------------------------------------------------ + This file may use parts of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + ------------------------------------------------------------------------------ + This file may use parts of the fftw library + Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology + ------------------------------------------------------------------------------ + This file may use parts of the wave++ library + Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic + ============================================================================== +*/ +#pragma once + +#include +#include + +class SpecletParameters { + // --------------- constants --------------- // +public: + //--------------------------------------------------------------------------- + //IMPORTANT: Since juce components like combo boxes can't handle index = 0, + // enums must not contain a zero value + // Only exception: PARAMETER_INDEX (since it has to do with VST SDK) + //--------------------------------------------------------------------------- + + enum Constants { + TIMEOUT_WAIT_BEFORE_SET = 5000 + }; + enum Parameters { + //Has to start with index=0. TOTAL_NUMBER_OF_PARAMS needs to be the last parameter. + PARAMETER_INDEX_Routing = 0, + PARAMETER_INDEX_Transformation, + PARAMETER_INDEX_Resolution, + PARAMETER_INDEX_WaveletPacketBasis, + PARAMETER_INDEX_Windowing, + PARAMETER_INDEX_Wavelet, + PARAMETER_INDEX_Generator, + PARAMETER_INDEX_GeneratorFrequency, + PARAMETER_INDEX_LogFrequency, + PARAMETER_INDEX_LogMagnitude, + PARAMETER_INDEX_ColorMode, + + TOTAL_NUMBER_OF_PARAMS + }; + enum OptionsResolution { + RESOLUTION_256 = 1, + RESOLUTION_512, + RESOLUTION_1024, + RESOLUTION_2048, + RESOLUTION_4096, + RESOLUTION_8192, + RESOLUTION_16384, + RESOLUTION_32768, + RESOLUTION_65536, + + RESOLUTION_MAX = RESOLUTION_65536, + RESOLUTION_DEFAULT = RESOLUTION_4096 + }; + enum OptionsRouting { + ROUTING_MID = 1, + ROUTING_SIDE, + ROUTING_LEFT, + ROUTING_RIGHT, + ROUTING_GENERATOR, + + ROUTING_NUMBER_OF_OPTIONS, + ROUTING_DEFAULT = ROUTING_MID + }; + + //"Initialization ... with static storage duration may throw an exception that cannot be caught" + //Juce doesn't support string_view yet, constexpr doesn't work with string literals and the exception is very unlikely to happen + //NOLINTBEGIN + inline const static juce::String PROPERTY_ID{"id"}; + inline const static juce::String PROPERTY_VALUE{"value"}; + inline const static juce::String PARAMETER_COLORMODE{"colormode"}; + inline const static juce::String PARAMETER_LOGMAGNITUDE{"logmagnitude"}; + inline const static juce::String PARAMETER_LOGFREQUENCY{"logfrequency"}; + inline const static juce::String PARAMETER_RESOLUTION{"resolution"}; + inline const static juce::String PARAMETER_ROUTING{"routing"}; + inline const static juce::String PARAMETER_GENERATOR{"generator"}; + inline const static juce::String PARAMETER_GENERATORFREQUENCY{"generatorfrequency"}; + inline const static juce::String PARAMETER_TRANSFORMATION{"transform"}; + inline const static juce::String PARAMETER_WAVELET{"wavelet"}; + inline const static juce::String PARAMETER_WAVELETPACKETBASIS{"waveletpacketbasis"}; + inline const static juce::String PARAMETER_WINDOWING{"windowing"}; + //NOLINTEND + + // --------------- methods --------------- // + + explicit SpecletParameters(juce::AudioProcessor & audioProcessor); + ~SpecletParameters() = default; + SpecletParameters(const SpecletParameters& other) = delete; //non-copyable + SpecletParameters(SpecletParameters&& other) = delete; //non-moveable + auto operator=(const SpecletParameters& other) -> SpecletParameters& = delete; //non-copyable + auto operator=(SpecletParameters&& other) -> SpecletParameters& = delete; //non-moveable + + static auto isTransformationParameter(const juce::String& parameterID) -> bool; + + void blockParameterChanges() const { waitForParameterChange.reset(); } + void unblockParameterChanges() const { waitForParameterChange.signal(); } + + auto getParameter(const juce::String &name) const -> float; + auto getParameterAsSelection(const juce::String &name) const -> int; + auto getParameterList(const juce::String &name) const -> juce::StringArray; + + auto getColorMode() const -> int { return getParameterAsSelection(PARAMETER_COLORMODE); } + auto getGenerator() const -> int { return getParameterAsSelection(PARAMETER_GENERATOR); } + auto getGeneratorFrequency() const -> float { return getParameter(PARAMETER_GENERATORFREQUENCY); } + auto getLogMagnitude() const -> bool { return getParameterAsSelection(PARAMETER_LOGMAGNITUDE); } + auto getLogFrequency() const -> bool { return getParameterAsSelection(PARAMETER_LOGFREQUENCY); } + auto getResolution() const -> unsigned long; + auto getRouting() const -> int { return getParameterAsSelection(PARAMETER_ROUTING); } + auto getTransformation() const -> int { return getParameterAsSelection(PARAMETER_TRANSFORMATION); } + auto getWavelet() const -> int { return getParameterAsSelection(PARAMETER_WAVELET); } + auto getWaveletPacketBasis() const -> int { return getParameterAsSelection(PARAMETER_WAVELETPACKETBASIS); } + auto getWindowing() const -> int { return getParameterAsSelection(PARAMETER_WINDOWING); } + auto getParameters() -> juce::AudioProcessorValueTreeState & { return parameters; } + /** + * @brief Adds the given listener for all parameters so it gets notified when a parameter changes. + * + * @param listener + */ + void addListener(juce::AudioProcessorValueTreeState::Listener *listener); + /** + * @brief Removes the given listener for all parameters so it won't get notified on parameter changes any more. + * + * @param listener + */ + void removeListener(juce::AudioProcessorValueTreeState::Listener *listener); + /** + * @brief Loads the current state of all parameters into the given memory block. + * + * @param destData + */ + void getStateInformation(juce::MemoryBlock &destData) const; + /** + * @brief Set the state of all parameters to values of the given value tree. + * + * @param tree + */ + void setStateInformation(const juce::ValueTree & tree); + +private: + enum class ChildIndices { + CHILD_INDEX_PARAMETERS = 0, + CHILD_INDEX_METADATA, + + NUMBER_OF_INDICES + }; + + // --------------- members --------------- // + juce::ValueTree properties = juce::ValueTree("SpecletParameters"); + juce::WaitableEvent waitForParameterChange = juce::WaitableEvent(true); + juce::CriticalSection criticalSection; + juce::AudioProcessorValueTreeState parameters; + + // --------------- methods --------------- // + template + auto enumOptionToFloat(const _Tp& enumType) const -> float; + + static auto createParameterLayout() -> juce::AudioProcessorValueTreeState::ParameterLayout; +}; diff --git a/src/plugin/SpectronAudioProcessor.cpp b/src/plugin/SpectronAudioProcessor.cpp deleted file mode 100644 index e25f69a8..00000000 --- a/src/plugin/SpectronAudioProcessor.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - ============================================================================== - ============================================================================== -*/ - -#include "../plugin/SpectronAudioProcessor.h" -#include "../user interface/SpectronMainUI.h" -#include "../dsp/transformations/TransformationFactory.h" -#include "../utilities/PerformanceManager.h" -#include "../user interface/ColourGradients.h" - -#define DEFAULT_SAMPLINGRATE 44100 - -//============================================================================== -SpectronAudioProcessor::SpectronAudioProcessor() - : signalGenerator(0), currentTransformation(0), parameters(0) -{ - //TODO height and width later for flexible resizing? - lastUIWidth = 800; - lastUIHeight = 360; - //lastPosInfo.resetToDefault(); - - #if _LOGTOFILE - juce::Logger::setCurrentLogger(new juce::FileLogger(juce::File("c:/temp/speclet.log"), "Speclet LogFile"), true); - #endif - - //gets the pointer to the parameters singelton - for a better readability - parameters = SpectronParameters::getSingletonInstance(); - - //Initialize with default settings - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_ColorMode, SpectronParameters::COLORMODE_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Generator, SpectronParameters::GENERATOR_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_GeneratorFrequency, 1000.0); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_LogFrequency, SpectronParameters::PLOT_AXIS_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_LogMagnitude, SpectronParameters::PLOT_AXIS_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Resolution, SpectronParameters::RESOLUTION_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Routing, SpectronParameters::ROUTING_MID); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Transformation, SpectronParameters::TRANSFORM_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Wavelet, SpectronParameters::WAVELET_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_WaveletPaketBase, SpectronParameters::RESOLUTION_RATIO_DEFAULT); - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Windowing, SpectronParameters::WINDOWING_DEFAULT); - - parameter_routing = parameters->getRouting(); - //registeres itself as listener for parameter-changes - parameters->addListener(this); - DBG("SpectronAudioProcessor as parameter listener added"); - LOG("SpectronAudioProcessor as parameter listener added"); -} - -SpectronAudioProcessor::~SpectronAudioProcessor() -{ - SpectronParameters::getSingletonInstance()->removeListener(this); - DBG("SpectronAudioProcessor as parameter listener removed"); - LOG("SpectronAudioProcessor as parameter listener removed"); - - currentTransformation = NULL; - parameters = NULL; - - TransformationFactory::getSingletonInstance()->destruct(); - WindowFunctionsFactory::getSingletonInstance()->destruct(); - PerformanceManager::getSingletonInstance()->destruct(); - ColourGradients::getSingletonInstance()->destruct(); - SpectronParameters::getSingletonInstance()->destruct(); - - #if _LOGTOFILE - juce::Logger::setCurrentLogger(0, true); - #endif - - deleteAndZero(signalGenerator); -} - -//============================================================================== -int SpectronAudioProcessor::getNumParameters() -{ - return parameters->TOTAL_NUMBER_OF_PARAMS; -} - -float SpectronAudioProcessor::getParameter (int index) -{ - // This method will be called by the host, probably on the audio thread, so - // it's absolutely time-critical. Don't use critical sections or anything - // UI-related, or anything at all that may block in any way! - return parameters->getParameter(index); -} - -void SpectronAudioProcessor::setParameter (int index, float newValue) -{ - // This method will be called by the host, probably on the audio thread, so - // it's absolutely time-critical. Don't use critical sections or anything - // UI-related, or anything at all that may block in any way! - parameters->setParameter(index, newValue); -} - -const String SpectronAudioProcessor::getParameterName (int index) -{ - return parameters->getParameterName(index); -} - -const String SpectronAudioProcessor::getParameterText (int index) -{ - return String (getParameter (index), 2); -} - -//This method is called when a parameter changes (listener) -void SpectronAudioProcessor::valueTreePropertyChanged (ValueTree &treeWhosePropertyHasChanged, const Identifier &changedProperty) { - const ScopedLock myScopedLock (criticalSection); - juce::String changedParameterName = treeWhosePropertyHasChanged.getType().toString(); - LOG("SpectronAudioProcessor::valueTreePropertyChanged: " + changedParameterName); - - if ( (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_RESOLUTION)) - || (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_TRANSFORMATION)) - || (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_WAVELET)) - || (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_WAVELETPAKETBASE)) - || (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_WINDOWING)) ) { - updateTransformation(); - } - if (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_ROUTING)) { - parameter_routing = parameters->getRouting(); - } - if ( (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_GENERATOR)) - || (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_GENERATORFREQUENCY)) ) { - updateSignalGenerator(); - } - - //TODO setParameterNotifyingHost -}; - -void SpectronAudioProcessor::getStateInformation (MemoryBlock& destData) -{ - // You should use this method to store your parameters in the memory block. - // Here's an example of how you can use XML to make it easy and more robust: - - // Create an outer XML element.. - ScopedPointer xml = parameters->writeToXML(); - - // then use this helper function to stuff it into the binary blob and return it.. - copyXmlToBinary (*xml, destData); -} - -void SpectronAudioProcessor::setStateInformation (const void* data, int sizeInBytes) -{ - // You should use this method to restore your parameters from this memory block, - // whose contents will have been created by the getStateInformation() call. - - // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. - ScopedPointer xmlState (getXmlFromBinary (data, sizeInBytes)); - - if (xmlState != 0) { - parameters->readFromXML(*xmlState); - } -} - -//============================================================================== -void SpectronAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/) -{ - // Use this method as the place to do any pre-playback - // initialisation that you need.. - // parameters->setParameter(SpectronParameters::PARAMETER_TRANSFORMATION, parameters->getTransformation()); - if (!currentTransformation) updateTransformation(); - if (!signalGenerator) updateSignalGenerator(); -} - -void SpectronAudioProcessor::releaseResources() -{ - // When playback stops, you can use this as an opportunity to free up any - // spare memory, etc. -} - -void SpectronAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) -{ - const ScopedLock myScopedLock (criticalSection); - parameters->blockParameterChanges(); - - const int numSamples = buffer.getNumSamples(); - const int numChannels = getNumInputChannels(); - - float* inR = 0; - float* inL = 0; - - if (numChannels <= 0) return; - if (numChannels == 1) { - inR = buffer.getSampleData(0); - inL = buffer.getSampleData(0); - } - if (numChannels >= 2) { - inR = buffer.getSampleData(0); - inL = buffer.getSampleData(1); - } - - for (int s = 0; s < numSamples; ++s, ++inL, ++inR) { - if (currentTransformation) currentTransformation->setNextInputSample(getSampleFromRouting(inL, inR)); - } - // In case we have more outputs than inputs, we'll clear any output - // channels that didn't contain input data, (because these aren't - // guaranteed to be empty - they may contain garbage). - for (int i = numChannels; i < numChannels; ++i) { - buffer.clear (i, 0, numSamples); - } - - parameters->unblockParameterChanges(); -} - -void SpectronAudioProcessor::updateTransformation() { - const ScopedLock myScopedLock (criticalSection); - LOG("SpectronAudioProcessor::updateTransformation()"); - parameters->blockParameterChanges(); - - currentTransformation = 0; - double sampleRate = (getSampleRate() <= 100) ? DEFAULT_SAMPLINGRATE : getSampleRate(); - - TransformationFactory::getSingletonInstance()->createTransformation( - parameters->getTransformation(), - sampleRate, - parameters->getResolution(), - parameters->getWindowing(), - parameters->getWavelet(), - parameters->getWaveletPaketBase() - ); - - parameters->unblockParameterChanges(); - currentTransformation = TransformationFactory::getSingletonInstance()->getCurrentTransformation(); -} - -void SpectronAudioProcessor::updateSignalGenerator() { - double sampleRate = (getSampleRate() <= 100) ? DEFAULT_SAMPLINGRATE : getSampleRate(); - deleteAndZero(signalGenerator); - signalGenerator = new SignalGenerator(parameters->getGenerator(), parameters->getGeneratorFrequency(), sampleRate); -} - -float SpectronAudioProcessor::getSampleFromRouting(float* inL, float* inR) { - switch (parameter_routing) { - case SpectronParameters::ROUTING_SIDE: return *inL - *inR; - case SpectronParameters::ROUTING_MID: return (*inL + *inR) / 2.0; - case SpectronParameters::ROUTING_R: return *inR; - case SpectronParameters::ROUTING_L: return *inL; - case SpectronParameters::ROUTING_GENERATOR: return (signalGenerator)? signalGenerator->getNextSample() : 0.0; - default: return (*inL + *inR) / 2.0; - } -} -//============================================================================== - -//============================================================================== -AudioProcessorEditor* SpectronAudioProcessor::createEditor() -{ - return new SpectronMainUI (this); -} - -//============================================================================== -// This creates new instances of the plugin.. -AudioProcessor* JUCE_CALLTYPE createPluginFilter() -{ - return new SpectronAudioProcessor(); -} \ No newline at end of file diff --git a/src/plugin/SpectronAudioProcessor.h b/src/plugin/SpectronAudioProcessor.h deleted file mode 100644 index 0197798c..00000000 --- a/src/plugin/SpectronAudioProcessor.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - ============================================================================== - This file is part of the VST spectrum analyzer plugin "speclet" (working title) - Copyright 2011 by Johannes Troppacher - ------------------------------------------------------------------------------ - This file may use parts of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - ------------------------------------------------------------------------------ - This file may use parts of the fftw library - Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology - ------------------------------------------------------------------------------ - This file may use parts of the wave++ library - Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic - ============================================================================== -*/ -#pragma once -/* - ============================================================================== - - ============================================================================== -*/ - -#ifndef __PLUGINPROCESSOR_H_526ED7A9__ -#define __PLUGINPROCESSOR_H_526ED7A9__ - -//#include -#include "..\..\libs\juce\JuceLibraryCode\JuceHeader.h" -#include "..\config\JucePluginCharacteristics.h" -#include "..\dsp\SignalGenerator.h" -#include "..\dsp\transformations\TransformationFactory.h" -#include "SpectronParameters.h" - -//============================================================================== -/** -*/ -class SpectronAudioProcessor : public AudioProcessor, - public juce::ValueTree::Listener -{ -public: - //============================================================================== - SpectronAudioProcessor(); - ~SpectronAudioProcessor(); - //============================================================================== - void prepareToPlay (double sampleRate, int samplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); - //============================================================================== - bool hasEditor() const { return true; } - AudioProcessorEditor* createEditor(); - //============================================================================== - const String getName() const { return JucePlugin_Name; } - - int getNumParameters(); - float getParameter (int index); - void setParameter (int index, float newValue); - const String getParameterName (int index); - const String getParameterText (int index); - - const String getInputChannelName (int channelIndex) const {return String (channelIndex + 1);} - const String getOutputChannelName (int channelIndex) const {return String (channelIndex + 1);} - bool isInputChannelStereoPair (int index) const {return true;} - bool isOutputChannelStereoPair (int index) const {return true;} - - bool acceptsMidi() const {return false;} - bool producesMidi() const {return false;} - //============================================================================== - int getNumPrograms() { return 0; } - int getCurrentProgram() { return 0; } - void setCurrentProgram (int /*index*/) { } - const String getProgramName (int /*index*/) { return String::empty; } - void changeProgramName (int /*index*/, const String& /*newName*/) { } - //============================================================================== - //these methods are called, when parameter changes were recognised - void valueTreePropertyChanged (ValueTree &treeWhosePropertyHasChanged, const Identifier &property); - void valueTreeChildrenChanged (ValueTree &treeWhoseChildHasChanged) {}; - void valueTreeParentChanged (ValueTree &treeWhoseParentHasChanged) {}; - //============================================================================== - void getStateInformation (MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - //============================================================================== - void updateTransformation(); - void updateSignalGenerator(); - //============================================================================== - // these are used to persist the UI's size - the values are stored along with the - // filter's other parameters, and the UI component will update them when it gets - // resized. - int lastUIWidth, lastUIHeight; - //============================================================================== - -private: - //A pointer to the parameters-singleton is kept here - //as local member, since it is easier to read it that way - SpectronParameters* parameters; - - //Some parameter need to be kept local (as copy), - //since they are called in critical sections - //e.g. during Audioprocessing on every sample - int parameter_routing; - Transformation* currentTransformation; - SignalGenerator* signalGenerator; - juce::CriticalSection criticalSection; - //============================================================================== - float getSampleFromRouting(float* inL, float* inR); - //============================================================================== - - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SpectronAudioProcessor); -}; - -#endif // __PLUGINPROCESSOR_H_526ED7A9__ \ No newline at end of file diff --git a/src/plugin/SpectronParameters.cpp b/src/plugin/SpectronParameters.cpp deleted file mode 100644 index f309b0e6..00000000 --- a/src/plugin/SpectronParameters.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "SpectronParameters.h" -#include "..\utilities\PerformanceManager.h" - -// Singleton instance variable (only one instance of this class) -SpectronParameters* SpectronParameters::singletonInstance = 0; - -const juce::String SpectronParameters::PROPERTY_VALUE = "value"; -const juce::String SpectronParameters::PARAMETER_COLORMODE = "colormode"; -const juce::String SpectronParameters::PARAMETER_LOGMAGNITUDE = "logmagnitude"; -const juce::String SpectronParameters::PARAMETER_LOGFREQUENCY = "logfrequency"; -const juce::String SpectronParameters::PARAMETER_RESOLUTION = "resolution"; -const juce::String SpectronParameters::PARAMETER_ROUTING = "routing"; -const juce::String SpectronParameters::PARAMETER_GENERATOR = "generator"; -const juce::String SpectronParameters::PARAMETER_GENERATORFREQUENCY = "generatorfrequency"; -const juce::String SpectronParameters::PARAMETER_TRANSFORMATION = "transform"; -const juce::String SpectronParameters::PARAMETER_WAVELET = "wavelet"; -const juce::String SpectronParameters::PARAMETER_WAVELETPAKETBASE = "waveletpaketbase"; -const juce::String SpectronParameters::PARAMETER_WINDOWING = "windowing"; - -SpectronParameters::SpectronParameters(void) { - //create ValueTree object, which stores all parameters as childs in a tree structure - - properties = new juce::ValueTree("SpectronParameters"); - - //add parameters as tree childs (must start with zero and use ascending indizes -> enum) - properties->addChild(juce::ValueTree(PARAMETER_ROUTING), PARAMETER_INDEX_Routing, NULL); - properties->addChild(juce::ValueTree(PARAMETER_TRANSFORMATION), PARAMETER_INDEX_Transformation, NULL); - properties->addChild(juce::ValueTree(PARAMETER_RESOLUTION), PARAMETER_INDEX_Resolution, NULL); - properties->addChild(juce::ValueTree(PARAMETER_WAVELETPAKETBASE), PARAMETER_INDEX_WaveletPaketBase, NULL); - properties->addChild(juce::ValueTree(PARAMETER_WINDOWING), PARAMETER_INDEX_Windowing, NULL); - properties->addChild(juce::ValueTree(PARAMETER_WAVELET), PARAMETER_INDEX_Wavelet, NULL); - properties->addChild(juce::ValueTree(PARAMETER_GENERATOR), PARAMETER_INDEX_Generator, NULL); - properties->addChild(juce::ValueTree(PARAMETER_GENERATORFREQUENCY), PARAMETER_INDEX_GeneratorFrequency, NULL); - properties->addChild(juce::ValueTree(PARAMETER_LOGFREQUENCY), PARAMETER_INDEX_LogFrequency, NULL); - properties->addChild(juce::ValueTree(PARAMETER_LOGMAGNITUDE), PARAMETER_INDEX_LogMagnitude, NULL); - properties->addChild(juce::ValueTree(PARAMETER_COLORMODE), PARAMETER_INDEX_ColorMode, NULL); - - //add value zero to every tree child's value property - for (int i = 0; i < properties->getNumChildren(); i++) { - properties->getChild(i).setProperty(PROPERTY_VALUE, (float)0, NULL); - } - - waitForParameterChange = new juce::WaitableEvent(true); - waitForParameterChange->signal(); -} - -SpectronParameters::~SpectronParameters() { -} - -SpectronParameters* SpectronParameters::getSingletonInstance() { -// Method to get the single instance of this class (Singleton) - if (SpectronParameters::singletonInstance == 0) { - SpectronParameters::singletonInstance = new SpectronParameters(); - } - return SpectronParameters::singletonInstance; -} - -void SpectronParameters::destruct() { - if (!singletonInstance) return; - - delete(singletonInstance); - singletonInstance = 0; -} - -float SpectronParameters::getParameter(int index) { - juce::ValueTree child = properties->getChild(index); - if (!child.isValid()) return (float)0; - return child.getProperty(PROPERTY_VALUE); -} - -float SpectronParameters::getParameter(juce::String name) { - juce::ValueTree child = properties->getChildWithName(name); - if (!child.isValid()) return (float)0; - return child.getProperty(PROPERTY_VALUE, (float)0); -} - -void SpectronParameters::setParameter(int index, float newValue) { - const ScopedLock myScopedLock (criticalSection); - - PerformanceManager::getSingletonInstance()->start("waitForParameterChange"); - bool timeoutDuringWait = waitForParameterChange->wait(TIMEOUT_WAIT_BEFORE_SET); - PerformanceManager::getSingletonInstance()->stop("waitForParameterChange"); - if (!timeoutDuringWait) DBG("SpectronParameters::setParameter: Timeout during wait!"); - - juce::ValueTree child = properties->getChild(index); - if (!child.isValid()) return; - child.setProperty(PROPERTY_VALUE, newValue, NULL); -} - -void SpectronParameters::setParameter(juce::String name, float newValue) { - const ScopedLock myScopedLock (criticalSection); - - PerformanceManager::getSingletonInstance()->start("waitForParameterChange"); - bool timeoutDuringWait = waitForParameterChange->wait(TIMEOUT_WAIT_BEFORE_SET); - PerformanceManager::getSingletonInstance()->stop("waitForParameterChange"); - if (!timeoutDuringWait) DBG("SpectronParameters::setParameter: Timeout during wait!"); - - juce::ValueTree child = properties->getChildWithName(name); - if (!child.isValid()) return; - child.setProperty(PROPERTY_VALUE, newValue, NULL); -} - -int SpectronParameters::getParameterIndex(juce::String name) { - juce::ValueTree child = properties->getChildWithName(name); - if (!child.isValid()) return -1; - return properties->indexOf(child); -} - -juce::String SpectronParameters::getParameterName(int index) { - juce::ValueTree child = properties->getChild(index); - if (!child.isValid()) return juce::String::empty; - return child.getType().toString(); -} - -//Adds a listener by delegating it to juce::ValueTree (see juce API documentation) -void SpectronParameters::addListener (juce::ValueTree::Listener* listener, bool sendAllParametersForInitialisation) { - properties->addListener(listener); - - //if selected, the already added listener will be informed about every parameter as if it had been changed - if (sendAllParametersForInitialisation) { - for (int i = 0; i < properties->getNumChildren(); i++) { - juce::ValueTree parameter = properties->getChild(i); - if (!parameter.isValid()) continue; - listener->valueTreePropertyChanged(parameter, PROPERTY_VALUE); - } - } -} -//Removes a listener by delegating it to juce::ValueTree (see juce API documentation) -void SpectronParameters::removeListener (juce::ValueTree::Listener* listener) { - properties->removeListener(listener); -} - -//Removes a listener by delegating it to juce::ValueTree (see juce API documentation) -void SpectronParameters::readFromXML(const XmlElement &xml) { - const ScopedLock myScopedLock (criticalSection); - juce::ValueTree importedProperties = juce::ValueTree::fromXml(xml); - - //validate imported data (skip import if invalid) - if (!importedProperties.isValid()) return; - if (importedProperties.getNumChildren() < properties->getNumChildren()) return; - - //reads all currently defined parameters and overwrites their values, - //if the same parameter is found in the imported ValueTree - for (int i = 0; i < properties->getNumChildren(); i++) { - //get current parameter, skip if invalid - juce::ValueTree old_parameter = properties->getChild(i); - if (!old_parameter.isValid()) continue; - - //get new parameter with same name, skip if invalid - juce::Identifier paramter_identifier = old_parameter.getType(); - juce::ValueTree new_parameter = importedProperties.getChildWithName(paramter_identifier); - if (!new_parameter.isValid()) continue; - - //overwrite old parameter using the public setter, which will lead to listener notification - juce::var new_parameter_value = new_parameter.getProperty(PROPERTY_VALUE); - setParameter(paramter_identifier.toString(), (float)new_parameter_value); - } -} diff --git a/src/plugin/SpectronParameters.h b/src/plugin/SpectronParameters.h deleted file mode 100644 index 11ffb058..00000000 --- a/src/plugin/SpectronParameters.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - ============================================================================== - This file is part of the VST spectrum analyzer plugin "speclet" (working title) - Copyright 2011 by Johannes Troppacher - ------------------------------------------------------------------------------ - This file may use parts of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - ------------------------------------------------------------------------------ - This file may use parts of the fftw library - Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology - ------------------------------------------------------------------------------ - This file may use parts of the wave++ library - Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic - ============================================================================== -*/ -#pragma once -#include "..\..\libs\juce\JuceLibraryCode\JuceHeader.h" -#define PARAMETERS SpectronParameters::getSingletonInstance() - - -class SpectronParameters { -// --------------- constants --------------- // -public: - //--------------------------------------------------------------------------- - //IMPORTANT: Since juce components like combo boxes can't handle index = 0, - // enums must not contain a zero value - // Only exception: PARAMETER_INDEX (since it has to do with VST SDK) - //--------------------------------------------------------------------------- - - static enum Constants { - TIMEOUT_WAIT_BEFORE_SET = 5000 - }; - static enum Parameters { - //Has to start with index=0 and end with TOTAL_NUMBER_OF_PARAMS - //Be aware of that when you extend it - PARAMETER_INDEX_Routing = 0, - PARAMETER_INDEX_Transformation, - PARAMETER_INDEX_Resolution, - PARAMETER_INDEX_WaveletPaketBase, - PARAMETER_INDEX_Windowing, - PARAMETER_INDEX_Wavelet, - PARAMETER_INDEX_Generator, - PARAMETER_INDEX_GeneratorFrequency, - PARAMETER_INDEX_LogFrequency, - PARAMETER_INDEX_LogMagnitude, - PARAMETER_INDEX_ColorMode, - - TOTAL_NUMBER_OF_PARAMS - }; - static enum OptionsColormode { - COLORMODE_BLUE = 1, - COLORMODE_GREEN , - COLORMODE_RAINBOW , - COLORMODE_FIRE , - - COLORMODES_NumOptions, - COLORMODE_DEFAULT = COLORMODE_BLUE - }; - static enum OptionsGenerator { - GENERATOR_SINE = 1, - GENERATOR_TRANGLE , - GENERATOR_RAMP , - GENERATOR_SQUARE , - GENERATOR_NOISE , - - GENERATORS_NumOptions, - GENERATOR_DEFAULT = GENERATOR_SINE - }; - static enum OptionsPlotAxis { - PLOT_AXIS_LINEAR = 1, - PLOT_AXIS_LOGARITHMIC, - - PLOT_AXIS_NumOptions, - PLOT_AXIS_DEFAULT = PLOT_AXIS_LOGARITHMIC - }; - static enum OptionsResolution { - RESOLUTION_256 = 256, - RESOLUTION_512 = 512, - RESOLUTION_1024 = 1024, - RESOLUTION_2048 = 2048, - RESOLUTION_4096 = 4096, - RESOLUTION_8192 = 8192, - RESOLUTION_16384 = 16384, - RESOLUTION_32768 = 32768, - RESOLUTION_65536 = 65536, - - RESOLUTION_MAX = RESOLUTION_65536, - RESOLUTION_DEFAULT = RESOLUTION_4096 - }; - static enum OptionsResolutionRatio { - RESOLUTION_RATIO_Equal = 99, - RESOLUTION_RATIO_TimeX2 = -2, - RESOLUTION_RATIO_TimeX4 = -1, - RESOLUTION_RATIO_FreqX2 = 1, - RESOLUTION_RATIO_FreqX4 = 2, - - RESOLUTION_RATIO_NumOptions = 5, - RESOLUTION_RATIO_DEFAULT = RESOLUTION_RATIO_Equal - }; - static enum OptionsRouting { - ROUTING_GENERATOR = 1, - ROUTING_L, - ROUTING_R, - ROUTING_MID, - ROUTING_SIDE, - - ROUTING_NumOptions, - ROUTING_DEFAUTL = ROUTING_MID - }; - static enum OptionsTransform { - TRANSFORM_FFT = 1, - TRANSFORM_FWT, - TRANSFORM_FWPT, - TRANSFORM_FWPT_BB, - TRANSFORM_OFF, - - TRANSFORM_NumOptions, - TRANSFORM_DEFAULT = TRANSFORM_FFT - }; - static enum OptionsWavelet { - WAVELET_DAUBECHIES_02 = 1, - WAVELET_DAUBECHIES_04, - WAVELET_DAUBECHIES_06, - WAVELET_DAUBECHIES_08, - WAVELET_DAUBECHIES_10, - WAVELET_DAUBECHIES_12, - WAVELET_DAUBECHIES_14, - WAVELET_DAUBECHIES_16, - WAVELET_DAUBECHIES_18, - WAVELET_DAUBECHIES_20, - WAVELET_COIFMAN_06, - WAVELET_COIFMAN_12, - WAVELET_COIFMAN_18, - WAVELET_COIFMAN_24, - WAVELET_COIFMAN_30, - WAVELET_BEYLKIN_18, - WAVELET_VAIDYANATHAN_18, - - WAVELETS_NumOptions, - WAVELET_HAAR = WAVELET_DAUBECHIES_02, - WAVELET_DEFAULT = WAVELET_DAUBECHIES_08 - }; - static enum OptionsWindowing { - WINDOWING_BARTLETT = 1 , - WINDOWING_BLACKMAN , - WINDOWING_BLACKMAN_HARRIS , - WINDOWING_HAMMING , - WINDOWING_HANNING , - WINDOWING_PARZEN , - WINDOWING_WELCH , - WINDOWING_SQUARE , - WINDOWINGS_NumOptions , - WINDOWING_DEFAULT = WINDOWING_BLACKMAN_HARRIS - }; - - const static juce::String PROPERTY_VALUE; - const static juce::String PARAMETER_COLORMODE; - const static juce::String PARAMETER_LOGMAGNITUDE; - const static juce::String PARAMETER_LOGFREQUENCY; - const static juce::String PARAMETER_RESOLUTION; - const static juce::String PARAMETER_ROUTING; - const static juce::String PARAMETER_GENERATOR; - const static juce::String PARAMETER_GENERATORFREQUENCY; - const static juce::String PARAMETER_TRANSFORMATION; - const static juce::String PARAMETER_WAVELET; - const static juce::String PARAMETER_WAVELETPAKETBASE; - const static juce::String PARAMETER_WINDOWING; - -private: - static enum ChildIndizes { - CHILD_INDEX_Parameters = 0, - CHILD_INDEX_Metadata, - - CHILD_INDEX_NumIndizes - }; - -// --------------- members --------------- // -private: - static SpectronParameters* singletonInstance; - - juce::ScopedPointer properties; - juce::ScopedPointer waitForParameterChange; - juce::CriticalSection criticalSection; - -// --------------- methods --------------- // -public: - static SpectronParameters* getSingletonInstance(); - void destruct(); - - void blockParameterChanges(void) {waitForParameterChange->reset();}; - void unblockParameterChanges(void) {waitForParameterChange->signal();}; - - void setParameter(int index, float newValue); - void setParameter(juce::String name, float newValue); - float getParameter(int index); - float getParameter(juce::String name); - juce::String getParameterName(int index); - int getParameterIndex(juce::String name); - - int getColorMode() {return (int)getParameter(PARAMETER_INDEX_ColorMode);} - int getGenerator() {return (int)getParameter(PARAMETER_INDEX_Generator);} - float getGeneratorFrequency() {return getParameter(PARAMETER_INDEX_GeneratorFrequency);} - bool getLogMagnitude() {return (bool)getParameter(PARAMETER_INDEX_LogMagnitude);} - bool getLogFrequency() {return (bool)getParameter(PARAMETER_INDEX_LogFrequency);} - long getResolution() {return (long)getParameter(PARAMETER_INDEX_Resolution);} - int getRouting() {return (int)getParameter(PARAMETER_INDEX_Routing);} - int getTransformation() {return (int)getParameter(PARAMETER_INDEX_Transformation);} - int getWavelet() {return (int)getParameter(PARAMETER_INDEX_Wavelet);} - int getWaveletPaketBase() {return (int)getParameter(PARAMETER_INDEX_WaveletPaketBase);} - int getWindowing() {return (int)getParameter(PARAMETER_INDEX_Windowing);} - - //Adds a listener by delegating it to juce::ValueTree (see juce API documentation) - void addListener (juce::ValueTree::Listener* listener, bool sendAllParametersForInitialisation = true); - //Removes a listener by delegating it to juce::ValueTree (see juce API documentation) - void removeListener (juce::ValueTree::Listener* listener); - //read and write to XML - void readFromXML(const XmlElement &xml); - juce::XmlElement* writeToXML() const {return properties->createXml();}; - -private: - SpectronParameters(void); - ~SpectronParameters(void); - SpectronParameters(const SpectronParameters&); - -}; - diff --git a/src/standalone/Main.cpp b/src/standalone/Main.cpp deleted file mode 100644 index 8a29e258..00000000 --- a/src/standalone/Main.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "..\config\JucePluginCharacteristics.h" -#include "JuceHeader.h" -#include "juce_StandaloneFilterWindow.h" - -class SpectronJuceStandalone : public JUCEApplication { -public: - SpectronJuceStandalone() {} - - void initialise(const String& commandLineParameters) - { - ApplicationProperties::getInstance()->setStorageParameters ( - JucePlugin_Name + juce::String("Standalone"), - juce::String::empty, - JucePlugin_Name, - 400, - PropertiesFile::storeAsXML); - filterWindow = new StandaloneFilterWindow (getApplicationName(), Colours::black); - filterWindow->setTitleBarButtonsRequired (DocumentWindow::allButtons, false); - filterWindow->setVisible (true); - filterWindow->setResizable (true, true); - } - - void shutdown() { - deleteAndZero (filterWindow); - } - - const String getApplicationName() { - return JucePlugin_Name + juce::String(" (Standalone) v") + JucePlugin_VersionString; - } - - const String getApplicationVersion() { - return JucePlugin_VersionString; - } - -private: - StandaloneFilterWindow *filterWindow; -}; - -START_JUCE_APPLICATION (SpectronJuceStandalone) \ No newline at end of file diff --git a/src/ui/ColorGradientsParameters.h b/src/ui/ColorGradientsParameters.h new file mode 100644 index 00000000..f25521c1 --- /dev/null +++ b/src/ui/ColorGradientsParameters.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace ColorGradientsParameters { + + enum class ColorMode { + BLUE = 1, + GREEN, + RAINBOW, + FIRE, + + NUMBER_OF_OPTIONS, + DEFAULT = FIRE + }; + + struct ColorModeNames { + static std::map createMap() { + return { + {ColorMode::BLUE, "Blue"}, + {ColorMode::GREEN, "Green"}, + {ColorMode::RAINBOW, "Rainbow"}, + {ColorMode::FIRE, "Fire"}, + }; + } + static const std::map map; + }; + + inline const std::map ColorModeNames::map = ColorModeNames::createMap(); + +}// namespace ColorGradientsParameters \ No newline at end of file diff --git a/src/ui/ColourGradients.cpp b/src/ui/ColourGradients.cpp new file mode 100644 index 00000000..6f8b9599 --- /dev/null +++ b/src/ui/ColourGradients.cpp @@ -0,0 +1,57 @@ +#include "ColourGradients.h" +#include "../parameter/SpecletParameters.h" +#include "ColorGradientsParameters.h" + +auto ColourGradients::forIndex(int colorModeIndex) -> juce::ColourGradient { + auto colorMode = static_cast(colorModeIndex); + if (colorMode == ColorGradientsParameters::ColorMode::BLUE) { + return BLUE; + } + if (colorMode == ColorGradientsParameters::ColorMode::GREEN) { + return GREEN; + } + if (colorMode == ColorGradientsParameters::ColorMode::RAINBOW) { + return rainbow(); + } + if (colorMode == ColorGradientsParameters::ColorMode::FIRE) { + return fire(); + } + DBG("juce::ColourGradient ColourGradients::get(): index not found (replaced by blue)! i=" + juce::String(colorModeIndex)); + return BLUE; +} + +auto ColourGradients::fire() noexcept -> juce::ColourGradient { + DBG("juce::ColourGradient ColourGradients::fire()"); + auto gradient = juce::ColourGradient(); + + gradient.addColour(1.0F, juce::Colours::yellow); + gradient.addColour(0.9F, juce::Colours::red); + gradient.addColour(0.6F, juce::Colours::darkred); + gradient.addColour(0.3F, juce::Colours::darkviolet); + gradient.addColour(0.0F, juce::Colours::black); + + return gradient; +} + +auto ColourGradients::rainbow() noexcept -> juce::ColourGradient { + DBG("juce::ColourGradient ColourGradients::rainbow()"); + auto gradient = juce::ColourGradient(); + + gradient.addColour(1.0F, juce::Colours::red); + gradient.addColour(0.8F, juce::Colours::yellow); + gradient.addColour(0.6F, juce::Colours::green); + gradient.addColour(0.4F, juce::Colours::darkblue); + gradient.addColour(0.2F, juce::Colours::darkviolet); + gradient.addColour(0.0F, juce::Colours::black); + + return gradient; +} + +auto ColourGradients::fadeToBlack(const juce::Colour& colour) noexcept -> juce::ColourGradient { + DBG("juce::ColourGradient ColourGradients::blue()"); + auto gradient = juce::ColourGradient(); + gradient.addColour(1.0F, colour); + gradient.addColour(0.9F, colour.darker(0.1F)); + gradient.addColour(0.0F, juce::Colours::black); + return gradient; +} \ No newline at end of file diff --git a/src/user interface/ColourGradients.h b/src/ui/ColourGradients.h similarity index 50% rename from src/user interface/ColourGradients.h rename to src/ui/ColourGradients.h index ee3939ed..2af5a540 100644 --- a/src/user interface/ColourGradients.h +++ b/src/ui/ColourGradients.h @@ -14,30 +14,23 @@ ============================================================================== */ #pragma once -#include "..\..\libs\juce\JuceLibraryCode\JuceHeader.h" -#define COLOURGRADIENT ColourGradients::getSingletonInstance() -#define GRADIENT_BLUE ColourGradients::getSingletonInstance()->getBlue() -#define GRADIENT_GREEN ColourGradients::getSingletonInstance()->getGreen() -#define GRADIENT_FIRE ColourGradients::getSingletonInstance()->getFire() -#define GRADIENT_RAINBOW ColourGradients::getSingletonInstance()->getRainbow() +#include "juce_audio_utils/juce_audio_utils.h" +namespace ColourGradients { + /** + * @brief A colour gradient for the spectrum analyzer for the given index. + * + * @param index int starting with 1 for the first colour gradient (blue) + * @return juce::ColourGradient + */ + auto forIndex(int index) -> juce::ColourGradient; -class ColourGradients { -public: - static ColourGradients* getSingletonInstance(); - void destruct(); - - juce::ColourGradient get(int index); + auto fadeToBlack(const juce::Colour &colour) noexcept -> juce::ColourGradient; + auto fire() noexcept -> juce::ColourGradient; + auto rainbow() noexcept -> juce::ColourGradient; - juce::ColourGradient getBlue(); - juce::ColourGradient getGreen(); - juce::ColourGradient getFire(); - juce::ColourGradient getRainbow(); - -private: - static ColourGradients* singletonInstance; - - ColourGradients(void) {}; - ~ColourGradients(void) {}; - ColourGradients(const ColourGradients&); -}; + const juce::ColourGradient GREEN = fadeToBlack(juce::Colour::fromRGB(45, 255, 45)); + const juce::ColourGradient BLUE = fadeToBlack(juce::Colour::fromRGB(10, 80, 255)); + const juce::ColourGradient FIRE = fire(); + const juce::ColourGradient RAINBOW = rainbow(); +} diff --git a/src/ui/SpecletAnalyzerComponent.cpp b/src/ui/SpecletAnalyzerComponent.cpp new file mode 100644 index 00000000..ddcd61ec --- /dev/null +++ b/src/ui/SpecletAnalyzerComponent.cpp @@ -0,0 +1,631 @@ +/* + ============================================================================== + + This is an automatically generated file created by the Jucer! + + Creation date: 23 May 2011 11:58:55pm + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Jucer version: 1.12 + + ------------------------------------------------------------------------------ + + The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + + ============================================================================== +*/ + +//[Headers] You can add your own extra header files here... +#include "../dsp/SignalGeneratorParameters.h" +#include "../dsp/transformations/TransformationParameters.h" +#include "../dsp/transformations/WaveletParameters.h" +#include "../dsp/windowing/WindowParameters.h" +#include "../ui/ColorGradientsParameters.h" +#include "../ui/SpecletDrawerParameters.h" +#include "SpecletDrawer.h" + +//[/Headers] + +#include "SpecletAnalyzerComponent.h" +#include +#include + + +//[MiscUserDefs] You can add your own user definitions and misc code here... +void SpecletTooltipWindowLookAndFeel::drawTooltip(juce::Graphics &g, const juce::String &text, int width, int height) { + g.fillAll(juce::Colours::black.withAlpha(0.8F)); + g.setColour(juce::Colours::white); + g.drawFittedText(text, 0, 0, width, height, juce::Justification::left, 1); +} +//[/MiscUserDefs] + +//============================================================================== +SpecletAnalyzerComponent::SpecletAnalyzerComponent(SpecletParameters ¶metersToAttach) + : Component("SpecletAnalyzerComponent"), + parameters(parametersToAttach), + spectralviewport(new juce::Viewport("spectralviewport")), + tooltipWindow(new juce::TooltipWindow(this)), + + comboBoxResolution(createComboBox("comboBoxResolution", SpecletParameters::PARAMETER_RESOLUTION)), + labelResolution(createLabel("labelResolution", "Resolution")), + comboBoxTransformation(createComboBox("comboBoxTransformation", SpecletParameters::PARAMETER_TRANSFORMATION)), + labelTransformation(createLabel("labelTransformation", "Transformation")), + comboBoxWindowing(createComboBox("comboBoxWindowing", SpecletParameters::PARAMETER_WINDOWING)), + labelWindowing(createLabel("labelWindowing", "Window Function")), + comboBoxWavelet(createComboBox("comboBoxWavelet", SpecletParameters::PARAMETER_WAVELET)), + labelWavelet(createLabel("labelWavelet", "Wavelet")), + comboBoxWaveletPacketBasis(createComboBox("comboBoxWaveletPacketBasis", SpecletParameters::PARAMETER_WAVELETPACKETBASIS)), + labelWaveletPacketBasis(createLabel("labelWaveletPacketBasis", "Wavelet Packet Basis")), + comboBoxSignalGenerator(createComboBox("comboBoxSignalgenerator", SpecletParameters::PARAMETER_GENERATOR)), + labelSignalGenerator(createLabel("labelSignalGenerator", "Oscillator")), + comboBoxRouting(createComboBox("comboBoxRouting", SpecletParameters::PARAMETER_ROUTING)), + labelRouting(createLabel("labelRouting", "Audio Source")), + labelSignalGeneratorFrequency(createLabel("labelGeneratorFrequency", "Oscillator Frequency")), + sliderSignalGeneratorFrequency(createSlider("sliderGeneratorFrequency", SpecletParameters::PARAMETER_GENERATORFREQUENCY)), + labelLogF(createLabel("labelLogF", "Frequency Scale")), + labelLogA(createLabel("labelLogA", "Magnitude Scale")), + comboBoxLogF(createComboBox("comboBoxLogF", SpecletParameters::PARAMETER_LOGFREQUENCY)), + comboBoxLogA(createComboBox("comboBoxLogA", SpecletParameters::PARAMETER_LOGMAGNITUDE)), + labelColorMode(createLabel("labelColorMode", "Color Mode")), + comboBoxColorMode(createComboBox("comboBoxColorMode", SpecletParameters::PARAMETER_COLORMODE)), + + resolutionParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_RESOLUTION, *comboBoxResolution)), + transformationParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_TRANSFORMATION, *comboBoxTransformation)), + windowingParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_WINDOWING, *comboBoxWindowing)), + waveletParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_WAVELET, *comboBoxWavelet)), + waveletPacketBasisParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_WAVELETPACKETBASIS, *comboBoxWaveletPacketBasis)), + signalGeneratorParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_GENERATOR, *comboBoxSignalGenerator)), + routingParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_ROUTING, *comboBoxRouting)), + logFParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_LOGFREQUENCY, *comboBoxLogF)), + logAParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_LOGMAGNITUDE, *comboBoxLogA)), + colorModeParameterAttachment(new ComboBoxAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_COLORMODE, *comboBoxColorMode)), + signalGeneratorFrequencyParameterAttachment(new SliderAttachment(parametersToAttach.getParameters(), SpecletParameters::PARAMETER_GENERATORFREQUENCY, *sliderSignalGeneratorFrequency)) { + + for (auto *component : getComponents()) { + addAndMakeVisible(component); + } + spectralviewport->setScrollBarsShown(false, true); + spectralviewport->setScrollBarThickness(10); + + comboBoxResolution->setTooltip( + "Sets the block size for the analysis.\n" + "Higher values lead to better frequency resolution, lower time resolution and more cpu consumption. " + "For efficient calculation, all values are a power of 2."); + comboBoxTransformation->setTooltip( + "Transformation methods:\n\n" + " - FFT: Fast Fourier Transform\n" + " - FWT: Fast Wavelet Transform\n" + " - WPT: Wavelet Packet Transform\n" + " - WPT BestBasis: WPT with best basis\n"); + comboBoxWindowing->setTooltip( + "Window functions are like fades at the beginning and end of the block of input samples.\n" + "They reduce 'leakage' in the frequency spectrum. " + "Choose a sine input and use different windows to see the impact. " + "The rectangular window shows how the spectrum is affected when there is no smoothing.\n"); + comboBoxWavelet->setTooltip( + "Wavelets are represented by their high- and low-pass filter coefficients.\n" + "The number in the parentheses is the number of coefficients.\n" + "More coefficients lead to more precise results, less aliasing and higher cpu consuption.\n"); + comboBoxWaveletPacketBasis->setTooltip( + "The wavelet packet transform (WPT) also uses the output of the high pass filter (details) and splits it up further.\n" + "This results in evenly/linear spaced frequency bands in contrast to the dyadic wavelet transform.\n" + "More levels lead to finer frequency resolution and less time resolution."); + comboBoxSignalGenerator->setTooltip("Sets the waveform of the build-in test signal generator/oscillator."); + comboBoxRouting->setTooltip( + "Sets the input audio source to\n" + "either one of the input channels\n" + "or the build-in signal generator/oscillator."); + sliderSignalGeneratorFrequency->setTooltip( + "Sets the frequency of the\n" + "build-in signal generator/oscillator."); + comboBoxLogF->setTooltip("Set the frequency scale to linear or logarithmic."); + comboBoxLogA->setTooltip("Set the magnitude scale to linear or logarithmic."); + comboBoxColorMode->setTooltip("Sets the colors that reflect the magnitude of the signal."); + + tooltipWindow->setLookAndFeel(&tooltipWindowLookAndFeel); + //[UserPreSize] + //[/UserPreSize] + + setSize(800, 360); + + //[Constructor] You can add your own custom stuff here.. + + transformationChanged(parameters.getTransformation()); + routingChanged(parameters.getRouting()); + + //registers itself as listener for parameter-changes + parameters.addListener(this); + DBG("SpecletAnalyzerComponent as parameter listener added"); + //adds spectrum drawing component to the scrollable view + spectralviewport->setViewedComponent(specletDrawer); + parameters.addListener(specletDrawer); + + //adds entries to the popup/context menu + popupMenu.addItem(POPUPMENU_INDEX_1_ABOUT, "about"); + addMouseListener(this, true); + //[/Constructor] +} + +SpecletAnalyzerComponent::~SpecletAnalyzerComponent() { + //[Destructor_pre]. You can add your own custom destruction code here.. + parameters.removeListener(specletDrawer); + parameters.removeListener(this); + + deleteAndZero(resolutionParameterAttachment); + deleteAndZero(transformationParameterAttachment); + deleteAndZero(windowingParameterAttachment); + deleteAndZero(waveletParameterAttachment); + deleteAndZero(waveletPacketBasisParameterAttachment); + deleteAndZero(signalGeneratorParameterAttachment); + deleteAndZero(routingParameterAttachment); + deleteAndZero(logFParameterAttachment); + deleteAndZero(logAParameterAttachment); + deleteAndZero(colorModeParameterAttachment); + deleteAndZero(signalGeneratorFrequencyParameterAttachment); + + //[/Destructor_pre] + deleteAndZero(comboBoxResolution); + deleteAndZero(labelResolution); + deleteAndZero(comboBoxTransformation); + deleteAndZero(labelTransformation); + deleteAndZero(comboBoxWindowing); + deleteAndZero(labelWindowing); + deleteAndZero(comboBoxWavelet); + deleteAndZero(labelWavelet); + deleteAndZero(comboBoxWaveletPacketBasis); + deleteAndZero(labelWaveletPacketBasis); + deleteAndZero(comboBoxSignalGenerator); + deleteAndZero(labelSignalGenerator); + deleteAndZero(comboBoxRouting); + deleteAndZero(labelRouting); + deleteAndZero(labelSignalGeneratorFrequency); + deleteAndZero(sliderSignalGeneratorFrequency); + deleteAndZero(labelLogF); + deleteAndZero(labelLogA); + deleteAndZero(comboBoxLogF); + deleteAndZero(comboBoxLogA); + deleteAndZero(labelColorMode); + deleteAndZero(comboBoxColorMode); + deleteAndZero(tooltipWindow); + deleteAndZero(specletDrawer); + deleteAndZero(spectralviewport); + + //[Destructor]. You can add your own custom destruction code here.. + //unregisters itself as listener for parameter-changes + parameters.removeListener(this); + DBG("SpecletAnalyzerComponent as parameter listener removed"); + //[/Destructor] +} + +auto SpecletAnalyzerComponent::createComboBox(const juce::String &componentName, const juce::String ¶meterName) -> juce::ComboBox * { + auto comboBox = new juce::ComboBox(componentName); + comboBox->addItemList(parameters.getParameterList(parameterName), 1); + comboBox->setSelectedItemIndex(parameters.getParameterAsSelection(parameterName), juce::dontSendNotification); + comboBox->setEditableText(false); + comboBox->setJustificationType(juce::Justification::centredLeft); + comboBox->setTextWhenNothingSelected(juce::String()); + comboBox->setTextWhenNoChoicesAvailable(juce::String()); + return comboBox; +} + +auto SpecletAnalyzerComponent::createLabel(const juce::String &componentName, const juce::String &labelText) -> juce::Label * { + auto label = new juce::Label(componentName, labelText); + label->setFont(juce::Font(15.0000f, juce::Font::plain)); + label->setJustificationType(juce::Justification::centredLeft); + label->setEditable(false, false, false); + label->setColour(juce::Label::backgroundColourId, juce::Colour(0x30007bfc)); + label->setColour(juce::Label::textColourId, juce::Colours::white); + label->setColour(juce::Label::outlineColourId, juce::Colour(0xff0082f7)); + label->setColour(juce::TextEditor::textColourId, juce::Colours::black); + label->setColour(juce::TextEditor::backgroundColourId, juce::Colour(0x0)); + return label; +} + +auto SpecletAnalyzerComponent::createSlider(const juce::String &componentName, const juce::String ¶meterName) -> juce::Slider * { + auto slider = new juce::Slider(componentName); + slider->setValue(parameters.getParameter(parameterName), juce::dontSendNotification); + slider->setExplicitFocusOrder(7); + slider->setRange(10, 22000, 1); + slider->setSliderStyle(juce::Slider::LinearHorizontal); + slider->setTextBoxStyle(juce::Slider::TextBoxLeft, false, 50, 25); + return slider; +} + +auto SpecletAnalyzerComponent::getComponents() -> std::vector { + return { + spectralviewport, + tooltipWindow, + comboBoxResolution, + labelResolution, + comboBoxTransformation, + labelTransformation, + comboBoxWindowing, + labelWindowing, + comboBoxWavelet, + labelWavelet, + comboBoxWaveletPacketBasis, + labelWaveletPacketBasis, + comboBoxSignalGenerator, + labelSignalGenerator, + comboBoxRouting, + labelRouting, + labelSignalGeneratorFrequency, + sliderSignalGeneratorFrequency, + labelLogF, + labelLogA, + comboBoxLogF, + comboBoxLogA, + labelColorMode, + comboBoxColorMode, + + }; +} + +//============================================================================== +void SpecletAnalyzerComponent::paint(juce::Graphics &g) { + //[UserPrePaint] Add your own custom painting code here.. + //[/UserPrePaint] + + g.fillAll(juce::Colours::black); + + //[UserPaint] Add your own custom painting code here.. + //[/UserPaint] +} + +void SpecletAnalyzerComponent::resized() { + //TODO (JohT) Had been generated by "Projucer". Could be adapted to be resizeable. + comboBoxResolution->setBounds((128) + 0, (8) + 32, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + spectralviewport->setBounds(256, 8, 528, 344); + labelResolution->setBounds((8) + 0, (8) + 32, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxTransformation->setBounds(128, 8, 120, 24); + labelTransformation->setBounds(8, 8, 120, 24); + comboBoxWindowing->setBounds((128) + 0, (8) + 64, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelWindowing->setBounds((8) + 0, (8) + 64, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxWavelet->setBounds((128) + 0, (8) + 96, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelWavelet->setBounds((8) + 0, (8) + 96, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxWaveletPacketBasis->setBounds((128) + 0, (8) + 128, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelWaveletPacketBasis->setBounds((8) + 0, (8) + 128, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxSignalGenerator->setBounds((128) + 0, (8) + 288, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelSignalGenerator->setBounds((8) + 0, (8) + 288, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxRouting->setBounds((128) + 0, (8) + 160, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelRouting->setBounds((8) + 0, (8) + 160, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelSignalGeneratorFrequency->setBounds((8) + 0, (8) + 320, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + sliderSignalGeneratorFrequency->setBounds((128) + 0, (8) + 320, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelLogF->setBounds((8) + 0, (8) + 192, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelLogA->setBounds((8) + 0, (8) + 224, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxLogF->setBounds((128) + 0, (8) + 192, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxLogA->setBounds((128) + 0, (8) + 224, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + labelColorMode->setBounds((8) + 1, (8) + 256, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + comboBoxColorMode->setBounds((128) + 1, (8) + 256, juce::roundToInt((120) * 1.0000f), juce::roundToInt((24) * 1.0000f)); + //[UserResized] Add your own custom resize handling here.. + //[/UserResized] +} + +void SpecletAnalyzerComponent::visibilityChanged() { + //[UserCode_visibilityChanged] -- Add your code here... + //[/UserCode_visibilityChanged] +} + +void SpecletAnalyzerComponent::parentSizeChanged() { + //[UserCode_parentSizeChanged] -- Add your code here... + //[/UserCode_parentSizeChanged] +} + +void SpecletAnalyzerComponent::broughtToFront() { + //[UserCode_broughtToFront] -- Add your code here... + //[/UserCode_broughtToFront] +} + +void SpecletAnalyzerComponent::childrenChanged() { + //[UserCode_childrenChanged] -- Add your code here... + //[/UserCode_childrenChanged] +} + +void SpecletAnalyzerComponent::enablementChanged() { + //[UserCode_enablementChanged] -- Add your code here... + //[/UserCode_enablementChanged] +} + +void SpecletAnalyzerComponent::mouseMove(const juce::MouseEvent &e) { + //[UserCode_mouseMove] -- Add your code here... + //[/UserCode_mouseMove] +} + +void SpecletAnalyzerComponent::mouseDown(const juce::MouseEvent &e) { + //[UserCode_mouseDown] -- Add your code here... + if (e.mods.isRightButtonDown()) { + auto options = juce::PopupMenu::Options().withTargetComponent(this).withMousePosition(); + popupMenu.showMenuAsync(options, [](auto index) { + if (index == POPUPMENU_INDEX_1_ABOUT) { + juce::AlertWindow::AlertIconType icon = juce::AlertWindow::InfoIcon; + + juce::StringRef message = + "Written by Johannes Troppacher (c)2011\n" + "Modernized 2022\n" + "\n" + "Audio Spectrum Analyzer Plugin using\n" + "Fourier- and Wavelet-Transformation\n" + "\n" + "Made with:\n" + "- Framework 'JUCE' originally (2011) by Raw Material Software\n" + "- VST-Interface 'VST SDK 2.4 rev2' by Steinberg(2011)\n" + "- VST 3 Audio Plug-Ins SDK by Steinberg (2022)\n" + "- FFT-Library 'FFTW' by MIT (Matteo Frigo and Steven G. Johnson)\n" + "- Wavelet-Library 'wave++'\n" + " by Ryerson Computrational Signal Analysis Group\n" + " (S. E. Ferrando, L. A. Kolasa and N. Kovacevic)\n" + "- std::span for C++11 by Tristan Brindle (2019)\n"; + + juce::AlertWindow::showMessageBoxAsync(icon, "About Speclet", message, "OK"); + } + }); + } + //[/UserCode_mouseDown] +} + +void SpecletAnalyzerComponent::mouseWheelMove(const juce::MouseEvent & /* event */, const juce::MouseWheelDetails & /* wheel */) { + //[UserCode_mouseWheelMove] -- Add your code here... + //[/UserCode_mouseWheelMove] +} + +void SpecletAnalyzerComponent::parameterChanged(const juce::String ¶meterID, float newValue) { + const juce::ScopedLock myScopedLock(criticalSection); + auto newIndex = static_cast(newValue) + 1;// +1 because of 0 is not a valid combo box selection index + if (parameterID == SpecletParameters::PARAMETER_TRANSFORMATION) { + transformationChanged(newIndex); + } + if (parameterID == SpecletParameters::PARAMETER_ROUTING) { + routingChanged(newIndex); + } +} + +void SpecletAnalyzerComponent::transformationChanged(int selectedOption) { + if (selectedOption == enumOptionIntValue(TransformationParameters::Type::BYPASS)) { + //Analyzer is turned off (bypass). Disable all related controls. + comboBoxResolution->setEnabled(false); + labelResolution->setEnabled(false); + comboBoxWindowing->setEnabled(false); + labelWindowing->setEnabled(false); + comboBoxSignalGenerator->setEnabled(false); + labelSignalGenerator->setEnabled(false); + comboBoxRouting->setEnabled(false); + labelRouting->setEnabled(false); + labelSignalGeneratorFrequency->setEnabled(false); + sliderSignalGeneratorFrequency->setEnabled(false); + labelLogF->setEnabled(false); + labelLogA->setEnabled(false); + comboBoxLogF->setEnabled(false); + comboBoxLogA->setEnabled(false); + labelColorMode->setEnabled(false); + comboBoxColorMode->setEnabled(false); + } else { + //Analyzer is turned on. Enable all related controls. + comboBoxResolution->setEnabled(true); + labelResolution->setEnabled(true); + comboBoxWindowing->setEnabled(true); + labelWindowing->setEnabled(true); + comboBoxRouting->setEnabled(true); + labelRouting->setEnabled(true); + labelLogF->setEnabled(true); + labelLogA->setEnabled(true); + comboBoxLogF->setEnabled(true); + comboBoxLogA->setEnabled(true); + labelColorMode->setEnabled(true); + comboBoxColorMode->setEnabled(true); + + routingChanged(comboBoxRouting->getSelectedId()); + } + + if (selectedOption == enumOptionIntValue(TransformationParameters::Type::FAST_WAVELET_TRANSFORM) || + selectedOption == enumOptionIntValue(TransformationParameters::Type::FAST_WAVELET_PACKET_BEST_BASIS_TRANSFORM)) { + //Disable Wavelet Tree/Packet related control for the dyadic Fast Wavelet Transform. + comboBoxWavelet->setEnabled(true); + labelWavelet->setEnabled(true); + comboBoxWaveletPacketBasis->setEnabled(false); + labelWaveletPacketBasis->setEnabled(false); + } else if (selectedOption == enumOptionIntValue(TransformationParameters::Type::FAST_WAVELET_PACKET_TRANSFORM)) { + //Enable all controls for the Fast Wavelet Packet Transform. + comboBoxWavelet->setEnabled(true); + labelWavelet->setEnabled(true); + comboBoxWaveletPacketBasis->setEnabled(true); + labelWaveletPacketBasis->setEnabled(true); + } else { + //Disable all wavelet related controls for FFT or otherwise (e.g. when analyzer is bypassed) + comboBoxWavelet->setEnabled(false); + labelWavelet->setEnabled(false); + comboBoxWaveletPacketBasis->setEnabled(false); + labelWaveletPacketBasis->setEnabled(false); + } +} + +void SpecletAnalyzerComponent::routingChanged(int selectedOption) { + if (selectedOption == enumOptionIntValue(SpecletParameters::OptionsRouting::ROUTING_GENERATOR)) { + //Audio source is set to generator. Enable all related controls. + comboBoxSignalGenerator->setEnabled(true); + labelSignalGenerator->setEnabled(true); + labelSignalGeneratorFrequency->setEnabled(true); + sliderSignalGeneratorFrequency->setEnabled(true); + } else { + //Audio source is set to input signal. Disable all related controls. + comboBoxSignalGenerator->setEnabled(false); + labelSignalGenerator->setEnabled(false); + labelSignalGeneratorFrequency->setEnabled(false); + sliderSignalGeneratorFrequency->setEnabled(false); + } +} + +template +auto SpecletAnalyzerComponent::enumOptionIntValue(const _Tp &enumType) const -> int { + auto enumValue = static_cast>(enumType); + return static_cast(enumValue); +} +//[/MiscUserCode] + + +//============================================================================== +#if 0 +/* -- Jucer information section -- + + This is where the Jucer puts all of its metadata, so don't change anything in here! + +BEGIN_JUCER_METADATA + + + + + + + + + + + + + + + + + +END_JUCER_METADATA +*/ +#endif diff --git a/src/ui/SpecletAnalyzerComponent.h b/src/ui/SpecletAnalyzerComponent.h new file mode 100644 index 00000000..fbed142e --- /dev/null +++ b/src/ui/SpecletAnalyzerComponent.h @@ -0,0 +1,145 @@ +/* + ============================================================================== + + This is an automatically generated file created by the Jucer! + + Creation date: 23 May 2011 11:58:55pm + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Jucer version: 1.12 + + ------------------------------------------------------------------------------ + + The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + + ============================================================================== +*/ +#pragma once + +//[Headers] -- You can add your own extra header files here -- +#include "../dsp/transformations/Transformation.h" +#include "../parameter/SpecletParameters.h" +#include "ColourGradients.h" +#include "SpecletDrawer.h" +#include "juce_core/juce_core.h" + +//[/Headers] + +class SpecletTooltipWindowLookAndFeel : public juce::LookAndFeel_V4 { +public: + SpecletTooltipWindowLookAndFeel() = default; + ~SpecletTooltipWindowLookAndFeel() override = default; + void drawTooltip(juce::Graphics &g, const juce::String &text, int width, int height) override; +}; + +//============================================================================== +/** + //[Comments] + An auto-generated component, created by the Jucer. + + Describe your class and how it works here! + //[/Comments] +*/ +class SpecletAnalyzerComponent : public juce::Component, + public juce::AudioProcessorValueTreeState::Listener, + public juce::SettableTooltipClient { +public: + //============================================================================== + explicit SpecletAnalyzerComponent(SpecletParameters & parametersToAttach); + ~SpecletAnalyzerComponent() override; + + //============================================================================== + //[UserMethods] -- You can add your own custom methods in this section. + auto createComboBox(const juce::String& componentName, const juce::String& parameterName) -> juce::ComboBox *; + auto createLabel(const juce::String& componentName, const juce::String& labelText) -> juce::Label *; + auto createSlider(const juce::String& componentName, const juce::String& parameterName) -> juce::Slider *; + auto getComponents() -> std::vector; + //[/UserMethods] + + void paint(juce::Graphics &g) override; + void resized() override; + void visibilityChanged() override; + void parentSizeChanged() override; + void broughtToFront() override; + void childrenChanged() override; + void enablementChanged() override; + void mouseMove(const juce::MouseEvent &e) override; + void mouseDown(const juce::MouseEvent &e) override; + void mouseWheelMove(const juce::MouseEvent &event, const juce::MouseWheelDetails &wheel) override; + + //============================================================================== + juce_UseDebuggingNewOperator + +private : + //[UserVariables] -- You can add your own custom variables in this section. + enum PopupMenuEntryIndices { + POPUPMENU_INDEX_1_ABOUT = 1 + }; + + SpecletParameters ¶meters; + juce::CriticalSection criticalSection; + juce::PopupMenu popupMenu; + + void parameterChanged(const juce::String& parameterID, float newValue) override; + void transformationChanged(int selectedOption); + void routingChanged(int selectedOption); + + template + auto enumOptionIntValue(const _Tp &enumType) const -> int; + + SpecletDrawer *specletDrawer = new SpecletDrawer(parameters.getLogFrequency(), parameters.getLogMagnitude(), ColourGradients::forIndex(parameters.getColorMode())); + //[/UserVariables] + + //============================================================================== + juce::Viewport *spectralviewport = nullptr; + + juce::TooltipWindow *tooltipWindow = nullptr; + SpecletTooltipWindowLookAndFeel tooltipWindowLookAndFeel; + + juce::ComboBox *comboBoxResolution = nullptr; + juce::Label *labelResolution = nullptr; + juce::ComboBox *comboBoxTransformation = nullptr; + juce::Label *labelTransformation = nullptr; + juce::ComboBox *comboBoxWindowing = nullptr; + juce::Label *labelWindowing = nullptr; + juce::ComboBox *comboBoxWavelet = nullptr; + juce::Label *labelWavelet = nullptr; + juce::ComboBox *comboBoxWaveletPacketBasis = nullptr; + juce::Label *labelWaveletPacketBasis = nullptr; + juce::ComboBox *comboBoxSignalGenerator = nullptr; + juce::Label *labelSignalGenerator = nullptr; + juce::ComboBox *comboBoxRouting = nullptr; + juce::Label *labelRouting = nullptr; + juce::Label *labelSignalGeneratorFrequency = nullptr; + juce::Slider *sliderSignalGeneratorFrequency = nullptr; + juce::Label *labelLogF = nullptr; + juce::Label *labelLogA = nullptr; + juce::ComboBox *comboBoxLogF = nullptr; + juce::ComboBox *comboBoxLogA = nullptr; + juce::Label *labelColorMode = nullptr; + juce::ComboBox *comboBoxColorMode = nullptr; + + using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment; + ComboBoxAttachment * resolutionParameterAttachment; + ComboBoxAttachment * transformationParameterAttachment; + ComboBoxAttachment * windowingParameterAttachment; + ComboBoxAttachment * waveletParameterAttachment; + ComboBoxAttachment * waveletPacketBasisParameterAttachment; + ComboBoxAttachment * signalGeneratorParameterAttachment; + ComboBoxAttachment * routingParameterAttachment; + ComboBoxAttachment * logFParameterAttachment; + ComboBoxAttachment * logAParameterAttachment; + ComboBoxAttachment * colorModeParameterAttachment; + + using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; + SliderAttachment * signalGeneratorFrequencyParameterAttachment; + + //============================================================================== + // (prevent copy constructor and operator= being generated..) + SpecletAnalyzerComponent(const SpecletAnalyzerComponent &) = delete; + auto operator=(const SpecletAnalyzerComponent &) -> SpecletAnalyzerComponent & = delete; +}; \ No newline at end of file diff --git a/src/ui/SpecletDrawer.cpp b/src/ui/SpecletDrawer.cpp new file mode 100644 index 00000000..14e28ddf --- /dev/null +++ b/src/ui/SpecletDrawer.cpp @@ -0,0 +1,364 @@ +/* + ============================================================================== + + This is an automatically generated file created by the Jucer! + + Creation date: 4 May 2011 11:36:36pm + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Jucer version: 1.12 + + ------------------------------------------------------------------------------ + + The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + + ============================================================================== +*/ + +//[Headers] You can add your own extra header files here... +#include "SpecletDrawer.h" +#include "../dsp/transformations/TransformationFactory.h" +#include "../utilities/PerformanceLogger.h" +#include "ColourGradients.h" +#include "SpecletDrawerParameters.h" +#include "juce_audio_utils/juce_audio_utils.h" +#include "juce_core/juce_core.h" +#include + +//[/Headers] + + +//[MiscUserDefs] You can add your own user definitions and misc code here... +const juce::Colour SpecletDrawer::AXIS_COLOR(0xffffffc0); + +//[/MiscUserDefs] + +//============================================================================== +SpecletDrawer::SpecletDrawer(bool logFrequency, bool logMagnitude, const juce::ColourGradient &initialColourGradient) + : settings({logFrequency, logMagnitude}), + renderingHelper(initialColourGradient) { + + //[UserPreSize] + //[/UserPreSize] + + setSize(sizeX, sizeY); + + //[Constructor] You can add your own custom stuff here.. + + //registers itself also as a transformation-result-lister for every transformation that will be created in future + TransformationFactory::getSingletonInstance().registerForTransformationResults(this); + + startTimer(TIMER); + updateFrequencyAxisImage(); + ready = true; + waitForDestruction.signal(); + //[/Constructor] +} + +SpecletDrawer::~SpecletDrawer() { + //[Destructor_pre]. You can add your own custom destruction code here.. + ready = false; + { + LOG_PERFORMANCE_OF_SCOPE("SpecletDrawer waitForDestruction"); + bool timeoutDuringWait = waitForDestruction.wait(WAIT_FOR_DESTRUCTION_TIMEOUT); + if (!timeoutDuringWait) { + DBG("SpecletDrawer destruction: Timeout during wait!"); + } + } + //[/Destructor_pre] + + //[Destructor]. You can add your own custom destruction code here.. + TransformationFactory::getSingletonInstance().registerForTransformationResults(nullptr); + waitForDestruction.signal(); + DBG("SpecletDrawer removed as parameter and transformation results listener and finally destructed"); + //[/Destructor] +} + +//============================================================================== +void SpecletDrawer::paint(juce::Graphics &g) { + //[UserPrePaint] Add your own custom painting code here.. + //[/UserPrePaint] + + g.fillAll(juce::Colours::black); + + //[UserPaint] Add your own custom painting code here.. + + //draw spectrum ---------------- + { + LOG_PERFORMANCE_OF_SCOPE("SpecletDrawer paint draw spectrum"); + g.drawImageAt(spectrumImage, 0, 0); + } + + //draw red position cursor ---------------- + g.setColour(juce::Colours::red); + float cursorX = static_cast(currentCursorXPos); + g.drawLine(cursorX, 0, cursorX, static_cast(sizeY), 1.0); + + //draw frequency and time axis ---------------- + { + LOG_PERFORMANCE_OF_SCOPE("SpecletDrawer paint draw axis"); + g.drawImageAt(axisImage, 0, 0); + } + + //[/UserPaint] +} + +void SpecletDrawer::resized() { + //[UserResized] Add your own custom resize handling here.. + //[/UserResized] +} + +//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... +void SpecletDrawer::timerCallback() { + stopTimer(); + if (!ready) { + return; + } + repaint(); + startTimer(TIMER); +} + +void SpecletDrawer::onTransformationEvent(TransformationResult *result) { + //This method is automatically called, when there are new transformation results available, + //as far as it had been successfully been registered as a listener by "SpecletJuceMainUI" + int watchDog = 200; + while (result->isOutputAvailable()) { + waitForDestruction.reset(); + appendSpectralImage(result); + waitForDestruction.signal(); + if (!ready) { + DBG("SpecletDrawer: Transformation result received, but not ready to draw."); + return; + } + watchDog--; + if (watchDog <= 0) { + //prevents endless loop + DBG("SpecletDrawer::onTransformationEvent watchDog canceled drawing!"); + break; + } + } + //if effective timeresolution didn't change, the timeresolution-axis needn't to be redrawn + auto const& spectralDataInfo = result->getSpectralDataInfo(); + auto timeResolution = spectralDataInfo.getTimeResolutionMs(); + if (timeResolution != currentTimeResolution) { + updateTimeAxisImage(timeResolution); + } + currentSamplingFrequency = spectralDataInfo.getSamplingFrequency(); +} + +//This method is called when a parameter changes (listener) +void SpecletDrawer::parameterChanged(const juce::String& parameterID, float newValue) { + const juce::ScopedLock myScopedLock(criticalSection); + auto newIndex = static_cast(newValue) + 1; //+1 because 0 is not a valid combo box selection id + + if (parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_LOGFREQUENCY)) { + settings.logFrequency = (newIndex == static_cast>(SpecletDrawerParameters::Axis::LOGARITHMIC)); + updateFrequencyAxisImage(); + } + if (parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_LOGMAGNITUDE)) { + settings.logMagnitude = (newIndex == static_cast>(SpecletDrawerParameters::Axis::LOGARITHMIC)); + } + // The colour gradient change could be moved to the RenderingHelper (thus making it a listener too). + // But registering it as a listener leads to additional coupling while initialization (add the listener). + // So it stays here for now. + if (parameterID.equalsIgnoreCase(SpecletParameters::PARAMETER_COLORMODE)) { + renderingHelper.setColourGradient(ColourGradients::forIndex(newIndex)); + } +} + +void SpecletDrawer::appendSpectralImage(TransformationResult *result) { + if (result == nullptr) { + DBG("SpecletDrawer::appendSpectralImage(..) no result parameter"); + return; + } + if (currentCursorXPos > (sizeX - 1)) { + currentCursorXPos = 0; + } + if (spectrumImage.isNull()) { + spectrumImage = juce::Image(juce::Image::RGB, sizeX, sizeY, true); + } + + renderingHelper.renderVerticalPoints( + result, + settings, + currentCursorXPos, + &spectrumImage); + currentCursorXPos++; +} + + +/* +This method draws the currently active frequency axis into the local member frequencyAxisImage, +which is drawn on top of the spectrum whithin the paint() method. +*/ +void SpecletDrawer::updateFrequencyAxisImage() { + int maxWidthOfFrequencyAxis = 80; + + //clears the part of the axis image, where the frequency resolution is drawn at + juce::Rectangle areaToClear(axisImage.getBounds().withRight(maxWidthOfFrequencyAxis)); + axisImage.clear(areaToClear, juce::Colours::transparentBlack); + + //get drawing context + juce::Graphics g(axisImage); + + // --- gets max frequency + double maxSpectralFrequency = 22050; + if (currentSamplingFrequency > 0) { + maxSpectralFrequency = currentSamplingFrequency / 2; + } + + int axisLineLength = 10; + g.setColour(AXIS_COLOR); + juce::Font oldFont = g.getCurrentFont(); + + if (settings.logFrequency) { + double logSubDivisionsPerDecade[] = {log10(1.0), log10(2.0), log10(5.0), log10(7.0)}; + int numberOfSubDivisions = sizeof logSubDivisionsPerDecade / sizeof logSubDivisionsPerDecade[0]; + + double maxFrequencyLog = log10(maxSpectralFrequency); + double minFrequencyLog = 1.0; + + for (auto logFreqDecade = 1; logFreqDecade <= 4; logFreqDecade++) { + for (auto i = 0; i < (numberOfSubDivisions - 1); i++) { + double logFreq = logSubDivisionsPerDecade[i] + logFreqDecade; + if (logFreq > maxFrequencyLog) { + break; + } + double posPercent = (logFreq - minFrequencyLog) / (maxFrequencyLog - minFrequencyLog); + auto yPos = static_cast(lrint((sizeY - 1) * (1.0f - posPercent))); + double freqDouble = pow(10, logFreq); + double freqCeil = ceil(freqDouble); + double freqFloor = floor(freqDouble); + int freq = ((freqCeil - freqDouble) > (freqDouble - freqFloor)) ? (int) freqFloor : (int) freqCeil; + + juce::String frequencyText(freq); + frequencyText += " Hz"; + + if (i == 0) { + //full decade: full line length and text size + g.setFont(12.0F); + g.drawFittedText(frequencyText, + axisLineLength + 3, + yPos - 7, + maxWidthOfFrequencyAxis, + yPos + 10, + juce::Justification::topLeft, + 1); + g.fillRect(juce::Rectangle(0, yPos, axisLineLength, 2)); + } else { + //subdivision: smaller font, smaller lines + g.setFont(9.0F); + g.drawFittedText( + frequencyText, + axisLineLength + 3, + yPos - 3, + maxWidthOfFrequencyAxis, + yPos + 10, + juce::Justification::topLeft, + 1); + g.fillRect(juce::Rectangle(0, yPos, axisLineLength, 1)); + } + } + } + } else { + double frequencyLabels = 10; + double step = 1.0 / frequencyLabels; + + for (double posPercent = step; posPercent < 1.0F; posPercent += step) { + int yPos = static_cast(lrint(getHeight() * (1.0f - posPercent))); + + juce::String frequencyText(maxSpectralFrequency * posPercent); + frequencyText += " Hz"; + + g.setFont(12.0F); + g.drawFittedText( + frequencyText, + axisLineLength + 3, + yPos - 7, + maxWidthOfFrequencyAxis, + yPos, + juce::Justification::topLeft, + 1); + g.fillRect(juce::Rectangle(0, yPos, axisLineLength, 1)); + } + } + g.setFont(oldFont); +} + +void SpecletDrawer::updateTimeAxisImage(double timeresolution) { + int timeAxisWidth = 60; + int lineLength = 50; + int xPosStart = sizeX - timeAxisWidth; + int yPosStart = getHeight() - 50; + int yPosLine = getHeight() - 30; + + currentTimeResolution = timeresolution; + + //clears the part of the axis image, where the time resolution is drawn at + auto axisImageBounds = axisImage.getBounds(); + auto rectangleToClear = axisImageBounds.withLeft(xPosStart).withTop(yPosStart); + + if (rectangleToClear.getWidth() <= 0) { + DBG("updateTimeAxisImage: Time axis width is 0. Skip axis update."); + + auto topLeftX = axisImageBounds.getTopLeft().getX(); + auto topLeftY = axisImageBounds.getTopLeft().getY(); + DBG("image topLeftX to clear: " << topLeftX << " image topLeftY to clear: " << topLeftY); + + auto rectangleTopLeftX = rectangleToClear.getTopLeft().getX(); + auto rectangleTopLeftY = rectangleToClear.getTopLeft().getY(); + DBG("rectangle topLeftX to clear: " << rectangleTopLeftX << " rectangle topLeftY to clear: " << rectangleTopLeftY); + + return; + } + juce::Rectangle areaToClear(rectangleToClear); + axisImage.clear(areaToClear, juce::Colours::transparentBlack); + + //get drawing context + juce::Graphics g(axisImage); + + double timeResultionForGivenLength = timeresolution * lineLength; + juce::String timeResolutionText; + if (timeResultionForGivenLength < 1.0) { + timeResolutionText = juce::String((int) timeResultionForGivenLength * 1000); + timeResolutionText += " ns"; + } else if (timeResultionForGivenLength < 1000.0) { + timeResolutionText = juce::String((int) timeResultionForGivenLength); + timeResolutionText += " ms"; + } else if (timeResultionForGivenLength > 1000.0) { + timeResolutionText = juce::String((int) (timeResultionForGivenLength / 1000.0)); + timeResolutionText += " s"; + } + g.setColour(AXIS_COLOR); + g.drawFittedText(timeResolutionText, xPosStart + 7, yPosStart, 79, 49, juce::Justification::topLeft, true); + g.fillRect(juce::Rectangle(xPosStart, yPosLine, lineLength, 1)); + g.fillRect(juce::Rectangle(xPosStart, yPosLine - 5, 1, 10)); + g.fillRect(juce::Rectangle(xPosStart + lineLength, yPosLine - 5, 1, 10)); +} + +//[/MiscUserCode] + + +//============================================================================== +#if 0 +/* -- Jucer information section -- + + This is where the Jucer puts all of its metadata, so don't change anything in here! + +BEGIN_JUCER_METADATA + + + + + +END_JUCER_METADATA +*/ +#endif diff --git a/src/ui/SpecletDrawer.h b/src/ui/SpecletDrawer.h new file mode 100644 index 00000000..4dd50b42 --- /dev/null +++ b/src/ui/SpecletDrawer.h @@ -0,0 +1,99 @@ +/* + ============================================================================== + + This is an automatically generated file created by the Jucer! + + Creation date: 4 May 2011 11:36:36pm + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Jucer version: 1.12 + + ------------------------------------------------------------------------------ + + The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-6 by Raw Material Software ltd. + + ============================================================================== +*/ +#pragma once + +//[Headers] -- You can add your own extra header files here -- +#include "../dsp/transformations/Transformation.h" +#include "../utilities/RenderingHelper.h" +#include "ColourGradients.h" +#include "juce_core/juce_core.h" + +//[/Headers] + + +//============================================================================== +/** + //[Comments] + An auto-generated component, created by the Jucer. + + Describe your class and how it works here! + //[/Comments] +*/ +class SpecletDrawer : public juce::Component, + public TransformationListener, + public juce::Timer, + public juce::AudioProcessorValueTreeState::Listener { +public: + //============================================================================== + SpecletDrawer(bool logFrequency = true, bool logMagnitude = true, const juce::ColourGradient & initialColourGradient = ColourGradients::BLUE); + ~SpecletDrawer() override; + + //============================================================================== + //[UserMethods] -- You can add your own custom methods in this section. + void timerCallback() override; + void onTransformationEvent(TransformationResult *result) override; + void parameterChanged(const juce::String& parameterID, float newValue) override; + //============================================================================== + //[/UserMethods] + + void paint(juce::Graphics &g) override; + void resized() override; + + //============================================================================== + juce_UseDebuggingNewOperator + +private : + //[UserVariables] -- You can add your own custom variables in this section. + static const juce::Colour AXIS_COLOR; + enum Constants { + SIZE_X = 528, + SIZE_Y = 360, + TIMER = 20, + WAIT_FOR_DESTRUCTION_TIMEOUT = 3000 + }; + void updateFrequencyAxisImage(); + void updateTimeAxisImage(double timeresolution); + void appendSpectralImage(TransformationResult *result); + juce::Viewport *getParentViewPort() const { return static_cast(getParentComponent()); } + + int sizeX = Constants::SIZE_X; + int sizeY = Constants::SIZE_Y; + double currentTimeResolution = 0; + double currentSamplingFrequency = 0.0; + RenderingHelper::TAnalyzerSettings settings; + RenderingHelper renderingHelper; + int currentCursorXPos = 0; + juce::Image spectrumImage = {juce::Image::RGB, sizeX, sizeY, true}; + juce::Image axisImage = {juce::Image::PixelFormat::ARGB, sizeX, sizeY, true}; + juce::CriticalSection criticalSection = {}; + juce::WaitableEvent waitForDestruction{true}; + bool ready = false; + + //[/UserVariables] + + //============================================================================== + + + //============================================================================== + // (prevent copy constructor and operator= being generated..) + SpecletDrawer(const SpecletDrawer &); + const SpecletDrawer &operator=(const SpecletDrawer &); +}; \ No newline at end of file diff --git a/src/ui/SpecletDrawerParameters.h b/src/ui/SpecletDrawerParameters.h new file mode 100644 index 00000000..7afd5904 --- /dev/null +++ b/src/ui/SpecletDrawerParameters.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace SpecletDrawerParameters { + + enum class Axis { + LINEAR = 1, + LOGARITHMIC, + + NUMBER_OF_OPTIONS, + DEFAULT = LOGARITHMIC + }; + + struct AxisNames { + static std::map createMap() { + return { + {Axis::LINEAR, "linear"}, + {Axis::LOGARITHMIC, "logarithmic"}, + }; + } + static const std::map map; + }; + + inline const std::map AxisNames::map = AxisNames::createMap(); + +}// namespace SpecletDrawerParameters \ No newline at end of file diff --git a/src/ui/SpecletMainUI.cpp b/src/ui/SpecletMainUI.cpp new file mode 100644 index 00000000..6b800e27 --- /dev/null +++ b/src/ui/SpecletMainUI.cpp @@ -0,0 +1,32 @@ +/* + ============================================================================== + ============================================================================== +*/ + +#include "SpecletMainUI.h" +#include "SpecletDrawer.h" + +//============================================================================== +SpecletMainUI::SpecletMainUI(SpecletAudioProcessor &ownerAudioProcessor) + : AudioProcessorEditor(ownerAudioProcessor), + audioProcessor(ownerAudioProcessor), + spectralAnalyzerComponent(ownerAudioProcessor.getSpecletParameters()) { + // set our component's initial size to be the last one that was stored in the filter's settings + setSize(ownerAudioProcessor.lastUIWidth, ownerAudioProcessor.lastUIHeight); + + //GUI, that holds all parameter-widgets and the spectralview + addAndMakeVisible(&spectralAnalyzerComponent); + + // set our component's initial size to be the last one that was stored in the filter's settings + setSize(ownerAudioProcessor.lastUIWidth, ownerAudioProcessor.lastUIHeight); +} + +//============================================================================== +void SpecletMainUI::paint(juce::Graphics &) { +} + +void SpecletMainUI::resized() { + spectralAnalyzerComponent.setBounds(0, 0, MAINGUI_SIZE_X, MAINGUI_SIZE_Y); + audioProcessor.lastUIWidth = getWidth(); + audioProcessor.lastUIHeight = getHeight(); +} \ No newline at end of file diff --git a/src/ui/SpecletMainUI.h b/src/ui/SpecletMainUI.h new file mode 100644 index 00000000..2aadaef1 --- /dev/null +++ b/src/ui/SpecletMainUI.h @@ -0,0 +1,38 @@ +#pragma once +/* + ============================================================================== + + This file was auto-generated by the Jucer! + + It contains the basic startup code for a Juce application. + + ============================================================================== +*/ + +#include "../SpecletPluginProcessor.h" +#include "../dsp/transformations/TransformationFactory.h" +#include "juce_core/juce_core.h" +#include "SpecletAnalyzerComponent.h" + +//============================================================================== +/** This is the editor component that our filter will display. +*/ +class SpecletMainUI : public juce::AudioProcessorEditor { +public: + enum Constants { + MAINGUI_SIZE_X = 800, + MAINGUI_SIZE_Y = 360 + }; + explicit SpecletMainUI(SpecletAudioProcessor &ownerFilter); + ~SpecletMainUI() override = default; + + void paint(juce::Graphics &graphics) override; + void resized() override; + +private: + // This reference is provided as a quick way for your editor to + // access the processor object that created it. + SpecletAudioProcessor &audioProcessor; + + SpecletAnalyzerComponent spectralAnalyzerComponent; +}; \ No newline at end of file diff --git a/src/user interface/ColourGradients.cpp b/src/user interface/ColourGradients.cpp deleted file mode 100644 index 809c87b1..00000000 --- a/src/user interface/ColourGradients.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "ColourGradients.h" -#include "../../source/plugin/SpectronParameters.h" - -// Singleton instance variable (only one instance of this class) -ColourGradients* ColourGradients::singletonInstance = 0; - -ColourGradients* ColourGradients::getSingletonInstance() { -// Method to get the single instance of this class (Singleton) - if (ColourGradients::singletonInstance == 0) { - ColourGradients::singletonInstance = new ColourGradients(); - } - return ColourGradients::singletonInstance; -} - -void ColourGradients::destruct() { - if (!singletonInstance) return; - delete(singletonInstance); - singletonInstance = 0; -} - -juce::ColourGradient ColourGradients::get(int index) { - if (index == SpectronParameters::COLORMODE_BLUE) { - return getBlue(); - } - else if (index == SpectronParameters::COLORMODE_GREEN) { - return getGreen(); - } - else if (index == SpectronParameters::COLORMODE_FIRE) { - return getFire(); - } - else if (index == SpectronParameters::COLORMODE_RAINBOW) { - return getRainbow(); - } - - DBG(T("juce::ColourGradient ColourGradients::get(): index not found (replaced by blue)! i=") + juce::String(index)); - return getBlue(); -} - -juce::ColourGradient ColourGradients::getBlue() { - juce::ColourGradient gradient = ColourGradient(); - - gradient.addColour(1.0f, Colour::fromRGB(40, 40, 255)); - gradient.addColour(0.0f, Colour::fromRGB(0, 0, 0)); - - return gradient; -} - -juce::ColourGradient ColourGradients::getGreen() { - juce::ColourGradient gradient = ColourGradient(); - - gradient.addColour(1.0f, Colour::fromRGB(40, 255, 40)); - gradient.addColour(0.0f, Colour::fromRGB(0, 0, 0)); - - return gradient; -} - -juce::ColourGradient ColourGradients::getFire() { - juce::ColourGradient gradient = ColourGradient(); - - gradient.addColour(1.0f, juce::Colours::yellow); - gradient.addColour(0.8f, juce::Colours::red); - gradient.addColour(0.4f, juce::Colours::darkred); - gradient.addColour(0.2f, juce::Colours::darkblue); - gradient.addColour(0.0f, juce::Colours::black); - - return gradient; -} - -juce::ColourGradient ColourGradients::getRainbow() { - juce::ColourGradient gradient = ColourGradient(); - - gradient.addColour(1.0f, juce::Colours::red); - gradient.addColour(0.8f, juce::Colours::yellow); - gradient.addColour(0.6f, juce::Colours::green); - gradient.addColour(0.4f, juce::Colours::darkblue); - gradient.addColour(0.2f, juce::Colours::darkviolet); - gradient.addColour(0.0f, juce::Colours::black); - - return gradient; -} diff --git a/src/user interface/SpectronAnalyzerComponent.cpp b/src/user interface/SpectronAnalyzerComponent.cpp deleted file mode 100644 index c799d970..00000000 --- a/src/user interface/SpectronAnalyzerComponent.cpp +++ /dev/null @@ -1,812 +0,0 @@ -/* - ============================================================================== - - This is an automatically generated file created by the Jucer! - - Creation date: 23 May 2011 11:58:55pm - - Be careful when adding custom code to these files, as only the code within - the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded - and re-saved. - - Jucer version: 1.12 - - ------------------------------------------------------------------------------ - - The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - - ============================================================================== -*/ - -//[Headers] You can add your own extra header files here... -#include "SpectronDrawer.h" -#include "../utilities/PerformanceManager.h" -//[/Headers] - -#include "SpectronAnalyzerComponent.h" - - -//[MiscUserDefs] You can add your own user definitions and misc code here... -//[/MiscUserDefs] - -//============================================================================== -SpectronAnalyzerComponent::SpectronAnalyzerComponent () - : Component (T("SpectronAnalyzerComponent")), - comboBoxResolution (0), - spectralviewport (0), - labelResolution (0), - comboBoxTransformation (0), - labelTransformation (0), - comboBoxWindowing (0), - labelWindowing (0), - comboBoxWavelet (0), - labelWavelet (0), - comboBoxWaveletPaketBasis (0), - labelWaveletPaketBasis (0), - comboBoxSignalgenerator (0), - labelSignalgenerator (0), - comboBoxSignalquelle (0), - labelSignalquelle (0), - labelGeneratorfrequenz (0), - sliderGeneratorFrequenz (0), - labelLogF (0), - labelLogA (0), - comboBoxLogF (0), - comboBoxLogA (0), - labelColorMode (0), - comboBoxColorMode (0) -{ - addAndMakeVisible (comboBoxResolution = new ComboBox (T("comboBoxResolution"))); - comboBoxResolution->setTooltip (T("Aufl\xf6sung")); - comboBoxResolution->setEditableText (false); - comboBoxResolution->setJustificationType (Justification::centredLeft); - comboBoxResolution->setTextWhenNothingSelected (String::empty); - comboBoxResolution->setTextWhenNoChoicesAvailable (String::empty); - comboBoxResolution->addListener (this); - - addAndMakeVisible (spectralviewport = new Viewport (T("spectralviewport"))); - spectralviewport->setScrollBarsShown (false, true); - spectralviewport->setScrollBarThickness (10); - - addAndMakeVisible (labelResolution = new Label (T("labelResolution"), - T("Aufl\xf6sung"))); - labelResolution->setFont (Font (15.0000f, Font::plain)); - labelResolution->setJustificationType (Justification::centredLeft); - labelResolution->setEditable (false, false, false); - labelResolution->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelResolution->setColour (Label::textColourId, Colours::white); - labelResolution->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelResolution->setColour (TextEditor::textColourId, Colours::black); - labelResolution->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxTransformation = new ComboBox (T("comboBoxTransformation"))); - comboBoxTransformation->setTooltip (T("Transformation")); - comboBoxTransformation->setEditableText (false); - comboBoxTransformation->setJustificationType (Justification::centredLeft); - comboBoxTransformation->setTextWhenNothingSelected (String::empty); - comboBoxTransformation->setTextWhenNoChoicesAvailable (String::empty); - comboBoxTransformation->addListener (this); - - addAndMakeVisible (labelTransformation = new Label (T("labelTransformation"), - T("Transformation"))); - labelTransformation->setFont (Font (15.0000f, Font::plain)); - labelTransformation->setJustificationType (Justification::centredLeft); - labelTransformation->setEditable (false, false, false); - labelTransformation->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelTransformation->setColour (Label::textColourId, Colours::white); - labelTransformation->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelTransformation->setColour (TextEditor::textColourId, Colours::black); - labelTransformation->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxWindowing = new ComboBox (T("comboBoxWindowing"))); - comboBoxWindowing->setTooltip (T("Fensterfunktion")); - comboBoxWindowing->setEditableText (false); - comboBoxWindowing->setJustificationType (Justification::centredLeft); - comboBoxWindowing->setTextWhenNothingSelected (String::empty); - comboBoxWindowing->setTextWhenNoChoicesAvailable (String::empty); - comboBoxWindowing->addListener (this); - - addAndMakeVisible (labelWindowing = new Label (T("labelWindowing"), - T("Fensterfunktion"))); - labelWindowing->setFont (Font (15.0000f, Font::plain)); - labelWindowing->setJustificationType (Justification::centredLeft); - labelWindowing->setEditable (false, false, false); - labelWindowing->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelWindowing->setColour (Label::textColourId, Colours::white); - labelWindowing->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelWindowing->setColour (TextEditor::textColourId, Colours::black); - labelWindowing->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxWavelet = new ComboBox (T("comboBoxWavelet"))); - comboBoxWavelet->setTooltip (T("Wavelet (+Filterordnung)")); - comboBoxWavelet->setEditableText (false); - comboBoxWavelet->setJustificationType (Justification::centredLeft); - comboBoxWavelet->setTextWhenNothingSelected (String::empty); - comboBoxWavelet->setTextWhenNoChoicesAvailable (String::empty); - comboBoxWavelet->addListener (this); - - addAndMakeVisible (labelWavelet = new Label (T("labelWavelet"), - T("Wavelet"))); - labelWavelet->setFont (Font (15.0000f, Font::plain)); - labelWavelet->setJustificationType (Justification::centredLeft); - labelWavelet->setEditable (false, false, false); - labelWavelet->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelWavelet->setColour (Label::textColourId, Colours::white); - labelWavelet->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelWavelet->setColour (TextEditor::textColourId, Colours::black); - labelWavelet->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxWaveletPaketBasis = new ComboBox (T("comboBoxWaveletPaketBasis"))); - comboBoxWaveletPaketBasis->setTooltip (T("Wavelet Paket Basis")); - comboBoxWaveletPaketBasis->setEditableText (false); - comboBoxWaveletPaketBasis->setJustificationType (Justification::centredLeft); - comboBoxWaveletPaketBasis->setTextWhenNothingSelected (String::empty); - comboBoxWaveletPaketBasis->setTextWhenNoChoicesAvailable (String::empty); - comboBoxWaveletPaketBasis->addListener (this); - - addAndMakeVisible (labelWaveletPaketBasis = new Label (T("labelWaveletPaketBasis"), - T("Wavelet Paketbasis"))); - labelWaveletPaketBasis->setFont (Font (15.0000f, Font::plain)); - labelWaveletPaketBasis->setJustificationType (Justification::centredLeft); - labelWaveletPaketBasis->setEditable (false, false, false); - labelWaveletPaketBasis->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelWaveletPaketBasis->setColour (Label::textColourId, Colours::white); - labelWaveletPaketBasis->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelWaveletPaketBasis->setColour (TextEditor::textColourId, Colours::black); - labelWaveletPaketBasis->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxSignalgenerator = new ComboBox (T("comboBoxSignalgenerator"))); - comboBoxSignalgenerator->setTooltip (T("Signalgenerator")); - comboBoxSignalgenerator->setEditableText (false); - comboBoxSignalgenerator->setJustificationType (Justification::centredLeft); - comboBoxSignalgenerator->setTextWhenNothingSelected (String::empty); - comboBoxSignalgenerator->setTextWhenNoChoicesAvailable (String::empty); - comboBoxSignalgenerator->addListener (this); - - addAndMakeVisible (labelSignalgenerator = new Label (T("labelSignalgenerator"), - T("Signalgenerator"))); - labelSignalgenerator->setFont (Font (15.0000f, Font::plain)); - labelSignalgenerator->setJustificationType (Justification::centredLeft); - labelSignalgenerator->setEditable (false, false, false); - labelSignalgenerator->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelSignalgenerator->setColour (Label::textColourId, Colours::white); - labelSignalgenerator->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelSignalgenerator->setColour (TextEditor::textColourId, Colours::black); - labelSignalgenerator->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxSignalquelle = new ComboBox (T("comboBoxSignalquelle"))); - comboBoxSignalquelle->setTooltip (T("Signalquelle")); - comboBoxSignalquelle->setEditableText (false); - comboBoxSignalquelle->setJustificationType (Justification::centredLeft); - comboBoxSignalquelle->setTextWhenNothingSelected (String::empty); - comboBoxSignalquelle->setTextWhenNoChoicesAvailable (String::empty); - comboBoxSignalquelle->addListener (this); - - addAndMakeVisible (labelSignalquelle = new Label (T("labelSignalquelle"), - T("Signalquelle"))); - labelSignalquelle->setFont (Font (15.0000f, Font::plain)); - labelSignalquelle->setJustificationType (Justification::centredLeft); - labelSignalquelle->setEditable (false, false, false); - labelSignalquelle->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelSignalquelle->setColour (Label::textColourId, Colours::white); - labelSignalquelle->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelSignalquelle->setColour (TextEditor::textColourId, Colours::black); - labelSignalquelle->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (labelGeneratorfrequenz = new Label (T("labelGeneratorfrequenz"), - T("Generatorfrequenz"))); - labelGeneratorfrequenz->setFont (Font (15.0000f, Font::plain)); - labelGeneratorfrequenz->setJustificationType (Justification::centredLeft); - labelGeneratorfrequenz->setEditable (false, false, false); - labelGeneratorfrequenz->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelGeneratorfrequenz->setColour (Label::textColourId, Colours::white); - labelGeneratorfrequenz->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelGeneratorfrequenz->setColour (TextEditor::textColourId, Colours::black); - labelGeneratorfrequenz->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (sliderGeneratorFrequenz = new Slider (T("sliderGeneratorFrequenz"))); - sliderGeneratorFrequenz->setTooltip (T("Frequenz des Signalgenerators")); - sliderGeneratorFrequenz->setExplicitFocusOrder (7); - sliderGeneratorFrequenz->setRange (10, 22000, 1); - sliderGeneratorFrequenz->setSliderStyle (Slider::LinearHorizontal); - sliderGeneratorFrequenz->setTextBoxStyle (Slider::TextBoxLeft, false, 50, 25); - sliderGeneratorFrequenz->addListener (this); - - addAndMakeVisible (labelLogF = new Label (T("labelLogF"), - T("Frequenzachse"))); - labelLogF->setFont (Font (15.0000f, Font::plain)); - labelLogF->setJustificationType (Justification::centredLeft); - labelLogF->setEditable (false, false, false); - labelLogF->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelLogF->setColour (Label::textColourId, Colours::white); - labelLogF->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelLogF->setColour (TextEditor::textColourId, Colours::black); - labelLogF->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (labelLogA = new Label (T("labelLogA"), - T("Amplitudenachse"))); - labelLogA->setFont (Font (15.0000f, Font::plain)); - labelLogA->setJustificationType (Justification::centredLeft); - labelLogA->setEditable (false, false, false); - labelLogA->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelLogA->setColour (Label::textColourId, Colours::white); - labelLogA->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelLogA->setColour (TextEditor::textColourId, Colours::black); - labelLogA->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxLogF = new ComboBox (T("comboBoxLogF"))); - comboBoxLogF->setTooltip (T("Frequenzachse")); - comboBoxLogF->setEditableText (false); - comboBoxLogF->setJustificationType (Justification::centredLeft); - comboBoxLogF->setTextWhenNothingSelected (String::empty); - comboBoxLogF->setTextWhenNoChoicesAvailable (String::empty); - comboBoxLogF->addListener (this); - - addAndMakeVisible (comboBoxLogA = new ComboBox (T("comboBoxLogA"))); - comboBoxLogA->setTooltip (T("Logarithmische Amplitudenachse")); - comboBoxLogA->setEditableText (false); - comboBoxLogA->setJustificationType (Justification::centredLeft); - comboBoxLogA->setTextWhenNothingSelected (String::empty); - comboBoxLogA->setTextWhenNoChoicesAvailable (String::empty); - comboBoxLogA->addListener (this); - - addAndMakeVisible (labelColorMode = new Label (T("labelColorMode"), - T("Farbmodus"))); - labelColorMode->setFont (Font (15.0000f, Font::plain)); - labelColorMode->setJustificationType (Justification::centredLeft); - labelColorMode->setEditable (false, false, false); - labelColorMode->setColour (Label::backgroundColourId, Colour (0x30007bfc)); - labelColorMode->setColour (Label::textColourId, Colours::white); - labelColorMode->setColour (Label::outlineColourId, Colour (0xff0082f7)); - labelColorMode->setColour (TextEditor::textColourId, Colours::black); - labelColorMode->setColour (TextEditor::backgroundColourId, Colour (0x0)); - - addAndMakeVisible (comboBoxColorMode = new ComboBox (T("comboBoxColorMode"))); - comboBoxColorMode->setTooltip (T("Farbmodus")); - comboBoxColorMode->setEditableText (false); - comboBoxColorMode->setJustificationType (Justification::centredLeft); - comboBoxColorMode->setTextWhenNothingSelected (String::empty); - comboBoxColorMode->setTextWhenNoChoicesAvailable (String::empty); - comboBoxColorMode->addListener (this); - - - //[UserPreSize] - fillComboBoxes(); - //[/UserPreSize] - - setSize (800, 360); - - //[Constructor] You can add your own custom stuff here.. - - //gets the pointer to the parameters singelton - for a better readability - parameters = SpectronParameters::getSingletonInstance(); - //registeres itself as listener for parameter-changes - SpectronParameters::getSingletonInstance()->addListener(this); - DBG("SpectronAnalyzerComponent as parameter listener added"); - LOG("SpectronAnalyzerComponent as parameter listener added"); - //adds spectrum drawing component to the scrollable view - spectralviewport->setViewedComponent(new SpectronDrawer()); - - //adds entries to the popup/context menu - popupMenu.addItem(POPUPMENU_INDEX_1_ABOUT, "about"); - addMouseListener(this, true); - //[/Constructor] -} - -SpectronAnalyzerComponent::~SpectronAnalyzerComponent() -{ - //[Destructor_pre]. You can add your own custom destruction code here.. - //[/Destructor_pre] - - deleteAndZero (comboBoxResolution); - deleteAndZero (spectralviewport); - deleteAndZero (labelResolution); - deleteAndZero (comboBoxTransformation); - deleteAndZero (labelTransformation); - deleteAndZero (comboBoxWindowing); - deleteAndZero (labelWindowing); - deleteAndZero (comboBoxWavelet); - deleteAndZero (labelWavelet); - deleteAndZero (comboBoxWaveletPaketBasis); - deleteAndZero (labelWaveletPaketBasis); - deleteAndZero (comboBoxSignalgenerator); - deleteAndZero (labelSignalgenerator); - deleteAndZero (comboBoxSignalquelle); - deleteAndZero (labelSignalquelle); - deleteAndZero (labelGeneratorfrequenz); - deleteAndZero (sliderGeneratorFrequenz); - deleteAndZero (labelLogF); - deleteAndZero (labelLogA); - deleteAndZero (comboBoxLogF); - deleteAndZero (comboBoxLogA); - deleteAndZero (labelColorMode); - deleteAndZero (comboBoxColorMode); - - //[Destructor]. You can add your own custom destruction code here.. - //unregisteres itself as listener for parameter-changes - SpectronParameters::getSingletonInstance()->removeListener(this); - DBG("SpectronAnalyzerComponent as parameter listener removed"); - LOG("SpectronAnalyzerComponent as parameter listener removed"); - parameters = NULL; - //[/Destructor] -} - -//============================================================================== -void SpectronAnalyzerComponent::paint (Graphics& g) -{ - //[UserPrePaint] Add your own custom painting code here.. - //[/UserPrePaint] - - g.fillAll (Colours::black); - - //[UserPaint] Add your own custom painting code here.. - //[/UserPaint] -} - -void SpectronAnalyzerComponent::resized() -{ - comboBoxResolution->setBounds ((128) + 0, (8) + 32, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - spectralviewport->setBounds (256, 8, 528, 344); - labelResolution->setBounds ((8) + 0, (8) + 32, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxTransformation->setBounds (128, 8, 120, 24); - labelTransformation->setBounds (8, 8, 120, 24); - comboBoxWindowing->setBounds ((128) + 0, (8) + 64, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelWindowing->setBounds ((8) + 0, (8) + 64, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxWavelet->setBounds ((128) + 0, (8) + 96, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelWavelet->setBounds ((8) + 0, (8) + 96, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxWaveletPaketBasis->setBounds ((128) + 0, (8) + 128, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelWaveletPaketBasis->setBounds ((8) + 0, (8) + 128, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxSignalgenerator->setBounds ((128) + 0, (8) + 288, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelSignalgenerator->setBounds ((8) + 0, (8) + 288, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxSignalquelle->setBounds ((128) + 0, (8) + 160, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelSignalquelle->setBounds ((8) + 0, (8) + 160, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelGeneratorfrequenz->setBounds ((8) + 0, (8) + 320, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - sliderGeneratorFrequenz->setBounds ((128) + 0, (8) + 320, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelLogF->setBounds ((8) + 0, (8) + 192, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelLogA->setBounds ((8) + 0, (8) + 224, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxLogF->setBounds ((128) + 0, (8) + 192, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxLogA->setBounds ((128) + 0, (8) + 224, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - labelColorMode->setBounds ((8) + 1, (8) + 256, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - comboBoxColorMode->setBounds ((128) + 1, (8) + 256, roundFloatToInt ((120) * 1.0000f), roundFloatToInt ((24) * 1.0000f)); - //[UserResized] Add your own custom resize handling here.. - //[/UserResized] -} - -void SpectronAnalyzerComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged) -{ - //[UsercomboBoxChanged_Pre] - //[/UsercomboBoxChanged_Pre] - - if (comboBoxThatHasChanged == comboBoxResolution) - { - //[UserComboBoxCode_comboBoxResolution] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Resolution, comboBoxResolution->getText().getIntValue()); - //[/UserComboBoxCode_comboBoxResolution] - } - else if (comboBoxThatHasChanged == comboBoxTransformation) - { - //[UserComboBoxCode_comboBoxTransformation] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Transformation, comboBoxTransformation->getSelectedId()); - //[/UserComboBoxCode_comboBoxTransformation] - } - else if (comboBoxThatHasChanged == comboBoxWindowing) - { - //[UserComboBoxCode_comboBoxWindowing] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Windowing, comboBoxWindowing->getSelectedId()); - //[/UserComboBoxCode_comboBoxWindowing] - } - else if (comboBoxThatHasChanged == comboBoxWavelet) - { - //[UserComboBoxCode_comboBoxWavelet] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Wavelet, comboBoxWavelet->getSelectedId()); - //[/UserComboBoxCode_comboBoxWavelet] - } - else if (comboBoxThatHasChanged == comboBoxWaveletPaketBasis) - { - //[UserComboBoxCode_comboBoxWaveletPaketBasis] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_WaveletPaketBase, comboBoxWaveletPaketBasis->getSelectedId()); - //[/UserComboBoxCode_comboBoxWaveletPaketBasis] - } - else if (comboBoxThatHasChanged == comboBoxSignalgenerator) - { - //[UserComboBoxCode_comboBoxSignalgenerator] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Generator, comboBoxSignalgenerator->getSelectedId()); - //[/UserComboBoxCode_comboBoxSignalgenerator] - } - else if (comboBoxThatHasChanged == comboBoxSignalquelle) - { - //[UserComboBoxCode_comboBoxSignalquelle] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_Routing, comboBoxSignalquelle->getSelectedId()); - //[/UserComboBoxCode_comboBoxSignalquelle] - } - else if (comboBoxThatHasChanged == comboBoxLogF) - { - //[UserComboBoxCode_comboBoxLogF] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_LogFrequency, comboBoxLogF->getSelectedId()); - //[/UserComboBoxCode_comboBoxLogF] - } - else if (comboBoxThatHasChanged == comboBoxLogA) - { - //[UserComboBoxCode_comboBoxLogA] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_LogMagnitude, comboBoxLogA->getSelectedId()); - //[/UserComboBoxCode_comboBoxLogA] - } - else if (comboBoxThatHasChanged == comboBoxColorMode) - { - //[UserComboBoxCode_comboBoxColorMode] -- add your combo box handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_ColorMode, comboBoxColorMode->getSelectedId()); - //[/UserComboBoxCode_comboBoxColorMode] - } - - //[UsercomboBoxChanged_Post] - //[/UsercomboBoxChanged_Post] -} - -void SpectronAnalyzerComponent::sliderValueChanged (Slider* sliderThatWasMoved) -{ - //[UsersliderValueChanged_Pre] - //[/UsersliderValueChanged_Pre] - - if (sliderThatWasMoved == sliderGeneratorFrequenz) - { - //[UserSliderCode_sliderGeneratorFrequenz] -- add your slider handling code here.. - parameters->setParameter(SpectronParameters::PARAMETER_INDEX_GeneratorFrequency, sliderGeneratorFrequenz->getValue()); - //[/UserSliderCode_sliderGeneratorFrequenz] - } - - //[UsersliderValueChanged_Post] - //[/UsersliderValueChanged_Post] -} - -void SpectronAnalyzerComponent::visibilityChanged() -{ - //[UserCode_visibilityChanged] -- Add your code here... - //[/UserCode_visibilityChanged] -} - -void SpectronAnalyzerComponent::parentSizeChanged() -{ - //[UserCode_parentSizeChanged] -- Add your code here... - //[/UserCode_parentSizeChanged] -} - -void SpectronAnalyzerComponent::broughtToFront() -{ - //[UserCode_broughtToFront] -- Add your code here... - //[/UserCode_broughtToFront] -} - -void SpectronAnalyzerComponent::childrenChanged() -{ - //[UserCode_childrenChanged] -- Add your code here... - //[/UserCode_childrenChanged] -} - -void SpectronAnalyzerComponent::enablementChanged() -{ - //[UserCode_enablementChanged] -- Add your code here... - //[/UserCode_enablementChanged] -} - -void SpectronAnalyzerComponent::mouseMove (const MouseEvent& e) -{ - //[UserCode_mouseMove] -- Add your code here... - //[/UserCode_mouseMove] -} - -void SpectronAnalyzerComponent::mouseDown (const MouseEvent& e) -{ - //[UserCode_mouseDown] -- Add your code here... - if ((e.mouseWasClicked()) - && (e.mods.isRightButtonDown())) { - if (popupMenu.show() == POPUPMENU_INDEX_1_ABOUT) { - juce::AlertWindow::AlertIconType icon = AlertWindow::InfoIcon; - juce::String message; - message+=("Written by Johannes Troppacher (c)2011\n"); - message+=("\n"); - message+=("Framework 'JUCE' by Raw Material Software\n"); - message+=("VST-Interface 'VST SDK 2.4 rev2' by Steinberg\n"); - message+=("FFT-Library 'FFTW' by MIT (Matteo Frigo and Steven G. Johnson)\n"); - message+=("Wavelet-Library 'wave++' by Ryerson Computrational Signal Analysis Group"); - message+=(" (S. E. Ferrando, L. A. Kolasa and N. Kovacevic)"); - AlertWindow::showMessageBox (icon, "about", message, "ok"); - } - } - //[/UserCode_mouseDown] -} - -void SpectronAnalyzerComponent::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) -{ - //[UserCode_mouseWheelMove] -- Add your code here... - //[/UserCode_mouseWheelMove] -} - - - -//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... -void SpectronAnalyzerComponent::fillComboBoxes() { - comboBoxResolution->addItem (T("256"), SpectronParameters::RESOLUTION_256); - comboBoxResolution->addItem (T("512"), SpectronParameters::RESOLUTION_512); - comboBoxResolution->addItem (T("1024"), SpectronParameters::RESOLUTION_1024); - comboBoxResolution->addItem (T("2048"), SpectronParameters::RESOLUTION_2048); - comboBoxResolution->addItem (T("4096"), SpectronParameters::RESOLUTION_4096); - comboBoxResolution->addItem (T("8192"), SpectronParameters::RESOLUTION_8192); - comboBoxResolution->addItem (T("16384"), SpectronParameters::RESOLUTION_16384); - comboBoxResolution->addItem (T("32768"), SpectronParameters::RESOLUTION_32768); - comboBoxResolution->addItem (T("65536"), SpectronParameters::RESOLUTION_65536); - - comboBoxTransformation->addItem (T("FFT"), SpectronParameters::TRANSFORM_FFT); - comboBoxTransformation->addItem (T("FWT"), SpectronParameters::TRANSFORM_FWT); - comboBoxTransformation->addItem (T("WPT"), SpectronParameters::TRANSFORM_FWPT); - comboBoxTransformation->addItem (T("WPT BestBase"), SpectronParameters::TRANSFORM_FWPT_BB); - comboBoxTransformation->addItem (T("Deaktiviert"), SpectronParameters::TRANSFORM_OFF); - - comboBoxWindowing->addItem (T("Barlett"), SpectronParameters::WINDOWING_BARTLETT); - comboBoxWindowing->addItem (T("Blackman"), SpectronParameters::WINDOWING_BLACKMAN); - comboBoxWindowing->addItem (T("Blackman-Harris"), SpectronParameters::WINDOWING_BLACKMAN_HARRIS); - comboBoxWindowing->addItem (T("Hamming"), SpectronParameters::WINDOWING_HAMMING); - comboBoxWindowing->addItem (T("Welch"), SpectronParameters::WINDOWING_WELCH); - comboBoxWindowing->addItem (T("Parzen"), SpectronParameters::WINDOWING_PARZEN); - comboBoxWindowing->addItem (T("Square"), SpectronParameters::WINDOWING_SQUARE); - - comboBoxWavelet->addItem (T("Haar (2)"), SpectronParameters::WAVELET_HAAR); - comboBoxWavelet->addItem (T("Daubechies (4)"), SpectronParameters::WAVELET_DAUBECHIES_04); - comboBoxWavelet->addItem (T("Daubechies (6)"), SpectronParameters::WAVELET_DAUBECHIES_06); - comboBoxWavelet->addItem (T("Daubechies (8)"), SpectronParameters::WAVELET_DAUBECHIES_08); - comboBoxWavelet->addItem (T("Daubechies (10)"), SpectronParameters::WAVELET_DAUBECHIES_10); - comboBoxWavelet->addItem (T("Daubechies (12)"), SpectronParameters::WAVELET_DAUBECHIES_12); - comboBoxWavelet->addItem (T("Daubechies (14)"), SpectronParameters::WAVELET_DAUBECHIES_14); - comboBoxWavelet->addItem (T("Daubechies (16)"), SpectronParameters::WAVELET_DAUBECHIES_16); - comboBoxWavelet->addItem (T("Daubechies (18)"), SpectronParameters::WAVELET_DAUBECHIES_18); - comboBoxWavelet->addItem (T("Daubechies (20)"), SpectronParameters::WAVELET_DAUBECHIES_20); - comboBoxWavelet->addItem (T("Coifman (6)"), SpectronParameters::WAVELET_COIFMAN_06); - comboBoxWavelet->addItem (T("Coifman (12)"), SpectronParameters::WAVELET_COIFMAN_12); - comboBoxWavelet->addItem (T("Coifman (18)"), SpectronParameters::WAVELET_COIFMAN_18); - comboBoxWavelet->addItem (T("Coifman (24)"), SpectronParameters::WAVELET_COIFMAN_24); - comboBoxWavelet->addItem (T("Coifman (30)"), SpectronParameters::WAVELET_COIFMAN_30); - comboBoxWavelet->addItem (T("Beylkin (18)"), SpectronParameters::WAVELET_BEYLKIN_18); - comboBoxWavelet->addItem (T("Vaidyanathan (18)"), SpectronParameters::WAVELET_VAIDYANATHAN_18); - - comboBoxWaveletPaketBasis->addItem (T("freq/time x1"), SpectronParameters::RESOLUTION_RATIO_Equal); - comboBoxWaveletPaketBasis->addItem (T("freq x2"), SpectronParameters::RESOLUTION_RATIO_FreqX2); - comboBoxWaveletPaketBasis->addItem (T("freq x4"), SpectronParameters::RESOLUTION_RATIO_FreqX4); - comboBoxWaveletPaketBasis->addItem (T("time x2"), SpectronParameters::RESOLUTION_RATIO_TimeX2); - comboBoxWaveletPaketBasis->addItem (T("time x4"), SpectronParameters::RESOLUTION_RATIO_TimeX4); - - comboBoxSignalgenerator->addItem (T("Sinus"), SpectronParameters::GENERATOR_SINE); - comboBoxSignalgenerator->addItem (T("Dreieck"), SpectronParameters::GENERATOR_TRANGLE); - comboBoxSignalgenerator->addItem (T("S\xe4gezahn"), SpectronParameters::GENERATOR_RAMP); - comboBoxSignalgenerator->addItem (T("Rechteck"), SpectronParameters::GENERATOR_SQUARE); - comboBoxSignalgenerator->addItem (T("Rauschen"), SpectronParameters::GENERATOR_NOISE); - - comboBoxSignalquelle->addItem (T("Mitte"), SpectronParameters::ROUTING_MID); - comboBoxSignalquelle->addItem (T("Seite"), SpectronParameters::ROUTING_SIDE); - comboBoxSignalquelle->addItem (T("Links"), SpectronParameters::ROUTING_L); - comboBoxSignalquelle->addItem (T("Rechts"), SpectronParameters::ROUTING_R); - comboBoxSignalquelle->addItem (T("Signalgenerator"), SpectronParameters::ROUTING_GENERATOR); - - comboBoxLogF->addItem (T("linear"), SpectronParameters::PLOT_AXIS_LINEAR); - comboBoxLogF->addItem (T("logarithmisch"), SpectronParameters::PLOT_AXIS_LOGARITHMIC); - - comboBoxLogA->addItem (T("linear"), SpectronParameters::PLOT_AXIS_LINEAR); - comboBoxLogA->addItem (T("logarithmisch"), SpectronParameters::PLOT_AXIS_LOGARITHMIC); - - comboBoxColorMode->addItem (T("Blau"), SpectronParameters::COLORMODE_BLUE); - comboBoxColorMode->addItem (T("Gr\xfcn"), SpectronParameters::COLORMODE_GREEN); - comboBoxColorMode->addItem (T("Feuer"), SpectronParameters::COLORMODE_FIRE); - comboBoxColorMode->addItem (T("Regenbogen"), SpectronParameters::COLORMODE_RAINBOW); -} - -void SpectronAnalyzerComponent::valueTreePropertyChanged (ValueTree &treeWhosePropertyHasChanged, const Identifier &property) { - const ScopedLock myScopedLock (criticalSection); - - updateComboBox(SpectronParameters::PARAMETER_COLORMODE, comboBoxColorMode, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_GENERATOR, comboBoxSignalgenerator, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_LOGFREQUENCY, comboBoxLogF, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_LOGMAGNITUDE, comboBoxLogA, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_RESOLUTION, comboBoxResolution, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_ROUTING, comboBoxSignalquelle, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_TRANSFORMATION, comboBoxTransformation, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_WAVELET, comboBoxWavelet, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_WAVELETPAKETBASE, comboBoxWaveletPaketBasis, treeWhosePropertyHasChanged); - updateComboBox(SpectronParameters::PARAMETER_WINDOWING, comboBoxWindowing, treeWhosePropertyHasChanged); - updateSlider(SpectronParameters::PARAMETER_GENERATORFREQUENCY, sliderGeneratorFrequenz, treeWhosePropertyHasChanged); -} - -//This method updates a combobox-index within an parameter-change-event -void SpectronAnalyzerComponent::updateComboBox ( - juce::String parameterName, - juce::ComboBox* comboBox, - const ValueTree &treeWhosePropertyHasChanged -) { - juce::String changedParameterName = treeWhosePropertyHasChanged.getType().toString(); - juce::var changedParameterValue = treeWhosePropertyHasChanged.getProperty(SpectronParameters::PROPERTY_VALUE); - - if (!changedParameterName.equalsIgnoreCase(parameterName)) return; - - assert(comboBox); - comboBox->setSelectedId((int)changedParameterValue, true); -} - -//This method updates a slider-value within an parameter-change-event -void SpectronAnalyzerComponent::updateSlider ( - juce::String parameterName, - juce::Slider* slider, - const ValueTree &treeWhosePropertyHasChanged -) { - juce::String changedParameterName = treeWhosePropertyHasChanged.getType().toString(); - juce::var changedParameterValue = treeWhosePropertyHasChanged.getProperty(SpectronParameters::PROPERTY_VALUE); - - if (!changedParameterName.equalsIgnoreCase(parameterName)) return; - - assert(slider); - slider->setValue(changedParameterValue, false); -} - -//[/MiscUserCode] - - -//============================================================================== -#if 0 -/* -- Jucer information section -- - - This is where the Jucer puts all of its metadata, so don't change anything in here! - -BEGIN_JUCER_METADATA - - - - - - - - - - - - - - - - - -END_JUCER_METADATA -*/ -#endif diff --git a/src/user interface/SpectronAnalyzerComponent.h b/src/user interface/SpectronAnalyzerComponent.h deleted file mode 100644 index 4849b7c7..00000000 --- a/src/user interface/SpectronAnalyzerComponent.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - ============================================================================== - - This is an automatically generated file created by the Jucer! - - Creation date: 23 May 2011 11:58:55pm - - Be careful when adding custom code to these files, as only the code within - the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded - and re-saved. - - Jucer version: 1.12 - - ------------------------------------------------------------------------------ - - The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - - ============================================================================== -*/ - -#ifndef __JUCER_HEADER_SPECTRONANALYZERCOMPONENT_SPECTRONANALYZERCOMPONENT_BA2D5B48__ -#define __JUCER_HEADER_SPECTRONANALYZERCOMPONENT_SPECTRONANALYZERCOMPONENT_BA2D5B48__ - -//[Headers] -- You can add your own extra header files here -- -#include "..\..\libs\juce\JuceLibraryCode\JuceHeader.h" -#include "..\dsp\transformations\Transformation.h" -#include "SpectronDrawer.h" -#include "..\plugin\SpectronParameters.h" -//[/Headers] - - - -//============================================================================== -/** - //[Comments] - An auto-generated component, created by the Jucer. - - Describe your class and how it works here! - //[/Comments] -*/ -class SpectronAnalyzerComponent : public Component, - public juce::ValueTree::Listener, - public ComboBoxListener, - public SliderListener -{ -public: - //============================================================================== - SpectronAnalyzerComponent (); - ~SpectronAnalyzerComponent(); - - //============================================================================== - //[UserMethods] -- You can add your own custom methods in this section. - //[/UserMethods] - - void paint (Graphics& g); - void resized(); - void comboBoxChanged (ComboBox* comboBoxThatHasChanged); - void sliderValueChanged (Slider* sliderThatWasMoved); - void visibilityChanged(); - void parentSizeChanged(); - void broughtToFront(); - void childrenChanged(); - void enablementChanged(); - void mouseMove (const MouseEvent& e); - void mouseDown (const MouseEvent& e); - void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); - - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - //[UserVariables] -- You can add your own custom variables in this section. - static enum PopupMenuEntryIndizes { - POPUPMENU_INDEX_1_ABOUT = 1 - }; - SpectronParameters* parameters; - juce::CriticalSection criticalSection; - juce::PopupMenu popupMenu; - - void fillComboBoxes(); - void valueTreePropertyChanged (ValueTree &treeWhosePropertyHasChanged, const Identifier &property); - void valueTreeChildrenChanged (ValueTree &treeWhoseChildHasChanged) {}; - void valueTreeParentChanged (ValueTree &treeWhoseParentHasChanged) {}; - - void updateComboBox (juce::String parameterName, juce::ComboBox* comboBox, const ValueTree &treeWhosePropertyHasChanged); - void updateSlider (juce::String parameterName, juce::Slider* slider, const ValueTree &treeWhosePropertyHasChanged); - - //[/UserVariables] - - //============================================================================== - ComboBox* comboBoxResolution; - Viewport* spectralviewport; - Label* labelResolution; - ComboBox* comboBoxTransformation; - Label* labelTransformation; - ComboBox* comboBoxWindowing; - Label* labelWindowing; - ComboBox* comboBoxWavelet; - Label* labelWavelet; - ComboBox* comboBoxWaveletPaketBasis; - Label* labelWaveletPaketBasis; - ComboBox* comboBoxSignalgenerator; - Label* labelSignalgenerator; - ComboBox* comboBoxSignalquelle; - Label* labelSignalquelle; - Label* labelGeneratorfrequenz; - Slider* sliderGeneratorFrequenz; - Label* labelLogF; - Label* labelLogA; - ComboBox* comboBoxLogF; - ComboBox* comboBoxLogA; - Label* labelColorMode; - ComboBox* comboBoxColorMode; - - //============================================================================== - // (prevent copy constructor and operator= being generated..) - SpectronAnalyzerComponent (const SpectronAnalyzerComponent&); - const SpectronAnalyzerComponent& operator= (const SpectronAnalyzerComponent&); -}; - - -#endif // __JUCER_HEADER_SPECTRONANALYZERCOMPONENT_SPECTRONANALYZERCOMPONENT_BA2D5B48__ diff --git a/src/user interface/SpectronDrawer.cpp b/src/user interface/SpectronDrawer.cpp deleted file mode 100644 index 1a60dbc2..00000000 --- a/src/user interface/SpectronDrawer.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - ============================================================================== - - This is an automatically generated file created by the Jucer! - - Creation date: 4 May 2011 11:36:36pm - - Be careful when adding custom code to these files, as only the code within - the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded - and re-saved. - - Jucer version: 1.12 - - ------------------------------------------------------------------------------ - - The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - - ============================================================================== -*/ - -//[Headers] You can add your own extra header files here... -#include "../dsp/transformations/TransformationFactory.h" -#include "../utilities/PerformanceManager.h" -#include "ColourGradients.h" -//[/Headers] - -#include "SpectronDrawer.h" - - -//[MiscUserDefs] You can add your own user definitions and misc code here... -const juce::Colour SpectronDrawer::AXIS_COLOR(0xffffffc0); - -//[/MiscUserDefs] - -//============================================================================== -SpectronDrawer::SpectronDrawer () -{ - - //[UserPreSize] - //[/UserPreSize] - - setSize (100, 360); - - //[Constructor] You can add your own custom stuff here.. - - sizeX = SIZE_X; - sizeY = getHeight(); - setSize (sizeX, sizeY); - - currentCursorXPos = 0; - currentTimeResolution = 0; - - //creates and prepares the axislayer image - axisImage = Image(juce::Image::PixelFormat::ARGB, sizeX, sizeY, true); - - //registers itself as a transformation-result-lister - Transformation* transformation = TransformationFactory::getSingletonInstance()->getCurrentTransformation(); - if (transformation) transformation->setTransformResultListener(this); - //registers itself also as a transformation-result-lister for every transformation that will be created in future - TransformationFactory::getSingletonInstance()->registerForTransformationResults(this); - //registeres itself as listener for parameter-changes - SpectronParameters::getSingletonInstance()->addListener(this); - DBG("SpectronDrawer as parameter listener added"); - LOG("SpectronDrawer as parameter listener added"); - - startTimer (TIMER); - //[/Constructor] -} - -SpectronDrawer::~SpectronDrawer() -{ - //[Destructor_pre]. You can add your own custom destruction code here.. - //[/Destructor_pre] - - - - //[Destructor]. You can add your own custom destruction code here.. - SpectronParameters::getSingletonInstance()->removeListener(this); - DBG("SpectronDrawer as parameter listener removed"); - LOG("SpectronDrawer as parameter listener removed"); - //[/Destructor] -} - -//============================================================================== -void SpectronDrawer::paint (Graphics& g) -{ - //[UserPrePaint] Add your own custom painting code here.. - //[/UserPrePaint] - - g.fillAll (Colours::black); - - //[UserPaint] Add your own custom painting code here.. - - //draw spectrum ---------------- - PerformanceManager::getSingletonInstance()->start("imageDraw", 2000); - g.drawImageAt(spectrumImage, 0, 0); - PerformanceManager::getSingletonInstance()->stop("imageDraw"); - - //draw red position cursor ---------------- - g.setColour(Colours::red); - g.drawLine(currentCursorXPos, 0, currentCursorXPos, sizeY, 1.0); - - //draw frequency and time axis ---------------- - PerformanceManager::getSingletonInstance()->start(T("drawAxis"), 2000); - g.drawImageAt(axisImage, 0, 0); - PerformanceManager::getSingletonInstance()->stop(T("drawAxis")); - - //[/UserPaint] -} - -void SpectronDrawer::resized() -{ - //[UserResized] Add your own custom resize handling here.. - //[/UserResized] -} - - - -//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... -void SpectronDrawer::timerCallback() -{ - stopTimer(); - repaint(); - startTimer(TIMER); -} - -void SpectronDrawer::onTransformationEvent(Transformation* value) { -//This method is automatically called, when there are new transformation results available, -//as far as it had been successfully been registered as a listener by "SpectronJuceMainUI" - int watchDog = 200; - while (value->isOutputAvailable()) { - appendSpectralImage(value); - watchDog--; - if (watchDog <= 0) { - //prevents endless loop - DBG("SpectronDrawer::onTransformationEvent watchDog canceled drawing!"); - watchDog = 200; - break; - } - } -} - -//This method is called when a parameter changes (listener) -void SpectronDrawer::valueTreePropertyChanged (ValueTree &treeWhosePropertyHasChanged, const Identifier &changedProperty) { - const ScopedLock myScopedLock (criticalSection); - - juce::String changedParameterName = treeWhosePropertyHasChanged.getType().toString(); - juce::var changedParameterValue = treeWhosePropertyHasChanged.getProperty(SpectronParameters::PROPERTY_VALUE); - - if (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_LOGFREQUENCY)) { - if (changedParameterValue.equals(SpectronParameters::PLOT_AXIS_LOGARITHMIC)) { - settings.logFrequency = true; - } else { - settings.logFrequency = false; - } - updateFrequencyAxisImage(); - } - if (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_LOGMAGNITUDE)) { - if (changedParameterValue.equals(SpectronParameters::PLOT_AXIS_LOGARITHMIC)) { - settings.logMagnitude = true; - } else { - settings.logMagnitude = false; - } - } - if (changedParameterName.equalsIgnoreCase(SpectronParameters::PARAMETER_COLORMODE)) { - renderingHelper.setColourGradient(COLOURGRADIENT->get(changedParameterValue)); - } - - updateTimeAxisImage(); -}; - -void SpectronDrawer::appendSpectralImage(Transformation* value) { -// const ScopedLock myScopedLock (criticalSection); - - if (!value) { - DBG("SpectronDrawer::appendSpectralImage(..) no value"); - return; - } - if (!value->getSpectralDataInfo()) { - DBG("SpectronDrawer::appendSpectralImage(..) no data info"); - return; - } - if (!value->getSpectralDataBuffer()) { - DBG("SpectronDrawer::appendSpectralImage(..) no data buffer"); - return; - } - - if (currentCursorXPos > (sizeX - 1)) { - currentCursorXPos = 0; - } - if (spectrumImage.isNull()) { - spectrumImage = Image(Image::RGB, sizeX, sizeY, true); - } - - renderingHelper.renderVerticalPoints( - value, - settings, - currentCursorXPos, - &spectrumImage - ); - currentCursorXPos++; -} - - -/* -This method draws the currently active frequency axis into the local member frequencyAxisImage, -which is drawn on top of the spectrum whithin the paint() method. -*/ -void SpectronDrawer::updateFrequencyAxisImage() { -//TODO better encapsulation - int maxWidthOfFrequencyAxis = 80; - - //clears the part of the axis image, where the frequency resolution is drawn at - Rectangle areaToClear(axisImage.getBounds().withRight(maxWidthOfFrequencyAxis)); - axisImage.clear(areaToClear, juce::Colours::transparentBlack); - - //get drawing context - Graphics g(axisImage); - - // --- gets max frequency - double maxSpectralFrequency = 22050; - Transformation* transformation = TransformationFactory::getSingletonInstance()->getCurrentTransformation(); - if ((transformation != 0) - && (transformation->getSpectralDataInfo())) { - maxSpectralFrequency = transformation->getSpectralDataInfo()->getMaxFrequency(); - } - - int axisLineLength = 10; - g.setColour(AXIS_COLOR); - juce::Font oldFont = g.getCurrentFont(); - - if (settings.logFrequency) { - double logSubDivisionsPerDecade[] = {log10(1.0), log10(2.0), log10(5.0), log10(7.0)}; - int numberOfSubDivisions = sizeof logSubDivisionsPerDecade / sizeof logSubDivisionsPerDecade[0]; - - double maxFrequencyLog = log10(maxSpectralFrequency); - double minFrequencyLog = 1.0; - - for (double logFreqDecade = 1; logFreqDecade <= 4; logFreqDecade++) { - for (int i = 0; i < (numberOfSubDivisions - 1); i++) { - double logFreq = logSubDivisionsPerDecade[i] + logFreqDecade; - if (logFreq > maxFrequencyLog) break; - - double pos_percent = (logFreq - minFrequencyLog) / (maxFrequencyLog - minFrequencyLog); - double ypos = (sizeY - 1) * (1.0f - pos_percent); - double freqDouble = pow(10, logFreq); - double freqCeil = ceil(freqDouble); - double freqFloor = floor(freqDouble); - int freq = ((freqCeil - freqDouble) > (freqDouble - freqFloor))? (int)freqFloor : (int)freqCeil; - - juce::String frequencyText(freq); - frequencyText+=" Hz"; - - if (i == 0) { - //full decade: full line length and text size - g.setFont(12, juce::Font::plain); - g.drawFittedText(frequencyText, - axisLineLength + 3, - ypos - 7, - maxWidthOfFrequencyAxis, - ypos + 10, - juce::Justification::topLeft, - 1); - g.drawLine(0, ypos, axisLineLength, ypos, 0.4f); - } else { - //subdivision: smaller font, smaller lines - g.setFont(9, juce::Font::plain); - g.drawFittedText( - frequencyText, - axisLineLength + 3, - ypos - 3, - maxWidthOfFrequencyAxis, - ypos + 10, - juce::Justification::topLeft, - 1); - g.drawLine(0, ypos, axisLineLength, ypos, 0.4f); - } - } - } - } else { - double frequencyLabels = 10; //TODO replace by array of constants? - double step = 1.0 / frequencyLabels; - - for (double pos_percent = step; pos_percent < frequencyLabels; pos_percent+=step) { - double ypos = getHeight() * (1.0f - pos_percent); - - juce::String frequencyText(maxSpectralFrequency * pos_percent); - frequencyText+=" Hz"; - - g.setFont(12, juce::Font::plain); - g.drawFittedText( - frequencyText, - axisLineLength + 3, - ypos - 7, - maxWidthOfFrequencyAxis, - ypos, - juce::Justification::topLeft, - 1); - g.drawLine(0, ypos, axisLineLength, ypos, 0.4f); - } - } - g.setFont(oldFont); -} - -void SpectronDrawer::updateTimeAxisImage() { - //TODO better encapsulation - int timeAxisWidth = 60; - int lineLength = 50; - double timeresolution = 0.0; - double xpos_start = sizeX - timeAxisWidth; - double ypos_start = getHeight() - 50; - double ypos_line = getHeight() - 30; - - //gets the time resolution - Transformation* transformation = TransformationFactory::getSingletonInstance()->getCurrentTransformation(); - if ((transformation) - && (transformation->getSpectralDataInfo())) { - timeresolution = transformation->getSpectralDataInfo()->getTimeResolutionMs(); - } - - //if effective timeresolution didn't change, the timeresolution-axis needn't to be redrawn - if (timeresolution == currentTimeResolution) return; - currentTimeResolution = timeresolution; - - //clears the part of the axis image, where the time resolution is drawn at - Rectangle areaToClear(axisImage.getBounds().withLeft(xpos_start).withTop(ypos_start)); - axisImage.clear(areaToClear, juce::Colours::transparentBlack); - - //get drawing context - Graphics g(axisImage); - - - double timeResultionForGivenLength = timeresolution * lineLength; - juce::String timeResolutionText; - if (timeResultionForGivenLength < 1.0) { - timeResolutionText = juce::String((int)timeResultionForGivenLength * 1000); - timeResolutionText+=" ns"; - } else if (timeResultionForGivenLength < 1000.0) { - timeResolutionText = juce::String((int)timeResultionForGivenLength); - timeResolutionText+=" ms"; - } else if (timeResultionForGivenLength > 1000.0) { - timeResolutionText = juce::String((int)(timeResultionForGivenLength / 1000.0)); - timeResolutionText+=" s"; - } - g.setColour(AXIS_COLOR); - g.drawFittedText(timeResolutionText,xpos_start + 7, ypos_start, 79, 49, juce::Justification::topLeft, true); - g.drawLine(xpos_start, ypos_line, xpos_start + lineLength, ypos_line, 0.5f); - g.drawLine(xpos_start, ypos_line - 5, xpos_start, ypos_line + 5, 0.5f); - g.drawLine(xpos_start + lineLength, ypos_line - 5, xpos_start + lineLength, ypos_line + 5, 0.5f); -} - -//[/MiscUserCode] - - -//============================================================================== -#if 0 -/* -- Jucer information section -- - - This is where the Jucer puts all of its metadata, so don't change anything in here! - -BEGIN_JUCER_METADATA - - - - - -END_JUCER_METADATA -*/ -#endif diff --git a/src/user interface/SpectronDrawer.h b/src/user interface/SpectronDrawer.h deleted file mode 100644 index 4edae1b9..00000000 --- a/src/user interface/SpectronDrawer.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - ============================================================================== - - This is an automatically generated file created by the Jucer! - - Creation date: 4 May 2011 11:36:36pm - - Be careful when adding custom code to these files, as only the code within - the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded - and re-saved. - - Jucer version: 1.12 - - ------------------------------------------------------------------------------ - - The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - - ============================================================================== -*/ - -#ifndef __JUCER_HEADER_SPECTRONDRAWER_SPECTRONDRAWER_742E0E1E__ -#define __JUCER_HEADER_SPECTRONDRAWER_SPECTRONDRAWER_742E0E1E__ - -//[Headers] -- You can add your own extra header files here -- -#include "..\..\libs\juce\JuceLibraryCode\JuceHeader.h" -#include "..\dsp\transformations\Transformation.h" -#include "..\utilities\RenderingHelper.h" -//[/Headers] - - - -//============================================================================== -/** - //[Comments] - An auto-generated component, created by the Jucer. - - Describe your class and how it works here! - //[/Comments] -*/ -class SpectronDrawer : public Component, - public TransformationListener, - public Timer, - public juce::ValueTree::Listener -{ -public: - //============================================================================== - SpectronDrawer (); - ~SpectronDrawer(); - - //============================================================================== - //[UserMethods] -- You can add your own custom methods in this section. - static enum Constants { - SIZE_X = 528, - TIMER = 20 - }; - - void timerCallback(); - void appendSpectralImage(Transformation* value); - void onTransformationEvent(Transformation* value); - void valueTreePropertyChanged (ValueTree &treeWhosePropertyHasChanged, const Identifier &property); - void valueTreeChildrenChanged (ValueTree &treeWhoseChildHasChanged) {}; - void valueTreeParentChanged (ValueTree &treeWhoseParentHasChanged) {}; - //============================================================================== - //[/UserMethods] - - void paint (Graphics& g); - void resized(); - - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - //[UserVariables] -- You can add your own custom variables in this section. - static const juce::Colour AXIS_COLOR; - - void updateFrequencyAxisImage(); - void updateTimeAxisImage(); - juce::Viewport* getParentViewPort() const {return static_cast (getParentComponent());}; - - int sizeX, sizeY; - double currentTimeResolution; - RenderingHelper::TAnalyzerSettings settings; - RenderingHelper renderingHelper; - long currentCursorXPos; - juce::Image spectrumImage; - juce::Image axisImage; - juce::CriticalSection criticalSection; - - //[/UserVariables] - - //============================================================================== - - - //============================================================================== - // (prevent copy constructor and operator= being generated..) - SpectronDrawer (const SpectronDrawer&); - const SpectronDrawer& operator= (const SpectronDrawer&); -}; - - -#endif // __JUCER_HEADER_SPECTRONDRAWER_SPECTRONDRAWER_742E0E1E__ diff --git a/src/user interface/SpectronMainUI.cpp b/src/user interface/SpectronMainUI.cpp deleted file mode 100644 index e21f152b..00000000 --- a/src/user interface/SpectronMainUI.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - ============================================================================== - ============================================================================== -*/ - -#include "SpectronMainUI.h" -#include "SpectronDrawer.h" - -//============================================================================== -SpectronMainUI::SpectronMainUI (SpectronAudioProcessor* ownerFilter) - : AudioProcessorEditor (ownerFilter) -{ - // set our component's initial size to be the last one that was stored in the filter's settings - setSize (ownerFilter->lastUIWidth, ownerFilter->lastUIHeight); - - //GUI, that holds all parameter-widgets and the spectralview - addAndMakeVisible (&spectralAnalyzerComponent); - - // set our component's initial size to be the last one that was stored in the filter's settings - setSize (ownerFilter->lastUIWidth, ownerFilter->lastUIHeight); -} - -SpectronMainUI::~SpectronMainUI() -{ -} - -//============================================================================== -void SpectronMainUI::paint (Graphics& g) -{ -} - -void SpectronMainUI::resized() -{ - spectralAnalyzerComponent.setBounds(0, 0, MAINGUI_SIZE_X, MAINGUI_SIZE_Y); - getProcessor()->lastUIWidth = getWidth(); - getProcessor()->lastUIHeight = getHeight(); -} \ No newline at end of file diff --git a/src/user interface/SpectronMainUI.h b/src/user interface/SpectronMainUI.h deleted file mode 100644 index 18438fc2..00000000 --- a/src/user interface/SpectronMainUI.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -/* - ============================================================================== - - This file was auto-generated by the Jucer! - - It contains the basic startup code for a Juce application. - - ============================================================================== -*/ - -#ifndef __PLUGINEDITOR_H_4ACCBAA__ -#define __PLUGINEDITOR_H_4ACCBAA__ - -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" -#include "../config/JucePluginCharacteristics.h" -#include "../plugin/SpectronAudioProcessor.h" -#include "SpectronAnalyzerComponent.h" -#include "../dsp/transformations/TransformationFactory.h" - -//============================================================================== -/** This is the editor component that our filter will display. -*/ -class SpectronMainUI : public AudioProcessorEditor -{ -public: - static enum Constants { - MAINGUI_SIZE_X = 800, - MAINGUI_SIZE_Y = 360 - }; - SpectronMainUI (SpectronAudioProcessor* ownerFilter); - ~SpectronMainUI(); - - void paint (Graphics& g); - void resized(); - -private: - SpectronAnalyzerComponent spectralAnalyzerComponent; - SpectronAudioProcessor* getProcessor() const {return static_cast (getAudioProcessor());}; -}; - -#endif // __PLUGINEDITOR_H_4ACCBAA__ diff --git a/src/utilities/PerformanceLogger.h b/src/utilities/PerformanceLogger.h new file mode 100644 index 00000000..577c2d14 --- /dev/null +++ b/src/utilities/PerformanceLogger.h @@ -0,0 +1,142 @@ +// This implementation is inspired by these references: +// - The Cherno - VISUAL BENCHMARKING in C++ - https://www.youtube.com/watch?v=xlAH4dbMVnU&t=197s +// - TheCherno/Instrumentor.h - https://gist.github.com/TheCherno/31f135eea6ee729ab5f26a6908eb3a5e +// - David Churchill - https://pastebin.com/qw5Neq4U +// - Mattias Aronsson - https://gist.github.com/maronsson/073840bf94e4d6df94c5f294a6e96e03 +// +// Use Chrome browser to view the results of the JSON file using the following URI: chrome://tracing +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOG_PERFORMANCE +#define LOG_PERFORMANCE_BEGIN(name) PerformanceLogger::getInstance().beginSession(name) +#define LOG_PERFORMANCE_END() PerformanceLogger::getInstance().endSession() +#define LOG_PERFORMANCE_OF_SCOPE(name) PerformanceLogTimer timer##__LINE__(name) +#else +#define LOG_PERFORMANCE_BEGIN(name) +#define LOG_PERFORMANCE_END() +#define LOG_PERFORMANCE_OF_SCOPE(name) +#endif + +struct PerformanceLogEntry { + std::string name; + long long start; + long long end; + std::size_t threadID; +}; + +class PerformanceLogger { +private: + std::string sessionName = "None"; + std::ofstream outputStream; + int profileCount = 0; + std::mutex mutex; + bool activeSession = false; + + PerformanceLogger() = default; + PerformanceLogger(const PerformanceLogger &) = delete; // No copy constructor + PerformanceLogger(PerformanceLogger &&) = delete; // No move constructor + PerformanceLogger &operator=(const PerformanceLogger &) = delete;// No copy assignment + PerformanceLogger &operator=(PerformanceLogger &&) = delete; // No move assignment + +public: + ~PerformanceLogger() { + endSession(); + } + + void beginSession(const std::string &name, const std::string &filepath = "performancelog.json") { + if (activeSession) { + endSession(); + } + activeSession = true; + outputStream.open(filepath); + writeHeader(); + sessionName = name; + } + + void endSession() { + if (!activeSession) { + return; + } + activeSession = false; + writeFooter(); + profileCount = 0; + } + + void writeProfile(const PerformanceLogEntry &entry) { + std::scoped_lock lock(mutex); + + if (profileCount++ > 0) { + outputStream << ","; + } + std::string name = entry.name; + std::replace(name.begin(), name.end(), '"', '\''); + + outputStream << "{"; + outputStream << "\"cat\":\"function\","; + outputStream << "\"dur\":" << (entry.end - entry.start) << ','; + outputStream << "\"name\":\"" << name << "\","; + outputStream << "\"ph\":\"X\","; + outputStream << "\"pid\":0,"; + outputStream << "\"tid\":" << entry.threadID << ","; + outputStream << "\"ts\":" << entry.start; + outputStream << "}"; + } + + void writeHeader() { + outputStream << "{\"otherData\": {},\"traceEvents\":["; + } + + void writeFooter() { + outputStream << "]}"; + } + + static PerformanceLogger &getInstance() { + static PerformanceLogger instance; + return instance; + } +}; + +class PerformanceLogTimer { +private: + const char *name; + std::chrono::time_point startTimestamp; + bool stopped = false; + + PerformanceLogTimer() = default; + PerformanceLogTimer(const PerformanceLogTimer &) = delete; // No copy constructor + PerformanceLogTimer(PerformanceLogTimer &&) = delete; // No move constructor + PerformanceLogTimer &operator=(const PerformanceLogTimer &) = delete;// No copy assignment + PerformanceLogTimer &operator=(PerformanceLogTimer &&) = delete; // No move assignment + +public: + explicit PerformanceLogTimer(const char *newName) + : name(newName) { + startTimestamp = std::chrono::high_resolution_clock::now(); + } + + ~PerformanceLogTimer() { + if (!stopped) { + stop(); + } + } + + void stop() { + auto endTimestamp = std::chrono::high_resolution_clock::now(); + + auto start = std::chrono::time_point_cast(startTimestamp).time_since_epoch().count(); + auto end = std::chrono::time_point_cast(endTimestamp).time_since_epoch().count(); + auto threadID = std::hash{}(std::this_thread::get_id()); + + PerformanceLogger::getInstance().writeProfile({name, start, end, threadID}); + + stopped = true; + } +}; \ No newline at end of file diff --git a/src/utilities/PerformanceManager.cpp b/src/utilities/PerformanceManager.cpp deleted file mode 100644 index d89e1b9a..00000000 --- a/src/utilities/PerformanceManager.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "PerformanceManager.h" - -// Singleton instance variable (only one instance of this class) -PerformanceManager* PerformanceManager::singletonInstance = 0; - -PerformanceManager* PerformanceManager::getSingletonInstance() { -// Method to get the single instance of this class (Singleton) - if (PerformanceManager::singletonInstance == 0) { - PerformanceManager::singletonInstance = new PerformanceManager(); - } - return PerformanceManager::singletonInstance; -} - -void PerformanceManager::destruct() { - if (!singletonInstance) return; - singletonInstance->deletePerformanceManager(); - delete(singletonInstance); - singletonInstance = 0; -} - -void PerformanceManager::start(juce::String name, int runsPerPrintout, const juce::File &loggingFile) { - #if _PERFORMACETEST - TIterator iterator = map.find(name); - #if _LOGTOFILE - if (iterator == map.end()) { - if (loggingFile == juce::File::nonexistent) { - add(name, runsPerPrintout, juce::File("c:/temp/speclet.log")); - } else { - add(name, runsPerPrintout, loggingFile); - } - } - #else - if (iterator == map.end()) add(name, runsPerPrintout, loggingFile); - #endif - get(name)->start(); - #endif -} - -void PerformanceManager::stop(juce::String name) { - #if _PERFORMACETEST - TIterator iterator = map.find(name); - if (iterator == map.end()) { - DBG(T("PerformanceManager::stop(") + name + T("): PerformanceCounter not found")); - return; - } - get(name)->stop(); - #endif -} - -void PerformanceManager::add(juce::String name, int runsPerPrintout, const juce::File &loggingFile) { - TIterator iterator = map.find(name); - if (iterator != map.end()) return; - - PerformanceCounter* performanceCounter = new PerformanceCounter(name, runsPerPrintout, loggingFile); - TKeyValue keyValue(name, performanceCounter); - map.insert(keyValue); -} - -juce::PerformanceCounter* PerformanceManager::get(juce::String name) { - TIterator iterator = map.find(name); - return iterator->second; -} - -void PerformanceManager::deletePerformanceManager() { - TIterator iterator = map.begin(); - - while (iterator != map.end()) { - delete(iterator->second); - iterator++; - } -} \ No newline at end of file diff --git a/src/utilities/PerformanceManager.h b/src/utilities/PerformanceManager.h deleted file mode 100644 index 991a7e6f..00000000 --- a/src/utilities/PerformanceManager.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - ============================================================================== - This file is part of the VST spectrum analyzer plugin "speclet" (working title) - Copyright 2011 by Johannes Troppacher - ------------------------------------------------------------------------------ - This file may use parts of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-6 by Raw Material Software ltd. - ------------------------------------------------------------------------------ - This file may use parts of the fftw library - Copyright 2003 Matteo Frigo, Copyright 2003 Massachusetts Institute of Technology - ------------------------------------------------------------------------------ - This file may use parts of the wave++ library - Copyright 1999 by S. E. Ferrando, L. A. Kolasa and N. Kovacevic - ============================================================================== -*/ -#pragma once - -#if _DEBUG - #define _PERFORMACETEST 1 -#endif -#if _LOGTOFILE - #define LOG(logtext) {JUCE_NAMESPACE::String tempLogBuf; tempLogBuf << logtext; JUCE_NAMESPACE::Logger::writeToLog(tempLogBuf);} -#else - #define LOG(logtext) -#endif - -#include "..\..\libs\juce\JuceLibraryCode\JuceHeader.h" -#include - -/* -The class PerformanceManager is a global container for juce::PerformanceCounter-objects. -It's ment to be an easy way to start and stop performance measures without bothering about -creation and scope of the PerformanceCounter. -Internally, every new PerformanceCounter is added to a std::map. -*/ -class PerformanceManager { -public: - static PerformanceManager* getSingletonInstance(); - void destruct(); - void start(juce::String name, int runsPerPrintout = 100, const juce::File &loggingFile = juce::File::nonexistent); - void stop(juce::String name); - -private: - typedef std::map TMap; - typedef std::map ::const_iterator TIterator; - typedef std::pair TKeyValue; - - static PerformanceManager* singletonInstance; - TMap map; - - PerformanceManager(void) {}; - ~PerformanceManager(void) {}; - PerformanceManager(const PerformanceManager&); - - void add(juce::String name, int runsPerPrintout = 100, const juce::File &loggingFile = juce::File::nonexistent); - juce::PerformanceCounter* get(juce::String name); - - void deletePerformanceManager(); -}; \ No newline at end of file diff --git a/src/utilities/RenderingHelper.cpp b/src/utilities/RenderingHelper.cpp index cc3b4a30..d0d71cfb 100644 --- a/src/utilities/RenderingHelper.cpp +++ b/src/utilities/RenderingHelper.cpp @@ -1,151 +1,136 @@ #include "RenderingHelper.h" -#include "../utilities/PerformanceManager.h" +#include "../ui/ColourGradients.h" +#include "PerformanceLogger.h" #include -#include "../../source/user interface/ColourGradients.h" -using namespace std; - -RenderingHelper::RenderingHelper(void) { - colourGradient = GRADIENT_BLUE; -} - -RenderingHelper::~RenderingHelper(void) { +RenderingHelper::RenderingHelper(const juce::ColourGradient &initialColourGradient) + : colourGradient(initialColourGradient) { } //method for rendering one column of spectral data void RenderingHelper::renderVerticalPoints( - Transformation* transformation, - TAnalyzerSettings settings, - long currentXPos, - juce::Image* spectralImage -) { - PerformanceManager::getSingletonInstance()->start("renderVerticalPoints"); - - // --- inputdata check - assert(transformation); - assert(spectralImage); - SpectralDataBuffer* buffer = transformation->getSpectralDataBuffer(); - if (!buffer) { - DBG("RenderingHelper::renderVerticalPoints(..image): buffer is null!!"); - return; - } - if (buffer->size() <= 0) { - DBG("RenderingHelper::renderVerticalPoints(..image): spectral data empty!!"); - return; - } - - // --- get spectrum from buffer - SpectralDataBuffer::ItemType spectrum; - transformation->getNextSpectrum(&spectrum); - if (spectrum.empty()) { - DBG("RenderingHelper::renderVerticalPoints: spectrum empty!"); - return; - } - - // --- get statistics of spectrum (min, max, ...) - SpectralDataBuffer::ItemStatisticsType statistics = transformation->getSpectrumStatistics(&spectrum); - - // --- get info about spectral data (frequency/time-resolution...) - SpectralDataInfo* spectralDataInfo = transformation->getSpectralDataInfo(); - if (!spectralDataInfo) { - DBG("RenderingHelper::renderVerticalPoints: spectralDataInfo null!"); - return; - } - - if (spectrum.size() != spectralDataInfo->getFrequencyResolution()) { - DBG ("RenderingHelper::renderVerticalPoints: spectrum.size()=" + - juce::String(spectrum.size()) + - "!= fres=" + juce::String(spectralDataInfo->getFrequencyResolution())); - return; - } - - // --- define and init other temporary variables - int height = spectralImage->getHeight(); - int spectralLineIndexOfPixel = 0; - double amplitudeColorIndex = 0.0; - double magnitude = 0.0; - - for (int i = 0; i <= height; i++) { - spectralLineIndexOfPixel = pixelToIndex(i, height, spectralDataInfo, settings.logFrequency); - magnitude = spectrum[spectralLineIndexOfPixel]; - amplitudeColorIndex = getColorAmount(magnitude, statistics.min, statistics.max, settings.logMagnitude); - juce::Colour colour = colourGradient.getColourAtPosition(amplitudeColorIndex); - spectralImage->setPixelAt(currentXPos, (height - i), colour); - } - - PerformanceManager::getSingletonInstance()->stop("renderVerticalPoints"); -} - -double RenderingHelper::getColorAmount( - double magnitude, - double minMagnitude, - double maxMagnitude, - bool logMagnitude -){ - if (logMagnitude) { - maxMagnitude = 20 * log(maxMagnitude); - minMagnitude = 20 * log(minMagnitude); - magnitude = 20 * log(magnitude); - } - double rangeMagnitude = maxMagnitude - minMagnitude; - double amplitudeColorIndex = (magnitude - minMagnitude) / rangeMagnitude; - - amplitudeColorIndex = assureBorders("amplitudeColorIndex", amplitudeColorIndex, 0.0, 1.0); - return amplitudeColorIndex; + TransformationResult *transformationResult, + TAnalyzerSettings settings, + int currentXPos, + juce::Image *spectralImage) const { + + LOG_PERFORMANCE_OF_SCOPE("RenderingHelper renderVerticalPoints"); + + // --- inputdata check + assert(transformationResult); + assert(spectralImage); + SpectralDataBuffer *buffer = transformationResult->getSpectralDataBuffer(); + if (buffer == nullptr) { + DBG("RenderingHelper::renderVerticalPoints(..image): buffer is null!!"); + return; + } + if (buffer->size() <= 0) { + DBG("RenderingHelper::renderVerticalPoints(..image): spectral data empty!!"); + return; + } + + // --- get spectrum from buffer + SpectralDataBuffer::ItemType spectrum; + transformationResult->getNextSpectrum(&spectrum); + if (spectrum.empty()) { + DBG("RenderingHelper::renderVerticalPoints: spectrum empty!"); + return; + } + + // --- get statistics of spectrum (min, max, ...) + auto statistics = SpectralDataBuffer::ItemStatisticsType(spectrum); + + // --- get info about spectral data (frequency/time-resolution...) + SpectralDataInfo spectralDataInfo = transformationResult->getSpectralDataInfo(); + + if (spectrum.size() != spectralDataInfo.getFrequencyResolution()) { + DBG("RenderingHelper::renderVerticalPoints: spectrum.size()=" + + juce::String(spectrum.size()) + + "!= frequency resolution=" + juce::String(spectralDataInfo.getFrequencyResolution())); + return; + } + + // --- define and init other temporary variables + auto height = spectralImage->getHeight(); + unsigned long spectralLineIndexOfPixel = 0; + auto amplitudeColorIndex = 0.0; + auto magnitude = 0.0; + + for (auto i = 0; i <= height; i++) { + spectralLineIndexOfPixel = pixelToIndex(i, height, spectralDataInfo, settings.logFrequency); + magnitude = spectrum[spectralLineIndexOfPixel]; + amplitudeColorIndex = getColorAmount(magnitude, statistics.min, statistics.max, settings.logMagnitude); + juce::Colour colour = colourGradient.getColourAtPosition(amplitudeColorIndex); + spectralImage->setPixelAt(currentXPos, (height - i), colour); + } } -long RenderingHelper::pixelToIndex ( - int pixel, - int height, - SpectralDataInfo* spectralDataInfo, - bool logFrequency -) { - if (pixel <= 0) return 0; //DC in spectrum always on index = 0 - assert(height > 0); - assert(pixel <= height); - //assert(spectralDataInfo); - - double frequencyResolution = spectralDataInfo->getFrequencyResolution(); - double percentOfSpectrum = pixel / (double)height; - double percentOfSpectrumPerIndex = spectralDataInfo->getFrequencyPartitionSize(); - percentOfSpectrumPerIndex = assureBorders("percentOfSpectrumPerIndex", percentOfSpectrumPerIndex, 0.0, 1.0); - - if (logFrequency) { - double frequencyMax = spectralDataInfo->getSamplingFrequency() / 2.0; - double frequencyMaxLog = log10(frequencyMax); - double frequencyMinLog = 1.0; - double frequencyRangeLog= frequencyMaxLog - frequencyMinLog; - - percentOfSpectrum = pow(10, (frequencyRangeLog * percentOfSpectrum) + frequencyMinLog) / frequencyMax; - } - percentOfSpectrum = assureBorders("percentOfSpectrum", percentOfSpectrum, 0.0, 1.0); - - int index = roundToInt(percentOfSpectrum / percentOfSpectrumPerIndex); - if (index > (int)(frequencyResolution - 1)) index = (int)(frequencyResolution - 1); - index = (int)assureBorders("index", (double)index, 0.0, (double)(frequencyResolution - 1)); - - return index; +auto RenderingHelper::getColorAmount( + double magnitude, + double minMagnitude, + double maxMagnitude, + bool logMagnitude) const -> double { + if (logMagnitude) { + maxMagnitude = 20 * log(maxMagnitude); + minMagnitude = 20 * log(minMagnitude); + magnitude = 20 * log(magnitude); + } + double rangeMagnitude = maxMagnitude - minMagnitude; + double amplitudeColorIndex = (magnitude - minMagnitude) / rangeMagnitude; + + amplitudeColorIndex = assureBorders("amplitudeColorIndex", amplitudeColorIndex, 0.0, 1.0); + return amplitudeColorIndex; } -double RenderingHelper::assureBorders(const juce::String ¶mName, double value, double min, double max) { - if (value < min) { - //DBG(T("RenderingHelper::assureBorders: <") - // + paramName + T(">") - // + juce::String(value) + T(" is smaller than ") - // + juce::String(min) + T(", so it's set to min")); - return min; - } - if (value > max) { - //DBG(T("RenderingHelper::assureBorders: <") - // + paramName + T(">") - // + juce::String(value)+ T(" is greater than ") - // + juce::String(max) + T(", so it's set to max.")); - return max; - } - return value; +auto RenderingHelper::pixelToIndex( + int pixel, + int height, + const SpectralDataInfo &spectralDataInfo, + bool logFrequency) const -> unsigned long { + if (pixel <= 0) { + return 0;//DC in spectrum always on index = 0 + } + assert(height > 0); + assert(pixel <= height); + + auto frequencyResolution = spectralDataInfo.getFrequencyResolution(); + auto percentOfSpectrum = pixel / static_cast(height); + auto percentOfSpectrumPerIndex = spectralDataInfo.getFrequencyPartitionSize(); + percentOfSpectrumPerIndex = assureBorders("percentOfSpectrumPerIndex", percentOfSpectrumPerIndex, 0.0, 1.0); + + if (logFrequency) { + auto frequencyMax = spectralDataInfo.getSamplingFrequency() / 2.0; + auto frequencyMaxLog = log10(frequencyMax); + auto frequencyMinLog = 1.0; + auto frequencyRangeLog = frequencyMaxLog - frequencyMinLog; + + percentOfSpectrum = pow(10, (frequencyRangeLog * percentOfSpectrum) + frequencyMinLog) / frequencyMax; + } + percentOfSpectrum = assureBorders("percentOfSpectrum", percentOfSpectrum, 0.0, 1.0); + + auto index = juce::roundToInt(percentOfSpectrum / percentOfSpectrumPerIndex); + if (index > static_cast(frequencyResolution - 1)) { + index = static_cast(frequencyResolution - 1); + } + index = static_cast(assureBorders("index", static_cast(index), 0.0, (static_cast(frequencyResolution) - 1.0))); + assert(index >= 0); + return static_cast(index); } -int RenderingHelper::roundToInt(double d) -{ - return d<0?d-.5:d+.5; +auto RenderingHelper::assureBorders(const juce::String & /*paramName*/, double value, double min, double max) const -> double { + if (value < min) { + //DBG(T("RenderingHelper::assureBorders: <") + // + paramName + T(">") + // + juce::String(value) + T(" is smaller than ") + // + juce::String(min) + T(", so it's set to min")); + return min; + } + if (value > max) { + //DBG(T("RenderingHelper::assureBorders: <") + // + paramName + T(">") + // + juce::String(value)+ T(" is greater than ") + // + juce::String(max) + T(", so it's set to max.")); + return max; + } + return value; } \ No newline at end of file diff --git a/src/utilities/RenderingHelper.h b/src/utilities/RenderingHelper.h index 9d0762cb..d4a65802 100644 --- a/src/utilities/RenderingHelper.h +++ b/src/utilities/RenderingHelper.h @@ -16,50 +16,45 @@ #pragma once #include "../data/SpectralDataBuffer.h" #include "../dsp/transformations/Transformation.h" -#include "../../libs/juce/JuceLibraryCode/JuceHeader.h" +#include "juce_audio_utils/juce_audio_utils.h" #include class RenderingHelper { public: - typedef std::vector TVerticalColorIndizes; - typedef struct { - bool logFrequency; //false=lin, true=log - bool logMagnitude; //false=lin, true=log - } TAnalyzerSettings; + using TVerticalColorIndices = std::vector; + using TAnalyzerSettings = struct { + bool logFrequency;//false=lin, true=log + bool logMagnitude;//false=lin, true=log + }; - RenderingHelper(void); - ~RenderingHelper(void); + explicit RenderingHelper(const juce::ColourGradient &initialColourGradient); + ~RenderingHelper() = default; - void setColourGradient(juce::ColourGradient value) {colourGradient = value;}; + void setColourGradient(const juce::ColourGradient& value) { colourGradient = value; } - void renderVerticalPoints( - Transformation* transformation, - TAnalyzerSettings settings, - long currentXPos, - juce::Image* spectralImage - ); + void renderVerticalPoints( + TransformationResult *transformationResult, + TAnalyzerSettings settings, + int currentXPos, + juce::Image *spectralImage) const; -private: - double getColorAmount( - double magnitude, - double minMagnitude, - double maxMagnitude, - bool logMagnitude - ); - double assureBorders(const juce::String ¶mName, double value, double min, double max); - int roundToInt(double d); - -public: -//Normally, this method would be defined as "privat". -//But since it is directly adressed in a unit-test, it remains -//(until the test gets deprecated) "public" - long RenderingHelper::pixelToIndex ( - int pixel, - int height, - SpectralDataInfo* spectralDataInfo, - bool logFrequency); + //Normally, this method would be defined as "private". + //But since it is directly addressed in a unit-test, it remains + //(until the test gets deprecated) "public" + auto pixelToIndex( + int pixel, + int height, + const SpectralDataInfo &spectralDataInfo, + bool logFrequency) const -> unsigned long; private: - std::vector mColorTable; //color table - juce::ColourGradient colourGradient; //color gradient object + auto getColorAmount( + double magnitude, + double minMagnitude, + double maxMagnitude, + bool logMagnitude) const -> double; + auto assureBorders(const juce::String ¶mName, double value, double min, double max) const -> double; + + std::vector mColorTable;//color table + juce::ColourGradient colourGradient; //color gradient object }; \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..11a7d01f --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,84 @@ +# Test Build Configuration + +# Sets the test project name +# Uses the main project name defined in the main CMakeLists.txt file +set(TEST_PROJECT_NAME "${PROJECT_NAME}Tests") + +# Import test dependencies +# Needs CPM which comes from "Environment.cmake" in the root directory of this repository +CPMAddPackage("gh:catchorg/Catch2@3.0.0-preview5") + +# Enable testing with Catch2 +enable_testing() +add_test("${TEST_PROJECT_NAME}" "${TEST_PROJECT_NAME}") + +# find test sources +# GLOB_RECURSE is not recommended but used here for simplicity: https://cmake.org/cmake/help/latest/command/file.html?highlight=CONFIGURE_DEPENDS#filesystem +FILE(GLOB_RECURSE test_sources CONFIGURE_DEPENDS "*.h" "*Test.cpp") + +# log found sources when log message level is verbose (use set(CMAKE_MESSAGE_LOG_LEVEL VERBOSE) in CMakeLists.txt) +message(VERBOSE "${TEST_PROJECT_NAME} sources found:") +foreach(source ${test_sources}) + message(VERBOSE "'${source}'") +endforeach() + +add_executable("${TEST_PROJECT_NAME}" ${test_sources}) + +# use the same C++ standard +get_target_property (MAIN_TARGET_CXX_STANDARD ${PROJECT_NAME} CXX_STANDARD) +if (NOT MAIN_TARGET_CXX_STANDARD STREQUAL "MAIN_TARGET_CXX_STANDARD-NOTFOUND") + set_target_properties ("${TEST_PROJECT_NAME}" PROPERTIES CXX_STANDARD ${MAIN_TARGET_CXX_STANDARD}) + message (VERBOSE "${TEST_PROJECT_NAME}: Language Standard set to ${MAIN_TARGET_CXX_STANDARD}") +endif() + +# Use the same include directories as the main target +get_target_property (MAIN_TARGET_INCLUDE_DIRECTORIES ${PROJECT_NAME} INCLUDE_DIRECTORIES) +if (NOT MAIN_TARGET_INCLUDE_DIRECTORIES STREQUAL "MAIN_TARGET_INCLUDE_DIRECTORIES-NOTFOUND") + target_include_directories ("${TEST_PROJECT_NAME}" PRIVATE ${MAIN_TARGET_INCLUDE_DIRECTORIES}) + message (VERBOSE "${TEST_PROJECT_NAME}: Using include directories from ${PROJECT_NAME}: ${MAIN_TARGET_INCLUDE_DIRECTORIES}") +endif() + +# Use the same compile options as the main target and disable optimization +set(TEST_PROJECT_COMPILE_OPTIONS "-O0") +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(TEST_PROJECT_COMPILE_OPTIONS "/Od") +endif() +get_target_property (MAIN_TARGET_COMPILE_OPTIONS ${PROJECT_NAME} COMPILE_OPTIONS) +if (NOT MAIN_TARGET_COMPILE_OPTIONS STREQUAL "MAIN_TARGET_COMPILE_OPTIONS-NOTFOUND") + set(TEST_PROJECT_COMPILE_OPTIONS "${MAIN_TARGET_COMPILE_OPTIONS};${TEST_PROJECT_COMPILE_OPTIONS}") +endif() +message (VERBOSE "${TEST_PROJECT_NAME}: Using compile options from ${PROJECT_NAME}: ${TEST_PROJECT_COMPILE_OPTIONS}") +target_compile_options ("${TEST_PROJECT_NAME}" PRIVATE ${TEST_PROJECT_COMPILE_OPTIONS}) + +# Use the same compile definitions as the main target +get_target_property (MAIN_TARGET_COMPILE_DEFINITIONS ${PROJECT_NAME} COMPILE_DEFINITIONS) +if (NOT MAIN_TARGET_COMPILE_DEFINITIONS STREQUAL MAIN_TARGET_COMPILE_DEFINITIONS-NOTFOUND) + target_compile_definitions ("${TEST_PROJECT_NAME}" PRIVATE ${MAIN_TARGET_COMPILE_DEFINITIONS}) + message (VERBOSE "${TEST_PROJECT_NAME}: Using compiler definitions from ${PROJECT_NAME}: ${MAIN_TARGET_COMPILE_DEFINITIONS}") +endif() + +# Use the same link options as the main target and add the verbose output option +set(TEST_PROJECT_LINK_OPTIONS "-v") +get_target_property (MAIN_TARGET_LINK_OPTIONS ${PROJECT_NAME} LINK_OPTIONS) +if (NOT MAIN_TARGET_LINK_OPTIONS STREQUAL MAIN_TARGET_LINK_OPTIONS-NOTFOUND) + set(TEST_PROJECT_LINK_OPTIONS "${MAIN_TARGET_LINK_OPTIONS}LINKER:,{TEST_PROJECT_LINK_OPTIONS}") +endif() +target_link_options ("${TEST_PROJECT_NAME}" PRIVATE "LINKER:${TEST_PROJECT_LINK_OPTIONS}") +message (VERBOSE "${TEST_PROJECT_NAME}: Using link options including those of ${PROJECT_NAME}: ${TEST_PROJECT_LINK_OPTIONS}") + +# Additionally use the same link libraries as the main target +get_target_property (MAIN_TARGET_LINK_LIBRARIES ${PROJECT_NAME} LINK_LIBRARIES) +if (NOT MAIN_TARGET_LINK_LIBRARIES STREQUAL "MAIN_TARGET_LINK_LIBRARIES-NOTFOUND") + message (VERBOSE "${TEST_PROJECT_NAME}: Using link libraries from ${PROJECT_NAME}: ${MAIN_TARGET_LINK_LIBRARIES}") +elseif() + message (FATAL_ERROR "${TEST_PROJECT_NAME}: Link Libraries of main target not found.") +endif() + +# Link catch with build-in main function and the main project link libraries +target_link_libraries("${TEST_PROJECT_NAME}" PRIVATE Catch2::Catch2WithMain "${PROJECT_NAME}" ${MAIN_TARGET_LINK_LIBRARIES}) + +# Register tests to CMake's CTest using the integration provided by Catch2: +# https://github.com/catchorg/Catch2/blob/devel/docs/cmake-integration.md +include(CTest) +include(${Catch2_SOURCE_DIR}/extras/Catch.cmake) +catch_discover_tests("${TEST_PROJECT_NAME}") \ No newline at end of file diff --git a/test/CommonTestUtils.cpp b/test/CommonTestUtils.cpp deleted file mode 100644 index 37fff1e9..00000000 --- a/test/CommonTestUtils.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "CommonTestUtils.h" - -TCommonSettings setCommonSettings () { - return setCommonSettings (44100, 4096, 1); -} - -TCommonSettings setCommonSettings (double samplingRate, double resolution, short channels) { - TCommonSettings commonSettings; - commonSettings.samplingRate = samplingRate; - commonSettings.samplingPeriod = 1.0 / samplingRate; - commonSettings.frequencyResolutionInHz = (double)samplingRate/(double)resolution; - commonSettings.resolution = resolution; - commonSettings.channels = channels; - return commonSettings; -} - -void printCommonSettings (TCommonSettings settings) { - printf("commmon settings: resolution=%5d, channels=%2d, samplingrate=%6.0lf Hz, samplingPeriod=%0.8lf ms, deltaF_FFT=%6.2lf Hz \n", - settings.resolution, - settings.channels, - settings.samplingRate, - settings.samplingPeriod, - settings.frequencyResolutionInHz - ); -} - diff --git a/test/CommonTestUtils.h b/test/CommonTestUtils.h deleted file mode 100644 index 796097b5..00000000 --- a/test/CommonTestUtils.h +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include -#include -#include "WindowFunctions.h" -#include "TransformationFactory.h" -#include "PerformanceManager.h" -#include "../user interface/ColourGradients.h" -#include "SpectralDataBuffer.h" -#include "SpectralDataInfo.h" -#include "..\plugin\SpectronParameters.h" -//#include - -#define WINFUNC WindowFunctionsFactory::getSingletonInstance() -#define TRANSFORM_FACTORY TransformationFactory::getSingletonInstance() -#define WAVELET AbstractWaveletTransformation -using namespace std; - -typedef struct { - double samplingRate; - double samplingPeriod; - double frequencyResolutionInHz; - unsigned int resolution; - short channels; -} TCommonSettings; - -TCommonSettings setCommonSettings (); -TCommonSettings setCommonSettings (double samplingRate, double resolution, short channels); -void printCommonSettings (TCommonSettings settings); diff --git a/test/Main.cpp b/test/Main.cpp deleted file mode 100644 index 19dedc42..00000000 --- a/test/Main.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "TestCases.h" - -int _tmain(int argc, _TCHAR* argv[]) -{ - //Initialisation - int returnCode = 0; - TCommonSettings settings = setCommonSettings(); - - //Test 1: WindowFunctions - printf("test_WindowFunctionsFactory startet...\n"); - returnCode = test_WindowFunctionsFactory(settings); - assert(returnCode == 0); - printf("test_WindowFunctionsFactory endet ...\n"); - fflush(stdout); - fflush(stdin); - - //Test 2: FourierTransormation - printf("test_FourierTransormation(new) startet...\n"); - returnCode = test_FourierTransormation(settings); - assert(returnCode == 0); - printf("test_FourierTransormation(new) endet ...\n"); - fflush(stdout); - fflush(stdin); - - //Test 3: WaveletTransormation - printf("test_WaveletTransormation(new) startet...\n"); - returnCode = test_WaveletTransormation(settings); - assert(returnCode == 0); - printf("test_WaveletTransormation(new) endet ...\n"); - fflush(stdout); - fflush(stdin); - - //Test 4: RenderingHelper - printf("test_RenderingHelper startet...\n"); - returnCode = test_RenderingHelper(settings); - assert(returnCode == 0); - printf("test_RenderingHelper endet ...\n"); - fflush(stdout); - fflush(stdin); - - //Test 5: test_DWPT_BestBasis - printf("test_DWPT_BestBasis startet...\n"); - returnCode = test_DWPT_BestBasis(settings); - assert(returnCode == 0); - printf("test_DWPT_BestBasis endet ...\n"); - fflush(stdout); - fflush(stdin); - - ////Test 6: FourierPerformance - //printf("test_FourierPerformance startet...\n"); - //returnCode = test_FourierPerformance(settings); - //assert(returnCode == 0); - //printf("test_FourierPerformance endet ...\n"); - //fflush(stdout); - //fflush(stdin); - - //tear down - TransformationFactory::getSingletonInstance()->destruct(); - WindowFunctionsFactory::getSingletonInstance()->destruct(); - PerformanceManager::getSingletonInstance()->destruct(); - ColourGradients::getSingletonInstance()->destruct(); - SpectronParameters::getSingletonInstance()->destruct(); - - return 0; -} \ No newline at end of file diff --git a/test/PluginBasicsTest.cpp b/test/PluginBasicsTest.cpp new file mode 100644 index 00000000..f47c6414 --- /dev/null +++ b/test/PluginBasicsTest.cpp @@ -0,0 +1,10 @@ +#include "../src/SpecletPluginProcessor.h" + +#include +#include + +TEST_CASE("Plugin instance name", "[name]") { + auto gui = juce::ScopedJuceInitialiser_GUI{}; + SpecletAudioProcessor testPlugin; + CHECK_THAT(testPlugin.getName().toStdString(), Catch::Matchers::StartsWith("Speclet")); +} \ No newline at end of file diff --git a/test/RenderingHelperTest.cpp b/test/RenderingHelperTest.cpp new file mode 100644 index 00000000..cf74f711 --- /dev/null +++ b/test/RenderingHelperTest.cpp @@ -0,0 +1,90 @@ +#include "../src/utilities/RenderingHelper.h" +#include "../src/ui/ColourGradients.h" + +#include + +SCENARIO("Rendering Helper") { + GIVEN("pixel to index") { + RenderingHelper renderingHelper(ColourGradients::BLUE); + SpectralDataInfo info(44100, 4096, 4096); + const auto windowHeight = 100; + const bool linearFrequency = false; + const bool logarithmicFrequency = true; + + WHEN("pixel value isn't within the valid range") { + THEN("index is zero for pixel = 0") { + auto index = renderingHelper.pixelToIndex(0, windowHeight, info, linearFrequency); + REQUIRE(index == 0); + } + THEN("index is zero for pixel < 0") { + auto index = renderingHelper.pixelToIndex(-1, windowHeight, info, linearFrequency); + REQUIRE(index == 0); + } + } + WHEN("linear frequency scale") { + THEN("index is resolution dived by window height for pixel = 1") { + auto index = renderingHelper.pixelToIndex(1, windowHeight, info, linearFrequency); + REQUIRE(index == 41); + } + THEN("index is half resolution for pixel = half window height") { + auto index = renderingHelper.pixelToIndex(windowHeight / 2, windowHeight, info, linearFrequency); + REQUIRE(index == 2048); + } + THEN("index is nearly the resolution for pixel = window height") { + auto index = renderingHelper.pixelToIndex(windowHeight, windowHeight, info, linearFrequency); + REQUIRE(index >= 4095); + REQUIRE(index <= 4096); + } + } + WHEN("logarithmic frequency scale") { + THEN("index is resolution dived by window height for pixel = 1") { + auto index = renderingHelper.pixelToIndex(1, windowHeight, info, logarithmicFrequency); + REQUIRE(index == 2); + } + THEN("index doubles when pixel is a decade higher") { + auto index = renderingHelper.pixelToIndex(10, windowHeight, info, logarithmicFrequency); + REQUIRE(index == 4); + } + THEN("index is nearly the resolution for pixel = window height") { + auto index = renderingHelper.pixelToIndex(windowHeight, windowHeight, info, logarithmicFrequency); + REQUIRE(index >= 4095); + REQUIRE(index <= 4096); + } + } + } +} + +// These hidden tests (marked with the tag [.debug]) are used to debug the RenderingHelper. +// They are not part of the automated tests. +SCENARIO("Rendering Helper Debugging", "[.debug]") { + GIVEN("pixel to index") { + RenderingHelper renderingHelper(ColourGradients::BLUE); + SpectralDataInfo info(44100, 4096, 4096); + const auto windowHeight = 100; + + WHEN("linear frequency scale") { + THEN("printing the index values for debugging purposes") { + printf("printing pixelToIndex linear values for debugging purposes:\n"); + for (auto pixel = 0; pixel <= windowHeight; pixel++) { + auto index = renderingHelper.pixelToIndex(pixel, windowHeight, info, false); + printf("[%3d]: %3lu |", pixel, index); + if (((pixel + 1) % 4) == 0) { + printf("\n"); + } + } + } + } + WHEN("logarithmic frequency scale") { + THEN("printing the index values for debugging purposes") { + printf("\n\nprinting pixelToIndex logarithmic values for debugging purposes:\n"); + for (auto pixel = 0; pixel <= windowHeight; pixel++) { + auto index = renderingHelper.pixelToIndex(pixel, windowHeight, info, true); + printf("[%3d]: %3lu |", pixel, index); + if (((pixel + 1) % 4) == 0) { + printf("\n"); + } + } + } + } + } +} \ No newline at end of file diff --git a/test/TestCases.h b/test/TestCases.h deleted file mode 100644 index e4b9fafb..00000000 --- a/test/TestCases.h +++ /dev/null @@ -1,11 +0,0 @@ -#include "CommonTestUtils.h" - -int test_FFT_1 (TCommonSettings settings); -int test_DWPT_1 (TCommonSettings settings); -int test_DWPT_BestBasis (TCommonSettings settings); -int test_WindowFunctionsFactory (TCommonSettings settings); -int test_FourierTransormation (TCommonSettings settings); -int test_WaveletTransormation (TCommonSettings settings); -int test_RenderingHelper (TCommonSettings settings); -int test_WindowFunctionKaiser (TCommonSettings settings); -int test_FourierPerformance (TCommonSettings settings); \ No newline at end of file diff --git a/test/Test_DWPT_1.cpp b/test/Test_DWPT_1.cpp deleted file mode 100644 index 31e18061..00000000 --- a/test/Test_DWPT_1.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "TestCases.h" - -int test_DWPT_1 (TCommonSettings settings) { - TCommonSettings localSettings = setCommonSettings(settings.samplingRate, 16, settings.channels); - - Transformation* transformation = TRANSFORM_FACTORY->createTransformation( - SpectronParameters::TRANSFORM_FWT, - localSettings.samplingRate, - localSettings.resolution, - SpectronParameters::WINDOWING_HANNING, - SpectronParameters::WAVELET_DAUBECHIES_02 - ); - assert(transformation); - - double inputData[] = { 32.0, 10.0, 20.0, 38.0, 37.0, 28.0, 38.0, 34.0, - 18.0, 24.0, 18.0, 9.00, 23.0, 24.0, 28.0, 34.0 - }; - - //expected output after sqrt on every value and for WAVELET_DAUBECHIES_02 - //-> signs get lost since they come as x*x - //-> columns and rows are swapped - //[2,0]= +59.00 +52.50 +59.00 +37.00 - //[2,1]= +7.00 +22.50 -7.00 -5.00 - //[2,2]= -3.00 -5.50 -13.00 +10.00 - //[2,3]= +1.00 +4.50 -3.00 +4.00 - - for (int i = 0; i < localSettings.resolution; i++) { - transformation->setNextInputSample(inputData[i]); - } - - SpectralDataBuffer* spectralDataBuffer = transformation->getSpectralDataBuffer(); - int dataAvailableCounter = 1; - while (transformation->isOutputAvailable()) { - SpectralDataBuffer::ItemType spectralData; - spectralDataBuffer->read(&spectralData); - assert(spectralData.size()==(settings.resolution/2+1)); - - for (int i = 0; i < spectralData.size(); i++) { - printf("[%1d,%1d]=%6.2lf ", dataAvailableCounter, i,sqrt(spectralData[i])); - } - printf("\n"); - dataAvailableCounter++; - } - - printf("\nclosed\ntest successful!\n"); - fflush(stdout); - - //tear down - TRANSFORM_FACTORY->destruct(); - WINFUNC->destruct(); - - char c; - scanf_s(&c); - return 0; -} \ No newline at end of file diff --git a/test/Test_DWPT_BestBasis.cpp b/test/Test_DWPT_BestBasis.cpp deleted file mode 100644 index de057341..00000000 --- a/test/Test_DWPT_BestBasis.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "TestCases.h" - -int test_DWPT_BestBasis (TCommonSettings settings) { - TCommonSettings localSettings = setCommonSettings(settings.samplingRate, 16, settings.channels); - - Transformation* transform = TRANSFORM_FACTORY->createTransformation( - SpectronParameters::TRANSFORM_FWPT_BB, - localSettings.samplingRate, - localSettings.resolution, - SpectronParameters::WINDOWING_SQUARE, - SpectronParameters::WAVELET_DAUBECHIES_08 - ); - assert(transform); - - double inputData[] = { 32.0, 10.0, 20.0, 38.0, 37.0, 28.0, 38.0, 34.0, - 18.0, 24.0, 18.0, 9.00, 23.0, 24.0, 28.0, 34.0 - }; - - for (int i = 0; i < localSettings.resolution; i++) { - transform->setNextInputSample(inputData[i]/100.0); - } - - SpectralDataBuffer* spectralDataQueue = transform->getSpectralDataBuffer(); - int dataAvailableCounter = 1; - while (transform->isOutputAvailable()) { - SpectralDataBuffer::ItemType spectrum; - spectralDataQueue->read(&spectrum); - - long spectralLinesCount = spectrum.size(); - bool em = spectrum.empty(); - assert(spectralLinesCount); - - for (int i = 0; i < spectralLinesCount; i++) { - printf("[%2d,%2d]=%6.2lf ", dataAvailableCounter, i,sqrt(spectrum[i])); - if (((i+1)%4) == 0) printf("\n"); - } - printf("-------------------------------------------------------------------\n"); - dataAvailableCounter++; - } - - printf("\nclosed\ntest successful!\n"); - fflush(stdout); - - char c; - scanf_s(&c); - return 0; -} \ No newline at end of file diff --git a/test/Test_FourierPerformance.cpp b/test/Test_FourierPerformance.cpp deleted file mode 100644 index a2720c8e..00000000 --- a/test/Test_FourierPerformance.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "TestCases.h" -#define SPEKTREN_PRO_GENERIERUNG 10 -#define ANZAHL_SPEKTRALANALYSEN 500 -#define MAX_FEHLGESCHLAGENE 10 - -//forward declarations -void generateInput(TCommonSettings, Transformation*, double, int); - -int test_FourierPerformance (TCommonSettings settings) { - int failCounter = 0; - //Initialisierung des Zufallsgenerators - srand (time(NULL)); - - //Laden der Transformation mit den jeweiligen Einstellungen - Transformation* transformation = TRANSFORM_FACTORY->createTransformation( - SpectronParameters::TRANSFORM_FFT, - settings.samplingRate, - settings.resolution); - assert(transformation); - - SpectralDataBuffer* spectralDataBuffer = transformation->getSpectralDataBuffer(); - assert(spectralDataBuffer); - - printCommonSettings(settings); - - //Schleife - for (int wiederholungen = 1; wiederholungen <= (ANZAHL_SPEKTRALANALYSEN / SPEKTREN_PRO_GENERIERUNG); wiederholungen++) { - //Ermitteln einer zufaelligen Frequenz in Hz und generieren des Eingabesignals - double frequency = (rand() % (((int)settings.samplingRate / 2) - 1)) + 1; - generateInput(settings, transformation, frequency, SPEKTREN_PRO_GENERIERUNG); - - int processedSpectralDataBufferEntries = 0; - - while (transformation->isOutputAvailable()) { - //Einlesen der Spektraldaten - SpectralDataBuffer::ItemType spectralData; - spectralDataBuffer->read(&spectralData); - assert(spectralData.size()==(settings.resolution/2+1)); - - //Einlesen der Statistiken - SpectralDataBuffer::ItemStatisticsType statistics = spectralDataBuffer->getStatistics(&spectralData); - - //Ermitteln der Frequenz der hoechsten Spektrallinie (bzw. pruefen der Vorahnung) - double frequencyOfMax = 0; - int indexOfMainSpectralLine = floor(frequency / settings.frequencyResolutionInHz); - assert (indexOfMainSpectralLine > 0); - assert (indexOfMainSpectralLine < spectralData.size()); - - //Kurze Konsolenausgabe - printf("W[%2d,%2d] @%6.0lf Hz", - processedSpectralDataBufferEntries, - wiederholungen, - frequency - ); - - //Automatische Verifizierung, ob hoechste Spektrallinie = generierte Frequenz - if (spectralData[indexOfMainSpectralLine] == statistics.max) { - printf(" ...... ok\n"); - } else { - printf(" ...... failed\n"); - printf("->indexOfMainSpectralLine=%d", indexOfMainSpectralLine); - failCounter++; - if (failCounter > MAX_FEHLGESCHLAGENE) { - return 1; - break; - } - } - - processedSpectralDataBufferEntries++; - } - - fflush(stdout); - //Sleep(500); - } - - printf("\nclosed\ntest "); - if (failCounter > 0) printf("not fully (%d)", failCounter); - printf("successful!\n"); - - fflush(stdout); - - char c; - scanf_s(&c); - return 0; -} - -void generateInput(TCommonSettings settings, Transformation* transformation, double frequency, int spectrumCount) { - double omega = 2.0f * M_PI * frequency; - double periode = 1.0f / frequency; - double samplesPerPeriode = settings.samplingRate * periode; - printf("\nfrequency=%lf, periode=%lf ms, samplesPerPeriode=%lf\n", frequency, periode, samplesPerPeriode); - - for (int t = 0; t < settings.resolution * spectrumCount; t++) { - double sample = sin(omega * t * settings.samplingPeriod); - for (int channel = 0; channel < settings.channels; channel++) { - assert(sample >= -1.0); - assert(sample <= 1.0); - transformation->setNextInputSample(sample); - } - } -} \ No newline at end of file diff --git a/test/Test_FourierTransformation.cpp b/test/Test_FourierTransformation.cpp deleted file mode 100644 index b56fa8ac..00000000 --- a/test/Test_FourierTransformation.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "TestCases.h" -#define TRANSFORM FFT //aktuell zu testende Transformation - -int test_FourierTransormation (TCommonSettings settings) { - //Initialisierung - Transformation* transformation = TRANSFORM_FACTORY->createTransformation( - SpectronParameters::TRANSFORM_FFT, - settings.samplingRate, - settings.resolution - ); - assert(transformation); - - printCommonSettings(settings); - - for (int wiederholungen = 1; wiederholungen <= 10; wiederholungen++) { - double frequency = settings.resolution / 100.0f * pow(wiederholungen,2.6); - double omega = 2.0f * M_PI * frequency; - double periode = 1.0f / frequency; - double samplesPerPeriode = settings.samplingRate * periode; - printf("\nfrequency=%lf, periode=%lf ms, samplesPerPeriode=%lf\n", frequency, periode, samplesPerPeriode); - - for (int t = 0; t < settings.resolution * 10; t++) { - //Schleife mit Testdaten (generiertes Sinus-Signal) - double sample = sin(omega * t * settings.samplingPeriod); - for (int channel = 0; channel < settings.channels; channel++) { - assert(sample >= -1.0); - assert(sample <= 1.0); - transformation->setNextInputSample(sample); - } - } - - double max_magnitudeDB = -1000.0f; - double frequencyOfSpectralLine = 0.0f; - - SpectralDataBuffer* spectralDataBuffer = transformation->getSpectralDataBuffer(); - SpectralDataInfo* spectralDataInfo = transformation->getSpectralDataInfo(); - assert(spectralDataBuffer); - assert(spectralDataInfo); - - int processedSpectralDataBufferEntries = 0; - - while (transformation->isOutputAvailable()) { - //for (int i = 0; i < spectralDataBuffer->size(); i++) { - SpectralDataBuffer::ItemType spectralData; - spectralDataBuffer->read(&spectralData); - assert(spectralData.size()==(settings.resolution/2+1)); - - SpectralDataBuffer::ItemStatisticsType statistics = spectralDataBuffer->getStatistics(&spectralData); - - double frequencyOfMax = 0; - - int indexOfMainSpectralLine = floor(frequency / settings.frequencyResolutionInHz); - assert (indexOfMainSpectralLine > 0); - assert (indexOfMainSpectralLine <= spectralData.size()); - - if (spectralData[indexOfMainSpectralLine] == statistics.max) { - frequencyOfMax = (double)indexOfMainSpectralLine * settings.frequencyResolutionInHz; - } - - printf("W[%1d,%1d]: min=%8.4lf, max=%8.4lf (@%6.2lf Hz), avg=%8.4lf\n", - processedSpectralDataBufferEntries, - wiederholungen, - statistics.min, - statistics.max, - frequencyOfMax, - statistics.avg - ); - printf(" DataInfo: fRes=%3d, tRes=%3d", - spectralDataInfo->getFrequencyResolution(), - spectralDataInfo->getTimeResolution() - ); - - //test auto check - if (((frequencyOfMax + settings.frequencyResolutionInHz) > frequency) - && ((frequencyOfMax - settings.frequencyResolutionInHz) < frequency)) { - printf(" ...... ok\n"); - } else { - printf(" ...... failed\n"); - printf("->indexOfMainSpectralLine=%d", indexOfMainSpectralLine); - - return 1; - break; - } - - processedSpectralDataBufferEntries++; - } - - fflush(stdout); - Sleep(500); - } - - printf("\nclosed\ntest successful!\n"); - fflush(stdout); - - char c; - scanf_s(&c); - return 0; -} - diff --git a/test/Test_RenderingHelper.cpp b/test/Test_RenderingHelper.cpp deleted file mode 100644 index 69704276..00000000 --- a/test/Test_RenderingHelper.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "TestCases.h" -#include "RenderingHelper.h" -#include "SpectralDataInfo.h" - -#define ARRAYLENGTH 128 -#define WINDOWHEIGHT 10 - -int test_RenderingHelper (TCommonSettings settings) { - //override global settings for this especially this test case - settings.resolution = ARRAYLENGTH; - - //create sut and dependencies - RenderingHelper* render = new RenderingHelper(); - SpectralDataInfo* info = new SpectralDataInfo(settings.samplingRate, settings.resolution, settings.resolution); - - printf("method pixelToIndex lin.....\n"); - for (int pixel = 0; pixel <= WINDOWHEIGHT; pixel++) { - long index = render->pixelToIndex(pixel, WINDOWHEIGHT, info, false); - printf("[%3d]: %3d |", pixel, index); - if (((pixel+1) % 4) == 0) printf("\n"); - } - - printf("\n\nmethod pixelToIndex log.....\n"); - for (int pixel = 0; pixel <= WINDOWHEIGHT; pixel++) { - long index = render->pixelToIndex(pixel, WINDOWHEIGHT, info, true); - printf("[%3d]: %3d |", pixel, index); - if (((pixel+1) % 4) == 0) printf("\n"); - } - - delete(render); - delete(info); - - printf("\nclosed\ntest successful!\n"); - fflush(stdout); - - char c; - scanf_s(&c); - return 0; -} \ No newline at end of file diff --git a/test/Test_WaveletTransformation.cpp b/test/Test_WaveletTransformation.cpp deleted file mode 100644 index ab5a8d1f..00000000 --- a/test/Test_WaveletTransformation.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "TestCases.h" - -int test_WaveletTransormation (TCommonSettings settings) { - //this test will be done at a lower resolution for an easier result-checking - //settings.resolution = 16; - - Transformation* transformation = TRANSFORM_FACTORY->createTransformation( - SpectronParameters::TRANSFORM_FWT, - settings.samplingRate, - settings.resolution, - SpectronParameters::WINDOWING_SQUARE, - SpectronParameters::WAVELET_VAIDYANATHAN_18 - ); - assert(transformation); - - printCommonSettings(settings); - - int countOk = 0; - int countDriftedFrequency = 0; - int countDoubleFrequency = 0; - int maxWiederholungen = 1; - - for (int wiederholungen = 1; wiederholungen <= maxWiederholungen; wiederholungen++) { - double frequency = settings.resolution / 100.0f * pow(wiederholungen,2.6); - double omega = 2.0f * M_PI * frequency; - double periode = 1.0f / frequency; - double samplesPerPeriode = settings.samplingRate * periode; - printf("\nfrequency=%lf, periode=%lf ms, samplesPerPeriode=%lf\n", frequency, periode, samplesPerPeriode); - - for (int t = 0; t < settings.resolution * 10; t++) { - //Schleife mit Testdaten (generiertes Sinus-Signal) - double sample = 0.1f * sin(omega * t * settings.samplingPeriod); - for (int channel = 0; channel < settings.channels; channel++) { - assert(sample >= -1.0); - assert(sample <= 1.0); - transformation->setNextInputSample(sample); - } - } - - double max_magnitudeDB = -1000.0f; - double frequencyOfSpectralLine = 0.0f; - - SpectralDataBuffer* spectralDataBuffer = transformation->getSpectralDataBuffer(); - SpectralDataInfo* spectralDataInfo = transformation->getSpectralDataInfo(); - assert(spectralDataBuffer); - assert(spectralDataInfo); - - int processedSpectralDataBufferEntries = 0; - - while (transformation->isOutputAvailable()) { - SpectralDataBuffer::ItemType spectralData; - spectralDataBuffer->read(&spectralData); - assert(spectralData.size()==settings.resolution); - - SpectralDataBuffer::ItemStatisticsType statistics = spectralDataBuffer->getStatistics(&spectralData); - - double frequencyOfMax = 0; - int indexOfMainSpectralLine = 0; - - // find max frequency & amplitude - for (int specLine = 0; specLine < spectralData.size(); specLine++) { - if (spectralData[specLine] == statistics.max) { - indexOfMainSpectralLine = specLine; - frequencyOfMax = (double)specLine * (settings.samplingRate / settings.resolution / 2.0f); - } - } - assert (indexOfMainSpectralLine > 0); - assert (indexOfMainSpectralLine < spectralData.size()); - - printf("W[%1d,%1d]: min=%8.4lf, max=%8.4lf (@%6.2lf Hz), avg=%8.4lf\n", - processedSpectralDataBufferEntries, - wiederholungen, - statistics.min, - statistics.max, - frequencyOfMax, - statistics.avg - ); - printf(" DataInfo: fRes=%3d, tRes=%3d", - spectralDataInfo->getFrequencyResolution(), - spectralDataInfo->getTimeResolution() - ); - - //test auto check - if (((frequencyOfMax + settings.frequencyResolutionInHz) > frequency) - && ((frequencyOfMax - settings.frequencyResolutionInHz) < frequency)) { - printf(" ...... ok\n"); - countOk++; - } else { - printf(" ...... wrong frequency\n"); - printf("->indexOfMainSpectralLine=%d\n", indexOfMainSpectralLine); - countDriftedFrequency++; - } - if (((frequencyOfMax / 2.0f + settings.frequencyResolutionInHz) > frequency) - && ((frequencyOfMax / 2.0f - settings.frequencyResolutionInHz) < frequency)) { - countDoubleFrequency++; - } - } - - fflush(stdout); - Sleep(500); - } - - printf("OK: %i, fequency<>source: %i, frequency=source*2: %i", countOk, countDriftedFrequency, countDoubleFrequency); - if (countOk < countDriftedFrequency) { - printf("\nclosed\ntest failed!\n"); - fflush(stdout); - return 1; - } - printf("\nclosed\ntest successful!\n"); - fflush(stdout); - - char c; - scanf_s(&c); - return 0; -} - diff --git a/test/Test_WindowFunctionsFactory.cpp b/test/Test_WindowFunctionsFactory.cpp deleted file mode 100644 index 1c1f4df5..00000000 --- a/test/Test_WindowFunctionsFactory.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "TestCases.h" - -int test_WindowFunctionsFactory (TCommonSettings settings) { - double inputData[] = { 32.0, 10.0, 20.0, 38.0, 37.0, 28.0, 38.0, 34.0, - 18.0, 24.0, 18.0, 9.00, 23.0, 24.0, 28.0, 34.0 - }; - - queue windowFunctionVector; - - printf("Every windowing class @resolution is gonna be read the first time and should be created....\n"); - for (long resolution = settings.resolution; resolution < settings.resolution*3; resolution+=settings.resolution) { - for (int windowIndex = 1; windowIndex < SpectronParameters::WINDOWINGS_NumOptions; windowIndex++) { - WindowFunction* windowFunction = WINFUNC->createWindowFunction(windowIndex, resolution); - assert(windowFunction); - windowFunctionVector.push(windowFunction); - - char* name = windowFunction->getName(); - printf("window [%4d] (N=%1d): %s loaded\n", resolution, windowIndex, name); - } - } - - printf("Every already created windowing class will be read again...\n"); - - for (long resolution = settings.resolution; resolution < settings.resolution*3; resolution+=settings.resolution) { - for (int windowIndex = 1; windowIndex < SpectronParameters::WINDOWINGS_NumOptions; windowIndex++) { - WindowFunction* windowFunction = WINFUNC->createWindowFunction(windowIndex, resolution); - assert(windowFunction); - assert(windowFunction == windowFunctionVector.front()); //the references should be exactly equal here - windowFunctionVector.pop(); - - char* name = windowFunction->getName(); - printf("window [%4d] (N=%1d): %s reloaded\n", resolution, windowIndex, name); - } - } - - printf("\nclosed\ntest successful!\n"); - fflush(stdout); - - char c; - scanf_s(&c); - return 0; -} \ No newline at end of file diff --git a/test/TransformationIntegrationTest.cpp b/test/TransformationIntegrationTest.cpp new file mode 100644 index 00000000..60dc0c0e --- /dev/null +++ b/test/TransformationIntegrationTest.cpp @@ -0,0 +1,155 @@ +#include "../src/dsp/transformations/TransformationFactory.h" +#include "../src/dsp/transformations/TransformationParameters.h" +#include "../src/dsp/transformations/WaveletParameters.h" +#include "../src/dsp/windowing/WindowParameters.h" + +#include "catch2/generators/catch_generators.hpp" +#include "catch2/generators/catch_generators_range.hpp" +#include +#include + +#include +#include +#include +#include +#include + +void generateTestSineInput(double frequency, Transformation *transformation, double samplingRate, Transformation::ResolutionType resolution) { + double omega = 2.0f * M_PI * frequency; + double periode = 1.0f / frequency; + double samplesPerPeriode = samplingRate * periode; + INFO("\nfrequency=" << frequency << " Hz, omega=" << omega << " rad/s, periode=" << periode << " ms, samplesPerPeriode=" << samplesPerPeriode << "\n"); + + //Generate sine wave input samples + for (std::size_t t = 0; t < resolution * 10; t++) { + double samplingPeriod = 1.0 / samplingRate; + double sample = 0.1f * sin(omega * static_cast(t) * samplingPeriod); + REQUIRE(sample >= -1.0); + REQUIRE(sample <= 1.0); + transformation->setNextInputSample(sample); + } +} + +std::size_t getMaxMagnitudeSpectralLineIndex(const SpectralDataBuffer::ItemType &spectralData) { + double maxMagnitude = 0.0; + std::size_t maxMagnitudeSpectrumLine = 0; + for (std::size_t spectrumLine = 0; spectrumLine < spectralData.size(); spectrumLine++) { + if (spectralData[spectrumLine] >= maxMagnitude) { + maxMagnitude = spectralData[spectrumLine]; + maxMagnitudeSpectrumLine = spectrumLine; + } + } + return maxMagnitudeSpectrumLine; +} + +unsigned long getExpectedSpectralDataSize(const SpectralDataInfo &spectralDataInfo, const TransformationParameters::Type &transformationType) { + auto expectedSpectralDataSize = spectralDataInfo.getResolution(); + if (transformationType == TransformationParameters::Type::FAST_FOURIER_TRANSFORM) { + expectedSpectralDataSize = spectralDataInfo.getResolution() / 2 + 1; + } else if (transformationType == TransformationParameters::Type::FAST_WAVELET_PACKET_TRANSFORM) { + expectedSpectralDataSize = spectralDataInfo.getResolution() / 16;// Because of 4x frequency resolution ratio + } + auto transformationTypeName = std::string(TransformationParameters::TypeNames::map.at(transformationType)); + INFO("Expected spectral data size: " << expectedSpectralDataSize << " for transformation type: " << transformationTypeName << " and resolution: " << spectralDataInfo.getResolution()); + return expectedSpectralDataSize; +} + +double getFrequencyDeviationToleranceFactor(const TransformationParameters::Type &transformationType) { + auto frequencyDeviationToleranceFactor = 4.0F; + if (transformationType == TransformationParameters::Type::FAST_FOURIER_TRANSFORM) { + frequencyDeviationToleranceFactor = 5.0F; + } else if (transformationType == TransformationParameters::Type::FAST_WAVELET_PACKET_TRANSFORM) { + frequencyDeviationToleranceFactor = 8.0F; + } else if (transformationType == TransformationParameters::Type::FAST_WAVELET_PACKET_BEST_BASIS_TRANSFORM) { + frequencyDeviationToleranceFactor = 9.0F; + } + auto transformationTypeName = std::string(TransformationParameters::TypeNames::map.at(transformationType)); + INFO("Frequency deviation tolerance factor: " << frequencyDeviationToleranceFactor << " for transformation type: " << transformationTypeName); + return frequencyDeviationToleranceFactor; +} + +/** + * @brief Determines the frequency of the maximum magnitude spectral line to calculate its deviation from the expected frequency. + * + * @param transformation Transformation object + * @param expectedFrequency The expected ("right") frequency of the sine generator + * @return double + */ +double getMaxPowerFrequencyDeviation(Transformation *transformation, double expectedFrequency) { + auto &spectralDataInfo = transformation->getSpectralDataInfo(); + auto frequencyResolutionInHz = static_cast(spectralDataInfo.getSamplingFrequency()) * 0.5 / static_cast(spectralDataInfo.getResolution()); + + SpectralDataBuffer *spectralDataBuffer = transformation->getSpectralDataBuffer(); + REQUIRE(spectralDataBuffer != nullptr); + + auto expectedSpectralDataSize = getExpectedSpectralDataSize(spectralDataInfo, static_cast(transformation->getTransformationType())); + int count = 0; + double maxPowerFrequencyDeviationSum = 0.0; + while (transformation->isOutputAvailable()) { + SpectralDataBuffer::ItemType spectralData; + spectralDataBuffer->read(&spectralData); + REQUIRE(spectralData.size() == expectedSpectralDataSize); + + std::size_t maxMagnitudeSpectrumLine = getMaxMagnitudeSpectralLineIndex(spectralData); + double maxMagnitudeFrequency = static_cast(maxMagnitudeSpectrumLine) * frequencyResolutionInHz; + maxPowerFrequencyDeviationSum += (maxMagnitudeFrequency - expectedFrequency) * (maxMagnitudeFrequency - expectedFrequency); + count++; + } + return sqrt(maxPowerFrequencyDeviationSum / count); +} + +SCENARIO("Transformations Integration Test", "[integration]") { + auto gui = juce::ScopedJuceInitialiser_GUI{}; + auto samplingRate = 44100; + Transformation::ResolutionType resolution = GENERATE(2048U, 4096U); + auto frequencyResolutionInHz = static_cast(samplingRate) * 0.5 / static_cast(resolution); + + // Generates a list of all transformation except for the last one ("BYPASS") out of the typeNames Map (first value = transformation type) and uses them to call the tests methods below for each of them. + // See: https://github.com/catchorg/Catch2/blob/devel/docs/generators.md + auto transformationType = GENERATE(from_range(TransformationParameters::TypeNames::map.begin(), std::prev(TransformationParameters::TypeNames::map.end()))).first; + Transformation *transformation = TransformationFactory::getSingletonInstance().createTransformation( + transformationType, + samplingRate, + resolution, + WindowParameters::WindowFunction::BLACKMAN_HARRIS, + WaveletParameters::WaveletBase::VAIDYANATHAN_18, + WaveletParameters::ResolutionRatioOption::FREQUENCY_X4); + REQUIRE(transformation != nullptr); + REQUIRE(transformation->getTransformationType() == transformationType); + + auto frequencyDeviationToleranceFactor = getFrequencyDeviationToleranceFactor(transformation->getTransformationType()); + + GIVEN(transformation->getName() << " with resolution " << resolution) { + double frequency = static_cast(resolution) / 100.0f; + WHEN("sine signal with " << frequency << "Hz is analyzed") { + generateTestSineInput(frequency, transformation, samplingRate, resolution); + THEN("the frequency of the maximum magnitude spectral line is within " << frequencyDeviationToleranceFactor << " times " << frequencyResolutionInHz << "Hz (frequency resolution) of the expected frequency") { + double maxPowerFrequencyDeviationToleranceInHz = frequencyResolutionInHz * frequencyDeviationToleranceFactor; + double maxPowerFrequencyDeviationInHz = getMaxPowerFrequencyDeviation(transformation, frequency); + CAPTURE(maxPowerFrequencyDeviationInHz, maxPowerFrequencyDeviationToleranceInHz, frequencyResolutionInHz); + REQUIRE(maxPowerFrequencyDeviationInHz < maxPowerFrequencyDeviationToleranceInHz); + } + } + } + TransformationFactory::getSingletonInstance().destruct(); +} + +TEST_CASE("Transformations Performance Test", "[.performance]") { + auto samplingRate = 44100; + Transformation::ResolutionType resolution = 4096; + double frequency = static_cast(resolution) / 100.0f; + BENCHMARK_ADVANCED("Fast Fourier Transform Performance") + (Catch::Benchmark::Chronometer meter) { + Transformation *transformation = TransformationFactory::getSingletonInstance().createTransformation( + TransformationParameters::Type::FAST_FOURIER_TRANSFORM, + samplingRate, + resolution, + WindowParameters::WindowFunction::BLACKMAN_HARRIS, + WaveletParameters::WaveletBase::VAIDYANATHAN_18, + WaveletParameters::ResolutionRatioOption::FREQUENCY_X4); + generateTestSineInput(frequency, transformation, samplingRate, resolution); + getMaxPowerFrequencyDeviation(transformation, frequency); + meter.measure([transformation, frequency] { return getMaxPowerFrequencyDeviation(transformation, frequency); }); + }; + TransformationFactory::getSingletonInstance().destruct(); +} \ No newline at end of file diff --git a/test/WaveletTransformationTest.cpp b/test/WaveletTransformationTest.cpp new file mode 100644 index 00000000..68c4fabb --- /dev/null +++ b/test/WaveletTransformationTest.cpp @@ -0,0 +1,29 @@ +#include "../src/dsp/transformations/WaveletTransformation.h" +#include "../src/dsp/transformations/WaveletParameters.h" +#include "../src/dsp/windowing/WindowParameters.h" + +#include + +#include + +SCENARIO("Wavelet Transformation") { + auto gui = juce::ScopedJuceInitialiser_GUI{}; + auto samplingRate = 44100; + Transformation::ResolutionType resolution = 4096; + auto windowFunction = WindowParameters::WindowFunction::BLACKMAN_HARRIS; + auto waveletBase = WaveletParameters::WaveletBase::VAIDYANATHAN_18; + + GIVEN("Wavelet Transform is created") { + auto transformation = WaveletTransformation(samplingRate, resolution, windowFunction, waveletBase); + WHEN("name is requested") { + THEN("it should return the correct name") { + REQUIRE(transformation.getName() == std::string("Fast Wavelet Transformation")); + } + } + WHEN("window function is requested") { + THEN("it should return the correct window function") { + REQUIRE(transformation.getWindowFunction()->getName() == std::string("Blackman-Harris")); + } + } + } +} \ No newline at end of file diff --git a/test/WindowFunctionFactoryTest.cpp b/test/WindowFunctionFactoryTest.cpp new file mode 100644 index 00000000..55d28d3d --- /dev/null +++ b/test/WindowFunctionFactoryTest.cpp @@ -0,0 +1,45 @@ +#include "../src/dsp/windowing/WindowFunctionFactory.h" +#include "../src/dsp/windowing/WindowParameters.h" + +#include "catch2/matchers/catch_matchers.hpp" +#include "catch2/matchers/catch_matchers_contains.hpp" +#include "catch2/matchers/catch_matchers_string.hpp" +#include +#include + +#include + +SCENARIO("Window Function Factory") { + WindowFunctionFactory &factory = WindowFunctionFactory::getSingletonInstance(); + + GIVEN("get window") { + const auto resolution = 4096; + + WHEN("window functions are cached") { + // Generates a list of all window functions/methods and calls the tests methods below for each of them. + // See: https://github.com/catchorg/Catch2/blob/devel/docs/generators.md + auto windowMethod = static_cast(GENERATE(range(1, static_cast(WindowParameters::WindowFunction::NUMBER_OF_OPTIONS)))); + auto window = factory.getWindow(windowMethod, resolution); + REQUIRE(window != nullptr); + + THEN("window " + std::string(window->getName()) + " is only created once") { + auto reloadedWindow = factory.getWindow(windowMethod, resolution); + REQUIRE(window == reloadedWindow); + factory.clearCache(); + } + THEN("window " + std::string(window->getName()) + " is newly created when resolution changes") { + const auto anotherResolution = 2048; + auto windowWithAnotherResolution = factory.getWindow(windowMethod, anotherResolution); + REQUIRE(window != windowWithAnotherResolution); + factory.clearCache(); + } + } + WHEN("invalid window number is given") { + WindowParameters::WindowFunction invalidWindowMethod = WindowParameters::WindowFunction::NUMBER_OF_OPTIONS; + THEN("exception is thrown") { + auto expectedErrorMessage = "Unknown windowing function " + std::to_string(static_cast(invalidWindowMethod)); + REQUIRE_THROWS_WITH(factory.getWindow(invalidWindowMethod, resolution), expectedErrorMessage); + } + } + } +} \ No newline at end of file diff --git a/test/targetver.h b/test/targetver.h deleted file mode 100644 index 497706ee..00000000 --- a/test/targetver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Durch Einbeziehen von"SDKDDKVer.h" wird die höchste verfügbare Windows-Plattform definiert. - -// Wenn Sie die Anwendung für eine frühere Windows-Plattform erstellen möchten, schließen Sie "WinSDKVer.h" ein, und -// legen Sie das _WIN32_WINNT-Makro auf die zu unterstützende Plattform fest, bevor Sie "SDKDDKVer.h" einschließen. - -#include