diff --git a/plugins/Cardinal/plugin.json b/plugins/Cardinal/plugin.json index 357f2bd..dfeed6e 100644 --- a/plugins/Cardinal/plugin.json +++ b/plugins/Cardinal/plugin.json @@ -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", diff --git a/plugins/Cardinal/src/SassyScope.cpp b/plugins/Cardinal/src/SassyScope.cpp new file mode 100644 index 0000000..fcee478 --- /dev/null +++ b/plugins/Cardinal/src/SassyScope.cpp @@ -0,0 +1,274 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021-2022 Filipe Coelho + * + * 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 FFTRealWithSize : public FFTReal
{ +public: + explicit FFTRealWithSize() : FFTReal
(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 fftObj16; + ffft::FFTRealWithSize fftObj32; + ffft::FFTRealWithSize fftObj64; + ffft::FFTRealWithSize fftObj128; + ffft::FFTRealWithSize fftObj256; + ffft::FFTRealWithSize fftObj512; + ffft::FFTRealWithSize fftObj1024; + ffft::FFTRealWithSize fftObj2048; + ffft::FFTRealWithSize fftObj4096; + ffft::FFTRealWithSize fftObj8192; + ffft::FFTRealWithSize fftObj16384; + ffft::FFTRealWithSize fftObj32768; + ffft::FFTRealWithSize 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 fftObj16(16*2); + static ffft::FFTReal fftObj32(32*2); + static ffft::FFTReal fftObj64(64*2); + static ffft::FFTReal fftObj128(128*2); + static ffft::FFTReal fftObj256(256*2); + static ffft::FFTReal fftObj512(512*2); + static ffft::FFTReal fftObj1024(1024*2); + static ffft::FFTReal fftObj2048(2048*2); + static ffft::FFTReal fftObj4096(4096*2); + static ffft::FFTReal fftObj8192(8192*2); + static ffft::FFTReal fftObj16384(16384*2); + static ffft::FFTReal fftObj32768(32768*2); + static ffft::FFTReal 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(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; iscene->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("SassyScope"); + +// -------------------------------------------------------------------------------------------------------------------- + diff --git a/plugins/Cardinal/src/plugin.hpp b/plugins/Cardinal/src/plugin.hpp index 230e957..d87d648 100644 --- a/plugins/Cardinal/src/plugin.hpp +++ b/plugins/Cardinal/src/plugin.hpp @@ -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 hostTerminalModels; diff --git a/plugins/Cardinal/src/sassy/fftreal/Array.h b/plugins/Cardinal/src/sassy/fftreal/Array.h new file mode 100644 index 0000000..f31b897 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/Array.h @@ -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 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/Array.hpp b/plugins/Cardinal/src/sassy/fftreal/Array.hpp new file mode 100644 index 0000000..0c9c285 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/Array.hpp @@ -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 + + + +namespace ffft +{ + + + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +template +Array ::Array () +{ + // Nothing +} + + + +template +const typename Array ::DataType & Array ::operator [] (long pos) const +{ + assert (pos >= 0); + assert (pos < LEN); + + return (_data_arr [pos]); +} + + + +template +typename Array ::DataType & Array ::operator [] (long pos) +{ + assert (pos >= 0); + assert (pos < LEN); + + return (_data_arr [pos]); +} + + + +template +long Array ::size () +{ + return (LEN); +} + + + +/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +} // namespace ffft + + + +#endif // ffft_Array_CODEHEADER_INCLUDED + +#undef ffft_Array_CURRENT_CODEHEADER + + + +/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/DynArray.h b/plugins/Cardinal/src/sassy/fftreal/DynArray.h new file mode 100644 index 0000000..756e443 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/DynArray.h @@ -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 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/DynArray.hpp b/plugins/Cardinal/src/sassy/fftreal/DynArray.hpp new file mode 100644 index 0000000..38f1471 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/DynArray.hpp @@ -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 + + + +namespace ffft +{ + + + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +template +DynArray ::DynArray () +: _data_ptr (0) +, _len (0) +{ + // Nothing +} + + + +template +DynArray ::DynArray (long size) +: _data_ptr (0) +, _len (0) +{ + assert (size >= 0); + if (size > 0) + { + _data_ptr = new DataType [size]; + _len = size; + } +} + + + +template +DynArray ::~DynArray () +{ + delete [] _data_ptr; + _data_ptr = 0; + _len = 0; +} + + + +template +long DynArray ::size () const +{ + return (_len); +} + + + +template +void DynArray ::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 +const typename DynArray ::DataType & DynArray ::operator [] (long pos) const +{ + assert (pos >= 0); + assert (pos < _len); + + return (_data_ptr [pos]); +} + + + +template +typename DynArray ::DataType & DynArray ::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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTReal.h b/plugins/Cardinal/src/sassy/fftreal/FFTReal.h new file mode 100644 index 0000000..f9a2553 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTReal.h @@ -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 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 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 + _br_lut; + DynArray + _trigo_lut; + mutable DynArray + _buffer; + mutable DynArray + _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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTReal.hpp b/plugins/Cardinal/src/sassy/fftreal/FFTReal.hpp new file mode 100644 index 0000000..590a3ff --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTReal.hpp @@ -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 +#include + + + +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 +FFTReal
::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 +long FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +typename FFTReal
::DataType * FFTReal
::use_buffer () const +{ + return (&_buffer [0]); +} + + + +/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +template +void FFTReal
::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 +void FFTReal
::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 (cos (i * mul)); + } + } + } +} + + + +template +void FFTReal
::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 +const long * FFTReal
::get_br_ptr () const +{ + return (&_br_lut [0]); +} + + + +template +const typename FFTReal
::DataType * FFTReal
::get_trigo_ptr (int level) const +{ + assert (level >= 3); + + return (&_trigo_lut [get_trigo_level_index (level)]); +} + + + +template +long FFTReal
::get_trigo_level_index (int level) const +{ + assert (level >= 3); + + return ((1L << (level - 1)) - 4); +} + + + +// Transform in several passes +template +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 (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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 +void FFTReal
::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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.h b/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.h new file mode 100644 index 0000000..3c087a8 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.h @@ -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 +class FFTRealFixLen +{ + typedef int CompileTimeCheck1 [(LL2 >= 0) ? 1 : -1]; + typedef int CompileTimeCheck2 [(LL2 <= 30) ? 1 : -1]; + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +public: + + typedef FFTRealFixLenParam::DataType DataType; + typedef OscSinCos 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 + _buffer; + DynArray + _br_data; + DynArray + _trigo_data; + Array + _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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.hpp b/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.hpp new file mode 100644 index 0000000..d8ac5c5 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.hpp @@ -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 +#include + +namespace std { } + + + +namespace ffft +{ + + + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +template +FFTRealFixLen ::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 +long FFTRealFixLen ::get_length () const +{ + return (FFT_LEN); +} + + + +// General case +template +void FFTRealFixLen ::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 ::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 +void FFTRealFixLen ::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 ::sel_bin (&_buffer [0], x); + DataType * d_ptr = + FFTRealSelect ::sel_bin (x, &_buffer [0]); + const DataType * cos_ptr = &_trigo_data [0]; + const long * br_ptr = &_br_data [0]; + + FFTRealPassInverse ::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 +void FFTRealFixLen ::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 +void FFTRealFixLen ::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 +void FFTRealFixLen ::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 +void FFTRealFixLen ::build_trigo_osc () +{ + for (int i = 0; i < NBR_TRIGO_OSC; ++i) + { + OscType & osc = _trigo_osc [i]; + + const long len = static_cast (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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLenParam.h b/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLenParam.h new file mode 100644 index 0000000..2fde41c --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealFixLenParam.h @@ -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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.h b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.h new file mode 100644 index 0000000..7aa5816 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.h @@ -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 +class FFTRealPassDirect +{ + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +public: + + typedef FFTRealFixLenParam::DataType DataType; + typedef OscSinCos 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.hpp b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.hpp new file mode 100644 index 0000000..2cbc5bf --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.hpp @@ -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 +void FFTRealPassDirect ::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 ::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 ::prepare (osc_list [TRIGO_OSC]); + + // Others are conjugate complex numbers + for (long i = 1; i < dist; ++ i) + { + DataType c; + DataType s; + FFTRealUseTrigo ::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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.h b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.h new file mode 100644 index 0000000..f822231 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.h @@ -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 +class FFTRealPassInverse +{ + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +public: + + typedef FFTRealFixLenParam::DataType DataType; + typedef OscSinCos 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.hpp b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.hpp new file mode 100644 index 0000000..f93ef20 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.hpp @@ -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 +void FFTRealPassInverse ::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 ::process_rec ( + len, + src_ptr, + dest_ptr, + cos_ptr, + cos_len, + br_ptr, + osc_list + ); +} + + + +template +void FFTRealPassInverse ::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 ::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 +void FFTRealPassInverse ::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 ::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 ::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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.h b/plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.h new file mode 100644 index 0000000..c8bed63 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.h @@ -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 +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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.hpp b/plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.hpp new file mode 100644 index 0000000..a8ed9f1 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.hpp @@ -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 +float * FFTRealSelect

::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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.h b/plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.h new file mode 100644 index 0000000..47067f0 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.h @@ -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 +class FFTRealUseTrigo +{ + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +public: + + typedef FFTRealFixLenParam::DataType DataType; + typedef OscSinCos 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.hpp b/plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.hpp new file mode 100644 index 0000000..a6d5cb1 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.hpp @@ -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 +void FFTRealUseTrigo ::prepare (OscType &osc) +{ + osc.clear_buffers (); +} + +template <> +inline void FFTRealUseTrigo <0>::prepare (OscType &) +{ + // Nothing +} + + + +template +void FFTRealUseTrigo ::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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/OscSinCos.h b/plugins/Cardinal/src/sassy/fftreal/OscSinCos.h new file mode 100644 index 0000000..c4539f9 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/OscSinCos.h @@ -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 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/OscSinCos.hpp b/plugins/Cardinal/src/sassy/fftreal/OscSinCos.hpp new file mode 100644 index 0000000..b565ad1 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/OscSinCos.hpp @@ -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 + +namespace std { } + + + +namespace ffft +{ + + + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +template +OscSinCos ::OscSinCos () +: _pos_cos (1) +, _pos_sin (0) +, _step_cos (1) +, _step_sin (0) +{ + // Nothing +} + + + +template +void OscSinCos ::set_step (double angle_rad) +{ + using namespace std; + + _step_cos = static_cast (cos (angle_rad)); + _step_sin = static_cast (sin (angle_rad)); +} + + + +template +typename OscSinCos ::DataType OscSinCos ::get_cos () const +{ + return (_pos_cos); +} + + + +template +typename OscSinCos ::DataType OscSinCos ::get_sin () const +{ + return (_pos_sin); +} + + + +template +void OscSinCos ::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 +void OscSinCos ::clear_buffers () +{ + _pos_cos = static_cast (1); + _pos_sin = static_cast (0); +} + + + +/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +} // namespace ffft + + + +#endif // ffft_OscSinCos_CODEHEADER_INCLUDED + +#undef ffft_OscSinCos_CURRENT_CODEHEADER + + + +/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/def.h b/plugins/Cardinal/src/sassy/fftreal/def.h new file mode 100644 index 0000000..f226969 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/def.h @@ -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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/plugins/Cardinal/src/sassy/fftreal/license.txt b/plugins/Cardinal/src/sassy/fftreal/license.txt new file mode 100644 index 0000000..5a8e332 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/license.txt @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + 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. + diff --git a/plugins/Cardinal/src/sassy/fftreal/readme.txt b/plugins/Cardinal/src/sassy/fftreal/readme.txt new file mode 100644 index 0000000..abce4e6 --- /dev/null +++ b/plugins/Cardinal/src/sassy/fftreal/readme.txt @@ -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 +or FFTReal 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 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 + diff --git a/plugins/Cardinal/src/sassy/sassy.hpp b/plugins/Cardinal/src/sassy/sassy.hpp new file mode 100644 index 0000000..254db7f --- /dev/null +++ b/plugins/Cardinal/src/sassy/sassy.hpp @@ -0,0 +1,131 @@ +/* + * Sassy scope exported API + * Copyright (C) 2022 Filipe Coelho + * + * 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* obj16; + ffft::FFTReal* obj32; + ffft::FFTReal* obj64; + ffft::FFTReal* obj128; + ffft::FFTReal* obj256; + ffft::FFTReal* obj512; + ffft::FFTReal* obj1024; + ffft::FFTReal* obj2048; + ffft::FFTReal* obj4096; + ffft::FFTReal* obj8192; + ffft::FFTReal* obj16384; + ffft::FFTReal* obj32768; + ffft::FFTReal* 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); diff --git a/plugins/Cardinal/src/sassy/sassy_scope.cpp b/plugins/Cardinal/src/sassy/sassy_scope.cpp new file mode 100644 index 0000000..ad65533 --- /dev/null +++ b/plugins/Cardinal/src/sassy/sassy_scope.cpp @@ -0,0 +1,872 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021-2022 Filipe Coelho + * + * 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(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* 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(); + +} diff --git a/plugins/Makefile b/plugins/Makefile index 513ff68..59d9833 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -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 diff --git a/plugins/plugins.cpp b/plugins/plugins.cpp index c9c3eee..a88eb62 100644 --- a/plugins/plugins.cpp +++ b/plugins/plugins.cpp @@ -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