Browse Source

Add StereoSourceSeparation

tags/2018-04-16
falkTX 10 years ago
parent
commit
62a0ff4996
17 changed files with 2944 additions and 0 deletions
  1. +4
    -0
      ports/Makefile
  2. +17
    -0
      ports/stereosourceseparation/LV2/premake.lua
  3. +17
    -0
      ports/stereosourceseparation/VST/premake.lua
  4. +524
    -0
      ports/stereosourceseparation/source/ADRess.cpp
  5. +101
    -0
      ports/stereosourceseparation/source/ADRess.h
  6. +27
    -0
      ports/stereosourceseparation/source/JuceHeader.h
  7. +134
    -0
      ports/stereosourceseparation/source/JucePluginCharacteristics.h
  8. +385
    -0
      ports/stereosourceseparation/source/PluginEditor.cpp
  9. +77
    -0
      ports/stereosourceseparation/source/PluginEditor.h
  10. +349
    -0
      ports/stereosourceseparation/source/PluginProcessor.cpp
  11. +109
    -0
      ports/stereosourceseparation/source/PluginProcessor.h
  12. +164
    -0
      ports/stereosourceseparation/source/kiss_fft/_kiss_fft_guts.h
  13. +408
    -0
      ports/stereosourceseparation/source/kiss_fft/kiss_fft.c
  14. +124
    -0
      ports/stereosourceseparation/source/kiss_fft/kiss_fft.h
  15. +159
    -0
      ports/stereosourceseparation/source/kiss_fft/kiss_fftr.c
  16. +46
    -0
      ports/stereosourceseparation/source/kiss_fft/kiss_fftr.h
  17. +299
    -0
      ports/stereosourceseparation/source/kiss_fft/kissfft.hh

+ 4
- 0
ports/Makefile View File

@@ -40,6 +40,7 @@ lv2: libs
$(MAKE) -C luftikus/LV2
$(MAKE) -C obxd/LV2
$(MAKE) -C pitchedDelay/LV2
$(MAKE) -C stereosourceseparation/LV2
$(MAKE) -C tal-dub-3/LV2
$(MAKE) -C tal-filter/LV2
$(MAKE) -C tal-filter-2/LV2
@@ -77,6 +78,7 @@ vst: libs
$(MAKE) -C luftikus/VST
$(MAKE) -C obxd/VST
$(MAKE) -C pitchedDelay/VST
$(MAKE) -C stereosourceseparation/VST
$(MAKE) -C tal-dub-3/VST
$(MAKE) -C tal-filter/VST
$(MAKE) -C tal-filter-2/VST
@@ -114,6 +116,7 @@ clean:
$(MAKE) clean -C luftikus/LV2
$(MAKE) clean -C obxd/LV2
$(MAKE) clean -C pitchedDelay/LV2
$(MAKE) clean -C stereosourceseparation/LV2
$(MAKE) clean -C tal-dub-3/LV2
$(MAKE) clean -C tal-filter/LV2
$(MAKE) clean -C tal-filter-2/LV2
@@ -147,6 +150,7 @@ clean:
$(MAKE) clean -C luftikus/VST
$(MAKE) clean -C obxd/VST
$(MAKE) clean -C pitchedDelay/VST
$(MAKE) clean -C stereosourceseparation/VST
$(MAKE) clean -C tal-dub-3/VST
$(MAKE) clean -C tal-filter/VST
$(MAKE) clean -C tal-filter-2/VST


+ 17
- 0
ports/stereosourceseparation/LV2/premake.lua View File

@@ -0,0 +1,17 @@
dofile("../../../scripts/make-project.lua")
package = make_juce_lv2_project("StereoSourceSeparation")
package.files = {
matchfiles (
"../source/*.cpp",
"../source/kiss_fft/*.c",
"../../../libs/juce-plugin/JucePluginMain.cpp"
)
}
package.includepaths = {
package.includepaths,
"../source/kiss_fft"
}

+ 17
- 0
ports/stereosourceseparation/VST/premake.lua View File

@@ -0,0 +1,17 @@
dofile("../../../scripts/make-project.lua")
package = make_juce_vst_project("StereoSourceSeparation")
package.files = {
matchfiles (
"../source/*.cpp",
"../source/kiss_fft/*.c",
"../../../libs/juce-plugin/JucePluginMain.cpp"
)
}
package.includepaths = {
package.includepaths,
"../source/kiss_fft"
}

+ 524
- 0
ports/stereosourceseparation/source/ADRess.cpp View File

@@ -0,0 +1,524 @@
//
// ADRess.cpp
// StereoSourceSeparation
//
// Created by Xinyuan Lai on 4/8/14.
//
//

#include "ADRess.h"

#define SCALE_DOWN_FACTOR 2

ADRess::ADRess(double sampleRate, int blockSize, int beta):sampleRate_(sampleRate),BLOCK_SIZE(blockSize),BETA(beta)
{
currStatus_ = kBypass;
d_ = BETA/2;
H_ = BETA/4;
LR_ = 2;
currFilter_ = kAllPass;
cutOffFrequency_ = 0.0;
cutOffBinIndex_ = 0;
// Hann window
windowBuffer_ = new float[BLOCK_SIZE];
for (int i = 0; i < BLOCK_SIZE; i++)
windowBuffer_[i] = 0.5*(1.0 - cos(2.0*M_PI*(float)i/(BLOCK_SIZE-1)) );
// all pass frequency mask
frequencyMask_ = new float[BLOCK_SIZE/2+1];
for (int i = 0; i < BLOCK_SIZE/2+1; i++)
frequencyMask_[i] = 1.0;
// initialise FFt
fwd_= kiss_fftr_alloc(BLOCK_SIZE,0,NULL,NULL);
inv_= kiss_fftr_alloc(BLOCK_SIZE,1,NULL,NULL);
leftSpectrum_ = new complex<float>[BLOCK_SIZE];
rightSpectrum_ = new complex<float>[BLOCK_SIZE];
leftMag_ = new float[BLOCK_SIZE/2+1];
rightMag_ = new float[BLOCK_SIZE/2+1];
leftPhase_ = new float[BLOCK_SIZE/2+1];
rightPhase_ = new float[BLOCK_SIZE/2+1];
resynMagL_ = new float[BLOCK_SIZE/2+1];
for (int i = 0; i<=BLOCK_SIZE/2; i++)
resynMagL_[i] = 0;
resynMagR_ = new float[BLOCK_SIZE/2+1];
for (int i = 0; i<=BLOCK_SIZE/2; i++)
resynMagR_[i] = 0;
minIndicesL_ = new int[BLOCK_SIZE/2+1];
minValuesL_ = new float[BLOCK_SIZE/2+1];
maxValuesL_ = new float[BLOCK_SIZE/2+1];
minIndicesR_ = new int[BLOCK_SIZE/2+1];
minValuesR_ = new float[BLOCK_SIZE/2+1];
maxValuesR_ = new float[BLOCK_SIZE/2+1];
azimuthL_ = new float*[BLOCK_SIZE/2+1];
for (int n = 0; n<BLOCK_SIZE/2+1; n++)
azimuthL_[n] = new float[BETA+1];
azimuthR_ = new float*[BLOCK_SIZE/2+1];
for (int n = 0; n<BLOCK_SIZE/2+1; n++)
azimuthR_[n] = new float[BETA+1];
}



ADRess::~ADRess()
{
if (resynMagL_) {
delete [] resynMagL_;
resynMagL_ = 0;
}
if (resynMagR_) {
delete [] resynMagR_;
resynMagR_ = 0;
}
if (windowBuffer_) {
delete [] windowBuffer_;
windowBuffer_ = 0;
}
if (frequencyMask_) {
delete [] frequencyMask_;
frequencyMask_ = 0;
}
if (leftSpectrum_) {
delete [] leftSpectrum_;
leftSpectrum_ = 0;
}
if (rightSpectrum_) {
delete [] rightSpectrum_;
rightSpectrum_ = 0;
}
if (leftMag_) {
delete [] leftMag_;
leftMag_ = 0;
}
if (rightMag_) {
delete [] rightMag_;
rightMag_ = 0;
}
if (leftPhase_) {
delete [] leftPhase_;
leftPhase_ = 0;
}
if (rightPhase_) {
delete [] rightPhase_;
rightPhase_ = 0;
}
if (minIndicesL_) {
delete [] minIndicesL_;
minIndicesL_ = 0;
}
if (minValuesL_) {
delete [] minValuesL_;
minValuesL_ = 0;
}
if (maxValuesL_) {
delete [] maxValuesL_;
maxValuesL_ = 0;
}
if (minIndicesR_) {
delete [] minIndicesR_;
minIndicesR_ = 0;
}
if (minValuesR_) {
delete [] minValuesR_;
minValuesR_ = 0;
}
if (maxValuesR_) {
delete [] maxValuesR_;
maxValuesR_ = 0;
}
if (azimuthL_) {
for (int n = 0; n<BLOCK_SIZE/2+1; n++)
delete [] azimuthL_[n];
delete [] azimuthL_;
azimuthL_ = 0;
}
if (azimuthR_) {
for (int n = 0; n<BLOCK_SIZE/2+1; n++)
delete [] azimuthR_[n];
delete [] azimuthR_;
azimuthR_ = 0;
}
}



void ADRess::setStatus(Status_t newStatus)
{
currStatus_ = newStatus;
}


// left most to right most from 0 to BETA
void ADRess::setDirection(int newDirection)
{
if (newDirection == BETA/2) {
d_ = newDirection;
LR_ = 2;
}
else if (newDirection < BETA/2) {
d_ = newDirection;
LR_ = 0;
}
else {
d_ = BETA - newDirection;
LR_ = 1;
}

}



void ADRess::setWidth(int newWidth)
{
H_ = newWidth;
}



void ADRess::setFilterType(FilterType_t newFilterType)
{
currFilter_ = newFilterType;
updateFrequencyMask();
}



void ADRess::setCutOffFrequency(float newCutOffFrequency)
{
cutOffFrequency_ = newCutOffFrequency;
cutOffBinIndex_ = static_cast<int>(cutOffFrequency_/sampleRate_*BLOCK_SIZE);
updateFrequencyMask();
}



const ADRess::Status_t ADRess::getStatus()
{
return currStatus_;
}



const int ADRess::getDirection()
{
if (LR_) {
return BETA - d_;
} else {
return d_;
}
}



const int ADRess::getWidth()
{
return H_;
}



const ADRess::FilterType_t ADRess::getFilterType()
{
return currFilter_;
}



const int ADRess::getCutOffFrequency()
{
return cutOffFrequency_;
}



void ADRess::process(float *leftData, float *rightData)
{
// add window
for (int i = 0; i<BLOCK_SIZE; i++) {
leftData[i] *= windowBuffer_[i];
rightData[i] *= windowBuffer_[i];
}
if (currStatus_ != kBypass) {
// do fft
kiss_fftr(fwd_, (kiss_fft_scalar*)leftData, (kiss_fft_cpx*)leftSpectrum_);
kiss_fftr(fwd_, (kiss_fft_scalar*)rightData, (kiss_fft_cpx*)rightSpectrum_);
// convert complex to magnitude-phase representation
for (int i = 0; i<BLOCK_SIZE/2+1; i++) {
leftMag_[i] = std::abs(leftSpectrum_[i]);
rightMag_[i] = std::abs(rightSpectrum_[i]);
leftPhase_[i] = std::arg(leftSpectrum_[i]);
rightPhase_[i] = std::arg(rightSpectrum_[i]);
}
// generate right or left azimuth
if (LR_ == 1) { // when right channel dominates
for (int n = 0; n<BLOCK_SIZE/2+1; n++)
for (int g = 0; g<=BETA; g++)
azimuthR_[n][g] = std::abs(leftSpectrum_[n] - rightSpectrum_[n]*(float)2.0*(float)g/(float)BETA);
for (int n = 0; n<BLOCK_SIZE/2+1; n++) {
getMinimumMaximum(n, azimuthR_[n], minValuesR_, minIndicesR_, maxValuesR_);
for (int g = 0; g<=BETA; g++)
azimuthR_[n][g] = 0;
if (currStatus_ == kSolo)
// for better rejection of signal from other channel
azimuthR_[n][minIndicesR_[n]] = maxValuesR_[n] - minValuesR_[n];
else
azimuthR_[n][minIndicesR_[n]] = maxValuesR_[n];
resynMagR_[n] = sumUpPeaks(n, azimuthR_[n]);
}
// resynth and output
for (int i = 0; i<BLOCK_SIZE/2+1; i++)
rightSpectrum_[i] = std::polar(resynMagR_[i], rightPhase_[i]);
kiss_fftri(inv_, (kiss_fft_cpx*)rightSpectrum_, (kiss_fft_scalar*)rightData);
memcpy(leftData, rightData, BLOCK_SIZE*sizeof(float));
if (currStatus_ == kSolo)
for (int i = 0; i <BLOCK_SIZE; i++)
leftData[i] *= 2.0*d_/BETA;
} else if (LR_ == 0) { // when left channel dominates
for (int n = 0; n<BLOCK_SIZE/2+1; n++)
for (int g = 0; g<=BETA; g++)
azimuthL_[n][g] = std::abs(rightSpectrum_[n] - leftSpectrum_[n]*(float)2.0*(float)g/(float)BETA);
for (int n = 0; n<BLOCK_SIZE/2+1; n++) {
getMinimumMaximum(n, azimuthL_[n], minValuesL_, minIndicesL_, maxValuesL_);
for (int g = 0; g<=BETA; g++)
azimuthL_[n][g] = 0;
if (currStatus_ == kSolo)
// for better rejection of signal from other channel
azimuthL_[n][minIndicesL_[n]] = maxValuesL_[n] - minValuesL_[n];
else
azimuthL_[n][minIndicesL_[n]] = maxValuesL_[n];
resynMagL_[n] = sumUpPeaks(n, azimuthL_[n]);
}
// resynth and output
for (int i = 0; i<BLOCK_SIZE/2+1; i++)
leftSpectrum_[i] = std::polar(resynMagL_[i], leftPhase_[i]);
kiss_fftri(inv_, (kiss_fft_cpx*)leftSpectrum_, (kiss_fft_scalar*)leftData);
memcpy(rightData, leftData, BLOCK_SIZE*sizeof(float));
if (currStatus_ == kSolo)
for (int i = 0; i <BLOCK_SIZE; i++)
rightData[i] *= 2.0*d_/BETA;
} else {
// azimuthR_ for right channel
for (int n = 0; n<BLOCK_SIZE/2+1; n++) {
for (int g = 0; g<=BETA; g++) {
azimuthR_[n][g] = std::abs(leftSpectrum_[n] - rightSpectrum_[n]*(float)2.0*(float)g/(float)BETA);
azimuthL_[n][g] = std::abs(rightSpectrum_[n] - leftSpectrum_[n]*(float)2.0*(float)g/(float)BETA);
}
}
for (int n = 0; n<BLOCK_SIZE/2+1; n++) {
getMinimumMaximum(n, azimuthR_[n], minValuesR_, minIndicesR_, maxValuesR_);
getMinimumMaximum(n, azimuthL_[n], minValuesL_, minIndicesL_, maxValuesL_);
for (int g = 0; g<=BETA; g++) {
azimuthR_[n][g] = 0;
azimuthL_[n][g] = 0;
}
if (currStatus_ == kSolo) {
// for better rejection of signal from other channel
azimuthR_[n][minIndicesR_[n]] = maxValuesR_[n] - minValuesR_[n];
azimuthL_[n][minIndicesL_[n]] = maxValuesL_[n] - minValuesL_[n];
} else {
azimuthR_[n][minIndicesR_[n]] = maxValuesR_[n];
azimuthL_[n][minIndicesL_[n]] = maxValuesL_[n];
}
resynMagR_[n] = sumUpPeaks(n, azimuthR_[n]);
resynMagL_[n] = sumUpPeaks(n, azimuthL_[n]);
}
// resynth and output
for (int i = 0; i<BLOCK_SIZE/2+1; i++)
rightSpectrum_[i] = std::polar(resynMagR_[i], rightPhase_[i]);
kiss_fftri(inv_, (kiss_fft_cpx*)rightSpectrum_, (kiss_fft_scalar*)rightData);
for (int i = 0; i<BLOCK_SIZE/2+1; i++)
leftSpectrum_[i] = std::polar(resynMagL_[i], leftPhase_[i]);
kiss_fftri(inv_, (kiss_fft_cpx*)leftSpectrum_, (kiss_fft_scalar*)leftData);
}
// scale down ifft results and windowing
for (int i = 0; i<BLOCK_SIZE; i++) {
leftData[i] = leftData[i]*windowBuffer_[i]/BLOCK_SIZE/SCALE_DOWN_FACTOR;
rightData[i] = rightData[i]*windowBuffer_[i]/BLOCK_SIZE/SCALE_DOWN_FACTOR;
}
}
// when by-pass, compensate for the gain coming from 1/4 hopsize
else {
for (int i = 0; i<BLOCK_SIZE; i++) {
leftData[i] /= SCALE_DOWN_FACTOR;
rightData[i] /= SCALE_DOWN_FACTOR;
}
}
}



void ADRess::getMinimumMaximum(int nthBin, float* nthBinAzm, float* minValues, int* minIndices, float* maxValues)
{
int minIndex = 0;
float minValue = nthBinAzm[0];
float maxValue = nthBinAzm[0];
for (int i = 1; i<=BETA; i++) {
if (nthBinAzm[i] < minValue) {
minIndex = i;
minValue = nthBinAzm[i];
}
if (nthBinAzm[i]>maxValue) {
maxValue = nthBinAzm[i];
}
}
minIndices[nthBin] = minIndex;
minValues[nthBin] = minValue;
maxValues[nthBin] = maxValue;
}



float ADRess::sumUpPeaks(int nthBIn, float *nthBinAzm)
{
float sum = 0.0;
int startInd = std::max(0, d_-H_/2);
int endInd = std::min(BETA, d_+H_/2);
switch (currStatus_) {
case kSolo:
if (frequencyMask_[nthBIn] == 0.0)
return 0.0;
for (int i = startInd; i<=endInd; i++)
sum += nthBinAzm[i];
// add smoothing along azimuth
for (int i = 1; i<4 && startInd-i>=0; i++)
sum += nthBinAzm[startInd-i]*(4-i)/4;
for (int i = 1; i<4 && endInd+i<=BETA;i++)
sum += nthBinAzm[endInd+i]*(4-i)/4;
sum *= frequencyMask_[nthBIn];
break;
case kMute:
if (currFilter_) {
for (int i = startInd; i<=endInd; i++)
sum += nthBinAzm[i];
sum *= frequencyMask_[nthBIn];
}
for (int i = 0; i<=BETA; i++)
if (i<startInd || i>endInd)
sum += nthBinAzm[i];
case kBypass:
default:
break;
}
return sum;
}


void ADRess::updateFrequencyMask()
{
switch (currFilter_) {
case kAllPass:
for (int i = 0; i<BLOCK_SIZE/2+1; i++ )
frequencyMask_[i] = 1.0;
break;
case kLowPass:
for (int i = 0; i<cutOffBinIndex_; i++)
frequencyMask_[i] = 1.0;
for (int i = cutOffBinIndex_; i<BLOCK_SIZE/2+1; i++)
frequencyMask_[i] = 0.0;
frequencyMask_[cutOffBinIndex_] = 0.5;
if (cutOffBinIndex_-1 >= 0)
frequencyMask_[cutOffBinIndex_-1] = 0.75;
if (cutOffBinIndex_+1 <= BLOCK_SIZE/2)
frequencyMask_[cutOffBinIndex_+1] = 0.25;
break;
case kHighPass:
for (int i = 0; i<cutOffBinIndex_; i++)
frequencyMask_[i] = 0.0;
for (int i = cutOffBinIndex_; i<BLOCK_SIZE/2+1; i++)
frequencyMask_[i] = 1.0;
break;
frequencyMask_[cutOffBinIndex_] = 0.5;
if (cutOffBinIndex_-1 >= 0)
frequencyMask_[cutOffBinIndex_-1] = 0.25;
if (cutOffBinIndex_+1 <= BLOCK_SIZE/2)
frequencyMask_[cutOffBinIndex_+1] = 0.75;
default:
break;
}
}

+ 101
- 0
ports/stereosourceseparation/source/ADRess.h View File

@@ -0,0 +1,101 @@
//
// ADRess.h
// StereoSourceSeparation
//
// Created by Xinyuan Lai on 4/8/14.
//
//

#ifndef __StereoSourceSeparation__ADRess__
#define __StereoSourceSeparation__ADRess__

#include <iostream>
#include "kiss_fftr.h"
#include <cmath>
#include <complex>

using std::complex;

class ADRess
{
public:
ADRess (double sampleRate, int blockSize, int beta);
~ADRess ();
enum Status_t
{
kBypass,
kSolo,
kMute
};
enum FilterType_t
{
kAllPass,
kLowPass,
kHighPass
};
void setStatus(Status_t newStatus);
void setDirection(int newDirection); // this function is a little different from other set functions
void setWidth(int newWidth);
void setFilterType(FilterType_t newFilterType);
void setCutOffFrequency(float newCutOffFrequency);
const Status_t getStatus();
const int getDirection();
const int getWidth();
const FilterType_t getFilterType();
const int getCutOffFrequency();
void process (float* leftData, float* rightData);
private:
const double sampleRate_;
const int BLOCK_SIZE;
const int BETA;
Status_t currStatus_;
int d_;
int H_;
FilterType_t currFilter_;
float cutOffFrequency_;
int cutOffBinIndex_;
float* windowBuffer_;
float* frequencyMask_;
int LR_; // 0 for left, 1 for right, 2 for centre
kiss_fftr_cfg fwd_;
kiss_fftr_cfg inv_;
complex<float>* leftSpectrum_;
complex<float>* rightSpectrum_;
float* leftMag_;
float* rightMag_;
float* leftPhase_;
float* rightPhase_;
int* minIndicesL_;
float* minValuesL_;
float* maxValuesL_;
int* minIndicesR_;
float* minValuesR_;
float* maxValuesR_;
float** azimuthL_;
float** azimuthR_;
float* resynMagL_;
float* resynMagR_;
void getMinimumMaximum(int nthBin, float* nthBinAzm, float* minValues, int* minIndices, float* maxValues);
float sumUpPeaks(int nthBin, float* nthBinAzm);
void updateFrequencyMask();
};

#endif /* defined(__StereoSourceSeparation__ADRess__) */

+ 27
- 0
ports/stereosourceseparation/source/JuceHeader.h View File

@@ -0,0 +1,27 @@
/*
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_E5BN8L__
#define __APPHEADERFILE_E5BN8L__
#include "JucePluginMain.h"
using namespace juce;
namespace ProjectInfo
{
const char* const projectName = "StereoSourceSeparation";
const char* const versionString = "1.0.0";
const int versionNumber = 0x10000;
}
#endif // __APPHEADERFILE_E5BN8L__

+ 134
- 0
ports/stereosourceseparation/source/JucePluginCharacteristics.h View File

@@ -0,0 +1,134 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
There's a section below where you can add your own custom code safely, and the
Introjucer will preserve the contents of that block, but the best way to change
any of these definitions is by using the Introjucer's project settings.
Any commented-out settings will assume their default values.
*/
#ifndef __JUCE_APPCONFIG_E5BN8L__
#define __JUCE_APPCONFIG_E5BN8L__
//==============================================================================
// Audio plugin settings..
#ifndef JucePlugin_Name
#define JucePlugin_Name "StereoSourceSeparation"
#endif
#ifndef JucePlugin_Desc
#define JucePlugin_Desc "StereoSourceSeparation"
#endif
#ifndef JucePlugin_Manufacturer
#define JucePlugin_Manufacturer "AnnieShin"
#endif
#ifndef JucePlugin_ManufacturerWebsite
#define JucePlugin_ManufacturerWebsite ""
#endif
#ifndef JucePlugin_ManufacturerEmail
#define JucePlugin_ManufacturerEmail ""
#endif
#ifndef JucePlugin_ManufacturerCode
#define JucePlugin_ManufacturerCode 'AShi'
#endif
#ifndef JucePlugin_PluginCode
#define JucePlugin_PluginCode 'SsSe'
#endif
#ifndef JucePlugin_MaxNumInputChannels
#define JucePlugin_MaxNumInputChannels 2
#endif
#ifndef JucePlugin_MaxNumOutputChannels
#define JucePlugin_MaxNumOutputChannels 2
#endif
#ifndef JucePlugin_PreferredChannelConfigurations
#define JucePlugin_PreferredChannelConfigurations {2, 2}
#endif
#ifndef JucePlugin_IsSynth
#define JucePlugin_IsSynth 0
#endif
#ifndef JucePlugin_WantsMidiInput
#define JucePlugin_WantsMidiInput 0
#endif
#ifndef JucePlugin_ProducesMidiOutput
#define JucePlugin_ProducesMidiOutput 0
#endif
#ifndef JucePlugin_SilenceInProducesSilenceOut
#define JucePlugin_SilenceInProducesSilenceOut 0
#endif
#ifndef JucePlugin_EditorRequiresKeyboardFocus
#define JucePlugin_EditorRequiresKeyboardFocus 0
#endif
#ifndef JucePlugin_Version
#define JucePlugin_Version 1.0.0
#endif
#ifndef JucePlugin_VersionCode
#define JucePlugin_VersionCode 0x10000
#endif
#ifndef JucePlugin_VersionString
#define JucePlugin_VersionString "1.0.0"
#endif
#ifndef JucePlugin_VSTUniqueID
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
#endif
#ifndef JucePlugin_VSTCategory
#define JucePlugin_VSTCategory kPlugCategEffect
#endif
#ifndef JucePlugin_AUMainType
#define JucePlugin_AUMainType kAudioUnitType_Effect
#endif
#ifndef JucePlugin_AUSubType
#define JucePlugin_AUSubType JucePlugin_PluginCode
#endif
#ifndef JucePlugin_AUExportPrefix
#define JucePlugin_AUExportPrefix StereoSourceSeparationAU
#endif
#ifndef JucePlugin_AUExportPrefixQuoted
#define JucePlugin_AUExportPrefixQuoted "StereoSourceSeparationAU"
#endif
#ifndef JucePlugin_AUManufacturerCode
#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode
#endif
#ifndef JucePlugin_CFBundleIdentifier
#define JucePlugin_CFBundleIdentifier com.annieshin.StereoSourceSeparation
#endif
#ifndef JucePlugin_RTASCategory
#define JucePlugin_RTASCategory ePlugInCategory_None
#endif
#ifndef JucePlugin_RTASManufacturerCode
#define JucePlugin_RTASManufacturerCode JucePlugin_ManufacturerCode
#endif
#ifndef JucePlugin_RTASProductId
#define JucePlugin_RTASProductId JucePlugin_PluginCode
#endif
#ifndef JucePlugin_RTASDisableBypass
#define JucePlugin_RTASDisableBypass 0
#endif
#ifndef JucePlugin_RTASDisableMultiMono
#define JucePlugin_RTASDisableMultiMono 0
#endif
#ifndef JucePlugin_AAXIdentifier
#define JucePlugin_AAXIdentifier com.annieshin.StereoSourceSeparation
#endif
#ifndef JucePlugin_AAXManufacturerCode
#define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode
#endif
#ifndef JucePlugin_AAXProductId
#define JucePlugin_AAXProductId JucePlugin_PluginCode
#endif
#ifndef JucePlugin_AAXCategory
#define JucePlugin_AAXCategory AAX_ePlugInCategory_Dynamics
#endif
#ifndef JucePlugin_AAXDisableBypass
#define JucePlugin_AAXDisableBypass 0
#endif
#ifndef JucePlugin_AAXDisableMultiMono
#define JucePlugin_AAXDisableMultiMono 0
#endif
#define JucePlugin_LV2URI "https://github.com/laixinyuan/StereoSourceSepartion"
#endif // __JUCE_APPCONFIG_E5BN8L__

+ 385
- 0
ports/stereosourceseparation/source/PluginEditor.cpp View File

@@ -0,0 +1,385 @@
/*
==============================================================================
This file was auto-generated by the Introjucer!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#include "PluginEditor.h"
//==============================================================================
StereoSourceSeparationAudioProcessorEditor::StereoSourceSeparationAudioProcessorEditor (StereoSourceSeparationAudioProcessor* ownerFilter)
: AudioProcessorEditor (ownerFilter)
{
width_ = 400;
height_ = 200;
dirAngle = M_PI/2;
widAngle = M_PI/16;
radius = width_/2-5;
arrowLine = Line<float>(width_/2+50, height_+100, 50+width_/2+radius*cos(dirAngle), 100+height_-radius*sin(dirAngle));
paintColour = Colours::grey;
addAndMakeVisible(widthSlider = new Slider());
widthSlider->setTextBoxStyle(Slider::NoTextBox, true, 220, 30);
widthSlider->setColour(Slider::thumbColourId, Colours::wheat);
widthSlider->setColour(Slider::trackColourId, Colours::wheat);
widthSlider->setRange(0, 100);
widthSlider->setValue(12.5);
widthSlider->addListener(this);
addAndMakeVisible (modeLabel = new Label (String::empty, "Mode"));
modeLabel->setFont (Font (16.00f, Font::bold));
modeLabel->setJustificationType (Justification::centred);
modeLabel->setEditable (false, false, false);
modeLabel->setColour (Label::textColourId, Colours::wheat);
modeLabel->setColour (TextEditor::textColourId, Colours::wheat);
modeLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (filterLabel = new Label (String::empty, "Filter Type"));
filterLabel->setFont (Font (16.00f, Font::bold));
filterLabel->setJustificationType (Justification::centred);
filterLabel->setEditable (false, false, false);
filterLabel->setColour (Label::textColourId, Colours::wheat);
filterLabel->setColour (TextEditor::textColourId, Colours::wheat);
filterLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (bypassToggle = new ToggleButton ("Bypass"));
bypassToggle->setColour(TextButton::buttonColourId, Colours::lightgrey);
bypassToggle->setColour(ToggleButton::textColourId, Colours::lightgrey);
bypassToggle->setToggleState(true, juce::sendNotification);
bypassToggle->addListener (this);
addAndMakeVisible (soloToggle = new ToggleButton ("Solo"));
soloToggle->setColour(TextButton::buttonColourId, Colours::lightblue);
soloToggle->setColour(ToggleButton::textColourId, Colours::lightblue);
soloToggle->addListener (this);
addAndMakeVisible (muteToggle = new ToggleButton ("Mute"));
muteToggle->setColour(TextButton::buttonColourId, Colours::pink);
muteToggle->setColour(ToggleButton::textColourId, Colours::pink);
muteToggle->addListener (this);
addAndMakeVisible (allpassToggle = new ToggleButton ("No filter"));
allpassToggle->setColour(TextButton::buttonColourId, Colours::lightgrey);
allpassToggle->setColour(ToggleButton::textColourId, Colours::lightgrey);
allpassToggle->setToggleState(true, juce::sendNotification);
allpassToggle->setEnabled(false);
allpassToggle->addListener (this);
addAndMakeVisible (lowpassToggle = new ToggleButton ("Low pass"));
lowpassToggle->setColour(TextButton::buttonColourId, Colours::lightblue);
lowpassToggle->setColour(ToggleButton::textColourId, Colours::lightblue);
lowpassToggle->setEnabled(false);
lowpassToggle->addListener (this);
addAndMakeVisible (highpassToggle = new ToggleButton ("High pass"));
highpassToggle->setColour(TextButton::buttonColourId, Colours::pink);
highpassToggle->setColour(ToggleButton::textColourId, Colours::pink);
highpassToggle->setEnabled(false);
highpassToggle->addListener (this);
addAndMakeVisible(cutofffreqSlider = new Slider());
cutofffreqSlider->setSliderStyle(juce::Slider::LinearBar);
cutofffreqSlider->setTextBoxStyle(Slider::NoTextBox, true, 220, 30);
cutofffreqSlider->setColour(Slider::thumbColourId, Colours::lightgrey);
cutofffreqSlider->setColour(Slider::trackColourId, Colours::lightgrey);
cutofffreqSlider->setRange(0, 89.45);
cutofffreqSlider->setValue(20);
cutofffreqSlider->addListener(this);
addAndMakeVisible (freqLabel = new Label (String::empty,"Cutoff frequency :"));
freqLabel->setFont (Font (18.00f, Font::bold));
freqLabel->setJustificationType (Justification::centred);
freqLabel->setEditable (false, false, false);
freqLabel->setColour (Label::textColourId, Colours::wheat);
freqLabel->setColour (TextEditor::textColourId, Colours::wheat);
freqLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (freqVal = new Label (String::empty, "400 Hz"));
freqVal->setFont (Font (16.00f, Font::bold));
freqVal->setJustificationType (Justification::centred);
freqVal->setEditable (false, false, false);
freqVal->setColour (Label::textColourId, Colours::wheat);
freqVal->setColour (TextEditor::textColourId, Colours::wheat);
freqVal->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (lowboundLabel = new Label (String::empty, "0"));
lowboundLabel->setFont (Font (16.00f, Font::bold));
lowboundLabel->setJustificationType (Justification::centred);
lowboundLabel->setEditable (false, false, false);
lowboundLabel->setColour (Label::textColourId, Colours::wheat);
lowboundLabel->setColour (TextEditor::textColourId, Colours::wheat);
lowboundLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (highboundLabel = new Label (String::empty, "8000"));
highboundLabel->setFont (Font (16.00f, Font::bold));
highboundLabel->setJustificationType (Justification::centred);
highboundLabel->setEditable (false, false, false);
highboundLabel->setColour (Label::textColourId, Colours::wheat);
highboundLabel->setColour (TextEditor::textColourId, Colours::wheat);
highboundLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (resetButton = new TextButton ("C"));
resetButton->setColour(TextButton::buttonColourId, Colours::wheat);
resetButton->addListener (this);
addAndMakeVisible (dirLabel = new Label (String::empty,"Direction : "));
dirLabel->setFont (Font (20.00f, Font::bold));
dirLabel->setJustificationType (Justification::centred);
dirLabel->setEditable (false, false, false);
dirLabel->setColour (Label::textColourId, Colours::wheat);
dirLabel->setColour (TextEditor::textColourId, Colours::wheat);
dirLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (widLabel = new Label (String::empty,"Width : +/-"));
widLabel->setFont (Font (20.00f, Font::bold));
widLabel->setJustificationType (Justification::centred);
widLabel->setEditable (false, false, false);
widLabel->setColour (Label::textColourId, Colours::wheat);
widLabel->setColour (TextEditor::textColourId, Colours::wheat);
widLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (dirVal = new Label (String::empty, "0"));
dirVal->setFont (Font (20.00f, Font::bold));
dirVal->setJustificationType (Justification::centred);
dirVal->setEditable (false, false, false);
dirVal->setColour (Label::textColourId, Colours::wheat);
dirVal->setColour (TextEditor::textColourId, Colours::wheat);
dirVal->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (widVal = new Label (String::empty, "22.5"));
widVal->setFont (Font (20.00f, Font::bold));
widVal->setJustificationType (Justification::centred);
widVal->setEditable (false, false, false);
widVal->setColour (Label::textColourId, Colours::wheat);
widVal->setColour (TextEditor::textColourId, Colours::wheat);
widVal->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (sideVal = new Label (String::empty, "M"));
sideVal->setFont (Font (20.00f, Font::bold));
sideVal->setJustificationType (Justification::centred);
sideVal->setEditable (false, false, false);
sideVal->setColour (Label::textColourId, Colours::wheat);
sideVal->setColour (TextEditor::textColourId, Colours::wheat);
sideVal->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
setSize (750, 400);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kDirection, 50);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kWidth, 25);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kStatus, ADRess::kBypass);
}
StereoSourceSeparationAudioProcessorEditor::~StereoSourceSeparationAudioProcessorEditor()
{
widthSlider = nullptr;
soloToggle = nullptr;
muteToggle = nullptr;
bypassToggle = nullptr;
allpassToggle = nullptr;
lowpassToggle = nullptr;
highpassToggle = nullptr;
cutofffreqSlider = nullptr;
resetButton = nullptr;
dirLabel = nullptr;
widLabel = nullptr;
dirVal = nullptr;
widVal = nullptr;
sideVal = nullptr;
freqLabel = nullptr;
freqVal = nullptr;
lowboundLabel = nullptr;
highboundLabel = nullptr;
modeLabel = nullptr;
filterLabel = nullptr;
}
//==============================================================================
void StereoSourceSeparationAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll(Colours::darkgrey);
g.setColour (paintColour);
g.fillPath (internalPath);
g.strokePath (internalPath, PathStrokeType (1.200f));
g.setColour(Colour (0xff3b3b3b));
g.fillPath (arrowPath);
g.strokePath (arrowPath, PathStrokeType (1.200f));
g.setColour (Colours::wheat);
g.drawEllipse(50+5, 100+5, width_-10, 2*height_-10, 8);
g.setColour(Colours::lightgrey);
g.drawLine(600, 90, 600, 190);
g.setColour(Colours::darkgrey);
g.fillRect(0, 100+height_, getWidth(), getHeight()-height_-100);
}
void StereoSourceSeparationAudioProcessorEditor::resized()
{
internalPath.clear();
internalPath.startNewSubPath (width_/2+50, height_+100);
internalPath.addCentredArc(width_/2+50, height_+100, radius, radius, 0, (M_PI/2-dirAngle-widAngle+2*M_PI), (M_PI/2-dirAngle+widAngle+M_PI*2), false);
internalPath.closeSubPath();
arrowPath.clear();
arrowPath.startNewSubPath (width_/2+50, height_+100);
arrowPath.addArrow(arrowLine, 5, 20, 20);
arrowPath.closeSubPath();
widthSlider->setBounds(150, 330, 200, 40);
modeLabel->setBounds(510, 80, 40, 30);
filterLabel->setBounds(620, 80, 80, 30);
bypassToggle->setBounds(500, 110, 80, 30);
soloToggle->setBounds(500, 140, 80, 30);
muteToggle->setBounds(500, 170, 80, 30);
allpassToggle->setBounds(620, 110, 80, 30);
lowpassToggle->setBounds(620, 140, 80, 30);
highpassToggle->setBounds(620, 170, 80, 30);
cutofffreqSlider->setBounds(500, 280, 200, 20);
lowboundLabel->setBounds(500, 300, 20, 20);
freqLabel->setBounds(500, 230, 120, 40);
freqVal->setBounds(620, 230, 80, 40);
highboundLabel->setBounds(660, 300, 40, 20);
resetButton->setBounds(100, 340, 20, 20);
dirLabel->setBounds(50, 50, 80, 25);
widLabel->setBounds(300, 50, 100, 25);
dirVal->setBounds(150, 50, 60, 25);
widVal->setBounds(400, 50, 60, 25);
sideVal->setBounds(130, 50, 30, 25);
}
void StereoSourceSeparationAudioProcessorEditor::mouseDrag (const juce::MouseEvent &e)
{
if (e.getPosition().y > height_+98)
return;
dirAngle = atanf((height_+100-e.getPosition().y)*1.0/(e.getPosition().x-width_/2-100));
if (dirAngle < 0)
dirAngle += M_PI;
int dirAngle_toPass = (M_PI-dirAngle)/M_PI*100;
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kDirection, dirAngle_toPass);
if (dirAngle > M_PI/2)
sideVal->setText("L", juce::sendNotification);
else if (dirAngle == M_PI/2)
sideVal->setText("M", juce::sendNotification);
else
sideVal->setText("R", juce::sendNotification);
dirVal->setText(String(fabs((dirAngle-M_PI/2)*180/M_PI)), juce::sendNotification);
arrowLine.setEnd(50+width_/2+radius*cos(dirAngle), 100+height_-radius*sin(dirAngle));
resized();
repaint();
}
void StereoSourceSeparationAudioProcessorEditor::sliderValueChanged (Slider* slider)
{
if (slider == widthSlider){
widAngle = slider->getValue()/100*M_PI/2;
int widAngle_toPass = slider->getValue();
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kWidth, widAngle_toPass);
widVal->setText(String(widAngle*180/M_PI), juce::sendNotification);
}
else if (slider == cutofffreqSlider){
float val = slider->getValue();
float freq_toPass = val*val;
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kCutOffFrequency, freq_toPass);
freqVal->setText(String(freq_toPass)+" Hz", juce::sendNotification);
}
resized();
repaint();
}
void StereoSourceSeparationAudioProcessorEditor::buttonClicked (Button* buttonThatWasClicked)
{
if (buttonThatWasClicked == soloToggle && soloToggle->getToggleState())
{
muteToggle->setToggleState(false, juce::sendNotification);
bypassToggle->setToggleState(false, juce::sendNotification);
soloToggle->setEnabled(false);
muteToggle->setEnabled(true);
bypassToggle->setEnabled(true);
if (!allpassToggle->getToggleState())
allpassToggle->setEnabled(true);
if (!lowpassToggle->getToggleState())
lowpassToggle->setEnabled(true);
if (!highpassToggle->getToggleState())
highpassToggle->setEnabled(true);
allpassToggle->setButtonText("No filter");
paintColour = Colours::lightblue;
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kStatus, ADRess::kSolo);
}
else if (buttonThatWasClicked == muteToggle && muteToggle->getToggleState())
{
soloToggle->setToggleState(false, juce::sendNotification);
bypassToggle->setToggleState(false, juce::sendNotification);
soloToggle->setEnabled(true);
muteToggle->setEnabled(false);
bypassToggle->setEnabled(true);
if (!allpassToggle->getToggleState())
allpassToggle->setEnabled(true);
if (!lowpassToggle->getToggleState())
lowpassToggle->setEnabled(true);
if (!highpassToggle->getToggleState())
highpassToggle->setEnabled(true);
allpassToggle->setButtonText("All reject");
paintColour = Colour (0xfff08080);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kStatus, ADRess::kMute);
}
else if (buttonThatWasClicked == bypassToggle && bypassToggle->getToggleState())
{
soloToggle->setToggleState(false, juce::sendNotification);
muteToggle->setToggleState(false, juce::sendNotification);
soloToggle->setEnabled(true);
muteToggle->setEnabled(true);
bypassToggle->setEnabled(false);
allpassToggle->setEnabled(false);
lowpassToggle->setEnabled(false);
highpassToggle->setEnabled(false);
allpassToggle->setButtonText("No filter");
paintColour = Colours::grey;
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kStatus, ADRess::kBypass);
}
else if (buttonThatWasClicked == allpassToggle && allpassToggle->getToggleState())
{
lowpassToggle->setToggleState(false, juce::sendNotification);
highpassToggle->setToggleState(false, juce::sendNotification);
allpassToggle->setEnabled(false);
lowpassToggle->setEnabled(true);
highpassToggle->setEnabled(true);
cutofffreqSlider->setColour(Slider::thumbColourId, Colours::lightgrey);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kFilterType, ADRess::kAllPass);
}
else if (buttonThatWasClicked == lowpassToggle && lowpassToggle->getToggleState())
{
allpassToggle->setToggleState(false, juce::sendNotification);
highpassToggle->setToggleState(false, juce::sendNotification);
lowpassToggle->setEnabled(false);
allpassToggle->setEnabled(true);
highpassToggle->setEnabled(true);
cutofffreqSlider->setColour(Slider::thumbColourId, Colours::lightblue);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kFilterType, ADRess::kLowPass);
}
else if (buttonThatWasClicked == highpassToggle && highpassToggle->getToggleState())
{
allpassToggle->setToggleState(false, juce::sendNotification);
lowpassToggle->setToggleState(false, juce::sendNotification);
highpassToggle->setEnabled(false);
allpassToggle->setEnabled(true);
lowpassToggle->setEnabled(true);
cutofffreqSlider->setColour(Slider::thumbColourId, Colour (0xfff08080));
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kFilterType, ADRess::kHighPass);
}
else if (buttonThatWasClicked == resetButton)
{
dirAngle = M_PI/2;
sideVal->setText("M", juce::sendNotification);
dirVal->setText(String(0), juce::sendNotification);
arrowLine.setEnd(50+width_/2, 100+height_-radius);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kDirection, 50);
getProcessor()->setParameter(StereoSourceSeparationAudioProcessor::kWidth, widthSlider->getValue());
}
resized();
repaint();
}

+ 77
- 0
ports/stereosourceseparation/source/PluginEditor.h View File

@@ -0,0 +1,77 @@
/*
==============================================================================
This file was auto-generated by the Introjucer!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#ifndef PLUGINEDITOR_H_INCLUDED
#define PLUGINEDITOR_H_INCLUDED
#include "JuceHeader.h"
#include "PluginProcessor.h"
#include <math.h>
//==============================================================================
/**
*/
class StereoSourceSeparationAudioProcessorEditor : public AudioProcessorEditor,
public ButtonListener,
public SliderListener
{
public:
StereoSourceSeparationAudioProcessorEditor (StereoSourceSeparationAudioProcessor* ownerFilter);
~StereoSourceSeparationAudioProcessorEditor();
//==============================================================================
// This is just a standard Juce paint method...
void paint (Graphics&) override;
void resized() override;
void mouseDrag(const juce::MouseEvent &e) override;
void sliderValueChanged (Slider*) override;
void buttonClicked (Button* buttonThatWasClicked) override;
private:
int width_;
int height_;
float dirAngle;
float widAngle;
float radius;
Colour paintColour;
Path internalPath;
Path arrowPath;
Line<float> arrowLine;
ScopedPointer<Label> label;
ScopedPointer<Slider> widthSlider;
ScopedPointer<ToggleButton> soloToggle;
ScopedPointer<ToggleButton> muteToggle;
ScopedPointer<ToggleButton> bypassToggle;
ScopedPointer<ToggleButton> allpassToggle;
ScopedPointer<ToggleButton> lowpassToggle;
ScopedPointer<ToggleButton> highpassToggle;
ScopedPointer<Slider> cutofffreqSlider;
ScopedPointer<TextButton> resetButton;
ScopedPointer<Label> dirLabel;
ScopedPointer<Label> widLabel;
ScopedPointer<Label> dirVal;
ScopedPointer<Label> widVal;
ScopedPointer<Label> sideVal;
ScopedPointer<Label> freqLabel;
ScopedPointer<Label> freqVal;
ScopedPointer<Label> lowboundLabel;
ScopedPointer<Label> highboundLabel;
ScopedPointer<Label> modeLabel;
ScopedPointer<Label> filterLabel;
StereoSourceSeparationAudioProcessor* getProcessor() const
{
return static_cast <StereoSourceSeparationAudioProcessor*> (getAudioProcessor());
}
};
#endif // PLUGINEDITOR_H_INCLUDED

+ 349
- 0
ports/stereosourceseparation/source/PluginProcessor.cpp View File

@@ -0,0 +1,349 @@
/*
==============================================================================
This file was auto-generated!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
#define FFT_SIZE 4096
//==============================================================================
StereoSourceSeparationAudioProcessor::StereoSourceSeparationAudioProcessor()
:NUM_CHANNELS(2),
BLOCK_SIZE(FFT_SIZE),
HOP_SIZE(FFT_SIZE/4),
BETA(100),
inputBuffer_(2,FFT_SIZE),
outputBuffer_(2,FFT_SIZE*2),
processBuffer_(2, FFT_SIZE)
{
inputBufferLength_ = FFT_SIZE;
outputBufferLength_ = FFT_SIZE*2;
separator_ = 0;
status_ = ADRess::kBypass;
direction_ = BETA/2;
width_ = BETA/4;
filterType_ = ADRess::kAllPass;
cutOffFrequency_ = 0.0;
}
StereoSourceSeparationAudioProcessor::~StereoSourceSeparationAudioProcessor()
{
}
//==============================================================================
const String StereoSourceSeparationAudioProcessor::getName() const
{
return JucePlugin_Name;
}
int StereoSourceSeparationAudioProcessor::getNumParameters()
{
return kNumParameters;
}
float StereoSourceSeparationAudioProcessor::getParameter (int index)
{
switch (index) {
case kStatus:
return (float)status_;
case kDirection:
return (float)direction_;
case kWidth:
return (float)width_;
case kFilterType:
return (float)filterType_;
case kCutOffFrequency:
return cutOffFrequency_;
default:
return 0.0f;
}
}
void StereoSourceSeparationAudioProcessor::setParameter (int index, float newValue)
{
switch (index) {
case kStatus:
status_ = static_cast<ADRess::Status_t>(newValue);
if (separator_)
separator_->setStatus( status_ );
break;
case kDirection:
direction_ = static_cast<int>(newValue);
if (separator_)
separator_->setDirection( direction_ );
break;
case kWidth:
width_ = static_cast<int>(newValue);
if (separator_)
separator_->setWidth( width_ );
break;
case kFilterType:
filterType_ = static_cast<ADRess::FilterType_t>(newValue);
if (separator_)
separator_->setFilterType(filterType_);
break;
case kCutOffFrequency:
cutOffFrequency_ = newValue;
if (separator_)
separator_->setCutOffFrequency(cutOffFrequency_);
break;
default:
break;
}
}
const String StereoSourceSeparationAudioProcessor::getParameterName (int index)
{
switch (index) {
case kStatus:
return "Status";
case kDirection:
return "Direction";
case kWidth:
return "Width";
case kFilterType:
return "FilterType";
case kCutOffFrequency:
return "CutOffFrequency";
default:
return String::empty;
}
}
const String StereoSourceSeparationAudioProcessor::getParameterText (int index)
{
return String(getParameter(index));
}
const String StereoSourceSeparationAudioProcessor::getInputChannelName (int channelIndex) const
{
return String (channelIndex + 1);
}
const String StereoSourceSeparationAudioProcessor::getOutputChannelName (int channelIndex) const
{
return String (channelIndex + 1);
}
bool StereoSourceSeparationAudioProcessor::isInputChannelStereoPair (int index) const
{
return true;
}
bool StereoSourceSeparationAudioProcessor::isOutputChannelStereoPair (int index) const
{
return true;
}
bool StereoSourceSeparationAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool StereoSourceSeparationAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
bool StereoSourceSeparationAudioProcessor::silenceInProducesSilenceOut() const
{
return false;
}
double StereoSourceSeparationAudioProcessor::getTailLengthSeconds() const
{
return 0.0;
}
int StereoSourceSeparationAudioProcessor::getNumPrograms()
{
return 0;
}
int StereoSourceSeparationAudioProcessor::getCurrentProgram()
{
return 0;
}
void StereoSourceSeparationAudioProcessor::setCurrentProgram (int index)
{
}
const String StereoSourceSeparationAudioProcessor::getProgramName (int index)
{
return String::empty;
}
void StereoSourceSeparationAudioProcessor::changeProgramName (int index, const String& newName)
{
}
//==============================================================================
void StereoSourceSeparationAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
// Use this method as the place to do any pre-playback
// initialisation that you need..
separator_ = new ADRess(sampleRate, BLOCK_SIZE, BETA);
inputBuffer_.clear();
outputBuffer_.clear();
processBuffer_.clear();
inputBufferWritePosition_ = outputBufferWritePosition_ = 0;
outputBufferReadPosition_ = (outputBufferLength_ - HOP_SIZE - 1) % outputBufferLength_;
samplesSinceLastFFT_ = 0;
separator_->setStatus( status_ );
separator_->setDirection( direction_ );
separator_->setWidth( width_ );
separator_->setFilterType(filterType_);
separator_->setCutOffFrequency(cutOffFrequency_);
}
void StereoSourceSeparationAudioProcessor::releaseResources()
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
delete separator_;
}
void StereoSourceSeparationAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
// get pointers for buffer access
float* stereoData[NUM_CHANNELS];
stereoData[0] = buffer.getWritePointer(0);
stereoData[1] = buffer.getWritePointer(1);
float* inputBufferData[NUM_CHANNELS];
inputBufferData[0] = inputBuffer_.getWritePointer(0);
inputBufferData[1] = inputBuffer_.getWritePointer(1);
float* outputBufferData[NUM_CHANNELS];
outputBufferData[0] = outputBuffer_.getWritePointer(0);
outputBufferData[1] = outputBuffer_.getWritePointer(1);
float* processBufferData[NUM_CHANNELS];
processBufferData[0] = processBuffer_.getWritePointer(0);
processBufferData[1] = processBuffer_.getWritePointer(1);
for (int i = 0; i<buffer.getNumSamples(); i++) {
// store input sample data in input buffer
inputBufferData[0][inputBufferWritePosition_] = stereoData[0][i];
inputBufferData[1][inputBufferWritePosition_] = stereoData[1][i];
if (++inputBufferWritePosition_ >= inputBufferLength_)
inputBufferWritePosition_ = 0;
// output sample from output buffer
stereoData[0][i] = outputBufferData[0][outputBufferReadPosition_];
stereoData[1][i] = outputBufferData[1][outputBufferReadPosition_];
// clear output buffer sample in preparation for next overlap and add
outputBufferData[0][outputBufferReadPosition_] = 0.0;
outputBufferData[1][outputBufferReadPosition_] = 0.0;
if (++outputBufferReadPosition_ >= outputBufferLength_)
outputBufferReadPosition_ = 0;
// if samples since last fft exceeds hopsize, do fft
if (++samplesSinceLastFFT_ >= HOP_SIZE) {
samplesSinceLastFFT_ = 0;
// find start positino in input buffer, then load the process buffer leftData_ and rightData_
int inputBufferIndex = (inputBufferWritePosition_ - BLOCK_SIZE + inputBufferLength_) % inputBufferLength_;
for (int procBufferIndex = 0; procBufferIndex < BLOCK_SIZE; procBufferIndex++) {
processBufferData[0][procBufferIndex] = inputBufferData[0][inputBufferIndex];
processBufferData[1][procBufferIndex] = inputBufferData[1][inputBufferIndex];
if ( ++inputBufferIndex >= inputBufferLength_)
inputBufferIndex = 0;
}
// performs source separation here
separator_->process(processBufferData[0], processBufferData[1]);
// overlap and add in output buffer
int outputBufferIndex = outputBufferWritePosition_;
for (int procBufferIndex = 0; procBufferIndex < BLOCK_SIZE; procBufferIndex++) {
outputBufferData[0][outputBufferIndex] += processBufferData[0][procBufferIndex];
outputBufferData[1][outputBufferIndex] += processBufferData[1][procBufferIndex];
if (++outputBufferIndex >= outputBufferLength_)
outputBufferIndex = 0;
}
// advance write position by hop size
outputBufferWritePosition_ = (outputBufferWritePosition_ + HOP_SIZE) % outputBufferLength_;
}
}
// In case we have more outputs than inputs, we'll clear any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
{
buffer.clear (i, 0, buffer.getNumSamples());
}
}
//==============================================================================
bool StereoSourceSeparationAudioProcessor::hasEditor() const
{
return true; // (change this to false if you choose to not supply an editor)
}
AudioProcessorEditor* StereoSourceSeparationAudioProcessor::createEditor()
{
return new StereoSourceSeparationAudioProcessorEditor (this);
}
//==============================================================================
void StereoSourceSeparationAudioProcessor::getStateInformation (MemoryBlock& destData)
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
}
void StereoSourceSeparationAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
}
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new StereoSourceSeparationAudioProcessor();
}

+ 109
- 0
ports/stereosourceseparation/source/PluginProcessor.h View File

@@ -0,0 +1,109 @@
/*
==============================================================================
This file was auto-generated!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#ifndef PLUGINPROCESSOR_H_INCLUDED
#define PLUGINPROCESSOR_H_INCLUDED
#include "JuceHeader.h"
#include "ADRess.h"
//==============================================================================
/**
*/
class StereoSourceSeparationAudioProcessor : public AudioProcessor
{
public:
//==============================================================================
StereoSourceSeparationAudioProcessor();
~StereoSourceSeparationAudioProcessor();
enum Parameters
{
kStatus,
kDirection,
kWidth,
kFilterType,
kCutOffFrequency,
kNumParameters
};
//==============================================================================
void prepareToPlay (double sampleRate, int samplesPerBlock);
void releaseResources();
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages);
//==============================================================================
AudioProcessorEditor* createEditor();
bool hasEditor() const;
//==============================================================================
const String getName() const;
int getNumParameters();
float getParameter (int index);
void setParameter (int index, float newValue);
const String getParameterName (int index);
const String getParameterText (int index);
const String getInputChannelName (int channelIndex) const;
const String getOutputChannelName (int channelIndex) const;
bool isInputChannelStereoPair (int index) const;
bool isOutputChannelStereoPair (int index) const;
bool acceptsMidi() const;
bool producesMidi() const;
bool silenceInProducesSilenceOut() const;
double getTailLengthSeconds() const;
//==============================================================================
int getNumPrograms();
int getCurrentProgram();
void setCurrentProgram (int index);
const String getProgramName (int index);
void changeProgramName (int index, const String& newName);
//==============================================================================
void getStateInformation (MemoryBlock& destData);
void setStateInformation (const void* data, int sizeInBytes);
private:
const int NUM_CHANNELS;
const int BLOCK_SIZE;
const int HOP_SIZE;
const int BETA;
AudioSampleBuffer inputBuffer_;
int inputBufferLength_;
int inputBufferWritePosition_;
AudioSampleBuffer outputBuffer_;
int outputBufferLength_;
int outputBufferReadPosition_, outputBufferWritePosition_;
AudioSampleBuffer processBuffer_;
ADRess* separator_;
int samplesSinceLastFFT_;
ADRess::Status_t status_;
int direction_;
int width_;
ADRess::FilterType_t filterType_;
float cutOffFrequency_;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StereoSourceSeparationAudioProcessor)
};
#endif // PLUGINPROCESSOR_H_INCLUDED

+ 164
- 0
ports/stereosourceseparation/source/kiss_fft/_kiss_fft_guts.h View File

@@ -0,0 +1,164 @@
/*
Copyright (c) 2003-2010, Mark Borgerding

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* kiss_fft.h
defines kiss_fft_scalar as either short or a float type
and defines
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
#include "kiss_fft.h"
#include <limits.h>

#define MAXFACTORS 32
/* e.g. an fft of length 128 has 4 factors
as far as kissfft is concerned
4*4*4*2
*/

struct kiss_fft_state{
int nfft;
int inverse;
int factors[2*MAXFACTORS];
kiss_fft_cpx twiddles[1];
};

/*
Explanation of macros dealing with complex math:

C_MUL(m,a,b) : m = a*b
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
C_SUB( res, a,b) : res = a - b
C_SUBFROM( res , a) : res -= a
C_ADDTO( res , a) : res += a
* */
#ifdef FIXED_POINT
#if (FIXED_POINT==32)
# define FRACBITS 31
# define SAMPPROD int64_t
#define SAMP_MAX 2147483647
#else
# define FRACBITS 15
# define SAMPPROD int32_t
#define SAMP_MAX 32767
#endif

#define SAMP_MIN -SAMP_MAX

#if defined(CHECK_OVERFLOW)
# define CHECK_OVERFLOW_OP(a,op,b) \
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
#endif


# define smul(a,b) ( (SAMPPROD)(a)*(b) )
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )

# define S_MUL(a,b) sround( smul(a,b) )

# define C_MUL(m,a,b) \
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)

# define DIVSCALAR(x,k) \
(x) = sround( smul( x, SAMP_MAX/k ) )

# define C_FIXDIV(c,div) \
do { DIVSCALAR( (c).r , div); \
DIVSCALAR( (c).i , div); }while (0)

# define C_MULBYSCALAR( c, s ) \
do{ (c).r = sround( smul( (c).r , s ) ) ;\
(c).i = sround( smul( (c).i , s ) ) ; }while(0)

#else /* not FIXED_POINT*/

# define S_MUL(a,b) ( (a)*(b) )
#define C_MUL(m,a,b) \
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
# define C_FIXDIV(c,div) /* NOOP */
# define C_MULBYSCALAR( c, s ) \
do{ (c).r *= (s);\
(c).i *= (s); }while(0)
#endif

#ifndef CHECK_OVERFLOW_OP
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
#endif

#define C_ADD( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
}while(0)
#define C_SUB( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
}while(0)
#define C_ADDTO( res , a)\
do { \
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
(res).r += (a).r; (res).i += (a).i;\
}while(0)

#define C_SUBFROM( res , a)\
do {\
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
(res).r -= (a).r; (res).i -= (a).i; \
}while(0)


#ifdef FIXED_POINT
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
# define HALF_OF(x) ((x)>>1)
#elif defined(USE_SIMD)
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
#else
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
# define HALF_OF(x) ((x)*.5)
#endif

#define kf_cexp(x,phase) \
do{ \
(x)->r = KISS_FFT_COS(phase);\
(x)->i = KISS_FFT_SIN(phase);\
}while(0)


/* a debugging function */
#define pcpx(c)\
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )


#ifdef KISS_FFT_USE_ALLOCA
// define this to allow use of alloca instead of malloc for temporary buffers
// Temporary buffers are used in two case:
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
#include <alloca.h>
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
#define KISS_FFT_TMP_FREE(ptr)
#else
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
#endif

+ 408
- 0
ports/stereosourceseparation/source/kiss_fft/kiss_fft.c View File

@@ -0,0 +1,408 @@
/*
Copyright (c) 2003-2010, Mark Borgerding

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


#include "_kiss_fft_guts.h"
/* The guts header contains all the multiplication and addition macros that are defined for
fixed or floating point complex numbers. It also delares the kf_ internal functions.
*/

static void kf_bfly2(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx * Fout2;
kiss_fft_cpx * tw1 = st->twiddles;
kiss_fft_cpx t;
Fout2 = Fout + m;
do{
C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);

C_MUL (t, *Fout2 , *tw1);
tw1 += fstride;
C_SUB( *Fout2 , *Fout , t );
C_ADDTO( *Fout , t );
++Fout2;
++Fout;
}while (--m);
}

static void kf_bfly4(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
const size_t m
)
{
kiss_fft_cpx *tw1,*tw2,*tw3;
kiss_fft_cpx scratch[6];
size_t k=m;
const size_t m2=2*m;
const size_t m3=3*m;


tw3 = tw2 = tw1 = st->twiddles;

do {
C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);

C_MUL(scratch[0],Fout[m] , *tw1 );
C_MUL(scratch[1],Fout[m2] , *tw2 );
C_MUL(scratch[2],Fout[m3] , *tw3 );

C_SUB( scratch[5] , *Fout, scratch[1] );
C_ADDTO(*Fout, scratch[1]);
C_ADD( scratch[3] , scratch[0] , scratch[2] );
C_SUB( scratch[4] , scratch[0] , scratch[2] );
C_SUB( Fout[m2], *Fout, scratch[3] );
tw1 += fstride;
tw2 += fstride*2;
tw3 += fstride*3;
C_ADDTO( *Fout , scratch[3] );

if(st->inverse) {
Fout[m].r = scratch[5].r - scratch[4].i;
Fout[m].i = scratch[5].i + scratch[4].r;
Fout[m3].r = scratch[5].r + scratch[4].i;
Fout[m3].i = scratch[5].i - scratch[4].r;
}else{
Fout[m].r = scratch[5].r + scratch[4].i;
Fout[m].i = scratch[5].i - scratch[4].r;
Fout[m3].r = scratch[5].r - scratch[4].i;
Fout[m3].i = scratch[5].i + scratch[4].r;
}
++Fout;
}while(--k);
}

static void kf_bfly3(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
size_t m
)
{
size_t k=m;
const size_t m2 = 2*m;
kiss_fft_cpx *tw1,*tw2;
kiss_fft_cpx scratch[5];
kiss_fft_cpx epi3;
epi3 = st->twiddles[fstride*m];

tw1=tw2=st->twiddles;

do{
C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);

C_MUL(scratch[1],Fout[m] , *tw1);
C_MUL(scratch[2],Fout[m2] , *tw2);

C_ADD(scratch[3],scratch[1],scratch[2]);
C_SUB(scratch[0],scratch[1],scratch[2]);
tw1 += fstride;
tw2 += fstride*2;

Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);

C_MULBYSCALAR( scratch[0] , epi3.i );

C_ADDTO(*Fout,scratch[3]);

Fout[m2].r = Fout[m].r + scratch[0].i;
Fout[m2].i = Fout[m].i - scratch[0].r;

Fout[m].r -= scratch[0].i;
Fout[m].i += scratch[0].r;

++Fout;
}while(--k);
}

static void kf_bfly5(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
int u;
kiss_fft_cpx scratch[13];
kiss_fft_cpx * twiddles = st->twiddles;
kiss_fft_cpx *tw;
kiss_fft_cpx ya,yb;
ya = twiddles[fstride*m];
yb = twiddles[fstride*2*m];

Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;

tw=st->twiddles;
for ( u=0; u<m; ++u ) {
C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
scratch[0] = *Fout0;

C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);

C_ADD( scratch[7],scratch[1],scratch[4]);
C_SUB( scratch[10],scratch[1],scratch[4]);
C_ADD( scratch[8],scratch[2],scratch[3]);
C_SUB( scratch[9],scratch[2],scratch[3]);

Fout0->r += scratch[7].r + scratch[8].r;
Fout0->i += scratch[7].i + scratch[8].i;

scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);

scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);

C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);

scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);

C_ADD(*Fout2,scratch[11],scratch[12]);