| @@ -182,6 +182,15 @@ | |||
| "Visual" | |||
| ] | |||
| }, | |||
| { | |||
| "slug": "SassyScope", | |||
| "name": "Sassy Scope", | |||
| "description": "Scope from Sassy Audio Spreadsheet", | |||
| "manualUrl": "https://github.com/DISTRHO/Cardinal/blob/main/docs/CARDINAL-MODULES.md#sassy-scope", | |||
| "tags": [ | |||
| "Visual" | |||
| ] | |||
| }, | |||
| { | |||
| "slug": "TextEditor", | |||
| "name": "Text Editor", | |||
| @@ -0,0 +1,274 @@ | |||
| /* | |||
| * DISTRHO Cardinal Plugin | |||
| * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 3 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the LICENSE file. | |||
| */ | |||
| #include "plugincontext.hpp" | |||
| #include "ImGuiWidget.hpp" | |||
| #include "sassy/sassy.hpp" | |||
| #include "sassy/sassy_scope.cpp" | |||
| namespace ffft { | |||
| template<class DT, int size> | |||
| class FFTRealWithSize : public FFTReal<DT> { | |||
| public: | |||
| explicit FFTRealWithSize() : FFTReal<DT>(size) {} | |||
| }; | |||
| } | |||
| struct SassyScopeModule : Module { | |||
| enum ParamIds { | |||
| NUM_PARAMS | |||
| }; | |||
| enum InputIds { | |||
| INPUT1, | |||
| INPUT2, | |||
| INPUT3, | |||
| INPUT4, | |||
| NUM_INPUTS | |||
| }; | |||
| enum OutputIds { | |||
| NUM_OUTPUTS | |||
| }; | |||
| enum LightIds { | |||
| NUM_LIGHTS | |||
| }; | |||
| ScopeData scope; | |||
| ffft::FFTRealWithSize<float, 16*2> fftObj16; | |||
| ffft::FFTRealWithSize<float, 32*2> fftObj32; | |||
| ffft::FFTRealWithSize<float, 64*2> fftObj64; | |||
| ffft::FFTRealWithSize<float, 128*2> fftObj128; | |||
| ffft::FFTRealWithSize<float, 256*2> fftObj256; | |||
| ffft::FFTRealWithSize<float, 512*2> fftObj512; | |||
| ffft::FFTRealWithSize<float, 1024*2> fftObj1024; | |||
| ffft::FFTRealWithSize<float, 2048*2> fftObj2048; | |||
| ffft::FFTRealWithSize<float, 4096*2> fftObj4096; | |||
| ffft::FFTRealWithSize<float, 8192*2> fftObj8192; | |||
| ffft::FFTRealWithSize<float, 16384*2> fftObj16384; | |||
| ffft::FFTRealWithSize<float, 32768*2> fftObj32768; | |||
| ffft::FFTRealWithSize<float, 65536*2> fftObj65536; | |||
| SassyScopeModule() | |||
| { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
| scope.fft.average = 1; | |||
| scope.fft.obj16 = &fftObj16; | |||
| scope.fft.obj32 = &fftObj32; | |||
| scope.fft.obj64 = &fftObj64; | |||
| scope.fft.obj128 = &fftObj128; | |||
| scope.fft.obj256 = &fftObj256; | |||
| scope.fft.obj512 = &fftObj512; | |||
| scope.fft.obj1024 = &fftObj1024; | |||
| scope.fft.obj2048 = &fftObj2048; | |||
| scope.fft.obj4096 = &fftObj4096; | |||
| scope.fft.obj8192 = &fftObj8192; | |||
| scope.fft.obj16384 = &fftObj16384; | |||
| scope.fft.obj32768 = &fftObj32768; | |||
| scope.fft.obj65536 = &fftObj65536; | |||
| } | |||
| void process(const ProcessArgs&) override | |||
| { | |||
| scope.probe(inputs[INPUT1].getVoltage(), | |||
| inputs[INPUT2].getVoltage(), | |||
| inputs[INPUT3].getVoltage(), | |||
| inputs[INPUT4].getVoltage()); | |||
| } | |||
| void onSampleRateChange(const SampleRateChangeEvent& e) override | |||
| { | |||
| scope.realloc(e.sampleRate); | |||
| } | |||
| }; | |||
| // used for module browser | |||
| static ScopeData* getFakeScopeInstance() | |||
| { | |||
| static ScopeData scope; | |||
| static ffft::FFTReal<float> fftObj16(16*2); | |||
| static ffft::FFTReal<float> fftObj32(32*2); | |||
| static ffft::FFTReal<float> fftObj64(64*2); | |||
| static ffft::FFTReal<float> fftObj128(128*2); | |||
| static ffft::FFTReal<float> fftObj256(256*2); | |||
| static ffft::FFTReal<float> fftObj512(512*2); | |||
| static ffft::FFTReal<float> fftObj1024(1024*2); | |||
| static ffft::FFTReal<float> fftObj2048(2048*2); | |||
| static ffft::FFTReal<float> fftObj4096(4096*2); | |||
| static ffft::FFTReal<float> fftObj8192(8192*2); | |||
| static ffft::FFTReal<float> fftObj16384(16384*2); | |||
| static ffft::FFTReal<float> fftObj32768(32768*2); | |||
| static ffft::FFTReal<float> fftObj65536(65536*2); | |||
| static bool needsInit = true; | |||
| if (needsInit) | |||
| { | |||
| needsInit = false; | |||
| scope.fft.average = 1; | |||
| scope.fft.obj16 = &fftObj16; | |||
| scope.fft.obj32 = &fftObj32; | |||
| scope.fft.obj64 = &fftObj64; | |||
| scope.fft.obj128 = &fftObj128; | |||
| scope.fft.obj256 = &fftObj256; | |||
| scope.fft.obj512 = &fftObj512; | |||
| scope.fft.obj1024 = &fftObj1024; | |||
| scope.fft.obj2048 = &fftObj2048; | |||
| scope.fft.obj4096 = &fftObj4096; | |||
| scope.fft.obj8192 = &fftObj8192; | |||
| scope.fft.obj16384 = &fftObj16384; | |||
| scope.fft.obj32768 = &fftObj32768; | |||
| scope.fft.obj65536 = &fftObj65536; | |||
| scope.realloc(48000); | |||
| } | |||
| return &scope; | |||
| } | |||
| struct SassyScopeWidget : ImGuiWidget { | |||
| SassyScopeModule* module = nullptr; | |||
| void drawImGui() override | |||
| { | |||
| const float scaleFactor = getScaleFactor(); | |||
| ImGui::SetNextWindowPos(ImVec2(0, 0)); | |||
| ImGui::SetNextWindowSize(ImVec2(box.size.x * scaleFactor, box.size.y * scaleFactor)); | |||
| do_show_scope_window(module != nullptr ? &module->scope : getFakeScopeInstance(), scaleFactor); | |||
| } | |||
| void onButton(const ButtonEvent& e) | |||
| { | |||
| // if mouse press is over draggable areas, do nothing so event can go to Rack | |||
| if (e.action == GLFW_PRESS) | |||
| { | |||
| // bottom left | |||
| if (e.pos.x < 116 && e.pos.y >= 335) | |||
| return; | |||
| // bottom right | |||
| if (e.pos.x >= 456 && e.pos.y >= 348) | |||
| return; | |||
| // fft label | |||
| if (e.pos.x >= 491 && e.pos.y >= 54 && e.pos.y <= 74) | |||
| return; | |||
| // nudge label | |||
| if (e.pos.x >= 463 && e.pos.y >= 236 && e.pos.y <= 255) | |||
| return; | |||
| // center scope | |||
| if (e.pos.x >= 110 && e.pos.x <= 452 && e.pos.y >= 0 && e.pos.y <= 350) | |||
| return; | |||
| } | |||
| ImGuiWidget::onButton(e); | |||
| } | |||
| }; | |||
| struct SassyScopeModuleWidget : ModuleWidget { | |||
| SassyScopeModule* scopeModule = nullptr; | |||
| SassyScopeWidget* scopeWidget = nullptr; | |||
| SassyScopeModuleWidget(SassyScopeModule* const module) { | |||
| setModule(module); | |||
| box.size = Vec(RACK_GRID_WIDTH * 37, RACK_GRID_HEIGHT); | |||
| scopeModule = module; | |||
| scopeWidget = new SassyScopeWidget(); | |||
| scopeWidget->box.pos = Vec(0, 0); | |||
| scopeWidget->box.size = Vec(box.size.x, box.size.y); | |||
| scopeWidget->module = module; | |||
| addChild(scopeWidget); | |||
| for (int i=0; i<SassyScopeModule::NUM_INPUTS; ++i) | |||
| addInput(createInput<PJ301MPort>(Vec(5 + 26.5f * i, RACK_GRID_HEIGHT - 40), module, i)); | |||
| } | |||
| void draw(const DrawArgs& args) override | |||
| { | |||
| nvgBeginPath(args.vg); | |||
| nvgRect(args.vg, 0.0, 0.0, box.size.x, box.size.y); | |||
| nvgFillColor(args.vg, nvgRGB(0x20, 0x20, 0x20)); | |||
| nvgFill(args.vg); | |||
| ModuleWidget::draw(args); | |||
| } | |||
| void step() override | |||
| { | |||
| ModuleWidget::step(); | |||
| if (scopeModule == nullptr) | |||
| return; | |||
| // Update colors | |||
| for (int i=0; i<SassyScopeModule::NUM_INPUTS; ++i) | |||
| { | |||
| if (CableWidget* const cableWidget = APP->scene->rack->getTopCable(getInput(i))) | |||
| { | |||
| NVGcolor c = cableWidget->color; | |||
| scopeModule->scope.colors[i] = (clamp(int(c.a * 0xff), 0, 0xff) << 24) | |||
| | (clamp(int(c.b * 0xff), 0, 0xff) << 16) | |||
| | (clamp(int(c.g * 0xff), 0, 0xff) << 8) | |||
| | clamp(int(c.r * 0xff), 0, 0xff); | |||
| } | |||
| } | |||
| } | |||
| void appendContextMenu(Menu* const menu) override | |||
| { | |||
| menu->addChild(new MenuSeparator); | |||
| struct AveragingItem : MenuItem { | |||
| ScopeData* scope; | |||
| Menu* createChildMenu() override { | |||
| Menu* menu = new Menu; | |||
| menu->addChild(createCheckMenuItem("1x", "", | |||
| [=]() {return scope->fft.average == 1;}, | |||
| [=]() {scope->fft.average = 1;} | |||
| )); | |||
| menu->addChild(createCheckMenuItem("4x", "", | |||
| [=]() {return scope->fft.average == 4;}, | |||
| [=]() {scope->fft.average = 4;} | |||
| )); | |||
| menu->addChild(createCheckMenuItem("16x", "", | |||
| [=]() {return scope->fft.average == 16;}, | |||
| [=]() {scope->fft.average = 16;} | |||
| )); | |||
| menu->addChild(createCheckMenuItem("64x", "", | |||
| [=]() {return scope->fft.average == 64;}, | |||
| [=]() {scope->fft.average = 64;} | |||
| )); | |||
| menu->addChild(createCheckMenuItem("256x", "", | |||
| [=]() {return scope->fft.average == 256;}, | |||
| [=]() {scope->fft.average = 256;} | |||
| )); | |||
| return menu; | |||
| } | |||
| }; | |||
| AveragingItem* const averagingItem = new AveragingItem; | |||
| averagingItem->text = "Averaging (FFT mode)"; | |||
| averagingItem->rightText = string::f("%d", scopeModule->scope.fft.average) + " " + RIGHT_ARROW; | |||
| averagingItem->scope = &scopeModule->scope; | |||
| menu->addChild(averagingItem); | |||
| } | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| Model* modelSassyScope = createModel<SassyScopeModule, SassyScopeModuleWidget>("SassyScope"); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -46,6 +46,7 @@ extern Model* modelHostParameters; | |||
| extern Model* modelHostTime; | |||
| extern Model* modelIldaeil; | |||
| extern Model* modelMPV; | |||
| extern Model* modelSassyScope; | |||
| extern Model* modelTextEditor; | |||
| extern std::vector<Model*> hostTerminalModels; | |||
| @@ -0,0 +1,98 @@ | |||
| /***************************************************************************** | |||
| Array.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_Array_HEADER_INCLUDED) | |||
| #define ffft_Array_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| namespace ffft | |||
| { | |||
| template <class T, long LEN> | |||
| class Array | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| typedef T DataType; | |||
| Array (); | |||
| inline const DataType & | |||
| operator [] (long pos) const; | |||
| inline DataType & | |||
| operator [] (long pos); | |||
| static inline long | |||
| size (); | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| DataType _data_arr [LEN]; | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| Array (const Array &other); | |||
| Array & operator = (const Array &other); | |||
| bool operator == (const Array &other); | |||
| bool operator != (const Array &other); | |||
| }; // class Array | |||
| } // namespace ffft | |||
| #include "Array.hpp" | |||
| #endif // ffft_Array_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,99 @@ | |||
| /***************************************************************************** | |||
| Array.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_Array_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of Array code header. | |||
| #endif | |||
| #define ffft_Array_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_Array_CODEHEADER_INCLUDED) | |||
| #define ffft_Array_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include <cassert> | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <class T, long LEN> | |||
| Array <T, LEN>::Array () | |||
| { | |||
| // Nothing | |||
| } | |||
| template <class T, long LEN> | |||
| const typename Array <T, LEN>::DataType & Array <T, LEN>::operator [] (long pos) const | |||
| { | |||
| assert (pos >= 0); | |||
| assert (pos < LEN); | |||
| return (_data_arr [pos]); | |||
| } | |||
| template <class T, long LEN> | |||
| typename Array <T, LEN>::DataType & Array <T, LEN>::operator [] (long pos) | |||
| { | |||
| assert (pos >= 0); | |||
| assert (pos < LEN); | |||
| return (_data_arr [pos]); | |||
| } | |||
| template <class T, long LEN> | |||
| long Array <T, LEN>::size () | |||
| { | |||
| return (LEN); | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| } // namespace ffft | |||
| #endif // ffft_Array_CODEHEADER_INCLUDED | |||
| #undef ffft_Array_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,101 @@ | |||
| /***************************************************************************** | |||
| DynArray.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_DynArray_HEADER_INCLUDED) | |||
| #define ffft_DynArray_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| namespace ffft | |||
| { | |||
| template <class T> | |||
| class DynArray | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| typedef T DataType; | |||
| DynArray (); | |||
| explicit DynArray (long size); | |||
| ~DynArray (); | |||
| inline long size () const; | |||
| inline void resize (long size); | |||
| inline const DataType & | |||
| operator [] (long pos) const; | |||
| inline DataType & | |||
| operator [] (long pos); | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| DataType * _data_ptr; | |||
| long _len; | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| DynArray (const DynArray &other); | |||
| DynArray & operator = (const DynArray &other); | |||
| bool operator == (const DynArray &other); | |||
| bool operator != (const DynArray &other); | |||
| }; // class DynArray | |||
| } // namespace ffft | |||
| #include "DynArray.hpp" | |||
| #endif // ffft_DynArray_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,144 @@ | |||
| /***************************************************************************** | |||
| DynArray.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_DynArray_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of DynArray code header. | |||
| #endif | |||
| #define ffft_DynArray_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_DynArray_CODEHEADER_INCLUDED) | |||
| #define ffft_DynArray_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include <cassert> | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <class T> | |||
| DynArray <T>::DynArray () | |||
| : _data_ptr (0) | |||
| , _len (0) | |||
| { | |||
| // Nothing | |||
| } | |||
| template <class T> | |||
| DynArray <T>::DynArray (long size) | |||
| : _data_ptr (0) | |||
| , _len (0) | |||
| { | |||
| assert (size >= 0); | |||
| if (size > 0) | |||
| { | |||
| _data_ptr = new DataType [size]; | |||
| _len = size; | |||
| } | |||
| } | |||
| template <class T> | |||
| DynArray <T>::~DynArray () | |||
| { | |||
| delete [] _data_ptr; | |||
| _data_ptr = 0; | |||
| _len = 0; | |||
| } | |||
| template <class T> | |||
| long DynArray <T>::size () const | |||
| { | |||
| return (_len); | |||
| } | |||
| template <class T> | |||
| void DynArray <T>::resize (long size) | |||
| { | |||
| assert (size >= 0); | |||
| if (size > 0) | |||
| { | |||
| DataType * old_data_ptr = _data_ptr; | |||
| DataType * tmp_data_ptr = new DataType [size]; | |||
| _data_ptr = tmp_data_ptr; | |||
| _len = size; | |||
| delete [] old_data_ptr; | |||
| } | |||
| } | |||
| template <class T> | |||
| const typename DynArray <T>::DataType & DynArray <T>::operator [] (long pos) const | |||
| { | |||
| assert (pos >= 0); | |||
| assert (pos < _len); | |||
| return (_data_ptr [pos]); | |||
| } | |||
| template <class T> | |||
| typename DynArray <T>::DataType & DynArray <T>::operator [] (long pos) | |||
| { | |||
| assert (pos >= 0); | |||
| assert (pos < _len); | |||
| return (_data_ptr [pos]); | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| } // namespace ffft | |||
| #endif // ffft_DynArray_CODEHEADER_INCLUDED | |||
| #undef ffft_DynArray_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,143 @@ | |||
| /***************************************************************************** | |||
| FFTReal.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_FFTReal_HEADER_INCLUDED) | |||
| #define ffft_FFTReal_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "def.h" | |||
| #include "DynArray.h" | |||
| #include "OscSinCos.h" | |||
| namespace ffft | |||
| { | |||
| template <class DT> | |||
| class FFTReal | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| enum { MAX_BIT_DEPTH = 30 }; // So length can be represented as long int | |||
| typedef DT DataType; | |||
| explicit FFTReal (long length); | |||
| virtual ~FFTReal () {} | |||
| long get_length () const; | |||
| void do_fft (DataType f [], const DataType x []) const; | |||
| void do_ifft (const DataType f [], DataType x []) const; | |||
| void rescale (DataType x []) const; | |||
| DataType * use_buffer () const; | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| // Over this bit depth, we use direct calculation for sin/cos | |||
| enum { TRIGO_BD_LIMIT = 12 }; | |||
| typedef OscSinCos <DataType> OscType; | |||
| void init_br_lut (); | |||
| void init_trigo_lut (); | |||
| void init_trigo_osc (); | |||
| ffft_FORCEINLINE const long * | |||
| get_br_ptr () const; | |||
| ffft_FORCEINLINE const DataType * | |||
| get_trigo_ptr (int level) const; | |||
| ffft_FORCEINLINE long | |||
| get_trigo_level_index (int level) const; | |||
| inline void compute_fft_general (DataType f [], const DataType x []) const; | |||
| inline void compute_direct_pass_1_2 (DataType df [], const DataType x []) const; | |||
| inline void compute_direct_pass_3 (DataType df [], const DataType sf []) const; | |||
| inline void compute_direct_pass_n (DataType df [], const DataType sf [], int pass) const; | |||
| inline void compute_direct_pass_n_lut (DataType df [], const DataType sf [], int pass) const; | |||
| inline void compute_direct_pass_n_osc (DataType df [], const DataType sf [], int pass) const; | |||
| inline void compute_ifft_general (const DataType f [], DataType x []) const; | |||
| inline void compute_inverse_pass_n (DataType df [], const DataType sf [], int pass) const; | |||
| inline void compute_inverse_pass_n_osc (DataType df [], const DataType sf [], int pass) const; | |||
| inline void compute_inverse_pass_n_lut (DataType df [], const DataType sf [], int pass) const; | |||
| inline void compute_inverse_pass_3 (DataType df [], const DataType sf []) const; | |||
| inline void compute_inverse_pass_1_2 (DataType x [], const DataType sf []) const; | |||
| const long _length; | |||
| const int _nbr_bits; | |||
| DynArray <long> | |||
| _br_lut; | |||
| DynArray <DataType> | |||
| _trigo_lut; | |||
| mutable DynArray <DataType> | |||
| _buffer; | |||
| mutable DynArray <OscType> | |||
| _trigo_osc; | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| FFTReal (); | |||
| FFTReal (const FFTReal &other); | |||
| FFTReal & operator = (const FFTReal &other); | |||
| bool operator == (const FFTReal &other); | |||
| bool operator != (const FFTReal &other); | |||
| }; // class FFTReal | |||
| } // namespace ffft | |||
| #include "FFTReal.hpp" | |||
| #endif // ffft_FFTReal_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,917 @@ | |||
| /***************************************************************************** | |||
| FFTReal.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_FFTReal_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of FFTReal code header. | |||
| #endif | |||
| #define ffft_FFTReal_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_FFTReal_CODEHEADER_INCLUDED) | |||
| #define ffft_FFTReal_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include <cassert> | |||
| #include <cmath> | |||
| namespace ffft | |||
| { | |||
| static inline bool FFTReal_is_pow2 (long x) | |||
| { | |||
| assert (x > 0); | |||
| return ((x & -x) == x); | |||
| } | |||
| static inline int FFTReal_get_next_pow2 (long x) | |||
| { | |||
| --x; | |||
| int p = 0; | |||
| while ((x & ~0xFFFFL) != 0) | |||
| { | |||
| p += 16; | |||
| x >>= 16; | |||
| } | |||
| while ((x & ~0xFL) != 0) | |||
| { | |||
| p += 4; | |||
| x >>= 4; | |||
| } | |||
| while (x > 0) | |||
| { | |||
| ++p; | |||
| x >>= 1; | |||
| } | |||
| return (p); | |||
| } | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /* | |||
| ============================================================================== | |||
| Name: ctor | |||
| Input parameters: | |||
| - length: length of the array on which we want to do a FFT. Range: power of | |||
| 2 only, > 0. | |||
| Throws: std::bad_alloc | |||
| ============================================================================== | |||
| */ | |||
| template <class DT> | |||
| FFTReal <DT>::FFTReal (long length) | |||
| : _length (length) | |||
| , _nbr_bits (FFTReal_get_next_pow2 (length)) | |||
| , _br_lut () | |||
| , _trigo_lut () | |||
| , _buffer (length) | |||
| , _trigo_osc () | |||
| { | |||
| assert (FFTReal_is_pow2 (length)); | |||
| assert (_nbr_bits <= MAX_BIT_DEPTH); | |||
| init_br_lut (); | |||
| init_trigo_lut (); | |||
| init_trigo_osc (); | |||
| } | |||
| /* | |||
| ============================================================================== | |||
| Name: get_length | |||
| Description: | |||
| Returns the number of points processed by this FFT object. | |||
| Returns: The number of points, power of 2, > 0. | |||
| Throws: Nothing | |||
| ============================================================================== | |||
| */ | |||
| template <class DT> | |||
| long FFTReal <DT>::get_length () const | |||
| { | |||
| return (_length); | |||
| } | |||
| /* | |||
| ============================================================================== | |||
| Name: do_fft | |||
| Description: | |||
| Compute the FFT of the array. | |||
| Input parameters: | |||
| - x: pointer on the source array (time). | |||
| Output parameters: | |||
| - f: pointer on the destination array (frequencies). | |||
| f [0...length(x)/2] = real values, | |||
| f [length(x)/2+1...length(x)-1] = negative imaginary values of | |||
| coefficents 1...length(x)/2-1. | |||
| Throws: Nothing | |||
| ============================================================================== | |||
| */ | |||
| template <class DT> | |||
| void FFTReal <DT>::do_fft (DataType f [], const DataType x []) const | |||
| { | |||
| assert (f != 0); | |||
| assert (f != use_buffer ()); | |||
| assert (x != 0); | |||
| assert (x != use_buffer ()); | |||
| assert (x != f); | |||
| // General case | |||
| if (_nbr_bits > 2) | |||
| { | |||
| compute_fft_general (f, x); | |||
| } | |||
| // 4-point FFT | |||
| else if (_nbr_bits == 2) | |||
| { | |||
| f [1] = x [0] - x [2]; | |||
| f [3] = x [1] - x [3]; | |||
| const DataType b_0 = x [0] + x [2]; | |||
| const DataType b_2 = x [1] + x [3]; | |||
| f [0] = b_0 + b_2; | |||
| f [2] = b_0 - b_2; | |||
| } | |||
| // 2-point FFT | |||
| else if (_nbr_bits == 1) | |||
| { | |||
| f [0] = x [0] + x [1]; | |||
| f [1] = x [0] - x [1]; | |||
| } | |||
| // 1-point FFT | |||
| else | |||
| { | |||
| f [0] = x [0]; | |||
| } | |||
| } | |||
| /* | |||
| ============================================================================== | |||
| Name: do_ifft | |||
| Description: | |||
| Compute the inverse FFT of the array. Note that data must be post-scaled: | |||
| IFFT (FFT (x)) = x * length (x). | |||
| Input parameters: | |||
| - f: pointer on the source array (frequencies). | |||
| f [0...length(x)/2] = real values | |||
| f [length(x)/2+1...length(x)-1] = negative imaginary values of | |||
| coefficents 1...length(x)/2-1. | |||
| Output parameters: | |||
| - x: pointer on the destination array (time). | |||
| Throws: Nothing | |||
| ============================================================================== | |||
| */ | |||
| template <class DT> | |||
| void FFTReal <DT>::do_ifft (const DataType f [], DataType x []) const | |||
| { | |||
| assert (f != 0); | |||
| assert (f != use_buffer ()); | |||
| assert (x != 0); | |||
| assert (x != use_buffer ()); | |||
| assert (x != f); | |||
| // General case | |||
| if (_nbr_bits > 2) | |||
| { | |||
| compute_ifft_general (f, x); | |||
| } | |||
| // 4-point IFFT | |||
| else if (_nbr_bits == 2) | |||
| { | |||
| const DataType b_0 = f [0] + f [2]; | |||
| const DataType b_2 = f [0] - f [2]; | |||
| x [0] = b_0 + f [1] * 2; | |||
| x [2] = b_0 - f [1] * 2; | |||
| x [1] = b_2 + f [3] * 2; | |||
| x [3] = b_2 - f [3] * 2; | |||
| } | |||
| // 2-point IFFT | |||
| else if (_nbr_bits == 1) | |||
| { | |||
| x [0] = f [0] + f [1]; | |||
| x [1] = f [0] - f [1]; | |||
| } | |||
| // 1-point IFFT | |||
| else | |||
| { | |||
| x [0] = f [0]; | |||
| } | |||
| } | |||
| /* | |||
| ============================================================================== | |||
| Name: rescale | |||
| Description: | |||
| Scale an array by divide each element by its length. This function should | |||
| be called after FFT + IFFT. | |||
| Input parameters: | |||
| - x: pointer on array to rescale (time or frequency). | |||
| Throws: Nothing | |||
| ============================================================================== | |||
| */ | |||
| template <class DT> | |||
| void FFTReal <DT>::rescale (DataType x []) const | |||
| { | |||
| const DataType mul = DataType (1.0 / _length); | |||
| if (_length < 4) | |||
| { | |||
| long i = _length - 1; | |||
| do | |||
| { | |||
| x [i] *= mul; | |||
| --i; | |||
| } | |||
| while (i >= 0); | |||
| } | |||
| else | |||
| { | |||
| assert ((_length & 3) == 0); | |||
| // Could be optimized with SIMD instruction sets (needs alignment check) | |||
| long i = _length - 4; | |||
| do | |||
| { | |||
| x [i + 0] *= mul; | |||
| x [i + 1] *= mul; | |||
| x [i + 2] *= mul; | |||
| x [i + 3] *= mul; | |||
| i -= 4; | |||
| } | |||
| while (i >= 0); | |||
| } | |||
| } | |||
| /* | |||
| ============================================================================== | |||
| Name: use_buffer | |||
| Description: | |||
| Access the internal buffer, whose length is the FFT one. | |||
| Buffer content will be erased at each do_fft() / do_ifft() call! | |||
| This buffer cannot be used as: | |||
| - source for FFT or IFFT done with this object | |||
| - destination for FFT or IFFT done with this object | |||
| Returns: | |||
| Buffer start address | |||
| Throws: Nothing | |||
| ============================================================================== | |||
| */ | |||
| template <class DT> | |||
| typename FFTReal <DT>::DataType * FFTReal <DT>::use_buffer () const | |||
| { | |||
| return (&_buffer [0]); | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <class DT> | |||
| void FFTReal <DT>::init_br_lut () | |||
| { | |||
| const long length = 1L << _nbr_bits; | |||
| _br_lut.resize (length); | |||
| _br_lut [0] = 0; | |||
| long br_index = 0; | |||
| for (long cnt = 1; cnt < length; ++cnt) | |||
| { | |||
| // ++br_index (bit reversed) | |||
| long bit = length >> 1; | |||
| while (((br_index ^= bit) & bit) == 0) | |||
| { | |||
| bit >>= 1; | |||
| } | |||
| _br_lut [cnt] = br_index; | |||
| } | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::init_trigo_lut () | |||
| { | |||
| using namespace std; | |||
| if (_nbr_bits > 3) | |||
| { | |||
| const long total_len = (1L << (_nbr_bits - 1)) - 4; | |||
| _trigo_lut.resize (total_len); | |||
| for (int level = 3; level < _nbr_bits; ++level) | |||
| { | |||
| const long level_len = 1L << (level - 1); | |||
| DataType * const level_ptr = | |||
| &_trigo_lut [get_trigo_level_index (level)]; | |||
| const double mul = PI / (level_len << 1); | |||
| for (long i = 0; i < level_len; ++ i) | |||
| { | |||
| level_ptr [i] = static_cast <DataType> (cos (i * mul)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::init_trigo_osc () | |||
| { | |||
| const int nbr_osc = _nbr_bits - TRIGO_BD_LIMIT; | |||
| if (nbr_osc > 0) | |||
| { | |||
| _trigo_osc.resize (nbr_osc); | |||
| for (int osc_cnt = 0; osc_cnt < nbr_osc; ++osc_cnt) | |||
| { | |||
| OscType & osc = _trigo_osc [osc_cnt]; | |||
| const long len = 1L << (TRIGO_BD_LIMIT + osc_cnt); | |||
| const double mul = (0.5 * PI) / len; | |||
| osc.set_step (mul); | |||
| } | |||
| } | |||
| } | |||
| template <class DT> | |||
| const long * FFTReal <DT>::get_br_ptr () const | |||
| { | |||
| return (&_br_lut [0]); | |||
| } | |||
| template <class DT> | |||
| const typename FFTReal <DT>::DataType * FFTReal <DT>::get_trigo_ptr (int level) const | |||
| { | |||
| assert (level >= 3); | |||
| return (&_trigo_lut [get_trigo_level_index (level)]); | |||
| } | |||
| template <class DT> | |||
| long FFTReal <DT>::get_trigo_level_index (int level) const | |||
| { | |||
| assert (level >= 3); | |||
| return ((1L << (level - 1)) - 4); | |||
| } | |||
| // Transform in several passes | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_fft_general (DataType f [], const DataType x []) const | |||
| { | |||
| assert (f != 0); | |||
| assert (f != use_buffer ()); | |||
| assert (x != 0); | |||
| assert (x != use_buffer ()); | |||
| assert (x != f); | |||
| DataType * sf; | |||
| DataType * df; | |||
| if ((_nbr_bits & 1) != 0) | |||
| { | |||
| df = use_buffer (); | |||
| sf = f; | |||
| } | |||
| else | |||
| { | |||
| df = f; | |||
| sf = use_buffer (); | |||
| } | |||
| compute_direct_pass_1_2 (df, x); | |||
| compute_direct_pass_3 (sf, df); | |||
| for (int pass = 3; pass < _nbr_bits; ++ pass) | |||
| { | |||
| compute_direct_pass_n (df, sf, pass); | |||
| DataType * const temp_ptr = df; | |||
| df = sf; | |||
| sf = temp_ptr; | |||
| } | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_direct_pass_1_2 (DataType df [], const DataType x []) const | |||
| { | |||
| assert (df != 0); | |||
| assert (x != 0); | |||
| assert (df != x); | |||
| const long * const bit_rev_lut_ptr = get_br_ptr (); | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| const long rev_index_0 = bit_rev_lut_ptr [coef_index]; | |||
| const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1]; | |||
| const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2]; | |||
| const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3]; | |||
| DataType * const df2 = df + coef_index; | |||
| df2 [1] = x [rev_index_0] - x [rev_index_1]; | |||
| df2 [3] = x [rev_index_2] - x [rev_index_3]; | |||
| const DataType sf_0 = x [rev_index_0] + x [rev_index_1]; | |||
| const DataType sf_2 = x [rev_index_2] + x [rev_index_3]; | |||
| df2 [0] = sf_0 + sf_2; | |||
| df2 [2] = sf_0 - sf_2; | |||
| coef_index += 4; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_direct_pass_3 (DataType df [], const DataType sf []) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| const DataType sqrt2_2 = DataType (SQRT2 * 0.5); | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| DataType v; | |||
| df [coef_index] = sf [coef_index] + sf [coef_index + 4]; | |||
| df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4]; | |||
| df [coef_index + 2] = sf [coef_index + 2]; | |||
| df [coef_index + 6] = sf [coef_index + 6]; | |||
| v = (sf [coef_index + 5] - sf [coef_index + 7]) * sqrt2_2; | |||
| df [coef_index + 1] = sf [coef_index + 1] + v; | |||
| df [coef_index + 3] = sf [coef_index + 1] - v; | |||
| v = (sf [coef_index + 5] + sf [coef_index + 7]) * sqrt2_2; | |||
| df [coef_index + 5] = v + sf [coef_index + 3]; | |||
| df [coef_index + 7] = v - sf [coef_index + 3]; | |||
| coef_index += 8; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_direct_pass_n (DataType df [], const DataType sf [], int pass) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| assert (pass >= 3); | |||
| assert (pass < _nbr_bits); | |||
| if (pass <= TRIGO_BD_LIMIT) | |||
| { | |||
| compute_direct_pass_n_lut (df, sf, pass); | |||
| } | |||
| else | |||
| { | |||
| compute_direct_pass_n_osc (df, sf, pass); | |||
| } | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_direct_pass_n_lut (DataType df [], const DataType sf [], int pass) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| assert (pass >= 3); | |||
| assert (pass < _nbr_bits); | |||
| const long nbr_coef = 1 << pass; | |||
| const long h_nbr_coef = nbr_coef >> 1; | |||
| const long d_nbr_coef = nbr_coef << 1; | |||
| long coef_index = 0; | |||
| const DataType * const cos_ptr = get_trigo_ptr (pass); | |||
| do | |||
| { | |||
| const DataType * const sf1r = sf + coef_index; | |||
| const DataType * const sf2r = sf1r + nbr_coef; | |||
| DataType * const dfr = df + coef_index; | |||
| DataType * const dfi = dfr + nbr_coef; | |||
| // Extreme coefficients are always real | |||
| dfr [0] = sf1r [0] + sf2r [0]; | |||
| dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] = | |||
| dfr [h_nbr_coef] = sf1r [h_nbr_coef]; | |||
| dfi [h_nbr_coef] = sf2r [h_nbr_coef]; | |||
| // Others are conjugate complex numbers | |||
| const DataType * const sf1i = sf1r + h_nbr_coef; | |||
| const DataType * const sf2i = sf1i + nbr_coef; | |||
| for (long i = 1; i < h_nbr_coef; ++ i) | |||
| { | |||
| const DataType c = cos_ptr [i]; // cos (i*PI/nbr_coef); | |||
| const DataType s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); | |||
| DataType v; | |||
| v = sf2r [i] * c - sf2i [i] * s; | |||
| dfr [i] = sf1r [i] + v; | |||
| dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] = | |||
| v = sf2r [i] * s + sf2i [i] * c; | |||
| dfi [i] = v + sf1i [i]; | |||
| dfi [nbr_coef - i] = v - sf1i [i]; | |||
| } | |||
| coef_index += d_nbr_coef; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_direct_pass_n_osc (DataType df [], const DataType sf [], int pass) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| assert (pass > TRIGO_BD_LIMIT); | |||
| assert (pass < _nbr_bits); | |||
| const long nbr_coef = 1 << pass; | |||
| const long h_nbr_coef = nbr_coef >> 1; | |||
| const long d_nbr_coef = nbr_coef << 1; | |||
| long coef_index = 0; | |||
| OscType & osc = _trigo_osc [pass - (TRIGO_BD_LIMIT + 1)]; | |||
| do | |||
| { | |||
| const DataType * const sf1r = sf + coef_index; | |||
| const DataType * const sf2r = sf1r + nbr_coef; | |||
| DataType * const dfr = df + coef_index; | |||
| DataType * const dfi = dfr + nbr_coef; | |||
| osc.clear_buffers (); | |||
| // Extreme coefficients are always real | |||
| dfr [0] = sf1r [0] + sf2r [0]; | |||
| dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] = | |||
| dfr [h_nbr_coef] = sf1r [h_nbr_coef]; | |||
| dfi [h_nbr_coef] = sf2r [h_nbr_coef]; | |||
| // Others are conjugate complex numbers | |||
| const DataType * const sf1i = sf1r + h_nbr_coef; | |||
| const DataType * const sf2i = sf1i + nbr_coef; | |||
| for (long i = 1; i < h_nbr_coef; ++ i) | |||
| { | |||
| osc.step (); | |||
| const DataType c = osc.get_cos (); | |||
| const DataType s = osc.get_sin (); | |||
| DataType v; | |||
| v = sf2r [i] * c - sf2i [i] * s; | |||
| dfr [i] = sf1r [i] + v; | |||
| dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] = | |||
| v = sf2r [i] * s + sf2i [i] * c; | |||
| dfi [i] = v + sf1i [i]; | |||
| dfi [nbr_coef - i] = v - sf1i [i]; | |||
| } | |||
| coef_index += d_nbr_coef; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| // Transform in several pass | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_ifft_general (const DataType f [], DataType x []) const | |||
| { | |||
| assert (f != 0); | |||
| assert (f != use_buffer ()); | |||
| assert (x != 0); | |||
| assert (x != use_buffer ()); | |||
| assert (x != f); | |||
| DataType * sf = const_cast <DataType *> (f); | |||
| DataType * df; | |||
| DataType * df_temp; | |||
| if (_nbr_bits & 1) | |||
| { | |||
| df = use_buffer (); | |||
| df_temp = x; | |||
| } | |||
| else | |||
| { | |||
| df = x; | |||
| df_temp = use_buffer (); | |||
| } | |||
| for (int pass = _nbr_bits - 1; pass >= 3; -- pass) | |||
| { | |||
| compute_inverse_pass_n (df, sf, pass); | |||
| if (pass < _nbr_bits - 1) | |||
| { | |||
| DataType * const temp_ptr = df; | |||
| df = sf; | |||
| sf = temp_ptr; | |||
| } | |||
| else | |||
| { | |||
| sf = df; | |||
| df = df_temp; | |||
| } | |||
| } | |||
| compute_inverse_pass_3 (df, sf); | |||
| compute_inverse_pass_1_2 (x, df); | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_inverse_pass_n (DataType df [], const DataType sf [], int pass) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| assert (pass >= 3); | |||
| assert (pass < _nbr_bits); | |||
| if (pass <= TRIGO_BD_LIMIT) | |||
| { | |||
| compute_inverse_pass_n_lut (df, sf, pass); | |||
| } | |||
| else | |||
| { | |||
| compute_inverse_pass_n_osc (df, sf, pass); | |||
| } | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_inverse_pass_n_lut (DataType df [], const DataType sf [], int pass) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| assert (pass >= 3); | |||
| assert (pass < _nbr_bits); | |||
| const long nbr_coef = 1 << pass; | |||
| const long h_nbr_coef = nbr_coef >> 1; | |||
| const long d_nbr_coef = nbr_coef << 1; | |||
| long coef_index = 0; | |||
| const DataType * const cos_ptr = get_trigo_ptr (pass); | |||
| do | |||
| { | |||
| const DataType * const sfr = sf + coef_index; | |||
| const DataType * const sfi = sfr + nbr_coef; | |||
| DataType * const df1r = df + coef_index; | |||
| DataType * const df2r = df1r + nbr_coef; | |||
| // Extreme coefficients are always real | |||
| df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef] | |||
| df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef] | |||
| df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2; | |||
| df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2; | |||
| // Others are conjugate complex numbers | |||
| DataType * const df1i = df1r + h_nbr_coef; | |||
| DataType * const df2i = df1i + nbr_coef; | |||
| for (long i = 1; i < h_nbr_coef; ++ i) | |||
| { | |||
| df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i] | |||
| df1i [i] = sfi [i] - sfi [nbr_coef - i]; | |||
| const DataType c = cos_ptr [i]; // cos (i*PI/nbr_coef); | |||
| const DataType s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); | |||
| const DataType vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i] | |||
| const DataType vi = sfi [i] + sfi [nbr_coef - i]; | |||
| df2r [i] = vr * c + vi * s; | |||
| df2i [i] = vi * c - vr * s; | |||
| } | |||
| coef_index += d_nbr_coef; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_inverse_pass_n_osc (DataType df [], const DataType sf [], int pass) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| assert (pass > TRIGO_BD_LIMIT); | |||
| assert (pass < _nbr_bits); | |||
| const long nbr_coef = 1 << pass; | |||
| const long h_nbr_coef = nbr_coef >> 1; | |||
| const long d_nbr_coef = nbr_coef << 1; | |||
| long coef_index = 0; | |||
| OscType & osc = _trigo_osc [pass - (TRIGO_BD_LIMIT + 1)]; | |||
| do | |||
| { | |||
| const DataType * const sfr = sf + coef_index; | |||
| const DataType * const sfi = sfr + nbr_coef; | |||
| DataType * const df1r = df + coef_index; | |||
| DataType * const df2r = df1r + nbr_coef; | |||
| osc.clear_buffers (); | |||
| // Extreme coefficients are always real | |||
| df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef] | |||
| df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef] | |||
| df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2; | |||
| df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2; | |||
| // Others are conjugate complex numbers | |||
| DataType * const df1i = df1r + h_nbr_coef; | |||
| DataType * const df2i = df1i + nbr_coef; | |||
| for (long i = 1; i < h_nbr_coef; ++ i) | |||
| { | |||
| df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i] | |||
| df1i [i] = sfi [i] - sfi [nbr_coef - i]; | |||
| osc.step (); | |||
| const DataType c = osc.get_cos (); | |||
| const DataType s = osc.get_sin (); | |||
| const DataType vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i] | |||
| const DataType vi = sfi [i] + sfi [nbr_coef - i]; | |||
| df2r [i] = vr * c + vi * s; | |||
| df2i [i] = vi * c - vr * s; | |||
| } | |||
| coef_index += d_nbr_coef; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_inverse_pass_3 (DataType df [], const DataType sf []) const | |||
| { | |||
| assert (df != 0); | |||
| assert (sf != 0); | |||
| assert (df != sf); | |||
| const DataType sqrt2_2 = DataType (SQRT2 * 0.5); | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| df [coef_index] = sf [coef_index] + sf [coef_index + 4]; | |||
| df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4]; | |||
| df [coef_index + 2] = sf [coef_index + 2] * 2; | |||
| df [coef_index + 6] = sf [coef_index + 6] * 2; | |||
| df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3]; | |||
| df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7]; | |||
| const DataType vr = sf [coef_index + 1] - sf [coef_index + 3]; | |||
| const DataType vi = sf [coef_index + 5] + sf [coef_index + 7]; | |||
| df [coef_index + 5] = (vr + vi) * sqrt2_2; | |||
| df [coef_index + 7] = (vi - vr) * sqrt2_2; | |||
| coef_index += 8; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| template <class DT> | |||
| void FFTReal <DT>::compute_inverse_pass_1_2 (DataType x [], const DataType sf []) const | |||
| { | |||
| assert (x != 0); | |||
| assert (sf != 0); | |||
| assert (x != sf); | |||
| const long * bit_rev_lut_ptr = get_br_ptr (); | |||
| const DataType * sf2 = sf; | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| { | |||
| const DataType b_0 = sf2 [0] + sf2 [2]; | |||
| const DataType b_2 = sf2 [0] - sf2 [2]; | |||
| const DataType b_1 = sf2 [1] * 2; | |||
| const DataType b_3 = sf2 [3] * 2; | |||
| x [bit_rev_lut_ptr [0]] = b_0 + b_1; | |||
| x [bit_rev_lut_ptr [1]] = b_0 - b_1; | |||
| x [bit_rev_lut_ptr [2]] = b_2 + b_3; | |||
| x [bit_rev_lut_ptr [3]] = b_2 - b_3; | |||
| } | |||
| { | |||
| const DataType b_0 = sf2 [4] + sf2 [6]; | |||
| const DataType b_2 = sf2 [4] - sf2 [6]; | |||
| const DataType b_1 = sf2 [5] * 2; | |||
| const DataType b_3 = sf2 [7] * 2; | |||
| x [bit_rev_lut_ptr [4]] = b_0 + b_1; | |||
| x [bit_rev_lut_ptr [5]] = b_0 - b_1; | |||
| x [bit_rev_lut_ptr [6]] = b_2 + b_3; | |||
| x [bit_rev_lut_ptr [7]] = b_2 - b_3; | |||
| } | |||
| sf2 += 8; | |||
| coef_index += 8; | |||
| bit_rev_lut_ptr += 8; | |||
| } | |||
| while (coef_index < _length); | |||
| } | |||
| } // namespace ffft | |||
| #endif // ffft_FFTReal_CODEHEADER_INCLUDED | |||
| #undef ffft_FFTReal_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,131 @@ | |||
| /***************************************************************************** | |||
| FFTRealFixLen.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_FFTRealFixLen_HEADER_INCLUDED) | |||
| #define ffft_FFTRealFixLen_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "Array.h" | |||
| #include "DynArray.h" | |||
| #include "FFTRealFixLenParam.h" | |||
| #include "OscSinCos.h" | |||
| namespace ffft | |||
| { | |||
| template <int LL2> | |||
| class FFTRealFixLen | |||
| { | |||
| typedef int CompileTimeCheck1 [(LL2 >= 0) ? 1 : -1]; | |||
| typedef int CompileTimeCheck2 [(LL2 <= 30) ? 1 : -1]; | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| typedef FFTRealFixLenParam::DataType DataType; | |||
| typedef OscSinCos <DataType> OscType; | |||
| enum { FFT_LEN_L2 = LL2 }; | |||
| enum { FFT_LEN = 1 << FFT_LEN_L2 }; | |||
| FFTRealFixLen (); | |||
| inline long get_length () const; | |||
| void do_fft (DataType f [], const DataType x []); | |||
| void do_ifft (const DataType f [], DataType x []); | |||
| void rescale (DataType x []) const; | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| enum { TRIGO_BD_LIMIT = FFTRealFixLenParam::TRIGO_BD_LIMIT }; | |||
| enum { BR_ARR_SIZE_L2 = ((FFT_LEN_L2 - 3) < 0) ? 0 : (FFT_LEN_L2 - 2) }; | |||
| enum { BR_ARR_SIZE = 1 << BR_ARR_SIZE_L2 }; | |||
| enum { TRIGO_BD = ((FFT_LEN_L2 - TRIGO_BD_LIMIT) < 0) | |||
| ? (int)FFT_LEN_L2 | |||
| : (int)TRIGO_BD_LIMIT }; | |||
| enum { TRIGO_TABLE_ARR_SIZE_L2 = (LL2 < 4) ? 0 : (TRIGO_BD - 2) }; | |||
| enum { TRIGO_TABLE_ARR_SIZE = 1 << TRIGO_TABLE_ARR_SIZE_L2 }; | |||
| enum { NBR_TRIGO_OSC = FFT_LEN_L2 - TRIGO_BD }; | |||
| enum { TRIGO_OSC_ARR_SIZE = (NBR_TRIGO_OSC > 0) ? NBR_TRIGO_OSC : 1 }; | |||
| void build_br_lut (); | |||
| void build_trigo_lut (); | |||
| void build_trigo_osc (); | |||
| DynArray <DataType> | |||
| _buffer; | |||
| DynArray <long> | |||
| _br_data; | |||
| DynArray <DataType> | |||
| _trigo_data; | |||
| Array <OscType, TRIGO_OSC_ARR_SIZE> | |||
| _trigo_osc; | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| FFTRealFixLen (const FFTRealFixLen &other); | |||
| FFTRealFixLen& operator = (const FFTRealFixLen &other); | |||
| bool operator == (const FFTRealFixLen &other); | |||
| bool operator != (const FFTRealFixLen &other); | |||
| }; // class FFTRealFixLen | |||
| } // namespace ffft | |||
| #include "FFTRealFixLen.hpp" | |||
| #endif // ffft_FFTRealFixLen_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,323 @@ | |||
| /***************************************************************************** | |||
| FFTRealFixLen.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_FFTRealFixLen_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of FFTRealFixLen code header. | |||
| #endif | |||
| #define ffft_FFTRealFixLen_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_FFTRealFixLen_CODEHEADER_INCLUDED) | |||
| #define ffft_FFTRealFixLen_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "def.h" | |||
| #include "FFTRealPassDirect.h" | |||
| #include "FFTRealPassInverse.h" | |||
| #include "FFTRealSelect.h" | |||
| #include <cassert> | |||
| #include <cmath> | |||
| namespace std { } | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <int LL2> | |||
| FFTRealFixLen <LL2>::FFTRealFixLen () | |||
| : _buffer (FFT_LEN) | |||
| , _br_data (BR_ARR_SIZE) | |||
| , _trigo_data (TRIGO_TABLE_ARR_SIZE) | |||
| , _trigo_osc () | |||
| { | |||
| build_br_lut (); | |||
| build_trigo_lut (); | |||
| build_trigo_osc (); | |||
| } | |||
| template <int LL2> | |||
| long FFTRealFixLen <LL2>::get_length () const | |||
| { | |||
| return (FFT_LEN); | |||
| } | |||
| // General case | |||
| template <int LL2> | |||
| void FFTRealFixLen <LL2>::do_fft (DataType f [], const DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| assert (x != f); | |||
| assert (FFT_LEN_L2 >= 3); | |||
| // Do the transform in several passes | |||
| const DataType * cos_ptr = &_trigo_data [0]; | |||
| const long * br_ptr = &_br_data [0]; | |||
| FFTRealPassDirect <FFT_LEN_L2 - 1>::process ( | |||
| FFT_LEN, | |||
| f, | |||
| &_buffer [0], | |||
| x, | |||
| cos_ptr, | |||
| TRIGO_TABLE_ARR_SIZE, | |||
| br_ptr, | |||
| &_trigo_osc [0] | |||
| ); | |||
| } | |||
| // 4-point FFT | |||
| template <> | |||
| inline void FFTRealFixLen <2>::do_fft (DataType f [], const DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| assert (x != f); | |||
| f [1] = x [0] - x [2]; | |||
| f [3] = x [1] - x [3]; | |||
| const DataType b_0 = x [0] + x [2]; | |||
| const DataType b_2 = x [1] + x [3]; | |||
| f [0] = b_0 + b_2; | |||
| f [2] = b_0 - b_2; | |||
| } | |||
| // 2-point FFT | |||
| template <> | |||
| inline void FFTRealFixLen <1>::do_fft (DataType f [], const DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| assert (x != f); | |||
| f [0] = x [0] + x [1]; | |||
| f [1] = x [0] - x [1]; | |||
| } | |||
| // 1-point FFT | |||
| template <> | |||
| inline void FFTRealFixLen <0>::do_fft (DataType f [], const DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| f [0] = x [0]; | |||
| } | |||
| // General case | |||
| template <int LL2> | |||
| void FFTRealFixLen <LL2>::do_ifft (const DataType f [], DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| assert (x != f); | |||
| assert (FFT_LEN_L2 >= 3); | |||
| // Do the transform in several passes | |||
| DataType * s_ptr = | |||
| FFTRealSelect <FFT_LEN_L2 & 1>::sel_bin (&_buffer [0], x); | |||
| DataType * d_ptr = | |||
| FFTRealSelect <FFT_LEN_L2 & 1>::sel_bin (x, &_buffer [0]); | |||
| const DataType * cos_ptr = &_trigo_data [0]; | |||
| const long * br_ptr = &_br_data [0]; | |||
| FFTRealPassInverse <FFT_LEN_L2 - 1>::process ( | |||
| FFT_LEN, | |||
| d_ptr, | |||
| s_ptr, | |||
| f, | |||
| cos_ptr, | |||
| TRIGO_TABLE_ARR_SIZE, | |||
| br_ptr, | |||
| &_trigo_osc [0] | |||
| ); | |||
| } | |||
| // 4-point IFFT | |||
| template <> | |||
| inline void FFTRealFixLen <2>::do_ifft (const DataType f [], DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| assert (x != f); | |||
| const DataType b_0 = f [0] + f [2]; | |||
| const DataType b_2 = f [0] - f [2]; | |||
| x [0] = b_0 + f [1] * 2; | |||
| x [2] = b_0 - f [1] * 2; | |||
| x [1] = b_2 + f [3] * 2; | |||
| x [3] = b_2 - f [3] * 2; | |||
| } | |||
| // 2-point IFFT | |||
| template <> | |||
| inline void FFTRealFixLen <1>::do_ifft (const DataType f [], DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| assert (x != f); | |||
| x [0] = f [0] + f [1]; | |||
| x [1] = f [0] - f [1]; | |||
| } | |||
| // 1-point IFFT | |||
| template <> | |||
| inline void FFTRealFixLen <0>::do_ifft (const DataType f [], DataType x []) | |||
| { | |||
| assert (f != 0); | |||
| assert (x != 0); | |||
| assert (x != f); | |||
| x [0] = f [0]; | |||
| } | |||
| template <int LL2> | |||
| void FFTRealFixLen <LL2>::rescale (DataType x []) const | |||
| { | |||
| assert (x != 0); | |||
| const DataType mul = DataType (1.0 / FFT_LEN); | |||
| if (FFT_LEN < 4) | |||
| { | |||
| long i = FFT_LEN - 1; | |||
| do | |||
| { | |||
| x [i] *= mul; | |||
| --i; | |||
| } | |||
| while (i >= 0); | |||
| } | |||
| else | |||
| { | |||
| assert ((FFT_LEN & 3) == 0); | |||
| // Could be optimized with SIMD instruction sets (needs alignment check) | |||
| long i = FFT_LEN - 4; | |||
| do | |||
| { | |||
| x [i + 0] *= mul; | |||
| x [i + 1] *= mul; | |||
| x [i + 2] *= mul; | |||
| x [i + 3] *= mul; | |||
| i -= 4; | |||
| } | |||
| while (i >= 0); | |||
| } | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <int LL2> | |||
| void FFTRealFixLen <LL2>::build_br_lut () | |||
| { | |||
| _br_data [0] = 0; | |||
| for (long cnt = 1; cnt < BR_ARR_SIZE; ++cnt) | |||
| { | |||
| long index = cnt << 2; | |||
| long br_index = 0; | |||
| int bit_cnt = FFT_LEN_L2; | |||
| do | |||
| { | |||
| br_index <<= 1; | |||
| br_index += (index & 1); | |||
| index >>= 1; | |||
| -- bit_cnt; | |||
| } | |||
| while (bit_cnt > 0); | |||
| _br_data [cnt] = br_index; | |||
| } | |||
| } | |||
| template <int LL2> | |||
| void FFTRealFixLen <LL2>::build_trigo_lut () | |||
| { | |||
| const double mul = (0.5 * PI) / TRIGO_TABLE_ARR_SIZE; | |||
| for (long i = 0; i < TRIGO_TABLE_ARR_SIZE; ++ i) | |||
| { | |||
| using namespace std; | |||
| _trigo_data [i] = DataType (cos (i * mul)); | |||
| } | |||
| } | |||
| template <int LL2> | |||
| void FFTRealFixLen <LL2>::build_trigo_osc () | |||
| { | |||
| for (int i = 0; i < NBR_TRIGO_OSC; ++i) | |||
| { | |||
| OscType & osc = _trigo_osc [i]; | |||
| const long len = static_cast <long> (TRIGO_TABLE_ARR_SIZE) << (i + 1); | |||
| const double mul = (0.5 * PI) / len; | |||
| osc.set_step (mul); | |||
| } | |||
| } | |||
| } // namespace ffft | |||
| #endif // ffft_FFTRealFixLen_CODEHEADER_INCLUDED | |||
| #undef ffft_FFTRealFixLen_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,90 @@ | |||
| /***************************************************************************** | |||
| FFTRealFixLenParam.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_FFTRealFixLenParam_HEADER_INCLUDED) | |||
| #define ffft_FFTRealFixLenParam_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| namespace ffft | |||
| { | |||
| class FFTRealFixLenParam | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| // Over this bit depth, we use direct calculation for sin/cos | |||
| enum { TRIGO_BD_LIMIT = 12 }; | |||
| typedef float DataType; | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| FFTRealFixLenParam (); | |||
| FFTRealFixLenParam (const FFTRealFixLenParam &other); | |||
| FFTRealFixLenParam & | |||
| operator = (const FFTRealFixLenParam &other); | |||
| bool operator == (const FFTRealFixLenParam &other); | |||
| bool operator != (const FFTRealFixLenParam &other); | |||
| }; // class FFTRealFixLenParam | |||
| } // namespace ffft | |||
| //#include "ffft/FFTRealFixLenParam.hpp" | |||
| #endif // ffft_FFTRealFixLenParam_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,96 @@ | |||
| /***************************************************************************** | |||
| FFTRealPassDirect.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_FFTRealPassDirect_HEADER_INCLUDED) | |||
| #define ffft_FFTRealPassDirect_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "def.h" | |||
| #include "FFTRealFixLenParam.h" | |||
| #include "OscSinCos.h" | |||
| namespace ffft | |||
| { | |||
| template <int PASS> | |||
| class FFTRealPassDirect | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| typedef FFTRealFixLenParam::DataType DataType; | |||
| typedef OscSinCos <DataType> OscType; | |||
| ffft_FORCEINLINE static void | |||
| process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []); | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| FFTRealPassDirect (); | |||
| FFTRealPassDirect (const FFTRealPassDirect &other); | |||
| FFTRealPassDirect & | |||
| operator = (const FFTRealPassDirect &other); | |||
| bool operator == (const FFTRealPassDirect &other); | |||
| bool operator != (const FFTRealPassDirect &other); | |||
| }; // class FFTRealPassDirect | |||
| } // namespace ffft | |||
| #include "FFTRealPassDirect.hpp" | |||
| #endif // ffft_FFTRealPassDirect_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,205 @@ | |||
| /***************************************************************************** | |||
| FFTRealPassDirect.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_FFTRealPassDirect_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of FFTRealPassDirect code header. | |||
| #endif | |||
| #define ffft_FFTRealPassDirect_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_FFTRealPassDirect_CODEHEADER_INCLUDED) | |||
| #define ffft_FFTRealPassDirect_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "FFTRealUseTrigo.h" | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <> | |||
| inline void FFTRealPassDirect <1>::process (long len, DataType dest_ptr [], DataType [], const DataType x_ptr [], const DataType [], long , const long br_ptr [], OscType []) | |||
| { | |||
| // First and second pass at once | |||
| const long qlen = len >> 2; | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| // To do: unroll the loop (2x). | |||
| const long ri_0 = br_ptr [coef_index >> 2]; | |||
| const long ri_1 = ri_0 + 2 * qlen; // bit_rev_lut_ptr [coef_index + 1]; | |||
| const long ri_2 = ri_0 + 1 * qlen; // bit_rev_lut_ptr [coef_index + 2]; | |||
| const long ri_3 = ri_0 + 3 * qlen; // bit_rev_lut_ptr [coef_index + 3]; | |||
| DataType * const df2 = dest_ptr + coef_index; | |||
| df2 [1] = x_ptr [ri_0] - x_ptr [ri_1]; | |||
| df2 [3] = x_ptr [ri_2] - x_ptr [ri_3]; | |||
| const DataType sf_0 = x_ptr [ri_0] + x_ptr [ri_1]; | |||
| const DataType sf_2 = x_ptr [ri_2] + x_ptr [ri_3]; | |||
| df2 [0] = sf_0 + sf_2; | |||
| df2 [2] = sf_0 - sf_2; | |||
| coef_index += 4; | |||
| } | |||
| while (coef_index < len); | |||
| } | |||
| template <> | |||
| inline void FFTRealPassDirect <2>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
| { | |||
| // Executes "previous" passes first. Inverts source and destination buffers | |||
| FFTRealPassDirect <1>::process ( | |||
| len, | |||
| src_ptr, | |||
| dest_ptr, | |||
| x_ptr, | |||
| cos_ptr, | |||
| cos_len, | |||
| br_ptr, | |||
| osc_list | |||
| ); | |||
| // Third pass | |||
| const DataType sqrt2_2 = DataType (SQRT2 * 0.5); | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| dest_ptr [coef_index ] = src_ptr [coef_index] + src_ptr [coef_index + 4]; | |||
| dest_ptr [coef_index + 4] = src_ptr [coef_index] - src_ptr [coef_index + 4]; | |||
| dest_ptr [coef_index + 2] = src_ptr [coef_index + 2]; | |||
| dest_ptr [coef_index + 6] = src_ptr [coef_index + 6]; | |||
| DataType v; | |||
| v = (src_ptr [coef_index + 5] - src_ptr [coef_index + 7]) * sqrt2_2; | |||
| dest_ptr [coef_index + 1] = src_ptr [coef_index + 1] + v; | |||
| dest_ptr [coef_index + 3] = src_ptr [coef_index + 1] - v; | |||
| v = (src_ptr [coef_index + 5] + src_ptr [coef_index + 7]) * sqrt2_2; | |||
| dest_ptr [coef_index + 5] = v + src_ptr [coef_index + 3]; | |||
| dest_ptr [coef_index + 7] = v - src_ptr [coef_index + 3]; | |||
| coef_index += 8; | |||
| } | |||
| while (coef_index < len); | |||
| } | |||
| template <int PASS> | |||
| void FFTRealPassDirect <PASS>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
| { | |||
| // Executes "previous" passes first. Inverts source and destination buffers | |||
| FFTRealPassDirect <PASS - 1>::process ( | |||
| len, | |||
| src_ptr, | |||
| dest_ptr, | |||
| x_ptr, | |||
| cos_ptr, | |||
| cos_len, | |||
| br_ptr, | |||
| osc_list | |||
| ); | |||
| const long dist = 1L << (PASS - 1); | |||
| const long c1_r = 0; | |||
| const long c1_i = dist; | |||
| const long c2_r = dist * 2; | |||
| const long c2_i = dist * 3; | |||
| const long cend = dist * 4; | |||
| const long table_step = cos_len >> (PASS - 1); | |||
| enum { TRIGO_OSC = PASS - FFTRealFixLenParam::TRIGO_BD_LIMIT }; | |||
| enum { TRIGO_DIRECT = (TRIGO_OSC >= 0) ? 1 : 0 }; | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| const DataType * const sf = src_ptr + coef_index; | |||
| DataType * const df = dest_ptr + coef_index; | |||
| // Extreme coefficients are always real | |||
| df [c1_r] = sf [c1_r] + sf [c2_r]; | |||
| df [c2_r] = sf [c1_r] - sf [c2_r]; | |||
| df [c1_i] = sf [c1_i]; | |||
| df [c2_i] = sf [c2_i]; | |||
| FFTRealUseTrigo <TRIGO_DIRECT>::prepare (osc_list [TRIGO_OSC]); | |||
| // Others are conjugate complex numbers | |||
| for (long i = 1; i < dist; ++ i) | |||
| { | |||
| DataType c; | |||
| DataType s; | |||
| FFTRealUseTrigo <TRIGO_DIRECT>::iterate ( | |||
| osc_list [TRIGO_OSC], | |||
| c, | |||
| s, | |||
| cos_ptr, | |||
| i * table_step, | |||
| (dist - i) * table_step | |||
| ); | |||
| const DataType sf_r_i = sf [c1_r + i]; | |||
| const DataType sf_i_i = sf [c1_i + i]; | |||
| const DataType v1 = sf [c2_r + i] * c - sf [c2_i + i] * s; | |||
| df [c1_r + i] = sf_r_i + v1; | |||
| df [c2_r - i] = sf_r_i - v1; | |||
| const DataType v2 = sf [c2_r + i] * s + sf [c2_i + i] * c; | |||
| df [c2_r + i] = v2 + sf_i_i; | |||
| df [cend - i] = v2 - sf_i_i; | |||
| } | |||
| coef_index += cend; | |||
| } | |||
| while (coef_index < len); | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| } // namespace ffft | |||
| #endif // ffft_FFTRealPassDirect_CODEHEADER_INCLUDED | |||
| #undef ffft_FFTRealPassDirect_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,101 @@ | |||
| /***************************************************************************** | |||
| FFTRealPassInverse.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_FFTRealPassInverse_HEADER_INCLUDED) | |||
| #define ffft_FFTRealPassInverse_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "def.h" | |||
| #include "FFTRealFixLenParam.h" | |||
| #include "OscSinCos.h" | |||
| namespace ffft | |||
| { | |||
| template <int PASS> | |||
| class FFTRealPassInverse | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| typedef FFTRealFixLenParam::DataType DataType; | |||
| typedef OscSinCos <DataType> OscType; | |||
| ffft_FORCEINLINE static void | |||
| process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType f_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []); | |||
| ffft_FORCEINLINE static void | |||
| process_rec (long len, DataType dest_ptr [], DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []); | |||
| ffft_FORCEINLINE static void | |||
| process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []); | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| FFTRealPassInverse (); | |||
| FFTRealPassInverse (const FFTRealPassInverse &other); | |||
| FFTRealPassInverse & | |||
| operator = (const FFTRealPassInverse &other); | |||
| bool operator == (const FFTRealPassInverse &other); | |||
| bool operator != (const FFTRealPassInverse &other); | |||
| }; // class FFTRealPassInverse | |||
| } // namespace ffft | |||
| #include "FFTRealPassInverse.hpp" | |||
| #endif // ffft_FFTRealPassInverse_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,230 @@ | |||
| /***************************************************************************** | |||
| FFTRealPassInverse.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_FFTRealPassInverse_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of FFTRealPassInverse code header. | |||
| #endif | |||
| #define ffft_FFTRealPassInverse_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_FFTRealPassInverse_CODEHEADER_INCLUDED) | |||
| #define ffft_FFTRealPassInverse_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "FFTRealUseTrigo.h" | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <int PASS> | |||
| void FFTRealPassInverse <PASS>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType f_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
| { | |||
| process_internal ( | |||
| len, | |||
| dest_ptr, | |||
| f_ptr, | |||
| cos_ptr, | |||
| cos_len, | |||
| br_ptr, | |||
| osc_list | |||
| ); | |||
| FFTRealPassInverse <PASS - 1>::process_rec ( | |||
| len, | |||
| src_ptr, | |||
| dest_ptr, | |||
| cos_ptr, | |||
| cos_len, | |||
| br_ptr, | |||
| osc_list | |||
| ); | |||
| } | |||
| template <int PASS> | |||
| void FFTRealPassInverse <PASS>::process_rec (long len, DataType dest_ptr [], DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
| { | |||
| process_internal ( | |||
| len, | |||
| dest_ptr, | |||
| src_ptr, | |||
| cos_ptr, | |||
| cos_len, | |||
| br_ptr, | |||
| osc_list | |||
| ); | |||
| FFTRealPassInverse <PASS - 1>::process_rec ( | |||
| len, | |||
| src_ptr, | |||
| dest_ptr, | |||
| cos_ptr, | |||
| cos_len, | |||
| br_ptr, | |||
| osc_list | |||
| ); | |||
| } | |||
| template <> | |||
| inline void FFTRealPassInverse <0>::process_rec (long , DataType [], DataType [], const DataType [], long , const long [], OscType []) | |||
| { | |||
| // Stops recursion | |||
| } | |||
| template <int PASS> | |||
| void FFTRealPassInverse <PASS>::process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
| { | |||
| const long dist = 1L << (PASS - 1); | |||
| const long c1_r = 0; | |||
| const long c1_i = dist; | |||
| const long c2_r = dist * 2; | |||
| const long c2_i = dist * 3; | |||
| const long cend = dist * 4; | |||
| const long table_step = cos_len >> (PASS - 1); | |||
| enum { TRIGO_OSC = PASS - FFTRealFixLenParam::TRIGO_BD_LIMIT }; | |||
| enum { TRIGO_DIRECT = (TRIGO_OSC >= 0) ? 1 : 0 }; | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| const DataType * const sf = src_ptr + coef_index; | |||
| DataType * const df = dest_ptr + coef_index; | |||
| // Extreme coefficients are always real | |||
| df [c1_r] = sf [c1_r] + sf [c2_r]; | |||
| df [c2_r] = sf [c1_r] - sf [c2_r]; | |||
| df [c1_i] = sf [c1_i] * 2; | |||
| df [c2_i] = sf [c2_i] * 2; | |||
| FFTRealUseTrigo <TRIGO_DIRECT>::prepare (osc_list [TRIGO_OSC]); | |||
| // Others are conjugate complex numbers | |||
| for (long i = 1; i < dist; ++ i) | |||
| { | |||
| df [c1_r + i] = sf [c1_r + i] + sf [c2_r - i]; | |||
| df [c1_i + i] = sf [c2_r + i] - sf [cend - i]; | |||
| DataType c; | |||
| DataType s; | |||
| FFTRealUseTrigo <TRIGO_DIRECT>::iterate ( | |||
| osc_list [TRIGO_OSC], | |||
| c, | |||
| s, | |||
| cos_ptr, | |||
| i * table_step, | |||
| (dist - i) * table_step | |||
| ); | |||
| const DataType vr = sf [c1_r + i] - sf [c2_r - i]; | |||
| const DataType vi = sf [c2_r + i] + sf [cend - i]; | |||
| df [c2_r + i] = vr * c + vi * s; | |||
| df [c2_i + i] = vi * c - vr * s; | |||
| } | |||
| coef_index += cend; | |||
| } | |||
| while (coef_index < len); | |||
| } | |||
| template <> | |||
| inline void FFTRealPassInverse <2>::process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType [], long , const long [], OscType []) | |||
| { | |||
| // Antepenultimate pass | |||
| const DataType sqrt2_2 = DataType (SQRT2 * 0.5); | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| dest_ptr [coef_index ] = src_ptr [coef_index] + src_ptr [coef_index + 4]; | |||
| dest_ptr [coef_index + 4] = src_ptr [coef_index] - src_ptr [coef_index + 4]; | |||
| dest_ptr [coef_index + 2] = src_ptr [coef_index + 2] * 2; | |||
| dest_ptr [coef_index + 6] = src_ptr [coef_index + 6] * 2; | |||
| dest_ptr [coef_index + 1] = src_ptr [coef_index + 1] + src_ptr [coef_index + 3]; | |||
| dest_ptr [coef_index + 3] = src_ptr [coef_index + 5] - src_ptr [coef_index + 7]; | |||
| const DataType vr = src_ptr [coef_index + 1] - src_ptr [coef_index + 3]; | |||
| const DataType vi = src_ptr [coef_index + 5] + src_ptr [coef_index + 7]; | |||
| dest_ptr [coef_index + 5] = (vr + vi) * sqrt2_2; | |||
| dest_ptr [coef_index + 7] = (vi - vr) * sqrt2_2; | |||
| coef_index += 8; | |||
| } | |||
| while (coef_index < len); | |||
| } | |||
| template <> | |||
| inline void FFTRealPassInverse <1>::process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType [], long , const long br_ptr [], OscType []) | |||
| { | |||
| // Penultimate and last pass at once | |||
| const long qlen = len >> 2; | |||
| long coef_index = 0; | |||
| do | |||
| { | |||
| const long ri_0 = br_ptr [coef_index >> 2]; | |||
| const DataType b_0 = src_ptr [coef_index ] + src_ptr [coef_index + 2]; | |||
| const DataType b_2 = src_ptr [coef_index ] - src_ptr [coef_index + 2]; | |||
| const DataType b_1 = src_ptr [coef_index + 1] * 2; | |||
| const DataType b_3 = src_ptr [coef_index + 3] * 2; | |||
| dest_ptr [ri_0 ] = b_0 + b_1; | |||
| dest_ptr [ri_0 + 2 * qlen] = b_0 - b_1; | |||
| dest_ptr [ri_0 + 1 * qlen] = b_2 + b_3; | |||
| dest_ptr [ri_0 + 3 * qlen] = b_2 - b_3; | |||
| coef_index += 4; | |||
| } | |||
| while (coef_index < len); | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| } // namespace ffft | |||
| #endif // ffft_FFTRealPassInverse_CODEHEADER_INCLUDED | |||
| #undef ffft_FFTRealPassInverse_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,78 @@ | |||
| /***************************************************************************** | |||
| FFTRealSelect.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_FFTRealSelect_HEADER_INCLUDED) | |||
| #define ffft_FFTRealSelect_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "def.h" | |||
| namespace ffft | |||
| { | |||
| template <int P> | |||
| class FFTRealSelect | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| ffft_FORCEINLINE static float * | |||
| sel_bin (float *e_ptr, float *o_ptr); | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| FFTRealSelect (); | |||
| ~FFTRealSelect (); | |||
| FFTRealSelect (const FFTRealSelect &other); | |||
| FFTRealSelect& operator = (const FFTRealSelect &other); | |||
| bool operator == (const FFTRealSelect &other); | |||
| bool operator != (const FFTRealSelect &other); | |||
| }; // class FFTRealSelect | |||
| } // namespace ffft | |||
| #include "FFTRealSelect.hpp" | |||
| #endif // ffft_FFTRealSelect_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,63 @@ | |||
| /***************************************************************************** | |||
| FFTRealSelect.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_FFTRealSelect_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of FFTRealSelect code header. | |||
| #endif | |||
| #define ffft_FFTRealSelect_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_FFTRealSelect_CODEHEADER_INCLUDED) | |||
| #define ffft_FFTRealSelect_CODEHEADER_INCLUDED | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <int P> | |||
| float * FFTRealSelect <P>::sel_bin (float *e_ptr, float *o_ptr) | |||
| { | |||
| return (o_ptr); | |||
| } | |||
| template <> | |||
| inline float * FFTRealSelect <0>::sel_bin (float *e_ptr, float *) | |||
| { | |||
| return (e_ptr); | |||
| } | |||
| } // namespace ffft | |||
| #endif // ffft_FFTRealSelect_CODEHEADER_INCLUDED | |||
| #undef ffft_FFTRealSelect_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,99 @@ | |||
| /***************************************************************************** | |||
| FFTRealUseTrigo.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_FFTRealUseTrigo_HEADER_INCLUDED) | |||
| #define ffft_FFTRealUseTrigo_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "def.h" | |||
| #include "FFTRealFixLenParam.h" | |||
| #include "OscSinCos.h" | |||
| namespace ffft | |||
| { | |||
| template <int ALGO> | |||
| class FFTRealUseTrigo | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| typedef FFTRealFixLenParam::DataType DataType; | |||
| typedef OscSinCos <DataType> OscType; | |||
| ffft_FORCEINLINE static void | |||
| prepare (OscType &osc); | |||
| ffft_FORCEINLINE static void | |||
| iterate (OscType &osc, DataType &c, DataType &s, const DataType cos_ptr [], long index_c, long index_s); | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| FFTRealUseTrigo (); | |||
| ~FFTRealUseTrigo (); | |||
| FFTRealUseTrigo (const FFTRealUseTrigo &other); | |||
| FFTRealUseTrigo & | |||
| operator = (const FFTRealUseTrigo &other); | |||
| bool operator == (const FFTRealUseTrigo &other); | |||
| bool operator != (const FFTRealUseTrigo &other); | |||
| }; // class FFTRealUseTrigo | |||
| } // namespace ffft | |||
| #include "FFTRealUseTrigo.hpp" | |||
| #endif // ffft_FFTRealUseTrigo_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,92 @@ | |||
| /***************************************************************************** | |||
| FFTRealUseTrigo.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_FFTRealUseTrigo_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of FFTRealUseTrigo code header. | |||
| #endif | |||
| #define ffft_FFTRealUseTrigo_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED) | |||
| #define ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "OscSinCos.h" | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <int ALGO> | |||
| void FFTRealUseTrigo <ALGO>::prepare (OscType &osc) | |||
| { | |||
| osc.clear_buffers (); | |||
| } | |||
| template <> | |||
| inline void FFTRealUseTrigo <0>::prepare (OscType &) | |||
| { | |||
| // Nothing | |||
| } | |||
| template <int ALGO> | |||
| void FFTRealUseTrigo <ALGO>::iterate (OscType &osc, DataType &c, DataType &s, const DataType cos_ptr [], long index_c, long index_s) | |||
| { | |||
| osc.step (); | |||
| c = osc.get_cos (); | |||
| s = osc.get_sin (); | |||
| } | |||
| template <> | |||
| inline void FFTRealUseTrigo <0>::iterate (OscType &, DataType &c, DataType &s, const DataType cos_ptr [], long index_c, long index_s) | |||
| { | |||
| c = cos_ptr [index_c]; | |||
| s = cos_ptr [index_s]; | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| } // namespace ffft | |||
| #endif // ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED | |||
| #undef ffft_FFTRealUseTrigo_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,107 @@ | |||
| /***************************************************************************** | |||
| OscSinCos.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_OscSinCos_HEADER_INCLUDED) | |||
| #define ffft_OscSinCos_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include "def.h" | |||
| namespace ffft | |||
| { | |||
| template <class T> | |||
| class OscSinCos | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| public: | |||
| typedef T DataType; | |||
| OscSinCos (); | |||
| ffft_FORCEINLINE void | |||
| set_step (double angle_rad); | |||
| ffft_FORCEINLINE DataType | |||
| get_cos () const; | |||
| ffft_FORCEINLINE DataType | |||
| get_sin () const; | |||
| ffft_FORCEINLINE void | |||
| step (); | |||
| ffft_FORCEINLINE void | |||
| clear_buffers (); | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| protected: | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| DataType _pos_cos; // Current phase expressed with sin and cos. [-1 ; 1] | |||
| DataType _pos_sin; // - | |||
| DataType _step_cos; // Phase increment per step, [-1 ; 1] | |||
| DataType _step_sin; // - | |||
| /*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| private: | |||
| OscSinCos (const OscSinCos &other); | |||
| OscSinCos & operator = (const OscSinCos &other); | |||
| bool operator == (const OscSinCos &other); | |||
| bool operator != (const OscSinCos &other); | |||
| }; // class OscSinCos | |||
| } // namespace ffft | |||
| #include "OscSinCos.hpp" | |||
| #endif // ffft_OscSinCos_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,123 @@ | |||
| /***************************************************************************** | |||
| OscSinCos.hpp | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if defined (ffft_OscSinCos_CURRENT_CODEHEADER) | |||
| #error Recursive inclusion of OscSinCos code header. | |||
| #endif | |||
| #define ffft_OscSinCos_CURRENT_CODEHEADER | |||
| #if ! defined (ffft_OscSinCos_CODEHEADER_INCLUDED) | |||
| #define ffft_OscSinCos_CODEHEADER_INCLUDED | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| #include <cmath> | |||
| namespace std { } | |||
| namespace ffft | |||
| { | |||
| /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| template <class T> | |||
| OscSinCos <T>::OscSinCos () | |||
| : _pos_cos (1) | |||
| , _pos_sin (0) | |||
| , _step_cos (1) | |||
| , _step_sin (0) | |||
| { | |||
| // Nothing | |||
| } | |||
| template <class T> | |||
| void OscSinCos <T>::set_step (double angle_rad) | |||
| { | |||
| using namespace std; | |||
| _step_cos = static_cast <DataType> (cos (angle_rad)); | |||
| _step_sin = static_cast <DataType> (sin (angle_rad)); | |||
| } | |||
| template <class T> | |||
| typename OscSinCos <T>::DataType OscSinCos <T>::get_cos () const | |||
| { | |||
| return (_pos_cos); | |||
| } | |||
| template <class T> | |||
| typename OscSinCos <T>::DataType OscSinCos <T>::get_sin () const | |||
| { | |||
| return (_pos_sin); | |||
| } | |||
| template <class T> | |||
| void OscSinCos <T>::step () | |||
| { | |||
| const DataType old_cos = _pos_cos; | |||
| const DataType old_sin = _pos_sin; | |||
| _pos_cos = old_cos * _step_cos - old_sin * _step_sin; | |||
| _pos_sin = old_cos * _step_sin + old_sin * _step_cos; | |||
| } | |||
| template <class T> | |||
| void OscSinCos <T>::clear_buffers () | |||
| { | |||
| _pos_cos = static_cast <DataType> (1); | |||
| _pos_sin = static_cast <DataType> (0); | |||
| } | |||
| /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| } // namespace ffft | |||
| #endif // ffft_OscSinCos_CODEHEADER_INCLUDED | |||
| #undef ffft_OscSinCos_CURRENT_CODEHEADER | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,60 @@ | |||
| /***************************************************************************** | |||
| def.h | |||
| By Laurent de Soras | |||
| --- Legal stuff --- | |||
| This program is free software. It comes without any warranty, to | |||
| the extent permitted by applicable law. You can redistribute it | |||
| and/or modify it under the terms of the Do What The Fuck You Want | |||
| To Public License, Version 2, as published by Sam Hocevar. See | |||
| http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| *Tab=3***********************************************************************/ | |||
| #if ! defined (ffft_def_HEADER_INCLUDED) | |||
| #define ffft_def_HEADER_INCLUDED | |||
| #if defined (_MSC_VER) | |||
| #pragma once | |||
| #pragma warning (4 : 4250) // "Inherits via dominance." | |||
| #endif | |||
| /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| namespace ffft | |||
| { | |||
| const double PI = 3.1415926535897932384626433832795; | |||
| const double SQRT2 = 1.41421356237309514547462185873883; | |||
| #if defined (_MSC_VER) | |||
| #define ffft_FORCEINLINE __forceinline | |||
| #else | |||
| #define ffft_FORCEINLINE inline | |||
| #endif | |||
| } // namespace ffft | |||
| #endif // ffft_def_HEADER_INCLUDED | |||
| /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
| @@ -0,0 +1,14 @@ | |||
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |||
| Version 2, December 2004 | |||
| Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | |||
| Everyone is permitted to copy and distribute verbatim or modified | |||
| copies of this license document, and changing it is allowed as long | |||
| as the name is changed. | |||
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |||
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |||
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |||
| @@ -0,0 +1,293 @@ | |||
| ============================================================================== | |||
| FFTReal | |||
| Version 2.11 | |||
| Fourier transformation (FFT, IFFT) library specialised for real data | |||
| Portable ISO C++ | |||
| Copyright (c) 1999-2010 Laurent de Soras | |||
| Object Pascal port (c) Frederic Vanmol | |||
| ============================================================================== | |||
| Contents: | |||
| 1. Legal | |||
| 2. Content | |||
| 3. Using FFTReal | |||
| 3.1 FFTReal - Length fixed at run-time | |||
| 3.2 FFTRealFixLen - Length fixed at compile-time | |||
| 3.3 Data organisation | |||
| 4. Compilation and testing | |||
| 5. History | |||
| 6. Contact | |||
| 1. Legal | |||
| -------- | |||
| FFTReal is distributed under the terms of the Do What The Fuck You Want To | |||
| Public License. | |||
| Check the file license.txt to get full information about the license. | |||
| 2. Content | |||
| ---------- | |||
| FFTReal is a library to compute Discrete Fourier Transforms (DFT) with the | |||
| FFT algorithm (Fast Fourier Transform) on arrays of real numbers. It can | |||
| also compute the inverse transform. | |||
| You should find in this package a lot of files ; some of them are of | |||
| particular interest: | |||
| - readme.txt : you are reading it | |||
| - ffft/FFTReal.h : FFT, length fixed at run-time | |||
| - ffft/FFTRealFixLen.h: FFT, length fixed at compile-time | |||
| - delphi/FFTReal.pas : Pascal implementation (working but not up-to-date) | |||
| 3. Using FFTReal | |||
| ---------------- | |||
| Important - if you were using older versions of FFTReal (up to 1.03), some | |||
| things have changed. FFTReal is now a template. Therefore use FFTReal<float> | |||
| or FFTReal<double> in your code depending on the application datatype. The | |||
| flt_t typedef has been removed. And if you were previously using FFTReal 2.0, | |||
| note that all the classes have moved to the ffft namespace. | |||
| You have two ways to use FFTReal. In the first way, the FFT has its length | |||
| fixed at run-time, when the object is instanciated. It means that you have | |||
| not to know the length when you write the code. This is the usual way of | |||
| proceeding. | |||
| 3.1 FFTReal - Length fixed at run-time | |||
| -------------------------------------- | |||
| Just instanciate one time a FFTReal object. Specify the data type you want | |||
| as template parameter (only floating point: float, double, long double or | |||
| custom type). The constructor precompute a lot of things, so it may be a bit | |||
| long. The parameter is the number of points used for the next FFTs. It must | |||
| be a power of 2: | |||
| #include "ffft/FFTReal.h" | |||
| ... | |||
| long len = 1024; | |||
| ... | |||
| // 1024-point FFT object constructed. | |||
| ffft::FFTReal <float> fft_object (len); | |||
| Then you can use this object to compute as many FFTs and IFFTs as you want. | |||
| They will be computed very quickly because a lot of work has been done in the | |||
| object construction. | |||
| float x [1024]; | |||
| float f [1024]; | |||
| ... | |||
| fft_object.do_fft (f, x); // x (real) --FFT---> f (complex) | |||
| ... | |||
| fft_object.do_ifft (f, x); // f (complex) --IFFT--> x (real) | |||
| fft_object.rescale (x); // Post-scaling should be done after FFT+IFFT | |||
| ... | |||
| x [] and f [] are floating point number arrays. x [] is the real number | |||
| sequence which we want to compute the FFT. f [] is the result, in the | |||
| "frequency" domain. f has the same number of elements as x [], but f [] | |||
| elements are complex numbers. The routine uses some FFT properties to | |||
| optimize memory and to reduce calculations: the transformaton of a real | |||
| number sequence is a conjugate complex number sequence: F [k] = F [-k]*. | |||
| 3.2 FFTRealFixLen - Length fixed at compile-time | |||
| ------------------------------------------------ | |||
| This class is significantly faster than the previous one, giving a speed | |||
| gain between 50 and 100 %. The template parameter is the base-2 logarithm of | |||
| the FFT length. The datatype is float; it can be changed by modifying the | |||
| DataType typedef in FFTRealFixLenParam.h. As FFTReal class, it supports | |||
| only floating-point types or equivalent. | |||
| Use is similar as the one of FFTReal. To instanciate the object, just proceed | |||
| as indicated below: | |||
| #include "ffft/FFTRealFixLen.h" | |||
| ... | |||
| // 1024-point (2^10) FFT object constructed. | |||
| ffft::FFTRealFixLen <10> fft_object; | |||
| Warning: long FFT objects may take a very long time to compile, depending on | |||
| the compiler and its optimisation options. If compilation time is too high, | |||
| encapsulate the FFT object in a seprate class whose header doesn't need | |||
| to include FFTRealFixLen.h, so you just have to compile the wrapper once | |||
| and only link it the other times. For example (quick, dirty and incomplete): | |||
| ffft/FFTWrapper.h: | ffft/FFTWrapper.cpp: | |||
| | | |||
| class FFTWrapper | #include "ffft/FFTRealFixLen.h" | |||
| { | #include "ffft/FFTWrapper.h" | |||
| public: | | |||
| FFTWrapper (); | FFTWrapper::FFTWrapper () | |||
| ~FFTWrapper (); | : _impl_ptr ((void*) new FTRealFixLen <10>) | |||
| void do_fft (...); | { | |||
| void do_ifft (...); | } | |||
| private: | | |||
| void *_impl_ptr; | ... | |||
| } | | |||
| 3.3 Data organisation | |||
| --------------------- | |||
| Mathematically speaking, DFT formulas below show what does FFTReal: | |||
| do_fft() : f(k) = sum (p = 0, N-1, x(p) * exp (+j*2*pi*k*p/N)) | |||
| do_ifft(): x(k) = sum (p = 0, N-1, f(p) * exp (-j*2*pi*k*p/N)) | |||
| Where j is the square root of -1. The formulas differ only by the sign of | |||
| the exponential. When the sign is positive, the transform is called positive. | |||
| Common formulas for Fourier transform are negative for the direct tranform and | |||
| positive for the inverse one. | |||
| However in these formulas, f is an array of complex numbers and doesn't | |||
| correspound exactly to the f[] array taken as function parameter. The | |||
| following table shows how the f[] sequence is mapped onto the usable FFT | |||
| coefficients (called bins): | |||
| FFTReal output | Positive FFT equiv. | Negative FFT equiv. | |||
| ---------------+-----------------------+----------------------- | |||
| f [0] | Real (bin 0) | Real (bin 0) | |||
| f [...] | Real (bin ...) | Real (bin ...) | |||
| f [length/2] | Real (bin length/2) | Real (bin length/2) | |||
| f [length/2+1] | Imag (bin 1) | -Imag (bin 1) | |||
| f [...] | Imag (bin ...) | -Imag (bin ...) | |||
| f [length-1] | Imag (bin length/2-1) | -Imag (bin length/2-1) | |||
| And FFT bins are distributed in f [] as above: | |||
| | | Positive FFT | Negative FFT | |||
| Bin | Real part | imaginary part | imaginary part | |||
| ------------+----------------+-----------------+--------------- | |||
| 0 | f [0] | 0 | 0 | |||
| 1 | f [1] | f [length/2+1] | -f [length/2+1] | |||
| ... | f [...], | f [...] | -f [...] | |||
| length/2-1 | f [length/2-1] | f [length-1] | -f [length-1] | |||
| length/2 | f [length/2] | 0 | 0 | |||
| length/2+1 | f [length/2-1] | -f [length-1] | f [length-1] | |||
| ... | f [...] | -f [...] | f [...] | |||
| length-1 | f [1] | -f [length/2+1] | f [length/2+1] | |||
| f [] coefficients have the same layout for FFT and IFFT functions. You may | |||
| notice that scaling must be done if you want to retrieve x after FFT and IFFT. | |||
| Actually, IFFT (FFT (x)) = x * length(x). This is a not a problem because | |||
| most of the applications don't care about absolute values. Thus, the operation | |||
| requires less calculation. If you want to use the FFT and IFFT to transform a | |||
| signal, you have to apply post- (or pre-) processing yourself. Multiplying | |||
| or dividing floating point numbers by a power of 2 doesn't generate extra | |||
| computation noise. | |||
| 4. Compilation and testing | |||
| -------------------------- | |||
| Drop the following files into your project or makefile: | |||
| ffft/Array.* | |||
| ffft/def.h | |||
| ffft/DynArray.* | |||
| ffft/FFTReal*.h* | |||
| ffft/OscSinCos.* | |||
| Other files are for testing purpose only, do not include them if you just need | |||
| to use the library; they are not needed to use FFTReal in your own programs. | |||
| FFTReal may be compiled in two versions: release and debug. Debug version | |||
| has checks that could slow down the code. Define NDEBUG to set the Release | |||
| mode. For example, the command line to compile the test bench on GCC would | |||
| look like: | |||
| Debug mode: | |||
| g++ -Wall -I. -o ./fftreal_debug.exe ffft/test/*.cpp ffft/test/stopwatch/*.cpp | |||
| Release mode: | |||
| g++ -Wall -I. -o ./fftreal_release.exe -DNDEBUG -O3 ffft/test/*.cpp ffft/test/stopwatch/*.cpp | |||
| It may be tricky to compile the test bench because the speed tests use the | |||
| stopwatch sub-library, which is not that cross-platform. If you encounter | |||
| any problem that you cannot easily fix while compiling it, edit the file | |||
| ffft/test/conf.h and un-define the speed test macro. Remove the stopwatch | |||
| directory from your source file list, too. | |||
| If it's not done by default, you should activate the exception handling | |||
| of your compiler to get the class memory-leak-safe. Thus, when a memory | |||
| allocation fails (in the constructor), an exception is thrown and the entire | |||
| object is safely destructed. It reduces the permanent error checking overhead | |||
| in the client code. Also, the test bench requires Run-Time Type Information | |||
| (RTTI) to be enabled in order to display the names of the tested classes - | |||
| sometimes mangled, depending on the compiler. | |||
| Please note: the test bench may take an insane time to compile, especially in | |||
| Release mode, because a lot of recursive templates are instanciated. | |||
| 5. History | |||
| ---------- | |||
| v2.11 (2010.09.12) | |||
| - The LGPL was not well suited to 100% template code, therefore I changed | |||
| the license again. Everything is released under the WTFPL. | |||
| - Removed warnings in the testcode on MSVC++ 8.0 | |||
| - Fixed the multiple definition linking error with template specialisations | |||
| on GCC 4. | |||
| v2.10 (2008.05.28) | |||
| - Classes are now in the ffft namespace | |||
| - Changed directory structure | |||
| - Fixed compilation information in the documentation | |||
| v2.00 (2005.10.18) | |||
| - Turned FFTReal class into template (data type as parameter) | |||
| - Added FFTRealFixLen | |||
| - Trigonometric tables are size-limited in order to preserve cache memory; | |||
| over a given size, sin/cos functions are computed on the fly. | |||
| - Better test bench for accuracy and speed | |||
| - Changed license to LGPL | |||
| v1.03 (2001.06.15) | |||
| - Thanks to Frederic Vanmol for the Pascal port (works with Delphi). | |||
| - Documentation improvement | |||
| v1.02 (2001.03.25) | |||
| - sqrt() is now precomputed when the object FFTReal is constructed, resulting | |||
| in speed impovement for small size FFT. | |||
| v1.01 (2000) | |||
| - Small modifications, I don't remember what. | |||
| v1.00 (1999.08.14) | |||
| - First version released | |||
| 6. Contact | |||
| ---------- | |||
| Please address any comment, bug report or flame to: | |||
| Laurent de Soras | |||
| laurent.de.soras@free.fr | |||
| http://ldesoras.free.fr | |||
| For the Pascal port: | |||
| Frederic Vanmol | |||
| frederic@fruityloops.com | |||
| @@ -0,0 +1,131 @@ | |||
| /* | |||
| * Sassy scope exported API | |||
| * Copyright (C) 2022 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 3 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the LICENSE file. | |||
| */ | |||
| /** | |||
| * This file contains a substantial amount of code from Sassy Audio Spreadsheet | |||
| * Copyright (c) 2022 Jari Komppa. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice and this permission notice shall be included in all | |||
| * copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| * SOFTWARE. | |||
| */ | |||
| #pragma once | |||
| #include "fftreal/FFTReal.h" | |||
| // int gFFTAverage = 1; | |||
| // int gSamplerate; | |||
| // float mUIScale; | |||
| // // gScope | |||
| struct ScopeData { | |||
| int mIndex = 0; | |||
| int mSampleRate = 0; | |||
| float mScroll = 0; | |||
| float mTimeScale = 0.01f; | |||
| int mTimeScaleSlider = 0; | |||
| int mSyncMode = 0; | |||
| int mSyncChannel = 0; | |||
| int mMode = 0; | |||
| int mDisplay = 0; | |||
| int mFFTZoom = 0; | |||
| int mPot = 0; | |||
| float fft1[65536 * 2]; | |||
| float fft2[65536 * 2]; | |||
| float ffta[65536 * 2]; | |||
| unsigned int colors[4] = { | |||
| 0xffc0c0c0, | |||
| 0xffa0a0ff, | |||
| 0xffffa0a0, | |||
| 0xff30d0d0 | |||
| }; | |||
| struct Channel { | |||
| bool mEnabled = true; | |||
| float mScale = 1.0f / 5.0f; | |||
| int mScaleSlider = 0; | |||
| float mOffset = 0; | |||
| float* mData = nullptr; | |||
| ~Channel() | |||
| { | |||
| delete[] mData; | |||
| } | |||
| void realloc(const int sampleRate) | |||
| { | |||
| mData = new float[sampleRate * 10]; | |||
| memset(mData, 0, sizeof(float) * sampleRate * 10); | |||
| } | |||
| } mCh[4]; | |||
| struct { | |||
| int average; | |||
| ffft::FFTReal<float>* obj16; | |||
| ffft::FFTReal<float>* obj32; | |||
| ffft::FFTReal<float>* obj64; | |||
| ffft::FFTReal<float>* obj128; | |||
| ffft::FFTReal<float>* obj256; | |||
| ffft::FFTReal<float>* obj512; | |||
| ffft::FFTReal<float>* obj1024; | |||
| ffft::FFTReal<float>* obj2048; | |||
| ffft::FFTReal<float>* obj4096; | |||
| ffft::FFTReal<float>* obj8192; | |||
| ffft::FFTReal<float>* obj16384; | |||
| ffft::FFTReal<float>* obj32768; | |||
| ffft::FFTReal<float>* obj65536; | |||
| } fft; | |||
| void realloc(const int sampleRate) | |||
| { | |||
| mIndex = 0; | |||
| mSampleRate = sampleRate; | |||
| for (int i = 0; i < 4; i++) | |||
| mCh[i].realloc(sampleRate); | |||
| } | |||
| inline void probe(float data1, float data2, float data3, float data4) | |||
| { | |||
| // since probe has several channels, need to deal with index here | |||
| if (mMode == 0) | |||
| { | |||
| mCh[0].mData[mIndex] = data1; | |||
| mCh[1].mData[mIndex] = data2; | |||
| mCh[2].mData[mIndex] = data3; | |||
| mCh[3].mData[mIndex] = data4; | |||
| mIndex = (mIndex + 1) % (mSampleRate * 10); | |||
| } | |||
| } | |||
| }; | |||
| void do_show_scope_window(ScopeData* scope, float uiScale); | |||
| @@ -0,0 +1,872 @@ | |||
| /* | |||
| * DISTRHO Cardinal Plugin | |||
| * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 3 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the LICENSE file. | |||
| */ | |||
| /** | |||
| * This file is copied and adapted from Sassy Audio Spreadsheet (sassy_scope.cpp) | |||
| * Copyright (c) 2022 Jari Komppa. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice and this permission notice shall be included in all | |||
| * copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| * SOFTWARE. | |||
| */ | |||
| #include "sassy.hpp" | |||
| #define POW_2_3_4TH 1.6817928305074290860622509524664297900800685247135690216264521719 | |||
| static double catmullrom(double t, double p0, double p1, double p2, double p3) | |||
| { | |||
| return 0.5 * ( | |||
| (2 * p1) + | |||
| (-p0 + p2) * t + | |||
| (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t + | |||
| (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t | |||
| ); | |||
| } | |||
| static const char* gNotestr[128] = | |||
| { | |||
| "C0-1","C#-1","D-1","D#-1","E-1","F-1","F#-1","G-1","G#-1","A-1","A#-1","B-1", | |||
| "C0","C#0","D0","D#0","E0","F0","F#0","G0","G#0","A0","A#0","B0", | |||
| "C1","C#1","D1","D#1","E1","F1","F#1","G1","G#1","A1","A#1","B1", | |||
| "C2","C#2","D2","D#2","E2","F2","F#2","G2","G#2","A2","A#2","B2", | |||
| "C3","C#3","D3","D#3","E3","F3","F#3","G3","G#3","A3","A#3","B3", | |||
| "C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4", | |||
| "C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5", | |||
| "C6","C#6","D6","D#6","E6","F6","F#6","G6","G#6","A6","A#6","B6", | |||
| "C7","C#7","D7","D#7","E7","F7","F#7","G7","G#7","A7","A#7","B7", | |||
| "C8","C#8","D8","D#8","E8","F8","F#8","G8","G#8","A8","A#8","B8", | |||
| "C9","C#9","D9","D#9","E9","F9","F#9","G9" | |||
| }; | |||
| static const float timescalesteps[5] = | |||
| { | |||
| 0.0001f, | |||
| 0.001f, | |||
| 0.01f, | |||
| 0.1f, | |||
| 1.0f, | |||
| }; | |||
| static const char* timescaletext[5] = | |||
| { | |||
| "0.1ms", | |||
| "1ms", | |||
| "10ms", | |||
| "100ms", | |||
| "1000ms" | |||
| }; | |||
| static const float scalesteps[9] = | |||
| { | |||
| 1.0f / 32.0f, | |||
| 1.0f / 16.0f, | |||
| 1.0f / 10.0f, | |||
| 1.0f / 8.0f, | |||
| 1.0f / 5.0f, | |||
| 1.0f / 4.0f, | |||
| 1.0f / 2.0f, | |||
| 1.0f, | |||
| 2.0f, | |||
| }; | |||
| static const char* scaletexts[9] = { | |||
| "x1/32", | |||
| "x1/16", | |||
| "x1/10", | |||
| "x1/8", | |||
| "x1/5", | |||
| "x1/4", | |||
| "x1/2", | |||
| "x1", | |||
| "x2", | |||
| }; | |||
| static constexpr const int grid_size = 340; | |||
| static constexpr const int grid_half_size = grid_size / 2 + 10; | |||
| static constexpr const int grid_quarter_size = static_cast<int>(grid_half_size / 2); | |||
| static constexpr const int grid_1_8_size = grid_quarter_size / 2; | |||
| static constexpr const int grid_3_8_size = grid_quarter_size + grid_quarter_size / 2; | |||
| static void scope_grid(const float uiScale) | |||
| { | |||
| ImVec2 p = ImGui::GetItemRectMin(); | |||
| // zero | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2) * uiScale), 0xff000000, 3.0f); | |||
| // 1.0 | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 - grid_quarter_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 - grid_quarter_size) * uiScale), 0xff000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 + grid_quarter_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 + grid_quarter_size) * uiScale), 0xff000000); | |||
| // 0.5 | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 - grid_1_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 - grid_1_8_size) * uiScale), 0x3f000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 + grid_1_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 + grid_1_8_size) * uiScale), 0x3f000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 - grid_3_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 - grid_3_8_size) * uiScale), 0x3f000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 + grid_3_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 + grid_3_8_size) * uiScale), 0x3f000000); | |||
| // zero | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2) * uiScale, p.y + (grid_size) * uiScale), 0xff000000, 3.0f); | |||
| // 1.0 | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 - grid_quarter_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 - grid_quarter_size) * uiScale, p.y + (grid_size) * uiScale), 0xff000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 + grid_quarter_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 + grid_quarter_size) * uiScale, p.y + (grid_size) * uiScale), 0xff000000); | |||
| // 0.5 | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 - grid_1_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 - grid_1_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 + grid_1_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 + grid_1_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 - grid_3_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 - grid_3_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 + grid_3_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 + grid_3_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000); | |||
| } | |||
| static int scope_sync(ScopeData* gScope, int index) | |||
| { | |||
| const float gSamplerate = gScope->mSampleRate; | |||
| int samples = (int)(gSamplerate * gScope->mTimeScale); | |||
| int cycle = gSamplerate * 10; | |||
| int ofs = samples; | |||
| if (gScope->mMode == 0) | |||
| { | |||
| // calculate sync | |||
| if (gScope->mSyncMode == 0) | |||
| { | |||
| float* graphdata = gScope->mCh[gScope->mSyncChannel].mData; | |||
| int over = ofs; | |||
| while (over < (cycle - ofs) && graphdata[(index - over + cycle) % cycle] < 0) over++; | |||
| int under = over; | |||
| while (under < (cycle - ofs) && graphdata[(index - under + cycle) % cycle] > 0) under++; | |||
| ofs = under; | |||
| } | |||
| else | |||
| if (gScope->mSyncMode == 1) | |||
| { | |||
| float* graphdata = gScope->mCh[gScope->mSyncChannel].mData; | |||
| int under = ofs; | |||
| while (under < (cycle - ofs) && graphdata[(index - under + cycle) % cycle] > 0) under++; | |||
| int over = under; | |||
| while (over < (cycle - ofs) && graphdata[(index - over + cycle) % cycle] < 0) over++; | |||
| ofs = over; | |||
| } | |||
| // default: ofs = samples | |||
| } | |||
| else | |||
| { | |||
| // pause mode, scroll bar is active | |||
| ofs = -(int)(gScope->mScroll * gSamplerate); | |||
| if (ofs < samples) | |||
| ofs = samples; | |||
| if (ofs > gSamplerate * 10 - samples) | |||
| ofs = gSamplerate * 10 - samples; | |||
| } | |||
| gScope->mScroll = -((float)ofs / gSamplerate); | |||
| return ofs; | |||
| } | |||
| #if 0 | |||
| static void scope_plot(ScopeData* gScope, const float uiScale, int index) | |||
| { | |||
| ImVec2 p = ImGui::GetItemRectMin(); | |||
| const float gSamplerate = gScope->mSampleRate; | |||
| int cycle = gSamplerate * 10; | |||
| /* | |||
| Okay, max scale is 1 second, so.. | |||
| */ | |||
| int samples = (int)(gSamplerate * gScope->mTimeScale); | |||
| scope_grid(uiScale); | |||
| int ofs = scope_sync(gScope, index); | |||
| if (gScope->mDisplay == 2) | |||
| { | |||
| for (int i = 0; i < 16384; i++) | |||
| { | |||
| for (int j = 0; j < 4; j++) | |||
| { | |||
| if (gScope->mCh[j].mEnabled) | |||
| { | |||
| float* graphdata = gScope->mCh[j].mData; | |||
| float y = graphdata[(index - ofs + i * samples / 16384 + cycle) % cycle]; | |||
| float x = graphdata[(index - ofs + i * samples / 16384 + cycle - 1) % cycle]; | |||
| x = x * gScope->mCh[j].mScale; | |||
| y = y * gScope->mCh[j].mScale - gScope->mCh[j].mOffset; | |||
| ImGui::GetWindowDrawList()->AddCircleFilled( | |||
| ImVec2(p.x + (grid_size / 2 + x * grid_quarter_size) * uiScale, p.y + (grid_size / 2 + y * grid_quarter_size) * uiScale), | |||
| 1, | |||
| (gScope->colors[j] & 0xffffff) | 0x3f000000); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (int i = 0; i < 32768; i++) | |||
| { | |||
| for (int j = 0; j < 2; j++) | |||
| { | |||
| if (gScope->mCh[j*2].mEnabled) | |||
| { | |||
| float* graphdata = gScope->mCh[j * 2].mData; | |||
| float x = graphdata[(index - ofs + i * samples / 32768 + cycle) % cycle]; | |||
| graphdata = gScope->mCh[j * 2 + 1].mData; | |||
| float y = graphdata[(index - ofs + i * samples / 32768 + cycle) % cycle]; | |||
| x = x * gScope->mCh[j * 2].mScale - gScope->mCh[j * 2].mOffset; | |||
| y = y * gScope->mCh[j * 2 + 1].mScale - gScope->mCh[j * 2 + 1].mOffset; | |||
| ImGui::GetWindowDrawList()->AddCircleFilled( | |||
| ImVec2(p.x + (grid_size / 2 + x * grid_quarter_size) * uiScale, p.y + (grid_size / 2 + y * grid_quarter_size) * uiScale), | |||
| 1, | |||
| (gScope->colors[j*2] & 0xffffff) | 0x3f000000); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| #endif | |||
| static void scope_time(ScopeData* gScope, const float uiScale, int index) | |||
| { | |||
| ImVec2 p = ImGui::GetItemRectMin(); | |||
| const float gSamplerate = gScope->mSampleRate; | |||
| int cycle = gSamplerate * 10; | |||
| ImDrawList* dl = ImGui::GetWindowDrawList(); | |||
| /* | |||
| Okay, max scale is 1 second, so.. | |||
| */ | |||
| int samples = (int)(gSamplerate * gScope->mTimeScale); | |||
| scope_grid(uiScale); | |||
| int ofs = scope_sync(gScope, index); | |||
| if (samples > grid_size) | |||
| { | |||
| for (int j = 0; j < 4; j++) | |||
| { | |||
| if (gScope->mCh[j].mEnabled) | |||
| { | |||
| float* graphdata = gScope->mCh[j].mData; | |||
| ImVec2 vert[grid_size]; | |||
| for (int i = 0; i < grid_size; i++) | |||
| { | |||
| float v0 = -graphdata[(index - ofs + i * samples / grid_size + cycle) % cycle]; | |||
| float v1 = -graphdata[(index - ofs + (i + 1) * samples / grid_size + cycle) % cycle]; | |||
| v0 = v0 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset; | |||
| v1 = v1 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset; | |||
| vert[i].x = p.x + i * uiScale; | |||
| vert[i].y = p.y + (grid_size / 2 + v0 * grid_quarter_size) * uiScale; | |||
| } | |||
| float v0 = p.y + (grid_size / 2 + (-gScope->mCh[j].mOffset) * grid_quarter_size) * uiScale; | |||
| dl->Flags = 0; | |||
| for (int i = 0; i < grid_size-1; i++) | |||
| { | |||
| ImVec2 quad[4]; | |||
| quad[0] = ImVec2(vert[i].x, v0); | |||
| quad[1] = ImVec2(vert[i].x, vert[i].y); | |||
| quad[2] = ImVec2(vert[i + 1].x, vert[i + 1].y); | |||
| quad[3] = ImVec2(vert[i + 1].x, v0); | |||
| dl->AddConvexPolyFilled(quad, 4, (gScope->colors[j] & 0xffffff) | 0x3f000000 ); | |||
| } | |||
| if (gScope->mTimeScale < 0.1) | |||
| { | |||
| dl->Flags = ImDrawListFlags_AntiAliasedLines; | |||
| dl->AddPolyline(vert, grid_size, gScope->colors[j], false, 2 * uiScale); | |||
| } | |||
| else | |||
| { | |||
| dl->Flags = ImDrawListFlags_AntiAliasedLines; | |||
| dl->AddPolyline(vert, grid_size, gScope->colors[j], false, 1); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // less than 1 sample per pixel | |||
| for (int j = 0; j < 4; j++) | |||
| { | |||
| if (gScope->mCh[j].mEnabled) | |||
| { | |||
| float* graphdata = gScope->mCh[j].mData; | |||
| for (int i = 0; i < samples; i++) | |||
| { | |||
| float v0 = -graphdata[(index - ofs + i + cycle) % cycle]; | |||
| float v1 = 0; | |||
| v0 = v0 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset; | |||
| v1 = v1 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset; | |||
| float x0 = p.x + (i * grid_size / samples) * uiScale; | |||
| ImGui::GetWindowDrawList()->AddCircleFilled( | |||
| ImVec2(x0, p.y + (grid_size / 2 + v0 * grid_quarter_size) * uiScale), | |||
| 4 * uiScale, | |||
| gScope->colors[j]); | |||
| ImGui::GetWindowDrawList()->AddLine( | |||
| ImVec2(x0, p.y + (grid_size / 2 + v0 * grid_quarter_size) * uiScale), | |||
| ImVec2(x0, p.y + (grid_size / 2 + v1 * grid_quarter_size) * uiScale), | |||
| gScope->colors[j]); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| ImGui::GetWindowDrawList()->AddText(p, 0xffc0c0c0, timescaletext[gScope->mTimeScaleSlider + 2]); | |||
| ImGui::GetWindowDrawList()->AddText(ImVec2(p.x, p.y + (grid_size / 2 - grid_quarter_size - 7) * uiScale), 0xffc0c0c0, "+1"); | |||
| ImGui::GetWindowDrawList()->AddText(ImVec2(p.x, p.y + (grid_size / 2 + grid_quarter_size - 7) * uiScale), 0xffc0c0c0, "-1"); | |||
| ImVec2 mp = ImGui::GetMousePos(); | |||
| mp.x -= p.x; | |||
| mp.y -= p.y; | |||
| if (mp.x > 0 && mp.x < grid_size * uiScale && | |||
| mp.y > 0 && mp.y < grid_size * uiScale) | |||
| { | |||
| ImGui::GetWindowDrawList()->AddLine( | |||
| ImVec2(p.x + mp.x, p.y), | |||
| ImVec2(p.x + mp.x, p.y + grid_size * uiScale), | |||
| 0xff00ff00, 1 * uiScale); | |||
| if (gScope->mCh[0].mEnabled || gScope->mCh[1].mEnabled || gScope->mCh[2].mEnabled || gScope->mCh[3].mEnabled) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| if (gScope->mCh[0].mEnabled) ImGui::Text("Ch 0: %3.3f", gScope->mCh[0].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]); | |||
| if (gScope->mCh[1].mEnabled) ImGui::Text("Ch 1: %3.3f", gScope->mCh[1].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]); | |||
| if (gScope->mCh[2].mEnabled) ImGui::Text("Ch 2: %3.3f", gScope->mCh[2].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]); | |||
| if (gScope->mCh[3].mEnabled) ImGui::Text("Ch 3: %3.3f", gScope->mCh[3].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| } | |||
| } | |||
| static void vertline(const float uiScale, const float x, const float w) | |||
| { | |||
| ImVec2 p = ImGui::GetItemRectMin(); | |||
| ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + x * uiScale, p.y * uiScale), ImVec2(p.x + x * uiScale, p.y + (grid_size) * uiScale), 0xff000000, w); | |||
| } | |||
| static void scope_freq(ScopeData* gScope, const float uiScale, int index) | |||
| { | |||
| ImVec2 p = ImGui::GetItemRectMin(); | |||
| ImDrawList* dl = ImGui::GetWindowDrawList(); | |||
| const float gSamplerate = gScope->mSampleRate; | |||
| int cycle = gSamplerate * 10; | |||
| /* | |||
| Okay, max scale is 1 second, so.. | |||
| */ | |||
| int samples = (int)(gSamplerate * gScope->mTimeScale); | |||
| // what's the biggest PoT < samples? | |||
| // 192000 takes 18 bits to encode. | |||
| // Fill 32 bits: | |||
| int pot = samples | (samples >> 16); | |||
| pot = pot | (pot >> 8); | |||
| pot = pot | (pot >> 4); | |||
| pot = pot | (pot >> 2); | |||
| pot = pot | (pot >> 1); | |||
| // Shift down and add one to round it up | |||
| pot = (pot >> 1) + 1; | |||
| if (pot < 16) pot = 16; | |||
| if (pot > 65536) pot = 65536; | |||
| gScope->mPot = pot; | |||
| ffft::FFTReal<float>* fft = NULL; | |||
| switch (pot) | |||
| { | |||
| case 16: fft = gScope->fft.obj16; break; | |||
| case 32: fft = gScope->fft.obj32; break; | |||
| case 64: fft = gScope->fft.obj64; break; | |||
| case 128: fft = gScope->fft.obj128; break; | |||
| case 256: fft = gScope->fft.obj256; break; | |||
| case 512: fft = gScope->fft.obj512; break; | |||
| case 1024: fft = gScope->fft.obj1024; break; | |||
| case 2048: fft = gScope->fft.obj2048; break; | |||
| case 4096: fft = gScope->fft.obj4096; break; | |||
| case 8192: fft = gScope->fft.obj8192; break; | |||
| case 16384: fft = gScope->fft.obj16384; break; | |||
| case 32768: fft = gScope->fft.obj32768; break; | |||
| case 65536: fft = gScope->fft.obj65536; break; | |||
| } | |||
| if (!fft) return; | |||
| int average = gScope->fft.average; | |||
| int ofs = scope_sync(gScope, index); | |||
| int size = grid_size - 1; | |||
| float sizef = size; | |||
| float freqbin = gSamplerate / (float)(pot / 2); | |||
| float freqbins[size]; | |||
| float zoom = 1.0f / (1 << gScope->mFFTZoom); | |||
| for (int i = 0; i < 10; i++) | |||
| { | |||
| vertline(uiScale, sqrt(100 / freqbin * i / (pot / 4)) / zoom * sizef, 1); | |||
| vertline(uiScale, sqrt(1000 / freqbin * i / (pot / 4)) / zoom * sizef, 1); | |||
| vertline(uiScale, sqrt(10000 / freqbin * i / (pot / 4)) / zoom * sizef, 1); | |||
| } | |||
| for (int j = 0; j < 4; j++) | |||
| { | |||
| if (gScope->mCh[j].mEnabled) | |||
| { | |||
| memset(gScope->ffta, 0, sizeof(float) * 65536 * 2); | |||
| for (int k = 0; k < average; k++) | |||
| { | |||
| float* graphdata = gScope->mCh[j].mData; | |||
| for (int i = 0; i < pot; i++) | |||
| { | |||
| gScope->fft1[i * 2] = graphdata[(index - ofs + i + cycle - k) % cycle]; | |||
| gScope->fft1[i * 2 + 1] = 0; | |||
| } | |||
| fft->do_fft(gScope->fft2, gScope->fft1); | |||
| for (int i = 0; i < pot / 4; i++) | |||
| gScope->ffta[i] += (1.0f / average) * sqrt(gScope->fft2[i * 2 + 0] * gScope->fft2[i * 2 + 0] + gScope->fft2[i * 2 + 1] * gScope->fft2[i * 2 + 1]); | |||
| } | |||
| ImVec2 vert[size]; | |||
| for (int i = 0; i < size; i++) | |||
| { | |||
| float ppos = powf(zoom * i / sizef, 2.0f) * pot / 4; | |||
| freqbins[i] = ppos * freqbin; | |||
| float f = ppos - (int)ppos; | |||
| float a = i ? gScope->ffta[(int)ppos - 1] : 0; | |||
| float b = gScope->ffta[(int)ppos]; | |||
| float c = i < size ? gScope->ffta[(int)ppos + 1] : 0; | |||
| float d = i < (size-1) ? gScope->ffta[(int)ppos + 2] : 0; | |||
| float v0 = (float)catmullrom(f, a, b, c, d); | |||
| v0 = v0 * gScope->mCh[j].mScale + gScope->mCh[j].mOffset * 50; | |||
| vert[i] = ImVec2(p.x + i * uiScale, | |||
| p.y + (sizef - v0 * 4) * uiScale); | |||
| } | |||
| float v0 = p.y + (size - gScope->mCh[j].mOffset * 50 * 4) * uiScale; | |||
| dl->Flags = 0; | |||
| for (int i = 0; i < size-1; i++) | |||
| { | |||
| ImVec2 quad[4]; | |||
| quad[0] = ImVec2(vert[i].x, v0); | |||
| quad[1] = ImVec2(vert[i].x, vert[i].y); | |||
| quad[2] = ImVec2(vert[i + 1].x, vert[i + 1].y); | |||
| quad[3] = ImVec2(vert[i + 1].x, v0); | |||
| dl->AddConvexPolyFilled(quad, 4, (gScope->colors[j] & 0xffffff) | 0x3f000000); | |||
| } | |||
| dl->Flags = ImDrawListFlags_AntiAliasedLines; | |||
| dl->AddPolyline(vert, size, gScope->colors[j], false, 1); | |||
| } | |||
| } | |||
| if (!ImGui::IsPopupOpen("Freq Context",ImGuiPopupFlags_AnyPopupId)) | |||
| if (gScope->mCh[0].mEnabled || gScope->mCh[1].mEnabled || gScope->mCh[2].mEnabled || gScope->mCh[3].mEnabled) | |||
| { | |||
| ImVec2 mp = ImGui::GetMousePos(); | |||
| mp.x -= p.x; | |||
| mp.y -= p.y; | |||
| if (mp.x > 0 && mp.x < grid_size * uiScale && | |||
| mp.y > 0 && mp.y < grid_size * uiScale) | |||
| { | |||
| ImGui::GetWindowDrawList()->AddLine( | |||
| ImVec2(p.x + mp.x, p.y), | |||
| ImVec2(p.x + mp.x, p.y + grid_size * uiScale), | |||
| 0xff00ff00, 1 * uiScale); | |||
| ImGui::BeginTooltip(); | |||
| int note = (int)(12 * log(32 * POW_2_3_4TH * (freqbins[(int)mp.x] / 440)) / log(2)); | |||
| if (note < 0 || note > 127) note = -1; | |||
| ImGui::Text("%3.3fHz%s%s", freqbins[(int)mp.x], note==-1?"":"\n", note==-1?"":gNotestr[note]); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| } | |||
| } | |||
| #if 0 | |||
| static int groups(ScopeData* gScope, double h) | |||
| { | |||
| int count = 0; | |||
| for (int i = 1; i < gScope->mPot / 4; i++) | |||
| { | |||
| if (gScope->fft1[i - 1] < h && gScope->fft1[i] > h) | |||
| count++; | |||
| } | |||
| return count; | |||
| } | |||
| static void detect_fundamentals(ScopeData* gScope) | |||
| { | |||
| // gScope->fft1[1..pot/4] has our mags | |||
| double maxmag = 0; | |||
| for (int i = 0; i < gScope->mPot / 4; i++) | |||
| if (maxmag < gScope->fft1[i]) | |||
| maxmag = gScope->fft1[i]; | |||
| double minmag = 0; | |||
| int count = 0; | |||
| int iters = 0; | |||
| double h = (minmag + maxmag) / 2; | |||
| double step = h / 2; | |||
| while (iters < 100 && count != 16) | |||
| { | |||
| count = groups(gScope, h); | |||
| if (count < 16) | |||
| { | |||
| h -= step; | |||
| } | |||
| else | |||
| { | |||
| h += step; | |||
| } | |||
| step /= 2; | |||
| iters++; | |||
| } | |||
| char temp[1024]; | |||
| int ofs = 0; | |||
| temp[0] = 0; | |||
| const float gSamplerate = gScope->mSampleRate; | |||
| float freqbin = gSamplerate / (float)(gScope->mPot / 2); | |||
| int startbin = 0; | |||
| for (int i = 2; i < gScope->mPot / 4; i++) | |||
| { | |||
| if (gScope->fft1[i - 1] < h && gScope->fft1[i] > h) | |||
| { | |||
| startbin = i; | |||
| } | |||
| if (gScope->fft1[i - 1] > h && gScope->fft1[i] < h) | |||
| { | |||
| double sum = 0; | |||
| double magsum = 0; | |||
| for (int j = startbin; j < i; j++) | |||
| { | |||
| sum += gScope->fft1[j]; | |||
| magsum += gScope->fft1[j] * j * freqbin; | |||
| } | |||
| if (sum != 0) | |||
| { | |||
| magsum /= sum; | |||
| sum /= i - startbin; | |||
| sum /= maxmag / 2; // normalize | |||
| ofs += sprintf(temp + ofs, "%3.3f\t%3.3f\n", magsum, sum); | |||
| } | |||
| } | |||
| } | |||
| for (int i = 0; i < count; i++) | |||
| { | |||
| } | |||
| ImGui::SetClipboardText(temp); | |||
| } | |||
| #endif | |||
| void do_show_scope_window(ScopeData* gScope, const float uiScale) | |||
| { | |||
| // Data is updated live, so let's take local copies of critical stuff. | |||
| int index = gScope->mIndex; | |||
| ImGui::Begin("Scope", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize); | |||
| ImGui::BeginChild("Channel options", ImVec2((4 * 25)*uiScale, (2 * 152 + 32) * uiScale)); | |||
| ImGui::Checkbox("###ea", &gScope->mCh[0].mEnabled); ImGui::SameLine(); | |||
| ImGui::Checkbox("###eb", &gScope->mCh[1].mEnabled); ImGui::SameLine(); | |||
| ImGui::Checkbox("###ec", &gScope->mCh[2].mEnabled); ImGui::SameLine(); | |||
| ImGui::Checkbox("###ed", &gScope->mCh[3].mEnabled); | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[0]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[0]); | |||
| if (ImGui::VSliderInt("###0a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[0].mScaleSlider, -4, 4, "")) | |||
| { | |||
| if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) | |||
| { | |||
| gScope->mCh[0].mScaleSlider = 0; | |||
| gScope->mCh[0].mScale = scalesteps[4]; | |||
| } | |||
| else | |||
| { | |||
| gScope->mCh[0].mScale = scalesteps[gScope->mCh[0].mScaleSlider + 4]; | |||
| } | |||
| } | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%s", scaletexts[gScope->mCh[0].mScaleSlider + 4]); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::PopStyleColor(2); ImGui::SameLine(); | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[1]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[1]); | |||
| if (ImGui::VSliderInt("###1a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[1].mScaleSlider, -4, 4, "")) | |||
| { | |||
| gScope->mCh[1].mScale = scalesteps[gScope->mCh[1].mScaleSlider + 4]; | |||
| } | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%s", scaletexts[gScope->mCh[1].mScaleSlider + 4]); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::PopStyleColor(2); ImGui::SameLine(); | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[2]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[2]); | |||
| if (ImGui::VSliderInt("###2a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[2].mScaleSlider, -4, 4, "")) | |||
| { | |||
| gScope->mCh[2].mScale = scalesteps[gScope->mCh[2].mScaleSlider + 4]; | |||
| } | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%s", scaletexts[gScope->mCh[2].mScaleSlider + 4]); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::PopStyleColor(2); ImGui::SameLine(); | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[3]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[3]); | |||
| if (ImGui::VSliderInt("###3a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[3].mScaleSlider, -4, 4, "")) | |||
| { | |||
| gScope->mCh[3].mScale = scalesteps[gScope->mCh[3].mScaleSlider + 4]; | |||
| } | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%s", scaletexts[gScope->mCh[3].mScaleSlider + 4]); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::PopStyleColor(2); | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[0]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[0]); ImGui::VSliderFloat("###0b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[0].mOffset, -2, 2, ""); ImGui::PopStyleColor(2); ImGui::SameLine(); | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%3.3f", gScope->mCh[0].mOffset); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[1]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[1]); ImGui::VSliderFloat("###1b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[1].mOffset, -2, 2, ""); ImGui::PopStyleColor(2); ImGui::SameLine(); | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%3.3f", gScope->mCh[1].mOffset); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[2]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[2]); ImGui::VSliderFloat("###2b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[2].mOffset, -2, 2, ""); ImGui::PopStyleColor(2); ImGui::SameLine(); | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%3.3f", gScope->mCh[2].mOffset); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[3]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[3]); ImGui::VSliderFloat("###3b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[3].mOffset, -2, 2, ""); ImGui::PopStyleColor(2); | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%3.3f", gScope->mCh[3].mOffset); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::EndChild(); | |||
| ImGui::SameLine(); | |||
| ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyle().Colors[ImGuiCol_FrameBg]); | |||
| ImGui::BeginChild("Scope and scroll", ImVec2(grid_size * uiScale, (grid_size + 24)* uiScale)); | |||
| ImGui::BeginChild("Scope proper", ImVec2(grid_size * uiScale, grid_size * uiScale)); | |||
| if (gScope->mDisplay == 0) | |||
| scope_time(gScope, uiScale, index); | |||
| if (gScope->mDisplay == 1) | |||
| scope_freq(gScope, uiScale, index); | |||
| /* | |||
| if (gScope->mDisplay == 2 || gScope->mDisplay == 3) | |||
| scope_plot(gScope, uiScale, index); | |||
| */ | |||
| ImGui::EndChild(); | |||
| ImGui::PopStyleColor(1); | |||
| /* | |||
| if (gScope->mDisplay == 1) | |||
| { | |||
| if (ImGui::BeginPopupContextItem("Freq Context")) | |||
| { | |||
| if (ImGui::BeginMenu("Experimental..")) | |||
| { | |||
| if (ImGui::MenuItem("Detect and copy fundamental frequencies")) | |||
| { | |||
| detect_fundamentals(gScope); | |||
| } | |||
| ImGui::EndMenu(); | |||
| } | |||
| if (ImGui::BeginMenu("Averaging..")) | |||
| { | |||
| if (ImGui::MenuItem("1x")) | |||
| gScope->fft.average = 1; | |||
| if (ImGui::MenuItem("4x")) | |||
| gScope->fft.average = 4; | |||
| if (ImGui::MenuItem("16x")) | |||
| gScope->fft.average = 16; | |||
| if (ImGui::MenuItem("64x")) | |||
| gScope->fft.average = 64; | |||
| if (ImGui::MenuItem("256x")) | |||
| gScope->fft.average = 256; | |||
| ImGui::EndMenu(); | |||
| } | |||
| ImGui::EndPopup(); | |||
| } | |||
| } | |||
| //context_menu(1, 1, 1); | |||
| */ | |||
| if (gScope->mMode) | |||
| { | |||
| ImGui::SetNextItemWidth(grid_size * uiScale); | |||
| ImGui::SliderFloat("###scroll", &gScope->mScroll, -10.0f, 0.0f, "%.3f s"); | |||
| } | |||
| else | |||
| { | |||
| ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xff3f3f3f); | |||
| ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0xff3f3f3f); | |||
| ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0xff3f3f3f); | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrab, 0xff7f7f7f); | |||
| ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, 0xff7f7f7f); | |||
| ImGui::SetNextItemWidth(grid_size * uiScale); | |||
| float x = gScope->mScroll; | |||
| ImGui::SliderFloat("###scroll", &x, -10.0f, 0.0f, "%.3f s"); | |||
| ImGui::PopStyleColor(5); | |||
| } | |||
| ImGui::EndChild(); | |||
| ImGui::SameLine(); | |||
| ImGui::BeginChild("Scope options", ImVec2((4 * 21) * uiScale, 364 * uiScale)); | |||
| if (ImGui::VSliderInt("###0a", ImVec2(19 * uiScale, 155 * uiScale), &gScope->mTimeScaleSlider, -2, 2, "")) | |||
| { | |||
| gScope->mTimeScale = timescalesteps[gScope->mTimeScaleSlider + 2]; | |||
| } | |||
| if (ImGui::IsItemHovered()) | |||
| { | |||
| ImGui::BeginTooltip(); | |||
| ImGui::Text("%s", timescaletext[gScope->mTimeScaleSlider + 2]); | |||
| ImGui::EndTooltip(); | |||
| } | |||
| ImGui::SameLine(); | |||
| ImGui::BeginChild("moderadio", ImVec2(100 * uiScale, 155 * uiScale)); | |||
| if (ImGui::RadioButton("Time", gScope->mDisplay == 0)) gScope->mDisplay = 0; | |||
| if (ImGui::RadioButton("Freq", gScope->mDisplay == 1)) gScope->mDisplay = 1; | |||
| /* | |||
| if (ImGui::RadioButton("X,X'", gScope->mDisplay == 2)) gScope->mDisplay = 2; | |||
| if (ImGui::RadioButton("X,Y", gScope->mDisplay == 3)) gScope->mDisplay = 3; | |||
| */ | |||
| ImGui::Separator(); | |||
| ImGui::Text("FFT"); | |||
| if (ImGui::RadioButton("1x", gScope->mFFTZoom == 0)) gScope->mFFTZoom = 0; | |||
| if (ImGui::RadioButton("2x", gScope->mFFTZoom == 1)) gScope->mFFTZoom = 1; | |||
| if (ImGui::RadioButton("4x", gScope->mFFTZoom == 2)) gScope->mFFTZoom = 2; | |||
| if (ImGui::RadioButton("8x", gScope->mFFTZoom == 3)) gScope->mFFTZoom = 3; | |||
| ImGui::EndChild(); | |||
| char temp[64]; | |||
| sprintf(temp, "Sync ch %d###sc", gScope->mSyncChannel + 1); | |||
| if (ImGui::Button(temp, ImVec2(80 * uiScale, 20 * uiScale))) | |||
| gScope->mSyncChannel = (gScope->mSyncChannel + 1) % 4; | |||
| const char* syncmodes[3] = { "^", "v", "off" }; | |||
| sprintf(temp, "Sync %s###sm", syncmodes[gScope->mSyncMode]); | |||
| if (ImGui::Button(temp, ImVec2(80 * uiScale, 20 * uiScale))) | |||
| gScope->mSyncMode = (gScope->mSyncMode + 1) % 3; | |||
| if (gScope->mMode == 0) | |||
| { | |||
| if (ImGui::Button("Pause", ImVec2(80 * uiScale, 20 * uiScale))) | |||
| gScope->mMode = 1; | |||
| ImGui::Text("Nudge (ms)"); | |||
| ImGui::PushStyleColor(ImGuiCol_Button, 0xff3f3f3f); | |||
| ImGui::PushStyleColor(ImGuiCol_ButtonActive, 0xff3f3f3f); | |||
| ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xff3f3f3f); | |||
| ImGui::Button("-0.1", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::SameLine(); | |||
| ImGui::Button("+0.1", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::Button("-1", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::SameLine(); | |||
| ImGui::Button("+1", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::Button("-10", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::SameLine(); | |||
| ImGui::Button("+10", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::Button("-100", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::SameLine(); | |||
| ImGui::Button("+100", ImVec2(38 * uiScale, 20 * uiScale)); | |||
| ImGui::PopStyleColor(3); | |||
| } | |||
| else | |||
| { | |||
| if (ImGui::Button("Capture", ImVec2(80 * uiScale, 20 * uiScale))) | |||
| gScope->mMode = 0; | |||
| ImGui::Text("Nudge (ms)"); | |||
| if (ImGui::Button("-0.1", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll -= 0.0001f; | |||
| } | |||
| ImGui::SameLine(); | |||
| if (ImGui::Button("+0.1", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll += 0.0001f; | |||
| } | |||
| if (ImGui::Button("-1", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll -= 0.001f; | |||
| } | |||
| ImGui::SameLine(); | |||
| if (ImGui::Button("+1", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll += 0.001f; | |||
| } | |||
| if (ImGui::Button("-10", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll -= 0.01f; | |||
| } | |||
| ImGui::SameLine(); | |||
| if (ImGui::Button("+10", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll += 0.01f; | |||
| } | |||
| if (ImGui::Button("-100", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll -= 0.1f; | |||
| } | |||
| ImGui::SameLine(); | |||
| if (ImGui::Button("+100", ImVec2(38 * uiScale, 20 * uiScale))) | |||
| { | |||
| gScope->mScroll += 0.1f; | |||
| } | |||
| } | |||
| ImGui::EndChild(); | |||
| ImGui::End(); | |||
| } | |||
| @@ -218,6 +218,8 @@ endif | |||
| ifneq ($(HEADLESS),true) | |||
| PLUGIN_FILES += Cardinal/src/ImGuiWidget.cpp | |||
| PLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp | |||
| PLUGIN_FILES += Cardinal/src/SassyScope.cpp | |||
| # PLUGIN_FILES += Cardinal/src/sassy/sassy_scope.cpp | |||
| PLUGIN_FILES += $(wildcard Cardinal/src/DearImGui/*.cpp) | |||
| PLUGIN_FILES += $(wildcard Cardinal/src/DearImGuiColorTextEditor/*.cpp) | |||
| endif | |||
| @@ -878,6 +878,11 @@ static void initStatic__Cardinal() | |||
| spl.removeModule("Carla"); | |||
| spl.removeModule("Ildaeil"); | |||
| #endif | |||
| #ifndef HEADLESS | |||
| p->addModel(modelSassyScope); | |||
| #else | |||
| spl.removeModule("SassyScope"); | |||
| #endif | |||
| #if defined(HAVE_X11) && !defined(HEADLESS) && !defined(STATIC_BUILD) | |||
| p->addModel(modelMPV); | |||
| #else | |||