├── .appveyor.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── examples └── futur3soundz │ ├── CreateTables.sh │ └── LICENSE.md ├── sources ├── Random.h ├── WCreate.cpp ├── WMorph.cpp ├── WaveFormula.cpp ├── WaveFormula.h ├── Wavetable.cpp ├── Wavetable.h ├── interactive │ ├── WEdit.cpp │ └── dsp │ │ ├── Buffer.h │ │ ├── Config.h │ │ ├── Interpolators.h │ │ ├── Interpolators.hpp │ │ ├── MathHelpers.h │ │ ├── SIMDConfig.h │ │ ├── Wavetables.cpp │ │ └── Wavetables.h ├── random │ ├── LICENSE.txt │ └── ms_stl_random.h ├── series │ ├── SeriesExpr.cpp │ ├── SeriesExpr.h │ ├── SeriesExprGrammar.l │ ├── SeriesExprGrammar.tab.cpp │ ├── SeriesExprGrammar.tab.h │ ├── SeriesExprGrammar.y │ ├── SeriesExprGrammar.yy.cpp │ ├── SeriesExprGrammar.yy.h │ └── SeriesExprGrammarExtra.h └── utility │ ├── Locale.h │ ├── unicodefile.c │ ├── unicodefile.h │ └── utf8main.c └── thirdparty └── kiss_fft ├── CMakeLists.txt ├── COPYING ├── _kiss_fft_guts.h ├── kiss_fft.c ├── kiss_fft.h └── tools ├── kiss_fftr.c └── kiss_fftr.h /.appveyor.yml: -------------------------------------------------------------------------------- 1 | configuration: 2 | - Release 3 | image: 4 | - Visual Studio 2019 5 | 6 | environment: 7 | matrix: 8 | - platform: Win32 9 | 10 | for: 11 | - 12 | matrix: 13 | only: 14 | - platform: Win32 15 | image: Visual Studio 2019 16 | environment: 17 | VCPKG_TRIPLET: x86-windows-static 18 | CMAKE_GENERATOR: Visual Studio 16 2019 19 | CMAKE_GENERATOR_PLATFORM: Win32 20 | install: 21 | - cmd: vcpkg install sdl2:%VCPKG_TRIPLET% 22 | before_build: 23 | - cmd: git submodule update --init --recursive 24 | build_script: 25 | - cmd: mkdir CMakeBuild 26 | - cmd: cd CMakeBuild 27 | - cmd: cmake -G"%CMAKE_GENERATOR%" -A"%CMAKE_GENERATOR_PLATFORM%" -DCMAKE_BUILD_TYPE=Release -DVCPKG_TARGET_TRIPLET="%VCPKG_TRIPLET%" -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake .. 28 | - cmd: cd .. 29 | - cmd: cmake --build CMakeBuild --config Release -j 30 | - cmd: mkdir "%APPVEYOR_PROJECT_NAME%-%platform%" 31 | - cmd: cp CMakeBuild/Release/*.exe "%APPVEYOR_PROJECT_NAME%-%platform%/" 32 | - cmd: 7z a "%APPVEYOR_PROJECT_NAME%-%platform%.zip" "%APPVEYOR_PROJECT_NAME%-%platform%/" 33 | artifacts: 34 | - path: $(APPVEYOR_PROJECT_NAME)-$(platform).zip 35 | name: 'Windows 32-bit' 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /trash/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "thirdparty/imgui"] 2 | path = thirdparty/imgui 3 | url = https://github.com/ocornut/imgui.git 4 | [submodule "thirdparty/implot"] 5 | path = thirdparty/implot 6 | url = https://github.com/epezent/implot.git 7 | [submodule "thirdparty/osdialog"] 8 | path = thirdparty/osdialog 9 | url = https://github.com/sfztools/osdialog.git 10 | [submodule "thirdparty/span-lite"] 11 | path = thirdparty/span-lite 12 | url = https://github.com/martinmoene/span-lite.git 13 | [submodule "thirdparty/dr_libs"] 14 | path = thirdparty/dr_libs 15 | url = https://github.com/mackron/dr_libs.git 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION "3.7") 2 | 3 | project(WaveTableTools) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | option(WAVE_TABLE_TOOLS_ENABLE_UI "Enable UI" ON) 8 | 9 | if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") 10 | string(APPEND CMAKE_CXX_FLAGS " -Wall") 11 | endif() 12 | 13 | if(MINGW) 14 | string(APPEND CMAKE_C_FLAGS " -municode") 15 | string(APPEND CMAKE_CXX_FLAGS " -municode") 16 | endif() 17 | 18 | if(WIN32) 19 | add_definitions("-D_CRT_SECURE_NO_WARNINGS=1") 20 | endif() 21 | 22 | add_subdirectory("thirdparty/kiss_fft" EXCLUDE_FROM_ALL) 23 | add_subdirectory("thirdparty/span-lite" EXCLUDE_FROM_ALL) 24 | 25 | add_library(dr_wav INTERFACE) 26 | target_include_directories(dr_wav INTERFACE "thirdparty/dr_libs") 27 | 28 | # 29 | find_package(PkgConfig) 30 | 31 | # 32 | include(GNUInstallDirs) 33 | 34 | # 35 | add_library(WLib STATIC 36 | "sources/Random.h" 37 | "sources/Wavetable.h" 38 | "sources/Wavetable.cpp" 39 | "sources/WaveFormula.h" 40 | "sources/WaveFormula.cpp" 41 | "sources/series/SeriesExpr.h" 42 | "sources/series/SeriesExpr.cpp" 43 | "sources/series/SeriesExprGrammar.tab.cpp" 44 | "sources/series/SeriesExprGrammar.tab.h" 45 | "sources/series/SeriesExprGrammar.yy.cpp" 46 | "sources/series/SeriesExprGrammar.yy.h" 47 | "sources/series/SeriesExprGrammarExtra.h" 48 | "sources/utility/Locale.h") 49 | #if(TRUE) 50 | # target_compile_definitions(WLib PRIVATE "YYDEBUG=1") 51 | #endif() 52 | target_include_directories(WLib PUBLIC 53 | "sources") 54 | target_link_libraries(WLib PRIVATE 55 | sfizz-kissfft) 56 | 57 | # 58 | add_executable(WCreate 59 | "sources/WCreate.cpp" 60 | "sources/utility/unicodefile.h" 61 | "sources/utility/unicodefile.c") 62 | target_link_libraries(WCreate PRIVATE WLib) 63 | 64 | # 65 | add_executable(WMorph 66 | "sources/WMorph.cpp" 67 | "sources/utility/unicodefile.h" 68 | "sources/utility/unicodefile.c") 69 | target_link_libraries(WMorph PRIVATE WLib dr_wav) 70 | 71 | # 72 | find_program(FLEX_PROGRAM "flex") 73 | find_program(BISON_PROGRAM "bison") 74 | if(FLEX_PROGRAM AND BISON_PROGRAM) 75 | add_custom_command( 76 | OUTPUT "${PROJECT_SOURCE_DIR}/sources/series/SeriesExprGrammar.tab.cpp" 77 | COMMAND "${BISON_PROGRAM}" "--output=SeriesExprGrammar.tab.cpp" "--defines=SeriesExprGrammar.tab.h" "SeriesExprGrammar.y" 78 | DEPENDS "${PROJECT_SOURCE_DIR}/sources/series/SeriesExprGrammar.y" 79 | BYPRODUCTS "${PROJECT_SOURCE_DIR}/sources/series/SeriesExprGrammar.tab.h" 80 | WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/sources/series") 81 | add_custom_command( 82 | OUTPUT "${PROJECT_SOURCE_DIR}/sources/series/SeriesExprGrammar.yy.cpp" 83 | COMMAND "${FLEX_PROGRAM}" "--outfile=SeriesExprGrammar.yy.cpp" "--header-file=SeriesExprGrammar.yy.h" "SeriesExprGrammar.l" 84 | DEPENDS "${PROJECT_SOURCE_DIR}/sources/series/SeriesExprGrammar.l" 85 | BYPRODUCTS "${PROJECT_SOURCE_DIR}/sources/series/SeriesExprGrammar.yy.h" 86 | WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/sources/series") 87 | endif() 88 | 89 | # 90 | if(WAVE_TABLE_TOOLS_ENABLE_UI) 91 | find_package(SDL2 REQUIRED) 92 | set(OpenGL_GL_PREFERENCE "GLVND") 93 | find_package(OpenGL REQUIRED) 94 | if(WIN32) 95 | elseif(APPLE) 96 | find_library(APPKIT_FRAMEWORK "AppKit") 97 | else() 98 | pkg_check_modules(Gtk3 "gtk+-3.0" REQUIRED IMPORTED_TARGET) 99 | endif() 100 | 101 | # 102 | add_library(imgui STATIC 103 | "thirdparty/imgui/imgui.cpp" 104 | "thirdparty/imgui/imgui_draw.cpp" 105 | "thirdparty/imgui/imgui_widgets.cpp" 106 | "thirdparty/imgui/misc/cpp/imgui_stdlib.cpp") 107 | target_include_directories(imgui PUBLIC 108 | "thirdparty/imgui" 109 | "thirdparty/imgui/misc/cpp") 110 | 111 | # 112 | add_library(implot STATIC 113 | "thirdparty/implot/implot.cpp" 114 | "thirdparty/implot/implot_items.cpp") 115 | target_include_directories(implot PUBLIC "thirdparty/implot") 116 | target_link_libraries(implot PUBLIC imgui) 117 | 118 | # 119 | add_library(imgui-backend STATIC 120 | "thirdparty/imgui/backends/imgui_impl_sdl.cpp" 121 | "thirdparty/imgui/backends/imgui_impl_opengl2.cpp") 122 | target_include_directories(imgui-backend PUBLIC 123 | "thirdparty/imgui" 124 | "thirdparty/imgui/backends") 125 | if(MSVC AND TARGET SDL2::SDL2-static) 126 | target_link_libraries(imgui-backend PUBLIC SDL2::SDL2-static) 127 | elseif(TARGET SDL2::SDL2) 128 | target_link_libraries(imgui-backend PUBLIC SDL2::SDL2) 129 | else() 130 | target_include_directories(imgui-backend PUBLIC ${SDL2_INCLUDE_DIRS}) 131 | target_link_libraries(imgui-backend PUBLIC ${SDL2_LIBRARIES}) 132 | endif() 133 | target_link_libraries(imgui-backend PUBLIC OpenGL::GL) 134 | 135 | # 136 | add_library(osdialog STATIC "thirdparty/osdialog/osdialog.c") 137 | target_include_directories(osdialog PUBLIC "thirdparty/osdialog") 138 | if(WIN32) 139 | target_sources(osdialog PRIVATE "thirdparty/osdialog/osdialog_win.c") 140 | target_link_libraries(osdialog PRIVATE "comdlg32") 141 | elseif(APPLE) 142 | target_sources(osdialog PRIVATE "thirdparty/osdialog/osdialog_mac.m") 143 | target_link_libraries(osdialog PRIVATE "${APPKIT_FRAMEWORK}") 144 | else() 145 | target_sources(osdialog PRIVATE "thirdparty/osdialog/osdialog_gtk3.c") 146 | target_link_libraries(osdialog PRIVATE PkgConfig::Gtk3) 147 | endif() 148 | 149 | # 150 | add_executable(WEdit WIN32 151 | "sources/interactive/WEdit.cpp" 152 | "sources/interactive/dsp/Wavetables.cpp" 153 | "sources/interactive/dsp/Wavetables.h" 154 | "sources/utility/unicodefile.h" 155 | "sources/utility/unicodefile.c" 156 | "sources/utility/utf8main.c") 157 | target_link_libraries(WEdit PRIVATE WLib implot imgui imgui-backend osdialog sfizz-kissfft nonstd::span-lite) 158 | if(NOT (WIN32 OR APPLE)) 159 | target_link_libraries(WEdit PRIVATE PkgConfig::Gtk3) 160 | endif() 161 | endif() 162 | 163 | # 164 | install(TARGETS WCreate WMorph RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") 165 | if(WAVE_TABLE_TOOLS_ENABLE_UI) 166 | install(TARGETS WEdit RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") 167 | endif() 168 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | The code is copyrighted by their respective authors, as indicated by the 4 | source control mechanism. 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WaveTableTools 2 | 3 | This is an approximate clone of wavetable generation utilities WCreate and WMorph by futur3soundz. 4 | 5 | This is NOT the source code of the official utilities. 6 | 7 | More information and usage guide at this web location: 8 | https://www.futur3soundz.com/wavetable-synthesis 9 | 10 | ## Extra functionality 11 | 12 | This software creates wavetables which are compatible with the Xfer specification. 13 | The wavetables created with size 2048 are expected to be directly compatible with 14 | Xfer Serum, and other software like Surge which are compatible with the specification. 15 | -------------------------------------------------------------------------------- /examples/futur3soundz/LICENSE.md: -------------------------------------------------------------------------------- 1 | # CreateTables.sh 2 | 3 | This code is a derivative work from WTableTools originally published as 4 | freeware by futur3soundz, edited for interoperability. 5 | 6 | As the original work does not include a license notice, the license is: 7 | All rights reserved, www.futur3soundz.com 8 | 9 | This file is published as is without having contacted the author yet, 10 | so it is not covered by the free software license. 11 | -------------------------------------------------------------------------------- /sources/Random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "random/ms_stl_random.h" 3 | 4 | class RandomGenerator { 5 | public: 6 | typedef ms_stl::mt19937 prng_type; 7 | typedef typename prng_type::result_type result_type; 8 | 9 | public: 10 | void seed(long seed); 11 | double next_double(); 12 | float next_float(); 13 | 14 | private: 15 | prng_type source_; 16 | }; 17 | 18 | //------------------------------------------------------------------------------ 19 | inline void RandomGenerator::seed(long seed) 20 | { 21 | source_.seed(seed); 22 | } 23 | 24 | inline double RandomGenerator::next_double() 25 | { 26 | ms_stl::uniform_real_distribution dist(0, 1); 27 | return dist(source_); 28 | } 29 | 30 | inline float RandomGenerator::next_float() 31 | { 32 | ms_stl::uniform_real_distribution dist(0, 1); 33 | return dist(source_); 34 | } 35 | -------------------------------------------------------------------------------- /sources/WCreate.cpp: -------------------------------------------------------------------------------- 1 | #include "series/SeriesExpr.h" 2 | #include "Random.h" 3 | #include "Wavetable.h" 4 | #include "WaveFormula.h" 5 | #include "utility/unicodefile.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace { 11 | int table_size = 0; 12 | const char *expression = nullptr; 13 | const char *output_filename = nullptr; 14 | long default_seed = 0; 15 | } 16 | 17 | static bool parse_args(int argc, char *argv[]) 18 | { 19 | if (argc < 2) { 20 | std::cerr << "Usage: WCreate [-seed number]\n"; 21 | return false; 22 | } 23 | 24 | unsigned nth_positional_arg = 0; 25 | 26 | for (int argi = 1; argi < argc; ++argi) { 27 | const char *arg = argv[argi]; 28 | 29 | if (!strcmp(arg, "-seed")) { 30 | if (++argi >= argc) { 31 | std::cerr << "Flag requires an argument: " << arg << '\n'; 32 | return false; 33 | } 34 | arg = argv[argi]; 35 | default_seed = strtol(arg, nullptr, 0); 36 | } 37 | else { 38 | switch (nth_positional_arg) { 39 | case 0: 40 | table_size = atoi(arg); 41 | break; 42 | case 1: 43 | expression = arg; 44 | break; 45 | case 2: 46 | output_filename = arg; 47 | break; 48 | } 49 | ++nth_positional_arg; 50 | } 51 | } 52 | 53 | if (nth_positional_arg != 3) { 54 | std::cerr << "Bad number of positional arguments\n"; 55 | return false; 56 | } 57 | 58 | if (table_size <= 0 || table_size > 8192 || (table_size & 1)) { 59 | std::cerr << "Invalid table size\n"; 60 | return false; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | static void display_wave(const float* data, unsigned size, int rows, int cols) 67 | { 68 | if ((rows & 1) == 0) 69 | --rows; 70 | if ((cols & 1) == 0) 71 | --cols; 72 | 73 | if (rows < 1 || cols < 1) 74 | return; 75 | 76 | std::unique_ptr text(new char[rows * (cols + 1) + 1]); 77 | { 78 | char *p = text.get(); 79 | for (int r = 0; r < rows; ++r) { 80 | for (int c = 0; c < cols; ++c) 81 | *p++ = ' '; 82 | *p++ = '\n'; 83 | } 84 | *p++ = '\0'; 85 | } 86 | auto put_char = [&text, rows, cols](int r, int c, char ch) { 87 | if (r >= 0 && c >= 0 && r < rows && c < cols) 88 | text[r * (cols + 1) + c] = ch; 89 | }; 90 | auto put_hline = [&put_char](int r, int c1, int c2, char ch) { 91 | for (int c = c1; c <= c2; ++c) 92 | put_char(r, c, ch); 93 | }; 94 | auto put_vline = [&put_char](int r1, int r2, int c, char ch) { 95 | for (int r = r1; r <= r2; ++r) 96 | put_char(r, c, ch); 97 | }; 98 | 99 | auto lerp = [data, size](float x) { 100 | float index = x * size; 101 | unsigned i1 = (unsigned)index % size; 102 | unsigned i2 = ((unsigned)index + 1) % size; 103 | float mu = index - i1; 104 | return (1 - mu) * data[i1] + mu * data[i2]; 105 | }; 106 | 107 | 108 | // borders 109 | put_hline(0, 0, cols - 1, '-'); 110 | put_hline(rows - 1, 0, cols - 1, '-'); 111 | put_vline(0, rows - 1, 0, '|'); 112 | put_vline(0, rows - 1, cols - 1, '|'); 113 | // corners 114 | put_char(0, 0, '+'); 115 | put_char(0, cols - 1, '+'); 116 | put_char(rows - 1, 0, '+'); 117 | put_char(rows - 1, cols - 1, '+'); 118 | // axes 119 | put_hline(rows / 2, 1, cols - 2, '.'); 120 | put_vline(1, rows - 2, cols / 2, '.'); 121 | 122 | // data points 123 | float rr = 0.0f; 124 | for (int c = 1; c < cols - 1; ++c) { 125 | float x = (float)(c - 1) / (float)(cols - 2); 126 | float y = lerp(x); 127 | float oldrr = rr; 128 | rr = 1 + 0.5f * (1 - y) * (rows - 2); 129 | put_char((int)std::lround(rr), c, '*'); 130 | if (c > 1) { 131 | int r1 = (int)std::lround(oldrr); 132 | int r2 = (int)std::lround(rr); 133 | int rinc = (r1 < r2) ? +1 : -1; 134 | for (int r = r1; r != r2; r += rinc) { 135 | float mu = (r - oldrr) / (rr - oldrr); 136 | float midrr = (1 - mu) * oldrr + mu * rr; 137 | put_char((int)std::lround(midrr), c, '*'); 138 | } 139 | } 140 | } 141 | 142 | // 143 | fputs(text.get(), stdout); 144 | } 145 | 146 | static WaveFormulaPtr create_formula(const char* expr) 147 | { 148 | WaveFormulaPtr formula; 149 | 150 | if (!formula) { 151 | formula.reset(new SeriesFormula(expr)); 152 | if (!formula->is_valid()) 153 | formula.reset(); 154 | } 155 | 156 | if (!formula) { 157 | formula.reset(new PartialsFormula(expr)); 158 | if (!formula->is_valid()) 159 | formula.reset(); 160 | } 161 | 162 | return formula; 163 | } 164 | 165 | int main(int argc, char *argv[]) 166 | { 167 | if (!parse_args(argc, argv)) 168 | return 1; 169 | 170 | // 171 | Wavetable wt; 172 | wt.count = 1; 173 | wt.frames = table_size; 174 | wt.data.reset(new float[wt.frames]); 175 | 176 | // 177 | WaveFormulaPtr formula = create_formula(expression); 178 | if (!formula) { 179 | std::cerr << "Invalid expression\n"; 180 | return 1; 181 | } 182 | formula->set_size(wt.frames); 183 | formula->set_amplitude_type(WaveFormula::AmplitudeType::normalized); 184 | formula->set_seed(default_seed); 185 | 186 | // 187 | const float* wave = formula->get_wave(); 188 | std::copy(wave, wave + wt.frames, wt.data.get()); 189 | 190 | // 191 | if (1) 192 | display_wave(wt.data.get(), wt.frames, 20, 80); 193 | 194 | // 195 | FILE *stream = fopen_utf8(output_filename, "wb"); 196 | if (!stream) { 197 | std::cerr << "Cannot open file for writing.\n"; 198 | return 1; 199 | } 200 | Wavetables::saveToWAVFile(stream, wt); 201 | int stream_err = ferror(stream); 202 | fclose(stream); 203 | 204 | if (stream_err) { 205 | std::cerr << "Could not write the output file.\n"; 206 | return 1; 207 | } 208 | 209 | return 0; 210 | } 211 | -------------------------------------------------------------------------------- /sources/WMorph.cpp: -------------------------------------------------------------------------------- 1 | #include "Wavetable.h" 2 | #include "utility/unicodefile.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /// 9 | #define DR_WAV_IMPLEMENTATION 10 | #include 11 | 12 | struct drwav_deleter { 13 | void operator()(drwav *x) const noexcept { drwav_uninit(x); delete x; } 14 | }; 15 | 16 | typedef std::unique_ptr drwav_u; 17 | 18 | /// 19 | namespace { 20 | int num_stages = 0; 21 | const char *input1_filename = nullptr; 22 | const char *input2_filename = nullptr; 23 | const char *output_filename = nullptr; 24 | bool no_last = false; 25 | } 26 | 27 | static bool parse_args(int argc, char *argv[]) 28 | { 29 | if (argc < 2) { 30 | std::cerr << "Usage: WMorph [-nolast]\n"; 31 | return false; 32 | } 33 | 34 | unsigned nth_positional_arg = 0; 35 | 36 | for (int argi = 1; argi < argc; ++argi) { 37 | const char *arg = argv[argi]; 38 | 39 | if (!strcmp(arg, "-nolast")) { 40 | no_last = true; 41 | } 42 | else { 43 | switch (nth_positional_arg) { 44 | case 0: 45 | num_stages = atoi(arg); 46 | break; 47 | case 1: 48 | input1_filename = arg; 49 | break; 50 | case 2: 51 | input2_filename = arg; 52 | break; 53 | case 3: 54 | output_filename = arg; 55 | break; 56 | } 57 | ++nth_positional_arg; 58 | } 59 | } 60 | 61 | if (nth_positional_arg != 4) { 62 | std::cerr << "Bad number of positional arguments\n"; 63 | return false; 64 | } 65 | 66 | if (num_stages < 3 || num_stages > 256) { 67 | std::cerr << "Invalid nuber of stages\n"; 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | 74 | int main(int argc, char *argv[]) 75 | { 76 | if (!parse_args(argc, argv)) 77 | return 1; 78 | 79 | drwav_u snd1(new drwav); 80 | if (!drwav_init_file(snd1.get(), input1_filename, nullptr)) { 81 | delete snd1.release(); 82 | std::cerr << "Cannot open sound file: " << input1_filename << '\n'; 83 | return 1; 84 | } 85 | drwav_u snd2(new drwav); 86 | if (!drwav_init_file(snd2.get(), input2_filename, nullptr)) { 87 | delete snd2.release(); 88 | std::cerr << "Cannot open sound file: " << input2_filename << '\n'; 89 | return 1; 90 | } 91 | 92 | // 93 | const uint64_t frames = snd1->totalPCMFrameCount; 94 | if (frames < 2 || frames > 8192) { 95 | std::cerr << "Invalid number of frames in input file 1.\n"; 96 | return 1; 97 | } 98 | if (frames != snd2->totalPCMFrameCount) { 99 | std::cerr << "The number of frames does not match in the input files.\n"; 100 | return 1; 101 | } 102 | if (snd1->channels != 1) { 103 | std::cerr << "The input file 1 does not contain exactly one audio channel.\n"; 104 | return 1; 105 | } 106 | if (snd2->channels != 1) { 107 | std::cerr << "The input file 2 does not contain exactly one audio channel.\n"; 108 | return 1; 109 | } 110 | 111 | // 112 | std::unique_ptr wave1(new float[frames]); 113 | std::unique_ptr wave2(new float[frames]); 114 | 115 | if (drwav_read_pcm_frames_f32(snd1.get(), frames, wave1.get()) != frames) { 116 | std::cerr << "Cannot read the input file 1.\n"; 117 | return 1; 118 | } 119 | if (drwav_read_pcm_frames_f32(snd2.get(), frames, wave2.get()) != frames) { 120 | std::cerr << "Cannot read the input file 2.\n"; 121 | return 1; 122 | } 123 | 124 | snd1.reset(); 125 | snd2.reset(); 126 | 127 | // 128 | const unsigned num_stages = ::num_stages; 129 | const unsigned num_stages_written = num_stages - (no_last ? 1 : 0); 130 | 131 | Wavetable wt; 132 | wt.count = num_stages_written; 133 | wt.frames = frames; 134 | wt.data.reset(new float[num_stages_written * frames]); 135 | 136 | for (unsigned t = 0; t < num_stages_written; ++t) { 137 | float *table = &wt.data[t * frames]; 138 | float ratio = float(t) / float(num_stages - 1); 139 | for (unsigned i = 0; i < frames; ++i) 140 | table[i] = (1 - ratio) * wave1[i] + ratio * wave2[i]; 141 | } 142 | 143 | FILE *stream = fopen_utf8(output_filename, "wb"); 144 | if (!stream) { 145 | std::cerr << "Cannot open file for writing.\n"; 146 | return 1; 147 | } 148 | Wavetables::saveToWAVFile(stream, wt); 149 | int stream_err = ferror(stream); 150 | fclose(stream); 151 | 152 | if (stream_err) { 153 | std::cerr << "Could not write the output file.\n"; 154 | return 1; 155 | } 156 | 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /sources/WaveFormula.cpp: -------------------------------------------------------------------------------- 1 | #include "WaveFormula.h" 2 | #include "Random.h" 3 | #include "utility/Locale.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if !defined(M_PI) 15 | # define M_PI 3.14159265358979323846 16 | #endif 17 | 18 | void WaveFormula::set_size(unsigned size) 19 | { 20 | if (size_ == size) 21 | return; 22 | size_ = size; 23 | invalidate(); 24 | } 25 | 26 | void WaveFormula::set_amplitude_type(AmplitudeType amptype) 27 | { 28 | if (amptype_ == amptype) 29 | return; 30 | amptype_ = amptype; 31 | invalidate(); 32 | } 33 | 34 | void WaveFormula::set_seed(long seed) 35 | { 36 | if (seed_ == seed) 37 | return; 38 | seed_ = seed; 39 | invalidate(); 40 | } 41 | 42 | float* WaveFormula::get_wave() const 43 | { 44 | float* wave = wave_.get(); 45 | const unsigned size = size_; 46 | if (!wave) { 47 | wave = new float[size]; 48 | wave_.reset(wave); 49 | compute_wave(wave); 50 | switch (amptype_) { 51 | default: 52 | case AmplitudeType::raw: 53 | break; 54 | case AmplitudeType::normalized: 55 | normalize_wave(wave, size); 56 | break; 57 | case AmplitudeType::saturated: 58 | saturate_wave(wave, size); 59 | break; 60 | } 61 | } 62 | return wave; 63 | } 64 | 65 | void WaveFormula::invalidate() const 66 | { 67 | wave_.reset(); 68 | } 69 | 70 | void WaveFormula::normalize_wave(float* wave, unsigned size) 71 | { 72 | float max_amplitude = 0.0; 73 | for (unsigned i = 0; i < size; ++i) 74 | max_amplitude = std::max(std::fabs(wave[i]), max_amplitude); 75 | if (max_amplitude > 0.0f) { 76 | for (unsigned i = 0; i < size; ++i) 77 | wave[i] = std::max(-1.0f, std::min(1.0f, wave[i] / max_amplitude)); 78 | } 79 | } 80 | 81 | void WaveFormula::saturate_wave(float* wave, unsigned size) 82 | { 83 | for (unsigned i = 0; i < size; ++i) 84 | wave[i] = std::max(-1.0f, std::min(1.0f, wave[i])); 85 | } 86 | 87 | //------------------------------------------------------------------------------ 88 | std::complex* SpectralFormula::get_spectrum() const 89 | { 90 | std::complex* spec = spec_.get(); 91 | const unsigned size = get_size(); 92 | if (!spec) { 93 | spec = new std::complex[size / 2 + 1]; 94 | spec_.reset(spec); 95 | compute_spectrum(spec); 96 | } 97 | return spec; 98 | } 99 | 100 | void SpectralFormula::invalidate() const 101 | { 102 | WaveFormula::invalidate(); 103 | spec_.reset(); 104 | } 105 | 106 | void SpectralFormula::compute_wave(float* wave) const 107 | { 108 | std::complex* spec = get_spectrum(); 109 | const unsigned size = get_size(); 110 | 111 | static_assert(std::is_same::value, 112 | "The kissfft scalar is not matching"); 113 | 114 | kiss_fftr_cfg cfg = kiss_fftr_alloc(size, 1, nullptr, nullptr); 115 | if (!cfg) 116 | throw std::runtime_error("Cannot create the kissfft configuration"); 117 | kiss_fftri(cfg, reinterpret_cast(spec), wave); 118 | kiss_fftr_free(cfg); 119 | } 120 | 121 | //------------------------------------------------------------------------------ 122 | SeriesFormula::SeriesFormula(const char* text) 123 | { 124 | std::array expr = Expr::parse(text); 125 | mag_expr_ = std::move(expr[0]); 126 | phase_expr_ = std::move(expr[1]); 127 | } 128 | 129 | bool SeriesFormula::is_valid() const 130 | { 131 | return mag_expr_ != nullptr; 132 | } 133 | 134 | void SeriesFormula::compute_spectrum(std::complex* spec) const 135 | { 136 | const unsigned size = get_size(); 137 | Expr* mag_expr = mag_expr_.get(); 138 | Expr* phase_expr = phase_expr_.get(); 139 | 140 | if (!mag_expr) { 141 | std::fill(spec, spec + size / 2 + 1, 0.0f); 142 | return; 143 | } 144 | 145 | ExprContext ctx; 146 | 147 | RandomGenerator mag_prng; 148 | RandomGenerator phase_prng; 149 | mag_prng.seed(get_seed()); 150 | phase_prng.seed(get_seed()); 151 | 152 | spec[0] = 0.0f; 153 | for (unsigned i = 1; i < size / 2 + 1; ++i) { 154 | ctx.vars["x"] = expr_float_t(i); 155 | 156 | ctx.prng = &mag_prng; 157 | expr_float_t mod = mag_expr->evalInterpreted(ctx); 158 | 159 | expr_float_t arg = 0.0; 160 | if (phase_expr) { 161 | ctx.prng = &phase_prng; 162 | arg = phase_expr->evalInterpreted(ctx) * expr_float_t(M_PI * 2); 163 | } 164 | 165 | spec[i] = std::polar(expr_float_t(0.5) * mod, expr_float_t(-M_PI / 2) + arg); 166 | } 167 | } 168 | 169 | //------------------------------------------------------------------------------ 170 | PartialsFormula::PartialsFormula(const char* text) 171 | { 172 | partials_.reset(parse(text, &num_partials_)); 173 | } 174 | 175 | bool PartialsFormula::is_valid() const 176 | { 177 | return partials_ != nullptr; 178 | } 179 | 180 | void PartialsFormula::compute_spectrum(std::complex* spec) const 181 | { 182 | const std::complex* partials = partials_.get(); 183 | if (!partials) 184 | return; 185 | 186 | const size_t spec_size = get_size() / 2 + 1; 187 | const size_t num_partials = num_partials_; 188 | 189 | std::copy(partials, partials + num_partials, spec); 190 | std::fill(spec + num_partials, spec + spec_size, 0.0f); 191 | } 192 | 193 | std::complex* PartialsFormula::parse(const char* text, unsigned* num_partials_ptr) 194 | { 195 | unsigned fill_index; 196 | const char *pos = text; 197 | 198 | std::vector mag; 199 | std::vector phase; 200 | 201 | mag.reserve(64); 202 | phase.reserve(64); 203 | 204 | // 205 | #if !defined(_WIN32) 206 | Locale c_locale(newlocale(LC_ALL_MASK, "C", (locale_t)0)); 207 | #else 208 | Locale c_locale(_create_locale(LC_ALL, "C")); 209 | #endif 210 | if (!c_locale) 211 | throw std::system_error(errno, std::generic_category()); 212 | 213 | // 214 | static auto skip_ws = +[](const char *&pos) { 215 | auto is_ws = [](char c) -> bool { 216 | return c == ' ' || c == '\t' || c == '\r' || c == '\n'; 217 | }; 218 | for (char c; (c = *pos) != '\0' && is_ws(c); ++pos); 219 | }; 220 | 221 | static auto extract_number = +[](const char *&pos, float &num, Locale::handle_type loc) -> bool { 222 | unsigned count; 223 | #if !defined(_WIN32) 224 | loc = uselocale(loc); 225 | int ret = sscanf(pos, "%f%n", &num, &count); 226 | uselocale(loc); 227 | #else 228 | int ret = _sscanf_l(pos, "%f%n", loc, &num, &count); 229 | #endif 230 | if (ret != 1) 231 | return false; 232 | pos += count; 233 | return true; 234 | }; 235 | 236 | static auto extract_char = +[](const char *&pos, char ch) -> bool { 237 | if (*pos != ch) 238 | return false; 239 | ++pos; 240 | return true; 241 | }; 242 | 243 | // 244 | mag.push_back(0.0f); 245 | phase.push_back(0.0f); 246 | 247 | fill_index = 0; 248 | for (unsigned i = 1;; ++i) { 249 | skip_ws(pos); 250 | if (i > 1) { 251 | if (!extract_char(pos, ',')) 252 | break; 253 | } 254 | float value = 0.0f; 255 | extract_number(pos, value, *c_locale); 256 | mag.push_back(value); 257 | } 258 | 259 | // 260 | skip_ws(pos); 261 | if (extract_char(pos, ';')) { 262 | for (unsigned i = 1;; ++i) { 263 | skip_ws(pos); 264 | if (i > 1) { 265 | if (!extract_char(pos, ',')) 266 | break; 267 | } 268 | float value = 0.0f; 269 | extract_number(pos, value, *c_locale); 270 | phase.push_back(value * float(M_PI / 2)); 271 | } 272 | } 273 | 274 | // 275 | skip_ws(pos); 276 | if (*pos != '\0') 277 | return nullptr; 278 | 279 | // 280 | while (!mag.empty() && mag.back() == 0.0f) 281 | mag.pop_back(); 282 | size_t num_partials = mag.size(); 283 | phase.resize(num_partials); 284 | 285 | // 286 | std::unique_ptr[]> partials(new std::complex[num_partials]); 287 | for (unsigned i = 1; i < num_partials; ++i) 288 | partials[i] = std::polar(0.5f * mag[i], float(-M_PI / 2) + phase[i]); 289 | 290 | *num_partials_ptr = num_partials; 291 | return partials.release(); 292 | } 293 | -------------------------------------------------------------------------------- /sources/WaveFormula.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "series/SeriesExpr.h" 3 | #include 4 | #include 5 | 6 | class WaveFormula; 7 | typedef std::shared_ptr WaveFormulaPtr; 8 | 9 | //------------------------------------------------------------------------------ 10 | class WaveFormula : public std::enable_shared_from_this { 11 | protected: 12 | WaveFormula() = default; 13 | 14 | public: 15 | enum class AmplitudeType { 16 | raw, 17 | normalized, 18 | saturated, 19 | }; 20 | 21 | public: 22 | virtual ~WaveFormula() {} 23 | virtual bool is_valid() const = 0; 24 | unsigned get_size() const { return size_; } 25 | void set_size(unsigned size); 26 | AmplitudeType get_amplitude_type() const { return amptype_; } 27 | void set_amplitude_type(AmplitudeType amptype); 28 | long get_seed() const { return seed_; } 29 | void set_seed(long seed); 30 | float* get_wave() const; 31 | 32 | protected: 33 | virtual void invalidate() const; 34 | virtual void compute_wave(float* wave) const = 0; 35 | 36 | private: 37 | static void normalize_wave(float* wave, unsigned size); 38 | static void saturate_wave(float* wave, unsigned size); 39 | 40 | private: 41 | unsigned size_ = 2048; 42 | AmplitudeType amptype_ = AmplitudeType::raw; 43 | long seed_ = 0; 44 | mutable std::unique_ptr wave_; 45 | }; 46 | 47 | //------------------------------------------------------------------------------ 48 | class SpectralFormula : public WaveFormula { 49 | protected: 50 | SpectralFormula() = default; 51 | 52 | public: 53 | virtual ~SpectralFormula() {} 54 | std::complex* get_spectrum() const; 55 | 56 | protected: 57 | void invalidate() const override; 58 | void compute_wave(float* wave) const override; 59 | virtual void compute_spectrum(std::complex* spec) const = 0; 60 | 61 | private: 62 | mutable std::unique_ptr[]> spec_; 63 | }; 64 | 65 | //------------------------------------------------------------------------------ 66 | class SeriesFormula : public SpectralFormula { 67 | public: 68 | explicit SeriesFormula(const char* text); 69 | bool is_valid() const override; 70 | 71 | protected: 72 | void compute_spectrum(std::complex* spec) const override; 73 | 74 | private: 75 | ExprPtr mag_expr_; 76 | ExprPtr phase_expr_; 77 | }; 78 | 79 | //------------------------------------------------------------------------------ 80 | class PartialsFormula : public SpectralFormula { 81 | public: 82 | explicit PartialsFormula(const char* text); 83 | bool is_valid() const override; 84 | 85 | protected: 86 | void compute_spectrum(std::complex* spec) const override; 87 | 88 | private: 89 | static std::complex* parse(const char* text, unsigned* num_partials_ptr); 90 | 91 | private: 92 | unsigned num_partials_ = 0; 93 | std::unique_ptr[]> partials_; 94 | }; 95 | -------------------------------------------------------------------------------- /sources/Wavetable.cpp: -------------------------------------------------------------------------------- 1 | #include "Wavetable.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | static std::array leImpl(T x) 9 | { 10 | typename std::make_unsigned::type u = x; 11 | constexpr size_t n = sizeof(T); 12 | std::array b; 13 | for (size_t i = 0; i < n; ++i) 14 | b[i] = uint8_t(u >> (8 * i)); 15 | return b; 16 | } 17 | 18 | static std::array le16(uint16_t x) { return leImpl(x); } 19 | static std::array le32(uint32_t x) { return leImpl(x); } 20 | 21 | static size_t writeLE16(FILE *stream, uint16_t x) 22 | { 23 | return fwrite(reinterpret_cast(le16(x).data()), 1, sizeof(x), stream); 24 | } 25 | static size_t writeLE32(FILE *stream, uint32_t x) 26 | { 27 | return fwrite(reinterpret_cast(le32(x).data()), 1, sizeof(x), stream); 28 | } 29 | static size_t writeF32(FILE *stream, float x) 30 | { 31 | union { uint32_t i; float f; } u; 32 | u.f = x; 33 | return writeLE32(stream, u.i); 34 | } 35 | 36 | void Wavetables::saveToWAVFile( 37 | FILE *stream, const Wavetable &wt, 38 | const char *chunkId, const void *chunkData, uint32_t chunkSize) 39 | { 40 | const unsigned sampleRate = 44100; 41 | const unsigned channels = 1; 42 | 43 | fwrite("RIFF", 1, 4, stream); 44 | fwrite("\0\0\0\0", 1, 4, stream); 45 | off_t riffStart = ftell(stream); 46 | fwrite("WAVE", 1, 4, stream); 47 | 48 | // fmt chunk 49 | fwrite("fmt ", 1, 4, stream); 50 | fwrite("\0\0\0\0", 1, 4, stream); 51 | off_t fmtStart = ftell(stream); 52 | writeLE16(stream, 3); // float 32 bits 53 | writeLE16(stream, channels); // mono 54 | writeLE32(stream, sampleRate); // sample rate 55 | writeLE32(stream, sampleRate * channels * sizeof(float)); // bytes per second 56 | writeLE16(stream, channels * sizeof(float)); // frame alignment 57 | writeLE16(stream, 8 * sizeof(float)); // bits per sample 58 | writeLE16(stream, 0); // extension size 59 | off_t fmtEnd = ftell(stream); 60 | 61 | // fact chunk 62 | fwrite("fact", 1, 4, stream); 63 | writeLE32(stream, 4); 64 | writeLE32(stream, wt.count * wt.frames); 65 | 66 | // data chunk 67 | fwrite("data", 1, 4, stream); 68 | writeLE32(stream, channels * wt.count * wt.frames * sizeof(float)); 69 | for (unsigned i = 0, n = channels * wt.count * wt.frames; i < n; ++i) { 70 | float sample = wt.data[i]; 71 | writeF32(stream, sample); 72 | } 73 | 74 | // clm chunk 75 | if (wt.frames <= 8192) { 76 | fwrite("clm ", 1, 4, stream); 77 | // 78 | std::string data; 79 | data.reserve(256); 80 | data.append(""); 81 | data.push_back("0123456789"[(wt.frames / 1000) % 10]); 82 | data.push_back("0123456789"[(wt.frames / 100) % 10]); 83 | data.push_back("0123456789"[(wt.frames / 10) % 10]); 84 | data.push_back("0123456789"[wt.frames % 10]); 85 | data.push_back(' '); 86 | data.append("10000000"); 87 | data.push_back(' '); 88 | data.append("wavetable (jpcima.sdf1.org)"); 89 | // 90 | writeLE32(stream, data.size()); 91 | fwrite(data.data(), 1, data.size(), stream); 92 | if (data.size() & 1) 93 | fputc('\0', stream); 94 | } 95 | 96 | // extra chunk 97 | if (chunkId) { 98 | fwrite(chunkId, 1, 4, stream); 99 | writeLE32(stream, chunkSize); 100 | fwrite(chunkData, 1, chunkSize, stream); 101 | if (chunkSize & 1) 102 | fputc('\0', stream); 103 | } 104 | 105 | off_t riffEnd = ftell(stream); 106 | 107 | fflush(stream); 108 | fseek(stream, fmtStart - 4, SEEK_SET); 109 | writeLE32(stream, fmtEnd - fmtStart); 110 | fflush(stream); 111 | fseek(stream, riffStart - 4, SEEK_SET); 112 | writeLE32(stream, riffEnd - riffStart); 113 | fflush(stream); 114 | } 115 | -------------------------------------------------------------------------------- /sources/Wavetable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | struct Wavetable { 7 | unsigned count = 0; // number of subtables 8 | unsigned frames = 0; // number of frames per subtable 9 | std::unique_ptr data; // wave data [count * frames] 10 | }; 11 | 12 | typedef std::shared_ptr Wavetable_s; 13 | typedef std::unique_ptr Wavetable_u; 14 | 15 | namespace Wavetables { 16 | void saveToWAVFile( 17 | FILE *stream, const Wavetable &wt, 18 | const char *chunkId = nullptr, const void *chunkData = nullptr, uint32_t chunkSize = 0); 19 | } 20 | -------------------------------------------------------------------------------- /sources/interactive/dsp/Buffer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // Copyright (c) 2019-2020, Paul Ferrand, Andrea Zanellato 4 | // All rights reserved. 5 | 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | 9 | // 1. Redistributions of source code must retain the above copyright notice, this 10 | // list of conditions and the following disclaimer. 11 | // 2. Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #pragma once 27 | #include "Config.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #ifndef NDEBUG 37 | #include 38 | #endif 39 | namespace sfz 40 | { 41 | 42 | /** 43 | * @brief A buffer counting class that tries to track the memory usage. 44 | */ 45 | class BufferCounter 46 | { 47 | public: 48 | /** 49 | * @brief Return the buffer counter object. 50 | */ 51 | static BufferCounter& counter() noexcept 52 | { 53 | static BufferCounter counter; 54 | return counter; 55 | } 56 | 57 | protected: 58 | BufferCounter() noexcept = default; 59 | ~BufferCounter() noexcept 60 | { 61 | #ifndef NDEBUG 62 | std::cout << "Remaining buffers on destruction: " << numBuffers << '\n'; 63 | std::cout << "Total size: " << bytes << '\n'; 64 | #endif 65 | } 66 | 67 | public: 68 | template 69 | void newBuffer(I size) noexcept 70 | { 71 | ++numBuffers; 72 | bytes.fetch_add(static_cast(size)); 73 | } 74 | 75 | template 76 | void bufferResized(I oldSize, I newSize) noexcept 77 | { 78 | bytes.fetch_add(static_cast(newSize)); 79 | bytes.fetch_sub(static_cast(oldSize)); 80 | } 81 | 82 | template 83 | void bufferDeleted(I size) noexcept 84 | { 85 | --numBuffers; 86 | bytes.fetch_sub(static_cast(size)); 87 | } 88 | 89 | size_t getNumBuffers() const noexcept { return numBuffers; } 90 | size_t getTotalBytes() const noexcept { return bytes; } 91 | private: 92 | std::atomic numBuffers { 0 }; 93 | std::atomic bytes { 0 }; 94 | }; 95 | 96 | 97 | 98 | /** 99 | * @brief A heap buffer structure that tries to align its beginning and 100 | * adds a small offset at the end for alignment too. 101 | * 102 | * Apparently on Linux this effort is mostly useless, and in the end 103 | * most of the SIMD operations are coded with alignment checks and 104 | * sentinels so this class could probably be much simpler. It does 105 | * however wrap realloc which in some cases should be a bit more 106 | * efficient than allocating a whole new block. 107 | * 108 | * @tparam Type The buffer type 109 | * @tparam Alignment the required alignment in bytes (defaults to 110 | * config::defaultAlignment) 111 | */ 112 | template 113 | class Buffer { 114 | public: 115 | using value_type = typename std::remove_cv::type; 116 | using pointer = value_type*; 117 | using const_pointer = const value_type*; 118 | using reference = value_type&; 119 | using const_reference = const value_type&; 120 | using iterator = pointer; 121 | using const_iterator = const_pointer; 122 | using reverse_iterator = std::reverse_iterator; 123 | using const_reverse_iterator = std::reverse_iterator; 124 | using size_type = size_t; 125 | 126 | /** 127 | * @brief Construct a new Buffer object that is empty 128 | * 129 | */ 130 | Buffer() noexcept 131 | { 132 | } 133 | 134 | /** 135 | * @brief Construct a new Buffer object with size 136 | * 137 | * @param size 138 | */ 139 | explicit Buffer(size_t size) 140 | { 141 | resize(size); 142 | } 143 | 144 | /** 145 | * @brief Resizes the buffer. Given that std::realloc may return either the same pointer 146 | * or a new one, you need to account for both cases in the code if you are using deep pointers. 147 | * 148 | * @param newSize the new buffer size in bytes 149 | * @return true if allocation succeeded 150 | * @return false otherwise 151 | */ 152 | bool resize(size_t newSize, std::nothrow_t) noexcept 153 | { 154 | if (newSize == 0) { 155 | clear(); 156 | return true; 157 | } 158 | 159 | Type* oldData = paddedData.get(); 160 | Type* oldNormalData = normalData; 161 | std::size_t oldSize = alignedSize; 162 | 163 | std::size_t tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end 164 | Type* newData = reinterpret_cast(std::calloc(tempSize, sizeof(value_type))); 165 | if (newData == nullptr) { 166 | return false; 167 | } 168 | 169 | if (largerSize > 0) 170 | counter().bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type)); 171 | else 172 | counter().newBuffer(tempSize * sizeof(value_type)); 173 | 174 | largerSize = tempSize; 175 | alignedSize = newSize; 176 | paddedData.release(); // realloc has invalidated the old pointer 177 | paddedData.reset(static_cast(newData)); 178 | normalData = static_cast(align(Alignment, alignedSize, newData, tempSize)); 179 | normalEnd = normalData + alignedSize; 180 | std::size_t endMisalignment = (alignedSize & TypeAlignmentMask); 181 | if (endMisalignment != 0) 182 | _alignedEnd = normalEnd + Alignment - endMisalignment; 183 | else 184 | _alignedEnd = normalEnd; 185 | 186 | std::memcpy(normalData, oldNormalData, std::min(newSize, oldSize) * sizeof(Type)); 187 | std::free(oldData); 188 | 189 | return true; 190 | } 191 | 192 | /** 193 | * @brief Resizes the buffer. Given that std::realloc may return either the same pointer 194 | * or a new one, you need to account for both cases in the code if you are using deep pointers. 195 | * 196 | * @param newSize the new buffer size in bytes 197 | */ 198 | void resize(size_t newSize) 199 | { 200 | if (!resize(newSize, std::nothrow)) 201 | throw std::bad_alloc(); 202 | } 203 | 204 | /** 205 | * @brief Clear the buffers and frees the underlying memory 206 | * 207 | */ 208 | void clear() noexcept 209 | { 210 | if (largerSize > 0) 211 | counter().bufferDeleted(largerSize * sizeof(value_type)); 212 | _clear(); 213 | } 214 | ~Buffer() noexcept 215 | { 216 | if (largerSize > 0) 217 | counter().bufferDeleted(largerSize * sizeof(value_type)); 218 | } 219 | 220 | /** 221 | * @brief Get the size of the larger block which is actually allocated 222 | */ 223 | size_t allocationSize() const noexcept 224 | { 225 | return largerSize; 226 | } 227 | 228 | /** 229 | * @brief Construct a new Buffer object from an existing one 230 | * 231 | * @param other 232 | */ 233 | Buffer(const Buffer& other) 234 | { 235 | resize(other.size()); 236 | std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); 237 | } 238 | 239 | /** 240 | * @brief Construct a new Buffer object by moving an existing one 241 | * 242 | * @param other 243 | */ 244 | Buffer(Buffer&& other) noexcept 245 | : largerSize(other.largerSize), 246 | alignedSize(other.alignedSize), 247 | normalData(other.normalData), 248 | paddedData(std::move(other.paddedData)), 249 | normalEnd(other.normalEnd), 250 | _alignedEnd(other._alignedEnd) 251 | { 252 | other._clear(); 253 | } 254 | 255 | Buffer& operator=(const Buffer& other) 256 | { 257 | if (this != &other) { 258 | resize(other.size()); 259 | std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); 260 | } 261 | return *this; 262 | } 263 | 264 | Buffer& operator=(Buffer&& other) noexcept 265 | { 266 | if (this != &other) { 267 | if (largerSize > 0) 268 | counter().bufferDeleted(largerSize * sizeof(value_type)); 269 | largerSize = other.largerSize; 270 | alignedSize = other.alignedSize; 271 | normalData = other.normalData; 272 | paddedData = std::move(other.paddedData); 273 | normalEnd = other.normalEnd; 274 | _alignedEnd = other._alignedEnd; 275 | other._clear(); 276 | } 277 | return *this; 278 | } 279 | 280 | Type& operator[](size_t idx) noexcept { return *(normalData + idx); } 281 | constexpr pointer data() const noexcept { return normalData; } 282 | constexpr size_type size() const noexcept { return alignedSize; } 283 | constexpr bool empty() const noexcept { return alignedSize == 0; } 284 | constexpr iterator begin() const noexcept { return data(); } 285 | constexpr iterator end() const noexcept { return normalEnd; } 286 | constexpr pointer alignedEnd() const noexcept { return _alignedEnd; } 287 | 288 | /** 289 | * @brief Return the buffer counter object. 290 | * 291 | * @return The buffer counter on which you can call various methods. 292 | */ 293 | static BufferCounter& counter() noexcept 294 | { 295 | return BufferCounter::counter(); 296 | } 297 | 298 | private: 299 | void _clear() 300 | { 301 | largerSize = 0; 302 | alignedSize = 0; 303 | paddedData.reset(); 304 | normalData = nullptr; 305 | normalEnd = nullptr; 306 | _alignedEnd = nullptr; 307 | } 308 | 309 | private: 310 | static constexpr int AlignmentMask { Alignment - 1 }; 311 | static constexpr int TypeAlignment { Alignment / sizeof(value_type) }; 312 | static constexpr int TypeAlignmentMask { TypeAlignment - 1 }; 313 | static_assert(std::is_trivial::value, "Type should be trivial"); 314 | static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16 || Alignment == 32, "Bad alignment value"); 315 | static_assert(TypeAlignment * sizeof(value_type) == Alignment || !std::is_arithmetic::value, 316 | "The alignment does not appear to be divided by the size of the arithmetic Type"); 317 | void* align(std::size_t alignment, std::size_t size, void *ptr, std::size_t &space) 318 | { 319 | std::uintptr_t pn = reinterpret_cast(ptr); 320 | std::uintptr_t aligned = (pn + alignment - 1) & - alignment; 321 | std::size_t padding = aligned - pn; 322 | if (space < size + padding) return nullptr; 323 | space -= padding; 324 | return reinterpret_cast(aligned); 325 | } 326 | 327 | struct deleter { 328 | void operator()(void *p) const noexcept { std::free(p); } 329 | }; 330 | 331 | size_type largerSize { 0 }; 332 | size_type alignedSize { 0 }; 333 | pointer normalData { nullptr }; 334 | std::unique_ptr paddedData; 335 | pointer normalEnd { nullptr }; 336 | pointer _alignedEnd { nullptr }; 337 | }; 338 | 339 | } 340 | -------------------------------------------------------------------------------- /sources/interactive/dsp/Config.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // This code is part of the sfizz library and is licensed under a BSD 2-clause 4 | // license. You should have receive a LICENSE.md file along with the code. 5 | // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz 6 | 7 | #pragma once 8 | 9 | namespace sfz { 10 | 11 | namespace config { 12 | 13 | constexpr unsigned int defaultAlignment { 16 }; 14 | 15 | constexpr unsigned tableSize = 1024; 16 | constexpr double tableRefSampleRate = 44100.0 * 1.1; // +10% aliasing permissivity 17 | 18 | } // namespace config 19 | 20 | } // namespace sfz 21 | -------------------------------------------------------------------------------- /sources/interactive/dsp/Interpolators.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // This code is part of the sfizz library and is licensed under a BSD 2-clause 4 | // license. You should have receive a LICENSE.md file along with the code. 5 | // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz 6 | 7 | #pragma once 8 | 9 | namespace sfz { 10 | 11 | enum InterpolatorModel : int { 12 | // a nearest interpolator 13 | kInterpolatorNearest, 14 | // a linear interpolator 15 | kInterpolatorLinear, 16 | // a Hermite 3rd order interpolator 17 | kInterpolatorHermite3, 18 | // a B-spline 3rd order interpolator 19 | kInterpolatorBspline3, 20 | }; 21 | 22 | /** 23 | * @brief Interpolate from a vector of values 24 | * 25 | * @tparam M the interpolator model 26 | * @tparam R the sample type 27 | * @param values Pointer to a value in a larger vector of values. 28 | * Depending on the interpolator the algorithm may 29 | * read samples before and after. Usually you need 30 | * to ensure that you have order - 1 samples available 31 | * before and after the pointer, padding if necessary. 32 | * @param coeff the interpolation coefficient 33 | * @return R 34 | */ 35 | template 36 | R interpolate(const R* values, R coeff); 37 | 38 | } // namespace sfz 39 | 40 | #include "Interpolators.hpp" 41 | -------------------------------------------------------------------------------- /sources/interactive/dsp/Interpolators.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // This code is part of the sfizz library and is licensed under a BSD 2-clause 4 | // license. You should have receive a LICENSE.md file along with the code. 5 | // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz 6 | 7 | #include "Interpolators.h" 8 | #include "MathHelpers.h" 9 | #include "SIMDConfig.h" 10 | #if SFIZZ_HAVE_SSE 11 | #include 12 | #endif 13 | 14 | namespace sfz { 15 | 16 | template 17 | class Interpolator; 18 | 19 | template 20 | inline R interpolate(const R* values, R coeff) 21 | { 22 | return Interpolator::process(values, coeff); 23 | } 24 | 25 | //------------------------------------------------------------------------------ 26 | // Nearest 27 | 28 | template 29 | class Interpolator 30 | { 31 | public: 32 | static inline R process(const R* values, R coeff) 33 | { 34 | return values[coeff > static_cast(0.5)]; 35 | } 36 | }; 37 | 38 | //------------------------------------------------------------------------------ 39 | // Linear 40 | 41 | template 42 | class Interpolator 43 | { 44 | public: 45 | static inline R process(const R* values, R coeff) 46 | { 47 | return values[0] * (static_cast(1.0) - coeff) + values[1] * coeff; 48 | } 49 | }; 50 | 51 | //------------------------------------------------------------------------------ 52 | // Hermite 3rd order, SSE specialization 53 | 54 | #if SFIZZ_HAVE_SSE 55 | template <> 56 | class Interpolator 57 | { 58 | public: 59 | static inline float process(const float* values, float coeff) 60 | { 61 | __m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff)); 62 | __m128 h = hermite3x4(x); 63 | __m128 y = _mm_mul_ps(h, _mm_loadu_ps(values - 1)); 64 | // sum 4 to 1 65 | __m128 xmm0 = y; 66 | __m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5); 67 | __m128 xmm2 = _mm_movehl_ps(xmm0, xmm0); 68 | xmm1 = _mm_add_ss(xmm1, xmm0); 69 | xmm0 = _mm_shuffle_ps(xmm0, xmm0, 0xe7); 70 | xmm2 = _mm_add_ss(xmm2, xmm1); 71 | xmm0 = _mm_add_ss(xmm0, xmm2); 72 | return _mm_cvtss_f32(xmm0); 73 | } 74 | }; 75 | #endif 76 | 77 | //------------------------------------------------------------------------------ 78 | // Hermite 3rd order, generic 79 | 80 | template 81 | class Interpolator 82 | { 83 | public: 84 | static inline R process(const R* values, R coeff) 85 | { 86 | R y = 0; 87 | for (int i = -1; i < 3; ++i) { 88 | R h = hermite3(i - coeff); 89 | y += h * values[i]; 90 | } 91 | return y; 92 | } 93 | }; 94 | 95 | //------------------------------------------------------------------------------ 96 | // B-spline 3rd order, SSE specialization 97 | 98 | #if SFIZZ_HAVE_SSE 99 | template <> 100 | class Interpolator 101 | { 102 | public: 103 | static inline float process(const float* values, float coeff) 104 | { 105 | __m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff)); 106 | __m128 h = bspline3x4(x); 107 | __m128 y = _mm_mul_ps(h, _mm_loadu_ps(values - 1)); 108 | // sum 4 to 1 109 | __m128 xmm0 = y; 110 | __m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5); 111 | __m128 xmm2 = _mm_movehl_ps(xmm0, xmm0); 112 | xmm1 = _mm_add_ss(xmm1, xmm0); 113 | xmm0 = _mm_shuffle_ps(xmm0, xmm0, 0xe7); 114 | xmm2 = _mm_add_ss(xmm2, xmm1); 115 | xmm0 = _mm_add_ss(xmm0, xmm2); 116 | return _mm_cvtss_f32(xmm0); 117 | } 118 | }; 119 | #endif 120 | 121 | //------------------------------------------------------------------------------ 122 | // B-spline 3rd order, generic 123 | 124 | template 125 | class Interpolator 126 | { 127 | public: 128 | static inline R process(const R* values, R coeff) 129 | { 130 | R y = 0; 131 | for (int i = -1; i < 3; ++i) { 132 | R h = bspline3(i - coeff); 133 | y += h * values[i]; 134 | } 135 | return y; 136 | } 137 | }; 138 | 139 | } // namespace sfz 140 | -------------------------------------------------------------------------------- /sources/interactive/dsp/MathHelpers.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // This code is part of the sfizz library and is licensed under a BSD 2-clause 4 | // license. You should have receive a LICENSE.md file along with the code. 5 | // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz 6 | 7 | #pragma once 8 | #include "SIMDConfig.h" 9 | #include 10 | #if SFIZZ_HAVE_SSE 11 | #include 12 | #endif 13 | 14 | template 15 | constexpr T clamp(T v, T lo, T hi) 16 | { 17 | return std::max(std::min(v, hi), lo); 18 | } 19 | 20 | /** 21 | * @brief Compute the 3rd-order Hermite interpolation polynomial. 22 | * 23 | * @tparam R 24 | * @param x 25 | * @return R 26 | */ 27 | template 28 | R hermite3(R x) 29 | { 30 | x = std::abs(x); 31 | R x2 = x * x; 32 | R x3 = x2 * x; 33 | R y = 0; 34 | R q = R(5./2.) * x2; // a reoccurring term 35 | R p1 = R(1) - q + R(3./2.) * x3; 36 | R p2 = R(2) - R(4) * x + q - R(1./2.) * x3; 37 | y = (x < R(2)) ? p2 : y; 38 | y = (x < R(1)) ? p1 : y; 39 | return y; 40 | } 41 | 42 | #if SFIZZ_HAVE_SSE 43 | /** 44 | * @brief Compute 4 parallel elements of the 3rd-order Hermite interpolation polynomial. 45 | * 46 | * @param x 47 | * @return __m128 48 | */ 49 | inline __m128 hermite3x4(__m128 x) 50 | { 51 | x = _mm_andnot_ps(_mm_set1_ps(-0.0f), x); 52 | __m128 x2 = _mm_mul_ps(x, x); 53 | __m128 x3 = _mm_mul_ps(x2, x); 54 | __m128 y = _mm_set1_ps(0.0f); 55 | __m128 q = _mm_mul_ps(_mm_set1_ps(5./2.), x2); 56 | __m128 p1 = _mm_add_ps(_mm_sub_ps(_mm_set1_ps(1), q), _mm_mul_ps(_mm_set1_ps(3./2.), x3)); 57 | __m128 p2 = _mm_sub_ps(_mm_add_ps(_mm_sub_ps(_mm_set1_ps(2), _mm_mul_ps(_mm_set1_ps(4), x)), q), _mm_mul_ps(_mm_set1_ps(1./2.), x3)); 58 | __m128 m2 = _mm_cmple_ps(x, _mm_set1_ps(2)); 59 | y = _mm_or_ps(_mm_and_ps(m2, p2), _mm_andnot_ps(m2, y)); 60 | __m128 m1 = _mm_cmple_ps(x, _mm_set1_ps(1)); 61 | y = _mm_or_ps(_mm_and_ps(m1, p1), _mm_andnot_ps(m1, y)); 62 | return y; 63 | } 64 | #endif 65 | 66 | /** 67 | * @brief Compute the 3rd-order B-spline interpolation polynomial. 68 | * 69 | * @tparam R 70 | * @param x 71 | * @return R 72 | */ 73 | template 74 | R bspline3(R x) 75 | { 76 | x = std::abs(x); 77 | R x2 = x * x; 78 | R x3 = x2 * x; 79 | R y = 0; 80 | R p1 = R(2./3.) - x2 + R(1./2.) * x3; 81 | R p2 = R(4./3.) - R(2) * x + x2 - R(1./6.) * x3; 82 | y = (x < R(2)) ? p2 : y; 83 | y = (x < R(1)) ? p1 : y; 84 | return y; 85 | } 86 | 87 | #if SFIZZ_HAVE_SSE 88 | /** 89 | * @brief Compute 4 parallel elements of the 3rd-order B-spline interpolation polynomial. 90 | * 91 | * @param x 92 | * @return __m128 93 | */ 94 | inline __m128 bspline3x4(__m128 x) 95 | { 96 | x = _mm_andnot_ps(_mm_set1_ps(-0.0f), x); 97 | __m128 x2 = _mm_mul_ps(x, x); 98 | __m128 x3 = _mm_mul_ps(x2, x); 99 | __m128 y = _mm_set1_ps(0.0f); 100 | __m128 p1 = _mm_add_ps(_mm_sub_ps(_mm_set1_ps(2./3.), x2), _mm_mul_ps(_mm_set1_ps(1./2.), x3)); 101 | __m128 p2 = _mm_sub_ps(_mm_add_ps(_mm_sub_ps(_mm_set1_ps(4./3.), _mm_mul_ps(_mm_set1_ps(2), x)), x2), _mm_mul_ps(_mm_set1_ps(1./6.), x3)); 102 | __m128 m2 = _mm_cmple_ps(x, _mm_set1_ps(2)); 103 | y = _mm_or_ps(_mm_and_ps(m2, p2), _mm_andnot_ps(m2, y)); 104 | __m128 m1 = _mm_cmple_ps(x, _mm_set1_ps(1)); 105 | y = _mm_or_ps(_mm_and_ps(m1, p1), _mm_andnot_ps(m1, y)); 106 | return y; 107 | } 108 | #endif 109 | -------------------------------------------------------------------------------- /sources/interactive/dsp/SIMDConfig.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // This code is part of the sfizz library and is licensed under a BSD 2-clause 4 | // license. You should have receive a LICENSE.md file along with the code. 5 | // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz 6 | 7 | #pragma once 8 | 9 | /** 10 | Define the following macros, 1 if available, otherwise 0. 11 | These are allowed to be defined externally, in case automatic detection would 12 | fail based on the compiler predefinitions. 13 | 14 | - SFIZZ_HAVE_SSE 15 | - SFIZZ_HAVE_SSE2 16 | - SFIZZ_HAVE_AVX 17 | - SFIZZ_HAVE_NEON 18 | */ 19 | 20 | #if defined(__GNUC__) 21 | # if defined(__AVX__) 22 | # define SFIZZ_DETECT_SSE 1 23 | # define SFIZZ_DETECT_SSE2 1 24 | # define SFIZZ_DETECT_AVX 1 25 | # elif defined(__SSE2__) 26 | # define SFIZZ_DETECT_SSE 1 27 | # define SFIZZ_DETECT_SSE2 1 28 | # define SFIZZ_DETECT_AVX 0 29 | # elif defined(__SSE__) 30 | # define SFIZZ_DETECT_SSE 1 31 | # define SFIZZ_DETECT_SSE2 0 32 | # define SFIZZ_DETECT_AVX 0 33 | # else 34 | # define SFIZZ_DETECT_SSE 0 35 | # define SFIZZ_DETECT_SSE2 0 36 | # define SFIZZ_DETECT_AVX 0 37 | # endif 38 | # if defined(__ARM_NEON__) || defined(__ARM_NEON) 39 | # define SFIZZ_DETECT_NEON 1 40 | # else 41 | # define SFIZZ_DETECT_NEON 0 42 | # endif 43 | #elif defined(_MSC_VER) 44 | # if defined(__AVX__) 45 | # define SFIZZ_DETECT_SSE 1 46 | # define SFIZZ_DETECT_SSE2 1 47 | # define SFIZZ_DETECT_AVX 1 48 | # elif defined(_M_AMD64) || defined(_M_X64) 49 | # define SFIZZ_DETECT_SSE 1 50 | # define SFIZZ_DETECT_SSE2 1 51 | # define SFIZZ_DETECT_AVX 0 52 | # elif _M_IX86_FP == 2 53 | # define SFIZZ_DETECT_SSE 1 54 | # define SFIZZ_DETECT_SSE2 1 55 | # define SFIZZ_DETECT_AVX 0 56 | # elif _M_IX86_FP == 1 57 | # define SFIZZ_DETECT_SSE 1 58 | # define SFIZZ_DETECT_SSE2 0 59 | # define SFIZZ_DETECT_AVX 0 60 | # endif 61 | // TODO: how to check for NEON on MSVC ARM? 62 | #endif 63 | 64 | #ifndef SFIZZ_HAVE_SSE 65 | # ifdef SFIZZ_DETECT_SSE 66 | # define SFIZZ_HAVE_SSE SFIZZ_DETECT_SSE 67 | # else 68 | # define SFIZZ_HAVE_SSE 0 69 | # endif 70 | #endif 71 | #ifndef SFIZZ_HAVE_SSE2 72 | # ifdef SFIZZ_DETECT_SSE2 73 | # define SFIZZ_HAVE_SSE2 SFIZZ_DETECT_SSE2 74 | # else 75 | # define SFIZZ_HAVE_SSE2 0 76 | # endif 77 | #endif 78 | #ifndef SFIZZ_HAVE_AVX 79 | # ifdef SFIZZ_DETECT_AVX 80 | # define SFIZZ_HAVE_AVX SFIZZ_DETECT_AVX 81 | # else 82 | # define SFIZZ_HAVE_AVX 0 83 | # endif 84 | #endif 85 | #ifndef SFIZZ_HAVE_NEON 86 | # ifdef SFIZZ_DETECT_NEON 87 | # define SFIZZ_HAVE_NEON SFIZZ_DETECT_NEON 88 | # else 89 | # define SFIZZ_HAVE_NEON 0 90 | # endif 91 | #endif 92 | 93 | /** 94 | Detect one of the following the processor families. 95 | 96 | - SFIZZ_CPU_FAMILY_X86_64 97 | - SFIZZ_CPU_FAMILY_I386 98 | - SFIZZ_CPU_FAMILY_AARCH64 99 | - SFIZZ_CPU_FAMILY_ARM 100 | */ 101 | 102 | #if defined(_MSC_VER) && defined(_M_AMD64) 103 | # define SFIZZ_CPU_FAMILY_X86_64 1 104 | #elif defined(_MSC_VER) && defined(_M_IX86) 105 | # define SFIZZ_CPU_FAMILY_I386 1 106 | #elif defined(_MSC_VER) && defined(_M_ARM64) 107 | # define SFIZZ_CPU_FAMILY_AARCH64 1 108 | #elif defined(_MSC_VER) && defined(_M_ARM) 109 | # define SFIZZ_CPU_FAMILY_ARM 1 110 | #elif defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) 111 | # define SFIZZ_CPU_FAMILY_X86_64 1 112 | #elif defined(__i386__) || defined(__i386) 113 | # define SFIZZ_CPU_FAMILY_I386 1 114 | #elif defined(__aarch64__) 115 | # define SFIZZ_CPU_FAMILY_AARCH64 1 116 | #elif defined(__arm__) || defined(__arm) 117 | # define SFIZZ_CPU_FAMILY_ARM 1 118 | #endif 119 | -------------------------------------------------------------------------------- /sources/interactive/dsp/Wavetables.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // This code is part of the sfizz library and is licensed under a BSD 2-clause 4 | // license. You should have receive a LICENSE.md file along with the code. 5 | // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz 6 | 7 | #include "Wavetables.h" 8 | #include "Interpolators.h" 9 | #include "MathHelpers.h" 10 | #include 11 | #include 12 | #include 13 | 14 | #if !defined(M_PI) 15 | # define M_PI 3.14159265358979323846 16 | #endif 17 | 18 | namespace sfz { 19 | 20 | void WavetableOscillator::init(double sampleRate) 21 | { 22 | _sampleInterval = 1.0 / sampleRate; 23 | _multi = WavetableMulti::getSilenceWavetable(); 24 | clear(); 25 | } 26 | 27 | void WavetableOscillator::clear() 28 | { 29 | _phase = 0.0f; 30 | } 31 | 32 | void WavetableOscillator::setWavetable(const WavetableMulti* wave) 33 | { 34 | _multi = wave ? wave : WavetableMulti::getSilenceWavetable(); 35 | } 36 | 37 | void WavetableOscillator::setPhase(float phase) 38 | { 39 | assert(phase >= 0.0f && phase <= 1.0f); 40 | _phase = phase; 41 | } 42 | 43 | static float incrementAndWrap(float phase, float inc) 44 | { 45 | phase += inc; 46 | phase -= static_cast(phase); 47 | phase += phase < 0.0f; // in case of negative frequencies 48 | return phase; 49 | } 50 | 51 | template 52 | void WavetableOscillator::processSingle(float frequency, float detuneRatio, float* output, unsigned nframes) 53 | { 54 | float phase = _phase; 55 | float phaseInc = frequency * (detuneRatio * _sampleInterval); 56 | 57 | const WavetableMulti& multi = *_multi; 58 | unsigned tableSize = multi.tableSize(); 59 | nonstd::span table = multi.getTableForFrequency(frequency); 60 | 61 | for (unsigned i = 0; i < nframes; ++i) { 62 | float position = phase * tableSize; 63 | unsigned index = static_cast(position); 64 | float frac = position - index; 65 | output[i] = interpolate(&table[index], frac); 66 | 67 | phase = incrementAndWrap(phase, phaseInc); 68 | } 69 | 70 | _phase = phase; 71 | } 72 | 73 | template 74 | void WavetableOscillator::processModulatedSingle(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes) 75 | { 76 | float phase = _phase; 77 | float sampleInterval = _sampleInterval; 78 | 79 | const WavetableMulti& multi = *_multi; 80 | unsigned tableSize = multi.tableSize(); 81 | 82 | for (unsigned i = 0; i < nframes; ++i) { 83 | float frequency = frequencies[i]; 84 | float phaseInc = frequency * (detuneRatios[i] * sampleInterval); 85 | nonstd::span table = multi.getTableForFrequency(frequency); 86 | 87 | float position = phase * tableSize; 88 | unsigned index = static_cast(position); 89 | float frac = position - index; 90 | output[i] = interpolate(&table[index], frac); 91 | 92 | phase = incrementAndWrap(phase, phaseInc); 93 | } 94 | 95 | _phase = phase; 96 | } 97 | 98 | template 99 | void WavetableOscillator::processDual(float frequency, float detuneRatio, float* output, unsigned nframes) 100 | { 101 | float phase = _phase; 102 | float phaseInc = frequency * (detuneRatio * _sampleInterval); 103 | 104 | const WavetableMulti& multi = *_multi; 105 | unsigned tableSize = multi.tableSize(); 106 | WavetableMulti::DualTable dt = multi.getInterpolationPairForFrequency(frequency); 107 | 108 | for (unsigned i = 0; i < nframes; ++i) { 109 | float position = phase * tableSize; 110 | unsigned index = static_cast(position); 111 | float frac = position - index; 112 | output[i] = 113 | (1 - dt.delta) * interpolate(&dt.table1[index], frac) + 114 | dt.delta * interpolate(&dt.table2[index], frac); 115 | 116 | phase = incrementAndWrap(phase, phaseInc); 117 | } 118 | 119 | _phase = phase; 120 | } 121 | 122 | template 123 | void WavetableOscillator::processModulatedDual(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes) 124 | { 125 | float phase = _phase; 126 | float sampleInterval = _sampleInterval; 127 | 128 | const WavetableMulti& multi = *_multi; 129 | unsigned tableSize = multi.tableSize(); 130 | 131 | for (unsigned i = 0; i < nframes; ++i) { 132 | float frequency = frequencies[i]; 133 | float phaseInc = frequency * (detuneRatios[i] * sampleInterval); 134 | 135 | WavetableMulti::DualTable dt = multi.getInterpolationPairForFrequency(frequency); 136 | 137 | float position = phase * tableSize; 138 | unsigned index = static_cast(position); 139 | float frac = position - index; 140 | output[i] = 141 | (1 - dt.delta) * interpolate(&dt.table1[index], frac) + 142 | dt.delta * interpolate(&dt.table2[index], frac); 143 | 144 | phase = incrementAndWrap(phase, phaseInc); 145 | } 146 | 147 | _phase = phase; 148 | } 149 | 150 | void WavetableOscillator::process(float frequency, float detuneRatio, float* output, unsigned nframes) 151 | { 152 | int quality = clamp(_quality, 0, 3); 153 | 154 | switch (quality) { 155 | case 0: 156 | processSingle(frequency, detuneRatio, output, nframes); 157 | break; 158 | case 1: 159 | processSingle(frequency, detuneRatio, output, nframes); 160 | break; 161 | case 2: 162 | processSingle(frequency, detuneRatio, output, nframes); 163 | break; 164 | case 3: 165 | processDual(frequency, detuneRatio, output, nframes); 166 | break; 167 | } 168 | } 169 | 170 | void WavetableOscillator::processModulated(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes) 171 | { 172 | int quality = clamp(_quality, 0, 3); 173 | 174 | switch (quality) { 175 | case 0: 176 | processModulatedSingle(frequencies, detuneRatios, output, nframes); 177 | break; 178 | case 1: 179 | processModulatedSingle(frequencies, detuneRatios, output, nframes); 180 | break; 181 | case 2: 182 | processModulatedSingle(frequencies, detuneRatios, output, nframes); 183 | break; 184 | case 3: 185 | processModulatedDual(frequencies, detuneRatios, output, nframes); 186 | break; 187 | } 188 | } 189 | 190 | //------------------------------------------------------------------------------ 191 | void HarmonicProfile::generate( 192 | nonstd::span table, double amplitude, double cutoff) const 193 | { 194 | size_t size = table.size(); 195 | 196 | typedef std::complex cpx; 197 | 198 | // allocate a spectrum of size N/2+1 199 | // bins are equispaced in frequency, with index N/2 being nyquist 200 | std::unique_ptr spec(new cpx[size / 2 + 1]()); 201 | 202 | kiss_fftr_cfg cfg = kiss_fftr_alloc(size, true, nullptr, nullptr); 203 | if (!cfg) 204 | throw std::bad_alloc(); 205 | 206 | // bins need scaling and phase offset; this IFFT is a sum of cosines 207 | const std::complex k = std::polar(amplitude * 0.5, M_PI / 2); 208 | 209 | // start filling at bin index 1; 1 is fundamental, 0 is DC 210 | for (size_t index = 1; index < size / 2 + 1; ++index) { 211 | if (index * (1.0 / size) > cutoff) 212 | break; 213 | 214 | std::complex harmonic = getHarmonic(index); 215 | spec[index] = k * harmonic; 216 | } 217 | 218 | kiss_fftri(cfg, reinterpret_cast(spec.get()), table.data()); 219 | kiss_fftr_free(cfg); 220 | } 221 | 222 | #if 0 223 | class SineProfile : public HarmonicProfile { 224 | public: 225 | std::complex getHarmonic(size_t index) const 226 | { 227 | return (index == 1) ? 1.0 : 0.0; 228 | } 229 | }; 230 | 231 | class TriangleProfile : public HarmonicProfile { 232 | public: 233 | std::complex getHarmonic(size_t index) const 234 | { 235 | if ((index & 1) == 0) 236 | return 0.0; 237 | 238 | bool s = (index >> 1) & 1; 239 | return std::polar( 240 | (8 / (M_PI * M_PI)) * (1.0 / (index * index)), 241 | s ? 0.0 : M_PI); 242 | } 243 | }; 244 | 245 | class SawProfile : public HarmonicProfile { 246 | public: 247 | std::complex getHarmonic(size_t index) const 248 | { 249 | if (index < 1) 250 | return 0.0; 251 | 252 | return std::polar( 253 | (2.0 / M_PI) / index, 254 | (index & 1) ? 0.0 : M_PI); 255 | } 256 | }; 257 | 258 | class SquareProfile : public HarmonicProfile { 259 | public: 260 | std::complex getHarmonic(size_t index) const 261 | { 262 | if ((index & 1) == 0) 263 | return 0.0; 264 | 265 | return std::polar((4.0 / M_PI) / index, M_PI); 266 | } 267 | }; 268 | 269 | static const SineProfile sineProfile; 270 | static const TriangleProfile triangleProfile; 271 | static const SawProfile sawProfile; 272 | static const SquareProfile squareProfile; 273 | 274 | const HarmonicProfile& HarmonicProfile::getSine() 275 | { 276 | return sineProfile; 277 | } 278 | 279 | const HarmonicProfile& HarmonicProfile::getTriangle() 280 | { 281 | return triangleProfile; 282 | } 283 | 284 | const HarmonicProfile& HarmonicProfile::getSaw() 285 | { 286 | return sawProfile; 287 | } 288 | 289 | const HarmonicProfile& HarmonicProfile::getSquare() 290 | { 291 | return squareProfile; 292 | } 293 | #endif 294 | 295 | //------------------------------------------------------------------------------ 296 | constexpr unsigned MipmapRange::N; 297 | constexpr float MipmapRange::F1; 298 | constexpr float MipmapRange::FN; 299 | 300 | const float MipmapRange::K = 1.0 / F1; 301 | const float MipmapRange::LogB = std::log(FN / F1) / (N - 1); 302 | 303 | const std::array MipmapRange::FrequencyToIndex = []() 304 | { 305 | std::array table; 306 | 307 | for (unsigned i = 0; i < table.size() - 1; ++i) { 308 | float r = i * (1.0f / (table.size() - 1)); 309 | float f = F1 + r * (FN - F1); 310 | table[i] = getExactIndexForFrequency(f); 311 | } 312 | // ensure the last element to be exact 313 | table[table.size() - 1] = N - 1; 314 | 315 | return table; 316 | }(); 317 | 318 | float MipmapRange::getIndexForFrequency(float f) 319 | { 320 | static constexpr unsigned tableSize = FrequencyToIndex.size(); 321 | 322 | float pos = (f - F1) * ((tableSize - 1) / static_cast(FN - F1)); 323 | pos = clamp(pos, 0, tableSize - 1); 324 | 325 | int index1 = static_cast(pos); 326 | int index2 = std::min(index1 + 1, tableSize - 1); 327 | float frac = pos - index1; 328 | 329 | return (1.0f - frac) * FrequencyToIndex[index1] + 330 | frac * FrequencyToIndex[index2]; 331 | } 332 | 333 | float MipmapRange::getExactIndexForFrequency(float f) 334 | { 335 | float t = (f < F1) ? 0.0f : (std::log(K * f) / LogB); 336 | return clamp(t, 0, N - 1); 337 | } 338 | 339 | const std::array MipmapRange::IndexToStartFrequency = []() 340 | { 341 | std::array table; 342 | for (unsigned t = 0; t < N; ++t) 343 | table[t] = std::exp(t * LogB) / K; 344 | // end value for final table 345 | table[N] = 22050.0; 346 | 347 | return table; 348 | }(); 349 | 350 | MipmapRange MipmapRange::getRangeForIndex(int o) 351 | { 352 | o = clamp(o, 0, N - 1); 353 | 354 | MipmapRange range; 355 | range.minFrequency = IndexToStartFrequency[o]; 356 | range.maxFrequency = IndexToStartFrequency[o + 1]; 357 | 358 | return range; 359 | } 360 | 361 | MipmapRange MipmapRange::getRangeForFrequency(float f) 362 | { 363 | int index = static_cast(getIndexForFrequency(f)); 364 | return getRangeForIndex(index); 365 | } 366 | 367 | //------------------------------------------------------------------------------ 368 | constexpr unsigned WavetableMulti::_tableExtra; 369 | 370 | WavetableMulti WavetableMulti::createForHarmonicProfile( 371 | const HarmonicProfile& hp, double amplitude, unsigned tableSize, double refSampleRate) 372 | { 373 | WavetableMulti wm; 374 | constexpr unsigned numTables = WavetableMulti::numTables(); 375 | 376 | wm.allocateStorage(tableSize); 377 | 378 | for (unsigned m = 0; m < numTables; ++m) { 379 | MipmapRange range = MipmapRange::getRangeForIndex(m); 380 | 381 | double freq = range.maxFrequency; 382 | 383 | // A spectrum S of fundamental F has: S[1]=F and S[N/2]=Fs'/2 384 | // which lets it generate frequency up to Fs'/2=F*N/2. 385 | // Therefore it's desired to cut harmonics at C=0.5*Fs/Fs'=0.5*Fs/(F*N). 386 | double cutoff = (0.5 * refSampleRate / tableSize) / freq; 387 | 388 | float* ptr = const_cast(wm.getTablePointer(m)); 389 | nonstd::span table(ptr, tableSize); 390 | 391 | hp.generate(table, amplitude, cutoff); 392 | } 393 | 394 | wm.fillExtra(); 395 | 396 | return wm; 397 | } 398 | 399 | const WavetableMulti* WavetableMulti::getSilenceWavetable() 400 | { 401 | static WavetableMulti wm; 402 | static bool initialized { false }; 403 | 404 | if (!initialized) { 405 | constexpr unsigned numTables = WavetableMulti::numTables(); 406 | wm.allocateStorage(1); 407 | for (unsigned m = 0; m < numTables; ++m) { 408 | float* ptr = const_cast(wm.getTablePointer(m)); 409 | *ptr = 0; 410 | } 411 | wm.fillExtra(); 412 | initialized = true; 413 | } 414 | return &wm; 415 | } 416 | 417 | void WavetableMulti::allocateStorage(unsigned tableSize) 418 | { 419 | _multiData.resize((tableSize + 2 * _tableExtra) * numTables()); 420 | _tableSize = tableSize; 421 | } 422 | 423 | void WavetableMulti::fillExtra() 424 | { 425 | unsigned tableSize = _tableSize; 426 | constexpr unsigned tableExtra = _tableExtra; 427 | constexpr unsigned numTables = WavetableMulti::numTables(); 428 | 429 | for (unsigned m = 0; m < numTables; ++m) { 430 | float* beg = const_cast(getTablePointer(m)); 431 | float* end = beg + tableSize; 432 | // fill right 433 | float* src = beg; 434 | float* dst = end; 435 | for (unsigned i = 0; i < tableExtra; ++i) { 436 | *dst++ = *src; 437 | src = (src + 1 != end) ? (src + 1) : beg; 438 | } 439 | // fill left 440 | src = end - 1; 441 | dst = beg - 1; 442 | for (unsigned i = 0; i < tableExtra; ++i) { 443 | *dst-- = *src; 444 | src = (src != beg) ? (src - 1) : (end - 1); 445 | } 446 | } 447 | } 448 | 449 | //------------------------------------------------------------------------------ 450 | 451 | /** 452 | * @brief Harmonic profile which takes its values from a table. 453 | */ 454 | class TabulatedHarmonicProfile : public HarmonicProfile { 455 | public: 456 | explicit TabulatedHarmonicProfile(nonstd::span> harmonics) 457 | : _harmonics(harmonics) 458 | { 459 | } 460 | 461 | std::complex getHarmonic(size_t index) const override 462 | { 463 | if (index >= _harmonics.size()) 464 | return {}; 465 | 466 | return _harmonics[index]; 467 | } 468 | 469 | private: 470 | nonstd::span> _harmonics; 471 | }; 472 | 473 | //------------------------------------------------------------------------------ 474 | 475 | WavetableMulti Wavetables::createFromAudioData(const float* audioData, unsigned audioSize) 476 | { 477 | // an even size is required for FFT 478 | if (audioSize % 2 != 0) 479 | throw std::runtime_error("The wavetable size must be a multiple of 2."); 480 | 481 | size_t fftSize = audioSize; 482 | size_t specSize = fftSize / 2 + 1; 483 | 484 | typedef std::complex cpx; 485 | std::unique_ptr spec { new cpx[specSize] }; 486 | 487 | kiss_fftr_cfg cfg = kiss_fftr_alloc(fftSize, false, nullptr, nullptr); 488 | if (!cfg) 489 | throw std::bad_alloc(); 490 | 491 | kiss_fftr(cfg, audioData, reinterpret_cast(spec.get())); 492 | kiss_fftr_free(cfg); 493 | 494 | // scale transform, and normalize amplitude and phase 495 | const std::complex k = std::polar(2.0 / fftSize, -M_PI / 2); 496 | for (size_t i = 0; i < specSize; ++i) 497 | spec[i] *= k; 498 | 499 | TabulatedHarmonicProfile hp { 500 | nonstd::span> { spec.get(), specSize } 501 | }; 502 | 503 | return WavetableMulti::createForHarmonicProfile(hp, 1.0); 504 | } 505 | 506 | } // namespace sfz 507 | -------------------------------------------------------------------------------- /sources/interactive/dsp/Wavetables.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-2-Clause 2 | 3 | // This code is part of the sfizz library and is licensed under a BSD 2-clause 4 | // license. You should have receive a LICENSE.md file along with the code. 5 | // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz 6 | 7 | #pragma once 8 | #include "Config.h" 9 | #include "Buffer.h" 10 | #include "MathHelpers.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace sfz { 17 | class WavetableMulti; 18 | 19 | enum InterpolatorModel : int; 20 | 21 | /** 22 | An oscillator based on wavetables 23 | */ 24 | class WavetableOscillator { 25 | public: 26 | /** 27 | Initialize with the given sample rate. 28 | Run it once after instantiating. 29 | */ 30 | void init(double sampleRate); 31 | 32 | /** 33 | Reset the oscillation to the initial phase. 34 | */ 35 | void clear(); 36 | 37 | /** 38 | Set the wavetable to generate with this oscillator. 39 | */ 40 | void setWavetable(const WavetableMulti* wave); 41 | 42 | /** 43 | Set the current phase of this oscillator, between 0 and 1 excluded. 44 | */ 45 | void setPhase(float phase); 46 | 47 | /** 48 | Set the quality of this oscillator. (cf. `oscillator_quality`) 49 | 50 | 0: nearest 51 | 1: linear 52 | 2: high 53 | 3: dual-high 54 | */ 55 | void setQuality(int q) { _quality = q; } 56 | 57 | /** 58 | Get the quality of this oscillator. (cf. `oscillator_quality`) 59 | */ 60 | int quality() const { return _quality; } 61 | 62 | /** 63 | Compute a cycle of the oscillator, with constant frequency. 64 | */ 65 | void process(float frequency, float detuneRatio, float* output, unsigned nframes); 66 | 67 | /** 68 | Compute a cycle of the oscillator, with varying frequency. 69 | */ 70 | void processModulated(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes); 71 | 72 | private: 73 | // single-table interpolation 74 | template 75 | void processSingle(float frequency, float detuneRatio, float* output, unsigned nframes); 76 | template 77 | void processModulatedSingle(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes); 78 | 79 | // dual-table interpolation 80 | template 81 | void processDual(float frequency, float detuneRatio, float* output, unsigned nframes); 82 | template 83 | void processModulatedDual(const float* frequencies, const float* detuneRatios, float* output, unsigned nframes); 84 | 85 | private: 86 | float _phase = 0.0f; 87 | float _sampleInterval = 0.0f; 88 | const WavetableMulti* _multi = nullptr; 89 | int _quality = 1; 90 | }; 91 | 92 | /** 93 | A description of the harmonics of a particular wave form 94 | */ 95 | class HarmonicProfile { 96 | public: 97 | virtual ~HarmonicProfile() {} 98 | 99 | #if 0 100 | static const HarmonicProfile& getSine(); 101 | static const HarmonicProfile& getTriangle(); 102 | static const HarmonicProfile& getSaw(); 103 | static const HarmonicProfile& getSquare(); 104 | #endif 105 | 106 | /** 107 | @brief Get the value at the given index of the frequency spectrum. 108 | 109 | The modulus and the argument of the complex number are equal to the 110 | amplitude and the phase of the harmonic component. 111 | */ 112 | virtual std::complex getHarmonic(size_t index) const = 0; 113 | 114 | /** 115 | @brief Generate a period of the waveform and store it in the table. 116 | 117 | Do not generate harmonics above cutoff, which is expressed as Fc/Fs. 118 | */ 119 | void generate(nonstd::span table, double amplitude, double cutoff) const; 120 | }; 121 | 122 | /** 123 | A helper to select ranges of a mip-mapped wave, according to the 124 | frequency of an oscillator. 125 | 126 | The ranges are identified by octave numbers; not octaves in a musical sense, 127 | but as logarithmic divisions of the frequency range. 128 | */ 129 | class MipmapRange { 130 | public: 131 | float minFrequency = 0; 132 | float maxFrequency = 0; 133 | 134 | // number of tables in the mipmap 135 | static constexpr unsigned N = 24; 136 | // start frequency of the first table in the mipmap 137 | static constexpr float F1 = 20.0; 138 | // start frequency of the last table in the mipmap 139 | static constexpr float FN = 12000.0; 140 | 141 | static float getIndexForFrequency(float f); 142 | static float getExactIndexForFrequency(float f); 143 | static MipmapRange getRangeForIndex(int o); 144 | static MipmapRange getRangeForFrequency(float f); 145 | 146 | // the frequency mapping of the mipmap is defined by formula: 147 | // T(f) = log(k*f)/log(b) 148 | // - T is the table number, converted to index by rounding down 149 | // - f is the oscillation frequency 150 | // - k and b are adjustment parameters according to constant parameters 151 | // k = 1/F1 152 | // b = exp(log(FN/F1)/(N-1)) 153 | 154 | static const float K; 155 | static const float LogB; 156 | 157 | static const std::array FrequencyToIndex; 158 | static const std::array IndexToStartFrequency; 159 | }; 160 | 161 | /** 162 | Multisample of a wavetable, which is a collection of FFT-filtered mipmaps 163 | adapted for various playback frequencies. 164 | */ 165 | class WavetableMulti { 166 | public: 167 | // number of elements in each table 168 | unsigned tableSize() const { return _tableSize; } 169 | 170 | // number of tables in the multisample 171 | static constexpr unsigned numTables() { return MipmapRange::N; } 172 | 173 | // get the N-th table in the multisample 174 | nonstd::span getTable(unsigned index) const 175 | { 176 | return { getTablePointer(index), _tableSize }; 177 | } 178 | 179 | // get the table which is adequate for a given playback frequency 180 | nonstd::span getTableForFrequency(float freq) const 181 | { 182 | return getTable(MipmapRange::getIndexForFrequency(freq)); 183 | } 184 | 185 | // adjacent tables with interpolation factor between them 186 | struct DualTable { 187 | const float* table1; 188 | const float* table2; 189 | float delta; 190 | }; 191 | 192 | // get the pair of tables at the fractional multisample position (range checked) 193 | DualTable getInterpolationPair(float position) const 194 | { 195 | DualTable dt; 196 | int index = static_cast(position); 197 | dt.delta = position - index; 198 | dt.table1 = getTablePointer(clamp(index, 0, MipmapRange::N - 1)); 199 | dt.table2 = getTablePointer(clamp(index + 1, 0, MipmapRange::N - 1)); 200 | return dt; 201 | } 202 | 203 | // get the pair of tables for the given playback frequency (range checked) 204 | DualTable getInterpolationPairForFrequency(float freq) const 205 | { 206 | float position = MipmapRange::getIndexForFrequency(freq); 207 | return getInterpolationPair(position); 208 | } 209 | 210 | // create a multisample according to a given harmonic profile 211 | // the reference sample rate is the minimum value accepted by the DSP 212 | // system (most defavorable wrt. aliasing) 213 | static WavetableMulti createForHarmonicProfile( 214 | const HarmonicProfile& hp, double amplitude, 215 | unsigned tableSize = config::tableSize, 216 | double refSampleRate = config::tableRefSampleRate); 217 | 218 | // get a tiny silent wavetable with null content for use with oscillators 219 | static const WavetableMulti* getSilenceWavetable(); 220 | 221 | private: 222 | // get a pointer to the beginning of the N-th table 223 | const float* getTablePointer(unsigned index) const 224 | { 225 | return _multiData.data() + index * (_tableSize + 2 * _tableExtra) + _tableExtra; 226 | } 227 | 228 | // allocate the internal data for tables of the given size 229 | void allocateStorage(unsigned tableSize); 230 | 231 | // fill extra data at table ends with repetitions of the first samples 232 | void fillExtra(); 233 | 234 | // length of each individual table of the multisample 235 | unsigned _tableSize = 0; 236 | 237 | // number X of extra elements, for safe interpolations up to X-th order. 238 | static constexpr unsigned _tableExtra = 4; 239 | 240 | // internal storage, having `multiSize` rows and `tableSize` columns. 241 | sfz::Buffer _multiData; 242 | }; 243 | 244 | namespace Wavetables { 245 | 246 | WavetableMulti createFromAudioData(const float* audioData, unsigned audioSize); 247 | 248 | } 249 | 250 | } // namespace sfz 251 | -------------------------------------------------------------------------------- /sources/random/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The Microsoft C++ Standard Library is under the Apache License v2.0 with LLVM Exception: 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | 205 | ---- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. 220 | -------------------------------------------------------------------------------- /sources/random/ms_stl_random.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 2 | // Based on `microsoft/STL` file `stl/inc/random` at revision a8b62af 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ms_stl { 12 | 13 | using namespace std; 14 | 15 | #if !defined(NODISCARD_) 16 | # if __has_cpp_attribute(nodiscard) 17 | # define NODISCARD_ [[nodiscard]] 18 | # else 19 | # define NODISCARD_ 20 | # endif 21 | #endif 22 | 23 | // FUNCTION TEMPLATE generate_canonical 24 | template 25 | NODISCARD_ Real_ generate_canonical(Gen_& Gx_) { // build a floating-point value from random sequence 26 | const size_t Digits_ = static_cast(numeric_limits::digits); 27 | const size_t Minbits_ = Digits_ < Bits_ ? Digits_ : Bits_; 28 | 29 | const Real_ Gxmin_ = static_cast((Gx_.min)()); 30 | const Real_ Gxmax_ = static_cast((Gx_.max)()); 31 | const Real_ Rx_ = (Gxmax_ - Gxmin_) + Real_{1}; 32 | 33 | const int Ceil_ = static_cast(std::ceil(static_cast(Minbits_) / std::log2(Rx_))); 34 | const int Kx_ = Ceil_ < 1 ? 1 : Ceil_; 35 | 36 | Real_ Ans_{0}; 37 | Real_ Factor_{1}; 38 | 39 | for (int Idx_ = 0; Idx_ < Kx_; ++Idx_) { // add in another set of bits 40 | Ans_ += (static_cast(Gx_()) - Gxmin_) * Factor_; 41 | Factor_ *= Rx_; 42 | } 43 | 44 | return Ans_ / Factor_; 45 | } 46 | 47 | #define NRAND_(eng, resty) (generate_canonical(-1)>(eng)) 48 | 49 | // ALIAS TEMPLATE Enable_if_seed_seq_t_ 50 | template 51 | using Enable_if_seed_seq_t_ = typename enable_if< 52 | !is_convertible::type, 53 | typename Self_:: 54 | result_type>::value && !is_same::type, Self_>::value && !is_same::type, Engine_>::value, 55 | int>::type; 56 | 57 | // CLASS TEMPLATE Circ_buf_ FOR subtract_with_carry, subtract_with_carry_01, AND mersenne_twister 58 | template 59 | struct Circ_buf_ { // holds historical values for generators 60 | Ty_ At_(size_t Ix_) const { 61 | return Ax_[Base_(Ix_)]; 62 | } 63 | 64 | bool Equals_(const Circ_buf_& Right_) const { 65 | const Ty_* Last1_ = Ax_ + Idx_; 66 | const Ty_* Last2_ = Right_.Ax_ + Right_.Idx_; 67 | const Ty_* First_; 68 | const Ty_* Last_; 69 | const Ty_* Other_; 70 | bool Use2_ = Base_() < Right_.Base_(); 71 | 72 | if (Use2_) { // Right_'s range is higher up in the array 73 | // than ours, so scan it first 74 | First_ = Right_.Ax_ + Right_.Base_(); 75 | Last_ = Last2_; 76 | Other_ = Ax_ + Base_(); 77 | } else { // our range is higher up in the array 78 | // than Right_'s, so scan ours first 79 | First_ = Ax_ + Base_(); 80 | Last_ = Last1_; 81 | Other_ = Right_.Ax_ + Right_.Base_(); 82 | } 83 | 84 | ptrdiff_t Nx0_ = Nw_; 85 | while (0 < Nx0_) { // scan 86 | // note: may need up to three passes; each scan starts at the 87 | // current highest array position and ends at the end of the 88 | // array or the Idx_ value, whichever comes first; the 89 | // equality test succeeds only by reaching the Idx_ value. 90 | const Ty_* Limit_ = First_ < Last_ ? Last_ : Use2_ ? Right_.Ax_ + 2 * Nw_ : Ax_ + 2 * Nw_; 91 | Nx0_ -= Limit_ - First_; 92 | while (First_ != Limit_) { 93 | if (*First_++ != *Other_++) { 94 | return false; 95 | } 96 | } 97 | 98 | First_ = Other_; 99 | Last_ = Use2_ ? Last1_ : Last2_; 100 | Other_ = Use2_ ? Right_.Ax_ : Ax_; 101 | Use2_ = !Use2_; 102 | } 103 | return true; 104 | } 105 | 106 | size_t Base_(size_t Ix_ = 0) const { 107 | return (Ix_ += Idx_) < Nw_ ? (Ix_ + Nw_) : (Ix_ - Nw_); 108 | } 109 | 110 | unsigned int Idx_; 111 | Ty_ Ax_[2 * Nw_]; 112 | }; 113 | 114 | // CLASS TEMPLATE mersenne_twister 115 | template 116 | class mersenne_twister : public Circ_buf_ { // mersenne twister generator 117 | public: 118 | using result_type = Ty_; 119 | 120 | static constexpr int word_size = Wx_; 121 | static constexpr int state_size = Nx_; 122 | static constexpr int shift_size = Mx_; 123 | static constexpr int mask_bits = Rx_; 124 | static constexpr Ty_ parameter_a = Px_; 125 | static constexpr int output_u = Ux_; 126 | static constexpr int output_s = Sx_; 127 | static constexpr Ty_ output_b = Bx_; 128 | static constexpr int output_t = Tx_; 129 | static constexpr Ty_ output_c = Cx_; 130 | static constexpr int output_l = Lx_; 131 | 132 | static constexpr Ty_ default_seed = 5489U; 133 | 134 | mersenne_twister() : Dxval_(WMSK_) { 135 | seed(default_seed, static_cast(1812433253)); 136 | } 137 | 138 | explicit mersenne_twister(Ty_ Xx0_, Ty_ Dxarg_ = WMSK_, Ty_ Fxarg_ = static_cast(1812433253)) 139 | : Dxval_(Dxarg_) { 140 | seed(Xx0_, Fxarg_); 141 | } 142 | 143 | template = 0> 144 | explicit mersenne_twister(Gen_& Gx_) : Dxval_(WMSK_) { 145 | seed(Gx_); 146 | } 147 | 148 | void seed(Ty_ Xx0_ = default_seed, Ty_ Fx_ = static_cast(1812433253)) { 149 | // set initial values from specified value 150 | Ty_ Prev_ = this->Ax_[0] = Xx0_ & WMSK_; 151 | for (size_t Ix_ = 1; Ix_ < Nx_; ++Ix_) { 152 | Prev_ = this->Ax_[Ix_] = (Ix_ + Fx_ * (Prev_ ^ (Prev_ >> (Wx_ - 2)))) & WMSK_; 153 | } 154 | 155 | this->Idx_ = Nx_; 156 | } 157 | 158 | template = 0> 159 | void seed(Gen_& Gx_, bool = false) { // set initial values from range 160 | for (size_t Ix_ = 0; Ix_ < Nx_; ++Ix_) { 161 | this->Ax_[Ix_] = Gx_() & WMSK_; 162 | } 163 | 164 | this->Idx_ = Nx_; 165 | } 166 | 167 | template 168 | basic_ostream& Write_(basic_ostream& Ostr_) const { // write state to Ostr_ 169 | for (size_t Ix_ = 0; Ix_ < Nx_; ++Ix_) { 170 | Ostr_ << this->At_(Ix_) << ' '; 171 | } 172 | 173 | return Ostr_; 174 | } 175 | 176 | NODISCARD_ result_type(min)() const { 177 | return 0; 178 | } 179 | 180 | NODISCARD_ result_type(max)() const { 181 | return WMSK_; 182 | } 183 | 184 | NODISCARD_ result_type operator()() { 185 | if (this->Idx_ == Nx_) { 186 | Refill_upper_(); 187 | } else if (2 * Nx_ <= this->Idx_) { 188 | Refill_lower_(); 189 | } 190 | 191 | Ty_ Res_ = this->Ax_[this->Idx_++] & WMSK_; 192 | Res_ ^= (Res_ >> Ux_) & Dxval_; 193 | Res_ ^= (Res_ << Sx_) & Bx_; 194 | Res_ ^= (Res_ << Tx_) & Cx_; 195 | Res_ ^= (Res_ & WMSK_) >> Lx_; 196 | return Res_; 197 | } 198 | 199 | void discard(unsigned long long Nskip_) { // discard Nskip_ elements 200 | for (; 0 < Nskip_; --Nskip_) { 201 | (void) (*this)(); 202 | } 203 | } 204 | 205 | protected: 206 | void Refill_lower_() { // compute values for the lower half of the history array 207 | size_t Ix_; 208 | for (Ix_ = 0; Ix_ < Nx_ - Mx_; ++Ix_) { // fill in lower region 209 | Ty_ Tmp_ = (this->Ax_[Ix_ + Nx_] & HMSK_) | (this->Ax_[Ix_ + Nx_ + 1] & LMSK_); 210 | this->Ax_[Ix_] = (Tmp_ >> 1) ^ (Tmp_ & 1 ? Px_ : 0) ^ this->Ax_[Ix_ + Nx_ + Mx_]; 211 | } 212 | 213 | for (; Ix_ < Nx_ - 1; ++Ix_) { // fill in upper region (avoids modulus operation) 214 | Ty_ Tmp_ = (this->Ax_[Ix_ + Nx_] & HMSK_) | (this->Ax_[Ix_ + Nx_ + 1] & LMSK_); 215 | this->Ax_[Ix_] = (Tmp_ >> 1) ^ (Tmp_ & 1 ? Px_ : 0) ^ this->Ax_[Ix_ - Nx_ + Mx_]; 216 | } 217 | 218 | Ty_ Tmp_ = (this->Ax_[Ix_ + Nx_] & HMSK_) | (this->Ax_[0] & LMSK_); 219 | this->Ax_[Ix_] = (Tmp_ >> 1) ^ (Tmp_ & 1 ? Px_ : 0) ^ this->Ax_[Mx_ - 1]; 220 | this->Idx_ = 0; 221 | } 222 | 223 | void Refill_upper_() { // compute values for the upper half of the history array 224 | size_t Ix_; 225 | for (Ix_ = Nx_; Ix_ < 2 * Nx_; ++Ix_) { // fill in values 226 | Ty_ Tmp_ = (this->Ax_[Ix_ - Nx_] & HMSK_) | (this->Ax_[Ix_ - Nx_ + 1] & LMSK_); 227 | this->Ax_[Ix_] = (Tmp_ >> 1) ^ (Tmp_ & 1 ? Px_ : 0) ^ this->Ax_[Ix_ - Nx_ + Mx_]; 228 | } 229 | } 230 | 231 | Ty_ Dxval_; 232 | 233 | static constexpr Ty_ WMSK_ = ~((~Ty_{0} << (Wx_ - 1)) << 1); 234 | static constexpr Ty_ HMSK_ = (WMSK_ << Rx_) & WMSK_; 235 | static constexpr Ty_ LMSK_ = ~HMSK_ & WMSK_; 236 | }; 237 | 238 | // CLASS TEMPLATE mersenne_twister_engine 239 | template 241 | class mersenne_twister_engine : public mersenne_twister { 242 | public: 243 | static constexpr unsigned long long Max_ = (((1ULL << (Wx_ - 1)) - 1) << 1) + 1; 244 | 245 | static_assert(0 < Mx_ && Mx_ <= Nx_ && 2U < Wx_ && Rx_ <= Wx_ && Ux_ <= Wx_ && Sx_ <= Wx_ && Tx_ <= Wx_ 246 | && Lx_ <= Wx_ && Wx_ <= numeric_limits::digits && Px_ <= Max_ && Bx_ <= Max_ && Cx_ <= Max_ 247 | && Dx_ <= Max_ && Fx_ <= Max_, 248 | "invalid template argument for mersenne_twister_engine"); 249 | 250 | using Mybase_ = mersenne_twister; 251 | using result_type = Ty_; 252 | 253 | static constexpr size_t word_size = Wx_; 254 | static constexpr size_t state_size = Nx_; 255 | static constexpr size_t shift_size = Mx_; 256 | static constexpr size_t mask_bits = Rx_; 257 | static constexpr Ty_ xor_mask = Px_; 258 | static constexpr size_t tempering_u = Ux_; 259 | static constexpr Ty_ tempering_d = Dx_; 260 | static constexpr size_t tempering_s = Sx_; 261 | static constexpr Ty_ tempering_b = Bx_; 262 | static constexpr size_t tempering_t = Tx_; 263 | static constexpr Ty_ tempering_c = Cx_; 264 | static constexpr size_t tempering_l = Lx_; 265 | static constexpr Ty_ initialization_multiplier = Fx_; 266 | 267 | static constexpr result_type default_seed = 5489U; 268 | 269 | mersenne_twister_engine() : Mybase_(default_seed, Dx_, Fx_) {} 270 | 271 | explicit mersenne_twister_engine(result_type Xx0_) : Mybase_(Xx0_, Dx_, Fx_) {} 272 | 273 | template = 0> 274 | explicit mersenne_twister_engine(Seed_seq_& Seq_) : Mybase_(default_seed, Dx_, Fx_) { 275 | seed(Seq_); 276 | } 277 | 278 | void seed(result_type Xx0_ = default_seed) { // set initial values from specified value 279 | Mybase_::seed(Xx0_, Fx_); 280 | } 281 | 282 | template = 0> 283 | void seed(Seed_seq_& Seq_) { // reset sequence from seed sequence 284 | constexpr int Kx_ = (Wx_ + 31) / 32; 285 | unsigned long Arr_[Kx_ * Nx_]; 286 | Seq_.generate(&Arr_[0], &Arr_[Kx_ * Nx_]); 287 | 288 | int Idx0_ = 0; 289 | Ty_ Sum_ = 0; 290 | for (size_t Ix_ = 0; Ix_ < Nx_; ++Ix_, Idx0_ += Kx_) { // pack Kx_ words 291 | this->Ax_[Ix_] = Arr_[Idx0_]; 292 | for (int Jx_ = 1; Jx_ < Kx_; ++Jx_) { 293 | this->Ax_[Ix_] |= static_cast(Arr_[Idx0_ + Jx_]) << (32 * Jx_); 294 | } 295 | 296 | this->Ax_[Ix_] &= this->WMSK_; 297 | 298 | if (Ix_ == 0) { 299 | Sum_ = this->Ax_[Ix_] >> Rx_; 300 | } else { 301 | Sum_ |= this->Ax_[Ix_]; 302 | } 303 | } 304 | 305 | if (Sum_ == 0) { 306 | this->Ax_[0] = this->WMSK_; 307 | } 308 | 309 | this->Idx_ = Nx_; 310 | } 311 | 312 | NODISCARD_ static constexpr result_type(min)() { 313 | return 0; 314 | } 315 | 316 | NODISCARD_ static constexpr result_type(max)() { 317 | return Mybase_::WMSK_; 318 | } 319 | }; 320 | 321 | using mt19937 = mersenne_twister_engine; 323 | 324 | // CLASS TEMPLATE uniform_real 325 | template 326 | class uniform_real { // uniform real distribution 327 | public: 328 | using result_type = Ty_; 329 | 330 | struct param_type { // parameter package 331 | using distribution_type = uniform_real; 332 | 333 | param_type() { 334 | Init_(Ty_{0}, Ty_{1}); 335 | } 336 | 337 | explicit param_type(Ty_ Min0_, Ty_ Max0_ = Ty_{1}) { 338 | Init_(Min0_, Max0_); 339 | } 340 | 341 | NODISCARD_ bool operator==(const param_type& Right_) const { 342 | return Min_ == Right_.Min_ && Max_ == Right_.Max_; 343 | } 344 | 345 | NODISCARD_ bool operator!=(const param_type& Right_) const { 346 | return !(*this == Right_); 347 | } 348 | 349 | NODISCARD_ result_type a() const { 350 | return Min_; 351 | } 352 | 353 | NODISCARD_ result_type b() const { 354 | return Max_; 355 | } 356 | 357 | void Init_(Ty_ Min0_, Ty_ Max0_) { // set internal state 358 | assert(Min0_ <= Max0_ && (0 <= Min0_ || Max0_ <= Min0_ + (numeric_limits::max)()) && 359 | "invalid min and max arguments for uniform_real"); 360 | Min_ = Min0_; 361 | Max_ = Max0_; 362 | } 363 | 364 | result_type Min_; 365 | result_type Max_; 366 | }; 367 | 368 | uniform_real() : Par_(Ty_{0}, Ty_{1}) {} 369 | 370 | explicit uniform_real(Ty_ Min0_, Ty_ Max0_ = Ty_{1}) : Par_(Min0_, Max0_) {} 371 | 372 | explicit uniform_real(const param_type& Par0_) : Par_(Par0_) {} 373 | 374 | NODISCARD_ result_type a() const { 375 | return Par_.a(); 376 | } 377 | 378 | NODISCARD_ result_type b() const { 379 | return Par_.b(); 380 | } 381 | 382 | NODISCARD_ param_type param() const { 383 | return Par_; 384 | } 385 | 386 | void param(const param_type& Par0_) { // set parameter package 387 | Par_ = Par0_; 388 | } 389 | 390 | NODISCARD_ result_type(min)() const { 391 | return Par_.Min_; 392 | } 393 | 394 | NODISCARD_ result_type(max)() const { 395 | return Par_.Max_; 396 | } 397 | 398 | void reset() {} // clear internal state 399 | 400 | template 401 | NODISCARD_ result_type operator()(Engine_& Eng_) const { 402 | return Eval_(Eng_, Par_); 403 | } 404 | 405 | template 406 | NODISCARD_ result_type operator()(Engine_& Eng_, const param_type& Par0_) const { 407 | return Eval_(Eng_, Par0_); 408 | } 409 | 410 | template 411 | basic_istream& Read_(basic_istream& Istr_) { // read state from Istr_ 412 | Ty_ Min0_; 413 | Ty_ Max0_; 414 | In_(Istr_, Min0_); 415 | In_(Istr_, Max0_); 416 | Par_.Init_(Min0_, Max0_); 417 | return Istr_; 418 | } 419 | 420 | template 421 | basic_ostream& Write_(basic_ostream& Ostr_) const { // write state to Ostr_ 422 | Out_(Ostr_, Par_.Min_); 423 | Out_(Ostr_, Par_.Max_); 424 | return Ostr_; 425 | } 426 | 427 | private: 428 | template 429 | result_type Eval_(Engine_& Eng_, const param_type& Par0_) const { 430 | return NRAND_(Eng_, Ty_) * (Par0_.Max_ - Par0_.Min_) + Par0_.Min_; 431 | } 432 | 433 | param_type Par_; 434 | }; 435 | 436 | template 437 | basic_istream& operator>>(basic_istream& Istr_, 438 | uniform_real& Dist_) { // read state from Istr_ 439 | return Dist_.Read_(Istr_); 440 | } 441 | 442 | template 443 | basic_ostream& operator<<(basic_ostream& Ostr_, 444 | const uniform_real& Dist_) { // write state to Ostr_ 445 | return Dist_.Write_(Ostr_); 446 | } 447 | 448 | // CLASS TEMPLATE uniform_real_distribution 449 | template 450 | class uniform_real_distribution : public uniform_real { // uniform real distribution 451 | public: 452 | using Mybase_ = uniform_real; 453 | using Mypbase_ = typename Mybase_::param_type; 454 | using result_type = typename Mybase_::result_type; 455 | 456 | struct param_type : Mypbase_ { // parameter package 457 | using distribution_type = uniform_real_distribution; 458 | 459 | param_type() : Mypbase_(Ty_{0}, Ty_{1}) {} 460 | 461 | explicit param_type(Ty_ Min0_, Ty_ Max0_ = Ty_{1}) : Mypbase_(Min0_, Max0_) {} 462 | 463 | param_type(const Mypbase_& Right_) : Mypbase_(Right_) {} 464 | }; 465 | 466 | uniform_real_distribution() : Mybase_(Ty_{0}, Ty_{1}) {} 467 | 468 | explicit uniform_real_distribution(Ty_ Min0_, Ty_ Max0_ = Ty_{1}) : Mybase_(Min0_, Max0_) {} 469 | 470 | explicit uniform_real_distribution(const param_type& Par0_) : Mybase_(Par0_) {} 471 | }; 472 | 473 | template 474 | NODISCARD_ bool operator==(const uniform_real_distribution& Left_, const uniform_real_distribution& Right_) { 475 | return Left_.param() == Right_.param(); 476 | } 477 | 478 | template 479 | NODISCARD_ bool operator!=(const uniform_real_distribution& Left_, const uniform_real_distribution& Right_) { 480 | return !(Left_ == Right_); 481 | } 482 | 483 | #undef NRAND_ 484 | 485 | #undef NODISCARD_ 486 | 487 | } // namespace ms_stl 488 | -------------------------------------------------------------------------------- /sources/series/SeriesExpr.cpp: -------------------------------------------------------------------------------- 1 | #include "SeriesExpr.h" 2 | #include "Random.h" 3 | #include "SeriesExprGrammar.tab.h" 4 | #include "SeriesExprGrammar.yy.h" 5 | #include "SeriesExprGrammarExtra.h" 6 | #include 7 | #include 8 | #include 9 | 10 | bool ExprFunctionId::operator==(const ExprFunctionId &other) const 11 | { 12 | return name == other.name && arity == other.arity; 13 | } 14 | 15 | bool ExprFunctionId::operator!=(const ExprFunctionId &other) const 16 | { 17 | return !operator==(other); 18 | } 19 | 20 | size_t std::hash::operator()(const ExprFunctionId& id) const noexcept 21 | { 22 | return std::hash()(id.name) ^ std::hash()(id.arity); 23 | } 24 | 25 | /// 26 | std::array Expr::parse(const char *text) 27 | { 28 | yyscan_t scanner; 29 | YY_BUFFER_STATE buffer; 30 | ParserResult result; 31 | 32 | #if !defined(_WIN32) 33 | Locale c_locale(newlocale(LC_ALL_MASK, "C", (locale_t)0)); 34 | #else 35 | Locale c_locale(_create_locale(LC_ALL, "C")); 36 | #endif 37 | if (!c_locale) 38 | throw std::system_error(errno, std::generic_category()); 39 | 40 | if (yylex_init(&scanner) != 0) 41 | goto end1; 42 | 43 | SeriesExprGrammarExtra extra; 44 | extra.c_locale = *c_locale; 45 | yyset_extra(&extra, scanner); 46 | 47 | #if YYDEBUG 48 | yydebug = 1; 49 | yyset_debug(1 , scanner); 50 | #endif 51 | 52 | buffer = yy_scan_string(text, scanner); 53 | if (!buffer) 54 | goto end2; 55 | 56 | if (yyparse(scanner, &result) != 0) 57 | goto end3; 58 | 59 | end3: 60 | yy_delete_buffer(buffer, scanner); 61 | end2: 62 | yylex_destroy(scanner); 63 | end1: 64 | 65 | return result.expr; 66 | } 67 | 68 | /// 69 | expr_float_t Var::evalInterpreted(ExprContext& ctx) const 70 | { 71 | auto it = ctx.vars.find(id); 72 | if (it == ctx.vars.end()) 73 | return 0; // inexistent variable 74 | return it->second; 75 | } 76 | 77 | expr_float_t Call::evalInterpreted(ExprContext& ctx) const 78 | { 79 | auto it = ctx.funcs.find(id); 80 | if (it == ctx.funcs.end()) 81 | return 0; // inexistent function 82 | 83 | size_t arity = id.arity; 84 | std::unique_ptr values(new expr_float_t[arity]); 85 | 86 | for (size_t i = 0; i < arity; ++i) 87 | values[i] = args[i]->evalInterpreted(ctx); 88 | 89 | return it->second(values.get()); 90 | } 91 | 92 | expr_float_t Random::evalInterpreted(ExprContext& ctx) const 93 | { 94 | assert(ctx.prng); 95 | return ctx.prng->next_float(); 96 | } 97 | 98 | expr_float_t Number::evalInterpreted(ExprContext& ctx) const 99 | { 100 | return static_cast(number); 101 | } 102 | 103 | expr_float_t Add::evalInterpreted(ExprContext& ctx) const 104 | { 105 | return lhs->evalInterpreted(ctx) + rhs->evalInterpreted(ctx); 106 | } 107 | 108 | expr_float_t Sub::evalInterpreted(ExprContext& ctx) const 109 | { 110 | return lhs->evalInterpreted(ctx) - rhs->evalInterpreted(ctx); 111 | } 112 | 113 | expr_float_t Mul::evalInterpreted(ExprContext& ctx) const 114 | { 115 | return lhs->evalInterpreted(ctx) * rhs->evalInterpreted(ctx); 116 | } 117 | 118 | expr_float_t Div::evalInterpreted(ExprContext& ctx) const 119 | { 120 | return lhs->evalInterpreted(ctx) / rhs->evalInterpreted(ctx); 121 | } 122 | 123 | expr_float_t Mod::evalInterpreted(ExprContext& ctx) const 124 | { 125 | return std::fmod(lhs->evalInterpreted(ctx), rhs->evalInterpreted(ctx)); 126 | } 127 | 128 | expr_float_t Pow::evalInterpreted(ExprContext& ctx) const 129 | { 130 | return std::pow(lhs->evalInterpreted(ctx), rhs->evalInterpreted(ctx)); 131 | } 132 | 133 | expr_float_t Eq::evalInterpreted(ExprContext& ctx) const 134 | { 135 | return lhs->evalInterpreted(ctx) == rhs->evalInterpreted(ctx); 136 | } 137 | 138 | expr_float_t Neq::evalInterpreted(ExprContext& ctx) const 139 | { 140 | return lhs->evalInterpreted(ctx) != rhs->evalInterpreted(ctx); 141 | } 142 | 143 | expr_float_t Lt::evalInterpreted(ExprContext& ctx) const 144 | { 145 | return lhs->evalInterpreted(ctx) < rhs->evalInterpreted(ctx); 146 | } 147 | 148 | expr_float_t Gt::evalInterpreted(ExprContext& ctx) const 149 | { 150 | return lhs->evalInterpreted(ctx) > rhs->evalInterpreted(ctx); 151 | } 152 | 153 | expr_float_t Le::evalInterpreted(ExprContext& ctx) const 154 | { 155 | return lhs->evalInterpreted(ctx) <= rhs->evalInterpreted(ctx); 156 | } 157 | 158 | expr_float_t Ge::evalInterpreted(ExprContext& ctx) const 159 | { 160 | return lhs->evalInterpreted(ctx) >= rhs->evalInterpreted(ctx); 161 | } 162 | 163 | expr_float_t And::evalInterpreted(ExprContext& ctx) const 164 | { 165 | return lhs->evalInterpreted(ctx) && rhs->evalInterpreted(ctx); 166 | } 167 | 168 | expr_float_t Or::evalInterpreted(ExprContext& ctx) const 169 | { 170 | return lhs->evalInterpreted(ctx) || rhs->evalInterpreted(ctx); 171 | } 172 | 173 | /// 174 | void Var::repr(std::ostream &out) const 175 | { 176 | out << id; 177 | } 178 | 179 | void Call::repr(std::ostream &out) const 180 | { 181 | out << id.name << '('; 182 | for (size_t i = 0, n = args.size(); i < n; ++i) { 183 | if (i > 0) 184 | out << ", "; 185 | out << *args[i]; 186 | } 187 | out << ')'; 188 | } 189 | 190 | void Random::repr(std::ostream &out) const 191 | { 192 | out << '#'; 193 | } 194 | 195 | void Number::repr(std::ostream &out) const 196 | { 197 | out << number; 198 | } 199 | 200 | void Binop::genericRepr(std::ostream &out, const char *sym) const 201 | { 202 | out << '(' << *lhs << sym << *rhs << ')'; 203 | } 204 | 205 | /// 206 | std::ostream &operator<<(std::ostream &out, const Expr &expr) 207 | { 208 | expr.repr(out); 209 | return out; 210 | } 211 | -------------------------------------------------------------------------------- /sources/series/SeriesExpr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | class RandomGenerator; 9 | 10 | typedef double expr_float_t; 11 | typedef expr_float_t (expr_function_t)(const expr_float_t *args); 12 | 13 | /// 14 | class Expr; 15 | typedef std::shared_ptr ExprPtr; 16 | 17 | struct ExprFunctionId { 18 | ExprFunctionId() = default; 19 | ExprFunctionId(std::string name, size_t arity) : name(std::move(name)), arity(arity) {} 20 | bool operator==(const ExprFunctionId &other) const; 21 | bool operator!=(const ExprFunctionId &other) const; 22 | std::string name; 23 | size_t arity = 0; 24 | }; 25 | 26 | namespace std { 27 | template <> struct hash { 28 | size_t operator()(const ExprFunctionId& id) const noexcept; 29 | }; 30 | } 31 | 32 | /// 33 | struct ExprContext { 34 | RandomGenerator* prng = nullptr; 35 | std::unordered_map vars; 36 | std::unordered_map funcs; 37 | }; 38 | 39 | /// 40 | class Expr : public std::enable_shared_from_this { 41 | public: 42 | enum class Type { 43 | Var, 44 | Call, 45 | Number, 46 | Random, 47 | Add, 48 | Sub, 49 | Mul, 50 | Div, 51 | Mod, 52 | Pow, 53 | Eq, 54 | Neq, 55 | Lt, 56 | Gt, 57 | Le, 58 | Ge, 59 | And, 60 | Or, 61 | }; 62 | 63 | protected: 64 | explicit Expr(Type t) : type(t) {} 65 | 66 | public: 67 | virtual ~Expr() {} 68 | virtual void repr(std::ostream &out) const = 0; 69 | static std::array parse(const char *text); 70 | virtual expr_float_t evalInterpreted(ExprContext& ctx) const = 0; 71 | Type type; 72 | }; 73 | 74 | /// 75 | class Var : public Expr { 76 | public: 77 | explicit Var(std::string id) : Expr(Type::Var), id(std::move(id)) {} 78 | void repr(std::ostream &out) const override; 79 | expr_float_t evalInterpreted(ExprContext& ctx) const override; 80 | std::string id; 81 | }; 82 | 83 | /// 84 | class Call : public Expr { 85 | public: 86 | Call(std::string id, std::vector args) : Expr(Type::Call), id{std::move(id), args.size()}, args(std::move(args)) {} 87 | void repr(std::ostream &out) const override; 88 | expr_float_t evalInterpreted(ExprContext& ctx) const override; 89 | ExprFunctionId id; 90 | std::vector args; 91 | }; 92 | 93 | /// 94 | class Random : public Expr { 95 | public: 96 | Random() : Expr(Type::Random) {} 97 | void repr(std::ostream &out) const override; 98 | expr_float_t evalInterpreted(ExprContext& ctx) const override; 99 | }; 100 | 101 | /// 102 | class Number : public Expr { 103 | public: 104 | explicit Number(double n) : Expr(Type::Number), number(n) {} 105 | void repr(std::ostream &out) const override; 106 | expr_float_t evalInterpreted(ExprContext& ctx) const override; 107 | double number; 108 | }; 109 | 110 | /// 111 | class Binop : public Expr { 112 | protected: 113 | Binop(Type t, ExprPtr lhs, ExprPtr rhs) : Expr(t), lhs(lhs), rhs(rhs) {} 114 | void genericRepr(std::ostream &out, const char *sym) const; 115 | 116 | public: 117 | virtual ~Binop() {} 118 | ExprPtr lhs, rhs; 119 | }; 120 | 121 | /// 122 | template class BinopT : public Binop { 123 | public: 124 | BinopT(ExprPtr lhs, ExprPtr rhs) : Binop(T, lhs, rhs) {} 125 | virtual ~BinopT() {} 126 | }; 127 | 128 | /// 129 | #define DefBinop(id, sym) \ 130 | class id : public BinopT { \ 131 | public: \ 132 | using BinopT::BinopT; \ 133 | void repr(std::ostream &out) const override { genericRepr(out, sym); } \ 134 | expr_float_t evalInterpreted(ExprContext& ctx) const override; \ 135 | } 136 | 137 | DefBinop(Add, "+"); 138 | DefBinop(Sub, "-"); 139 | DefBinop(Mul, "*"); 140 | DefBinop(Div, "/"); 141 | DefBinop(Mod, "%"); 142 | DefBinop(Pow, "^"); 143 | DefBinop(Eq, "="); 144 | DefBinop(Neq, "!="); 145 | DefBinop(Lt, "<"); 146 | DefBinop(Gt, ">"); 147 | DefBinop(Le, "<="); 148 | DefBinop(Ge, ">="); 149 | DefBinop(And, "&&"); 150 | DefBinop(Or, "||"); 151 | 152 | /// 153 | std::ostream &operator<<(std::ostream &out, const Expr &expr); 154 | -------------------------------------------------------------------------------- /sources/series/SeriesExprGrammar.l: -------------------------------------------------------------------------------- 1 | %top{ 2 | #if defined(_WIN32) 3 | #define YY_NO_UNISTD_H 1 4 | #endif 5 | 6 | #if !defined(FLEXINT_H) 7 | # define FLEXINT_H 8 | # include 9 | typedef int8_t flex_int8_t; 10 | typedef uint8_t flex_uint8_t; 11 | typedef int16_t flex_int16_t; 12 | typedef uint16_t flex_uint16_t; 13 | typedef int32_t flex_int32_t; 14 | typedef uint32_t flex_uint32_t; 15 | #endif 16 | } 17 | 18 | %option noinput nounput noyywrap 8bit nodefault 19 | %option yylineno 20 | %option reentrant bison-bridge bison-locations 21 | %option extra-type="struct SeriesExprGrammarExtra *" 22 | /* %option debug */ 23 | 24 | %{ 25 | #include 26 | #include 27 | #include "SeriesExprGrammar.tab.h" 28 | #include "SeriesExprGrammarExtra.h" 29 | #include 30 | 31 | #if defined(_WIN32) 32 | #include 33 | #define fileno _fileno 34 | #define isatty _isatty 35 | #endif 36 | 37 | static double to_number(const char *text, Locale::handle_type loc) 38 | { 39 | double number; 40 | #if !defined(_WIN32) 41 | loc = uselocale(loc); 42 | number = atof(text); 43 | uselocale(loc); 44 | #else 45 | return _atof_l(text, loc); 46 | #endif 47 | return number; 48 | } 49 | %} 50 | 51 | DECIMAL [0-9]+ 52 | REAL ([0-9]+)?\.[0-9]* 53 | EXPONENT [eE][-\+]?[0-9]+ 54 | NUMBER ({DECIMAL}{EXPONENT}?)|({REAL}{EXPONENT}?) 55 | IDENTIFIER [_a-zA-Z][_a-zA-Z0-9]* 56 | 57 | %% 58 | 59 | [ \t\r\n]+ ; 60 | ";" return SEMICOLON; 61 | "#" return SHARP; 62 | "(" return OPEN; 63 | ")" return CLOSE; 64 | "," return COMMA; 65 | {IDENTIFIER} yylval->s = new std::string(yytext); return IDENTIFIER; 66 | {NUMBER} yylval->n = to_number(yytext, yyextra->c_locale); return NUMBER; 67 | "+" return PLUS; 68 | "-" return MINUS; 69 | "*" return TIMES; 70 | "/" return DIVIDE; 71 | "%" return MODULO; 72 | "^" return POWER; 73 | "=" return EQUAL; 74 | "!=" return NOTEQUAL; 75 | "<" return LT; 76 | ">" return GT; 77 | "<=" return LE; 78 | ">=" return GE; 79 | "||" return OR; 80 | "&&" return AND; 81 | <> return END; 82 | .|\n return INVALID; 83 | -------------------------------------------------------------------------------- /sources/series/SeriesExprGrammar.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.7.2. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, 6 | Inc. 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . */ 20 | 21 | /* As a special exception, you may create a larger work that contains 22 | part or all of the Bison parser skeleton and distribute that work 23 | under terms of your choice, so long as that work isn't itself a 24 | parser generator using the skeleton or a modified version thereof 25 | as a parser skeleton. Alternatively, if you modify or redistribute 26 | the parser skeleton itself, you may (at your option) remove this 27 | special exception, which will cause the skeleton and the resulting 28 | Bison output files to be licensed under the GNU General Public 29 | License without this special exception. 30 | 31 | This special exception was added by the Free Software Foundation in 32 | version 2.2 of Bison. */ 33 | 34 | /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, 35 | especially those whose name start with YY_ or yy_. They are 36 | private implementation details that can be changed or removed. */ 37 | 38 | #ifndef YY_YY_SERIESEXPRGRAMMAR_TAB_H_INCLUDED 39 | # define YY_YY_SERIESEXPRGRAMMAR_TAB_H_INCLUDED 40 | /* Debug traces. */ 41 | #ifndef YYDEBUG 42 | # define YYDEBUG 0 43 | #endif 44 | #if YYDEBUG 45 | extern int yydebug; 46 | #endif 47 | /* "%code requires" blocks. */ 48 | #line 11 "SeriesExprGrammar.y" 49 | 50 | #include "SeriesExpr.h" 51 | #include 52 | 53 | typedef std::vector ArgList; 54 | typedef std::unique_ptr ArgListPtr; 55 | 56 | union sval { 57 | Expr *e; 58 | double n; 59 | std::string *s; 60 | ArgList *a; 61 | }; 62 | 63 | struct ParserResult { 64 | std::array expr; 65 | }; 66 | 67 | typedef void *yyscan_t; 68 | 69 | #line 70 "SeriesExprGrammar.tab.h" 70 | 71 | /* Token kinds. */ 72 | #ifndef YYTOKENTYPE 73 | # define YYTOKENTYPE 74 | enum yytokentype 75 | { 76 | YYEMPTY = -2, 77 | YYEOF = 0, /* "end of file" */ 78 | YYerror = 256, /* error */ 79 | YYUNDEF = 257, /* "invalid token" */ 80 | SEMICOLON = 258, /* SEMICOLON */ 81 | SHARP = 259, /* SHARP */ 82 | OPEN = 260, /* OPEN */ 83 | CLOSE = 261, /* CLOSE */ 84 | COMMA = 262, /* COMMA */ 85 | NUMBER = 263, /* NUMBER */ 86 | IDENTIFIER = 264, /* IDENTIFIER */ 87 | INVALID = 265, /* INVALID */ 88 | END = 266, /* END */ 89 | UNOP = 267, /* UNOP */ 90 | PLUS = 268, /* PLUS */ 91 | MINUS = 269, /* MINUS */ 92 | EQUAL = 270, /* EQUAL */ 93 | NOTEQUAL = 271, /* NOTEQUAL */ 94 | LT = 272, /* LT */ 95 | GT = 273, /* GT */ 96 | LE = 274, /* LE */ 97 | GE = 275, /* GE */ 98 | TIMES = 276, /* TIMES */ 99 | DIVIDE = 277, /* DIVIDE */ 100 | MODULO = 278, /* MODULO */ 101 | AND = 279, /* AND */ 102 | OR = 280, /* OR */ 103 | POWER = 281 /* POWER */ 104 | }; 105 | typedef enum yytokentype yytoken_kind_t; 106 | #endif 107 | 108 | /* Value type. */ 109 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 110 | typedef union sval YYSTYPE; 111 | # define YYSTYPE_IS_TRIVIAL 1 112 | # define YYSTYPE_IS_DECLARED 1 113 | #endif 114 | 115 | /* Location type. */ 116 | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED 117 | typedef struct YYLTYPE YYLTYPE; 118 | struct YYLTYPE 119 | { 120 | int first_line; 121 | int first_column; 122 | int last_line; 123 | int last_column; 124 | }; 125 | # define YYLTYPE_IS_DECLARED 1 126 | # define YYLTYPE_IS_TRIVIAL 1 127 | #endif 128 | 129 | 130 | 131 | int yyparse (yyscan_t scanner, ParserResult *parser_result); 132 | 133 | #endif /* !YY_YY_SERIESEXPRGRAMMAR_TAB_H_INCLUDED */ 134 | -------------------------------------------------------------------------------- /sources/series/SeriesExprGrammar.y: -------------------------------------------------------------------------------- 1 | %define api.pure full 2 | %define api.value.type {union sval} 3 | %locations 4 | %param { yyscan_t scanner } 5 | %parse-param { ParserResult *parser_result } 6 | 7 | %code top { 8 | #include 9 | } 10 | 11 | %code requires { 12 | #include "SeriesExpr.h" 13 | #include 14 | 15 | typedef std::vector ArgList; 16 | typedef std::unique_ptr ArgListPtr; 17 | 18 | union sval { 19 | Expr *e; 20 | double n; 21 | std::string *s; 22 | ArgList *a; 23 | }; 24 | 25 | struct ParserResult { 26 | std::array expr; 27 | }; 28 | 29 | typedef void *yyscan_t; 30 | } 31 | 32 | %code { 33 | int yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t scanner); 34 | void yyerror(YYLTYPE *yyllocp, yyscan_t unused, ParserResult *parser_result, const char *msg); 35 | 36 | static ExprPtr takeExpr(sval &sv) 37 | { 38 | ExprPtr e(sv.e); 39 | sv.e = nullptr; 40 | return e; 41 | } 42 | 43 | static std::string takeString(sval &sv) 44 | { 45 | std::string s = std::move(*sv.s); 46 | delete sv.s; 47 | sv.s = nullptr; 48 | return s; 49 | } 50 | 51 | static ArgListPtr takeArgListPtr(sval &sv) 52 | { 53 | ArgListPtr a(sv.a); 54 | sv.a = nullptr; 55 | return a; 56 | } 57 | 58 | static ArgList takeArgList(sval &sv) 59 | { 60 | ArgList a = std::move(*sv.a); 61 | sv.a = nullptr; 62 | return a; 63 | } 64 | } 65 | 66 | %destructor { delete $$.e; } expr 67 | %destructor { delete $$.a; } maybe_args args_rest 68 | %destructor { delete $$.s; } IDENTIFIER 69 | 70 | %token SEMICOLON SHARP OPEN CLOSE COMMA NUMBER IDENTIFIER INVALID END UNOP 71 | %left PLUS MINUS 72 | %nonassoc EQUAL NOTEQUAL LT GT LE GE 73 | %left TIMES DIVIDE MODULO 74 | %left AND OR 75 | %left POWER 76 | %precedence UNOP 77 | 78 | %% 79 | 80 | input: expr input2exp END { parser_result->expr[0].reset($1.e); $1.e = nullptr; } 81 | input2exp: SEMICOLON expr { parser_result->expr[1].reset($2.e); $2.e = nullptr; } 82 | | %empty { } 83 | 84 | expr : NUMBER { $$.e = new Number($1.n); } 85 | | IDENTIFIER maybe_args { if (!$2.a) $$.e = new Var(takeString($1)); 86 | else $$.e = new Call(takeString($1), takeArgList($2)); } 87 | | SHARP { $$.e = new Random; } 88 | | OPEN expr CLOSE { $$.e = $2.e; $2.e = nullptr; } 89 | | PLUS expr %prec UNOP { $$.e = $2.e; $2.e = nullptr; } 90 | | MINUS expr %prec UNOP { $$.e = new Sub(ExprPtr(new Number(0)), takeExpr($2)); } 91 | | expr PLUS expr { $$.e = new Add(takeExpr($1), takeExpr($3)); } 92 | | expr MINUS expr { $$.e = new Sub(takeExpr($1), takeExpr($3)); } 93 | | expr TIMES expr { $$.e = new Mul(takeExpr($1), takeExpr($3)); } 94 | | expr DIVIDE expr { $$.e = new Div(takeExpr($1), takeExpr($3)); } 95 | | expr MODULO expr { $$.e = new Mod(takeExpr($1), takeExpr($3)); } 96 | | expr POWER expr { $$.e = new Pow(takeExpr($1), takeExpr($3)); } 97 | | expr EQUAL expr { $$.e = new Eq(takeExpr($1), takeExpr($3)); } 98 | | expr NOTEQUAL expr { $$.e = new Neq(takeExpr($1), takeExpr($3)); } 99 | | expr LT expr { $$.e = new Lt(takeExpr($1), takeExpr($3)); } 100 | | expr GT expr { $$.e = new Gt(takeExpr($1), takeExpr($3)); } 101 | | expr LE expr { $$.e = new Le(takeExpr($1), takeExpr($3)); } 102 | | expr GE expr { $$.e = new Ge(takeExpr($1), takeExpr($3)); } 103 | | expr AND expr { $$.e = new And(takeExpr($1), takeExpr($3)); } 104 | | expr OR expr { $$.e = new Or(takeExpr($1), takeExpr($3)); } 105 | 106 | maybe_args : OPEN CLOSE { $$.a = new ArgList; } 107 | | OPEN expr args_rest { $$.a = takeArgListPtr($3).release(); 108 | $$.a->insert($$.a->begin(), takeExpr($2)); } 109 | | %empty { $$.a = nullptr; } 110 | 111 | args_rest : CLOSE { $$.a = new ArgList; } 112 | | COMMA expr args_rest { $$.a = takeArgListPtr($3).release(); 113 | $$.a->insert($$.a->begin(), takeExpr($2)); } 114 | 115 | %% 116 | 117 | void yyerror(YYLTYPE *yyllocp, yyscan_t unused, ParserResult *parser_result, const char *msg) 118 | { 119 | //fprintf(stderr, "[%d:%d]: %s\n", yyllocp->first_line, yyllocp->first_column, msg); 120 | } 121 | -------------------------------------------------------------------------------- /sources/series/SeriesExprGrammar.yy.h: -------------------------------------------------------------------------------- 1 | #ifndef yyHEADER_H 2 | #define yyHEADER_H 1 3 | #define yyIN_HEADER 1 4 | 5 | #line 5 "SeriesExprGrammar.yy.h" 6 | #if defined(_WIN32) 7 | #define YY_NO_UNISTD_H 1 8 | #endif 9 | 10 | #if !defined(FLEXINT_H) 11 | # define FLEXINT_H 12 | # include 13 | typedef int8_t flex_int8_t; 14 | typedef uint8_t flex_uint8_t; 15 | typedef int16_t flex_int16_t; 16 | typedef uint16_t flex_uint16_t; 17 | typedef int32_t flex_int32_t; 18 | typedef uint32_t flex_uint32_t; 19 | #endif 20 | 21 | #line 21 "SeriesExprGrammar.yy.h" 22 | 23 | #define YY_INT_ALIGNED short int 24 | 25 | /* A lexical scanner generated by flex */ 26 | 27 | #define FLEX_SCANNER 28 | #define YY_FLEX_MAJOR_VERSION 2 29 | #define YY_FLEX_MINOR_VERSION 6 30 | #define YY_FLEX_SUBMINOR_VERSION 4 31 | #if YY_FLEX_SUBMINOR_VERSION > 0 32 | #define FLEX_BETA 33 | #endif 34 | 35 | #ifdef yyget_lval 36 | #define yyget_lval_ALREADY_DEFINED 37 | #else 38 | #define yyget_lval yyget_lval 39 | #endif 40 | 41 | #ifdef yyset_lval 42 | #define yyset_lval_ALREADY_DEFINED 43 | #else 44 | #define yyset_lval yyset_lval 45 | #endif 46 | 47 | #ifdef yyget_lloc 48 | #define yyget_lloc_ALREADY_DEFINED 49 | #else 50 | #define yyget_lloc yyget_lloc 51 | #endif 52 | 53 | #ifdef yyset_lloc 54 | #define yyset_lloc_ALREADY_DEFINED 55 | #else 56 | #define yyset_lloc yyset_lloc 57 | #endif 58 | 59 | /* First, we deal with platform-specific or compiler-specific issues. */ 60 | 61 | /* begin standard C headers. */ 62 | #include 63 | #include 64 | #include 65 | #include 66 | 67 | /* end standard C headers. */ 68 | 69 | /* flex integer type definitions */ 70 | 71 | #ifndef FLEXINT_H 72 | #define FLEXINT_H 73 | 74 | /* C99 systems have . Non-C99 systems may or may not. */ 75 | 76 | #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 77 | 78 | /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, 79 | * if you want the limit (max/min) macros for int types. 80 | */ 81 | #ifndef __STDC_LIMIT_MACROS 82 | #define __STDC_LIMIT_MACROS 1 83 | #endif 84 | 85 | #include 86 | typedef int8_t flex_int8_t; 87 | typedef uint8_t flex_uint8_t; 88 | typedef int16_t flex_int16_t; 89 | typedef uint16_t flex_uint16_t; 90 | typedef int32_t flex_int32_t; 91 | typedef uint32_t flex_uint32_t; 92 | #else 93 | typedef signed char flex_int8_t; 94 | typedef short int flex_int16_t; 95 | typedef int flex_int32_t; 96 | typedef unsigned char flex_uint8_t; 97 | typedef unsigned short int flex_uint16_t; 98 | typedef unsigned int flex_uint32_t; 99 | 100 | /* Limits of integral types. */ 101 | #ifndef INT8_MIN 102 | #define INT8_MIN (-128) 103 | #endif 104 | #ifndef INT16_MIN 105 | #define INT16_MIN (-32767-1) 106 | #endif 107 | #ifndef INT32_MIN 108 | #define INT32_MIN (-2147483647-1) 109 | #endif 110 | #ifndef INT8_MAX 111 | #define INT8_MAX (127) 112 | #endif 113 | #ifndef INT16_MAX 114 | #define INT16_MAX (32767) 115 | #endif 116 | #ifndef INT32_MAX 117 | #define INT32_MAX (2147483647) 118 | #endif 119 | #ifndef UINT8_MAX 120 | #define UINT8_MAX (255U) 121 | #endif 122 | #ifndef UINT16_MAX 123 | #define UINT16_MAX (65535U) 124 | #endif 125 | #ifndef UINT32_MAX 126 | #define UINT32_MAX (4294967295U) 127 | #endif 128 | 129 | #ifndef SIZE_MAX 130 | #define SIZE_MAX (~(size_t)0) 131 | #endif 132 | 133 | #endif /* ! C99 */ 134 | 135 | #endif /* ! FLEXINT_H */ 136 | 137 | /* begin standard C++ headers. */ 138 | 139 | /* TODO: this is always defined, so inline it */ 140 | #define yyconst const 141 | 142 | #if defined(__GNUC__) && __GNUC__ >= 3 143 | #define yynoreturn __attribute__((__noreturn__)) 144 | #else 145 | #define yynoreturn 146 | #endif 147 | 148 | /* An opaque pointer. */ 149 | #ifndef YY_TYPEDEF_YY_SCANNER_T 150 | #define YY_TYPEDEF_YY_SCANNER_T 151 | typedef void* yyscan_t; 152 | #endif 153 | 154 | /* For convenience, these vars (plus the bison vars far below) 155 | are macros in the reentrant scanner. */ 156 | #define yyin yyg->yyin_r 157 | #define yyout yyg->yyout_r 158 | #define yyextra yyg->yyextra_r 159 | #define yyleng yyg->yyleng_r 160 | #define yytext yyg->yytext_r 161 | #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) 162 | #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) 163 | #define yy_flex_debug yyg->yy_flex_debug_r 164 | 165 | /* Size of default input buffer. */ 166 | #ifndef YY_BUF_SIZE 167 | #ifdef __ia64__ 168 | /* On IA-64, the buffer size is 16k, not 8k. 169 | * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. 170 | * Ditto for the __ia64__ case accordingly. 171 | */ 172 | #define YY_BUF_SIZE 32768 173 | #else 174 | #define YY_BUF_SIZE 16384 175 | #endif /* __ia64__ */ 176 | #endif 177 | 178 | #ifndef YY_TYPEDEF_YY_BUFFER_STATE 179 | #define YY_TYPEDEF_YY_BUFFER_STATE 180 | typedef struct yy_buffer_state *YY_BUFFER_STATE; 181 | #endif 182 | 183 | #ifndef YY_TYPEDEF_YY_SIZE_T 184 | #define YY_TYPEDEF_YY_SIZE_T 185 | typedef size_t yy_size_t; 186 | #endif 187 | 188 | #ifndef YY_STRUCT_YY_BUFFER_STATE 189 | #define YY_STRUCT_YY_BUFFER_STATE 190 | struct yy_buffer_state 191 | { 192 | FILE *yy_input_file; 193 | 194 | char *yy_ch_buf; /* input buffer */ 195 | char *yy_buf_pos; /* current position in input buffer */ 196 | 197 | /* Size of input buffer in bytes, not including room for EOB 198 | * characters. 199 | */ 200 | int yy_buf_size; 201 | 202 | /* Number of characters read into yy_ch_buf, not including EOB 203 | * characters. 204 | */ 205 | int yy_n_chars; 206 | 207 | /* Whether we "own" the buffer - i.e., we know we created it, 208 | * and can realloc() it to grow it, and should free() it to 209 | * delete it. 210 | */ 211 | int yy_is_our_buffer; 212 | 213 | /* Whether this is an "interactive" input source; if so, and 214 | * if we're using stdio for input, then we want to use getc() 215 | * instead of fread(), to make sure we stop fetching input after 216 | * each newline. 217 | */ 218 | int yy_is_interactive; 219 | 220 | /* Whether we're considered to be at the beginning of a line. 221 | * If so, '^' rules will be active on the next match, otherwise 222 | * not. 223 | */ 224 | int yy_at_bol; 225 | 226 | int yy_bs_lineno; /**< The line count. */ 227 | int yy_bs_column; /**< The column count. */ 228 | 229 | /* Whether to try to fill the input buffer when we reach the 230 | * end of it. 231 | */ 232 | int yy_fill_buffer; 233 | 234 | int yy_buffer_status; 235 | 236 | }; 237 | #endif /* !YY_STRUCT_YY_BUFFER_STATE */ 238 | 239 | void yyrestart ( FILE *input_file , yyscan_t yyscanner ); 240 | void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); 241 | YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); 242 | void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); 243 | void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); 244 | void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); 245 | void yypop_buffer_state ( yyscan_t yyscanner ); 246 | 247 | YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); 248 | YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); 249 | YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); 250 | 251 | void *yyalloc ( yy_size_t , yyscan_t yyscanner ); 252 | void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); 253 | void yyfree ( void * , yyscan_t yyscanner ); 254 | 255 | #define yywrap(yyscanner) (/*CONSTCOND*/1) 256 | #define YY_SKIP_YYWRAP 257 | 258 | #define yytext_ptr yytext_r 259 | 260 | #ifdef YY_HEADER_EXPORT_START_CONDITIONS 261 | #define INITIAL 0 262 | 263 | #endif 264 | 265 | #ifndef YY_NO_UNISTD_H 266 | /* Special case for "unistd.h", since it is non-ANSI. We include it way 267 | * down here because we want the user's section 1 to have been scanned first. 268 | * The user has a chance to override it with an option. 269 | */ 270 | #include 271 | #endif 272 | 273 | #define YY_EXTRA_TYPE struct SeriesExprGrammarExtra * 274 | 275 | int yylex_init (yyscan_t* scanner); 276 | 277 | int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); 278 | 279 | /* Accessor methods to globals. 280 | These are made visible to non-reentrant scanners for convenience. */ 281 | 282 | int yylex_destroy ( yyscan_t yyscanner ); 283 | 284 | int yyget_debug ( yyscan_t yyscanner ); 285 | 286 | void yyset_debug ( int debug_flag , yyscan_t yyscanner ); 287 | 288 | YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); 289 | 290 | void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); 291 | 292 | FILE *yyget_in ( yyscan_t yyscanner ); 293 | 294 | void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); 295 | 296 | FILE *yyget_out ( yyscan_t yyscanner ); 297 | 298 | void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); 299 | 300 | int yyget_leng ( yyscan_t yyscanner ); 301 | 302 | char *yyget_text ( yyscan_t yyscanner ); 303 | 304 | int yyget_lineno ( yyscan_t yyscanner ); 305 | 306 | void yyset_lineno ( int _line_number , yyscan_t yyscanner ); 307 | 308 | int yyget_column ( yyscan_t yyscanner ); 309 | 310 | void yyset_column ( int _column_no , yyscan_t yyscanner ); 311 | 312 | YYSTYPE * yyget_lval ( yyscan_t yyscanner ); 313 | 314 | void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); 315 | 316 | YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); 317 | 318 | void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); 319 | 320 | /* Macros after this point can all be overridden by user definitions in 321 | * section 1. 322 | */ 323 | 324 | #ifndef YY_SKIP_YYWRAP 325 | #ifdef __cplusplus 326 | extern "C" int yywrap ( yyscan_t yyscanner ); 327 | #else 328 | extern int yywrap ( yyscan_t yyscanner ); 329 | #endif 330 | #endif 331 | 332 | #ifndef yytext_ptr 333 | static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); 334 | #endif 335 | 336 | #ifdef YY_NEED_STRLEN 337 | static int yy_flex_strlen ( const char * , yyscan_t yyscanner); 338 | #endif 339 | 340 | #ifndef YY_NO_INPUT 341 | 342 | #endif 343 | 344 | /* Amount of stuff to slurp up with each read. */ 345 | #ifndef YY_READ_BUF_SIZE 346 | #ifdef __ia64__ 347 | /* On IA-64, the buffer size is 16k, not 8k */ 348 | #define YY_READ_BUF_SIZE 16384 349 | #else 350 | #define YY_READ_BUF_SIZE 8192 351 | #endif /* __ia64__ */ 352 | #endif 353 | 354 | /* Number of entries by which start-condition stack grows. */ 355 | #ifndef YY_START_STACK_INCR 356 | #define YY_START_STACK_INCR 25 357 | #endif 358 | 359 | /* Default declaration of generated scanner - a define so the user can 360 | * easily add parameters. 361 | */ 362 | #ifndef YY_DECL 363 | #define YY_DECL_IS_OURS 1 364 | 365 | extern int yylex \ 366 | (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); 367 | 368 | #define YY_DECL int yylex \ 369 | (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) 370 | #endif /* !YY_DECL */ 371 | 372 | /* yy_get_previous_state - get the state just before the EOB char was reached */ 373 | 374 | #undef YY_NEW_FILE 375 | #undef YY_FLUSH_BUFFER 376 | #undef yy_set_bol 377 | #undef yy_new_buffer 378 | #undef yy_set_interactive 379 | #undef YY_DO_BEFORE_ACTION 380 | 381 | #ifdef YY_DECL_IS_OURS 382 | #undef YY_DECL_IS_OURS 383 | #undef YY_DECL 384 | #endif 385 | 386 | #ifndef yy_create_buffer_ALREADY_DEFINED 387 | #undef yy_create_buffer 388 | #endif 389 | #ifndef yy_delete_buffer_ALREADY_DEFINED 390 | #undef yy_delete_buffer 391 | #endif 392 | #ifndef yy_scan_buffer_ALREADY_DEFINED 393 | #undef yy_scan_buffer 394 | #endif 395 | #ifndef yy_scan_string_ALREADY_DEFINED 396 | #undef yy_scan_string 397 | #endif 398 | #ifndef yy_scan_bytes_ALREADY_DEFINED 399 | #undef yy_scan_bytes 400 | #endif 401 | #ifndef yy_init_buffer_ALREADY_DEFINED 402 | #undef yy_init_buffer 403 | #endif 404 | #ifndef yy_flush_buffer_ALREADY_DEFINED 405 | #undef yy_flush_buffer 406 | #endif 407 | #ifndef yy_load_buffer_state_ALREADY_DEFINED 408 | #undef yy_load_buffer_state 409 | #endif 410 | #ifndef yy_switch_to_buffer_ALREADY_DEFINED 411 | #undef yy_switch_to_buffer 412 | #endif 413 | #ifndef yypush_buffer_state_ALREADY_DEFINED 414 | #undef yypush_buffer_state 415 | #endif 416 | #ifndef yypop_buffer_state_ALREADY_DEFINED 417 | #undef yypop_buffer_state 418 | #endif 419 | #ifndef yyensure_buffer_stack_ALREADY_DEFINED 420 | #undef yyensure_buffer_stack 421 | #endif 422 | #ifndef yylex_ALREADY_DEFINED 423 | #undef yylex 424 | #endif 425 | #ifndef yyrestart_ALREADY_DEFINED 426 | #undef yyrestart 427 | #endif 428 | #ifndef yylex_init_ALREADY_DEFINED 429 | #undef yylex_init 430 | #endif 431 | #ifndef yylex_init_extra_ALREADY_DEFINED 432 | #undef yylex_init_extra 433 | #endif 434 | #ifndef yylex_destroy_ALREADY_DEFINED 435 | #undef yylex_destroy 436 | #endif 437 | #ifndef yyget_debug_ALREADY_DEFINED 438 | #undef yyget_debug 439 | #endif 440 | #ifndef yyset_debug_ALREADY_DEFINED 441 | #undef yyset_debug 442 | #endif 443 | #ifndef yyget_extra_ALREADY_DEFINED 444 | #undef yyget_extra 445 | #endif 446 | #ifndef yyset_extra_ALREADY_DEFINED 447 | #undef yyset_extra 448 | #endif 449 | #ifndef yyget_in_ALREADY_DEFINED 450 | #undef yyget_in 451 | #endif 452 | #ifndef yyset_in_ALREADY_DEFINED 453 | #undef yyset_in 454 | #endif 455 | #ifndef yyget_out_ALREADY_DEFINED 456 | #undef yyget_out 457 | #endif 458 | #ifndef yyset_out_ALREADY_DEFINED 459 | #undef yyset_out 460 | #endif 461 | #ifndef yyget_leng_ALREADY_DEFINED 462 | #undef yyget_leng 463 | #endif 464 | #ifndef yyget_text_ALREADY_DEFINED 465 | #undef yyget_text 466 | #endif 467 | #ifndef yyget_lineno_ALREADY_DEFINED 468 | #undef yyget_lineno 469 | #endif 470 | #ifndef yyset_lineno_ALREADY_DEFINED 471 | #undef yyset_lineno 472 | #endif 473 | #ifndef yyget_column_ALREADY_DEFINED 474 | #undef yyget_column 475 | #endif 476 | #ifndef yyset_column_ALREADY_DEFINED 477 | #undef yyset_column 478 | #endif 479 | #ifndef yywrap_ALREADY_DEFINED 480 | #undef yywrap 481 | #endif 482 | #ifndef yyget_lval_ALREADY_DEFINED 483 | #undef yyget_lval 484 | #endif 485 | #ifndef yyset_lval_ALREADY_DEFINED 486 | #undef yyset_lval 487 | #endif 488 | #ifndef yyget_lloc_ALREADY_DEFINED 489 | #undef yyget_lloc 490 | #endif 491 | #ifndef yyset_lloc_ALREADY_DEFINED 492 | #undef yyset_lloc 493 | #endif 494 | #ifndef yyalloc_ALREADY_DEFINED 495 | #undef yyalloc 496 | #endif 497 | #ifndef yyrealloc_ALREADY_DEFINED 498 | #undef yyrealloc 499 | #endif 500 | #ifndef yyfree_ALREADY_DEFINED 501 | #undef yyfree 502 | #endif 503 | #ifndef yytext_ALREADY_DEFINED 504 | #undef yytext 505 | #endif 506 | #ifndef yyleng_ALREADY_DEFINED 507 | #undef yyleng 508 | #endif 509 | #ifndef yyin_ALREADY_DEFINED 510 | #undef yyin 511 | #endif 512 | #ifndef yyout_ALREADY_DEFINED 513 | #undef yyout 514 | #endif 515 | #ifndef yy_flex_debug_ALREADY_DEFINED 516 | #undef yy_flex_debug 517 | #endif 518 | #ifndef yylineno_ALREADY_DEFINED 519 | #undef yylineno 520 | #endif 521 | #ifndef yytables_fload_ALREADY_DEFINED 522 | #undef yytables_fload 523 | #endif 524 | #ifndef yytables_destroy_ALREADY_DEFINED 525 | #undef yytables_destroy 526 | #endif 527 | #ifndef yyTABLES_NAME_ALREADY_DEFINED 528 | #undef yyTABLES_NAME 529 | #endif 530 | 531 | #line 83 "SeriesExprGrammar.l" 532 | 533 | #line 533 "SeriesExprGrammar.yy.h" 534 | #undef yyIN_HEADER 535 | #endif /* yyHEADER_H */ 536 | -------------------------------------------------------------------------------- /sources/series/SeriesExprGrammarExtra.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "utility/Locale.h" 3 | #include 4 | 5 | struct SeriesExprGrammarExtra { 6 | Locale::handle_type c_locale; 7 | }; 8 | -------------------------------------------------------------------------------- /sources/utility/Locale.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Locale { 5 | public: 6 | #if !defined(_WIN32) 7 | typedef locale_t handle_type; 8 | #else 9 | typedef _locale_t handle_type; 10 | #endif 11 | 12 | explicit Locale(handle_type handle = (handle_type)0) noexcept; 13 | ~Locale() noexcept; 14 | explicit operator bool() const noexcept; 15 | handle_type operator*() const noexcept; 16 | 17 | private: 18 | handle_type handle_ = (handle_type)0; 19 | }; 20 | 21 | //------------------------------------------------------------------------------ 22 | inline Locale::Locale(handle_type handle) noexcept 23 | : handle_(handle) 24 | { 25 | } 26 | 27 | inline Locale::~Locale() noexcept 28 | { 29 | #if !defined(_WIN32) 30 | if (*this) freelocale(handle_); 31 | #else 32 | if (*this) _free_locale(handle_); 33 | #endif 34 | } 35 | 36 | inline Locale::operator bool() const noexcept 37 | { 38 | return handle_ != (handle_type)0; 39 | } 40 | 41 | inline Locale::handle_type Locale::operator*() const noexcept 42 | { 43 | return handle_; 44 | } 45 | -------------------------------------------------------------------------------- /sources/utility/unicodefile.c: -------------------------------------------------------------------------------- 1 | #include "unicodefile.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #if defined(_WIN32) 8 | #include 9 | #endif 10 | 11 | #if !defined(_WIN32) 12 | FILE *fopen_utf8(const char *path, const char *mode) 13 | { 14 | return fopen(path, mode); 15 | } 16 | #else 17 | FILE *fopen_utf8(const char *path, const char *mode) 18 | { 19 | size_t npath = strlen(path); 20 | size_t nmode = strlen(mode); 21 | wchar_t *wpath = NULL; 22 | wchar_t wmode[8]; 23 | FILE *fh = NULL; 24 | 25 | if (npath > (size_t)INT_MAX || nmode > 7) { 26 | errno = EINVAL; 27 | goto end; 28 | } 29 | 30 | wpath = (wchar_t *)malloc((npath + 1) * sizeof(wchar_t)); 31 | if (!wpath) { 32 | errno = ENOMEM; 33 | goto end; 34 | } 35 | 36 | if (!MultiByteToWideChar(CP_UTF8, 0, path, npath + 1, wpath, npath + 1) || 37 | !MultiByteToWideChar(CP_UTF8, 0, mode, nmode + 1, wmode, nmode + 1)) 38 | { 39 | errno = EINVAL; 40 | goto end; 41 | } 42 | 43 | fh = _wfopen(wpath, wmode); 44 | 45 | end: 46 | if (wpath) 47 | free(wpath); 48 | return fh; 49 | } 50 | #endif 51 | -------------------------------------------------------------------------------- /sources/utility/unicodefile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #if defined(__cplusplus) 5 | extern "C" { 6 | #endif 7 | 8 | FILE *fopen_utf8(const char *path, const char *mode); 9 | 10 | #if defined(__cplusplus) 11 | } // extern "C" 12 | #endif 13 | -------------------------------------------------------------------------------- /sources/utility/utf8main.c: -------------------------------------------------------------------------------- 1 | #if defined(_WIN32) 2 | #include 3 | #include 4 | #endif 5 | 6 | int utf8main(int argc, char *argv[]); 7 | 8 | #if !defined(_WIN32) 9 | int main(int argc, char *argv[]) 10 | { 11 | return utf8main(argc, argv); 12 | } 13 | #else 14 | int WINAPI wWinMain( 15 | HINSTANCE instance, HINSTANCE prev_instance, PWSTR cmd_line, int cmd_show) 16 | { 17 | int ret = 1; 18 | unsigned argc = __argc; 19 | wchar_t **wide_argv = __wargv; 20 | char **utf8_argv; 21 | unsigned count; 22 | unsigned i; 23 | 24 | (void)instance; 25 | (void)prev_instance; 26 | (void)cmd_line; 27 | (void)cmd_show; 28 | 29 | utf8_argv = calloc(argc + 1, sizeof(char *)); 30 | if (!utf8_argv) 31 | goto end; 32 | 33 | for (i = 0; i < argc; ++i) { 34 | count = WideCharToMultiByte(CP_UTF8, 0, wide_argv[i], -1, NULL, 0, NULL, NULL); 35 | if (count == 0) 36 | goto end; 37 | utf8_argv[i] = malloc(count); 38 | if (!utf8_argv[i]) 39 | goto end; 40 | if ((unsigned)WideCharToMultiByte(CP_UTF8, 0, wide_argv[i], -1, utf8_argv[i], count, NULL, NULL) != count) 41 | goto end; 42 | } 43 | 44 | ret = utf8main(argc, utf8_argv); 45 | 46 | end: 47 | if (utf8_argv) { 48 | for (i = 0; i < argc; ++i) 49 | free(utf8_argv[i]); 50 | free(utf8_argv); 51 | } 52 | return ret; 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /thirdparty/kiss_fft/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This CMake build file is part of sfizz 2 | 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | project(sfizz-kissfft VERSION "1.3.0" LANGUAGES C) 6 | 7 | add_library(sfizz-kissfft STATIC 8 | kiss_fft.c 9 | tools/kiss_fftr.c) 10 | target_include_directories(sfizz-kissfft 11 | PUBLIC "." 12 | PUBLIC "tools") 13 | -------------------------------------------------------------------------------- /thirdparty/kiss_fft/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2010 Mark Borgerding 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /thirdparty/kiss_fft/_kiss_fft_guts.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2010, Mark Borgerding 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | /* kiss_fft.h 16 | defines kiss_fft_scalar as either short or a float type 17 | and defines 18 | typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ 19 | #include "kiss_fft.h" 20 | #include 21 | 22 | #define MAXFACTORS 32 23 | /* e.g. an fft of length 128 has 4 factors 24 | as far as kissfft is concerned 25 | 4*4*4*2 26 | */ 27 | 28 | struct kiss_fft_state{ 29 | int nfft; 30 | int inverse; 31 | int factors[2*MAXFACTORS]; 32 | kiss_fft_cpx twiddles[1]; 33 | }; 34 | 35 | /* 36 | Explanation of macros dealing with complex math: 37 | 38 | C_MUL(m,a,b) : m = a*b 39 | C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise 40 | C_SUB( res, a,b) : res = a - b 41 | C_SUBFROM( res , a) : res -= a 42 | C_ADDTO( res , a) : res += a 43 | * */ 44 | #ifdef FIXED_POINT 45 | #if (FIXED_POINT==32) 46 | # define FRACBITS 31 47 | # define SAMPPROD int64_t 48 | #define SAMP_MAX 2147483647 49 | #else 50 | # define FRACBITS 15 51 | # define SAMPPROD int32_t 52 | #define SAMP_MAX 32767 53 | #endif 54 | 55 | #define SAMP_MIN -SAMP_MAX 56 | 57 | #if defined(CHECK_OVERFLOW) 58 | # define CHECK_OVERFLOW_OP(a,op,b) \ 59 | if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ 60 | fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } 61 | #endif 62 | 63 | 64 | # define smul(a,b) ( (SAMPPROD)(a)*(b) ) 65 | # define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS ) 66 | 67 | # define S_MUL(a,b) sround( smul(a,b) ) 68 | 69 | # define C_MUL(m,a,b) \ 70 | do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ 71 | (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) 72 | 73 | # define DIVSCALAR(x,k) \ 74 | (x) = sround( smul( x, SAMP_MAX/k ) ) 75 | 76 | # define C_FIXDIV(c,div) \ 77 | do { DIVSCALAR( (c).r , div); \ 78 | DIVSCALAR( (c).i , div); }while (0) 79 | 80 | # define C_MULBYSCALAR( c, s ) \ 81 | do{ (c).r = sround( smul( (c).r , s ) ) ;\ 82 | (c).i = sround( smul( (c).i , s ) ) ; }while(0) 83 | 84 | #else /* not FIXED_POINT*/ 85 | 86 | # define S_MUL(a,b) ( (a)*(b) ) 87 | #define C_MUL(m,a,b) \ 88 | do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ 89 | (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) 90 | # define C_FIXDIV(c,div) /* NOOP */ 91 | # define C_MULBYSCALAR( c, s ) \ 92 | do{ (c).r *= (s);\ 93 | (c).i *= (s); }while(0) 94 | #endif 95 | 96 | #ifndef CHECK_OVERFLOW_OP 97 | # define CHECK_OVERFLOW_OP(a,op,b) /* noop */ 98 | #endif 99 | 100 | #define C_ADD( res, a,b)\ 101 | do { \ 102 | CHECK_OVERFLOW_OP((a).r,+,(b).r)\ 103 | CHECK_OVERFLOW_OP((a).i,+,(b).i)\ 104 | (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ 105 | }while(0) 106 | #define C_SUB( res, a,b)\ 107 | do { \ 108 | CHECK_OVERFLOW_OP((a).r,-,(b).r)\ 109 | CHECK_OVERFLOW_OP((a).i,-,(b).i)\ 110 | (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ 111 | }while(0) 112 | #define C_ADDTO( res , a)\ 113 | do { \ 114 | CHECK_OVERFLOW_OP((res).r,+,(a).r)\ 115 | CHECK_OVERFLOW_OP((res).i,+,(a).i)\ 116 | (res).r += (a).r; (res).i += (a).i;\ 117 | }while(0) 118 | 119 | #define C_SUBFROM( res , a)\ 120 | do {\ 121 | CHECK_OVERFLOW_OP((res).r,-,(a).r)\ 122 | CHECK_OVERFLOW_OP((res).i,-,(a).i)\ 123 | (res).r -= (a).r; (res).i -= (a).i; \ 124 | }while(0) 125 | 126 | 127 | #ifdef FIXED_POINT 128 | # define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase)) 129 | # define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase)) 130 | # define HALF_OF(x) ((x)>>1) 131 | #elif defined(USE_SIMD) 132 | # define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) 133 | # define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) 134 | # define HALF_OF(x) ((x)*_mm_set1_ps(.5)) 135 | #else 136 | # define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) 137 | # define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) 138 | # define HALF_OF(x) ((x)*.5) 139 | #endif 140 | 141 | #define kf_cexp(x,phase) \ 142 | do{ \ 143 | (x)->r = KISS_FFT_COS(phase);\ 144 | (x)->i = KISS_FFT_SIN(phase);\ 145 | }while(0) 146 | 147 | 148 | /* a debugging function */ 149 | #define pcpx(c)\ 150 | fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) 151 | 152 | 153 | #ifdef KISS_FFT_USE_ALLOCA 154 | // define this to allow use of alloca instead of malloc for temporary buffers 155 | // Temporary buffers are used in two case: 156 | // 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5 157 | // 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform. 158 | #include 159 | #define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes) 160 | #define KISS_FFT_TMP_FREE(ptr) 161 | #else 162 | #define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes) 163 | #define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr) 164 | #endif 165 | -------------------------------------------------------------------------------- /thirdparty/kiss_fft/kiss_fft.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2010, Mark Borgerding 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | 16 | #include "_kiss_fft_guts.h" 17 | /* The guts header contains all the multiplication and addition macros that are defined for 18 | fixed or floating point complex numbers. It also delares the kf_ internal functions. 19 | */ 20 | 21 | static void kf_bfly2( 22 | kiss_fft_cpx * Fout, 23 | const size_t fstride, 24 | const kiss_fft_cfg st, 25 | int m 26 | ) 27 | { 28 | kiss_fft_cpx * Fout2; 29 | kiss_fft_cpx * tw1 = st->twiddles; 30 | kiss_fft_cpx t; 31 | Fout2 = Fout + m; 32 | do{ 33 | C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); 34 | 35 | C_MUL (t, *Fout2 , *tw1); 36 | tw1 += fstride; 37 | C_SUB( *Fout2 , *Fout , t ); 38 | C_ADDTO( *Fout , t ); 39 | ++Fout2; 40 | ++Fout; 41 | }while (--m); 42 | } 43 | 44 | static void kf_bfly4( 45 | kiss_fft_cpx * Fout, 46 | const size_t fstride, 47 | const kiss_fft_cfg st, 48 | const size_t m 49 | ) 50 | { 51 | kiss_fft_cpx *tw1,*tw2,*tw3; 52 | kiss_fft_cpx scratch[6]; 53 | size_t k=m; 54 | const size_t m2=2*m; 55 | const size_t m3=3*m; 56 | 57 | 58 | tw3 = tw2 = tw1 = st->twiddles; 59 | 60 | do { 61 | C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); 62 | 63 | C_MUL(scratch[0],Fout[m] , *tw1 ); 64 | C_MUL(scratch[1],Fout[m2] , *tw2 ); 65 | C_MUL(scratch[2],Fout[m3] , *tw3 ); 66 | 67 | C_SUB( scratch[5] , *Fout, scratch[1] ); 68 | C_ADDTO(*Fout, scratch[1]); 69 | C_ADD( scratch[3] , scratch[0] , scratch[2] ); 70 | C_SUB( scratch[4] , scratch[0] , scratch[2] ); 71 | C_SUB( Fout[m2], *Fout, scratch[3] ); 72 | tw1 += fstride; 73 | tw2 += fstride*2; 74 | tw3 += fstride*3; 75 | C_ADDTO( *Fout , scratch[3] ); 76 | 77 | if(st->inverse) { 78 | Fout[m].r = scratch[5].r - scratch[4].i; 79 | Fout[m].i = scratch[5].i + scratch[4].r; 80 | Fout[m3].r = scratch[5].r + scratch[4].i; 81 | Fout[m3].i = scratch[5].i - scratch[4].r; 82 | }else{ 83 | Fout[m].r = scratch[5].r + scratch[4].i; 84 | Fout[m].i = scratch[5].i - scratch[4].r; 85 | Fout[m3].r = scratch[5].r - scratch[4].i; 86 | Fout[m3].i = scratch[5].i + scratch[4].r; 87 | } 88 | ++Fout; 89 | }while(--k); 90 | } 91 | 92 | static void kf_bfly3( 93 | kiss_fft_cpx * Fout, 94 | const size_t fstride, 95 | const kiss_fft_cfg st, 96 | size_t m 97 | ) 98 | { 99 | size_t k=m; 100 | const size_t m2 = 2*m; 101 | kiss_fft_cpx *tw1,*tw2; 102 | kiss_fft_cpx scratch[5]; 103 | kiss_fft_cpx epi3; 104 | epi3 = st->twiddles[fstride*m]; 105 | 106 | tw1=tw2=st->twiddles; 107 | 108 | do{ 109 | C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); 110 | 111 | C_MUL(scratch[1],Fout[m] , *tw1); 112 | C_MUL(scratch[2],Fout[m2] , *tw2); 113 | 114 | C_ADD(scratch[3],scratch[1],scratch[2]); 115 | C_SUB(scratch[0],scratch[1],scratch[2]); 116 | tw1 += fstride; 117 | tw2 += fstride*2; 118 | 119 | Fout[m].r = Fout->r - HALF_OF(scratch[3].r); 120 | Fout[m].i = Fout->i - HALF_OF(scratch[3].i); 121 | 122 | C_MULBYSCALAR( scratch[0] , epi3.i ); 123 | 124 | C_ADDTO(*Fout,scratch[3]); 125 | 126 | Fout[m2].r = Fout[m].r + scratch[0].i; 127 | Fout[m2].i = Fout[m].i - scratch[0].r; 128 | 129 | Fout[m].r -= scratch[0].i; 130 | Fout[m].i += scratch[0].r; 131 | 132 | ++Fout; 133 | }while(--k); 134 | } 135 | 136 | static void kf_bfly5( 137 | kiss_fft_cpx * Fout, 138 | const size_t fstride, 139 | const kiss_fft_cfg st, 140 | int m 141 | ) 142 | { 143 | kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; 144 | int u; 145 | kiss_fft_cpx scratch[13]; 146 | kiss_fft_cpx * twiddles = st->twiddles; 147 | kiss_fft_cpx *tw; 148 | kiss_fft_cpx ya,yb; 149 | ya = twiddles[fstride*m]; 150 | yb = twiddles[fstride*2*m]; 151 | 152 | Fout0=Fout; 153 | Fout1=Fout0+m; 154 | Fout2=Fout0+2*m; 155 | Fout3=Fout0+3*m; 156 | Fout4=Fout0+4*m; 157 | 158 | tw=st->twiddles; 159 | for ( u=0; ur += scratch[7].r + scratch[8].r; 174 | Fout0->i += scratch[7].i + scratch[8].i; 175 | 176 | scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); 177 | scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); 178 | 179 | scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); 180 | scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); 181 | 182 | C_SUB(*Fout1,scratch[5],scratch[6]); 183 | C_ADD(*Fout4,scratch[5],scratch[6]); 184 | 185 | scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); 186 | scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); 187 | scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); 188 | scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); 189 | 190 | C_ADD(*Fout2,scratch[11],scratch[12]); 191 | C_SUB(*Fout3,scratch[11],scratch[12]); 192 | 193 | ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; 194 | } 195 | } 196 | 197 | /* perform the butterfly for one stage of a mixed radix FFT */ 198 | static void kf_bfly_generic( 199 | kiss_fft_cpx * Fout, 200 | const size_t fstride, 201 | const kiss_fft_cfg st, 202 | int m, 203 | int p 204 | ) 205 | { 206 | int u,k,q1,q; 207 | kiss_fft_cpx * twiddles = st->twiddles; 208 | kiss_fft_cpx t; 209 | int Norig = st->nfft; 210 | 211 | kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); 212 | 213 | for ( u=0; u=Norig) twidx-=Norig; 228 | C_MUL(t,scratch[q] , twiddles[twidx] ); 229 | C_ADDTO( Fout[ k ] ,t); 230 | } 231 | k += m; 232 | } 233 | } 234 | KISS_FFT_TMP_FREE(scratch); 235 | } 236 | 237 | static 238 | void kf_work( 239 | kiss_fft_cpx * Fout, 240 | const kiss_fft_cpx * f, 241 | const size_t fstride, 242 | int in_stride, 243 | int * factors, 244 | const kiss_fft_cfg st 245 | ) 246 | { 247 | kiss_fft_cpx * Fout_beg=Fout; 248 | const int p=*factors++; /* the radix */ 249 | const int m=*factors++; /* stage's fft length/p */ 250 | const kiss_fft_cpx * Fout_end = Fout + p*m; 251 | 252 | #ifdef _OPENMP 253 | // use openmp extensions at the 254 | // top-level (not recursive) 255 | if (fstride==1 && p<=5) 256 | { 257 | int k; 258 | 259 | // execute the p different work units in different threads 260 | # pragma omp parallel for 261 | for (k=0;k floor_sqrt) 324 | p = n; /* no more factors, skip to end */ 325 | } 326 | n /= p; 327 | *facbuf++ = p; 328 | *facbuf++ = n; 329 | } while (n > 1); 330 | } 331 | 332 | /* 333 | * 334 | * User-callable function to allocate all necessary storage space for the fft. 335 | * 336 | * The return value is a contiguous block of memory, allocated with malloc. As such, 337 | * It can be freed with free(), rather than a kiss_fft-specific function. 338 | * */ 339 | kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) 340 | { 341 | kiss_fft_cfg st=NULL; 342 | size_t memneeded = sizeof(struct kiss_fft_state) 343 | + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ 344 | 345 | if ( lenmem==NULL ) { 346 | st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); 347 | }else{ 348 | if (mem != NULL && *lenmem >= memneeded) 349 | st = (kiss_fft_cfg)mem; 350 | *lenmem = memneeded; 351 | } 352 | if (st) { 353 | int i; 354 | st->nfft=nfft; 355 | st->inverse = inverse_fft; 356 | 357 | for (i=0;iinverse) 361 | phase *= -1; 362 | kf_cexp(st->twiddles+i, phase ); 363 | } 364 | 365 | kf_factor(nfft,st->factors); 366 | } 367 | return st; 368 | } 369 | 370 | 371 | void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) 372 | { 373 | if (fin == fout) { 374 | //NOTE: this is not really an in-place FFT algorithm. 375 | //It just performs an out-of-place FFT into a temp buffer 376 | kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft); 377 | kf_work(tmpbuf,fin,1,in_stride, st->factors,st); 378 | memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); 379 | KISS_FFT_TMP_FREE(tmpbuf); 380 | }else{ 381 | kf_work( fout, fin, 1,in_stride, st->factors,st ); 382 | } 383 | } 384 | 385 | void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) 386 | { 387 | kiss_fft_stride(cfg,fin,fout,1); 388 | } 389 | 390 | 391 | void kiss_fft_cleanup(void) 392 | { 393 | // nothing needed any more 394 | } 395 | 396 | int kiss_fft_next_fast_size(int n) 397 | { 398 | while(1) { 399 | int m=n; 400 | while ( (m%2) == 0 ) m/=2; 401 | while ( (m%3) == 0 ) m/=3; 402 | while ( (m%5) == 0 ) m/=5; 403 | if (m<=1) 404 | break; /* n is completely factorable by twos, threes, and fives */ 405 | n++; 406 | } 407 | return n; 408 | } 409 | -------------------------------------------------------------------------------- /thirdparty/kiss_fft/kiss_fft.h: -------------------------------------------------------------------------------- 1 | #ifndef KISS_FFT_H 2 | #define KISS_FFT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | /* 14 | ATTENTION! 15 | If you would like a : 16 | -- a utility that will handle the caching of fft objects 17 | -- real-only (no imaginary time component ) FFT 18 | -- a multi-dimensional FFT 19 | -- a command-line utility to perform ffts 20 | -- a command-line utility to perform fast-convolution filtering 21 | 22 | Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c 23 | in the tools/ directory. 24 | */ 25 | 26 | #ifdef USE_SIMD 27 | # include 28 | # define kiss_fft_scalar __m128 29 | #define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) 30 | #define KISS_FFT_FREE _mm_free 31 | #else 32 | #define KISS_FFT_MALLOC malloc 33 | #define KISS_FFT_FREE free 34 | #endif 35 | 36 | 37 | #ifdef FIXED_POINT 38 | #include 39 | # if (FIXED_POINT == 32) 40 | # define kiss_fft_scalar int32_t 41 | # else 42 | # define kiss_fft_scalar int16_t 43 | # endif 44 | #else 45 | # ifndef kiss_fft_scalar 46 | /* default is float */ 47 | # define kiss_fft_scalar float 48 | # endif 49 | #endif 50 | 51 | typedef struct { 52 | kiss_fft_scalar r; 53 | kiss_fft_scalar i; 54 | }kiss_fft_cpx; 55 | 56 | typedef struct kiss_fft_state* kiss_fft_cfg; 57 | 58 | /* 59 | * kiss_fft_alloc 60 | * 61 | * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. 62 | * 63 | * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); 64 | * 65 | * The return value from fft_alloc is a cfg buffer used internally 66 | * by the fft routine or NULL. 67 | * 68 | * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. 69 | * The returned value should be free()d when done to avoid memory leaks. 70 | * 71 | * The state can be placed in a user supplied buffer 'mem': 72 | * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, 73 | * then the function places the cfg in mem and the size used in *lenmem 74 | * and returns mem. 75 | * 76 | * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), 77 | * then the function returns NULL and places the minimum cfg 78 | * buffer size in *lenmem. 79 | * */ 80 | 81 | kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); 82 | 83 | /* 84 | * kiss_fft(cfg,in_out_buf) 85 | * 86 | * Perform an FFT on a complex input buffer. 87 | * for a forward FFT, 88 | * fin should be f[0] , f[1] , ... ,f[nfft-1] 89 | * fout will be F[0] , F[1] , ... ,F[nfft-1] 90 | * Note that each element is complex and can be accessed like 91 | f[k].r and f[k].i 92 | * */ 93 | void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); 94 | 95 | /* 96 | A more generic version of the above function. It reads its input from every Nth sample. 97 | * */ 98 | void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); 99 | 100 | /* If kiss_fft_alloc allocated a buffer, it is one contiguous 101 | buffer and can be simply free()d when no longer needed*/ 102 | #define kiss_fft_free free 103 | 104 | /* 105 | Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up 106 | your compiler output to call this before you exit. 107 | */ 108 | void kiss_fft_cleanup(void); 109 | 110 | 111 | /* 112 | * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) 113 | */ 114 | int kiss_fft_next_fast_size(int n); 115 | 116 | /* for real ffts, we need an even size */ 117 | #define kiss_fftr_next_fast_size_real(n) \ 118 | (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) 119 | 120 | #ifdef __cplusplus 121 | } 122 | #endif 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /thirdparty/kiss_fft/tools/kiss_fftr.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2004, Mark Borgerding 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | */ 14 | 15 | #include "kiss_fftr.h" 16 | #include "_kiss_fft_guts.h" 17 | 18 | struct kiss_fftr_state{ 19 | kiss_fft_cfg substate; 20 | kiss_fft_cpx * tmpbuf; 21 | kiss_fft_cpx * super_twiddles; 22 | #ifdef USE_SIMD 23 | void * pad; 24 | #endif 25 | }; 26 | 27 | kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) 28 | { 29 | int i; 30 | kiss_fftr_cfg st = NULL; 31 | size_t subsize, memneeded; 32 | 33 | if (nfft & 1) { 34 | fprintf(stderr,"Real FFT optimization must be even.\n"); 35 | return NULL; 36 | } 37 | nfft >>= 1; 38 | 39 | kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); 40 | memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2); 41 | 42 | if (lenmem == NULL) { 43 | st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); 44 | } else { 45 | if (*lenmem >= memneeded) 46 | st = (kiss_fftr_cfg) mem; 47 | *lenmem = memneeded; 48 | } 49 | if (!st) 50 | return NULL; 51 | 52 | st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ 53 | st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); 54 | st->super_twiddles = st->tmpbuf + nfft; 55 | kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); 56 | 57 | for (i = 0; i < nfft/2; ++i) { 58 | double phase = 59 | -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); 60 | if (inverse_fft) 61 | phase *= -1; 62 | kf_cexp (st->super_twiddles+i,phase); 63 | } 64 | return st; 65 | } 66 | 67 | void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) 68 | { 69 | /* input buffer timedata is stored row-wise */ 70 | int k,ncfft; 71 | kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; 72 | 73 | if ( st->substate->inverse) { 74 | fprintf(stderr,"kiss fft usage error: improper alloc\n"); 75 | exit(1); 76 | } 77 | 78 | ncfft = st->substate->nfft; 79 | 80 | /*perform the parallel fft of two real signals packed in real,imag*/ 81 | kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); 82 | /* The real part of the DC element of the frequency spectrum in st->tmpbuf 83 | * contains the sum of the even-numbered elements of the input time sequence 84 | * The imag part is the sum of the odd-numbered elements 85 | * 86 | * The sum of tdc.r and tdc.i is the sum of the input time sequence. 87 | * yielding DC of input time sequence 88 | * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... 89 | * yielding Nyquist bin of input time sequence 90 | */ 91 | 92 | tdc.r = st->tmpbuf[0].r; 93 | tdc.i = st->tmpbuf[0].i; 94 | C_FIXDIV(tdc,2); 95 | CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); 96 | CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); 97 | freqdata[0].r = tdc.r + tdc.i; 98 | freqdata[ncfft].r = tdc.r - tdc.i; 99 | #ifdef USE_SIMD 100 | freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); 101 | #else 102 | freqdata[ncfft].i = freqdata[0].i = 0; 103 | #endif 104 | 105 | for ( k=1;k <= ncfft/2 ; ++k ) { 106 | fpk = st->tmpbuf[k]; 107 | fpnk.r = st->tmpbuf[ncfft-k].r; 108 | fpnk.i = - st->tmpbuf[ncfft-k].i; 109 | C_FIXDIV(fpk,2); 110 | C_FIXDIV(fpnk,2); 111 | 112 | C_ADD( f1k, fpk , fpnk ); 113 | C_SUB( f2k, fpk , fpnk ); 114 | C_MUL( tw , f2k , st->super_twiddles[k-1]); 115 | 116 | freqdata[k].r = HALF_OF(f1k.r + tw.r); 117 | freqdata[k].i = HALF_OF(f1k.i + tw.i); 118 | freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); 119 | freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); 120 | } 121 | } 122 | 123 | void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) 124 | { 125 | /* input buffer timedata is stored row-wise */ 126 | int k, ncfft; 127 | 128 | if (st->substate->inverse == 0) { 129 | fprintf (stderr, "kiss fft usage error: improper alloc\n"); 130 | exit (1); 131 | } 132 | 133 | ncfft = st->substate->nfft; 134 | 135 | st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; 136 | st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; 137 | C_FIXDIV(st->tmpbuf[0],2); 138 | 139 | for (k = 1; k <= ncfft / 2; ++k) { 140 | kiss_fft_cpx fk, fnkc, fek, fok, tmp; 141 | fk = freqdata[k]; 142 | fnkc.r = freqdata[ncfft - k].r; 143 | fnkc.i = -freqdata[ncfft - k].i; 144 | C_FIXDIV( fk , 2 ); 145 | C_FIXDIV( fnkc , 2 ); 146 | 147 | C_ADD (fek, fk, fnkc); 148 | C_SUB (tmp, fk, fnkc); 149 | C_MUL (fok, tmp, st->super_twiddles[k-1]); 150 | C_ADD (st->tmpbuf[k], fek, fok); 151 | C_SUB (st->tmpbuf[ncfft - k], fek, fok); 152 | #ifdef USE_SIMD 153 | st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); 154 | #else 155 | st->tmpbuf[ncfft - k].i *= -1; 156 | #endif 157 | } 158 | kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); 159 | } 160 | -------------------------------------------------------------------------------- /thirdparty/kiss_fft/tools/kiss_fftr.h: -------------------------------------------------------------------------------- 1 | #ifndef KISS_FTR_H 2 | #define KISS_FTR_H 3 | 4 | #include "kiss_fft.h" 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | 10 | /* 11 | 12 | Real optimized version can save about 45% cpu time vs. complex fft of a real seq. 13 | 14 | 15 | 16 | */ 17 | 18 | typedef struct kiss_fftr_state *kiss_fftr_cfg; 19 | 20 | 21 | kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); 22 | /* 23 | nfft must be even 24 | 25 | If you don't care to allocate space, use mem = lenmem = NULL 26 | */ 27 | 28 | 29 | void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); 30 | /* 31 | input timedata has nfft scalar points 32 | output freqdata has nfft/2+1 complex points 33 | */ 34 | 35 | void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); 36 | /* 37 | input freqdata has nfft/2+1 complex points 38 | output timedata has nfft scalar points 39 | */ 40 | 41 | #define kiss_fftr_free free 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | #endif 47 | --------------------------------------------------------------------------------