@@ -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 | |||
@@ -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" | |||
} |
@@ -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" | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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__) */ |
@@ -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__ |
@@ -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__ |
@@ -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(); | |||
} |
@@ -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 |
@@ -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(); | |||
} |
@@ -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 |
@@ -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 |
@@ -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]); | |||