├── .gitmodules ├── lib ├── kissfft │ ├── library.json │ ├── CMakeLists.txt │ ├── COPYING │ ├── .travis.yml │ ├── tools │ │ ├── kiss_fftnd.h │ │ ├── kiss_fftr.h │ │ ├── kiss_fftndr.h │ │ ├── kfc.h │ │ ├── Makefile │ │ ├── kfc.c │ │ ├── kiss_fftndr.c │ │ ├── kiss_fftr.c │ │ ├── fftutil.c │ │ ├── kiss_fftnd.c │ │ └── kiss_fastfir.c │ ├── .gitignore │ ├── kiss_fft_log.h │ ├── LICENSES │ │ ├── Unlicense │ │ └── BSD-3-Clause │ ├── Makefile │ ├── TIPS │ ├── README.simd │ ├── kiss_fft.h │ ├── _kiss_fft_guts.h │ ├── CHANGELOG │ ├── README │ ├── README.md │ ├── kissfft_i32.hh │ ├── kiss_fft.c │ └── kissfft.hh └── README ├── .github └── FUNDING.yml ├── .gitignore ├── src ├── UI │ ├── Bitmap.h │ ├── Waveform.h │ ├── Palette.h │ ├── Spectrogram.h │ ├── GraphicEqualiser.h │ ├── Component.h │ ├── UI.h │ ├── Bitmap.cpp │ ├── Spectrogram.cpp │ ├── Palette.cpp │ ├── Waveform.cpp │ ├── UI.cpp │ └── GraphicEqualiser.cpp ├── AudioProcessing │ ├── HammingWindow.h │ ├── Processor.h │ ├── HammingWindow.cpp │ └── Processor.cpp ├── Application.h ├── main.cpp ├── I2S │ ├── I2SSampler.h │ └── I2SSampler.cpp └── Application.cpp ├── .vscode └── extensions.json ├── test └── README ├── platformio.ini ├── README.md ├── include └── README ├── LICENSE └── .clang-format /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/kissfft/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "flags": "-Ofast" 4 | } 5 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [atomic14] 4 | ko_fi: atomic14 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /src/UI/Bitmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Bitmap { 5 | public: 6 | uint16_t *pixels; 7 | uint16_t **rows; 8 | uint16_t width; 9 | uint16_t height; 10 | Bitmap(int width, int height); 11 | void scroll_left(); 12 | }; 13 | -------------------------------------------------------------------------------- /lib/kissfft/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(kissfft) 3 | 4 | add_library(kissfft 5 | kiss_fft.c) 6 | 7 | target_include_directories(kissfft PUBLIC 8 | $ 9 | $) 10 | -------------------------------------------------------------------------------- /src/AudioProcessing/HammingWindow.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class HammingWindow { 4 | private: 5 | float *m_coefficients; 6 | int m_window_size; 7 | 8 | public: 9 | HammingWindow(int window_size); 10 | ~HammingWindow(); 11 | void applyWindow(float *input); 12 | }; 13 | -------------------------------------------------------------------------------- /lib/kissfft/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2010 Mark Borgerding . All rights reserved. 2 | 3 | KISS FFT is provided under: 4 | 5 | SPDX-License-Identifier: BSD-3-Clause 6 | 7 | Being under the terms of the BSD 3-clause "New" or "Revised" License, 8 | according with: 9 | 10 | LICENSES/BSD-3-Clause 11 | 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/UI/Waveform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Component.h" 4 | 5 | class Waveform : public Component { 6 | private: 7 | float *m_samples; 8 | int m_num_samples; 9 | 10 | public: 11 | Waveform(M5Display &display, int x, int y, int width, int height, int num_samples); 12 | void update(const float *samples); 13 | void _draw(M5Display &display); 14 | }; 15 | -------------------------------------------------------------------------------- /src/UI/Palette.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | 9 | #undef min 10 | 11 | class Palette { 12 | protected: 13 | uint16_t colors[256]; 14 | 15 | public: 16 | Palette(); 17 | inline uint16_t get_color(int index) 18 | { 19 | return colors[std::max(0, std::min(255, index))]; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/UI/Spectrogram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Component.h" 4 | 5 | class Bitmap; 6 | class Palette; 7 | 8 | class Spectrogram : public Component { 9 | private: 10 | Palette *m_palette; 11 | 12 | Bitmap *bitmap; 13 | 14 | public: 15 | Spectrogram(Palette *palette, int x, int y, int width, int height); 16 | void update(float *magnitudes); 17 | void _draw(M5Display &display); 18 | }; 19 | -------------------------------------------------------------------------------- /src/UI/GraphicEqualiser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Component.h" 4 | 5 | class Palette; 6 | class GraphicEqualiser : public Component { 7 | private: 8 | Palette *m_palette; 9 | int m_num_bins; 10 | float *bar_chart; 11 | float *bar_chart_peaks; 12 | 13 | public: 14 | GraphicEqualiser(Palette *palette, int x, int y, int width, int height, int num_bins); 15 | void update(float *mag); 16 | void _draw(M5Display &display); 17 | }; 18 | -------------------------------------------------------------------------------- /src/UI/Component.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class M5Display; 4 | class Component { 5 | public: 6 | int x, y, width, height; 7 | bool visible; 8 | Component(int x, int y, int width, int height) 9 | : x(x), y(y), width(width), height(height), visible(true) 10 | { 11 | } 12 | virtual void _draw(M5Display &display) = 0; 13 | void draw(M5Display &display) 14 | { 15 | if (visible) { 16 | _draw(display); 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/AudioProcessing/Processor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tools/kiss_fftr.h" 4 | #include 5 | 6 | 7 | class HammingWindow; 8 | 9 | class Processor { 10 | private: 11 | HammingWindow *m_hamming_window; 12 | int m_fft_size; 13 | int m_window_size; 14 | kiss_fftr_cfg m_cfg; 15 | kiss_fft_cpx *m_fft_output; 16 | 17 | public: 18 | float *m_energy; 19 | float *m_fft_input; 20 | 21 | Processor(int window_size); 22 | void update(int16_t *samples); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/kissfft/.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "3.7" 5 | 6 | dist: bionic 7 | 8 | before_install: 9 | - sudo apt-get install -y libfftw3-dev 10 | 11 | addons: 12 | apt: 13 | update: true 14 | 15 | install: true 16 | 17 | jobs: 18 | include: 19 | - name: "build (make)" 20 | script: 21 | - make all 22 | - make testall 23 | - name: "build (cmake)" 24 | script: 25 | - mkdir build && cd build 26 | - cmake .. 27 | - make 28 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | -------------------------------------------------------------------------------- /src/Application.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class UI; 6 | class Processor; 7 | class I2SSampler; 8 | class M5Display; 9 | class M5Touch; 10 | 11 | class Application { 12 | private: 13 | int m_window_size; 14 | UI *m_ui; 15 | Processor *m_processor; 16 | I2SSampler *m_sampler; 17 | M5Touch &m_touch; 18 | 19 | void process_samples(); 20 | 21 | public: 22 | Application(M5Display &display, M5Touch &touch, int window_size); 23 | void begin(const i2s_config_t &i2s_config, const i2s_pin_config_t &i2s_pins); 24 | void loop(); 25 | 26 | friend void processing_task(void *param); 27 | }; 28 | -------------------------------------------------------------------------------- /src/UI/UI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Palette; 6 | class Waveform; 7 | class GraphicEqualiser; 8 | class Spectrogram; 9 | class M5Display; 10 | 11 | class UI { 12 | private: 13 | Palette *m_palette; 14 | Waveform *m_waveform; 15 | GraphicEqualiser *m_graphic_equaliser; 16 | Spectrogram *m_spectrogram; 17 | M5Display &m_display; 18 | TaskHandle_t m_draw_task_handle; 19 | 20 | public: 21 | UI(M5Display &display, int window_size); 22 | void toggle_display(); 23 | void update(float *samples, float *fft); 24 | void draw(); 25 | friend void drawing_task(void *param); 26 | }; 27 | -------------------------------------------------------------------------------- /src/UI/Bitmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Bitmap.h" 4 | 5 | Bitmap::Bitmap(int width, int height) 6 | { 7 | this->width = width; 8 | this->height = height; 9 | pixels = (uint16_t *)ps_malloc(width * height * sizeof(uint16_t)); 10 | rows = (uint16_t **)malloc(height * sizeof(uint16_t **)); 11 | for (int i = 0; i < height; i++) { 12 | rows[i] = pixels + width * i; 13 | } 14 | for (int y = 0; y < height; y++) { 15 | for (int x = 0; x < width; x++) { 16 | rows[y][x] = 0; 17 | } 18 | } 19 | } 20 | 21 | void Bitmap::scroll_left() 22 | { 23 | for (int y = 0; y < height; y++) { 24 | memmove(rows[y], rows[y] + 1, this->width * sizeof(uint16_t) - 1); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kiss_fftnd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #ifndef KISS_FFTND_H 10 | #define KISS_FFTND_H 11 | 12 | #include "kiss_fft.h" 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | typedef struct kiss_fftnd_state * kiss_fftnd_cfg; 19 | 20 | kiss_fftnd_cfg kiss_fftnd_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem); 21 | void kiss_fftnd(kiss_fftnd_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | #endif 27 | -------------------------------------------------------------------------------- /src/UI/Spectrogram.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Bitmap.h" 5 | #include "Palette.h" 6 | #include "Spectrogram.h" 7 | 8 | Spectrogram::Spectrogram(Palette *palette, int x, int y, int width, int height) 9 | : Component(x, y, width, height) 10 | { 11 | m_palette = palette; 12 | this->bitmap = new Bitmap(width, height); 13 | } 14 | 15 | void Spectrogram::update(float *mag) 16 | { 17 | bitmap->scroll_left(); 18 | for (int i = 0; i < bitmap->height; i++) { 19 | bitmap->rows[bitmap->height - i - 1][bitmap->width - 1] = m_palette->get_color(mag[i]); 20 | } 21 | } 22 | 23 | void Spectrogram::_draw(M5Display &display) 24 | { 25 | display.drawBitmap(x, y, bitmap->width, bitmap->height, bitmap->pixels); 26 | } 27 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:m5stack-core2] 12 | platform = espressif32 13 | board = m5stack-core2 14 | framework = arduino 15 | monitor_port = COM12 16 | monitor_speed = 115200 17 | upload_speed = 115200 18 | upload_port = COM12 19 | monitor_filters = esp32_exception_decoder 20 | build_flags = -DBOARD_HAS_PSRAM -Ofast 21 | lib_deps = 22 | 126@3.4.0 ; FastLED 23 | M5Core2 24 | -------------------------------------------------------------------------------- /src/AudioProcessing/HammingWindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "HammingWindow.h" 5 | 6 | HammingWindow::HammingWindow(int window_size) 7 | { 8 | m_window_size = window_size; 9 | m_coefficients = static_cast(malloc(sizeof(float) * m_window_size)); 10 | // create the constants for a hamming window 11 | const float arg = M_PI * 2.0 / window_size; 12 | for (int i = 0; i < window_size; i++) { 13 | float float_value = 0.5 - (0.5 * cos(arg * (i + 0.5))); 14 | // Scale it to fixed point and round it. 15 | m_coefficients[i] = float_value; 16 | } 17 | } 18 | 19 | HammingWindow::~HammingWindow() 20 | { 21 | free(m_coefficients); 22 | } 23 | 24 | void HammingWindow::applyWindow(float *input) 25 | { 26 | for (int i = 0; i < m_window_size; i++) { 27 | input[i] = input[i] * m_coefficients[i]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/UI/Palette.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Palette.h" 4 | 5 | uint16_t rgb_to_uint16(float r, float g, float b) 6 | { 7 | int ri = (int)(r * 31.0f); 8 | int gi = (int)(g * 63.0f); 9 | int bi = (int)(b * 31.0f); 10 | // Serial.printf("%d %d %d\n", ri, gi, bi); 11 | return (ri << 11) | (gi << 5) | bi; 12 | } 13 | 14 | DEFINE_GRADIENT_PALETTE(blackToRed){ 15 | 0, 0, 0, 50, // dark blue 16 | 128, 0, 255, 0, // green 17 | 164, 255, 220, 0, // bright yellow 18 | 192, 255, 128, 0, // bright orange 19 | 255, 255, 0, 0 // full red 20 | }; 21 | CRGBPalette256 blackToRedPal = blackToRed; 22 | 23 | Palette::Palette() 24 | { 25 | for (int i = 0; i < 256; ++i) { 26 | CRGB color = ColorFromPalette(blackToRedPal, i, 255, LINEARBLEND); 27 | colors[i] = rgb_to_uint16(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # M5Stack Core 2 Audio 2 | 3 | You can watch a video explainer [here (YouTube)](https://www.youtube.com/watch?v=CwIWpBqa-nM) which goes into a bit more detail on the audio capabilities of the device. 4 | 5 | [![Demo Video](https://img.youtube.com/vi/CwIWpBqa-nM/0.jpg)](https://www.youtube.com/watch?v=CwIWpBqa-nM) 6 | 7 | This project is a nice little demo of audio on the M5Stack Core 2 with some simple visualisations. 8 | 9 | You'll need to use PlatformIO to build the project. 10 | 11 | To clone the repo use: 12 | 13 | ``` 14 | git clone https://github.com/atomic14/m5stack-core2-audio-monitor.git 15 | ``` 16 | 17 | Hopefully, the code should be easy to understand. 18 | 19 | If you want to add some more visualisations then please open up a pull request and contribute some code. 20 | 21 | And if you'd like to buy me a coffee... 22 | 23 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Z8Z734F5Y) 24 | -------------------------------------------------------------------------------- /lib/kissfft/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | *.so 4 | *.a 5 | *.dylib 6 | test/testcpp 7 | test/bm_fftw_double 8 | test/bm_fftw_float 9 | test/bm_fftw_int16_t 10 | test/bm_fftw_int32_t 11 | test/bm_fftw_simd 12 | test/bm_kiss_double 13 | test/bm_kiss_float 14 | test/bm_kiss_int16_t 15 | test/bm_kiss_int32_t 16 | test/bm_kiss_simd 17 | test/st_double 18 | test/st_float 19 | test/st_int16_t 20 | test/st_int32_t 21 | test/st_simd 22 | test/tkfc_double 23 | test/tkfc_float 24 | test/tkfc_int16_t 25 | test/tkfc_int32_t 26 | test/tkfc_simd 27 | test/tr_double 28 | test/tr_float 29 | test/tr_int16_t 30 | test/tr_int32_t 31 | test/tr_simd 32 | tools/fastconv_double 33 | tools/fastconv_float 34 | tools/fastconv_int16_t 35 | tools/fastconv_int32_t 36 | tools/fastconv_simd 37 | tools/fastconvr_double 38 | tools/fastconvr_float 39 | tools/fastconvr_int16_t 40 | tools/fastconvr_int32_t 41 | tools/fastconvr_simd 42 | tools/fft_double 43 | tools/fft_float 44 | tools/fft_int16_t 45 | tools/fft_int32_t 46 | tools/fft_simd 47 | test/test_simd 48 | build 49 | -------------------------------------------------------------------------------- /lib/kissfft/kiss_fft_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #ifndef kiss_fft_log_h 10 | #define kiss_fft_log_h 11 | 12 | #define ERROR 1 13 | #define WARNING 2 14 | #define INFO 3 15 | #define DEBUG 4 16 | 17 | #define STRINGIFY(x) #x 18 | #define TOSTRING(x) STRINGIFY(x) 19 | 20 | #if defined(NDEBUG) 21 | # define KISS_FFT_LOG_MSG(severity, ...) ((void)0) 22 | #else 23 | # define KISS_FFT_LOG_MSG(severity, ...) \ 24 | fprintf(stderr, "[" #severity "] " __FILE__ ":" TOSTRING(__LINE__) " "); \ 25 | fprintf(stderr, __VA_ARGS__); \ 26 | fprintf(stderr, "\n") 27 | #endif 28 | 29 | #define KISS_FFT_ERROR(...) KISS_FFT_LOG_MSG(ERROR, __VA_ARGS__) 30 | #define KISS_FFT_WARNING(...) KISS_FFT_LOG_MSG(WARNING, __VA_ARGS__) 31 | #define KISS_FFT_INFO(...) KISS_FFT_LOG_MSG(INFO, __VA_ARGS__) 32 | #define KISS_FFT_DEBUG(...) KISS_FFT_LOG_MSG(DEBUG, __VA_ARGS__) 33 | 34 | 35 | 36 | #endif /* kiss_fft_log_h */ -------------------------------------------------------------------------------- /src/UI/Waveform.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Waveform.h" 5 | 6 | Waveform::Waveform(M5Display &display, int x, int y, int width, int height, int num_samples) 7 | : Component(x, y, width, height) 8 | { 9 | m_num_samples = num_samples; 10 | m_samples = static_cast(ps_malloc(sizeof(float) * num_samples)); 11 | } 12 | 13 | void Waveform::update(const float *samples) 14 | { 15 | memcpy(m_samples, samples, sizeof(float) * m_num_samples); 16 | } 17 | 18 | void Waveform::_draw(M5Display &display) 19 | { 20 | const float x_step = (float)width / (float)m_num_samples; 21 | const float y_offset = 60; 22 | 23 | display.fillRect(x, y, width, height, 0); 24 | // display.drawLine(x, y_offset, x + width, y_offset, 0xf000); 25 | 26 | float sample_x = 0; 27 | for (int i = 4; i < m_num_samples; i += 4) { 28 | display.drawLine(sample_x, 29 | y_offset + m_samples[i - 4] * 3, 30 | sample_x + x_step * 4, 31 | y_offset + m_samples[i] * 3, 32 | 0xfff); 33 | sample_x += x_step * 4; 34 | } 35 | // m_sprite->pushSprite(0, 0); 36 | } 37 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kiss_fftr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #ifndef KISS_FTR_H 10 | #define KISS_FTR_H 11 | 12 | #include "kiss_fft.h" 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | 18 | /* 19 | 20 | Real optimized version can save about 45% cpu time vs. complex fft of a real seq. 21 | 22 | 23 | 24 | */ 25 | 26 | typedef struct kiss_fftr_state *kiss_fftr_cfg; 27 | 28 | 29 | kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); 30 | /* 31 | nfft must be even 32 | 33 | If you don't care to allocate space, use mem = lenmem = NULL 34 | */ 35 | 36 | 37 | void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); 38 | /* 39 | input timedata has nfft scalar points 40 | output freqdata has nfft/2+1 complex points 41 | */ 42 | 43 | void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); 44 | /* 45 | input freqdata has nfft/2+1 complex points 46 | output timedata has nfft scalar points 47 | */ 48 | 49 | #define kiss_fftr_free KISS_FFT_FREE 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | #endif 55 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kiss_fftndr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #ifndef KISS_NDR_H 10 | #define KISS_NDR_H 11 | 12 | #include "kiss_fft.h" 13 | #include "kiss_fftr.h" 14 | #include "kiss_fftnd.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct kiss_fftndr_state *kiss_fftndr_cfg; 21 | 22 | 23 | kiss_fftndr_cfg kiss_fftndr_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem); 24 | /* 25 | dims[0] must be even 26 | 27 | If you don't care to allocate space, use mem = lenmem = NULL 28 | */ 29 | 30 | 31 | void kiss_fftndr( 32 | kiss_fftndr_cfg cfg, 33 | const kiss_fft_scalar *timedata, 34 | kiss_fft_cpx *freqdata); 35 | /* 36 | input timedata has dims[0] X dims[1] X ... X dims[ndims-1] scalar points 37 | output freqdata has dims[0] X dims[1] X ... X dims[ndims-1]/2+1 complex points 38 | */ 39 | 40 | void kiss_fftndri( 41 | kiss_fftndr_cfg cfg, 42 | const kiss_fft_cpx *freqdata, 43 | kiss_fft_scalar *timedata); 44 | /* 45 | input and output dimensions are the exact opposite of kiss_fftndr 46 | */ 47 | 48 | 49 | #define kiss_fftndr_free free 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Application.h" 6 | 7 | // approx 30ms of audio @ 16KHz 8 | #define WINDOW_SIZE 512 9 | 10 | // i2s config for reading from both m5stack mic 11 | i2s_config_t i2s_config = { 12 | .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM), 13 | .sample_rate = 64000, 14 | .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, 15 | .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, 16 | .communication_format = I2S_COMM_FORMAT_STAND_I2S, 17 | .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, 18 | .dma_buf_count = 2, 19 | .dma_buf_len = 1024, 20 | }; 21 | 22 | // i2s pins 23 | i2s_pin_config_t i2s_pins = {.bck_io_num = GPIO_NUM_12, 24 | .ws_io_num = GPIO_NUM_0, 25 | .data_out_num = I2S_PIN_NO_CHANGE, 26 | .data_in_num = GPIO_NUM_34}; 27 | 28 | Application *application; 29 | 30 | void setup() 31 | { 32 | Serial.begin(115200); 33 | M5.begin(true, // LCDEnable 34 | false, // SDEnable 35 | false, // SerialEnable !important 36 | false // I2CEnable 37 | ); 38 | M5.Axp.SetLcdVoltage(3000); 39 | M5.Lcd.fillScreen(TFT_BLACK); 40 | // M5Core grabs I2S_NUM_0 so let's get it back 41 | i2s_driver_uninstall(I2S_NUM_0); 42 | 43 | // create our application 44 | application = new Application(M5.Lcd, M5.Touch, WINDOW_SIZE); 45 | application->begin(i2s_config, i2s_pins); 46 | } 47 | 48 | void loop() 49 | { 50 | // service the application 51 | application->loop(); 52 | } 53 | -------------------------------------------------------------------------------- /lib/kissfft/LICENSES/Unlicense: -------------------------------------------------------------------------------- 1 | Valid-License-Identifier: Unlicense 2 | SPDX-URL: https://spdx.org/licenses/Unlicense.html 3 | Usage-Guide: 4 | To use the Unlicense put the following SPDX tag/value pair into a 5 | comment according to the placement guidelines in the licensing rules 6 | documentation: 7 | SPDX-License-Identifier: Unlicense 8 | License-Text: 9 | 10 | This is free and unencumbered software released into the public domain. 11 | 12 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute 13 | this software, either in source code form or as a compiled binary, for any 14 | purpose, commercial or non-commercial, and by any means. 15 | 16 | In jurisdictions that recognize copyright laws, the author or authors of this 17 | software dedicate any and all copyright interest in the software to the public 18 | domain. We make this dedication for the benefit of the public at large and 19 | to the detriment of our heirs and successors. We intend this dedication to be 20 | an overt act of relinquishment in perpetuity of all present and future rights 21 | to this software under copyright law. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 25 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 26 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 28 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | For more information, please refer to 31 | -------------------------------------------------------------------------------- /src/I2S/I2SSampler.h: -------------------------------------------------------------------------------- 1 | #ifndef __sampler_base_h__ 2 | #define __sampler_base_h__ 3 | 4 | #include "driver/i2s.h" 5 | #include 6 | 7 | 8 | /** 9 | * Base Class for both the ADC and I2S sampler 10 | **/ 11 | class I2SSampler { 12 | private: 13 | // double buffered audio - the processing task can be running against one buffer while we fill 14 | // the other buffer 15 | int16_t *m_audioBuffer1; 16 | int16_t *m_audioBuffer2; 17 | // current position in the audio buffer 18 | int32_t m_audioBufferPos = 0; 19 | // current audio buffer 20 | int16_t *m_currentAudioBuffer; 21 | // buffer containing samples that have been captured already 22 | int16_t *m_capturedAudioBuffer; 23 | // size of the audio buffers in bytes 24 | int32_t m_bufferSizeInBytes; 25 | // size of the audio buffer in samples 26 | int32_t m_bufferSizeInSamples; 27 | // I2S reader task 28 | TaskHandle_t m_readerTaskHandle; 29 | // processor task 30 | TaskHandle_t m_processorTaskHandle; 31 | // i2s reader queue 32 | QueueHandle_t m_i2sQueue; 33 | // i2s port 34 | i2s_port_t m_i2sPort; 35 | // i2s pins 36 | i2s_pin_config_t m_i2sPins; 37 | 38 | protected: 39 | void addSample(int16_t sample); 40 | 41 | public: 42 | int16_t getBufferSizeInBytes() 43 | { 44 | return m_bufferSizeInBytes; 45 | }; 46 | int16_t *getCapturedAudioBuffer() 47 | { 48 | return m_capturedAudioBuffer; 49 | } 50 | void start(i2s_port_t i2sPort, 51 | const i2s_pin_config_t &i2sPins, 52 | const i2s_config_t &i2sConfig, 53 | int32_t bufferSizeInSamples, 54 | TaskHandle_t processorTaskHandle); 55 | 56 | friend void i2sReaderTask(void *param); 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kfc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #ifndef KFC_H 10 | #define KFC_H 11 | #include "kiss_fft.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /* 18 | KFC -- Kiss FFT Cache 19 | 20 | Not needing to deal with kiss_fft_alloc and a config 21 | object may be handy for a lot of programs. 22 | 23 | KFC uses the underlying KISS FFT functions, but caches the config object. 24 | The first time kfc_fft or kfc_ifft for a given FFT size, the cfg 25 | object is created for it. All subsequent calls use the cached 26 | configuration object. 27 | 28 | NOTE: 29 | You should probably not use this if your program will be using a lot 30 | of various sizes of FFTs. There is a linear search through the 31 | cached objects. If you are only using one or two FFT sizes, this 32 | will be negligible. Otherwise, you may want to use another method 33 | of managing the cfg objects. 34 | 35 | There is no automated cleanup of the cached objects. This could lead 36 | to large memory usage in a program that uses a lot of *DIFFERENT* 37 | sized FFTs. If you want to force all cached cfg objects to be freed, 38 | call kfc_cleanup. 39 | 40 | */ 41 | 42 | /*forward complex FFT */ 43 | void kfc_fft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout); 44 | /*reverse complex FFT */ 45 | void kfc_ifft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout); 46 | 47 | /*free all cached objects*/ 48 | void kfc_cleanup(void); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/Application.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Application.h" 4 | #include "AudioProcessing/Processor.h" 5 | #include "I2S/I2SSampler.h" 6 | #include "UI/UI.h" 7 | 8 | // Task to process samples 9 | void processing_task(void *param) 10 | { 11 | Application *application = (Application *)param; 12 | const TickType_t xMaxBlockTime = pdMS_TO_TICKS(100); 13 | while (true) { 14 | // wait for some samples to process 15 | uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, xMaxBlockTime); 16 | if (ulNotificationValue > 0) { 17 | application->process_samples(); 18 | } 19 | } 20 | } 21 | 22 | Application::Application(M5Display &display, M5Touch &touch, int window_size) : m_touch(touch) 23 | { 24 | m_window_size = window_size; 25 | m_ui = new UI(display, window_size); 26 | m_processor = new Processor(window_size); 27 | m_sampler = new I2SSampler(); 28 | } 29 | 30 | void Application::begin(const i2s_config_t &i2s_config, const i2s_pin_config_t &i2s_pins) 31 | { 32 | // set up the processing 33 | TaskHandle_t processing_task_handle; 34 | xTaskCreatePinnedToCore( 35 | processing_task, "Processing Task", 4096, this, 2, &processing_task_handle, 0); 36 | 37 | // start sampling from i2s device 38 | m_sampler->start(I2S_NUM_0, i2s_pins, i2s_config, m_window_size, processing_task_handle); 39 | } 40 | 41 | void Application::process_samples() 42 | { 43 | // grab the samples 44 | int16_t *samples = m_sampler->getCapturedAudioBuffer(); 45 | m_processor->update(samples); 46 | m_ui->update(m_processor->m_fft_input, m_processor->m_energy); 47 | } 48 | 49 | void Application::loop() 50 | { 51 | if (m_touch.ispressed()) { 52 | m_ui->toggle_display(); 53 | // delay to allow for the touch to finish 54 | vTaskDelay(pdMS_TO_TICKS(500)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/kissfft/tools/Makefile: -------------------------------------------------------------------------------- 1 | WARNINGS=-W -Wall -Wstrict-prototypes -Wmissing-prototypes -Waggregate-return \ 2 | -Wcast-align -Wcast-qual -Wnested-externs -Wshadow -Wbad-function-cast \ 3 | -Wwrite-strings 4 | 5 | ifeq "$(DATATYPE)" "" 6 | DATATYPE=float 7 | endif 8 | 9 | ifeq "$(DATATYPE)" "int32_t" 10 | TYPEFLAGS=-DFIXED_POINT=32 11 | endif 12 | 13 | ifeq "$(DATATYPE)" "int16_t" 14 | TYPEFLAGS=-DFIXED_POINT=16 15 | endif 16 | 17 | ifeq "$(DATATYPE)" "simd" 18 | TYPEFLAGS=-DUSE_SIMD=1 -msse 19 | endif 20 | 21 | ifeq "$(TYPEFLAGS)" "" 22 | TYPEFLAGS=-Dkiss_fft_scalar=$(DATATYPE) 23 | endif 24 | 25 | ifneq ("$(KISS_FFT_USE_ALLOCA)","") 26 | CFLAGS+= -DKISS_FFT_USE_ALLOCA=1 27 | endif 28 | CFLAGS+= $(CFLAGADD) 29 | 30 | 31 | FFTUTIL=fft_$(DATATYPE) 32 | FASTFILT=fastconv_$(DATATYPE) 33 | FASTFILTREAL=fastconvr_$(DATATYPE) 34 | PSDPNG=psdpng_$(DATATYPE) 35 | DUMPHDR=dumphdr_$(DATATYPE) 36 | 37 | all: $(FFTUTIL) $(FASTFILT) $(FASTFILTREAL) 38 | # $(PSDPNG) 39 | # $(DUMPHDR) 40 | 41 | #CFLAGS=-Wall -O3 -pedantic -march=pentiumpro -ffast-math -fomit-frame-pointer $(WARNINGS) 42 | # If the above flags do not work, try the following 43 | CFLAGS=-Wall -O3 $(WARNINGS) 44 | # tip: try -openmp or -fopenmp to use multiple cores 45 | 46 | $(FASTFILTREAL): ../kiss_fft.c kiss_fastfir.c kiss_fftr.c 47 | $(CC) -o $@ $(CFLAGS) -I.. $(TYPEFLAGS) -DREAL_FASTFIR $+ -DFAST_FILT_UTIL -lm 48 | 49 | $(FASTFILT): ../kiss_fft.c kiss_fastfir.c 50 | $(CC) -o $@ $(CFLAGS) -I.. $(TYPEFLAGS) $+ -DFAST_FILT_UTIL -lm 51 | 52 | $(FFTUTIL): ../kiss_fft.c fftutil.c kiss_fftnd.c kiss_fftr.c kiss_fftndr.c 53 | $(CC) -o $@ $(CFLAGS) -I.. $(TYPEFLAGS) $+ -lm 54 | 55 | $(PSDPNG): ../kiss_fft.c psdpng.c kiss_fftr.c 56 | $(CC) -o $@ $(CFLAGS) -I.. $(TYPEFLAGS) $+ -lpng -lm 57 | 58 | $(DUMPHDR): ../kiss_fft.c dumphdr.c 59 | $(CC) -o $@ $(CFLAGS) -I.. $(TYPEFLAGS) $+ -lm 60 | 61 | clean: 62 | rm -f *~ fft fft_* fastconv fastconv_* fastconvr fastconvr_* psdpng psdpng_* 63 | -------------------------------------------------------------------------------- /lib/kissfft/LICENSES/BSD-3-Clause: -------------------------------------------------------------------------------- 1 | Valid-License-Identifier: BSD-3-Clause 2 | SPDX-URL: https://spdx.org/licenses/BSD-3-Clause.html 3 | Usage-Guide: 4 | To use the BSD 3-clause "New" or "Revised" License put the following SPDX 5 | tag/value pair into a comment according to the placement guidelines in 6 | the licensing rules documentation: 7 | SPDX-License-Identifier: BSD-3-Clause 8 | License-Text: 9 | 10 | Copyright (c) . All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without modification, 13 | are permitted provided that the following conditions are met: 14 | 15 | 1. Redistributions of source code must retain the above copyright notice, 16 | this list of conditions and the following disclaimer. 17 | 18 | 2. Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | 22 | 3. Neither the name of the copyright holder nor the names of its contributors 23 | may be used to endorse or promote products derived from this software without 24 | specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 27 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 30 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 34 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 35 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | -------------------------------------------------------------------------------- /src/UI/UI.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "UI.h" 5 | #include "UI/GraphicEqualiser.h" 6 | #include "UI/Palette.h" 7 | #include "UI/Spectrogram.h" 8 | #include "UI/Waveform.h" 9 | 10 | // Task to process samples 11 | void drawing_task(void *param) 12 | { 13 | UI *ui = reinterpret_cast(param); 14 | const TickType_t xMaxBlockTime = pdMS_TO_TICKS(1000); 15 | while (true) { 16 | // wait to be told to redraw 17 | uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, xMaxBlockTime); 18 | if (ulNotificationValue != 0) { 19 | ui->draw(); 20 | } 21 | } 22 | } 23 | 24 | UI::UI(M5Display &display, int window_size) : m_display(display) 25 | { 26 | m_palette = new Palette(); 27 | m_waveform = new Waveform(display, 0, 0, 320, 120, window_size); 28 | m_graphic_equaliser = new GraphicEqualiser(m_palette, 0, 120, 320, 120, window_size); 29 | m_spectrogram = new Spectrogram(m_palette, 0, 120, 320, 120); 30 | // start off with the spectrogram hidden 31 | m_spectrogram->visible = false; 32 | // create a drawing task to update our UI 33 | xTaskCreatePinnedToCore(drawing_task, "Drawing Task", 4096, this, 1, &m_draw_task_handle, 1); 34 | } 35 | 36 | void UI::toggle_display() 37 | { 38 | m_graphic_equaliser->visible = !m_graphic_equaliser->visible; 39 | m_spectrogram->visible = !m_spectrogram->visible; 40 | } 41 | 42 | void UI::update(float *samples, float *fft) 43 | { 44 | m_waveform->update(samples); 45 | m_graphic_equaliser->update(fft); 46 | m_spectrogram->update(fft); 47 | xTaskNotify(m_draw_task_handle, 1, eIncrement); 48 | } 49 | 50 | unsigned long draw_time = 0; 51 | int draw_count = 0; 52 | void UI::draw() 53 | { 54 | auto start = millis(); 55 | m_spectrogram->draw(m_display); 56 | m_graphic_equaliser->draw(m_display); 57 | m_waveform->draw(m_display); 58 | auto end = millis(); 59 | draw_time += end - start; 60 | draw_count++; 61 | if (draw_count == 20) { 62 | Serial.printf("Drawing time %ld\n", draw_time / 20); 63 | draw_count = 0; 64 | draw_time = 0; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/AudioProcessing/Processor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "HammingWindow.h" 4 | #include "Processor.h" 5 | 6 | Processor::Processor(int window_size) 7 | { 8 | m_window_size = window_size; 9 | // work out the FFT size 10 | m_fft_size = 1; 11 | while (m_fft_size < window_size) { 12 | m_fft_size *= 2; 13 | } 14 | // create the hamming window to apply to the samples 15 | m_hamming_window = new HammingWindow(m_fft_size); 16 | // initialise kiss fftr 17 | m_cfg = kiss_fftr_alloc(m_fft_size, false, 0, 0); 18 | 19 | m_fft_input = static_cast(malloc(sizeof(float) * m_fft_size)); 20 | for (int i = 0; i < m_fft_size; i++) { 21 | m_fft_input[i] = 0; 22 | } 23 | int energy_size = m_fft_size / 2; 24 | m_fft_output = static_cast(malloc(sizeof(kiss_fft_cpx) * energy_size)); 25 | 26 | m_energy = static_cast(malloc(sizeof(float) * window_size / 4)); 27 | } 28 | 29 | void Processor::update(int16_t *samples) 30 | { 31 | int offset = (m_fft_size - m_window_size) / 2; 32 | double input_avg = 0.0; 33 | 34 | for (int i = 0; i < m_window_size; i++) { 35 | const float input_sample = samples[i] / 30.0f; 36 | m_fft_input[offset + i] = input_sample; 37 | input_avg += double(input_sample) / double(m_window_size); 38 | } 39 | 40 | // Remove any DC offset. Various M5Stack Core2 devices have shown different DC 41 | // offsets in their recordings, and this resolves that at least for the 42 | // sampling window. This does mean that signals with periods longer than the 43 | // sampling window will be negatively affected. 44 | for (int i = 0; i < m_window_size; i++) { 45 | m_fft_input[offset + i] -= input_avg; 46 | } 47 | 48 | // apply the hamming window 49 | m_hamming_window->applyWindow(m_fft_input); 50 | 51 | // do the fft 52 | kiss_fftr(m_cfg, m_fft_input, m_fft_output); 53 | 54 | for (int i = 0; i < m_window_size / 4; i++) { 55 | const float real = m_fft_output[i].r; 56 | const float imag = m_fft_output[i].i; 57 | m_energy[i] = sqrt((real * real) + (imag * imag)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/UI/GraphicEqualiser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "GraphicEqualiser.h" 6 | #include "Palette.h" 7 | 8 | #undef min 9 | 10 | GraphicEqualiser::GraphicEqualiser( 11 | Palette *palette, int x, int y, int width, int height, int num_bins) 12 | : Component(x, y, width, height) 13 | { 14 | m_palette = palette; 15 | m_num_bins = num_bins; 16 | bar_chart = static_cast(malloc(sizeof(float) * num_bins)); 17 | for (int i = 0; i < num_bins; i++) { 18 | bar_chart[i] = 0.0f; 19 | } 20 | bar_chart_peaks = static_cast(malloc(sizeof(float) * num_bins)); 21 | for (int i = 0; i < num_bins; i++) { 22 | bar_chart_peaks[i] = 0.0f; 23 | } 24 | } 25 | 26 | void GraphicEqualiser::update(float *mag) 27 | { 28 | for (int i = 0; i < m_num_bins; i++) { 29 | float m = mag[i]; 30 | if (m > bar_chart[i]) { 31 | bar_chart[i] = m; 32 | } 33 | else { 34 | bar_chart[i] = 0.7 * bar_chart[i] + 0.3 * m; 35 | } 36 | if (m > bar_chart_peaks[i]) { 37 | bar_chart_peaks[i] = m; 38 | } 39 | else { 40 | bar_chart_peaks[i] = 0.95 * bar_chart_peaks[i] + 0.05 * m; 41 | } 42 | } 43 | } 44 | 45 | void GraphicEqualiser::_draw(M5Display &display) 46 | { 47 | int x = 0; 48 | int x_step = int(320.0f / (m_num_bins / 16)); 49 | for (int i = 2; i < m_num_bins / 4; i += 4) { 50 | float ave = 0; 51 | for (int j = 0; j < 4; j++) { 52 | ave += bar_chart[i + j]; 53 | } 54 | ave /= 4; 55 | int bar_value = std::min(120.0f, 0.25f * ave); 56 | ave = 0; 57 | for (int j = 0; j < 4; j++) { 58 | ave += bar_chart_peaks[i + j]; 59 | } 60 | ave /= 4; 61 | int peak_value = std::min(120.0f, 0.25f * ave); 62 | display.fillRect(x, 120, x_step, 120 - bar_value, 0); 63 | display.drawLine(x, 64 | 240 - peak_value, 65 | x + x_step - 1, 66 | 240 - peak_value, 67 | m_palette->get_color(135 + peak_value)); 68 | display.fillRect( 69 | x, 240 - bar_value, x_step - 1, bar_value, m_palette->get_color(135 + bar_value)); 70 | x += x_step; 71 | } 72 | display.fillRect(x, 120, 320 - x, 120, 0); 73 | } 74 | -------------------------------------------------------------------------------- /lib/kissfft/Makefile: -------------------------------------------------------------------------------- 1 | KFVER=131 2 | 3 | DATATYPE ?= float 4 | 5 | PREFIX ?= /usr/local 6 | LIBDIR ?= $(PREFIX)/lib 7 | 8 | INSTALL ?= install 9 | 10 | ifeq ($(shell uname -s),Darwin) 11 | SHARED_NAME := libkissfft.dylib 12 | SHARED_FLAGS := -Wl,-install_name,$(SHARED_NAME) 13 | else 14 | SHARED_NAME := libkissfft.so 15 | SHARED_FLAGS := -Wl,-soname,$(SHARED_NAME) 16 | endif 17 | 18 | all: 19 | gcc -Wall -fPIC -c *.c -Dkiss_fft_scalar=$(DATATYPE) -o kiss_fft.o 20 | ar crus libkissfft.a kiss_fft.o 21 | gcc -shared $(SHARED_FLAGS) -o $(SHARED_NAME) kiss_fft.o 22 | 23 | install: all 24 | $(INSTALL) -Dt $(LIBDIR) $(SHARED_NAME) 25 | 26 | doc: 27 | @echo "Start by reading the README file. If you want to build and test lots of stuff, do a 'make testall'" 28 | @echo "but be aware that 'make testall' has dependencies that the basic kissfft software does not." 29 | @echo "It is generally unneeded to run these tests yourself, unless you plan on changing the inner workings" 30 | @echo "of kissfft and would like to make use of its regression tests." 31 | 32 | testall: 33 | # The simd and int32_t types may or may not work on your machine 34 | make -C test testcpp && test/testcpp 35 | make -C test DATATYPE=simd CFLAGADD="$(CFLAGADD)" test 36 | make -C test DATATYPE=int32_t CFLAGADD="$(CFLAGADD)" test 37 | make -C test DATATYPE=int16_t CFLAGADD="$(CFLAGADD)" test 38 | make -C test DATATYPE=float CFLAGADD="$(CFLAGADD)" test 39 | make -C test DATATYPE=double CFLAGADD="$(CFLAGADD)" test 40 | make -C test testsse 41 | echo "all tests passed" 42 | 43 | tarball: clean 44 | git archive --prefix=kissfft/ -o kissfft$(KFVER).tar.gz v$(KFVER) 45 | git archive --prefix=kissfft/ -o kissfft$(KFVER).zip v$(KFVER) 46 | 47 | clean: 48 | cd test && make clean 49 | cd tools && make clean 50 | rm -f kiss_fft*.tar.gz *~ *.pyc kiss_fft*.zip 51 | 52 | asm: kiss_fft.s 53 | 54 | kiss_fft.s: kiss_fft.c kiss_fft.h _kiss_fft_guts.h 55 | [ -e kiss_fft.s ] && mv kiss_fft.s kiss_fft.s~ || true 56 | gcc -S kiss_fft.c -O3 -mtune=native -ffast-math -fomit-frame-pointer -unroll-loops -dA -fverbose-asm 57 | gcc -o kiss_fft_short.s -S kiss_fft.c -O3 -mtune=native -ffast-math -fomit-frame-pointer -dA -fverbose-asm -DFIXED_POINT 58 | [ -e kiss_fft.s~ ] && diff kiss_fft.s~ kiss_fft.s || true 59 | -------------------------------------------------------------------------------- /lib/kissfft/TIPS: -------------------------------------------------------------------------------- 1 | Speed: 2 | * If you want to use multiple cores, then compile with -openmp or -fopenmp (see your compiler docs). 3 | Realize that larger FFTs will reap more benefit than smaller FFTs. This generally uses more CPU time, but 4 | less wall time. 5 | 6 | * experiment with compiler flags 7 | Special thanks to Oscar Lesta. He suggested some compiler flags 8 | for gcc that make a big difference. They shave 10-15% off 9 | execution time on some systems. Try some combination of: 10 | -march=pentiumpro 11 | -ffast-math 12 | -fomit-frame-pointer 13 | 14 | * If the input data has no imaginary component, use the kiss_fftr code under tools/. 15 | Real ffts are roughly twice as fast as complex. 16 | 17 | * If you can rearrange your code to do 4 FFTs in parallel and you are on a recent Intel or AMD machine, 18 | then you might want to experiment with the USE_SIMD code. See README.simd 19 | 20 | 21 | Reducing code size: 22 | * remove some of the butterflies. There are currently butterflies optimized for radices 23 | 2,3,4,5. It is worth mentioning that you can still use FFT sizes that contain 24 | other factors, they just won't be quite as fast. You can decide for yourself 25 | whether to keep radix 2 or 4. If you do some work in this area, let me 26 | know what you find. 27 | 28 | * For platforms where ROM/code space is more plentiful than RAM, 29 | consider creating a hardcoded kiss_fft_state. In other words, decide which 30 | FFT size(s) you want and make a structure with the correct factors and twiddles. 31 | 32 | * Frank van der Hulst offered numerous suggestions for smaller code size and correct operation 33 | on embedded targets. "I'm happy to help anyone who is trying to implement KISSFFT on a micro" 34 | 35 | Some of these were rolled into the mainline code base: 36 | - using long casts to promote intermediate results of short*short multiplication 37 | - delaying allocation of buffers that are sometimes unused. 38 | In some cases, it may be desirable to limit capability in order to better suit the target: 39 | - predefining the twiddle tables for the desired fft size. 40 | -------------------------------------------------------------------------------- /src/I2S/I2SSampler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "I2SSampler.h" 5 | 6 | void I2SSampler::addSample(int16_t sample) 7 | { 8 | // add the sample to the current audio buffer 9 | m_currentAudioBuffer[m_audioBufferPos] = sample; 10 | m_audioBufferPos++; 11 | // have we filled the buffer with data? 12 | if (m_audioBufferPos == m_bufferSizeInSamples) { 13 | // swap to the other buffer 14 | std::swap(m_currentAudioBuffer, m_capturedAudioBuffer); 15 | // reset the buffer position 16 | m_audioBufferPos = 0; 17 | // tell the writer task to save the data 18 | xTaskNotify(m_processorTaskHandle, 1, eIncrement); 19 | } 20 | } 21 | 22 | void i2sReaderTask(void *param) 23 | { 24 | I2SSampler *sampler = (I2SSampler *)param; 25 | while (true) { 26 | // wait for some data to arrive on the queue 27 | i2s_event_t evt; 28 | if (xQueueReceive(sampler->m_i2sQueue, &evt, portMAX_DELAY) != pdPASS) { 29 | continue; 30 | } 31 | 32 | if (evt.type != I2S_EVENT_RX_DONE) { 33 | continue; 34 | } 35 | 36 | size_t bytesRead = 0; 37 | do { 38 | // read data from the I2S peripheral 39 | int16_t i2sData[2048]; 40 | // read from i2s 41 | i2s_read(sampler->m_i2sPort, i2sData, 4096, &bytesRead, 10); 42 | for (int i = 0; i < bytesRead / 2; i += 4) { 43 | sampler->addSample((i2sData[i] + i2sData[i + 1] + i2sData[i + 2] + i2sData[i + 3]) / 4); 44 | } 45 | } while (bytesRead > 0); 46 | } 47 | } 48 | 49 | void I2SSampler::start(i2s_port_t i2sPort, 50 | const i2s_pin_config_t &i2sPins, 51 | const i2s_config_t &i2sConfig, 52 | int32_t bufferSizeInSamples, 53 | TaskHandle_t processorTaskHandle) 54 | { 55 | m_i2sPort = i2sPort; 56 | m_processorTaskHandle = processorTaskHandle; 57 | m_bufferSizeInSamples = bufferSizeInSamples; 58 | m_bufferSizeInBytes = bufferSizeInSamples * sizeof(int16_t); 59 | m_audioBuffer1 = (int16_t *)malloc(m_bufferSizeInBytes); 60 | m_audioBuffer2 = (int16_t *)malloc(m_bufferSizeInBytes); 61 | 62 | m_currentAudioBuffer = m_audioBuffer1; 63 | m_capturedAudioBuffer = m_audioBuffer2; 64 | 65 | // install and start i2s driver 66 | i2s_driver_install(m_i2sPort, &i2sConfig, 4, &m_i2sQueue); 67 | i2s_set_pin(i2sPort, &i2sPins); 68 | i2s_set_clk(i2sPort, i2sConfig.sample_rate, i2sConfig.bits_per_sample, I2S_CHANNEL_MONO); 69 | // i2s_set_pdm_rx_down_sample(i2sPort, I2S_PDM_DSR_8S); 70 | // start a task to read samples from the ADC 71 | TaskHandle_t readerTaskHandle; 72 | xTaskCreatePinnedToCore(i2sReaderTask, "i2s Reader Task", 8192, this, 1, &readerTaskHandle, 0); 73 | } 74 | -------------------------------------------------------------------------------- /lib/kissfft/README.simd: -------------------------------------------------------------------------------- 1 | If you are reading this, it means you think you may be interested in using the SIMD extensions in kissfft 2 | to do 4 *separate* FFTs at once. 3 | 4 | Beware! Beyond here there be dragons! 5 | 6 | This API is not easy to use, is not well documented, and breaks the KISS principle. 7 | 8 | 9 | Still reading? Okay, you may get rewarded for your patience with a considerable speedup 10 | (2-3x) on intel x86 machines with SSE if you are willing to jump through some hoops. 11 | 12 | The basic idea is to use the packed 4 float __m128 data type as a scalar element. 13 | This means that the format is pretty convoluted. It performs 4 FFTs per fft call on signals A,B,C,D. 14 | 15 | For complex data, the data is interlaced as follows: 16 | rA0,rB0,rC0,rD0, iA0,iB0,iC0,iD0, rA1,rB1,rC1,rD1, iA1,iB1,iC1,iD1 ... 17 | where "rA0" is the real part of the zeroth sample for signal A 18 | 19 | Real-only data is laid out: 20 | rA0,rB0,rC0,rD0, rA1,rB1,rC1,rD1, ... 21 | 22 | Compile with gcc flags something like 23 | -O3 -mpreferred-stack-boundary=4 -DUSE_SIMD=1 -msse 24 | 25 | Be aware of SIMD alignment. This is the most likely cause of segfaults. 26 | The code within kissfft uses scratch variables on the stack. 27 | With SIMD, these must have addresses on 16 byte boundaries. 28 | Search on "SIMD alignment" for more info. 29 | 30 | 31 | 32 | Robin at Divide Concept was kind enough to share his code for formatting to/from the SIMD kissfft. 33 | I have not run it -- use it at your own risk. It appears to do 4xN and Nx4 transpositions 34 | (out of place). 35 | 36 | void SSETools::pack128(float* target, float* source, unsigned long size128) 37 | { 38 | __m128* pDest = (__m128*)target; 39 | __m128* pDestEnd = pDest+size128; 40 | float* source0=source; 41 | float* source1=source0+size128; 42 | float* source2=source1+size128; 43 | float* source3=source2+size128; 44 | 45 | while(pDestnfft == nfft && inverse == cur->inverse ) 31 | break;/*found the right node*/ 32 | prev = cur; 33 | cur = prev->next; 34 | } 35 | if (cur== NULL) { 36 | /* no cached node found, need to create a new one*/ 37 | kiss_fft_alloc(nfft,inverse,0,&len); 38 | #ifdef USE_SIMD 39 | int padding = (16-sizeof(struct cached_fft)) & 15; 40 | // make sure the cfg aligns on a 16 byte boundary 41 | len += padding; 42 | #endif 43 | cur = (kfc_cfg)KISS_FFT_MALLOC((sizeof(struct cached_fft) + len )); 44 | if (cur == NULL) 45 | return NULL; 46 | cur->cfg = (kiss_fft_cfg)(cur+1); 47 | #ifdef USE_SIMD 48 | cur->cfg = (kiss_fft_cfg) ((char*)(cur+1)+padding); 49 | #endif 50 | kiss_fft_alloc(nfft,inverse,cur->cfg,&len); 51 | cur->nfft=nfft; 52 | cur->inverse=inverse; 53 | cur->next = NULL; 54 | if ( prev ) 55 | prev->next = cur; 56 | else 57 | cache_root = cur; 58 | ++ncached; 59 | } 60 | return cur->cfg; 61 | } 62 | 63 | void kfc_cleanup(void) 64 | { 65 | kfc_cfg cur=cache_root; 66 | kfc_cfg next=NULL; 67 | while (cur){ 68 | next = cur->next; 69 | free(cur); 70 | cur=next; 71 | } 72 | ncached=0; 73 | cache_root = NULL; 74 | } 75 | void kfc_fft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout) 76 | { 77 | kiss_fft( find_cached_fft(nfft,0),fin,fout ); 78 | } 79 | 80 | void kfc_ifft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout) 81 | { 82 | kiss_fft( find_cached_fft(nfft,1),fin,fout ); 83 | } 84 | 85 | #ifdef KFC_TEST 86 | static void check(int nc) 87 | { 88 | if (ncached != nc) { 89 | fprintf(stderr,"ncached should be %d,but it is %d\n",nc,ncached); 90 | exit(1); 91 | } 92 | } 93 | 94 | int main(void) 95 | { 96 | kiss_fft_cpx buf1[1024],buf2[1024]; 97 | memset(buf1,0,sizeof(buf1)); 98 | check(0); 99 | kfc_fft(512,buf1,buf2); 100 | check(1); 101 | kfc_fft(512,buf1,buf2); 102 | check(1); 103 | kfc_ifft(512,buf1,buf2); 104 | check(2); 105 | kfc_cleanup(); 106 | check(0); 107 | return 0; 108 | } 109 | #endif 110 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kiss_fftndr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #include "kiss_fftndr.h" 10 | #include "_kiss_fft_guts.h" 11 | #define MAX(x,y) ( ( (x)<(y) )?(y):(x) ) 12 | 13 | struct kiss_fftndr_state 14 | { 15 | int dimReal; 16 | int dimOther; 17 | kiss_fftr_cfg cfg_r; 18 | kiss_fftnd_cfg cfg_nd; 19 | void * tmpbuf; 20 | }; 21 | 22 | static int prod(const int *dims, int ndims) 23 | { 24 | int x=1; 25 | while (ndims--) 26 | x *= *dims++; 27 | return x; 28 | } 29 | 30 | kiss_fftndr_cfg kiss_fftndr_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem) 31 | { 32 | KISS_FFT_ALIGN_CHECK(mem) 33 | 34 | kiss_fftndr_cfg st = NULL; 35 | size_t nr=0 , nd=0,ntmp=0; 36 | int dimReal = dims[ndims-1]; 37 | int dimOther = prod(dims,ndims-1); 38 | size_t memneeded; 39 | char * ptr = NULL; 40 | 41 | (void)kiss_fftr_alloc(dimReal,inverse_fft,NULL,&nr); 42 | (void)kiss_fftnd_alloc(dims,ndims-1,inverse_fft,NULL,&nd); 43 | ntmp = 44 | MAX( 2*dimOther , dimReal+2) * sizeof(kiss_fft_scalar) // freq buffer for one pass 45 | + dimOther*(dimReal+2) * sizeof(kiss_fft_scalar); // large enough to hold entire input in case of in-place 46 | 47 | memneeded = KISS_FFT_ALIGN_SIZE_UP(sizeof( struct kiss_fftndr_state )) + KISS_FFT_ALIGN_SIZE_UP(nr) + KISS_FFT_ALIGN_SIZE_UP(nd) + KISS_FFT_ALIGN_SIZE_UP(ntmp); 48 | 49 | if (lenmem==NULL) { 50 | ptr = (char*) malloc(memneeded); 51 | }else{ 52 | if (*lenmem >= memneeded) 53 | ptr = (char *)mem; 54 | *lenmem = memneeded; 55 | } 56 | if (ptr==NULL) 57 | return NULL; 58 | 59 | st = (kiss_fftndr_cfg) ptr; 60 | memset( st , 0 , memneeded); 61 | ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fftndr_state)); 62 | 63 | st->dimReal = dimReal; 64 | st->dimOther = dimOther; 65 | st->cfg_r = kiss_fftr_alloc( dimReal,inverse_fft,ptr,&nr); 66 | ptr += KISS_FFT_ALIGN_SIZE_UP(nr); 67 | st->cfg_nd = kiss_fftnd_alloc(dims,ndims-1,inverse_fft, ptr,&nd); 68 | ptr += KISS_FFT_ALIGN_SIZE_UP(nd); 69 | st->tmpbuf = ptr; 70 | 71 | return st; 72 | } 73 | 74 | void kiss_fftndr(kiss_fftndr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) 75 | { 76 | int k1,k2; 77 | int dimReal = st->dimReal; 78 | int dimOther = st->dimOther; 79 | int nrbins = dimReal/2+1; 80 | 81 | kiss_fft_cpx * tmp1 = (kiss_fft_cpx*)st->tmpbuf; 82 | kiss_fft_cpx * tmp2 = tmp1 + MAX(nrbins,dimOther); 83 | 84 | // timedata is N0 x N1 x ... x Nk real 85 | 86 | // take a real chunk of data, fft it and place the output at correct intervals 87 | for (k1=0;k1cfg_r, timedata + k1*dimReal , tmp1 ); // tmp1 now holds nrbins complex points 89 | for (k2=0;k2cfg_nd, tmp2+k2*dimOther, tmp1); // tmp1 now holds dimOther complex points 95 | for (k1=0;k1dimReal; 104 | int dimOther = st->dimOther; 105 | int nrbins = dimReal/2+1; 106 | kiss_fft_cpx * tmp1 = (kiss_fft_cpx*)st->tmpbuf; 107 | kiss_fft_cpx * tmp2 = tmp1 + MAX(nrbins,dimOther); 108 | 109 | for (k2=0;k2cfg_nd, tmp1, tmp2+k2*dimOther); 113 | } 114 | 115 | for (k1=0;k1cfg_r,tmp1,timedata + k1*dimReal); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/kissfft/kiss_fft.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #ifndef KISS_FFT_H 10 | #define KISS_FFT_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /* 22 | ATTENTION! 23 | If you would like a : 24 | -- a utility that will handle the caching of fft objects 25 | -- real-only (no imaginary time component ) FFT 26 | -- a multi-dimensional FFT 27 | -- a command-line utility to perform ffts 28 | -- a command-line utility to perform fast-convolution filtering 29 | 30 | Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c 31 | in the tools/ directory. 32 | */ 33 | 34 | /* User may override KISS_FFT_MALLOC and/or KISS_FFT_FREE. */ 35 | #ifdef USE_SIMD 36 | # include 37 | # define kiss_fft_scalar __m128 38 | # ifndef KISS_FFT_MALLOC 39 | # define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) 40 | # define KISS_FFT_ALIGN_CHECK(ptr) 41 | # define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 15UL) & ~0xFUL) 42 | # endif 43 | # ifndef KISS_FFT_FREE 44 | # define KISS_FFT_FREE _mm_free 45 | # endif 46 | #else 47 | # define KISS_FFT_ALIGN_CHECK(ptr) 48 | # define KISS_FFT_ALIGN_SIZE_UP(size) (size) 49 | # ifndef KISS_FFT_MALLOC 50 | # define KISS_FFT_MALLOC malloc 51 | # endif 52 | # ifndef KISS_FFT_FREE 53 | # define KISS_FFT_FREE free 54 | # endif 55 | #endif 56 | 57 | 58 | #ifdef FIXED_POINT 59 | #include 60 | # if (FIXED_POINT == 32) 61 | # define kiss_fft_scalar int32_t 62 | # else 63 | # define kiss_fft_scalar int16_t 64 | # endif 65 | #else 66 | # ifndef kiss_fft_scalar 67 | /* default is float */ 68 | # define kiss_fft_scalar float 69 | # endif 70 | #endif 71 | 72 | typedef struct { 73 | kiss_fft_scalar r; 74 | kiss_fft_scalar i; 75 | }kiss_fft_cpx; 76 | 77 | typedef struct kiss_fft_state* kiss_fft_cfg; 78 | 79 | /* 80 | * kiss_fft_alloc 81 | * 82 | * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. 83 | * 84 | * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); 85 | * 86 | * The return value from fft_alloc is a cfg buffer used internally 87 | * by the fft routine or NULL. 88 | * 89 | * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. 90 | * The returned value should be free()d when done to avoid memory leaks. 91 | * 92 | * The state can be placed in a user supplied buffer 'mem': 93 | * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, 94 | * then the function places the cfg in mem and the size used in *lenmem 95 | * and returns mem. 96 | * 97 | * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), 98 | * then the function returns NULL and places the minimum cfg 99 | * buffer size in *lenmem. 100 | * */ 101 | 102 | kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); 103 | 104 | /* 105 | * kiss_fft(cfg,in_out_buf) 106 | * 107 | * Perform an FFT on a complex input buffer. 108 | * for a forward FFT, 109 | * fin should be f[0] , f[1] , ... ,f[nfft-1] 110 | * fout will be F[0] , F[1] , ... ,F[nfft-1] 111 | * Note that each element is complex and can be accessed like 112 | f[k].r and f[k].i 113 | * */ 114 | void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); 115 | 116 | /* 117 | A more generic version of the above function. It reads its input from every Nth sample. 118 | * */ 119 | void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); 120 | 121 | /* If kiss_fft_alloc allocated a buffer, it is one contiguous 122 | buffer and can be simply free()d when no longer needed*/ 123 | #define kiss_fft_free KISS_FFT_FREE 124 | 125 | /* 126 | Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up 127 | your compiler output to call this before you exit. 128 | */ 129 | void kiss_fft_cleanup(void); 130 | 131 | 132 | /* 133 | * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) 134 | */ 135 | int kiss_fft_next_fast_size(int n); 136 | 137 | /* for real ffts, we need an even size */ 138 | #define kiss_fftr_next_fast_size_real(n) \ 139 | (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) 140 | 141 | #ifdef __cplusplus 142 | } 143 | #endif 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kiss_fftr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #include "kiss_fftr.h" 10 | #include "_kiss_fft_guts.h" 11 | 12 | struct kiss_fftr_state{ 13 | kiss_fft_cfg substate; 14 | kiss_fft_cpx * tmpbuf; 15 | kiss_fft_cpx * super_twiddles; 16 | #ifdef USE_SIMD 17 | void * pad; 18 | #endif 19 | }; 20 | 21 | kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) 22 | { 23 | KISS_FFT_ALIGN_CHECK(mem) 24 | 25 | int i; 26 | kiss_fftr_cfg st = NULL; 27 | size_t subsize = 0, memneeded; 28 | 29 | if (nfft & 1) { 30 | KISS_FFT_ERROR("Real FFT optimization must be even."); 31 | return NULL; 32 | } 33 | nfft >>= 1; 34 | 35 | kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); 36 | memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2); 37 | 38 | if (lenmem == NULL) { 39 | st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); 40 | } else { 41 | if (*lenmem >= memneeded) 42 | st = (kiss_fftr_cfg) mem; 43 | *lenmem = memneeded; 44 | } 45 | if (!st) 46 | return NULL; 47 | 48 | st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ 49 | st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); 50 | st->super_twiddles = st->tmpbuf + nfft; 51 | kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); 52 | 53 | for (i = 0; i < nfft/2; ++i) { 54 | double phase = 55 | -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); 56 | if (inverse_fft) 57 | phase *= -1; 58 | kf_cexp (st->super_twiddles+i,phase); 59 | } 60 | return st; 61 | } 62 | 63 | void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) 64 | { 65 | /* input buffer timedata is stored row-wise */ 66 | int k,ncfft; 67 | kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; 68 | 69 | if ( st->substate->inverse) { 70 | KISS_FFT_ERROR("kiss fft usage error: improper alloc"); 71 | return;/* The caller did not call the correct function */ 72 | } 73 | 74 | ncfft = st->substate->nfft; 75 | 76 | /*perform the parallel fft of two real signals packed in real,imag*/ 77 | kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); 78 | /* The real part of the DC element of the frequency spectrum in st->tmpbuf 79 | * contains the sum of the even-numbered elements of the input time sequence 80 | * The imag part is the sum of the odd-numbered elements 81 | * 82 | * The sum of tdc.r and tdc.i is the sum of the input time sequence. 83 | * yielding DC of input time sequence 84 | * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... 85 | * yielding Nyquist bin of input time sequence 86 | */ 87 | 88 | tdc.r = st->tmpbuf[0].r; 89 | tdc.i = st->tmpbuf[0].i; 90 | C_FIXDIV(tdc,2); 91 | CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); 92 | CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); 93 | freqdata[0].r = tdc.r + tdc.i; 94 | freqdata[ncfft].r = tdc.r - tdc.i; 95 | #ifdef USE_SIMD 96 | freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); 97 | #else 98 | freqdata[ncfft].i = freqdata[0].i = 0; 99 | #endif 100 | 101 | for ( k=1;k <= ncfft/2 ; ++k ) { 102 | fpk = st->tmpbuf[k]; 103 | fpnk.r = st->tmpbuf[ncfft-k].r; 104 | fpnk.i = - st->tmpbuf[ncfft-k].i; 105 | C_FIXDIV(fpk,2); 106 | C_FIXDIV(fpnk,2); 107 | 108 | C_ADD( f1k, fpk , fpnk ); 109 | C_SUB( f2k, fpk , fpnk ); 110 | C_MUL( tw , f2k , st->super_twiddles[k-1]); 111 | 112 | freqdata[k].r = HALF_OF(f1k.r + tw.r); 113 | freqdata[k].i = HALF_OF(f1k.i + tw.i); 114 | freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); 115 | freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); 116 | } 117 | } 118 | 119 | void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) 120 | { 121 | /* input buffer timedata is stored row-wise */ 122 | int k, ncfft; 123 | 124 | if (st->substate->inverse == 0) { 125 | KISS_FFT_ERROR("kiss fft usage error: improper alloc"); 126 | return;/* The caller did not call the correct function */ 127 | } 128 | 129 | ncfft = st->substate->nfft; 130 | 131 | st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; 132 | st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; 133 | C_FIXDIV(st->tmpbuf[0],2); 134 | 135 | for (k = 1; k <= ncfft / 2; ++k) { 136 | kiss_fft_cpx fk, fnkc, fek, fok, tmp; 137 | fk = freqdata[k]; 138 | fnkc.r = freqdata[ncfft - k].r; 139 | fnkc.i = -freqdata[ncfft - k].i; 140 | C_FIXDIV( fk , 2 ); 141 | C_FIXDIV( fnkc , 2 ); 142 | 143 | C_ADD (fek, fk, fnkc); 144 | C_SUB (tmp, fk, fnkc); 145 | C_MUL (fok, tmp, st->super_twiddles[k-1]); 146 | C_ADD (st->tmpbuf[k], fek, fok); 147 | C_SUB (st->tmpbuf[ncfft - k], fek, fok); 148 | #ifdef USE_SIMD 149 | st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); 150 | #else 151 | st->tmpbuf[ncfft - k].i *= -1; 152 | #endif 153 | } 154 | kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); 155 | } 156 | -------------------------------------------------------------------------------- /lib/kissfft/_kiss_fft_guts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | /* kiss_fft.h 10 | defines kiss_fft_scalar as either short or a float type 11 | and defines 12 | typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ 13 | 14 | #ifndef _kiss_fft_guts_h 15 | #define _kiss_fft_guts_h 16 | 17 | #include "kiss_fft.h" 18 | #include "kiss_fft_log.h" 19 | #include 20 | 21 | #define MAXFACTORS 32 22 | /* e.g. an fft of length 128 has 4 factors 23 | as far as kissfft is concerned 24 | 4*4*4*2 25 | */ 26 | 27 | struct kiss_fft_state{ 28 | int nfft; 29 | int inverse; 30 | int factors[2*MAXFACTORS]; 31 | kiss_fft_cpx twiddles[1]; 32 | }; 33 | 34 | /* 35 | Explanation of macros dealing with complex math: 36 | 37 | C_MUL(m,a,b) : m = a*b 38 | C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise 39 | C_SUB( res, a,b) : res = a - b 40 | C_SUBFROM( res , a) : res -= a 41 | C_ADDTO( res , a) : res += a 42 | * */ 43 | #ifdef FIXED_POINT 44 | #include 45 | #if (FIXED_POINT==32) 46 | # define FRACBITS 31 47 | # define SAMPPROD int64_t 48 | #define SAMP_MAX INT32_MAX 49 | #define SAMP_MIN INT32_MIN 50 | #else 51 | # define FRACBITS 15 52 | # define SAMPPROD int32_t 53 | #define SAMP_MAX INT16_MAX 54 | #define SAMP_MIN INT16_MIN 55 | #endif 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 | KISS_FFT_WARNING("overflow (%d " #op" %d) = %ld", (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)*((kiss_fft_scalar).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 | KISS_FFT_DEBUG("%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 | 166 | #endif /* _kiss_fft_guts_h */ 167 | 168 | -------------------------------------------------------------------------------- /lib/kissfft/CHANGELOG: -------------------------------------------------------------------------------- 1 | 1.3.0 2012-07-18 2 | removed non-standard malloc.h from kiss_fft.h 3 | 4 | moved -lm to end of link line 5 | 6 | checked various return values 7 | 8 | converted python Numeric code to NumPy 9 | 10 | fixed test of int32_t on 64 bit OS 11 | 12 | added padding in a couple of places to allow SIMD alignment of structs 13 | 14 | 1.2.9 2010-05-27 15 | threadsafe ( including OpenMP ) 16 | 17 | first edition of kissfft.hh the C++ template fft engine 18 | 19 | 1.2.8 20 | Changed memory.h to string.h -- apparently more standard 21 | 22 | Added openmp extensions. This can have fairly linear speedups for larger FFT sizes. 23 | 24 | 1.2.7 25 | Shrank the real-fft memory footprint. Thanks to Galen Seitz. 26 | 27 | 1.2.6 (Nov 14, 2006) The "thanks to GenArts" release. 28 | Added multi-dimensional real-optimized FFT, see tools/kiss_fftndr 29 | Thanks go to GenArts, Inc. for sponsoring the development. 30 | 31 | 1.2.5 (June 27, 2006) The "release for no good reason" release. 32 | Changed some harmless code to make some compilers' warnings go away. 33 | Added some more digits to pi -- why not. 34 | Added kiss_fft_next_fast_size() function to help people decide how much to pad. 35 | Changed multidimensional test from 8 dimensions to only 3 to avoid testing 36 | problems with fixed point (sorry Buckaroo Banzai). 37 | 38 | 1.2.4 (Oct 27, 2005) The "oops, inverse fixed point real fft was borked" release. 39 | Fixed scaling bug for inverse fixed point real fft -- also fixed test code that should've been failing. 40 | Thanks to Jean-Marc Valin for bug report. 41 | 42 | Use sys/types.h for more portable types than short,int,long => int16_t,int32_t,int64_t 43 | If your system does not have these, you may need to define them -- but at least it breaks in a 44 | loud and easily fixable way -- unlike silently using the wrong size type. 45 | 46 | Hopefully tools/psdpng.c is fixed -- thanks to Steve Kellog for pointing out the weirdness. 47 | 48 | 1.2.3 (June 25, 2005) The "you want to use WHAT as a sample" release. 49 | Added ability to use 32 bit fixed point samples -- requires a 64 bit intermediate result, a la 'long long' 50 | 51 | Added ability to do 4 FFTs in parallel by using SSE SIMD instructions. This is accomplished by 52 | using the __m128 (vector of 4 floats) as kiss_fft_scalar. Define USE_SIMD to use this. 53 | 54 | I know, I know ... this is drifting a bit from the "kiss" principle, but the speed advantages 55 | make it worth it for some. Also recent gcc makes it SOO easy to use vectors of 4 floats like a POD type. 56 | 57 | 1.2.2 (May 6, 2005) The Matthew release 58 | Replaced fixed point division with multiply&shift. Thanks to Jean-Marc Valin for 59 | discussions regarding. Considerable speedup for fixed-point. 60 | 61 | Corrected overflow protection in real fft routines when using fixed point. 62 | Finder's Credit goes to Robert Oschler of robodance for pointing me at the bug. 63 | This also led to the CHECK_OVERFLOW_OP macro. 64 | 65 | 1.2.1 (April 4, 2004) 66 | compiles cleanly with just about every -W warning flag under the sun 67 | 68 | reorganized kiss_fft_state so it could be read-only/const. This may be useful for embedded systems 69 | that are willing to predeclare twiddle factors, factorization. 70 | 71 | Fixed C_MUL,S_MUL on 16-bit platforms. 72 | 73 | tmpbuf will only be allocated if input & output buffers are same 74 | scratchbuf will only be allocated for ffts that are not multiples of 2,3,5 75 | 76 | NOTE: The tmpbuf,scratchbuf changes may require synchronization code for multi-threaded apps. 77 | 78 | 79 | 1.2 (Feb 23, 2004) 80 | interface change -- cfg object is forward declaration of struct instead of void* 81 | This maintains type saftey and lets the compiler warn/error about stupid mistakes. 82 | (prompted by suggestion from Erik de Castro Lopo) 83 | 84 | small speed improvements 85 | 86 | added psdpng.c -- sample utility that will create png spectrum "waterfalls" from an input file 87 | ( not terribly useful yet) 88 | 89 | 1.1.1 (Feb 1, 2004 ) 90 | minor bug fix -- only affects odd rank, in-place, multi-dimensional FFTs 91 | 92 | 1.1 : (Jan 30,2004) 93 | split sample_code/ into test/ and tools/ 94 | 95 | Removed 2-D fft and added N-D fft (arbitrary) 96 | 97 | modified fftutil.c to allow multi-d FFTs 98 | 99 | Modified core fft routine to allow an input stride via kiss_fft_stride() 100 | (eased support of multi-D ffts) 101 | 102 | Added fast convolution filtering (FIR filtering using overlap-scrap method, with tail scrap) 103 | 104 | Add kfc.[ch]: the KISS FFT Cache. It takes care of allocs for you ( suggested by Oscar Lesta ). 105 | 106 | 1.0.1 (Dec 15, 2003) 107 | fixed bug that occurred when nfft==1. Thanks to Steven Johnson. 108 | 109 | 1.0 : (Dec 14, 2003) 110 | changed kiss_fft function from using a single buffer, to two buffers. 111 | If the same buffer pointer is supplied for both in and out, kiss will 112 | manage the buffer copies. 113 | 114 | added kiss_fft2d and kiss_fftr as separate source files (declarations in kiss_fft.h ) 115 | 116 | 0.4 :(Nov 4,2003) optimized for radix 2,3,4,5 117 | 118 | 0.3 :(Oct 28, 2003) woops, version 2 didn't actually factor out any radices other than 2. 119 | Thanks to Steven Johnson for finding this one. 120 | 121 | 0.2 :(Oct 27, 2003) added mixed radix, only radix 2,4 optimized versions 122 | 123 | 0.1 :(May 19 2003) initial release, radix 2 only 124 | -------------------------------------------------------------------------------- /lib/kissfft/README: -------------------------------------------------------------------------------- 1 | KISS FFT - A mixed-radix Fast Fourier Transform based up on the principle, 2 | "Keep It Simple, Stupid." 3 | 4 | There are many great fft libraries already around. Kiss FFT is not trying 5 | to be better than any of them. It only attempts to be a reasonably efficient, 6 | moderately useful FFT that can use fixed or floating data types and can be 7 | incorporated into someone's C program in a few minutes with trivial licensing. 8 | 9 | USAGE: 10 | 11 | The basic usage for 1-d complex FFT is: 12 | 13 | #include "kiss_fft.h" 14 | 15 | kiss_fft_cfg cfg = kiss_fft_alloc( nfft ,is_inverse_fft ,0,0 ); 16 | 17 | while ... 18 | 19 | ... // put kth sample in cx_in[k].r and cx_in[k].i 20 | 21 | kiss_fft( cfg , cx_in , cx_out ); 22 | 23 | ... // transformed. DC is in cx_out[0].r and cx_out[0].i 24 | 25 | kiss_fft_free(cfg); 26 | 27 | Note: frequency-domain data is stored from dc up to 2pi. 28 | so cx_out[0] is the dc bin of the FFT 29 | and cx_out[nfft/2] is the Nyquist bin (if exists) 30 | 31 | Declarations are in "kiss_fft.h", along with a brief description of the 32 | functions you'll need to use. 33 | 34 | Code definitions for 1d complex FFTs are in kiss_fft.c. 35 | 36 | You can do other cool stuff with the extras you'll find in tools/ 37 | 38 | * multi-dimensional FFTs 39 | * real-optimized FFTs (returns the positive half-spectrum: (nfft/2+1) complex frequency bins) 40 | * fast convolution FIR filtering (not available for fixed point) 41 | * spectrum image creation 42 | 43 | The core fft and most tools/ code can be compiled to use float, double, 44 | Q15 short or Q31 samples. The default is float. 45 | 46 | 47 | BACKGROUND: 48 | 49 | I started coding this because I couldn't find a fixed point FFT that didn't 50 | use assembly code. I started with floating point numbers so I could get the 51 | theory straight before working on fixed point issues. In the end, I had a 52 | little bit of code that could be recompiled easily to do ffts with short, float 53 | or double (other types should be easy too). 54 | 55 | Once I got my FFT working, I was curious about the speed compared to 56 | a well respected and highly optimized fft library. I don't want to criticize 57 | this great library, so let's call it FFT_BRANDX. 58 | During this process, I learned: 59 | 60 | 1. FFT_BRANDX has more than 100K lines of code. The core of kiss_fft is about 500 lines (cpx 1-d). 61 | 2. It took me an embarrassingly long time to get FFT_BRANDX working. 62 | 3. A simple program using FFT_BRANDX is 522KB. A similar program using kiss_fft is 18KB (without optimizing for size). 63 | 4. FFT_BRANDX is roughly twice as fast as KISS FFT in default mode. 64 | 65 | It is wonderful that free, highly optimized libraries like FFT_BRANDX exist. 66 | But such libraries carry a huge burden of complexity necessary to extract every 67 | last bit of performance. 68 | 69 | Sometimes simpler is better, even if it's not better. 70 | 71 | FREQUENTLY ASKED QUESTIONS: 72 | Q: Can I use kissfft in a project with a ___ license? 73 | A: Yes. See LICENSE below. 74 | 75 | Q: Why don't I get the output I expect? 76 | A: The two most common causes of this are 77 | 1) scaling : is there a constant multiplier between what you got and what you want? 78 | 2) mixed build environment -- all code must be compiled with same preprocessor 79 | definitions for FIXED_POINT and kiss_fft_scalar 80 | 81 | Q: Will you write/debug my code for me? 82 | A: Probably not unless you pay me. I am happy to answer pointed and topical questions, but 83 | I may refer you to a book, a forum, or some other resource. 84 | 85 | 86 | PERFORMANCE: 87 | (on Athlon XP 2100+, with gcc 2.96, float data type) 88 | 89 | Kiss performed 10000 1024-pt cpx ffts in .63 s of cpu time. 90 | For comparison, it took md5sum twice as long to process the same amount of data. 91 | 92 | Transforming 5 minutes of CD quality audio takes less than a second (nfft=1024). 93 | 94 | DO NOT: 95 | ... use Kiss if you need the Fastest Fourier Transform in the World 96 | ... ask me to add features that will bloat the code 97 | 98 | UNDER THE HOOD: 99 | 100 | Kiss FFT uses a time decimation, mixed-radix, out-of-place FFT. If you give it an input buffer 101 | and output buffer that are the same, a temporary buffer will be created to hold the data. 102 | 103 | No static data is used. The core routines of kiss_fft are thread-safe (but not all of the tools directory). 104 | 105 | No scaling is done for the floating point version (for speed). 106 | Scaling is done both ways for the fixed-point version (for overflow prevention). 107 | 108 | Optimized butterflies are used for factors 2,3,4, and 5. 109 | 110 | The real (i.e. not complex) optimization code only works for even length ffts. It does two half-length 111 | FFTs in parallel (packed into real&imag), and then combines them via twiddling. The result is 112 | nfft/2+1 complex frequency bins from DC to Nyquist. If you don't know what this means, search the web. 113 | 114 | The fast convolution filtering uses the overlap-scrap method, slightly 115 | modified to put the scrap at the tail. 116 | 117 | LICENSE: 118 | Revised BSD License, see COPYING for verbiage. 119 | Basically, "free to use&change, give credit where due, no guarantees" 120 | Note this license is compatible with GPL at one end of the spectrum and closed, commercial software at 121 | the other end. See http://www.fsf.org/licensing/licenses 122 | 123 | TODO: 124 | *) Add real optimization for odd length FFTs 125 | *) Document/revisit the input/output fft scaling 126 | *) Make doc describing the overlap (tail) scrap fast convolution filtering in kiss_fastfir.c 127 | *) Test all the ./tools/ code with fixed point (kiss_fastfir.c doesn't work, maybe others) 128 | 129 | AUTHOR: 130 | Mark Borgerding 131 | Mark@Borgerding.net 132 | -------------------------------------------------------------------------------- /lib/kissfft/README.md: -------------------------------------------------------------------------------- 1 | # KISS FFT [![Build Status](https://travis-ci.com/mborgerding/kissfft.svg?branch=master)](https://travis-ci.com/mborgerding/kissfft) 2 | 3 | KISS FFT - A mixed-radix Fast Fourier Transform based up on the principle, 4 | "Keep It Simple, Stupid." 5 | 6 | There are many great fft libraries already around. Kiss FFT is not trying 7 | to be better than any of them. It only attempts to be a reasonably efficient, 8 | moderately useful FFT that can use fixed or floating data types and can be 9 | incorporated into someone's C program in a few minutes with trivial licensing. 10 | 11 | ## USAGE: 12 | 13 | The basic usage for 1-d complex FFT is: 14 | 15 | ```c 16 | #include "kiss_fft.h" 17 | kiss_fft_cfg cfg = kiss_fft_alloc( nfft ,is_inverse_fft ,0,0 ); 18 | while ... 19 | 20 | ... // put kth sample in cx_in[k].r and cx_in[k].i 21 | 22 | kiss_fft( cfg , cx_in , cx_out ); 23 | 24 | ... // transformed. DC is in cx_out[0].r and cx_out[0].i 25 | 26 | kiss_fft_free(cfg); 27 | ``` 28 | - **Note**: frequency-domain data is stored from dc up to 2pi. 29 | so cx_out[0] is the dc bin of the FFT 30 | and cx_out[nfft/2] is the Nyquist bin (if exists) 31 | 32 | Declarations are in "kiss_fft.h", along with a brief description of the 33 | functions you'll need to use. 34 | 35 | Code definitions for 1d complex FFTs are in kiss_fft.c. 36 | 37 | You can do other cool stuff with the extras you'll find in tools/ 38 | > - multi-dimensional FFTs 39 | > - real-optimized FFTs (returns the positive half-spectrum: 40 | (nfft/2+1) complex frequency bins) 41 | > - fast convolution FIR filtering (not available for fixed point) 42 | > - spectrum image creation 43 | 44 | The core fft and most tools/ code can be compiled to use float, double, 45 | Q15 short or Q31 samples. The default is float. 46 | 47 | 48 | ## BACKGROUND 49 | 50 | I started coding this because I couldn't find a fixed point FFT that didn't 51 | use assembly code. I started with floating point numbers so I could get the 52 | theory straight before working on fixed point issues. In the end, I had a 53 | little bit of code that could be recompiled easily to do ffts with short, float 54 | or double (other types should be easy too). 55 | 56 | Once I got my FFT working, I was curious about the speed compared to 57 | a well respected and highly optimized fft library. I don't want to criticize 58 | this great library, so let's call it FFT_BRANDX. 59 | During this process, I learned: 60 | 61 | > 1. FFT_BRANDX has more than 100K lines of code. The core of kiss_fft is about 500 lines (cpx 1-d). 62 | > 2. It took me an embarrassingly long time to get FFT_BRANDX working. 63 | > 3. A simple program using FFT_BRANDX is 522KB. A similar program using kiss_fft is 18KB (without optimizing for size). 64 | > 4. FFT_BRANDX is roughly twice as fast as KISS FFT in default mode. 65 | 66 | It is wonderful that free, highly optimized libraries like FFT_BRANDX exist. 67 | But such libraries carry a huge burden of complexity necessary to extract every 68 | last bit of performance. 69 | 70 | **Sometimes simpler is better, even if it's not better.** 71 | 72 | ## FREQUENTLY ASKED QUESTIONS: 73 | > Q: Can I use kissfft in a project with a ___ license?
74 | > A: Yes. See LICENSE below. 75 | 76 | > Q: Why don't I get the output I expect?
77 | > A: The two most common causes of this are 78 | > 1) scaling : is there a constant multiplier between what you got and what you want? 79 | > 2) mixed build environment -- all code must be compiled with same preprocessor 80 | > definitions for FIXED_POINT and kiss_fft_scalar 81 | 82 | > Q: Will you write/debug my code for me?
83 | > A: Probably not unless you pay me. I am happy to answer pointed and topical questions, but 84 | > I may refer you to a book, a forum, or some other resource. 85 | 86 | 87 | ## PERFORMANCE 88 | (on Athlon XP 2100+, with gcc 2.96, float data type) 89 | 90 | Kiss performed 10000 1024-pt cpx ffts in .63 s of cpu time. 91 | For comparison, it took md5sum twice as long to process the same amount of data. 92 | Transforming 5 minutes of CD quality audio takes less than a second (nfft=1024). 93 | 94 | **DO NOT:** 95 | - use Kiss if you need the Fastest Fourier Transform in the World 96 | - ask me to add features that will bloat the code 97 | 98 | ## UNDER THE HOOD 99 | 100 | Kiss FFT uses a time decimation, mixed-radix, out-of-place FFT. If you give it an input buffer 101 | and output buffer that are the same, a temporary buffer will be created to hold the data. 102 | 103 | No static data is used. The core routines of kiss_fft are thread-safe (but not all of the tools directory).[ 104 | 105 | No scaling is done for the floating point version (for speed). 106 | Scaling is done both ways for the fixed-point version (for overflow prevention). 107 | 108 | Optimized butterflies are used for factors 2,3,4, and 5. 109 | 110 | The real (i.e. not complex) optimization code only works for even length ffts. It does two half-length 111 | FFTs in parallel (packed into real&imag), and then combines them via twiddling. The result is 112 | nfft/2+1 complex frequency bins from DC to Nyquist. If you don't know what this means, search the web. 113 | 114 | The fast convolution filtering uses the overlap-scrap method, slightly 115 | modified to put the scrap at the tail. 116 | 117 | ## LICENSE 118 | Revised BSD License, see COPYING for verbiage. 119 | Basically, "free to use&change, give credit where due, no guarantees" 120 | Note this license is compatible with GPL at one end of the spectrum and closed, commercial software at 121 | the other end. See http://www.fsf.org/licensing/licenses 122 | 123 | ## TODO 124 | - Add real optimization for odd length FFTs 125 | - Document/revisit the input/output fft scaling 126 | - Make doc describing the overlap (tail) scrap fast convolution filtering in kiss_fastfir.c 127 | - Test all the ./tools/ code with fixed point (kiss_fastfir.c doesn't work, maybe others) 128 | 129 | ## AUTHOR 130 | Mark Borgerding 131 | Mark@Borgerding.net 132 | -------------------------------------------------------------------------------- /lib/kissfft/tools/fftutil.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "kiss_fft.h" 16 | #include "kiss_fftndr.h" 17 | 18 | static 19 | void fft_file(FILE * fin,FILE * fout,int nfft,int isinverse) 20 | { 21 | kiss_fft_cfg st; 22 | kiss_fft_cpx * buf; 23 | kiss_fft_cpx * bufout; 24 | 25 | buf = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * nfft ); 26 | bufout = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * nfft ); 27 | st = kiss_fft_alloc( nfft ,isinverse ,0,0); 28 | 29 | while ( fread( buf , sizeof(kiss_fft_cpx) * nfft ,1, fin ) > 0 ) { 30 | kiss_fft( st , buf ,bufout); 31 | fwrite( bufout , sizeof(kiss_fft_cpx) , nfft , fout ); 32 | } 33 | free(st); 34 | free(buf); 35 | free(bufout); 36 | } 37 | 38 | static 39 | void fft_filend(FILE * fin,FILE * fout,int *dims,int ndims,int isinverse) 40 | { 41 | kiss_fftnd_cfg st; 42 | kiss_fft_cpx *buf; 43 | int dimprod=1,i; 44 | for (i=0;i 0) { 51 | kiss_fftnd (st, buf, buf); 52 | fwrite (buf, sizeof (kiss_fft_cpx), dimprod, fout); 53 | } 54 | free (st); 55 | free (buf); 56 | } 57 | 58 | 59 | 60 | static 61 | void fft_filend_real(FILE * fin,FILE * fout,int *dims,int ndims,int isinverse) 62 | { 63 | int dimprod=1,i; 64 | kiss_fftndr_cfg st; 65 | void *ibuf; 66 | void *obuf; 67 | int insize,outsize; // size in bytes 68 | 69 | for (i=0;i 0) { 85 | if (isinverse) { 86 | kiss_fftndri(st, 87 | (kiss_fft_cpx*)ibuf, 88 | (kiss_fft_scalar*)obuf); 89 | }else{ 90 | kiss_fftndr(st, 91 | (kiss_fft_scalar*)ibuf, 92 | (kiss_fft_cpx*)obuf); 93 | } 94 | fwrite (obuf, sizeof(kiss_fft_scalar), outsize,fout); 95 | } 96 | free(st); 97 | free(ibuf); 98 | free(obuf); 99 | } 100 | 101 | static 102 | void fft_file_real(FILE * fin,FILE * fout,int nfft,int isinverse) 103 | { 104 | kiss_fftr_cfg st; 105 | kiss_fft_scalar * rbuf; 106 | kiss_fft_cpx * cbuf; 107 | 108 | rbuf = (kiss_fft_scalar*)malloc(sizeof(kiss_fft_scalar) * nfft ); 109 | cbuf = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * (nfft/2+1) ); 110 | st = kiss_fftr_alloc( nfft ,isinverse ,0,0); 111 | 112 | if (isinverse==0) { 113 | while ( fread( rbuf , sizeof(kiss_fft_scalar) * nfft ,1, fin ) > 0 ) { 114 | kiss_fftr( st , rbuf ,cbuf); 115 | fwrite( cbuf , sizeof(kiss_fft_cpx) , (nfft/2 + 1) , fout ); 116 | } 117 | }else{ 118 | while ( fread( cbuf , sizeof(kiss_fft_cpx) * (nfft/2+1) ,1, fin ) > 0 ) { 119 | kiss_fftri( st , cbuf ,rbuf); 120 | fwrite( rbuf , sizeof(kiss_fft_scalar) , nfft , fout ); 121 | } 122 | } 123 | free(st); 124 | free(rbuf); 125 | free(cbuf); 126 | } 127 | 128 | static 129 | int get_dims(char * arg,int * dims) 130 | { 131 | char *p0; 132 | int ndims=0; 133 | 134 | do{ 135 | p0 = strchr(arg,','); 136 | if (p0) 137 | *p0++ = '\0'; 138 | dims[ndims++] = atoi(arg); 139 | // fprintf(stderr,"dims[%d] = %d\n",ndims-1,dims[ndims-1]); 140 | arg = p0; 141 | }while (p0); 142 | return ndims; 143 | } 144 | 145 | int main(int argc,char ** argv) 146 | { 147 | int isinverse=0; 148 | int isreal=0; 149 | FILE *fin=stdin; 150 | FILE *fout=stdout; 151 | int ndims=1; 152 | int dims[32]; 153 | dims[0] = 1024; /*default fft size*/ 154 | 155 | while (1) { 156 | int c=getopt(argc,argv,"n:iR"); 157 | if (c==-1) break; 158 | switch (c) { 159 | case 'n': 160 | ndims = get_dims(optarg,dims); 161 | break; 162 | case 'i':isinverse=1;break; 163 | case 'R':isreal=1;break; 164 | case '?': 165 | fprintf(stderr,"usage options:\n" 166 | "\t-n d1[,d2,d3...]: fft dimension(s)\n" 167 | "\t-i : inverse\n" 168 | "\t-R : real input samples, not complex\n"); 169 | exit (1); 170 | default:fprintf(stderr,"bad %c\n",c);break; 171 | } 172 | } 173 | 174 | if ( optind < argc ) { 175 | if (strcmp("-",argv[optind]) !=0) 176 | fin = fopen(argv[optind],"rb"); 177 | ++optind; 178 | } 179 | 180 | if ( optind < argc ) { 181 | if ( strcmp("-",argv[optind]) !=0 ) 182 | fout = fopen(argv[optind],"wb"); 183 | ++optind; 184 | } 185 | 186 | if (ndims==1) { 187 | if (isreal) 188 | fft_file_real(fin,fout,dims[0],isinverse); 189 | else 190 | fft_file(fin,fout,dims[0],isinverse); 191 | }else{ 192 | if (isreal) 193 | fft_filend_real(fin,fout,dims,ndims,isinverse); 194 | else 195 | fft_filend(fin,fout,dims,ndims,isinverse); 196 | } 197 | 198 | if (fout!=stdout) fclose(fout); 199 | if (fin!=stdin) fclose(fin); 200 | 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kiss_fftnd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #include "kiss_fftnd.h" 10 | #include "_kiss_fft_guts.h" 11 | 12 | struct kiss_fftnd_state{ 13 | int dimprod; /* dimsum would be mighty tasty right now */ 14 | int ndims; 15 | int *dims; 16 | kiss_fft_cfg *states; /* cfg states for each dimension */ 17 | kiss_fft_cpx * tmpbuf; /*buffer capable of hold the entire input */ 18 | }; 19 | 20 | kiss_fftnd_cfg kiss_fftnd_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem) 21 | { 22 | KISS_FFT_ALIGN_CHECK(mem) 23 | 24 | kiss_fftnd_cfg st = NULL; 25 | int i; 26 | int dimprod=1; 27 | size_t memneeded = KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fftnd_state)); 28 | char * ptr = NULL; 29 | 30 | for (i=0;istates[i] */ 34 | dimprod *= dims[i]; 35 | } 36 | memneeded += KISS_FFT_ALIGN_SIZE_UP(sizeof(int) * ndims);/* st->dims */ 37 | memneeded += KISS_FFT_ALIGN_SIZE_UP(sizeof(void*) * ndims);/* st->states */ 38 | memneeded += KISS_FFT_ALIGN_SIZE_UP(sizeof(kiss_fft_cpx) * dimprod); /* st->tmpbuf */ 39 | 40 | if (lenmem == NULL) {/* allocate for the caller*/ 41 | ptr = (char *) malloc (memneeded); 42 | } else { /* initialize supplied buffer if big enough */ 43 | if (*lenmem >= memneeded) 44 | ptr = (char *) mem; 45 | *lenmem = memneeded; /*tell caller how big struct is (or would be) */ 46 | } 47 | if (!ptr) 48 | return NULL; /*malloc failed or buffer too small */ 49 | 50 | st = (kiss_fftnd_cfg) ptr; 51 | st->dimprod = dimprod; 52 | st->ndims = ndims; 53 | ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fftnd_state)); 54 | 55 | st->states = (kiss_fft_cfg *)ptr; 56 | ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(void*) * ndims); 57 | 58 | st->dims = (int*)ptr; 59 | ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(int) * ndims); 60 | 61 | st->tmpbuf = (kiss_fft_cpx*)ptr; 62 | ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(kiss_fft_cpx) * dimprod); 63 | 64 | for (i=0;idims[i] = dims[i]; 67 | kiss_fft_alloc (st->dims[i], inverse_fft, NULL, &len); 68 | st->states[i] = kiss_fft_alloc (st->dims[i], inverse_fft, ptr,&len); 69 | ptr += len; 70 | } 71 | /* 72 | Hi there! 73 | 74 | If you're looking at this particular code, it probably means you've got a brain-dead bounds checker 75 | that thinks the above code overwrites the end of the array. 76 | 77 | It doesn't. 78 | 79 | -- Mark 80 | 81 | P.S. 82 | The below code might give you some warm fuzzies and help convince you. 83 | */ 84 | if ( ptr - (char*)st != (int)memneeded ) { 85 | fprintf(stderr, 86 | "################################################################################\n" 87 | "Internal error! Memory allocation miscalculation\n" 88 | "################################################################################\n" 89 | ); 90 | } 91 | return st; 92 | } 93 | 94 | /* 95 | This works by tackling one dimension at a time. 96 | 97 | In effect, 98 | Each stage starts out by reshaping the matrix into a DixSi 2d matrix. 99 | A Di-sized fft is taken of each column, transposing the matrix as it goes. 100 | 101 | Here's a 3-d example: 102 | Take a 2x3x4 matrix, laid out in memory as a contiguous buffer 103 | [ [ [ a b c d ] [ e f g h ] [ i j k l ] ] 104 | [ [ m n o p ] [ q r s t ] [ u v w x ] ] ] 105 | 106 | Stage 0 ( D=2): treat the buffer as a 2x12 matrix 107 | [ [a b ... k l] 108 | [m n ... w x] ] 109 | 110 | FFT each column with size 2. 111 | Transpose the matrix at the same time using kiss_fft_stride. 112 | 113 | [ [ a+m a-m ] 114 | [ b+n b-n] 115 | ... 116 | [ k+w k-w ] 117 | [ l+x l-x ] ] 118 | 119 | Note fft([x y]) == [x+y x-y] 120 | 121 | Stage 1 ( D=3) treats the buffer (the output of stage D=2) as an 3x8 matrix, 122 | [ [ a+m a-m b+n b-n c+o c-o d+p d-p ] 123 | [ e+q e-q f+r f-r g+s g-s h+t h-t ] 124 | [ i+u i-u j+v j-v k+w k-w l+x l-x ] ] 125 | 126 | And perform FFTs (size=3) on each of the columns as above, transposing 127 | the matrix as it goes. The output of stage 1 is 128 | (Legend: ap = [ a+m e+q i+u ] 129 | am = [ a-m e-q i-u ] ) 130 | 131 | [ [ sum(ap) fft(ap)[0] fft(ap)[1] ] 132 | [ sum(am) fft(am)[0] fft(am)[1] ] 133 | [ sum(bp) fft(bp)[0] fft(bp)[1] ] 134 | [ sum(bm) fft(bm)[0] fft(bm)[1] ] 135 | [ sum(cp) fft(cp)[0] fft(cp)[1] ] 136 | [ sum(cm) fft(cm)[0] fft(cm)[1] ] 137 | [ sum(dp) fft(dp)[0] fft(dp)[1] ] 138 | [ sum(dm) fft(dm)[0] fft(dm)[1] ] ] 139 | 140 | Stage 2 ( D=4) treats this buffer as a 4*6 matrix, 141 | [ [ sum(ap) fft(ap)[0] fft(ap)[1] sum(am) fft(am)[0] fft(am)[1] ] 142 | [ sum(bp) fft(bp)[0] fft(bp)[1] sum(bm) fft(bm)[0] fft(bm)[1] ] 143 | [ sum(cp) fft(cp)[0] fft(cp)[1] sum(cm) fft(cm)[0] fft(cm)[1] ] 144 | [ sum(dp) fft(dp)[0] fft(dp)[1] sum(dm) fft(dm)[0] fft(dm)[1] ] ] 145 | 146 | Then FFTs each column, transposing as it goes. 147 | 148 | The resulting matrix is the 3d FFT of the 2x3x4 input matrix. 149 | 150 | Note as a sanity check that the first element of the final 151 | stage's output (DC term) is 152 | sum( [ sum(ap) sum(bp) sum(cp) sum(dp) ] ) 153 | , i.e. the summation of all 24 input elements. 154 | 155 | */ 156 | void kiss_fftnd(kiss_fftnd_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) 157 | { 158 | int i,k; 159 | const kiss_fft_cpx * bufin=fin; 160 | kiss_fft_cpx * bufout; 161 | 162 | /*arrange it so the last bufout == fout*/ 163 | if ( st->ndims & 1 ) { 164 | bufout = fout; 165 | if (fin==fout) { 166 | memcpy( st->tmpbuf, fin, sizeof(kiss_fft_cpx) * st->dimprod ); 167 | bufin = st->tmpbuf; 168 | } 169 | }else 170 | bufout = st->tmpbuf; 171 | 172 | for ( k=0; k < st->ndims; ++k) { 173 | int curdim = st->dims[k]; 174 | int stride = st->dimprod / curdim; 175 | 176 | for ( i=0 ; istates[k], bufin+i , bufout+i*curdim, stride ); 178 | 179 | /*toggle back and forth between the two buffers*/ 180 | if (bufout == st->tmpbuf){ 181 | bufout = fout; 182 | bufin = st->tmpbuf; 183 | }else{ 184 | bufout = st->tmpbuf; 185 | bufin = fout; 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | 2 | # Configuration of clang-format 3 | # ============================= 4 | # 5 | # Tested to work with versions: 8 to 11. 6 | 7 | # This causes parameters on continuations to align to the opening brace. 8 | # 9 | # like_this_long_name(parameter_one, 10 | # parameter_two, 11 | # parameter_three); 12 | # 13 | AlignAfterOpenBracket: 'Align' 14 | 15 | # Disallow short functions on one line; break them up. 16 | AllowShortBlocksOnASingleLine: false 17 | 18 | # These two settings trigger stacking of parameters in most cases; this is 19 | # easier to read and also makes diffs easier to read (since an added or removed 20 | # parameter is obvious). For example, function calls will look like this: 21 | # 22 | # like_this_long_name(parameter_one, 23 | # parameter_two, 24 | # parameter_three, 25 | # parameter_four, 26 | # parameter_five, 27 | # parameter_six); 28 | # 29 | # Instead of: 30 | # 31 | # like_this_long_name(parameter_one, parameter_two, parameter_three, parameter_four, 32 | # parameter_five, parameter_six); 33 | # 34 | BinPackArguments: false 35 | BinPackParameters: false 36 | 37 | # Line width (don't exceed 100). 38 | ColumnLimit: 99 39 | 40 | # Cause initializer lists to have one member initialized per line, in the case 41 | # that all initializers can't fit on a single line. 42 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 43 | 44 | # Indent the : after a constructor. For example: 45 | # 46 | # explicit foo_class () 47 | # : member1_(5) 48 | # { 49 | # } 50 | # 51 | ConstructorInitializerIndentWidth: 4 52 | 53 | # Make access modifier slightly more visible. 54 | AccessModifierOffset: -1 55 | 56 | # This will unfortunately use spaces in some cases where it's not desired (like 57 | # function calls) but the overall result is better since it will allow 58 | # alignment to work properly with different tab width settings. 59 | ContinuationIndentWidth: 4 60 | 61 | # This tries to match Blender's style as much as possible. One 62 | BreakBeforeBraces: Custom 63 | BraceWrapping: { 64 | AfterClass: 'false', 65 | AfterControlStatement: 'MultiLine', 66 | AfterEnum : 'false', 67 | AfterFunction : 'true', 68 | AfterNamespace : 'false', 69 | AfterStruct : 'false', 70 | AfterUnion : 'false', 71 | BeforeCatch : 'true', 72 | BeforeElse : 'true', 73 | IndentBraces : 'false', 74 | AfterObjCDeclaration: 'true', 75 | } 76 | 77 | # For switch statements, indent the cases. 78 | IndentCaseLabels: true 79 | 80 | # Indent after the hash inside preprocessor directives 81 | IndentPPDirectives: AfterHash 82 | 83 | BreakBeforeTernaryOperators: false 84 | 85 | SpaceAfterTemplateKeyword: false 86 | 87 | # Handy comment at the end of each C++ name space. 88 | FixNamespaceComments: true 89 | 90 | # Use "if (...)" instead of "if(...)", but have function calls like foo(). 91 | SpaceBeforeParens: ControlStatements 92 | SpaceInEmptyParentheses: false 93 | 94 | # Use two spaces before trailing comments, for example 95 | # 96 | # foo = bar; // comment 97 | # 98 | # Note that this doesn't work for C-style comments. 99 | SpacesBeforeTrailingComments: 2 100 | 101 | # Reflow comments, developers must disable formatting as with code to override this. 102 | ReflowComments: true 103 | 104 | # Never use tabs for indentation. 105 | # Note: TabWidth and IndentWidth must be the same, or strange things happen. 106 | UseTab: Never 107 | TabWidth: 2 108 | IndentWidth: 2 109 | 110 | # Add a big penalty on breaking after the return type of functions. For example, 111 | # 112 | # static void foo(...) 113 | # 114 | # Instead of: 115 | # 116 | # static void 117 | # foo(very long content here that maybe could be stacked) 118 | # 119 | PenaltyReturnTypeOnItsOwnLine: 10000 120 | 121 | # Avoid having function calls broken onto a new line: 122 | # 123 | # int a = foo( 124 | # long, list, of, many, params); 125 | # 126 | # Instead of: 127 | # 128 | # int a = 129 | # foo(long, list, of, many, params); 130 | # 131 | PenaltyBreakAssignment: 100 132 | 133 | AllowShortFunctionsOnASingleLine: Empty 134 | 135 | SortIncludes: true 136 | 137 | # Don't right align escaped newlines to the right because we have a wide default 138 | AlignEscapedNewlines: DontAlign 139 | 140 | # Always break: 141 | # 142 | # const char *foo = 143 | # "multi" 144 | # "line"; 145 | # 146 | # Instead of: 147 | # 148 | # const char *foo = "multi" 149 | # "line"; 150 | # 151 | AlwaysBreakBeforeMultilineStrings: true 152 | 153 | # We don't want literal strings to break, 154 | # however clang-format seems to ignore this (sigh). 155 | PenaltyBreakString: 1000000 156 | 157 | # There are macros in Blender for custom for loops; tell Clang to treat them 158 | # like loops rather than an expression, and so put the { on the same line. 159 | # 160 | # To find these use multi-line regex search: 161 | # "^\s+[A-Z][A-Z0-9_]+\s*\([^\n]*\)\n\s*\{" 162 | ForEachMacros: 163 | - BEGIN_ANIMFILTER_SUBCHANNELS 164 | - BKE_pbvh_vertex_iter_begin 165 | - BKE_pbvh_face_iter_begin 166 | - BLI_FOREACH_SPARSE_RANGE 167 | - BLI_SMALLSTACK_ITER_BEGIN 168 | - BMO_ITER 169 | - BMO_ITER_INDEX 170 | - BMW_ITER 171 | - BM_FACES_OF_VERT_ITER_BEGIN 172 | - BM_ITER_BPY_BM_SEQ 173 | - BM_ITER_ELEM 174 | - BM_ITER_ELEM_INDEX 175 | - BM_ITER_MESH 176 | - BM_ITER_MESH_INDEX 177 | - BM_ITER_MESH_MUTABLE 178 | - BM_LOOPS_OF_VERT_ITER_BEGIN 179 | - BOOST_FOREACH 180 | - CTX_DATA_BEGIN 181 | - CTX_DATA_BEGIN_WITH_ID 182 | - DEG_OBJECT_ITER_BEGIN 183 | - DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN 184 | - DRW_ENABLED_ENGINE_ITER 185 | - DRIVER_TARGETS_LOOPER_BEGIN 186 | - DRIVER_TARGETS_USED_LOOPER_BEGIN 187 | - FOREACH_BASE_IN_EDIT_MODE_BEGIN 188 | - FOREACH_BASE_IN_MODE_BEGIN 189 | - FOREACH_BEGIN 190 | - FOREACH_COLLECTION_BEGIN 191 | - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN 192 | - FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN 193 | - FOREACH_EDIT_OBJECT_BEGIN 194 | - FOREACH_MAIN_ID_BEGIN 195 | - FOREACH_MAIN_LISTBASE_BEGIN 196 | - FOREACH_MAIN_LISTBASE_ID_BEGIN 197 | - FOREACH_MESH_BUFFER_CACHE 198 | - FOREACH_NODETREE_BEGIN 199 | - FOREACH_OBJECT_BEGIN 200 | - FOREACH_OBJECT_FLAG_BEGIN 201 | - FOREACH_OBJECT_IN_EDIT_MODE_BEGIN 202 | - FOREACH_OBJECT_IN_MODE_BEGIN 203 | - FOREACH_OBJECT_RENDERABLE_BEGIN 204 | - FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN 205 | - FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN 206 | - FOREACH_SCENE_COLLECTION_BEGIN 207 | - FOREACH_SCENE_OBJECT_BEGIN 208 | - FOREACH_SELECTED_BASE_BEGIN 209 | - FOREACH_SELECTED_BEZT_BEGIN 210 | - FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN 211 | - FOREACH_SELECTED_OBJECT_BEGIN 212 | - FOREACH_TRANS_DATA_CONTAINER 213 | - FOREACH_VIEW_LAYER_TO_RENDER_BEGIN 214 | - FOREACH_VISIBLE_BASE_BEGIN 215 | - FOREACH_VISIBLE_OBJECT_BEGIN 216 | - GHASH_FOREACH_BEGIN 217 | - GHASH_ITER 218 | - GHASH_ITER_INDEX 219 | - GPU_SELECT_LOAD_IF_PICKSEL_LIST 220 | - GP_EDITABLE_STROKES_BEGIN 221 | - GP_EVALUATED_STROKES_BEGIN 222 | - GSET_FOREACH_BEGIN 223 | - GSET_ITER 224 | - GSET_ITER_INDEX 225 | - ITER_BEGIN 226 | - ITER_PIXELS 227 | - ITER_SLOTS 228 | - ITER_SLOTS_BEGIN 229 | - LOOP_EDITED_POINTS 230 | - LOOP_KEYS 231 | - LOOP_POINTS 232 | - LOOP_SELECTED_KEYS 233 | - LOOP_SELECTED_POINTS 234 | - LOOP_TAGGED_KEYS 235 | - LOOP_TAGGED_POINTS 236 | - LOOP_UNSELECTED_POINTS 237 | - LOOP_VISIBLE_KEYS 238 | - LOOP_VISIBLE_POINTS 239 | - LIGHT_FOREACH_BEGIN_DIRECTIONAL 240 | - LIGHT_FOREACH_BEGIN_LOCAL 241 | - LISTBASE_CIRCULAR_BACKWARD_BEGIN 242 | - LISTBASE_CIRCULAR_FORWARD_BEGIN 243 | - LISTBASE_FOREACH 244 | - LISTBASE_FOREACH_BACKWARD 245 | - LISTBASE_FOREACH_MUTABLE 246 | - LISTBASE_FOREACH_BACKWARD_MUTABLE 247 | - LISTBASE_FOREACH_INDEX 248 | - MAN_ITER_AXES_BEGIN 249 | - NODE_INSTANCE_HASH_ITER 250 | - NODE_SOCKET_TYPES_BEGIN 251 | - NODE_TREE_TYPES_BEGIN 252 | - NODE_TYPES_BEGIN 253 | - PIXEL_LOOPER_BEGIN 254 | - PIXEL_LOOPER_BEGIN_CHANNELS 255 | - RENDER_PASS_ITER_BEGIN 256 | - RNA_BEGIN 257 | - RNA_PROP_BEGIN 258 | - RNA_STRUCT_BEGIN 259 | - RNA_STRUCT_BEGIN_SKIP_RNA_TYPE 260 | - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN 261 | - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN 262 | - SEQ_ALL_BEGIN 263 | - SEQ_ITERATOR_FOREACH 264 | - SURFACE_QUAD_ITER_BEGIN 265 | - foreach 266 | - ED_screen_areas_iter 267 | - SLOT_PROBING_BEGIN 268 | - SET_SLOT_PROBING_BEGIN 269 | - MAP_SLOT_PROBING_BEGIN 270 | - VECTOR_SET_SLOT_PROBING_BEGIN 271 | - WL_ARRAY_FOR_EACH 272 | - FOREACH_SPECTRUM_CHANNEL 273 | 274 | StatementMacros: 275 | - PyObject_HEAD 276 | - PyObject_VAR_HEAD 277 | - ccl_gpu_kernel_postfix 278 | 279 | MacroBlockBegin: "^OSL_CLOSURE_STRUCT_BEGIN$" 280 | MacroBlockEnd: "^OSL_CLOSURE_STRUCT_END$" 281 | -------------------------------------------------------------------------------- /lib/kissfft/kissfft_i32.hh: -------------------------------------------------------------------------------- 1 | #ifndef KISSFFT_I32_CLASS_HH 2 | #define KISSFFT_I32_CLASS_HH 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // TODO1: substitute complex (behaviour not defined for nonfloats), should be faster 9 | // TODO2: use std:: namespace 10 | // TODO3: make unittests for all ffts (c, cpp, i32) 11 | 12 | template 13 | struct complex_s 14 | { 15 | DType real; 16 | DType imag; 17 | }; 18 | 19 | class kissfft_i32 20 | { 21 | private: 22 | 23 | using scalar_type = int32_t; 24 | using cpx_type = complex; 25 | 26 | scalar_type _scale_factor; 27 | std::size_t _nfft; 28 | bool _inverse; 29 | std::vector _twiddles; 30 | std::vector _stageRadix; 31 | std::vector _stageRemainder; 32 | 33 | public: 34 | 35 | // scale_factor: upscale twiddle-factors otherwise they lie between 0..1 (out of range for integer) --> fixed point math 36 | kissfft_i32(const std::size_t nfft, const bool inverse, const double scale_factor = 1024.0) 37 | : _scale_factor(scalar_type(scale_factor)), _nfft(nfft), _inverse(inverse) 38 | { 39 | // fill twiddle factors 40 | _twiddles.resize(_nfft); 41 | const double phinc = (_inverse ? 2 : -2) * acos(-1.0) / _nfft; 42 | for (std::size_t i = 0; i < _nfft; ++i) 43 | { 44 | _twiddles[i] = scale_factor * exp(complex(0, i * phinc)); 45 | } 46 | //factorize 47 | //start factoring out 4's, then 2's, then 3,5,7,9,... 48 | std::size_t n = _nfft; 49 | std::size_t p = 4; 50 | do 51 | { 52 | while (n % p) 53 | { 54 | switch (p) 55 | { 56 | case 4: 57 | p = 2; 58 | break; 59 | case 2: 60 | p = 3; 61 | break; 62 | default: 63 | p += 2; 64 | break; 65 | } 66 | if (p * p > n) p = n;// no more factors 67 | } 68 | n /= p; 69 | _stageRadix.push_back(p); 70 | _stageRemainder.push_back(n); 71 | } while (n > 1); 72 | } 73 | 74 | /// Calculates the complex Discrete Fourier Transform. 75 | /// 76 | /// The size of the passed arrays must be passed in the constructor. 77 | /// The sum of the squares of the absolute values in the @c dst 78 | /// array will be @c N times the sum of the squares of the absolute 79 | /// values in the @c src array, where @c N is the size of the array. 80 | /// In other words, the l_2 norm of the resulting array will be 81 | /// @c sqrt(N) times as big as the l_2 norm of the input array. 82 | /// This is also the case when the inverse flag is set in the 83 | /// constructor. Hence when applying the same transform twice, but with 84 | /// the inverse flag changed the second time, then the result will 85 | /// be equal to the original input times @c N. 86 | void transform(const cpx_type * FSrc, 87 | cpx_type * FDst, 88 | const std::size_t stage = 0, 89 | const std::size_t fstride = 1, 90 | const std::size_t in_stride = 1) const 91 | { 92 | const std::size_t p = _stageRadix[stage]; 93 | const std::size_t m = _stageRemainder[stage]; 94 | cpx_type *const Fout_beg = FDst; 95 | cpx_type *const Fout_end = FDst + p * m; 96 | 97 | if (m == 1) 98 | { 99 | do 100 | { 101 | *FDst = *FSrc; 102 | FSrc += fstride * in_stride; 103 | } while (++FDst != Fout_end); 104 | } 105 | else 106 | { 107 | do 108 | { 109 | // recursive call: 110 | // DFT of size m*p performed by doing 111 | // p instances of smaller DFTs of size m, 112 | // each one takes a decimated version of the input 113 | transform(FSrc, FDst, stage + 1, fstride * p, in_stride); 114 | FSrc += fstride * in_stride; 115 | } while ((FDst += m) != Fout_end); 116 | } 117 | 118 | FDst = Fout_beg; 119 | 120 | // recombine the p smaller DFTs 121 | switch (p) 122 | { 123 | case 2: 124 | kf_bfly2(FDst, fstride, m); 125 | break; 126 | case 3: 127 | kf_bfly3(FDst, fstride, m); 128 | break; 129 | case 4: 130 | kf_bfly4(FDst, fstride, m); 131 | break; 132 | case 5: 133 | kf_bfly5(FDst, fstride, m); 134 | break; 135 | default: 136 | kf_bfly_generic(FDst, fstride, m, p); 137 | break; 138 | } 139 | } 140 | 141 | private: 142 | 143 | void kf_bfly2(cpx_type *const Fout, const size_t fstride, const std::size_t m) const 144 | { 145 | for (std::size_t k = 0; k < m; ++k) 146 | { 147 | const cpx_type t = (Fout[m + k] * _twiddles[k * fstride]) / _scale_factor; 148 | Fout[m + k] = Fout[k] - t; 149 | Fout[k] += t; 150 | } 151 | } 152 | 153 | void kf_bfly3(cpx_type *Fout, const std::size_t fstride, const std::size_t m) const 154 | { 155 | std::size_t k = m; 156 | const std::size_t m2 = 2 * m; 157 | const cpx_type *tw1, *tw2; 158 | cpx_type scratch[5]; 159 | const cpx_type epi3 = _twiddles[fstride * m]; 160 | 161 | tw1 = tw2 = &_twiddles[0]; 162 | 163 | do 164 | { 165 | scratch[1] = (Fout[m] * *tw1) / _scale_factor; 166 | scratch[2] = (Fout[m2] * *tw2) / _scale_factor; 167 | 168 | scratch[3] = scratch[1] + scratch[2]; 169 | scratch[0] = scratch[1] - scratch[2]; 170 | tw1 += fstride; 171 | tw2 += fstride * 2; 172 | 173 | Fout[m] = Fout[0] - (scratch[3] / 2); 174 | scratch[0] *= epi3.imag(); 175 | scratch[0] /= _scale_factor; 176 | 177 | Fout[0] += scratch[3]; 178 | 179 | Fout[m2] = cpx_type(Fout[m].real() + scratch[0].imag(), Fout[m].imag() - scratch[0].real()); 180 | 181 | Fout[m] += cpx_type(-scratch[0].imag(), scratch[0].real()); 182 | ++Fout; 183 | } while (--k); 184 | } 185 | 186 | void kf_bfly4(cpx_type *const Fout, const std::size_t fstride, const std::size_t m) const 187 | { 188 | cpx_type scratch[7]; 189 | const scalar_type negative_if_inverse = _inverse ? -1 : +1; 190 | 191 | for (std::size_t k = 0; k < m; ++k) 192 | { 193 | scratch[0] = (Fout[k + m] * _twiddles[k * fstride]) / _scale_factor; 194 | scratch[1] = (Fout[k + 2 * m] * _twiddles[k * fstride * 2]) / _scale_factor; 195 | scratch[2] = (Fout[k + 3 * m] * _twiddles[k * fstride * 3]) / _scale_factor; 196 | scratch[5] = Fout[k] - scratch[1]; 197 | 198 | Fout[k] += scratch[1]; 199 | scratch[3] = scratch[0] + scratch[2]; 200 | scratch[4] = scratch[0] - scratch[2]; 201 | scratch[4] = cpx_type(scratch[4].imag() * negative_if_inverse, 202 | -scratch[4].real() * negative_if_inverse); 203 | 204 | Fout[k + 2 * m] = Fout[k] - scratch[3]; 205 | Fout[k] += scratch[3]; 206 | Fout[k + m] = scratch[5] + scratch[4]; 207 | Fout[k + 3 * m] = scratch[5] - scratch[4]; 208 | } 209 | } 210 | 211 | void kf_bfly5(cpx_type *const Fout, const std::size_t fstride, const std::size_t m) const 212 | { 213 | cpx_type *Fout0, *Fout1, *Fout2, *Fout3, *Fout4; 214 | cpx_type scratch[13]; 215 | const cpx_type ya = _twiddles[fstride * m]; 216 | const cpx_type yb = _twiddles[fstride * 2 * m]; 217 | 218 | Fout0 = Fout; 219 | Fout1 = Fout0 + m; 220 | Fout2 = Fout0 + 2 * m; 221 | Fout3 = Fout0 + 3 * m; 222 | Fout4 = Fout0 + 4 * m; 223 | 224 | for (std::size_t u = 0; u < m; ++u) 225 | { 226 | scratch[0] = *Fout0; 227 | 228 | scratch[1] = (*Fout1 * _twiddles[u * fstride]) / _scale_factor; 229 | scratch[2] = (*Fout2 * _twiddles[2 * u * fstride]) / _scale_factor; 230 | scratch[3] = (*Fout3 * _twiddles[3 * u * fstride]) / _scale_factor; 231 | scratch[4] = (*Fout4 * _twiddles[4 * u * fstride]) / _scale_factor; 232 | 233 | scratch[7] = scratch[1] + scratch[4]; 234 | scratch[10] = scratch[1] - scratch[4]; 235 | scratch[8] = scratch[2] + scratch[3]; 236 | scratch[9] = scratch[2] - scratch[3]; 237 | 238 | *Fout0 += scratch[7]; 239 | *Fout0 += scratch[8]; 240 | 241 | scratch[5] = scratch[0] + (cpx_type( 242 | scratch[7].real() * ya.real() + scratch[8].real() * yb.real(), 243 | scratch[7].imag() * ya.real() + scratch[8].imag() * yb.real() ) / _scale_factor); 244 | 245 | scratch[6] = cpx_type( 246 | scratch[10].imag() * ya.imag() + scratch[9].imag() * yb.imag(), 247 | -scratch[10].real() * ya.imag() - scratch[9].real() * yb.imag() ) / _scale_factor; 248 | 249 | *Fout1 = scratch[5] - scratch[6]; 250 | *Fout4 = scratch[5] + scratch[6]; 251 | 252 | scratch[11] = scratch[0] + (cpx_type( 253 | scratch[7].real() * yb.real() + scratch[8].real() * ya.real(), 254 | scratch[7].imag() * yb.real() + scratch[8].imag() * ya.real() ) / _scale_factor); 255 | 256 | scratch[12] = cpx_type( 257 | -scratch[10].imag() * yb.imag() + scratch[9].imag() * ya.imag(), 258 | scratch[10].real() * yb.imag() - scratch[9].real() * ya.imag() ) / _scale_factor; 259 | 260 | *Fout2 = scratch[11] + scratch[12]; 261 | *Fout3 = scratch[11] - scratch[12]; 262 | 263 | ++Fout0; 264 | ++Fout1; 265 | ++Fout2; 266 | ++Fout3; 267 | ++Fout4; 268 | } 269 | } 270 | 271 | /* perform the butterfly for one stage of a mixed radix FFT */ 272 | void kf_bfly_generic(cpx_type * const Fout, const size_t fstride, const std::size_t m, const std::size_t p) const 273 | { 274 | const cpx_type *twiddles = &_twiddles[0]; 275 | cpx_type scratchbuf[p]; 276 | 277 | for (std::size_t u = 0; u < m; ++u) 278 | { 279 | std::size_t k = u; 280 | for (std::size_t q1 = 0; q1 < p; ++q1) 281 | { 282 | scratchbuf[q1] = Fout[k]; 283 | k += m; 284 | } 285 | 286 | k = u; 287 | for (std::size_t q1 = 0; q1 < p; ++q1) 288 | { 289 | std::size_t twidx = 0; 290 | Fout[k] = scratchbuf[0]; 291 | for (std::size_t q = 1; q < p; ++q) 292 | { 293 | twidx += fstride * k; 294 | if (twidx >= _nfft) 295 | twidx -= _nfft; 296 | Fout[k] += (scratchbuf[q] * twiddles[twidx]) / _scale_factor; 297 | } 298 | k += m; 299 | } 300 | } 301 | } 302 | }; 303 | 304 | #endif 305 | -------------------------------------------------------------------------------- /lib/kissfft/kiss_fft.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | 10 | #include "_kiss_fft_guts.h" 11 | /* The guts header contains all the multiplication and addition macros that are defined for 12 | fixed or floating point complex numbers. It also delares the kf_ internal functions. 13 | */ 14 | 15 | static void kf_bfly2( 16 | kiss_fft_cpx * Fout, 17 | const size_t fstride, 18 | const kiss_fft_cfg st, 19 | int m 20 | ) 21 | { 22 | kiss_fft_cpx * Fout2; 23 | kiss_fft_cpx * tw1 = st->twiddles; 24 | kiss_fft_cpx t; 25 | Fout2 = Fout + m; 26 | do{ 27 | C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); 28 | 29 | C_MUL (t, *Fout2 , *tw1); 30 | tw1 += fstride; 31 | C_SUB( *Fout2 , *Fout , t ); 32 | C_ADDTO( *Fout , t ); 33 | ++Fout2; 34 | ++Fout; 35 | }while (--m); 36 | } 37 | 38 | static void kf_bfly4( 39 | kiss_fft_cpx * Fout, 40 | const size_t fstride, 41 | const kiss_fft_cfg st, 42 | const size_t m 43 | ) 44 | { 45 | kiss_fft_cpx *tw1,*tw2,*tw3; 46 | kiss_fft_cpx scratch[6]; 47 | size_t k=m; 48 | const size_t m2=2*m; 49 | const size_t m3=3*m; 50 | 51 | 52 | tw3 = tw2 = tw1 = st->twiddles; 53 | 54 | do { 55 | C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); 56 | 57 | C_MUL(scratch[0],Fout[m] , *tw1 ); 58 | C_MUL(scratch[1],Fout[m2] , *tw2 ); 59 | C_MUL(scratch[2],Fout[m3] , *tw3 ); 60 | 61 | C_SUB( scratch[5] , *Fout, scratch[1] ); 62 | C_ADDTO(*Fout, scratch[1]); 63 | C_ADD( scratch[3] , scratch[0] , scratch[2] ); 64 | C_SUB( scratch[4] , scratch[0] , scratch[2] ); 65 | C_SUB( Fout[m2], *Fout, scratch[3] ); 66 | tw1 += fstride; 67 | tw2 += fstride*2; 68 | tw3 += fstride*3; 69 | C_ADDTO( *Fout , scratch[3] ); 70 | 71 | if(st->inverse) { 72 | Fout[m].r = scratch[5].r - scratch[4].i; 73 | Fout[m].i = scratch[5].i + scratch[4].r; 74 | Fout[m3].r = scratch[5].r + scratch[4].i; 75 | Fout[m3].i = scratch[5].i - scratch[4].r; 76 | }else{ 77 | Fout[m].r = scratch[5].r + scratch[4].i; 78 | Fout[m].i = scratch[5].i - scratch[4].r; 79 | Fout[m3].r = scratch[5].r - scratch[4].i; 80 | Fout[m3].i = scratch[5].i + scratch[4].r; 81 | } 82 | ++Fout; 83 | }while(--k); 84 | } 85 | 86 | static void kf_bfly3( 87 | kiss_fft_cpx * Fout, 88 | const size_t fstride, 89 | const kiss_fft_cfg st, 90 | size_t m 91 | ) 92 | { 93 | size_t k=m; 94 | const size_t m2 = 2*m; 95 | kiss_fft_cpx *tw1,*tw2; 96 | kiss_fft_cpx scratch[5]; 97 | kiss_fft_cpx epi3; 98 | epi3 = st->twiddles[fstride*m]; 99 | 100 | tw1=tw2=st->twiddles; 101 | 102 | do{ 103 | C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); 104 | 105 | C_MUL(scratch[1],Fout[m] , *tw1); 106 | C_MUL(scratch[2],Fout[m2] , *tw2); 107 | 108 | C_ADD(scratch[3],scratch[1],scratch[2]); 109 | C_SUB(scratch[0],scratch[1],scratch[2]); 110 | tw1 += fstride; 111 | tw2 += fstride*2; 112 | 113 | Fout[m].r = Fout->r - HALF_OF(scratch[3].r); 114 | Fout[m].i = Fout->i - HALF_OF(scratch[3].i); 115 | 116 | C_MULBYSCALAR( scratch[0] , epi3.i ); 117 | 118 | C_ADDTO(*Fout,scratch[3]); 119 | 120 | Fout[m2].r = Fout[m].r + scratch[0].i; 121 | Fout[m2].i = Fout[m].i - scratch[0].r; 122 | 123 | Fout[m].r -= scratch[0].i; 124 | Fout[m].i += scratch[0].r; 125 | 126 | ++Fout; 127 | }while(--k); 128 | } 129 | 130 | static void kf_bfly5( 131 | kiss_fft_cpx * Fout, 132 | const size_t fstride, 133 | const kiss_fft_cfg st, 134 | int m 135 | ) 136 | { 137 | kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; 138 | int u; 139 | kiss_fft_cpx scratch[13]; 140 | kiss_fft_cpx * twiddles = st->twiddles; 141 | kiss_fft_cpx *tw; 142 | kiss_fft_cpx ya,yb; 143 | ya = twiddles[fstride*m]; 144 | yb = twiddles[fstride*2*m]; 145 | 146 | Fout0=Fout; 147 | Fout1=Fout0+m; 148 | Fout2=Fout0+2*m; 149 | Fout3=Fout0+3*m; 150 | Fout4=Fout0+4*m; 151 | 152 | tw=st->twiddles; 153 | for ( u=0; ur += scratch[7].r + scratch[8].r; 168 | Fout0->i += scratch[7].i + scratch[8].i; 169 | 170 | scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); 171 | scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); 172 | 173 | scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); 174 | scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); 175 | 176 | C_SUB(*Fout1,scratch[5],scratch[6]); 177 | C_ADD(*Fout4,scratch[5],scratch[6]); 178 | 179 | scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); 180 | scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); 181 | scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); 182 | scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); 183 | 184 | C_ADD(*Fout2,scratch[11],scratch[12]); 185 | C_SUB(*Fout3,scratch[11],scratch[12]); 186 | 187 | ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; 188 | } 189 | } 190 | 191 | /* perform the butterfly for one stage of a mixed radix FFT */ 192 | static void kf_bfly_generic( 193 | kiss_fft_cpx * Fout, 194 | const size_t fstride, 195 | const kiss_fft_cfg st, 196 | int m, 197 | int p 198 | ) 199 | { 200 | int u,k,q1,q; 201 | kiss_fft_cpx * twiddles = st->twiddles; 202 | kiss_fft_cpx t; 203 | int Norig = st->nfft; 204 | 205 | kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); 206 | if (scratch == NULL){ 207 | KISS_FFT_ERROR("Memory allocation failed."); 208 | return; 209 | } 210 | 211 | for ( u=0; u=Norig) twidx-=Norig; 226 | C_MUL(t,scratch[q] , twiddles[twidx] ); 227 | C_ADDTO( Fout[ k ] ,t); 228 | } 229 | k += m; 230 | } 231 | } 232 | KISS_FFT_TMP_FREE(scratch); 233 | } 234 | 235 | static 236 | void kf_work( 237 | kiss_fft_cpx * Fout, 238 | const kiss_fft_cpx * f, 239 | const size_t fstride, 240 | int in_stride, 241 | int * factors, 242 | const kiss_fft_cfg st 243 | ) 244 | { 245 | kiss_fft_cpx * Fout_beg=Fout; 246 | const int p=*factors++; /* the radix */ 247 | const int m=*factors++; /* stage's fft length/p */ 248 | const kiss_fft_cpx * Fout_end = Fout + p*m; 249 | 250 | #ifdef _OPENMP 251 | // use openmp extensions at the 252 | // top-level (not recursive) 253 | if (fstride==1 && p<=5 && m!=1) 254 | { 255 | int k; 256 | 257 | // execute the p different work units in different threads 258 | # pragma omp parallel for 259 | for (k=0;k floor_sqrt) 322 | p = n; /* no more factors, skip to end */ 323 | } 324 | n /= p; 325 | *facbuf++ = p; 326 | *facbuf++ = n; 327 | } while (n > 1); 328 | } 329 | 330 | /* 331 | * 332 | * User-callable function to allocate all necessary storage space for the fft. 333 | * 334 | * The return value is a contiguous block of memory, allocated with malloc. As such, 335 | * It can be freed with free(), rather than a kiss_fft-specific function. 336 | * */ 337 | kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) 338 | { 339 | KISS_FFT_ALIGN_CHECK(mem) 340 | 341 | kiss_fft_cfg st=NULL; 342 | size_t memneeded = KISS_FFT_ALIGN_SIZE_UP(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 | if (fout == NULL){ 377 | KISS_FFT_ERROR("fout buffer NULL."); 378 | return; 379 | } 380 | 381 | kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft); 382 | if (tmpbuf == NULL){ 383 | KISS_FFT_ERROR("Memory allocation error."); 384 | return; 385 | } 386 | 387 | 388 | 389 | kf_work(tmpbuf,fin,1,in_stride, st->factors,st); 390 | memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); 391 | KISS_FFT_TMP_FREE(tmpbuf); 392 | }else{ 393 | kf_work( fout, fin, 1,in_stride, st->factors,st ); 394 | } 395 | } 396 | 397 | void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) 398 | { 399 | kiss_fft_stride(cfg,fin,fout,1); 400 | } 401 | 402 | 403 | void kiss_fft_cleanup(void) 404 | { 405 | // nothing needed any more 406 | } 407 | 408 | int kiss_fft_next_fast_size(int n) 409 | { 410 | while(1) { 411 | int m=n; 412 | while ( (m%2) == 0 ) m/=2; 413 | while ( (m%3) == 0 ) m/=3; 414 | while ( (m%5) == 0 ) m/=5; 415 | if (m<=1) 416 | break; /* n is completely factorable by twos, threes, and fives */ 417 | n++; 418 | } 419 | return n; 420 | } 421 | -------------------------------------------------------------------------------- /lib/kissfft/tools/kiss_fastfir.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #include "_kiss_fft_guts.h" 10 | 11 | 12 | /* 13 | Some definitions that allow real or complex filtering 14 | */ 15 | #ifdef REAL_FASTFIR 16 | #define MIN_FFT_LEN 2048 17 | #include "kiss_fftr.h" 18 | typedef kiss_fft_scalar kffsamp_t; 19 | typedef kiss_fftr_cfg kfcfg_t; 20 | #define FFT_ALLOC kiss_fftr_alloc 21 | #define FFTFWD kiss_fftr 22 | #define FFTINV kiss_fftri 23 | #else 24 | #define MIN_FFT_LEN 1024 25 | typedef kiss_fft_cpx kffsamp_t; 26 | typedef kiss_fft_cfg kfcfg_t; 27 | #define FFT_ALLOC kiss_fft_alloc 28 | #define FFTFWD kiss_fft 29 | #define FFTINV kiss_fft 30 | #endif 31 | 32 | typedef struct kiss_fastfir_state *kiss_fastfir_cfg; 33 | 34 | 35 | 36 | kiss_fastfir_cfg kiss_fastfir_alloc(const kffsamp_t * imp_resp,size_t n_imp_resp, 37 | size_t * nfft,void * mem,size_t*lenmem); 38 | 39 | /* see do_file_filter for usage */ 40 | size_t kiss_fastfir( kiss_fastfir_cfg cfg, kffsamp_t * inbuf, kffsamp_t * outbuf, size_t n, size_t *offset); 41 | 42 | 43 | 44 | static int verbose=0; 45 | 46 | 47 | struct kiss_fastfir_state{ 48 | size_t nfft; 49 | size_t ngood; 50 | kfcfg_t fftcfg; 51 | kfcfg_t ifftcfg; 52 | kiss_fft_cpx * fir_freq_resp; 53 | kiss_fft_cpx * freqbuf; 54 | size_t n_freq_bins; 55 | kffsamp_t * tmpbuf; 56 | }; 57 | 58 | 59 | kiss_fastfir_cfg kiss_fastfir_alloc( 60 | const kffsamp_t * imp_resp,size_t n_imp_resp, 61 | size_t *pnfft, /* if <= 0, an appropriate size will be chosen */ 62 | void * mem,size_t*lenmem) 63 | { 64 | kiss_fastfir_cfg st = NULL; 65 | size_t len_fftcfg,len_ifftcfg; 66 | size_t memneeded = sizeof(struct kiss_fastfir_state); 67 | char * ptr; 68 | size_t i; 69 | size_t nfft=0; 70 | float scale; 71 | int n_freq_bins; 72 | if (pnfft) 73 | nfft=*pnfft; 74 | 75 | if (nfft<=0) { 76 | /* determine fft size as next power of two at least 2x 77 | the impulse response length*/ 78 | i=n_imp_resp-1; 79 | nfft=2; 80 | do{ 81 | nfft<<=1; 82 | }while (i>>=1); 83 | #ifdef MIN_FFT_LEN 84 | if ( nfft < MIN_FFT_LEN ) 85 | nfft=MIN_FFT_LEN; 86 | #endif 87 | } 88 | if (pnfft) 89 | *pnfft = nfft; 90 | 91 | #ifdef REAL_FASTFIR 92 | n_freq_bins = nfft/2 + 1; 93 | #else 94 | n_freq_bins = nfft; 95 | #endif 96 | /*fftcfg*/ 97 | FFT_ALLOC (nfft, 0, NULL, &len_fftcfg); 98 | memneeded += len_fftcfg; 99 | /*ifftcfg*/ 100 | FFT_ALLOC (nfft, 1, NULL, &len_ifftcfg); 101 | memneeded += len_ifftcfg; 102 | /* tmpbuf */ 103 | memneeded += sizeof(kffsamp_t) * nfft; 104 | /* fir_freq_resp */ 105 | memneeded += sizeof(kiss_fft_cpx) * n_freq_bins; 106 | /* freqbuf */ 107 | memneeded += sizeof(kiss_fft_cpx) * n_freq_bins; 108 | 109 | if (lenmem == NULL) { 110 | st = (kiss_fastfir_cfg) malloc (memneeded); 111 | } else { 112 | if (*lenmem >= memneeded) 113 | st = (kiss_fastfir_cfg) mem; 114 | *lenmem = memneeded; 115 | } 116 | if (!st) 117 | return NULL; 118 | 119 | st->nfft = nfft; 120 | st->ngood = nfft - n_imp_resp + 1; 121 | st->n_freq_bins = n_freq_bins; 122 | ptr=(char*)(st+1); 123 | 124 | st->fftcfg = (kfcfg_t)ptr; 125 | ptr += len_fftcfg; 126 | 127 | st->ifftcfg = (kfcfg_t)ptr; 128 | ptr += len_ifftcfg; 129 | 130 | st->tmpbuf = (kffsamp_t*)ptr; 131 | ptr += sizeof(kffsamp_t) * nfft; 132 | 133 | st->freqbuf = (kiss_fft_cpx*)ptr; 134 | ptr += sizeof(kiss_fft_cpx) * n_freq_bins; 135 | 136 | st->fir_freq_resp = (kiss_fft_cpx*)ptr; 137 | ptr += sizeof(kiss_fft_cpx) * n_freq_bins; 138 | 139 | FFT_ALLOC (nfft,0,st->fftcfg , &len_fftcfg); 140 | FFT_ALLOC (nfft,1,st->ifftcfg , &len_ifftcfg); 141 | 142 | memset(st->tmpbuf,0,sizeof(kffsamp_t)*nfft); 143 | /*zero pad in the middle to left-rotate the impulse response 144 | This puts the scrap samples at the end of the inverse fft'd buffer */ 145 | st->tmpbuf[0] = imp_resp[ n_imp_resp - 1 ]; 146 | for (i=0;itmpbuf[ nfft - n_imp_resp + 1 + i ] = imp_resp[ i ]; 148 | } 149 | 150 | FFTFWD(st->fftcfg,st->tmpbuf,st->fir_freq_resp); 151 | 152 | /* TODO: this won't work for fixed point */ 153 | scale = 1.0 / st->nfft; 154 | 155 | for ( i=0; i < st->n_freq_bins; ++i ) { 156 | #ifdef USE_SIMD 157 | st->fir_freq_resp[i].r *= _mm_set1_ps(scale); 158 | st->fir_freq_resp[i].i *= _mm_set1_ps(scale); 159 | #else 160 | st->fir_freq_resp[i].r *= scale; 161 | st->fir_freq_resp[i].i *= scale; 162 | #endif 163 | } 164 | return st; 165 | } 166 | 167 | static void fastconv1buf(const kiss_fastfir_cfg st,const kffsamp_t * in,kffsamp_t * out) 168 | { 169 | size_t i; 170 | /* multiply the frequency response of the input signal by 171 | that of the fir filter*/ 172 | FFTFWD( st->fftcfg, in , st->freqbuf ); 173 | for ( i=0; in_freq_bins; ++i ) { 174 | kiss_fft_cpx tmpsamp; 175 | C_MUL(tmpsamp,st->freqbuf[i],st->fir_freq_resp[i]); 176 | st->freqbuf[i] = tmpsamp; 177 | } 178 | 179 | /* perform the inverse fft*/ 180 | FFTINV(st->ifftcfg,st->freqbuf,out); 181 | } 182 | 183 | /* n : the size of inbuf and outbuf in samples 184 | return value: the number of samples completely processed 185 | n-retval samples should be copied to the front of the next input buffer */ 186 | static size_t kff_nocopy( 187 | kiss_fastfir_cfg st, 188 | const kffsamp_t * inbuf, 189 | kffsamp_t * outbuf, 190 | size_t n) 191 | { 192 | size_t norig=n; 193 | while (n >= st->nfft ) { 194 | fastconv1buf(st,inbuf,outbuf); 195 | inbuf += st->ngood; 196 | outbuf += st->ngood; 197 | n -= st->ngood; 198 | } 199 | return norig - n; 200 | } 201 | 202 | static 203 | size_t kff_flush(kiss_fastfir_cfg st,const kffsamp_t * inbuf,kffsamp_t * outbuf,size_t n) 204 | { 205 | size_t zpad=0,ntmp; 206 | 207 | ntmp = kff_nocopy(st,inbuf,outbuf,n); 208 | n -= ntmp; 209 | inbuf += ntmp; 210 | outbuf += ntmp; 211 | 212 | zpad = st->nfft - n; 213 | memset(st->tmpbuf,0,sizeof(kffsamp_t)*st->nfft ); 214 | memcpy(st->tmpbuf,inbuf,sizeof(kffsamp_t)*n ); 215 | 216 | fastconv1buf(st,st->tmpbuf,st->tmpbuf); 217 | 218 | memcpy(outbuf,st->tmpbuf,sizeof(kffsamp_t)*( st->ngood - zpad )); 219 | return ntmp + st->ngood - zpad; 220 | } 221 | 222 | size_t kiss_fastfir( 223 | kiss_fastfir_cfg vst, 224 | kffsamp_t * inbuf, 225 | kffsamp_t * outbuf, 226 | size_t n_new, 227 | size_t *offset) 228 | { 229 | size_t ntot = n_new + *offset; 230 | if (n_new==0) { 231 | return kff_flush(vst,inbuf,outbuf,ntot); 232 | }else{ 233 | size_t nwritten = kff_nocopy(vst,inbuf,outbuf,ntot); 234 | *offset = ntot - nwritten; 235 | /*save the unused or underused samples at the front of the input buffer */ 236 | memcpy( inbuf , inbuf+nwritten , *offset * sizeof(kffsamp_t) ); 237 | return nwritten; 238 | } 239 | } 240 | 241 | #ifdef FAST_FILT_UTIL 242 | #include 243 | #include 244 | #include 245 | #include 246 | 247 | static 248 | void direct_file_filter( 249 | FILE * fin, 250 | FILE * fout, 251 | const kffsamp_t * imp_resp, 252 | size_t n_imp_resp) 253 | { 254 | size_t nlag = n_imp_resp - 1; 255 | 256 | const kffsamp_t *tmph; 257 | kffsamp_t *buf, *circbuf; 258 | kffsamp_t outval; 259 | size_t nread; 260 | size_t nbuf; 261 | size_t oldestlag = 0; 262 | size_t k, tap; 263 | #ifndef REAL_FASTFIR 264 | kffsamp_t tmp; 265 | #endif 266 | 267 | nbuf = 4096; 268 | buf = (kffsamp_t *) malloc ( sizeof (kffsamp_t) * nbuf); 269 | circbuf = (kffsamp_t *) malloc (sizeof (kffsamp_t) * nlag); 270 | if (!circbuf || !buf) { 271 | perror("circbuf allocation"); 272 | exit(1); 273 | } 274 | 275 | if ( fread (circbuf, sizeof (kffsamp_t), nlag, fin) != nlag ) { 276 | perror ("insufficient data to overcome transient"); 277 | exit (1); 278 | } 279 | 280 | do { 281 | nread = fread (buf, sizeof (kffsamp_t), nbuf, fin); 282 | if (nread <= 0) 283 | break; 284 | 285 | for (k = 0; k < nread; ++k) { 286 | tmph = imp_resp+nlag; 287 | #ifdef REAL_FASTFIR 288 | # ifdef USE_SIMD 289 | outval = _mm_set1_ps(0); 290 | #else 291 | outval = 0; 292 | #endif 293 | for (tap = oldestlag; tap < nlag; ++tap) 294 | outval += circbuf[tap] * *tmph--; 295 | for (tap = 0; tap < oldestlag; ++tap) 296 | outval += circbuf[tap] * *tmph--; 297 | outval += buf[k] * *tmph; 298 | #else 299 | # ifdef USE_SIMD 300 | outval.r = outval.i = _mm_set1_ps(0); 301 | #else 302 | outval.r = outval.i = 0; 303 | #endif 304 | for (tap = oldestlag; tap < nlag; ++tap){ 305 | C_MUL(tmp,circbuf[tap],*tmph); 306 | --tmph; 307 | C_ADDTO(outval,tmp); 308 | } 309 | 310 | for (tap = 0; tap < oldestlag; ++tap) { 311 | C_MUL(tmp,circbuf[tap],*tmph); 312 | --tmph; 313 | C_ADDTO(outval,tmp); 314 | } 315 | C_MUL(tmp,buf[k],*tmph); 316 | C_ADDTO(outval,tmp); 317 | #endif 318 | 319 | circbuf[oldestlag++] = buf[k]; 320 | buf[k] = outval; 321 | 322 | if (oldestlag == nlag) 323 | oldestlag = 0; 324 | } 325 | 326 | if (fwrite (buf, sizeof (buf[0]), nread, fout) != nread) { 327 | perror ("short write"); 328 | exit (1); 329 | } 330 | } while (nread); 331 | free (buf); 332 | free (circbuf); 333 | } 334 | 335 | static 336 | void do_file_filter( 337 | FILE * fin, 338 | FILE * fout, 339 | const kffsamp_t * imp_resp, 340 | size_t n_imp_resp, 341 | size_t nfft ) 342 | { 343 | int fdout; 344 | size_t n_samps_buf; 345 | 346 | kiss_fastfir_cfg cfg; 347 | kffsamp_t *inbuf,*outbuf; 348 | int nread,nwrite; 349 | size_t idx_inbuf; 350 | 351 | fdout = fileno(fout); 352 | 353 | cfg=kiss_fastfir_alloc(imp_resp,n_imp_resp,&nfft,0,0); 354 | 355 | /* use length to minimize buffer shift*/ 356 | n_samps_buf = 8*4096/sizeof(kffsamp_t); 357 | n_samps_buf = nfft + 4*(nfft-n_imp_resp+1); 358 | 359 | if (verbose) fprintf(stderr,"bufsize=%d\n",(int)(sizeof(kffsamp_t)*n_samps_buf) ); 360 | 361 | 362 | /*allocate space and initialize pointers */ 363 | inbuf = (kffsamp_t*)malloc(sizeof(kffsamp_t)*n_samps_buf); 364 | outbuf = (kffsamp_t*)malloc(sizeof(kffsamp_t)*n_samps_buf); 365 | 366 | idx_inbuf=0; 367 | do{ 368 | /* start reading at inbuf[idx_inbuf] */ 369 | nread = fread( inbuf + idx_inbuf, sizeof(kffsamp_t), n_samps_buf - idx_inbuf,fin ); 370 | 371 | /* If nread==0, then this is a flush. 372 | The total number of samples in input is idx_inbuf + nread . */ 373 | nwrite = kiss_fastfir(cfg, inbuf, outbuf,nread,&idx_inbuf) * sizeof(kffsamp_t); 374 | /* kiss_fastfir moved any unused samples to the front of inbuf and updated idx_inbuf */ 375 | 376 | if ( write(fdout, outbuf, nwrite) != nwrite ) { 377 | perror("short write"); 378 | exit(1); 379 | } 380 | }while ( nread ); 381 | free(cfg); 382 | free(inbuf); 383 | free(outbuf); 384 | } 385 | 386 | int main(int argc,char**argv) 387 | { 388 | kffsamp_t * h; 389 | int use_direct=0; 390 | size_t nh,nfft=0; 391 | FILE *fin=stdin; 392 | FILE *fout=stdout; 393 | FILE *filtfile=NULL; 394 | while (1) { 395 | int c=getopt(argc,argv,"n:h:i:o:vd"); 396 | if (c==-1) break; 397 | switch (c) { 398 | case 'v': 399 | verbose=1; 400 | break; 401 | case 'n': 402 | nfft=atoi(optarg); 403 | break; 404 | case 'i': 405 | fin = fopen(optarg,"rb"); 406 | if (fin==NULL) { 407 | perror(optarg); 408 | exit(1); 409 | } 410 | break; 411 | case 'o': 412 | fout = fopen(optarg,"w+b"); 413 | if (fout==NULL) { 414 | perror(optarg); 415 | exit(1); 416 | } 417 | break; 418 | case 'h': 419 | filtfile = fopen(optarg,"rb"); 420 | if (filtfile==NULL) { 421 | perror(optarg); 422 | exit(1); 423 | } 424 | break; 425 | case 'd': 426 | use_direct=1; 427 | break; 428 | case '?': 429 | fprintf(stderr,"usage options:\n" 430 | "\t-n nfft: fft size to use\n" 431 | "\t-d : use direct FIR filtering, not fast convolution\n" 432 | "\t-i filename: input file\n" 433 | "\t-o filename: output(filtered) file\n" 434 | "\t-n nfft: fft size to use\n" 435 | "\t-h filename: impulse response\n"); 436 | exit (1); 437 | default:fprintf(stderr,"bad %c\n",c);break; 438 | } 439 | } 440 | if (filtfile==NULL) { 441 | fprintf(stderr,"You must supply the FIR coeffs via -h\n"); 442 | exit(1); 443 | } 444 | fseek(filtfile,0,SEEK_END); 445 | nh = ftell(filtfile) / sizeof(kffsamp_t); 446 | if (verbose) fprintf(stderr,"%d samples in FIR filter\n",(int)nh); 447 | h = (kffsamp_t*)malloc(sizeof(kffsamp_t)*nh); 448 | fseek(filtfile,0,SEEK_SET); 449 | if (fread(h,sizeof(kffsamp_t),nh,filtfile) != nh) 450 | fprintf(stderr,"short read on filter file\n"); 451 | 452 | fclose(filtfile); 453 | 454 | if (use_direct) 455 | direct_file_filter( fin, fout, h,nh); 456 | else 457 | do_file_filter( fin, fout, h,nh,nfft); 458 | 459 | if (fout!=stdout) fclose(fout); 460 | if (fin!=stdin) fclose(fin); 461 | 462 | return 0; 463 | } 464 | #endif 465 | -------------------------------------------------------------------------------- /lib/kissfft/kissfft.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. 3 | * This file is part of KISS FFT - https://github.com/mborgerding/kissfft 4 | * 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | * See COPYING file for more information. 7 | */ 8 | 9 | #ifndef KISSFFT_CLASS_HH 10 | #define KISSFFT_CLASS_HH 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | template 17 | class kissfft 18 | { 19 | public: 20 | 21 | typedef std::complex cpx_t; 22 | 23 | kissfft( const std::size_t nfft, 24 | const bool inverse ) 25 | :_nfft(nfft) 26 | ,_inverse(inverse) 27 | { 28 | // fill twiddle factors 29 | _twiddles.resize(_nfft); 30 | const scalar_t phinc = (_inverse?2:-2)* std::acos( (scalar_t) -1) / _nfft; 31 | for (std::size_t i=0;i<_nfft;++i) 32 | _twiddles[i] = std::exp( cpx_t(0,i*phinc) ); 33 | 34 | //factorize 35 | //start factoring out 4's, then 2's, then 3,5,7,9,... 36 | std::size_t n= _nfft; 37 | std::size_t p=4; 38 | do { 39 | while (n % p) { 40 | switch (p) { 41 | case 4: p = 2; break; 42 | case 2: p = 3; break; 43 | default: p += 2; break; 44 | } 45 | if (p*p>n) 46 | p = n;// no more factors 47 | } 48 | n /= p; 49 | _stageRadix.push_back(p); 50 | _stageRemainder.push_back(n); 51 | }while(n>1); 52 | } 53 | 54 | 55 | /// Changes the FFT-length and/or the transform direction. 56 | /// 57 | /// @post The @c kissfft object will be in the same state as if it 58 | /// had been newly constructed with the passed arguments. 59 | /// However, the implementation may be faster than constructing a 60 | /// new fft object. 61 | void assign( const std::size_t nfft, 62 | const bool inverse ) 63 | { 64 | if ( nfft != _nfft ) 65 | { 66 | kissfft tmp( nfft, inverse ); // O(n) time. 67 | std::swap( tmp, *this ); // this is O(1) in C++11, O(n) otherwise. 68 | } 69 | else if ( inverse != _inverse ) 70 | { 71 | // conjugate the twiddle factors. 72 | for ( typename std::vector::iterator it = _twiddles.begin(); 73 | it != _twiddles.end(); ++it ) 74 | it->imag( -it->imag() ); 75 | } 76 | } 77 | 78 | /// Calculates the complex Discrete Fourier Transform. 79 | /// 80 | /// The size of the passed arrays must be passed in the constructor. 81 | /// The sum of the squares of the absolute values in the @c dst 82 | /// array will be @c N times the sum of the squares of the absolute 83 | /// values in the @c src array, where @c N is the size of the array. 84 | /// In other words, the l_2 norm of the resulting array will be 85 | /// @c sqrt(N) times as big as the l_2 norm of the input array. 86 | /// This is also the case when the inverse flag is set in the 87 | /// constructor. Hence when applying the same transform twice, but with 88 | /// the inverse flag changed the second time, then the result will 89 | /// be equal to the original input times @c N. 90 | void transform(const cpx_t * fft_in, cpx_t * fft_out, const std::size_t stage = 0, const std::size_t fstride = 1, const std::size_t in_stride = 1) const 91 | { 92 | const std::size_t p = _stageRadix[stage]; 93 | const std::size_t m = _stageRemainder[stage]; 94 | cpx_t * const Fout_beg = fft_out; 95 | cpx_t * const Fout_end = fft_out + p*m; 96 | 97 | if (m==1) { 98 | do{ 99 | *fft_out = *fft_in; 100 | fft_in += fstride*in_stride; 101 | }while(++fft_out != Fout_end ); 102 | }else{ 103 | do{ 104 | // recursive call: 105 | // DFT of size m*p performed by doing 106 | // p instances of smaller DFTs of size m, 107 | // each one takes a decimated version of the input 108 | transform(fft_in, fft_out, stage+1, fstride*p,in_stride); 109 | fft_in += fstride*in_stride; 110 | }while( (fft_out += m) != Fout_end ); 111 | } 112 | 113 | fft_out=Fout_beg; 114 | 115 | // recombine the p smaller DFTs 116 | switch (p) { 117 | case 2: kf_bfly2(fft_out,fstride,m); break; 118 | case 3: kf_bfly3(fft_out,fstride,m); break; 119 | case 4: kf_bfly4(fft_out,fstride,m); break; 120 | case 5: kf_bfly5(fft_out,fstride,m); break; 121 | default: kf_bfly_generic(fft_out,fstride,m,p); break; 122 | } 123 | } 124 | 125 | /// Calculates the Discrete Fourier Transform (DFT) of a real input 126 | /// of size @c 2*N. 127 | /// 128 | /// The 0-th and N-th value of the DFT are real numbers. These are 129 | /// stored in @c dst[0].real() and @c dst[1].imag() respectively. 130 | /// The remaining DFT values up to the index N-1 are stored in 131 | /// @c dst[1] to @c dst[N-1]. 132 | /// The other half of the DFT values can be calculated from the 133 | /// symmetry relation 134 | /// @code 135 | /// DFT(src)[2*N-k] == conj( DFT(src)[k] ); 136 | /// @endcode 137 | /// The same scaling factors as in @c transform() apply. 138 | /// 139 | /// @note For this to work, the types @c scalar_t and @c cpx_t 140 | /// must fulfill the following requirements: 141 | /// 142 | /// For any object @c z of type @c cpx_t, 143 | /// @c reinterpret_cast(z)[0] is the real part of @c z and 144 | /// @c reinterpret_cast(z)[1] is the imaginary part of @c z. 145 | /// For any pointer to an element of an array of @c cpx_t named @c p 146 | /// and any valid array index @c i, @c reinterpret_cast(p)[2*i] 147 | /// is the real part of the complex number @c p[i], and 148 | /// @c reinterpret_cast(p)[2*i+1] is the imaginary part of the 149 | /// complex number @c p[i]. 150 | /// 151 | /// Since C++11, these requirements are guaranteed to be satisfied for 152 | /// @c scalar_ts being @c float, @c double or @c long @c double 153 | /// together with @c cpx_t being @c std::complex. 154 | void transform_real( const scalar_t * const src, 155 | cpx_t * const dst ) const 156 | { 157 | const std::size_t N = _nfft; 158 | if ( N == 0 ) 159 | return; 160 | 161 | // perform complex FFT 162 | transform( reinterpret_cast(src), dst ); 163 | 164 | // post processing for k = 0 and k = N 165 | dst[0] = cpx_t( dst[0].real() + dst[0].imag(), 166 | dst[0].real() - dst[0].imag() ); 167 | 168 | // post processing for all the other k = 1, 2, ..., N-1 169 | const scalar_t pi = std::acos( (scalar_t) -1); 170 | const scalar_t half_phi_inc = ( _inverse ? pi : -pi ) / N; 171 | const cpx_t twiddle_mul = std::exp( cpx_t(0, half_phi_inc) ); 172 | for ( std::size_t k = 1; 2*k < N; ++k ) 173 | { 174 | const cpx_t w = (scalar_t)0.5 * cpx_t( 175 | dst[k].real() + dst[N-k].real(), 176 | dst[k].imag() - dst[N-k].imag() ); 177 | const cpx_t z = (scalar_t)0.5 * cpx_t( 178 | dst[k].imag() + dst[N-k].imag(), 179 | -dst[k].real() + dst[N-k].real() ); 180 | const cpx_t twiddle = 181 | k % 2 == 0 ? 182 | _twiddles[k/2] : 183 | _twiddles[k/2] * twiddle_mul; 184 | dst[ k] = w + twiddle * z; 185 | dst[N-k] = std::conj( w - twiddle * z ); 186 | } 187 | if ( N % 2 == 0 ) 188 | dst[N/2] = std::conj( dst[N/2] ); 189 | } 190 | 191 | private: 192 | 193 | void kf_bfly2( cpx_t * Fout, const size_t fstride, const std::size_t m) const 194 | { 195 | for (std::size_t k=0;k _scratchbuf.size()) _scratchbuf.resize(p); 331 | 332 | for ( std::size_t u=0; u=_nfft) 346 | twidx-=_nfft; 347 | Fout[ k ] += _scratchbuf[q] * twiddles[twidx]; 348 | } 349 | k += m; 350 | } 351 | } 352 | } 353 | 354 | std::size_t _nfft; 355 | bool _inverse; 356 | std::vector _twiddles; 357 | std::vector _stageRadix; 358 | std::vector _stageRemainder; 359 | mutable std::vector _scratchbuf; 360 | }; 361 | #endif 362 | --------------------------------------------------------------------------------