Browse Source

Bring in Sassy Scope

Signed-off-by: falkTX <falktx@falktx.com>
tags/22.06
falkTX 3 years ago
parent
commit
f83b483e82
29 changed files with 4901 additions and 0 deletions
  1. +9
    -0
      plugins/Cardinal/plugin.json
  2. +274
    -0
      plugins/Cardinal/src/SassyScope.cpp
  3. +1
    -0
      plugins/Cardinal/src/plugin.hpp
  4. +98
    -0
      plugins/Cardinal/src/sassy/fftreal/Array.h
  5. +99
    -0
      plugins/Cardinal/src/sassy/fftreal/Array.hpp
  6. +101
    -0
      plugins/Cardinal/src/sassy/fftreal/DynArray.h
  7. +144
    -0
      plugins/Cardinal/src/sassy/fftreal/DynArray.hpp
  8. +143
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTReal.h
  9. +917
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTReal.hpp
  10. +131
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.h
  11. +323
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.hpp
  12. +90
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealFixLenParam.h
  13. +96
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.h
  14. +205
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.hpp
  15. +101
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.h
  16. +230
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.hpp
  17. +78
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.h
  18. +63
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.hpp
  19. +99
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.h
  20. +92
    -0
      plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.hpp
  21. +107
    -0
      plugins/Cardinal/src/sassy/fftreal/OscSinCos.h
  22. +123
    -0
      plugins/Cardinal/src/sassy/fftreal/OscSinCos.hpp
  23. +60
    -0
      plugins/Cardinal/src/sassy/fftreal/def.h
  24. +14
    -0
      plugins/Cardinal/src/sassy/fftreal/license.txt
  25. +293
    -0
      plugins/Cardinal/src/sassy/fftreal/readme.txt
  26. +131
    -0
      plugins/Cardinal/src/sassy/sassy.hpp
  27. +872
    -0
      plugins/Cardinal/src/sassy/sassy_scope.cpp
  28. +2
    -0
      plugins/Makefile
  29. +5
    -0
      plugins/plugins.cpp

+ 9
- 0
plugins/Cardinal/plugin.json View File

@@ -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",


+ 274
- 0
plugins/Cardinal/src/SassyScope.cpp View File

@@ -0,0 +1,274 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/

#include "plugincontext.hpp"
#include "ImGuiWidget.hpp"
#include "sassy/sassy.hpp"
#include "sassy/sassy_scope.cpp"

namespace ffft {
template<class DT, int size>
class FFTRealWithSize : public FFTReal<DT> {
public:
explicit FFTRealWithSize() : FFTReal<DT>(size) {}
};
}

struct SassyScopeModule : Module {
enum ParamIds {
NUM_PARAMS
};
enum InputIds {
INPUT1,
INPUT2,
INPUT3,
INPUT4,
NUM_INPUTS
};
enum OutputIds {
NUM_OUTPUTS
};
enum LightIds {
NUM_LIGHTS
};

ScopeData scope;

ffft::FFTRealWithSize<float, 16*2> fftObj16;
ffft::FFTRealWithSize<float, 32*2> fftObj32;
ffft::FFTRealWithSize<float, 64*2> fftObj64;
ffft::FFTRealWithSize<float, 128*2> fftObj128;
ffft::FFTRealWithSize<float, 256*2> fftObj256;
ffft::FFTRealWithSize<float, 512*2> fftObj512;
ffft::FFTRealWithSize<float, 1024*2> fftObj1024;
ffft::FFTRealWithSize<float, 2048*2> fftObj2048;
ffft::FFTRealWithSize<float, 4096*2> fftObj4096;
ffft::FFTRealWithSize<float, 8192*2> fftObj8192;
ffft::FFTRealWithSize<float, 16384*2> fftObj16384;
ffft::FFTRealWithSize<float, 32768*2> fftObj32768;
ffft::FFTRealWithSize<float, 65536*2> fftObj65536;

SassyScopeModule()
{
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);

scope.fft.average = 1;
scope.fft.obj16 = &fftObj16;
scope.fft.obj32 = &fftObj32;
scope.fft.obj64 = &fftObj64;
scope.fft.obj128 = &fftObj128;
scope.fft.obj256 = &fftObj256;
scope.fft.obj512 = &fftObj512;
scope.fft.obj1024 = &fftObj1024;
scope.fft.obj2048 = &fftObj2048;
scope.fft.obj4096 = &fftObj4096;
scope.fft.obj8192 = &fftObj8192;
scope.fft.obj16384 = &fftObj16384;
scope.fft.obj32768 = &fftObj32768;
scope.fft.obj65536 = &fftObj65536;
}

void process(const ProcessArgs&) override
{
scope.probe(inputs[INPUT1].getVoltage(),
inputs[INPUT2].getVoltage(),
inputs[INPUT3].getVoltage(),
inputs[INPUT4].getVoltage());
}

void onSampleRateChange(const SampleRateChangeEvent& e) override
{
scope.realloc(e.sampleRate);
}
};

// used for module browser
static ScopeData* getFakeScopeInstance()
{
static ScopeData scope;
static ffft::FFTReal<float> fftObj16(16*2);
static ffft::FFTReal<float> fftObj32(32*2);
static ffft::FFTReal<float> fftObj64(64*2);
static ffft::FFTReal<float> fftObj128(128*2);
static ffft::FFTReal<float> fftObj256(256*2);
static ffft::FFTReal<float> fftObj512(512*2);
static ffft::FFTReal<float> fftObj1024(1024*2);
static ffft::FFTReal<float> fftObj2048(2048*2);
static ffft::FFTReal<float> fftObj4096(4096*2);
static ffft::FFTReal<float> fftObj8192(8192*2);
static ffft::FFTReal<float> fftObj16384(16384*2);
static ffft::FFTReal<float> fftObj32768(32768*2);
static ffft::FFTReal<float> fftObj65536(65536*2);

static bool needsInit = true;

if (needsInit)
{
needsInit = false;
scope.fft.average = 1;
scope.fft.obj16 = &fftObj16;
scope.fft.obj32 = &fftObj32;
scope.fft.obj64 = &fftObj64;
scope.fft.obj128 = &fftObj128;
scope.fft.obj256 = &fftObj256;
scope.fft.obj512 = &fftObj512;
scope.fft.obj1024 = &fftObj1024;
scope.fft.obj2048 = &fftObj2048;
scope.fft.obj4096 = &fftObj4096;
scope.fft.obj8192 = &fftObj8192;
scope.fft.obj16384 = &fftObj16384;
scope.fft.obj32768 = &fftObj32768;
scope.fft.obj65536 = &fftObj65536;
scope.realloc(48000);
}

return &scope;
}

struct SassyScopeWidget : ImGuiWidget {
SassyScopeModule* module = nullptr;

void drawImGui() override
{
const float scaleFactor = getScaleFactor();

ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(box.size.x * scaleFactor, box.size.y * scaleFactor));

do_show_scope_window(module != nullptr ? &module->scope : getFakeScopeInstance(), scaleFactor);
}

void onButton(const ButtonEvent& e)
{
// if mouse press is over draggable areas, do nothing so event can go to Rack
if (e.action == GLFW_PRESS)
{
// bottom left
if (e.pos.x < 116 && e.pos.y >= 335)
return;
// bottom right
if (e.pos.x >= 456 && e.pos.y >= 348)
return;
// fft label
if (e.pos.x >= 491 && e.pos.y >= 54 && e.pos.y <= 74)
return;
// nudge label
if (e.pos.x >= 463 && e.pos.y >= 236 && e.pos.y <= 255)
return;
// center scope
if (e.pos.x >= 110 && e.pos.x <= 452 && e.pos.y >= 0 && e.pos.y <= 350)
return;
}

ImGuiWidget::onButton(e);
}
};

struct SassyScopeModuleWidget : ModuleWidget {
SassyScopeModule* scopeModule = nullptr;
SassyScopeWidget* scopeWidget = nullptr;

SassyScopeModuleWidget(SassyScopeModule* const module) {
setModule(module);
box.size = Vec(RACK_GRID_WIDTH * 37, RACK_GRID_HEIGHT);

scopeModule = module;
scopeWidget = new SassyScopeWidget();
scopeWidget->box.pos = Vec(0, 0);
scopeWidget->box.size = Vec(box.size.x, box.size.y);
scopeWidget->module = module;
addChild(scopeWidget);

for (int i=0; i<SassyScopeModule::NUM_INPUTS; ++i)
addInput(createInput<PJ301MPort>(Vec(5 + 26.5f * i, RACK_GRID_HEIGHT - 40), module, i));
}

void draw(const DrawArgs& args) override
{
nvgBeginPath(args.vg);
nvgRect(args.vg, 0.0, 0.0, box.size.x, box.size.y);
nvgFillColor(args.vg, nvgRGB(0x20, 0x20, 0x20));
nvgFill(args.vg);
ModuleWidget::draw(args);
}

void step() override
{
ModuleWidget::step();

if (scopeModule == nullptr)
return;

// Update colors
for (int i=0; i<SassyScopeModule::NUM_INPUTS; ++i)
{
if (CableWidget* const cableWidget = APP->scene->rack->getTopCable(getInput(i)))
{
NVGcolor c = cableWidget->color;
scopeModule->scope.colors[i] = (clamp(int(c.a * 0xff), 0, 0xff) << 24)
| (clamp(int(c.b * 0xff), 0, 0xff) << 16)
| (clamp(int(c.g * 0xff), 0, 0xff) << 8)
| clamp(int(c.r * 0xff), 0, 0xff);
}
}
}

void appendContextMenu(Menu* const menu) override
{
menu->addChild(new MenuSeparator);

struct AveragingItem : MenuItem {
ScopeData* scope;
Menu* createChildMenu() override {
Menu* menu = new Menu;
menu->addChild(createCheckMenuItem("1x", "",
[=]() {return scope->fft.average == 1;},
[=]() {scope->fft.average = 1;}
));
menu->addChild(createCheckMenuItem("4x", "",
[=]() {return scope->fft.average == 4;},
[=]() {scope->fft.average = 4;}
));
menu->addChild(createCheckMenuItem("16x", "",
[=]() {return scope->fft.average == 16;},
[=]() {scope->fft.average = 16;}
));
menu->addChild(createCheckMenuItem("64x", "",
[=]() {return scope->fft.average == 64;},
[=]() {scope->fft.average = 64;}
));
menu->addChild(createCheckMenuItem("256x", "",
[=]() {return scope->fft.average == 256;},
[=]() {scope->fft.average = 256;}
));
return menu;
}
};
AveragingItem* const averagingItem = new AveragingItem;
averagingItem->text = "Averaging (FFT mode)";
averagingItem->rightText = string::f("%d", scopeModule->scope.fft.average) + " " + RIGHT_ARROW;
averagingItem->scope = &scopeModule->scope;
menu->addChild(averagingItem);
}
};

// --------------------------------------------------------------------------------------------------------------------

Model* modelSassyScope = createModel<SassyScopeModule, SassyScopeModuleWidget>("SassyScope");

// --------------------------------------------------------------------------------------------------------------------


+ 1
- 0
plugins/Cardinal/src/plugin.hpp View File

@@ -46,6 +46,7 @@ extern Model* modelHostParameters;
extern Model* modelHostTime;
extern Model* modelIldaeil;
extern Model* modelMPV;
extern Model* modelSassyScope;
extern Model* modelTextEditor;

extern std::vector<Model*> hostTerminalModels;


+ 98
- 0
plugins/Cardinal/src/sassy/fftreal/Array.h View File

@@ -0,0 +1,98 @@
/*****************************************************************************

Array.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_Array_HEADER_INCLUDED)
#define ffft_Array_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



namespace ffft
{



template <class T, long LEN>
class Array
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

typedef T DataType;

Array ();

inline const DataType &
operator [] (long pos) const;
inline DataType &
operator [] (long pos);

static inline long
size ();



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

DataType _data_arr [LEN];



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

Array (const Array &other);
Array & operator = (const Array &other);
bool operator == (const Array &other);
bool operator != (const Array &other);

}; // class Array



} // namespace ffft



#include "Array.hpp"



#endif // ffft_Array_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 99
- 0
plugins/Cardinal/src/sassy/fftreal/Array.hpp View File

@@ -0,0 +1,99 @@
/*****************************************************************************

Array.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_Array_CURRENT_CODEHEADER)
#error Recursive inclusion of Array code header.
#endif
#define ffft_Array_CURRENT_CODEHEADER

#if ! defined (ffft_Array_CODEHEADER_INCLUDED)
#define ffft_Array_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include <cassert>



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <class T, long LEN>
Array <T, LEN>::Array ()
{
// Nothing
}



template <class T, long LEN>
const typename Array <T, LEN>::DataType & Array <T, LEN>::operator [] (long pos) const
{
assert (pos >= 0);
assert (pos < LEN);

return (_data_arr [pos]);
}



template <class T, long LEN>
typename Array <T, LEN>::DataType & Array <T, LEN>::operator [] (long pos)
{
assert (pos >= 0);
assert (pos < LEN);

return (_data_arr [pos]);
}



template <class T, long LEN>
long Array <T, LEN>::size ()
{
return (LEN);
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



} // namespace ffft



#endif // ffft_Array_CODEHEADER_INCLUDED

#undef ffft_Array_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 101
- 0
plugins/Cardinal/src/sassy/fftreal/DynArray.h View File

@@ -0,0 +1,101 @@
/*****************************************************************************

DynArray.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_DynArray_HEADER_INCLUDED)
#define ffft_DynArray_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



namespace ffft
{



template <class T>
class DynArray
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

typedef T DataType;

DynArray ();
explicit DynArray (long size);
~DynArray ();

inline long size () const;
inline void resize (long size);

inline const DataType &
operator [] (long pos) const;
inline DataType &
operator [] (long pos);



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

DataType * _data_ptr;
long _len;



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

DynArray (const DynArray &other);
DynArray & operator = (const DynArray &other);
bool operator == (const DynArray &other);
bool operator != (const DynArray &other);

}; // class DynArray



} // namespace ffft



#include "DynArray.hpp"



#endif // ffft_DynArray_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 144
- 0
plugins/Cardinal/src/sassy/fftreal/DynArray.hpp View File

@@ -0,0 +1,144 @@
/*****************************************************************************

DynArray.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_DynArray_CURRENT_CODEHEADER)
#error Recursive inclusion of DynArray code header.
#endif
#define ffft_DynArray_CURRENT_CODEHEADER

#if ! defined (ffft_DynArray_CODEHEADER_INCLUDED)
#define ffft_DynArray_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include <cassert>



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <class T>
DynArray <T>::DynArray ()
: _data_ptr (0)
, _len (0)
{
// Nothing
}



template <class T>
DynArray <T>::DynArray (long size)
: _data_ptr (0)
, _len (0)
{
assert (size >= 0);
if (size > 0)
{
_data_ptr = new DataType [size];
_len = size;
}
}



template <class T>
DynArray <T>::~DynArray ()
{
delete [] _data_ptr;
_data_ptr = 0;
_len = 0;
}



template <class T>
long DynArray <T>::size () const
{
return (_len);
}



template <class T>
void DynArray <T>::resize (long size)
{
assert (size >= 0);
if (size > 0)
{
DataType * old_data_ptr = _data_ptr;
DataType * tmp_data_ptr = new DataType [size];

_data_ptr = tmp_data_ptr;
_len = size;

delete [] old_data_ptr;
}
}



template <class T>
const typename DynArray <T>::DataType & DynArray <T>::operator [] (long pos) const
{
assert (pos >= 0);
assert (pos < _len);

return (_data_ptr [pos]);
}



template <class T>
typename DynArray <T>::DataType & DynArray <T>::operator [] (long pos)
{
assert (pos >= 0);
assert (pos < _len);

return (_data_ptr [pos]);
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



} // namespace ffft



#endif // ffft_DynArray_CODEHEADER_INCLUDED

#undef ffft_DynArray_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 143
- 0
plugins/Cardinal/src/sassy/fftreal/FFTReal.h View File

@@ -0,0 +1,143 @@
/*****************************************************************************

FFTReal.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_FFTReal_HEADER_INCLUDED)
#define ffft_FFTReal_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "def.h"
#include "DynArray.h"
#include "OscSinCos.h"



namespace ffft
{



template <class DT>
class FFTReal
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

enum { MAX_BIT_DEPTH = 30 }; // So length can be represented as long int

typedef DT DataType;

explicit FFTReal (long length);
virtual ~FFTReal () {}

long get_length () const;
void do_fft (DataType f [], const DataType x []) const;
void do_ifft (const DataType f [], DataType x []) const;
void rescale (DataType x []) const;
DataType * use_buffer () const;



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

// Over this bit depth, we use direct calculation for sin/cos
enum { TRIGO_BD_LIMIT = 12 };

typedef OscSinCos <DataType> OscType;

void init_br_lut ();
void init_trigo_lut ();
void init_trigo_osc ();

ffft_FORCEINLINE const long *
get_br_ptr () const;
ffft_FORCEINLINE const DataType *
get_trigo_ptr (int level) const;
ffft_FORCEINLINE long
get_trigo_level_index (int level) const;

inline void compute_fft_general (DataType f [], const DataType x []) const;
inline void compute_direct_pass_1_2 (DataType df [], const DataType x []) const;
inline void compute_direct_pass_3 (DataType df [], const DataType sf []) const;
inline void compute_direct_pass_n (DataType df [], const DataType sf [], int pass) const;
inline void compute_direct_pass_n_lut (DataType df [], const DataType sf [], int pass) const;
inline void compute_direct_pass_n_osc (DataType df [], const DataType sf [], int pass) const;

inline void compute_ifft_general (const DataType f [], DataType x []) const;
inline void compute_inverse_pass_n (DataType df [], const DataType sf [], int pass) const;
inline void compute_inverse_pass_n_osc (DataType df [], const DataType sf [], int pass) const;
inline void compute_inverse_pass_n_lut (DataType df [], const DataType sf [], int pass) const;
inline void compute_inverse_pass_3 (DataType df [], const DataType sf []) const;
inline void compute_inverse_pass_1_2 (DataType x [], const DataType sf []) const;

const long _length;
const int _nbr_bits;
DynArray <long>
_br_lut;
DynArray <DataType>
_trigo_lut;
mutable DynArray <DataType>
_buffer;
mutable DynArray <OscType>
_trigo_osc;



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

FFTReal ();
FFTReal (const FFTReal &other);
FFTReal & operator = (const FFTReal &other);
bool operator == (const FFTReal &other);
bool operator != (const FFTReal &other);

}; // class FFTReal



} // namespace ffft



#include "FFTReal.hpp"



#endif // ffft_FFTReal_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 917
- 0
plugins/Cardinal/src/sassy/fftreal/FFTReal.hpp View File

@@ -0,0 +1,917 @@
/*****************************************************************************

FFTReal.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_FFTReal_CURRENT_CODEHEADER)
#error Recursive inclusion of FFTReal code header.
#endif
#define ffft_FFTReal_CURRENT_CODEHEADER

#if ! defined (ffft_FFTReal_CODEHEADER_INCLUDED)
#define ffft_FFTReal_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include <cassert>
#include <cmath>



namespace ffft
{



static inline bool FFTReal_is_pow2 (long x)
{
assert (x > 0);

return ((x & -x) == x);
}



static inline int FFTReal_get_next_pow2 (long x)
{
--x;

int p = 0;
while ((x & ~0xFFFFL) != 0)
{
p += 16;
x >>= 16;
}
while ((x & ~0xFL) != 0)
{
p += 4;
x >>= 4;
}
while (x > 0)
{
++p;
x >>= 1;
}

return (p);
}



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*
==============================================================================
Name: ctor
Input parameters:
- length: length of the array on which we want to do a FFT. Range: power of
2 only, > 0.
Throws: std::bad_alloc
==============================================================================
*/

template <class DT>
FFTReal <DT>::FFTReal (long length)
: _length (length)
, _nbr_bits (FFTReal_get_next_pow2 (length))
, _br_lut ()
, _trigo_lut ()
, _buffer (length)
, _trigo_osc ()
{
assert (FFTReal_is_pow2 (length));
assert (_nbr_bits <= MAX_BIT_DEPTH);

init_br_lut ();
init_trigo_lut ();
init_trigo_osc ();
}



/*
==============================================================================
Name: get_length
Description:
Returns the number of points processed by this FFT object.
Returns: The number of points, power of 2, > 0.
Throws: Nothing
==============================================================================
*/

template <class DT>
long FFTReal <DT>::get_length () const
{
return (_length);
}



/*
==============================================================================
Name: do_fft
Description:
Compute the FFT of the array.
Input parameters:
- x: pointer on the source array (time).
Output parameters:
- f: pointer on the destination array (frequencies).
f [0...length(x)/2] = real values,
f [length(x)/2+1...length(x)-1] = negative imaginary values of
coefficents 1...length(x)/2-1.
Throws: Nothing
==============================================================================
*/

template <class DT>
void FFTReal <DT>::do_fft (DataType f [], const DataType x []) const
{
assert (f != 0);
assert (f != use_buffer ());
assert (x != 0);
assert (x != use_buffer ());
assert (x != f);

// General case
if (_nbr_bits > 2)
{
compute_fft_general (f, x);
}

// 4-point FFT
else if (_nbr_bits == 2)
{
f [1] = x [0] - x [2];
f [3] = x [1] - x [3];

const DataType b_0 = x [0] + x [2];
const DataType b_2 = x [1] + x [3];
f [0] = b_0 + b_2;
f [2] = b_0 - b_2;
}

// 2-point FFT
else if (_nbr_bits == 1)
{
f [0] = x [0] + x [1];
f [1] = x [0] - x [1];
}

// 1-point FFT
else
{
f [0] = x [0];
}
}



/*
==============================================================================
Name: do_ifft
Description:
Compute the inverse FFT of the array. Note that data must be post-scaled:
IFFT (FFT (x)) = x * length (x).
Input parameters:
- f: pointer on the source array (frequencies).
f [0...length(x)/2] = real values
f [length(x)/2+1...length(x)-1] = negative imaginary values of
coefficents 1...length(x)/2-1.
Output parameters:
- x: pointer on the destination array (time).
Throws: Nothing
==============================================================================
*/

template <class DT>
void FFTReal <DT>::do_ifft (const DataType f [], DataType x []) const
{
assert (f != 0);
assert (f != use_buffer ());
assert (x != 0);
assert (x != use_buffer ());
assert (x != f);

// General case
if (_nbr_bits > 2)
{
compute_ifft_general (f, x);
}

// 4-point IFFT
else if (_nbr_bits == 2)
{
const DataType b_0 = f [0] + f [2];
const DataType b_2 = f [0] - f [2];

x [0] = b_0 + f [1] * 2;
x [2] = b_0 - f [1] * 2;
x [1] = b_2 + f [3] * 2;
x [3] = b_2 - f [3] * 2;
}

// 2-point IFFT
else if (_nbr_bits == 1)
{
x [0] = f [0] + f [1];
x [1] = f [0] - f [1];
}

// 1-point IFFT
else
{
x [0] = f [0];
}
}



/*
==============================================================================
Name: rescale
Description:
Scale an array by divide each element by its length. This function should
be called after FFT + IFFT.
Input parameters:
- x: pointer on array to rescale (time or frequency).
Throws: Nothing
==============================================================================
*/

template <class DT>
void FFTReal <DT>::rescale (DataType x []) const
{
const DataType mul = DataType (1.0 / _length);

if (_length < 4)
{
long i = _length - 1;
do
{
x [i] *= mul;
--i;
}
while (i >= 0);
}

else
{
assert ((_length & 3) == 0);

// Could be optimized with SIMD instruction sets (needs alignment check)
long i = _length - 4;
do
{
x [i + 0] *= mul;
x [i + 1] *= mul;
x [i + 2] *= mul;
x [i + 3] *= mul;
i -= 4;
}
while (i >= 0);
}
}



/*
==============================================================================
Name: use_buffer
Description:
Access the internal buffer, whose length is the FFT one.
Buffer content will be erased at each do_fft() / do_ifft() call!
This buffer cannot be used as:
- source for FFT or IFFT done with this object
- destination for FFT or IFFT done with this object
Returns:
Buffer start address
Throws: Nothing
==============================================================================
*/

template <class DT>
typename FFTReal <DT>::DataType * FFTReal <DT>::use_buffer () const
{
return (&_buffer [0]);
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <class DT>
void FFTReal <DT>::init_br_lut ()
{
const long length = 1L << _nbr_bits;
_br_lut.resize (length);

_br_lut [0] = 0;
long br_index = 0;
for (long cnt = 1; cnt < length; ++cnt)
{
// ++br_index (bit reversed)
long bit = length >> 1;
while (((br_index ^= bit) & bit) == 0)
{
bit >>= 1;
}

_br_lut [cnt] = br_index;
}
}



template <class DT>
void FFTReal <DT>::init_trigo_lut ()
{
using namespace std;

if (_nbr_bits > 3)
{
const long total_len = (1L << (_nbr_bits - 1)) - 4;
_trigo_lut.resize (total_len);

for (int level = 3; level < _nbr_bits; ++level)
{
const long level_len = 1L << (level - 1);
DataType * const level_ptr =
&_trigo_lut [get_trigo_level_index (level)];
const double mul = PI / (level_len << 1);

for (long i = 0; i < level_len; ++ i)
{
level_ptr [i] = static_cast <DataType> (cos (i * mul));
}
}
}
}



template <class DT>
void FFTReal <DT>::init_trigo_osc ()
{
const int nbr_osc = _nbr_bits - TRIGO_BD_LIMIT;
if (nbr_osc > 0)
{
_trigo_osc.resize (nbr_osc);

for (int osc_cnt = 0; osc_cnt < nbr_osc; ++osc_cnt)
{
OscType & osc = _trigo_osc [osc_cnt];

const long len = 1L << (TRIGO_BD_LIMIT + osc_cnt);
const double mul = (0.5 * PI) / len;
osc.set_step (mul);
}
}
}



template <class DT>
const long * FFTReal <DT>::get_br_ptr () const
{
return (&_br_lut [0]);
}



template <class DT>
const typename FFTReal <DT>::DataType * FFTReal <DT>::get_trigo_ptr (int level) const
{
assert (level >= 3);

return (&_trigo_lut [get_trigo_level_index (level)]);
}



template <class DT>
long FFTReal <DT>::get_trigo_level_index (int level) const
{
assert (level >= 3);

return ((1L << (level - 1)) - 4);
}



// Transform in several passes
template <class DT>
void FFTReal <DT>::compute_fft_general (DataType f [], const DataType x []) const
{
assert (f != 0);
assert (f != use_buffer ());
assert (x != 0);
assert (x != use_buffer ());
assert (x != f);

DataType * sf;
DataType * df;

if ((_nbr_bits & 1) != 0)
{
df = use_buffer ();
sf = f;
}
else
{
df = f;
sf = use_buffer ();
}

compute_direct_pass_1_2 (df, x);
compute_direct_pass_3 (sf, df);

for (int pass = 3; pass < _nbr_bits; ++ pass)
{
compute_direct_pass_n (df, sf, pass);

DataType * const temp_ptr = df;
df = sf;
sf = temp_ptr;
}
}



template <class DT>
void FFTReal <DT>::compute_direct_pass_1_2 (DataType df [], const DataType x []) const
{
assert (df != 0);
assert (x != 0);
assert (df != x);

const long * const bit_rev_lut_ptr = get_br_ptr ();
long coef_index = 0;
do
{
const long rev_index_0 = bit_rev_lut_ptr [coef_index];
const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1];
const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2];
const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3];

DataType * const df2 = df + coef_index;
df2 [1] = x [rev_index_0] - x [rev_index_1];
df2 [3] = x [rev_index_2] - x [rev_index_3];

const DataType sf_0 = x [rev_index_0] + x [rev_index_1];
const DataType sf_2 = x [rev_index_2] + x [rev_index_3];

df2 [0] = sf_0 + sf_2;
df2 [2] = sf_0 - sf_2;
coef_index += 4;
}
while (coef_index < _length);
}



template <class DT>
void FFTReal <DT>::compute_direct_pass_3 (DataType df [], const DataType sf []) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);

const DataType sqrt2_2 = DataType (SQRT2 * 0.5);
long coef_index = 0;
do
{
DataType v;

df [coef_index] = sf [coef_index] + sf [coef_index + 4];
df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4];
df [coef_index + 2] = sf [coef_index + 2];
df [coef_index + 6] = sf [coef_index + 6];

v = (sf [coef_index + 5] - sf [coef_index + 7]) * sqrt2_2;
df [coef_index + 1] = sf [coef_index + 1] + v;
df [coef_index + 3] = sf [coef_index + 1] - v;

v = (sf [coef_index + 5] + sf [coef_index + 7]) * sqrt2_2;
df [coef_index + 5] = v + sf [coef_index + 3];
df [coef_index + 7] = v - sf [coef_index + 3];

coef_index += 8;
}
while (coef_index < _length);
}



template <class DT>
void FFTReal <DT>::compute_direct_pass_n (DataType df [], const DataType sf [], int pass) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);
assert (pass >= 3);
assert (pass < _nbr_bits);

if (pass <= TRIGO_BD_LIMIT)
{
compute_direct_pass_n_lut (df, sf, pass);
}
else
{
compute_direct_pass_n_osc (df, sf, pass);
}
}



template <class DT>
void FFTReal <DT>::compute_direct_pass_n_lut (DataType df [], const DataType sf [], int pass) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);
assert (pass >= 3);
assert (pass < _nbr_bits);

const long nbr_coef = 1 << pass;
const long h_nbr_coef = nbr_coef >> 1;
const long d_nbr_coef = nbr_coef << 1;
long coef_index = 0;
const DataType * const cos_ptr = get_trigo_ptr (pass);
do
{
const DataType * const sf1r = sf + coef_index;
const DataType * const sf2r = sf1r + nbr_coef;
DataType * const dfr = df + coef_index;
DataType * const dfi = dfr + nbr_coef;

// Extreme coefficients are always real
dfr [0] = sf1r [0] + sf2r [0];
dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] =
dfr [h_nbr_coef] = sf1r [h_nbr_coef];
dfi [h_nbr_coef] = sf2r [h_nbr_coef];

// Others are conjugate complex numbers
const DataType * const sf1i = sf1r + h_nbr_coef;
const DataType * const sf2i = sf1i + nbr_coef;
for (long i = 1; i < h_nbr_coef; ++ i)
{
const DataType c = cos_ptr [i]; // cos (i*PI/nbr_coef);
const DataType s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef);
DataType v;

v = sf2r [i] * c - sf2i [i] * s;
dfr [i] = sf1r [i] + v;
dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] =

v = sf2r [i] * s + sf2i [i] * c;
dfi [i] = v + sf1i [i];
dfi [nbr_coef - i] = v - sf1i [i];
}

coef_index += d_nbr_coef;
}
while (coef_index < _length);
}



template <class DT>
void FFTReal <DT>::compute_direct_pass_n_osc (DataType df [], const DataType sf [], int pass) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);
assert (pass > TRIGO_BD_LIMIT);
assert (pass < _nbr_bits);

const long nbr_coef = 1 << pass;
const long h_nbr_coef = nbr_coef >> 1;
const long d_nbr_coef = nbr_coef << 1;
long coef_index = 0;
OscType & osc = _trigo_osc [pass - (TRIGO_BD_LIMIT + 1)];
do
{
const DataType * const sf1r = sf + coef_index;
const DataType * const sf2r = sf1r + nbr_coef;
DataType * const dfr = df + coef_index;
DataType * const dfi = dfr + nbr_coef;

osc.clear_buffers ();

// Extreme coefficients are always real
dfr [0] = sf1r [0] + sf2r [0];
dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] =
dfr [h_nbr_coef] = sf1r [h_nbr_coef];
dfi [h_nbr_coef] = sf2r [h_nbr_coef];

// Others are conjugate complex numbers
const DataType * const sf1i = sf1r + h_nbr_coef;
const DataType * const sf2i = sf1i + nbr_coef;
for (long i = 1; i < h_nbr_coef; ++ i)
{
osc.step ();
const DataType c = osc.get_cos ();
const DataType s = osc.get_sin ();
DataType v;

v = sf2r [i] * c - sf2i [i] * s;
dfr [i] = sf1r [i] + v;
dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] =

v = sf2r [i] * s + sf2i [i] * c;
dfi [i] = v + sf1i [i];
dfi [nbr_coef - i] = v - sf1i [i];
}

coef_index += d_nbr_coef;
}
while (coef_index < _length);
}



// Transform in several pass
template <class DT>
void FFTReal <DT>::compute_ifft_general (const DataType f [], DataType x []) const
{
assert (f != 0);
assert (f != use_buffer ());
assert (x != 0);
assert (x != use_buffer ());
assert (x != f);

DataType * sf = const_cast <DataType *> (f);
DataType * df;
DataType * df_temp;

if (_nbr_bits & 1)
{
df = use_buffer ();
df_temp = x;
}
else
{
df = x;
df_temp = use_buffer ();
}

for (int pass = _nbr_bits - 1; pass >= 3; -- pass)
{
compute_inverse_pass_n (df, sf, pass);

if (pass < _nbr_bits - 1)
{
DataType * const temp_ptr = df;
df = sf;
sf = temp_ptr;
}
else
{
sf = df;
df = df_temp;
}
}

compute_inverse_pass_3 (df, sf);
compute_inverse_pass_1_2 (x, df);
}



template <class DT>
void FFTReal <DT>::compute_inverse_pass_n (DataType df [], const DataType sf [], int pass) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);
assert (pass >= 3);
assert (pass < _nbr_bits);

if (pass <= TRIGO_BD_LIMIT)
{
compute_inverse_pass_n_lut (df, sf, pass);
}
else
{
compute_inverse_pass_n_osc (df, sf, pass);
}
}



template <class DT>
void FFTReal <DT>::compute_inverse_pass_n_lut (DataType df [], const DataType sf [], int pass) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);
assert (pass >= 3);
assert (pass < _nbr_bits);

const long nbr_coef = 1 << pass;
const long h_nbr_coef = nbr_coef >> 1;
const long d_nbr_coef = nbr_coef << 1;
long coef_index = 0;
const DataType * const cos_ptr = get_trigo_ptr (pass);
do
{
const DataType * const sfr = sf + coef_index;
const DataType * const sfi = sfr + nbr_coef;
DataType * const df1r = df + coef_index;
DataType * const df2r = df1r + nbr_coef;

// Extreme coefficients are always real
df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef]
df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef]
df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2;
df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2;

// Others are conjugate complex numbers
DataType * const df1i = df1r + h_nbr_coef;
DataType * const df2i = df1i + nbr_coef;
for (long i = 1; i < h_nbr_coef; ++ i)
{
df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i]
df1i [i] = sfi [i] - sfi [nbr_coef - i];

const DataType c = cos_ptr [i]; // cos (i*PI/nbr_coef);
const DataType s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef);
const DataType vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i]
const DataType vi = sfi [i] + sfi [nbr_coef - i];

df2r [i] = vr * c + vi * s;
df2i [i] = vi * c - vr * s;
}

coef_index += d_nbr_coef;
}
while (coef_index < _length);
}



template <class DT>
void FFTReal <DT>::compute_inverse_pass_n_osc (DataType df [], const DataType sf [], int pass) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);
assert (pass > TRIGO_BD_LIMIT);
assert (pass < _nbr_bits);

const long nbr_coef = 1 << pass;
const long h_nbr_coef = nbr_coef >> 1;
const long d_nbr_coef = nbr_coef << 1;
long coef_index = 0;
OscType & osc = _trigo_osc [pass - (TRIGO_BD_LIMIT + 1)];
do
{
const DataType * const sfr = sf + coef_index;
const DataType * const sfi = sfr + nbr_coef;
DataType * const df1r = df + coef_index;
DataType * const df2r = df1r + nbr_coef;

osc.clear_buffers ();

// Extreme coefficients are always real
df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef]
df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef]
df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2;
df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2;

// Others are conjugate complex numbers
DataType * const df1i = df1r + h_nbr_coef;
DataType * const df2i = df1i + nbr_coef;
for (long i = 1; i < h_nbr_coef; ++ i)
{
df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i]
df1i [i] = sfi [i] - sfi [nbr_coef - i];

osc.step ();
const DataType c = osc.get_cos ();
const DataType s = osc.get_sin ();
const DataType vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i]
const DataType vi = sfi [i] + sfi [nbr_coef - i];

df2r [i] = vr * c + vi * s;
df2i [i] = vi * c - vr * s;
}

coef_index += d_nbr_coef;
}
while (coef_index < _length);
}



template <class DT>
void FFTReal <DT>::compute_inverse_pass_3 (DataType df [], const DataType sf []) const
{
assert (df != 0);
assert (sf != 0);
assert (df != sf);

const DataType sqrt2_2 = DataType (SQRT2 * 0.5);
long coef_index = 0;
do
{
df [coef_index] = sf [coef_index] + sf [coef_index + 4];
df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4];
df [coef_index + 2] = sf [coef_index + 2] * 2;
df [coef_index + 6] = sf [coef_index + 6] * 2;

df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3];
df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7];

const DataType vr = sf [coef_index + 1] - sf [coef_index + 3];
const DataType vi = sf [coef_index + 5] + sf [coef_index + 7];

df [coef_index + 5] = (vr + vi) * sqrt2_2;
df [coef_index + 7] = (vi - vr) * sqrt2_2;

coef_index += 8;
}
while (coef_index < _length);
}



template <class DT>
void FFTReal <DT>::compute_inverse_pass_1_2 (DataType x [], const DataType sf []) const
{
assert (x != 0);
assert (sf != 0);
assert (x != sf);

const long * bit_rev_lut_ptr = get_br_ptr ();
const DataType * sf2 = sf;
long coef_index = 0;
do
{
{
const DataType b_0 = sf2 [0] + sf2 [2];
const DataType b_2 = sf2 [0] - sf2 [2];
const DataType b_1 = sf2 [1] * 2;
const DataType b_3 = sf2 [3] * 2;

x [bit_rev_lut_ptr [0]] = b_0 + b_1;
x [bit_rev_lut_ptr [1]] = b_0 - b_1;
x [bit_rev_lut_ptr [2]] = b_2 + b_3;
x [bit_rev_lut_ptr [3]] = b_2 - b_3;
}
{
const DataType b_0 = sf2 [4] + sf2 [6];
const DataType b_2 = sf2 [4] - sf2 [6];
const DataType b_1 = sf2 [5] * 2;
const DataType b_3 = sf2 [7] * 2;

x [bit_rev_lut_ptr [4]] = b_0 + b_1;
x [bit_rev_lut_ptr [5]] = b_0 - b_1;
x [bit_rev_lut_ptr [6]] = b_2 + b_3;
x [bit_rev_lut_ptr [7]] = b_2 - b_3;
}

sf2 += 8;
coef_index += 8;
bit_rev_lut_ptr += 8;
}
while (coef_index < _length);
}



} // namespace ffft



#endif // ffft_FFTReal_CODEHEADER_INCLUDED

#undef ffft_FFTReal_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 131
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.h View File

@@ -0,0 +1,131 @@
/*****************************************************************************

FFTRealFixLen.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_FFTRealFixLen_HEADER_INCLUDED)
#define ffft_FFTRealFixLen_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "Array.h"
#include "DynArray.h"
#include "FFTRealFixLenParam.h"
#include "OscSinCos.h"



namespace ffft
{



template <int LL2>
class FFTRealFixLen
{
typedef int CompileTimeCheck1 [(LL2 >= 0) ? 1 : -1];
typedef int CompileTimeCheck2 [(LL2 <= 30) ? 1 : -1];

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

typedef FFTRealFixLenParam::DataType DataType;
typedef OscSinCos <DataType> OscType;

enum { FFT_LEN_L2 = LL2 };
enum { FFT_LEN = 1 << FFT_LEN_L2 };

FFTRealFixLen ();

inline long get_length () const;
void do_fft (DataType f [], const DataType x []);
void do_ifft (const DataType f [], DataType x []);
void rescale (DataType x []) const;



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

enum { TRIGO_BD_LIMIT = FFTRealFixLenParam::TRIGO_BD_LIMIT };

enum { BR_ARR_SIZE_L2 = ((FFT_LEN_L2 - 3) < 0) ? 0 : (FFT_LEN_L2 - 2) };
enum { BR_ARR_SIZE = 1 << BR_ARR_SIZE_L2 };

enum { TRIGO_BD = ((FFT_LEN_L2 - TRIGO_BD_LIMIT) < 0)
? (int)FFT_LEN_L2
: (int)TRIGO_BD_LIMIT };
enum { TRIGO_TABLE_ARR_SIZE_L2 = (LL2 < 4) ? 0 : (TRIGO_BD - 2) };
enum { TRIGO_TABLE_ARR_SIZE = 1 << TRIGO_TABLE_ARR_SIZE_L2 };

enum { NBR_TRIGO_OSC = FFT_LEN_L2 - TRIGO_BD };
enum { TRIGO_OSC_ARR_SIZE = (NBR_TRIGO_OSC > 0) ? NBR_TRIGO_OSC : 1 };

void build_br_lut ();
void build_trigo_lut ();
void build_trigo_osc ();

DynArray <DataType>
_buffer;
DynArray <long>
_br_data;
DynArray <DataType>
_trigo_data;
Array <OscType, TRIGO_OSC_ARR_SIZE>
_trigo_osc;



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

FFTRealFixLen (const FFTRealFixLen &other);
FFTRealFixLen& operator = (const FFTRealFixLen &other);
bool operator == (const FFTRealFixLen &other);
bool operator != (const FFTRealFixLen &other);

}; // class FFTRealFixLen



} // namespace ffft



#include "FFTRealFixLen.hpp"



#endif // ffft_FFTRealFixLen_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 323
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealFixLen.hpp View File

@@ -0,0 +1,323 @@
/*****************************************************************************

FFTRealFixLen.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_FFTRealFixLen_CURRENT_CODEHEADER)
#error Recursive inclusion of FFTRealFixLen code header.
#endif
#define ffft_FFTRealFixLen_CURRENT_CODEHEADER

#if ! defined (ffft_FFTRealFixLen_CODEHEADER_INCLUDED)
#define ffft_FFTRealFixLen_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "def.h"
#include "FFTRealPassDirect.h"
#include "FFTRealPassInverse.h"
#include "FFTRealSelect.h"

#include <cassert>
#include <cmath>

namespace std { }



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <int LL2>
FFTRealFixLen <LL2>::FFTRealFixLen ()
: _buffer (FFT_LEN)
, _br_data (BR_ARR_SIZE)
, _trigo_data (TRIGO_TABLE_ARR_SIZE)
, _trigo_osc ()
{
build_br_lut ();
build_trigo_lut ();
build_trigo_osc ();
}



template <int LL2>
long FFTRealFixLen <LL2>::get_length () const
{
return (FFT_LEN);
}



// General case
template <int LL2>
void FFTRealFixLen <LL2>::do_fft (DataType f [], const DataType x [])
{
assert (f != 0);
assert (x != 0);
assert (x != f);
assert (FFT_LEN_L2 >= 3);

// Do the transform in several passes
const DataType * cos_ptr = &_trigo_data [0];
const long * br_ptr = &_br_data [0];

FFTRealPassDirect <FFT_LEN_L2 - 1>::process (
FFT_LEN,
f,
&_buffer [0],
x,
cos_ptr,
TRIGO_TABLE_ARR_SIZE,
br_ptr,
&_trigo_osc [0]
);
}

// 4-point FFT
template <>
inline void FFTRealFixLen <2>::do_fft (DataType f [], const DataType x [])
{
assert (f != 0);
assert (x != 0);
assert (x != f);

f [1] = x [0] - x [2];
f [3] = x [1] - x [3];

const DataType b_0 = x [0] + x [2];
const DataType b_2 = x [1] + x [3];
f [0] = b_0 + b_2;
f [2] = b_0 - b_2;
}

// 2-point FFT
template <>
inline void FFTRealFixLen <1>::do_fft (DataType f [], const DataType x [])
{
assert (f != 0);
assert (x != 0);
assert (x != f);

f [0] = x [0] + x [1];
f [1] = x [0] - x [1];
}

// 1-point FFT
template <>
inline void FFTRealFixLen <0>::do_fft (DataType f [], const DataType x [])
{
assert (f != 0);
assert (x != 0);

f [0] = x [0];
}



// General case
template <int LL2>
void FFTRealFixLen <LL2>::do_ifft (const DataType f [], DataType x [])
{
assert (f != 0);
assert (x != 0);
assert (x != f);
assert (FFT_LEN_L2 >= 3);

// Do the transform in several passes
DataType * s_ptr =
FFTRealSelect <FFT_LEN_L2 & 1>::sel_bin (&_buffer [0], x);
DataType * d_ptr =
FFTRealSelect <FFT_LEN_L2 & 1>::sel_bin (x, &_buffer [0]);
const DataType * cos_ptr = &_trigo_data [0];
const long * br_ptr = &_br_data [0];

FFTRealPassInverse <FFT_LEN_L2 - 1>::process (
FFT_LEN,
d_ptr,
s_ptr,
f,
cos_ptr,
TRIGO_TABLE_ARR_SIZE,
br_ptr,
&_trigo_osc [0]
);
}

// 4-point IFFT
template <>
inline void FFTRealFixLen <2>::do_ifft (const DataType f [], DataType x [])
{
assert (f != 0);
assert (x != 0);
assert (x != f);

const DataType b_0 = f [0] + f [2];
const DataType b_2 = f [0] - f [2];

x [0] = b_0 + f [1] * 2;
x [2] = b_0 - f [1] * 2;
x [1] = b_2 + f [3] * 2;
x [3] = b_2 - f [3] * 2;
}

// 2-point IFFT
template <>
inline void FFTRealFixLen <1>::do_ifft (const DataType f [], DataType x [])
{
assert (f != 0);
assert (x != 0);
assert (x != f);

x [0] = f [0] + f [1];
x [1] = f [0] - f [1];
}

// 1-point IFFT
template <>
inline void FFTRealFixLen <0>::do_ifft (const DataType f [], DataType x [])
{
assert (f != 0);
assert (x != 0);
assert (x != f);

x [0] = f [0];
}




template <int LL2>
void FFTRealFixLen <LL2>::rescale (DataType x []) const
{
assert (x != 0);

const DataType mul = DataType (1.0 / FFT_LEN);

if (FFT_LEN < 4)
{
long i = FFT_LEN - 1;
do
{
x [i] *= mul;
--i;
}
while (i >= 0);
}

else
{
assert ((FFT_LEN & 3) == 0);

// Could be optimized with SIMD instruction sets (needs alignment check)
long i = FFT_LEN - 4;
do
{
x [i + 0] *= mul;
x [i + 1] *= mul;
x [i + 2] *= mul;
x [i + 3] *= mul;
i -= 4;
}
while (i >= 0);
}
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <int LL2>
void FFTRealFixLen <LL2>::build_br_lut ()
{
_br_data [0] = 0;
for (long cnt = 1; cnt < BR_ARR_SIZE; ++cnt)
{
long index = cnt << 2;
long br_index = 0;

int bit_cnt = FFT_LEN_L2;
do
{
br_index <<= 1;
br_index += (index & 1);
index >>= 1;

-- bit_cnt;
}
while (bit_cnt > 0);

_br_data [cnt] = br_index;
}
}



template <int LL2>
void FFTRealFixLen <LL2>::build_trigo_lut ()
{
const double mul = (0.5 * PI) / TRIGO_TABLE_ARR_SIZE;
for (long i = 0; i < TRIGO_TABLE_ARR_SIZE; ++ i)
{
using namespace std;

_trigo_data [i] = DataType (cos (i * mul));
}
}



template <int LL2>
void FFTRealFixLen <LL2>::build_trigo_osc ()
{
for (int i = 0; i < NBR_TRIGO_OSC; ++i)
{
OscType & osc = _trigo_osc [i];

const long len = static_cast <long> (TRIGO_TABLE_ARR_SIZE) << (i + 1);
const double mul = (0.5 * PI) / len;
osc.set_step (mul);
}
}



} // namespace ffft



#endif // ffft_FFTRealFixLen_CODEHEADER_INCLUDED

#undef ffft_FFTRealFixLen_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 90
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealFixLenParam.h View File

@@ -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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 96
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.h View File

@@ -0,0 +1,96 @@
/*****************************************************************************

FFTRealPassDirect.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_FFTRealPassDirect_HEADER_INCLUDED)
#define ffft_FFTRealPassDirect_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "def.h"
#include "FFTRealFixLenParam.h"
#include "OscSinCos.h"



namespace ffft
{



template <int PASS>
class FFTRealPassDirect
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

typedef FFTRealFixLenParam::DataType DataType;
typedef OscSinCos <DataType> OscType;

ffft_FORCEINLINE static void
process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []);



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

FFTRealPassDirect ();
FFTRealPassDirect (const FFTRealPassDirect &other);
FFTRealPassDirect &
operator = (const FFTRealPassDirect &other);
bool operator == (const FFTRealPassDirect &other);
bool operator != (const FFTRealPassDirect &other);

}; // class FFTRealPassDirect



} // namespace ffft



#include "FFTRealPassDirect.hpp"



#endif // ffft_FFTRealPassDirect_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 205
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealPassDirect.hpp View File

@@ -0,0 +1,205 @@
/*****************************************************************************

FFTRealPassDirect.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_FFTRealPassDirect_CURRENT_CODEHEADER)
#error Recursive inclusion of FFTRealPassDirect code header.
#endif
#define ffft_FFTRealPassDirect_CURRENT_CODEHEADER

#if ! defined (ffft_FFTRealPassDirect_CODEHEADER_INCLUDED)
#define ffft_FFTRealPassDirect_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "FFTRealUseTrigo.h"



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <>
inline void FFTRealPassDirect <1>::process (long len, DataType dest_ptr [], DataType [], const DataType x_ptr [], const DataType [], long , const long br_ptr [], OscType [])
{
// First and second pass at once
const long qlen = len >> 2;

long coef_index = 0;
do
{
// To do: unroll the loop (2x).
const long ri_0 = br_ptr [coef_index >> 2];
const long ri_1 = ri_0 + 2 * qlen; // bit_rev_lut_ptr [coef_index + 1];
const long ri_2 = ri_0 + 1 * qlen; // bit_rev_lut_ptr [coef_index + 2];
const long ri_3 = ri_0 + 3 * qlen; // bit_rev_lut_ptr [coef_index + 3];

DataType * const df2 = dest_ptr + coef_index;
df2 [1] = x_ptr [ri_0] - x_ptr [ri_1];
df2 [3] = x_ptr [ri_2] - x_ptr [ri_3];

const DataType sf_0 = x_ptr [ri_0] + x_ptr [ri_1];
const DataType sf_2 = x_ptr [ri_2] + x_ptr [ri_3];

df2 [0] = sf_0 + sf_2;
df2 [2] = sf_0 - sf_2;

coef_index += 4;
}
while (coef_index < len);
}

template <>
inline void FFTRealPassDirect <2>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list [])
{
// Executes "previous" passes first. Inverts source and destination buffers
FFTRealPassDirect <1>::process (
len,
src_ptr,
dest_ptr,
x_ptr,
cos_ptr,
cos_len,
br_ptr,
osc_list
);

// Third pass
const DataType sqrt2_2 = DataType (SQRT2 * 0.5);

long coef_index = 0;
do
{
dest_ptr [coef_index ] = src_ptr [coef_index] + src_ptr [coef_index + 4];
dest_ptr [coef_index + 4] = src_ptr [coef_index] - src_ptr [coef_index + 4];
dest_ptr [coef_index + 2] = src_ptr [coef_index + 2];
dest_ptr [coef_index + 6] = src_ptr [coef_index + 6];

DataType v;

v = (src_ptr [coef_index + 5] - src_ptr [coef_index + 7]) * sqrt2_2;
dest_ptr [coef_index + 1] = src_ptr [coef_index + 1] + v;
dest_ptr [coef_index + 3] = src_ptr [coef_index + 1] - v;

v = (src_ptr [coef_index + 5] + src_ptr [coef_index + 7]) * sqrt2_2;
dest_ptr [coef_index + 5] = v + src_ptr [coef_index + 3];
dest_ptr [coef_index + 7] = v - src_ptr [coef_index + 3];

coef_index += 8;
}
while (coef_index < len);
}

template <int PASS>
void FFTRealPassDirect <PASS>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list [])
{
// Executes "previous" passes first. Inverts source and destination buffers
FFTRealPassDirect <PASS - 1>::process (
len,
src_ptr,
dest_ptr,
x_ptr,
cos_ptr,
cos_len,
br_ptr,
osc_list
);

const long dist = 1L << (PASS - 1);
const long c1_r = 0;
const long c1_i = dist;
const long c2_r = dist * 2;
const long c2_i = dist * 3;
const long cend = dist * 4;
const long table_step = cos_len >> (PASS - 1);

enum { TRIGO_OSC = PASS - FFTRealFixLenParam::TRIGO_BD_LIMIT };
enum { TRIGO_DIRECT = (TRIGO_OSC >= 0) ? 1 : 0 };

long coef_index = 0;
do
{
const DataType * const sf = src_ptr + coef_index;
DataType * const df = dest_ptr + coef_index;

// Extreme coefficients are always real
df [c1_r] = sf [c1_r] + sf [c2_r];
df [c2_r] = sf [c1_r] - sf [c2_r];
df [c1_i] = sf [c1_i];
df [c2_i] = sf [c2_i];

FFTRealUseTrigo <TRIGO_DIRECT>::prepare (osc_list [TRIGO_OSC]);

// Others are conjugate complex numbers
for (long i = 1; i < dist; ++ i)
{
DataType c;
DataType s;
FFTRealUseTrigo <TRIGO_DIRECT>::iterate (
osc_list [TRIGO_OSC],
c,
s,
cos_ptr,
i * table_step,
(dist - i) * table_step
);

const DataType sf_r_i = sf [c1_r + i];
const DataType sf_i_i = sf [c1_i + i];

const DataType v1 = sf [c2_r + i] * c - sf [c2_i + i] * s;
df [c1_r + i] = sf_r_i + v1;
df [c2_r - i] = sf_r_i - v1;

const DataType v2 = sf [c2_r + i] * s + sf [c2_i + i] * c;
df [c2_r + i] = v2 + sf_i_i;
df [cend - i] = v2 - sf_i_i;
}

coef_index += cend;
}
while (coef_index < len);
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



} // namespace ffft



#endif // ffft_FFTRealPassDirect_CODEHEADER_INCLUDED

#undef ffft_FFTRealPassDirect_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 101
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.h View File

@@ -0,0 +1,101 @@
/*****************************************************************************

FFTRealPassInverse.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_FFTRealPassInverse_HEADER_INCLUDED)
#define ffft_FFTRealPassInverse_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "def.h"
#include "FFTRealFixLenParam.h"
#include "OscSinCos.h"




namespace ffft
{



template <int PASS>
class FFTRealPassInverse
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

typedef FFTRealFixLenParam::DataType DataType;
typedef OscSinCos <DataType> OscType;

ffft_FORCEINLINE static void
process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType f_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []);
ffft_FORCEINLINE static void
process_rec (long len, DataType dest_ptr [], DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []);
ffft_FORCEINLINE static void
process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []);



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

FFTRealPassInverse ();
FFTRealPassInverse (const FFTRealPassInverse &other);
FFTRealPassInverse &
operator = (const FFTRealPassInverse &other);
bool operator == (const FFTRealPassInverse &other);
bool operator != (const FFTRealPassInverse &other);

}; // class FFTRealPassInverse



} // namespace ffft



#include "FFTRealPassInverse.hpp"



#endif // ffft_FFTRealPassInverse_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 230
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealPassInverse.hpp View File

@@ -0,0 +1,230 @@
/*****************************************************************************

FFTRealPassInverse.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_FFTRealPassInverse_CURRENT_CODEHEADER)
#error Recursive inclusion of FFTRealPassInverse code header.
#endif
#define ffft_FFTRealPassInverse_CURRENT_CODEHEADER

#if ! defined (ffft_FFTRealPassInverse_CODEHEADER_INCLUDED)
#define ffft_FFTRealPassInverse_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "FFTRealUseTrigo.h"



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <int PASS>
void FFTRealPassInverse <PASS>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType f_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list [])
{
process_internal (
len,
dest_ptr,
f_ptr,
cos_ptr,
cos_len,
br_ptr,
osc_list
);
FFTRealPassInverse <PASS - 1>::process_rec (
len,
src_ptr,
dest_ptr,
cos_ptr,
cos_len,
br_ptr,
osc_list
);
}



template <int PASS>
void FFTRealPassInverse <PASS>::process_rec (long len, DataType dest_ptr [], DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list [])
{
process_internal (
len,
dest_ptr,
src_ptr,
cos_ptr,
cos_len,
br_ptr,
osc_list
);
FFTRealPassInverse <PASS - 1>::process_rec (
len,
src_ptr,
dest_ptr,
cos_ptr,
cos_len,
br_ptr,
osc_list
);
}

template <>
inline void FFTRealPassInverse <0>::process_rec (long , DataType [], DataType [], const DataType [], long , const long [], OscType [])
{
// Stops recursion
}



template <int PASS>
void FFTRealPassInverse <PASS>::process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list [])
{
const long dist = 1L << (PASS - 1);
const long c1_r = 0;
const long c1_i = dist;
const long c2_r = dist * 2;
const long c2_i = dist * 3;
const long cend = dist * 4;
const long table_step = cos_len >> (PASS - 1);

enum { TRIGO_OSC = PASS - FFTRealFixLenParam::TRIGO_BD_LIMIT };
enum { TRIGO_DIRECT = (TRIGO_OSC >= 0) ? 1 : 0 };

long coef_index = 0;
do
{
const DataType * const sf = src_ptr + coef_index;
DataType * const df = dest_ptr + coef_index;

// Extreme coefficients are always real
df [c1_r] = sf [c1_r] + sf [c2_r];
df [c2_r] = sf [c1_r] - sf [c2_r];
df [c1_i] = sf [c1_i] * 2;
df [c2_i] = sf [c2_i] * 2;

FFTRealUseTrigo <TRIGO_DIRECT>::prepare (osc_list [TRIGO_OSC]);

// Others are conjugate complex numbers
for (long i = 1; i < dist; ++ i)
{
df [c1_r + i] = sf [c1_r + i] + sf [c2_r - i];
df [c1_i + i] = sf [c2_r + i] - sf [cend - i];

DataType c;
DataType s;
FFTRealUseTrigo <TRIGO_DIRECT>::iterate (
osc_list [TRIGO_OSC],
c,
s,
cos_ptr,
i * table_step,
(dist - i) * table_step
);

const DataType vr = sf [c1_r + i] - sf [c2_r - i];
const DataType vi = sf [c2_r + i] + sf [cend - i];

df [c2_r + i] = vr * c + vi * s;
df [c2_i + i] = vi * c - vr * s;
}

coef_index += cend;
}
while (coef_index < len);
}

template <>
inline void FFTRealPassInverse <2>::process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType [], long , const long [], OscType [])
{
// Antepenultimate pass
const DataType sqrt2_2 = DataType (SQRT2 * 0.5);

long coef_index = 0;
do
{
dest_ptr [coef_index ] = src_ptr [coef_index] + src_ptr [coef_index + 4];
dest_ptr [coef_index + 4] = src_ptr [coef_index] - src_ptr [coef_index + 4];
dest_ptr [coef_index + 2] = src_ptr [coef_index + 2] * 2;
dest_ptr [coef_index + 6] = src_ptr [coef_index + 6] * 2;

dest_ptr [coef_index + 1] = src_ptr [coef_index + 1] + src_ptr [coef_index + 3];
dest_ptr [coef_index + 3] = src_ptr [coef_index + 5] - src_ptr [coef_index + 7];

const DataType vr = src_ptr [coef_index + 1] - src_ptr [coef_index + 3];
const DataType vi = src_ptr [coef_index + 5] + src_ptr [coef_index + 7];

dest_ptr [coef_index + 5] = (vr + vi) * sqrt2_2;
dest_ptr [coef_index + 7] = (vi - vr) * sqrt2_2;

coef_index += 8;
}
while (coef_index < len);
}

template <>
inline void FFTRealPassInverse <1>::process_internal (long len, DataType dest_ptr [], const DataType src_ptr [], const DataType [], long , const long br_ptr [], OscType [])
{
// Penultimate and last pass at once
const long qlen = len >> 2;

long coef_index = 0;
do
{
const long ri_0 = br_ptr [coef_index >> 2];

const DataType b_0 = src_ptr [coef_index ] + src_ptr [coef_index + 2];
const DataType b_2 = src_ptr [coef_index ] - src_ptr [coef_index + 2];
const DataType b_1 = src_ptr [coef_index + 1] * 2;
const DataType b_3 = src_ptr [coef_index + 3] * 2;

dest_ptr [ri_0 ] = b_0 + b_1;
dest_ptr [ri_0 + 2 * qlen] = b_0 - b_1;
dest_ptr [ri_0 + 1 * qlen] = b_2 + b_3;
dest_ptr [ri_0 + 3 * qlen] = b_2 - b_3;

coef_index += 4;
}
while (coef_index < len);
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



} // namespace ffft



#endif // ffft_FFTRealPassInverse_CODEHEADER_INCLUDED

#undef ffft_FFTRealPassInverse_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 78
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.h View File

@@ -0,0 +1,78 @@
/*****************************************************************************

FFTRealSelect.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_FFTRealSelect_HEADER_INCLUDED)
#define ffft_FFTRealSelect_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "def.h"



namespace ffft
{



template <int P>
class FFTRealSelect
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

ffft_FORCEINLINE static float *
sel_bin (float *e_ptr, float *o_ptr);



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

FFTRealSelect ();
~FFTRealSelect ();
FFTRealSelect (const FFTRealSelect &other);
FFTRealSelect& operator = (const FFTRealSelect &other);
bool operator == (const FFTRealSelect &other);
bool operator != (const FFTRealSelect &other);

}; // class FFTRealSelect



} // namespace ffft



#include "FFTRealSelect.hpp"



#endif // ffft_FFTRealSelect_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 63
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealSelect.hpp View File

@@ -0,0 +1,63 @@
/*****************************************************************************

FFTRealSelect.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_FFTRealSelect_CURRENT_CODEHEADER)
#error Recursive inclusion of FFTRealSelect code header.
#endif
#define ffft_FFTRealSelect_CURRENT_CODEHEADER

#if ! defined (ffft_FFTRealSelect_CODEHEADER_INCLUDED)
#define ffft_FFTRealSelect_CODEHEADER_INCLUDED



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <int P>
float * FFTRealSelect <P>::sel_bin (float *e_ptr, float *o_ptr)
{
return (o_ptr);
}



template <>
inline float * FFTRealSelect <0>::sel_bin (float *e_ptr, float *)
{
return (e_ptr);
}



} // namespace ffft



#endif // ffft_FFTRealSelect_CODEHEADER_INCLUDED

#undef ffft_FFTRealSelect_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 99
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.h View File

@@ -0,0 +1,99 @@
/*****************************************************************************

FFTRealUseTrigo.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_FFTRealUseTrigo_HEADER_INCLUDED)
#define ffft_FFTRealUseTrigo_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "def.h"
#include "FFTRealFixLenParam.h"
#include "OscSinCos.h"



namespace ffft
{



template <int ALGO>
class FFTRealUseTrigo
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

typedef FFTRealFixLenParam::DataType DataType;
typedef OscSinCos <DataType> OscType;

ffft_FORCEINLINE static void
prepare (OscType &osc);
ffft_FORCEINLINE static void
iterate (OscType &osc, DataType &c, DataType &s, const DataType cos_ptr [], long index_c, long index_s);



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

FFTRealUseTrigo ();
~FFTRealUseTrigo ();
FFTRealUseTrigo (const FFTRealUseTrigo &other);
FFTRealUseTrigo &
operator = (const FFTRealUseTrigo &other);
bool operator == (const FFTRealUseTrigo &other);
bool operator != (const FFTRealUseTrigo &other);

}; // class FFTRealUseTrigo



} // namespace ffft



#include "FFTRealUseTrigo.hpp"



#endif // ffft_FFTRealUseTrigo_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 92
- 0
plugins/Cardinal/src/sassy/fftreal/FFTRealUseTrigo.hpp View File

@@ -0,0 +1,92 @@
/*****************************************************************************

FFTRealUseTrigo.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_FFTRealUseTrigo_CURRENT_CODEHEADER)
#error Recursive inclusion of FFTRealUseTrigo code header.
#endif
#define ffft_FFTRealUseTrigo_CURRENT_CODEHEADER

#if ! defined (ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED)
#define ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "OscSinCos.h"



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <int ALGO>
void FFTRealUseTrigo <ALGO>::prepare (OscType &osc)
{
osc.clear_buffers ();
}

template <>
inline void FFTRealUseTrigo <0>::prepare (OscType &)
{
// Nothing
}



template <int ALGO>
void FFTRealUseTrigo <ALGO>::iterate (OscType &osc, DataType &c, DataType &s, const DataType cos_ptr [], long index_c, long index_s)
{
osc.step ();
c = osc.get_cos ();
s = osc.get_sin ();
}

template <>
inline void FFTRealUseTrigo <0>::iterate (OscType &, DataType &c, DataType &s, const DataType cos_ptr [], long index_c, long index_s)
{
c = cos_ptr [index_c];
s = cos_ptr [index_s];
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



} // namespace ffft



#endif // ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED

#undef ffft_FFTRealUseTrigo_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 107
- 0
plugins/Cardinal/src/sassy/fftreal/OscSinCos.h View File

@@ -0,0 +1,107 @@
/*****************************************************************************

OscSinCos.h
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if ! defined (ffft_OscSinCos_HEADER_INCLUDED)
#define ffft_OscSinCos_HEADER_INCLUDED

#if defined (_MSC_VER)
#pragma once
#pragma warning (4 : 4250) // "Inherits via dominance."
#endif



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "def.h"



namespace ffft
{



template <class T>
class OscSinCos
{

/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

public:

typedef T DataType;

OscSinCos ();

ffft_FORCEINLINE void
set_step (double angle_rad);

ffft_FORCEINLINE DataType
get_cos () const;
ffft_FORCEINLINE DataType
get_sin () const;
ffft_FORCEINLINE void
step ();
ffft_FORCEINLINE void
clear_buffers ();



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

protected:



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

DataType _pos_cos; // Current phase expressed with sin and cos. [-1 ; 1]
DataType _pos_sin; // -
DataType _step_cos; // Phase increment per step, [-1 ; 1]
DataType _step_sin; // -



/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

private:

OscSinCos (const OscSinCos &other);
OscSinCos & operator = (const OscSinCos &other);
bool operator == (const OscSinCos &other);
bool operator != (const OscSinCos &other);

}; // class OscSinCos



} // namespace ffft



#include "OscSinCos.hpp"



#endif // ffft_OscSinCos_HEADER_INCLUDED



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 123
- 0
plugins/Cardinal/src/sassy/fftreal/OscSinCos.hpp View File

@@ -0,0 +1,123 @@
/*****************************************************************************

OscSinCos.hpp
By Laurent de Soras

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



#if defined (ffft_OscSinCos_CURRENT_CODEHEADER)
#error Recursive inclusion of OscSinCos code header.
#endif
#define ffft_OscSinCos_CURRENT_CODEHEADER

#if ! defined (ffft_OscSinCos_CODEHEADER_INCLUDED)
#define ffft_OscSinCos_CODEHEADER_INCLUDED



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include <cmath>

namespace std { }



namespace ffft
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



template <class T>
OscSinCos <T>::OscSinCos ()
: _pos_cos (1)
, _pos_sin (0)
, _step_cos (1)
, _step_sin (0)
{
// Nothing
}



template <class T>
void OscSinCos <T>::set_step (double angle_rad)
{
using namespace std;

_step_cos = static_cast <DataType> (cos (angle_rad));
_step_sin = static_cast <DataType> (sin (angle_rad));
}



template <class T>
typename OscSinCos <T>::DataType OscSinCos <T>::get_cos () const
{
return (_pos_cos);
}



template <class T>
typename OscSinCos <T>::DataType OscSinCos <T>::get_sin () const
{
return (_pos_sin);
}



template <class T>
void OscSinCos <T>::step ()
{
const DataType old_cos = _pos_cos;
const DataType old_sin = _pos_sin;

_pos_cos = old_cos * _step_cos - old_sin * _step_sin;
_pos_sin = old_cos * _step_sin + old_sin * _step_cos;
}



template <class T>
void OscSinCos <T>::clear_buffers ()
{
_pos_cos = static_cast <DataType> (1);
_pos_sin = static_cast <DataType> (0);
}



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



} // namespace ffft



#endif // ffft_OscSinCos_CODEHEADER_INCLUDED

#undef ffft_OscSinCos_CURRENT_CODEHEADER



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 60
- 0
plugins/Cardinal/src/sassy/fftreal/def.h View File

@@ -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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

+ 14
- 0
plugins/Cardinal/src/sassy/fftreal/license.txt View File

@@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004

Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>

Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. You just DO WHAT THE FUCK YOU WANT TO.


+ 293
- 0
plugins/Cardinal/src/sassy/fftreal/readme.txt View File

@@ -0,0 +1,293 @@
==============================================================================

FFTReal
Version 2.11

Fourier transformation (FFT, IFFT) library specialised for real data
Portable ISO C++

Copyright (c) 1999-2010 Laurent de Soras
Object Pascal port (c) Frederic Vanmol

==============================================================================



Contents:

1. Legal
2. Content
3. Using FFTReal
3.1 FFTReal - Length fixed at run-time
3.2 FFTRealFixLen - Length fixed at compile-time
3.3 Data organisation
4. Compilation and testing
5. History
6. Contact



1. Legal
--------

FFTReal is distributed under the terms of the Do What The Fuck You Want To
Public License.

Check the file license.txt to get full information about the license.



2. Content
----------

FFTReal is a library to compute Discrete Fourier Transforms (DFT) with the
FFT algorithm (Fast Fourier Transform) on arrays of real numbers. It can
also compute the inverse transform.

You should find in this package a lot of files ; some of them are of
particular interest:
- readme.txt : you are reading it
- ffft/FFTReal.h : FFT, length fixed at run-time
- ffft/FFTRealFixLen.h: FFT, length fixed at compile-time
- delphi/FFTReal.pas : Pascal implementation (working but not up-to-date)



3. Using FFTReal
----------------

Important - if you were using older versions of FFTReal (up to 1.03), some
things have changed. FFTReal is now a template. Therefore use FFTReal<float>
or FFTReal<double> in your code depending on the application datatype. The
flt_t typedef has been removed. And if you were previously using FFTReal 2.0,
note that all the classes have moved to the ffft namespace.

You have two ways to use FFTReal. In the first way, the FFT has its length
fixed at run-time, when the object is instanciated. It means that you have
not to know the length when you write the code. This is the usual way of
proceeding.


3.1 FFTReal - Length fixed at run-time
--------------------------------------

Just instanciate one time a FFTReal object. Specify the data type you want
as template parameter (only floating point: float, double, long double or
custom type). The constructor precompute a lot of things, so it may be a bit
long. The parameter is the number of points used for the next FFTs. It must
be a power of 2:

#include "ffft/FFTReal.h"
...
long len = 1024;
...
// 1024-point FFT object constructed.
ffft::FFTReal <float> fft_object (len);

Then you can use this object to compute as many FFTs and IFFTs as you want.
They will be computed very quickly because a lot of work has been done in the
object construction.

float x [1024];
float f [1024];

...
fft_object.do_fft (f, x); // x (real) --FFT---> f (complex)
...
fft_object.do_ifft (f, x); // f (complex) --IFFT--> x (real)
fft_object.rescale (x); // Post-scaling should be done after FFT+IFFT
...

x [] and f [] are floating point number arrays. x [] is the real number
sequence which we want to compute the FFT. f [] is the result, in the
"frequency" domain. f has the same number of elements as x [], but f []
elements are complex numbers. The routine uses some FFT properties to
optimize memory and to reduce calculations: the transformaton of a real
number sequence is a conjugate complex number sequence: F [k] = F [-k]*.


3.2 FFTRealFixLen - Length fixed at compile-time
------------------------------------------------

This class is significantly faster than the previous one, giving a speed
gain between 50 and 100 %. The template parameter is the base-2 logarithm of
the FFT length. The datatype is float; it can be changed by modifying the
DataType typedef in FFTRealFixLenParam.h. As FFTReal class, it supports
only floating-point types or equivalent.

Use is similar as the one of FFTReal. To instanciate the object, just proceed
as indicated below:

#include "ffft/FFTRealFixLen.h"
...
// 1024-point (2^10) FFT object constructed.
ffft::FFTRealFixLen <10> fft_object;

Warning: long FFT objects may take a very long time to compile, depending on
the compiler and its optimisation options. If compilation time is too high,
encapsulate the FFT object in a seprate class whose header doesn't need
to include FFTRealFixLen.h, so you just have to compile the wrapper once
and only link it the other times. For example (quick, dirty and incomplete):

ffft/FFTWrapper.h: | ffft/FFTWrapper.cpp:
|
class FFTWrapper | #include "ffft/FFTRealFixLen.h"
{ | #include "ffft/FFTWrapper.h"
public: |
FFTWrapper (); | FFTWrapper::FFTWrapper ()
~FFTWrapper (); | : _impl_ptr ((void*) new FTRealFixLen <10>)
void do_fft (...); | {
void do_ifft (...); | }
private: |
void *_impl_ptr; | ...
} |


3.3 Data organisation
---------------------

Mathematically speaking, DFT formulas below show what does FFTReal:

do_fft() : f(k) = sum (p = 0, N-1, x(p) * exp (+j*2*pi*k*p/N))
do_ifft(): x(k) = sum (p = 0, N-1, f(p) * exp (-j*2*pi*k*p/N))

Where j is the square root of -1. The formulas differ only by the sign of
the exponential. When the sign is positive, the transform is called positive.
Common formulas for Fourier transform are negative for the direct tranform and
positive for the inverse one.

However in these formulas, f is an array of complex numbers and doesn't
correspound exactly to the f[] array taken as function parameter. The
following table shows how the f[] sequence is mapped onto the usable FFT
coefficients (called bins):

FFTReal output | Positive FFT equiv. | Negative FFT equiv.
---------------+-----------------------+-----------------------
f [0] | Real (bin 0) | Real (bin 0)
f [...] | Real (bin ...) | Real (bin ...)
f [length/2] | Real (bin length/2) | Real (bin length/2)
f [length/2+1] | Imag (bin 1) | -Imag (bin 1)
f [...] | Imag (bin ...) | -Imag (bin ...)
f [length-1] | Imag (bin length/2-1) | -Imag (bin length/2-1)

And FFT bins are distributed in f [] as above:

| | Positive FFT | Negative FFT
Bin | Real part | imaginary part | imaginary part
------------+----------------+-----------------+---------------
0 | f [0] | 0 | 0
1 | f [1] | f [length/2+1] | -f [length/2+1]
... | f [...], | f [...] | -f [...]
length/2-1 | f [length/2-1] | f [length-1] | -f [length-1]
length/2 | f [length/2] | 0 | 0
length/2+1 | f [length/2-1] | -f [length-1] | f [length-1]
... | f [...] | -f [...] | f [...]
length-1 | f [1] | -f [length/2+1] | f [length/2+1]

f [] coefficients have the same layout for FFT and IFFT functions. You may
notice that scaling must be done if you want to retrieve x after FFT and IFFT.
Actually, IFFT (FFT (x)) = x * length(x). This is a not a problem because
most of the applications don't care about absolute values. Thus, the operation
requires less calculation. If you want to use the FFT and IFFT to transform a
signal, you have to apply post- (or pre-) processing yourself. Multiplying
or dividing floating point numbers by a power of 2 doesn't generate extra
computation noise.



4. Compilation and testing
--------------------------

Drop the following files into your project or makefile:

ffft/Array.*
ffft/def.h
ffft/DynArray.*
ffft/FFTReal*.h*
ffft/OscSinCos.*

Other files are for testing purpose only, do not include them if you just need
to use the library; they are not needed to use FFTReal in your own programs.

FFTReal may be compiled in two versions: release and debug. Debug version
has checks that could slow down the code. Define NDEBUG to set the Release
mode. For example, the command line to compile the test bench on GCC would
look like:

Debug mode:
g++ -Wall -I. -o ./fftreal_debug.exe ffft/test/*.cpp ffft/test/stopwatch/*.cpp

Release mode:
g++ -Wall -I. -o ./fftreal_release.exe -DNDEBUG -O3 ffft/test/*.cpp ffft/test/stopwatch/*.cpp

It may be tricky to compile the test bench because the speed tests use the
stopwatch sub-library, which is not that cross-platform. If you encounter
any problem that you cannot easily fix while compiling it, edit the file
ffft/test/conf.h and un-define the speed test macro. Remove the stopwatch
directory from your source file list, too.

If it's not done by default, you should activate the exception handling
of your compiler to get the class memory-leak-safe. Thus, when a memory
allocation fails (in the constructor), an exception is thrown and the entire
object is safely destructed. It reduces the permanent error checking overhead
in the client code. Also, the test bench requires Run-Time Type Information
(RTTI) to be enabled in order to display the names of the tested classes -
sometimes mangled, depending on the compiler.

Please note: the test bench may take an insane time to compile, especially in
Release mode, because a lot of recursive templates are instanciated.



5. History
----------

v2.11 (2010.09.12)
- The LGPL was not well suited to 100% template code, therefore I changed
the license again. Everything is released under the WTFPL.
- Removed warnings in the testcode on MSVC++ 8.0
- Fixed the multiple definition linking error with template specialisations
on GCC 4.

v2.10 (2008.05.28)
- Classes are now in the ffft namespace
- Changed directory structure
- Fixed compilation information in the documentation

v2.00 (2005.10.18)
- Turned FFTReal class into template (data type as parameter)
- Added FFTRealFixLen
- Trigonometric tables are size-limited in order to preserve cache memory;
over a given size, sin/cos functions are computed on the fly.
- Better test bench for accuracy and speed
- Changed license to LGPL

v1.03 (2001.06.15)
- Thanks to Frederic Vanmol for the Pascal port (works with Delphi).
- Documentation improvement

v1.02 (2001.03.25)
- sqrt() is now precomputed when the object FFTReal is constructed, resulting
in speed impovement for small size FFT.

v1.01 (2000)
- Small modifications, I don't remember what.

v1.00 (1999.08.14)
- First version released



6. Contact
----------

Please address any comment, bug report or flame to:

Laurent de Soras
laurent.de.soras@free.fr
http://ldesoras.free.fr

For the Pascal port:
Frederic Vanmol
frederic@fruityloops.com


+ 131
- 0
plugins/Cardinal/src/sassy/sassy.hpp View File

@@ -0,0 +1,131 @@
/*
* Sassy scope exported API
* Copyright (C) 2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/

/**
* This file contains a substantial amount of code from Sassy Audio Spreadsheet
* Copyright (c) 2022 Jari Komppa.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#pragma once

#include "fftreal/FFTReal.h"

// int gFFTAverage = 1;
// int gSamplerate;
// float mUIScale;
// // gScope

struct ScopeData {
int mIndex = 0;
int mSampleRate = 0;
float mScroll = 0;
float mTimeScale = 0.01f;
int mTimeScaleSlider = 0;
int mSyncMode = 0;
int mSyncChannel = 0;
int mMode = 0;
int mDisplay = 0;
int mFFTZoom = 0;
int mPot = 0;
float fft1[65536 * 2];
float fft2[65536 * 2];
float ffta[65536 * 2];
unsigned int colors[4] = {
0xffc0c0c0,
0xffa0a0ff,
0xffffa0a0,
0xff30d0d0
};

struct Channel {
bool mEnabled = true;
float mScale = 1.0f / 5.0f;
int mScaleSlider = 0;
float mOffset = 0;
float* mData = nullptr;

~Channel()
{
delete[] mData;
}

void realloc(const int sampleRate)
{
mData = new float[sampleRate * 10];
memset(mData, 0, sizeof(float) * sampleRate * 10);
}
} mCh[4];

struct {
int average;
ffft::FFTReal<float>* obj16;
ffft::FFTReal<float>* obj32;
ffft::FFTReal<float>* obj64;
ffft::FFTReal<float>* obj128;
ffft::FFTReal<float>* obj256;
ffft::FFTReal<float>* obj512;
ffft::FFTReal<float>* obj1024;
ffft::FFTReal<float>* obj2048;
ffft::FFTReal<float>* obj4096;
ffft::FFTReal<float>* obj8192;
ffft::FFTReal<float>* obj16384;
ffft::FFTReal<float>* obj32768;
ffft::FFTReal<float>* obj65536;
} fft;

void realloc(const int sampleRate)
{
mIndex = 0;
mSampleRate = sampleRate;

for (int i = 0; i < 4; i++)
mCh[i].realloc(sampleRate);
}

inline void probe(float data1, float data2, float data3, float data4)
{
// since probe has several channels, need to deal with index here
if (mMode == 0)
{
mCh[0].mData[mIndex] = data1;
mCh[1].mData[mIndex] = data2;
mCh[2].mData[mIndex] = data3;
mCh[3].mData[mIndex] = data4;
mIndex = (mIndex + 1) % (mSampleRate * 10);
}
}
};

void do_show_scope_window(ScopeData* scope, float uiScale);

+ 872
- 0
plugins/Cardinal/src/sassy/sassy_scope.cpp View File

@@ -0,0 +1,872 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/

/**
* This file is copied and adapted from Sassy Audio Spreadsheet (sassy_scope.cpp)
* Copyright (c) 2022 Jari Komppa.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include "sassy.hpp"

#define POW_2_3_4TH 1.6817928305074290860622509524664297900800685247135690216264521719

static double catmullrom(double t, double p0, double p1, double p2, double p3)
{
return 0.5 * (
(2 * p1) +
(-p0 + p2) * t +
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
(-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t
);
}

static const char* gNotestr[128] =
{
"C0-1","C#-1","D-1","D#-1","E-1","F-1","F#-1","G-1","G#-1","A-1","A#-1","B-1",
"C0","C#0","D0","D#0","E0","F0","F#0","G0","G#0","A0","A#0","B0",
"C1","C#1","D1","D#1","E1","F1","F#1","G1","G#1","A1","A#1","B1",
"C2","C#2","D2","D#2","E2","F2","F#2","G2","G#2","A2","A#2","B2",
"C3","C#3","D3","D#3","E3","F3","F#3","G3","G#3","A3","A#3","B3",
"C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4",
"C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5",
"C6","C#6","D6","D#6","E6","F6","F#6","G6","G#6","A6","A#6","B6",
"C7","C#7","D7","D#7","E7","F7","F#7","G7","G#7","A7","A#7","B7",
"C8","C#8","D8","D#8","E8","F8","F#8","G8","G#8","A8","A#8","B8",
"C9","C#9","D9","D#9","E9","F9","F#9","G9"
};

static const float timescalesteps[5] =
{
0.0001f,
0.001f,
0.01f,
0.1f,
1.0f,
};

static const char* timescaletext[5] =
{
"0.1ms",
"1ms",
"10ms",
"100ms",
"1000ms"
};

static const float scalesteps[9] =
{
1.0f / 32.0f,
1.0f / 16.0f,
1.0f / 10.0f,
1.0f / 8.0f,
1.0f / 5.0f,
1.0f / 4.0f,
1.0f / 2.0f,
1.0f,
2.0f,
};

static const char* scaletexts[9] = {
"x1/32",
"x1/16",
"x1/10",
"x1/8",
"x1/5",
"x1/4",
"x1/2",
"x1",
"x2",
};

static constexpr const int grid_size = 340;
static constexpr const int grid_half_size = grid_size / 2 + 10;
static constexpr const int grid_quarter_size = static_cast<int>(grid_half_size / 2);
static constexpr const int grid_1_8_size = grid_quarter_size / 2;
static constexpr const int grid_3_8_size = grid_quarter_size + grid_quarter_size / 2;

static void scope_grid(const float uiScale)
{
ImVec2 p = ImGui::GetItemRectMin();

// zero
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2) * uiScale), 0xff000000, 3.0f);
// 1.0
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 - grid_quarter_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 - grid_quarter_size) * uiScale), 0xff000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 + grid_quarter_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 + grid_quarter_size) * uiScale), 0xff000000);
// 0.5
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 - grid_1_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 - grid_1_8_size) * uiScale), 0x3f000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 + grid_1_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 + grid_1_8_size) * uiScale), 0x3f000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 - grid_3_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 - grid_3_8_size) * uiScale), 0x3f000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + (grid_size / 2 + grid_3_8_size) * uiScale), ImVec2(p.x + (grid_size) * uiScale, p.y + (grid_size / 2 + grid_3_8_size) * uiScale), 0x3f000000);

// zero
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2) * uiScale, p.y + (grid_size) * uiScale), 0xff000000, 3.0f);
// 1.0
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 - grid_quarter_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 - grid_quarter_size) * uiScale, p.y + (grid_size) * uiScale), 0xff000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 + grid_quarter_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 + grid_quarter_size) * uiScale, p.y + (grid_size) * uiScale), 0xff000000);
// 0.5
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 - grid_1_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 - grid_1_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 + grid_1_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 + grid_1_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 - grid_3_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 - grid_3_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000);
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + (grid_size / 2 + grid_3_8_size) * uiScale, p.y * uiScale), ImVec2(p.x + (grid_size / 2 + grid_3_8_size) * uiScale, p.y + (grid_size) * uiScale), 0x3f000000);
}


static int scope_sync(ScopeData* gScope, int index)
{
const float gSamplerate = gScope->mSampleRate;
int samples = (int)(gSamplerate * gScope->mTimeScale);
int cycle = gSamplerate * 10;
int ofs = samples;

if (gScope->mMode == 0)
{
// calculate sync
if (gScope->mSyncMode == 0)
{
float* graphdata = gScope->mCh[gScope->mSyncChannel].mData;
int over = ofs;
while (over < (cycle - ofs) && graphdata[(index - over + cycle) % cycle] < 0) over++;
int under = over;
while (under < (cycle - ofs) && graphdata[(index - under + cycle) % cycle] > 0) under++;
ofs = under;
}
else
if (gScope->mSyncMode == 1)
{
float* graphdata = gScope->mCh[gScope->mSyncChannel].mData;
int under = ofs;
while (under < (cycle - ofs) && graphdata[(index - under + cycle) % cycle] > 0) under++;
int over = under;
while (over < (cycle - ofs) && graphdata[(index - over + cycle) % cycle] < 0) over++;
ofs = over;
}
// default: ofs = samples
}
else
{
// pause mode, scroll bar is active
ofs = -(int)(gScope->mScroll * gSamplerate);
if (ofs < samples)
ofs = samples;
if (ofs > gSamplerate * 10 - samples)
ofs = gSamplerate * 10 - samples;
}
gScope->mScroll = -((float)ofs / gSamplerate);

return ofs;
}

#if 0
static void scope_plot(ScopeData* gScope, const float uiScale, int index)
{
ImVec2 p = ImGui::GetItemRectMin();
const float gSamplerate = gScope->mSampleRate;
int cycle = gSamplerate * 10;
/*
Okay, max scale is 1 second, so..
*/
int samples = (int)(gSamplerate * gScope->mTimeScale);

scope_grid(uiScale);

int ofs = scope_sync(gScope, index);

if (gScope->mDisplay == 2)
{
for (int i = 0; i < 16384; i++)
{
for (int j = 0; j < 4; j++)
{
if (gScope->mCh[j].mEnabled)
{
float* graphdata = gScope->mCh[j].mData;
float y = graphdata[(index - ofs + i * samples / 16384 + cycle) % cycle];
float x = graphdata[(index - ofs + i * samples / 16384 + cycle - 1) % cycle];
x = x * gScope->mCh[j].mScale;
y = y * gScope->mCh[j].mScale - gScope->mCh[j].mOffset;
ImGui::GetWindowDrawList()->AddCircleFilled(
ImVec2(p.x + (grid_size / 2 + x * grid_quarter_size) * uiScale, p.y + (grid_size / 2 + y * grid_quarter_size) * uiScale),
1,
(gScope->colors[j] & 0xffffff) | 0x3f000000);
}
}
}
}
else
{
for (int i = 0; i < 32768; i++)
{
for (int j = 0; j < 2; j++)
{
if (gScope->mCh[j*2].mEnabled)
{
float* graphdata = gScope->mCh[j * 2].mData;
float x = graphdata[(index - ofs + i * samples / 32768 + cycle) % cycle];
graphdata = gScope->mCh[j * 2 + 1].mData;
float y = graphdata[(index - ofs + i * samples / 32768 + cycle) % cycle];
x = x * gScope->mCh[j * 2].mScale - gScope->mCh[j * 2].mOffset;
y = y * gScope->mCh[j * 2 + 1].mScale - gScope->mCh[j * 2 + 1].mOffset;
ImGui::GetWindowDrawList()->AddCircleFilled(
ImVec2(p.x + (grid_size / 2 + x * grid_quarter_size) * uiScale, p.y + (grid_size / 2 + y * grid_quarter_size) * uiScale),
1,
(gScope->colors[j*2] & 0xffffff) | 0x3f000000);
}
}
}
}

}
#endif

static void scope_time(ScopeData* gScope, const float uiScale, int index)
{
ImVec2 p = ImGui::GetItemRectMin();
const float gSamplerate = gScope->mSampleRate;
int cycle = gSamplerate * 10;
ImDrawList* dl = ImGui::GetWindowDrawList();
/*
Okay, max scale is 1 second, so..
*/
int samples = (int)(gSamplerate * gScope->mTimeScale);

scope_grid(uiScale);

int ofs = scope_sync(gScope, index);

if (samples > grid_size)
{
for (int j = 0; j < 4; j++)
{
if (gScope->mCh[j].mEnabled)
{
float* graphdata = gScope->mCh[j].mData;
ImVec2 vert[grid_size];
for (int i = 0; i < grid_size; i++)
{
float v0 = -graphdata[(index - ofs + i * samples / grid_size + cycle) % cycle];
float v1 = -graphdata[(index - ofs + (i + 1) * samples / grid_size + cycle) % cycle];
v0 = v0 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset;
v1 = v1 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset;
vert[i].x = p.x + i * uiScale;
vert[i].y = p.y + (grid_size / 2 + v0 * grid_quarter_size) * uiScale;
}
float v0 = p.y + (grid_size / 2 + (-gScope->mCh[j].mOffset) * grid_quarter_size) * uiScale;
dl->Flags = 0;
for (int i = 0; i < grid_size-1; i++)
{
ImVec2 quad[4];
quad[0] = ImVec2(vert[i].x, v0);
quad[1] = ImVec2(vert[i].x, vert[i].y);
quad[2] = ImVec2(vert[i + 1].x, vert[i + 1].y);
quad[3] = ImVec2(vert[i + 1].x, v0);
dl->AddConvexPolyFilled(quad, 4, (gScope->colors[j] & 0xffffff) | 0x3f000000 );

}

if (gScope->mTimeScale < 0.1)
{
dl->Flags = ImDrawListFlags_AntiAliasedLines;
dl->AddPolyline(vert, grid_size, gScope->colors[j], false, 2 * uiScale);
}
else
{
dl->Flags = ImDrawListFlags_AntiAliasedLines;
dl->AddPolyline(vert, grid_size, gScope->colors[j], false, 1);

}
}
}
}
else
{
// less than 1 sample per pixel
for (int j = 0; j < 4; j++)
{
if (gScope->mCh[j].mEnabled)
{
float* graphdata = gScope->mCh[j].mData;
for (int i = 0; i < samples; i++)
{
float v0 = -graphdata[(index - ofs + i + cycle) % cycle];
float v1 = 0;
v0 = v0 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset;
v1 = v1 * gScope->mCh[j].mScale - gScope->mCh[j].mOffset;
float x0 = p.x + (i * grid_size / samples) * uiScale;
ImGui::GetWindowDrawList()->AddCircleFilled(
ImVec2(x0, p.y + (grid_size / 2 + v0 * grid_quarter_size) * uiScale),
4 * uiScale,
gScope->colors[j]);
ImGui::GetWindowDrawList()->AddLine(
ImVec2(x0, p.y + (grid_size / 2 + v0 * grid_quarter_size) * uiScale),
ImVec2(x0, p.y + (grid_size / 2 + v1 * grid_quarter_size) * uiScale),
gScope->colors[j]);
}
}
}
}

ImGui::GetWindowDrawList()->AddText(p, 0xffc0c0c0, timescaletext[gScope->mTimeScaleSlider + 2]);
ImGui::GetWindowDrawList()->AddText(ImVec2(p.x, p.y + (grid_size / 2 - grid_quarter_size - 7) * uiScale), 0xffc0c0c0, "+1");
ImGui::GetWindowDrawList()->AddText(ImVec2(p.x, p.y + (grid_size / 2 + grid_quarter_size - 7) * uiScale), 0xffc0c0c0, "-1");

ImVec2 mp = ImGui::GetMousePos();
mp.x -= p.x;
mp.y -= p.y;
if (mp.x > 0 && mp.x < grid_size * uiScale &&
mp.y > 0 && mp.y < grid_size * uiScale)
{
ImGui::GetWindowDrawList()->AddLine(
ImVec2(p.x + mp.x, p.y),
ImVec2(p.x + mp.x, p.y + grid_size * uiScale),
0xff00ff00, 1 * uiScale);
if (gScope->mCh[0].mEnabled || gScope->mCh[1].mEnabled || gScope->mCh[2].mEnabled || gScope->mCh[3].mEnabled)
{
ImGui::BeginTooltip();
if (gScope->mCh[0].mEnabled) ImGui::Text("Ch 0: %3.3f", gScope->mCh[0].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]);
if (gScope->mCh[1].mEnabled) ImGui::Text("Ch 1: %3.3f", gScope->mCh[1].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]);
if (gScope->mCh[2].mEnabled) ImGui::Text("Ch 2: %3.3f", gScope->mCh[2].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]);
if (gScope->mCh[3].mEnabled) ImGui::Text("Ch 3: %3.3f", gScope->mCh[3].mData[(index - ofs + ((int)mp.x / (int)uiScale) * samples / grid_size + cycle) % cycle]);
ImGui::EndTooltip();
}
}
}


static void vertline(const float uiScale, const float x, const float w)
{
ImVec2 p = ImGui::GetItemRectMin();
ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + x * uiScale, p.y * uiScale), ImVec2(p.x + x * uiScale, p.y + (grid_size) * uiScale), 0xff000000, w);
}

static void scope_freq(ScopeData* gScope, const float uiScale, int index)
{
ImVec2 p = ImGui::GetItemRectMin();
ImDrawList* dl = ImGui::GetWindowDrawList();
const float gSamplerate = gScope->mSampleRate;
int cycle = gSamplerate * 10;
/*
Okay, max scale is 1 second, so..
*/
int samples = (int)(gSamplerate * gScope->mTimeScale);

// what's the biggest PoT < samples?
// 192000 takes 18 bits to encode.
// Fill 32 bits:
int pot = samples | (samples >> 16);
pot = pot | (pot >> 8);
pot = pot | (pot >> 4);
pot = pot | (pot >> 2);
pot = pot | (pot >> 1);
// Shift down and add one to round it up
pot = (pot >> 1) + 1;

if (pot < 16) pot = 16;
if (pot > 65536) pot = 65536;

gScope->mPot = pot;

ffft::FFTReal<float>* fft = NULL;
switch (pot)
{
case 16: fft = gScope->fft.obj16; break;
case 32: fft = gScope->fft.obj32; break;
case 64: fft = gScope->fft.obj64; break;
case 128: fft = gScope->fft.obj128; break;
case 256: fft = gScope->fft.obj256; break;
case 512: fft = gScope->fft.obj512; break;
case 1024: fft = gScope->fft.obj1024; break;
case 2048: fft = gScope->fft.obj2048; break;
case 4096: fft = gScope->fft.obj4096; break;
case 8192: fft = gScope->fft.obj8192; break;
case 16384: fft = gScope->fft.obj16384; break;
case 32768: fft = gScope->fft.obj32768; break;
case 65536: fft = gScope->fft.obj65536; break;
}
if (!fft) return;

int average = gScope->fft.average;
int ofs = scope_sync(gScope, index);
int size = grid_size - 1;
float sizef = size;
float freqbin = gSamplerate / (float)(pot / 2);
float freqbins[size];
float zoom = 1.0f / (1 << gScope->mFFTZoom);

for (int i = 0; i < 10; i++)
{
vertline(uiScale, sqrt(100 / freqbin * i / (pot / 4)) / zoom * sizef, 1);
vertline(uiScale, sqrt(1000 / freqbin * i / (pot / 4)) / zoom * sizef, 1);
vertline(uiScale, sqrt(10000 / freqbin * i / (pot / 4)) / zoom * sizef, 1);
}

for (int j = 0; j < 4; j++)
{
if (gScope->mCh[j].mEnabled)
{


memset(gScope->ffta, 0, sizeof(float) * 65536 * 2);
for (int k = 0; k < average; k++)
{
float* graphdata = gScope->mCh[j].mData;

for (int i = 0; i < pot; i++)
{
gScope->fft1[i * 2] = graphdata[(index - ofs + i + cycle - k) % cycle];
gScope->fft1[i * 2 + 1] = 0;
}

fft->do_fft(gScope->fft2, gScope->fft1);

for (int i = 0; i < pot / 4; i++)
gScope->ffta[i] += (1.0f / average) * sqrt(gScope->fft2[i * 2 + 0] * gScope->fft2[i * 2 + 0] + gScope->fft2[i * 2 + 1] * gScope->fft2[i * 2 + 1]);
}

ImVec2 vert[size];

for (int i = 0; i < size; i++)
{
float ppos = powf(zoom * i / sizef, 2.0f) * pot / 4;
freqbins[i] = ppos * freqbin;
float f = ppos - (int)ppos;
float a = i ? gScope->ffta[(int)ppos - 1] : 0;
float b = gScope->ffta[(int)ppos];
float c = i < size ? gScope->ffta[(int)ppos + 1] : 0;
float d = i < (size-1) ? gScope->ffta[(int)ppos + 2] : 0;

float v0 = (float)catmullrom(f, a, b, c, d);
v0 = v0 * gScope->mCh[j].mScale + gScope->mCh[j].mOffset * 50;
vert[i] = ImVec2(p.x + i * uiScale,
p.y + (sizef - v0 * 4) * uiScale);
}
float v0 = p.y + (size - gScope->mCh[j].mOffset * 50 * 4) * uiScale;
dl->Flags = 0;
for (int i = 0; i < size-1; i++)
{
ImVec2 quad[4];
quad[0] = ImVec2(vert[i].x, v0);
quad[1] = ImVec2(vert[i].x, vert[i].y);
quad[2] = ImVec2(vert[i + 1].x, vert[i + 1].y);
quad[3] = ImVec2(vert[i + 1].x, v0);
dl->AddConvexPolyFilled(quad, 4, (gScope->colors[j] & 0xffffff) | 0x3f000000);

}

dl->Flags = ImDrawListFlags_AntiAliasedLines;
dl->AddPolyline(vert, size, gScope->colors[j], false, 1);

}
}

if (!ImGui::IsPopupOpen("Freq Context",ImGuiPopupFlags_AnyPopupId))
if (gScope->mCh[0].mEnabled || gScope->mCh[1].mEnabled || gScope->mCh[2].mEnabled || gScope->mCh[3].mEnabled)
{
ImVec2 mp = ImGui::GetMousePos();
mp.x -= p.x;
mp.y -= p.y;
if (mp.x > 0 && mp.x < grid_size * uiScale &&
mp.y > 0 && mp.y < grid_size * uiScale)
{
ImGui::GetWindowDrawList()->AddLine(
ImVec2(p.x + mp.x, p.y),
ImVec2(p.x + mp.x, p.y + grid_size * uiScale),
0xff00ff00, 1 * uiScale);
ImGui::BeginTooltip();
int note = (int)(12 * log(32 * POW_2_3_4TH * (freqbins[(int)mp.x] / 440)) / log(2));
if (note < 0 || note > 127) note = -1;
ImGui::Text("%3.3fHz%s%s", freqbins[(int)mp.x], note==-1?"":"\n", note==-1?"":gNotestr[note]);
ImGui::EndTooltip();
}
}
}

#if 0
static int groups(ScopeData* gScope, double h)
{
int count = 0;
for (int i = 1; i < gScope->mPot / 4; i++)
{
if (gScope->fft1[i - 1] < h && gScope->fft1[i] > h)
count++;
}
return count;
}

static void detect_fundamentals(ScopeData* gScope)
{
// gScope->fft1[1..pot/4] has our mags
double maxmag = 0;
for (int i = 0; i < gScope->mPot / 4; i++)
if (maxmag < gScope->fft1[i])
maxmag = gScope->fft1[i];

double minmag = 0;
int count = 0;
int iters = 0;
double h = (minmag + maxmag) / 2;
double step = h / 2;
while (iters < 100 && count != 16)
{
count = groups(gScope, h);
if (count < 16)
{
h -= step;
}
else
{
h += step;
}
step /= 2;
iters++;
}
char temp[1024];
int ofs = 0;
temp[0] = 0;
const float gSamplerate = gScope->mSampleRate;
float freqbin = gSamplerate / (float)(gScope->mPot / 2);

int startbin = 0;
for (int i = 2; i < gScope->mPot / 4; i++)
{
if (gScope->fft1[i - 1] < h && gScope->fft1[i] > h)
{
startbin = i;
}
if (gScope->fft1[i - 1] > h && gScope->fft1[i] < h)
{
double sum = 0;
double magsum = 0;
for (int j = startbin; j < i; j++)
{
sum += gScope->fft1[j];
magsum += gScope->fft1[j] * j * freqbin;
}
if (sum != 0)
{
magsum /= sum;
sum /= i - startbin;
sum /= maxmag / 2; // normalize
ofs += sprintf(temp + ofs, "%3.3f\t%3.3f\n", magsum, sum);
}
}
}


for (int i = 0; i < count; i++)
{
}
ImGui::SetClipboardText(temp);
}
#endif

void do_show_scope_window(ScopeData* gScope, const float uiScale)
{
// Data is updated live, so let's take local copies of critical stuff.
int index = gScope->mIndex;

ImGui::Begin("Scope", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize);

ImGui::BeginChild("Channel options", ImVec2((4 * 25)*uiScale, (2 * 152 + 32) * uiScale));
ImGui::Checkbox("###ea", &gScope->mCh[0].mEnabled); ImGui::SameLine();
ImGui::Checkbox("###eb", &gScope->mCh[1].mEnabled); ImGui::SameLine();
ImGui::Checkbox("###ec", &gScope->mCh[2].mEnabled); ImGui::SameLine();
ImGui::Checkbox("###ed", &gScope->mCh[3].mEnabled);

ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[0]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[0]);
if (ImGui::VSliderInt("###0a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[0].mScaleSlider, -4, 4, ""))
{
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
gScope->mCh[0].mScaleSlider = 0;
gScope->mCh[0].mScale = scalesteps[4];
}
else
{
gScope->mCh[0].mScale = scalesteps[gScope->mCh[0].mScaleSlider + 4];
}
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", scaletexts[gScope->mCh[0].mScaleSlider + 4]);
ImGui::EndTooltip();
}
ImGui::PopStyleColor(2); ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[1]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[1]);
if (ImGui::VSliderInt("###1a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[1].mScaleSlider, -4, 4, ""))
{
gScope->mCh[1].mScale = scalesteps[gScope->mCh[1].mScaleSlider + 4];
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", scaletexts[gScope->mCh[1].mScaleSlider + 4]);
ImGui::EndTooltip();
}
ImGui::PopStyleColor(2); ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[2]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[2]);
if (ImGui::VSliderInt("###2a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[2].mScaleSlider, -4, 4, ""))
{
gScope->mCh[2].mScale = scalesteps[gScope->mCh[2].mScaleSlider + 4];
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", scaletexts[gScope->mCh[2].mScaleSlider + 4]);
ImGui::EndTooltip();
}
ImGui::PopStyleColor(2); ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[3]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[3]);
if (ImGui::VSliderInt("###3a", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[3].mScaleSlider, -4, 4, ""))
{
gScope->mCh[3].mScale = scalesteps[gScope->mCh[3].mScaleSlider + 4];
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", scaletexts[gScope->mCh[3].mScaleSlider + 4]);
ImGui::EndTooltip();
}
ImGui::PopStyleColor(2);

ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[0]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[0]); ImGui::VSliderFloat("###0b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[0].mOffset, -2, 2, ""); ImGui::PopStyleColor(2); ImGui::SameLine();
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%3.3f", gScope->mCh[0].mOffset);
ImGui::EndTooltip();
}
ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[1]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[1]); ImGui::VSliderFloat("###1b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[1].mOffset, -2, 2, ""); ImGui::PopStyleColor(2); ImGui::SameLine();
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%3.3f", gScope->mCh[1].mOffset);
ImGui::EndTooltip();
}
ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[2]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[2]); ImGui::VSliderFloat("###2b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[2].mOffset, -2, 2, ""); ImGui::PopStyleColor(2); ImGui::SameLine();
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%3.3f", gScope->mCh[2].mOffset);
ImGui::EndTooltip();
}
ImGui::PushStyleColor(ImGuiCol_SliderGrab, gScope->colors[3]); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, gScope->colors[3]); ImGui::VSliderFloat("###3b", ImVec2(19 * uiScale, 150 * uiScale), &gScope->mCh[3].mOffset, -2, 2, ""); ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%3.3f", gScope->mCh[3].mOffset);
ImGui::EndTooltip();
}
ImGui::EndChild();

ImGui::SameLine();

ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyle().Colors[ImGuiCol_FrameBg]);
ImGui::BeginChild("Scope and scroll", ImVec2(grid_size * uiScale, (grid_size + 24)* uiScale));
ImGui::BeginChild("Scope proper", ImVec2(grid_size * uiScale, grid_size * uiScale));

if (gScope->mDisplay == 0)
scope_time(gScope, uiScale, index);
if (gScope->mDisplay == 1)
scope_freq(gScope, uiScale, index);
/*
if (gScope->mDisplay == 2 || gScope->mDisplay == 3)
scope_plot(gScope, uiScale, index);
*/

ImGui::EndChild();
ImGui::PopStyleColor(1);
/*
if (gScope->mDisplay == 1)
{
if (ImGui::BeginPopupContextItem("Freq Context"))
{
if (ImGui::BeginMenu("Experimental.."))
{
if (ImGui::MenuItem("Detect and copy fundamental frequencies"))
{
detect_fundamentals(gScope);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Averaging.."))
{
if (ImGui::MenuItem("1x"))
gScope->fft.average = 1;
if (ImGui::MenuItem("4x"))
gScope->fft.average = 4;
if (ImGui::MenuItem("16x"))
gScope->fft.average = 16;
if (ImGui::MenuItem("64x"))
gScope->fft.average = 64;
if (ImGui::MenuItem("256x"))
gScope->fft.average = 256;
ImGui::EndMenu();
}

ImGui::EndPopup();
}
}
//context_menu(1, 1, 1);
*/

if (gScope->mMode)
{
ImGui::SetNextItemWidth(grid_size * uiScale);
ImGui::SliderFloat("###scroll", &gScope->mScroll, -10.0f, 0.0f, "%.3f s");
}
else
{
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xff3f3f3f);
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0xff3f3f3f);
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0xff3f3f3f);
ImGui::PushStyleColor(ImGuiCol_SliderGrab, 0xff7f7f7f);
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, 0xff7f7f7f);
ImGui::SetNextItemWidth(grid_size * uiScale);
float x = gScope->mScroll;
ImGui::SliderFloat("###scroll", &x, -10.0f, 0.0f, "%.3f s");
ImGui::PopStyleColor(5);
}
ImGui::EndChild();

ImGui::SameLine();

ImGui::BeginChild("Scope options", ImVec2((4 * 21) * uiScale, 364 * uiScale));
if (ImGui::VSliderInt("###0a", ImVec2(19 * uiScale, 155 * uiScale), &gScope->mTimeScaleSlider, -2, 2, ""))
{
gScope->mTimeScale = timescalesteps[gScope->mTimeScaleSlider + 2];
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", timescaletext[gScope->mTimeScaleSlider + 2]);
ImGui::EndTooltip();
}
ImGui::SameLine();
ImGui::BeginChild("moderadio", ImVec2(100 * uiScale, 155 * uiScale));
if (ImGui::RadioButton("Time", gScope->mDisplay == 0)) gScope->mDisplay = 0;
if (ImGui::RadioButton("Freq", gScope->mDisplay == 1)) gScope->mDisplay = 1;
/*
if (ImGui::RadioButton("X,X'", gScope->mDisplay == 2)) gScope->mDisplay = 2;
if (ImGui::RadioButton("X,Y", gScope->mDisplay == 3)) gScope->mDisplay = 3;
*/
ImGui::Separator();
ImGui::Text("FFT");
if (ImGui::RadioButton("1x", gScope->mFFTZoom == 0)) gScope->mFFTZoom = 0;
if (ImGui::RadioButton("2x", gScope->mFFTZoom == 1)) gScope->mFFTZoom = 1;
if (ImGui::RadioButton("4x", gScope->mFFTZoom == 2)) gScope->mFFTZoom = 2;
if (ImGui::RadioButton("8x", gScope->mFFTZoom == 3)) gScope->mFFTZoom = 3;
ImGui::EndChild();
char temp[64];
sprintf(temp, "Sync ch %d###sc", gScope->mSyncChannel + 1);
if (ImGui::Button(temp, ImVec2(80 * uiScale, 20 * uiScale)))
gScope->mSyncChannel = (gScope->mSyncChannel + 1) % 4;
const char* syncmodes[3] = { "^", "v", "off" };
sprintf(temp, "Sync %s###sm", syncmodes[gScope->mSyncMode]);
if (ImGui::Button(temp, ImVec2(80 * uiScale, 20 * uiScale)))
gScope->mSyncMode = (gScope->mSyncMode + 1) % 3;

if (gScope->mMode == 0)
{
if (ImGui::Button("Pause", ImVec2(80 * uiScale, 20 * uiScale)))
gScope->mMode = 1;
ImGui::Text("Nudge (ms)");
ImGui::PushStyleColor(ImGuiCol_Button, 0xff3f3f3f);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, 0xff3f3f3f);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xff3f3f3f);
ImGui::Button("-0.1", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::SameLine();
ImGui::Button("+0.1", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::Button("-1", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::SameLine();
ImGui::Button("+1", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::Button("-10", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::SameLine();
ImGui::Button("+10", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::Button("-100", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::SameLine();
ImGui::Button("+100", ImVec2(38 * uiScale, 20 * uiScale));
ImGui::PopStyleColor(3);
}
else
{
if (ImGui::Button("Capture", ImVec2(80 * uiScale, 20 * uiScale)))
gScope->mMode = 0;
ImGui::Text("Nudge (ms)");
if (ImGui::Button("-0.1", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll -= 0.0001f;
}
ImGui::SameLine();
if (ImGui::Button("+0.1", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll += 0.0001f;
}
if (ImGui::Button("-1", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll -= 0.001f;
}
ImGui::SameLine();
if (ImGui::Button("+1", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll += 0.001f;
}
if (ImGui::Button("-10", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll -= 0.01f;
}
ImGui::SameLine();
if (ImGui::Button("+10", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll += 0.01f;
}
if (ImGui::Button("-100", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll -= 0.1f;
}
ImGui::SameLine();
if (ImGui::Button("+100", ImVec2(38 * uiScale, 20 * uiScale)))
{
gScope->mScroll += 0.1f;
}
}
ImGui::EndChild();

ImGui::End();

}

+ 2
- 0
plugins/Makefile View File

@@ -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


+ 5
- 0
plugins/plugins.cpp View File

@@ -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


Loading…
Cancel
Save