@@ -0,0 +1,11 @@ | |||
dofile("../../../scripts/make-project.lua") | |||
package = make_juce_lv2_project("ReFine") | |||
package.files = { | |||
matchfiles ( | |||
"../source/*.cpp", | |||
"../../../libs/juce-plugin/JucePluginMain.cpp" | |||
) | |||
} |
@@ -0,0 +1,11 @@ | |||
dofile("../../../scripts/make-project.lua") | |||
package = make_juce_vst_project("ReFine") | |||
package.files = { | |||
matchfiles ( | |||
"../source/*.cpp", | |||
"../../../libs/juce-plugin/JucePluginMain.cpp" | |||
) | |||
} |
@@ -0,0 +1,251 @@ | |||
#include "Analyzer.h" | |||
struct Analyzer::Data | |||
{ | |||
Data(int size_); | |||
void clear(); | |||
void copyFrom (const Data& other); | |||
const juce::CriticalSection& getLock() const; | |||
const int size; | |||
juce::HeapBlock<float> mags; | |||
juce::HeapBlock<float> angles; | |||
private: | |||
juce::CriticalSection lock; | |||
JUCE_DECLARE_NON_COPYABLE(Data) | |||
}; | |||
Analyzer::Data::Data (int size_) | |||
: size (size_), | |||
mags (size), | |||
angles (size) | |||
{ | |||
clear(); | |||
} | |||
void Analyzer::Data::clear() | |||
{ | |||
juce::zeromem(mags, sizeof(float) * size); | |||
juce::zeromem(angles, sizeof(float) * size); | |||
} | |||
void Analyzer::Data::copyFrom (const Data& other) | |||
{ | |||
if (other.size == size) | |||
{ | |||
memcpy(mags, other.mags, sizeof(float) * size); | |||
memcpy(angles, other.angles, sizeof(float) * size); | |||
} | |||
} | |||
const juce::CriticalSection& Analyzer::Data::getLock() const | |||
{ | |||
return lock; | |||
} | |||
RmsEnvelope::RmsEnvelope (int envsize, double rmsLength, double updatetime) | |||
: rms (rmsLength), rmsVals (envsize+1), updateTime (updatetime), sampleRate (44100) | |||
{ | |||
clear(); | |||
} | |||
void RmsEnvelope::setSampleRate (double newSampleRate) | |||
{ | |||
sampleRate = newSampleRate; | |||
rms.setSampleRate(sampleRate); | |||
clear(); | |||
} | |||
void RmsEnvelope::clear() | |||
{ | |||
rms.clear(); | |||
rmsVals.clear(); | |||
updateIndex = 0; | |||
} | |||
bool RmsEnvelope::process (const float inL, const float inR) | |||
{ | |||
rms.process(inL, inR); | |||
const int ovSize = int(updateTime * sampleRate); | |||
jassert(ovSize > 100); | |||
if (++updateIndex >= ovSize) | |||
{ | |||
updateIndex = 0; | |||
const float rmsVal = rms.getRms(); | |||
rmsVals.push(rmsVal); | |||
return true; | |||
} | |||
return false; | |||
} | |||
void RmsEnvelope::processBlock (const float* inL, const float* inR, int numSamples) | |||
{ | |||
ScopedLock lock(processLock); | |||
const int ovSize = int(updateTime * sampleRate); | |||
jassert(ovSize > 100); | |||
int idx = 0; | |||
while (numSamples > 0) | |||
{ | |||
const int curNumSamples = jmin(ovSize-updateIndex, numSamples); | |||
rms.processBlock(&inL[idx], &inR[idx], curNumSamples); | |||
numSamples -= curNumSamples; | |||
idx += curNumSamples; | |||
updateIndex += curNumSamples; | |||
if (updateIndex >= ovSize) | |||
{ | |||
updateIndex = 0; | |||
const float rmsVal = rms.getRms(); | |||
rmsVals.push(rmsVal); | |||
} | |||
} | |||
} | |||
bool RmsEnvelope::getData (Array<float>& data) const | |||
{ | |||
const int dataLength = getDataLength(); | |||
if (data.size() < dataLength) | |||
{ | |||
ScopedTryLock lock(processLock); | |||
if (! lock.isLocked()) | |||
return false; | |||
for (int i=0; i<data.size(); ++i) | |||
data.getReference(i) = rmsVals[i]; | |||
return true; | |||
} | |||
return false; | |||
} | |||
int RmsEnvelope::getDataLength() const | |||
{ | |||
return rmsVals.getSize() - 1; | |||
} | |||
Analyzer::Analyzer() | |||
: sampleRate (0) | |||
{ | |||
setSampleRate(44100); | |||
} | |||
void Analyzer::clear() | |||
{ | |||
fftIndex = 0; | |||
data->clear(); | |||
} | |||
void Analyzer::processBlock (const float* inL, const float* inR, int numSamples) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
{ | |||
x[fftIndex] = 0.5f * (inL[i] + inR[i]) * window[fftIndex]; | |||
++fftIndex; | |||
if (fftIndex >= fftBlockSize) | |||
{ | |||
processFFT(); | |||
fftIndex = 0; | |||
} | |||
} | |||
} | |||
int Analyzer::getFFTSize() const | |||
{ | |||
return fftBlockSize; | |||
} | |||
int Analyzer::getNumBins() const | |||
{ | |||
return numBins; | |||
} | |||
bool Analyzer::getData (Data& d) const | |||
{ | |||
ScopedTryLock lock(data->getLock()); | |||
if (lock.isLocked()) | |||
{ | |||
d.copyFrom(*data); | |||
return true; | |||
} | |||
return false; | |||
} | |||
void Analyzer::setSampleRate (double newSampleRate) | |||
{ | |||
if (sampleRate != newSampleRate) | |||
{ | |||
sampleRate = newSampleRate; | |||
fftBlockSize = 512 * jmax(1, int(sampleRate / 44100)); | |||
numBins = fftBlockSize / 2 + 1; | |||
fft = new ffft::FFTReal<float> (fftBlockSize); | |||
x.realloc(fftBlockSize); | |||
f.realloc(fftBlockSize); | |||
window.realloc(fftBlockSize); | |||
data = new Data(numBins); | |||
{ | |||
const float alpha = 0.16f; | |||
const float a0 = 0.5f * (1-alpha); | |||
const float a1 = 0.5f; | |||
const float a2 = alpha*0.5f; | |||
for (int i=0; i<fftBlockSize; ++i) | |||
window[i] = a0 - a1*cos(2*float_Pi*i/(fftBlockSize-1)) + a2*cos(4*float_Pi*i/(fftBlockSize-1)); | |||
} | |||
clear(); | |||
} | |||
} | |||
double Analyzer::getSampleRate() const | |||
{ | |||
return sampleRate; | |||
} | |||
void Analyzer::processFFT() | |||
{ | |||
fft->do_fft(f, x); | |||
const int imagOffset = fftBlockSize / 2; | |||
const float weight = 1.f / fftBlockSize; | |||
{ | |||
ScopedLock lock(data->getLock()); | |||
data->mags[0] = f[0]*f[0] * weight; | |||
data->mags[imagOffset] = f[imagOffset]*f[imagOffset] * weight; | |||
data->angles[0] = 0; | |||
data->angles[numBins-1] = 0; | |||
for (int i=1; i<numBins-1; ++i) | |||
{ | |||
const float re = f[i]; | |||
const float im = f[imagOffset + i]; | |||
data->mags[i] = (re*re + im*im) * weight; | |||
data->angles[i] = atan2(im, re); | |||
} | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
#ifndef __ANALYZER_H__ | |||
#define __ANALYZER_H__ | |||
#include "JuceHeader.h" | |||
#include "Buffers.h" | |||
#include "ffft/FFTReal.h" | |||
class RmsEnvelope | |||
{ | |||
public: | |||
RmsEnvelope (int envsize, double rmsLength, double updatetime); | |||
void setSampleRate (double newSampleRate); | |||
void clear(); | |||
bool process (const float inL, const float inR); | |||
void processBlock (const float* inL, const float* inR, int numSamples); | |||
bool getData (Array<float>& data) const; | |||
int getDataLength() const; | |||
private: | |||
RmsLevel rms; | |||
CircularBuffer<float> rmsVals; | |||
double updateTime; | |||
double sampleRate; | |||
int updateIndex; | |||
juce::CriticalSection processLock; | |||
JUCE_DECLARE_NON_COPYABLE (RmsEnvelope) | |||
}; | |||
class Analyzer | |||
{ | |||
public: | |||
struct Data; | |||
Analyzer(); | |||
void clear(); | |||
void processBlock (const float* inL, const float* inR, int numSamples); | |||
int getFFTSize() const; | |||
int getNumBins() const; | |||
bool getData(Data& d) const; | |||
void setSampleRate (double newSampleRate); | |||
double getSampleRate() const; | |||
private: | |||
void processFFT(); | |||
juce::ScopedPointer<ffft::FFTReal<float> > fft; | |||
juce::HeapBlock<float> x; | |||
juce::HeapBlock<float> f; | |||
juce::HeapBlock<float> window; | |||
juce::ScopedPointer<Data> data; | |||
int fftIndex; | |||
int fftBlockSize; | |||
int numBins; | |||
double sampleRate; | |||
}; | |||
#endif // __ANALYZER_H__ |
@@ -0,0 +1,31 @@ | |||
/* (Auto-generated binary data file). */ | |||
#pragma once | |||
namespace BinaryData | |||
{ | |||
extern const char* background_png; | |||
const int background_pngSize = 49833; | |||
extern const char* blue_png; | |||
const int blue_pngSize = 66845; | |||
extern const char* green_png; | |||
const int green_pngSize = 66938; | |||
extern const char* red_png; | |||
const int red_pngSize = 66547; | |||
extern const char* vu_blue_png; | |||
const int vu_blue_pngSize = 13293; | |||
extern const char* vu_green_png; | |||
const int vu_green_pngSize = 13138; | |||
extern const char* vu_red_png; | |||
const int vu_red_pngSize = 13086; | |||
extern const char* x2button_png; | |||
const int x2button_pngSize = 2312; | |||
} |
@@ -0,0 +1,143 @@ | |||
#include "Buffers.h" | |||
RmsBuffer::RmsBuffer (int initSize) | |||
: buffer (initSize) | |||
{ | |||
clear(); | |||
} | |||
void RmsBuffer::clear() | |||
{ | |||
rms = 0; | |||
buffer.clear(); | |||
} | |||
double RmsBuffer::process (double in) | |||
{ | |||
const double x = in * in; | |||
const double tmp = buffer.pushAndGet(x); | |||
rms = rms + x - tmp; | |||
return rms / buffer.getSize(); | |||
} | |||
void RmsBuffer::processBlock (const double* in, int numSamples) | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
process(in[i]); | |||
} | |||
void RmsBuffer::setSize (int newSize) | |||
{ | |||
if (buffer.setSize(newSize)) | |||
rms = 0; | |||
} | |||
int RmsBuffer::getSize() | |||
{ | |||
return buffer.getSize(); | |||
} | |||
double RmsBuffer::getRms() | |||
{ | |||
return rms / buffer.getSize(); | |||
} | |||
RmsLevel::RmsLevel (double lengthMs) | |||
: ms (lengthMs), size (0), index (0) | |||
{ | |||
setSampleRate(44100); | |||
} | |||
void RmsLevel::setSampleRate (double sampleRate) | |||
{ | |||
const int newSize = int(sampleRate*ms*0.001); | |||
if (size != newSize) | |||
{ | |||
size = newSize; | |||
data.realloc(size); | |||
} | |||
clear(); | |||
} | |||
void RmsLevel::clear() | |||
{ | |||
zeromem(data, sizeof(double) * size); | |||
rms = 0; | |||
index = 0; | |||
} | |||
void RmsLevel::process (float in) | |||
{ | |||
const double x = in*in; | |||
rms += x - data[index]; | |||
if (rms < 1e-8) | |||
rms = 0; | |||
data[index] = x; | |||
if (++index >= size) | |||
index = 0; | |||
} | |||
void RmsLevel::process (float inL, float inR) | |||
{ | |||
const double x = 0.5f * (inL*inL + inR*inR); | |||
rms += x - data[index]; | |||
if (rms < 1e-8) | |||
rms = 0; | |||
data[index] = x; | |||
if (++index >= size) | |||
index = 0; | |||
} | |||
void RmsLevel::processBlock (const float* inL, const float* inR, int numSamples) | |||
{ | |||
if (inR == nullptr) | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
{ | |||
const double x = inL[i] * inL[i]; | |||
rms += x - data[index]; | |||
if (rms < 1e-8) | |||
rms = 0; | |||
data[index] = x; | |||
if (++index >= size) | |||
index = 0; | |||
} | |||
} | |||
else | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
{ | |||
const double x = 0.5f * (inL[i] * inL[i] + inR[i] * inR[i]); | |||
rms += x - data[index]; | |||
if (rms < 1e-8) | |||
rms = 0; | |||
data[index] = x; | |||
if (++index >= size) | |||
index = 0; | |||
} | |||
} | |||
} | |||
float RmsLevel::getRms() | |||
{ | |||
return sqrt(float(rms / size)); | |||
} | |||
double RmsLevel::getRmsLength() | |||
{ | |||
return ms; | |||
} |
@@ -0,0 +1,136 @@ | |||
#ifndef BUFFERS_H_INCLUDED | |||
#define BUFFERS_H_INCLUDED | |||
#include "JuceHeader.h" | |||
template <class DataType> | |||
class CircularBuffer | |||
{ | |||
public: | |||
CircularBuffer (int initSize) | |||
: size (0), index (0) | |||
{ | |||
setSize(initSize); | |||
clear(); | |||
} | |||
bool setSize (int newSize) | |||
{ | |||
jassert(newSize > 0); | |||
if (size != newSize && newSize > 0) | |||
{ | |||
size = newSize; | |||
data.realloc(size); | |||
clear(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
int getSize() const | |||
{ | |||
return size; | |||
} | |||
void clear() | |||
{ | |||
juce::zeromem(data, sizeof(DataType) * size); | |||
index = 0; | |||
} | |||
void push (DataType x) | |||
{ | |||
data[index] = x; | |||
if (++index >= size) | |||
index = 0; | |||
} | |||
DataType pushAndGet (DataType x) | |||
{ | |||
DataType ret = data[index]; | |||
push(x); | |||
return ret; | |||
} | |||
DataType operator[] (int offset) const | |||
{ | |||
int idx = index - offset - 1; | |||
if (idx < 0) | |||
idx += size; | |||
jassert(idx >= 0 && idx < size); | |||
return data[idx]; | |||
} | |||
void processBlock (DataType* proc, int numSamples) | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
proc[i] = pushAndGet(proc[i]); | |||
} | |||
private: | |||
int size; | |||
juce::HeapBlock<DataType> data; | |||
int index; | |||
}; | |||
class RmsBuffer | |||
{ | |||
public: | |||
RmsBuffer (int initSize); | |||
void clear(); | |||
double process (double in); | |||
void processBlock (const double* in, int numSamples); | |||
void setSize (int newSize); | |||
int getSize(); | |||
double getRms(); | |||
private: | |||
double rms; | |||
CircularBuffer<double> buffer; | |||
}; | |||
class RmsLevel | |||
{ | |||
public: | |||
RmsLevel (double lengthMs); | |||
void setSampleRate (double sampleRate); | |||
void clear(); | |||
void process (float in); | |||
void process (float inL, float inR); | |||
void processBlock (const float* inL, const float* inR, int numSamples); | |||
float getRms(); | |||
double getRmsLength(); | |||
private: | |||
const double ms; | |||
int size; | |||
HeapBlock<double> data; | |||
double rms; | |||
int index; | |||
JUCE_DECLARE_NON_COPYABLE(RmsLevel) | |||
}; | |||
#endif // BUFFERS_H_INCLUDED |
@@ -0,0 +1,28 @@ | |||
/* | |||
IMPORTANT! This file is auto-generated each time you save your | |||
project - if you alter its contents, your changes may be overwritten! | |||
This is the header file that your files should include in order to get all the | |||
JUCE library headers. You should avoid including the JUCE headers directly in | |||
your own source files, because that wouldn't pick up the correct configuration | |||
options for your app. | |||
*/ | |||
#ifndef __APPHEADERFILE_JH7QDM__ | |||
#define __APPHEADERFILE_JH7QDM__ | |||
#include "JucePluginMain.h" | |||
#include "BinaryData.h" | |||
using namespace juce; | |||
namespace ProjectInfo | |||
{ | |||
const char* const projectName = "ReFine"; | |||
const char* const versionString = "1.1.0"; | |||
const int versionNumber = 0x10100; | |||
} | |||
#endif // __APPHEADERFILE_JH7QDM__ |
@@ -0,0 +1,60 @@ | |||
/* | |||
IMPORTANT! This file is auto-generated by the Jucer each time you save your | |||
project - if you alter its contents, your changes may be overwritten! | |||
This header file contains configuration options for the plug-in. If you need to change any of | |||
these, it'd be wise to do so using the Jucer, rather than editing this file directly... | |||
*/ | |||
#ifndef __PLUGINCHARACTERISTICS_D4EFFF1A__ | |||
#define __PLUGINCHARACTERISTICS_D4EFFF1A__ | |||
#define JucePlugin_Name "ReFine" | |||
#define JucePlugin_Desc "ReFine" | |||
#define JucePlugin_Manufacturer "Lkjb" | |||
#define JucePlugin_ManufacturerCode 'Lkjb' | |||
#define JucePlugin_PluginCode 'LkRf' | |||
#define JucePlugin_MaxNumInputChannels 2 | |||
#define JucePlugin_MaxNumOutputChannels 2 | |||
#define JucePlugin_PreferredChannelConfigurations {2, 2}, {1, 1} | |||
#define JucePlugin_IsSynth 0 | |||
#define JucePlugin_IsMidiEffect 0 | |||
#define JucePlugin_WantsMidiInput 0 | |||
#define JucePlugin_ProducesMidiOutput 0 | |||
#define JucePlugin_SilenceInProducesSilenceOut 0 | |||
#define JucePlugin_TailLengthSeconds 0 | |||
#define JucePlugin_EditorRequiresKeyboardFocus 1 | |||
#define JucePlugin_Version 1.1.0 | |||
#define JucePlugin_VersionCode 0x10100 | |||
#define JucePlugin_VersionString "1.1.0" | |||
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode | |||
#define JucePlugin_VSTCategory kPlugCategEffect | |||
#define JucePlugin_AUMainType kAudioUnitType_Effect | |||
#define JucePlugin_AUSubType JucePlugin_PluginCode | |||
#define JucePlugin_AUExportPrefix ReFineAU | |||
#define JucePlugin_AUExportPrefixQuoted "ReFineAU" | |||
#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode | |||
#define JucePlugin_CFBundleIdentifier com.lkjb.ReFine | |||
#define JucePlugin_AUCocoaViewClassName PitchedDelayAU_V1 | |||
#define JucePlugin_RTASCategory ePlugInCategory_None | |||
#define JucePlugin_RTASManufacturerCode JucePlugin_ManufacturerCode | |||
#define JucePlugin_RTASProductId JucePlugin_PluginCode | |||
#define JucePlugin_RTASDisableBypass 0 | |||
#define JucePlugin_RTASDisableMultiMono 0 | |||
#define JucePlugin_AAXIdentifier com.yourcompany.ReFine | |||
#define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode | |||
#define JucePlugin_AAXProductId JucePlugin_PluginCode | |||
#define JucePlugin_AAXPluginId JucePlugin_PluginCode | |||
#define JucePlugin_AAXCategory AAX_ePlugInCategory_Dynamics | |||
#define JucePlugin_AAXDisableBypass 0 | |||
#define JucePlugin_LV2URI "https://github.com/lkjbdsp/lkjb-plugins#ReFine" | |||
//#define JucePlugin_LV2Category "DelayPlugin" | |||
#define JucePlugin_WantsLV2Latency 0 | |||
#define JucePlugin_WantsLV2State 0 | |||
#define JucePlugin_WantsLV2TimePos 0 | |||
#define JucePlugin_WantsLV2Presets 0 | |||
#endif // __PLUGINCHARACTERISTICS_D4EFFF1A__ |
@@ -0,0 +1,156 @@ | |||
#include "MiscDsp.h" | |||
BiquadCoefficients::BiquadCoefficients() : b0 (1), b1 (0), b2 (0), a1 (0), a2 (0) {} | |||
void BiquadCoefficients::create (BiquadType::BiquadType type, double freq, double Q, double sampleRate) | |||
{ | |||
const double w0 = 2 * juce::double_Pi * freq / sampleRate; | |||
const double cosw0 = cos(w0); | |||
const double alpha = sin(w0) / (2 * Q); | |||
double a0 = 1; | |||
switch (type) | |||
{ | |||
case BiquadType::kBypass: | |||
b0 = 1; b1 = 0; b2 = 0; a1 = 0; a2 = 0; | |||
break; | |||
case BiquadType::kHighPass6: | |||
a1 = (tan(w0 / 2) - 1) / (tan(w0 / 2) + 1); | |||
b0 = (1 - a1) * 0.5; | |||
b1 = (a1 - 1) * 0.5; | |||
b2 = 0; | |||
a2 = 0; | |||
break; | |||
case BiquadType::kLowPass: | |||
a0 = 1 + alpha; | |||
b0 = (1 - cosw0) / 2; | |||
b1 = 1 - cosw0; | |||
b2 = (1 - cosw0) / 2; | |||
a1 = -2 * cosw0; | |||
a2 = 1 - alpha; | |||
break; | |||
case BiquadType::kBandPass: | |||
a0 = 1 + alpha; | |||
b0 = alpha; | |||
b1 = 0; | |||
b2 = -alpha; | |||
a1 = -2 * cosw0; | |||
a2 = 1 - alpha; | |||
break; | |||
default: | |||
jassertfalse; | |||
break; | |||
} | |||
b0 /= a0; | |||
b1 /= a0; | |||
b2 /= a0; | |||
a1 /= a0; | |||
a2 /= a0; | |||
} | |||
StaticBiquad::StaticBiquad() | |||
: sampleRate (44100) | |||
{ | |||
setFilter(BiquadType::kBypass, 1000, 0.7); | |||
clear(); | |||
} | |||
void StaticBiquad::clear() | |||
{ | |||
xl[0] = 0; xl[1] = 0; | |||
yl[0] = 0; yl[1] = 0; | |||
xr[0] = 0; xr[1] = 0; | |||
yr[0] = 0; yr[1] = 0; | |||
} | |||
void StaticBiquad::setSampleRate (double newSampleRate) | |||
{ | |||
sampleRate = newSampleRate; | |||
setFilter(curType, curFreq, curQ); | |||
} | |||
void StaticBiquad::setFilter (BiquadType::BiquadType type, double freq, double Q) | |||
{ | |||
curType = type; | |||
curFreq = freq; | |||
curQ = Q; | |||
coeff.create(curType, curFreq, curQ, sampleRate); | |||
} | |||
float StaticBiquad::process (float in) | |||
{ | |||
const double x = (double) in; | |||
double y = x*coeff.b0 + xl[0] * coeff.b1 + xl[1] * coeff.b2 - yl[0] * coeff.a1 - yl[1] * coeff.a2; | |||
if (y > -1e-8 && y < 1e-8) | |||
y = 0; | |||
xl[1] = xl[0]; xl[0] = x; | |||
yl[1] = yl[0]; yl[0] = y; | |||
return (float) y; | |||
} | |||
void StaticBiquad::processBlock (float* in, int numSamples) | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
in[i] = process(in[i]); | |||
} | |||
void StaticBiquad::processBlock (float* inL, float* inR, int numSamples) | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
{ | |||
const double xL = (double) inL[i]; | |||
const double xR = (double) inR[i]; | |||
double yL = xL*coeff.b0 + xl[0] * coeff.b1 + xl[1] * coeff.b2 - yl[0] * coeff.a1 - yl[1] * coeff.a2; | |||
double yR = xR*coeff.b0 + xr[0] * coeff.b1 + xr[1] * coeff.b2 - yr[0] * coeff.a1 - yr[1] * coeff.a2; | |||
if (yL > -1e-8 && yL < 1e-8) | |||
yL = 0; | |||
if (yR > -1e-8 && yR < 1e-8) | |||
yR = 0; | |||
xl[1] = xl[0]; xl[0] = xL; | |||
xr[1] = xr[0]; xr[0] = xR; | |||
yl[1] = yl[0]; yl[0] = yL; | |||
yr[1] = yr[0]; yr[0] = yR; | |||
inL[i] = (float) yL; | |||
inR[i] = (float) yR; | |||
} | |||
} | |||
float StaticBiquad::getLast (bool left) | |||
{ | |||
return float(left ? yl[0] : yr[0]); | |||
} | |||
SimpleNoiseGen::SimpleNoiseGen(float levelDb) | |||
: level (pow(10.f, 0.05f * levelDb)) | |||
{ | |||
} | |||
void SimpleNoiseGen::processBlock (float* chL, float* chR, int numSamples) | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
{ | |||
const float noise = (rnd.nextFloat() - 0.5f) * 2.f * level; | |||
chL[i] += noise; | |||
chR[i] += noise; | |||
} | |||
} | |||
void SimpleNoiseGen::processBlock (float* ch, int numSamples) | |||
{ | |||
for (int i = 0; i<numSamples; ++i) | |||
{ | |||
const float noise = (rnd.nextFloat() - 0.5f) * 2.f * level; | |||
ch[i] += noise; | |||
} | |||
} |
@@ -0,0 +1,82 @@ | |||
#ifndef DSPSTUFF_H_INCLUDED | |||
#define DSPSTUFF_H_INCLUDED | |||
#include "JuceHeader.h" | |||
namespace BiquadType | |||
{ | |||
enum BiquadType | |||
{ | |||
kBypass, | |||
kHighPass6, | |||
kLowPass, | |||
kBandPass, | |||
kNumFilters | |||
}; | |||
} | |||
struct BiquadCoefficients | |||
{ | |||
BiquadCoefficients(); | |||
void create (BiquadType::BiquadType type, double freq, double Q, double sampleRate); | |||
double b0; | |||
double b1; | |||
double b2; | |||
double a1; | |||
double a2; | |||
}; | |||
class StaticBiquad | |||
{ | |||
public: | |||
StaticBiquad(); | |||
void clear(); | |||
void setSampleRate (double newSampleRate); | |||
void setFilter (BiquadType::BiquadType type, double freq, double Q); | |||
float process (float in); | |||
void processBlock (float* in, int numSamples); | |||
void processBlock (float* inL, float* inR, int numSamples); | |||
float getLast (bool left); | |||
private: | |||
double sampleRate; | |||
BiquadType::BiquadType curType; | |||
double curFreq; | |||
double curQ; | |||
BiquadCoefficients coeff; | |||
double xl[2]; | |||
double yl[2]; | |||
double xr[2]; | |||
double yr[2]; | |||
}; | |||
class SimpleNoiseGen | |||
{ | |||
public: | |||
SimpleNoiseGen (float levelDb); | |||
void processBlock (float* chL, float* chR, int numSamples); | |||
void processBlock (float* ch, int numSamples); | |||
private: | |||
float level; | |||
juce::Random rnd; | |||
}; | |||
#endif // DSPSTUFF_H_INCLUDED |
@@ -0,0 +1,70 @@ | |||
#include "PluginProcessor.h" | |||
#include "PluginEditor.h" | |||
//============================================================================== | |||
ReFinedAudioProcessorEditor::ReFinedAudioProcessorEditor (ReFinedAudioProcessor& p) | |||
: AudioProcessorEditor (&p), processor (p), | |||
background (ImageCache::getFromMemory(BinaryData::background_png, BinaryData::background_pngSize)), | |||
redSlider (ImageCache::getFromMemory(BinaryData::red_png, BinaryData::red_pngSize), ImageCache::getFromMemory(BinaryData::vu_red_png, BinaryData::vu_red_pngSize), *p.parameters, "red"), | |||
greenSlider (ImageCache::getFromMemory(BinaryData::green_png, BinaryData::green_pngSize), ImageCache::getFromMemory (BinaryData::vu_green_png, BinaryData::vu_green_pngSize), *p.parameters, "green"), | |||
blueSlider (ImageCache::getFromMemory(BinaryData::blue_png, BinaryData::blue_pngSize), ImageCache::getFromMemory(BinaryData::vu_blue_png, BinaryData::vu_blue_pngSize), *p.parameters, "blue"), | |||
x2Button (*p.parameters, "x2"), | |||
visualisation (p.getDsp()) | |||
{ | |||
setLookAndFeel(refinedLookAndFeel); | |||
addAndMakeVisible(redSlider); | |||
addAndMakeVisible(greenSlider); | |||
addAndMakeVisible(blueSlider); | |||
addAndMakeVisible(x2Button); | |||
addAndMakeVisible(visualisation); | |||
setSize (450, 300); | |||
startTimer(100); | |||
} | |||
ReFinedAudioProcessorEditor::~ReFinedAudioProcessorEditor() | |||
{ | |||
} | |||
//============================================================================== | |||
void ReFinedAudioProcessorEditor::paint (Graphics& g) | |||
{ | |||
g.setColour(Colours::black); | |||
g.drawImageAt(background, 0, 0); | |||
} | |||
void ReFinedAudioProcessorEditor::resized() | |||
{ | |||
const int sliderWidth = 64; | |||
const int sliderHeight = 64; | |||
const int ySlider = 221; | |||
const int yButton = 7; | |||
const int xRed = 65; | |||
const int xGreen = 193; | |||
const int xBlue = 321; | |||
const int xButton = 209; | |||
const int buttonWidth = 32; | |||
const int buttonHeight = 16; | |||
redSlider.setBounds(xRed, ySlider, sliderWidth, sliderHeight); | |||
greenSlider.setBounds(xGreen, ySlider, sliderWidth, sliderHeight); | |||
blueSlider.setBounds(xBlue, ySlider, sliderWidth, sliderHeight); | |||
x2Button.setBounds(xButton, yButton, buttonWidth, buttonHeight); | |||
visualisation.setBounds(31, 35, 388, 150); | |||
} | |||
void ReFinedAudioProcessorEditor::timerCallback() | |||
{ | |||
const RefineDsp& dsp(processor.getDsp()); | |||
const float transient = dsp.getTransient(); | |||
const float nonTransient = dsp.getNonTransient(); | |||
const float level = dsp.getLevel(); | |||
redSlider.setVuValue(nonTransient); | |||
greenSlider.setVuValue(level); | |||
blueSlider.setVuValue(transient); | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef PLUGINEDITOR_H_INCLUDED | |||
#define PLUGINEDITOR_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "PluginProcessor.h" | |||
#include "ReFineLookAndFeel.h" | |||
#include "Visualisation.h" | |||
class ReFinedAudioProcessorEditor : public AudioProcessorEditor, public Timer | |||
{ | |||
public: | |||
ReFinedAudioProcessorEditor (ReFinedAudioProcessor&); | |||
~ReFinedAudioProcessorEditor(); | |||
void paint (Graphics&) override; | |||
void resized() override; | |||
void timerCallback() override; | |||
private: | |||
ReFinedAudioProcessor& processor; | |||
Image background; | |||
RefinedSlider redSlider; | |||
RefinedSlider greenSlider; | |||
RefinedSlider blueSlider; | |||
X2Button x2Button; | |||
Visualisation visualisation; | |||
SharedResourcePointer<RefineLookAndFeel> refinedLookAndFeel; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReFinedAudioProcessorEditor) | |||
}; | |||
#endif // PLUGINEDITOR_H_INCLUDED |
@@ -0,0 +1,126 @@ | |||
#include "PluginProcessor.h" | |||
#include "PluginEditor.h" | |||
#include "xmmintrin.h" | |||
ReFinedAudioProcessor::ReFinedAudioProcessor() | |||
{ | |||
parameters = new AudioProcessorValueTreeState(*this, nullptr); | |||
parameters->createAndAddParameter("red", "red", "", NormalisableRange<float>(0.f, 1.f), 0.f, [](float val) { return String(val, 2); }, [](const String& s) { return (float) s.getDoubleValue(); }); | |||
parameters->createAndAddParameter("green", "green", "", NormalisableRange<float>(0.f, 1.f), 0.f, [](float val) { return String(val, 2); }, [](const String& s) { return (float) s.getDoubleValue(); }); | |||
parameters->createAndAddParameter("blue", "blue", "", NormalisableRange<float>(0.f, 1.f), 0.f, [](float val) { return String(val, 2); }, [](const String& s) { return (float) s.getDoubleValue(); }); | |||
parameters->createAndAddParameter("x2", "x2", "", NormalisableRange<float>(0.f, 1.f), 0.f, [](float val) { return val < 0.5f ? "Off" : "On"; }, [](const String& s) { return s.trim() == "1" || s.trim().toLowerCase() == "on" ? 1.f : 0.f; }); | |||
} | |||
ReFinedAudioProcessor::~ReFinedAudioProcessor() | |||
{ | |||
} | |||
const String ReFinedAudioProcessor::getName() const | |||
{ | |||
return JucePlugin_Name; | |||
} | |||
bool ReFinedAudioProcessor::acceptsMidi() const | |||
{ | |||
return false; | |||
} | |||
bool ReFinedAudioProcessor::producesMidi() const | |||
{ | |||
return false; | |||
} | |||
double ReFinedAudioProcessor::getTailLengthSeconds() const | |||
{ | |||
return 0.0; | |||
} | |||
int ReFinedAudioProcessor::getNumPrograms() | |||
{ | |||
return 1; | |||
} | |||
int ReFinedAudioProcessor::getCurrentProgram() | |||
{ | |||
return 0; | |||
} | |||
void ReFinedAudioProcessor::setCurrentProgram (int /*index*/) | |||
{ | |||
} | |||
const String ReFinedAudioProcessor::getProgramName (int /*index*/) | |||
{ | |||
return String(); | |||
} | |||
void ReFinedAudioProcessor::changeProgramName (int /*index*/, const String& /*newName*/) | |||
{ | |||
} | |||
void ReFinedAudioProcessor::prepareToPlay (double newSampleRate, int /*samplesPerBlock*/) | |||
{ | |||
dsp.setSampleRate(newSampleRate); | |||
} | |||
void ReFinedAudioProcessor::releaseResources() | |||
{ | |||
} | |||
void ReFinedAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& /*midiMessages*/) | |||
{ | |||
{ | |||
const float low = parameters->getParameter("red")->getValue(); | |||
const float mid = parameters->getParameter("green")->getValue(); | |||
const float high = parameters->getParameter("blue")->getValue(); | |||
const float x2mode = parameters->getParameter("x2")->getValue(); | |||
dsp.setLow(0.9f * low + 0.05f * mid + 0.05f * high); | |||
dsp.setMid(0.9f * mid + 0.05f * high + 0.05f * low); | |||
dsp.setHigh(0.9f * high + 0.05f * low + 0.05f * mid); | |||
dsp.setX2Mode(x2mode > 0.5f); | |||
} | |||
const int numChannels = buffer.getNumChannels(); | |||
const int numSamples = buffer.getNumSamples(); | |||
float* chL = buffer.getWritePointer(0); | |||
float* chR = numChannels > 1 ? buffer.getWritePointer(1) : nullptr; | |||
dsp.processBlock(chL, chR, numSamples); | |||
} | |||
bool ReFinedAudioProcessor::hasEditor() const | |||
{ | |||
return true; | |||
} | |||
AudioProcessorEditor* ReFinedAudioProcessor::createEditor() | |||
{ | |||
return new ReFinedAudioProcessorEditor (*this); | |||
} | |||
void ReFinedAudioProcessor::getStateInformation (MemoryBlock& destData) | |||
{ | |||
XmlElement xml("REFINED"); | |||
for (int i = 0; i < getNumParameters(); ++i) | |||
xml.setAttribute(getParameterName(i), getParameter(i)); | |||
copyXmlToBinary(xml, destData); | |||
} | |||
void ReFinedAudioProcessor::setStateInformation (const void* data, int sizeInBytes) | |||
{ | |||
ScopedPointer<XmlElement> xml(getXmlFromBinary(data, sizeInBytes)); | |||
if (xml != nullptr) | |||
{ | |||
for (int i = 0; i < getNumParameters(); ++i) | |||
setParameterNotifyingHost(i, (float) xml->getDoubleAttribute(getParameterName(i), getParameter(i))); | |||
} | |||
} | |||
AudioProcessor* JUCE_CALLTYPE createPluginFilter() | |||
{ | |||
return new ReFinedAudioProcessor(); | |||
} |
@@ -0,0 +1,48 @@ | |||
#ifndef PLUGINPROCESSOR_H_INCLUDED | |||
#define PLUGINPROCESSOR_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "RefineDsp.h" | |||
class ReFinedAudioProcessor : public AudioProcessor | |||
{ | |||
public: | |||
ReFinedAudioProcessor(); | |||
~ReFinedAudioProcessor(); | |||
void prepareToPlay (double sampleRate, int samplesPerBlock) override; | |||
void releaseResources() override; | |||
void processBlock (AudioSampleBuffer&, MidiBuffer&) override; | |||
AudioProcessorEditor* createEditor() override; | |||
bool hasEditor() const override; | |||
//============================================================================== | |||
const String getName() const override; | |||
bool acceptsMidi() const override; | |||
bool producesMidi() const override; | |||
double getTailLengthSeconds() const override; | |||
int getNumPrograms() override; | |||
int getCurrentProgram() override; | |||
void setCurrentProgram (int index) override; | |||
const String getProgramName (int index) override; | |||
void changeProgramName (int index, const String& newName) override; | |||
void getStateInformation (MemoryBlock& destData) override; | |||
void setStateInformation (const void* data, int sizeInBytes) override; | |||
const RefineDsp& getDsp() const { return dsp; } | |||
ScopedPointer<AudioProcessorValueTreeState> parameters; | |||
private: | |||
RefineDsp dsp; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReFinedAudioProcessor) | |||
}; | |||
#endif // PLUGINPROCESSOR_H_INCLUDED |
@@ -0,0 +1,19 @@ | |||
#include "ReFineLookAndFeel.h" | |||
void RefineLookAndFeel::drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPosProportional, float /*rotaryStartAngle*/, float /*rotaryEndAngle*/, Slider& slider) | |||
{ | |||
if (RefinedSlider* s = dynamic_cast<RefinedSlider*> (&slider)) | |||
{ | |||
const Image& sliderImg = s->sliderImage; | |||
const Image& vuImg = s->vuImage; | |||
jassert(sliderImg.getWidth() == vuImg.getWidth() && sliderImg.getHeight() == vuImg.getHeight()); | |||
const int wSubImage = sliderImg.getWidth(); | |||
const int numImages = sliderImg.getHeight() / wSubImage; | |||
const int ySliderImage = wSubImage * jlimit(0, numImages - 1, static_cast<int> (sliderPosProportional * (numImages - 1) + 0.5f)); | |||
const int yVuImage = wSubImage * jlimit(0, numImages - 1, static_cast<int> (s->getVuValue() * (numImages - 1) + 0.5f)); | |||
g.drawImage(sliderImg, x, y, width, height, 0, ySliderImage, wSubImage, wSubImage, false); | |||
g.drawImage(vuImg, x, y, width, height, 0, yVuImage, wSubImage, wSubImage, false); | |||
} | |||
} |
@@ -0,0 +1,86 @@ | |||
#ifndef REFINELOOKANDFEEL_H_INCLUDED | |||
#define REFINELOOKANDFEEL_H_INCLUDED | |||
#include "JuceHeader.h" | |||
class RefinedSlider : public Slider | |||
{ | |||
public: | |||
RefinedSlider (Image slider, Image vu, AudioProcessorValueTreeState& state, const String& parameterID) | |||
: sliderImage (slider), vuImage (vu), vuValue (0.f) | |||
{ | |||
attachment = new AudioProcessorValueTreeState::SliderAttachment(state, parameterID, *this); | |||
setSliderStyle(Slider::RotaryVerticalDrag); | |||
setTextBoxStyle(Slider::NoTextBox, true, 0, 0); | |||
} | |||
virtual ~RefinedSlider() | |||
{ | |||
} | |||
void setVuValue (float newValue) | |||
{ | |||
if (newValue != vuValue) | |||
{ | |||
vuValue = newValue; | |||
repaint(); | |||
} | |||
} | |||
float getVuValue() const | |||
{ | |||
return vuValue; | |||
} | |||
const Image sliderImage; | |||
const Image vuImage; | |||
private: | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> attachment; | |||
float vuValue; | |||
JUCE_DECLARE_NON_COPYABLE(RefinedSlider) | |||
}; | |||
class X2Button : public Button | |||
{ | |||
public: | |||
X2Button (AudioProcessorValueTreeState& state, const String& parameterID) | |||
: Button ("X2Button"), | |||
image (ImageCache::getFromMemory(BinaryData::x2button_png, BinaryData::x2button_pngSize)) | |||
{ | |||
attachment = new AudioProcessorValueTreeState::ButtonAttachment(state, parameterID, *this); | |||
setClickingTogglesState(true); | |||
} | |||
void paintButton (Graphics& g, bool /*isMouseOverButton*/, bool isButtonDown) | |||
{ | |||
const bool state = getToggleState() || isButtonDown; | |||
const int iw = image.getWidth(); | |||
const int ih = image.getHeight(); | |||
g.drawImageAt(image.getClippedImage(juce::Rectangle<int> (0, state ? ih/2 : 0, iw, ih/2)), 0, 0); | |||
} | |||
private: | |||
ScopedPointer<AudioProcessorValueTreeState::ButtonAttachment> attachment; | |||
Image image; | |||
}; | |||
class RefineLookAndFeel : public LookAndFeel_V3 | |||
{ | |||
public: | |||
void drawRotarySlider (Graphics&, int x, int y, int width, int height, | |||
float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, | |||
Slider&) override; | |||
private: | |||
}; | |||
#endif // REFINELOOKANDFEEL_H_INCLUDED |
@@ -0,0 +1,298 @@ | |||
#include "RefineDsp.h" | |||
RefineDsp::RefineDsp() | |||
: sampleRate (0), | |||
gainLow (0.f), | |||
gainMid (0.f), | |||
gainHigh (0.f), | |||
noise (-140.f), | |||
auxSize (4096), | |||
auxLowL (auxSize), | |||
auxHighL (auxSize), | |||
auxLowR (auxSize), | |||
auxHighR (auxSize), | |||
rms300 (static_cast<int> (44100*0.3)), | |||
rms5 (static_cast<int> (44100*0.005)), | |||
rms (400, 100, 0.025), | |||
colors (1), | |||
delayL (512), | |||
delayR (512) | |||
{ | |||
setSampleRate(44100); | |||
clear(); | |||
} | |||
void RefineDsp::clear() | |||
{ | |||
lowL.clear(); | |||
lowR.clear(); | |||
highL.clear(); | |||
highR.clear(); | |||
levelSlow.clear(); | |||
levelMid.clear(); | |||
levelFast.clear(); | |||
levelHold.clear(); | |||
rms300.clear(); | |||
rms5.clear(); | |||
transient = 0; | |||
nonTransient = 0; | |||
level = 0; | |||
} | |||
void RefineDsp::setBlockSize (int newBlockSize) | |||
{ | |||
if (newBlockSize > auxSize) | |||
{ | |||
auxSize = newBlockSize; | |||
auxLowL.realloc(auxSize); | |||
auxLowR.realloc(auxSize); | |||
auxHighL.realloc(auxSize); | |||
auxHighR.realloc(auxSize); | |||
} | |||
} | |||
void RefineDsp::setSampleRate (double newSampleRate) | |||
{ | |||
if (newSampleRate != sampleRate) | |||
{ | |||
sampleRate = newSampleRate; | |||
rms.setSampleRate(sampleRate); | |||
colors.setSize(rms.getDataLength()+1); | |||
lowL.setFilter(BiquadType::kBandPass, 80.f, 0.5f); | |||
lowR.setFilter(BiquadType::kBandPass, 80.f, 0.5f); | |||
highL.setFilter(BiquadType::kHighPass6, 10e3f, 0.5f); | |||
highR.setFilter(BiquadType::kHighPass6, 10e3f, 0.5f); | |||
levelSlow.setFilter(BiquadType::kLowPass, 10, sqrt(0.5)); | |||
levelMid.setFilter(BiquadType::kLowPass, 50, sqrt(0.5)); | |||
levelFast.setFilter(BiquadType::kLowPass, 200, sqrt(0.5)); | |||
rms300.setSize(int(0.3*sampleRate)); | |||
rms5.setSize(int(0.02*sampleRate)); | |||
trSmooth.setSampleRate(sampleRate, 0.3); | |||
delayL.setSize(512 * int(sampleRate / 44100)); | |||
delayR.setSize(512 * int(sampleRate / 44100)); | |||
} | |||
} | |||
void RefineDsp::setLow (float gain) | |||
{ | |||
gainLow = gain; | |||
} | |||
void RefineDsp::setMid (float gain) | |||
{ | |||
gainMid = gain; | |||
} | |||
void RefineDsp::setHigh (float gain) | |||
{ | |||
gainHigh = gain; | |||
} | |||
void RefineDsp::setX2Mode (bool enabled) | |||
{ | |||
x2Mode = enabled; | |||
} | |||
void RefineDsp::processBlock (float* dataL, float* dataR, int numSamples) | |||
{ | |||
const float x2gain = x2Mode ? 1.9475f : 1.f; | |||
if (dataR != nullptr) | |||
noise.processBlock(dataL, dataR, numSamples); | |||
else | |||
noise.processBlock(dataL, numSamples); | |||
delayL.processBlock(dataL, numSamples); | |||
if (dataR != nullptr) | |||
delayR.processBlock(dataR, numSamples); | |||
{ | |||
ScopedLock lock(processLock); | |||
if (dataR != nullptr) | |||
levelHold.processBlock(dataL, dataR, numSamples); | |||
else | |||
levelHold.processBlock(dataL, dataL, numSamples); | |||
const float release = 1.f - 1.f / float(sampleRate * 0.001 * 100); | |||
if (dataR == nullptr) | |||
dataR = dataL; | |||
for (int i=0; i<numSamples; ++i) | |||
{ | |||
if (std::abs(dataL[i]) < 1e-8f) | |||
dataL[i] = 0; | |||
if (std::abs(dataR[i]) < 1e-8f) | |||
dataR[i] = 0; | |||
float x = 0.5f * (dataL[i] + dataR[i]); | |||
if (x > -1e-4f && x < 1e-4f) | |||
x = 0; | |||
double r300 = rms300.process(x); | |||
if (r300 != 0. && r300 < 1e-8f) | |||
{ | |||
rms300.clear(); | |||
r300 = 0; | |||
} | |||
double r5 = rms5.process(x); | |||
if (r5 != 0. && r5 < 1e-8f) | |||
{ | |||
r5 = 0; | |||
rms5.clear(); | |||
} | |||
const float y = r300 > 0 ? (float) jlimit(0., 1., (r5 / r300 - 1.)) : 0.f; | |||
trSmooth.processAttack(y*y); | |||
const float lHold = levelHold.getValue(); | |||
const float r300s = (float) sqrt(jmax(0., r300)); | |||
level = lHold > 0 ? jlimit(0.f, 1.f, 1.4142f * r300s / lHold) : 0; | |||
jassert(level >= 0 && level < 1e8); | |||
const float newTrans = jmin(1.f, trSmooth.getValue()) * jmin<float>(1.f, sqrt(level) * 20.f); | |||
transient = newTrans > transient ? newTrans : transient * release; | |||
const float newNonTransient = (1 - pow(newTrans, 0.2f)) * jmin<float>(1.f, sqrt(level) * 20.f); | |||
nonTransient = newNonTransient > nonTransient ? newNonTransient : nonTransient * release; | |||
colorProc.add(transient, nonTransient, level); | |||
if (rms.process(dataL[i], dataR[i])) | |||
{ | |||
colors.push(colorProc.getColor()); | |||
} | |||
} | |||
} | |||
if (gainLow > 0) | |||
{ | |||
if (dataL != nullptr) | |||
{ | |||
memcpy(auxLowL, dataL, numSamples * sizeof(float)); | |||
lowL.processBlock(auxLowL, numSamples); | |||
} | |||
if (dataR != nullptr) | |||
{ | |||
memcpy(auxLowR, dataR, numSamples * sizeof(float)); | |||
lowR.processBlock(auxLowR, numSamples); | |||
} | |||
} | |||
if (gainHigh > 0) | |||
{ | |||
if (dataL != nullptr) | |||
{ | |||
memcpy(auxHighL, dataL, numSamples * sizeof(float)); | |||
highL.processBlock(auxHighL, numSamples); | |||
} | |||
if (dataR != nullptr) | |||
{ | |||
memcpy(auxHighR, dataR, numSamples * sizeof(float)); | |||
highR.processBlock(auxHighR, numSamples); | |||
} | |||
} | |||
if (gainMid > 0) | |||
{ | |||
const float gain = pow(10.f, x2gain * 1.25f * sqrt(gainMid) / 20.f); | |||
if (dataL != nullptr) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
dataL[i] *= gain; | |||
} | |||
if (dataR != nullptr) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
dataR[i] *= gain; | |||
} | |||
} | |||
if (gainLow > 0) | |||
{ | |||
const float gain = pow(10.f, x2gain * 0.95f * sqrt(gainLow) / 20.f) - 1.f; | |||
if (dataL != nullptr) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
dataL[i] += auxLowL[i] * gain; | |||
} | |||
if (dataR != nullptr) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
dataR[i] += auxLowR[i] * gain; | |||
} | |||
} | |||
if (gainHigh > 0) | |||
{ | |||
const float gain = pow(10.f, x2gain * 1.28f * sqrt(gainHigh) / 20.f) - 1.f; | |||
if (dataL != nullptr) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
dataL[i] += auxHighL[i] * gain; | |||
} | |||
if (dataR != nullptr) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
dataR[i] += auxHighR[i] * gain; | |||
} | |||
} | |||
} | |||
float RefineDsp::getTransient() const | |||
{ | |||
return transient; | |||
} | |||
float RefineDsp::getNonTransient() const | |||
{ | |||
return nonTransient; | |||
} | |||
float RefineDsp::getLevel() const | |||
{ | |||
return level; | |||
} | |||
bool RefineDsp::getRmsData (Array<float>& d, Array<uint32>& c) const | |||
{ | |||
{ | |||
ScopedTryLock lock(processLock); | |||
if (! lock.isLocked() || c.size() >= colors.getSize()) | |||
return false; | |||
for (int i=0; i<c.size(); ++i) | |||
c.getReference(i) = colors[i]; | |||
} | |||
return rms.getData(d); | |||
} | |||
@@ -0,0 +1,185 @@ | |||
#ifndef SEPDSP_H_INCLUDED | |||
#define SEPDSP_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "Analyzer.h" | |||
#include "Buffers.h" | |||
#include "MiscDsp.h" | |||
class LevelHold | |||
{ | |||
public: | |||
LevelHold() | |||
{ | |||
setSampleRate(44100); | |||
clear(); | |||
} | |||
void setSampleRate (double sampleRate, double releaseTime = 1) | |||
{ | |||
attack = 1 / (0.25 * releaseTime * sampleRate); | |||
release = 1 - 1 / (releaseTime * sampleRate); | |||
} | |||
void clear() | |||
{ | |||
value = 0; | |||
} | |||
void process (double x) | |||
{ | |||
value *= release; | |||
if (value < 1e-8) | |||
value = 0; | |||
if (x > value) | |||
value = x; | |||
} | |||
void processAttack (double x) | |||
{ | |||
value *= release; | |||
if (value < 1e-8) | |||
value = 0; | |||
if (x > value) | |||
value = value + (x - value) * attack; | |||
} | |||
void processBlock (const float* inL, const float* inR, int numSamples) | |||
{ | |||
for (int i=0; i<numSamples; ++i) | |||
{ | |||
const double x = 0.5 * (inL[i] + inR[i]); | |||
process(x); | |||
} | |||
} | |||
float getValue() | |||
{ | |||
return (float) value; | |||
} | |||
private: | |||
double attack; | |||
double release; | |||
double value; | |||
}; | |||
class RefineDsp | |||
{ | |||
public: | |||
RefineDsp(); | |||
void clear(); | |||
void setBlockSize(int newBlockSize); | |||
void setSampleRate(double newSampleRate); | |||
void setLow(float gain); | |||
void setMid(float gain); | |||
void setHigh(float gain); | |||
void setX2Mode(bool enabled); | |||
void processBlock(float* dataL, float* dataR, int numSamples); | |||
float getTransient() const; | |||
float getNonTransient() const; | |||
float getLevel() const; | |||
bool getRmsData(Array<float>& d, Array<uint32>& c) const; | |||
private: | |||
struct ColorProc | |||
{ | |||
ColorProc() | |||
: transient (0), nonTransient (0), level (0) | |||
{ | |||
} | |||
void add (float tr, float ntr, float l) | |||
{ | |||
transient += tr; | |||
nonTransient += ntr; | |||
level += l; | |||
} | |||
juce::uint32 getColor() | |||
{ | |||
juce::uint32 ret = 0; | |||
const float sum = transient + nonTransient + level; | |||
if (sum > 0) | |||
{ | |||
const juce::uint32 tr = juce::uint8(255 * transient / sum); | |||
const juce::uint32 ntr = juce::uint8(255 * nonTransient / sum); | |||
const juce::uint32 l = juce::uint8(255 * level / sum); | |||
ret = (tr << 16) | (ntr << 8) | l; | |||
} | |||
transient = 0; | |||
nonTransient = 0; | |||
level = 0; | |||
return ret; | |||
} | |||
float transient; | |||
float nonTransient; | |||
float level; | |||
}; | |||
double sampleRate; | |||
float gainLow; | |||
float gainMid; | |||
float gainHigh; | |||
bool x2Mode; | |||
float transient; | |||
float nonTransient; | |||
float level; | |||
SimpleNoiseGen noise; | |||
int auxSize; | |||
juce::HeapBlock<float> auxLowL; | |||
juce::HeapBlock<float> auxHighL; | |||
juce::HeapBlock<float> auxLowR; | |||
juce::HeapBlock<float> auxHighR; | |||
StaticBiquad lowL; | |||
StaticBiquad lowR; | |||
StaticBiquad highL; | |||
StaticBiquad highR; | |||
StaticBiquad levelSlow; | |||
StaticBiquad levelMid; | |||
StaticBiquad levelFast; | |||
RmsBuffer rms300; | |||
RmsBuffer rms5; | |||
LevelHold trSmooth; | |||
LevelHold levelHold; | |||
RmsEnvelope rms; | |||
CircularBuffer<juce::uint32> colors; | |||
ColorProc colorProc; | |||
CircularBuffer<float> delayL; | |||
CircularBuffer<float> delayR; | |||
juce::CriticalSection processLock; | |||
}; | |||
#endif // SEPDSP_H_INCLUDED |
@@ -0,0 +1,127 @@ | |||
#include "Visualisation.h" | |||
Visualisation::Visualisation (const RefineDsp& dsp) | |||
: sepDsp (dsp), | |||
minMag (-30.f), | |||
maxMag (0.f) | |||
{ | |||
setOpaque(true); | |||
startTimer(100); | |||
} | |||
Visualisation::~Visualisation() | |||
{ | |||
} | |||
void Visualisation::paint (Graphics& g) | |||
{ | |||
g.fillAll(Colour(0xFF101010)); | |||
const int w = jmin(rmsData.size(), colourData.size(), getWidth()); | |||
const float yBottom = getHeight() - 1.f; | |||
for (int i = 0; i<w; ++i) | |||
{ | |||
const int idx = w - 1 - i; | |||
const float mag = jlimit(0.f, 1.f, rmsData.getReference(idx)); | |||
const float y = magToY(mag); | |||
g.setColour(Colour(colourData.getReference(idx))); | |||
g.drawVerticalLine(i, y, yBottom); | |||
} | |||
} | |||
void Visualisation::resized() | |||
{ | |||
const int w = getWidth(); | |||
rmsData.clearQuick(); | |||
colourData.clearQuick(); | |||
rmsData.ensureStorageAllocated(w); | |||
colourData.ensureStorageAllocated(w); | |||
for (int i = 0; i < w; ++i) | |||
{ | |||
rmsData.add(0.f); | |||
colourData.add(0); | |||
} | |||
} | |||
void Visualisation::timerCallback() | |||
{ | |||
if (sepDsp.getRmsData(rmsData, colourData)) | |||
{ | |||
float newMin = 10; | |||
float newMax = 0; | |||
float newMean = 0; | |||
int meanSize = 0; | |||
const Colour red(191, 0, 48); | |||
const Colour green(0, 191, 44); | |||
const Colour blue(0, 96, 191); | |||
for (int i = 0; i<rmsData.size(); ++i) | |||
{ | |||
if (rmsData.getReference(i) < 1e-8f) | |||
{ | |||
rmsData.getReference(i) = -80.f; | |||
continue; | |||
} | |||
newMean += rmsData.getReference(i); | |||
++meanSize; | |||
const float curMean = newMean / meanSize; | |||
const float x = rmsData.getReference(i); | |||
const float weight = 0.2f + 0.8f * sqrt(1.f - i / (rmsData.size() - 1.f)); | |||
const float xMin = curMean - weight * (curMean - x); | |||
const float xMax = curMean + weight * (x - curMean); | |||
newMin = jmin(newMin, xMin); | |||
newMax = jmax(newMax, xMax); | |||
rmsData.getReference(i) = 10 * log10(rmsData.getReference(i)); | |||
{ | |||
const float mulBlue = ((colourData.getReference(i) >> 16) & 0xFF) / 255.f; | |||
const float mulGreen = ((colourData.getReference(i) >> 8) & 0xFF) / 255.f; | |||
const float mulRed = ((colourData.getReference(i)) & 0xFF) / 255.f; | |||
uint8_t r = uint8_t(blue.getRed()*mulBlue + green.getRed()*mulGreen + red.getRed()*mulRed); | |||
uint8_t g = uint8_t(blue.getGreen()*mulBlue + green.getGreen()*mulGreen + red.getGreen()*mulRed); | |||
uint8_t b = uint8_t(blue.getBlue()*mulBlue + green.getBlue()*mulGreen + red.getBlue()*mulRed); | |||
r = 64 + r / 2; | |||
g = 64 + g / 2; | |||
b = 64 + b / 2; | |||
colourData.getReference(i) = (255 << 24) | (r << 16) | (g << 8) | (b << 0); | |||
} | |||
} | |||
if (newMin < 10 && newMin > 0) | |||
{ | |||
newMean /= meanSize; | |||
const float dist = jmax(0.01f, jmin(newMax - newMean, newMean - newMin)); | |||
newMin = jmax(1e-8f, jmin(0.9f*newMin, newMean - dist)); | |||
newMax = jmax(1.1f*newMax, newMean + dist); | |||
const float minDb = 10 * std::log10(newMin); | |||
const float maxDb = 10 * std::log10(newMax); | |||
minMag = minMag*0.95f + 0.05f*minDb; | |||
maxMag = maxMag*0.95f + 0.05f*maxDb; | |||
} | |||
for (int i = 0; i<rmsData.size(); ++i) | |||
rmsData.getReference(i) = jlimit(0.f, 1.f, float((rmsData.getReference(i) - minMag) / (maxMag - minMag))); | |||
repaint(); | |||
} | |||
} | |||
float Visualisation::magToY (float mag) const | |||
{ | |||
return 1.f + (getHeight() - 2.f) * (1.f - mag); | |||
} |
@@ -0,0 +1,31 @@ | |||
#ifndef VISUALISATION_H_INCLUDED | |||
#define VISUALISATION_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "RefineDsp.h" | |||
class Visualisation : public Component, public Timer | |||
{ | |||
public: | |||
Visualisation (const RefineDsp& dsp); | |||
~Visualisation(); | |||
void paint (Graphics& g); | |||
void resized(); | |||
void timerCallback(); | |||
float magToY (float mag) const; | |||
private: | |||
const RefineDsp& sepDsp; | |||
Array<float> rmsData; | |||
Array<uint32> colourData; | |||
float minMag; | |||
float maxMag; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Visualisation) | |||
}; | |||
#endif // VISUALISATION_H_INCLUDED |
@@ -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 "ffft/Array.hpp" | |||
#endif // ffft_Array_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,99 @@ | |||
/***************************************************************************** | |||
Array.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_Array_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of Array code header. | |||
#endif | |||
#define ffft_Array_CURRENT_CODEHEADER | |||
#if ! defined (ffft_Array_CODEHEADER_INCLUDED) | |||
#define ffft_Array_CODEHEADER_INCLUDED | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include <cassert> | |||
namespace ffft | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <class T, long LEN> | |||
Array <T, LEN>::Array () | |||
{ | |||
// Nothing | |||
} | |||
template <class T, long LEN> | |||
const typename Array <T, LEN>::DataType & Array <T, LEN>::operator [] (long pos) const | |||
{ | |||
assert (pos >= 0); | |||
assert (pos < LEN); | |||
return (_data_arr [pos]); | |||
} | |||
template <class T, long LEN> | |||
typename Array <T, LEN>::DataType & Array <T, LEN>::operator [] (long pos) | |||
{ | |||
assert (pos >= 0); | |||
assert (pos < LEN); | |||
return (_data_arr [pos]); | |||
} | |||
template <class T, long LEN> | |||
long Array <T, LEN>::size () | |||
{ | |||
return (LEN); | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
} // namespace ffft | |||
#endif // ffft_Array_CODEHEADER_INCLUDED | |||
#undef ffft_Array_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,101 @@ | |||
/***************************************************************************** | |||
DynArray.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_DynArray_HEADER_INCLUDED) | |||
#define ffft_DynArray_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
namespace ffft | |||
{ | |||
template <class T> | |||
class DynArray | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
public: | |||
typedef T DataType; | |||
DynArray (); | |||
explicit DynArray (long size); | |||
~DynArray (); | |||
inline long size () const; | |||
inline void resize (long size); | |||
inline const DataType & | |||
operator [] (long pos) const; | |||
inline DataType & | |||
operator [] (long pos); | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
protected: | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
DataType * _data_ptr; | |||
long _len; | |||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
DynArray (const DynArray &other); | |||
DynArray & operator = (const DynArray &other); | |||
bool operator == (const DynArray &other); | |||
bool operator != (const DynArray &other); | |||
}; // class DynArray | |||
} // namespace ffft | |||
#include "DynArray.hpp" | |||
#endif // ffft_DynArray_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,144 @@ | |||
/***************************************************************************** | |||
DynArray.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_DynArray_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of DynArray code header. | |||
#endif | |||
#define ffft_DynArray_CURRENT_CODEHEADER | |||
#if ! defined (ffft_DynArray_CODEHEADER_INCLUDED) | |||
#define ffft_DynArray_CODEHEADER_INCLUDED | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include <cassert> | |||
namespace ffft | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <class T> | |||
DynArray <T>::DynArray () | |||
: _data_ptr (0) | |||
, _len (0) | |||
{ | |||
// Nothing | |||
} | |||
template <class T> | |||
DynArray <T>::DynArray (long size) | |||
: _data_ptr (0) | |||
, _len (0) | |||
{ | |||
assert (size >= 0); | |||
if (size > 0) | |||
{ | |||
_data_ptr = new DataType [size]; | |||
_len = size; | |||
} | |||
} | |||
template <class T> | |||
DynArray <T>::~DynArray () | |||
{ | |||
delete [] _data_ptr; | |||
_data_ptr = 0; | |||
_len = 0; | |||
} | |||
template <class T> | |||
long DynArray <T>::size () const | |||
{ | |||
return (_len); | |||
} | |||
template <class T> | |||
void DynArray <T>::resize (long size) | |||
{ | |||
assert (size >= 0); | |||
if (size > 0) | |||
{ | |||
DataType * old_data_ptr = _data_ptr; | |||
DataType * tmp_data_ptr = new DataType [size]; | |||
_data_ptr = tmp_data_ptr; | |||
_len = size; | |||
delete [] old_data_ptr; | |||
} | |||
} | |||
template <class T> | |||
const typename DynArray <T>::DataType & DynArray <T>::operator [] (long pos) const | |||
{ | |||
assert (pos >= 0); | |||
assert (pos < _len); | |||
return (_data_ptr [pos]); | |||
} | |||
template <class T> | |||
typename DynArray <T>::DataType & DynArray <T>::operator [] (long pos) | |||
{ | |||
assert (pos >= 0); | |||
assert (pos < _len); | |||
return (_data_ptr [pos]); | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
} // namespace ffft | |||
#endif // ffft_DynArray_CODEHEADER_INCLUDED | |||
#undef ffft_DynArray_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,146 @@ | |||
/***************************************************************************** | |||
FFTReal.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_FFTReal_HEADER_INCLUDED) | |||
#define ffft_FFTReal_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "def.h" | |||
#include "DynArray.h" | |||
#include "OscSinCos.h" | |||
namespace ffft | |||
{ | |||
template <class DT> | |||
class FFTReal | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
public: | |||
enum { MAX_BIT_DEPTH = 30 }; // So length can be represented as long int | |||
typedef DT DataType; | |||
explicit FFTReal (long length); | |||
virtual ~FFTReal () {} | |||
long get_length () const; | |||
void do_fft (DataType f [], const DataType x []) const; | |||
void do_ifft (const DataType f [], DataType x []) const; | |||
void rescale (DataType x []) const; | |||
DataType * use_buffer () const; | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
protected: | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
// Over this bit depth, we use direct calculation for sin/cos | |||
enum { TRIGO_BD_LIMIT = 12 }; | |||
typedef OscSinCos <DataType> OscType; | |||
void init_br_lut (); | |||
void init_trigo_lut (); | |||
void init_trigo_osc (); | |||
ffft_FORCEINLINE const long * | |||
get_br_ptr () const; | |||
ffft_FORCEINLINE const DataType * | |||
get_trigo_ptr (int level) const; | |||
ffft_FORCEINLINE long | |||
get_trigo_level_index (int level) const; | |||
inline void compute_fft_general (DataType f [], const DataType x []) const; | |||
inline void compute_direct_pass_1_2 (DataType df [], const DataType x []) const; | |||
inline void compute_direct_pass_3 (DataType df [], const DataType sf []) const; | |||
inline void compute_direct_pass_n (DataType df [], const DataType sf [], int pass) const; | |||
inline void compute_direct_pass_n_lut (DataType df [], const DataType sf [], int pass) const; | |||
inline void compute_direct_pass_n_osc (DataType df [], const DataType sf [], int pass) const; | |||
inline void compute_ifft_general (const DataType f [], DataType x []) const; | |||
inline void compute_inverse_pass_n (DataType df [], const DataType sf [], int pass) const; | |||
inline void compute_inverse_pass_n_osc (DataType df [], const DataType sf [], int pass) const; | |||
inline void compute_inverse_pass_n_lut (DataType df [], const DataType sf [], int pass) const; | |||
inline void compute_inverse_pass_3 (DataType df [], const DataType sf []) const; | |||
inline void compute_inverse_pass_1_2 (DataType x [], const DataType sf []) const; | |||
const long _length; | |||
const int _nbr_bits; | |||
DynArray <long> | |||
_br_lut; | |||
DynArray <DataType> | |||
_trigo_lut; | |||
mutable DynArray <DataType> | |||
_buffer; | |||
mutable DynArray <OscType> | |||
_trigo_osc; | |||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
FFTReal (); | |||
FFTReal (const FFTReal &other); | |||
FFTReal & operator = (const FFTReal &other); | |||
bool operator == (const FFTReal &other); | |||
bool operator != (const FFTReal &other); | |||
}; // class FFTReal | |||
} // namespace ffft | |||
#include "FFTReal.hpp" | |||
#endif // ffft_FFTReal_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,934 @@ | |||
/***************************************************************************** | |||
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 (); | |||
} | |||
/* | |||
void FFTReal <DT>::initFFT(long length) | |||
{ | |||
_length = length; | |||
_nbr_bits = FFTReal_get_next_pow2 (length); | |||
_buffer = length; | |||
assert (FFTReal_is_pow2 (length)); | |||
assert (_nbr_bits <= MAX_BIT_DEPTH); | |||
init_br_lut (); | |||
init_trigo_lut (); | |||
init_trigo_osc (); | |||
} | |||
*/ | |||
/* | |||
============================================================================== | |||
Name: get_length | |||
Description: | |||
Returns the number of points processed by this FFT object. | |||
Returns: The number of points, power of 2, > 0. | |||
Throws: Nothing | |||
============================================================================== | |||
*/ | |||
template <class DT> | |||
long FFTReal <DT>::get_length () const | |||
{ | |||
return (_length); | |||
} | |||
/* | |||
============================================================================== | |||
Name: do_fft | |||
Description: | |||
Compute the FFT of the array. | |||
Input parameters: | |||
- x: pointer on the source array (time). | |||
Output parameters: | |||
- f: pointer on the destination array (frequencies). | |||
f [0...length(x)/2] = real values, | |||
f [length(x)/2+1...length(x)-1] = negative imaginary values of | |||
coefficents 1...length(x)/2-1. | |||
Throws: Nothing | |||
============================================================================== | |||
*/ | |||
template <class DT> | |||
void FFTReal <DT>::do_fft (DataType f [], const DataType x []) const | |||
{ | |||
assert (f != 0); | |||
assert (f != use_buffer ()); | |||
assert (x != 0); | |||
assert (x != use_buffer ()); | |||
assert (x != f); | |||
// General case | |||
if (_nbr_bits > 2) | |||
{ | |||
compute_fft_general (f, x); | |||
} | |||
// 4-point FFT | |||
else if (_nbr_bits == 2) | |||
{ | |||
f [1] = x [0] - x [2]; | |||
f [3] = x [1] - x [3]; | |||
const DataType b_0 = x [0] + x [2]; | |||
const DataType b_2 = x [1] + x [3]; | |||
f [0] = b_0 + b_2; | |||
f [2] = b_0 - b_2; | |||
} | |||
// 2-point FFT | |||
else if (_nbr_bits == 1) | |||
{ | |||
f [0] = x [0] + x [1]; | |||
f [1] = x [0] - x [1]; | |||
} | |||
// 1-point FFT | |||
else | |||
{ | |||
f [0] = x [0]; | |||
} | |||
} | |||
/* | |||
============================================================================== | |||
Name: do_ifft | |||
Description: | |||
Compute the inverse FFT of the array. Note that data must be post-scaled: | |||
IFFT (FFT (x)) = x * length (x). | |||
Input parameters: | |||
- f: pointer on the source array (frequencies). | |||
f [0...length(x)/2] = real values | |||
f [length(x)/2+1...length(x)-1] = negative imaginary values of | |||
coefficents 1...length(x)/2-1. | |||
Output parameters: | |||
- x: pointer on the destination array (time). | |||
Throws: Nothing | |||
============================================================================== | |||
*/ | |||
template <class DT> | |||
void FFTReal <DT>::do_ifft (const DataType f [], DataType x []) const | |||
{ | |||
assert (f != 0); | |||
assert (f != use_buffer ()); | |||
assert (x != 0); | |||
assert (x != use_buffer ()); | |||
assert (x != f); | |||
// General case | |||
if (_nbr_bits > 2) | |||
{ | |||
compute_ifft_general (f, x); | |||
} | |||
// 4-point IFFT | |||
else if (_nbr_bits == 2) | |||
{ | |||
const DataType b_0 = f [0] + f [2]; | |||
const DataType b_2 = f [0] - f [2]; | |||
x [0] = b_0 + f [1] * 2; | |||
x [2] = b_0 - f [1] * 2; | |||
x [1] = b_2 + f [3] * 2; | |||
x [3] = b_2 - f [3] * 2; | |||
} | |||
// 2-point IFFT | |||
else if (_nbr_bits == 1) | |||
{ | |||
x [0] = f [0] + f [1]; | |||
x [1] = f [0] - f [1]; | |||
} | |||
// 1-point IFFT | |||
else | |||
{ | |||
x [0] = f [0]; | |||
} | |||
} | |||
/* | |||
============================================================================== | |||
Name: rescale | |||
Description: | |||
Scale an array by divide each element by its length. This function should | |||
be called after FFT + IFFT. | |||
Input parameters: | |||
- x: pointer on array to rescale (time or frequency). | |||
Throws: Nothing | |||
============================================================================== | |||
*/ | |||
template <class DT> | |||
void FFTReal <DT>::rescale (DataType x []) const | |||
{ | |||
const DataType mul = DataType (1.0 / _length); | |||
if (_length < 4) | |||
{ | |||
long i = _length - 1; | |||
do | |||
{ | |||
x [i] *= mul; | |||
--i; | |||
} | |||
while (i >= 0); | |||
} | |||
else | |||
{ | |||
assert ((_length & 3) == 0); | |||
// Could be optimized with SIMD instruction sets (needs alignment check) | |||
long i = _length - 4; | |||
do | |||
{ | |||
x [i + 0] *= mul; | |||
x [i + 1] *= mul; | |||
x [i + 2] *= mul; | |||
x [i + 3] *= mul; | |||
i -= 4; | |||
} | |||
while (i >= 0); | |||
} | |||
} | |||
/* | |||
============================================================================== | |||
Name: use_buffer | |||
Description: | |||
Access the internal buffer, whose length is the FFT one. | |||
Buffer content will be erased at each do_fft() / do_ifft() call! | |||
This buffer cannot be used as: | |||
- source for FFT or IFFT done with this object | |||
- destination for FFT or IFFT done with this object | |||
Returns: | |||
Buffer start address | |||
Throws: Nothing | |||
============================================================================== | |||
*/ | |||
template <class DT> | |||
typename FFTReal <DT>::DataType * FFTReal <DT>::use_buffer () const | |||
{ | |||
return (&_buffer [0]); | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <class DT> | |||
void FFTReal <DT>::init_br_lut () | |||
{ | |||
const long length = 1L << _nbr_bits; | |||
_br_lut.resize (length); | |||
_br_lut [0] = 0; | |||
long br_index = 0; | |||
for (long cnt = 1; cnt < length; ++cnt) | |||
{ | |||
// ++br_index (bit reversed) | |||
long bit = length >> 1; | |||
while (((br_index ^= bit) & bit) == 0) | |||
{ | |||
bit >>= 1; | |||
} | |||
_br_lut [cnt] = br_index; | |||
} | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::init_trigo_lut () | |||
{ | |||
using namespace std; | |||
if (_nbr_bits > 3) | |||
{ | |||
const long total_len = (1L << (_nbr_bits - 1)) - 4; | |||
_trigo_lut.resize (total_len); | |||
for (int level = 3; level < _nbr_bits; ++level) | |||
{ | |||
const long level_len = 1L << (level - 1); | |||
DataType * const level_ptr = | |||
&_trigo_lut [get_trigo_level_index (level)]; | |||
const double mul = PI / (level_len << 1); | |||
for (long i = 0; i < level_len; ++ i) | |||
{ | |||
level_ptr [i] = static_cast <DataType> (cos (i * mul)); | |||
} | |||
} | |||
} | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::init_trigo_osc () | |||
{ | |||
const int nbr_osc = _nbr_bits - TRIGO_BD_LIMIT; | |||
if (nbr_osc > 0) | |||
{ | |||
_trigo_osc.resize (nbr_osc); | |||
for (int osc_cnt = 0; osc_cnt < nbr_osc; ++osc_cnt) | |||
{ | |||
OscType & osc = _trigo_osc [osc_cnt]; | |||
const long len = 1L << (TRIGO_BD_LIMIT + osc_cnt); | |||
const double mul = (0.5 * PI) / len; | |||
osc.set_step (mul); | |||
} | |||
} | |||
} | |||
template <class DT> | |||
const long * FFTReal <DT>::get_br_ptr () const | |||
{ | |||
return (&_br_lut [0]); | |||
} | |||
template <class DT> | |||
const typename FFTReal <DT>::DataType * FFTReal <DT>::get_trigo_ptr (int level) const | |||
{ | |||
assert (level >= 3); | |||
return (&_trigo_lut [get_trigo_level_index (level)]); | |||
} | |||
template <class DT> | |||
long FFTReal <DT>::get_trigo_level_index (int level) const | |||
{ | |||
assert (level >= 3); | |||
return ((1L << (level - 1)) - 4); | |||
} | |||
// Transform in several passes | |||
template <class DT> | |||
void FFTReal <DT>::compute_fft_general (DataType f [], const DataType x []) const | |||
{ | |||
assert (f != 0); | |||
assert (f != use_buffer ()); | |||
assert (x != 0); | |||
assert (x != use_buffer ()); | |||
assert (x != f); | |||
DataType * sf; | |||
DataType * df; | |||
if ((_nbr_bits & 1) != 0) | |||
{ | |||
df = use_buffer (); | |||
sf = f; | |||
} | |||
else | |||
{ | |||
df = f; | |||
sf = use_buffer (); | |||
} | |||
compute_direct_pass_1_2 (df, x); | |||
compute_direct_pass_3 (sf, df); | |||
for (int pass = 3; pass < _nbr_bits; ++ pass) | |||
{ | |||
compute_direct_pass_n (df, sf, pass); | |||
DataType * const temp_ptr = df; | |||
df = sf; | |||
sf = temp_ptr; | |||
} | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_direct_pass_1_2 (DataType df [], const DataType x []) const | |||
{ | |||
assert (df != 0); | |||
assert (x != 0); | |||
assert (df != x); | |||
const long * const bit_rev_lut_ptr = get_br_ptr (); | |||
long coef_index = 0; | |||
do | |||
{ | |||
const long rev_index_0 = bit_rev_lut_ptr [coef_index]; | |||
const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1]; | |||
const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2]; | |||
const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3]; | |||
DataType * const df2 = df + coef_index; | |||
df2 [1] = x [rev_index_0] - x [rev_index_1]; | |||
df2 [3] = x [rev_index_2] - x [rev_index_3]; | |||
const DataType sf_0 = x [rev_index_0] + x [rev_index_1]; | |||
const DataType sf_2 = x [rev_index_2] + x [rev_index_3]; | |||
df2 [0] = sf_0 + sf_2; | |||
df2 [2] = sf_0 - sf_2; | |||
coef_index += 4; | |||
} | |||
while (coef_index < _length); | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_direct_pass_3 (DataType df [], const DataType sf []) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
const DataType sqrt2_2 = DataType (SQRT2 * 0.5); | |||
long coef_index = 0; | |||
do | |||
{ | |||
DataType v; | |||
df [coef_index] = sf [coef_index] + sf [coef_index + 4]; | |||
df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4]; | |||
df [coef_index + 2] = sf [coef_index + 2]; | |||
df [coef_index + 6] = sf [coef_index + 6]; | |||
v = (sf [coef_index + 5] - sf [coef_index + 7]) * sqrt2_2; | |||
df [coef_index + 1] = sf [coef_index + 1] + v; | |||
df [coef_index + 3] = sf [coef_index + 1] - v; | |||
v = (sf [coef_index + 5] + sf [coef_index + 7]) * sqrt2_2; | |||
df [coef_index + 5] = v + sf [coef_index + 3]; | |||
df [coef_index + 7] = v - sf [coef_index + 3]; | |||
coef_index += 8; | |||
} | |||
while (coef_index < _length); | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_direct_pass_n (DataType df [], const DataType sf [], int pass) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
assert (pass >= 3); | |||
assert (pass < _nbr_bits); | |||
if (pass <= TRIGO_BD_LIMIT) | |||
{ | |||
compute_direct_pass_n_lut (df, sf, pass); | |||
} | |||
else | |||
{ | |||
compute_direct_pass_n_osc (df, sf, pass); | |||
} | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_direct_pass_n_lut (DataType df [], const DataType sf [], int pass) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
assert (pass >= 3); | |||
assert (pass < _nbr_bits); | |||
const long nbr_coef = 1 << pass; | |||
const long h_nbr_coef = nbr_coef >> 1; | |||
const long d_nbr_coef = nbr_coef << 1; | |||
long coef_index = 0; | |||
const DataType * const cos_ptr = get_trigo_ptr (pass); | |||
do | |||
{ | |||
const DataType * const sf1r = sf + coef_index; | |||
const DataType * const sf2r = sf1r + nbr_coef; | |||
DataType * const dfr = df + coef_index; | |||
DataType * const dfi = dfr + nbr_coef; | |||
// Extreme coefficients are always real | |||
dfr [0] = sf1r [0] + sf2r [0]; | |||
dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] = | |||
dfr [h_nbr_coef] = sf1r [h_nbr_coef]; | |||
dfi [h_nbr_coef] = sf2r [h_nbr_coef]; | |||
// Others are conjugate complex numbers | |||
const DataType * const sf1i = sf1r + h_nbr_coef; | |||
const DataType * const sf2i = sf1i + nbr_coef; | |||
for (long i = 1; i < h_nbr_coef; ++ i) | |||
{ | |||
const DataType c = cos_ptr [i]; // cos (i*PI/nbr_coef); | |||
const DataType s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); | |||
DataType v; | |||
v = sf2r [i] * c - sf2i [i] * s; | |||
dfr [i] = sf1r [i] + v; | |||
dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] = | |||
v = sf2r [i] * s + sf2i [i] * c; | |||
dfi [i] = v + sf1i [i]; | |||
dfi [nbr_coef - i] = v - sf1i [i]; | |||
} | |||
coef_index += d_nbr_coef; | |||
} | |||
while (coef_index < _length); | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_direct_pass_n_osc (DataType df [], const DataType sf [], int pass) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
assert (pass > TRIGO_BD_LIMIT); | |||
assert (pass < _nbr_bits); | |||
const long nbr_coef = 1 << pass; | |||
const long h_nbr_coef = nbr_coef >> 1; | |||
const long d_nbr_coef = nbr_coef << 1; | |||
long coef_index = 0; | |||
OscType & osc = _trigo_osc [pass - (TRIGO_BD_LIMIT + 1)]; | |||
do | |||
{ | |||
const DataType * const sf1r = sf + coef_index; | |||
const DataType * const sf2r = sf1r + nbr_coef; | |||
DataType * const dfr = df + coef_index; | |||
DataType * const dfi = dfr + nbr_coef; | |||
osc.clear_buffers (); | |||
// Extreme coefficients are always real | |||
dfr [0] = sf1r [0] + sf2r [0]; | |||
dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] = | |||
dfr [h_nbr_coef] = sf1r [h_nbr_coef]; | |||
dfi [h_nbr_coef] = sf2r [h_nbr_coef]; | |||
// Others are conjugate complex numbers | |||
const DataType * const sf1i = sf1r + h_nbr_coef; | |||
const DataType * const sf2i = sf1i + nbr_coef; | |||
for (long i = 1; i < h_nbr_coef; ++ i) | |||
{ | |||
osc.step (); | |||
const DataType c = osc.get_cos (); | |||
const DataType s = osc.get_sin (); | |||
DataType v; | |||
v = sf2r [i] * c - sf2i [i] * s; | |||
dfr [i] = sf1r [i] + v; | |||
dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] = | |||
v = sf2r [i] * s + sf2i [i] * c; | |||
dfi [i] = v + sf1i [i]; | |||
dfi [nbr_coef - i] = v - sf1i [i]; | |||
} | |||
coef_index += d_nbr_coef; | |||
} | |||
while (coef_index < _length); | |||
} | |||
// Transform in several pass | |||
template <class DT> | |||
void FFTReal <DT>::compute_ifft_general (const DataType f [], DataType x []) const | |||
{ | |||
assert (f != 0); | |||
assert (f != use_buffer ()); | |||
assert (x != 0); | |||
assert (x != use_buffer ()); | |||
assert (x != f); | |||
DataType * sf = const_cast <DataType *> (f); | |||
DataType * df; | |||
DataType * df_temp; | |||
if (_nbr_bits & 1) | |||
{ | |||
df = use_buffer (); | |||
df_temp = x; | |||
} | |||
else | |||
{ | |||
df = x; | |||
df_temp = use_buffer (); | |||
} | |||
for (int pass = _nbr_bits - 1; pass >= 3; -- pass) | |||
{ | |||
compute_inverse_pass_n (df, sf, pass); | |||
if (pass < _nbr_bits - 1) | |||
{ | |||
DataType * const temp_ptr = df; | |||
df = sf; | |||
sf = temp_ptr; | |||
} | |||
else | |||
{ | |||
sf = df; | |||
df = df_temp; | |||
} | |||
} | |||
compute_inverse_pass_3 (df, sf); | |||
compute_inverse_pass_1_2 (x, df); | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_inverse_pass_n (DataType df [], const DataType sf [], int pass) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
assert (pass >= 3); | |||
assert (pass < _nbr_bits); | |||
if (pass <= TRIGO_BD_LIMIT) | |||
{ | |||
compute_inverse_pass_n_lut (df, sf, pass); | |||
} | |||
else | |||
{ | |||
compute_inverse_pass_n_osc (df, sf, pass); | |||
} | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_inverse_pass_n_lut (DataType df [], const DataType sf [], int pass) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
assert (pass >= 3); | |||
assert (pass < _nbr_bits); | |||
const long nbr_coef = 1 << pass; | |||
const long h_nbr_coef = nbr_coef >> 1; | |||
const long d_nbr_coef = nbr_coef << 1; | |||
long coef_index = 0; | |||
const DataType * const cos_ptr = get_trigo_ptr (pass); | |||
do | |||
{ | |||
const DataType * const sfr = sf + coef_index; | |||
const DataType * const sfi = sfr + nbr_coef; | |||
DataType * const df1r = df + coef_index; | |||
DataType * const df2r = df1r + nbr_coef; | |||
// Extreme coefficients are always real | |||
df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef] | |||
df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef] | |||
df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2; | |||
df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2; | |||
// Others are conjugate complex numbers | |||
DataType * const df1i = df1r + h_nbr_coef; | |||
DataType * const df2i = df1i + nbr_coef; | |||
for (long i = 1; i < h_nbr_coef; ++ i) | |||
{ | |||
df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i] | |||
df1i [i] = sfi [i] - sfi [nbr_coef - i]; | |||
const DataType c = cos_ptr [i]; // cos (i*PI/nbr_coef); | |||
const DataType s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); | |||
const DataType vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i] | |||
const DataType vi = sfi [i] + sfi [nbr_coef - i]; | |||
df2r [i] = vr * c + vi * s; | |||
df2i [i] = vi * c - vr * s; | |||
} | |||
coef_index += d_nbr_coef; | |||
} | |||
while (coef_index < _length); | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_inverse_pass_n_osc (DataType df [], const DataType sf [], int pass) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
assert (pass > TRIGO_BD_LIMIT); | |||
assert (pass < _nbr_bits); | |||
const long nbr_coef = 1 << pass; | |||
const long h_nbr_coef = nbr_coef >> 1; | |||
const long d_nbr_coef = nbr_coef << 1; | |||
long coef_index = 0; | |||
OscType & osc = _trigo_osc [pass - (TRIGO_BD_LIMIT + 1)]; | |||
do | |||
{ | |||
const DataType * const sfr = sf + coef_index; | |||
const DataType * const sfi = sfr + nbr_coef; | |||
DataType * const df1r = df + coef_index; | |||
DataType * const df2r = df1r + nbr_coef; | |||
osc.clear_buffers (); | |||
// Extreme coefficients are always real | |||
df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef] | |||
df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef] | |||
df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2; | |||
df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2; | |||
// Others are conjugate complex numbers | |||
DataType * const df1i = df1r + h_nbr_coef; | |||
DataType * const df2i = df1i + nbr_coef; | |||
for (long i = 1; i < h_nbr_coef; ++ i) | |||
{ | |||
df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i] | |||
df1i [i] = sfi [i] - sfi [nbr_coef - i]; | |||
osc.step (); | |||
const DataType c = osc.get_cos (); | |||
const DataType s = osc.get_sin (); | |||
const DataType vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i] | |||
const DataType vi = sfi [i] + sfi [nbr_coef - i]; | |||
df2r [i] = vr * c + vi * s; | |||
df2i [i] = vi * c - vr * s; | |||
} | |||
coef_index += d_nbr_coef; | |||
} | |||
while (coef_index < _length); | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_inverse_pass_3 (DataType df [], const DataType sf []) const | |||
{ | |||
assert (df != 0); | |||
assert (sf != 0); | |||
assert (df != sf); | |||
const DataType sqrt2_2 = DataType (SQRT2 * 0.5); | |||
long coef_index = 0; | |||
do | |||
{ | |||
df [coef_index] = sf [coef_index] + sf [coef_index + 4]; | |||
df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4]; | |||
df [coef_index + 2] = sf [coef_index + 2] * 2; | |||
df [coef_index + 6] = sf [coef_index + 6] * 2; | |||
df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3]; | |||
df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7]; | |||
const DataType vr = sf [coef_index + 1] - sf [coef_index + 3]; | |||
const DataType vi = sf [coef_index + 5] + sf [coef_index + 7]; | |||
df [coef_index + 5] = (vr + vi) * sqrt2_2; | |||
df [coef_index + 7] = (vi - vr) * sqrt2_2; | |||
coef_index += 8; | |||
} | |||
while (coef_index < _length); | |||
} | |||
template <class DT> | |||
void FFTReal <DT>::compute_inverse_pass_1_2 (DataType x [], const DataType sf []) const | |||
{ | |||
assert (x != 0); | |||
assert (sf != 0); | |||
assert (x != sf); | |||
const long * bit_rev_lut_ptr = get_br_ptr (); | |||
const DataType * sf2 = sf; | |||
long coef_index = 0; | |||
do | |||
{ | |||
{ | |||
const DataType b_0 = sf2 [0] + sf2 [2]; | |||
const DataType b_2 = sf2 [0] - sf2 [2]; | |||
const DataType b_1 = sf2 [1] * 2; | |||
const DataType b_3 = sf2 [3] * 2; | |||
x [bit_rev_lut_ptr [0]] = b_0 + b_1; | |||
x [bit_rev_lut_ptr [1]] = b_0 - b_1; | |||
x [bit_rev_lut_ptr [2]] = b_2 + b_3; | |||
x [bit_rev_lut_ptr [3]] = b_2 - b_3; | |||
} | |||
{ | |||
const DataType b_0 = sf2 [4] + sf2 [6]; | |||
const DataType b_2 = sf2 [4] - sf2 [6]; | |||
const DataType b_1 = sf2 [5] * 2; | |||
const DataType b_3 = sf2 [7] * 2; | |||
x [bit_rev_lut_ptr [4]] = b_0 + b_1; | |||
x [bit_rev_lut_ptr [5]] = b_0 - b_1; | |||
x [bit_rev_lut_ptr [6]] = b_2 + b_3; | |||
x [bit_rev_lut_ptr [7]] = b_2 - b_3; | |||
} | |||
sf2 += 8; | |||
coef_index += 8; | |||
bit_rev_lut_ptr += 8; | |||
} | |||
while (coef_index < _length); | |||
} | |||
} // namespace ffft | |||
#endif // ffft_FFTReal_CODEHEADER_INCLUDED | |||
#undef ffft_FFTReal_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,131 @@ | |||
/***************************************************************************** | |||
FFTRealFixLen.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_FFTRealFixLen_HEADER_INCLUDED) | |||
#define ffft_FFTRealFixLen_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/Array.h" | |||
#include "ffft/DynArray.h" | |||
#include "ffft/FFTRealFixLenParam.h" | |||
#include "ffft/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 "ffft/FFTRealFixLen.hpp" | |||
#endif // ffft_FFTRealFixLen_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,323 @@ | |||
/***************************************************************************** | |||
FFTRealFixLen.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_FFTRealFixLen_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of FFTRealFixLen code header. | |||
#endif | |||
#define ffft_FFTRealFixLen_CURRENT_CODEHEADER | |||
#if ! defined (ffft_FFTRealFixLen_CODEHEADER_INCLUDED) | |||
#define ffft_FFTRealFixLen_CODEHEADER_INCLUDED | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/def.h" | |||
#include "ffft/FFTRealPassDirect.h" | |||
#include "ffft/FFTRealPassInverse.h" | |||
#include "ffft/FFTRealSelect.h" | |||
#include <cassert> | |||
#include <cmath> | |||
namespace std { } | |||
namespace ffft | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <int LL2> | |||
FFTRealFixLen <LL2>::FFTRealFixLen () | |||
: _buffer (FFT_LEN) | |||
, _br_data (BR_ARR_SIZE) | |||
, _trigo_data (TRIGO_TABLE_ARR_SIZE) | |||
, _trigo_osc () | |||
{ | |||
build_br_lut (); | |||
build_trigo_lut (); | |||
build_trigo_osc (); | |||
} | |||
template <int LL2> | |||
long FFTRealFixLen <LL2>::get_length () const | |||
{ | |||
return (FFT_LEN); | |||
} | |||
// General case | |||
template <int LL2> | |||
void FFTRealFixLen <LL2>::do_fft (DataType f [], const DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
assert (x != f); | |||
assert (FFT_LEN_L2 >= 3); | |||
// Do the transform in several passes | |||
const DataType * cos_ptr = &_trigo_data [0]; | |||
const long * br_ptr = &_br_data [0]; | |||
FFTRealPassDirect <FFT_LEN_L2 - 1>::process ( | |||
FFT_LEN, | |||
f, | |||
&_buffer [0], | |||
x, | |||
cos_ptr, | |||
TRIGO_TABLE_ARR_SIZE, | |||
br_ptr, | |||
&_trigo_osc [0] | |||
); | |||
} | |||
// 4-point FFT | |||
template <> | |||
inline void FFTRealFixLen <2>::do_fft (DataType f [], const DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
assert (x != f); | |||
f [1] = x [0] - x [2]; | |||
f [3] = x [1] - x [3]; | |||
const DataType b_0 = x [0] + x [2]; | |||
const DataType b_2 = x [1] + x [3]; | |||
f [0] = b_0 + b_2; | |||
f [2] = b_0 - b_2; | |||
} | |||
// 2-point FFT | |||
template <> | |||
inline void FFTRealFixLen <1>::do_fft (DataType f [], const DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
assert (x != f); | |||
f [0] = x [0] + x [1]; | |||
f [1] = x [0] - x [1]; | |||
} | |||
// 1-point FFT | |||
template <> | |||
inline void FFTRealFixLen <0>::do_fft (DataType f [], const DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
f [0] = x [0]; | |||
} | |||
// General case | |||
template <int LL2> | |||
void FFTRealFixLen <LL2>::do_ifft (const DataType f [], DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
assert (x != f); | |||
assert (FFT_LEN_L2 >= 3); | |||
// Do the transform in several passes | |||
DataType * s_ptr = | |||
FFTRealSelect <FFT_LEN_L2 & 1>::sel_bin (&_buffer [0], x); | |||
DataType * d_ptr = | |||
FFTRealSelect <FFT_LEN_L2 & 1>::sel_bin (x, &_buffer [0]); | |||
const DataType * cos_ptr = &_trigo_data [0]; | |||
const long * br_ptr = &_br_data [0]; | |||
FFTRealPassInverse <FFT_LEN_L2 - 1>::process ( | |||
FFT_LEN, | |||
d_ptr, | |||
s_ptr, | |||
f, | |||
cos_ptr, | |||
TRIGO_TABLE_ARR_SIZE, | |||
br_ptr, | |||
&_trigo_osc [0] | |||
); | |||
} | |||
// 4-point IFFT | |||
template <> | |||
inline void FFTRealFixLen <2>::do_ifft (const DataType f [], DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
assert (x != f); | |||
const DataType b_0 = f [0] + f [2]; | |||
const DataType b_2 = f [0] - f [2]; | |||
x [0] = b_0 + f [1] * 2; | |||
x [2] = b_0 - f [1] * 2; | |||
x [1] = b_2 + f [3] * 2; | |||
x [3] = b_2 - f [3] * 2; | |||
} | |||
// 2-point IFFT | |||
template <> | |||
inline void FFTRealFixLen <1>::do_ifft (const DataType f [], DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
assert (x != f); | |||
x [0] = f [0] + f [1]; | |||
x [1] = f [0] - f [1]; | |||
} | |||
// 1-point IFFT | |||
template <> | |||
inline void FFTRealFixLen <0>::do_ifft (const DataType f [], DataType x []) | |||
{ | |||
assert (f != 0); | |||
assert (x != 0); | |||
assert (x != f); | |||
x [0] = f [0]; | |||
} | |||
template <int LL2> | |||
void FFTRealFixLen <LL2>::rescale (DataType x []) const | |||
{ | |||
assert (x != 0); | |||
const DataType mul = DataType (1.0 / FFT_LEN); | |||
if (FFT_LEN < 4) | |||
{ | |||
long i = FFT_LEN - 1; | |||
do | |||
{ | |||
x [i] *= mul; | |||
--i; | |||
} | |||
while (i >= 0); | |||
} | |||
else | |||
{ | |||
assert ((FFT_LEN & 3) == 0); | |||
// Could be optimized with SIMD instruction sets (needs alignment check) | |||
long i = FFT_LEN - 4; | |||
do | |||
{ | |||
x [i + 0] *= mul; | |||
x [i + 1] *= mul; | |||
x [i + 2] *= mul; | |||
x [i + 3] *= mul; | |||
i -= 4; | |||
} | |||
while (i >= 0); | |||
} | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <int LL2> | |||
void FFTRealFixLen <LL2>::build_br_lut () | |||
{ | |||
_br_data [0] = 0; | |||
for (long cnt = 1; cnt < BR_ARR_SIZE; ++cnt) | |||
{ | |||
long index = cnt << 2; | |||
long br_index = 0; | |||
int bit_cnt = FFT_LEN_L2; | |||
do | |||
{ | |||
br_index <<= 1; | |||
br_index += (index & 1); | |||
index >>= 1; | |||
-- bit_cnt; | |||
} | |||
while (bit_cnt > 0); | |||
_br_data [cnt] = br_index; | |||
} | |||
} | |||
template <int LL2> | |||
void FFTRealFixLen <LL2>::build_trigo_lut () | |||
{ | |||
const double mul = (0.5 * PI) / TRIGO_TABLE_ARR_SIZE; | |||
for (long i = 0; i < TRIGO_TABLE_ARR_SIZE; ++ i) | |||
{ | |||
using namespace std; | |||
_trigo_data [i] = DataType (cos (i * mul)); | |||
} | |||
} | |||
template <int LL2> | |||
void FFTRealFixLen <LL2>::build_trigo_osc () | |||
{ | |||
for (int i = 0; i < NBR_TRIGO_OSC; ++i) | |||
{ | |||
OscType & osc = _trigo_osc [i]; | |||
const long len = static_cast <long> (TRIGO_TABLE_ARR_SIZE) << (i + 1); | |||
const double mul = (0.5 * PI) / len; | |||
osc.set_step (mul); | |||
} | |||
} | |||
} // namespace ffft | |||
#endif // ffft_FFTRealFixLen_CODEHEADER_INCLUDED | |||
#undef ffft_FFTRealFixLen_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,90 @@ | |||
/***************************************************************************** | |||
FFTRealFixLenParam.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_FFTRealFixLenParam_HEADER_INCLUDED) | |||
#define ffft_FFTRealFixLenParam_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
namespace ffft | |||
{ | |||
class FFTRealFixLenParam | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
public: | |||
// Over this bit depth, we use direct calculation for sin/cos | |||
enum { TRIGO_BD_LIMIT = 12 }; | |||
typedef float DataType; | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
protected: | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
FFTRealFixLenParam (); | |||
FFTRealFixLenParam (const FFTRealFixLenParam &other); | |||
FFTRealFixLenParam & | |||
operator = (const FFTRealFixLenParam &other); | |||
bool operator == (const FFTRealFixLenParam &other); | |||
bool operator != (const FFTRealFixLenParam &other); | |||
}; // class FFTRealFixLenParam | |||
} // namespace ffft | |||
//#include "ffft/FFTRealFixLenParam.hpp" | |||
#endif // ffft_FFTRealFixLenParam_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,96 @@ | |||
/***************************************************************************** | |||
FFTRealPassDirect.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_FFTRealPassDirect_HEADER_INCLUDED) | |||
#define ffft_FFTRealPassDirect_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/def.h" | |||
#include "ffft/FFTRealFixLenParam.h" | |||
#include "ffft/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 "ffft/FFTRealPassDirect.hpp" | |||
#endif // ffft_FFTRealPassDirect_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,205 @@ | |||
/***************************************************************************** | |||
FFTRealPassDirect.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_FFTRealPassDirect_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of FFTRealPassDirect code header. | |||
#endif | |||
#define ffft_FFTRealPassDirect_CURRENT_CODEHEADER | |||
#if ! defined (ffft_FFTRealPassDirect_CODEHEADER_INCLUDED) | |||
#define ffft_FFTRealPassDirect_CODEHEADER_INCLUDED | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/FFTRealUseTrigo.h" | |||
namespace ffft | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <> | |||
inline void FFTRealPassDirect <1>::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 []) | |||
{ | |||
// First and second pass at once | |||
const long qlen = len >> 2; | |||
long coef_index = 0; | |||
do | |||
{ | |||
// To do: unroll the loop (2x). | |||
const long ri_0 = br_ptr [coef_index >> 2]; | |||
const long ri_1 = ri_0 + 2 * qlen; // bit_rev_lut_ptr [coef_index + 1]; | |||
const long ri_2 = ri_0 + 1 * qlen; // bit_rev_lut_ptr [coef_index + 2]; | |||
const long ri_3 = ri_0 + 3 * qlen; // bit_rev_lut_ptr [coef_index + 3]; | |||
DataType * const df2 = dest_ptr + coef_index; | |||
df2 [1] = x_ptr [ri_0] - x_ptr [ri_1]; | |||
df2 [3] = x_ptr [ri_2] - x_ptr [ri_3]; | |||
const DataType sf_0 = x_ptr [ri_0] + x_ptr [ri_1]; | |||
const DataType sf_2 = x_ptr [ri_2] + x_ptr [ri_3]; | |||
df2 [0] = sf_0 + sf_2; | |||
df2 [2] = sf_0 - sf_2; | |||
coef_index += 4; | |||
} | |||
while (coef_index < len); | |||
} | |||
template <> | |||
inline void FFTRealPassDirect <2>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
{ | |||
// Executes "previous" passes first. Inverts source and destination buffers | |||
FFTRealPassDirect <1>::process ( | |||
len, | |||
src_ptr, | |||
dest_ptr, | |||
x_ptr, | |||
cos_ptr, | |||
cos_len, | |||
br_ptr, | |||
osc_list | |||
); | |||
// Third pass | |||
const DataType sqrt2_2 = DataType (SQRT2 * 0.5); | |||
long coef_index = 0; | |||
do | |||
{ | |||
dest_ptr [coef_index ] = src_ptr [coef_index] + src_ptr [coef_index + 4]; | |||
dest_ptr [coef_index + 4] = src_ptr [coef_index] - src_ptr [coef_index + 4]; | |||
dest_ptr [coef_index + 2] = src_ptr [coef_index + 2]; | |||
dest_ptr [coef_index + 6] = src_ptr [coef_index + 6]; | |||
DataType v; | |||
v = (src_ptr [coef_index + 5] - src_ptr [coef_index + 7]) * sqrt2_2; | |||
dest_ptr [coef_index + 1] = src_ptr [coef_index + 1] + v; | |||
dest_ptr [coef_index + 3] = src_ptr [coef_index + 1] - v; | |||
v = (src_ptr [coef_index + 5] + src_ptr [coef_index + 7]) * sqrt2_2; | |||
dest_ptr [coef_index + 5] = v + src_ptr [coef_index + 3]; | |||
dest_ptr [coef_index + 7] = v - src_ptr [coef_index + 3]; | |||
coef_index += 8; | |||
} | |||
while (coef_index < len); | |||
} | |||
template <int PASS> | |||
void FFTRealPassDirect <PASS>::process (long len, DataType dest_ptr [], DataType src_ptr [], const DataType x_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
{ | |||
// Executes "previous" passes first. Inverts source and destination buffers | |||
FFTRealPassDirect <PASS - 1>::process ( | |||
len, | |||
src_ptr, | |||
dest_ptr, | |||
x_ptr, | |||
cos_ptr, | |||
cos_len, | |||
br_ptr, | |||
osc_list | |||
); | |||
const long dist = 1L << (PASS - 1); | |||
const long c1_r = 0; | |||
const long c1_i = dist; | |||
const long c2_r = dist * 2; | |||
const long c2_i = dist * 3; | |||
const long cend = dist * 4; | |||
const long table_step = cos_len >> (PASS - 1); | |||
enum { TRIGO_OSC = PASS - FFTRealFixLenParam::TRIGO_BD_LIMIT }; | |||
enum { TRIGO_DIRECT = (TRIGO_OSC >= 0) ? 1 : 0 }; | |||
long coef_index = 0; | |||
do | |||
{ | |||
const DataType * const sf = src_ptr + coef_index; | |||
DataType * const df = dest_ptr + coef_index; | |||
// Extreme coefficients are always real | |||
df [c1_r] = sf [c1_r] + sf [c2_r]; | |||
df [c2_r] = sf [c1_r] - sf [c2_r]; | |||
df [c1_i] = sf [c1_i]; | |||
df [c2_i] = sf [c2_i]; | |||
FFTRealUseTrigo <TRIGO_DIRECT>::prepare (osc_list [TRIGO_OSC]); | |||
// Others are conjugate complex numbers | |||
for (long i = 1; i < dist; ++ i) | |||
{ | |||
DataType c; | |||
DataType s; | |||
FFTRealUseTrigo <TRIGO_DIRECT>::iterate ( | |||
osc_list [TRIGO_OSC], | |||
c, | |||
s, | |||
cos_ptr, | |||
i * table_step, | |||
(dist - i) * table_step | |||
); | |||
const DataType sf_r_i = sf [c1_r + i]; | |||
const DataType sf_i_i = sf [c1_i + i]; | |||
const DataType v1 = sf [c2_r + i] * c - sf [c2_i + i] * s; | |||
df [c1_r + i] = sf_r_i + v1; | |||
df [c2_r - i] = sf_r_i - v1; | |||
const DataType v2 = sf [c2_r + i] * s + sf [c2_i + i] * c; | |||
df [c2_r + i] = v2 + sf_i_i; | |||
df [cend - i] = v2 - sf_i_i; | |||
} | |||
coef_index += cend; | |||
} | |||
while (coef_index < len); | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
} // namespace ffft | |||
#endif // ffft_FFTRealPassDirect_CODEHEADER_INCLUDED | |||
#undef ffft_FFTRealPassDirect_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,101 @@ | |||
/***************************************************************************** | |||
FFTRealPassInverse.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_FFTRealPassInverse_HEADER_INCLUDED) | |||
#define ffft_FFTRealPassInverse_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/def.h" | |||
#include "ffft/FFTRealFixLenParam.h" | |||
#include "ffft/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 "ffft/FFTRealPassInverse.hpp" | |||
#endif // ffft_FFTRealPassInverse_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,230 @@ | |||
/***************************************************************************** | |||
FFTRealPassInverse.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_FFTRealPassInverse_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of FFTRealPassInverse code header. | |||
#endif | |||
#define ffft_FFTRealPassInverse_CURRENT_CODEHEADER | |||
#if ! defined (ffft_FFTRealPassInverse_CODEHEADER_INCLUDED) | |||
#define ffft_FFTRealPassInverse_CODEHEADER_INCLUDED | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/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 len, DataType dest_ptr [], DataType src_ptr [], const DataType cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
{ | |||
// 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 cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
{ | |||
// 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 cos_ptr [], long cos_len, const long br_ptr [], OscType osc_list []) | |||
{ | |||
// Penultimate and last pass at once | |||
const long qlen = len >> 2; | |||
long coef_index = 0; | |||
do | |||
{ | |||
const long ri_0 = br_ptr [coef_index >> 2]; | |||
const DataType b_0 = src_ptr [coef_index ] + src_ptr [coef_index + 2]; | |||
const DataType b_2 = src_ptr [coef_index ] - src_ptr [coef_index + 2]; | |||
const DataType b_1 = src_ptr [coef_index + 1] * 2; | |||
const DataType b_3 = src_ptr [coef_index + 3] * 2; | |||
dest_ptr [ri_0 ] = b_0 + b_1; | |||
dest_ptr [ri_0 + 2 * qlen] = b_0 - b_1; | |||
dest_ptr [ri_0 + 1 * qlen] = b_2 + b_3; | |||
dest_ptr [ri_0 + 3 * qlen] = b_2 - b_3; | |||
coef_index += 4; | |||
} | |||
while (coef_index < len); | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
} // namespace ffft | |||
#endif // ffft_FFTRealPassInverse_CODEHEADER_INCLUDED | |||
#undef ffft_FFTRealPassInverse_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,78 @@ | |||
/***************************************************************************** | |||
FFTRealSelect.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_FFTRealSelect_HEADER_INCLUDED) | |||
#define ffft_FFTRealSelect_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/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 "ffft/FFTRealSelect.hpp" | |||
#endif // ffft_FFTRealSelect_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,63 @@ | |||
/***************************************************************************** | |||
FFTRealSelect.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_FFTRealSelect_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of FFTRealSelect code header. | |||
#endif | |||
#define ffft_FFTRealSelect_CURRENT_CODEHEADER | |||
#if ! defined (ffft_FFTRealSelect_CODEHEADER_INCLUDED) | |||
#define ffft_FFTRealSelect_CODEHEADER_INCLUDED | |||
namespace ffft | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <int P> | |||
float * FFTRealSelect <P>::sel_bin (float *e_ptr, float *o_ptr) | |||
{ | |||
return (o_ptr); | |||
} | |||
template <> | |||
inline float * FFTRealSelect <0>::sel_bin (float *e_ptr, float *o_ptr) | |||
{ | |||
return (e_ptr); | |||
} | |||
} // namespace ffft | |||
#endif // ffft_FFTRealSelect_CODEHEADER_INCLUDED | |||
#undef ffft_FFTRealSelect_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,99 @@ | |||
/***************************************************************************** | |||
FFTRealUseTrigo.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_FFTRealUseTrigo_HEADER_INCLUDED) | |||
#define ffft_FFTRealUseTrigo_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/def.h" | |||
#include "ffft/FFTRealFixLenParam.h" | |||
#include "ffft/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 "ffft/FFTRealUseTrigo.hpp" | |||
#endif // ffft_FFTRealUseTrigo_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,92 @@ | |||
/***************************************************************************** | |||
FFTRealUseTrigo.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_FFTRealUseTrigo_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of FFTRealUseTrigo code header. | |||
#endif | |||
#define ffft_FFTRealUseTrigo_CURRENT_CODEHEADER | |||
#if ! defined (ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED) | |||
#define ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "ffft/OscSinCos.h" | |||
namespace ffft | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <int ALGO> | |||
void FFTRealUseTrigo <ALGO>::prepare (OscType &osc) | |||
{ | |||
osc.clear_buffers (); | |||
} | |||
template <> | |||
inline void FFTRealUseTrigo <0>::prepare (OscType &osc) | |||
{ | |||
// 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 &osc, DataType &c, DataType &s, const DataType cos_ptr [], long index_c, long index_s) | |||
{ | |||
c = cos_ptr [index_c]; | |||
s = cos_ptr [index_s]; | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
} // namespace ffft | |||
#endif // ffft_FFTRealUseTrigo_CODEHEADER_INCLUDED | |||
#undef ffft_FFTRealUseTrigo_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,107 @@ | |||
/***************************************************************************** | |||
OscSinCos.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_OscSinCos_HEADER_INCLUDED) | |||
#define ffft_OscSinCos_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include "def.h" | |||
namespace ffft | |||
{ | |||
template <class T> | |||
class OscSinCos | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
public: | |||
typedef T DataType; | |||
OscSinCos (); | |||
ffft_FORCEINLINE void | |||
set_step (double angle_rad); | |||
ffft_FORCEINLINE DataType | |||
get_cos () const; | |||
ffft_FORCEINLINE DataType | |||
get_sin () const; | |||
ffft_FORCEINLINE void | |||
step (); | |||
ffft_FORCEINLINE void | |||
clear_buffers (); | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
protected: | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
DataType _pos_cos; // Current phase expressed with sin and cos. [-1 ; 1] | |||
DataType _pos_sin; // - | |||
DataType _step_cos; // Phase increment per step, [-1 ; 1] | |||
DataType _step_sin; // - | |||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
private: | |||
OscSinCos (const OscSinCos &other); | |||
OscSinCos & operator = (const OscSinCos &other); | |||
bool operator == (const OscSinCos &other); | |||
bool operator != (const OscSinCos &other); | |||
}; // class OscSinCos | |||
} // namespace ffft | |||
#include "OscSinCos.hpp" | |||
#endif // ffft_OscSinCos_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,123 @@ | |||
/***************************************************************************** | |||
OscSinCos.hpp | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if defined (ffft_OscSinCos_CURRENT_CODEHEADER) | |||
#error Recursive inclusion of OscSinCos code header. | |||
#endif | |||
#define ffft_OscSinCos_CURRENT_CODEHEADER | |||
#if ! defined (ffft_OscSinCos_CODEHEADER_INCLUDED) | |||
#define ffft_OscSinCos_CODEHEADER_INCLUDED | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
#include <cmath> | |||
namespace std { } | |||
namespace ffft | |||
{ | |||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
template <class T> | |||
OscSinCos <T>::OscSinCos () | |||
: _pos_cos (1) | |||
, _pos_sin (0) | |||
, _step_cos (1) | |||
, _step_sin (0) | |||
{ | |||
// Nothing | |||
} | |||
template <class T> | |||
void OscSinCos <T>::set_step (double angle_rad) | |||
{ | |||
using namespace std; | |||
_step_cos = static_cast <DataType> (cos (angle_rad)); | |||
_step_sin = static_cast <DataType> (sin (angle_rad)); | |||
} | |||
template <class T> | |||
typename OscSinCos <T>::DataType OscSinCos <T>::get_cos () const | |||
{ | |||
return (_pos_cos); | |||
} | |||
template <class T> | |||
typename OscSinCos <T>::DataType OscSinCos <T>::get_sin () const | |||
{ | |||
return (_pos_sin); | |||
} | |||
template <class T> | |||
void OscSinCos <T>::step () | |||
{ | |||
const DataType old_cos = _pos_cos; | |||
const DataType old_sin = _pos_sin; | |||
_pos_cos = old_cos * _step_cos - old_sin * _step_sin; | |||
_pos_sin = old_cos * _step_sin + old_sin * _step_cos; | |||
} | |||
template <class T> | |||
void OscSinCos <T>::clear_buffers () | |||
{ | |||
_pos_cos = static_cast <DataType> (1); | |||
_pos_sin = static_cast <DataType> (0); | |||
} | |||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
} // namespace ffft | |||
#endif // ffft_OscSinCos_CODEHEADER_INCLUDED | |||
#undef ffft_OscSinCos_CURRENT_CODEHEADER | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,60 @@ | |||
/***************************************************************************** | |||
def.h | |||
By Laurent de Soras | |||
--- Legal stuff --- | |||
This program is free software. It comes without any warranty, to | |||
the extent permitted by applicable law. You can redistribute it | |||
and/or modify it under the terms of the Do What The Fuck You Want | |||
To Public License, Version 2, as published by Sam Hocevar. See | |||
http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*Tab=3***********************************************************************/ | |||
#if ! defined (ffft_def_HEADER_INCLUDED) | |||
#define ffft_def_HEADER_INCLUDED | |||
#if defined (_MSC_VER) | |||
#pragma once | |||
#pragma warning (4 : 4250) // "Inherits via dominance." | |||
#endif | |||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | |||
namespace ffft | |||
{ | |||
const double PI = 3.1415926535897932384626433832795; | |||
const double SQRT2 = 1.41421356237309514547462185873883; | |||
#if defined (_MSC_VER) | |||
#define ffft_FORCEINLINE __forceinline | |||
#else | |||
#define ffft_FORCEINLINE inline | |||
#endif | |||
} // namespace ffft | |||
#endif // ffft_def_HEADER_INCLUDED | |||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ |
@@ -0,0 +1,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 | |||