Fixes on FX to increase stability and performances

pull/409/head
abscisys 2 years ago
parent 6fb78be603
commit a264fdf230
  1. 16
      src/effect_platervbstereo.cpp
  2. 3
      src/extra_features.h
  3. 5
      src/fx.cpp
  4. 4
      src/fx.h
  5. 5
      src/fx_delay.cpp
  6. 49
      src/fx_engine.hpp
  7. 22
      src/fx_flanger.cpp
  8. 23
      src/fx_orbitone.cpp
  9. 63
      src/fx_phaser.cpp
  10. 17
      src/fx_phaser.h
  11. 23
      src/fx_rack.cpp
  12. 15
      src/fx_shimmer_reverb.cpp
  13. 10
      src/fx_tube.cpp
  14. 4
      src/fx_unit.hpp
  15. 26
      src/test/Makefile
  16. 11
      src/test/cppcheck-suppression-list.txt
  17. BIN
      src/test/test3.wav
  18. 67
      src/test/test_cpp.cpp
  19. 2
      src/test/test_cpp_performance.cpp
  20. 2
      src/test/test_framework.cpp
  21. 363
      src/test/test_fxLevelTuning.cpp
  22. 2
      src/test/test_fx_components.cpp
  23. 24
      src/test/test_fx_helper.cpp
  24. 39
      src/test/test_fx_helper.h
  25. 5
      src/test/test_fx_rack.cpp
  26. 182
      src/test/test_fx_shimmer_reverb.cpp
  27. 130
      src/test/test_unitFXTuning.cpp
  28. 15
      src/test/wave.h
  29. 95
      src/test/wavein.cpp
  30. 4
      src/test/waveout.cpp

@ -84,11 +84,10 @@ const int16_t AudioWaveformSine[257] = {
}; };
AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) : AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) :
FXElement(samplerate) FXElement(samplerate, 2.54f),
input_attn(0.5f),
in_allp_k(INP_ALLP_COEFF)
{ {
input_attn = 0.5f;
in_allp_k = INP_ALLP_COEFF;
memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL)); memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL));
memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL)); memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL));
memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL)); memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL));
@ -98,6 +97,8 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) :
in_allp3_idxL = 0; in_allp3_idxL = 0;
in_allp4_idxL = 0; in_allp4_idxL = 0;
in_allp_out_L = 0.0f;
memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR)); memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR));
memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR)); memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR));
memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR)); memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR));
@ -149,6 +150,9 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) :
master_lowpass_l = 0.0f; master_lowpass_l = 0.0f;
master_lowpass_r = 0.0f; master_lowpass_r = 0.0f;
rv_time_k = 0.0f;
rv_time_scaler = 0.0f;
lfo1_phase_acc = 0; lfo1_phase_acc = 0;
lfo1_adder = (UINT32_MAX + 1)/(samplerate * LFO1_FREQ_HZ); lfo1_adder = (UINT32_MAX + 1)/(samplerate * LFO1_FREQ_HZ);
lfo2_phase_acc = 0; lfo2_phase_acc = 0;
@ -398,7 +402,7 @@ void AudioEffectPlateReverb::processSample(float32_t inL, float32_t inR, float32
temp1 = acc - master_lowpass_l; temp1 = acc - master_lowpass_l;
master_lowpass_l += temp1 * master_lowpass_f; master_lowpass_l += temp1 * master_lowpass_f;
outL = master_lowpass_l; outL = master_lowpass_l * this->OutputLevelCorrector;
// Channel R // Channel R
#ifdef TAP1_MODULATED #ifdef TAP1_MODULATED
@ -442,7 +446,7 @@ void AudioEffectPlateReverb::processSample(float32_t inL, float32_t inR, float32
temp1 = acc - master_lowpass_r; temp1 = acc - master_lowpass_r;
master_lowpass_r += temp1 * master_lowpass_f; master_lowpass_r += temp1 * master_lowpass_f;
outR = master_lowpass_r; outR = master_lowpass_r * this->OutputLevelCorrector;
} }
void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR, uint16_t len) void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR, uint16_t len)

@ -37,6 +37,7 @@
inline long long int getElapseTime(std::string marker = "") inline long long int getElapseTime(std::string marker = "")
{ {
static std::unordered_map<std::string, std::chrono::high_resolution_clock::time_point> marker_times; static std::unordered_map<std::string, std::chrono::high_resolution_clock::time_point> marker_times;
auto current_time = std::chrono::high_resolution_clock::now(); auto current_time = std::chrono::high_resolution_clock::now();
auto it = marker_times.find(marker); auto it = marker_times.find(marker);
if (it != marker_times.end()) if (it != marker_times.end())
@ -54,12 +55,12 @@ inline long long int getElapseTime(std::string marker = "")
#define LAP_TIME(marker) getElapseTime(marker) #define LAP_TIME(marker) getElapseTime(marker)
#define LOG_LAP_TIME(marker) { auto __d = getElapseTime(marker); if(__d > 0) std::cout << "Execution time for " << marker << ": " << __d << std::endl; } #define LOG_LAP_TIME(marker) { auto __d = getElapseTime(marker); if(__d > 0) std::cout << "Execution time for " << marker << ": " << __d << std::endl; }
#define DEBUG_VALUE(lbl, idx, v) std::cout << lbl << " " << idx << ": " << v << std::endl #define DEBUG_VALUE(lbl, idx, v) std::cout << lbl << " " << idx << ": " << v << std::endl
#else #else
#define LAP_TIME(marker) #define LAP_TIME(marker)
#define LOG_LAP_TIME(marker) #define LOG_LAP_TIME(marker)
#define DEBUG_VALUE(lbl, idx, v)
#endif #endif

@ -14,8 +14,9 @@ float32_t FXBase::getSamplingRate() const
return this->SamplingRate; return this->SamplingRate;
} }
FXElement::FXElement(float32_t sampling_rate) : FXElement::FXElement(float32_t sampling_rate, float32_t output_level_corrector) :
FXBase(sampling_rate) FXBase(sampling_rate),
OutputLevelCorrector(output_level_corrector)
{ {
} }

@ -48,7 +48,9 @@ class FXElement : public FXBase
DISALLOW_COPY_AND_ASSIGN(FXElement); DISALLOW_COPY_AND_ASSIGN(FXElement);
protected: protected:
FXElement(float32_t sampling_rate); FXElement(float32_t sampling_rate, float32_t output_level_corrector = 1.0f);
const float32_t OutputLevelCorrector;
public: public:
virtual ~FXElement(); virtual ~FXElement();

@ -50,7 +50,7 @@ void Delay::LowHighPassFilter::processSample(float32_t inL, float32_t inR, float
} }
Delay::Delay(const float32_t sampling_rate, float32_t default_delay_time, float32_t default_flutter_level, float32_t default_feedback_level) : Delay::Delay(const float32_t sampling_rate, float32_t default_delay_time, float32_t default_flutter_level, float32_t default_feedback_level) :
FXElement(sampling_rate), FXElement(sampling_rate, 3.46f),
MaxSampleDelayTime((MAX_DELAY_TIME + MAX_FLUTTER_DELAY_TIME) * sampling_rate * MAX_DELAY_TIME), MaxSampleDelayTime((MAX_DELAY_TIME + MAX_FLUTTER_DELAY_TIME) * sampling_rate * MAX_DELAY_TIME),
read_pos_L_(0), read_pos_L_(0),
read_pos_R_(0), read_pos_R_(0),
@ -133,6 +133,9 @@ void Delay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32
{ {
this->read_pos_R_ -= this->MaxSampleDelayTime; this->read_pos_R_ -= this->MaxSampleDelayTime;
} }
outL *= this->OutputLevelCorrector;
outR *= this->OutputLevelCorrector;
} }
void Delay::setLeftDelayTime(float32_t delay_time) void Delay::setLeftDelayTime(float32_t delay_time)

@ -207,14 +207,14 @@ public:
this->accumulator_ = value; this->accumulator_ = value;
} }
inline void read(float32_t value, float32_t scale) inline void read(float32_t value)
{ {
this->accumulator_ += value * scale; this->accumulator_ += value;
} }
inline void read(float32_t value) inline void read(float32_t value, float32_t scale)
{ {
this->accumulator_ += value; this->accumulator_ += value * scale;
} }
inline void write(float32_t& value) inline void write(float32_t& value)
@ -228,10 +228,24 @@ public:
this->accumulator_ *= scale; this->accumulator_ *= scale;
} }
inline void writeAndLoad(float32_t& value, float32_t newValue)
{
value = this->accumulator_;
this->load(newValue);
}
template <typename D> template <typename D>
inline void write(D& d, int32_t offset, float32_t scale) inline void directWrite(float32_t value, D& d)
{
this->load(value);
this->writeAndLoad(d, 0, 0.0f);
}
template <typename D>
inline void write(D& d, int32_t offset)
{ {
assert((D::base + D::length) <= size); assert((D::base + D::length) <= size);
T w = DataType<format>::compress(this->accumulator_); T w = DataType<format>::compress(this->accumulator_);
if(offset == -1) if(offset == -1)
{ {
@ -241,15 +255,34 @@ public:
{ {
this->buffer_[(this->write_ptr_ + D::base + offset) & MASK] = w; this->buffer_[(this->write_ptr_ + D::base + offset) & MASK] = w;
} }
}
template <typename D>
inline void write(D& d, int32_t offset, float32_t scale)
{
this->write(d, offset);
this->accumulator_ *= scale; this->accumulator_ *= scale;
} }
template <typename D>
inline void writeAndLoad(D& d, int32_t offset, float32_t newValue)
{
this->write(d, offset);
this->load(newValue);
}
template <typename D> template <typename D>
inline void write(D& d, float32_t scale) inline void write(D& d, float32_t scale)
{ {
this->write(d, 0, scale); this->write(d, 0, scale);
} }
template <typename D>
inline void writeAndLoad(D& d, float32_t newValue)
{
this->writeAndLoad(d, 0, newValue);
}
template <typename D> template <typename D>
inline void writeAllPass(D& d, int32_t offset, float32_t scale) inline void writeAllPass(D& d, int32_t offset, float32_t scale)
{ {
@ -267,6 +300,7 @@ public:
inline void read(D& d, int32_t offset, float32_t scale) inline void read(D& d, int32_t offset, float32_t scale)
{ {
assert((D::base + D::length) <= size); assert((D::base + D::length) <= size);
T r; T r;
if(offset == -1) if(offset == -1)
{ {
@ -307,8 +341,9 @@ public:
int32_t offset_integral = static_cast<int32_t>(offset); int32_t offset_integral = static_cast<int32_t>(offset);
float32_t offset_fractional = offset - static_cast<float32_t>(offset_integral); float32_t offset_fractional = offset - static_cast<float32_t>(offset_integral);
float32_t a = DataType<format>::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base) & MASK]); int32_t index = this->write_ptr_ + offset_integral + D::base;
float32_t b = DataType<format>::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base + 1) & MASK]); float32_t a = DataType<format>::decompress(this->buffer_[index & MASK]);
float32_t b = DataType<format>::decompress(this->buffer_[(index + 1) & MASK]);
float32_t x = a + (b - a) * offset_fractional; float32_t x = a + (b - a) * offset_fractional;
this->previous_read_ = x; this->previous_read_ = x;

@ -1,9 +1,7 @@
#include "fx_flanger.h" #include "fx_flanger.h"
#include <cmath>
Flanger::Flanger(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback) : Flanger::Flanger(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback) :
FXElement(sampling_rate), FXElement(sampling_rate, 1.17f),
MaxDelayLineSize(static_cast<unsigned>(MAX_FLANGER_DELAY * sampling_rate)), MaxDelayLineSize(static_cast<unsigned>(MAX_FLANGER_DELAY * sampling_rate)),
write_index_(0) write_index_(0)
{ {
@ -40,7 +38,7 @@ void Flanger::reset()
{ {
memset(this->delay_lineL_, 0, this->MaxDelayLineSize * sizeof(float32_t)); memset(this->delay_lineL_, 0, this->MaxDelayLineSize * sizeof(float32_t));
memset(this->delay_lineR_, 0, this->MaxDelayLineSize * sizeof(float32_t)); memset(this->delay_lineR_, 0, this->MaxDelayLineSize * sizeof(float32_t));
memset(this->feedback_samples_, 0, 2 * sizeof(float32_t)); memset(this->feedback_samples_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
this->write_index_ = 0; this->write_index_ = 0;
for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i) for(unsigned i = 0; i < LFOIndex::kLFOCount; ++i)
@ -52,8 +50,8 @@ void Flanger::reset()
void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{ {
// Write sample and any feedback into delay buffers // Write sample and any feedback into delay buffers
this->delay_lineL_[this->write_index_] = inL + this->feedback_samples_[0]; this->delay_lineL_[this->write_index_] = inL + this->feedback_samples_[StereoChannels::Left ];
this->delay_lineR_[this->write_index_] = inR + this->feedback_samples_[1]; this->delay_lineR_[this->write_index_] = inR + this->feedback_samples_[StereoChannels::Right];
++this->write_index_; ++this->write_index_;
if(this->write_index_ >= this->MaxDelayLineSize) if(this->write_index_ >= this->MaxDelayLineSize)
@ -86,8 +84,8 @@ void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float
} }
// Calculate linear interpolation point for left channel // Calculate linear interpolation point for left channel
int currentL = (int)delayReadHeadL; int32_t currentL = static_cast<int32_t>(delayReadHeadL);
int nextL = currentL + 1; int32_t nextL = currentL + 1;
float32_t fractionL = delayReadHeadL - currentL; float32_t fractionL = delayReadHeadL - currentL;
if(nextL >= static_cast<int>(this->MaxDelayLineSize)) if(nextL >= static_cast<int>(this->MaxDelayLineSize))
{ {
@ -95,8 +93,8 @@ void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float
} }
// Calculate linear interpolation point for right channel // Calculate linear interpolation point for right channel
int currentR = (int)delayReadHeadR; int32_t currentR = static_cast<int32_t>(delayReadHeadR);
int nextR = currentR + 1; int32_t nextR = currentR + 1;
float32_t fractionR = delayReadHeadR - currentR; float32_t fractionR = delayReadHeadR - currentR;
if(nextR >= static_cast<int>(this->MaxDelayLineSize)) if(nextR >= static_cast<int>(this->MaxDelayLineSize))
{ {
@ -111,8 +109,8 @@ void Flanger::processSample(float32_t inL, float32_t inR, float32_t& outL, float
this->feedback_samples_[StereoChannels::Left ] = delay_sample_l * this->feedback_; this->feedback_samples_[StereoChannels::Left ] = delay_sample_l * this->feedback_;
this->feedback_samples_[StereoChannels::Right] = delay_sample_r * this->feedback_; this->feedback_samples_[StereoChannels::Right] = delay_sample_r * this->feedback_;
outL = delay_sample_l; outL = delay_sample_l * this->OutputLevelCorrector;
outR = delay_sample_r; outR = delay_sample_r * this->OutputLevelCorrector;
} }
void Flanger::setRate(float32_t rate) void Flanger::setRate(float32_t rate)

@ -1,12 +1,10 @@
#include "fx_orbitone.h" #include "fx_orbitone.h"
#include <cmath>
#define LFO_SLOW_MAX_FREQUENCY 1.0f #define LFO_SLOW_MAX_FREQUENCY 1.0f
#define LFO_FAST_MAX_FREQUENCY 8.8f #define LFO_FAST_MAX_FREQUENCY 8.8f
Orbitone::Orbitone(float32_t sampling_rate, float32_t rate, float32_t depth) : Orbitone::Orbitone(float32_t sampling_rate, float32_t rate, float32_t depth) :
FXElement(sampling_rate), FXElement(sampling_rate, 1.8f),
engine_(sampling_rate, 0.0f), engine_(sampling_rate, 0.0f),
depth_(0.0f), depth_(0.0f),
fullscale_depth_(0.0f) fullscale_depth_(0.0f)
@ -47,8 +45,8 @@ void Orbitone::reset()
void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{ {
typedef Engine::Reserve<2047, Engine::Reserve<2047> > Memory; typedef Engine::Reserve<2047, Engine::Reserve<2047> > Memory;
Engine::DelayLine<Memory, 0> line_l; Engine::DelayLine<Memory, StereoChannels::Left > line_l;
Engine::DelayLine<Memory, 1> line_r; Engine::DelayLine<Memory, StereoChannels::Right> line_r;
Engine::Context c; Engine::Context c;
this->engine_.start(&c); this->engine_.start(&c);
@ -70,23 +68,20 @@ void Orbitone::processSample(float32_t inL, float32_t inR, float32_t& outL, floa
float32_t wet = 0.0f; float32_t wet = 0.0f;
// Sum L & R channel to send to chorus line. c.directWrite(inL, line_l);
c.read(inL, 1.0f); c.directWrite(inR, line_r);
c.write(line_l, 0.0f);
c.read(inR, 1.0f);
c.write(line_r, 0.0f);
c.interpolate(line_l, mod_1 + 1024, 0.33f); c.interpolate(line_l, mod_1 + 1024, 0.33f);
c.interpolate(line_l, mod_2 + 1024, 0.33f); c.interpolate(line_l, mod_2 + 1024, 0.33f);
c.interpolate(line_r, mod_3 + 1024, 0.33f); c.interpolate(line_r, mod_3 + 1024, 0.33f);
c.write(wet, 0.0f); c.writeAndLoad(wet, 0.0f);
outL = wet; outL = wet * this->OutputLevelCorrector;
c.interpolate(line_r, mod_1 + 1024, 0.33f); c.interpolate(line_r, mod_1 + 1024, 0.33f);
c.interpolate(line_r, mod_2 + 1024, 0.33f); c.interpolate(line_r, mod_2 + 1024, 0.33f);
c.interpolate(line_l, mod_3 + 1024, 0.33f); c.interpolate(line_l, mod_3 + 1024, 0.33f);
c.write(wet, 0.0f); c.writeAndLoad(wet, 0.0f);
outR = wet; outR = wet * this->OutputLevelCorrector;
} }
void Orbitone::setRate(float32_t rate) void Orbitone::setRate(float32_t rate)

@ -1,11 +1,7 @@
#include "fx_phaser.h" #include "fx_phaser.h"
#include <algorithm>
#include <cmath>
Phaser::AllpassDelay::AllpassDelay() : Phaser::AllpassDelay::AllpassDelay() :
FXElement(0.0f), FXElement(0.0f)
a1_(0.0f)
{ {
this->reset(); this->reset();
} }
@ -16,32 +12,37 @@ Phaser::AllpassDelay::~AllpassDelay()
void Phaser::AllpassDelay::reset() void Phaser::AllpassDelay::reset()
{ {
memset(this->z_, 0, 2 * sizeof(float32_t)); memset(this->a1_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
memset(this->z_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
} }
void Phaser::AllpassDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) void Phaser::AllpassDelay::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{ {
outL = inL * -this->a1_ + this->z_[0]; outL = inL * -this->a1_[StereoChannels::Left ] + this->z_[StereoChannels::Left ];
this->z_[0] = outL * this->a1_ + inL; this->z_[StereoChannels::Left ] = outL * this->a1_[StereoChannels::Left ] + inL;
outR = inR * -this->a1_ + this->z_[1]; outR = inR * -this->a1_[StereoChannels::Right] + this->z_[StereoChannels::Right];
this->z_[1] = outR * this->a1_ + inR; this->z_[StereoChannels::Right] = outR * this->a1_[StereoChannels::Right] + inR;
} }
void Phaser::AllpassDelay::setDelay(float32_t delay) void Phaser::AllpassDelay::setDelay(float32_t delayL, float32_t delayR)
{ {
this->a1_ = (1.0f - delay) / (1.0f + delay); this->a1_[StereoChannels::Left ] = (1.0f - delayL) / (1.0f + delayL);
this->a1_[StereoChannels::Right] = (1.0f - delayR) / (1.0f + delayR);
} }
Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback, unsigned nb_stages) : Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32_t feedback, unsigned nb_stages) :
FXElement(sampling_rate), FXElement(sampling_rate),
lfo_(sampling_rate, 0.0f, 2.5f),
depth_(0.0f), depth_(0.0f),
gain_(1.0f),
feedback_(0.0f), feedback_(0.0f),
dmin_(0.0f), dmin_(0.0f),
dmax_(0.0f) dmax_(0.0f)
{ {
this->lfo_[StereoChannels::Left ] = new LFO(sampling_rate, 0.0f, 2.5f);
this->lfo_[StereoChannels::Right] = new LFO(sampling_rate, 0.0f, 2.5f, Constants::MPI_2);
this->setRate(rate); this->setRate(rate);
this->setDepth(depth); this->setDepth(depth);
this->setFeedback(feedback); this->setFeedback(feedback);
@ -53,36 +54,42 @@ Phaser::Phaser(float32_t sampling_rate, float32_t rate, float32_t depth, float32
Phaser::~Phaser() Phaser::~Phaser()
{ {
delete this->lfo_[StereoChannels::Left ];
delete this->lfo_[StereoChannels::Right];
} }
void Phaser::reset() void Phaser::reset()
{ {
memset(this->z_, 0, 2 * sizeof(float32_t)); memset(this->z_, 0, StereoChannels::kNumChannels * sizeof(float32_t));
for(unsigned i = 0; i < MAX_NB_PHASES; ++i) for(unsigned i = 0; i < MAX_NB_PHASES; ++i)
{ {
this->stages_[i].reset(); this->stages_[i].reset();
} }
this->lfo_.reset(); this->lfo_[StereoChannels::Left ]->reset();
this->lfo_[StereoChannels::Right]->reset();
} }
void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) void Phaser::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{ {
float32_t d = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_.process()) / 2.0f); float32_t dL = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_[StereoChannels::Left ]->process()) / 2.0f);
float32_t dR = this->dmin_ + (this->dmax_ - this->dmin_) * ((1.0f + this->lfo_[StereoChannels::Right]->process()) / 2.0f);
float32_t sampleL = inL + this->feedback_ * this->z_[0]; float32_t sampleL = inL + this->feedback_ * this->z_[StereoChannels::Left ];
float32_t sampleR = inR + this->feedback_ * this->z_[1]; float32_t sampleR = inR + this->feedback_ * this->z_[StereoChannels::Right];
for(unsigned i = 0; i < this->nb_stages_; ++i) for(unsigned i = 0; i < this->nb_stages_; ++i)
{ {
this->stages_[i].setDelay(d); this->stages_[i].setDelay(dL, dR);
this->stages_[i].processSample(sampleL, sampleR, sampleL, sampleR); this->stages_[i].processSample(sampleL, sampleR, sampleL, sampleR);
} }
this->z_[0] = sampleL; this->z_[StereoChannels::Left ] = sampleL;
this->z_[1] = sampleR; this->z_[StereoChannels::Right] = sampleR;
outL = inL + this->z_[StereoChannels::Left ] * this->depth_; outL = inL + this->z_[StereoChannels::Left ] * this->depth_;
outR = inR + this->z_[StereoChannels::Right] * this->depth_; outR = inR + this->z_[StereoChannels::Right] * this->depth_;
outL *= this->gain_;
outR *= this->gain_;
} }
void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency) void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency)
@ -94,21 +101,23 @@ void Phaser::setFrequencyRange(float32_t min_frequency, float32_t max_frequency)
void Phaser::setRate(float32_t rate) void Phaser::setRate(float32_t rate)
{ {
rate = constrain(rate, 0.0f, 1.0f); rate = constrain(rate, 0.0f, 1.0f);
this->lfo_.setNormalizedFrequency(rate); this->lfo_[StereoChannels::Left ]->setNormalizedFrequency(rate);
this->lfo_[StereoChannels::Right]->setNormalizedFrequency(rate);
} }
inline float32_t Phaser::getRate() const float32_t Phaser::getRate() const
{ {
return this->lfo_.getNormalizedFrequency(); return this->lfo_[StereoChannels::Left]->getNormalizedFrequency();
} }
void Phaser::setDepth(float32_t depth) void Phaser::setDepth(float32_t depth)
{ {
depth = constrain(depth, 0.0f, 1.0f); depth = constrain(depth, 0.0f, 1.0f);
this->depth_ = depth; this->depth_ = depth;
this->gain_ = this->OutputLevelCorrector / (1.0f + depth);
} }
inline float32_t Phaser::getDepth() const float32_t Phaser::getDepth() const
{ {
return this->depth_; return this->depth_;
} }
@ -119,7 +128,7 @@ void Phaser::setFeedback(float32_t feedback)
this->feedback_ = feedback; this->feedback_ = feedback;
} }
inline float32_t Phaser::getFeedback() const float32_t Phaser::getFeedback() const
{ {
return this->feedback_; return this->feedback_;
} }

@ -35,13 +35,13 @@ public:
AllpassDelay(); AllpassDelay();
virtual ~AllpassDelay(); virtual ~AllpassDelay();
virtual void reset(); virtual void reset() override;
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR); virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override;
void setDelay(float32_t delay); void setDelay(float32_t delayL, float32_t delayR);
private: private:
float32_t a1_; float32_t a1_[StereoChannels::kNumChannels];
float32_t z_[StereoChannels::kNumChannels]; float32_t z_[StereoChannels::kNumChannels];
IMPLEMENT_DUMP( IMPLEMENT_DUMP(
@ -99,8 +99,9 @@ public:
unsigned getNbStages() const; unsigned getNbStages() const;
private: private:
LFO lfo_; LFO* lfo_[StereoChannels::kNumChannels];
float32_t depth_; float32_t depth_;
float32_t gain_;
float32_t feedback_; float32_t feedback_;
float32_t dmin_; float32_t dmin_;
float32_t dmax_; float32_t dmax_;
@ -142,7 +143,8 @@ private:
if(deepInspection) if(deepInspection)
{ {
this->lfo_.dump(out, deepInspection, tag + ".lfo_"); this->lfo_[StereoChannels::Left ]->dump(out, deepInspection, tag + ".lfo_[ L ]");
this->lfo_[StereoChannels::Right]->dump(out, deepInspection, tag + ".lfo_[ R ]");
for(unsigned i = 0; i < MAX_NB_PHASES; ++i) for(unsigned i = 0; i < MAX_NB_PHASES; ++i)
{ {
this->stages_[i].dump(out, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]"); this->stages_[i].dump(out, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]");
@ -161,7 +163,8 @@ private:
if(deepInspection) if(deepInspection)
{ {
nb_errors += this->lfo_.inspect(inspector, deepInspection, tag + ".lfo_"); nb_errors += this->lfo_[StereoChannels::Left ]->inspect(inspector, deepInspection, tag + ".lfo_[ L ]");
nb_errors += this->lfo_[StereoChannels::Right]->inspect(inspector, deepInspection, tag + ".lfo_[ R ]");
for(unsigned i = 0; i < MAX_NB_PHASES; ++i) for(unsigned i = 0; i < MAX_NB_PHASES; ++i)
{ {
nb_errors += this->stages_[i].inspect(inspector, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]"); nb_errors += this->stages_[i].inspect(inspector, deepInspection, tag + ".stages_[ " + std::to_string(i) + " ]");

@ -41,19 +41,17 @@ FXRack::~FXRack()
inline void FXRack::reset() inline void FXRack::reset()
{ {
auto end = this->fx_chain_.end(); for(FXElement* fx : this->fx_chain_)
for(FXChain::iterator it = this->fx_chain_.begin(); it != end; it++)
{ {
(*it)->reset();; fx->reset();
} }
} }
inline void FXRack::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) inline void FXRack::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{ {
FXChain::iterator end = this->fx_chain_.end(); for(FXElement* fx : this->fx_chain_)
for(FXChain::iterator it = this->fx_chain_.begin(); it != end; it++)
{ {
(*it)->processSample(inL, inR, outL, outR); fx->processSample(inL, inR, outL, outR);
inL = outL; inL = outL;
inR = outR; inR = outR;
@ -62,17 +60,12 @@ inline void FXRack::processSample(float32_t inL, float32_t inR, float32_t& outL,
void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples) void FXRack::process(float32_t* left_input, float32_t* right_input, float32_t* left_output, float32_t* right_output, size_t nSamples)
{ {
float32_t sampleInL;
float32_t sampleInR;
float32_t sampleOutL;
float32_t sampleOutR;
for(unsigned i = 0; i < nSamples; ++i) for(unsigned i = 0; i < nSamples; ++i)
{ {
sampleInL = *left_input; float32_t sampleInL = *left_input;
sampleInR = *right_input; float32_t sampleInR = *right_input;
sampleOutL = 0.0f; float32_t sampleOutL = 0.0f;
sampleOutR = 0.0f; float32_t sampleOutR = 0.0f;
if(this->isEnable()) if(this->isEnable())
{ {

@ -1,14 +1,12 @@
#include "fx_shimmer_reverb.h" #include "fx_shimmer_reverb.h"
#include <cmath>
#include <algorithm>
#define TAIL , -1 #define TAIL , -1
ShimmerReverb::ShimmerReverb(float32_t sampling_rate) : ShimmerReverb::ShimmerReverb(float32_t sampling_rate) :
FXElement(sampling_rate), FXElement(sampling_rate),
engine_(sampling_rate), engine_(sampling_rate),
input_gain_(-1.0f), input_gain_(-1.0f),
reverb_time_(0.0f),
diffusion_(-1.0f), diffusion_(-1.0f),
lp_(-1.0f), lp_(-1.0f),
lp_decay_1_(0.0f), lp_decay_1_(0.0f),
@ -18,8 +16,11 @@ ShimmerReverb::ShimmerReverb(float32_t sampling_rate) :
this->engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); this->engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f);
this->setInputGain(1.0f); this->setInputGain(1.0f);
this->setLP(0.7f); this->setTime(0.7f);
this->setDiffusion(0.625f); this->setDiffusion(0.625f);
this->setLP(0.7f);
this->reset();
} }
ShimmerReverb::~ShimmerReverb() ShimmerReverb::~ShimmerReverb()
@ -75,7 +76,7 @@ void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL,
// Smear AP1 inside the loop. // Smear AP1 inside the loop.
c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f);
c.write(ap1, 100, 0.0f); c.writeAndLoad(ap1, 100, 0.0f);
c.read(inL + inR, gain); c.read(inL + inR, gain);
// Diffuse through 4 allpasses. // Diffuse through 4 allpasses.
@ -98,7 +99,7 @@ void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL,
c.read(dap1b TAIL, kap); c.read(dap1b TAIL, kap);
c.writeAllPass(dap1b, -kap); c.writeAllPass(dap1b, -kap);
c.write(del1, 2.0f); c.write(del1, 2.0f);
c.write(wet, 0.0f); c.writeAndLoad(wet, 0.0f);
outL = wet; outL = wet;
@ -110,7 +111,7 @@ void ShimmerReverb::processSample(float32_t inL, float32_t inR, float32_t& outL,
c.read(dap2b TAIL, -kap); c.read(dap2b TAIL, -kap);
c.writeAllPass(dap2b, kap); c.writeAllPass(dap2b, kap);
c.write(del2, 2.0f); c.write(del2, 2.0f);
c.write(wet, 0.0f); c.writeAndLoad(wet, 0.0f);
outR = wet; outR = wet;

@ -23,14 +23,14 @@ void Tube::reset()
void Tube::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) void Tube::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR)
{ {
float32_t x = inL * this->saturator_factor_; float32_t x = inL * this->saturator_factor_;
float32_t abs_x = std::abs(x); float32_t abs_x = abs(x);
float32_t sat_x = std::log(1.0f + abs_x) * this->gain_factor_; float32_t sat_x = log(1.0f + abs_x) * this->gain_factor_;
outL = inL > 0 ? sat_x : -sat_x; outL = inL > 0 ? sat_x : -sat_x;
x = inR * this->saturator_factor_; x = inR * this->saturator_factor_;
abs_x = std::abs(x); abs_x = abs(x);
sat_x = std::log(1.0f + abs_x) * this->gain_factor_; sat_x = log(1.0f + abs_x) * this->gain_factor_;
outR = inR > 0 ? sat_x : -sat_x; outR = inR > 0 ? sat_x : -sat_x;
} }
@ -44,7 +44,7 @@ void Tube::setOverdrive(float32_t overdrive)
{ {
this->overdrive_ = overdrive; this->overdrive_ = overdrive;
this->saturator_factor_ = 1.0f + N * overdrive; this->saturator_factor_ = 1.0f + N * overdrive;
this->gain_factor_ = 1.0f / std::log(1.0f + this->saturator_factor_); this->gain_factor_ = this->OutputLevelCorrector / log(1.0f + this->saturator_factor_);
} }
} }

@ -81,7 +81,7 @@ public:
{ {
} }
void reset() void reset() override
{ {
if(!this->is_reset_) if(!this->is_reset_)
{ {
@ -90,7 +90,7 @@ public:
} }
} }
void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override
{ {
if(!this->isEnable() || this->getWetLevel() == 0.0f) if(!this->isEnable() || this->getWetLevel() == 0.0f)
{ {

@ -1,15 +1,23 @@
BINDIR := bin
OBJDIR := objects OBJDIR := objects
OUTPUT_FOLDER = results OUTPUT_FOLDER := results
EXE := all_tests.bin
BETA := beta.bin
CXX := g++ EXE := $(BINDIR)/all_tests.bin
CXXFLAGS = -g -std=c++20 -MMD -MP BETA := $(BINDIR)/beta.bin
CXX = g++
CXXFLAGS = -g -Wall -std=c++20 -MMD -MP
DEFINES = -DCPU=x86 -DDEBUG -DOUTPUT_FOLDER="\"$(OUTPUT_FOLDER)\"" DEFINES = -DCPU=x86 -DDEBUG -DOUTPUT_FOLDER="\"$(OUTPUT_FOLDER)\""
INCLUDES = -I../../CMSIS_5/CMSIS/DSP/Include/ \ INCLUDES = -I../../CMSIS_5/CMSIS/DSP/Include/ \
-I../../CMSIS_5/CMSIS/Core/Include/ \ -I../../CMSIS_5/CMSIS/Core/Include/ \
-I../../Synth_Dexed/src/ -I../../Synth_Dexed/src/
CPPCHECK = cppcheck
CHECKFLAGS = -q -j 8 --enable=all --force --language=c++ \
$(INCLUDES) --platform=unix64 \
--error-exitcode=0 \
--suppressions-list=cppcheck-suppression-list.txt
-include $(TST_OBJS:.o=.d) -include $(TST_OBJS:.o=.d)
-include $(FX__OBJS:.o=.d) -include $(FX__OBJS:.o=.d)
@ -36,6 +44,7 @@ TST_OBJS = $(TST_SRCS:%.cpp=$(OBJDIR)/%.o)
all: $(EXE) test all: $(EXE) test
build: $(EXE)
test: $(EXE) $(OUTPUT_FOLDER) test: $(EXE) $(OUTPUT_FOLDER)
rm -rf $(OUTPUT_FOLDER)/* rm -rf $(OUTPUT_FOLDER)/*
./$(EXE) ./$(EXE)
@ -44,6 +53,9 @@ test-debug: $(EXE) $(OUTPUT_FOLDER)
rm -rf $(OUTPUT_FOLDER)/* 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) 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)
$(BINDIR):
mkdir -p $@
$(OBJDIR): $(OBJDIR):
mkdir -p $@ mkdir -p $@
@ -56,8 +68,8 @@ $(OBJDIR)/%.o: %.cpp $(OBJDIR)
$(OBJDIR)/%.o: ../%.cpp $(OBJDIR) $(OBJDIR)/%.o: ../%.cpp $(OBJDIR)
$(CXX) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@ $(CXX) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@
$(EXE): $(TST_OBJS) $(FX__OBJS) $(EXE): $(BINDIR) $(TST_OBJS) $(FX__OBJS)
$(LD) $(CXXFLAGS) $(call wildcard,$(TST_OBJS)) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS) $(LD) $(CXXFLAGS) $(call wildcard,$(TST_OBJS)) $(call wildcard,$(FX__OBJS)) -o $@ $(LIBS)
clean: clean:
rm -rf *.o $(OBJDIR) $(EXE) $(OUTPUT_FOLDER) rm -rf $(OBJDIR) $(BINDIR) $(OUTPUT_FOLDER)

@ -0,0 +1,11 @@
*:../../CMSIS_5/*
toomanyconfigs:*
noExplicitConstructor:*
unusedFunction:*
missingIncludeSystem:*
unmatchedSuppression:*
// unexplained exceptions
syntaxError:beta.cpp:52
syntaxError:test_fx_mixing_console.cpp:207
internalAstError:test_cpp_performance.cpp:22

Binary file not shown.

@ -1,67 +0,0 @@
#include <gtest/gtest.h>
#include "../fx_components.h"
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);
}

@ -29,7 +29,7 @@ TEST(CppPerformance, LFOPerformance_ComplexLFO_InterpolatedSineOscillator)
} }
auto d2 = LAP_TIME("lfo2"); auto d2 = LAP_TIME("lfo2");
EXPECT_LE(d1, d2 + 100); EXPECT_LE(d1, d2);
} }
TEST(CppPerformance, LFOPerformance_ComplexLFO_FastLFO) TEST(CppPerformance, LFOPerformance_ComplexLFO_FastLFO)

@ -17,5 +17,5 @@ TEST(Framework, TestWaveIn)
nb_errors += fullInspector("R", samples[StereoChannels::Right][i], -1.0f, 1.0f, true); nb_errors += fullInspector("R", samples[StereoChannels::Right][i], -1.0f, 1.0f, true);
} }
EXPECT_EQ(nb_errors, 0); ASSERT_EQ(nb_errors, 0) << "readWaveFile returns NaN of out of bounds samples: " << nb_errors << " out of " << size;
} }

@ -0,0 +1,363 @@
#include "test_fx_helper.h"
#include "../fx_tube.h"
#include "../fx_chorus.h"
#include "../fx_flanger.h"
#include "../fx_orbitone.h"
#include "../fx_phaser.h"
#include "../fx_delay.h"
#include "../effect_platervbstereo.h"
#include "../fx_shimmer_reverb.h"
TEST(LevelTuning, Tube)
{
Tube fx(SAMPLING_FREQUENCY);
fx.reset();
fx.setOverdrive(0.75f);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for Tube";
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}
TEST(LevelTuning, Chorus)
{
Chorus fx(SAMPLING_FREQUENCY);
fx.reset();
fx.setRate(0.4f);
fx.setDepth(0.5f);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for Chorus";
EXPECT_LE(ratio, 1.0f);
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}
TEST(LevelTuning, Flanger)
{
Flanger fx(SAMPLING_FREQUENCY);
fx.reset();
fx.setRate(0.03f);
fx.setDepth(0.75f);
fx.setFeedback(0.5f);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for Flanger";
EXPECT_LE(ratio, 1.0f);
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}
TEST(LevelTuning, Orbitone)
{
Orbitone fx(SAMPLING_FREQUENCY);
fx.reset();
fx.setRate(0.4f);
fx.setDepth(0.5f);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for Orbitone";
EXPECT_LE(ratio, 1.0f);
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}
TEST(LevelTuning, Phaser)
{
Phaser fx(SAMPLING_FREQUENCY);
fx.reset();
fx.setRate(0.1f);
fx.setDepth(1.0f);
fx.setFeedback(0.5f);
fx.setNbStages(12);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for Phaser";
EXPECT_LE(ratio, 1.0f);
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}
TEST(LevelTuning, Delay)
{
Delay fx(SAMPLING_FREQUENCY);
fx.reset();
fx.setLeftDelayTime(0.15f);
fx.setLeftDelayTime(0.2f);
fx.setFeedback(0.35f);
fx.setFlutterRate(0.0f);
fx.setFlutterAmount(0.0f);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for Delay";
EXPECT_LE(ratio, 1.0f);
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}
TEST(LevelTuning, PlateReverb)
{
AudioEffectPlateReverb fx(SAMPLING_FREQUENCY);
fx.reset();
fx.set_bypass(false);
fx.size(0.7f);
fx.hidamp(0.5f);
fx.lodamp(0.5f);
fx.lowpass(0.3f);
fx.diffusion(0.65f);
fx.level(1.0f);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for PlateReverb";
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}
TEST(LevelTuning, ShimmerReverb)
{
ShimmerReverb fx(SAMPLING_FREQUENCY);
fx.reset();
fx.setInputGain(0.35f);
fx.setTime(0.89f);
fx.setDiffusion(0.75f);
fx.setLP(0.8f);
size_t size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t sumIn = 0.0f;
float32_t sumOut = 0.0f;
size_t nb_errors = 0;
for(size_t i = 0; i < size; ++i)
{
float32_t inL = inSamples[0][i];
float32_t inR = inSamples[1][i];
float32_t outL;
float32_t outR;
sumIn += inL * inL;
fx.processSample(inL, inR, outL, outR);
sumOut += outL * outL;
nb_errors += std::abs(outL) > 1.0f ? 1 : 0;
nb_errors += std::abs(outR) > 1.0f ? 1 : 0;
}
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
float32_t ratio = std::sqrt(sumOut / sumIn);
ASSERT_EQ(nb_errors, 0) << "Sample value error for ShimmerReverb";
EXPECT_GE(ratio, 0.9f);
EXPECT_LE(1.0f / ratio, 1.1f);
}

@ -145,5 +145,5 @@ TEST(CppOptimization, FastLFOPrecisionTest)
max_delta = std::max(max_delta, std::abs(v1 - v2)); max_delta = std::max(max_delta, std::abs(v1 - v2));
} }
// EXPECT_GT(epsilon, max_delta); EXPECT_GT(epsilon, max_delta);
} }

@ -1,6 +1,5 @@
#include "test_fx_helper.h" #include "test_fx_helper.h"
#include <iostream>
#include <filesystem> #include <filesystem>
std::string getScenarioName(int scenario) std::string getScenarioName(int scenario)
@ -21,7 +20,7 @@ std::string getScenarioName(int scenario)
if(fxTube) if(fxTube)
{ {
if(!first) ss << ", "; // if(!first) ss << ", ";
ss << "Tube"; ss << "Tube";
first = false; first = false;
} }
@ -72,7 +71,7 @@ std::string getScenarioName(int scenario)
{ {
if(!first) ss << ", "; if(!first) ss << ", ";
ss << "Shim"; ss << "Shim";
first = false; // first = false;
} }
ss << " ]"; ss << " ]";
@ -130,3 +129,22 @@ float32_t getRandomValue()
return dist(gen); return dist(gen);
} }
float32_t** loadAudioTest(size_t& size, WaveHeader* hdr)
{
float32_t** samples = readWaveFile(AUDIO_SOURCE_FILE, size, hdr);
assert(samples != nullptr);
return samples;
}
void freeAudioSamples(float32_t** samples, size_t size)
{
assert(samples != nullptr);
for(size_t i = 0; i < size; ++i)
{
delete[] samples[i];
}
delete[] samples;
}

@ -7,10 +7,44 @@
#include "wave.h" #include "wave.h"
#include "../fx.h" #include "../fx.h"
#define AUDIO_SOURCE_FILE "test2.wav" #define AUDIO_SOURCE_FILE "test3.wav"
#define SAMPLING_FREQUENCY 44100.0f #define SAMPLING_FREQUENCY 44100.0f
#define PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name)\
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();\
WaveHeader hdr;\
size_t size;\
float32_t** inSamples = loadAudioTest(size, &hdr);\
float32_t** outSamples = new float32_t*[2];\
outSamples[0] = new float32_t[size]; memset(outSamples[0], 0, size * sizeof(float32_t));\
outSamples[1] = new float32_t[size]; memset(outSamples[1], 0, size * sizeof(float32_t))
#define CLEANUP_AUDIO_TEST(inSamples, outSamples)\
freeAudioSamples(inSamples, 2);\
freeAudioSamples(outSamples, 2)
#define AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, code)\
for(size_t i = 0; i < size; ++i)\
{\
float32_t inL = inSamples[0][i];\
float32_t inR = inSamples[1][i];\
float32_t outL;\
float32_t outR;\
code\
outSamples[0][i] = outL;\
outSamples[1][i] = outR;\
} //
#define SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx)\
AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx.processSample(inL, inR, outL, outR);)
#define SAVE_AUDIO_RESULTS(filename, samples, size)\
saveWaveFile(getResultFile(filename + ".wav", true), samples[0], samples[1], size, static_cast<unsigned>(SAMPLING_FREQUENCY), 16)
#define Active(scenarioKey, FxID) ((scenarioKey & (1 << FxID)) == (1 << FxID)) #define Active(scenarioKey, FxID) ((scenarioKey & (1 << FxID)) == (1 << FxID))
std::string getScenarioName(int scenario); std::string getScenarioName(int scenario);
@ -37,3 +71,6 @@ std::string getResultFile(const std::string& filename, bool createPath);
float32_t getRandomValue(); float32_t getRandomValue();
class FXScenarioTest : public testing::TestWithParam<int> {}; class FXScenarioTest : public testing::TestWithParam<int> {};
float32_t** loadAudioTest(size_t& size, WaveHeader* hdr);
void freeAudioSamples(float32_t** samples, size_t size);

@ -1,6 +1,3 @@
#include <gtest/gtest.h>
#include <cmath>
#include "test_fx_helper.h" #include "test_fx_helper.h"
#include "../fx_rack.h" #include "../fx_rack.h"
@ -32,7 +29,7 @@ void setupRack(FXRack* rack, int scenario)
rack->getOrbitone()->setEnable(Active(scenario, FXSwitch::FX__Orbitone)); rack->getOrbitone()->setEnable(Active(scenario, FXSwitch::FX__Orbitone));
rack->getOrbitone()->setWetLevel(0.8f); rack->getOrbitone()->setWetLevel(0.8f);
rack->getOrbitone()->setRate(0.4f); rack->getOrbitone()->setRate(0.4f);
rack->getOrbitone()->setDepth(0.5f); rack->getOrbitone()->setDepth(0.7f);
rack->getPhaser()->setEnable(Active(scenario, FXSwitch::FX__Phaser)); rack->getPhaser()->setEnable(Active(scenario, FXSwitch::FX__Phaser));
rack->getPhaser()->setWetLevel(1.0f); rack->getPhaser()->setWetLevel(1.0f);

@ -0,0 +1,182 @@
#include <gtest/gtest.h>
#include "test_fx_helper.h"
#include "../fx_shimmer_reverb.h"
TEST(FXShimmerReverb, TransientSilence)
{
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 size_t size = static_cast<size_t>(SAMPLING_FREQUENCY);
float32_t* inSamples = new float32_t[size];
memset(inSamples, 0, size * sizeof(float32_t));
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[i],
inSamples[i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}
TEST(FXShimmerReverb, TransientSilenceWithDirac)
{
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 size_t size = 4 * static_cast<size_t>(SAMPLING_FREQUENCY);
float32_t* inSamples = new float32_t[size];
memset(inSamples, 0, size * sizeof(float32_t));
inSamples[0] = 1.0f;
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[i],
inSamples[i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}
TEST(FXShimmerReverb, TransientNoise)
{
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 size_t size = static_cast<size_t>(SAMPLING_FREQUENCY);
float32_t* inSamples = new float32_t[size];
for(size_t i = 0; i < size; ++i) inSamples[i] = getRandomValue();
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[i],
inSamples[i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}
TEST(FXShimmerReverb, TransientMusic)
{
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 size;
float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size);
float32_t* outSamplesL = new float32_t[size];
float32_t* outSamplesR = new float32_t[size];
memset(outSamplesL, 0, size * sizeof(float32_t));
memset(outSamplesR, 0, size * sizeof(float32_t));
ShimmerReverb* shimmer = new ShimmerReverb(SAMPLING_FREQUENCY);
shimmer->setInputGain(0.55f);
shimmer->setTime(0.75f);
shimmer->setDiffusion(0.8f);
shimmer->setLP(0.7f);
shimmer->reset();
for(size_t i = 0; i < size; ++i)
{
shimmer->processSample(
inSamples[0][i],
inSamples[1][i],
outSamplesL[i],
outSamplesR[i]
);
}
saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16);
delete shimmer;
delete[] inSamples[0];
delete[] inSamples[1];
delete[] inSamples;
delete[] outSamplesL;
delete[] outSamplesR;
}

@ -0,0 +1,130 @@
#include "test_fx_helper.h"
#include "../fx_dry.h"
#include "../fx_tube.h"
#include "../fx_chorus.h"
#include "../fx_flanger.h"
#include "../fx_orbitone.h"
#include "../fx_phaser.h"
#include "../fx_delay.h"
#include "../effect_platervbstereo.h"
#include "../fx_shimmer_reverb.h"
TEST(UnitFXTuning, Dry)
{
Dry fx(SAMPLING_FREQUENCY);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Tube)
{
Tube fx(SAMPLING_FREQUENCY);
fx.setOverdrive(0.5f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Chorus)
{
Chorus fx(SAMPLING_FREQUENCY);
fx.setRate(0.4f);
fx.setDepth(0.7f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Flanger)
{
Flanger fx(SAMPLING_FREQUENCY);
fx.setRate(0.03f);
fx.setDepth(0.75f);
fx.setFeedback(0.5f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Orbitone)
{
Orbitone fx(SAMPLING_FREQUENCY);
fx.setRate(0.4f);
fx.setDepth(0.7f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Phaser)
{
Phaser fx(SAMPLING_FREQUENCY);
fx.setRate(0.1f);
fx.setDepth(1.0f);
fx.setFeedback(0.5f);
fx.setNbStages(12);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, Delay)
{
Delay fx(SAMPLING_FREQUENCY);
fx.setLeftDelayTime(0.25f);
fx.setLeftDelayTime(0.40f);
fx.setFeedback(0.55f);
fx.setFlutterRate(0.01f);
fx.setFlutterAmount(0.05f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, PlateReverb)
{
AudioEffectPlateReverb fx(SAMPLING_FREQUENCY);
fx.set_bypass(false);
fx.size(0.7f);
fx.hidamp(0.5f);
fx.lodamp(0.5f);
fx.lowpass(0.3f);
fx.diffusion(0.65f);
fx.level(1.0f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}
TEST(UnitFXTuning, ShimmerReverb)
{
ShimmerReverb fx(SAMPLING_FREQUENCY);
fx.setInputGain(0.65f);
fx.setTime(0.89f);
fx.setDiffusion(0.75f);
fx.setLP(0.8f);
PREPARE_AUDIO_TEST(size, inSamples, outSamples, full_test_name);
SIMPLE_AUDIO_LOOP(inSamples, outSamples, size, inL, inR, outL, outR, fx);
SAVE_AUDIO_RESULTS(full_test_name, outSamples, size);
CLEANUP_AUDIO_TEST(inSamples, outSamples);
}

@ -3,6 +3,7 @@
#include <stdint.h> #include <stdint.h>
#include <arm_math.h> #include <arm_math.h>
#include <string> #include <string>
#include <iostream>
inline uint32_t id2int(const char id[4]) inline uint32_t id2int(const char id[4])
{ {
@ -57,15 +58,21 @@ struct WaveHeaderFMT {
}; };
struct WaveHeaderDATA { struct WaveHeaderDATA {
char subchunk2Id[4]; ChunkID subchunk2Id;
uint32_t subchunk2Size; uint32_t subchunk2Size;
}; };
float32_t** readWaveFile(const std::string& fileName, size_t& size); std::ostream& operator<<(std::ostream& out, const ChunkID& id);
std::ostream& operator<<(std::ostream& out, const WaveHeader& hdr);
std::ostream& operator<<(std::ostream& out, const WaveHeaderRIFF& riff);
std::ostream& operator<<(std::ostream& out, const WaveHeaderFMT& fmt);
std::ostream& operator<<(std::ostream& out, const WaveHeaderDATA& data);
float32_t** readWaveFile(const std::string& fileName, size_t& size, WaveHeader* hdr = nullptr);
void saveWaveFile(const std::string& fileName, void saveWaveFile(const std::string& fileName,
float32_t* LChannel, const float32_t* LChannel,
float32_t* RChannel, const float32_t* RChannel,
size_t size, size_t size,
int sampleRate, int sampleRate,
int bitsPerSample); int bitsPerSample);

@ -10,6 +10,72 @@
#define ASSERT_NORMALIZED(x) #define ASSERT_NORMALIZED(x)
#endif #endif
std::ostream& operator<<(std::ostream& out, const WaveHeader& hdr)
{
out << "WaveHeader" << std::endl;
out << " + chunkId : " << hdr.chunkId << std::endl;
out << " + chunkSize : " << hdr.chunkSize << std::endl;
out << " + format : " << hdr.format << std::endl;
out << " + subchunk1Id : " << hdr.subchunk1Id << std::endl;
out << " + subchunk1Size : " << hdr.subchunk1Size << std::endl;
out << " + audioFormat : " << hdr.audioFormat << std::endl;
out << " + numChannels : " << hdr.numChannels << std::endl;
out << " + sampleRate : " << hdr.sampleRate << std::endl;
out << " + byteRate : " << hdr.byteRate << std::endl;
out << " + blockAlign : " << hdr.blockAlign << std::endl;
out << " + bitsPerSample : " << hdr.bitsPerSample << std::endl;
out << " + subchunk2Id : " << hdr.subchunk2Id << std::endl;
out << " + subchunk2Size : " << hdr.subchunk2Size << std::endl;
return out;
}
std::ostream& operator<<(std::ostream& out, const ChunkID& id)
{
out << "'"
<< id.ID[0]
<< id.ID[1]
<< id.ID[2]
<< id.ID[3]
<< "'";
return out;
}
std::ostream& operator<<(std::ostream& out, const WaveHeaderRIFF& riff)
{
out << "WaveHeaderRIFF" << std::endl;
out << " + chunkId : " << riff.chunkId << std::endl;
out << " + chunkSize : " << riff.chunkSize << std::endl;
out << " + format : " << riff.format << std::endl;
return out;
}
std::ostream& operator<<(std::ostream& out, const WaveHeaderFMT& fmt)
{
out << "WaveHeaderFMT" << std::endl;
out << " + subchunk1Id : " << fmt.subchunk1Id << std::endl;
out << " + subchunk1Size : " << fmt.subchunk1Size << std::endl;
out << " + audioFormat : " << fmt.audioFormat << std::endl;
out << " + numChannels : " << fmt.numChannels << std::endl;
out << " + sampleRate : " << fmt.sampleRate << std::endl;
out << " + byteRate : " << fmt.byteRate << std::endl;
out << " + blockAlign : " << fmt.blockAlign << std::endl;
out << " + bitsPerSample : " << fmt.bitsPerSample << std::endl;
return out;
}
std::ostream& operator<<(std::ostream& out, const WaveHeaderDATA& data)
{
out << "WaveHeaderDATA" << std::endl;
out << " + subchunk2Id : " << data.subchunk2Id << std::endl;
out << " + subchunk2Size : " << data.subchunk2Size << std::endl;
return out;
}
template<typename T> template<typename T>
bool readChunk(std::ifstream& in, uint32_t id, T& chunk) bool readChunk(std::ifstream& in, uint32_t id, T& chunk)
{ {
@ -33,7 +99,7 @@ bool readChunk(std::ifstream& in, uint32_t id, T& chunk)
return false; return false;
} }
float32_t** readWaveFile(const std::string& fileName, size_t& size) float32_t** readWaveFile(const std::string& fileName, size_t& size, WaveHeader* hdr)
{ {
std::ifstream file(fileName, std::ios::binary); std::ifstream file(fileName, std::ios::binary);
if(!file) if(!file)
@ -75,12 +141,28 @@ float32_t** readWaveFile(const std::string& fileName, size_t& size)
return nullptr; return nullptr;
} }
size = data.subchunk2Size / (fmt.bitsPerSample / 8); if(hdr != nullptr)
{
hdr->chunkId = riff.chunkId;
hdr->chunkSize = riff.chunkSize;
hdr->format = riff.format;
hdr->subchunk1Id = fmt.subchunk1Id;
hdr->subchunk1Size = fmt.subchunk1Size;
hdr->audioFormat = fmt.audioFormat;
hdr->numChannels = fmt.numChannels;
hdr->sampleRate = fmt.sampleRate;
hdr->byteRate = fmt.byteRate;
hdr->blockAlign = fmt.blockAlign;
hdr->bitsPerSample = fmt.bitsPerSample;
hdr->subchunk2Id = data.subchunk2Id;
hdr->subchunk2Size = data.subchunk2Size;
}
size = data.subchunk2Size / fmt.blockAlign;
float32_t* LChannel = new float32_t[size]; float32_t* LChannel = new float32_t[size];
float32_t* RChannel = new float32_t[size]; float32_t* RChannel = new float32_t[size];
unsigned increment = fmt.numChannels; size_t i = 0;
unsigned i = 0;
while(!file.eof() && i < size) while(!file.eof() && i < size)
{ {
if(fmt.bitsPerSample == 8) if(fmt.bitsPerSample == 8)
@ -153,10 +235,7 @@ float32_t** readWaveFile(const std::string& fileName, size_t& size)
return nullptr; return nullptr;
} }
// ASSERT_NORMALIZED(LChannel[i]); ++i;
// ASSERT_NORMALIZED(RChannel[i]);
i += increment;
} }
assert(i == size); assert(i == size);

@ -5,8 +5,8 @@
#include <cstring> #include <cstring>
void saveWaveFile(const std::string& fileName, void saveWaveFile(const std::string& fileName,
float32_t* LChannel, const float32_t* LChannel,
float32_t* RChannel, const float32_t* RChannel,
size_t size, size_t size,
int sampleRate, int sampleRate,
int bitsPerSample) int bitsPerSample)

Loading…
Cancel
Save