mirror of https://github.com/probonopd/MiniDexed
parent
f14ac0a967
commit
926ba5339e
@ -0,0 +1,117 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#if defined(DEBUG) |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
#include <string> |
||||||
|
#include <iostream> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
typedef size_t (*ValueInpector)(const std::string& tag, const float32_t valueToTest, const float32_t _min, float32_t _max, bool outDebugInfo); |
||||||
|
|
||||||
|
inline size_t nanValueInspector(const std::string& tag, const float32_t valueToTest, const float32_t _min, float32_t _max, bool outDebugInfo) |
||||||
|
{ |
||||||
|
if(std::isnan(valueToTest)) |
||||||
|
{ |
||||||
|
if(outDebugInfo) |
||||||
|
{ |
||||||
|
std::cerr << tag << ": nan" << std::endl; |
||||||
|
} |
||||||
|
return 1u; |
||||||
|
} |
||||||
|
|
||||||
|
return 0u; |
||||||
|
} |
||||||
|
|
||||||
|
inline size_t normalizedValueInspector(const std::string& tag, const float32_t valueToTest, const float32_t _min, float32_t _max, bool outDebugInfo) |
||||||
|
{ |
||||||
|
if(valueToTest > _max || valueToTest < _min) |
||||||
|
{ |
||||||
|
if(outDebugInfo) |
||||||
|
{ |
||||||
|
std::cerr << tag << ": out of bounds - value: " << valueToTest << " - boundaries: [" << _min << ", " << _max << "]" << std::endl; |
||||||
|
} |
||||||
|
return 1u; |
||||||
|
} |
||||||
|
|
||||||
|
return 0u; |
||||||
|
} |
||||||
|
|
||||||
|
inline size_t fullInspector(const std::string& tag, const float32_t valueToTest, const float32_t _min = -1.0f, float32_t _max = 1.0f, bool outDebugInfo = true) |
||||||
|
{ |
||||||
|
if(std::isnan(valueToTest)) |
||||||
|
{ |
||||||
|
if(outDebugInfo) |
||||||
|
{ |
||||||
|
std::cerr << tag << ": nan" << std::endl; |
||||||
|
} |
||||||
|
return 1u; |
||||||
|
} |
||||||
|
else if(valueToTest > _max || valueToTest < _min) |
||||||
|
{ |
||||||
|
if(outDebugInfo) |
||||||
|
{ |
||||||
|
std::cerr << tag << ": out of bounds - value: " << valueToTest << " - boundaries: [" << _min << ", " << _max << "]" << std::endl; |
||||||
|
} |
||||||
|
return 1u; |
||||||
|
} |
||||||
|
|
||||||
|
return 0u; |
||||||
|
} |
||||||
|
|
||||||
|
#define SS_RESET(ss, prec, align) ss.str(""); ss.precision(prec); ss << align; ss << std::fixed |
||||||
|
#define SS_SPACE(ss, spc, nb, align, sep) ss.fill(spc); ss.width(nb); ss << "" << sep |
||||||
|
#define SS__TEXT(ss, spc, nb, align, sep, txt) ss << align; ss.fill(spc); ss.width(nb); ss << txt << sep |
||||||
|
|
||||||
|
class ValueInpectorDebugger |
||||||
|
{ |
||||||
|
public: |
||||||
|
ValueInpectorDebugger() {} |
||||||
|
virtual ~ValueInpectorDebugger() {} |
||||||
|
|
||||||
|
virtual void dump(std::ostream& out, bool deepInspection = true, const std::string& tag = "") const = 0; |
||||||
|
virtual size_t inspect(ValueInpector inspector, bool deepInspection = true, const std::string& tag = "") const = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
#define INSPECTABLE(clazz) clazz : public ValueInpectorDebugger |
||||||
|
#define IMPLEMENT_DUMP(code) \ |
||||||
|
public:\
|
||||||
|
virtual void dump(std::ostream& out, bool deepInspection = true, const std::string& tag = "") const override\
|
||||||
|
{\
|
||||||
|
code\
|
||||||
|
} |
||||||
|
|
||||||
|
#define DUMP(clazz, out) clazz->dump(out, true, "") |
||||||
|
#define DUMP2(clazz, out, tag) clazz->dump(out, true, tag) |
||||||
|
#define FAST_DUMP(clazz, out, tag) clazz->dump(out, false, "") |
||||||
|
#define FAST_DUMP2(clazz, out, tag) clazz->dump(out, false, tag) |
||||||
|
|
||||||
|
#define IMPLEMENT_INSPECT(code) \ |
||||||
|
public:\
|
||||||
|
virtual size_t inspect(ValueInpector inspector, bool deepInspection = true, const std::string& tag = "") const override\
|
||||||
|
{\
|
||||||
|
code\
|
||||||
|
} |
||||||
|
|
||||||
|
#define INSPECT(obj, inspector) obj->inspect(inspector, true) |
||||||
|
#define INSPECT2(obj, inspector, deepInspection) obj->inspect(inspector, deepInspection) |
||||||
|
#define FULL_INSPECT(obj, deepInspection) obj->inspect(fullInspector, deepInspection) |
||||||
|
#define FULL_INSPECT2(obj, deepInspection, tag) obj->inspect(fullInspector, deepInspection, tag) |
||||||
|
|
||||||
|
#else |
||||||
|
|
||||||
|
#define INSPECTABLE(clazz) clazz |
||||||
|
#define IMPLEMENT_DUMP(code) |
||||||
|
#define IMPLEMENT_INSPECT(code) |
||||||
|
|
||||||
|
#define DUMP(clazz, out) |
||||||
|
#define DUMP2(clazz, out, tag) |
||||||
|
#define FAST_DUMP(clazz, out, tag) |
||||||
|
#define FAST_DUMP2(clazz, out, tag) |
||||||
|
|
||||||
|
#define INSPECT(obj, inspector) |
||||||
|
#define INSPECT2(obj, inspector, deepInspection) |
||||||
|
#define FULL_INSPECT(obj, deepInspection) |
||||||
|
#define FULL_INSPECT2(obj, deepInspection, tag) |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,10 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "extra_features.h" |
||||||
|
|
||||||
|
enum StereoChannels |
||||||
|
{ |
||||||
|
Left = 0, |
||||||
|
Right, |
||||||
|
kNumChannels |
||||||
|
}; |
@ -1,92 +1,63 @@ |
|||||||
CXX := gcc
|
OBJDIR := objects
|
||||||
# CXXFLAGS := -O2
|
OUTPUT_FOLDER = results
|
||||||
CXXFLAGS := -g
|
EXE := all_test.bin
|
||||||
DEFINES := -DCPU=x86
|
BETA := beta.bin
|
||||||
INCLUDES := -I../../CMSIS_5/CMSIS/DSP/Include/ -I../../CMSIS_5/CMSIS/Core/Include/
|
|
||||||
GCC := $(CXX) $(INCLUDES) $(CXXFLAGS)
|
|
||||||
|
|
||||||
LD := gcc
|
CXX := g++
|
||||||
LIBS := -lm -lstdc++
|
CXXFLAGS = -g -std=c++20 -MMD -MP
|
||||||
|
DEFINES = -DCPU=x86 -DDEBUG -DOUTPUT_FOLDER="\"$(OUTPUT_FOLDER)\""
|
||||||
|
INCLUDES = -I../../CMSIS_5/CMSIS/DSP/Include/ \
|
||||||
|
-I../../CMSIS_5/CMSIS/Core/Include/ \
|
||||||
|
-I../../Synth_Dexed/src/
|
||||||
|
|
||||||
OBJS := \
|
-include $(TST_OBJS:.o=.d) |
||||||
wavein.o \
|
-include $(FX__OBJS:.o=.d) |
||||||
waveout.o \
|
|
||||||
fx.o \
|
|
||||||
fx_components.o \
|
|
||||||
fx_svf.o \
|
|
||||||
fx_tube.o \
|
|
||||||
fx_chorus.o \
|
|
||||||
fx_phaser.o \
|
|
||||||
fx_orbitone.o \
|
|
||||||
fx_flanger.o \
|
|
||||||
fx_delay.o \
|
|
||||||
fx_shimmer_reverb.o \
|
|
||||||
fx_rack.o \
|
|
||||||
fxrack_test.o
|
|
||||||
|
|
||||||
test: fxrack_test |
LD := g++
|
||||||
./fxrack_test
|
LIBS := -lm -lstdc++ -lgtest -lpthread
|
||||||
|
|
||||||
# %.o: ../%.cpp
|
FX__SRCS := ../fx.cpp
|
||||||
# $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
FX__SRCS += ../fx_components.cpp
|
||||||
|
FX__SRCS += ../fx_svf.cpp
|
||||||
|
FX__SRCS += ../fx_tube.cpp
|
||||||
|
FX__SRCS += ../fx_chorus.cpp
|
||||||
|
FX__SRCS += ../fx_phaser.cpp
|
||||||
|
FX__SRCS += ../fx_orbitone.cpp
|
||||||
|
FX__SRCS += ../fx_flanger.cpp
|
||||||
|
FX__SRCS += ../fx_delay.cpp
|
||||||
|
FX__SRCS += ../effect_platervbstereo.cpp
|
||||||
|
FX__SRCS += ../fx_shimmer_reverb.cpp
|
||||||
|
FX__SRCS += ../fx_rack.cpp
|
||||||
|
|
||||||
wavein.o: wavein.cpp |
TST_SRCS := $(filter-out waveplay.cpp, $(wildcard *.cpp))
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
waveout.o: waveout.cpp |
FX__OBJS = $(patsubst ../%, $(OBJDIR)/%, $(FX__SRCS:.cpp=.o))
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
TST_OBJS = $(TST_SRCS:%.cpp=$(OBJDIR)/%.o)
|
||||||
|
|
||||||
# waveplay.o: waveplay.cpp
|
all: $(EXE) test |
||||||
# $(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
fx.o: ../fx.cpp |
test: $(EXE) $(OUTPUT_FOLDER) |
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
rm -rf $(OUTPUT_FOLDER)/*
|
||||||
|
./$(EXE)
|
||||||
|
|
||||||
fx_components.o: ../fx_components.cpp |
test-debug: $(EXE) $(OUTPUT_FOLDER) |
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
rm -rf $(OUTPUT_FOLDER)/*
|
||||||
|
valgrind --leak-check=full --leak-resolution=high --show-leak-kinds=all --xtree-leak=yes --show-mismatched-frees=yes --error-limit=no --log-file=$(OUTPUT_FOLDER)/valgrind-analysis-results.txt ./$(EXE)
|
||||||
|
|
||||||
fx_svf.o: ../fx_svf.cpp |
$(OBJDIR): |
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
mkdir -p $@
|
||||||
|
|
||||||
fx_tube.o: ../fx_tube.cpp |
$(OUTPUT_FOLDER): |
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
mkdir -p $@
|
||||||
|
|
||||||
../fx_chorus.cpp: ../fx_engine.hpp |
$(OBJDIR)/%.o: %.cpp $(OBJDIR) |
||||||
touch ../fx_chorus.cpp
|
$(CXX) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
fx_chorus.o: ../fx_chorus.cpp |
$(OBJDIR)/%.o: ../%.cpp $(OBJDIR) |
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
$(CXX) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
fx_phaser.o: ../fx_phaser.cpp |
$(EXE): $(TST_OBJS) $(FX__OBJS) |
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
$(LD) $(CXXFLAGS) $(call wildcard,$(TST_OBJS)) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS)
|
||||||
|
|
||||||
../fx_orbitone.cpp: ../fx_engine.hpp |
|
||||||
touch ../fx_orbitone.cpp
|
|
||||||
|
|
||||||
fx_orbitone.o: ../fx_orbitone.cpp |
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
fx_flanger.o: ../fx_flanger.cpp |
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
fx_delay.o: ../fx_delay.cpp |
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
../fx_shimmer_reverb.cpp: ../fx_engine.hpp |
|
||||||
touch ../fx_shimmer_reverb.cpp
|
|
||||||
|
|
||||||
fx_shimmer_reverb.o: ../fx_shimmer_reverb.cpp |
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
fx_rack.o: ../fx_rack.cpp |
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
fxrack_test.o: fxrack_test.cpp |
|
||||||
$(CXX) $(DEFINES) $(INCLUDES) $(CXXFLAGS) -c $^ -o $@
|
|
||||||
|
|
||||||
|
|
||||||
fxrack_test: $(OBJS) |
|
||||||
$(LD) $(OBJS) -o fxrack_test $(LIBS)
|
|
||||||
|
|
||||||
clean: |
clean: |
||||||
rm -f *.o fxrack_test
|
rm -rf *.o $(OBJDIR) $(EXE) $(OUTPUT_FOLDER)
|
||||||
|
@ -0,0 +1,7 @@ |
|||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
int main(int argc, char **argv) |
||||||
|
{ |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
#include <arm_math.h> |
||||||
|
|
||||||
|
float32_t arm_sin_f32(float32_t phase) |
||||||
|
{ |
||||||
|
return sin(phase); |
||||||
|
} |
||||||
|
|
||||||
|
float32_t arm_cos_f32(float32_t phase) |
||||||
|
{ |
||||||
|
return cos(phase); |
||||||
|
} |
||||||
|
|
||||||
|
void arm_scale_f32(const float32_t *pSrc, float32_t scale, float32_t *pDst, uint32_t blockSize) |
||||||
|
{ |
||||||
|
for(unsigned i = 0; i < blockSize; ++i) |
||||||
|
{ |
||||||
|
pDst[i] = scale * pSrc[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void arm_copy_f32(const float32_t *pSrc, float32_t *pDst, uint32_t blockSize) |
||||||
|
{ |
||||||
|
memcpy(pDst, pSrc, blockSize * sizeof(float32_t)); |
||||||
|
} |
||||||
|
|
||||||
|
void arm_add_f32(const float32_t *pSrcA, const float32_t *pSrcB, float32_t *pDst, uint32_t blockSize) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < blockSize; ++i) pDst[i] = pSrcA[i] + pSrcB[i]; |
||||||
|
} |
||||||
|
|
||||||
|
void arm_fill_f32(float32_t value, float32_t *pDst, uint32_t blockSize) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < blockSize; ++i) pDst[i] = value; |
||||||
|
} |
||||||
|
|
||||||
|
float32_t arm_weighted_sum_f32(const float32_t *in, const float32_t *weights, uint32_t blockSize) |
||||||
|
{ |
||||||
|
float32_t m = 0.0f; |
||||||
|
for(size_t i = 0; i < blockSize; ++i) m += in[i] * weights[i]; |
||||||
|
return m; |
||||||
|
} |
||||||
|
|
||||||
|
void arm_clip_f32(const float32_t *pSrc, float32_t *pDst, float32_t low, float32_t high, uint32_t numSamples) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < numSamples; ++i) pDst[i] = (pSrc[i] < low) ? low : (pSrc[i] > high) ? high : pSrc[i]; |
||||||
|
} |
@ -1,235 +0,0 @@ |
|||||||
#include "../fx_rack.h" |
|
||||||
|
|
||||||
#include <iomanip> |
|
||||||
#include <iostream> |
|
||||||
#include <fstream> |
|
||||||
#include <locale> |
|
||||||
#include <ctime> |
|
||||||
#include <random> |
|
||||||
#include "wave.h" |
|
||||||
|
|
||||||
using namespace std; |
|
||||||
|
|
||||||
#define FS 44100.0f |
|
||||||
#define MAX_SVF_SAMPLES 10000000 |
|
||||||
#define MAX_NB_ERRORS 100 |
|
||||||
|
|
||||||
std::random_device rd; |
|
||||||
std::mt19937 gen(rd()); |
|
||||||
std::uniform_real_distribution<float32_t> dist(-1.0f, 1.0f); |
|
||||||
|
|
||||||
void testLFO(unsigned& step) |
|
||||||
{ |
|
||||||
cout << "Step #" << (++step) << ": Testing LFO" << endl; |
|
||||||
|
|
||||||
const float32_t freq = 10.0f; |
|
||||||
|
|
||||||
LFO lfo(FS, LFO::Waveform::Sine, 0.0f, freq); |
|
||||||
unsigned size = static_cast<unsigned>(8.0f * FS / freq); |
|
||||||
float32_t rate = 0.0f; |
|
||||||
float32_t rate_increment = freq / 2.0f / FS; |
|
||||||
|
|
||||||
// float32_t* output = new float32_t[size];
|
|
||||||
ofstream out("result.csv"); |
|
||||||
|
|
||||||
struct comma_separator : std::numpunct<char> |
|
||||||
{ |
|
||||||
virtual char do_decimal_point() const override { return ','; } |
|
||||||
}; |
|
||||||
|
|
||||||
out.imbue(std::locale(out.getloc(), new comma_separator)); |
|
||||||
out << fixed << showpoint; |
|
||||||
|
|
||||||
out << "index;LFO" << endl; |
|
||||||
for(unsigned i = 0; i < size; ++i) |
|
||||||
{ |
|
||||||
lfo.setNormalizedFrequency(rate); |
|
||||||
out << i << ";" << lfo.process() << endl; |
|
||||||
rate += rate_increment; |
|
||||||
|
|
||||||
if(rate >= 1.0f || rate <= 0.0f) |
|
||||||
{ |
|
||||||
rate_increment *= -1.0f; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void testFlutter(unsigned& step) |
|
||||||
{ |
|
||||||
cout << "Step #" << (++step) << ": Testing JitterGenerator" << endl; |
|
||||||
|
|
||||||
JitterGenerator jg(FS); |
|
||||||
jg.setSpeed(1.0f); |
|
||||||
jg.setMagnitude(0.1f); |
|
||||||
|
|
||||||
for (unsigned i = 0; i < 1000; ++i) |
|
||||||
{ |
|
||||||
cout << jg.process() << endl; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void testSVF(unsigned& step) |
|
||||||
{ |
|
||||||
float32_t inL, inR; |
|
||||||
float32_t outL, outR; |
|
||||||
StateVariableFilter svf(FS, StateVariableFilter::Type::LPF, 12000.0f); |
|
||||||
|
|
||||||
cout << "Step #" << (++step) << ": Testing SVF in LPF mode" << endl; |
|
||||||
{ |
|
||||||
svf.setFilterType(StateVariableFilter::Type::LPF); |
|
||||||
svf.setCutoff(12000.0f); |
|
||||||
svf.setResonance(0.0f); |
|
||||||
unsigned nbSamples = 0; |
|
||||||
unsigned nbErrors = 0; |
|
||||||
while (nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) |
|
||||||
{ |
|
||||||
nbSamples++; |
|
||||||
inL = dist(gen); |
|
||||||
inR = dist(gen); |
|
||||||
svf.processSample(inL, inR, outL, outR); |
|
||||||
|
|
||||||
if (std::abs(outL) > 1.0f) |
|
||||||
nbErrors++; |
|
||||||
if (std::abs(outR) > 1.0f) |
|
||||||
nbErrors++; |
|
||||||
} |
|
||||||
cout << "nbSamples: " << nbSamples << " -- nbErrors: " << nbErrors << endl; |
|
||||||
} |
|
||||||
|
|
||||||
cout << "Step #" << (++step) << ": Testing SVF in HPF mode" << endl; |
|
||||||
{ |
|
||||||
svf.setFilterType(StateVariableFilter::Type::LPF); |
|
||||||
svf.setCutoff(60.0f); |
|
||||||
svf.setResonance(0.0f); |
|
||||||
unsigned nbSamples = 0; |
|
||||||
unsigned nbErrors = 0; |
|
||||||
while (nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) |
|
||||||
{ |
|
||||||
nbSamples++; |
|
||||||
inL = dist(gen); |
|
||||||
inR = dist(gen); |
|
||||||
svf.processSample(inL, inR, outL, outR); |
|
||||||
|
|
||||||
if (std::abs(outL) > 1.0f) |
|
||||||
nbErrors++; |
|
||||||
if (std::abs(outR) > 1.0f) |
|
||||||
nbErrors++; |
|
||||||
} |
|
||||||
cout << "nbSamples: " << nbSamples << " -- nbErrors: " << nbErrors << endl; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
enum FXSitch |
|
||||||
{ |
|
||||||
Tube = 1 << 0, |
|
||||||
Chorus = 1 << 1, |
|
||||||
Phaser = 1 << 2, |
|
||||||
Orbitone = 1 << 3, |
|
||||||
Flanger = 1 << 4, |
|
||||||
Delay = 1 << 5, |
|
||||||
Shimmer = 1 << 6 |
|
||||||
}; |
|
||||||
|
|
||||||
#define Active(fxSwitch, FxID) (fxSwitch & FxID) == FxID |
|
||||||
|
|
||||||
void testFXRack(unsigned& step, unsigned fxSwitch) |
|
||||||
{ |
|
||||||
cout << "Step #" << (++step) << ": Intanciation FXRack" << endl; |
|
||||||
FXRack *rack = new FXRack(44100.0f); |
|
||||||
|
|
||||||
cout << "Step #" << (++step) << ": Test preparation" << endl; |
|
||||||
rack->setEnable(true); |
|
||||||
rack->setWetLevel(1.0f); |
|
||||||
|
|
||||||
rack->getTube()->setEnable(Active(fxSwitch, FXSitch::Tube)); |
|
||||||
rack->getTube()->setWetLevel(0.25f); |
|
||||||
rack->getTube()->setOverdrive(0.25f); |
|
||||||
|
|
||||||
rack->getChorus()->setEnable(Active(fxSwitch, FXSitch::Chorus)); |
|
||||||
rack->getChorus()->setWetLevel(0.5f); |
|
||||||
rack->getChorus()->setRate(0.4f); |
|
||||||
rack->getChorus()->setDepth(0.5f); |
|
||||||
|
|
||||||
rack->getPhaser()->setEnable(Active(fxSwitch, FXSitch::Phaser)); |
|
||||||
rack->getPhaser()->setWetLevel(1.0f); |
|
||||||
rack->getPhaser()->setRate(0.1f); |
|
||||||
rack->getPhaser()->setDepth(1.0f); |
|
||||||
rack->getPhaser()->setFeedback(0.5f); |
|
||||||
rack->getPhaser()->setNbStages(12); |
|
||||||
|
|
||||||
rack->getOrbitone()->setEnable(Active(fxSwitch, FXSitch::Orbitone)); |
|
||||||
rack->getOrbitone()->setWetLevel(0.8f); |
|
||||||
rack->getOrbitone()->setRate(0.4f); |
|
||||||
rack->getOrbitone()->setDepth(0.5f); |
|
||||||
|
|
||||||
rack->getFlanger()->setEnable(Active(fxSwitch, FXSitch::Flanger)); |
|
||||||
rack->getFlanger()->setWetLevel(0.5f); |
|
||||||
rack->getFlanger()->setRate(0.03f); |
|
||||||
rack->getFlanger()->setDepth(0.75f); |
|
||||||
rack->getFlanger()->setFeedback(0.5f); |
|
||||||
|
|
||||||
rack->getDelay()->setEnable(Active(fxSwitch, FXSitch::Delay)); |
|
||||||
rack->getDelay()->setWetLevel(0.6f); |
|
||||||
rack->getDelay()->setLeftDelayTime(0.075f); |
|
||||||
rack->getDelay()->setLeftDelayTime(0.05f); |
|
||||||
rack->getDelay()->setFeedbak(0.5f); |
|
||||||
|
|
||||||
rack->getShimmerReverb()->setEnable(Active(fxSwitch, FXSitch::Shimmer)); |
|
||||||
rack->getShimmerReverb()->setWetLevel(0.5f); |
|
||||||
rack->getShimmerReverb()->setInputGain(0.35f); |
|
||||||
rack->getShimmerReverb()->setTime(0.89f); |
|
||||||
rack->getShimmerReverb()->setDiffusion(0.75f); |
|
||||||
rack->getShimmerReverb()->setLP(0.8f); |
|
||||||
|
|
||||||
unsigned nbRepeats = 4; |
|
||||||
|
|
||||||
unsigned size; |
|
||||||
float32_t** samples = readWaveFile("test.wav", size); |
|
||||||
float32_t* sampleOutL = new float32_t[size * nbRepeats]; |
|
||||||
float32_t* sampleOutR = new float32_t[size * nbRepeats]; |
|
||||||
memset(sampleOutL, 0, size * nbRepeats * sizeof(float32_t)); |
|
||||||
memset(sampleOutR, 0, size * nbRepeats * sizeof(float32_t)); |
|
||||||
|
|
||||||
for (unsigned i = 0; i < nbRepeats; ++i) |
|
||||||
{ |
|
||||||
rack->process(samples[0], samples[1], sampleOutL + i * size, sampleOutR + i * size, size); |
|
||||||
} |
|
||||||
|
|
||||||
saveWaveFile("result.wav", sampleOutL, sampleOutR, nbRepeats * size, static_cast<unsigned>(FS), 16); |
|
||||||
|
|
||||||
delete[] sampleOutL; |
|
||||||
delete[] sampleOutR; |
|
||||||
delete[] samples[0]; |
|
||||||
delete[] samples[1]; |
|
||||||
delete[] samples; |
|
||||||
|
|
||||||
cout << "Step #" << (++step) << ": Test cleanup" << endl; |
|
||||||
delete rack; |
|
||||||
} |
|
||||||
|
|
||||||
int main() |
|
||||||
{ |
|
||||||
unsigned step = 0; |
|
||||||
|
|
||||||
// testLFO(step);
|
|
||||||
// testFlutter(step);
|
|
||||||
// testSVF(step);
|
|
||||||
// testFXRack(step, FXSitch::Tube);
|
|
||||||
// testFXRack(step, FXSitch::Flanger);
|
|
||||||
// testFXRack(step, FXSitch::Phaser);
|
|
||||||
// testFXRack(step, FXSitch::Chorus);
|
|
||||||
// testFXRack(step, FXSitch::Orbitone);
|
|
||||||
// testFXRack(step, FXSitch::Delay);
|
|
||||||
// testFXRack(step, FXSitch::Shimmer);
|
|
||||||
testFXRack( |
|
||||||
step, |
|
||||||
FXSitch::Tube |
|
|
||||||
FXSitch::Chorus |
|
|
||||||
FXSitch::Flanger |
|
|
||||||
FXSitch::Orbitone |
|
|
||||||
FXSitch::Phaser |
|
|
||||||
FXSitch::Delay |
|
|
||||||
FXSitch::Shimmer); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
@ -0,0 +1,69 @@ |
|||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
#include "../fx_components.h" |
||||||
|
#include <chrono> |
||||||
|
#include <fstream> |
||||||
|
|
||||||
|
int nb = 0; |
||||||
|
|
||||||
|
int NbIteration() { |
||||||
|
nb++; |
||||||
|
return 3; |
||||||
|
} |
||||||
|
|
||||||
|
TEST(Cpp, NbCallsInUpperBoudariesInForLoop) |
||||||
|
{ |
||||||
|
for(int i = 0; i < NbIteration(); ++i) |
||||||
|
{ |
||||||
|
// Does something
|
||||||
|
} |
||||||
|
EXPECT_EQ(nb, 4); |
||||||
|
} |
||||||
|
|
||||||
|
#define CLASS_INIT(clazz) clazz::StaticInit() |
||||||
|
class StaticCtorTest |
||||||
|
{ |
||||||
|
private: |
||||||
|
static int n_; |
||||||
|
|
||||||
|
public: |
||||||
|
int i_; |
||||||
|
|
||||||
|
static int StaticInit() |
||||||
|
{ |
||||||
|
static int i = 0; |
||||||
|
i++; |
||||||
|
|
||||||
|
StaticCtorTest::n_ = 2; |
||||||
|
|
||||||
|
return i; |
||||||
|
} |
||||||
|
|
||||||
|
StaticCtorTest() : i_(0) |
||||||
|
{ |
||||||
|
static int init = CLASS_INIT(StaticCtorTest); |
||||||
|
static int NB = 0; |
||||||
|
EXPECT_EQ(init, 1); |
||||||
|
|
||||||
|
this->i_ = ++NB; |
||||||
|
|
||||||
|
EXPECT_EQ(StaticCtorTest::n_, 2); |
||||||
|
} |
||||||
|
|
||||||
|
~StaticCtorTest() |
||||||
|
{ |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
int StaticCtorTest::n_ = 0; |
||||||
|
|
||||||
|
TEST(Cpp, StaticCtorTest) |
||||||
|
{ |
||||||
|
StaticCtorTest obj1; |
||||||
|
StaticCtorTest obj2; |
||||||
|
StaticCtorTest obj3; |
||||||
|
|
||||||
|
EXPECT_EQ(obj1.i_, 1); |
||||||
|
EXPECT_EQ(obj2.i_, 2); |
||||||
|
EXPECT_EQ(obj3.i_, 3); |
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
#include <fstream> |
||||||
|
|
||||||
|
#include "test_fx_helper.h" |
||||||
|
#include "../fx_components.h" |
||||||
|
|
||||||
|
TEST(CppPerformance, LFOPerformance_ComplexLFO_InterpolatedSineOscillator) |
||||||
|
{ |
||||||
|
const size_t NB = 10000000; |
||||||
|
float32_t freq = 0.1f; |
||||||
|
|
||||||
|
ComplexLFO lfo1(SAMPLING_FREQUENCY, 0.0f, 10.0f); |
||||||
|
InterpolatedSineOscillator lfo2(SAMPLING_FREQUENCY, 0.0f, 10.0f); |
||||||
|
|
||||||
|
lfo1.setFrequency(freq); |
||||||
|
LAP_TIME("lfo1"); |
||||||
|
for(size_t i = 0; i < NB; ++i) |
||||||
|
{ |
||||||
|
lfo1.process(); |
||||||
|
} |
||||||
|
auto d1 = LAP_TIME("lfo1"); |
||||||
|
|
||||||
|
lfo2.setFrequency(freq); |
||||||
|
LAP_TIME("lfo2"); |
||||||
|
for(size_t i = 0; i < NB; ++i) |
||||||
|
{ |
||||||
|
lfo2.process(); |
||||||
|
} |
||||||
|
auto d2 = LAP_TIME("lfo2"); |
||||||
|
|
||||||
|
EXPECT_LE(d1, d2 + 100); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(CppPerformance, LFOPerformance_ComplexLFO_FastLFO) |
||||||
|
{ |
||||||
|
const size_t NB = 10000000; |
||||||
|
float32_t freq = 0.1f; |
||||||
|
|
||||||
|
ComplexLFO lfo1(SAMPLING_FREQUENCY, 0.0f, 10.0f, Constants::MPI_2); |
||||||
|
FastLFO lfo2(SAMPLING_FREQUENCY, 0.0f, 10.0f); |
||||||
|
|
||||||
|
lfo1.setFrequency(freq); |
||||||
|
LAP_TIME("lfo1"); |
||||||
|
for(size_t i = 0; i < NB; ++i) |
||||||
|
{ |
||||||
|
lfo1.process(); |
||||||
|
} |
||||||
|
auto d1 = LAP_TIME("lfo1"); |
||||||
|
|
||||||
|
lfo2.setFrequency(freq); |
||||||
|
LAP_TIME("lfo2"); |
||||||
|
for(size_t i = 0; i < NB; ++i) |
||||||
|
{ |
||||||
|
lfo2.process(); |
||||||
|
} |
||||||
|
auto d2 = LAP_TIME("lfo2"); |
||||||
|
|
||||||
|
EXPECT_GE(d1, d2); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(CppPerformance, FastLFOTuning) |
||||||
|
{ |
||||||
|
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||||
|
std::string full_test_name = test_info->test_case_name(); |
||||||
|
full_test_name += "."; |
||||||
|
full_test_name += test_info->name(); |
||||||
|
|
||||||
|
size_t NB = static_cast<size_t>(1.0f * SAMPLING_FREQUENCY); |
||||||
|
float32_t freq = 5.0f; |
||||||
|
|
||||||
|
FastLFO lfo1(SAMPLING_FREQUENCY, freq, 440.0f); |
||||||
|
lfo1.setFrequency(freq); |
||||||
|
|
||||||
|
ComplexLFO lfo2(SAMPLING_FREQUENCY, freq, 440.0f); |
||||||
|
lfo2.setFrequency(freq); |
||||||
|
|
||||||
|
std::ofstream out(getResultFile(full_test_name + ".FastLFOTuning-data.csv", true)); |
||||||
|
setupOuputStreamForCSV(out); |
||||||
|
out << "index;FastLFO;ComplexLFO" << std::endl; |
||||||
|
for(size_t i = 0; i < NB; ++i) |
||||||
|
{ |
||||||
|
out
|
||||||
|
<< i << ";"
|
||||||
|
<< lfo1.process() << ";"
|
||||||
|
<< lfo2.process() << std::endl; |
||||||
|
} |
||||||
|
out.close(); |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
#include "test_fx_helper.h" |
||||||
|
#include "wave.h" |
||||||
|
|
||||||
|
#include "../debug.hpp" |
||||||
|
#include "../fx_base.h" |
||||||
|
|
||||||
|
TEST(Framework, TestWaveIn) |
||||||
|
{ |
||||||
|
size_t size; |
||||||
|
float32_t** samples = readWaveFile(AUDIO_SOURCE_FILE, size); |
||||||
|
|
||||||
|
size_t nb_errors = 0; |
||||||
|
for(size_t i = 0; i < size; ++i) |
||||||
|
{ |
||||||
|
nb_errors += fullInspector("L", samples[StereoChannels::Left ][i], -1.0f, 1.0f, true); |
||||||
|
nb_errors += fullInspector("R", samples[StereoChannels::Right][i], -1.0f, 1.0f, true); |
||||||
|
} |
||||||
|
|
||||||
|
EXPECT_EQ(nb_errors, 0); |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
#include "test_fx_helper.h" |
||||||
|
#include "wave.h" |
||||||
|
|
||||||
|
#include "../effect_platervbstereo.h" |
||||||
|
|
||||||
|
TEST(FXElement, PlateReverbMigration) |
||||||
|
{ |
||||||
|
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||||
|
std::string full_test_name = test_info->test_case_name(); |
||||||
|
full_test_name += "."; |
||||||
|
full_test_name += test_info->name(); |
||||||
|
|
||||||
|
const unsigned nbRepeats = 4; |
||||||
|
|
||||||
|
AudioEffectPlateReverb* reverb = new AudioEffectPlateReverb(SAMPLING_FREQUENCY); |
||||||
|
reverb->set_bypass(false); |
||||||
|
reverb->size(0.7f); |
||||||
|
reverb->hidamp(0.5f); |
||||||
|
reverb->lodamp(0.5f); |
||||||
|
reverb->lowpass(0.3f); |
||||||
|
reverb->diffusion(0.65f); |
||||||
|
reverb->level(1.0f); |
||||||
|
|
||||||
|
size_t size; |
||||||
|
float32_t** samples = readWaveFile(AUDIO_SOURCE_FILE, size); |
||||||
|
float32_t* sampleOutL = new float32_t[size * nbRepeats]; |
||||||
|
float32_t* sampleOutR = new float32_t[size * nbRepeats]; |
||||||
|
memset(sampleOutL, 0, size * nbRepeats * sizeof(float32_t)); |
||||||
|
memset(sampleOutR, 0, size * nbRepeats * sizeof(float32_t)); |
||||||
|
|
||||||
|
unsigned index = 0; |
||||||
|
for(unsigned i = 0; i < nbRepeats; ++i) |
||||||
|
{ |
||||||
|
for(unsigned j = 0; j < size; ++j) |
||||||
|
{ |
||||||
|
reverb->processSample(samples[0][j], samples[1][j], sampleOutL[index], sampleOutR[index]); |
||||||
|
++index; |
||||||
|
} |
||||||
|
} |
||||||
|
saveWaveFile(getResultFile(full_test_name + ".PlateReverb-new.wav", true), sampleOutL, sampleOutR, nbRepeats * size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||||
|
|
||||||
|
unsigned indexOut = 0; |
||||||
|
for (unsigned i = 0; i < nbRepeats; ++i) |
||||||
|
{ |
||||||
|
unsigned len = size; |
||||||
|
unsigned indexIn = 0; |
||||||
|
|
||||||
|
while(len > 0) |
||||||
|
{ |
||||||
|
unsigned grainSize = (len < 1024 ? len : 1024); |
||||||
|
|
||||||
|
reverb->doReverb(samples[0] + indexIn, samples[1] + indexIn, sampleOutL + indexOut, sampleOutR + indexOut, grainSize); |
||||||
|
|
||||||
|
indexIn += grainSize; |
||||||
|
indexOut += grainSize; |
||||||
|
len -= grainSize; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
saveWaveFile(getResultFile(full_test_name + ".PlateReverb-legacy.wav", true), sampleOutL, sampleOutR, nbRepeats * size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||||
|
|
||||||
|
delete[] sampleOutL; |
||||||
|
delete[] sampleOutR; |
||||||
|
delete[] samples[0]; |
||||||
|
delete[] samples[1]; |
||||||
|
delete[] samples; |
||||||
|
|
||||||
|
delete reverb; |
||||||
|
} |
@ -0,0 +1,156 @@ |
|||||||
|
#include <gtest/gtest.h> |
||||||
|
#include <iomanip> |
||||||
|
#include <iostream> |
||||||
|
#include <fstream> |
||||||
|
#include <sstream> |
||||||
|
#include <string> |
||||||
|
#include <locale> |
||||||
|
#include <ctime> |
||||||
|
#include <cmath> |
||||||
|
#include <random> |
||||||
|
#include "wave.h" |
||||||
|
|
||||||
|
#include "test_fx_helper.h" |
||||||
|
|
||||||
|
#include "../fx_rack.h" |
||||||
|
#include "../effect_platervbstereo.h" |
||||||
|
|
||||||
|
#define MAX_SVF_SAMPLES 10000000 |
||||||
|
#define MAX_NB_ERRORS 100 |
||||||
|
|
||||||
|
TEST(FXComponent, LFO) |
||||||
|
{ |
||||||
|
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||||
|
std::string full_test_name = test_info->test_case_name(); |
||||||
|
full_test_name += "."; |
||||||
|
full_test_name += test_info->name(); |
||||||
|
|
||||||
|
const float32_t freq = 10.0f; |
||||||
|
|
||||||
|
LFO lfo(SAMPLING_FREQUENCY, 0.0f, freq); |
||||||
|
unsigned size = static_cast<unsigned>(8.0f * SAMPLING_FREQUENCY / freq); |
||||||
|
float32_t rate = 0.0f; |
||||||
|
float32_t rate_increment = freq / 2.0f / SAMPLING_FREQUENCY; |
||||||
|
|
||||||
|
std::ofstream out(getResultFile(full_test_name + ".FXComponent.LFO.csv", true)); |
||||||
|
setupOuputStreamForCSV(out); |
||||||
|
out << std::fixed << std::showpoint; |
||||||
|
|
||||||
|
out << "index;LFO" << std::endl; |
||||||
|
for(unsigned i = 0; i < size; ++i) |
||||||
|
{ |
||||||
|
lfo.setNormalizedFrequency(rate); |
||||||
|
out << i << ";" << lfo.process() << std::endl; |
||||||
|
rate += rate_increment; |
||||||
|
|
||||||
|
if(rate >= 1.0f || rate <= 0.0f) |
||||||
|
{ |
||||||
|
rate_increment *= -1.0f; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FXComponent, Flutter) |
||||||
|
{ |
||||||
|
JitterGenerator jg(SAMPLING_FREQUENCY); |
||||||
|
jg.setSpeed(1.0f); |
||||||
|
jg.setMagnitude(0.1f); |
||||||
|
|
||||||
|
for (unsigned i = 0; i < 1000; ++i) |
||||||
|
{ |
||||||
|
jg.process(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FXComponent, SVF) |
||||||
|
{ |
||||||
|
float32_t inL, inR; |
||||||
|
float32_t outL, outR; |
||||||
|
StateVariableFilter svf(SAMPLING_FREQUENCY, StateVariableFilter::Type::LPF, 12000.0f); |
||||||
|
|
||||||
|
{ |
||||||
|
svf.setFilterType(StateVariableFilter::Type::LPF); |
||||||
|
svf.setCutoff(12000.0f); |
||||||
|
svf.setResonance(0.0f); |
||||||
|
unsigned nbSamples = 0; |
||||||
|
unsigned nbErrors = 0; |
||||||
|
while(nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) |
||||||
|
{ |
||||||
|
nbSamples++; |
||||||
|
inL = getRandomValue(); |
||||||
|
inR = getRandomValue(); |
||||||
|
svf.processSample(inL, inR, outL, outR); |
||||||
|
|
||||||
|
if(std::abs(outL) > 1.0f) |
||||||
|
nbErrors++; |
||||||
|
if(std::abs(outR) > 1.0f) |
||||||
|
nbErrors++; |
||||||
|
} |
||||||
|
EXPECT_LT(nbErrors, MAX_NB_ERRORS); |
||||||
|
} |
||||||
|
|
||||||
|
{ |
||||||
|
svf.setFilterType(StateVariableFilter::Type::LPF); |
||||||
|
svf.setCutoff(60.0f); |
||||||
|
svf.setResonance(0.0f); |
||||||
|
unsigned nbSamples = 0; |
||||||
|
unsigned nbErrors = 0; |
||||||
|
while(nbErrors < MAX_NB_ERRORS && nbSamples < MAX_SVF_SAMPLES) |
||||||
|
{ |
||||||
|
nbSamples++; |
||||||
|
inL = getRandomValue(); |
||||||
|
inR = getRandomValue(); |
||||||
|
svf.processSample(inL, inR, outL, outR); |
||||||
|
|
||||||
|
if(std::abs(outL) > 1.0f) |
||||||
|
nbErrors++; |
||||||
|
if(std::abs(outR) > 1.0f) |
||||||
|
nbErrors++; |
||||||
|
} |
||||||
|
EXPECT_LT(nbErrors, MAX_NB_ERRORS); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST(CppOptimization, InterpolatedSineOscillatorPrecisionTest) |
||||||
|
{ |
||||||
|
const float32_t freq = 0.15f; |
||||||
|
const size_t NB = static_cast<size_t>(2.0f * SAMPLING_FREQUENCY); |
||||||
|
|
||||||
|
const float32_t epsilon = 1e-3; |
||||||
|
|
||||||
|
ComplexLFO lfo1(SAMPLING_FREQUENCY, 0.0f, 10.0f); |
||||||
|
InterpolatedSineOscillator lfo2(SAMPLING_FREQUENCY, 0.0f, 10.0f); |
||||||
|
lfo1.setFrequency(freq); |
||||||
|
lfo2.setFrequency(freq); |
||||||
|
float32_t max_delta = 0.0f; |
||||||
|
for(size_t i = 0; i < NB; ++i) |
||||||
|
{ |
||||||
|
float32_t v1 = lfo1.process(); |
||||||
|
float32_t v2 = lfo2.process(); |
||||||
|
|
||||||
|
max_delta = std::max(max_delta, std::abs(v1 - v2)); |
||||||
|
} |
||||||
|
EXPECT_GT(epsilon, max_delta); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(CppOptimization, FastLFOPrecisionTest) |
||||||
|
{ |
||||||
|
const float32_t freq = 0.15f; |
||||||
|
const size_t NB = static_cast<size_t>(2.0f * SAMPLING_FREQUENCY); |
||||||
|
|
||||||
|
const float32_t epsilon = 1e-3; |
||||||
|
|
||||||
|
ComplexLFO lfo1(SAMPLING_FREQUENCY, 0.0f, 10.0f); |
||||||
|
FastLFO lfo2(SAMPLING_FREQUENCY, 0.0f, 10.0f); |
||||||
|
lfo1.setFrequency(freq); |
||||||
|
lfo2.setFrequency(freq); |
||||||
|
float32_t max_delta = 0.0f; |
||||||
|
for(size_t i = 0; i < NB; ++i) |
||||||
|
{ |
||||||
|
float32_t v1 = lfo1.process(); |
||||||
|
float32_t v2 = lfo2.process(); |
||||||
|
|
||||||
|
max_delta = std::max(max_delta, std::abs(v1 - v2)); |
||||||
|
} |
||||||
|
// EXPECT_GT(epsilon, max_delta);
|
||||||
|
} |
@ -0,0 +1,132 @@ |
|||||||
|
#include "test_fx_helper.h" |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <filesystem> |
||||||
|
|
||||||
|
std::string getScenarioName(int scenario) |
||||||
|
{ |
||||||
|
std::stringstream ss; |
||||||
|
|
||||||
|
bool fxTube = Active(scenario, FXSwitch::FX__Tube); |
||||||
|
bool fxChorus = Active(scenario, FXSwitch::FX__Chorus); |
||||||
|
bool fxPhaser = Active(scenario, FXSwitch::FX__Phaser); |
||||||
|
bool fxOrbitone = Active(scenario, FXSwitch::FX__Orbitone); |
||||||
|
bool fxFlanger = Active(scenario, FXSwitch::FX__Flanger); |
||||||
|
bool fxDelay = Active(scenario, FXSwitch::FX__Delay); |
||||||
|
bool fxShimmer = Active(scenario, FXSwitch::FX__ShimmerReverb); |
||||||
|
bool fxReverb = Active(scenario, FXSwitch::FX__PlateReverb); |
||||||
|
bool first = true; |
||||||
|
|
||||||
|
ss << "[ "; |
||||||
|
|
||||||
|
if(fxTube)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Tube"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
if(fxChorus)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Chrs"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
if(fxPhaser)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Phsr"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
if(fxOrbitone)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Orbt"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
if(fxFlanger)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Flgr"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
if(fxDelay)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Dely"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
if(fxReverb)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Revb"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
if(fxShimmer)
|
||||||
|
{ |
||||||
|
if(!first) ss << ", "; |
||||||
|
ss << "Shim"; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
|
||||||
|
ss << " ]"; |
||||||
|
|
||||||
|
return ss.str(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void setupOuputStreamForCSV(std::ostream& out) |
||||||
|
{ |
||||||
|
struct comma_separator : std::numpunct<char> |
||||||
|
{ |
||||||
|
virtual char do_decimal_point() const override { return ','; } |
||||||
|
}; |
||||||
|
|
||||||
|
out.imbue(std::locale(out.getloc(), new comma_separator)); |
||||||
|
out << std::fixed << std::showpoint; |
||||||
|
} |
||||||
|
|
||||||
|
bool createFolderStructure(std::string& path) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
std::filesystem::path file_path(path); |
||||||
|
if(!std::filesystem::exists(file_path.parent_path())) |
||||||
|
{ |
||||||
|
std::filesystem::create_directories(file_path.parent_path()); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
catch(const std::exception& e) |
||||||
|
{ |
||||||
|
std::cerr << e.what() << '\n'; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string getResultFile(const std::string& filename, bool createPath) |
||||||
|
{ |
||||||
|
std::string f = std::string(OUTPUT_FOLDER) + "/" + filename; |
||||||
|
if(createPath) |
||||||
|
{ |
||||||
|
createFolderStructure(f); |
||||||
|
} |
||||||
|
|
||||||
|
return f; |
||||||
|
} |
||||||
|
|
||||||
|
float32_t getRandomValue() |
||||||
|
{ |
||||||
|
static std::random_device rd; |
||||||
|
static std::mt19937 gen(rd()); |
||||||
|
static std::uniform_real_distribution<float32_t> dist(-1.0f, 1.0f); |
||||||
|
|
||||||
|
return dist(gen); |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <random> |
||||||
|
#include <string> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
#include "../fx.h" |
||||||
|
|
||||||
|
#define AUDIO_SOURCE_FILE "test.wav" |
||||||
|
|
||||||
|
#define SAMPLING_FREQUENCY 44100.0f |
||||||
|
|
||||||
|
#define Active(scenarioKey, FxID) ((scenarioKey & (1 << FxID)) == (1 << FxID)) |
||||||
|
|
||||||
|
std::string getScenarioName(int scenario); |
||||||
|
|
||||||
|
enum FXSwitch |
||||||
|
{ |
||||||
|
FX__Tube = 0, |
||||||
|
FX__Chorus, |
||||||
|
FX__Flanger, |
||||||
|
FX__Orbitone, |
||||||
|
FX__Phaser, |
||||||
|
FX__Delay, |
||||||
|
FX__ShimmerReverb, |
||||||
|
FX__PlateReverb, |
||||||
|
__kFXCount |
||||||
|
}; |
||||||
|
|
||||||
|
void setupOuputStreamForCSV(std::ostream& out); |
||||||
|
|
||||||
|
bool createFolderStructure(std::string& path); |
||||||
|
|
||||||
|
std::string getResultFile(const std::string& filename, bool createPath); |
||||||
|
|
||||||
|
float32_t getRandomValue(); |
||||||
|
|
||||||
|
class FXScenarioTest : public testing::TestWithParam<int> {}; |
@ -0,0 +1,117 @@ |
|||||||
|
#include <gtest/gtest.h> |
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
#include "test_fx_helper.h" |
||||||
|
#include "wave.h" |
||||||
|
|
||||||
|
#include "../fx_rack.h" |
||||||
|
#include "../effect_platervbstereo.h" |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
#define MAX_SVF_SAMPLES 10000000 |
||||||
|
#define MAX_NB_ERRORS 100 |
||||||
|
|
||||||
|
void setupRack(FXRack* rack, int scenario) |
||||||
|
{ |
||||||
|
rack->setWetLevel(1.0f); |
||||||
|
|
||||||
|
rack->getTube()->setEnable(Active(scenario, FXSwitch::FX__Tube)); |
||||||
|
rack->getTube()->setWetLevel(0.25f); |
||||||
|
rack->getTube()->setOverdrive(0.25f); |
||||||
|
|
||||||
|
rack->getChorus()->setEnable(Active(scenario, FXSwitch::FX__Chorus)); |
||||||
|
rack->getChorus()->setWetLevel(0.5f); |
||||||
|
rack->getChorus()->setRate(0.4f); |
||||||
|
rack->getChorus()->setDepth(0.5f); |
||||||
|
|
||||||
|
rack->getFlanger()->setEnable(Active(scenario, FXSwitch::FX__Flanger)); |
||||||
|
rack->getFlanger()->setWetLevel(0.5f); |
||||||
|
rack->getFlanger()->setRate(0.03f); |
||||||
|
rack->getFlanger()->setDepth(0.75f); |
||||||
|
rack->getFlanger()->setFeedback(0.5f); |
||||||
|
|
||||||
|
rack->getOrbitone()->setEnable(Active(scenario, FXSwitch::FX__Orbitone)); |
||||||
|
rack->getOrbitone()->setWetLevel(0.8f); |
||||||
|
rack->getOrbitone()->setRate(0.4f); |
||||||
|
rack->getOrbitone()->setDepth(0.5f); |
||||||
|
|
||||||
|
rack->getPhaser()->setEnable(Active(scenario, FXSwitch::FX__Phaser)); |
||||||
|
rack->getPhaser()->setWetLevel(1.0f); |
||||||
|
rack->getPhaser()->setRate(0.1f); |
||||||
|
rack->getPhaser()->setDepth(1.0f); |
||||||
|
rack->getPhaser()->setFeedback(0.5f); |
||||||
|
rack->getPhaser()->setNbStages(12); |
||||||
|
|
||||||
|
rack->getDelay()->setEnable(Active(scenario, FXSwitch::FX__Delay)); |
||||||
|
rack->getDelay()->setWetLevel(0.6f); |
||||||
|
rack->getDelay()->setLeftDelayTime(0.15f); |
||||||
|
rack->getDelay()->setLeftDelayTime(0.2f); |
||||||
|
rack->getDelay()->setFeedback(0.35f); |
||||||
|
rack->getDelay()->setFlutterRate(0.0f); |
||||||
|
rack->getDelay()->setFlutterAmount(0.0f); |
||||||
|
|
||||||
|
rack->getShimmerReverb()->setEnable(Active(scenario, FXSwitch::FX__ShimmerReverb)); |
||||||
|
rack->getShimmerReverb()->setWetLevel(0.5f); |
||||||
|
rack->getShimmerReverb()->setInputGain(0.35f); |
||||||
|
rack->getShimmerReverb()->setTime(0.89f); |
||||||
|
rack->getShimmerReverb()->setDiffusion(0.75f); |
||||||
|
rack->getShimmerReverb()->setLP(0.8f); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(FXScenarioTest, FXRackResetAllScenarios) |
||||||
|
{ |
||||||
|
FXRack *rack = new FXRack(SAMPLING_FREQUENCY); |
||||||
|
|
||||||
|
int fxSwitch = this->GetParam(); |
||||||
|
rack->setEnable(true); |
||||||
|
setupRack(rack, fxSwitch); |
||||||
|
rack->reset(); |
||||||
|
|
||||||
|
delete rack; |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(FXScenarioTest, ScenarioProcessing) |
||||||
|
{ |
||||||
|
const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
||||||
|
std::string full_test_name = test_info->test_case_name(); |
||||||
|
full_test_name += "."; |
||||||
|
full_test_name += test_info->name(); |
||||||
|
|
||||||
|
const unsigned nbRepeats = 1; |
||||||
|
size_t size; |
||||||
|
float32_t** samples = readWaveFile(AUDIO_SOURCE_FILE, size); |
||||||
|
float32_t* sampleOutL = new float32_t[size * nbRepeats]; |
||||||
|
float32_t* sampleOutR = new float32_t[size * nbRepeats]; |
||||||
|
memset(sampleOutL, 0, size * nbRepeats * sizeof(float32_t)); |
||||||
|
memset(sampleOutR, 0, size * nbRepeats * sizeof(float32_t)); |
||||||
|
|
||||||
|
FXRack *rack = new FXRack(SAMPLING_FREQUENCY); |
||||||
|
|
||||||
|
int fxSwitch = this->GetParam(); |
||||||
|
rack->setEnable(true); |
||||||
|
setupRack(rack, fxSwitch); |
||||||
|
rack->reset(); |
||||||
|
|
||||||
|
string name = getScenarioName(fxSwitch); |
||||||
|
|
||||||
|
for(unsigned i = 0; i < nbRepeats; ++i) |
||||||
|
{ |
||||||
|
rack->process(samples[0], samples[1], sampleOutL + i * size, sampleOutR + i * size, size); |
||||||
|
} |
||||||
|
|
||||||
|
stringstream ss; |
||||||
|
ss << full_test_name << "-fx-rack" << name << ".wav"; |
||||||
|
saveWaveFile(getResultFile(ss.str(), true), sampleOutL, sampleOutR, nbRepeats * size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16); |
||||||
|
|
||||||
|
delete[] samples[0]; |
||||||
|
delete[] samples[1]; |
||||||
|
delete[] samples; |
||||||
|
|
||||||
|
delete[] sampleOutL; |
||||||
|
delete[] sampleOutR; |
||||||
|
|
||||||
|
delete rack; |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(FXRack, FXScenarioTest, testing::Range(0, 1 << (FXSwitch::FX__ShimmerReverb + 1))); |
Loading…
Reference in new issue